Jump to content
  • Advertisement
Sign in to follow this  
AvengerDr

[SlimDX] [DX10/11] NV_STEREO_IMAGE_SIGNATURE and DirectX10/11

This topic is 2623 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi there,
a while ago I posted a thread asking for advice on how to control the stereoization process on the 3D Vision kit by nVidia. As some of you may be aware there is a low-level hack in which you render the left and right eye images manually, then you write a special value in the last row. Upon rendering this new scene, the nVidia driver picks the value up and activates the shutter glasses. I've been able to test it under DX9 so I know that it definitely can work but due to some methods not being available anymore in DX10/11 I've been unable to make it work.

The algorithm goes like this:
  1. Render left eye image
  2. Render right eye image
  3. Create a texture able to contain them both PLUS an extra row (so the texture size would be 2 * width, height + 1)
  4. Write this NV_STEREO_IMAGE_SIGNATURE value
  5. Render this texture on the screen

My test code skips the first two steps, as I already have a stereo texture. It was a former .JPS file, specifically one of those included in the sample pictures coming with the nvidia 3D kit. Step number 5 uses a full screen quad and a shader to render the stereoized texture onto it through an ortho-projection matrix. The sample code I've seen for DX9 doesn't need this and simply calls the StretchRect(...) method to copy the texture back onto the backbuffer. So maybe it is for this reason that is not working? Is there a similar method to accomplish this in DX10? I thought that rendering onto the backbuffer would theoretically be the same than copying (or StretchRecting) a texture onto it, but maybe it is not?

Here follows my code:
Stereoization procedure
[source lang="csharp"]

static Texture2D Make3D(Texture2D stereoTexture)
{
// 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 = 3840,
Height = 1081,
BindFlags = BindFlags.None,
CpuAccessFlags = CpuAccessFlags.Write,
Format = SlimDX.DXGI.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 = 1080, Left = 0, Right = 3840 };
// Copy it to the staging texture
device.CopySubresourceRegion(stereoTexture, 0, stereoSrcBox, staging, 0, 0, 0, 0);

// Open the staging texture for reading
DataRectangle box = staging.Map(0, MapMode.Write, SlimDX.Direct3D10.MapFlags.None);
// Go to the last row
box.Data.Seek(stereoTexture.Description.Width * stereoTexture.Description.Height * 4, System.IO.SeekOrigin.Begin);
// Write the NVSTEREO header
box.Data.Write(data, 0, data.Length);
staging.Unmap(0);

// Create the final stereoized texture
Texture2DDescription finalDesc = new Texture2DDescription()
{
ArraySize = 1,
Width = 3840,
Height = 1081,
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.CopyResource(staging, final);
staging.Dispose();
return final;
}[/source]
NV_STEREO_IMAGE_SIGNATURE data
[source lang="csharp"]

// The NVSTEREO header.
static 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;
0x02, 0x00, 0x00, 0x00}; //dwFlags = SIH_SCALE_TO_FIT = 0x00000002
[/source]
Main
[source lang="csharp"]

private static Device device;

[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 = true,
OutputHandle = form.Handle,
SampleDescription = new SampleDescription(1, 0),
SwapEffect = SwapEffect.Discard,
Usage = Usage.RenderTargetOutput
};

SwapChain swapChain;
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 = 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
Texture2D sourceTexture = Texture2D.FromFile(device, "medusa.jpg", info);
Texture2D stereoizedTexture = Make3D(sourceTexture);
ShaderResourceView srv = new ShaderResourceView(device, stereoizedTexture);

// Create a quad that fills the whole screen
ushort[] idx;
TexturedVertex[] quad = CreateTexturedQuad(Vector3.Zero, 1920, 1080, out idx);

// fill vertex and index buffers
DataStream stream = new DataStream(4*24, true, true);
stream.WriteRange(quad);
stream.Position = 0;

Buffer vertices = new SlimDX.Direct3D10.Buffer(device, stream, new BufferDescription()
{
BindFlags = BindFlags.VertexBuffer,
CpuAccessFlags = CpuAccessFlags.None,
OptionFlags = ResourceOptionFlags.None,
SizeInBytes = 4*24,
Usage = ResourceUsage.Default
});
stream.Close();

