diff --git a/src/ObjCommon/Shader/ShaderCommon.cpp b/src/ObjCommon/Shader/ShaderCommon.cpp index 7dc6c7bb..b92d7473 100644 --- a/src/ObjCommon/Shader/ShaderCommon.cpp +++ b/src/ObjCommon/Shader/ShaderCommon.cpp @@ -4,6 +4,11 @@ namespace shader { + std::string GetSourceFileNameForShaderAssetName(const std::string& assetName) + { + return std::format("shader/{}", assetName); + } + std::string GetFileNameForPixelShaderAssetName(const std::string& assetName) { return std::format("shader_bin/ps_{}.cso", assetName); diff --git a/src/ObjCommon/Shader/ShaderCommon.h b/src/ObjCommon/Shader/ShaderCommon.h index 0e3ef878..521fbc88 100644 --- a/src/ObjCommon/Shader/ShaderCommon.h +++ b/src/ObjCommon/Shader/ShaderCommon.h @@ -4,6 +4,8 @@ namespace shader { + std::string GetSourceFileNameForShaderAssetName(const std::string& assetName); + std::string GetFileNameForPixelShaderAssetName(const std::string& assetName); std::string GetFileNameForVertexShaderAssetName(const std::string& assetName); } // namespace shader diff --git a/src/ObjCompiling/Game/IW4/Material/CompilerMaterialIW4.cpp b/src/ObjCompiling/Game/IW4/Material/CompilerMaterialIW4.cpp index d2e20bc0..79b53d03 100644 --- a/src/ObjCompiling/Game/IW4/Material/CompilerMaterialIW4.cpp +++ b/src/ObjCompiling/Game/IW4/Material/CompilerMaterialIW4.cpp @@ -4,7 +4,6 @@ #include "Game/IW4/IW4.h" #include "Game/IW4/MaterialConstantsIW4.h" #include "Game/IW4/ObjConstantsIW4.h" -#include "Game/IW4/Techset/CompilerTechsetIW4.h" #include "Game/IW4/Techset/TechsetConstantsIW4.h" #include "Gdt/AbstractGdtEntryReader.h" #include "Gdt/IGdtQueryable.h" diff --git a/src/ObjCompiling/Game/IW4/ObjCompilerIW4.cpp b/src/ObjCompiling/Game/IW4/ObjCompilerIW4.cpp index 88457744..95a24fe0 100644 --- a/src/ObjCompiling/Game/IW4/ObjCompilerIW4.cpp +++ b/src/ObjCompiling/Game/IW4/ObjCompilerIW4.cpp @@ -6,6 +6,8 @@ #include "Game/IW4/Techset/VertexDeclCompilerIW4.h" #include "Image/ImageIwdPostProcessor.h" #include "Material/CompilerMaterialIW4.h" +#include "Techset/PixelShaderCompilerIW4.h" +#include "Techset/VertexShaderCompilerIW4.h" #include @@ -21,6 +23,8 @@ namespace collection.AddAssetCreator(material::CreateCompilerIW4(memory, searchPath, gdt)); #endif collection.AddAssetCreator(techset::CreateVertexDeclCompilerIW4(memory)); + collection.AddAssetCreator(techset::CreateVertexShaderCompilerIW4(memory, searchPath)); + collection.AddAssetCreator(techset::CreatePixelShaderCompilerIW4(memory, searchPath)); collection.AddAssetCreator(techset::CreateTechsetCompilerIW4(memory, searchPath)); collection.AddSubAssetCreator(techset::CreateTechniqueCompilerIW4(memory, zone, searchPath)); diff --git a/src/ObjCompiling/Game/IW4/Techset/PixelShaderCompilerIW4.cpp b/src/ObjCompiling/Game/IW4/Techset/PixelShaderCompilerIW4.cpp new file mode 100644 index 00000000..e59ade53 --- /dev/null +++ b/src/ObjCompiling/Game/IW4/Techset/PixelShaderCompilerIW4.cpp @@ -0,0 +1,117 @@ +#include "PixelShaderCompilerIW4.h" + +#include "Game/IW4/IW4.h" +#include "Shader/ShaderCommon.h" +#include "Techset/ShaderIncludeHandler.h" +#include "Utils/Logging/Log.h" + +#include +#include +#include + +#ifdef _WIN32 +#include + +#pragma comment(lib, "d3dcompiler.lib") +#endif + +using namespace IW4; + +namespace +{ + class PixelShaderCompilerIW4 final : public AssetCreator + { + public: + PixelShaderCompilerIW4(MemoryManager& memory, ISearchPath& searchPath) + : m_memory(memory), + m_search_path(searchPath) + { + } + + AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override + { +#ifdef _WIN32 + const auto fileName = shader::GetSourceFileNameForShaderAssetName(assetName); + auto file = m_search_path.Open(fileName); + if (!file.IsOpen() || file.m_length <= 0) + return AssetCreationResult::NoAction(); + + if (std::cmp_greater(file.m_length, techset::ShaderIncludeHandler::MAX_SHADER_SIZE)) + { + con::error("Invalid shader source \"{}\": File too big: {}", assetName, file.m_length); + return AssetCreationResult::Failure(); + } + + const auto shaderSize = static_cast(file.m_length); + const auto shaderData = std::make_unique(shaderSize); + file.m_stream->read(shaderData.get(), shaderSize); + file.m_stream.reset(); + + constexpr unsigned shaderFlags = D3DCOMPILE_OPTIMIZATION_LEVEL1 +#ifdef _DEBUG + | D3DCOMPILE_DEBUG +#endif + ; + + techset::ShaderIncludeHandler shaderIncluder(m_search_path); + + ID3DBlob* shaderBlob = nullptr; + ID3DBlob* errorBlob = nullptr; + const auto errorCode = D3DCompile( + shaderData.get(), shaderSize, assetName.c_str(), nullptr, &shaderIncluder, "PSMain", "ps_3_0", shaderFlags, 0u, &shaderBlob, &errorBlob); + + if (FAILED(errorCode)) + { + con::error("Invalid pixel shader \"{}\": Compilation error", assetName); + + if (errorBlob) + { + std::cerr << " " << static_cast(errorBlob->GetBufferPointer()) << "\n"; + errorBlob->Release(); + } + + if (shaderBlob) + shaderBlob->Release(); + + return AssetCreationResult::Failure(); + } + + con::info("Compiled pixel shader \"{}\"", assetName); + + const auto shaderBlobSize = static_cast(shaderBlob->GetBufferSize()); + assert(shaderBlobSize % sizeof(uint32_t) == 0); + + auto* pixelShader = m_memory.Alloc(); + pixelShader->name = m_memory.Dup(assetName.c_str()); + pixelShader->prog.loadDef.programSize = static_cast(shaderBlobSize / sizeof(uint32_t)); + pixelShader->prog.loadDef.loadForRenderer = 0; + pixelShader->prog.ps = nullptr; + + auto* assetShaderBuffer = m_memory.Alloc(shaderBlobSize); + memcpy(assetShaderBuffer, shaderBlob->GetBufferPointer(), shaderBlobSize); + pixelShader->prog.loadDef.program = reinterpret_cast(assetShaderBuffer); + + shaderBlob->Release(); + + return AssetCreationResult::Success(context.AddAsset(AssetRegistration(assetName, pixelShader))); +#else + // Shader compilation is only support with Windows + return AssetCreationResult::NoAction(); +#endif + } + + void FinalizeZone(AssetCreationContext& context) override {} + + private: + MemoryManager& m_memory; + ISearchPath& m_search_path; + }; +} // namespace + +namespace techset +{ + std::unique_ptr CreatePixelShaderCompilerIW4(MemoryManager& memory, ISearchPath& searchPath) + { + return std::make_unique(memory, searchPath); + } +} // namespace techset diff --git a/src/ObjCompiling/Game/IW4/Techset/PixelShaderCompilerIW4.h b/src/ObjCompiling/Game/IW4/Techset/PixelShaderCompilerIW4.h new file mode 100644 index 00000000..0486277b --- /dev/null +++ b/src/ObjCompiling/Game/IW4/Techset/PixelShaderCompilerIW4.h @@ -0,0 +1,11 @@ +#pragma once + +#include "Asset/IAssetCreator.h" +#include "Utils/MemoryManager.h" + +#include + +namespace techset +{ + std::unique_ptr CreatePixelShaderCompilerIW4(MemoryManager& memory, ISearchPath& searchPath); +} diff --git a/src/ObjCompiling/Game/IW4/Techset/VertexShaderCompilerIW4.cpp b/src/ObjCompiling/Game/IW4/Techset/VertexShaderCompilerIW4.cpp new file mode 100644 index 00000000..be8181aa --- /dev/null +++ b/src/ObjCompiling/Game/IW4/Techset/VertexShaderCompilerIW4.cpp @@ -0,0 +1,117 @@ +#include "VertexShaderCompilerIW4.h" + +#include "Game/IW4/IW4.h" +#include "Shader/ShaderCommon.h" +#include "Techset/ShaderIncludeHandler.h" +#include "Utils/Logging/Log.h" + +#include +#include +#include + +#ifdef _WIN32 +#include + +#pragma comment(lib, "d3dcompiler.lib") +#endif + +using namespace IW4; + +namespace +{ + class VertexShaderCompilerIW4 final : public AssetCreator + { + public: + VertexShaderCompilerIW4(MemoryManager& memory, ISearchPath& searchPath) + : m_memory(memory), + m_search_path(searchPath) + { + } + + AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override + { +#ifdef _WIN32 + const auto fileName = shader::GetSourceFileNameForShaderAssetName(assetName); + auto file = m_search_path.Open(fileName); + if (!file.IsOpen() || file.m_length <= 0) + return AssetCreationResult::NoAction(); + + if (std::cmp_greater(file.m_length, techset::ShaderIncludeHandler::MAX_SHADER_SIZE)) + { + con::error("Invalid shader source \"{}\": File too big: {}", assetName, file.m_length); + return AssetCreationResult::Failure(); + } + + const auto shaderSize = static_cast(file.m_length); + const auto shaderData = std::make_unique(shaderSize); + file.m_stream->read(shaderData.get(), shaderSize); + file.m_stream.reset(); + + constexpr unsigned shaderFlags = D3DCOMPILE_OPTIMIZATION_LEVEL1 +#ifdef _DEBUG + | D3DCOMPILE_DEBUG +#endif + ; + + techset::ShaderIncludeHandler shaderIncluder(m_search_path); + + ID3DBlob* shaderBlob = nullptr; + ID3DBlob* errorBlob = nullptr; + const auto errorCode = D3DCompile( + shaderData.get(), shaderSize, assetName.c_str(), nullptr, &shaderIncluder, "VSMain", "vs_3_0", shaderFlags, 0u, &shaderBlob, &errorBlob); + + if (FAILED(errorCode)) + { + con::error("Invalid vertex shader \"{}\": Compilation error", assetName); + + if (errorBlob) + { + std::cerr << " " << static_cast(errorBlob->GetBufferPointer()) << "\n"; + errorBlob->Release(); + } + + if (shaderBlob) + shaderBlob->Release(); + + return AssetCreationResult::Failure(); + } + + con::info("Compiled vertex shader \"{}\"", assetName); + + const auto shaderBlobSize = static_cast(shaderBlob->GetBufferSize()); + assert(shaderBlobSize % sizeof(uint32_t) == 0); + + auto* vertexShader = m_memory.Alloc(); + vertexShader->name = m_memory.Dup(assetName.c_str()); + vertexShader->prog.loadDef.programSize = static_cast(shaderBlobSize / sizeof(uint32_t)); + vertexShader->prog.loadDef.loadForRenderer = 0; + vertexShader->prog.vs = nullptr; + + auto* assetShaderBuffer = m_memory.Alloc(shaderBlobSize); + memcpy(assetShaderBuffer, shaderBlob->GetBufferPointer(), shaderBlobSize); + vertexShader->prog.loadDef.program = reinterpret_cast(assetShaderBuffer); + + shaderBlob->Release(); + + return AssetCreationResult::Success(context.AddAsset(AssetRegistration(assetName, vertexShader))); +#else + // Shader compilation is only support with Windows + return AssetCreationResult::NoAction(); +#endif + } + + void FinalizeZone(AssetCreationContext& context) override {} + + private: + MemoryManager& m_memory; + ISearchPath& m_search_path; + }; +} // namespace + +namespace techset +{ + std::unique_ptr CreateVertexShaderCompilerIW4(MemoryManager& memory, ISearchPath& searchPath) + { + return std::make_unique(memory, searchPath); + } +} // namespace techset diff --git a/src/ObjCompiling/Game/IW4/Techset/VertexShaderCompilerIW4.h b/src/ObjCompiling/Game/IW4/Techset/VertexShaderCompilerIW4.h new file mode 100644 index 00000000..c9f5042b --- /dev/null +++ b/src/ObjCompiling/Game/IW4/Techset/VertexShaderCompilerIW4.h @@ -0,0 +1,11 @@ +#pragma once + +#include "Asset/IAssetCreator.h" +#include "Utils/MemoryManager.h" + +#include + +namespace techset +{ + std::unique_ptr CreateVertexShaderCompilerIW4(MemoryManager& memory, ISearchPath& searchPath); +} diff --git a/src/ObjCompiling/Techset/ShaderIncludeHandler.cpp b/src/ObjCompiling/Techset/ShaderIncludeHandler.cpp new file mode 100644 index 00000000..d77a0439 --- /dev/null +++ b/src/ObjCompiling/Techset/ShaderIncludeHandler.cpp @@ -0,0 +1,59 @@ +#include "ShaderIncludeHandler.h" + +#include "Shader/ShaderCommon.h" +#include "Utils/Logging/Log.h" + +#include +#include + +#ifdef _WIN32 + +namespace techset +{ + ShaderIncludeHandler::ShaderIncludeHandler(ISearchPath& searchPath) + : m_search_path(searchPath) + { + } + + HRESULT ShaderIncludeHandler::Open(D3D_INCLUDE_TYPE includeType, const LPCSTR pFileName, LPCVOID pParentData, LPCVOID* ppData, UINT* pBytes) + { + const auto fileName = shader::GetSourceFileNameForShaderAssetName(pFileName); + auto file = m_search_path.Open(fileName); + if (!file.IsOpen() || file.m_length <= 0) + return E_FAIL; + + if (std::cmp_greater(file.m_length, MAX_SHADER_SIZE)) + { + con::error("Invalid shader source \"{}\": File too big: {}\n", pFileName, file.m_length); + return E_FAIL; + } + + const auto shaderSize = static_cast(file.m_length); + auto shaderData = std::make_unique(shaderSize); + file.m_stream->read(shaderData.get(), shaderSize); + file.m_stream.reset(); + + *ppData = shaderData.get(); + *pBytes = shaderSize; + + m_file_buffers_in_use.push_back(std::move(shaderData)); + + return S_OK; + } + + HRESULT ShaderIncludeHandler::Close(const LPCVOID pData) + { + for (auto i = m_file_buffers_in_use.begin(); i != m_file_buffers_in_use.end(); ++i) + { + if (i->get() == pData) + { + m_file_buffers_in_use.erase(i); + return S_OK; + } + } + + return E_FAIL; + } +} // namespace techset + +#endif diff --git a/src/ObjCompiling/Techset/ShaderIncludeHandler.h b/src/ObjCompiling/Techset/ShaderIncludeHandler.h new file mode 100644 index 00000000..e9558521 --- /dev/null +++ b/src/ObjCompiling/Techset/ShaderIncludeHandler.h @@ -0,0 +1,27 @@ +#pragma once + +#include "SearchPath/ISearchPath.h" + +// Shader compilation only available on Windows +#ifdef _WIN32 +#include + +namespace techset +{ + class ShaderIncludeHandler : public ID3DInclude + { + public: + static constexpr size_t MAX_SHADER_SIZE = 0x1900000u; + + explicit ShaderIncludeHandler(ISearchPath& searchPath); + virtual ~ShaderIncludeHandler() = default; + + HRESULT __declspec(nothrow) __stdcall Open(D3D_INCLUDE_TYPE includeType, LPCSTR pFileName, LPCVOID pParentData, LPCVOID* ppData, UINT* pBytes) override; + HRESULT __declspec(nothrow) __stdcall Close(LPCVOID pData) override; + + private: + ISearchPath& m_search_path; + std::vector> m_file_buffers_in_use; + }; +} // namespace techset +#endif diff --git a/src/Utils/Utils/Logging/Log.h b/src/Utils/Utils/Logging/Log.h index 814d57d5..2a5a537e 100644 --- a/src/Utils/Utils/Logging/Log.h +++ b/src/Utils/Utils/Logging/Log.h @@ -5,6 +5,11 @@ #include #include +// Thanks wingdi.h +#ifdef ERROR +#undef ERROR +#endif + namespace con { enum class LogLevel : std::uint8_t