diff --git a/src/ObjCommon/Shader/D3D11ShaderAnalyser.cpp b/src/ObjCommon/Shader/D3D11ShaderAnalyser.cpp new file mode 100644 index 00000000..4f4225e8 --- /dev/null +++ b/src/ObjCommon/Shader/D3D11ShaderAnalyser.cpp @@ -0,0 +1,685 @@ +#include "D3D11ShaderAnalyser.h" + +#include "Utils/FileUtils.h" + +#include + +using namespace d3d11; + +namespace d3d11 +{ + BoundResource::BoundResource() + : m_type(BoundResourceType::UNKNOWN), + m_return_type(BoundResourceReturnType::UNKNOWN), + m_dimension(BoundResourceDimension::UNKNOWN), + m_num_samples(0u), + m_bind_point(0u), + m_bind_count(0u), + m_flags(0u) + { + } + + ConstantBuffer::ConstantBuffer() + : m_size(0u), + m_flags(0u), + m_type(ConstantBufferType::UNKNOWN) + { + } + + ConstantBufferVariable::ConstantBufferVariable() + : m_offset(0u), + m_size(0u), + m_flags(0u) + { + } + + static constexpr auto TAG_RDEF = FileUtils::MakeMagic32('R', 'D', 'E', 'F'); + static constexpr auto TAG_SHDR = FileUtils::MakeMagic32('S', 'H', 'D', 'R'); + + static constexpr auto VERSION_5_0 = 0x500; + static constexpr auto VERSION_5_1 = 0x501; + static constexpr auto TARGET_VERSION_MASK = 0xFFFF; + static constexpr auto CHUNK_TABLE_OFFSET = 28u; + + struct FileRdefHeader + { + uint32_t constantBufferCount; + uint32_t constantBufferOffset; + uint32_t boundResourceCount; + uint32_t boundResourceOffset; + uint32_t target; + uint32_t flags; + uint32_t creatorOffset; + }; + + struct FileRdefExtraHeader_5_0 + { + // Wine project does not seem to know what this is + uint32_t unknown[8]; + }; + + static_assert(sizeof(FileRdefHeader) == 28); + static_assert(sizeof(FileRdefExtraHeader_5_0) == 32); + + // https://learn.microsoft.com/en-us/windows/win32/api/d3dcommon/ne-d3dcommon-d3d_shader_input_type + enum D3D_SHADER_INPUT_TYPE : uint32_t + { + D3D_SIT_CBUFFER = 0, + D3D_SIT_TBUFFER, + D3D_SIT_TEXTURE, + D3D_SIT_SAMPLER, + D3D_SIT_UAV_RWTYPED, + D3D_SIT_STRUCTURED, + D3D_SIT_UAV_RWSTRUCTURED, + D3D_SIT_BYTEADDRESS, + D3D_SIT_UAV_RWBYTEADDRESS, + D3D_SIT_UAV_APPEND_STRUCTURED, + D3D_SIT_UAV_CONSUME_STRUCTURED, + D3D_SIT_UAV_RWSTRUCTURED_WITH_COUNTER, + D3D_SIT_RTACCELERATIONSTRUCTURE, + D3D_SIT_UAV_FEEDBACKTEXTURE, + D3D10_SIT_CBUFFER, + D3D10_SIT_TBUFFER, + D3D10_SIT_TEXTURE, + D3D10_SIT_SAMPLER, + D3D11_SIT_UAV_RWTYPED, + D3D11_SIT_STRUCTURED, + D3D11_SIT_UAV_RWSTRUCTURED, + D3D11_SIT_BYTEADDRESS, + D3D11_SIT_UAV_RWBYTEADDRESS, + D3D11_SIT_UAV_APPEND_STRUCTURED, + D3D11_SIT_UAV_CONSUME_STRUCTURED, + D3D11_SIT_UAV_RWSTRUCTURED_WITH_COUNTER + }; + + // https://learn.microsoft.com/en-us/windows/win32/api/d3dcommon/ne-d3dcommon-d3d_resource_return_type + enum D3D_RESOURCE_RETURN_TYPE : uint32_t + { + D3D_RETURN_TYPE_UNORM = 1, + D3D_RETURN_TYPE_SNORM = 2, + D3D_RETURN_TYPE_SINT = 3, + D3D_RETURN_TYPE_UINT = 4, + D3D_RETURN_TYPE_FLOAT = 5, + D3D_RETURN_TYPE_MIXED = 6, + D3D_RETURN_TYPE_DOUBLE = 7, + D3D_RETURN_TYPE_CONTINUED = 8, + D3D10_RETURN_TYPE_UNORM, + D3D10_RETURN_TYPE_SNORM, + D3D10_RETURN_TYPE_SINT, + D3D10_RETURN_TYPE_UINT, + D3D10_RETURN_TYPE_FLOAT, + D3D10_RETURN_TYPE_MIXED, + D3D11_RETURN_TYPE_UNORM, + D3D11_RETURN_TYPE_SNORM, + D3D11_RETURN_TYPE_SINT, + D3D11_RETURN_TYPE_UINT, + D3D11_RETURN_TYPE_FLOAT, + D3D11_RETURN_TYPE_MIXED, + D3D11_RETURN_TYPE_DOUBLE, + D3D11_RETURN_TYPE_CONTINUED + }; + + // https://learn.microsoft.com/en-us/windows/win32/api/d3dcommon/ne-d3dcommon-d3d_srv_dimension + enum D3D_SRV_DIMENSION : uint32_t + { + D3D_SRV_DIMENSION_UNKNOWN = 0, + D3D_SRV_DIMENSION_BUFFER = 1, + D3D_SRV_DIMENSION_TEXTURE1D = 2, + D3D_SRV_DIMENSION_TEXTURE1DARRAY = 3, + D3D_SRV_DIMENSION_TEXTURE2D = 4, + D3D_SRV_DIMENSION_TEXTURE2DARRAY = 5, + D3D_SRV_DIMENSION_TEXTURE2DMS = 6, + D3D_SRV_DIMENSION_TEXTURE2DMSARRAY = 7, + D3D_SRV_DIMENSION_TEXTURE3D = 8, + D3D_SRV_DIMENSION_TEXTURECUBE = 9, + D3D_SRV_DIMENSION_TEXTURECUBEARRAY = 10, + D3D_SRV_DIMENSION_BUFFEREX = 11, + D3D10_SRV_DIMENSION_UNKNOWN, + D3D10_SRV_DIMENSION_BUFFER, + D3D10_SRV_DIMENSION_TEXTURE1D, + D3D10_SRV_DIMENSION_TEXTURE1DARRAY, + D3D10_SRV_DIMENSION_TEXTURE2D, + D3D10_SRV_DIMENSION_TEXTURE2DARRAY, + D3D10_SRV_DIMENSION_TEXTURE2DMS, + D3D10_SRV_DIMENSION_TEXTURE2DMSARRAY, + D3D10_SRV_DIMENSION_TEXTURE3D, + D3D10_SRV_DIMENSION_TEXTURECUBE, + D3D10_1_SRV_DIMENSION_UNKNOWN, + D3D10_1_SRV_DIMENSION_BUFFER, + D3D10_1_SRV_DIMENSION_TEXTURE1D, + D3D10_1_SRV_DIMENSION_TEXTURE1DARRAY, + D3D10_1_SRV_DIMENSION_TEXTURE2D, + D3D10_1_SRV_DIMENSION_TEXTURE2DARRAY, + D3D10_1_SRV_DIMENSION_TEXTURE2DMS, + D3D10_1_SRV_DIMENSION_TEXTURE2DMSARRAY, + D3D10_1_SRV_DIMENSION_TEXTURE3D, + D3D10_1_SRV_DIMENSION_TEXTURECUBE, + D3D10_1_SRV_DIMENSION_TEXTURECUBEARRAY, + D3D11_SRV_DIMENSION_UNKNOWN, + D3D11_SRV_DIMENSION_BUFFER, + D3D11_SRV_DIMENSION_TEXTURE1D, + D3D11_SRV_DIMENSION_TEXTURE1DARRAY, + D3D11_SRV_DIMENSION_TEXTURE2D, + D3D11_SRV_DIMENSION_TEXTURE2DARRAY, + D3D11_SRV_DIMENSION_TEXTURE2DMS, + D3D11_SRV_DIMENSION_TEXTURE2DMSARRAY, + D3D11_SRV_DIMENSION_TEXTURE3D, + D3D11_SRV_DIMENSION_TEXTURECUBE, + D3D11_SRV_DIMENSION_TEXTURECUBEARRAY, + D3D11_SRV_DIMENSION_BUFFEREX + }; + + struct FileBoundResource + { + uint32_t nameOffset; + D3D_SHADER_INPUT_TYPE type; + D3D_RESOURCE_RETURN_TYPE returnType; + D3D_SRV_DIMENSION dimension; + uint32_t numSamples; + uint32_t bindPoint; + uint32_t bindCount; + uint32_t uFlags; + }; + + struct FileBoundResource_5_1 : FileBoundResource + { + uint32_t space; + uint32_t uID; + }; + + static_assert(sizeof(FileBoundResource) == 32); + static_assert(sizeof(FileBoundResource_5_1) == 40); + + // https://learn.microsoft.com/en-us/windows/win32/api/d3dcommon/ne-d3dcommon-d3d_cbuffer_type + enum D3D_CBUFFER_TYPE : uint32_t + { + D3D_CT_CBUFFER = 0, + D3D_CT_TBUFFER, + D3D_CT_INTERFACE_POINTERS, + D3D_CT_RESOURCE_BIND_INFO, + D3D10_CT_CBUFFER, + D3D10_CT_TBUFFER, + D3D11_CT_CBUFFER, + D3D11_CT_TBUFFER, + D3D11_CT_INTERFACE_POINTERS, + D3D11_CT_RESOURCE_BIND_INFO + }; + + struct FileConstantBuffer + { + uint32_t nameOffset; + uint32_t variableCount; + uint32_t variableOffset; + uint32_t size; + uint32_t flags; + D3D_CBUFFER_TYPE type; + }; + + struct FileConstantBufferVariable + { + uint32_t nameOffset; + uint32_t startOffset; + uint32_t size; + uint32_t flags; + uint32_t typeOffset; + uint32_t defaultValueOffset; + }; + + struct FileConstantBufferVariable_5_0 : FileConstantBufferVariable + { + // Wine project does not seem to know what this is + uint32_t unknown[4]; + }; + + static_assert(sizeof(FileConstantBufferVariable) == 24); + static_assert(sizeof(FileConstantBufferVariable_5_0) == 40); + + enum FileProgramType : uint32_t + { + D3D10_SB_PIXEL_SHADER = 0, + D3D10_SB_VERTEX_SHADER = 1, + D3D10_SB_GEOMETRY_SHADER = 2, + + // D3D11 Shaders + D3D11_SB_HULL_SHADER = 3, + D3D11_SB_DOMAIN_SHADER = 4, + D3D11_SB_COMPUTE_SHADER = 5, + + D3D11_SB_RESERVED0 = 0xFFF0 + }; + + struct FileShaderHeader + { + uint32_t versionMinor : 4; + uint32_t versionMajor : 4; + uint32_t unused : 8; + FileProgramType programType : 16; + }; + + static_assert(sizeof(FileShaderHeader) == 4); + + uint32_t ReadU32(const uint8_t*& ptr) + { + const auto result = *reinterpret_cast(ptr); + ptr += sizeof(uint32_t); + return result; + } + + bool FindChunk(const uint32_t magic, const uint8_t* byteCode, const size_t byteCodeSize, size_t& chunkOffset, size_t& chunkSize) + { + if (byteCodeSize < CHUNK_TABLE_OFFSET + sizeof(uint32_t)) + return false; + + const auto* ptr = byteCode + CHUNK_TABLE_OFFSET; + const auto chunkCount = ReadU32(ptr); + if (byteCodeSize < (ptr - byteCode) + chunkCount * sizeof(uint32_t)) + return false; + + for (auto chunkIndex = 0u; chunkIndex < chunkCount; chunkIndex++) + { + const auto currentChunkOffset = ReadU32(ptr); + if (byteCodeSize < currentChunkOffset + sizeof(uint32_t) * 2) + return false; + + const auto currentChunkTag = *reinterpret_cast(byteCode + currentChunkOffset + 0); + const auto currentChunkSize = *reinterpret_cast(byteCode + currentChunkOffset + 4); + + if (byteCodeSize < currentChunkOffset + sizeof(uint32_t) * 2 + currentChunkSize) + return false; + + if (currentChunkTag == magic) + { + chunkOffset = currentChunkOffset + 8; + chunkSize = currentChunkSize; + return true; + } + } + + return false; + } + + bool StringFitsInChunk(const char* str, const uint8_t* shaderByteCode, const size_t shaderByteCodeSize) + { + const auto strLen = strnlen(str, shaderByteCodeSize - (reinterpret_cast(str) - shaderByteCode)); + return str[strLen] == '\0'; + } + + BoundResourceType GetType(const D3D_SHADER_INPUT_TYPE type) + { + switch (type) + { + case D3D_SIT_CBUFFER: + case D3D10_SIT_CBUFFER: + return BoundResourceType::CBUFFER; + case D3D_SIT_TBUFFER: + case D3D10_SIT_TBUFFER: + return BoundResourceType::TBUFFER; + case D3D_SIT_TEXTURE: + case D3D10_SIT_TEXTURE: + return BoundResourceType::TEXTURE; + case D3D_SIT_SAMPLER: + case D3D10_SIT_SAMPLER: + return BoundResourceType::SAMPLER; + default: + return BoundResourceType::UNKNOWN; + } + } + + BoundResourceReturnType GetReturnType(const D3D_RESOURCE_RETURN_TYPE returnType) + { + switch (returnType) + { + case D3D_RETURN_TYPE_UNORM: + case D3D10_RETURN_TYPE_UNORM: + case D3D11_RETURN_TYPE_UNORM: + return BoundResourceReturnType::UNORM; + case D3D_RETURN_TYPE_SNORM: + case D3D10_RETURN_TYPE_SNORM: + case D3D11_RETURN_TYPE_SNORM: + return BoundResourceReturnType::SNORM; + case D3D_RETURN_TYPE_SINT: + case D3D10_RETURN_TYPE_SINT: + case D3D11_RETURN_TYPE_SINT: + return BoundResourceReturnType::SINT; + case D3D_RETURN_TYPE_UINT: + case D3D10_RETURN_TYPE_UINT: + case D3D11_RETURN_TYPE_UINT: + return BoundResourceReturnType::UINT; + case D3D_RETURN_TYPE_FLOAT: + case D3D10_RETURN_TYPE_FLOAT: + case D3D11_RETURN_TYPE_FLOAT: + return BoundResourceReturnType::FLOAT; + case D3D_RETURN_TYPE_MIXED: + case D3D10_RETURN_TYPE_MIXED: + case D3D11_RETURN_TYPE_MIXED: + return BoundResourceReturnType::MIXED; + case D3D_RETURN_TYPE_DOUBLE: + case D3D11_RETURN_TYPE_DOUBLE: + return BoundResourceReturnType::DOUBLE; + case D3D_RETURN_TYPE_CONTINUED: + case D3D11_RETURN_TYPE_CONTINUED: + return BoundResourceReturnType::CONTINUED; + default: + return BoundResourceReturnType::UNKNOWN; + } + } + + BoundResourceDimension GetDimension(const D3D_SRV_DIMENSION dimension) + { + switch (dimension) + { + case D3D_SRV_DIMENSION_BUFFER: + case D3D10_SRV_DIMENSION_BUFFER: + case D3D10_1_SRV_DIMENSION_BUFFER: + case D3D11_SRV_DIMENSION_BUFFER: + return BoundResourceDimension::BUFFER; + case D3D_SRV_DIMENSION_TEXTURE1D: + case D3D10_SRV_DIMENSION_TEXTURE1D: + case D3D10_1_SRV_DIMENSION_TEXTURE1D: + case D3D11_SRV_DIMENSION_TEXTURE1D: + return BoundResourceDimension::TEXTURE_1D; + case D3D_SRV_DIMENSION_TEXTURE1DARRAY: + case D3D10_SRV_DIMENSION_TEXTURE1DARRAY: + case D3D10_1_SRV_DIMENSION_TEXTURE1DARRAY: + case D3D11_SRV_DIMENSION_TEXTURE1DARRAY: + return BoundResourceDimension::TEXTURE_1D_ARRAY; + case D3D_SRV_DIMENSION_TEXTURE2D: + case D3D10_SRV_DIMENSION_TEXTURE2D: + case D3D10_1_SRV_DIMENSION_TEXTURE2D: + case D3D11_SRV_DIMENSION_TEXTURE2D: + return BoundResourceDimension::TEXTURE_2D; + case D3D_SRV_DIMENSION_TEXTURE2DARRAY: + case D3D10_SRV_DIMENSION_TEXTURE2DARRAY: + case D3D10_1_SRV_DIMENSION_TEXTURE2DARRAY: + case D3D11_SRV_DIMENSION_TEXTURE2DARRAY: + return BoundResourceDimension::TEXTURE_2D_ARRAY; + case D3D_SRV_DIMENSION_TEXTURE2DMS: + case D3D10_SRV_DIMENSION_TEXTURE2DMS: + case D3D10_1_SRV_DIMENSION_TEXTURE2DMS: + case D3D11_SRV_DIMENSION_TEXTURE2DMS: + return BoundResourceDimension::TEXTURE_2D_MS; + case D3D_SRV_DIMENSION_TEXTURE2DMSARRAY: + case D3D10_SRV_DIMENSION_TEXTURE2DMSARRAY: + case D3D10_1_SRV_DIMENSION_TEXTURE2DMSARRAY: + case D3D11_SRV_DIMENSION_TEXTURE2DMSARRAY: + return BoundResourceDimension::TEXTURE_2D_MS_ARRAY; + case D3D_SRV_DIMENSION_TEXTURE3D: + case D3D10_SRV_DIMENSION_TEXTURE3D: + case D3D10_1_SRV_DIMENSION_TEXTURE3D: + case D3D11_SRV_DIMENSION_TEXTURE3D: + return BoundResourceDimension::TEXTURE_3D; + case D3D_SRV_DIMENSION_TEXTURECUBE: + case D3D10_SRV_DIMENSION_TEXTURECUBE: + case D3D10_1_SRV_DIMENSION_TEXTURECUBE: + case D3D11_SRV_DIMENSION_TEXTURECUBE: + return BoundResourceDimension::TEXTURE_CUBE; + case D3D_SRV_DIMENSION_TEXTURECUBEARRAY: + case D3D10_1_SRV_DIMENSION_TEXTURECUBEARRAY: + case D3D11_SRV_DIMENSION_TEXTURECUBEARRAY: + return BoundResourceDimension::TEXTURE_CUBE_ARRAY; + case D3D_SRV_DIMENSION_BUFFEREX: + case D3D11_SRV_DIMENSION_BUFFEREX: + return BoundResourceDimension::BUFFER_EX; + default: + return BoundResourceDimension::UNKNOWN; + } + } + + bool PopulateBoundResource(BoundResource& boundResource, + const FileBoundResource& fileBoundResource, + const uint8_t* shaderByteCode, + const size_t shaderByteCodeSize, + const size_t chunkOffset) + { + const auto nameString = reinterpret_cast(shaderByteCode + chunkOffset + fileBoundResource.nameOffset); + + if (!StringFitsInChunk(nameString, shaderByteCode, shaderByteCodeSize)) + return false; + + boundResource.m_name = std::string(nameString); + boundResource.m_type = GetType(fileBoundResource.type); + boundResource.m_return_type = GetReturnType(fileBoundResource.returnType); + boundResource.m_dimension = GetDimension(fileBoundResource.dimension); + boundResource.m_num_samples = fileBoundResource.numSamples; + boundResource.m_bind_point = fileBoundResource.bindPoint; + boundResource.m_bind_count = fileBoundResource.bindCount; + boundResource.m_flags = fileBoundResource.uFlags; + + return true; + } + + bool PopulateConstantBufferVariable(ConstantBufferVariable& constantBufferVariable, + const FileConstantBufferVariable& fileConstantBufferVariable, + const uint8_t* shaderByteCode, + const size_t shaderByteCodeSize, + const size_t chunkOffset, + const size_t chunkSize) + { + const auto nameString = reinterpret_cast(shaderByteCode + chunkOffset + fileConstantBufferVariable.nameOffset); + + if (!StringFitsInChunk(nameString, shaderByteCode, shaderByteCodeSize)) + return false; + + constantBufferVariable.m_name = std::string(nameString); + constantBufferVariable.m_offset = fileConstantBufferVariable.startOffset; + constantBufferVariable.m_size = fileConstantBufferVariable.size; + constantBufferVariable.m_flags = fileConstantBufferVariable.flags; + + return true; + } + + ConstantBufferType GetType(const D3D_CBUFFER_TYPE type) + { + switch (type) + { + case D3D_CT_CBUFFER: + case D3D10_CT_CBUFFER: + case D3D11_CT_CBUFFER: + return ConstantBufferType::CBUFFER; + case D3D_CT_TBUFFER: + case D3D10_CT_TBUFFER: + case D3D11_CT_TBUFFER: + return ConstantBufferType::TBUFFER; + case D3D_CT_INTERFACE_POINTERS: + case D3D11_CT_INTERFACE_POINTERS: + return ConstantBufferType::INTERFACE_POINTERS; + case D3D_CT_RESOURCE_BIND_INFO: + case D3D11_CT_RESOURCE_BIND_INFO: + return ConstantBufferType::RESOURCE_BIND_INFO; + default: + return ConstantBufferType::UNKNOWN; + } + } + + bool PopulateConstantBuffer(ConstantBuffer& constantBuffer, + const FileConstantBuffer& fileConstantBuffer, + const uint8_t* shaderByteCode, + const size_t shaderByteCodeSize, + const size_t chunkOffset, + const size_t chunkSize, + const unsigned targetVersion) + { + const auto nameString = reinterpret_cast(shaderByteCode + chunkOffset + fileConstantBuffer.nameOffset); + + if (!StringFitsInChunk(nameString, shaderByteCode, shaderByteCodeSize)) + return false; + + constantBuffer.m_name = std::string(nameString); + + constantBuffer.m_size = fileConstantBuffer.size; + constantBuffer.m_flags = fileConstantBuffer.flags; + constantBuffer.m_type = GetType(fileConstantBuffer.type); + + if (targetVersion < VERSION_5_0) + { + const auto* variables = reinterpret_cast(shaderByteCode + chunkOffset + fileConstantBuffer.variableOffset); + if (fileConstantBuffer.variableOffset + sizeof(FileConstantBufferVariable) * fileConstantBuffer.variableCount > chunkSize) + return false; + + for (auto variableIndex = 0u; variableIndex < fileConstantBuffer.variableCount; variableIndex++) + { + const auto& fileVariable = variables[variableIndex]; + ConstantBufferVariable variable; + + if (!PopulateConstantBufferVariable(variable, fileVariable, shaderByteCode, shaderByteCodeSize, chunkOffset, chunkSize)) + return false; + + constantBuffer.m_variables.emplace_back(std::move(variable)); + } + } + else + { + const auto* variables = reinterpret_cast(shaderByteCode + chunkOffset + fileConstantBuffer.variableOffset); + if (fileConstantBuffer.variableOffset + sizeof(FileConstantBufferVariable_5_0) * fileConstantBuffer.variableCount > chunkSize) + return false; + + for (auto variableIndex = 0u; variableIndex < fileConstantBuffer.variableCount; variableIndex++) + { + const auto& fileVariable = variables[variableIndex]; + ConstantBufferVariable variable; + + if (!PopulateConstantBufferVariable(variable, fileVariable, shaderByteCode, shaderByteCodeSize, chunkOffset, chunkSize)) + return false; + + constantBuffer.m_variables.emplace_back(std::move(variable)); + } + } + + return true; + } + + bool PopulateShaderInfoFromRdef(ShaderInfo& shaderInfo, const uint8_t* shaderByteCode, const size_t shaderByteCodeSize) + { + size_t chunkOffset, chunkSize; + if (!FindChunk(TAG_RDEF, shaderByteCode, shaderByteCodeSize, chunkOffset, chunkSize)) + return false; + + if (sizeof(FileRdefHeader) > chunkSize) + return false; + + const auto* header = reinterpret_cast(shaderByteCode + chunkOffset); + + const auto targetVersion = header->target & TARGET_VERSION_MASK; + const auto creatorString = reinterpret_cast(shaderByteCode + chunkOffset + header->creatorOffset); + + if (!StringFitsInChunk(creatorString, shaderByteCode, shaderByteCodeSize)) + return false; + + shaderInfo.m_creator = std::string(creatorString); + + if (targetVersion < VERSION_5_1) + { + const auto* boundResources = reinterpret_cast(shaderByteCode + chunkOffset + header->boundResourceOffset); + if (header->boundResourceOffset + sizeof(FileBoundResource) * header->boundResourceCount > chunkSize) + return false; + + for (auto boundResourceIndex = 0u; boundResourceIndex < header->boundResourceCount; boundResourceIndex++) + { + const auto& fileBoundResource = boundResources[boundResourceIndex]; + BoundResource boundResource; + + PopulateBoundResource(boundResource, fileBoundResource, shaderByteCode, shaderByteCodeSize, chunkOffset); + + shaderInfo.m_bound_resources.emplace_back(std::move(boundResource)); + } + } + else + { + const auto* boundResources = reinterpret_cast(shaderByteCode + chunkOffset + header->boundResourceOffset); + if (header->boundResourceOffset + sizeof(FileBoundResource_5_1) * header->boundResourceCount > chunkSize) + return false; + + for (auto boundResourceIndex = 0u; boundResourceIndex < header->boundResourceCount; boundResourceIndex++) + { + const auto& fileBoundResource = boundResources[boundResourceIndex]; + BoundResource boundResource; + + if (!PopulateBoundResource(boundResource, fileBoundResource, shaderByteCode, shaderByteCodeSize, chunkOffset)) + return false; + + shaderInfo.m_bound_resources.emplace_back(std::move(boundResource)); + } + } + + const auto* constantBuffers = reinterpret_cast(shaderByteCode + chunkOffset + header->constantBufferOffset); + if (header->constantBufferOffset + sizeof(FileConstantBuffer) * header->constantBufferCount > chunkSize) + return false; + + for (auto constantBufferIndex = 0u; constantBufferIndex < header->constantBufferCount; constantBufferIndex++) + { + const auto& fileConstantBuffer = constantBuffers[constantBufferIndex]; + ConstantBuffer constantBuffer; + + if (!PopulateConstantBuffer(constantBuffer, fileConstantBuffer, shaderByteCode, shaderByteCodeSize, chunkOffset, chunkSize, targetVersion)) + return false; + + shaderInfo.m_constant_buffers.emplace_back(std::move(constantBuffer)); + } + + return true; + } + + ShaderType GetShaderType(const FileProgramType programType) + { + switch (programType) + { + case D3D10_SB_PIXEL_SHADER: + return ShaderType::PIXEL_SHADER; + case D3D10_SB_VERTEX_SHADER: + return ShaderType::VERTEX_SHADER; + case D3D10_SB_GEOMETRY_SHADER: + return ShaderType::GEOMETRY_SHADER; + case D3D11_SB_HULL_SHADER: + return ShaderType::HULL_SHADER; + case D3D11_SB_DOMAIN_SHADER: + return ShaderType::DOMAIN_SHADER; + case D3D11_SB_COMPUTE_SHADER: + return ShaderType::COMPUTE_SHADER; + default: + return ShaderType::UNKNOWN; + } + } + + bool PopulateShaderInfoFromShdr(ShaderInfo& shaderInfo, const uint8_t* shaderByteCode, const size_t shaderByteCodeSize) + { + size_t chunkOffset, chunkSize; + if (!FindChunk(TAG_SHDR, shaderByteCode, shaderByteCodeSize, chunkOffset, chunkSize)) + return false; + + if (sizeof(FileShaderHeader) > chunkSize) + return false; + + const auto* header = reinterpret_cast(shaderByteCode + chunkOffset); + + shaderInfo.m_version_major = header->versionMajor; + shaderInfo.m_version_minor = header->versionMinor; + + shaderInfo.m_type = GetShaderType(header->programType); + + return true; + } + + bool PopulateShaderInfoFromBytes(ShaderInfo& shaderInfo, const uint8_t* shaderByteCode, const size_t shaderByteCodeSize) + { + if (!PopulateShaderInfoFromRdef(shaderInfo, shaderByteCode, shaderByteCodeSize)) + return false; + + if (!PopulateShaderInfoFromShdr(shaderInfo, shaderByteCode, shaderByteCodeSize)) + return false; + + return true; + } +} // namespace d3d11 + +std::unique_ptr ShaderAnalyser::GetShaderInfo(const uint8_t* shader, const size_t shaderSize) +{ + if (shader == nullptr || shaderSize == 0) + return nullptr; + + auto shaderInfo = std::make_unique(); + + if (!PopulateShaderInfoFromBytes(*shaderInfo, shader, shaderSize)) + return nullptr; + + return shaderInfo; +} diff --git a/src/ObjCommon/Shader/D3D11ShaderAnalyser.h b/src/ObjCommon/Shader/D3D11ShaderAnalyser.h new file mode 100644 index 00000000..59f9f9aa --- /dev/null +++ b/src/ObjCommon/Shader/D3D11ShaderAnalyser.h @@ -0,0 +1,136 @@ +#pragma once + +#include +#include +#include + +namespace d3d11 +{ + enum class ShaderType + { + UNKNOWN, + PIXEL_SHADER, + VERTEX_SHADER, + GEOMETRY_SHADER, + HULL_SHADER, + DOMAIN_SHADER, + COMPUTE_SHADER + }; + + class ConstantBufferVariable + { + public: + ConstantBufferVariable(); + ~ConstantBufferVariable() = default; + ConstantBufferVariable(const ConstantBufferVariable& other) = default; + ConstantBufferVariable(ConstantBufferVariable&& other) noexcept = default; + ConstantBufferVariable& operator=(const ConstantBufferVariable& other) = default; + ConstantBufferVariable& operator=(ConstantBufferVariable&& other) noexcept = default; + + std::string m_name; + unsigned m_offset; + unsigned m_size; + unsigned m_flags; + }; + + enum class ConstantBufferType + { + UNKNOWN, + CBUFFER, + TBUFFER, + INTERFACE_POINTERS, + RESOURCE_BIND_INFO, + }; + + class ConstantBuffer + { + public: + ConstantBuffer(); + ~ConstantBuffer() = default; + ConstantBuffer(const ConstantBuffer& other) = default; + ConstantBuffer(ConstantBuffer&& other) noexcept = default; + ConstantBuffer& operator=(const ConstantBuffer& other) = default; + ConstantBuffer& operator=(ConstantBuffer&& other) noexcept = default; + + std::string m_name; + unsigned m_size; + unsigned m_flags; + ConstantBufferType m_type; + std::vector m_variables; + }; + + enum class BoundResourceType + { + UNKNOWN, + CBUFFER, + TBUFFER, + TEXTURE, + SAMPLER + }; + + enum class BoundResourceReturnType + { + UNKNOWN, + UNORM, + SNORM, + SINT, + UINT, + FLOAT, + MIXED, + DOUBLE, + CONTINUED, + }; + + enum class BoundResourceDimension + { + UNKNOWN, + BUFFER, + TEXTURE_1D, + TEXTURE_1D_ARRAY, + TEXTURE_2D, + TEXTURE_2D_ARRAY, + TEXTURE_2D_MS, + TEXTURE_2D_MS_ARRAY, + TEXTURE_3D, + TEXTURE_CUBE, + TEXTURE_CUBE_ARRAY, + BUFFER_EX, + }; + + class BoundResource + { + public: + BoundResource(); + ~BoundResource() = default; + BoundResource(const BoundResource& other) = default; + BoundResource(BoundResource&& other) noexcept = default; + BoundResource& operator=(const BoundResource& other) = default; + BoundResource& operator=(BoundResource&& other) noexcept = default; + + std::string m_name; + BoundResourceType m_type; + BoundResourceReturnType m_return_type; + BoundResourceDimension m_dimension; + unsigned m_num_samples; + unsigned m_bind_point; + unsigned m_bind_count; + unsigned m_flags; + }; + + class ShaderInfo + { + public: + ShaderType m_type = ShaderType::UNKNOWN; + unsigned m_version_major = 0; + unsigned m_version_minor = 0; + std::string m_creator; + std::vector m_constant_buffers; + std::vector m_bound_resources; + }; + + class ShaderAnalyser + { + public: + static std::unique_ptr GetShaderInfo(const uint8_t* shader, size_t shaderSize); + }; +} // namespace d3d11 diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperMaterial.cpp b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperMaterial.cpp index bcdac91f..29b663e8 100644 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperMaterial.cpp +++ b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperMaterial.cpp @@ -1,12 +1,17 @@ #include "AssetDumperMaterial.h" #include "Game/T6/CommonT6.h" +#include "Game/T6/GameAssetPoolT6.h" +#include "Game/T6/GameT6.h" #include "Game/T6/MaterialConstantsT6.h" #include "Game/T6/TechsetConstantsT6.h" +#include "ObjWriting.h" +#include "Shader/D3D11ShaderAnalyser.h" #include #include #include +#include using namespace T6; using namespace nlohmann; @@ -18,17 +23,647 @@ namespace T6::material Common::R_HashString(strValue, 0), strValue \ } - std::unordered_map knownHashes{ - KNOWN_HASH("colorMap"), - KNOWN_HASH("normalMap"), - KNOWN_HASH("specularMap"), + static constexpr const char* SAMPLER_STR = "Sampler"; + static constexpr const char* GLOBALS_CBUFFER_NAME = "$Globals"; + static constexpr const char* PER_OBJECT_CONSTS_CBUFFER_NAME = "PerObjectConsts"; + + const char* KNOWN_CONSTANT_NAMES[]{ + "AngularVelocityScale", + "AnimSpeed", + "Background", + "BackgroundColor", + "BackgroundNoise", + "BakedLightingIntensity", + "BloodBrightness", + "BloodIntensity", + "BlurAmount", + "CapWidth", + "Char_Size", + "Char_Width", + "Coarseness", + "Color", + "ColorAmount", + "ColorBias", + "Color_Map_Noise", + "Color_Map_Scale", + "Color_Map_Size_Scale", + "DDXScale", + "DDYScale", + "DarkenAmount", + "DarkenPower", + "Detail_Amount", + "Detail_Normal_Tile", + "Diffuse_Normal_Height_Facing", + "Dimensions", + "DispersionAmount", + "Dolly", + "EdgeColor", + "EdgeHarshness", + "EdgeIntensity", + "EdgeMaxDist", + "EdgeMinDist", + "EdgeSize", + "Edge_Color_Multiplier", + "Emissive_Amount", + "EnemiesColor", + "Exposure", + "FPS", + "Fade_Distance", + "Fill_Direction", + "Fill_Direction2", + "FirstFrame", + "FlareIntensity", + "FlareScale", + "FlattenEdges", + "Flicker_Max", + "Flicker_Min", + "Flicker_Seed", + "Flicker_Speed", + "Font_Color", + "Gamma", + "GlossAmount", + "Gloss_Amount", + "Glow_Alt_Color", + "Glow_Color", + "Glow_Falloff", + "GradientColor", + "GradientMax", + "GradientMin", + "Grain_Amount", + "Grain_Color", + "Grid", + "Hardness", + "Heart_Rate_Offset", + "Heart_Rate_Scale", + "Highlight_1_Brightness", + "Highlight_1_Sharpness", + "Highlight_2_Brightness", + "Highlight_2_Sharpness", + "Highlight_2_Size", + "Hightlight_1_Size", + "Holo_Scale", + "LastFrame", + "Layer1Alpha", + "Layer1Depth", + "Layer1Offset", + "Layer1OffsetBobbleDelay", + "Layer1OffsetBobbleSpeedAndSize", + "Layer1Origin", + "Layer1Rotation", + "Layer1Scale", + "Layer1ScaleBobbleDelay", + "Layer1ScaleBobbleSpeedAndSize", + "Layer1Scroll", + "Layer2Alpha", + "Layer2Depth", + "Layer2Offset", + "Layer2OffsetBobbleDelay", + "Layer2OffsetBobbleSpeedAndSize", + "Layer2Origin", + "Layer2Rotation", + "Layer2Scale", + "Layer2ScaleBobbleDelay", + "Layer2ScaleBobbleSpeedAndSize", + "Layer2Scroll", + "Layer3Alpha", + "Layer3Depth", + "Layer3Offset", + "Layer3Origin", + "Layer3Rotation", + "Layer3Scale", + "Layer3Scroll", + "Layer4Alpha", + "Layer4Depth", + "Layer4Offset", + "Layer4Origin", + "Layer4Rotation", + "Layer4Scale", + "Layer4Scroll", + "LineColor", + "LineNoise", + "LineWidth", + "MaxDepth", + "MaxFlickerColor", + "MaxPulseDepth", + "MaxResolution", + "Max_Color", + "Maximum_Distance", + "Midlayer_Depth", + "MinDepth", + "MinFlickerColor", + "MinResolution", + "MinStatic", + "MinVelocityFraction", + "Min_Color", + "Min_Player_Intensity", + "MomentumColor", + "NegativeColor", + "NoisePower", + "Noise_Scale", + "NormalHeightMultiplier", + "Normal_Detail_Height", + "Normal_Detail_Scale", + "Normal_Map_Size_Scale", + "Normal_Variance_Scale", + "NumFrames", + "Outline_Lookup_Scale", + "OverallAmount", + "OverallBrightness", + "Overlay_Color", + "P1", + "P2", + "Padding", + "Player_Color_Multiplier", + "Player_Lookup_Scale", + "PositiveColor", + "Power", + "PulseColor", + "PulseInterval", + "PulseTime", + "Pulse_Color_Multiplier", + "Pulse_Lookup_Scale", + "Radius", + "ReflectionAmount", + "Reflection_Amount", + "Reflection_Blur", + "Reticle_Alt_Color", + "Reticle_Color", + "Row_Chars_", + "Scale", + "ScanlineColor", + "ScanlineIntensity", + "ScanlineOffset", + "ScanlinePower", + "ScanlineSpeed", + "ScatterAmount", + "ScatterSize", + "SceneNoise", + "SparkleBrightness", + "SparkleDensity", + "SparklePower", + "SparkleProbeAmount", + "SparkleScale", + "SparkleSpecAmount", + "SparkleWash", + "SpecGloss_Map_Size_Scale", + "SpecularAmount", + "SpecularColor", + "Specular_Amount", + "Specular_Decay_Threshold", + "Speed", + "StaticAmount", + "StaticLookupSpeed", + "StaticLookupX", + "StaticScale", + "Static_Size", + "Static_amount", + "TearLookupMaxX", + "TearLookupMinX", + "TearLookupSpeed", + "TearMultiplier", + "TearPower", + "Thickness", + "TickMarkColorAndHarshness", + "Tint", + "VelocityScale", + "VignetteMultiplier", + "VignettePower", + "WarpAmount", + "WarpHeight", + "WarpScale", + "WarpSpeed", + "WashOut", + "WashoutMultiply", + "WaterDirection", + "WaterHeight", + "WaterRefraction", + "WaterScale1", + "WaterScale2", + "WaterSpeed1", + "WaterSpeed2", + "Zoom", + "alphaDissolveParms", + "alphaRevealParms", + "alphaRevealParms1", + "alphaRevealParms2", + "alphaRevealParms3", + "alphaRevealParms4", + "clipSpaceLookupOffset", + "clipSpaceLookupScale", + "cloudsFeather", + "cloudsHeights", + "cloudsUVMad1", + "cloudsUVMad2", + "cloudsUVMul1", + "cloudsUVMul2", + "codeMeshArg", + "colorDetailScale", + "colorObjMax", + "colorObjMaxBaseBlend", + "colorObjMin", + "colorObjMinBaseBlend", + "colorTint", + "debugBumpmap", + "debugPerformance", + "detailScale", + "detailScale1", + "detailScale2", + "detailScale3", + "detailScale4", + "distortionScale", + "dofEquationScene", + "dofEquationViewModelAndFarBlur", + "dofLerpBias", + "dofLerpDownBias", + "dofLerpDownScale", + "dofLerpScale", + "dofLerpUpBias", + "dofLerpUpScale", + "dofRowDelta", + "eyeOffsetParms", + "falloffBeginColor", + "falloffEndColor", + "falloffParms", + "featherParms", + "flagParams", + "framebufferRead", + "gameTime", + "hdrAmount", + "inverseTransposeWorldMatrix", + "inverseTransposeWorldViewMatrix", + "inverseWorldMatrix", + "inverseWorldViewMatrix", + "motionblurDirectionAndMagnitude", + "occlusionAmount", + "occlusionAmount1", + "occlusionAmount2", + "occlusionAmount3", + "occlusionAmount4", + "particleCloudColor", + "particleCloudMatrix", + "particleCloudVelWorld", + "resizeParams1", + "resizeParams2", + "scaleRGB", + "scriptVector0", + "scriptVector1", + "scriptVector2", + "scriptVector3", + "scriptVector4", + "scriptVector5", + "scriptVector6", + "scriptVector7", + "skyBoxCloudWeights", + "skyBoxRotationSize", + "skyColorParms", + "spotLightWeight", + "treeCanopyLightingParms", + "treeCanopyScatterColor", + "treeCanopySwayParms", + "ui3dUVSetup0", + "ui3dUVSetup1", + "ui3dUVSetup2", + "ui3dUVSetup3", + "ui3dUVSetup4", + "ui3dUVSetup5", + "uvAnimParms", + "uvScroll", + "viewMatrix", + "weaponParam0", + "weaponParam1", + "weaponParam2", + "weaponParam3", + "weaponParam4", + "weaponParam5", + "weaponParam6", + "weaponParam7", + "weaponParam8", + "weaponParam9", + "worldViewMatrix", + "worldViewProjectionMatrix", + }; + + const char* KNOWN_TEXTURE_DEF_NAMES[]{ + "AddMap", + "Blip_Mask", + "BlockNoise", + "CS_Z_buffer", + "Camo_Detail_Map", + "Color_Map", + "CompassMap", + "Detail_Map", + "Diffuse", + "Diffuse_Map", + "DpadTexture", + "FontTextutre", + "Grain_Map", + "GridTexture", + "GrimeMap", + "Heart_Rate_Image", + "Hologram_Diffuse", + "Image", + "Layer1Map", + "Layer2Map", + "Layer3Map", + "Layer4Map", + "Lookup", + "Lookup2", + "LookupMap", + "Mask", + "Noise", + "Noise_Texture", + "NormalDetailMap", + "Normal_Detail_Map", + "Normal_Map", + "Overlay_Map", + "Reflection_Mask", + "Reveal_Map", + "Rim_Color_Mask", + "Rim_Specular_Mask", + "Rim_Occlusion_Mask", + "Scanline", + "SparkleMap", + "SpecularAndGloss", + "SpecularAndGloss2", + "Specular_Color_Map", + "Specular_Gloss_Map", + "Specular_Map", + "SpotShadowSamplerState", + "SpotShadowState", + "SpriteMap", + "Static", + "StaticMap", + "Static_Noise_Map", + "SunShadowSamplerState", + "SunShadowState", + "Surface_Normal_Map", + "ThermalMapMask", + "Thermal_Gradient", + "Thermal_Map", + "TickMarkMaterial", + "Tile", + "WarpMap", + "WaterNormalMap", + "Weapon_Normal_Map", + "Weapon_Specular_Map", + "Wireframe", + "ZBuffer_Map", + "attenuation", + "attenuationSampler", + "baseLut2D", + "baseLut2DSampler", + "cinematicA", + "cinematicASampler", + "cinematicCb", + "cinematicCbSampler", + "cinematicCr", + "cinematicCrSampler", + "cinematicY", + "cinematicYSampler", + "codeTexture0", + "codeTexture1", + "codeTexture2", + "color", + "colorDetailMap", + "colorDetailMapSampler", + "colorMap", + "colorMap1", + "colorMap2", + "colorMap2D", + "colorMapPostSun", + "colorMapPostSunSampler", + "colorMapSampler", + "colorMapSampler1", + "colorMapSampler2", + "colorSampler", + "detailMap", + "detailMapSampler", + "dlightAttenuation", + "dlightAttenuationSampler", + "floatZ", + "floatZSampler", + "imageSampler", + "lightmapSamplerSecondary", + "lightmapSecondary", + "lut2D", + "lut2DSampler", + "lut3D", + "lut3DSampler", + "missileCam", + "missileCamSampler", + "modelLighting", + "modelLightingSampler", + "normalMap", + "normalMap1", + "normalMap2", + "normalMapSampler", + "normalMapSampler1", + "normalMapSampler2", + "occlusionMap", + "occlusionMapSampler", + "occMap", + "occMapSampler", + "outdoorMap", + "outdoorMapSampler", + "radiantDiffuseMap", + "rawFloatZ", + "rawFloatZSampler", + "reflectionProbe", + "reflectionProbeSampler", + "shadowmapSamplerSpot", + "shadowmapSamplerSun", + "shadowmapSpot", + "shadowmapSun", + "sonarColor", + "sonarColorSampler", + "sonarDepth", + "sonarDepthSampler", + "source", + "specularMap", + "specularMap1", + "specularMap2", + "specularMapSampler", + "specularMapSampler1", + "specularMapSampler2", + "stencil", + "stencilSampler", + "ui3d", + "ui3dSampler", + }; + + class MaterialConstantZoneState final : public IZoneAssetDumperState + { + public: + void ExtractNamesFromZone() + { + if (ObjWriting::Configuration.Verbose) + std::cout << "Building material constant name lookup...\n"; + + const auto begin = std::chrono::high_resolution_clock::now(); + + AddStaticKnownNames(); + + for (const auto* zone : g_GameT6.GetZones()) + { + const auto* t6AssetPools = dynamic_cast(zone->m_pools.get()); + if (!t6AssetPools) + return; + + for (const auto* techniqueSetInfo : *t6AssetPools->m_technique_set) + { + const auto* techniqueSet = techniqueSetInfo->Asset(); + + for (const auto* technique : techniqueSet->techniques) + { + if (technique) + ExtractNamesFromTechnique(technique); + } + } + } + + const auto end = std::chrono::high_resolution_clock::now(); + + if (ObjWriting::Configuration.Verbose) + { + const auto durationInMs = std::chrono::duration_cast(end - begin); + std::cout << "Built material constant name lookup in " << durationInMs.count() << "ms: " << m_constant_names_from_shaders.size() + << " constant names; " << m_texture_def_names_from_shaders.size() << " texture def names\n"; + } + } + + bool GetConstantName(const unsigned hash, std::string& constantName) const + { + const auto existingConstantName = m_constant_names_from_shaders.find(hash); + if (existingConstantName != m_constant_names_from_shaders.end()) + { + constantName = existingConstantName->second; + return true; + } + + return false; + } + + bool GetTextureDefName(const unsigned hash, std::string& textureDefName) const + { + const auto existingTextureDefName = m_texture_def_names_from_shaders.find(hash); + if (existingTextureDefName != m_texture_def_names_from_shaders.end()) + { + textureDefName = existingTextureDefName->second; + return true; + } + + return false; + } + + private: + void ExtractNamesFromTechnique(const MaterialTechnique* technique) + { + const auto existingTechnique = m_dumped_techniques.find(technique); + if (existingTechnique != m_dumped_techniques.end()) + return; + + m_dumped_techniques.emplace(technique); + + for (auto passIndex = 0u; passIndex < technique->passCount; passIndex++) + { + const auto& pass = technique->passArray[passIndex]; + + if (pass.vertexShader && pass.vertexShader->prog.loadDef.program) + ExtractNamesFromShader(pass.vertexShader->prog.loadDef.program, pass.vertexShader->prog.loadDef.programSize); + + if (pass.pixelShader && pass.pixelShader->prog.loadDef.program) + ExtractNamesFromShader(pass.pixelShader->prog.loadDef.program, pass.pixelShader->prog.loadDef.programSize); + } + } + + void ExtractNamesFromShader(const char* shader, const size_t shaderSize) + { + const auto shaderInfo = d3d11::ShaderAnalyser::GetShaderInfo(reinterpret_cast(shader), shaderSize); + if (!shaderInfo) + return; + + const auto globalsConstantBuffer = std::find_if(shaderInfo->m_constant_buffers.cbegin(), + shaderInfo->m_constant_buffers.cend(), + [](const d3d11::ConstantBuffer& constantBuffer) + { + return constantBuffer.m_name == GLOBALS_CBUFFER_NAME; + }); + + const auto perObjectConsts = std::find_if(shaderInfo->m_constant_buffers.cbegin(), + shaderInfo->m_constant_buffers.cend(), + [](const d3d11::ConstantBuffer& constantBuffer) + { + return constantBuffer.m_name == PER_OBJECT_CONSTS_CBUFFER_NAME; + }); + + if (globalsConstantBuffer != shaderInfo->m_constant_buffers.end()) + { + for (const auto& variable : globalsConstantBuffer->m_variables) + AddConstantName(variable.m_name); + } + + if (perObjectConsts != shaderInfo->m_constant_buffers.end()) + { + for (const auto& variable : perObjectConsts->m_variables) + AddConstantName(variable.m_name); + } + + for (const auto& boundResource : shaderInfo->m_bound_resources) + { + if (boundResource.m_type == d3d11::BoundResourceType::SAMPLER || boundResource.m_type == d3d11::BoundResourceType::TEXTURE) + { + if (AddTextureDefName(boundResource.m_name)) + { + const auto samplerPos = boundResource.m_name.rfind(SAMPLER_STR); + if (samplerPos != std::string::npos) + { + auto nameWithoutSamplerStr = boundResource.m_name; + nameWithoutSamplerStr.erase(samplerPos, std::char_traits::length(SAMPLER_STR)); + AddTextureDefName(std::move(nameWithoutSamplerStr)); + } + } + } + } + } + + void AddStaticKnownNames() + { + for (const auto* knownConstantName : KNOWN_CONSTANT_NAMES) + AddConstantName(knownConstantName); + for (const auto* knownTextureDefName : KNOWN_TEXTURE_DEF_NAMES) + AddTextureDefName(knownTextureDefName); + } + + void AddConstantName(std::string constantName) + { + const auto hash = Common::R_HashString(constantName.c_str(), 0); + if (m_constant_names_from_shaders.find(hash) != m_constant_names_from_shaders.end()) + return; + + m_constant_names_from_shaders.emplace(hash, std::move(constantName)); + } + + bool AddTextureDefName(std::string textureDefName) + { + const auto hash = Common::R_HashString(textureDefName.c_str(), 0); + if (m_texture_def_names_from_shaders.find(hash) != m_texture_def_names_from_shaders.end()) + return false; + + m_texture_def_names_from_shaders.emplace(hash, std::move(textureDefName)); + return true; + } + + std::unordered_set m_dumped_techniques; + std::unordered_map m_constant_names_from_shaders; + std::unordered_map m_texture_def_names_from_shaders; }; class JsonDumper { public: - explicit JsonDumper(std::ostream& stream) - : m_stream(stream) + explicit JsonDumper(AssetDumpingContext& context, std::ostream& stream) + : m_stream(stream), + m_material_constants(*context.GetZoneAssetDumperState()) { } @@ -61,8 +696,9 @@ namespace T6::material j[key] = nullptr; } - static void MaterialToJson(json& jRoot, const Material* material) + void MaterialToJson(json& jRoot, const Material* material) const { + MaterialInfoToJson(jRoot, material); StateBitsEntryToJson(jRoot, material); jRoot["stateFlags"] = material->stateFlags; @@ -128,7 +764,7 @@ namespace T6::material jRoot["techniqueSet"] = nullptr; } - static void TextureTableToJson(json& jRoot, const Material* material) + void TextureTableToJson(json& jRoot, const Material* material) const { json jTextures = json::array(); if (material->textureTable) @@ -143,19 +779,19 @@ namespace T6::material jRoot["textures"] = std::move(jTextures); } - static void TextureDefToJson(json& jTexture, const MaterialTextureDef* textureDef) + void TextureDefToJson(json& jTexture, const MaterialTextureDef* textureDef) const { - const auto knownHash = knownHashes.find(textureDef->nameHash); - if (knownHash == knownHashes.end()) + std::string textureDefName; + if (m_material_constants.GetTextureDefName(textureDef->nameHash, textureDefName)) + { + jTexture["name"] = textureDefName; + } + else { jTexture["nameHash"] = textureDef->nameHash; jTexture["nameStart"] = std::string(1u, textureDef->nameStart); jTexture["nameEnd"] = std::string(1u, textureDef->nameEnd); } - else - { - jTexture["name"] = knownHash->second; - } JsonEnumEntry(jTexture, "semantic", textureDef->semantic, textureSemanticNames); jTexture["isMatureContent"] = textureDef->isMatureContent; @@ -179,7 +815,7 @@ namespace T6::material jSamplerState["clampW"] = samplerState.clampW ? true : false; } - static void ConstantTableToJson(json& jRoot, const Material* material) + void ConstantTableToJson(json& jRoot, const Material* material) const { json jConstants = json::array(); if (material->constantTable) @@ -194,26 +830,31 @@ namespace T6::material jRoot["constants"] = std::move(jConstants); } - static void ConstantDefToJson(json& jConstant, const MaterialConstantDef* textureDef) + void ConstantDefToJson(json& jConstant, const MaterialConstantDef* constantDef) const { - const auto fragmentLength = strnlen(textureDef->name, std::extent_v); - const std::string nameFragment(textureDef->name, fragmentLength); + const auto fragmentLength = strnlen(constantDef->name, std::extent_v); + const std::string nameFragment(constantDef->name, fragmentLength); + std::string knownConstantName; - if (fragmentLength < std::extent_v || Common::R_HashString(nameFragment.c_str(), 0) == textureDef->nameHash) + if (fragmentLength < std::extent_v || Common::R_HashString(nameFragment.c_str(), 0) == constantDef->nameHash) { jConstant["name"] = nameFragment; } + else if (m_material_constants.GetConstantName(constantDef->nameHash, knownConstantName)) + { + jConstant["name"] = knownConstantName; + } else { - jConstant["nameHash"] = textureDef->nameHash; + jConstant["nameHash"] = constantDef->nameHash; jConstant["nameFragment"] = nameFragment; } json jLiteral; - jLiteral.push_back(textureDef->literal.v[0]); - jLiteral.push_back(textureDef->literal.v[1]); - jLiteral.push_back(textureDef->literal.v[2]); - jLiteral.push_back(textureDef->literal.v[3]); + jLiteral.push_back(constantDef->literal.v[0]); + jLiteral.push_back(constantDef->literal.v[1]); + jLiteral.push_back(constantDef->literal.v[2]); + jLiteral.push_back(constantDef->literal.v[3]); jConstant["literal"] = std::move(jLiteral); } @@ -324,6 +965,7 @@ namespace T6::material } std::ostream& m_stream; + const MaterialConstantZoneState& m_material_constants; }; } // namespace T6::material @@ -357,6 +999,14 @@ void AssetDumperMaterial::DumpAsset(AssetDumpingContext& context, XAssetInfoAsset()); } + +void AssetDumperMaterial::DumpPool(AssetDumpingContext& context, AssetPool* pool) +{ + auto* materialConstantState = context.GetZoneAssetDumperState(); + materialConstantState->ExtractNamesFromZone(); + + AbstractAssetDumper::DumpPool(context, pool); +} diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperMaterial.h b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperMaterial.h index 087f97c2..9f8c8703 100644 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperMaterial.h +++ b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperMaterial.h @@ -14,5 +14,8 @@ namespace T6 protected: bool ShouldDump(XAssetInfo* asset) override; void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + + public: + void DumpPool(AssetDumpingContext& context, AssetPool* pool) override; }; } // namespace T6