diff --git a/raw/iw4/shader/trivial_vertcol_simple.hlsl b/raw/iw4/shader/trivial_vertcol_simple.hlsl index dd6f41ba..1f35c409 100644 --- a/raw/iw4/shader/trivial_vertcol_simple.hlsl +++ b/raw/iw4/shader/trivial_vertcol_simple.hlsl @@ -7,7 +7,7 @@ struct VSInput struct VSOutput { - float4 position : POSITION; + float4 position : SV_POSITION; half4 color : COLOR0; half2 texcoord : TEXCOORD0; }; diff --git a/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderPixelShader.cpp b/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderPixelShader.cpp index df5e5128..9d7536a9 100644 --- a/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderPixelShader.cpp +++ b/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderPixelShader.cpp @@ -8,6 +8,13 @@ #include "ObjLoading.h" #include "Game/IW4/IW4.h" #include "Pool/GlobalAssetPool.h" +#include "Shader/ShaderIncludeHandler.h" + +#ifdef OS_TARGET_WINDOWS +#include + +#pragma comment(lib,"d3dcompiler.lib") +#endif using namespace IW4; @@ -24,16 +31,95 @@ bool AssetLoaderPixelShader::CanLoadFromRaw() const return true; } -std::string AssetLoaderPixelShader::GetFileNameForAsset(const std::string& assetName) +std::string AssetLoaderPixelShader::GetFileNameForSourceShader(const std::string& assetName) +{ + std::ostringstream ss; + ss << "shader/" << assetName; + return ss.str(); +} + +bool AssetLoaderPixelShader::LoadFromSource(const std::string& assetName, ISearchPath* searchPath, MemoryManager* memory, IAssetLoadingManager* manager) +{ +#ifdef OS_TARGET_WINDOWS + const auto fileName = GetFileNameForSourceShader(assetName); + auto file = searchPath->Open(fileName); + if (!file.IsOpen() || file.m_length <= 0) + return false; + + if (file.m_length > ShaderIncludeHandler::MAX_SHADER_SIZE) + { + std::cerr << "Invalid pixel shader source \"" << assetName << "\": File too big: " << file.m_length << "\n"; + return false; + } + + 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 + ; + + ShaderIncludeHandler shaderIncluder(searchPath); + + 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)) + { + std::cerr << "Invalid pixel shader \"" << assetName << "\": Compilation error\n"; + + if (errorBlob) + { + std::cerr << " " << static_cast(errorBlob->GetBufferPointer()) << "\n"; + errorBlob->Release(); + } + + if (shaderBlob) + shaderBlob->Release(); + + return false; + } + + std::cout << "Compiled pixel shader \"" << assetName << "\"\n"; + + const auto shaderBlobSize = static_cast(shaderBlob->GetBufferSize()); + assert(shaderBlobSize % sizeof(uint32_t) == 0); + + auto* pixelShader = memory->Create(); + pixelShader->name = 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 = static_cast(memory->Alloc(shaderBlobSize)); + memcpy(assetShaderBuffer, shaderBlob->GetBufferPointer(), shaderBlobSize); + pixelShader->prog.loadDef.program = reinterpret_cast(assetShaderBuffer); + manager->AddAsset(ASSET_TYPE_PIXELSHADER, assetName, pixelShader); + + shaderBlob->Release(); + + return true; +#else + // Shader compilation is only support with Windows + return false; +#endif +} + +std::string AssetLoaderPixelShader::GetFileNameForCompiledShader(const std::string& assetName) { std::ostringstream ss; ss << "shader_bin/ps_" << assetName << ".cso"; return ss.str(); } -bool AssetLoaderPixelShader::LoadFromRaw(const std::string& assetName, ISearchPath* searchPath, MemoryManager* memory, IAssetLoadingManager* manager, Zone* zone) const +bool AssetLoaderPixelShader::LoadCompiled(const std::string& assetName, ISearchPath* searchPath, MemoryManager* memory, IAssetLoadingManager* manager) { - const auto fileName = GetFileNameForAsset(assetName); + const auto fileName = GetFileNameForCompiledShader(assetName); const auto file = searchPath->Open(fileName); if (!file.IsOpen()) return false; @@ -60,3 +146,8 @@ bool AssetLoaderPixelShader::LoadFromRaw(const std::string& assetName, ISearchPa return true; } + +bool AssetLoaderPixelShader::LoadFromRaw(const std::string& assetName, ISearchPath* searchPath, MemoryManager* memory, IAssetLoadingManager* manager, Zone* zone) const +{ + return LoadFromSource(assetName, searchPath, memory, manager) || LoadCompiled(assetName, searchPath, memory, manager); +} diff --git a/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderPixelShader.h b/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderPixelShader.h index 4a96e825..b976cdb2 100644 --- a/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderPixelShader.h +++ b/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderPixelShader.h @@ -11,7 +11,11 @@ namespace IW4 class AssetLoaderPixelShader final : public BasicAssetLoader { public: - _NODISCARD static std::string GetFileNameForAsset(const std::string& assetName); + _NODISCARD static std::string GetFileNameForSourceShader(const std::string& assetName); + static bool LoadFromSource(const std::string& assetName, ISearchPath* searchPath, MemoryManager* memory, IAssetLoadingManager* manager); + + _NODISCARD static std::string GetFileNameForCompiledShader(const std::string& assetName); + static bool LoadCompiled(const std::string& assetName, ISearchPath* searchPath, MemoryManager* memory, IAssetLoadingManager* manager); _NODISCARD void* CreateEmptyAsset(const std::string& assetName, MemoryManager* memory) override; _NODISCARD bool CanLoadFromRaw() const override; diff --git a/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderTechniqueSet.cpp b/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderTechniqueSet.cpp index b7f84e62..ccd4eec0 100644 --- a/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderTechniqueSet.cpp +++ b/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderTechniqueSet.cpp @@ -503,7 +503,7 @@ namespace IW4 if (pass.m_pixel_shader->Asset()->name && pass.m_pixel_shader->Asset()->name[0] == ',') { - pass.m_pixel_shader_info = m_shader_info_cache->LoadShaderInfoFromDisk(m_search_path, AssetLoaderPixelShader::GetFileNameForAsset(pixelShaderName)); + pass.m_pixel_shader_info = m_shader_info_cache->LoadShaderInfoFromDisk(m_search_path, AssetLoaderPixelShader::GetFileNameForCompiledShader(pixelShaderName)); } else { diff --git a/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderVertexShader.cpp b/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderVertexShader.cpp index a7953cfc..db010b70 100644 --- a/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderVertexShader.cpp +++ b/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderVertexShader.cpp @@ -4,11 +4,11 @@ #include #include #include -#include #include "ObjLoading.h" #include "Game/IW4/IW4.h" #include "Pool/GlobalAssetPool.h" +#include "Shader/ShaderIncludeHandler.h" #ifdef OS_TARGET_WINDOWS #include @@ -18,65 +18,6 @@ using namespace IW4; -class ShaderIncluder : public ID3DInclude -{ - static constexpr auto MAX_SHADER_SIZE = 0x1900000u; - -public: - ShaderIncluder(ISearchPath* searchPath) - : m_search_path(searchPath) - { - } - - virtual ~ShaderIncluder() = default; - - HRESULT __stdcall Open(D3D_INCLUDE_TYPE IncludeType, LPCSTR pFileName, LPCVOID pParentData, LPCVOID* ppData, UINT* pBytes) override - { - std::ostringstream ss; - ss << "shaders/" << pFileName; - - auto file = m_search_path->Open(ss.str()); - if (!file.IsOpen() || file.m_length <= 0) - return E_FAIL; - - if (file.m_length > MAX_SHADER_SIZE) - { - std::cerr << "Invalid shader source \"" << pFileName << "\": File too big: " << file.m_length << "\n"; - return false; - } - - 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 __stdcall Close(LPCVOID pData) override - { - 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; - } - -private: - ISearchPath* m_search_path; - std::vector> m_file_buffers_in_use; -}; - void* AssetLoaderVertexShader::CreateEmptyAsset(const std::string& assetName, MemoryManager* memory) { auto* vertexShader = memory->Create(); @@ -97,7 +38,7 @@ std::string AssetLoaderVertexShader::GetFileNameForSourceShader(const std::strin return ss.str(); } -bool AssetLoaderVertexShader::LoadFromSource(const std::string& assetName, ISearchPath* searchPath, MemoryManager* memory, IAssetLoadingManager* manager, Zone* zone) +bool AssetLoaderVertexShader::LoadFromSource(const std::string& assetName, ISearchPath* searchPath, MemoryManager* memory, IAssetLoadingManager* manager) { #ifdef OS_TARGET_WINDOWS const auto fileName = GetFileNameForSourceShader(assetName); @@ -105,7 +46,7 @@ bool AssetLoaderVertexShader::LoadFromSource(const std::string& assetName, ISear if (!file.IsOpen() || file.m_length <= 0) return false; - if (file.m_length > MAX_SHADER_SIZE) + if (file.m_length > ShaderIncludeHandler::MAX_SHADER_SIZE) { std::cerr << "Invalid vertex shader source \"" << assetName << "\": File too big: " << file.m_length << "\n"; return false; @@ -122,7 +63,7 @@ bool AssetLoaderVertexShader::LoadFromSource(const std::string& assetName, ISear #endif ; - ShaderIncluder shaderIncluder(searchPath); + ShaderIncludeHandler shaderIncluder(searchPath); ID3DBlob* shaderBlob = nullptr; ID3DBlob* errorBlob = nullptr; @@ -176,7 +117,7 @@ std::string AssetLoaderVertexShader::GetFileNameForCompiledShader(const std::str return ss.str(); } -bool AssetLoaderVertexShader::LoadCompiled(const std::string& assetName, ISearchPath* searchPath, MemoryManager* memory, IAssetLoadingManager* manager, Zone* zone) +bool AssetLoaderVertexShader::LoadCompiled(const std::string& assetName, ISearchPath* searchPath, MemoryManager* memory, IAssetLoadingManager* manager) { const auto fileName = GetFileNameForCompiledShader(assetName); const auto file = searchPath->Open(fileName); @@ -208,5 +149,5 @@ bool AssetLoaderVertexShader::LoadCompiled(const std::string& assetName, ISearch bool AssetLoaderVertexShader::LoadFromRaw(const std::string& assetName, ISearchPath* searchPath, MemoryManager* memory, IAssetLoadingManager* manager, Zone* zone) const { - return LoadFromSource(assetName, searchPath, memory, manager, zone) || LoadCompiled(assetName, searchPath, memory, manager, zone); + return LoadFromSource(assetName, searchPath, memory, manager) || LoadCompiled(assetName, searchPath, memory, manager); } diff --git a/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderVertexShader.h b/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderVertexShader.h index fc235db0..ba9f94b3 100644 --- a/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderVertexShader.h +++ b/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderVertexShader.h @@ -8,14 +8,12 @@ namespace IW4 { class AssetLoaderVertexShader final : public BasicAssetLoader { - static constexpr auto MAX_SHADER_SIZE = 0x1900000u; - public: _NODISCARD static std::string GetFileNameForSourceShader(const std::string& assetName); - static bool LoadFromSource(const std::string& assetName, ISearchPath* searchPath, MemoryManager* memory, IAssetLoadingManager* manager, Zone* zone); + static bool LoadFromSource(const std::string& assetName, ISearchPath* searchPath, MemoryManager* memory, IAssetLoadingManager* manager); _NODISCARD static std::string GetFileNameForCompiledShader(const std::string& assetName); - static bool LoadCompiled(const std::string& assetName, ISearchPath* searchPath, MemoryManager* memory, IAssetLoadingManager* manager, Zone* zone); + static bool LoadCompiled(const std::string& assetName, ISearchPath* searchPath, MemoryManager* memory, IAssetLoadingManager* manager); _NODISCARD void* CreateEmptyAsset(const std::string& assetName, MemoryManager* memory) override; _NODISCARD bool CanLoadFromRaw() const override; diff --git a/src/ObjLoading/Shader/ShaderIncludeHandler.cpp b/src/ObjLoading/Shader/ShaderIncludeHandler.cpp new file mode 100644 index 00000000..5d236ecb --- /dev/null +++ b/src/ObjLoading/Shader/ShaderIncludeHandler.cpp @@ -0,0 +1,57 @@ +#include "ShaderIncludeHandler.h" + +#include +#include + +#ifdef OS_TARGET_WINDOWS + +ShaderIncludeHandler::ShaderIncludeHandler(ISearchPath* searchPath) + : m_search_path(searchPath) +{ +} + +ShaderIncludeHandler::~ShaderIncludeHandler() = default; + +HRESULT ShaderIncludeHandler::Open(D3D_INCLUDE_TYPE includeType, const LPCSTR pFileName, LPCVOID pParentData, LPCVOID* ppData, UINT* pBytes) +{ + std::ostringstream ss; + ss << "shaders/" << pFileName; + + auto file = m_search_path->Open(ss.str()); + if (!file.IsOpen() || file.m_length <= 0) + return E_FAIL; + + if (file.m_length > MAX_SHADER_SIZE) + { + std::cerr << "Invalid shader source \"" << pFileName << "\": File too big: " << file.m_length << "\n"; + 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; +} + +#endif diff --git a/src/ObjLoading/Shader/ShaderIncludeHandler.h b/src/ObjLoading/Shader/ShaderIncludeHandler.h new file mode 100644 index 00000000..cd1b242d --- /dev/null +++ b/src/ObjLoading/Shader/ShaderIncludeHandler.h @@ -0,0 +1,25 @@ +#pragma once + +#include "SearchPath/ISearchPath.h" + +// Shader compilation only available on Windows +#ifdef OS_TARGET_WINDOWS +#include + +class ShaderIncludeHandler : public ID3DInclude +{ +public: + static constexpr auto MAX_SHADER_SIZE = 0x1900000u; + + ShaderIncludeHandler(ISearchPath* searchPath); + + virtual ~ShaderIncludeHandler(); + + 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; +}; +#endif \ No newline at end of file