Is it possible to combine HDR with MSAA in a DirectX 12 desktop application? - directx-12

Using the DirectX Tool Kit for DirectX 12, I'm able to successfully compile and run the individual MSAA and HDR tutorial samples.
When I combined the relevant code for the MSAA and HDR components together into a single Game.cpp file however, compilation fails with the debug layer message:
D3D12 ERROR : ID3D12CommandList::ResolveSubresource : The specified format is not compatible with the source resource.Format : R10G10B10A2_UNORM, Source Resource Format : R16G16B16A16_FLOAT [RESOURCE_MANIPULATION ERROR #878: RESOLVESUBRESOURCE_INVALID_FORMAT]
I am using the HDR sample code for an SDR display monitor, and therefore need to apply tone mapping. With respect to the order in which calls are made, I make a call to end the HDR scene before attempting to resolve the MSAA render target:
// 3d rendering completed
m_hdrScene->EndScene(commandList);
if (m_msaa)
{
// Resolve the MSAA render target.
//PIXBeginEvent(commandList, PIX_COLOR_DEFAULT, L"Resolve");
auto backBuffer = m_deviceResources->GetRenderTarget();
...
Then following the MSAA resolve block, I place the tone mapping statement as follows:
// Unbind depth/stencil for sprites (UI)
auto rtvDescriptor = m_deviceResources->GetRenderTargetView();
commandList->OMSetRenderTargets(1, &rtvDescriptor, FALSE, nullptr);
// set texture descriptor heap in prep for sprite drawing
commandList->SetDescriptorHeaps(static_cast<UINT>(std::size(heaps)), heaps);
// apply tonemapping to hdr scene
switch (m_deviceResources->GetColorSpace())
{
default:
m_toneMap->Process(commandList);
break;
...
I found that attempting to tone map before setting the descriptor heap for drawing 2D sprites (over the 3D scene) would result in the error:
D3D12 ERROR: CGraphicsCommandList::SetGraphicsRootDescriptorTable: The descriptor heap (0x0000025428203230:'DescriptorHeap') containing handle 0x80000253a82ff205 is different from currently set descriptor heap 0x0000025428203540:'EffectTextureFactory'. [ EXECUTION ERROR #708: SET_DESCRIPTOR_TABLE_INVALID]
D3D12: BREAK enabled for the previous message, which was: [ ERROR EXECUTION #708: SET_DESCRIPTOR_TABLE_INVALID ]
I admit this was a rather naive first attempt to combine HDR and MSAA, but I'm concerned that these features could be incompatible and/or mutually exclusive in DirectX 12. I understand why a resource compatibility issue arises during MSAA resolve, as we need to use floating point render targets for HDR. I should note that my program will run and render correctly with HDR if I skip the MSAA code blocks by setting my m_msaa boolean to false.
Looking forward to any advice anyone may have. If sufficient code or other details about the program are required, I'll be happy to update my post.

To successfully render a HDR scene with 4xMSAA, I made the following revisions:
In CreateWindowSizeDependentResources(), the MSAA render target (and view) are defined with the same format as the HDR render texture:
// Create an MSAA render target.
D3D12_RESOURCE_DESC msaaRTDesc = CD3DX12_RESOURCE_DESC::Tex2D(
//m_deviceResources->GetBackBufferFormat(),
m_hdrScene->GetFormat(), // set matching format to HDR target (_R16G16B16A16_FLOAT)
backBufferWidth,
backBufferHeight,
1, // This render target view has only one texture.
1, // Use a single mipmap level
4 // <--- Use 4x MSAA
);
...
The next major change is in Render(), where the MSAA resolve to the HDR target occurs after ending the HDR scene render and before the tone mapping process:
// Resolve the MSAA render target.
auto backBuffer = m_deviceResources->GetRenderTarget();
auto hdrBuffer = m_hdrScene->GetResource(); // HDR destination texture
{
D3D12_RESOURCE_BARRIER barriers[2] =
{
// transition msaa texture from target to resolve source
CD3DX12_RESOURCE_BARRIER::Transition(
m_msaaRenderTarget.Get(),
D3D12_RESOURCE_STATE_RENDER_TARGET,
D3D12_RESOURCE_STATE_RESOLVE_SOURCE),
// transition hdr texture from pixel shader resource to resolve destination
CD3DX12_RESOURCE_BARRIER::Transition(
hdrBuffer,
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
D3D12_RESOURCE_STATE_RESOLVE_DEST)
};
commandList->ResourceBarrier(2, barriers);
}
//commandList->ResolveSubresource(backBuffer, 0, m_msaaRenderTarget.Get(), 0,
// m_deviceResources->GetBackBufferFormat());
// resolve MSAA to the single sample 16FP resource destination
commandList->ResolveSubresource(hdrBuffer, 0, m_msaaRenderTarget.Get(), 0,
m_hdrScene->GetFormat());
// prepare backbuffer for 2D sprite drawing, typically rendered without MSAA
{
D3D12_RESOURCE_BARRIER barriers[2] =
{
// transition hdr texture from resolve destination back to p.s resource
CD3DX12_RESOURCE_BARRIER::Transition(
hdrBuffer,
D3D12_RESOURCE_STATE_RESOLVE_DEST,
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE),
// transition backbuffer from Present to render target (for sprite drawing after the MSAA resolve)
CD3DX12_RESOURCE_BARRIER::Transition(
backBuffer,
D3D12_RESOURCE_STATE_PRESENT,
D3D12_RESOURCE_STATE_RENDER_TARGET)
};
commandList->ResourceBarrier(2, barriers);
}
... do tone mapping
... do 2D sprite drawing
Output was to a conventional 1080p display (no HDR10 or V-Sync). The LHS screen capture is a HDR scene without MSAA, whereas the RHS capture shows the smoothing effect of 4xMSAA on the 3D wireframe objects, kettle & earth models, and triangle billboard.
The cat and 'Hello World' text are 2D sprites drawn to the backbuffer after MSAA resolve and tone mapping.
Thanks again to #ChuckWalbourn for pointing me in the right direction, and I look forward to progressing my projects with the DirectX Tool Kit.

ResolveSubresource cannot do the conversion from 16fp to 10:10:10:2 HDR10.
Generally you need to:
Render to MSAA floating-point in High Dynamic Range (linear color space)
Resolve MSAA to single-sample floating-point. (Many games choose to use advanced software antialiasing techniques as part of this process such as FXAA, SMAA, etc.)
Perform the tone-map and Rec.2020 colorspace conversion from floating-point to 10:10:10:2
Display HDR10
Rendering the UI sometimes happen before step 3, other times after. If done before, you typically have to 'scale up' the UI colors to make them stand out.
See the SimpleMSAA DX12 and SimpleHDR DX12 samples for the technical details.
DirectX Tool Kit includes a PostProcess class which can perform the HDR10 tone-map. See this tutorial.

Related

OpenGL: Objects are smooth if normally drawn, but edged when rendering to FBO

I have a problem with different visual results when using a FBO compared to the default framebuffer:
I render my OpenGL scene into a framebuffer object, because I use this for color picking. The thing is that if I render the scene directly to the default framebuffer, the output on the screen is quite smooth, meaning the edges of my objects look a bit like if they were anti-aliased. When I render the scene into the FBO and afterwards use the output to texture a quad that spans the whole viewport, the objects have very hard edges where you can easily see every single colored pixel that belongs to the objects.
Good:
Bad:
At the moment I have no idea what the reason for this could be. I am not using some kind of anti-aliasing.
System:
Fedora 18 x64
Intel HD Graphics 4000 and Nvidia GT 740M (same result)
Edit1:
As stated by Damon and Steven Lu, there is probably some kind of anti-aliasing enabled by the system by default. I couldn't figure out so far how to disable this feature.
The thing is that I was just curious why this setting only had an effect on the default framebuffer and not the one handled by the FBO. To get anti-aliased edges for the FBO too, I will probably have to implement my own AA method.
Once you draw your scene into custom FBO the externally defined MSAA level doesn't apply anymore.You must configure your FBO to have Multi-sample texture or render buffer attachments setting number of sample levels along the way.Here is a reference.

Can I have a default Framebuffer without alpha and depth?

I am looking to save some video card memory by not allocating what I do not use. I am far from running out of memory, but it would feel 'cleaner' to me.
I can't really think of a reason to have an alpha value in the default framebuffer since my window is not going to alpha-blend with my desktop anyway. I was wondering if I could save a few bytes or have more color depth by removing its alpha.
Likewise, I am doing some deferred lighting and all my depth calculations occur in a framebuffer that is not the default one. Then I simply render a quad (two tris) to the default frame buffer with the texture in which I rendered my scene as well as a few GUI elements, none of which requires depth-testing. I call glDisable(GL_DEPTH_TEST) when rendering the default framebuffer, but I wouldn't mind not having a depth buffer at all instead of a depth buffer that I don't use.
Can I do that within OpenGl ? Or within SDL with which I create my OpenGl context ?
I try to create my OpenGl context with the following SDL attributes
sdl.GL_SetAttribute(sdl.GL_DOUBLEBUFFER, 1)
sdl.GL_SetAttribute(sdl.GL_DEPTH_SIZE, 0)
sdl.GL_SetAttribute(sdl.GL_ALPHA_SIZE, 0)
info := sdl.GetVideoInfo()
bpp := int(info.Vfmt.BitsPerPixel)
if screen := sdl.SetVideoMode(640, 480, bpp, sdl.OPENGL); screen == nil {
panic("Could not open SDL window: " + sdl.GetError())
}
if err := gl.Init(); err != nil {
panic(err)
}
Unfortunately, SDL's binding for GoLang lack the sdl_gl_GetAttribute function that would allow me to check whether my wishes are granted.
As I said, there is no emergency. I am mostly curious.
I can't really think of a reason to have an alpha value in the default framebuffer since my window is not going to alpha-blend with my desktop anyway.
That's good, because the default framebuffer having an alpha channel wouldn't actually do that (on Windows anyway).
The framebuffer alpha is there for use in blending operations. It is sometimes useful to do blending that is in some way based on a destination alpha color. For example, I once used the destination alpha as a "reflectivity" value for a reflective surface, when drawing the reflected objects after having drawn that reflective surface. It was necessary to do it in that order, because the reflective surface had to be drawn in multiple passes.
In any case, the low level WGL/GLX/etc APIs for creating OpenGL contexts do allow you to ask to not have alpha or depth. Note that if you ask for 0 alpha bits, that will almost certainly save you 0 memory, since it's more efficient to render to a 32-bit framebuffer than a 24-bit one. So you may as well keep it.
However, since you're using SDL, and the Go binding of SDL, that's up to SDL and it's Go binding. The sdl_gl_SetAttribute function should work, assuming SDL implements it correctly. If you want to verify this, you can just ask the framebuffer through OpenGL:
glBindFramebuffer(GL_FRAMEBUFFER, 0); //Use the default framebuffer.
GLint depthBits, alphaBits;
glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, GL_DEPTH, GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE, &depthBits);
glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, GL_BACK_LEFT, GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE, &alphaBits);

Why does OpenGL lighten my scene when multisampling with an FBO?

I just switched my OpenGL drawing code from drawing to the display directly to using an off-screen FBO with render buffers attached. The off-screen FBO is blitted to the screen correctly when I allocate normal render buffer storage.
However, when I enable multisampling on the render buffers (via glRenderbufferStorageMultisample), every color in the scene seems like it has been brightened (thus giving different colors than the non-multisampled part).
I suspect there's some glEnable option that I need to set to maintain the same colors, but I can't seem to find any mention of this problem elsewhere.
Any ideas?
I stumbled upon the same problem, due to the lack of proper downsampling because of mismatching sample locations. What worked for me was:
A separate "single sample" FBO with identical attachments, format and dimension (with texture or renderbuffer attached) to blit into for downsampling and then draw/blit this to the window buffer
Render into a multisample window buffer with multisample texture having the same sample count as input, by passing all corresponding samples per fragment using a GLSL fragment shader. This worked with sample shading enabled and is the overkill approach for deferred shading as you can calculate light, shadow, AO, etc. per sample.
I did also rather sloppy manual downsampling to single sample framebuffers using GLSL, where I had to fetch each sample separately using texelFetch().
Things got really slow with multisampling. Although CSAA performed better than MSAA, I recommend to take a look at FXAA shaders for postprocessing as a considerable alternative, when performance is an issue or those rather new extensions required, such as ARB_texture_multisample, are not available.
Accessing samples in GLSL:
vec4 texelDownsampleAvg(sampler2DMS sampler,ivec2 texelCoord,const int sampleCount)
{
vec4 accum = texelFetch(sampler,texelCoord,0);
for(int sample = 1; sample < sampleCount; ++sample) {
accum += texelFetch(sampler,texelCoord,sample);
}
return accum / sampleCount;
}
http://developer.download.nvidia.com/opengl/specs/GL_EXT_framebuffer_multisample.txt
http://developer.download.nvidia.com/opengl/specs/GL_EXT_framebuffer_blit.txt
11) Should blits be allowed between buffers of different bit sizes?
Resolved: Yes, for color buffers only. Attempting to blit
between depth or stencil buffers of different size generates
INVALID_OPERATION.
13) How should BlitFramebuffer color space conversion be
specified? Do we allow context clamp state to affect the
blit?
Resolved: Blitting to a fixed point buffer always clamps,
blitting to a floating point buffer never clamps. The context
state is ignored.
http://www.opengl.org/registry/specs/ARB/sample_shading.txt
Blitting multisampled FBO with multiple color attachments in OpenGL
The solution that worked for me was changing the renderbuffer color format. I picked GL_RGBA32F and GL_DEPTH_COMPONENT32F (figuring that I wanted the highest precision), and the NVIDIA drivers interpret that differently (I suspect sRGB compensation, but I could be wrong).
The renderbuffer image formats I found to work are GL_RGBA8 with GL_DEPTH_COMPONENT24.

Order of operations for multisampling in DirectX 10

I'm confused on the process that needs to be done for anti-aliasing in DirectX 10. I haven't done it at all before, so it may just be that I'm confused on the procedure in general.
Here's what I know so far (or think I know): you need to enable multisampling for your RasterizerState object that you use in the shader, and that the SampleDesc in the swap chain description (DXGI_SWAP_CHAIN_DESC) needs to be set to a supported value for Count (and that Quality should stay 0, since other values or hardware-specific - though I don't know what for). Between the calls to prepare for more drawing (ClearRenderTargetView and IASetInputLayout) and the call to Present, the back buffer should be downsampled (via ResolveSubresource) to an otherwise equal-sized texture. Then, (this is the part I can't find anything on) somehow present the downsampled texture.
Do I have something messed up along the way? Or am I just mistaken on the last step? I saw a few resources refer to doing the sampling resolution during one last draw to a full-screen quad with a multisampled shader texture (Texture2DMS<>), but can't figure out what that would entail, or why you would do it over the device call to just resolve it that way.
Any attempt I've made at this doesn't produce any increase in image quality.
EDIT: I'm using DX11, you just use D3D10_ instead of D3D11_, thanks DeadMG.
You don't need to do any downsampling.
Find out what kind of quality modes and sample count your GPU supports.
You said that the quality level should be 0, that is wrong. Use this to get the supported quality modes with sample counts:
UINT GetMSAAQuality(UINT numSamples,DXGI_FORMAT format)
{
UINT q=-1;
HR(m_device->CheckMultisampleQualityLevels(format,numSamples,&q));
return q-1;
}
// Use it like this:
UINT sampleCount=4; // You set this
UINT sampleQuality=GetMSAAQuality(sampleCount,DXGI_FORMAT_CHOOSE_ONE);
// For swap chain
DXGI_SWAP_CHAIN_DESC d;
d.SampleDesc.Count=sampleCount;
d.SampleDesc.Quality=sampleQuality;
// Now for all the textures that you create for your
// render targets and also for your depth buffer
D3D11_TEXTURE2D_DESC dtex;
dtex.SampleDesc.Quality=sampleQuality;
dtex.SampleDesc.Count=sampleCount;
// Now for all your render target views you must set the correct dimension type
// based on if you use multisampling or not
D3D11_RENDER_TARGET_VIEW_DESC drtv;
dRtSwapChain.ViewDimension=sampleQuality==0?D3D11_RTV_DIMENSION_TEXTURE2D:D3D11_RTV_DIMENSION_TEXTURE2DMS;
// For depth stencil view
D3D11_DEPTH_STENCIL_VIEW_DESC ddsv;
ddsv.ViewDimension=sampleQuality==0?D3D11_DSV_DIMENSION_TEXTURE2D:D3D11_DSV_DIMENSION_TEXTURE2DMS;
If you want to read a multisampled texture in your shader (if you render a fullscreen quad for example) you must declare the texture Texture2DMS<>.

How do i: convert surface to a texture or create a texture with certain multisampling parameters or render a surface with an alpha layer

I am creating two render targets, both must share the back buffer's depth buffer, so it is important that I set them to have the same multi sampling parameters, however pDevice->CreateTexture(..) does not give any parameters for setting the multi sampling types. So I created two render target surfaces using pDevice->CreateRenderTarget(...) giving the same values as the depth buffer, now the depth buffer works in conjunction with my render targets, however I am unable to render them over the screen properly because alpha blending does not work with ->StretchRect (or so I have been told, and it did not work when I tried).
So the title of this question is basically my questions, how do i:
- convert a surface to a texture or
- create a texture with certain multisampling parameters or
- render a surface properly with an alpha layer
The documentation for StretchRect specifically explains how to do this:
Using StretchRect to downsample a
Multisample Rendertarget
You can use StretchRect to copy from
one rendertarget to another. If the
source rendertarget is multisampled,
this results in downsampling the
source rendertarget. For instance you
could:
Create a multisampled rendertarget.
Create a second rendertarget of the
same size, that is not multisampled.
Copy (using StretchRect the
multisample rendertarget to the second
rendertarget.
Note that use of the
extra surface involved in using
StretchRect to downsample a
Multisample Rendertarget will result
in a performance hit.
So new response to an old question, but I came across this and thought I'd supply an answer in case someone else comes across it while running into this problem. Here is the solution with a stripped down version of my wrappers and functions for it.
I have a game in which the renderer has several layers, one of which is a geometry layer. When rendering, it iterates over all layers, calling their Draw functions. Each layer has its own instance of my RenderTarget wrapper. When the layer Draws, it "activates" its render target, clears the buffer to alpha, draws the scene, then "deactivates" its render target. After all layers have drawn to their render targets, all of those render targets are then combined onto the backbuffer to produce the final image.
GeometryLayer::Draw
* Activates the render target used by this layer
* Sets needed render states
* Clears the buffer
* Draws geometry
* Deactivates the render target used by this layer
void GeometryLayer::Draw( const math::mat4& viewProjection )
{
m_pRenderTarget->Activate();
pDevice->SetRenderState(D3DRS_ALPHABLENDENABLE,TRUE);
pDevice->SetRenderState(D3DRS_SRCBLEND,D3DBLEND_SRCALPHA);
pDevice->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA);
pDevice->Clear(0,0,D3DCLEAR_TARGET,m_clearColor,1.0,0);
pDevice->BeginScene();
pDevice->Clear(0,0,D3DCLEAR_ZBUFFER,0,1.0,0);
for(auto it = m->visibleGeometry.begin(); it != m->visibleGeometry.end(); ++it)
it->second->Draw(viewProjection);
pDevice->EndScene();
m_pRenderTarget->Deactivate();
}
My RenderTarget wrapper contains an IDirect3DTexture9* (m_pTexture) which is used with D3DXCreateTexture to generate the texture to be drawn to. It also contains an IDirect3DSurface9* (m_pSurface) which is given by the texture. It also contains another IDirect3DSurface9* (m_pMSAASurface).
In the initialization of my RenderTarget, there is an option to enable multisampling. If this option is turned off, the m_pMSAASurface is initialized to nullptr. If this option is turned on, the m_pMSAASurface is created for you using the IDirect3DDevice9::CreateRenderTarget function, specifying my current multisampling settings as the 4th and 5th arguments.
RenderTarget::Init
* Creates a texture
* Gets a surface off the texture (adds to surface's ref count)
* If MSAA, creates msaa-enabled surface
void RenderTarget::Init(const int width,const int height,const bool enableMSAA)
{
m_bEnableMSAA = enableMSAA;
D3DXCreateTexture(pDevice,
width,
height,
1,
D3DUSAGE_RENDERTARGET,
D3DFMT_A8R8G8B8,
D3DPOOL_DEFAULT,
&m_pTexture;
);
m_pTexture->GetSurfaceLevel(0,&m_pSurface);
if(enableMSAA)
{
Renderer::GetInstance()->GetDevice()->CreateRenderTarget(
width,
height,
D3DFMT_A8R8G8B8,
d3dpp.MultiSampleType,
d3dpp.MultiSampleQuality,
false,
&m_pMSAAsurface,
NULL
);
}
}
If this MSAA setting is off, RenderTarget::Activate sets m_pSurface as the render target. If this MSAA setting is on, RenderTarget::Activate sets m_pMSAASurface as the render target and enables the multisampling render state.
RenderTarget::Activate
* Stores the current render target (adds to that surface's ref count)
* If not MSAA, sets surface as the new render target
* If MSAA, sets msaa surface as the new render target, enables msaa render state
void RenderTarget::Activate()
{
pDevice->GetRenderTarget(0,&m_pOldSurface);
if(!m_bEnableMSAA)
{
pDevice->SetRenderTarget(0,m_pSurface);
}
else
{
pDevice->SetRenderTarget(0,m_pMSAAsurface);
pDevice->SetRenderState(D3DRS_MULTISAMPLEANTIALIAS,true);
}
}
If this MSAA setting is off, RenderTarget::Deactivate simply restores the original render target. If this MSAA setting is on, RenderTarget::Deactivate restores the original render target too, but also copies m_pMSAASurface onto m_pSurface.
RenderTarget::Deactivate
* If MSAA, disables MSAA render state
* Restores the previous render target
* Drops ref counts on the previous render target
void RenderTarget::Deactivate()
{
if(m_bEnableMSAA)
{
pDevice->SetRenderState(D3DRS_MULTISAMPLEANTIALIAS,false);
pDevice->StretchRect(m_pMSAAsurface,NULL,m_pSurface,NULL,D3DTEXF_NONE);
}
pDevice->SetRenderTarget(0,m_pOldSurface);
m_pOldSurface->Release();
m->pOldSurface = nullptr;
}
When the Renderer later asks the geometry layer for its RenderTarget texture in order to combine it with the other layers, that texture has the image copied from m_pMSAASurface on it. Assuming you're using a format that facilitates an alpha channel, this texture can be be blended with others, as I'm doing with the render targets of several layers.