0
\$\begingroup\$

Just for background, I'm designing a soft engine in C# using the SharpDX DirectX wrapper library. I've gotten around to implementing the render engine, and I can render a texture to a face, except it only renders exactly one frame. It renders a triangle once, but on the next draw call, the screen is cleared. Even though I've made sure the render call is still firing every frame, the rendered item goes away, and does not get re-drawn in the next or any subsequent frame.

I think it has something to do with the way I initialize my resources; to minimize shader switching and boost performance, I have my render engine hold a set of pre-defined shader classes. Then, only when it is necessary will the shader access the device context and set the vertex, constant, index, etc. buffers one time when the method Shader.SetActive() is called. When the shader needs to render something, it can write directly to the buffers that have been created and set in the Shader.SetActive() method, using a method called UpdateShader().


Here's the source for SetActive():

public override void SetActive(D3D11.Device device, D3D11.DeviceContext context)
{
    /*========================== Set Pointers to GPU Buffers ==========================*/
    // write Matrix data first to constant buffer
    matrixBuffer = new D3D11.Buffer(device, matrixBufferDesc);
    vertexBuffer = new D3D11.Buffer(device, vertexBufferDesc);
    indexBuffer = new D3D11.Buffer(device, indexBufferDesc);

    context.InputAssembler.SetVertexBuffers(0, new D3D11.VertexBufferBinding(vertexBuffer, 
    Utilities.SizeOf<VertexPositionTexture>(), 0));
    context.InputAssembler.SetIndexBuffer(indexBuffer, Format.R32_UInt, 0);
    context.VertexShader.SetConstantBuffer(matrixBufferPos, matrixBuffer);
    context.PixelShader.SetShaderResource(0, shaderTexture);

    context.InputAssembler.InputLayout = inputLayout;

    /*========================== set context shaders ==========================*/
    context.VertexShader.Set(vertexShader);
    context.PixelShader.Set(pixelShader);
    context.PixelShader.SetSampler(0, samplerState);

    context.InputAssembler.PrimitiveTopology = PrimitiveTopology.TriangleList;
}

Here's the code for UpdateShader():

public void UpdateShader(D3D11.Device device, D3D11.DeviceContext context, 
    Matrix worldViewProj, VertexPositionTexture[] vpts, int[] indices)
{
    DataStream mappedResource;

    //write to buffers
    context.MapSubresource(matrixBuffer, D3D11.MapMode.WriteDiscard, D3D11.MapFlags.None, 
        out mappedResource);
    mappedResource.Write(worldViewProj);
    context.UnmapSubresource(matrixBuffer, 0);

    context.MapSubresource(vertexBuffer, D3D11.MapMode.WriteDiscard, D3D11.MapFlags.None, 
        out mappedResource);
    mappedResource.WriteRange(vpts);
    context.UnmapSubresource(vertexBuffer, 0);

    context.MapSubresource(indexBuffer, D3D11.MapMode.WriteDiscard, D3D11.MapFlags.None, 
        out mappedResource);
    mappedResource.WriteRange(indices);
    context.UnmapSubresource(indexBuffer, 0);

    context.DrawIndexed(indices.Length, 0, 0);
}

I think something that happens once in the SetActive() method should need to actually happen multiple times, and should be moved to the UpdateShader() method, but for the life of me I can't figure out what it is, or even if my reasoning is the correct.

Why is my render engine only rendering one frame?

\$\endgroup\$
  • 1
    \$\begingroup\$ Are you calling set active only once? Anything you do on the DeviceContext has to happen each frame as that's how you set state, everything under where you setup your buffers should be moved to the updateshader method. \$\endgroup\$ – Justin William Stanley Bryant Jan 8 '17 at 1:59
1
\$\begingroup\$

A few things that I found.

First up don't use the datastream object for writing to buffers because in the long run it will end up killing GC performance(have tested), the best bet for writing to DX buffers is to use the one of the Utilities.Write methods to write to the native memory without having to use\dispose of the datastream.

