diff --git a/renderdoc/driver/d3d12/d3d12_common.cpp b/renderdoc/driver/d3d12/d3d12_common.cpp index 0b6c4fb16..2f0b146eb 100644 --- a/renderdoc/driver/d3d12/d3d12_common.cpp +++ b/renderdoc/driver/d3d12/d3d12_common.cpp @@ -1089,6 +1089,50 @@ rdcstr PIX3DecodeEventString(const UINT64 *pData, UINT64 &color) return formatString; } +D3D12_SAMPLER_DESC2 ConvertStaticSampler(const D3D12_STATIC_SAMPLER_DESC1 &samp) +{ + D3D12_SAMPLER_DESC2 desc; + desc.Filter = samp.Filter; + desc.AddressU = samp.AddressU; + desc.AddressV = samp.AddressV; + desc.AddressW = samp.AddressW; + desc.MipLODBias = samp.MipLODBias; + desc.MaxAnisotropy = samp.MaxAnisotropy; + desc.ComparisonFunc = samp.ComparisonFunc; + switch(samp.BorderColor) + { + default: + case D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK: + desc.FloatBorderColor[0] = desc.FloatBorderColor[1] = desc.FloatBorderColor[2] = + desc.FloatBorderColor[3] = 0.0f; + break; + case D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK: + desc.FloatBorderColor[0] = desc.FloatBorderColor[1] = desc.FloatBorderColor[2] = 0.0f; + desc.FloatBorderColor[3] = 1.0f; + break; + case D3D12_STATIC_BORDER_COLOR_OPAQUE_WHITE: + desc.FloatBorderColor[0] = desc.FloatBorderColor[1] = desc.FloatBorderColor[2] = + desc.FloatBorderColor[3] = 1.0f; + break; + case D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK_UINT: + desc.UintBorderColor[0] = desc.UintBorderColor[1] = desc.UintBorderColor[2] = 0; + desc.UintBorderColor[3] = 1; + // this flag is optional in D3D, add it here to ensure we can check it elsewhere + desc.Flags |= D3D12_SAMPLER_FLAG_UINT_BORDER_COLOR; + break; + case D3D12_STATIC_BORDER_COLOR_OPAQUE_WHITE_UINT: + desc.UintBorderColor[0] = desc.UintBorderColor[1] = desc.UintBorderColor[2] = + desc.UintBorderColor[3] = 1; + // this flag is optional in D3D, add it here to ensure we can check it elsewhere + desc.Flags |= D3D12_SAMPLER_FLAG_UINT_BORDER_COLOR; + break; + } + desc.MinLOD = samp.MinLOD; + desc.MaxLOD = samp.MaxLOD; + desc.Flags = samp.Flags; + return desc; +} + D3D12_DEPTH_STENCILOP_DESC1 Upconvert(const D3D12_DEPTH_STENCILOP_DESC &face) { D3D12_DEPTH_STENCILOP_DESC1 ret = {}; diff --git a/renderdoc/driver/d3d12/d3d12_common.h b/renderdoc/driver/d3d12/d3d12_common.h index 8ff7d1960..ece0b7846 100644 --- a/renderdoc/driver/d3d12/d3d12_common.h +++ b/renderdoc/driver/d3d12/d3d12_common.h @@ -233,6 +233,7 @@ struct BarrierSet D3D12_DEPTH_STENCIL_DESC2 Upconvert(const D3D12_DEPTH_STENCIL_DESC1 &desc); D3D12_RASTERIZER_DESC2 Upconvert(const D3D12_RASTERIZER_DESC &desc); +D3D12_SAMPLER_DESC2 ConvertStaticSampler(const D3D12_STATIC_SAMPLER_DESC1 &samp); ShaderStageMask ConvertVisibility(D3D12_SHADER_VISIBILITY ShaderVisibility); UINT GetNumSubresources(ID3D12Device *dev, const D3D12_RESOURCE_DESC *desc); diff --git a/renderdoc/driver/d3d12/d3d12_replay.cpp b/renderdoc/driver/d3d12/d3d12_replay.cpp index 91e2a177b..06c556774 100644 --- a/renderdoc/driver/d3d12/d3d12_replay.cpp +++ b/renderdoc/driver/d3d12/d3d12_replay.cpp @@ -2346,6 +2346,12 @@ rdcarray D3D12Replay::GetDescriptors(ResourceId descriptorStore, ID3D12DeviceChild *res = rm->GetCurrentAs(descriptorStore); + if(WrappedID3D12RootSignature::IsAlloc(res)) + { + // root signature descriptor storage is for static samplers + return ret; + } + if(WrappedID3D12PipelineState::IsAlloc(res)) { const D3D12RenderState &rs = m_pDevice->GetQueue()->GetCommandData()->m_RenderState; @@ -2502,6 +2508,32 @@ rdcarray D3D12Replay::GetSamplerDescriptors(ResourceId descri ID3D12DeviceChild *res = rm->GetCurrentAs(descriptorStore); + if(WrappedID3D12RootSignature::IsAlloc(res)) + { + WrappedID3D12RootSignature *sig = (WrappedID3D12RootSignature *)res; + size_t dst = 0; + for(const DescriptorRange &r : ranges) + { + uint32_t staticIdx = r.offset; + + for(uint32_t i = 0; i < r.count; i++) + { + if(staticIdx >= sig->sig.StaticSamplers.size()) + { + // silently drop out of bounds descriptor reads + } + else + { + FillSamplerDescriptor(ret[dst], ConvertStaticSampler(sig->sig.StaticSamplers[staticIdx])); + ret[dst].creationTimeConstant = true; + } + dst++; + staticIdx++; + } + } + return ret; + } + if(WrappedID3D12PipelineState::IsAlloc(res)) { // root constants, not sampler data @@ -2577,6 +2609,20 @@ rdcarray D3D12Replay::GetDescriptorAccess() for(DescriptorAccess &access : ret) { + const D3D12RenderState::RootSignature &rootSig = pipe->IsGraphics() ? rs.graphics : rs.compute; + + uint32_t rootIndex = (uint32_t)access.byteSize; + access.byteSize = 1; + + // access off the end of the root signature list indicates a static sampler. We virtualise + // this as root signature descriptor storage + if(access.type == DescriptorType::Sampler && rootIndex >= rootSig.sigelems.size()) + { + access.descriptorStore = rm->GetOriginalID(rootSig.rootsig); + // the access byteOffset is the index of the static sampler + continue; + } + if(access.type == DescriptorType::Sampler) access.descriptorStore = samplerHeap ? rm->GetOriginalID(samplerHeap->GetResourceID()) : ResourceId(); @@ -2584,10 +2630,7 @@ rdcarray D3D12Replay::GetDescriptorAccess() access.descriptorStore = resourceHeap ? rm->GetOriginalID(resourceHeap->GetResourceID()) : ResourceId(); - uint32_t rootIndex = (uint32_t)access.byteSize; - const D3D12RenderState::SignatureElement &rootEl = - pipe->IsGraphics() ? rs.graphics.sigelems[rootIndex] : rs.compute.sigelems[rootIndex]; - access.byteSize = 1; + const D3D12RenderState::SignatureElement &rootEl = rootSig.sigelems[rootIndex]; // this indicates a root parameter if(access.byteOffset == ~0U) @@ -2598,7 +2641,6 @@ rdcarray D3D12Replay::GetDescriptorAccess() // other types of virtual constants to handle we can use the pipeline state directly access.descriptorStore = rm->GetOriginalID(pipe->GetResourceID()); access.byteOffset = rootIndex; - access.byteSize = 1; } else { diff --git a/renderdoc/driver/d3d12/d3d12_resources.cpp b/renderdoc/driver/d3d12/d3d12_resources.cpp index 99b19bc3c..7c0062e01 100644 --- a/renderdoc/driver/d3d12/d3d12_resources.cpp +++ b/renderdoc/driver/d3d12/d3d12_resources.cpp @@ -713,6 +713,21 @@ rdcpair FindMatchingRootParameter(const D3D12RootSignature * } } + // if not found above, and looking for samplers, look at static samplers next + if(rangeType == D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER) + { + // indicate that we're looking up static samplers + uint32_t numRoots = (uint32_t)sig->Parameters.size(); + for(uint32_t samp = 0; samp < sig->StaticSamplers.size(); samp++) + { + if(sig->StaticSamplers[samp].RegisterSpace == space && + sig->StaticSamplers[samp].ShaderRegister == bind) + { + return {numRoots, samp}; + } + } + } + return {~0U, 0}; } diff --git a/renderdoc/driver/d3d12/d3d12_shaderdebug.cpp b/renderdoc/driver/d3d12/d3d12_shaderdebug.cpp index 35e1afa94..685ffcf83 100644 --- a/renderdoc/driver/d3d12/d3d12_shaderdebug.cpp +++ b/renderdoc/driver/d3d12/d3d12_shaderdebug.cpp @@ -619,43 +619,7 @@ D3D12Descriptor D3D12DebugAPIWrapper::FindDescriptor(DXBCBytecode::OperandType t { if(samp.RegisterSpace == slot.registerSpace && samp.ShaderRegister == slot.shaderRegister) { - D3D12_SAMPLER_DESC2 desc; - - desc.Filter = samp.Filter; - desc.AddressU = samp.AddressU; - desc.AddressV = samp.AddressV; - desc.AddressW = samp.AddressW; - desc.MipLODBias = samp.MipLODBias; - desc.MaxAnisotropy = samp.MaxAnisotropy; - desc.ComparisonFunc = samp.ComparisonFunc; - switch(samp.BorderColor) - { - default: - case D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK: - desc.FloatBorderColor[0] = desc.FloatBorderColor[1] = desc.FloatBorderColor[2] = - desc.FloatBorderColor[3] = 0.0f; - break; - case D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK: - desc.FloatBorderColor[0] = desc.FloatBorderColor[1] = desc.FloatBorderColor[2] = 0.0f; - desc.FloatBorderColor[3] = 1.0f; - break; - case D3D12_STATIC_BORDER_COLOR_OPAQUE_WHITE: - desc.FloatBorderColor[0] = desc.FloatBorderColor[1] = desc.FloatBorderColor[2] = - desc.FloatBorderColor[3] = 1.0f; - break; - case D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK_UINT: - desc.UintBorderColor[0] = desc.UintBorderColor[1] = desc.UintBorderColor[2] = 0; - desc.UintBorderColor[3] = 1; - break; - case D3D12_STATIC_BORDER_COLOR_OPAQUE_WHITE_UINT: - desc.UintBorderColor[0] = desc.UintBorderColor[1] = desc.UintBorderColor[2] = - desc.UintBorderColor[3] = 1; - break; - } - desc.MinLOD = samp.MinLOD; - desc.MaxLOD = samp.MaxLOD; - desc.Flags = samp.Flags; - + D3D12_SAMPLER_DESC2 desc = ConvertStaticSampler(samp); descriptor.Init(&desc); return descriptor; } diff --git a/renderdoc/driver/vulkan/vk_common.h b/renderdoc/driver/vulkan/vk_common.h index bb3dba6c7..87eb0025e 100644 --- a/renderdoc/driver/vulkan/vk_common.h +++ b/renderdoc/driver/vulkan/vk_common.h @@ -716,6 +716,9 @@ struct DescriptorSetSlot // used for buffers and inline blocks. We could steal some bits here if we needed them since 48 // bits would be plenty for a long time. + // + // Immutable samplers set this to 1 to indicate for replay purposes that the sampler came from an + // immutable binding when looking purely at the descriptor without knowing its layout VkDeviceSize offset; // resource IDs are kept separate rather than overlapping/union'ing with other types. This diff --git a/renderdoc/driver/vulkan/vk_info.cpp b/renderdoc/driver/vulkan/vk_info.cpp index a1832d6b6..c61ead8da 100644 --- a/renderdoc/driver/vulkan/vk_info.cpp +++ b/renderdoc/driver/vulkan/vk_info.cpp @@ -453,6 +453,8 @@ void DescSetLayout::CreateBindingsArray(BindingStorage &bindingStorage, uint32_t // samplers set the type from the layout. That way even if the descriptor is never // written we still process immutable samplers properly. bindingStorage.binds[i][a].type = convert(bindings[i].layoutDescType); + + bindingStorage.binds[i][a].offset = 1; } } diff --git a/renderdoc/driver/vulkan/vk_replay.cpp b/renderdoc/driver/vulkan/vk_replay.cpp index e1312ef34..d37be6d40 100644 --- a/renderdoc/driver/vulkan/vk_replay.cpp +++ b/renderdoc/driver/vulkan/vk_replay.cpp @@ -2516,6 +2516,10 @@ void VulkanReplay::FillSamplerDescriptor(SamplerDescriptor &dstel, const Descrip dstel.unnormalized = sampl.unnormalizedCoordinates; dstel.seamlessCubemaps = sampl.seamless; + // immutable samplers set the offset to non-zero so that we can check it here without knowing what + // layout this descriptor binding came from + dstel.creationTimeConstant = srcel.offset != 0; + if(sampl.ycbcr != ResourceId()) { const VulkanCreationInfo::YCbCrSampler &ycbcr = c.m_YCbCrSampler[sampl.ycbcr];