diff --git a/renderdoc/driver/d3d12/d3d12_device_wrap.cpp b/renderdoc/driver/d3d12/d3d12_device_wrap.cpp index d4018ce51..4a7d7b67f 100644 --- a/renderdoc/driver/d3d12/d3d12_device_wrap.cpp +++ b/renderdoc/driver/d3d12/d3d12_device_wrap.cpp @@ -1078,6 +1078,11 @@ bool WrappedID3D12Device::Serialise_CreateRootSignature(SerialiserType &ser, UIN wrapped->sig = GetShaderCache()->GetRootSig(pBlobWithRootSignature, (size_t)blobLengthInBytes); + if(wrapped->sig.Flags & D3D12_ROOT_SIGNATURE_FLAG_LOCAL_ROOT_SIGNATURE) + wrapped->localRootSigIdx = + GetResourceManager()->GetRaytracingResourceAndUtilHandler()->RegisterLocalRootSig( + wrapped->sig); + { StructuredSerialiser structuriser(ser.GetStructuredFile().chunks.back(), &GetChunkName); structuriser.SetUserData(GetResourceManager()); @@ -1143,6 +1148,11 @@ HRESULT WrappedID3D12Device::CreateRootSignature(UINT nodeMask, const void *pBlo wrapped->sig = GetShaderCache()->GetRootSig(pBlobWithRootSignature, blobLengthInBytes); + if(wrapped->sig.Flags & D3D12_ROOT_SIGNATURE_FLAG_LOCAL_ROOT_SIGNATURE) + wrapped->localRootSigIdx = + GetResourceManager()->GetRaytracingResourceAndUtilHandler()->RegisterLocalRootSig( + wrapped->sig); + if(!m_BindlessResourceUseActive) { // force ref-all-resources if the heap is directly indexed because we can't track resource diff --git a/renderdoc/driver/d3d12/d3d12_device_wrap5.cpp b/renderdoc/driver/d3d12/d3d12_device_wrap5.cpp index e2a0253df..1a09b86f4 100644 --- a/renderdoc/driver/d3d12/d3d12_device_wrap5.cpp +++ b/renderdoc/driver/d3d12/d3d12_device_wrap5.cpp @@ -250,6 +250,264 @@ HRESULT WrappedID3D12Device::CreateStateObject(const D3D12_STATE_OBJECT_DESC *pD SCOPED_SERIALISE_CHUNK(D3D12Chunk::Device_CreateStateObject); Serialise_CreateStateObject(ser, pDesc, riid, (void **)&wrapped); + D3D12ShaderExportDatabase *exports = wrapped->exports; + + // store the default local root signature - if we only find one in the whole state object then it becomes default + ID3D12RootSignature *defaultRoot = NULL; + bool unassocDefaultValid = false; + bool explicitDefault = false; + bool unassocDXILDefaultValid = false; + uint32_t dxilDefaultRoot = ~0U; + + rdcarray> explicitRootSigAssocs; + rdcarray explicitDefaultDxilAssocs; + rdcarray> explicitDxilAssocs; + rdcflatmap dxilLocalRootSigs; + + rdcarray> inheritedRootSigAssocs; + rdcarray> inheritedDXILRootSigAssocs; + rdcflatmap inheritedDXILLocalRootSigs; + + // fill shader exports list as well as local root signature lookups. + // shader exports that can be queried come from two sources: + // - hit groups + // - exports from a DXIL library + // - exports from a collection + for(size_t i = 0; i < subobjects.size(); i++) + { + if(subobjects[i].Type == D3D12_STATE_SUBOBJECT_TYPE_HIT_GROUP) + { + D3D12_HIT_GROUP_DESC *desc = (D3D12_HIT_GROUP_DESC *)subobjects[i].pDesc; + exports->AddExport(StringFormat::Wide2UTF8(desc->HitGroupExport)); + + rdcarray shaders; + if(desc->IntersectionShaderImport) + shaders.push_back(StringFormat::Wide2UTF8(desc->IntersectionShaderImport)); + if(desc->AnyHitShaderImport) + shaders.push_back(StringFormat::Wide2UTF8(desc->AnyHitShaderImport)); + if(desc->ClosestHitShaderImport) + shaders.push_back(StringFormat::Wide2UTF8(desc->ClosestHitShaderImport)); + + // register the hit group so that if we get associations with the individual shaders we + // can apply that up to the hit group + exports->AddLastHitGroupShaders(std::move(shaders)); + } + else if(subobjects[i].Type == D3D12_STATE_SUBOBJECT_TYPE_DXIL_LIBRARY) + { + D3D12_DXIL_LIBRARY_DESC *dxil = (D3D12_DXIL_LIBRARY_DESC *)subobjects[i].pDesc; + + if(dxil->NumExports > 0) + { + for(UINT e = 0; e < dxil->NumExports; e++) + { + // Name is always the name used for exports - if renaming then the renamed-from name + // is only used to lookup in the dxil library and not for any associations-by-name + exports->AddExport(StringFormat::Wide2UTF8(dxil->pExports[e].Name)); + } + } + else + { + // hard part, we need to parse the DXIL to get the entry points + DXBC::DXBCContainer container( + bytebuf((byte *)dxil->DXILLibrary.pShaderBytecode, dxil->DXILLibrary.BytecodeLength), + rdcstr(), GraphicsAPI::D3D12, ~0U, ~0U); + + rdcarray entries = container.GetEntryPoints(); + + for(const ShaderEntryPoint &e : entries) + exports->AddExport(e.name); + } + + // TODO: register local root signature subobjects into dxilLocalRootSigs. Override + // anything in there, unlike the import from a collection below. + } + else if(subobjects[i].Type == D3D12_STATE_SUBOBJECT_TYPE_EXISTING_COLLECTION) + { + D3D12_EXISTING_COLLECTION_DESC *coll = + (D3D12_EXISTING_COLLECTION_DESC *)subobjects[i].pDesc; + + WrappedID3D12StateObject *stateObj = (WrappedID3D12StateObject *)coll->pExistingCollection; + + if(coll->NumExports > 0) + { + for(UINT e = 0; e < coll->NumExports; e++) + exports->InheritCollectionExport( + stateObj->exports, StringFormat::Wide2UTF8(coll->pExports[e].Name), + StringFormat::Wide2UTF8(coll->pExports[e].ExportToRename + ? coll->pExports[e].ExportToRename + : coll->pExports[e].Name)); + } + else + { + exports->InheritAllCollectionExports(stateObj->exports); + } + + // inherit explicit associations from the collection as lowest priority + inheritedRootSigAssocs.append(stateObj->exports->danglingRootSigAssocs); + inheritedDXILRootSigAssocs.append(stateObj->exports->danglingDXILRootSigAssocs); + + for(auto it = stateObj->exports->danglingDXILLocalRootSigs.begin(); + it != stateObj->exports->danglingDXILLocalRootSigs.end(); ++it) + { + // don't override any local root signatures with the same name we already have. Not sure + // how this conflict should be resolved properly? + if(dxilLocalRootSigs.find(it->first) == dxilLocalRootSigs.end()) + dxilLocalRootSigs[it->first] = it->second; + } + } + else if(subobjects[i].Type == D3D12_STATE_SUBOBJECT_TYPE_LOCAL_ROOT_SIGNATURE) + { + // ignore these if an explicit default association has been made + if(!explicitDefault) + { + // if multiple root signatures are defined, then there can't be an unspecified default + unassocDefaultValid = defaultRoot != NULL; + defaultRoot = ((D3D12_LOCAL_ROOT_SIGNATURE *)subobjects[i].pDesc)->pLocalRootSignature; + } + } + else if(subobjects[i].Type == D3D12_STATE_SUBOBJECT_TYPE_SUBOBJECT_TO_EXPORTS_ASSOCIATION) + { + D3D12_SUBOBJECT_TO_EXPORTS_ASSOCIATION *assoc = + (D3D12_SUBOBJECT_TO_EXPORTS_ASSOCIATION *)subobjects[i].pDesc; + + const D3D12_STATE_SUBOBJECT *other = assoc->pSubobjectToAssociate; + + // only care about associating local root signatures + if(other->Type == D3D12_STATE_SUBOBJECT_TYPE_LOCAL_ROOT_SIGNATURE) + { + ID3D12RootSignature *root = + ((D3D12_LOCAL_ROOT_SIGNATURE *)other->pDesc)->pLocalRootSignature; + + WrappedID3D12RootSignature *wrappedRoot = (WrappedID3D12RootSignature *)root; + + // if there are no exports this is an explicit default association. We assume this + // matches and doesn't conflict + if(assoc->NumExports == NULL) + { + explicitDefault = true; + defaultRoot = root; + } + else + { + // otherwise record the explicit associations - these may refer to exports that + // haven't been seen yet so we record them locally + for(UINT e = 0; e < assoc->NumExports; e++) + explicitRootSigAssocs.push_back( + {StringFormat::Wide2UTF8(assoc->pExports[e]), wrappedRoot->localRootSigIdx}); + } + } + } + else if(subobjects[i].Type == D3D12_STATE_SUBOBJECT_TYPE_DXIL_SUBOBJECT_TO_EXPORTS_ASSOCIATION) + { + D3D12_DXIL_SUBOBJECT_TO_EXPORTS_ASSOCIATION *assoc = + (D3D12_DXIL_SUBOBJECT_TO_EXPORTS_ASSOCIATION *)subobjects[i].pDesc; + + rdcstr other = StringFormat::Wide2UTF8(assoc->SubobjectToAssociate); + + // we can't tell yet if this is a local root signature or not so we have to store it regardless + { + // if there are no exports this is an explicit default association, but we don't know if + // it's for a local root signature... + if(assoc->NumExports == NULL) + { + explicitDefaultDxilAssocs.push_back(other); + } + else + { + // otherwise record the explicit associations - these may refer to exports that + // haven't been seen yet so we record them locally + for(UINT e = 0; e < assoc->NumExports; e++) + explicitDxilAssocs.push_back({StringFormat::Wide2UTF8(assoc->pExports[e]), other}); + } + } + } + } + + // now that we have all exports registered, apply all associations we have in order of + // priority to get the right + + for(size_t i = 0; i < explicitRootSigAssocs.size(); i++) + { + exports->ApplyRoot(SubObjectPriority::CodeExplicitAssociation, + explicitRootSigAssocs[i].first, explicitRootSigAssocs[i].second); + } + + if(explicitDefault) + { + WrappedID3D12RootSignature *wrappedRoot = (WrappedID3D12RootSignature *)defaultRoot; + + exports->ApplyDefaultRoot(SubObjectPriority::CodeExplicitDefault, + wrappedRoot->localRootSigIdx); + } + // shouldn't be possible to have both explicit and implicit defaults? + else if(unassocDefaultValid) + { + WrappedID3D12RootSignature *wrappedRoot = (WrappedID3D12RootSignature *)defaultRoot; + + exports->ApplyDefaultRoot(SubObjectPriority::CodeImplicitDefault, + wrappedRoot->localRootSigIdx); + } + + for(size_t i = 0; i < explicitDxilAssocs.size(); i++) + { + auto it = dxilLocalRootSigs.find(explicitDxilAssocs[i].second); + + if(it == dxilLocalRootSigs.end()) + continue; + + uint32_t localRootSigIdx = it->second; + + exports->ApplyRoot(SubObjectPriority::DXILExplicitAssociation, explicitDxilAssocs[i].first, + localRootSigIdx); + } + + for(size_t i = 0; i < explicitDefaultDxilAssocs.size(); i++) + { + auto it = dxilLocalRootSigs.find(explicitDefaultDxilAssocs[i]); + + if(it == dxilLocalRootSigs.end()) + continue; + + uint32_t localRootSigIdx = it->second; + + exports->ApplyDefaultRoot(SubObjectPriority::DXILExplicitDefault, localRootSigIdx); + + // only expect one local root signature - the array is because we can't tell the type of the + // default subobject when we encounter it + break; + } + + if(unassocDXILDefaultValid) + { + exports->ApplyDefaultRoot(SubObjectPriority::DXILImplicitDefault, dxilDefaultRoot); + } + + // we assume it's not possible to inherit two different explicit associations for a single export + + for(size_t i = 0; i < inheritedRootSigAssocs.size(); i++) + { + exports->ApplyRoot(SubObjectPriority::CollectionExplicitAssociation, + inheritedRootSigAssocs[i].first, inheritedRootSigAssocs[i].second); + } + for(size_t i = 0; i < inheritedDXILRootSigAssocs.size(); i++) + { + auto it = dxilLocalRootSigs.find(inheritedDXILRootSigAssocs[i].second); + + if(it == dxilLocalRootSigs.end()) + continue; + + uint32_t localRootSigIdx = it->second; + + exports->ApplyRoot(SubObjectPriority::CollectionExplicitAssociation, + inheritedDXILRootSigAssocs[i].first, localRootSigIdx); + } + + exports->danglingRootSigAssocs.swap(inheritedRootSigAssocs); + exports->danglingDXILRootSigAssocs.swap(inheritedDXILRootSigAssocs); + exports->danglingDXILLocalRootSigs.swap(dxilLocalRootSigs); + + exports->UpdateHitGroupAssociations(); + D3D12ResourceRecord *record = GetResourceManager()->AddResourceRecord(wrapped->GetResourceID()); record->type = Resource_PipelineState; record->Length = 0; diff --git a/renderdoc/driver/d3d12/d3d12_manager.cpp b/renderdoc/driver/d3d12/d3d12_manager.cpp index eb7777db7..e54a9d02b 100644 --- a/renderdoc/driver/d3d12/d3d12_manager.cpp +++ b/renderdoc/driver/d3d12/d3d12_manager.cpp @@ -861,6 +861,54 @@ void D3D12RaytracingResourceAndUtilHandler::InitReplayBlasPatchingResources() } } +uint32_t D3D12RaytracingResourceAndUtilHandler::RegisterLocalRootSig(const D3D12RootSignature &sig) +{ + rdcarray tableOffsets; + uint32_t offset = D3D12_SHADER_IDENTIFIER_SIZE_IN_BYTES; + for(uint32_t i = 0; i < sig.Parameters.size(); i++) + { + // constants are 4-byte aligned, everything else is 8-byte + if(sig.Parameters[i].ParameterType != D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS) + offset = AlignUp(offset, 8U); + + if(sig.Parameters[i].ParameterType == D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE) + tableOffsets.push_back(offset); + + if(sig.Parameters[i].ParameterType == D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS) + offset += sig.Parameters[i].Constants.Num32BitValues * sizeof(uint32_t); + else + offset += sizeof(uint64_t); + } + + // no patching needed if no tables + if(tableOffsets.empty()) + return ~0U; + + int idx = m_UniqueLocalRootSigs.indexOf(tableOffsets); + if(idx < 0) + { + idx = m_UniqueLocalRootSigs.count(); + m_UniqueLocalRootSigs.push_back(tableOffsets); + } + + m_LookupBufferDirty = true; + + return idx; +} + +void D3D12RaytracingResourceAndUtilHandler::RegisterExportDatabase(D3D12ShaderExportDatabase *db) +{ + m_ExportDatabases.push_back(db); + m_LookupBufferDirty = true; +} + +void D3D12RaytracingResourceAndUtilHandler::UnregisterExportDatabase(D3D12ShaderExportDatabase *db) +{ + m_ExportDatabases.push_back(db); + // don't dirty the lookup buffer here, there's not much value in recreating it just to reduce + // memory use - next time we need to add data we'll reclaim that. +} + D3D12GpuBufferAllocator *D3D12GpuBufferAllocator::m_bufferAllocator = NULL; bool D3D12GpuBufferAllocator::CopyBufferRegion(WrappedID3D12GraphicsCommandList *wrappedCmd, diff --git a/renderdoc/driver/d3d12/d3d12_manager.h b/renderdoc/driver/d3d12/d3d12_manager.h index 4f5100723..fe54cf289 100644 --- a/renderdoc/driver/d3d12/d3d12_manager.h +++ b/renderdoc/driver/d3d12/d3d12_manager.h @@ -1072,6 +1072,11 @@ public: void InitInternalResources(); + uint32_t RegisterLocalRootSig(const D3D12RootSignature &sig); + + void RegisterExportDatabase(D3D12ShaderExportDatabase *db); + void UnregisterExportDatabase(D3D12ShaderExportDatabase *db); + void ResizeSerialisationBuffer(UINT64 size); // buffer in UAV state for emitting AS queries to, CPU accessible/mappable @@ -1091,6 +1096,17 @@ private: HANDLE m_gpuSyncHandle; UINT64 m_gpuSyncCounter; D3D12AccStructPatchInfo m_accStructPatchInfo; + + // each unique set of descriptor table offsets are stored here, so any root signatures which only + // vary in ways that don't affect which tables are contained within them (and so don't need + // patching) will have a single entry in here + rdcarray> m_UniqueLocalRootSigs; + + // export databases that are alive + rdcarray m_ExportDatabases; + + // is the lookup buffer dirty and needs to be recreated with the latest data? + bool m_LookupBufferDirty = true; }; struct D3D12ResourceManagerConfiguration diff --git a/renderdoc/driver/d3d12/d3d12_resources.cpp b/renderdoc/driver/d3d12/d3d12_resources.cpp index 13cfc8921..9ebb38b05 100644 --- a/renderdoc/driver/d3d12/d3d12_resources.cpp +++ b/renderdoc/driver/d3d12/d3d12_resources.cpp @@ -792,6 +792,251 @@ void WrappedID3D12PipelineState::ProcessDescriptorAccess() } } +D3D12ShaderExportDatabase::D3D12ShaderExportDatabase(ResourceId id, + D3D12RaytracingResourceAndUtilHandler *rayManager, + ID3D12StateObjectProperties *obj) + : RefCounter12(NULL), objectOriginalId(id), m_RayManager(rayManager), m_StateObjectProps(obj) +{ + m_RayManager->RegisterExportDatabase(this); +} + +D3D12ShaderExportDatabase::~D3D12ShaderExportDatabase() +{ + for(D3D12ShaderExportDatabase *parent : parents) + { + SAFE_RELEASE(parent); + } + + m_RayManager->UnregisterExportDatabase(this); +} + +void D3D12ShaderExportDatabase::AddExport(const rdcstr &exportName) +{ + void *identifier = NULL; + if(m_StateObjectProps) + identifier = m_StateObjectProps->GetShaderIdentifier(StringFormat::UTF82Wide(exportName).c_str()); + const bool complete = identifier != NULL; + + { + // store the wrapped identifier here in this database, ready to return to the application in + // this object or any child objects. + wrappedIdentifiers.push_back({objectOriginalId, (uint32_t)ownExports.size()}); + + // store the unwrapping information to go into the giant lookup table + ownExports.push_back({}); + // if there's a real identifier then store it. But we track this regardless so that we can + // know the root signature for hitgroup-component shaders. If this export is inherited then it + // will be detected as incomplete and copied and patched in the child + if(identifier) + memcpy(ownExports.back().real, identifier, sizeof(ShaderIdentifier)); + // a local root signature may never get specified, so default to none + ownExports.back().rootSigPrio = SubObjectPriority::NotYetDefined; + ownExports.back().localRootSigIndex = 0xffff; + } + + if(exportName.size() > 2 && exportName[0] == '\x1' && exportName[1] == '?') + { + int idx = exportName.indexOf('@'); + if(idx > 2) + { + exportLookups.emplace_back(exportName, exportName.substr(2, idx - 2), complete); + return; + } + } + exportLookups.emplace_back(exportName, rdcstr(), complete); +} + +void D3D12ShaderExportDatabase::InheritCollectionExport(D3D12ShaderExportDatabase *existing, + const rdcstr &nameToExport, + const rdcstr &nameInExisting) +{ + if(!parents.contains(existing)) + { + parents.push_back(existing); + existing->AddRef(); + } + + for(size_t i = 0; i < existing->exportLookups.size(); i++) + { + if(existing->exportLookups[i].name == nameInExisting || + existing->exportLookups[i].altName == nameInExisting) + { + InheritExport(nameInExisting, existing, i); + + // if we renamed, now that we found the right export in the existing collection use the + // desired name going forward. This may still find the existing identifier as that hasn't + // necessarily changed + if(nameToExport != nameInExisting) + { + exportLookups.back().name = nameToExport; + exportLookups.back().altName.clear(); + + if(exportLookups.back().hitgroup) + hitGroups.back().first = nameToExport; + } + } + } +} + +void D3D12ShaderExportDatabase::InheritExport(const rdcstr &exportName, + D3D12ShaderExportDatabase *existing, size_t i) +{ + void *identifier = NULL; + if(m_StateObjectProps) + identifier = m_StateObjectProps->GetShaderIdentifier(StringFormat::UTF82Wide(exportName).c_str()); + + wrappedIdentifiers.push_back(existing->wrappedIdentifiers[i]); + exportLookups.push_back(existing->exportLookups[i]); + + // if this export wasn't previously complete, consider it exported in this object + // note that identifier may be NULL if this is a shader that can't be used on its own like any + // hit, but we want to keep it in our export list so we can track its root signature to update + // the hit group's root signature. Since there is only one level of collection => RT PSO this + // won't cause too much wasted exports + // we don't inherit non-complete identifiers when doing AddToStateObject so this doesn't apply + if(!exportLookups.back().complete) + { + ownExports.push_back({}); + + // we expect this identifier to have come from the object we're inheriting + RDCASSERTEQUAL(wrappedIdentifiers.back().id, existing->objectOriginalId); + // which means we can copy any root signature it had associated even if it wasn't complete + ownExports.back() = existing->ownExports[wrappedIdentifiers.back().index]; + + // now set the identifier, if we got one + if(identifier) + memcpy(ownExports.back().real, identifier, sizeof(ShaderIdentifier)); + + // and re-point this to point to ourselves when queried as we have the best data for it. + wrappedIdentifiers.back() = {objectOriginalId, (uint32_t)ownExports.size()}; + + // if this is an incomplete hitgroup, also grab the hitgroup component data + if(exportLookups.back().hitgroup) + { + for(size_t h = 0; h < existing->hitGroups.size(); h++) + { + if(existing->hitGroups[h].first == exportName) + { + hitGroups.push_back(existing->hitGroups[h]); + break; + } + } + } + } +} + +void D3D12ShaderExportDatabase::ApplyRoot(SubObjectPriority priority, const rdcstr &exportName, + uint32_t localRootSigIndex) +{ + for(size_t i = 0; i < exportLookups.size(); i++) + { + if(exportLookups[i].name == exportName || exportLookups[i].altName == exportName) + { + ApplyRoot(wrappedIdentifiers[i], priority, localRootSigIndex); + break; + } + } +} + +void D3D12ShaderExportDatabase::ApplyRoot(const ShaderIdentifier &identifier, + SubObjectPriority priority, uint32_t localRootSigIndex) +{ + if(identifier.id == objectOriginalId) + { + // set this anywhere we have a looser/lower priority association already (including the most + // common case presumably where one isn't set at all) + ExportedIdentifier &exported = ownExports[identifier.index]; + if(exported.rootSigPrio < priority) + { + exported.rootSigPrio = priority; + exported.localRootSigIndex = (uint16_t)localRootSigIndex; + } + } +} + +void D3D12ShaderExportDatabase::AddLastHitGroupShaders(rdcarray &&shaders) +{ + exportLookups.back().hitgroup = true; + hitGroups.emplace_back(exportLookups.back().name, shaders); +} + +void D3D12ShaderExportDatabase::UpdateHitGroupAssociations() +{ + // for each hit group + for(size_t h = 0; h < hitGroups.size(); h++) + { + // find it in the exports, as it could have been dangling before + for(size_t e = 0; e < exportLookups.size(); e++) + { + if(hitGroups[h].first == exportLookups[e].name) + { + // if the export is our own (ie. not complete and finished in a parent), we might need to + // update its root sig + if(wrappedIdentifiers[e].id == objectOriginalId) + { + // if the hit group got a code association already we assume it must match, but a DXIL + // association or a default association could be overridden since it's unclear if a + // hitgroup is a 'candidate' for default + if(ownExports[wrappedIdentifiers[e].index].rootSigPrio != + SubObjectPriority::CodeExplicitAssociation) + { + // for each export, find it and try to update the root signature + for(const rdcstr &shaderExport : hitGroups[h].second) + { + for(size_t e2 = 0; e2 < exportLookups.size(); e2++) + { + if(shaderExport == exportLookups[e2].name || shaderExport == exportLookups[e2].altName) + { + RDCASSERTEQUAL(wrappedIdentifiers[e2].id, objectOriginalId); + uint32_t idx = wrappedIdentifiers[e2].index; + ApplyRoot(wrappedIdentifiers[e], ownExports[idx].rootSigPrio, + ownExports[idx].localRootSigIndex); + + // don't keep looking at exports, we found this shader + break; + } + } + + // if we've inherited an explicit code association from a component shader, that + // also must match so we can stop looking. Otherwise we keep looking to try and find + // a 'better' association that can't be overridden + if(ownExports[wrappedIdentifiers[e].index].rootSigPrio == + SubObjectPriority::CodeExplicitAssociation) + break; + } + } + } + + // found this hit group, don't keep looking + break; + } + } + } +} + +void D3D12ShaderExportDatabase::InheritAllCollectionExports(D3D12ShaderExportDatabase *existing) +{ + if(!parents.contains(existing)) + { + parents.push_back(existing); + existing->AddRef(); + } + + wrappedIdentifiers.reserve(wrappedIdentifiers.size() + existing->wrappedIdentifiers.size()); + exportLookups.reserve(exportLookups.size() + existing->exportLookups.size()); + for(size_t i = 0; i < existing->exportLookups.size(); i++) + { + InheritExport(existing->exportLookups[i].name, existing, i); + } +} + +void D3D12ShaderExportDatabase::ApplyDefaultRoot(SubObjectPriority priority, + uint32_t localRootSigIndex) +{ + for(size_t i = 0; i < wrappedIdentifiers.size(); i++) + ApplyRoot(wrappedIdentifiers[i], priority, localRootSigIndex); +} + UINT GetPlaneForSubresource(ID3D12Resource *res, int Subresource) { D3D12_RESOURCE_DESC desc = res->GetDesc(); diff --git a/renderdoc/driver/d3d12/d3d12_resources.h b/renderdoc/driver/d3d12/d3d12_resources.h index fd0fdcbcd..721096e03 100644 --- a/renderdoc/driver/d3d12/d3d12_resources.h +++ b/renderdoc/driver/d3d12/d3d12_resources.h @@ -911,17 +911,162 @@ public: } }; +// the priorities of subobject associations. Default associations are not(?) inherited from +// collections into PSOs, and are not inherited from DXIL libraries into state objects. +// explicit associations are passed down from collection to state object but have the lowest priority of them all +enum class SubObjectPriority : uint16_t +{ + NotYetDefined, + // an explicit association in a collection - either from DXIL or code matters not by the time we + // get to a PSO including the collection + CollectionExplicitAssociation, + // subobject declared in DXIL in the same library but not associated at all + DXILImplicitDefault, + // subobject declared in DXIL and defaulted but just with an empty export string' + DXILExplicitDefault, + // subobject declared in DXIL and explicitly associated to an export + DXILExplicitAssociation, + // Object declared in code but not associated at all + CodeImplicitDefault, + // Object declared in code and defaulted with NULL export array + CodeExplicitDefault, + // Object declared in code and explicitly associated to an export + CodeExplicitAssociation, +}; + typedef WrappedID3D12PipelineState::ShaderEntry WrappedID3D12Shader; +struct D3D12ShaderExportDatabase : public RefCounter12 +{ +public: + D3D12ShaderExportDatabase(ResourceId id, D3D12RaytracingResourceAndUtilHandler *rayManager, + ID3D12StateObjectProperties *obj); + ~D3D12ShaderExportDatabase(); + + ResourceId GetResourceId() { return objectOriginalId; } + + // register our own newly created export + void AddExport(const rdcstr &exportName); + + // import some or all of a collection's exports + void InheritCollectionExport(D3D12ShaderExportDatabase *existing, const rdcstr &nameToExport, + const rdcstr &nameInExisting); + void InheritAllCollectionExports(D3D12ShaderExportDatabase *existing); + + // we only apply root signature associations to our own exports. Anything that was considered exported + // in a parent object was already final and either had a local root signature, or didn't need one at all. + void ApplyDefaultRoot(SubObjectPriority priority, uint32_t localRootSigIndex); + void ApplyRoot(SubObjectPriority priority, const rdcstr &exportName, uint32_t localRootSigIndex); + + // register a hit group for local root sig inheritance + void AddLastHitGroupShaders(rdcarray &&shaders); + void UpdateHitGroupAssociations(); + + void *GetShaderIdentifier(const rdcstr &exportName) + { + RDCCOMPILE_ASSERT(sizeof(D3D12ShaderExportDatabase::ShaderIdentifier) == + D3D12_SHADER_IDENTIFIER_SIZE_IN_BYTES, + "Shader Identifier is wrongly sized"); + for(size_t i = 0; i < exportLookups.size(); i++) + if(exportLookups[i].name == exportName || exportLookups[i].altName == exportName) + return exportLookups[i].complete ? &wrappedIdentifiers[i] : NULL; + return NULL; + } + + // these are not technically part of the 'exports' interface but they are very helpful to keep + // around at the same time. These are explicit associations which might yet apply to future + // exports in child state objects + rdcarray> danglingRootSigAssocs; + rdcarray> danglingDXILRootSigAssocs; + rdcflatmap danglingDXILLocalRootSigs; + + // list of hitgroups, with the name of the hit group export and the names of each shader export + // we can't precalculate the indices in this list because they could be dangling references that + // get inherited, so we have to do string lookups every time + rdcarray>> hitGroups; + + struct ExportedIdentifier + { + // the unwrapped identifier to patch the contents of ShaderIdentifier into + uint32_t real[8]; + // the index of the local root signature data to look up if local parameters need to be patched. + uint16_t localRootSigIndex; + // Subobjects have many different levels of priority they can come from, and the correct maximum + // level may not be clear for a while and may be overridden late. This keeps track of where we + // got the root signature from so that it can be overridden as necessary + SubObjectPriority rootSigPrio; + }; + + // unwrapped identifiers of NON-INHERITED NEWLY CREATED exports + // inherited exports are stored in the ownExports array of the database created for those objects, + // even if the objects themselves are not even around anymore - since the identifiers returned for + // them need to stay valid. + rdcarray ownExports; + +private: + // the state object that originally created this export database. Some of our shader identifiers + // may come from other databases, but when uploading the unwrap buffer we store information such + // that if we want to unwrap an identifier that comes from this id we look up into unwrappedOwnExports + // below. This is the original ID since this is used to look up identifiers that came from the application + ResourceId objectOriginalId; + + rdcarray parents; + + ID3D12StateObjectProperties *m_StateObjectProps = NULL; + D3D12RaytracingResourceAndUtilHandler *m_RayManager = NULL; + + struct ExportLookup + { + ExportLookup(rdcstr name, rdcstr altName, bool complete) + : name(name), altName(altName), complete(complete) + { + } + ExportLookup() = default; + + // name of an export + rdcstr name; + // alternate names - for when name is mangled and unmangled names can be looked up + rdcstr altName; + // is this export complete - we rely on the runtime to resolve all the arcane rules and do any + // DXIL linking to tell if this export is actually complete or not. + // the first object where a shader identifier comes back is the one where we store the wrapped + // identifier, since identifiers must be cross-compatible and persistent + bool complete = false; + // whether this export is a hitgroup - incomplete hitgroups get inherited explicitly + bool hitgroup = false; + }; + + struct ShaderIdentifier + { + ResourceId id; // the object which has the actual identifier in its ownExports array + uint32_t index; // the index in the object's ownExports array + uint32_t pad[5]; // padding up to D3D12_SHADER_IDENTIFIER_SIZE_IN_BYTES + }; + + // wrapped identifiers for all exports in this database, ready to return to the application, + // including any inherited from parent objects and not referring to our exports + rdcarray wrappedIdentifiers; + + // parallel array to wrappedIdentifiers of export lookup information. This is parallel and not + // in-line with wrappedIdentifiers because we want that to be a tight array of actual identifiers + rdcarray exportLookups; + + void InheritExport(const rdcstr &exportName, D3D12ShaderExportDatabase *existing, size_t i); + + void ApplyRoot(const ShaderIdentifier &identifier, SubObjectPriority priority, + uint32_t localRootSigIndex); +}; + class WrappedID3D12StateObject : public WrappedDeviceChild12, public ID3D12StateObjectProperties { ID3D12StateObjectProperties *properties; - public: ALLOCATE_WITH_WRAPPED_POOL(WrappedID3D12StateObject); + D3D12ShaderExportDatabase *exports = NULL; + enum { TypeEnum = Resource_StateObject, @@ -931,10 +1076,14 @@ public: : WrappedDeviceChild12(real, device) { real->QueryInterface(__uuidof(ID3D12StateObjectProperties), (void **)&properties); + exports = new D3D12ShaderExportDatabase( + GetResourceID(), m_pDevice->GetResourceManager()->GetRaytracingResourceAndUtilHandler(), + properties); } virtual ~WrappedID3D12StateObject() { SAFE_RELEASE(properties); + SAFE_RELEASE(exports); Shutdown(); } @@ -959,7 +1108,7 @@ public: virtual void *STDMETHODCALLTYPE GetShaderIdentifier(LPCWSTR pExportName) { - return properties->GetShaderIdentifier(pExportName); + return exports->GetShaderIdentifier(StringFormat::Wide2UTF8(pExportName)); } virtual UINT64 STDMETHODCALLTYPE GetShaderStackSize(LPCWSTR pExportName) { @@ -1236,6 +1385,7 @@ public: ALLOCATE_WITH_WRAPPED_POOL(WrappedID3D12RootSignature); D3D12RootSignature sig; + uint32_t localRootSigIdx = ~0U; enum {