I think your main issue was that you were only setting all the renderstate once and then drawing but the next frame had no shaders or buffers set to draw with.

I moved all the state changing stuff into your updateshader method as shown below.

I would also pre build all index and vertex buffers rather then doing it each draw call so that you can use the immutable flag on them, I have a mesh class for this that contains all the draw methods and update methods. If you have time you should have a look how XNA has its mesh class setup as I based mine off of that.

public override void SetActive(D3D11.Device device, D3D11.DeviceContext context)
{

    // write Matrix data first to constant buffer
    matrixBuffer = new D3D11.Buffer(device, matrixBufferDesc);
    vertexBuffer = new D3D11.Buffer(device, vertexBufferDesc);
    indexBuffer = new D3D11.Buffer(device, indexBufferDesc);    
}


public void UpdateShader(D3D11.Device device, D3D11.DeviceContext context, Matrix worldViewProj, VertexPositionTexture[] vpts, int[] indices)
{
   //write to buffers
   // MapSubresource returns a databox that contains the native pointer to the buffers memory and you can write to this like shown below.
   DataBox dbox = _Context.MapSubresource(matrixBuffer, 0,   MapMode.WriteDiscard, MapFlags.None);
   Utilities.Write(dbox.DataPointer, worldViewProj);
   _Context.UnmapSubresource(matrixBuffer, 0);


    dbox = _Context.MapSubresource(vertexBuffer, 0,   MapMode.WriteDiscard, MapFlags.None);
   Utilities.Write(dbox.DataPointer, vpts,0,vpts.count);
   _Context.UnmapSubresource(vertexBuffer, 0);


    dbox = _Context.MapSubresource(indexBuffer, 0,   MapMode.WriteDiscard, MapFlags.None);
   Utilities.Write(dbox.DataPointer, indices,0,indices.count);
   _Context.UnmapSubresource(indexBuffer, 0);


    //Setup Context For Drawing

    // Only Change when you really need to
        context.InputAssembler.PrimitiveTopology = PrimitiveTopology.TriangleList;

    // Per mesh 
    context.InputAssembler.SetVertexBuffers(0, new D3D11.VertexBufferBinding(vertexBuffer, Utilities.SizeOf<VertexPositionTexture>(), 0));
    context.InputAssembler.SetIndexBuffer(indexBuffer, Format.R32_UInt, 0);

    // Per Batch
    context.VertexShader.SetConstantBuffer(matrixBufferPos, matrixBuffer);
    context.PixelShader.SetShaderResource(0, shaderTexture);      

    context.VertexShader.Set(vertexShader);
    context.PixelShader.Set(pixelShader);

    context.PixelShader.SetSampler(0, samplerState);    

    //Draw Per Mesh
    context.DrawIndexed(indices.Length, 0, 0);
}

I wanted to try keep your code as close to what you had as I could, happy to edit if you want more info.

Before you go and over engineer you code just have a play with the api I don't think you would have a need for fancy shader classes just yet, you can always just rewrite your code later and refactor along the way.

\$\endgroup\$
  • \$\begingroup\$ Ok thanks, I didn't realize that the shaders needed to be set every frame. That said, at what point does the device context clear its state? Would I need to set the shaders and vertex buffers every time I write to the buffer (aka for every object), or would it be possible to batch render all of the objects that use the same shader and only set the shader and the buffer once per batch? I imagine, if it's possible, the second method would still set the buffer and the shaders every frame for that batch without the overhead of having to reset the buffer multiple times per frame. \$\endgroup\$ – Scorch Jan 8 '17 at 10:20
  • 1
    \$\begingroup\$ Yes 100% batch up all the objects that use the same shader state and just set it once for each batch. You should start a few new questions about batching, state changing and anything that is not in the scope of the original question. Also have a look at the c++ sdk samples here: github.com/walbourn/directx-sdk-samples because you will see how things should be done and setup. Ill edit my answer to show what can be batched. \$\endgroup\$ – Justin William Stanley Bryant Jan 8 '17 at 10:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.