/****************************************************************************** * The MIT License (MIT) * * Copyright (c) 2019-2025 Baldur Karlsson * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. ******************************************************************************/ #include "driver/dxgi/dxgi_common.h" #include "d3d12_command_list.h" #include "d3d12_command_queue.h" #include "d3d12_debug.h" #include "d3d12_device.h" #include "driver/dx/official/d3dcompiler.h" rdcpair D3D12DebugManager::GetMSToArrayPSOs( DXGI_FORMAT format) { rdcpair &cache = m_MS2ArrayPSOCache[format]; if(cache.first == NULL) { const bool stencil = IsDepthAndStencilFormat(format); D3D12_GRAPHICS_PIPELINE_STATE_DESC pipeDesc = {}; pipeDesc.pRootSignature = Unwrap(m_ArrayMSAARootSig); pipeDesc.VS.BytecodeLength = m_FullscreenVS->GetBufferSize(); pipeDesc.VS.pShaderBytecode = m_FullscreenVS->GetBufferPointer(); pipeDesc.PS.BytecodeLength = m_FloatMS2Array->GetBufferSize(); pipeDesc.PS.pShaderBytecode = m_FloatMS2Array->GetBufferPointer(); pipeDesc.NumRenderTargets = 1; pipeDesc.RTVFormats[0] = format; pipeDesc.DSVFormat = DXGI_FORMAT_UNKNOWN; if(IsDepthFormat(format)) { pipeDesc.PS.BytecodeLength = m_DepthMS2Array->GetBufferSize(); pipeDesc.PS.pShaderBytecode = m_DepthMS2Array->GetBufferPointer(); pipeDesc.NumRenderTargets = 0; pipeDesc.RTVFormats[0] = DXGI_FORMAT_UNKNOWN; pipeDesc.DSVFormat = format; pipeDesc.DepthStencilState.DepthEnable = TRUE; pipeDesc.DepthStencilState.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL; pipeDesc.DepthStencilState.DepthFunc = D3D12_COMPARISON_FUNC_ALWAYS; } else if(IsUIntFormat(format) || IsIntFormat(format)) { pipeDesc.PS.BytecodeLength = m_IntMS2Array->GetBufferSize(); pipeDesc.PS.pShaderBytecode = m_IntMS2Array->GetBufferPointer(); } pipeDesc.RasterizerState.FillMode = D3D12_FILL_MODE_SOLID; pipeDesc.RasterizerState.CullMode = D3D12_CULL_MODE_NONE; pipeDesc.SampleMask = 0xFFFFFFFF; pipeDesc.SampleDesc.Count = 1; pipeDesc.IBStripCutValue = D3D12_INDEX_BUFFER_STRIP_CUT_VALUE_DISABLED; pipeDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; pipeDesc.BlendState.RenderTarget[0].BlendEnable = FALSE; pipeDesc.BlendState.RenderTarget[0].SrcBlend = D3D12_BLEND_SRC_ALPHA; pipeDesc.BlendState.RenderTarget[0].DestBlend = D3D12_BLEND_INV_SRC_ALPHA; pipeDesc.BlendState.RenderTarget[0].BlendOp = D3D12_BLEND_OP_ADD; pipeDesc.BlendState.RenderTarget[0].SrcBlendAlpha = D3D12_BLEND_SRC_ALPHA; pipeDesc.BlendState.RenderTarget[0].DestBlendAlpha = D3D12_BLEND_INV_SRC_ALPHA; pipeDesc.BlendState.RenderTarget[0].BlendOpAlpha = D3D12_BLEND_OP_ADD; pipeDesc.BlendState.RenderTarget[0].RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL; HRESULT hr = m_pDevice->GetReal()->CreateGraphicsPipelineState( &pipeDesc, __uuidof(ID3D12PipelineState), (void **)&cache.first); if(FAILED(hr)) { RDCERR("Couldn't create MSAA conversion pipeline for %s! HRESULT: %s", ToStr(format).c_str(), ToStr(hr).c_str()); SAFE_RELEASE(cache.first); SAFE_RELEASE(cache.second); return cache; } if(stencil) { pipeDesc.DepthStencilState.DepthEnable = FALSE; pipeDesc.DepthStencilState.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ZERO; pipeDesc.DepthStencilState.StencilEnable = TRUE; pipeDesc.DepthStencilState.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_ALWAYS; pipeDesc.DepthStencilState.FrontFace.StencilPassOp = D3D12_STENCIL_OP_REPLACE; pipeDesc.DepthStencilState.FrontFace.StencilFailOp = D3D12_STENCIL_OP_REPLACE; pipeDesc.DepthStencilState.FrontFace.StencilDepthFailOp = D3D12_STENCIL_OP_REPLACE; pipeDesc.DepthStencilState.BackFace = pipeDesc.DepthStencilState.FrontFace; pipeDesc.DepthStencilState.StencilReadMask = 0xff; pipeDesc.DepthStencilState.StencilWriteMask = 0xff; hr = m_pDevice->GetReal()->CreateGraphicsPipelineState( &pipeDesc, __uuidof(ID3D12PipelineState), (void **)&cache.second); if(FAILED(hr)) { RDCERR("Couldn't create MSAA stencil conversion pipeline for %s! HRESULT: %s", ToStr(format).c_str(), ToStr(hr).c_str()); SAFE_RELEASE(cache.first); SAFE_RELEASE(cache.second); return cache; } } } return cache; } void D3D12DebugManager::CopyTex2DMSToArray(ID3D12GraphicsCommandList *list, ID3D12Resource *destArray, ID3D12Resource *srcMS) { // this function operates during capture so we work on unwrapped objects const bool externalList = list != NULL; D3D12MarkerRegion renderoverlay = externalList ? D3D12MarkerRegion(list, "CopyTex2DMSToArray") : D3D12MarkerRegion(m_pDevice->GetQueue(), "CopyTex2DMSToArray"); D3D12_RESOURCE_DESC descMS = srcMS->GetDesc(); D3D12_RESOURCE_DESC descArr = destArray->GetDesc(); D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc; srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DMSARRAY; srvDesc.Texture2DMSArray.FirstArraySlice = 0; srvDesc.Texture2DMSArray.ArraySize = descMS.DepthOrArraySize; srvDesc.Format = GetTypedFormat(descMS.Format, CompType::UInt); srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; D3D12_RENDER_TARGET_VIEW_DESC rtvDesc; rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DARRAY; rtvDesc.Format = srvDesc.Format; rtvDesc.Texture2DArray.ArraySize = 1; rtvDesc.Texture2DArray.FirstArraySlice = 0; rtvDesc.Texture2DArray.MipSlice = 0; rtvDesc.Texture2DArray.PlaneSlice = 0; D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc; dsvDesc.Flags = D3D12_DSV_FLAG_NONE; dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2DARRAY; dsvDesc.Format = srvDesc.Format; dsvDesc.Texture2DArray.ArraySize = 1; dsvDesc.Texture2DArray.FirstArraySlice = 0; dsvDesc.Texture2DArray.MipSlice = 0; const bool isDepth = IsDepthFormat(rtvDesc.Format) || (descMS.Flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL) != 0 || (descArr.Flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL) != 0; const bool stencil = IsDepthAndStencilFormat(rtvDesc.Format); DXGI_FORMAT stencilFormat = DXGI_FORMAT_UNKNOWN; if(isDepth) { switch(descMS.Format) { case DXGI_FORMAT_D32_FLOAT: case DXGI_FORMAT_R32_FLOAT: case DXGI_FORMAT_R32_TYPELESS: dsvDesc.Format = DXGI_FORMAT_D32_FLOAT; srvDesc.Format = DXGI_FORMAT_R32_FLOAT; break; case DXGI_FORMAT_D32_FLOAT_S8X24_UINT: case DXGI_FORMAT_R32G8X24_TYPELESS: case DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS: case DXGI_FORMAT_X32_TYPELESS_G8X24_UINT: dsvDesc.Format = DXGI_FORMAT_D32_FLOAT_S8X24_UINT; srvDesc.Format = DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS; stencilFormat = DXGI_FORMAT_X32_TYPELESS_G8X24_UINT; break; case DXGI_FORMAT_D24_UNORM_S8_UINT: case DXGI_FORMAT_R24G8_TYPELESS: case DXGI_FORMAT_R24_UNORM_X8_TYPELESS: case DXGI_FORMAT_X24_TYPELESS_G8_UINT: dsvDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; srvDesc.Format = DXGI_FORMAT_R24_UNORM_X8_TYPELESS; stencilFormat = DXGI_FORMAT_X24_TYPELESS_G8_UINT; break; case DXGI_FORMAT_D16_UNORM: case DXGI_FORMAT_R16_TYPELESS: dsvDesc.Format = DXGI_FORMAT_D16_UNORM; srvDesc.Format = DXGI_FORMAT_R16_UNORM; break; default: break; } rtvDesc.Format = dsvDesc.Format; } else { dsvDesc.Format = DXGI_FORMAT_UNKNOWN; } for(CBVUAVSRVSlot slot : {MSAA_SRV2x, MSAA_SRV4x, MSAA_SRV8x, MSAA_SRV16x, MSAA_SRV32x}) { D3D12_CPU_DESCRIPTOR_HANDLE srv = Unwrap(GetCPUHandle(slot)); m_pDevice->GetReal()->CreateShaderResourceView(srcMS, &srvDesc, srv); } if(stencil) { srvDesc.Format = stencilFormat; for(CBVUAVSRVSlot slot : {STENCIL_MSAA_SRV2x, STENCIL_MSAA_SRV4x, STENCIL_MSAA_SRV8x, STENCIL_MSAA_SRV16x, STENCIL_MSAA_SRV32x}) { D3D12_CPU_DESCRIPTOR_HANDLE srv = Unwrap(GetCPUHandle(slot)); m_pDevice->GetReal()->CreateShaderResourceView(srcMS, &srvDesc, srv); } } D3D12_CPU_DESCRIPTOR_HANDLE rtv = Unwrap(GetCPUHandle(MSAA_RTV)); D3D12_CPU_DESCRIPTOR_HANDLE dsv = Unwrap(GetCPUHandle(MSAA_DSV)); ID3D12PipelineState *pso = NULL, *psoStencil = NULL; rdctie(pso, psoStencil) = GetMSToArrayPSOs(rtvDesc.Format); if(!externalList) { list = Unwrap(m_DebugList); list->Reset(Unwrap(m_DebugAlloc), NULL); } D3D12_VIEWPORT viewport = {0, 0, (float)descArr.Width, (float)descArr.Height, 0.0f, 1.0f}; D3D12_RECT scissor = {0, 0, (LONG)descArr.Width, (LONG)descArr.Height}; list->RSSetViewports(1, &viewport); list->RSSetScissorRects(1, &scissor); list->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); ID3D12DescriptorHeap *heap = Unwrap(cbvsrvuavHeap); list->SetDescriptorHeaps(1, &heap); list->SetPipelineState(pso); list->SetGraphicsRootSignature(Unwrap(m_ArrayMSAARootSig)); list->SetGraphicsRootDescriptorTable(1, Unwrap(GetGPUHandle(MSAA_SRV2x))); if(stencil) list->SetGraphicsRootDescriptorTable(2, Unwrap(GetGPUHandle(STENCIL_MSAA_SRV2x))); // loop over every array slice in MS texture for(UINT slice = 0; slice < descMS.DepthOrArraySize; slice++) { // loop over every multi sample for(UINT sample = 0; sample < descMS.SampleDesc.Count; sample++) { uint32_t cdata[4] = {descMS.SampleDesc.Count, 1000, sample, slice}; list->SetGraphicsRootConstantBufferView(0, UploadConstants(cdata, sizeof(cdata))); dsvDesc.Texture2DArray.FirstArraySlice = slice * descMS.SampleDesc.Count + sample; rtvDesc.Texture2DArray.FirstArraySlice = slice * descMS.SampleDesc.Count + sample; if(isDepth) { m_pDevice->GetReal()->CreateDepthStencilView(destArray, &dsvDesc, dsv); list->OMSetRenderTargets(0, NULL, FALSE, &dsv); } else { m_pDevice->GetReal()->CreateRenderTargetView(destArray, &rtvDesc, rtv); list->OMSetRenderTargets(1, &rtv, FALSE, NULL); } list->DrawInstanced(3, 1, 0, 0); } } if(stencil) { list->SetPipelineState(psoStencil); dsvDesc.Flags = D3D12_DSV_FLAG_READ_ONLY_DEPTH; dsvDesc.Texture2DArray.ArraySize = 1; // loop over every array slice in MS texture for(UINT slice = 0; slice < descMS.DepthOrArraySize; slice++) { // loop over every multi sample for(UINT sample = 0; sample < descMS.SampleDesc.Count; sample++) { dsvDesc.Texture2DArray.FirstArraySlice = slice * descMS.SampleDesc.Count + sample; m_pDevice->GetReal()->CreateDepthStencilView(destArray, &dsvDesc, dsv); list->OMSetRenderTargets(0, NULL, FALSE, &dsv); // loop over every stencil value (in theory we could use SV_StencilRef, but it's optional // and would mean a different shader) for(UINT stencilval = 0; stencilval < 256; stencilval++) { uint32_t cdata[4] = {descMS.SampleDesc.Count, stencilval, sample, slice}; list->SetGraphicsRootConstantBufferView(0, UploadConstants(cdata, sizeof(cdata))); list->OMSetStencilRef(stencilval); list->DrawInstanced(3, 1, 0, 0); } } } } if(!externalList) { list->Close(); if(m_pDevice->HasFatalError()) return; ID3D12CommandList *l = list; m_pDevice->GetQueue()->GetReal()->ExecuteCommandLists(1, &l); m_pDevice->QueueWaitForIdle(m_pDevice->GetQueue()->GetReal(), Unwrap(m_DebugFence)); m_DebugAlloc->Reset(); } } void D3D12DebugManager::CopyArrayToTex2DMS(ID3D12Resource *destMS, ID3D12Resource *srcArray, UINT selectedSlice) { bool singleSliceMode = (selectedSlice != ~0U); D3D12MarkerRegion renderoverlay(m_pDevice->GetQueue(), "CopyArrayToTex2DMS"); D3D12_RESOURCE_DESC descArr = srcArray->GetDesc(); D3D12_RESOURCE_DESC descMS = destMS->GetDesc(); UINT sampleMask = ~0U; if(singleSliceMode) { sampleMask = 1U << (selectedSlice % descMS.SampleDesc.Count); selectedSlice /= descMS.SampleDesc.Count; } D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {}; srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DARRAY; srvDesc.Texture2DArray.MipLevels = 1; srvDesc.Texture2DArray.ArraySize = descArr.DepthOrArraySize; srvDesc.Format = GetTypedFormat(descMS.Format, CompType::UInt); srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; D3D12_RENDER_TARGET_VIEW_DESC rtvDesc; rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DMSARRAY; rtvDesc.Format = srvDesc.Format; rtvDesc.Texture2DMSArray.ArraySize = 1; D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc; dsvDesc.Flags = D3D12_DSV_FLAG_NONE; dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2DMSARRAY; dsvDesc.Format = srvDesc.Format; dsvDesc.Texture2DMSArray.ArraySize = 1; bool isDepth = IsDepthFormat(rtvDesc.Format) || (descArr.Flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL) != 0 || (descMS.Flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL) != 0; bool intFormat = IsUIntFormat(rtvDesc.Format) || IsIntFormat(rtvDesc.Format); bool stencil = false; DXGI_FORMAT stencilFormat = DXGI_FORMAT_UNKNOWN; if(isDepth) { switch(descMS.Format) { case DXGI_FORMAT_D32_FLOAT: case DXGI_FORMAT_R32_FLOAT: case DXGI_FORMAT_R32_TYPELESS: dsvDesc.Format = DXGI_FORMAT_D32_FLOAT; srvDesc.Format = DXGI_FORMAT_R32_FLOAT; break; case DXGI_FORMAT_D32_FLOAT_S8X24_UINT: case DXGI_FORMAT_R32G8X24_TYPELESS: case DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS: case DXGI_FORMAT_X32_TYPELESS_G8X24_UINT: dsvDesc.Format = DXGI_FORMAT_D32_FLOAT_S8X24_UINT; srvDesc.Format = DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS; stencilFormat = DXGI_FORMAT_X32_TYPELESS_G8X24_UINT; stencil = true; break; case DXGI_FORMAT_D24_UNORM_S8_UINT: case DXGI_FORMAT_R24G8_TYPELESS: case DXGI_FORMAT_R24_UNORM_X8_TYPELESS: case DXGI_FORMAT_X24_TYPELESS_G8_UINT: dsvDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; srvDesc.Format = DXGI_FORMAT_R24_UNORM_X8_TYPELESS; stencilFormat = DXGI_FORMAT_X24_TYPELESS_G8_UINT; stencil = true; break; case DXGI_FORMAT_D16_UNORM: case DXGI_FORMAT_R16_TYPELESS: dsvDesc.Format = DXGI_FORMAT_D16_UNORM; srvDesc.Format = DXGI_FORMAT_R16_UNORM; break; default: break; } } for(CBVUAVSRVSlot slot : {MSAA_SRV2x, MSAA_SRV4x, MSAA_SRV8x, MSAA_SRV16x, MSAA_SRV32x}) { D3D12_CPU_DESCRIPTOR_HANDLE srv = GetCPUHandle(slot); m_pDevice->CreateShaderResourceView(srcArray, &srvDesc, srv); } if(stencil) { srvDesc.Format = stencilFormat; { D3D12_FEATURE_DATA_FORMAT_INFO formatInfo = {}; formatInfo.Format = srvDesc.Format; m_pDevice->CheckFeatureSupport(D3D12_FEATURE_FORMAT_INFO, &formatInfo, sizeof(formatInfo)); UINT planes = RDCMAX((UINT8)1, formatInfo.PlaneCount); if(planes > 1) srvDesc.Texture2DArray.PlaneSlice = 1; } for(CBVUAVSRVSlot slot : {STENCIL_MSAA_SRV2x, STENCIL_MSAA_SRV4x, STENCIL_MSAA_SRV8x, STENCIL_MSAA_SRV16x, STENCIL_MSAA_SRV32x}) { D3D12_CPU_DESCRIPTOR_HANDLE srv = GetCPUHandle(slot); m_pDevice->CreateShaderResourceView(srcArray, &srvDesc, srv); } } D3D12_CPU_DESCRIPTOR_HANDLE rtv = GetCPUHandle(MSAA_RTV); D3D12_CPU_DESCRIPTOR_HANDLE dsv = GetCPUHandle(MSAA_DSV); D3D12_GRAPHICS_PIPELINE_STATE_DESC pipeDesc = {}; pipeDesc.pRootSignature = m_ArrayMSAARootSig; pipeDesc.VS.BytecodeLength = m_FullscreenVS->GetBufferSize(); pipeDesc.VS.pShaderBytecode = m_FullscreenVS->GetBufferPointer(); pipeDesc.PS.BytecodeLength = m_FloatArray2MS->GetBufferSize(); pipeDesc.PS.pShaderBytecode = m_FloatArray2MS->GetBufferPointer(); pipeDesc.NumRenderTargets = 1; pipeDesc.RTVFormats[0] = rtvDesc.Format; pipeDesc.DSVFormat = DXGI_FORMAT_UNKNOWN; if(isDepth) { pipeDesc.PS.BytecodeLength = m_DepthArray2MS->GetBufferSize(); pipeDesc.PS.pShaderBytecode = m_DepthArray2MS->GetBufferPointer(); pipeDesc.NumRenderTargets = 0; pipeDesc.RTVFormats[0] = DXGI_FORMAT_UNKNOWN; pipeDesc.DSVFormat = dsvDesc.Format; pipeDesc.DepthStencilState.DepthEnable = TRUE; pipeDesc.DepthStencilState.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL; pipeDesc.DepthStencilState.DepthFunc = D3D12_COMPARISON_FUNC_ALWAYS; } else if(intFormat) { pipeDesc.PS.BytecodeLength = m_IntArray2MS->GetBufferSize(); pipeDesc.PS.pShaderBytecode = m_IntArray2MS->GetBufferPointer(); } pipeDesc.RasterizerState.FillMode = D3D12_FILL_MODE_SOLID; pipeDesc.RasterizerState.CullMode = D3D12_CULL_MODE_NONE; pipeDesc.SampleMask = sampleMask; pipeDesc.SampleDesc = descMS.SampleDesc; pipeDesc.IBStripCutValue = D3D12_INDEX_BUFFER_STRIP_CUT_VALUE_DISABLED; pipeDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; pipeDesc.BlendState.RenderTarget[0].BlendEnable = FALSE; pipeDesc.BlendState.RenderTarget[0].SrcBlend = D3D12_BLEND_SRC_ALPHA; pipeDesc.BlendState.RenderTarget[0].DestBlend = D3D12_BLEND_INV_SRC_ALPHA; pipeDesc.BlendState.RenderTarget[0].BlendOp = D3D12_BLEND_OP_ADD; pipeDesc.BlendState.RenderTarget[0].SrcBlendAlpha = D3D12_BLEND_SRC_ALPHA; pipeDesc.BlendState.RenderTarget[0].DestBlendAlpha = D3D12_BLEND_INV_SRC_ALPHA; pipeDesc.BlendState.RenderTarget[0].BlendOpAlpha = D3D12_BLEND_OP_ADD; pipeDesc.BlendState.RenderTarget[0].RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL; ID3D12PipelineState *pso = NULL, *psoStencil = NULL; HRESULT hr = m_pDevice->CreateGraphicsPipelineState(&pipeDesc, __uuidof(ID3D12PipelineState), (void **)&pso); if(FAILED(hr)) { RDCERR("Couldn't create MSAA conversion pipeline! HRESULT: %s", ToStr(hr).c_str()); return; } ID3D12GraphicsCommandList *list = m_DebugList; list->Reset(m_DebugAlloc, NULL); D3D12_VIEWPORT viewport = {0, 0, (float)descArr.Width, (float)descArr.Height, 0.0f, 1.0f}; D3D12_RECT scissor = {0, 0, (LONG)descArr.Width, (LONG)descArr.Height}; list->RSSetViewports(1, &viewport); list->RSSetScissorRects(1, &scissor); list->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); list->SetDescriptorHeaps(1, &cbvsrvuavHeap); list->SetPipelineState(pso); list->SetGraphicsRootSignature(m_ArrayMSAARootSig); list->SetGraphicsRootDescriptorTable(1, GetGPUHandle(MSAA_SRV2x)); if(stencil) list->SetGraphicsRootDescriptorTable(2, GetGPUHandle(STENCIL_MSAA_SRV2x)); // loop over every array slice in MS texture for(UINT slice = 0; slice < descMS.DepthOrArraySize; slice++) { if(singleSliceMode) slice = selectedSlice; uint32_t cdata[4] = {descMS.SampleDesc.Count, 1000, 0, slice}; list->SetGraphicsRootConstantBufferView(0, UploadConstants(cdata, sizeof(cdata))); rtvDesc.Texture2DMSArray.FirstArraySlice = slice; dsvDesc.Texture2DMSArray.FirstArraySlice = slice; if(isDepth) { m_pDevice->CreateDepthStencilView(destMS, &dsvDesc, dsv); list->OMSetRenderTargets(0, NULL, FALSE, &dsv); } else { m_pDevice->CreateRenderTargetView(destMS, &rtvDesc, rtv); list->OMSetRenderTargets(1, &rtv, FALSE, NULL); } list->DrawInstanced(3, 1, 0, 0); if(singleSliceMode) break; } if(stencil) { pipeDesc.DepthStencilState.DepthEnable = FALSE; pipeDesc.DepthStencilState.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ZERO; pipeDesc.DepthStencilState.StencilEnable = TRUE; pipeDesc.DepthStencilState.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_ALWAYS; pipeDesc.DepthStencilState.FrontFace.StencilPassOp = D3D12_STENCIL_OP_REPLACE; pipeDesc.DepthStencilState.FrontFace.StencilFailOp = D3D12_STENCIL_OP_REPLACE; pipeDesc.DepthStencilState.FrontFace.StencilDepthFailOp = D3D12_STENCIL_OP_REPLACE; pipeDesc.DepthStencilState.BackFace = pipeDesc.DepthStencilState.FrontFace; pipeDesc.DepthStencilState.StencilReadMask = 0xff; pipeDesc.DepthStencilState.StencilWriteMask = 0xff; hr = m_pDevice->CreateGraphicsPipelineState(&pipeDesc, __uuidof(ID3D12PipelineState), (void **)&psoStencil); RDCASSERTEQUAL(hr, S_OK); dsvDesc.Flags = D3D12_DSV_FLAG_READ_ONLY_DEPTH; dsvDesc.Texture2DMSArray.ArraySize = 1; list->SetPipelineState(psoStencil); // loop over every array slice in MS texture for(UINT slice = 0; slice < descMS.DepthOrArraySize; slice++) { if(singleSliceMode) slice = selectedSlice; dsvDesc.Texture2DMSArray.FirstArraySlice = slice; m_pDevice->CreateDepthStencilView(destMS, &dsvDesc, dsv); list->OMSetRenderTargets(0, NULL, FALSE, &dsv); // loop over every stencil value (in theory we could use SV_StencilRef, but it's optional // and would mean a different shader) for(UINT stencilval = 0; stencilval < 256; stencilval++) { uint32_t cdata[4] = {descMS.SampleDesc.Count, stencilval, 0, slice}; list->SetGraphicsRootConstantBufferView(0, UploadConstants(cdata, sizeof(cdata))); list->OMSetStencilRef(stencilval); list->DrawInstanced(3, 1, 0, 0); } } } list->Close(); ID3D12CommandList *l = m_DebugList; m_pDevice->GetQueue()->ExecuteCommandLists(1, &l); m_pDevice->InternalQueueWaitForIdle(); m_DebugAlloc->Reset(); SAFE_RELEASE(pso); SAFE_RELEASE(psoStencil); }