Bind multiple rendertargets

Started by
7 comments, last by DEADC0DE 12 years, 7 months ago
Hi,

I'm trying to bind multiple rendertargets. RT0 is working without any problems but if I switch to RT1 or higher, I got a crash in "pd3dImmediateContext->OMSetRenderTargets(numberRenderTargets, &_pRenderTargetView, _pDepthStencilView);".
I think it's my init-code (because 0 is working and if I swap RT0 and RT1 it's also working)

I read this post http://www.gamedev.net/topic/518035-dx10-deferred-rendering-and-render-to-multiple-render-targets/
and would like to know what to do when having a Texture2DArray.

Something like that:

[source lang="cpp"]
D3D11_TEXTURE2D_DESC td_target;
td_target.ArraySize = number_rendertargets;
//bla bla

pd3dDevice->CreateTexture2D(&td_target, NULL, &_pRenderTargetBuffer);

D3D11_RENDER_TARGET_VIEW_DESC rd_rendertargetview;
//Zero...
rd_rendertargetview.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY;
rd_rendertargetview.Texture2DArray.ArraySize = number_rendertargets;
//etc.

pd3dDevice->CreateRenderTargetView(_pRenderTargetBuffer, &rd_rendertargetview, &_pRenderTargetView);
//with: ID3D11RenderTargetView* _pRenderTargetView;
[/source]

Does _pRenderTargetView have to be an "ID3D11RenderTargetView* _pRenderTargetView[number_rendertargets];"? (with more calls to CreateRenderTargetView)

Thanks!
Advertisement
I just re read your post.. you need to include more code.
Wisdom is knowing when to shut up, so try it.
--Game Development http://nolimitsdesigns.com: Reliable UDP library, Threading library, Math Library, UI Library. Take a look, its all free.
Hm, ok let's see...

I want to bind an array of Textures to my graphic pipeline (multiple rendertargets).
Therefore I created a Textrue2D with "ArraySize" 8.
To bind it to the pipeline, one or more rendertargetview(s) are needed.

My question:
Is one rendertargetview enough (as it is with Texture2D where I just create one logical structure with only an additional parameter)
or do I have to build up an array of rendertargetviews manually?

[source lang="cpp"]
ID3D11Texture2D* RenderTargetBuffer; //no RenderTargetBuffer[8]...
ID3D11RenderTargetView* RenderTargetView; //...here too: just ONE structure

//create Texture2D with parameter "ArraySize" 8

D3D11_RENDER_TARGET_VIEW_DESC rendertargetview_desc;
rendertargetview_desc.Texture2DArray.ArraySize = 8;
//fill rest of desc

pd3dDevice->CreateRenderTargetView(RenderTargetBuffer, rendertargetview_desc, RenderTargetView);
[/source]

----- OR -----

[source lang="cpp"]
ID3D11Texture2D* RenderTargetBuffer; //no RenderTargetBuffer[8]...
ID3D11RenderTargetView* RenderTargetView[8]; //...but here: create 8 manually!

//create Texture2D with parameter "ArraySize" 8

//rendertargetview_desc: no parameter "ArraySize" set
//fill rest of desc

for(int i = 0; i < 8; ++i){
pd3dDevice->CreateRenderTargetView(RenderTargetBuffer->get(i), rendertargetview_desc, RenderTargetView);
}
[/source]


I didn't find any "RenderTargetBuffer->get(i)" function at the documentation (so I think possibility 1 seems right).
But I found that link from above, saying you have to build up the array manually (=> possibility 2 seems right, but no get-function)

[source lang="cpp"]
//source code from link (copy&paste)
ID3D10RenderTargetView* pRTViews[8] = {NULL, NULL, NULL, ... } //array manually!

CreateRenderTargetView(rtTex1, &pRTViews[0])
CreateRenderTargetView(rtTex2, &pRTViews[1])
[/source]

I can't find any documentation to this and only get a black screen with possibility 1 when using rendertarget1-7 (but rendertarget0 is working!)... :-/

