MSAA in DX12?

Started by
8 comments, last by Vilem Otte 6 years, 3 months ago

Does anyone have a working example of how to implement MSAA in DX12? I have read short descriptions and I have seen code fragments on how to do it with DirectX Tool Kit.

I get the idea, but with all the pipeline states, root descriptions etc I somehow get lost on the way.

Could someone help me with a link pointing to a small implementation in DirectX 12 (or SharpDX with DX12)?

 

Advertisement

I have made it, and it's actually not that hard

  1. Create a pipeline state for render target with specific MSAA samples and quality, like this (I've removed additional code)
     
    
    DXGI_SAMPLE_DESC samplerDesc;
    samplerDesc.Count = samplesMSAA;
    samplerDesc.Quality = qualityMSAA;
    
    D3D12_GRAPHICS_PIPELINE_STATE_DESC desc = { 0 };
    ...
    desc.SampleDesc = samplerDesc;
    
    device->CreateGraphicsPipelineState(&desc, __uuidof(ID3D12PipelineState), (void**)&pipelineState);
  2. Create render target buffers that hold rendered data (for both - color & depth buffers)
     
    
    	D3D12_RESOURCE_DESC desc = DescTex2D(...)
    	desc.SampleDesc.Count = samplesMSAA;
    	desc.SampleDesc.Quality = qualityMSAA;
    
     	....
    
    	CreateTextureResource(...)
    	CreateDerivedViews(...)
  3. Next you need to have pipeline state for resolving and I assume you want to resolve into backbuffer for swap chain (so you already have color (& depth) buffers for that). This pipeline state and texture buffers are no-MSAA, so samples count is 1 and sample quality is 0
     
  4. Rendering is then straight forward - you render your scene with MSAA pipeline state into MSAA buffers. And during resolve you attach SRV of those buffers and resolve in your shader like for example this (note SamplesMSAA is a define with number of samples used in MSAA):
     
    
    Texture2DMS<float4, SamplesMSAA> msaaTexture : register(t0);
      
    cbuffer msaaDesc : register(b0)
    {
    	uint2 dimensions;
    }
      
    struct Input
    {
    	float4 position : SV_POSITION;
    	float2 texCoord : TEXCOORD0;
    };
    
    Input VS(uint id : SV_VertexID, 
    	float3 position : POSITION,
    	float2 texCoord : TEXCOORD0)
    {
    	Input result;
    
    	result.position = float4(position, 1.0f);
    	result.texCoord = texCoord;
    
    	return result;
    }
    
    float4 PS(Input input)
    {
    	uint2 coord = uint2(input.texCoord.x * dimensions.x, input.texCoord.y * dimensions.y);
    	
    	float4 tex = float4(0.0f, 0.0f, 0.0f, 0.0f);
      
    #if SamplesMSAA == 1
    	tex = diffuse.Load(coord, 0);
    #else
    	for (uint i = 0; i < SamplesMSAA; i++)
    	{
    		tex += diffuse.Load(coord, i);
    	}
    	tex *= 1.0f / SamplesMSAA;
    #endif
    		
    	return tex;
    }

     

I could post working code somewhere, but I have most objects in DX12 wrapped, and whole engine attached to it - so it would probably make no sense and would be overly confusing (my most simple example does MSAA with deferred shading and post-tonemapping resolve... along with voxel cone tracing - that is a LOT of noise around).

My current blog on programming, linux and stuff - http://gameprogrammerdiary.blogspot.com

3 hours ago, Vilem Otte said:

I have made it, and it's actually not that hard

Thanks for the encouragement.

I guess I need even more details (that's where the devil is). Following your example I am at creating another pipeline state, more buffers, shaders etc, but so far I haven't gotten all the parameters right. There is always another clash of illegal parameter combinations it seems. For instance, how do you match 


Texture2DMS<float4, SamplesMSAA> msaaTexture : register(t0);

with an input structure? And how does the corresponding shader resource view description look like? Do I need to create new resource heaps for the new render target and depth targets (I guess adding to the already existing heaps is fine)? What parameters do you give when creating your resource descriptions?

Maybe you are not so keen on reading DX12 + SharpDX code, but if you are, here is an interesting example I found where they render to an off-screen buffer (for post processing effects). I understand the code, and it's working, but they don't do MSAA. Can you or someone else help me to figure out what would be needed in order to add multisampling here? I have a feeling it's not much missing.

https://github.com/RobyDX/SharpDX_D3D12HelloWorld/blob/master/D3D12HelloRenderTarget/HelloRenderTarget.cs

Yes, you will need additional: pipeline state (therefore shaders and most likely root signature) and buffers (color and depth).

As for matching msaaTexture - I have a root signature with 1 descriptor table there, that contains 1 descriptor range. This range contains just 1 descriptor of type D3D12_DESCRIPTOR_RANGE_TYPE_SRV.

This SRV is created standardly with CreateShaderResourceView. The resource passed in is committed resource for color buffer. I might read the code later (I just briefly went through - and it doesn't contain additional pipeline state, buffers, etc. yet) - as right now I'm just on short lunch brake in work.

My current blog on programming, linux and stuff - http://gameprogrammerdiary.blogspot.com

ResolveSubresource and ResolveSubresouceRegion (new to DX12) still exist if you don't want to do your MSAA resolve manually. If your resolve operation is just an average of the N samples then using the Resolve API will be at least as fast as doing it yourself.

Adam Miles - Principal Software Development Engineer - Microsoft Xbox Advanced Technology Group

16 hours ago, Vilem Otte said:

 it doesn't contain additional pipeline state, buffers, etc. yet

Actually it does. Almost everything is set up in the LoadRenderTargetData function. Compare it with the non-post-processing version in https://github.com/RobyDX/SharpDX_D3D12HelloWorld/blob/master/D3D12HelloMesh/HelloMesh.cs it's clear what has been added. 

Anyway, I'll continue trying today. Finding more than just code fragments for MSAA in Dx12 seems impossible.

ajmiles, thanks for the tip. I will use it when I get that far.

Ok, got it!

Going the ResolveSubresource way was really a lot easier. It's good enough for what I am looking for. With this method there is no need to have extra pipeline states, root signatures or shaders. Just the extra render target and depth targets will do.

Thanks Vilem Otte and ajmiles for standing by!

 

Oh, forgot to ask a question...

How do I switch MSAA on / off at run-time? The thing is that when I want MSAA I need to specify sample description count > 1 already when creating the pipeline state object. This number cannot be changed later on, can it?

Is the only solution to create another pipeline state object (with its own command lists etc) and then switch between these two sets at run-time?

You don't need additional command lists, etc. A pipeline state is enough.

You can create 2 pipeline states and in the code use:


ID3D12GraphicsCommandList::SetPipelineState(ID3D12PipelineState* pPipelineState)

 

In a way that:


if (msaa)
{
    cmdList.SetPipelineState(msaaPSO);
    ... 
}
else
{
    cmdList.SetPipelineState(nonMsaaPSO);
    ...
}

...

 

My current blog on programming, linux and stuff - http://gameprogrammerdiary.blogspot.com

This topic is closed to new replies.

Advertisement