From a5583b83ee8d857e0339a09575297ddd2a73170d Mon Sep 17 00:00:00 2001 From: Steve Karolewics Date: Sun, 1 Mar 2020 16:31:25 -0800 Subject: [PATCH] Add test for MSAA evals to D3D11_Shader_Debug_Zoo --- renderdoc/driver/d3d11/d3d11_shaderdebug.cpp | 34 ++++++++----- renderdoc/driver/shaders/dxbc/dxbc_debug.cpp | 12 +++-- .../demos/d3d11/d3d11_shader_debug_zoo.cpp | 49 +++++++++++++++++++ util/test/rdtest/testcase.py | 34 +++++++++++++ .../tests/D3D11/D3D11_Shader_Debug_Zoo.py | 36 ++++++++++++++ 5 files changed, 151 insertions(+), 14 deletions(-) diff --git a/renderdoc/driver/d3d11/d3d11_shaderdebug.cpp b/renderdoc/driver/d3d11/d3d11_shaderdebug.cpp index fdb824ee0..c75a3b325 100644 --- a/renderdoc/driver/d3d11/d3d11_shaderdebug.cpp +++ b/renderdoc/driver/d3d11/d3d11_shaderdebug.cpp @@ -2707,22 +2707,34 @@ void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position, { DebugHit *hit = (DebugHit *)(initialData + i * structStride); - if(winner == NULL || (winner->sample != sample && hit->sample == sample) || - depthFunc == D3D11_COMPARISON_ALWAYS || depthFunc == D3D11_COMPARISON_NEVER || - depthFunc == D3D11_COMPARISON_NOT_EQUAL || depthFunc == D3D11_COMPARISON_EQUAL) + if(winner == NULL) { + // If we haven't picked a winner at all yet, use the first one winner = hit; evalSampleCache = ((float *)evalData) + evalSampleCacheData.size() * 4 * i; - continue; } - - if((depthFunc == D3D11_COMPARISON_LESS && hit->depth < winner->depth) || - (depthFunc == D3D11_COMPARISON_LESS_EQUAL && hit->depth <= winner->depth) || - (depthFunc == D3D11_COMPARISON_GREATER && hit->depth > winner->depth) || - (depthFunc == D3D11_COMPARISON_GREATER_EQUAL && hit->depth >= winner->depth)) + else if(hit->sample == sample) { - if(hit->sample == sample) + // If this hit is for the sample we want, check whether it's a better pick + if(winner->sample != sample) { + // The previously selected winner was for the wrong sample, use this one + winner = hit; + evalSampleCache = ((float *)evalData) + evalSampleCacheData.size() * 4 * i; + } + else if((depthFunc == D3D11_COMPARISON_ALWAYS || depthFunc == D3D11_COMPARISON_NEVER || + depthFunc == D3D11_COMPARISON_NOT_EQUAL || depthFunc == D3D11_COMPARISON_EQUAL)) + { + // For depth functions without an inequality comparison, use the last sample encountered + winner = hit; + evalSampleCache = ((float *)evalData) + evalSampleCacheData.size() * 4 * i; + } + else if((depthFunc == D3D11_COMPARISON_LESS && hit->depth < winner->depth) || + (depthFunc == D3D11_COMPARISON_LESS_EQUAL && hit->depth <= winner->depth) || + (depthFunc == D3D11_COMPARISON_GREATER && hit->depth > winner->depth) || + (depthFunc == D3D11_COMPARISON_GREATER_EQUAL && hit->depth >= winner->depth)) + { + // For depth functions with an inequality, find the hit that "wins" the most winner = hit; evalSampleCache = ((float *)evalData) + evalSampleCacheData.size() * 4 * i; } @@ -2844,7 +2856,7 @@ void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position, global.sampleEvalCache[k] = var; } - // advance past this data - always by float4 as that's the buffer st ride + // advance past this data - always by float4 as that's the buffer stride evalSampleCache += 4; } diff --git a/renderdoc/driver/shaders/dxbc/dxbc_debug.cpp b/renderdoc/driver/shaders/dxbc/dxbc_debug.cpp index 0a0d42ea7..cc88c62e5 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_debug.cpp +++ b/renderdoc/driver/shaders/dxbc/dxbc_debug.cpp @@ -310,12 +310,18 @@ bool OperationFlushing(const DXBCBytecode::OpcodeType &op) case OPCODE_GATHER4_PO_C: return true; - // unclear if these flush and it's unlikely denorms will come up, so conservatively flush - case OPCODE_SAMPLE_INFO: - case OPCODE_SAMPLE_POS: + // don't flush eval ops as some inputs may be uint case OPCODE_EVAL_CENTROID: case OPCODE_EVAL_SAMPLE_INDEX: case OPCODE_EVAL_SNAPPED: + return false; + + // don't flush samplepos since an operand is scalar + case OPCODE_SAMPLE_POS: + return false; + + // unclear if these flush and it's unlikely denorms will come up, so conservatively flush + case OPCODE_SAMPLE_INFO: case OPCODE_LOD: case OPCODE_DERIV_RTX: case OPCODE_DERIV_RTX_COARSE: diff --git a/util/test/demos/d3d11/d3d11_shader_debug_zoo.cpp b/util/test/demos/d3d11/d3d11_shader_debug_zoo.cpp index 70c6d930d..9efcb9c14 100644 --- a/util/test/demos/d3d11/d3d11_shader_debug_zoo.cpp +++ b/util/test/demos/d3d11/d3d11_shader_debug_zoo.cpp @@ -560,6 +560,31 @@ float4 main(v2f IN) : SV_Target0 return float4(0.4f, 0.4f, 0.4f, 0.4f); } +)EOSHADER"; + + std::string msaaPixel = R"EOSHADER( + +struct v2f +{ + float4 pos : SV_POSITION; + float4 col : COLOR0; + float2 uv : TEXCOORD0; +}; + +float4 main(v2f IN, uint samp : SV_SampleIndex) : SV_Target0 +{ + float2 uvCentroid = EvaluateAttributeCentroid(IN.uv); + float2 uvSamp0 = EvaluateAttributeAtSample(IN.uv, 0) - IN.uv; + float2 uvSampThis = EvaluateAttributeAtSample(IN.uv, samp) - IN.uv; + float2 uvOffset = EvaluateAttributeSnapped(IN.uv, int2(1, 1)); + + float x = (uvCentroid.x + uvCentroid.y) * 0.5f; + float y = (uvSamp0.x + uvSamp0.y) * 0.5f; + float z = (uvSampThis.x + uvSampThis.y) * 0.5f; + float w = (uvOffset.x + uvOffset.y) * 0.5f; + return float4(x, y, z, w); +} + )EOSHADER"; int main() @@ -661,6 +686,21 @@ float4 main(v2f IN) : SV_Target0 ctx->PSSetShaderResources(0, ARRAY_COUNT(srvs), srvs); + // Create resources for MSAA draw + ID3DBlobPtr vsmsaablob = Compile(D3DDefaultVertex, "main", "vs_5_0"); + ID3DBlobPtr psmsaablob = Compile(msaaPixel, "main", "ps_5_0"); + + CreateDefaultInputLayout(vsmsaablob); + + ID3D11VertexShaderPtr vsmsaa = CreateVS(vsmsaablob); + ID3D11PixelShaderPtr psmsaa = CreatePS(psmsaablob); + + ID3D11BufferPtr vbmsaa = MakeBuffer().Vertex().Data(DefaultTri); + + ID3D11Texture2DPtr msaaTex = + MakeTexture(DXGI_FORMAT_R32G32B32A32_FLOAT, 8, 8).Multisampled(4).RTV(); + ID3D11RenderTargetViewPtr msaaRT = MakeRTV(msaaTex); + while(Running()) { ClearRenderTargetView(fltRT, {0.2f, 0.2f, 0.2f, 1.0f}); @@ -684,6 +724,15 @@ float4 main(v2f IN) : SV_Target0 ctx->DrawInstanced(3, numTests, 0, 0); + ctx->OMSetRenderTargets(1, &msaaRT.GetInterfacePtr(), NULL); + + RSSetViewport({0.0f, 0.0f, 8.0f, 8.0f, 0.0f, 1.0f}); + IASetVertexBuffer(vbmsaa, sizeof(DefaultA2V), 0); + ctx->IASetInputLayout(defaultLayout); + ctx->VSSetShader(vsmsaa, NULL, 0); + ctx->PSSetShader(psmsaa, NULL, 0); + ctx->Draw(3, 0); + Present(); } diff --git a/util/test/rdtest/testcase.py b/util/test/rdtest/testcase.py index a5cf88f54..49d89d6a4 100644 --- a/util/test/rdtest/testcase.py +++ b/util/test/rdtest/testcase.py @@ -333,6 +333,40 @@ class TestCase: log.success("Picked value at {},{} in {} is as expected".format(x, y, res_details.name)) + def check_pixel_sample_value(self, tex: rd.ResourceId, x, y, sample, value, eps=util.FLT_EPSILON): + tex_details = self.get_texture(tex) + res_details = self.get_resource(tex) + + if type(x) is float: + x = int((tex_details.width-1) * x) + if type(y) is float: + y = int((tex_details.height-1) * y) + + cast = rd.CompType.Typeless + if tex_details.creationFlags & rd.TextureCategory.SwapBuffer: + cast = rd.CompType.UNormSRGB + + # Reduce epsilon for RGBA8 textures if it's not already reduced + if tex_details.format.compByteWidth == 1 and eps == util.FLT_EPSILON: + eps = (1.0 / 255.0) + + picked: rd.PixelValue = self.controller.PickPixel(tex, x, y, rd.Subresource(0, 0, sample), cast) + + if not util.value_compare(picked.floatValue, value, eps): + save_data = rd.TextureSave() + save_data.resourceId = tex + save_data.destType = rd.FileType.PNG + + img_path = util.get_tmp_path('output.png') + + self.controller.SaveTexture(save_data, img_path) + + raise TestFailureException( + "Picked value {} at {},{} sample {} doesn't match expectation of {}". + format(picked.floatValue, x, y, sample, value), img_path) + + log.success("Picked value at {},{} sample {} in {} is as expected".format(x, y, sample, res_details.name)) + def check_triangle(self, out = None, back = None, fore = None, vp = None): pipe: rd.PipeState = self.controller.GetPipelineState() diff --git a/util/test/tests/D3D11/D3D11_Shader_Debug_Zoo.py b/util/test/tests/D3D11/D3D11_Shader_Debug_Zoo.py index e3ff75ffb..c60f2a377 100644 --- a/util/test/tests/D3D11/D3D11_Shader_Debug_Zoo.py +++ b/util/test/tests/D3D11/D3D11_Shader_Debug_Zoo.py @@ -17,6 +17,8 @@ class D3D11_Shader_Debug_Zoo(rdtest.TestCase): failed = False # Loop over every test + rdtest.log.print("Performing general tests:") + rdtest.log.indent() for test in range(draw.numInstances): # Debug the shader trace: rd.ShaderDebugTrace = self.controller.DebugPixel(4 * test, 0, rd.ReplayController.NoPreference, @@ -38,6 +40,40 @@ class D3D11_Shader_Debug_Zoo(rdtest.TestCase): self.controller.FreeTrace(trace) rdtest.log.success("Test {} matched as expected".format(test)) + rdtest.log.dedent() + + rdtest.log.print("Performing MSAA tests:") + rdtest.log.indent() + draw = draw.next + self.controller.SetFrameEvent(draw.eventId, False) + pipe: rd.PipeState = self.controller.GetPipelineState() + for test in range(4): + # Debug the shader + trace: rd.ShaderDebugTrace = self.controller.DebugPixel(4, 4, test, + rd.ReplayController.NoPreference) + + # Validate that the correct sample index was debugged + inputs: List[rd.ShaderVariable] = list(trace.inputs) + sampRegister = self.find_input_source_var(trace, rd.ShaderBuiltin.MSAASampleIndex) + sampInput = [var for var in inputs if var.name == sampRegister.variables[0].name][0] + if sampInput.value.uv[0] != test: + rdtest.log.error("Test {} did not pick the correct sample.".format(test)) + + cycles, variables = self.process_trace(trace) + + output = self.find_output_source_var(trace, rd.ShaderBuiltin.ColorOutput, 0) + + debugged = self.evalute_source_var(output, variables) + + # Validate the debug output result + try: + self.check_pixel_sample_value(pipe.GetOutputTargets()[0].resourceId, 4, 4, test, debugged.value.fv[0:4], 0.0) + except rdtest.TestFailureException as ex: + failed = True + rdtest.log.error("Test {} did not match. {}".format(test, str(ex))) + continue + + rdtest.log.dedent() if failed: raise rdtest.TestFailureException("Some tests were not as expected")