NV_STEREO_IMAGE_SIGNATURE and DirectX 10/11 (nVidia 3D Vision)
Asked Answered
G

1

3

I'm trying to use SlimDX and DirectX10 or 11 to control the stereoization process on the nVidia 3D Vision Kit. Thanks to this question I've been able to make it work in DirectX 9. However, due to some missing methods I've been unable to make it work under DirectX 10 or 11.

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 (slimdx): Stereoization procedure

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;
}

NV_STEREO_IMAGE_SIGNATURE data

// 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

Main

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();
}[/code]

Thanks in advance!

Gothicism answered 11/9, 2011 at 11:18 Comment(1)
I am trying to do something similar. I got a good idea of how it works from your first post but I think I am not getting it right. Could you please share the last bit. ThanksQuinby
G
4

I eventually managed to fix it. The key was in using the CopySubResourceRegion method on the stereoized texture back to the backbuffer, specifying its dimension (e.g.: 1920 x 1080 instead of 3840 x 1081).

Gothicism answered 19/9, 2011 at 22:0 Comment(1)
For posterity, here is a link to a working self-contained SlimDX program that uses this method.Gothicism

© 2022 - 2024 — McMap. All rights reserved.