So, how do I implement multiple rendertargets with the Texture2D and ArraySize shown above?
(or is it not possible and I have to do something completely different?)

Thank you
There are two possibilities for rendering to more than one render target - Multiple Render Targets (MRT) and Render Target Arrays (RTA). RTAs are used when you need to have a different rasterization for each render target, while MRTs all end up with the geometry rasterized in the exact same way. The key difference is that with RTAs you decide which render target receives a particular mesh in the rasterizer stage with the SV_RenderTargetIndex system value - the results from the rasterization only go to one of the render targets. With MRTs, the decision of what data to write to each render target is done in the pixel shader with the SV_Target[n] system value. With MRTs all render targets receive the results of rasterization.

So that you understand what the difference in these two methods means in terms of resources, use this rule of thumb:

MRTs: You bind multiple Render Target Views to the Output Merger stage to receive the results of rendering, and each view represents one render target surface.
RTAs: You bind one Render Target View to the Output Merger stage to receive the results of rendering, and the single view must represent an array render target.

In your case, it depends on what you are wanting to do. If you need to have multiple render targets for something like generating a G-buffer, then you shouldn't use a render target array. This is because you want all of the targets to receive the same rasterized scene, and just output different values to each render target. On the other hand, if you are generating a cube map, then you don't want every target to get the same rasterized scene, and you would choose the RTAs.

If you clarify a little more what you are trying to accomplish, then I think we can get you on the right pathway. If you have access to our book, take a look on page 245 - it covers this exact topic in more detail.
Thanks for the detailed answer. Now I understand what smasherprog meant by "include more code".

I'm trying to implement some kind of anti aliasing. To improve this I want to jitter coordinates of the mesh in my geometry-shader.
So I'm rendering the same mesh with different (jittered) positions. Finally it's evaluated in a postprocess.

Sounds like the MRT is the right way to do this (?), because I have the same stages (except the GS) and same rasterizer.
I already implemented the shaders (GS sets SV_RenderTargetArrayIndex, Pixelshader reads it and writes SV_Target).

When I got it right, I have to create more rendertargets ("RenderTargetView[8]" = possibility 2).
Is there a function for "RenderTargetBuffer->get(i)"? (didn't find any in the msdn documentation)


At the moment my code looks like this:
[source lang="cpp"]
ID3D11Texture2D* _pRenderTargetBuffer;
ID3D11RenderTargetView* _pRenderTargetView; //should be RenderTargetView[8]?

//...

D3D11_TEXTURE2D_DESC td_target;
ZeroMemory(&td_target, sizeof(D3D11_TEXTURE2D_DESC));
td_target.Width = _width;
td_target.Height = _height;
td_target.MipLevels = 1; //no mipmap
td_target.ArraySize = _number_rendertargets;
td_target.Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
td_target.Usage = D3D11_USAGE_DEFAULT;
td_target.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
td_target.CPUAccessFlags = 0; //no cpu access
td_target.MiscFlags = 0;
td_target.SampleDesc.Count = 1;
td_target.SampleDesc.Quality = 0;

pd3dDevice->CreateTexture2D(&td_target, NULL, &_pRenderTargetBuffer);

D3D11_RENDER_TARGET_VIEW_DESC rd_rendertargetview;
ZeroMemory(&rd_rendertargetview, sizeof(D3D11_RENDER_TARGET_VIEW_DESC));
rd_rendertargetview.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY;
rd_rendertargetview.Format = td_target.Format;
rd_rendertargetview.Texture2DArray.ArraySize = _number_rendertargets;
rd_rendertargetview.Texture2DArray.FirstArraySlice = 0;
rd_rendertargetview.Texture2DArray.MipSlice = 0;

//how do I get the right buffer out of the _pRenderTargetBuffer array if I have RenderTargetView[8] here?
pd3dDevice->CreateRenderTargetView(_pRenderTargetBuffer, &rd_rendertargetview, &_pRenderTargetView);
[/source]

Do you also need the render code or the shader code?

Thanks again :-)
If you want to jitter the vertex postions, then you will not be able to use MRT. Since it splits to multiple render targets in the pixel shader, there is no possibility to use the jittered positions - they will all have the same position information. Unless you pass the multiple position data as per-vertex attributes into the rasterizer and then try to use that information in the pixel shader to produce your anti-aliasing information from there...

