diff --git a/util/test/demos/d3d12/d3d12_pixel_history.cpp b/util/test/demos/d3d12/d3d12_pixel_history.cpp new file mode 100644 index 000000000..a001d13df --- /dev/null +++ b/util/test/demos/d3d12/d3d12_pixel_history.cpp @@ -0,0 +1,740 @@ +/****************************************************************************** + * The MIT License (MIT) + * + * Copyright (c) 2023 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 "d3d12_test.h" + +RD_TEST(D3D12_Pixel_History, D3D12GraphicsTest) +{ + static constexpr const char *Description = "Tests pixel history"; + + std::string common = R"EOSHADER( +struct v2f +{ + float4 pos : SV_POSITION; + float4 col : COLOR0; + float2 uv : TEXCOORD0; +}; + +)EOSHADER"; + + const std::string vertex = R"EOSHADER( + +struct vertin +{ + float3 pos : POSITION; + float4 col : COLOR0; + float2 uv : TEXCOORD0; +}; + +v2f main(vertin IN, uint vid : SV_VertexID) +{ + v2f OUT = (v2f)0; + + OUT.pos = float4(IN.pos, 1.0f); + OUT.col = IN.col; + OUT.uv = IN.uv; + + return OUT; +} + +)EOSHADER"; + + const std::string pixel = R"EOSHADER( + +float4 main(v2f vertIn) : SV_Target0 +{ + if (vertIn.pos.x < 151 && vertIn.pos.x > 150) + discard; + return vertIn.col + float4(0, 0, 0, 1.75); +} + +)EOSHADER"; + + std::string mspixel = R"EOSHADER( + +float4 main(v2f vertIn, uint primId : SV_PrimitiveID, uint sampleId : SV_SampleIndex) : SV_Target0 +{ + float4 color = (float4)0; + if(primId == 0) + { + color = float4(1, 0, 1, 2.75); + } + else + { + if (sampleId == 0) + color = float4(1, 0, 0, 2.75); + else if (sampleId == 1) + color = float4(0, 0, 1, 2.75); + else if (sampleId == 2) + color = float4(0, 1, 1, 2.75); + else if (sampleId == 3) + color = float4(1, 1, 1, 2.75); + } + + return color; +} + +)EOSHADER"; + + int main() + { + // initialise, create window, create context, etc + if(!Init()) + return 3; + + DefaultA2V VBData[] = { + // this triangle occludes in depth + {Vec3f(-0.5f, -0.5f, 0.0f), Vec4f(0.0f, 0.0f, 1.0f, 1.0f), Vec2f(0.0f, 0.0f)}, + {Vec3f(-0.5f, 0.0f, 0.0f), Vec4f(0.0f, 0.0f, 1.0f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(0.0f, 0.0f, 0.0f), Vec4f(0.0f, 0.0f, 1.0f, 1.0f), Vec2f(1.0f, 0.0f)}, + + // this triangle occludes in stencil + {Vec3f(-0.5f, 0.0f, 0.9f), Vec4f(1.0f, 0.0f, 0.0f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(-0.5f, 0.5f, 0.9f), Vec4f(1.0f, 0.0f, 0.0f, 1.0f), Vec2f(0.0f, 0.0f)}, + {Vec3f(0.0f, 0.0f, 0.9f), Vec4f(1.0f, 0.0f, 0.0f, 1.0f), Vec2f(1.0f, 0.0f)}, + + // this triangle is just in the background to contribute to overdraw + {Vec3f(-0.9f, -0.9f, 0.95f), Vec4f(1.0f, 0.0f, 1.0f, 1.0f), Vec2f(0.0f, 0.0f)}, + {Vec3f(0.0f, 0.9f, 0.95f), Vec4f(1.0f, 0.0f, 1.0f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(0.9f, -0.9f, 0.95f), Vec4f(1.0f, 0.0f, 1.0f, 1.0f), Vec2f(1.0f, 0.0f)}, + + // the draw has a few triangles, main one that is occluded for depth, another that is + // adding to overdraw complexity, one that is backface culled, then a few more of various + // sizes for triangle size overlay + {Vec3f(-0.3f, -0.5f, 0.5f), Vec4f(0.0f, 0.0f, 0.0f, 1.0f), Vec2f(0.0f, 0.0f)}, + {Vec3f(-0.3f, 0.5f, 0.5f), Vec4f(0.0f, 0.0f, 0.0f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(0.5f, 0.0f, 0.5f), Vec4f(1.0f, 1.0f, 1.0f, 1.0f), Vec2f(1.0f, 0.0f)}, + + {Vec3f(-0.2f, -0.2f, 0.6f), Vec4f(0.0f, 0.0f, 0.0f, 1.0f), Vec2f(0.0f, 0.0f)}, + {Vec3f(0.2f, 0.0f, 0.6f), Vec4f(0.0f, 0.0f, 0.0f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(0.2f, -0.4f, 0.6f), Vec4f(0.0f, 0.0f, 0.0f, 1.0f), Vec2f(1.0f, 0.0f)}, + + // backface culled + {Vec3f(0.1f, 0.0f, 0.5f), Vec4f(0.0f, 0.0f, 0.0f, 1.0f), Vec2f(0.0f, 0.0f)}, + {Vec3f(0.5f, -0.2f, 0.5f), Vec4f(0.0f, 0.0f, 0.0f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(0.5f, 0.2f, 0.5f), Vec4f(0.0f, 0.0f, 0.0f, 1.0f), Vec2f(1.0f, 0.0f)}, + + // depth clipped (i.e. not clamped) + {Vec3f(0.6f, 0.0f, 0.5f), Vec4f(0.0f, 0.0f, 0.0f, 1.0f), Vec2f(0.0f, 0.0f)}, + {Vec3f(0.7f, 0.2f, 0.5f), Vec4f(0.0f, 0.0f, 0.0f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(0.8f, 0.0f, 1.5f), Vec4f(0.0f, 0.0f, 0.0f, 1.0f), Vec2f(1.0f, 0.0f)}, + + // small triangles + // size=0.005 + {Vec3f(0.0f, 0.4f, 0.5f), Vec4f(0.0f, 1.0f, 0.0f, 1.0f), Vec2f(0.0f, 0.0f)}, + {Vec3f(0.0f, 0.41f, 0.5f), Vec4f(0.0f, 1.0f, 0.0f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(0.01f, 0.4f, 0.5f), Vec4f(0.0f, 1.0f, 0.0f, 1.0f), Vec2f(1.0f, 0.0f)}, + + // size=0.015 + {Vec3f(0.0f, 0.5f, 0.5f), Vec4f(0.0f, 1.0f, 1.0f, 1.0f), Vec2f(0.0f, 0.0f)}, + {Vec3f(0.0f, 0.515f, 0.5f), Vec4f(0.0f, 1.0f, 1.0f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(0.015f, 0.5f, 0.5f), Vec4f(0.0f, 1.0f, 1.0f, 1.0f), Vec2f(1.0f, 0.0f)}, + + // size=0.02 + {Vec3f(0.0f, 0.6f, 0.5f), Vec4f(1.0f, 1.0f, 0.0f, 1.0f), Vec2f(0.0f, 0.0f)}, + {Vec3f(0.0f, 0.62f, 0.5f), Vec4f(1.0f, 1.0f, 0.0f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(0.02f, 0.6f, 0.5f), Vec4f(1.0f, 1.0f, 0.0f, 1.0f), Vec2f(1.0f, 0.0f)}, + + // size=0.025 + {Vec3f(0.0f, 0.7f, 0.5f), Vec4f(1.0f, 0.5f, 1.0f, 1.0f), Vec2f(0.0f, 0.0f)}, + {Vec3f(0.0f, 0.725f, 0.5f), Vec4f(1.0f, 0.5f, 1.0f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(0.025f, 0.7f, 0.5f), Vec4f(1.0f, 0.5f, 1.0f, 1.0f), Vec2f(1.0f, 0.0f)}, + + // dynamic triangles + {Vec3f(-0.6f, -0.75f, 0.5f), Vec4f(1.0f, 0.0f, 0.0f, 1.0f), Vec2f(0.0f, 0.0f)}, + {Vec3f(-0.5f, -0.65f, 0.5f), Vec4f(1.0f, 0.0f, 0.0f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(-0.4f, -0.75f, 0.5f), Vec4f(1.0f, 0.0f, 0.0f, 1.0f), Vec2f(1.0f, 0.0f)}, + + {Vec3f(-0.6f, -0.75f, 0.5f), Vec4f(0.0f, 1.0f, 0.0f, 1.0f), Vec2f(0.0f, 0.0f)}, + {Vec3f(-0.5f, -0.65f, 0.5f), Vec4f(0.0f, 1.0f, 0.0f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(-0.4f, -0.75f, 0.5f), Vec4f(0.0f, 1.0f, 0.0f, 1.0f), Vec2f(1.0f, 0.0f)}, + + {Vec3f(-0.6f, -0.75f, 0.5f), Vec4f(0.0f, 0.0f, 1.0f, 1.0f), Vec2f(0.0f, 0.0f)}, + {Vec3f(-0.5f, -0.65f, 0.5f), Vec4f(0.0f, 0.0f, 1.0f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(-0.4f, -0.75f, 0.5f), Vec4f(0.0f, 0.0f, 1.0f, 1.0f), Vec2f(1.0f, 0.0f)}, + + {Vec3f(-0.6f, -0.75f, 0.5f), Vec4f(0.0f, 1.0f, 1.0f, 1.0f), Vec2f(0.0f, 0.0f)}, + {Vec3f(-0.5f, -0.65f, 0.5f), Vec4f(0.0f, 1.0f, 1.0f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(-0.4f, -0.75f, 0.5f), Vec4f(0.0f, 1.0f, 1.0f, 1.0f), Vec2f(1.0f, 0.0f)}, + + // Different depth triangles + {Vec3f(0.0f, -0.8f, 0.97f), Vec4f(1.0f, 1.0f, 1.0f, 1.0f), Vec2f(0.0f, 0.0f)}, + {Vec3f(0.4f, -0.2f, 0.97f), Vec4f(1.0f, 1.0f, 1.0f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(0.8f, -0.8f, 0.97f), Vec4f(1.0f, 1.0f, 1.0f, 1.0f), Vec2f(1.0f, 0.0f)}, + + {Vec3f(0.2f, -0.8f, 0.20f), Vec4f(1.0f, 1.0f, 0.0f, 1.0f), Vec2f(0.0f, 0.0f)}, + {Vec3f(0.4f, -0.4f, 0.20f), Vec4f(1.0f, 1.0f, 0.0f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(0.6f, -0.8f, 0.20f), Vec4f(1.0f, 1.0f, 0.0f, 1.0f), Vec2f(1.0f, 0.0f)}, + + {Vec3f(0.2f, -0.8f, 0.30f), Vec4f(1.0f, 0.0f, 0.0f, 1.0f), Vec2f(0.0f, 0.0f)}, + {Vec3f(0.4f, -0.6f, 0.30f), Vec4f(1.0f, 0.0f, 0.0f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(0.6f, -0.8f, 0.30f), Vec4f(1.0f, 0.0f, 0.0f, 1.0f), Vec2f(1.0f, 0.0f)}, + + {Vec3f(0.2f, -0.8f, 0.10f), Vec4f(0.0f, 0.0f, 1.0f, 1.0f), Vec2f(0.0f, 0.0f)}, + {Vec3f(0.4f, -0.7f, 0.10f), Vec4f(0.0f, 0.0f, 1.0f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(0.6f, -0.8f, 0.10f), Vec4f(0.0f, 0.0f, 1.0f, 1.0f), Vec2f(1.0f, 0.0f)}, + + // Fails depth bounds test. + {Vec3f(0.2f, -0.8f, 0.05f), Vec4f(1.0f, 1.0f, 1.0f, 1.0f), Vec2f(0.0f, 0.0f)}, + {Vec3f(0.4f, -0.7f, 0.05f), Vec4f(1.0f, 1.0f, 1.0f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(0.6f, -0.8f, 0.05f), Vec4f(1.0f, 1.0f, 1.0f, 1.0f), Vec2f(1.0f, 0.0f)}, + + // Should be back face culled. + {Vec3f(0.6f, -0.8f, 0.25f), Vec4f(0.0f, 1.0f, 0.0f, 1.0f), Vec2f(1.0f, 0.0f)}, + {Vec3f(0.4f, -0.7f, 0.25f), Vec4f(0.0f, 1.0f, 0.0f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(0.2f, -0.8f, 0.25f), Vec4f(0.0f, 1.0f, 0.0f, 1.0f), Vec2f(0.0f, 0.0f)}, + + // depth bounds prep + {Vec3f(0.6f, 0.3f, 0.3f), Vec4f(1.0f, 0.0f, 0.0f, 1.0f), Vec2f(0.0f, 0.0f)}, + {Vec3f(0.7f, 0.5f, 0.5f), Vec4f(1.0f, 0.0f, 0.0f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(0.8f, 0.3f, 0.7f), Vec4f(1.0f, 0.0f, 0.0f, 1.0f), Vec2f(1.0f, 0.0f)}, + // depth bounds clip + {Vec3f(0.6f, 0.3f, 0.3f), Vec4f(0.0f, 1.0f, 0.0f, 1.0f), Vec2f(0.0f, 0.0f)}, + {Vec3f(0.7f, 0.5f, 0.5f), Vec4f(0.0f, 1.0f, 0.0f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(0.8f, 0.3f, 0.7f), Vec4f(0.0f, 1.0f, 0.0f, 1.0f), Vec2f(1.0f, 0.0f)}, + }; + + ID3D12ResourcePtr vb = MakeBuffer().Data(VBData); + + uint32_t rtvIndex = 1; // Start at 1, backbuffer takes id 0 + uint32_t dsvIndex = 0; + + const DXGI_FORMAT renderSurfaceFormat = DXGI_FORMAT_R8G8B8A8_TYPELESS; + const DXGI_FORMAT renderViewFormat = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; + const DXGI_FORMAT depthFormat = DXGI_FORMAT_D24_UNORM_S8_UINT; + + struct PassResources + { + std::string markerName; + D3D_SHADER_MODEL shaderModel; + + ID3D12ResourcePtr mainRT; + D3D12_CPU_DESCRIPTOR_HANDLE mainRTV; + ID3D12ResourcePtr mainDS; + D3D12_CPU_DESCRIPTOR_HANDLE mainDSV; + + ID3D12ResourcePtr mipArrayRT; + D3D12_CPU_DESCRIPTOR_HANDLE mipArraySubRTV; + ID3D12ResourcePtr mipArrayDS; + D3D12_CPU_DESCRIPTOR_HANDLE mipArraySubDSV; + + ID3D12ResourcePtr msaaRT; + D3D12_CPU_DESCRIPTOR_HANDLE msaaRTV; + ID3D12ResourcePtr msaaDS; + D3D12_CPU_DESCRIPTOR_HANDLE msaaDSV; + + ID3D12ResourcePtr msaaMipArrayRT; + D3D12_CPU_DESCRIPTOR_HANDLE msaaMipArraySubRTV; + ID3D12ResourcePtr msaaMipArrayDS; + D3D12_CPU_DESCRIPTOR_HANDLE msaaMipArraySubDSV; + + ID3D12RootSignaturePtr rootSig; + ID3D12PipelineStatePtr depthWritePipe; + ID3D12PipelineStatePtr dynamicScissorPipe; + ID3D12PipelineStatePtr depthPipe; + ID3D12PipelineStatePtr stencilWritePipe; + ID3D12PipelineStatePtr backgroundPipe; + ID3D12PipelineStatePtr noPsPipe; + ID3D12PipelineStatePtr mainTestPipe; + ID3D12PipelineStatePtr cullFrontPipe; + ID3D12PipelineStatePtr depthBoundsPipe; + ID3D12PipelineStatePtr whitePipe; + ID3D12PipelineStatePtr msaaPipe; + }; + + struct DepthBoundsTestStream + { + D3D12_PIPELINE_STATE_SUBOBJECT_TYPE Type0 = D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_ROOT_SIGNATURE; + UINT Padding0; + ID3D12RootSignature *RootSignature; + + D3D12_PIPELINE_STATE_SUBOBJECT_TYPE Type1 = D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL1; + D3D12_DEPTH_STENCIL_DESC1 DepthStencil; + UINT Padding1; + + D3D12_PIPELINE_STATE_SUBOBJECT_TYPE Type2 = D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_RASTERIZER; + D3D12_RASTERIZER_DESC Rasterizer; + + D3D12_PIPELINE_STATE_SUBOBJECT_TYPE Type3 = D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_INPUT_LAYOUT; + D3D12_INPUT_LAYOUT_DESC InputLayout; + + D3D12_PIPELINE_STATE_SUBOBJECT_TYPE Type4 = + D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_RENDER_TARGET_FORMATS; + D3D12_RT_FORMAT_ARRAY RTVFormats; + + D3D12_PIPELINE_STATE_SUBOBJECT_TYPE Type5 = + D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL_FORMAT; + DXGI_FORMAT DSVFormat; + + D3D12_PIPELINE_STATE_SUBOBJECT_TYPE Type6 = D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_VS; + UINT Padding6; + D3D12_SHADER_BYTECODE VS; + + D3D12_PIPELINE_STATE_SUBOBJECT_TYPE Type7 = D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_PS; + UINT Padding7; + D3D12_SHADER_BYTECODE PS; + } depthBoundsTestStream; + + D3D12_STATIC_SAMPLER_DESC staticSamp = {}; + staticSamp.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR; + staticSamp.AddressU = staticSamp.AddressV = staticSamp.AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP; + staticSamp.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; + + PassResources passes[3]; + passes[0].shaderModel = D3D_SHADER_MODEL_5_1; // DXBC with optional bindless + passes[1].shaderModel = D3D_SHADER_MODEL_6_0; // DXIL with optional bindless + passes[2].shaderModel = D3D_SHADER_MODEL_6_6; // DXIL with direct heap access bindless + passes[0].markerName = "Begin SM5.1"; + passes[1].markerName = "Begin SM6.0"; + passes[2].markerName = "Begin SM6.6"; + + const std::string profileSuffix[3] = {"_5_1", "_6_0", "_6_6"}; + const std::wstring nameSuffix[3] = {L"_SM51", L"_SM60", L"_SM66"}; + + bool supportSM66 = m_HighestShaderModel >= D3D_SHADER_MODEL_6_6; + int numPasses = (supportSM66 && m_DXILSupport) ? 3 : (m_DXILSupport ? 2 : 1); + for(int i = 0; i < numPasses; ++i) + { + PassResources &pass = passes[i]; + pass.mainRT = MakeTexture(renderSurfaceFormat, screenWidth, screenHeight).RTV(); + pass.mainRT->SetName((L"mainRT" + nameSuffix[i]).c_str()); + pass.mainRTV = MakeRTV(pass.mainRT).Format(renderViewFormat).CreateCPU(rtvIndex++); + + pass.mainDS = MakeTexture(depthFormat, screenWidth, screenHeight).DSV(); + pass.mainDS->SetName((L"mainDS" + nameSuffix[i]).c_str()); + pass.mainDSV = MakeDSV(pass.mainDS).Format(depthFormat).CreateCPU(dsvIndex++); + + pass.mipArrayRT = + MakeTexture(renderSurfaceFormat, screenWidth, screenHeight).RTV().Mips(4).Array(5); + pass.mipArrayRT->SetName((L"mipArrayRT" + nameSuffix[i]).c_str()); + + pass.mipArraySubRTV = MakeRTV(pass.mipArrayRT) + .Format(renderViewFormat) + .FirstMip(2) + .NumMips(1) + .FirstSlice(2) + .NumSlices(1) + .CreateCPU(rtvIndex++); + + pass.mipArrayDS = MakeTexture(depthFormat, screenWidth, screenHeight).DSV().Mips(4).Array(5); + pass.mipArrayDS->SetName((L"mipArrayDS" + nameSuffix[i]).c_str()); + + pass.mipArraySubDSV = MakeDSV(pass.mipArrayDS) + .Format(depthFormat) + .FirstMip(2) + .NumMips(1) + .FirstSlice(2) + .NumSlices(1) + .CreateCPU(dsvIndex++); + + pass.msaaRT = MakeTexture(renderSurfaceFormat, screenWidth, screenHeight).RTV().Multisampled(4); + pass.msaaRT->SetName((L"msaaRT" + nameSuffix[i]).c_str()); + pass.msaaRTV = MakeRTV(pass.msaaRT).Format(renderViewFormat).CreateCPU(rtvIndex++); + + pass.msaaDS = MakeTexture(depthFormat, screenWidth, screenHeight).DSV().Multisampled(4); + pass.msaaDS->SetName((L"msaaDS" + nameSuffix[i]).c_str()); + pass.msaaDSV = MakeDSV(pass.msaaDS).Format(depthFormat).CreateCPU(dsvIndex++); + + pass.msaaMipArrayRT = MakeTexture(renderSurfaceFormat, screenWidth, screenHeight) + .RTV() + .Mips(1) + .Array(4) + .Multisampled(4); + pass.msaaMipArrayRT->SetName((L"msaaMipArrayRT" + nameSuffix[i]).c_str()); + pass.msaaMipArraySubRTV = MakeRTV(pass.msaaMipArrayRT) + .Format(renderViewFormat) + .FirstMip(0) + .NumMips(1) + .FirstSlice(2) + .NumSlices(1) + .CreateCPU(rtvIndex++); + + pass.msaaMipArrayDS = + MakeTexture(depthFormat, screenWidth, screenHeight).DSV().Mips(1).Array(4).Multisampled(4); + pass.msaaMipArrayDS->SetName((L"msaaMipArrayDS" + nameSuffix[i]).c_str()); + pass.msaaMipArraySubDSV = MakeDSV(pass.msaaMipArrayDS) + .Format(depthFormat) + .FirstMip(0) + .NumMips(1) + .FirstSlice(2) + .NumSlices(1) + .CreateCPU(dsvIndex++); + + ID3DBlobPtr vsBlob = Compile(common + vertex, "main", ("vs" + profileSuffix[i]).c_str()); + ID3DBlobPtr psBlob = Compile(common + pixel, "main", ("ps" + profileSuffix[i]).c_str()); + ID3DBlobPtr psMsaaBlob = Compile(common + mspixel, "main", ("ps" + profileSuffix[i]).c_str()); + + pass.rootSig = + MakeSig({}, D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT, 1, &staticSamp); + // TODO: Different root sig setup for SM6.6 + + D3D12PSOCreator baselinePSO = MakePSO() + .RootSig(pass.rootSig) + .InputLayout() + .RTVs({DXGI_FORMAT_R8G8B8A8_UNORM_SRGB}) + .DSV(depthFormat) + .VS(vsBlob) + .PS(psBlob); + + baselinePSO.GraphicsDesc.RasterizerState.DepthClipEnable = TRUE; + baselinePSO.GraphicsDesc.RasterizerState.CullMode = D3D12_CULL_MODE_BACK; + D3D12_DEPTH_STENCIL_DESC &depthState = baselinePSO.GraphicsDesc.DepthStencilState; + depthState.DepthEnable = TRUE; + depthState.DepthFunc = D3D12_COMPARISON_FUNC_ALWAYS; + depthState.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL; + depthState.StencilEnable = FALSE; + depthState.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_ALWAYS; + depthState.FrontFace.StencilPassOp = D3D12_STENCIL_OP_REPLACE; + depthState.StencilReadMask = 0xff; + depthState.StencilWriteMask = 0xff; + depthState.BackFace = depthState.FrontFace; + + depthState.DepthFunc = D3D12_COMPARISON_FUNC_ALWAYS; + pass.depthWritePipe = baselinePSO; + + depthState.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ZERO; + depthState.DepthEnable = FALSE; + pass.dynamicScissorPipe = baselinePSO; + + depthState.DepthFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL; + depthState.DepthEnable = TRUE; + depthState.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL; + + depthBoundsTestStream.RootSignature = baselinePSO.GraphicsDesc.pRootSignature; + depthBoundsTestStream.DepthStencil.DepthEnable = depthState.DepthEnable; + depthBoundsTestStream.DepthStencil.DepthWriteMask = depthState.DepthWriteMask; + depthBoundsTestStream.DepthStencil.DepthFunc = depthState.DepthFunc; + depthBoundsTestStream.DepthStencil.StencilEnable = depthState.StencilEnable; + depthBoundsTestStream.DepthStencil.StencilReadMask = depthState.StencilReadMask; + depthBoundsTestStream.DepthStencil.StencilWriteMask = depthState.StencilWriteMask; + depthBoundsTestStream.DepthStencil.FrontFace = depthState.FrontFace; + depthBoundsTestStream.DepthStencil.BackFace = depthState.BackFace; + depthBoundsTestStream.DepthStencil.DepthBoundsTestEnable = TRUE; + + depthBoundsTestStream.Rasterizer = baselinePSO.GraphicsDesc.RasterizerState; + depthBoundsTestStream.InputLayout = baselinePSO.GraphicsDesc.InputLayout; + depthBoundsTestStream.RTVFormats.NumRenderTargets = baselinePSO.GraphicsDesc.NumRenderTargets; + for(int j = 0; j < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; ++j) + depthBoundsTestStream.RTVFormats.RTFormats[j] = baselinePSO.GraphicsDesc.RTVFormats[j]; + depthBoundsTestStream.DSVFormat = baselinePSO.GraphicsDesc.DSVFormat; + + depthBoundsTestStream.VS.BytecodeLength = vsBlob->GetBufferSize(); + depthBoundsTestStream.VS.pShaderBytecode = vsBlob->GetBufferPointer(); + depthBoundsTestStream.PS.BytecodeLength = psBlob->GetBufferSize(); + depthBoundsTestStream.PS.pShaderBytecode = psBlob->GetBufferPointer(); + + D3D12_PIPELINE_STATE_STREAM_DESC depthBoundsTestStreamDesc = {}; + depthBoundsTestStreamDesc.SizeInBytes = sizeof(depthBoundsTestStream); + depthBoundsTestStreamDesc.pPipelineStateSubobjectStream = &depthBoundsTestStream; + HRESULT hr = dev2->CreatePipelineState( + &depthBoundsTestStreamDesc, __uuidof(ID3D12PipelineState), (void **)&pass.depthPipe); + TEST_ASSERT(hr == S_OK, "Pipe created"); + + depthState.StencilEnable = TRUE; + pass.stencilWritePipe = baselinePSO; + + depthState.StencilEnable = FALSE; + pass.backgroundPipe = baselinePSO; + + pass.noPsPipe = baselinePSO.PS(NULL); + + depthState.StencilEnable = TRUE; + depthState.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_GREATER; + pass.mainTestPipe = baselinePSO.PS(psBlob); + + depthState.StencilEnable = FALSE; + + baselinePSO.GraphicsDesc.RasterizerState.CullMode = D3D12_CULL_MODE_FRONT; + pass.cullFrontPipe = baselinePSO; + + baselinePSO.GraphicsDesc.RasterizerState.CullMode = D3D12_CULL_MODE_BACK; + + depthBoundsTestStream.RootSignature = baselinePSO.GraphicsDesc.pRootSignature; + depthBoundsTestStream.DepthStencil.DepthEnable = TRUE; + depthBoundsTestStream.DepthStencil.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL; + depthBoundsTestStream.DepthStencil.DepthFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL; + depthBoundsTestStream.DepthStencil.StencilEnable = depthState.StencilEnable; + depthBoundsTestStream.DepthStencil.StencilReadMask = depthState.StencilReadMask; + depthBoundsTestStream.DepthStencil.StencilWriteMask = depthState.StencilWriteMask; + depthBoundsTestStream.DepthStencil.FrontFace = depthState.FrontFace; + depthBoundsTestStream.DepthStencil.BackFace = depthState.BackFace; + depthBoundsTestStream.DepthStencil.DepthBoundsTestEnable = TRUE; + depthBoundsTestStream.Rasterizer = baselinePSO.GraphicsDesc.RasterizerState; + depthBoundsTestStream.InputLayout = baselinePSO.GraphicsDesc.InputLayout; + depthBoundsTestStream.RTVFormats.NumRenderTargets = baselinePSO.GraphicsDesc.NumRenderTargets; + for(int j = 0; j < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; ++j) + depthBoundsTestStream.RTVFormats.RTFormats[j] = baselinePSO.GraphicsDesc.RTVFormats[j]; + depthBoundsTestStream.DSVFormat = baselinePSO.GraphicsDesc.DSVFormat; + + // Depth bounds values are set on the command list before draw + depthBoundsTestStream.VS.BytecodeLength = vsBlob->GetBufferSize(); + depthBoundsTestStream.VS.pShaderBytecode = vsBlob->GetBufferPointer(); + depthBoundsTestStream.PS.BytecodeLength = psBlob->GetBufferSize(); + depthBoundsTestStream.PS.pShaderBytecode = psBlob->GetBufferPointer(); + hr = dev2->CreatePipelineState(&depthBoundsTestStreamDesc, __uuidof(ID3D12PipelineState), + (void **)&pass.depthBoundsPipe); + TEST_ASSERT(hr == S_OK, "Pipe created"); + + depthState.StencilEnable = FALSE; + depthState.DepthFunc = D3D12_COMPARISON_FUNC_ALWAYS; + pass.whitePipe = baselinePSO.DSV(DXGI_FORMAT_UNKNOWN); + + depthState.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL; + pass.msaaPipe = baselinePSO.PS(psMsaaBlob).DSV(depthFormat).SampleCount(4); + } + + // TODO: Additional testing: + // CS UAV usage that doesn't write, does direct write, does atomic write + // Bindless access of target resource as UAV + // SM6.6 bindless access of target resource as UAV + // Pixel history of RT with depth/stencil changing formats throughout history + // Pixel history of DSV with render target changing formats throughout history + // Several RTs bound, tracking history for pixel in various RT slots + // RTs swapping position in RT array, tracking history of a color pixel + // RT/DSV with mips/slices, getting history of pixel in non-0 mip/slice + + while(Running()) + { + ID3D12GraphicsCommandList1Ptr cmd = GetCommandBuffer(); + Reset(cmd); + + for(int i = 0; i < numPasses; ++i) + { + PassResources &pass = passes[i]; + + pushMarker(cmd, pass.markerName); + + ResourceBarrier(cmd, pass.mainDS, D3D12_RESOURCE_STATE_COMMON, + D3D12_RESOURCE_STATE_DEPTH_WRITE); + ResourceBarrier(cmd, pass.mainRT, D3D12_RESOURCE_STATE_COMMON, + D3D12_RESOURCE_STATE_RENDER_TARGET); + cmd->OMSetStencilRef(0x55); + + D3D12_VIEWPORT v; + v.TopLeftX = 10; + v.TopLeftY = 10; + v.MinDepth = 0; + v.MaxDepth = 1; + v.Width = screenWidth - 20.0f; + v.Height = screenHeight - 20.0f; + RSSetViewport(cmd, v); + + D3D12_RECT scissor; + scissor.left = scissor.top = 0; + scissor.right = screenWidth; + scissor.bottom = screenHeight; + RSSetScissorRect(cmd, scissor); + + IASetVertexBuffer(cmd, vb, sizeof(DefaultA2V), 0); + cmd->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + + float clearColor[4] = {0.2f, 0.2f, 0.2f, 1.0f}; + cmd->OMSetRenderTargets(1, &pass.mainRTV, FALSE, &pass.mainDSV); + cmd->ClearRenderTargetView(pass.mainRTV, clearColor, 0, NULL); + cmd->ClearDepthStencilView(pass.mainDSV, D3D12_CLEAR_FLAG_DEPTH | D3D12_CLEAR_FLAG_STENCIL, + 1.0f, 0, 0, NULL); + + cmd->SetGraphicsRootSignature(pass.rootSig); + + // Draw the setup triangles + + setMarker(cmd, "Depth Write"); + cmd->SetPipelineState(pass.depthWritePipe); + cmd->DrawInstanced(3, 1, 0, 0); + + setMarker(cmd, "Unbound Fragment Shader"); + cmd->SetPipelineState(pass.noPsPipe); + cmd->DrawInstanced(3, 1, 3, 0); + + setMarker(cmd, "Stencil Write"); + cmd->SetPipelineState(pass.stencilWritePipe); + cmd->DrawInstanced(3, 1, 3, 0); + + setMarker(cmd, "Background"); + cmd->SetPipelineState(pass.backgroundPipe); + cmd->DrawInstanced(3, 1, 6, 0); + + setMarker(cmd, "Cull Front"); + cmd->SetPipelineState(pass.cullFrontPipe); + cmd->DrawInstanced(3, 1, 0, 0); + + setMarker(cmd, "Depth Bounds Prep"); + cmd->SetPipelineState(pass.depthBoundsPipe); + cmd->OMSetDepthBounds(0.0f, 1.0f); + cmd->DrawInstanced(3, 1, 63, 0); + setMarker(cmd, "Depth Bounds Clip"); + cmd->OMSetDepthBounds(0.4f, 0.6f); + cmd->DrawInstanced(3, 1, 66, 0); + + // Add a marker so we can easily locate this draw + setMarker(cmd, "Test Begin"); + + cmd->SetPipelineState(pass.mainTestPipe); + cmd->DrawInstanced(24, 1, 9, 0); + + setMarker(cmd, "Fixed Scissor Fail"); + cmd->SetPipelineState(pass.dynamicScissorPipe); + D3D12_RECT testScissor; + testScissor.left = 95; + testScissor.top = 245; + testScissor.right = 99; + testScissor.bottom = 249; + RSSetScissorRect(cmd, testScissor); + cmd->DrawInstanced(3, 1, 33, 0); + + setMarker(cmd, "Fixed Scissor Pass"); + cmd->SetPipelineState(pass.dynamicScissorPipe); + testScissor.left = 95; + testScissor.top = 245; + testScissor.right = 105; + testScissor.bottom = 255; + RSSetScissorRect(cmd, testScissor); + cmd->DrawInstanced(3, 1, 36, 0); + + setMarker(cmd, "Dynamic Stencil Ref"); + cmd->SetPipelineState(pass.dynamicScissorPipe); + RSSetScissorRect(cmd, scissor); + cmd->OMSetStencilRef(0x67); + cmd->DrawInstanced(3, 1, 39, 0); + + setMarker(cmd, "Dynamic Stencil Mask"); + cmd->SetPipelineState(pass.dynamicScissorPipe); + cmd->DrawInstanced(3, 1, 42, 0); + + // Six triangles, five fragments reported. + // 0: Fails depth test + // 1: Passes + // 2: Fails depth test compared to 1st fragment + // 3: Passes + // 4: Fails depth bounds test + // 5: Fails backface culling, not reported. + setMarker(cmd, "Depth Test"); + cmd->SetPipelineState(pass.depthPipe); + cmd->OMSetDepthBounds(0.15f, 1.0f); + cmd->DrawInstanced(6 * 3, 1, 45, 0); + + { + pushMarker(cmd, "Begin MSAA"); + + ResourceBarrier(cmd, pass.msaaDS, D3D12_RESOURCE_STATE_COMMON, + D3D12_RESOURCE_STATE_DEPTH_WRITE); + ResourceBarrier(cmd, pass.msaaRT, D3D12_RESOURCE_STATE_COMMON, + D3D12_RESOURCE_STATE_RENDER_TARGET); + + cmd->OMSetRenderTargets(1, &pass.msaaRTV, FALSE, &pass.msaaDSV); + float clearColorMsaa[4] = {0.0f, 1.0f, 0.0f, 1.0f}; + cmd->ClearRenderTargetView(pass.msaaRTV, clearColorMsaa, 0, NULL); + cmd->ClearDepthStencilView( + pass.msaaDSV, D3D12_CLEAR_FLAG_DEPTH | D3D12_CLEAR_FLAG_STENCIL, 0.0f, 0, 0, NULL); + + cmd->SetPipelineState(pass.msaaPipe); + + setMarker(cmd, "Multisampled: test"); + cmd->DrawInstanced(6, 1, 3, 0); + + ResourceBarrier(cmd, pass.msaaDS, D3D12_RESOURCE_STATE_DEPTH_WRITE, + D3D12_RESOURCE_STATE_COMMON); + ResourceBarrier(cmd, pass.msaaRT, D3D12_RESOURCE_STATE_RENDER_TARGET, + D3D12_RESOURCE_STATE_COMMON); + + popMarker(cmd); + } + + v.Width = screenWidth / 4.0f - 10; + v.Height = screenHeight / 4.0f - 10; + v.TopLeftX = 5.0f; + v.TopLeftY = 5.0f; + + scissor.right = (scissor.right - scissor.left) / 4 + scissor.left; + scissor.bottom = (scissor.bottom - scissor.top) / 4 + scissor.top; + + // Render to a secondary surface + { + pushMarker(cmd, "Begin RenderPass Secondary"); + + ResourceBarrier(cmd, pass.mipArrayDS, D3D12_RESOURCE_STATE_COMMON, + D3D12_RESOURCE_STATE_DEPTH_WRITE); + ResourceBarrier(cmd, pass.mipArrayRT, D3D12_RESOURCE_STATE_COMMON, + D3D12_RESOURCE_STATE_RENDER_TARGET); + cmd->OMSetRenderTargets(1, &pass.mipArraySubRTV, FALSE, NULL); + float clearColorSecondary[4] = {0.0f, 1.0f, 0.0f, 1.0f}; + cmd->ClearRenderTargetView(pass.mipArraySubRTV, clearColorSecondary, 0, NULL); + cmd->OMSetStencilRef(0x55); + + IASetVertexBuffer(cmd, vb, sizeof(DefaultA2V), 0); + cmd->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + cmd->SetPipelineState(pass.whitePipe); + + RSSetViewport(cmd, v); + RSSetScissorRect(cmd, scissor); + + setMarker(cmd, "Secondary: background"); + cmd->DrawInstanced(6, 1, 3, 0); + setMarker(cmd, "Secondary: culled"); + cmd->DrawInstanced(6, 1, 12, 0); + setMarker(cmd, "Secondary: pink"); + cmd->DrawInstanced(9, 1, 24, 0); + setMarker(cmd, "Secondary: red and blue"); + cmd->DrawInstanced(6, 1, 0, 0); + + ResourceBarrier(cmd, pass.mipArrayDS, D3D12_RESOURCE_STATE_DEPTH_WRITE, + D3D12_RESOURCE_STATE_COMMON); + ResourceBarrier(cmd, pass.mipArrayRT, D3D12_RESOURCE_STATE_RENDER_TARGET, + D3D12_RESOURCE_STATE_COMMON); + popMarker(cmd); + } + + ResourceBarrier(cmd, pass.mainDS, D3D12_RESOURCE_STATE_DEPTH_WRITE, + D3D12_RESOURCE_STATE_COMMON); + ResourceBarrier(cmd, pass.mainRT, D3D12_RESOURCE_STATE_RENDER_TARGET, + D3D12_RESOURCE_STATE_COMMON); + + popMarker(cmd); + } + + // Now blit the main render targets to the back buffer + ID3D12ResourcePtr bb = StartUsingBackbuffer(cmd, D3D12_RESOURCE_STATE_RENDER_TARGET); + for(int i = 0; i < numPasses; ++i) + { + ResourceBarrier(cmd, passes[i].mainRT, D3D12_RESOURCE_STATE_COMMON, + D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); + + blitToSwap(cmd, passes[i].mainRT, bb, DXGI_FORMAT_R8G8B8A8_UNORM_SRGB); + + ResourceBarrier(cmd, passes[i].mainRT, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, + D3D12_RESOURCE_STATE_COMMON); + } + FinishUsingBackbuffer(cmd, D3D12_RESOURCE_STATE_RENDER_TARGET); + + cmd->Close(); + + Submit({cmd}); + Present(); + } + + return 0; + } +}; + +REGISTER_TEST(); diff --git a/util/test/demos/d3d12/d3d12_test.cpp b/util/test/demos/d3d12/d3d12_test.cpp index b084ac116..dedbf0bf7 100644 --- a/util/test/demos/d3d12/d3d12_test.cpp +++ b/util/test/demos/d3d12/d3d12_test.cpp @@ -342,14 +342,14 @@ void D3D12GraphicsTest::PostDeviceCreate() D3D12_DESCRIPTOR_HEAP_DESC desc; desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; desc.NodeMask = 1; - desc.NumDescriptors = 32; + desc.NumDescriptors = 128; desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; CHECK_HR(dev->CreateDescriptorHeap(&desc, __uuidof(ID3D12DescriptorHeap), (void **)&m_RTV)); m_RTV->SetName(L"RTV heap"); - desc.NumDescriptors = 1; + desc.NumDescriptors = 16; desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_DSV; CHECK_HR(dev->CreateDescriptorHeap(&desc, __uuidof(ID3D12DescriptorHeap), (void **)&m_DSV)); @@ -1027,7 +1027,7 @@ void D3D12GraphicsTest::popMarker(ID3D12GraphicsCommandListPtr cmd) } void D3D12GraphicsTest::blitToSwap(ID3D12GraphicsCommandListPtr cmd, ID3D12ResourcePtr src, - ID3D12ResourcePtr dst) + ID3D12ResourcePtr dst, DXGI_FORMAT srvFormat) { D3D12_CPU_DESCRIPTOR_HANDLE rtv = MakeRTV(dst).Format(DXGI_FORMAT_R8G8B8A8_UNORM_SRGB).CreateCPU(0); @@ -1040,7 +1040,11 @@ void D3D12GraphicsTest::blitToSwap(ID3D12GraphicsCommandListPtr cmd, ID3D12Resou idx++; idx %= 6; - D3D12_GPU_DESCRIPTOR_HANDLE handle = MakeSRV(src).CreateGPU(1024 + idx); + D3D12_GPU_DESCRIPTOR_HANDLE handle; + if(srvFormat == DXGI_FORMAT_UNKNOWN) + handle = MakeSRV(src).CreateGPU(1024 + idx); + else + handle = MakeSRV(src).Format(srvFormat).CreateGPU(1024 + idx); cmd->SetDescriptorHeaps(1, &m_CBVUAVSRV.GetInterfacePtr()); cmd->SetGraphicsRootDescriptorTable(0, handle); diff --git a/util/test/demos/d3d12/d3d12_test.h b/util/test/demos/d3d12/d3d12_test.h index e2c5d922e..6a9a261c8 100644 --- a/util/test/demos/d3d12/d3d12_test.h +++ b/util/test/demos/d3d12/d3d12_test.h @@ -151,7 +151,8 @@ struct D3D12GraphicsTest : public GraphicsTest void setMarker(ID3D12GraphicsCommandListPtr cmd, const std::string &name); void popMarker(ID3D12GraphicsCommandListPtr cmd); - void blitToSwap(ID3D12GraphicsCommandListPtr cmd, ID3D12ResourcePtr src, ID3D12ResourcePtr dst); + void blitToSwap(ID3D12GraphicsCommandListPtr cmd, ID3D12ResourcePtr src, ID3D12ResourcePtr dst, + DXGI_FORMAT srvFormat = DXGI_FORMAT_UNKNOWN); void ResourceBarrier(ID3D12GraphicsCommandListPtr cmd, ID3D12ResourcePtr res, D3D12_RESOURCE_STATES before, D3D12_RESOURCE_STATES after); diff --git a/util/test/demos/demos.vcxproj b/util/test/demos/demos.vcxproj index 8ab842f39..4b3ca4a97 100644 --- a/util/test/demos/demos.vcxproj +++ b/util/test/demos/demos.vcxproj @@ -202,6 +202,7 @@ + diff --git a/util/test/demos/demos.vcxproj.filters b/util/test/demos/demos.vcxproj.filters index 8bc0b96dd..234a05fef 100644 --- a/util/test/demos/demos.vcxproj.filters +++ b/util/test/demos/demos.vcxproj.filters @@ -679,6 +679,9 @@ Vulkan\demos + + D3D12\demos +