mirror of
https://github.com/Laupetin/OpenAssetTools.git
synced 2025-04-19 15:52:53 +00:00
Add analyser for directx9 shaders to extract information about constants and other stuff
This commit is contained in:
parent
935e6ac060
commit
55ccbfca9e
246
src/ObjWriting/Shader/D3D9ShaderAnalyser.cpp
Normal file
246
src/ObjWriting/Shader/D3D9ShaderAnalyser.cpp
Normal file
@ -0,0 +1,246 @@
|
||||
#include "D3D9ShaderAnalyser.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include "Utils/FileUtils.h"
|
||||
|
||||
using namespace d3d9;
|
||||
|
||||
namespace d3d9
|
||||
{
|
||||
// https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/d3d9types/ne-d3d9types-_d3dshader_instruction_opcode_type
|
||||
static constexpr uint32_t OPCODE_COMMENT = 0xFFFE;
|
||||
static constexpr uint32_t OPCODE_END = 0xFFFF;
|
||||
|
||||
static constexpr uint32_t OPCODE_MASK = 0x0000FFFF;
|
||||
|
||||
static constexpr uint32_t COMMENT_SIZE_MASK = 0xFFFF0000;
|
||||
static constexpr uint32_t COMMENT_SIZE_SHIFT = 16;
|
||||
|
||||
// https://docs.microsoft.com/en-us/windows/win32/direct3d9/d3dxshader-constanttable
|
||||
struct ConstantTable
|
||||
{
|
||||
uint32_t Size;
|
||||
uint32_t Creator;
|
||||
uint32_t Version;
|
||||
uint32_t Constants;
|
||||
uint32_t ConstantInfo;
|
||||
uint32_t Flags;
|
||||
uint32_t Target;
|
||||
};
|
||||
|
||||
// https://docs.microsoft.com/en-us/windows/win32/direct3d9/d3dxshader-constantinfo
|
||||
struct ConstantInfo
|
||||
{
|
||||
uint32_t Name;
|
||||
uint16_t RegisterSet;
|
||||
uint16_t RegisterIndex;
|
||||
uint16_t RegisterCount;
|
||||
uint16_t Reserved;
|
||||
uint32_t TypeInfo;
|
||||
uint32_t DefaultValue;
|
||||
};
|
||||
|
||||
// https://docs.microsoft.com/en-us/windows/win32/direct3d9/d3dxshader-typeinfo
|
||||
struct TypeInfo
|
||||
{
|
||||
uint16_t Class;
|
||||
uint16_t Type;
|
||||
uint16_t Rows;
|
||||
uint16_t Columns;
|
||||
uint16_t Elements;
|
||||
uint16_t StructMembers;
|
||||
uint32_t StructMemberInfo;
|
||||
};
|
||||
|
||||
// https://docs.microsoft.com/en-us/windows/win32/direct3d9/d3dxshader-structmemberinfo
|
||||
struct StructMemberInfo
|
||||
{
|
||||
uint32_t Name;
|
||||
uint32_t TypeInfo;
|
||||
};
|
||||
|
||||
bool PopulateVersionInfo(ShaderInfo& shaderInfo, const uint32_t* shaderByteCode, const size_t shaderByteCodeSize)
|
||||
{
|
||||
if (shaderByteCodeSize < sizeof(uint32_t))
|
||||
return false;
|
||||
|
||||
const auto version = *shaderByteCode;
|
||||
shaderInfo.m_version_minor = version & 0xFF;
|
||||
shaderInfo.m_version_major = (version & 0xFF00) >> 8;
|
||||
|
||||
switch ((version & 0xFFFF0000) >> 16)
|
||||
{
|
||||
case 0x4658: // FX
|
||||
case 0x5458: // TX
|
||||
case 0x7ffe: // ?
|
||||
case 0x7fff: // ?
|
||||
shaderInfo.m_type = ShaderType::UNKNOWN; // Valid according to wine
|
||||
return true;
|
||||
case 0xfffe:
|
||||
shaderInfo.m_type = ShaderType::VERTEX_SHADER;
|
||||
return true;
|
||||
case 0xffff:
|
||||
shaderInfo.m_type = ShaderType::PIXEL_SHADER;
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FindComment(const uint32_t* shaderByteCode, const size_t shaderByteCodeSize, const uint32_t magic, const char*& commentStart, size_t& commentSize)
|
||||
{
|
||||
const uint32_t* currentPos = shaderByteCode + 1;
|
||||
size_t currentOffset = sizeof(uint32_t);
|
||||
while (*currentPos != OPCODE_END && (currentOffset + sizeof(uint32_t) - 1) < shaderByteCodeSize)
|
||||
{
|
||||
const auto currentValue = *currentPos;
|
||||
if ((currentValue & OPCODE_MASK) == OPCODE_COMMENT)
|
||||
{
|
||||
assert(currentOffset + sizeof(uint32_t) < shaderByteCodeSize);
|
||||
if (currentOffset + sizeof(uint32_t) >= shaderByteCodeSize)
|
||||
return false;
|
||||
|
||||
const auto currentCommentSize = (currentValue & COMMENT_SIZE_MASK) >> COMMENT_SIZE_SHIFT;
|
||||
|
||||
if (currentPos[1] == magic)
|
||||
{
|
||||
commentStart = reinterpret_cast<const char*>(currentPos + 2);
|
||||
commentSize = (currentCommentSize - 1) * sizeof(uint32_t);
|
||||
return currentOffset + sizeof(uint32_t) * (currentCommentSize + 1) <= shaderByteCodeSize;
|
||||
}
|
||||
|
||||
currentPos += currentCommentSize;
|
||||
currentOffset += currentCommentSize * sizeof(uint32_t);
|
||||
}
|
||||
|
||||
currentPos++;
|
||||
currentOffset += sizeof(uint32_t);
|
||||
assert((currentOffset + sizeof(uint32_t) - 1) < shaderByteCodeSize);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool StringFitsInComment(const char* str, const char* commentStart, const size_t commentSize)
|
||||
{
|
||||
const auto strLen = strnlen(str, commentSize - (str - commentStart));
|
||||
return str[strLen] == '\0';
|
||||
}
|
||||
|
||||
bool PopulateShaderConstantFromConstantInfo(ShaderConstant& shaderConstant, const char* commentStart, const size_t commentSize, const ConstantInfo& constantInfo)
|
||||
{
|
||||
if (constantInfo.Name)
|
||||
{
|
||||
const auto* constantName = commentStart + constantInfo.Name;
|
||||
if (!StringFitsInComment(constantName, commentStart, commentSize))
|
||||
return false;
|
||||
shaderConstant.m_name = std::string(constantName);
|
||||
}
|
||||
|
||||
shaderConstant.m_register_set = static_cast<RegisterSet>(constantInfo.RegisterSet);
|
||||
if (shaderConstant.m_register_set >= RegisterSet::MAX)
|
||||
return false;
|
||||
|
||||
shaderConstant.m_register_index = constantInfo.RegisterIndex;
|
||||
shaderConstant.m_register_count = constantInfo.RegisterCount;
|
||||
|
||||
if(constantInfo.TypeInfo)
|
||||
{
|
||||
assert(commentStart + constantInfo.TypeInfo + sizeof(TypeInfo) <= commentStart + commentSize);
|
||||
if (commentStart + constantInfo.TypeInfo + sizeof(TypeInfo) > commentStart + commentSize)
|
||||
return false;
|
||||
|
||||
const auto* typeInfo = reinterpret_cast<const TypeInfo*>(commentStart + constantInfo.TypeInfo);
|
||||
|
||||
shaderConstant.m_class = static_cast<ParameterClass>(typeInfo->Class);
|
||||
if (shaderConstant.m_class >= ParameterClass::MAX)
|
||||
return false;
|
||||
|
||||
shaderConstant.m_type = static_cast<ParameterType>(typeInfo->Type);
|
||||
if (shaderConstant.m_type >= ParameterType::MAX)
|
||||
return false;
|
||||
|
||||
shaderConstant.m_type_rows = typeInfo->Rows;
|
||||
shaderConstant.m_type_columns = typeInfo->Columns;
|
||||
shaderConstant.m_type_elements = typeInfo->Elements;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PopulateShaderInfoFromConstantTable(ShaderInfo& shaderInfo, const char* commentStart, const size_t commentSize, const ConstantTable& constantTable)
|
||||
{
|
||||
if (constantTable.Size != sizeof(ConstantTable))
|
||||
return false;
|
||||
|
||||
if (constantTable.Creator)
|
||||
{
|
||||
const auto* creatorName = commentStart + constantTable.Creator;
|
||||
if (!StringFitsInComment(creatorName, commentStart, commentSize))
|
||||
return false;
|
||||
shaderInfo.m_creator = std::string(creatorName);
|
||||
}
|
||||
|
||||
if (constantTable.Target)
|
||||
{
|
||||
const auto* targetName = commentStart + constantTable.Target;
|
||||
if (!StringFitsInComment(targetName, commentStart, commentSize))
|
||||
return false;
|
||||
shaderInfo.m_target = std::string(targetName);
|
||||
}
|
||||
|
||||
if (constantTable.Constants > 0 && constantTable.ConstantInfo)
|
||||
{
|
||||
assert(commentStart + constantTable.ConstantInfo + sizeof(ConstantInfo) * constantTable.Constants <= commentStart + commentSize);
|
||||
if (commentStart + constantTable.ConstantInfo + sizeof(ConstantInfo) * constantTable.Constants > commentStart + commentSize)
|
||||
return false;
|
||||
|
||||
const auto* constantInfos = reinterpret_cast<const ConstantInfo*>(commentStart + constantTable.ConstantInfo);
|
||||
for (auto constantInfoIndex = 0u; constantInfoIndex < constantTable.Constants; constantInfoIndex++)
|
||||
{
|
||||
ShaderConstant constant;
|
||||
if (!PopulateShaderConstantFromConstantInfo(constant, commentStart, commentSize, constantInfos[constantInfoIndex]))
|
||||
return false;
|
||||
shaderInfo.m_constants.emplace_back(std::move(constant));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PopulateShaderInfoFromShaderByteCode(ShaderInfo& shaderInfo, const uint32_t* shaderByteCode, const size_t shaderByteCodeSize)
|
||||
{
|
||||
if (!PopulateVersionInfo(shaderInfo, shaderByteCode, shaderByteCodeSize))
|
||||
return false;
|
||||
|
||||
const char* constantTableComment;
|
||||
size_t constantTableCommentSize;
|
||||
if (!FindComment(shaderByteCode, shaderByteCodeSize, FileUtils::MakeMagic32('C', 'T', 'A', 'B'), constantTableComment, constantTableCommentSize))
|
||||
return false;
|
||||
|
||||
if (constantTableCommentSize < sizeof(ConstantTable))
|
||||
return false;
|
||||
|
||||
const auto* constantTable = reinterpret_cast<const ConstantTable*>(constantTableComment);
|
||||
if (!PopulateShaderInfoFromConstantTable(shaderInfo, constantTableComment, constantTableCommentSize, *constantTable))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<ShaderInfo> ShaderAnalyser::GetShaderInfo(const uint32_t* shaderByteCode, const size_t shaderByteCodeSize)
|
||||
{
|
||||
if (shaderByteCode == nullptr || shaderByteCodeSize == 0)
|
||||
return nullptr;
|
||||
|
||||
auto shaderInfo = std::make_unique<ShaderInfo>();
|
||||
|
||||
if (!PopulateShaderInfoFromShaderByteCode(*shaderInfo, shaderByteCode, shaderByteCodeSize))
|
||||
return nullptr;
|
||||
|
||||
return shaderInfo;
|
||||
}
|
100
src/ObjWriting/Shader/D3D9ShaderAnalyser.h
Normal file
100
src/ObjWriting/Shader/D3D9ShaderAnalyser.h
Normal file
@ -0,0 +1,100 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace d3d9
|
||||
{
|
||||
enum class ShaderType
|
||||
{
|
||||
UNKNOWN,
|
||||
PIXEL_SHADER,
|
||||
VERTEX_SHADER
|
||||
};
|
||||
|
||||
// https://docs.microsoft.com/en-us/windows/win32/direct3d9/d3dxregister-set
|
||||
enum class RegisterSet
|
||||
{
|
||||
BOOL,
|
||||
INT_4,
|
||||
FLOAT_4,
|
||||
SAMPLER,
|
||||
|
||||
// This entry only exist to mark the size of the enum and is not an actual valid value
|
||||
MAX
|
||||
};
|
||||
|
||||
// https://docs.microsoft.com/en-us/windows/win32/direct3d9/d3dxparameter-class
|
||||
enum class ParameterClass
|
||||
{
|
||||
SCALAR,
|
||||
VECTOR,
|
||||
MATRIX_ROWS,
|
||||
MATRIX_COLUMNS,
|
||||
OBJECT,
|
||||
STRUCT,
|
||||
|
||||
// This entry only exist to mark the size of the enum and is not an actual valid value
|
||||
MAX
|
||||
};
|
||||
|
||||
// https://docs.microsoft.com/en-us/windows/win32/direct3d9/d3dxparameter-type
|
||||
enum class ParameterType
|
||||
{
|
||||
VOID,
|
||||
BOOL,
|
||||
INT,
|
||||
FLOAT,
|
||||
STRING,
|
||||
TEXTURE,
|
||||
TEXTURE_1D,
|
||||
TEXTURE_2D,
|
||||
TEXTURE_3D,
|
||||
TEXTURE_CUBE,
|
||||
SAMPLER,
|
||||
SAMPLER_1D,
|
||||
SAMPLER_2D,
|
||||
SAMPLER_3D,
|
||||
SAMPLER_CUBE,
|
||||
PIXEL_SHADER,
|
||||
VERTEX_SHADER,
|
||||
PIXEL_FRAGMENT,
|
||||
VERTEX_FRAGMENT,
|
||||
UNSUPPORTED,
|
||||
|
||||
// This entry only exist to mark the size of the enum and is not an actual valid value
|
||||
MAX
|
||||
};
|
||||
|
||||
class ShaderConstant
|
||||
{
|
||||
public:
|
||||
std::string m_name;
|
||||
RegisterSet m_register_set{};
|
||||
unsigned m_register_index = 0;
|
||||
unsigned m_register_count = 0;
|
||||
ParameterClass m_class{};
|
||||
ParameterType m_type{};
|
||||
unsigned m_type_rows = 0;
|
||||
unsigned m_type_columns = 0;
|
||||
unsigned m_type_elements = 0;
|
||||
};
|
||||
|
||||
class ShaderInfo
|
||||
{
|
||||
public:
|
||||
ShaderType m_type = ShaderType::UNKNOWN;
|
||||
unsigned m_version_major = 0;
|
||||
unsigned m_version_minor = 0;
|
||||
std::string m_creator;
|
||||
std::string m_target;
|
||||
std::vector<ShaderConstant> m_constants;
|
||||
};
|
||||
|
||||
class ShaderAnalyser
|
||||
{
|
||||
public:
|
||||
static std::unique_ptr<ShaderInfo> GetShaderInfo(const uint32_t* shaderByteCode, size_t shaderByteCodeSize);
|
||||
};
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user