stream = new DataStream(6*sizeof (ushort), true, true);
stream.WriteRange(idx);
stream.Position = 0;
Buffer indices = new SlimDX.Direct3D10.Buffer(device, stream, new BufferDescription()
{
BindFlags = BindFlags.IndexBuffer,
CpuAccessFlags = CpuAccessFlags.None,
OptionFlags = ResourceOptionFlags.None,
SizeInBytes = 6*sizeof (ushort),
Usage = ResourceUsage.Default
});

// Create world view (ortho) projection matrices
QuaternionCam qCam = new QuaternionCam();

// Load effect from file. It is a basic effect that renders a full screen quad through
// an ortho projectio=n matrix
Effect effect = Effect.FromFile(device, "Texture.fx", "fx_4_0", ShaderFlags.Debug, EffectFlags.None);
EffectTechnique technique = effect.GetTechniqueByIndex(0);
EffectPass pass = technique.GetPassByIndex(0);
InputLayout layout = new InputLayout(device, pass.Description.Signature, new[]
{
new InputElement("POSITION", 0,
Format.
R32G32B32A32_Float,
0, 0),
new InputElement("TEXCOORD", 0,
Format.
R32G32_Float,
16, 0)
});
effect.GetVariableByName("mWorld").AsMatrix().SetMatrix(
Matrix.Translation(Layout.OrthographicTransform(Vector2.Zero, 90, new Size(1920, 1080))));
effect.GetVariableByName("mView").AsMatrix().SetMatrix(qCam.View);
effect.GetVariableByName("mProjection").AsMatrix().SetMatrix(qCam.OrthoProjection);
effect.GetVariableByName("tDiffuse").AsResource().SetResource(srv);

// Set RT and Viewports
device.OutputMerger.SetTargets(renderView);
device.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.Rasterizer.State = rState;

// Main Loop
MessagePump.Run(form, () =>
{
device.ClearRenderTargetView(renderView, Color.Cyan);

device.InputAssembler.SetInputLayout(layout);
device.InputAssembler.SetPrimitiveTopology(PrimitiveTopology.TriangleList);
device.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(vertices, 24, 0));
device.InputAssembler.SetIndexBuffer(indices, Format.R16_UInt, 0);

for (int i = 0; i < technique.Description.PassCount; ++i)
{
// Render the full screen quad
pass.Apply();
device.DrawIndexed(6, 0, 0);
}

swapChain.Present(0, PresentFlags.None);
});

// Dispose resources
vertices.Dispose();
layout.Dispose();
effect.Dispose();
renderView.Dispose();
backBuffer.Dispose();
device.Dispose();
swapChain.Dispose();

rState.Dispose();
stereoizedTexture.Dispose();
sourceTexture.Dispose();
indices.Dispose();
srv.Dispose();
}[/source]

Thanks in advance!

Share this post


Link to post
Share on other sites
Advertisement
I finally got it to work!
Apparently rendering the stereoized texture to a quad and then copying the results over to the backbuffer was the wrong approach. What needs to be done to emulate DX9's stretchrect is the following:

[source lang="csharp"]


MessagePump.Run(form, () =>
{
device.ClearRenderTargetView(renderView, Color.Cyan);
ResourceRegion stereoSrcBox = new ResourceRegion { Front = 0, Back = 1, Top = 0, Bottom = size.Height, Left = 0, Right = size.Width };
device.CopySubresourceRegion(stereoizedTexture, 0, stereoSrcBox, backBuffer, 0, 0, 0, 0);
swapChain.Present(0, PresentFlags.None);
});
[/source]

The help states that ResourceRegion is "The source region". So I thought that if I specified 1920x1080 instead of 3840 * 1081 it would only grab the leftmost part of the image instead of the whole picture. I'm not entirely sure why it is working.. but at least it definitely is.. I tried to erase one part of the image and if you block one eye you will see a blank screen.

For those of you who are experimenting with nVidia's default stereo pictures, the eyes are swapped so remember to also use the swap eyes flag 0x00000001.
If anyone else needs some help, I'd be happy to provide it!

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!