But from your description it sounds like you want to use a render target array, and then use the geometry shader to output multiple copies of your geometry to each array index after they have been jittered. If you want to have a reference for using the geometry shader in such a way, take a look at the MirrorMirror demo in Hieroglyph - the same method is used to generate multiple copies of geometry for a dual-paraboloid map generation.
Yes, I want to output multiple copies of the same geometry in the geometry shader.
RTA sounds like what I already did. (one Render Target View, representing an array of buffers)

I'll have a look at that demo, thank you very much for the hints and explanation. :)
I got it work, but I've got some strange effects. Perhaps someone can explain that to me.
Rendertarget Array is bound to pipeline and is working.

I tried different Pixelshaders (SV_RenderTargetArrayIndex "rta_index" is set by Geometryshader) and the size of the Rendertarget Array can be set during runtime.

1) Is working with every size of RT Array:

[source lang="cpp"]
struct PS_Out{
float4 Color : SV_TARGET;
};

PS_Out PShader(GS_Out input){
PS_Out output;
output.Color = input.vColor;
return output;
}
[/source]

2) Is also working with every size of RTA, ALSO WITH rta_index BIGGER THAN 3!!!

[source lang="cpp"]
struct PS_Out{
float4 Color0 : SV_TARGET0;
float4 Color1 : SV_TARGET1;
float4 Color2 : SV_TARGET2;
float4 Color3 : SV_TARGET3;
float4 Color4 : SV_TARGET4;
float4 Color5 : SV_TARGET5;
float4 Color6 : SV_TARGET6;
float4 Color7 : SV_TARGET7;
};

PS_Out PShader(GS_Out input){
PS_Out output;

if(input.rta_index == 0){
output.Color0 = input.vColor;
}
else if(input.rta_index == 1){
output.Color1 = input.vColor;
}
else if(input.rta_index == 2){
output.Color2 = input.vColor;
}
else if(input.rta_index == 3){
output.Color3 = input.vColor;
}
//no else!

return output;
}
[/source]

3) The one I think should work, but isn't! Only RTA 0 is filled correctly, rest: mesh is black, background is blue (clear color is blue!):

[source lang="cpp"]
struct PS_Out{
float4 Color0 : SV_TARGET0;
float4 Color1 : SV_TARGET1;
float4 Color2 : SV_TARGET2;
float4 Color3 : SV_TARGET3;
float4 Color4 : SV_TARGET4;
float4 Color5 : SV_TARGET5;
float4 Color6 : SV_TARGET6;
float4 Color7 : SV_TARGET7;
};

PS_Out PShader(GS_Out input){
PS_Out output;

if(input.rta_index == 0){
output.Color0 = input.vColor;
}
else if(input.rta_index == 1){
output.Color1 = input.vColor;
}
else if(input.rta_index == 2){
output.Color2 = input.vColor;
}
else if(input.rta_index == 3){
output.Color3 = input.vColor;
}
else if(input.rta_index == 4){
output.Color4 = input.vColor;
}
else if(input.rta_index == 5){
output.Color5 = input.vColor;
}
else if(input.rta_index == 6){
output.Color6 = input.vColor;
}
else if(input.rta_index == 7){
output.Color7 = input.vColor;
}
//no else! (but there can only be 8 rendertargets be bound => 7 should be maximum)

return output;
}
[/source]


I do not understand why 2) should work and 3) isn't. And I also don't understand how 1) knows which SV_Target to fill.

Can someone explain that to me? Thanks very much!
Is there no explanation for this behaviour?

This topic is closed to new replies.

Advertisement