Valentin
Valentin

Reputation: 1741

DirectX using multiple Render Targets as input to each other

I have a fairly simple DirectX 11 framework setup that I want to use for various 2D simulations. I am currently trying to implement the 2D Wave Equation on the GPU. It requires I keep the grid state of the simulation at 2 previous timesteps in order to compute the new one.

How I went about it was this - I have a class called FrameBuffer, which has the following public methods:

bool Initialize(D3DGraphicsObject* graphicsObject, int width, int height);

void BeginRender(float clearRed, float clearGreen, float clearBlue, float clearAlpha) const;
void EndRender() const;

// Return a pointer to the underlying texture resource
const ID3D11ShaderResourceView* GetTextureResource() const;

In my main draw loop I have an array of 3 of these buffers. Every loop I use the textures from the previous 2 buffers as inputs to the next frame buffer and I also draw any user input to change the simulation state. I then draw the result.

    int nextStep = simStep+1;
    if (nextStep > 2)
        nextStep = 0;

    mFrameArray[nextStep]->BeginRender(0.0f,0.0f,0.0f,1.0f);
    {
        mGraphicsObj->SetZBufferState(false);
        mQuad->GetRenderer()->RenderBuffers(d3dGraphicsObj->GetDeviceContext());
        ID3D11ShaderResourceView* texArray[2] = { mFrameArray[simStep]->GetTextureResource(),
                                                  mFrameArray[prevStep]->GetTextureResource() };
        result = mWaveShader->Render(d3dGraphicsObj, mQuad->GetRenderer()->GetIndexCount(), texArray);
        if (!result)
            return false;
        // perform any extra input
        I_InputSystem *inputSystem = ServiceProvider::Instance().GetInputSystem();
        if (inputSystem->IsMouseLeftDown()) {
            int x,y;
            inputSystem->GetMousePos(x,y);
            int width,height;
            mGraphicsObj->GetScreenDimensions(width,height);
            float xPos = MapValue((float)x,0.0f,(float)width,-1.0f,1.0f);
            float yPos = MapValue((float)y,0.0f,(float)height,-1.0f,1.0f);
            mColorQuad->mTransform.position = Vector3f(xPos,-yPos,0);
            result = mColorQuad->Render(&viewMatrix,&orthoMatrix);
            if (!result)
                return false;
        }
        mGraphicsObj->SetZBufferState(true);
    }
    mFrameArray[nextStep]->EndRender();

    prevStep = simStep;
    simStep = nextStep;

    ID3D11ShaderResourceView* currTexture = mFrameArray[nextStep]->GetTextureResource();

    // Render texture to screen
    mGraphicsObj->SetZBufferState(false);
    mQuad->SetTexture(currTexture);
    result = mQuad->Render(&viewMatrix,&orthoMatrix);
    if (!result)
        return false;
    mGraphicsObj->SetZBufferState(true);

The problem is nothing is happening. Whatever I draw appears on the screen(I draw using a small quad) but no part of the simulation is actually ran. I can provide the shader code if required, but I am certain it works since I've implemented this before on the CPU using the same algorithm. I'm just not certain how well D3D render targets work and if I'm just drawing wrong every frame.

EDIT 1: Here is the code for the begin and end render functions of the frame buffers:

void D3DFrameBuffer::BeginRender(float clearRed, float clearGreen, float clearBlue, float clearAlpha) const {

  ID3D11DeviceContext *context = pD3dGraphicsObject->GetDeviceContext();

context->OMSetRenderTargets(1, &(mRenderTargetView._Myptr), pD3dGraphicsObject->GetDepthStencilView());

float color[4];

// Setup the color to clear the buffer to.
color[0] = clearRed;
color[1] = clearGreen;
color[2] = clearBlue;
color[3] = clearAlpha;

// Clear the back buffer.
context->ClearRenderTargetView(mRenderTargetView.get(), color);

// Clear the depth buffer.
context->ClearDepthStencilView(pD3dGraphicsObject->GetDepthStencilView(), D3D11_CLEAR_DEPTH, 1.0f, 0);

void D3DFrameBuffer::EndRender() const {
    pD3dGraphicsObject->SetBackBufferRenderTarget();
}

Edit 2 Ok, I after I set up the DirectX debug layer I saw that I was using an SRV as a render target while it was still bound to the Pixel stage in out of the shaders. I fixed that by setting shader resources to NULL after I render with the wave shader, but the problem still persists - nothing actually gets ran or updated. I took the render target code from here and slightly modified it, if its any help: http://rastertek.com/dx11tut22.html

Upvotes: 2

Views: 3057

Answers (1)

Drop
Drop

Reputation: 13005

Okay, as I understand correct you need a multipass-rendering to texture.

Basiacally you do it like I've described here: link

  • You creating SRVs with both D3D11_BIND_SHADER_RESOURCE and D3D11_BIND_RENDER_TARGET bind flags.
  • You ctreating render targets from textures
  • You set first texture as input (*SetShaderResources()) and second texture as output (OMSetRenderTargets())
  • You Draw()*
  • then you bind second texture as input, and third as output
  • Draw()*
  • etc.

Additional advices:

  1. If your target GPU capable to write to UAVs from non-compute shaders, you can use it. It is much more simple and less error prone.
  2. If your target GPU suitable, consider using compute shader. It is a pleasure.
  3. Don't forget to enable DirectX debug layer. Sometimes we make obvious errors and debug output can point to them.
  4. Use graphics debugger to review your textures after each draw call.

Edit 1:

As I see, you call BeginRender and OMSetRenderTargets only once, so, all rendering goes into mRenderTargetView. But what you need is to interleave:

SetSRV(texture1);
SetRT(texture2);
Draw();
SetSRV(texture2);
SetRT(texture3);
Draw();
SetSRV(texture3);
SetRT(backBuffer);
Draw();

Also, we don't know what is mRenderTargetView yet.

so, before

result = mColorQuad->Render(&viewMatrix,&orthoMatrix);

somewhere must be OMSetRenderTargets .

Probably, it s better to review your Begin()/End() design, to make resource binding more clearly visible.

Happy coding! =)

Upvotes: 3

Related Questions