diff --git a/premake5.lua b/premake5.lua index a7072040..76c11256 100644 --- a/premake5.lua +++ b/premake5.lua @@ -43,6 +43,14 @@ workspace "OpenAssetTools" defines "ARCH_x64" filter {} + filter "system:windows" + defines "OS_TARGET_WINDOWS" + filter {} + + filter "system:linux" + defines "OS_TARGET_LINUX" + filter {} + filter "configurations:Debug" defines "_DEBUG" optimize "Debug" diff --git a/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderTechniqueSet.cpp b/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderTechniqueSet.cpp index 74cb7384..b7f84e62 100644 --- a/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderTechniqueSet.cpp +++ b/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderTechniqueSet.cpp @@ -464,7 +464,7 @@ namespace IW4 if (pass.m_vertex_shader->Asset()->name && pass.m_vertex_shader->Asset()->name[0] == ',') { - pass.m_vertex_shader_info = m_shader_info_cache->LoadShaderInfoFromDisk(m_search_path, AssetLoaderVertexShader::GetFileNameForAsset(vertexShaderName)); + pass.m_vertex_shader_info = m_shader_info_cache->LoadShaderInfoFromDisk(m_search_path, AssetLoaderVertexShader::GetFileNameForCompiledShader(vertexShaderName)); } else { diff --git a/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderVertexShader.cpp b/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderVertexShader.cpp index c619bbd6..a7953cfc 100644 --- a/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderVertexShader.cpp +++ b/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderVertexShader.cpp @@ -4,13 +4,79 @@ #include #include #include +#include #include "ObjLoading.h" #include "Game/IW4/IW4.h" #include "Pool/GlobalAssetPool.h" +#ifdef OS_TARGET_WINDOWS +#include + +#pragma comment(lib,"d3dcompiler.lib") +#endif + 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(); @@ -24,23 +90,102 @@ bool AssetLoaderVertexShader::CanLoadFromRaw() const return true; } -std::string AssetLoaderVertexShader::GetFileNameForAsset(const std::string& assetName) +std::string AssetLoaderVertexShader::GetFileNameForSourceShader(const std::string& assetName) +{ + std::ostringstream ss; + ss << "shader/" << assetName; + return ss.str(); +} + +bool AssetLoaderVertexShader::LoadFromSource(const std::string& assetName, ISearchPath* searchPath, MemoryManager* memory, IAssetLoadingManager* manager, Zone* zone) +{ +#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 > MAX_SHADER_SIZE) + { + std::cerr << "Invalid vertex 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 + ; + + ShaderIncluder shaderIncluder(searchPath); + + 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)) + { + std::cerr << "Invalid vertex 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 vertex shader \"" << assetName << "\"\n"; + + const auto shaderBlobSize = static_cast(shaderBlob->GetBufferSize()); + assert(shaderBlobSize % sizeof(uint32_t) == 0); + + auto* vertexShader = memory->Create(); + vertexShader->name = 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 = static_cast(memory->Alloc(shaderBlobSize)); + memcpy(assetShaderBuffer, shaderBlob->GetBufferPointer(), shaderBlobSize); + vertexShader->prog.loadDef.program = reinterpret_cast(assetShaderBuffer); + manager->AddAsset(ASSET_TYPE_VERTEXSHADER, assetName, vertexShader); + + shaderBlob->Release(); + + return true; +#else + // Shader compilation is only support with Windows + return false; +#endif +} + +std::string AssetLoaderVertexShader::GetFileNameForCompiledShader(const std::string& assetName) { std::ostringstream ss; ss << "shader_bin/vs_" << assetName << ".cso"; return ss.str(); } -bool AssetLoaderVertexShader::LoadFromRaw(const std::string& assetName, ISearchPath* searchPath, MemoryManager* memory, IAssetLoadingManager* manager, Zone* zone) const +bool AssetLoaderVertexShader::LoadCompiled(const std::string& assetName, ISearchPath* searchPath, MemoryManager* memory, IAssetLoadingManager* manager, Zone* zone) { - const auto fileName = GetFileNameForAsset(assetName); + const auto fileName = GetFileNameForCompiledShader(assetName); const auto file = searchPath->Open(fileName); if (!file.IsOpen()) return false; if (file.m_length % sizeof(uint32_t) != 0) { - std::cerr << "Invalid vertex shader \"" << assetName << "\": Size must be dividable by " << sizeof(uint32_t) << "\n"; + std::cerr << "Invalid compiled vertex shader \"" << assetName << "\": Size must be dividable by " << sizeof(uint32_t) << "\n"; return false; } @@ -60,3 +205,8 @@ bool AssetLoaderVertexShader::LoadFromRaw(const std::string& assetName, ISearchP return true; } + +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); +} diff --git a/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderVertexShader.h b/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderVertexShader.h index b2eab7e8..fc235db0 100644 --- a/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderVertexShader.h +++ b/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderVertexShader.h @@ -8,8 +8,14 @@ namespace IW4 { class AssetLoaderVertexShader final : public BasicAssetLoader { + static constexpr auto MAX_SHADER_SIZE = 0x1900000u; + 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, Zone* zone); + + _NODISCARD static std::string GetFileNameForCompiledShader(const std::string& assetName); + static bool LoadCompiled(const std::string& assetName, ISearchPath* searchPath, MemoryManager* memory, IAssetLoadingManager* manager, Zone* zone); _NODISCARD void* CreateEmptyAsset(const std::string& assetName, MemoryManager* memory) override; _NODISCARD bool CanLoadFromRaw() const override;