Texture wrappers design questions (C++)

Started by
4 comments, last by Happy SDE 7 years ago

Hi Forum!

I am in half way of redesigning abstraction representations of 2D textures in my engine (CPU side) for:
RenderTarget, DepthStencil, and array versions of them.

And I would like to clarify some design decisions before starting to code.

============================================

RenderTarget2D abstraction

Consider this interface for render target 2D (not array version):


class RenderTarget2D
{
    ComPtr<ID3D11Texture2D>           m_texture;
    ComPtr<ID3D11RenderTargetView>    m_rtv;
    ComPtr<ID3D11ShaderResourceView>  m_srv;
    ComPtr<ID3D11UnorderedAccessView> m_uav;
}

Questions1:
If I create UAV for any RenderTarget2D, can it degrade performance in runtime?
Can hardware treat texture with UAV as slower version than a texture, created without UAV?

============================================

DepthStencil abstraction
I haven’t found a good way of using Stencil part of it.
For deferred rendering and shadow generation I don’t use Stencil.

Question2:
Are there use cases in modern rendering when Stencil is useful?

============================================

DepthTexture2D abstraction

Question3:
Is it right way to say, that Depth texture without “Stencil” part can be treated as regular texture2D with next statements:
1. Format of Depth should start with D, but RenderTargets – from R?
2. CreateDepthStencilView() should be substituted with CreateRenderTargetView()
Are there other fundamental differences between them?

============================================

DepthArray2D abstraction (without stencil part)
In Shadow map generation, I need DepthStencilArray with 4 slices without stencil part.


class DepthArray2D
{
    ComPtr<ID3D11Texture2D> m_texture;

    ComPtr<ID3D11ShaderResourceView>              m_srv;       // As Texture2DArray in hlsl
    std::vector<ComPtr<ID3D11ShaderResourceView>> m_srvSlices; // As Texture2D  in hlsl

    ComPtr<ID3D11DepthStencilView>              m_dsv;
    ComPtr<ID3D11DepthStencilView>              m_dsvRO;
    std::vector<ComPtr<ID3D11DepthStencilView>> m_dsvSlices;
}

Question4:
Is it right thing to say that DepthStencilArray is the same as Texture2DArray (I can create SRV slices as in Texture)?

Thanks in advance!

Advertisement
1) yes which is why the resource creation API forces you to pass flags specifying what kind of views the resource will be compatible with. This affects the memory layout that the driver will choose.
You should pass along a similar abstraction to your users -- force them to tell you if they intend to use an RT as UAV/SRV or not.

2) yes... But if you don't need them right now, then just stub out enough of the API so that you can implement stencil support later, and then implement stencil support later! :)

3.1) if making an SRV for a depth texture, yeah.
3.2) i don't understand what you're asking. You don't create an RTV for a depth-only depth-texture.

4) pretty much. Note though that you can make a view of all slices, a view of one slice, or a view of any contiguous subset of slices. Not every array resource will need every possible view.

Thank you Hodgman for your answer!


Question2: Are there use cases in modern rendering when Stencil is useful?
2) yes... But if you don't need them right now, then just stub out enough of the API so that you can implement stencil support later, and then implement stencil support later! :)

That's the plan: remove all stencil-related code from current implementation and call the abstraction "DepthTexture"! :)

If one day I will need a stencil in it, I would probably create a new additional class "DepthStencilTexture".

But for now I would like to know, are there use cases at all in modern engines?

Do you use it?

Could you provide use cases, when it really needed?

3.2) i don't understand what you're asking. You don't create an RTV for a depth-only depth-texture.

I have next assumption: DepthTexture is the same as regular RenderTarget, but:

1. Can be bound as depth texture in the pipeline (3rd param instead of 2nd for regular RT:


m_context->OMSetRenderTargets(1, &rtv0, pDepthStencilView);

2. "RTV" for depth texture is called "DSV", so different function calls to create it from Device, but the functionality is the same: it is a target to write to.

3. SRV in both cases have the same functionality.

4. There is notion of "Read-only DSV".

And the question probably should be stated: are DepthTexture and RenderTexture more different than that (let's put aside "Stencil" part here)?

FWIW, I only use a single texture abstraction in my engine, not multiple classes each for render target, depth, array, etc... I just make the user specify the way they want to use it (bind as depth, bind as pixel shader input, N array slices, is cubemap, etc).
By default my array textures (when an SRV is requested) will have an SRV for the full array range. If the user wants an SRV/RTV/etc for a particular slice, they create a new texture object but pass the original texture into the constructor as an optional "alias" argument. This tells my engine to use the same "resource" as the original texture but to create new views. This the user the control to choose which slices of an array they want views of, and even whether they want slice-range views (e.g. an SRV of slices 3-5 in a six slice array resource).

1/2 - yeah RenderTargets and DepthStencils get bound to the color and depth slots of the pipeline. They're similar in function but not interchangeable. RTVs receive the PS outputs via SV_TARGET#, DSVs recieve the per pixel z/w values from the rasterizer or the SV_DEPTH output from the PS if that exists.

3. Yep SRVs are how you bind any kind of texture resource as a readable shader input.

4. Read-only DSVs are a rarely used but useful feature (like stencil :wink:).
You can't have a resource bound as writeable (SRV/DSV/UAV) and read-only (SRV) at the same time -- attempting to do so will cause D3D debug error spam messages, and will unbind enough views to resolve the conflict. This means you cant use a SRV of a depth-texture while it's bound via a DSV... Unless it's a Read-Only-DSV, which avoids the conflict.

But for now I would like to know, are there use cases at all in modern engines?
Do you use it?
Could you provide use cases, when it really needed?

I'm currently using it for OIT in a way similar to "stencil routed A buffers". Most pre-tiled deferred renderers will use them to masl light areas. They also used to be common for masking out reflective areas, portals, etc...
Modern GPUs can read stencil values via a SRV (and even newer ones can output custom stencil values from the PS), which opens up a whole suite of new algorithms not previously possible.
Most GPUs support early and hierarchical stencil tests, which lets them skip the pixel shader for pixels that would fail the stencil test, so it's a very handy hardware mechanism for skipping a particular set of pixels based on a mask.

And the question probably should be stated: are DepthTexture and RenderTexture more different than that (let's put aside "Stencil" part here)?

Different resource creation flags, different view types, different binding points. Both can also have SRVs created for them, can be arrays, can be cubes, etc.
Off topic but what you shown is not an abstraction, it is object members. An abstraction is about member functions and services, not implementation.

And on that, you probably want to limit the amount of abstract class for textures because it bloatter everything else.
Off topic but what you shown is not an abstraction, it is object members. An abstraction is about member functions and services, not implementation.

I showed only part of implementation: COM members.

The abstractions also include:

0. Construction

1. Resize()

2. Uniform SetPrivateData() for texture/SRV/RTV/UAV.

3. Holding texture formats/other types.

4. Lifetime management.

5. Accessors.

But all the things above were clear to me.

Some use cases were blurry.

Thanks to Hodgman, now I have all I need for redesign. :)

This topic is closed to new replies.

Advertisement