diff --git a/renderdoc/driver/d3d12/d3d12_shaderdebug.cpp b/renderdoc/driver/d3d12/d3d12_shaderdebug.cpp index 893e85ce5..94bb191e4 100644 --- a/renderdoc/driver/d3d12/d3d12_shaderdebug.cpp +++ b/renderdoc/driver/d3d12/d3d12_shaderdebug.cpp @@ -1591,266 +1591,275 @@ ShaderDebugTrace *D3D12Replay::DebugVertex(uint32_t eventId, uint32_t vertid, ui } } - InterpretDebugger *interpreter = new InterpretDebugger; - interpreter->eventId = eventId; - ShaderDebugTrace *ret = interpreter->BeginDebug(dxbc, refl, 0); - GlobalState &global = interpreter->global; - ThreadState &state = interpreter->activeLane(); - - // Fetch constant buffer data from root signature - GatherConstantBuffers(m_pDevice, *dxbc->GetDXBCByteCode(), rs.graphics, refl, global, - ret->sourceVars); - - for(size_t i = 0; i < state.inputs.size(); i++) + ShaderDebugTrace *ret = NULL; + if(dxbc->GetDXBCByteCode()) { - if(dxbc->GetReflection()->InputSig[i].systemValue == ShaderBuiltin::Undefined || - dxbc->GetReflection()->InputSig[i].systemValue == - ShaderBuiltin::Position) // SV_Position seems to get promoted - // automatically, but it's invalid for - // vertex input + InterpretDebugger *interpreter = new InterpretDebugger; + interpreter->eventId = eventId; + ret = interpreter->BeginDebug(dxbc, refl, 0); + GlobalState &global = interpreter->global; + ThreadState &state = interpreter->activeLane(); + + // Fetch constant buffer data from root signature + GatherConstantBuffers(m_pDevice, *dxbc->GetDXBCByteCode(), rs.graphics, refl, global, + ret->sourceVars); + + for(size_t i = 0; i < state.inputs.size(); i++) { - const D3D12_INPUT_ELEMENT_DESC *el = NULL; - - rdcstr signame = strlower(dxbc->GetReflection()->InputSig[i].semanticName); - - for(size_t l = 0; l < inputlayout.size(); l++) + if(dxbc->GetReflection()->InputSig[i].systemValue == ShaderBuiltin::Undefined || + dxbc->GetReflection()->InputSig[i].systemValue == + ShaderBuiltin::Position) // SV_Position seems to get promoted + // automatically, but it's invalid for + // vertex input { - rdcstr layoutname = strlower(inputlayout[l].SemanticName); + const D3D12_INPUT_ELEMENT_DESC *el = NULL; - if(signame == layoutname && - dxbc->GetReflection()->InputSig[i].semanticIndex == inputlayout[l].SemanticIndex) + rdcstr signame = strlower(dxbc->GetReflection()->InputSig[i].semanticName); + + for(size_t l = 0; l < inputlayout.size(); l++) { - el = &inputlayout[l]; - break; - } - if(signame == layoutname + ToStr(inputlayout[l].SemanticIndex)) - { - el = &inputlayout[l]; - break; - } - } + rdcstr layoutname = strlower(inputlayout[l].SemanticName); - RDCASSERT(el); - - if(!el) - continue; - - byte *srcData = NULL; - size_t dataSize = 0; - - if(el->InputSlotClass == D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA) - { - if(vertData[el->InputSlot].size() >= el->AlignedByteOffset) - { - srcData = &vertData[el->InputSlot][el->AlignedByteOffset]; - dataSize = vertData[el->InputSlot].size() - el->AlignedByteOffset; - } - } - else - { - if(el->InstanceDataStepRate == 0 || el->InstanceDataStepRate >= action->numInstances) - { - if(staticData[el->InputSlot].size() >= el->AlignedByteOffset) + if(signame == layoutname && + dxbc->GetReflection()->InputSig[i].semanticIndex == inputlayout[l].SemanticIndex) { - srcData = &staticData[el->InputSlot][el->AlignedByteOffset]; - dataSize = staticData[el->InputSlot].size() - el->AlignedByteOffset; + el = &inputlayout[l]; + break; + } + if(signame == layoutname + ToStr(inputlayout[l].SemanticIndex)) + { + el = &inputlayout[l]; + break; + } + } + + RDCASSERT(el); + + if(!el) + continue; + + byte *srcData = NULL; + size_t dataSize = 0; + + if(el->InputSlotClass == D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA) + { + if(vertData[el->InputSlot].size() >= el->AlignedByteOffset) + { + srcData = &vertData[el->InputSlot][el->AlignedByteOffset]; + dataSize = vertData[el->InputSlot].size() - el->AlignedByteOffset; } } else { - UINT isrIdx = el->InputSlot * MaxStepRate + (el->InstanceDataStepRate - 1); - if(instData[isrIdx].size() >= el->AlignedByteOffset) + if(el->InstanceDataStepRate == 0 || el->InstanceDataStepRate >= action->numInstances) { - srcData = &instData[isrIdx][el->AlignedByteOffset]; - dataSize = instData[isrIdx].size() - el->AlignedByteOffset; - } - } - } - - ResourceFormat fmt = MakeResourceFormat(el->Format); - - // more data needed than is provided - if(dxbc->GetReflection()->InputSig[i].compCount > fmt.compCount) - { - state.inputs[i].value.u32v[3] = 1; - - if(fmt.compType == CompType::Float) - state.inputs[i].value.f32v[3] = 1.0f; - } - - // interpret resource format types - if(fmt.Special()) - { - Vec3f *v3 = (Vec3f *)state.inputs[i].value.f32v.data(); - Vec4f *v4 = (Vec4f *)state.inputs[i].value.f32v.data(); - - // only pull in all or nothing from these, - // if there's only e.g. 3 bytes remaining don't read and unpack some of - // a 4-byte resource format type - size_t packedsize = 4; - if(fmt.type == ResourceFormatType::R5G5B5A1 || fmt.type == ResourceFormatType::R5G6B5 || - fmt.type == ResourceFormatType::R4G4B4A4) - packedsize = 2; - - if(srcData == NULL || packedsize > dataSize) - { - state.inputs[i].value.u32v[0] = state.inputs[i].value.u32v[1] = - state.inputs[i].value.u32v[2] = state.inputs[i].value.u32v[3] = 0; - } - else if(fmt.type == ResourceFormatType::R5G5B5A1) - { - RDCASSERT(fmt.BGRAOrder()); - uint16_t packed = ((uint16_t *)srcData)[0]; - *v4 = ConvertFromB5G5R5A1(packed); - } - else if(fmt.type == ResourceFormatType::R5G6B5) - { - RDCASSERT(fmt.BGRAOrder()); - uint16_t packed = ((uint16_t *)srcData)[0]; - *v3 = ConvertFromB5G6R5(packed); - } - else if(fmt.type == ResourceFormatType::R4G4B4A4) - { - RDCASSERT(fmt.BGRAOrder()); - uint16_t packed = ((uint16_t *)srcData)[0]; - *v4 = ConvertFromB4G4R4A4(packed); - } - else if(fmt.type == ResourceFormatType::R10G10B10A2) - { - uint32_t packed = ((uint32_t *)srcData)[0]; - - if(fmt.compType == CompType::UInt) - { - state.inputs[i].value.u32v[2] = (packed >> 0) & 0x3ff; - state.inputs[i].value.u32v[1] = (packed >> 10) & 0x3ff; - state.inputs[i].value.u32v[0] = (packed >> 20) & 0x3ff; - state.inputs[i].value.u32v[3] = (packed >> 30) & 0x003; + if(staticData[el->InputSlot].size() >= el->AlignedByteOffset) + { + srcData = &staticData[el->InputSlot][el->AlignedByteOffset]; + dataSize = staticData[el->InputSlot].size() - el->AlignedByteOffset; + } } else { - *v4 = ConvertFromR10G10B10A2(packed); + UINT isrIdx = el->InputSlot * MaxStepRate + (el->InstanceDataStepRate - 1); + if(instData[isrIdx].size() >= el->AlignedByteOffset) + { + srcData = &instData[isrIdx][el->AlignedByteOffset]; + dataSize = instData[isrIdx].size() - el->AlignedByteOffset; + } } } - else if(fmt.type == ResourceFormatType::R11G11B10) + + ResourceFormat fmt = MakeResourceFormat(el->Format); + + // more data needed than is provided + if(dxbc->GetReflection()->InputSig[i].compCount > fmt.compCount) { - uint32_t packed = ((uint32_t *)srcData)[0]; - *v3 = ConvertFromR11G11B10(packed); + state.inputs[i].value.u32v[3] = 1; + + if(fmt.compType == CompType::Float) + state.inputs[i].value.f32v[3] = 1.0f; } + + // interpret resource format types + if(fmt.Special()) + { + Vec3f *v3 = (Vec3f *)state.inputs[i].value.f32v.data(); + Vec4f *v4 = (Vec4f *)state.inputs[i].value.f32v.data(); + + // only pull in all or nothing from these, + // if there's only e.g. 3 bytes remaining don't read and unpack some of + // a 4-byte resource format type + size_t packedsize = 4; + if(fmt.type == ResourceFormatType::R5G5B5A1 || fmt.type == ResourceFormatType::R5G6B5 || + fmt.type == ResourceFormatType::R4G4B4A4) + packedsize = 2; + + if(srcData == NULL || packedsize > dataSize) + { + state.inputs[i].value.u32v[0] = state.inputs[i].value.u32v[1] = + state.inputs[i].value.u32v[2] = state.inputs[i].value.u32v[3] = 0; + } + else if(fmt.type == ResourceFormatType::R5G5B5A1) + { + RDCASSERT(fmt.BGRAOrder()); + uint16_t packed = ((uint16_t *)srcData)[0]; + *v4 = ConvertFromB5G5R5A1(packed); + } + else if(fmt.type == ResourceFormatType::R5G6B5) + { + RDCASSERT(fmt.BGRAOrder()); + uint16_t packed = ((uint16_t *)srcData)[0]; + *v3 = ConvertFromB5G6R5(packed); + } + else if(fmt.type == ResourceFormatType::R4G4B4A4) + { + RDCASSERT(fmt.BGRAOrder()); + uint16_t packed = ((uint16_t *)srcData)[0]; + *v4 = ConvertFromB4G4R4A4(packed); + } + else if(fmt.type == ResourceFormatType::R10G10B10A2) + { + uint32_t packed = ((uint32_t *)srcData)[0]; + + if(fmt.compType == CompType::UInt) + { + state.inputs[i].value.u32v[2] = (packed >> 0) & 0x3ff; + state.inputs[i].value.u32v[1] = (packed >> 10) & 0x3ff; + state.inputs[i].value.u32v[0] = (packed >> 20) & 0x3ff; + state.inputs[i].value.u32v[3] = (packed >> 30) & 0x003; + } + else + { + *v4 = ConvertFromR10G10B10A2(packed); + } + } + else if(fmt.type == ResourceFormatType::R11G11B10) + { + uint32_t packed = ((uint32_t *)srcData)[0]; + *v3 = ConvertFromR11G11B10(packed); + } + } + else + { + for(uint32_t c = 0; c < fmt.compCount; c++) + { + if(srcData == NULL || fmt.compByteWidth > dataSize) + { + state.inputs[i].value.u32v[c] = 0; + continue; + } + + dataSize -= fmt.compByteWidth; + + if(fmt.compByteWidth == 1) + { + byte *src = srcData + c * fmt.compByteWidth; + + if(fmt.compType == CompType::UInt) + state.inputs[i].value.u32v[c] = *src; + else if(fmt.compType == CompType::SInt) + state.inputs[i].value.s32v[c] = *((int8_t *)src); + else if(fmt.compType == CompType::UNorm || fmt.compType == CompType::UNormSRGB) + state.inputs[i].value.f32v[c] = float(*src) / 255.0f; + else if(fmt.compType == CompType::SNorm) + { + signed char *schar = (signed char *)src; + + // -128 is mapped to -1, then -127 to -127 are mapped to -1 to 1 + if(*schar == -128) + state.inputs[i].value.f32v[c] = -1.0f; + else + state.inputs[i].value.f32v[c] = float(*schar) / 127.0f; + } + else + RDCERR("Unexpected component type"); + } + else if(fmt.compByteWidth == 2) + { + uint16_t *src = (uint16_t *)(srcData + c * fmt.compByteWidth); + + if(fmt.compType == CompType::Float) + state.inputs[i].value.f32v[c] = ConvertFromHalf(*src); + else if(fmt.compType == CompType::UInt) + state.inputs[i].value.u32v[c] = *src; + else if(fmt.compType == CompType::SInt) + state.inputs[i].value.s32v[c] = *((int16_t *)src); + else if(fmt.compType == CompType::UNorm || fmt.compType == CompType::UNormSRGB) + state.inputs[i].value.f32v[c] = float(*src) / float(UINT16_MAX); + else if(fmt.compType == CompType::SNorm) + { + int16_t *sint = (int16_t *)src; + + // -32768 is mapped to -1, then -32767 to -32767 are mapped to -1 to 1 + if(*sint == -32768) + state.inputs[i].value.f32v[c] = -1.0f; + else + state.inputs[i].value.f32v[c] = float(*sint) / 32767.0f; + } + else + RDCERR("Unexpected component type"); + } + else if(fmt.compByteWidth == 4) + { + uint32_t *src = (uint32_t *)(srcData + c * fmt.compByteWidth); + + if(fmt.compType == CompType::Float || fmt.compType == CompType::UInt || + fmt.compType == CompType::SInt) + memcpy(&state.inputs[i].value.u32v[c], src, 4); + else + RDCERR("Unexpected component type"); + } + } + + if(fmt.BGRAOrder()) + { + RDCASSERT(fmt.compCount == 4); + std::swap(state.inputs[i].value.f32v[2], state.inputs[i].value.f32v[0]); + } + } + } + else if(dxbc->GetReflection()->InputSig[i].systemValue == ShaderBuiltin::VertexIndex) + { + uint32_t sv_vertid = vertid; + + if(action->flags & ActionFlags::Indexed) + sv_vertid = idx - action->baseVertex; + + if(dxbc->GetReflection()->InputSig[i].varType == VarType::Float) + state.inputs[i].value.f32v[0] = state.inputs[i].value.f32v[1] = + state.inputs[i].value.f32v[2] = state.inputs[i].value.f32v[3] = (float)sv_vertid; + else + state.inputs[i].value.u32v[0] = state.inputs[i].value.u32v[1] = + state.inputs[i].value.u32v[2] = state.inputs[i].value.u32v[3] = sv_vertid; + } + else if(dxbc->GetReflection()->InputSig[i].systemValue == ShaderBuiltin::InstanceIndex) + { + if(dxbc->GetReflection()->InputSig[i].varType == VarType::Float) + state.inputs[i].value.f32v[0] = state.inputs[i].value.f32v[1] = + state.inputs[i].value.f32v[2] = state.inputs[i].value.f32v[3] = (float)instid; + else + state.inputs[i].value.u32v[0] = state.inputs[i].value.u32v[1] = + state.inputs[i].value.u32v[2] = state.inputs[i].value.u32v[3] = instid; } else { - for(uint32_t c = 0; c < fmt.compCount; c++) - { - if(srcData == NULL || fmt.compByteWidth > dataSize) - { - state.inputs[i].value.u32v[c] = 0; - continue; - } - - dataSize -= fmt.compByteWidth; - - if(fmt.compByteWidth == 1) - { - byte *src = srcData + c * fmt.compByteWidth; - - if(fmt.compType == CompType::UInt) - state.inputs[i].value.u32v[c] = *src; - else if(fmt.compType == CompType::SInt) - state.inputs[i].value.s32v[c] = *((int8_t *)src); - else if(fmt.compType == CompType::UNorm || fmt.compType == CompType::UNormSRGB) - state.inputs[i].value.f32v[c] = float(*src) / 255.0f; - else if(fmt.compType == CompType::SNorm) - { - signed char *schar = (signed char *)src; - - // -128 is mapped to -1, then -127 to -127 are mapped to -1 to 1 - if(*schar == -128) - state.inputs[i].value.f32v[c] = -1.0f; - else - state.inputs[i].value.f32v[c] = float(*schar) / 127.0f; - } - else - RDCERR("Unexpected component type"); - } - else if(fmt.compByteWidth == 2) - { - uint16_t *src = (uint16_t *)(srcData + c * fmt.compByteWidth); - - if(fmt.compType == CompType::Float) - state.inputs[i].value.f32v[c] = ConvertFromHalf(*src); - else if(fmt.compType == CompType::UInt) - state.inputs[i].value.u32v[c] = *src; - else if(fmt.compType == CompType::SInt) - state.inputs[i].value.s32v[c] = *((int16_t *)src); - else if(fmt.compType == CompType::UNorm || fmt.compType == CompType::UNormSRGB) - state.inputs[i].value.f32v[c] = float(*src) / float(UINT16_MAX); - else if(fmt.compType == CompType::SNorm) - { - int16_t *sint = (int16_t *)src; - - // -32768 is mapped to -1, then -32767 to -32767 are mapped to -1 to 1 - if(*sint == -32768) - state.inputs[i].value.f32v[c] = -1.0f; - else - state.inputs[i].value.f32v[c] = float(*sint) / 32767.0f; - } - else - RDCERR("Unexpected component type"); - } - else if(fmt.compByteWidth == 4) - { - uint32_t *src = (uint32_t *)(srcData + c * fmt.compByteWidth); - - if(fmt.compType == CompType::Float || fmt.compType == CompType::UInt || - fmt.compType == CompType::SInt) - memcpy(&state.inputs[i].value.u32v[c], src, 4); - else - RDCERR("Unexpected component type"); - } - } - - if(fmt.BGRAOrder()) - { - RDCASSERT(fmt.compCount == 4); - std::swap(state.inputs[i].value.f32v[2], state.inputs[i].value.f32v[0]); - } + RDCERR("Unhandled system value semantic on VS input"); } } - else if(dxbc->GetReflection()->InputSig[i].systemValue == ShaderBuiltin::VertexIndex) - { - uint32_t sv_vertid = vertid; - if(action->flags & ActionFlags::Indexed) - sv_vertid = idx - action->baseVertex; + ret->constantBlocks = global.constantBlocks; + ret->inputs = state.inputs; - if(dxbc->GetReflection()->InputSig[i].varType == VarType::Float) - state.inputs[i].value.f32v[0] = state.inputs[i].value.f32v[1] = - state.inputs[i].value.f32v[2] = state.inputs[i].value.f32v[3] = (float)sv_vertid; - else - state.inputs[i].value.u32v[0] = state.inputs[i].value.u32v[1] = - state.inputs[i].value.u32v[2] = state.inputs[i].value.u32v[3] = sv_vertid; - } - else if(dxbc->GetReflection()->InputSig[i].systemValue == ShaderBuiltin::InstanceIndex) - { - if(dxbc->GetReflection()->InputSig[i].varType == VarType::Float) - state.inputs[i].value.f32v[0] = state.inputs[i].value.f32v[1] = - state.inputs[i].value.f32v[2] = state.inputs[i].value.f32v[3] = (float)instid; - else - state.inputs[i].value.u32v[0] = state.inputs[i].value.u32v[1] = - state.inputs[i].value.u32v[2] = state.inputs[i].value.u32v[3] = instid; - } - else - { - RDCERR("Unhandled system value semantic on VS input"); - } + delete[] instData; + } + else + { + RDCERR("TODO ADD DXIL VERTEX SHADER DEBUGGER SUPPORT"); } - ret->constantBlocks = global.constantBlocks; - ret->inputs = state.inputs; - - delete[] instData; - - dxbc->FillTraceLineInfo(*ret); + if(ret) + dxbc->FillTraceLineInfo(*ret); return ret; } @@ -1904,152 +1913,155 @@ ShaderDebugTrace *D3D12Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t dxbc->GetDisassembly(false); - // Fetch the previous stage's disassembly, to match outputs to PS inputs - DXBCContainer *prevDxbc = NULL; - // Check for geometry shader first + ShaderDebugTrace *ret = NULL; + if(dxbc->GetDXBCByteCode()) { - WrappedID3D12Shader *gs = (WrappedID3D12Shader *)pso->graphics->GS.pShaderBytecode; - if(gs) - prevDxbc = gs->GetDXBC(); - } - // Check for domain shader next - if(prevDxbc == NULL) - { - WrappedID3D12Shader *ds = (WrappedID3D12Shader *)pso->graphics->DS.pShaderBytecode; - if(ds) - prevDxbc = ds->GetDXBC(); - } - // Check for vertex shader last - if(prevDxbc == NULL) - { - WrappedID3D12Shader *vs = (WrappedID3D12Shader *)pso->graphics->VS.pShaderBytecode; - if(vs) - prevDxbc = vs->GetDXBC(); - } - - rdcarray initialValues; - rdcarray floatInputs; - rdcarray inputVarNames; - rdcstr extractHlsl; - int structureStride = 0; - - DXBCDebug::GatherPSInputDataForInitialValues(dxbc, *prevDxbc->GetReflection(), initialValues, - floatInputs, inputVarNames, extractHlsl, - structureStride); - - uint32_t overdrawLevels = 100; // maximum number of overdraw levels - - // If the pipe contains a geometry shader, then SV_PrimitiveID cannot be used in the pixel - // shader without being emitted from the geometry shader. For now, check if this semantic - // will succeed in a new pixel shader with the rest of the pipe unchanged - bool usePrimitiveID = (prevDxbc->m_Type != ShaderType::Geometry); - for(const PSInputElement &e : initialValues) - { - if(e.sysattribute == ShaderBuiltin::PrimitiveIndex) + // Fetch the previous stage's disassembly, to match outputs to PS inputs + DXBCContainer *prevDxbc = NULL; + // Check for geometry shader first { - usePrimitiveID = true; - break; + WrappedID3D12Shader *gs = (WrappedID3D12Shader *)pso->graphics->GS.pShaderBytecode; + if(gs) + prevDxbc = gs->GetDXBC(); } - } - - // Store a copy of the event's render state to restore later - D3D12RenderState prevState = rs; - - // Fetch the multisample count from the PSO - WrappedID3D12PipelineState *origPSO = - m_pDevice->GetResourceManager()->GetCurrentAs(rs.pipe); - - D3D12_EXPANDED_PIPELINE_STATE_STREAM_DESC pipeDesc; - origPSO->Fill(pipeDesc); - uint32_t outputSampleCount = RDCMAX(1U, pipeDesc.SampleDesc.Count); - - std::set evalSampleCacheData; - uint64_t sampleEvalRegisterMask = 0; - - // if we're not rendering at MSAA, no need to fill the cache because evaluates will all return the - // plain input anyway. - if(outputSampleCount > 1) - { - // scan the instructions to see if it contains any evaluates. - size_t numInstructions = dxbc->GetDXBCByteCode()->GetNumInstructions(); - for(size_t i = 0; i < numInstructions; ++i) + // Check for domain shader next + if(prevDxbc == NULL) { - const Operation &op = dxbc->GetDXBCByteCode()->GetInstruction(i); + WrappedID3D12Shader *ds = (WrappedID3D12Shader *)pso->graphics->DS.pShaderBytecode; + if(ds) + prevDxbc = ds->GetDXBC(); + } + // Check for vertex shader last + if(prevDxbc == NULL) + { + WrappedID3D12Shader *vs = (WrappedID3D12Shader *)pso->graphics->VS.pShaderBytecode; + if(vs) + prevDxbc = vs->GetDXBC(); + } - // skip any non-eval opcodes - if(op.operation != OPCODE_EVAL_CENTROID && op.operation != OPCODE_EVAL_SAMPLE_INDEX && - op.operation != OPCODE_EVAL_SNAPPED) - continue; + rdcarray initialValues; + rdcarray floatInputs; + rdcarray inputVarNames; + rdcstr extractHlsl; + int structureStride = 0; - // the generation of this key must match what we'll generate in the corresponding lookup - GlobalState::SampleEvalCacheKey key; + DXBCDebug::GatherPSInputDataForInitialValues(dxbc, *prevDxbc->GetReflection(), initialValues, + floatInputs, inputVarNames, extractHlsl, + structureStride); - // all the eval opcodes have rDst, vIn as the first two operands - key.inputRegisterIndex = (int32_t)op.operands[1].indices[0].index; + uint32_t overdrawLevels = 100; // maximum number of overdraw levels - for(int c = 0; c < 4; c++) + // If the pipe contains a geometry shader, then SV_PrimitiveID cannot be used in the pixel + // shader without being emitted from the geometry shader. For now, check if this semantic + // will succeed in a new pixel shader with the rest of the pipe unchanged + bool usePrimitiveID = (prevDxbc->m_Type != ShaderType::Geometry); + for(const PSInputElement &e : initialValues) + { + if(e.sysattribute == ShaderBuiltin::PrimitiveIndex) { - if(op.operands[0].comps[c] == 0xff) - break; - - key.numComponents = c + 1; + usePrimitiveID = true; + break; } + } - key.firstComponent = op.operands[1].comps[op.operands[0].comps[0]]; + // Store a copy of the event's render state to restore later + D3D12RenderState prevState = rs; - sampleEvalRegisterMask |= 1ULL << key.inputRegisterIndex; + // Fetch the multisample count from the PSO + WrappedID3D12PipelineState *origPSO = + m_pDevice->GetResourceManager()->GetCurrentAs(rs.pipe); - if(op.operation == OPCODE_EVAL_CENTROID) + D3D12_EXPANDED_PIPELINE_STATE_STREAM_DESC pipeDesc; + origPSO->Fill(pipeDesc); + uint32_t outputSampleCount = RDCMAX(1U, pipeDesc.SampleDesc.Count); + + std::set evalSampleCacheData; + uint64_t sampleEvalRegisterMask = 0; + + // if we're not rendering at MSAA, no need to fill the cache because evaluates will all return + // the plain input anyway. + if(outputSampleCount > 1) + { + // scan the instructions to see if it contains any evaluates. + size_t numInstructions = dxbc->GetDXBCByteCode()->GetNumInstructions(); + for(size_t i = 0; i < numInstructions; ++i) { - // nothing to do - default key is centroid, sample is -1 and offset x/y is 0 - evalSampleCacheData.insert(key); - } - else if(op.operation == OPCODE_EVAL_SAMPLE_INDEX) - { - if(op.operands[2].type == TYPE_IMMEDIATE32 || op.operands[2].type == TYPE_IMMEDIATE64) + const Operation &op = dxbc->GetDXBCByteCode()->GetInstruction(i); + + // skip any non-eval opcodes + if(op.operation != OPCODE_EVAL_CENTROID && op.operation != OPCODE_EVAL_SAMPLE_INDEX && + op.operation != OPCODE_EVAL_SNAPPED) + continue; + + // the generation of this key must match what we'll generate in the corresponding lookup + GlobalState::SampleEvalCacheKey key; + + // all the eval opcodes have rDst, vIn as the first two operands + key.inputRegisterIndex = (int32_t)op.operands[1].indices[0].index; + + for(int c = 0; c < 4; c++) { - // hooray, only sampling a single index, just add this key - key.sample = (int32_t)op.operands[2].values[0]; + if(op.operands[0].comps[c] == 0xff) + break; + key.numComponents = c + 1; + } + + key.firstComponent = op.operands[1].comps[op.operands[0].comps[0]]; + + sampleEvalRegisterMask |= 1ULL << key.inputRegisterIndex; + + if(op.operation == OPCODE_EVAL_CENTROID) + { + // nothing to do - default key is centroid, sample is -1 and offset x/y is 0 evalSampleCacheData.insert(key); } - else + else if(op.operation == OPCODE_EVAL_SAMPLE_INDEX) { - // parameter is a register and we don't know which sample will be needed, fetch them all. - // In most cases this will be a loop over them all, so they'll all be needed anyway - for(uint32_t c = 0; c < outputSampleCount; c++) + if(op.operands[2].type == TYPE_IMMEDIATE32 || op.operands[2].type == TYPE_IMMEDIATE64) { - key.sample = (int32_t)c; + // hooray, only sampling a single index, just add this key + key.sample = (int32_t)op.operands[2].values[0]; + evalSampleCacheData.insert(key); } + else + { + // parameter is a register and we don't know which sample will be needed, fetch them + // all. In most cases this will be a loop over them all, so they'll all be needed anyway + for(uint32_t c = 0; c < outputSampleCount; c++) + { + key.sample = (int32_t)c; + evalSampleCacheData.insert(key); + } + } + } + else if(op.operation == OPCODE_EVAL_SNAPPED) + { + if(op.operands[2].type == TYPE_IMMEDIATE32 || op.operands[2].type == TYPE_IMMEDIATE64) + { + // hooray, only sampling a single offset, just add this key + key.offsetx = (int32_t)op.operands[2].values[0]; + key.offsety = (int32_t)op.operands[2].values[1]; + + evalSampleCacheData.insert(key); + } + else + { + m_pDevice->AddDebugMessage( + MessageCategory::Shaders, MessageSeverity::Medium, MessageSource::RuntimeWarning, + "EvaluateAttributeSnapped called with dynamic parameter, caching all possible " + "evaluations which could have performance impact."); + + for(key.offsetx = -8; key.offsetx <= 7; key.offsetx++) + for(key.offsety = -8; key.offsety <= 7; key.offsety++) + evalSampleCacheData.insert(key); + } } } - else if(op.operation == OPCODE_EVAL_SNAPPED) - { - if(op.operands[2].type == TYPE_IMMEDIATE32 || op.operands[2].type == TYPE_IMMEDIATE64) - { - // hooray, only sampling a single offset, just add this key - key.offsetx = (int32_t)op.operands[2].values[0]; - key.offsety = (int32_t)op.operands[2].values[1]; - - evalSampleCacheData.insert(key); - } - else - { - m_pDevice->AddDebugMessage( - MessageCategory::Shaders, MessageSeverity::Medium, MessageSource::RuntimeWarning, - "EvaluateAttributeSnapped called with dynamic parameter, caching all possible " - "evaluations which could have performance impact."); - - for(key.offsetx = -8; key.offsetx <= 7; key.offsetx++) - for(key.offsety = -8; key.offsety <= 7; key.offsety++) - evalSampleCacheData.insert(key); - } - } } - } - extractHlsl += R"( + extractHlsl += R"( struct PSInitialData { // metadata we need ourselves @@ -2071,30 +2083,31 @@ struct PSInitialData )"; - WrappedID3D12RootSignature *sig = - m_pDevice->GetResourceManager()->GetCurrentAs(rs.graphics.rootsig); + WrappedID3D12RootSignature *sig = + m_pDevice->GetResourceManager()->GetCurrentAs(rs.graphics.rootsig); - // Need to be able to add a descriptor table with our UAV without hitting the 64 DWORD limit - RDCASSERT(sig->sig.dwordLength < 64); - D3D12RootSignature modsig = sig->sig; + // Need to be able to add a descriptor table with our UAV without hitting the 64 DWORD limit + RDCASSERT(sig->sig.dwordLength < 64); + D3D12RootSignature modsig = sig->sig; - UINT regSpace = GetFreeRegSpace(modsig, 0, D3D12DescriptorType::UAV, D3D12_SHADER_VISIBILITY_PIXEL); + UINT regSpace = + GetFreeRegSpace(modsig, 0, D3D12DescriptorType::UAV, D3D12_SHADER_VISIBILITY_PIXEL); - // If this event uses MSAA, then at least one render target must be preserved to get - // multisampling info. leave u0 alone and start with register u1 - extractHlsl += StringFormat::Fmt( - "RWStructuredBuffer PSInitialBuffer : register(u1, space%u);\n\n", regSpace); + // If this event uses MSAA, then at least one render target must be preserved to get + // multisampling info. leave u0 alone and start with register u1 + extractHlsl += StringFormat::Fmt( + "RWStructuredBuffer PSInitialBuffer : register(u1, space%u);\n\n", regSpace); - if(!evalSampleCacheData.empty()) - { - // float4 is wasteful in some cases but it's easier than using byte buffers and manual packing - extractHlsl += - StringFormat::Fmt("RWBuffer PSEvalBuffer : register(u2, space%u);\n\n", regSpace); - } + if(!evalSampleCacheData.empty()) + { + // float4 is wasteful in some cases but it's easier than using byte buffers and manual packing + extractHlsl += + StringFormat::Fmt("RWBuffer PSEvalBuffer : register(u2, space%u);\n\n", regSpace); + } - if(usePrimitiveID) - { - extractHlsl += R"( + if(usePrimitiveID) + { + extractHlsl += R"( void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position, uint prim : SV_PrimitiveID, @@ -2103,10 +2116,10 @@ void ExtractInputsPS(PSInput IN, uint covge : SV_Coverage) { )"; - } - else - { - extractHlsl += R"( + } + else + { + extractHlsl += R"( void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position, uint fface : SV_IsFrontFace, @@ -2114,573 +2127,580 @@ void ExtractInputsPS(PSInput IN, uint covge : SV_Coverage) { )"; - } - - extractHlsl += " uint idx = " + ToStr(overdrawLevels) + ";\n"; - extractHlsl += StringFormat::Fmt( - " if(abs(debug_pixelPos.x - %u.5) < 0.5f && abs(debug_pixelPos.y - %u.5) < 0.5f)\n", x, y); - extractHlsl += " InterlockedAdd(PSInitialBuffer[0].hit, 1, idx);\n\n"; - extractHlsl += " idx = min(idx, " + ToStr(overdrawLevels) + ");\n\n"; - extractHlsl += " PSInitialBuffer[idx].pos = debug_pixelPos.xyz;\n"; - - if(usePrimitiveID) - extractHlsl += " PSInitialBuffer[idx].prim = prim;\n"; - else - extractHlsl += " PSInitialBuffer[idx].prim = 0;\n"; - - extractHlsl += " PSInitialBuffer[idx].fface = fface;\n"; - extractHlsl += " PSInitialBuffer[idx].covge = covge;\n"; - extractHlsl += " PSInitialBuffer[idx].sample = sample;\n"; - extractHlsl += " PSInitialBuffer[idx].IN = IN;\n"; - extractHlsl += " PSInitialBuffer[idx].derivValid = ddx(debug_pixelPos.x);\n"; - extractHlsl += " PSInitialBuffer[idx].INddx = (PSInput)0;\n"; - extractHlsl += " PSInitialBuffer[idx].INddy = (PSInput)0;\n"; - extractHlsl += " PSInitialBuffer[idx].INddxfine = (PSInput)0;\n"; - extractHlsl += " PSInitialBuffer[idx].INddyfine = (PSInput)0;\n"; - - if(!evalSampleCacheData.empty()) - { - extractHlsl += StringFormat::Fmt(" uint evalIndex = idx * %zu;\n", evalSampleCacheData.size()); - - uint32_t evalIdx = 0; - for(const GlobalState::SampleEvalCacheKey &key : evalSampleCacheData) - { - uint32_t keyMask = 0; - - for(int32_t i = 0; i < key.numComponents; i++) - keyMask |= (1 << (key.firstComponent + i)); - - // find the name of the variable matching the operand, in the case of merged input variables. - rdcstr name, swizzle = "xyzw"; - for(size_t i = 0; i < dxbc->GetReflection()->InputSig.size(); i++) - { - if(dxbc->GetReflection()->InputSig[i].regIndex == (uint32_t)key.inputRegisterIndex && - dxbc->GetReflection()->InputSig[i].systemValue == ShaderBuiltin::Undefined && - (dxbc->GetReflection()->InputSig[i].regChannelMask & keyMask) == keyMask) - { - name = inputVarNames[i]; - - if(!name.empty()) - break; - } - } - - swizzle.resize(key.numComponents); - - if(name.empty()) - { - RDCERR("Couldn't find matching input variable for v%d [%d:%d]", key.inputRegisterIndex, - key.firstComponent, key.numComponents); - extractHlsl += StringFormat::Fmt(" PSEvalBuffer[evalIndex+%u] = 0;\n", evalIdx); - evalIdx++; - continue; - } - - name = StringFormat::Fmt("IN.%s.%s", name.c_str(), swizzle.c_str()); - - // we must write all components, so just swizzle the values - they'll be ignored later. - rdcstr expandSwizzle = swizzle; - while(expandSwizzle.size() < 4) - expandSwizzle.push_back('x'); - - if(key.sample >= 0) - { - extractHlsl += StringFormat::Fmt( - " PSEvalBuffer[evalIndex+%u] = EvaluateAttributeAtSample(%s, %d).%s;\n", evalIdx, - name.c_str(), key.sample, expandSwizzle.c_str()); - } - else - { - // we don't need to special-case EvaluateAttributeAtCentroid, since it's just a case with - // 0,0 - extractHlsl += StringFormat::Fmt( - " PSEvalBuffer[evalIndex+%u] = EvaluateAttributeSnapped(%s, int2(%d, %d)).%s;\n", - evalIdx, name.c_str(), key.offsetx, key.offsety, expandSwizzle.c_str()); - } - evalIdx++; } - } - for(size_t i = 0; i < floatInputs.size(); i++) - { - const rdcstr &name = floatInputs[i]; - extractHlsl += " PSInitialBuffer[idx].INddx." + name + " = ddx(IN." + name + ");\n"; - extractHlsl += " PSInitialBuffer[idx].INddy." + name + " = ddy(IN." + name + ");\n"; - extractHlsl += " PSInitialBuffer[idx].INddxfine." + name + " = ddx_fine(IN." + name + ");\n"; - extractHlsl += " PSInitialBuffer[idx].INddyfine." + name + " = ddy_fine(IN." + name + ");\n"; - } - extractHlsl += "\n}"; + extractHlsl += " uint idx = " + ToStr(overdrawLevels) + ";\n"; + extractHlsl += StringFormat::Fmt( + " if(abs(debug_pixelPos.x - %u.5) < 0.5f && abs(debug_pixelPos.y - %u.5) < 0.5f)\n", x, y); + extractHlsl += " InterlockedAdd(PSInitialBuffer[0].hit, 1, idx);\n\n"; + extractHlsl += " idx = min(idx, " + ToStr(overdrawLevels) + ");\n\n"; + extractHlsl += " PSInitialBuffer[idx].pos = debug_pixelPos.xyz;\n"; - // Create pixel shader to get initial values from previous stage output - ID3DBlob *psBlob = NULL; - UINT flags = D3DCOMPILE_WARNINGS_ARE_ERRORS; - if(m_pDevice->GetShaderCache()->GetShaderBlob(extractHlsl.c_str(), "ExtractInputsPS", flags, {}, - "ps_5_1", &psBlob) != "") - { - RDCERR("Failed to create shader to extract inputs"); - return new ShaderDebugTrace; - } + if(usePrimitiveID) + extractHlsl += " PSInitialBuffer[idx].prim = prim;\n"; + else + extractHlsl += " PSInitialBuffer[idx].prim = 0;\n"; - uint32_t structStride = sizeof(uint32_t) // uint hit; - + sizeof(float) * 3 // float3 pos; - + sizeof(uint32_t) // uint prim; - + sizeof(uint32_t) // uint fface; - + sizeof(uint32_t) // uint sample; - + sizeof(uint32_t) // uint covge; - + sizeof(float) // float derivValid; - + - structureStride * 5; // PSInput IN, INddx, INddy, INddxfine, INddyfine; + extractHlsl += " PSInitialBuffer[idx].fface = fface;\n"; + extractHlsl += " PSInitialBuffer[idx].covge = covge;\n"; + extractHlsl += " PSInitialBuffer[idx].sample = sample;\n"; + extractHlsl += " PSInitialBuffer[idx].IN = IN;\n"; + extractHlsl += " PSInitialBuffer[idx].derivValid = ddx(debug_pixelPos.x);\n"; + extractHlsl += " PSInitialBuffer[idx].INddx = (PSInput)0;\n"; + extractHlsl += " PSInitialBuffer[idx].INddy = (PSInput)0;\n"; + extractHlsl += " PSInitialBuffer[idx].INddxfine = (PSInput)0;\n"; + extractHlsl += " PSInitialBuffer[idx].INddyfine = (PSInput)0;\n"; - HRESULT hr = S_OK; + if(!evalSampleCacheData.empty()) + { + extractHlsl += StringFormat::Fmt(" uint evalIndex = idx * %zu;\n", evalSampleCacheData.size()); - // Create buffer to store initial values captured in pixel shader - D3D12_RESOURCE_DESC rdesc; - ZeroMemory(&rdesc, sizeof(D3D12_RESOURCE_DESC)); - rdesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; - rdesc.Width = structStride * (overdrawLevels + 1); - rdesc.Height = 1; - rdesc.DepthOrArraySize = 1; - rdesc.MipLevels = 1; - rdesc.Format = DXGI_FORMAT_UNKNOWN; - rdesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; - rdesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; - rdesc.SampleDesc.Count = 1; // TODO: Support MSAA - rdesc.SampleDesc.Quality = 0; + uint32_t evalIdx = 0; + for(const GlobalState::SampleEvalCacheKey &key : evalSampleCacheData) + { + uint32_t keyMask = 0; - D3D12_HEAP_PROPERTIES heapProps; - heapProps.Type = D3D12_HEAP_TYPE_DEFAULT; - heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; - heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; - heapProps.CreationNodeMask = 1; - heapProps.VisibleNodeMask = 1; + for(int32_t i = 0; i < key.numComponents; i++) + keyMask |= (1 << (key.firstComponent + i)); - ID3D12Resource *pInitialValuesBuffer = NULL; - D3D12_RESOURCE_STATES resourceState = D3D12_RESOURCE_STATE_UNORDERED_ACCESS; - hr = m_pDevice->CreateCommittedResource(&heapProps, D3D12_HEAP_FLAG_NONE, &rdesc, resourceState, - NULL, __uuidof(ID3D12Resource), - (void **)&pInitialValuesBuffer); - if(FAILED(hr)) - { - RDCERR("Failed to create buffer for pixel shader debugging HRESULT: %s", ToStr(hr).c_str()); - SAFE_RELEASE(psBlob); - return new ShaderDebugTrace; - } + // find the name of the variable matching the operand, in the case of merged input variables. + rdcstr name, swizzle = "xyzw"; + for(size_t i = 0; i < dxbc->GetReflection()->InputSig.size(); i++) + { + if(dxbc->GetReflection()->InputSig[i].regIndex == (uint32_t)key.inputRegisterIndex && + dxbc->GetReflection()->InputSig[i].systemValue == ShaderBuiltin::Undefined && + (dxbc->GetReflection()->InputSig[i].regChannelMask & keyMask) == keyMask) + { + name = inputVarNames[i]; - // Create buffer to store MSAA evaluations captured in pixel shader - ID3D12Resource *pMsaaEvalBuffer = NULL; - if(!evalSampleCacheData.empty()) - { - rdesc.Width = UINT(evalSampleCacheData.size() * sizeof(Vec4f) * (overdrawLevels + 1)); + if(!name.empty()) + break; + } + } + + swizzle.resize(key.numComponents); + + if(name.empty()) + { + RDCERR("Couldn't find matching input variable for v%d [%d:%d]", key.inputRegisterIndex, + key.firstComponent, key.numComponents); + extractHlsl += StringFormat::Fmt(" PSEvalBuffer[evalIndex+%u] = 0;\n", evalIdx); + evalIdx++; + continue; + } + + name = StringFormat::Fmt("IN.%s.%s", name.c_str(), swizzle.c_str()); + + // we must write all components, so just swizzle the values - they'll be ignored later. + rdcstr expandSwizzle = swizzle; + while(expandSwizzle.size() < 4) + expandSwizzle.push_back('x'); + + if(key.sample >= 0) + { + extractHlsl += StringFormat::Fmt( + " PSEvalBuffer[evalIndex+%u] = EvaluateAttributeAtSample(%s, %d).%s;\n", evalIdx, + name.c_str(), key.sample, expandSwizzle.c_str()); + } + else + { + // we don't need to special-case EvaluateAttributeAtCentroid, since it's just a case with + // 0,0 + extractHlsl += StringFormat::Fmt( + " PSEvalBuffer[evalIndex+%u] = EvaluateAttributeSnapped(%s, int2(%d, %d)).%s;\n", + evalIdx, name.c_str(), key.offsetx, key.offsety, expandSwizzle.c_str()); + } + evalIdx++; + } + } + + for(size_t i = 0; i < floatInputs.size(); i++) + { + const rdcstr &name = floatInputs[i]; + extractHlsl += " PSInitialBuffer[idx].INddx." + name + " = ddx(IN." + name + ");\n"; + extractHlsl += " PSInitialBuffer[idx].INddy." + name + " = ddy(IN." + name + ");\n"; + extractHlsl += " PSInitialBuffer[idx].INddxfine." + name + " = ddx_fine(IN." + name + ");\n"; + extractHlsl += " PSInitialBuffer[idx].INddyfine." + name + " = ddy_fine(IN." + name + ");\n"; + } + extractHlsl += "\n}"; + + // Create pixel shader to get initial values from previous stage output + ID3DBlob *psBlob = NULL; + UINT flags = D3DCOMPILE_WARNINGS_ARE_ERRORS; + if(m_pDevice->GetShaderCache()->GetShaderBlob(extractHlsl.c_str(), "ExtractInputsPS", flags, {}, + "ps_5_1", &psBlob) != "") + { + RDCERR("Failed to create shader to extract inputs"); + return new ShaderDebugTrace; + } + + uint32_t structStride = + sizeof(uint32_t) // uint hit; + + sizeof(float) * 3 // float3 pos; + + sizeof(uint32_t) // uint prim; + + sizeof(uint32_t) // uint fface; + + sizeof(uint32_t) // uint sample; + + sizeof(uint32_t) // uint covge; + + sizeof(float) // float derivValid; + + structureStride * 5; // PSInput IN, INddx, INddy, INddxfine, INddyfine; + + HRESULT hr = S_OK; + + // Create buffer to store initial values captured in pixel shader + D3D12_RESOURCE_DESC rdesc; + ZeroMemory(&rdesc, sizeof(D3D12_RESOURCE_DESC)); + rdesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; + rdesc.Width = structStride * (overdrawLevels + 1); + rdesc.Height = 1; + rdesc.DepthOrArraySize = 1; + rdesc.MipLevels = 1; + rdesc.Format = DXGI_FORMAT_UNKNOWN; + rdesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; + rdesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; + rdesc.SampleDesc.Count = 1; // TODO: Support MSAA + rdesc.SampleDesc.Quality = 0; + + D3D12_HEAP_PROPERTIES heapProps; + heapProps.Type = D3D12_HEAP_TYPE_DEFAULT; + heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; + heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; + heapProps.CreationNodeMask = 1; + heapProps.VisibleNodeMask = 1; + + ID3D12Resource *pInitialValuesBuffer = NULL; + D3D12_RESOURCE_STATES resourceState = D3D12_RESOURCE_STATE_UNORDERED_ACCESS; hr = m_pDevice->CreateCommittedResource(&heapProps, D3D12_HEAP_FLAG_NONE, &rdesc, resourceState, NULL, __uuidof(ID3D12Resource), - (void **)&pMsaaEvalBuffer); + (void **)&pInitialValuesBuffer); if(FAILED(hr)) { - RDCERR("Failed to create MSAA buffer for pixel shader debugging HRESULT: %s", - ToStr(hr).c_str()); - SAFE_RELEASE(pInitialValuesBuffer); + RDCERR("Failed to create buffer for pixel shader debugging HRESULT: %s", ToStr(hr).c_str()); SAFE_RELEASE(psBlob); return new ShaderDebugTrace; } - } - // Create UAV of initial values buffer - D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc; - ZeroMemory(&uavDesc, sizeof(D3D12_UNORDERED_ACCESS_VIEW_DESC)); - uavDesc.Format = DXGI_FORMAT_UNKNOWN; - uavDesc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER; - uavDesc.Buffer.NumElements = overdrawLevels + 1; - uavDesc.Buffer.StructureByteStride = structStride; - - D3D12_CPU_DESCRIPTOR_HANDLE uav = m_pDevice->GetDebugManager()->GetCPUHandle(SHADER_DEBUG_UAV); - m_pDevice->CreateUnorderedAccessView(pInitialValuesBuffer, NULL, &uavDesc, uav); - - uavDesc.Format = DXGI_FORMAT_R32_UINT; - uavDesc.Buffer.FirstElement = 0; - uavDesc.Buffer.NumElements = structStride * (overdrawLevels + 1) / sizeof(uint32_t); - uavDesc.Buffer.StructureByteStride = 0; - D3D12_CPU_DESCRIPTOR_HANDLE clearUav = - m_pDevice->GetDebugManager()->GetUAVClearHandle(SHADER_DEBUG_UAV); - m_pDevice->CreateUnorderedAccessView(pInitialValuesBuffer, NULL, &uavDesc, clearUav); - - // Create UAV of MSAA eval buffer - D3D12_CPU_DESCRIPTOR_HANDLE msaaClearUav = - m_pDevice->GetDebugManager()->GetUAVClearHandle(SHADER_DEBUG_MSAA_UAV); - if(pMsaaEvalBuffer) - { - D3D12_CPU_DESCRIPTOR_HANDLE msaaUav = - m_pDevice->GetDebugManager()->GetCPUHandle(SHADER_DEBUG_MSAA_UAV); - uavDesc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT; - uavDesc.Buffer.NumElements = (overdrawLevels + 1) * (uint32_t)evalSampleCacheData.size(); - m_pDevice->CreateUnorderedAccessView(pMsaaEvalBuffer, NULL, &uavDesc, msaaUav); - - uavDesc.Format = DXGI_FORMAT_R32_UINT; - uavDesc.Buffer.NumElements = - (UINT)evalSampleCacheData.size() * (overdrawLevels + 1) / sizeof(uint32_t); - m_pDevice->CreateUnorderedAccessView(pMsaaEvalBuffer, NULL, &uavDesc, msaaClearUav); - } - - // Create the descriptor table for our UAV - D3D12_DESCRIPTOR_RANGE1 descRange; - descRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV; - descRange.NumDescriptors = pMsaaEvalBuffer ? 2 : 1; - descRange.BaseShaderRegister = 1; - descRange.RegisterSpace = regSpace; - descRange.Flags = D3D12_DESCRIPTOR_RANGE_FLAG_NONE; - descRange.OffsetInDescriptorsFromTableStart = 0; - - modsig.Parameters.push_back(D3D12RootSignatureParameter()); - D3D12RootSignatureParameter ¶m = modsig.Parameters.back(); - param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; - param.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; - param.DescriptorTable.NumDescriptorRanges = 1; - param.DescriptorTable.pDescriptorRanges = &descRange; - - uint32_t sigElem = uint32_t(modsig.Parameters.size() - 1); - - modsig.Flags &= ~D3D12_ROOT_SIGNATURE_FLAG_DENY_PIXEL_SHADER_ROOT_ACCESS; - - // Create the root signature for gathering initial pixel shader values - ID3DBlob *root = m_pDevice->GetShaderCache()->MakeRootSig(modsig); - ID3D12RootSignature *pRootSignature = NULL; - hr = m_pDevice->CreateRootSignature(0, root->GetBufferPointer(), root->GetBufferSize(), - __uuidof(ID3D12RootSignature), (void **)&pRootSignature); - if(FAILED(hr)) - { - RDCERR("Failed to create root signature for pixel shader debugging HRESULT: %s", - ToStr(hr).c_str()); - SAFE_RELEASE(root); - SAFE_RELEASE(psBlob); - SAFE_RELEASE(pInitialValuesBuffer); - SAFE_RELEASE(pMsaaEvalBuffer); - return new ShaderDebugTrace; - } - SAFE_RELEASE(root); - - // All PSO state is the same as the event's, except for the pixel shader and root signature - pipeDesc.PS.BytecodeLength = psBlob->GetBufferSize(); - pipeDesc.PS.pShaderBytecode = psBlob->GetBufferPointer(); - pipeDesc.pRootSignature = pRootSignature; - - ID3D12PipelineState *initialPso = NULL; - hr = m_pDevice->CreatePipeState(pipeDesc, &initialPso); - if(FAILED(hr)) - { - RDCERR("Failed to create PSO for pixel shader debugging HRESULT: %s", ToStr(hr).c_str()); - SAFE_RELEASE(psBlob); - SAFE_RELEASE(pInitialValuesBuffer); - SAFE_RELEASE(pMsaaEvalBuffer); - SAFE_RELEASE(pRootSignature); - return new ShaderDebugTrace; - } - - // if we have a depth buffer bound and we are testing EQUAL grab the current depth value for our - // target sample - D3D12_COMPARISON_FUNC depthFunc = pipeDesc.DepthStencilState.DepthFunc; - float existingDepth = -1.0f; - ResourceId depthTarget = rs.dsv.GetResResourceId(); - - if(depthFunc == D3D12_COMPARISON_FUNC_EQUAL && depthTarget != ResourceId()) - { - float depthStencilValue[4] = {}; - PickPixel(depthTarget, x, y, - Subresource(rs.dsv.GetDSV().Texture2DArray.MipSlice, - rs.dsv.GetDSV().Texture2DArray.FirstArraySlice, sample), - CompType::Depth, depthStencilValue); - - existingDepth = depthStencilValue[0]; - } - - ID3D12GraphicsCommandListX *cmdList = m_pDevice->GetDebugManager()->ResetDebugList(); - - // clear our UAVs - m_pDevice->GetDebugManager()->SetDescriptorHeaps(cmdList, true, false); - D3D12_GPU_DESCRIPTOR_HANDLE gpuUav = m_pDevice->GetDebugManager()->GetGPUHandle(SHADER_DEBUG_UAV); - UINT zero[4] = {0, 0, 0, 0}; - cmdList->ClearUnorderedAccessViewUint(gpuUav, clearUav, pInitialValuesBuffer, zero, 0, NULL); - - if(pMsaaEvalBuffer) - { - D3D12_GPU_DESCRIPTOR_HANDLE gpuMsaaUav = - m_pDevice->GetDebugManager()->GetGPUHandle(SHADER_DEBUG_MSAA_UAV); - cmdList->ClearUnorderedAccessViewUint(gpuMsaaUav, msaaClearUav, pMsaaEvalBuffer, zero, 0, NULL); - } - - // Add the descriptor for our UAV - std::set copiedHeaps; - rdcarray debugHandles; - debugHandles.push_back(ToPortableHandle(GetDebugManager()->GetCPUHandle(SHADER_DEBUG_UAV))); - if(pMsaaEvalBuffer) - debugHandles.push_back(ToPortableHandle(GetDebugManager()->GetCPUHandle(SHADER_DEBUG_MSAA_UAV))); - AddDebugDescriptorsToRenderState(m_pDevice, rs, debugHandles, - D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, sigElem, copiedHeaps); - - rs.ApplyDescriptorHeaps(cmdList); - - // Execute the command to ensure that UAV clear and resource creation occur before replay - hr = cmdList->Close(); - if(FAILED(hr)) - { - RDCERR("Failed to close command list HRESULT: %s", ToStr(hr).c_str()); - SAFE_RELEASE(psBlob); - SAFE_RELEASE(pInitialValuesBuffer); - SAFE_RELEASE(pMsaaEvalBuffer); - SAFE_RELEASE(pRootSignature); - SAFE_RELEASE(initialPso); - return new ShaderDebugTrace; - } - - { - ID3D12CommandList *l = cmdList; - m_pDevice->GetQueue()->ExecuteCommandLists(1, &l); - m_pDevice->GPUSync(); - } - - { - D3D12MarkerRegion initState(m_pDevice->GetQueue()->GetReal(), - "Replaying event for initial states"); - - // Set the PSO and root signature - rs.pipe = GetResID(initialPso); - rs.graphics.rootsig = GetResID(pRootSignature); - - // Replay the event with our modified state - m_pDevice->ReplayLog(0, eventId, eReplay_OnlyDraw); - - // Restore D3D12 state to what the event uses - rs = prevState; - } - - bytebuf initialData; - m_pDevice->GetDebugManager()->GetBufferData(pInitialValuesBuffer, 0, 0, initialData); - - bytebuf evalData; - if(pMsaaEvalBuffer) - m_pDevice->GetDebugManager()->GetBufferData(pMsaaEvalBuffer, 0, 0, evalData); - - // Replaying the event has finished, and the data has been copied out. - // Free all the resources that were created. - SAFE_RELEASE(psBlob); - SAFE_RELEASE(pRootSignature); - SAFE_RELEASE(pInitialValuesBuffer); - SAFE_RELEASE(pMsaaEvalBuffer); - SAFE_RELEASE(initialPso); - - DebugHit *buf = (DebugHit *)initialData.data(); - - D3D12MarkerRegion::Set(m_pDevice->GetQueue()->GetReal(), - StringFormat::Fmt("Got %u hits", buf[0].numHits)); - if(buf[0].numHits == 0) - { - RDCLOG("No hit for this event"); - return new ShaderDebugTrace; - } - - // if we encounter multiple hits at our destination pixel co-ord (or any other) we - // check to see if a specific primitive was requested (via primitive parameter not - // being set to ~0U). If it was, debug that pixel, otherwise do a best-estimate - // of which fragment was the last to successfully depth test and debug that, just by - // checking if the depth test is ordered and picking the final fragment in the series - - // figure out the TL pixel's coords. Assume even top left (towards 0,0) - // this isn't spec'd but is a reasonable assumption. - int xTL = x & (~1); - int yTL = y & (~1); - - // get the index of our desired pixel - int destIdx = (x - xTL) + 2 * (y - yTL); - - // Get depth func and determine "winner" pixel - DebugHit *pWinnerHit = NULL; - float *evalSampleCache = (float *)evalData.data(); - - if(sample == ~0U) - sample = 0; - - if(primitive != ~0U) - { - for(size_t i = 0; i < buf[0].numHits && i < overdrawLevels; i++) + // Create buffer to store MSAA evaluations captured in pixel shader + ID3D12Resource *pMsaaEvalBuffer = NULL; + if(!evalSampleCacheData.empty()) { - DebugHit *pHit = (DebugHit *)(initialData.data() + i * structStride); - - if(pHit->primitive == primitive && pHit->sample == sample) + rdesc.Width = UINT(evalSampleCacheData.size() * sizeof(Vec4f) * (overdrawLevels + 1)); + hr = m_pDevice->CreateCommittedResource(&heapProps, D3D12_HEAP_FLAG_NONE, &rdesc, + resourceState, NULL, __uuidof(ID3D12Resource), + (void **)&pMsaaEvalBuffer); + if(FAILED(hr)) { - pWinnerHit = pHit; - evalSampleCache = ((float *)evalData.data() + evalSampleCacheData.size() * 4 * i); + RDCERR("Failed to create MSAA buffer for pixel shader debugging HRESULT: %s", + ToStr(hr).c_str()); + SAFE_RELEASE(pInitialValuesBuffer); + SAFE_RELEASE(psBlob); + return new ShaderDebugTrace; } } - } - if(pWinnerHit == NULL) - { - for(size_t i = 0; i < buf[0].numHits && i < overdrawLevels; i++) + // Create UAV of initial values buffer + D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc; + ZeroMemory(&uavDesc, sizeof(D3D12_UNORDERED_ACCESS_VIEW_DESC)); + uavDesc.Format = DXGI_FORMAT_UNKNOWN; + uavDesc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER; + uavDesc.Buffer.NumElements = overdrawLevels + 1; + uavDesc.Buffer.StructureByteStride = structStride; + + D3D12_CPU_DESCRIPTOR_HANDLE uav = m_pDevice->GetDebugManager()->GetCPUHandle(SHADER_DEBUG_UAV); + m_pDevice->CreateUnorderedAccessView(pInitialValuesBuffer, NULL, &uavDesc, uav); + + uavDesc.Format = DXGI_FORMAT_R32_UINT; + uavDesc.Buffer.FirstElement = 0; + uavDesc.Buffer.NumElements = structStride * (overdrawLevels + 1) / sizeof(uint32_t); + uavDesc.Buffer.StructureByteStride = 0; + D3D12_CPU_DESCRIPTOR_HANDLE clearUav = + m_pDevice->GetDebugManager()->GetUAVClearHandle(SHADER_DEBUG_UAV); + m_pDevice->CreateUnorderedAccessView(pInitialValuesBuffer, NULL, &uavDesc, clearUav); + + // Create UAV of MSAA eval buffer + D3D12_CPU_DESCRIPTOR_HANDLE msaaClearUav = + m_pDevice->GetDebugManager()->GetUAVClearHandle(SHADER_DEBUG_MSAA_UAV); + if(pMsaaEvalBuffer) { - DebugHit *pHit = (DebugHit *)(initialData.data() + i * structStride); + D3D12_CPU_DESCRIPTOR_HANDLE msaaUav = + m_pDevice->GetDebugManager()->GetCPUHandle(SHADER_DEBUG_MSAA_UAV); + uavDesc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT; + uavDesc.Buffer.NumElements = (overdrawLevels + 1) * (uint32_t)evalSampleCacheData.size(); + m_pDevice->CreateUnorderedAccessView(pMsaaEvalBuffer, NULL, &uavDesc, msaaUav); - if(pWinnerHit == NULL) + uavDesc.Format = DXGI_FORMAT_R32_UINT; + uavDesc.Buffer.NumElements = + (UINT)evalSampleCacheData.size() * (overdrawLevels + 1) / sizeof(uint32_t); + m_pDevice->CreateUnorderedAccessView(pMsaaEvalBuffer, NULL, &uavDesc, msaaClearUav); + } + + // Create the descriptor table for our UAV + D3D12_DESCRIPTOR_RANGE1 descRange; + descRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV; + descRange.NumDescriptors = pMsaaEvalBuffer ? 2 : 1; + descRange.BaseShaderRegister = 1; + descRange.RegisterSpace = regSpace; + descRange.Flags = D3D12_DESCRIPTOR_RANGE_FLAG_NONE; + descRange.OffsetInDescriptorsFromTableStart = 0; + + modsig.Parameters.push_back(D3D12RootSignatureParameter()); + D3D12RootSignatureParameter ¶m = modsig.Parameters.back(); + param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; + param.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; + param.DescriptorTable.NumDescriptorRanges = 1; + param.DescriptorTable.pDescriptorRanges = &descRange; + + uint32_t sigElem = uint32_t(modsig.Parameters.size() - 1); + + modsig.Flags &= ~D3D12_ROOT_SIGNATURE_FLAG_DENY_PIXEL_SHADER_ROOT_ACCESS; + + // Create the root signature for gathering initial pixel shader values + ID3DBlob *root = m_pDevice->GetShaderCache()->MakeRootSig(modsig); + ID3D12RootSignature *pRootSignature = NULL; + hr = m_pDevice->CreateRootSignature(0, root->GetBufferPointer(), root->GetBufferSize(), + __uuidof(ID3D12RootSignature), (void **)&pRootSignature); + if(FAILED(hr)) + { + RDCERR("Failed to create root signature for pixel shader debugging HRESULT: %s", + ToStr(hr).c_str()); + SAFE_RELEASE(root); + SAFE_RELEASE(psBlob); + SAFE_RELEASE(pInitialValuesBuffer); + SAFE_RELEASE(pMsaaEvalBuffer); + return new ShaderDebugTrace; + } + SAFE_RELEASE(root); + + // All PSO state is the same as the event's, except for the pixel shader and root signature + pipeDesc.PS.BytecodeLength = psBlob->GetBufferSize(); + pipeDesc.PS.pShaderBytecode = psBlob->GetBufferPointer(); + pipeDesc.pRootSignature = pRootSignature; + + ID3D12PipelineState *initialPso = NULL; + hr = m_pDevice->CreatePipeState(pipeDesc, &initialPso); + if(FAILED(hr)) + { + RDCERR("Failed to create PSO for pixel shader debugging HRESULT: %s", ToStr(hr).c_str()); + SAFE_RELEASE(psBlob); + SAFE_RELEASE(pInitialValuesBuffer); + SAFE_RELEASE(pMsaaEvalBuffer); + SAFE_RELEASE(pRootSignature); + return new ShaderDebugTrace; + } + + // if we have a depth buffer bound and we are testing EQUAL grab the current depth value for our + // target sample + D3D12_COMPARISON_FUNC depthFunc = pipeDesc.DepthStencilState.DepthFunc; + float existingDepth = -1.0f; + ResourceId depthTarget = rs.dsv.GetResResourceId(); + + if(depthFunc == D3D12_COMPARISON_FUNC_EQUAL && depthTarget != ResourceId()) + { + float depthStencilValue[4] = {}; + PickPixel(depthTarget, x, y, + Subresource(rs.dsv.GetDSV().Texture2DArray.MipSlice, + rs.dsv.GetDSV().Texture2DArray.FirstArraySlice, sample), + CompType::Depth, depthStencilValue); + + existingDepth = depthStencilValue[0]; + } + + ID3D12GraphicsCommandListX *cmdList = m_pDevice->GetDebugManager()->ResetDebugList(); + + // clear our UAVs + m_pDevice->GetDebugManager()->SetDescriptorHeaps(cmdList, true, false); + D3D12_GPU_DESCRIPTOR_HANDLE gpuUav = m_pDevice->GetDebugManager()->GetGPUHandle(SHADER_DEBUG_UAV); + UINT zero[4] = {0, 0, 0, 0}; + cmdList->ClearUnorderedAccessViewUint(gpuUav, clearUav, pInitialValuesBuffer, zero, 0, NULL); + + if(pMsaaEvalBuffer) + { + D3D12_GPU_DESCRIPTOR_HANDLE gpuMsaaUav = + m_pDevice->GetDebugManager()->GetGPUHandle(SHADER_DEBUG_MSAA_UAV); + cmdList->ClearUnorderedAccessViewUint(gpuMsaaUav, msaaClearUav, pMsaaEvalBuffer, zero, 0, NULL); + } + + // Add the descriptor for our UAV + std::set copiedHeaps; + rdcarray debugHandles; + debugHandles.push_back(ToPortableHandle(GetDebugManager()->GetCPUHandle(SHADER_DEBUG_UAV))); + if(pMsaaEvalBuffer) + debugHandles.push_back(ToPortableHandle(GetDebugManager()->GetCPUHandle(SHADER_DEBUG_MSAA_UAV))); + AddDebugDescriptorsToRenderState(m_pDevice, rs, debugHandles, + D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, sigElem, copiedHeaps); + + rs.ApplyDescriptorHeaps(cmdList); + + // Execute the command to ensure that UAV clear and resource creation occur before replay + hr = cmdList->Close(); + if(FAILED(hr)) + { + RDCERR("Failed to close command list HRESULT: %s", ToStr(hr).c_str()); + SAFE_RELEASE(psBlob); + SAFE_RELEASE(pInitialValuesBuffer); + SAFE_RELEASE(pMsaaEvalBuffer); + SAFE_RELEASE(pRootSignature); + SAFE_RELEASE(initialPso); + return new ShaderDebugTrace; + } + + { + ID3D12CommandList *l = cmdList; + m_pDevice->GetQueue()->ExecuteCommandLists(1, &l); + m_pDevice->GPUSync(); + } + + { + D3D12MarkerRegion initState(m_pDevice->GetQueue()->GetReal(), + "Replaying event for initial states"); + + // Set the PSO and root signature + rs.pipe = GetResID(initialPso); + rs.graphics.rootsig = GetResID(pRootSignature); + + // Replay the event with our modified state + m_pDevice->ReplayLog(0, eventId, eReplay_OnlyDraw); + + // Restore D3D12 state to what the event uses + rs = prevState; + } + + bytebuf initialData; + m_pDevice->GetDebugManager()->GetBufferData(pInitialValuesBuffer, 0, 0, initialData); + + bytebuf evalData; + if(pMsaaEvalBuffer) + m_pDevice->GetDebugManager()->GetBufferData(pMsaaEvalBuffer, 0, 0, evalData); + + // Replaying the event has finished, and the data has been copied out. + // Free all the resources that were created. + SAFE_RELEASE(psBlob); + SAFE_RELEASE(pRootSignature); + SAFE_RELEASE(pInitialValuesBuffer); + SAFE_RELEASE(pMsaaEvalBuffer); + SAFE_RELEASE(initialPso); + + DebugHit *buf = (DebugHit *)initialData.data(); + + D3D12MarkerRegion::Set(m_pDevice->GetQueue()->GetReal(), + StringFormat::Fmt("Got %u hits", buf[0].numHits)); + if(buf[0].numHits == 0) + { + RDCLOG("No hit for this event"); + return new ShaderDebugTrace; + } + + // if we encounter multiple hits at our destination pixel co-ord (or any other) we + // check to see if a specific primitive was requested (via primitive parameter not + // being set to ~0U). If it was, debug that pixel, otherwise do a best-estimate + // of which fragment was the last to successfully depth test and debug that, just by + // checking if the depth test is ordered and picking the final fragment in the series + + // figure out the TL pixel's coords. Assume even top left (towards 0,0) + // this isn't spec'd but is a reasonable assumption. + int xTL = x & (~1); + int yTL = y & (~1); + + // get the index of our desired pixel + int destIdx = (x - xTL) + 2 * (y - yTL); + + // Get depth func and determine "winner" pixel + DebugHit *pWinnerHit = NULL; + float *evalSampleCache = (float *)evalData.data(); + + if(sample == ~0U) + sample = 0; + + if(primitive != ~0U) + { + for(size_t i = 0; i < buf[0].numHits && i < overdrawLevels; i++) { - // If we haven't picked a winner at all yet, use the first one - pWinnerHit = pHit; - evalSampleCache = ((float *)evalData.data()) + evalSampleCacheData.size() * 4 * i; - } - else if(pHit->sample == sample) - { - // If this hit is for the sample we want, check whether it's a better pick - if(pWinnerHit->sample != sample) + DebugHit *pHit = (DebugHit *)(initialData.data() + i * structStride); + + if(pHit->primitive == primitive && pHit->sample == sample) { - // The previously selected winner was for the wrong sample, use this one + pWinnerHit = pHit; + evalSampleCache = ((float *)evalData.data() + evalSampleCacheData.size() * 4 * i); + } + } + } + + if(pWinnerHit == NULL) + { + for(size_t i = 0; i < buf[0].numHits && i < overdrawLevels; i++) + { + DebugHit *pHit = (DebugHit *)(initialData.data() + i * structStride); + + if(pWinnerHit == NULL) + { + // If we haven't picked a winner at all yet, use the first one pWinnerHit = pHit; evalSampleCache = ((float *)evalData.data()) + evalSampleCacheData.size() * 4 * i; } - else if(depthFunc == D3D12_COMPARISON_FUNC_EQUAL && existingDepth >= 0.0f) + else if(pHit->sample == sample) { - // for depth equal, check if this hit is closer than the winner, and if so use it. - if(fabs(pHit->depth - existingDepth) < fabs(pWinnerHit->depth - existingDepth)) + // If this hit is for the sample we want, check whether it's a better pick + if(pWinnerHit->sample != sample) { + // The previously selected winner was for the wrong sample, use this one + pWinnerHit = pHit; + evalSampleCache = ((float *)evalData.data()) + evalSampleCacheData.size() * 4 * i; + } + else if(depthFunc == D3D12_COMPARISON_FUNC_EQUAL && existingDepth >= 0.0f) + { + // for depth equal, check if this hit is closer than the winner, and if so use it. + if(fabs(pHit->depth - existingDepth) < fabs(pWinnerHit->depth - existingDepth)) + { + pWinnerHit = pHit; + evalSampleCache = ((float *)evalData.data()) + evalSampleCacheData.size() * 4 * i; + } + } + else if(depthFunc == D3D12_COMPARISON_FUNC_ALWAYS || + depthFunc == D3D12_COMPARISON_FUNC_NEVER || + depthFunc == D3D12_COMPARISON_FUNC_NOT_EQUAL) + { + // For depth functions without a sensible comparison, use the last sample encountered + pWinnerHit = pHit; + evalSampleCache = ((float *)evalData.data()) + evalSampleCacheData.size() * 4 * i; + } + else if((depthFunc == D3D12_COMPARISON_FUNC_LESS && pHit->depth < pWinnerHit->depth) || + (depthFunc == D3D12_COMPARISON_FUNC_LESS_EQUAL && pHit->depth <= pWinnerHit->depth) || + (depthFunc == D3D12_COMPARISON_FUNC_GREATER && pHit->depth > pWinnerHit->depth) || + (depthFunc == D3D12_COMPARISON_FUNC_GREATER_EQUAL && + pHit->depth >= pWinnerHit->depth)) + { + // For depth functions with an inequality, find the hit that "wins" the most pWinnerHit = pHit; evalSampleCache = ((float *)evalData.data()) + evalSampleCacheData.size() * 4 * i; } } - else if(depthFunc == D3D12_COMPARISON_FUNC_ALWAYS || - depthFunc == D3D12_COMPARISON_FUNC_NEVER || - depthFunc == D3D12_COMPARISON_FUNC_NOT_EQUAL) - { - // For depth functions without a sensible comparison, use the last sample encountered - pWinnerHit = pHit; - evalSampleCache = ((float *)evalData.data()) + evalSampleCacheData.size() * 4 * i; - } - else if((depthFunc == D3D12_COMPARISON_FUNC_LESS && pHit->depth < pWinnerHit->depth) || - (depthFunc == D3D12_COMPARISON_FUNC_LESS_EQUAL && pHit->depth <= pWinnerHit->depth) || - (depthFunc == D3D12_COMPARISON_FUNC_GREATER && pHit->depth > pWinnerHit->depth) || - (depthFunc == D3D12_COMPARISON_FUNC_GREATER_EQUAL && pHit->depth >= pWinnerHit->depth)) - { - // For depth functions with an inequality, find the hit that "wins" the most - pWinnerHit = pHit; - evalSampleCache = ((float *)evalData.data()) + evalSampleCacheData.size() * 4 * i; - } } } - } - if(pWinnerHit == NULL) - { - RDCLOG("Couldn't find any pixels that passed depth test at target coordinates"); - return new ShaderDebugTrace; - } - - InterpretDebugger *interpreter = new InterpretDebugger; - interpreter->eventId = eventId; - ShaderDebugTrace *ret = interpreter->BeginDebug(dxbc, refl, destIdx); - GlobalState &global = interpreter->global; - ThreadState &state = interpreter->activeLane(); - - // Fetch constant buffer data from root signature - GatherConstantBuffers(m_pDevice, *dxbc->GetDXBCByteCode(), rs.graphics, refl, global, - ret->sourceVars); - - global.sampleEvalRegisterMask = sampleEvalRegisterMask; - - { - DebugHit *pHit = pWinnerHit; - - rdcarray &ins = state.inputs; - if(!ins.empty() && ins.back().name == "vCoverage") - ins.back().value.u32v[0] = pHit->coverage; - - state.semantics.coverage = pHit->coverage; - state.semantics.primID = pHit->primitive; - state.semantics.isFrontFace = pHit->isFrontFace; - - uint32_t *data = &pHit->rawdata; - - float *pos_ddx = (float *)data; - - // ddx(SV_Position.x) MUST be 1.0 - if(*pos_ddx != 1.0f) + if(pWinnerHit == NULL) { - RDCERR("Derivatives invalid"); - delete interpreter; - delete ret; + RDCLOG("Couldn't find any pixels that passed depth test at target coordinates"); return new ShaderDebugTrace; } - data++; + InterpretDebugger *interpreter = new InterpretDebugger; + interpreter->eventId = eventId; + ret = interpreter->BeginDebug(dxbc, refl, destIdx); + GlobalState &global = interpreter->global; + ThreadState &state = interpreter->activeLane(); + + // Fetch constant buffer data from root signature + GatherConstantBuffers(m_pDevice, *dxbc->GetDXBCByteCode(), rs.graphics, refl, global, + ret->sourceVars); + + global.sampleEvalRegisterMask = sampleEvalRegisterMask; - for(size_t i = 0; i < initialValues.size(); i++) { - int32_t *rawout = NULL; + DebugHit *pHit = pWinnerHit; - if(initialValues[i].reg >= 0) + rdcarray &ins = state.inputs; + if(!ins.empty() && ins.back().name == "vCoverage") + ins.back().value.u32v[0] = pHit->coverage; + + state.semantics.coverage = pHit->coverage; + state.semantics.primID = pHit->primitive; + state.semantics.isFrontFace = pHit->isFrontFace; + + uint32_t *data = &pHit->rawdata; + + float *pos_ddx = (float *)data; + + // ddx(SV_Position.x) MUST be 1.0 + if(*pos_ddx != 1.0f) { - ShaderVariable &invar = ins[initialValues[i].reg]; - - if(initialValues[i].sysattribute == ShaderBuiltin::PrimitiveIndex) - { - invar.value.u32v[0] = pHit->primitive; - } - else if(initialValues[i].sysattribute == ShaderBuiltin::MSAASampleIndex) - { - invar.value.u32v[0] = pHit->sample; - } - else if(initialValues[i].sysattribute == ShaderBuiltin::MSAACoverage) - { - invar.value.u32v[0] = pHit->coverage; - } - else if(initialValues[i].sysattribute == ShaderBuiltin::IsFrontFace) - { - invar.value.u32v[0] = pHit->isFrontFace ? ~0U : 0; - } - else - { - rawout = &invar.value.s32v[initialValues[i].elem]; - - memcpy(rawout, data, initialValues[i].numwords * 4); - } + RDCERR("Derivatives invalid"); + delete interpreter; + delete ret; + return new ShaderDebugTrace; } - if(initialValues[i].included) - data += initialValues[i].numwords; - } + data++; - for(int i = 0; i < 4; i++) - { - if(i != destIdx) + for(size_t i = 0; i < initialValues.size(); i++) { - interpreter->workgroup[i].inputs = state.inputs; - interpreter->workgroup[i].semantics = state.semantics; - interpreter->workgroup[i].variables = state.variables; - interpreter->workgroup[i].SetHelper(); + int32_t *rawout = NULL; + + if(initialValues[i].reg >= 0) + { + ShaderVariable &invar = ins[initialValues[i].reg]; + + if(initialValues[i].sysattribute == ShaderBuiltin::PrimitiveIndex) + { + invar.value.u32v[0] = pHit->primitive; + } + else if(initialValues[i].sysattribute == ShaderBuiltin::MSAASampleIndex) + { + invar.value.u32v[0] = pHit->sample; + } + else if(initialValues[i].sysattribute == ShaderBuiltin::MSAACoverage) + { + invar.value.u32v[0] = pHit->coverage; + } + else if(initialValues[i].sysattribute == ShaderBuiltin::IsFrontFace) + { + invar.value.u32v[0] = pHit->isFrontFace ? ~0U : 0; + } + else + { + rawout = &invar.value.s32v[initialValues[i].elem]; + + memcpy(rawout, data, initialValues[i].numwords * 4); + } + } + + if(initialValues[i].included) + data += initialValues[i].numwords; } - } - // Fetch any inputs that were evaluated at sample granularity - for(const GlobalState::SampleEvalCacheKey &key : evalSampleCacheData) - { - // start with the basic input value - ShaderVariable var = state.inputs[key.inputRegisterIndex]; - - // copy over the value into the variable - memcpy(var.value.f32v.data(), evalSampleCache, var.columns * sizeof(float)); - - // store in the global cache for each quad. We'll apply derivatives below to adjust for each - GlobalState::SampleEvalCacheKey k = key; for(int i = 0; i < 4; i++) { - k.quadIndex = i; - global.sampleEvalCache[k] = var; + if(i != destIdx) + { + interpreter->workgroup[i].inputs = state.inputs; + interpreter->workgroup[i].semantics = state.semantics; + interpreter->workgroup[i].variables = state.variables; + interpreter->workgroup[i].SetHelper(); + } } - // advance past this data - always by float4 as that's the buffer stride - evalSampleCache += 4; + // Fetch any inputs that were evaluated at sample granularity + for(const GlobalState::SampleEvalCacheKey &key : evalSampleCacheData) + { + // start with the basic input value + ShaderVariable var = state.inputs[key.inputRegisterIndex]; + + // copy over the value into the variable + memcpy(var.value.f32v.data(), evalSampleCache, var.columns * sizeof(float)); + + // store in the global cache for each quad. We'll apply derivatives below to adjust for each + GlobalState::SampleEvalCacheKey k = key; + for(int i = 0; i < 4; i++) + { + k.quadIndex = i; + global.sampleEvalCache[k] = var; + } + + // advance past this data - always by float4 as that's the buffer stride + evalSampleCache += 4; + } + + ApplyAllDerivatives(global, interpreter->workgroup, destIdx, initialValues, (float *)data); } - ApplyAllDerivatives(global, interpreter->workgroup, destIdx, initialValues, (float *)data); + ret->constantBlocks = global.constantBlocks; + ret->inputs = state.inputs; + } + else + { + RDCERR("TODO ADD DXIL PIXEL SHADER DEBUGGER SUPPORT"); } - ret->constantBlocks = global.constantBlocks; - ret->inputs = state.inputs; - - dxbc->FillTraceLineInfo(*ret); + if(ret) + dxbc->FillTraceLineInfo(*ret); return ret; } @@ -2697,6 +2717,13 @@ ShaderDebugTrace *D3D12Replay::DebugThread(uint32_t eventId, StringFormat::Fmt("DebugThread @ %u: [%u, %u, %u] (%u, %u, %u)", eventId, groupid[0], groupid[1], groupid[2], threadid[0], threadid[1], threadid[2])); + const ActionDescription *action = m_pDevice->GetAction(eventId); + if(!(action->flags & ActionFlags::Dispatch)) + { + RDCERR("Can only debug a Dispatch action"); + return new ShaderDebugTrace(); + } + const D3D12RenderState &rs = m_pDevice->GetQueue()->GetCommandData()->m_RenderState; WrappedID3D12PipelineState *pso = @@ -2728,77 +2755,87 @@ ShaderDebugTrace *D3D12Replay::DebugThread(uint32_t eventId, dxbc->GetDisassembly(false); - InterpretDebugger *interpreter = new InterpretDebugger; - interpreter->eventId = eventId; - ShaderDebugTrace *ret = interpreter->BeginDebug(dxbc, refl, 0); - GlobalState &global = interpreter->global; - ThreadState &state = interpreter->activeLane(); - - GatherConstantBuffers(m_pDevice, *dxbc->GetDXBCByteCode(), rs.compute, refl, global, - ret->sourceVars); - - for(int i = 0; i < 3; i++) + ShaderDebugTrace *ret = NULL; + if(dxbc->GetDXBCByteCode()) { - state.semantics.GroupID[i] = groupid[i]; - state.semantics.ThreadID[i] = threadid[i]; - } + InterpretDebugger *interpreter = new InterpretDebugger; + interpreter->eventId = eventId; + ret = interpreter->BeginDebug(dxbc, refl, 0); + GlobalState &global = interpreter->global; + ThreadState &state = interpreter->activeLane(); - ret->constantBlocks = global.constantBlocks; + GatherConstantBuffers(m_pDevice, *dxbc->GetDXBCByteCode(), rs.compute, refl, global, + ret->sourceVars); - dxbc->FillTraceLineInfo(*ret); - - // add fake inputs for semantics - for(size_t i = 0; i < dxbc->GetDXBCByteCode()->GetNumDeclarations(); i++) - { - const DXBCBytecode::Declaration &decl = dxbc->GetDXBCByteCode()->GetDeclaration(i); - - if(decl.declaration == OPCODE_DCL_INPUT && - (decl.operand.type == TYPE_INPUT_THREAD_ID || decl.operand.type == TYPE_INPUT_THREAD_GROUP_ID || - decl.operand.type == TYPE_INPUT_THREAD_ID_IN_GROUP || - decl.operand.type == TYPE_INPUT_THREAD_ID_IN_GROUP_FLATTENED)) + for(int i = 0; i < 3; i++) { - ShaderVariable v; + state.semantics.GroupID[i] = groupid[i]; + state.semantics.ThreadID[i] = threadid[i]; + } - v.name = decl.operand.toString(dxbc->GetReflection(), ToString::IsDecl); - v.rows = 1; - v.type = VarType::UInt; + ret->constantBlocks = global.constantBlocks; - switch(decl.operand.type) + // add fake inputs for semantics + for(size_t i = 0; i < dxbc->GetDXBCByteCode()->GetNumDeclarations(); i++) + { + const DXBCBytecode::Declaration &decl = dxbc->GetDXBCByteCode()->GetDeclaration(i); + + if(decl.declaration == OPCODE_DCL_INPUT && + (decl.operand.type == TYPE_INPUT_THREAD_ID || + decl.operand.type == TYPE_INPUT_THREAD_GROUP_ID || + decl.operand.type == TYPE_INPUT_THREAD_ID_IN_GROUP || + decl.operand.type == TYPE_INPUT_THREAD_ID_IN_GROUP_FLATTENED)) { - case TYPE_INPUT_THREAD_GROUP_ID: - memcpy(v.value.u32v.data(), state.semantics.GroupID, sizeof(uint32_t) * 3); - v.columns = 3; - break; - case TYPE_INPUT_THREAD_ID_IN_GROUP: - memcpy(v.value.u32v.data(), state.semantics.ThreadID, sizeof(uint32_t) * 3); - v.columns = 3; - break; - case TYPE_INPUT_THREAD_ID: - v.value.u32v[0] = - state.semantics.GroupID[0] * dxbc->GetReflection()->DispatchThreadsDimension[0] + - state.semantics.ThreadID[0]; - v.value.u32v[1] = - state.semantics.GroupID[1] * dxbc->GetReflection()->DispatchThreadsDimension[1] + - state.semantics.ThreadID[1]; - v.value.u32v[2] = - state.semantics.GroupID[2] * dxbc->GetReflection()->DispatchThreadsDimension[2] + - state.semantics.ThreadID[2]; - v.columns = 3; - break; - case TYPE_INPUT_THREAD_ID_IN_GROUP_FLATTENED: - v.value.u32v[0] = - state.semantics.ThreadID[2] * dxbc->GetReflection()->DispatchThreadsDimension[0] * - dxbc->GetReflection()->DispatchThreadsDimension[1] + - state.semantics.ThreadID[1] * dxbc->GetReflection()->DispatchThreadsDimension[0] + - state.semantics.ThreadID[0]; - v.columns = 1; - break; - default: v.columns = 4; break; - } + ShaderVariable v; - ret->inputs.push_back(v); + v.name = decl.operand.toString(dxbc->GetReflection(), ToString::IsDecl); + v.rows = 1; + v.type = VarType::UInt; + + switch(decl.operand.type) + { + case TYPE_INPUT_THREAD_GROUP_ID: + memcpy(v.value.u32v.data(), state.semantics.GroupID, sizeof(uint32_t) * 3); + v.columns = 3; + break; + case TYPE_INPUT_THREAD_ID_IN_GROUP: + memcpy(v.value.u32v.data(), state.semantics.ThreadID, sizeof(uint32_t) * 3); + v.columns = 3; + break; + case TYPE_INPUT_THREAD_ID: + v.value.u32v[0] = + state.semantics.GroupID[0] * dxbc->GetReflection()->DispatchThreadsDimension[0] + + state.semantics.ThreadID[0]; + v.value.u32v[1] = + state.semantics.GroupID[1] * dxbc->GetReflection()->DispatchThreadsDimension[1] + + state.semantics.ThreadID[1]; + v.value.u32v[2] = + state.semantics.GroupID[2] * dxbc->GetReflection()->DispatchThreadsDimension[2] + + state.semantics.ThreadID[2]; + v.columns = 3; + break; + case TYPE_INPUT_THREAD_ID_IN_GROUP_FLATTENED: + v.value.u32v[0] = + state.semantics.ThreadID[2] * dxbc->GetReflection()->DispatchThreadsDimension[0] * + dxbc->GetReflection()->DispatchThreadsDimension[1] + + state.semantics.ThreadID[1] * dxbc->GetReflection()->DispatchThreadsDimension[0] + + state.semantics.ThreadID[0]; + v.columns = 1; + break; + default: v.columns = 4; break; + } + + ret->inputs.push_back(v); + } } } + else + { + RDCERR("TODO ADD DXIL COMPUTE SHADER DEBUGGER SUPPORT"); + } + + if(ret) + dxbc->FillTraceLineInfo(*ret); return ret; } diff --git a/renderdoc/driver/shaders/dxbc/dxbc_container.cpp b/renderdoc/driver/shaders/dxbc/dxbc_container.cpp index d397673f7..61627c981 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_container.cpp +++ b/renderdoc/driver/shaders/dxbc/dxbc_container.cpp @@ -657,6 +657,10 @@ void DXBCContainer::FillTraceLineInfo(ShaderDebugTrace &trace) const m_DebugInfo->GetLocals(this, i, op.offset, trace.instInfo[i].sourceVars); } } + else if(m_DXILByteCode) + { + RDCERR("DXIL FillTraceLineInfo not implemented"); + } } void DXBCContainer::StripChunk(bytebuf &ByteCode, uint32_t fourcc)