Skip to content

Instantly share code, notes, and snippets.

@AvengerDr
Last active October 29, 2018 23:45
Show Gist options
  • Save AvengerDr/6062614 to your computer and use it in GitHub Desktop.
Save AvengerDr/6062614 to your computer and use it in GitHub Desktop.
How to use the NV_STEREO_IMAGE_SIGNATURE "trick" to enable stereoscopy by using DirectX11 and SlimDX.
// StereoTest
//
// This program shows how to display a stereoscopic texture.
// It loads a texture from disk containing a left and right image side by side
// and writes the NV_STEREO_IMAGE_SIGNATURE to the last row.
// This line is intercepted by the nVidia drivers and causes the 3D IR emitter to turn on
// and synchronize the shutter glasses.
//
using System.Windows.Forms;
using SlimDX.Windows;
using SlimDX;
using SlimDX.Direct3D11;
using SlimDX.DXGI;
using System.Drawing;
using System;
namespace StereoTest
{
static class Program
{
private static readonly byte[] data = new byte[]
{
0x4e, 0x56, 0x33, 0x44, //NVSTEREO_IMAGE_SIGNATURE = 0x4433564e;
0x00, 0x0F, 0x00, 0x00, //Screen width * 2 = 1920*2 = 3840 = 0x00000F00;
0x38, 0x04, 0x00, 0x00, //Screen height = 1080 = 0x00000438;
0x20, 0x00, 0x00, 0x00, //dwBPP = 32 = 0x00000020;
0x03, 0x00, 0x00, 0x00 //dwFlags = SIH_SCALE_TO_FIT = 0x00000003
};
private static SlimDX.Direct3D11.Device device;
public static Texture2D Make3D(Texture2D stereoTexture, SlimDX.Direct3D11.Device device, Size screenSize)
{
//NvStereoImageHeader header = new NvStereoImageHeader(0x4433564e, 3840, 1080, 4, 0x00000002);
// stereoTexture contains a stereo image with the left eye image on the left half
// and the right eye image on the right half
// this staging texture will have an extra row to contain the stereo signature
Texture2DDescription stagingDesc = new Texture2DDescription()
{
ArraySize = 1,
Width = 2 * screenSize.Width,
Height = screenSize.Height + 1,
BindFlags = BindFlags.None,
CpuAccessFlags = CpuAccessFlags.Write,
Format = Format.R8G8B8A8_UNorm,
OptionFlags = ResourceOptionFlags.None,
Usage = ResourceUsage.Staging,
MipLevels = 1,
SampleDescription = new SampleDescription(1, 0)
};
Texture2D staging = new Texture2D(device, stagingDesc);
// Identify the source texture region to copy (all of it)
ResourceRegion stereoSrcBox = new ResourceRegion { Front = 0, Back = 1, Top = 0, Bottom = screenSize.Height, Left = 0, Right = 2 * screenSize.Width };
// Copy it to the staging texture
device.ImmediateContext.CopySubresourceRegion(stereoTexture, 0, stereoSrcBox, staging, 0, 0, 0, 0);
// Open the staging texture for reading
DataBox box = device.ImmediateContext.MapSubresource(staging, 0, MapMode.Write, SlimDX.Direct3D11.MapFlags.None);
//DataRectangle box = staging.Map(0, MapMode.Write, SlimDX.Direct3D10.MapFlags.None);
// Go to the last row
box.Data.Seek(box.RowPitch * screenSize.Height, System.IO.SeekOrigin.Begin);
// Write the NVSTEREO header
box.Data.Write(data, 0, data.Length);
device.ImmediateContext.UnmapSubresource(staging, 0);
// Create the final stereoized texture
Texture2DDescription finalDesc = new Texture2DDescription()
{
ArraySize = 1,
Width = 2 * screenSize.Width,
Height = screenSize.Height + 1,
BindFlags = BindFlags.ShaderResource,
CpuAccessFlags = CpuAccessFlags.Write,
Format = SlimDX.DXGI.Format.R8G8B8A8_UNorm,
OptionFlags = ResourceOptionFlags.None,
Usage = ResourceUsage.Dynamic,
MipLevels = 1,
SampleDescription = new SampleDescription(1, 0)
};
// Copy the staging texture on a new texture to be used as a shader resource
Texture2D final = new Texture2D(device, finalDesc);
device.ImmediateContext.CopyResource(staging, final);
staging.Dispose();
return final;
}
[STAThread]
static void Main()
{
// Device creation
var form = new RenderForm("Stereo test") { ClientSize = new Size(1920, 1080) };
var desc = new SwapChainDescription()
{
BufferCount = 1,
ModeDescription = new ModeDescription(1920, 1080, new Rational(120, 1), Format.R8G8B8A8_UNorm),
IsWindowed = false,
OutputHandle = form.Handle,
SampleDescription = new SampleDescription(1, 0),
SwapEffect = SwapEffect.Discard,
Usage = Usage.RenderTargetOutput
};
SwapChain swapChain;
SlimDX.Direct3D11.Device.CreateWithSwapChain(null, DriverType.Hardware, DeviceCreationFlags.Debug, desc, out device, out swapChain);
//Stops Alt+enter from causing fullscreen skrewiness.
Factory factory = swapChain.GetParent<Factory>();
factory.SetWindowAssociation(form.Handle, WindowAssociationFlags.IgnoreAll);
Texture2D backBuffer = SlimDX.Direct3D11.Resource.FromSwapChain<Texture2D>(swapChain, 0);
RenderTargetView renderView = new RenderTargetView(device, backBuffer);
ImageLoadInformation info = new ImageLoadInformation()
{
BindFlags = BindFlags.None,
CpuAccessFlags = CpuAccessFlags.Read,
FilterFlags = FilterFlags.None,
Format = SlimDX.DXGI.Format.R8G8B8A8_UNorm,
MipFilterFlags = FilterFlags.None,
OptionFlags = ResourceOptionFlags.None,
Usage = ResourceUsage.Staging,
MipLevels = 1
};
// Make texture 3D
Size screenSize = new Size(1920, 1080);
Texture2D sourceTexture = Texture2D.FromFile(device, "medusa.jpg", info);
Texture2D stereoTexture = Make3D(sourceTexture, device, screenSize);
// Set RT and Viewports
device.ImmediateContext.OutputMerger.SetTargets(renderView);
device.ImmediateContext.Rasterizer.SetViewports(new Viewport(0, 0, form.ClientSize.Width, form.ClientSize.Height, 0.0f, 1.0f));
// Create solid rasterizer state
RasterizerStateDescription rDesc = new RasterizerStateDescription()
{
CullMode = CullMode.None,
IsDepthClipEnabled = true,
FillMode = FillMode.Solid,
IsAntialiasedLineEnabled = true,
IsFrontCounterclockwise = true,
IsMultisampleEnabled = true
};
RasterizerState rState = RasterizerState.FromDescription(device, rDesc);
device.ImmediateContext.Rasterizer.State = rState;
ResourceRegion stereoSourceBox = new ResourceRegion
{
Front = 0,
Back = 1,
Top = 0,
Bottom = screenSize.Height,
Left = 0,
Right = screenSize.Width
};
// Main Loop
MessagePump.Run(form, () =>
{
DeviceContext context = device.ImmediateContext;
// to make sure at least the target is being cleared...
context.ClearRenderTargetView(renderView, Color.Cyan);
context.CopySubresourceRegion(stereoTexture, 0, stereoSourceBox, backBuffer, 0, 0, 0, 0);
swapChain.Present(0, PresentFlags.None);
});
renderView.Dispose();
backBuffer.Dispose();
device.Dispose();
swapChain.Dispose();
rState.Dispose();
stereoTexture.Dispose();
sourceTexture.Dispose();
}
}
}
@sevstels
Copy link

sevstels commented Mar 20, 2017

Application crashed by error in:

SlimDX.Direct3D11.Device.CreateWithSwapChain(null, DriverType.Hardware, DeviceCreationFlags.Debug, desc, out device, out swapChain);

'This method is confusing because only one of the adapter/driverType parameters is valid during the call. Use one of the overloads that explicitly uses the parameter you wish to provide.'

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment