792 lines
20 KiB
C++
792 lines
20 KiB
C++
#include "wiShaderCompiler.h"
|
|
#include "wiBacklog.h"
|
|
#include "wiPlatform.h"
|
|
#include "wiHelper.h"
|
|
#include "wiArchive.h"
|
|
#include "wiUnorderedSet.h"
|
|
|
|
#include <mutex>
|
|
#include <filesystem>
|
|
|
|
#ifdef PLATFORM_WINDOWS_DESKTOP
|
|
#define SHADERCOMPILER_ENABLED
|
|
#define SHADERCOMPILER_ENABLED_DXCOMPILER
|
|
#define SHADERCOMPILER_ENABLED_D3DCOMPILER
|
|
#include <atlbase.h> // ComPtr
|
|
#endif // _WIN32
|
|
|
|
#ifdef PLATFORM_LINUX
|
|
#define SHADERCOMPILER_ENABLED
|
|
#define SHADERCOMPILER_ENABLED_DXCOMPILER
|
|
#define __RPC_FAR
|
|
#endif // PLATFORM_LINUX
|
|
|
|
#ifdef SHADERCOMPILER_ENABLED_DXCOMPILER
|
|
#include "Utility/dxcapi.h"
|
|
#endif // SHADERCOMPILER_ENABLED_DXCOMPILER
|
|
|
|
#ifdef SHADERCOMPILER_ENABLED_D3DCOMPILER
|
|
#include <d3dcompiler.h>
|
|
#endif // SHADERCOMPILER_ENABLED_D3DCOMPILER
|
|
|
|
using namespace wi::graphics;
|
|
|
|
namespace wi::shadercompiler
|
|
{
|
|
|
|
#ifdef SHADERCOMPILER_ENABLED_DXCOMPILER
|
|
struct InternalState_DXC
|
|
{
|
|
DxcCreateInstanceProc DxcCreateInstance = nullptr;
|
|
|
|
InternalState_DXC()
|
|
{
|
|
#ifdef _WIN32
|
|
#define LIBDXCOMPILER "dxcompiler.dll"
|
|
HMODULE dxcompiler = wiLoadLibrary(LIBDXCOMPILER);
|
|
#elif defined(PLATFORM_LINUX)
|
|
#define LIBDXCOMPILER "libdxcompiler.so"
|
|
HMODULE dxcompiler = wiLoadLibrary("./" LIBDXCOMPILER);
|
|
#endif
|
|
if (dxcompiler != nullptr)
|
|
{
|
|
DxcCreateInstance = (DxcCreateInstanceProc)wiGetProcAddress(dxcompiler, "DxcCreateInstance");
|
|
if (DxcCreateInstance != nullptr)
|
|
{
|
|
CComPtr<IDxcCompiler3> dxcCompiler;
|
|
HRESULT hr = DxcCreateInstance(CLSID_DxcCompiler, IID_PPV_ARGS(&dxcCompiler));
|
|
assert(SUCCEEDED(hr));
|
|
CComPtr<IDxcVersionInfo> info;
|
|
hr = dxcCompiler->QueryInterface(&info);
|
|
assert(SUCCEEDED(hr));
|
|
uint32_t minor = 0;
|
|
uint32_t major = 0;
|
|
hr = info->GetVersion(&major, &minor);
|
|
assert(SUCCEEDED(hr));
|
|
wi::backlog::post("wi::shadercompiler: loaded " LIBDXCOMPILER " (version: " + std::to_string(major) + "." + std::to_string(minor) + ")");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
wi::backlog::post("wi::shadercompiler: could not load library " LIBDXCOMPILER);
|
|
}
|
|
|
|
}
|
|
};
|
|
inline InternalState_DXC& dxc_compiler()
|
|
{
|
|
static InternalState_DXC internal_state;
|
|
return internal_state;
|
|
}
|
|
|
|
void Compile_DXCompiler(const CompilerInput& input, CompilerOutput& output)
|
|
{
|
|
if (dxc_compiler().DxcCreateInstance == nullptr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
CComPtr<IDxcUtils> dxcUtils;
|
|
CComPtr<IDxcCompiler3> dxcCompiler;
|
|
|
|
HRESULT hr = dxc_compiler().DxcCreateInstance(CLSID_DxcUtils, IID_PPV_ARGS(&dxcUtils));
|
|
assert(SUCCEEDED(hr));
|
|
hr = dxc_compiler().DxcCreateInstance(CLSID_DxcCompiler, IID_PPV_ARGS(&dxcCompiler));
|
|
assert(SUCCEEDED(hr));
|
|
|
|
if (dxcCompiler == nullptr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
wi::vector<uint8_t> shadersourcedata;
|
|
if (!wi::helper::FileRead(input.shadersourcefilename, shadersourcedata))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// https://github.com/microsoft/DirectXShaderCompiler/wiki/Using-dxc.exe-and-dxcompiler.dll#dxcompiler-dll-interface
|
|
|
|
wi::vector<LPCWSTR> args = {
|
|
//L"-res-may-alias",
|
|
//L"-flegacy-macro-expansion",
|
|
//L"-no-legacy-cbuf-layout",
|
|
//L"-pack-optimized", // this has problem with tessellation shaders: https://github.com/microsoft/DirectXShaderCompiler/issues/3362
|
|
//L"-all-resources-bound",
|
|
//L"-Gis", // Force IEEE strictness
|
|
//L"-Gec", // Enable backward compatibility mode
|
|
//L"-Ges", // Enable strict mode
|
|
//L"-O0", // Optimization Level 0
|
|
};
|
|
|
|
if (has_flag(input.flags, Flags::DISABLE_OPTIMIZATION))
|
|
{
|
|
args.push_back(L"-Od");
|
|
}
|
|
|
|
switch (input.format)
|
|
{
|
|
case ShaderFormat::HLSL6:
|
|
args.push_back(L"-D"); args.push_back(L"HLSL6");
|
|
args.push_back(L"-rootsig-define"); args.push_back(L"WICKED_ENGINE_DEFAULT_ROOTSIGNATURE");
|
|
break;
|
|
case ShaderFormat::SPIRV:
|
|
args.push_back(L"-D"); args.push_back(L"SPIRV");
|
|
args.push_back(L"-spirv");
|
|
args.push_back(L"-fspv-target-env=vulkan1.2");
|
|
args.push_back(L"-fvk-use-dx-layout");
|
|
args.push_back(L"-fvk-use-dx-position-w");
|
|
//args.push_back(L"-fvk-b-shift"); args.push_back(L"0"); args.push_back(L"0");
|
|
args.push_back(L"-fvk-t-shift"); args.push_back(L"1000"); args.push_back(L"0");
|
|
args.push_back(L"-fvk-u-shift"); args.push_back(L"2000"); args.push_back(L"0");
|
|
args.push_back(L"-fvk-s-shift"); args.push_back(L"3000"); args.push_back(L"0");
|
|
break;
|
|
default:
|
|
assert(0);
|
|
return;
|
|
}
|
|
|
|
args.push_back(L"-T");
|
|
switch (input.stage)
|
|
{
|
|
case ShaderStage::MS:
|
|
switch (input.minshadermodel)
|
|
{
|
|
default:
|
|
args.push_back(L"ms_6_5");
|
|
break;
|
|
case ShaderModel::SM_6_6:
|
|
args.push_back(L"ms_6_6");
|
|
break;
|
|
case ShaderModel::SM_6_7:
|
|
args.push_back(L"ms_6_7");
|
|
break;
|
|
}
|
|
break;
|
|
case ShaderStage::AS:
|
|
switch (input.minshadermodel)
|
|
{
|
|
default:
|
|
args.push_back(L"as_6_5");
|
|
break;
|
|
case ShaderModel::SM_6_6:
|
|
args.push_back(L"as_6_6");
|
|
break;
|
|
case ShaderModel::SM_6_7:
|
|
args.push_back(L"as_6_7");
|
|
break;
|
|
}
|
|
break;
|
|
case ShaderStage::VS:
|
|
switch (input.minshadermodel)
|
|
{
|
|
default:
|
|
args.push_back(L"vs_6_0");
|
|
break;
|
|
case ShaderModel::SM_6_1:
|
|
args.push_back(L"vs_6_1");
|
|
break;
|
|
case ShaderModel::SM_6_2:
|
|
args.push_back(L"vs_6_2");
|
|
break;
|
|
case ShaderModel::SM_6_3:
|
|
args.push_back(L"vs_6_3");
|
|
break;
|
|
case ShaderModel::SM_6_4:
|
|
args.push_back(L"vs_6_4");
|
|
break;
|
|
case ShaderModel::SM_6_5:
|
|
args.push_back(L"vs_6_5");
|
|
break;
|
|
case ShaderModel::SM_6_6:
|
|
args.push_back(L"vs_6_6");
|
|
break;
|
|
case ShaderModel::SM_6_7:
|
|
args.push_back(L"vs_6_7");
|
|
break;
|
|
}
|
|
break;
|
|
case ShaderStage::HS:
|
|
switch (input.minshadermodel)
|
|
{
|
|
default:
|
|
args.push_back(L"hs_6_0");
|
|
break;
|
|
case ShaderModel::SM_6_1:
|
|
args.push_back(L"hs_6_1");
|
|
break;
|
|
case ShaderModel::SM_6_2:
|
|
args.push_back(L"hs_6_2");
|
|
break;
|
|
case ShaderModel::SM_6_3:
|
|
args.push_back(L"hs_6_3");
|
|
break;
|
|
case ShaderModel::SM_6_4:
|
|
args.push_back(L"hs_6_4");
|
|
break;
|
|
case ShaderModel::SM_6_5:
|
|
args.push_back(L"hs_6_5");
|
|
break;
|
|
case ShaderModel::SM_6_6:
|
|
args.push_back(L"hs_6_6");
|
|
break;
|
|
case ShaderModel::SM_6_7:
|
|
args.push_back(L"hs_6_7");
|
|
break;
|
|
}
|
|
break;
|
|
case ShaderStage::DS:
|
|
switch (input.minshadermodel)
|
|
{
|
|
default:
|
|
args.push_back(L"ds_6_0");
|
|
break;
|
|
case ShaderModel::SM_6_1:
|
|
args.push_back(L"ds_6_1");
|
|
break;
|
|
case ShaderModel::SM_6_2:
|
|
args.push_back(L"ds_6_2");
|
|
break;
|
|
case ShaderModel::SM_6_3:
|
|
args.push_back(L"ds_6_3");
|
|
break;
|
|
case ShaderModel::SM_6_4:
|
|
args.push_back(L"ds_6_4");
|
|
break;
|
|
case ShaderModel::SM_6_5:
|
|
args.push_back(L"ds_6_5");
|
|
break;
|
|
case ShaderModel::SM_6_6:
|
|
args.push_back(L"ds_6_6");
|
|
break;
|
|
case ShaderModel::SM_6_7:
|
|
args.push_back(L"ds_6_7");
|
|
break;
|
|
}
|
|
break;
|
|
case ShaderStage::GS:
|
|
switch (input.minshadermodel)
|
|
{
|
|
default:
|
|
args.push_back(L"gs_6_0");
|
|
break;
|
|
case ShaderModel::SM_6_1:
|
|
args.push_back(L"gs_6_1");
|
|
break;
|
|
case ShaderModel::SM_6_2:
|
|
args.push_back(L"gs_6_2");
|
|
break;
|
|
case ShaderModel::SM_6_3:
|
|
args.push_back(L"gs_6_3");
|
|
break;
|
|
case ShaderModel::SM_6_4:
|
|
args.push_back(L"gs_6_4");
|
|
break;
|
|
case ShaderModel::SM_6_5:
|
|
args.push_back(L"gs_6_5");
|
|
break;
|
|
case ShaderModel::SM_6_6:
|
|
args.push_back(L"gs_6_6");
|
|
break;
|
|
case ShaderModel::SM_6_7:
|
|
args.push_back(L"gs_6_7");
|
|
break;
|
|
}
|
|
break;
|
|
case ShaderStage::PS:
|
|
switch (input.minshadermodel)
|
|
{
|
|
default:
|
|
args.push_back(L"ps_6_0");
|
|
break;
|
|
case ShaderModel::SM_6_1:
|
|
args.push_back(L"ps_6_1");
|
|
break;
|
|
case ShaderModel::SM_6_2:
|
|
args.push_back(L"ps_6_2");
|
|
break;
|
|
case ShaderModel::SM_6_3:
|
|
args.push_back(L"ps_6_3");
|
|
break;
|
|
case ShaderModel::SM_6_4:
|
|
args.push_back(L"ps_6_4");
|
|
break;
|
|
case ShaderModel::SM_6_5:
|
|
args.push_back(L"ps_6_5");
|
|
break;
|
|
case ShaderModel::SM_6_6:
|
|
args.push_back(L"ps_6_6");
|
|
break;
|
|
case ShaderModel::SM_6_7:
|
|
args.push_back(L"ps_6_7");
|
|
break;
|
|
}
|
|
break;
|
|
case ShaderStage::CS:
|
|
switch (input.minshadermodel)
|
|
{
|
|
default:
|
|
args.push_back(L"cs_6_0");
|
|
break;
|
|
case ShaderModel::SM_6_1:
|
|
args.push_back(L"cs_6_1");
|
|
break;
|
|
case ShaderModel::SM_6_2:
|
|
args.push_back(L"cs_6_2");
|
|
break;
|
|
case ShaderModel::SM_6_3:
|
|
args.push_back(L"cs_6_3");
|
|
break;
|
|
case ShaderModel::SM_6_4:
|
|
args.push_back(L"cs_6_4");
|
|
break;
|
|
case ShaderModel::SM_6_5:
|
|
args.push_back(L"cs_6_5");
|
|
break;
|
|
case ShaderModel::SM_6_6:
|
|
args.push_back(L"cs_6_6");
|
|
break;
|
|
case ShaderModel::SM_6_7:
|
|
args.push_back(L"cs_6_7");
|
|
break;
|
|
}
|
|
break;
|
|
case ShaderStage::LIB:
|
|
switch (input.minshadermodel)
|
|
{
|
|
default:
|
|
args.push_back(L"lib_6_5");
|
|
break;
|
|
case ShaderModel::SM_6_6:
|
|
args.push_back(L"lib_6_6");
|
|
break;
|
|
case ShaderModel::SM_6_7:
|
|
args.push_back(L"lib_6_7");
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
assert(0);
|
|
return;
|
|
}
|
|
|
|
wi::vector<std::wstring> wstrings;
|
|
wstrings.reserve(input.defines.size() + input.include_directories.size());
|
|
|
|
for (auto& x : input.defines)
|
|
{
|
|
std::wstring& wstr = wstrings.emplace_back();
|
|
wi::helper::StringConvert(x, wstr);
|
|
args.push_back(L"-D");
|
|
args.push_back(wstr.c_str());
|
|
}
|
|
|
|
for (auto& x : input.include_directories)
|
|
{
|
|
std::wstring& wstr = wstrings.emplace_back();
|
|
wi::helper::StringConvert(x, wstr);
|
|
args.push_back(L"-I");
|
|
args.push_back(wstr.c_str());
|
|
}
|
|
|
|
// Entry point parameter:
|
|
std::wstring wentry;
|
|
wi::helper::StringConvert(input.entrypoint, wentry);
|
|
args.push_back(L"-E");
|
|
args.push_back(wentry.c_str());
|
|
|
|
// Add source file name as last parameter. This will be displayed in error messages
|
|
std::wstring wsource;
|
|
wi::helper::StringConvert(wi::helper::GetFileNameFromPath(input.shadersourcefilename), wsource);
|
|
args.push_back(wsource.c_str());
|
|
|
|
DxcBuffer Source;
|
|
Source.Ptr = shadersourcedata.data();
|
|
Source.Size = shadersourcedata.size();
|
|
Source.Encoding = DXC_CP_ACP;
|
|
|
|
struct IncludeHandler : public IDxcIncludeHandler
|
|
{
|
|
const CompilerInput* input = nullptr;
|
|
CompilerOutput* output = nullptr;
|
|
CComPtr<IDxcIncludeHandler> dxcIncludeHandler;
|
|
|
|
HRESULT STDMETHODCALLTYPE LoadSource(
|
|
_In_z_ LPCWSTR pFilename, // Candidate filename.
|
|
_COM_Outptr_result_maybenull_ IDxcBlob** ppIncludeSource // Resultant source object for included file, nullptr if not found.
|
|
) override
|
|
{
|
|
HRESULT hr = dxcIncludeHandler->LoadSource(pFilename, ppIncludeSource);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
std::string& filename = output->dependencies.emplace_back();
|
|
wi::helper::StringConvert(pFilename, filename);
|
|
}
|
|
return hr;
|
|
}
|
|
HRESULT STDMETHODCALLTYPE QueryInterface(
|
|
/* [in] */ REFIID riid,
|
|
/* [iid_is][out] */ _COM_Outptr_ void __RPC_FAR* __RPC_FAR* ppvObject) override
|
|
{
|
|
return dxcIncludeHandler->QueryInterface(riid, ppvObject);
|
|
}
|
|
|
|
ULONG STDMETHODCALLTYPE AddRef(void) override
|
|
{
|
|
return 0;
|
|
}
|
|
ULONG STDMETHODCALLTYPE Release(void) override
|
|
{
|
|
return 0;
|
|
}
|
|
} includehandler;
|
|
includehandler.input = &input;
|
|
includehandler.output = &output;
|
|
|
|
hr = dxcUtils->CreateDefaultIncludeHandler(&includehandler.dxcIncludeHandler);
|
|
assert(SUCCEEDED(hr));
|
|
|
|
CComPtr<IDxcResult> pResults;
|
|
hr = dxcCompiler->Compile(
|
|
&Source, // Source buffer.
|
|
args.data(), // Array of pointers to arguments.
|
|
(uint32_t)args.size(), // Number of arguments.
|
|
&includehandler, // User-provided interface to handle #include directives (optional).
|
|
IID_PPV_ARGS(&pResults) // Compiler output status, buffer, and errors.
|
|
);
|
|
assert(SUCCEEDED(hr));
|
|
|
|
CComPtr<IDxcBlobUtf8> pErrors = nullptr;
|
|
hr = pResults->GetOutput(DXC_OUT_ERRORS, IID_PPV_ARGS(&pErrors), nullptr);
|
|
assert(SUCCEEDED(hr));
|
|
if (pErrors != nullptr && pErrors->GetStringLength() != 0)
|
|
{
|
|
output.error_message = pErrors->GetStringPointer();
|
|
}
|
|
|
|
HRESULT hrStatus;
|
|
hr = pResults->GetStatus(&hrStatus);
|
|
assert(SUCCEEDED(hr));
|
|
if (FAILED(hrStatus))
|
|
{
|
|
return;
|
|
}
|
|
|
|
CComPtr<IDxcBlob> pShader = nullptr;
|
|
hr = pResults->GetOutput(DXC_OUT_OBJECT, IID_PPV_ARGS(&pShader), nullptr);
|
|
assert(SUCCEEDED(hr));
|
|
if (pShader != nullptr)
|
|
{
|
|
output.dependencies.push_back(input.shadersourcefilename);
|
|
output.shaderdata = (const uint8_t*)pShader->GetBufferPointer();
|
|
output.shadersize = pShader->GetBufferSize();
|
|
|
|
// keep the blob alive == keep shader pointer valid!
|
|
auto internal_state = std::make_shared<CComPtr<IDxcBlob>>();
|
|
*internal_state = pShader;
|
|
output.internal_state = internal_state;
|
|
}
|
|
|
|
if (input.format == ShaderFormat::HLSL6)
|
|
{
|
|
CComPtr<IDxcBlob> pHash = nullptr;
|
|
hr = pResults->GetOutput(DXC_OUT_SHADER_HASH, IID_PPV_ARGS(&pHash), nullptr);
|
|
assert(SUCCEEDED(hr));
|
|
if (pHash != nullptr)
|
|
{
|
|
DxcShaderHash* pHashBuf = (DxcShaderHash*)pHash->GetBufferPointer();
|
|
for (int i = 0; i < _countof(pHashBuf->HashDigest); i++)
|
|
{
|
|
output.shaderhash.push_back(pHashBuf->HashDigest[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif // SHADERCOMPILER_ENABLED_DXCOMPILER
|
|
|
|
#ifdef SHADERCOMPILER_ENABLED_D3DCOMPILER
|
|
struct InternalState_D3DCompiler
|
|
{
|
|
using PFN_D3DCOMPILE = decltype(&D3DCompile);
|
|
PFN_D3DCOMPILE D3DCompile = nullptr;
|
|
|
|
InternalState_D3DCompiler()
|
|
{
|
|
if (D3DCompile != nullptr)
|
|
{
|
|
return; // already initialized
|
|
}
|
|
|
|
HMODULE d3dcompiler = wiLoadLibrary("d3dcompiler_47.dll");
|
|
if (d3dcompiler != nullptr)
|
|
{
|
|
D3DCompile = (PFN_D3DCOMPILE)wiGetProcAddress(d3dcompiler, "D3DCompile");
|
|
if (D3DCompile != nullptr)
|
|
{
|
|
wi::backlog::post("wi::shadercompiler: loaded d3dcompiler_47.dll");
|
|
}
|
|
}
|
|
}
|
|
};
|
|
inline InternalState_D3DCompiler& d3d_compiler()
|
|
{
|
|
static InternalState_D3DCompiler internal_state;
|
|
return internal_state;
|
|
}
|
|
|
|
void Compile_D3DCompiler(const CompilerInput& input, CompilerOutput& output)
|
|
{
|
|
if (d3d_compiler().D3DCompile == nullptr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (input.minshadermodel > ShaderModel::SM_5_0)
|
|
{
|
|
output.error_message = "SHADERFORMAT_HLSL5 cannot support specified minshadermodel!";
|
|
return;
|
|
}
|
|
|
|
wi::vector<uint8_t> shadersourcedata;
|
|
if (!wi::helper::FileRead(input.shadersourcefilename, shadersourcedata))
|
|
{
|
|
return;
|
|
}
|
|
|
|
D3D_SHADER_MACRO defines[] = {
|
|
"HLSL5", "1",
|
|
"DISABLE_WAVE_INTRINSICS", "1",
|
|
NULL, NULL,
|
|
};
|
|
|
|
const char* target = nullptr;
|
|
switch (input.stage)
|
|
{
|
|
default:
|
|
case ShaderStage::MS:
|
|
case ShaderStage::AS:
|
|
case ShaderStage::LIB:
|
|
// not applicable
|
|
return;
|
|
case ShaderStage::VS:
|
|
target = "vs_5_0";
|
|
break;
|
|
case ShaderStage::HS:
|
|
target = "hs_5_0";
|
|
break;
|
|
case ShaderStage::DS:
|
|
target = "ds_5_0";
|
|
break;
|
|
case ShaderStage::GS:
|
|
target = "gs_5_0";
|
|
break;
|
|
case ShaderStage::PS:
|
|
target = "ps_5_0";
|
|
break;
|
|
case ShaderStage::CS:
|
|
target = "cs_5_0";
|
|
break;
|
|
}
|
|
|
|
struct IncludeHandler : public ID3DInclude
|
|
{
|
|
const CompilerInput* input = nullptr;
|
|
CompilerOutput* output = nullptr;
|
|
wi::vector<wi::vector<uint8_t>> filedatas;
|
|
|
|
HRESULT Open(D3D_INCLUDE_TYPE IncludeType, LPCSTR pFileName, LPCVOID pParentData, LPCVOID* ppData, UINT* pBytes) override
|
|
{
|
|
for (auto& x : input->include_directories)
|
|
{
|
|
std::string filename = x + pFileName;
|
|
if (!wi::helper::FileExists(filename))
|
|
continue;
|
|
wi::vector<uint8_t>& filedata = filedatas.emplace_back();
|
|
if (wi::helper::FileRead(filename, filedata))
|
|
{
|
|
output->dependencies.push_back(filename);
|
|
*ppData = filedata.data();
|
|
*pBytes = (UINT)filedata.size();
|
|
return S_OK;
|
|
}
|
|
}
|
|
return E_FAIL;
|
|
}
|
|
|
|
HRESULT Close(LPCVOID pData) override
|
|
{
|
|
return S_OK;
|
|
}
|
|
} includehandler;
|
|
includehandler.input = &input;
|
|
includehandler.output = &output;
|
|
|
|
// https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/d3dcompile-constants
|
|
UINT Flags1 = 0;
|
|
if (has_flag(input.flags, Flags::DISABLE_OPTIMIZATION))
|
|
{
|
|
Flags1 |= D3DCOMPILE_SKIP_OPTIMIZATION;
|
|
}
|
|
|
|
|
|
CComPtr<ID3DBlob> code;
|
|
CComPtr<ID3DBlob> errors;
|
|
HRESULT hr = d3d_compiler().D3DCompile(
|
|
shadersourcedata.data(),
|
|
shadersourcedata.size(),
|
|
input.shadersourcefilename.c_str(),
|
|
defines,
|
|
&includehandler, //D3D_COMPILE_STANDARD_FILE_INCLUDE,
|
|
input.entrypoint.c_str(),
|
|
target,
|
|
Flags1,
|
|
0,
|
|
&code,
|
|
&errors
|
|
);
|
|
|
|
if (errors)
|
|
{
|
|
output.error_message = (const char*)errors->GetBufferPointer();
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
output.dependencies.push_back(input.shadersourcefilename);
|
|
output.shaderdata = (const uint8_t*)code->GetBufferPointer();
|
|
output.shadersize = code->GetBufferSize();
|
|
|
|
// keep the blob alive == keep shader pointer valid!
|
|
auto internal_state = std::make_shared<CComPtr<ID3D10Blob>>();
|
|
*internal_state = code;
|
|
output.internal_state = internal_state;
|
|
}
|
|
}
|
|
#endif // SHADERCOMPILER_ENABLED_D3DCOMPILER
|
|
|
|
void Compile(const CompilerInput& input, CompilerOutput& output)
|
|
{
|
|
output = CompilerOutput();
|
|
|
|
#ifdef SHADERCOMPILER_ENABLED
|
|
switch (input.format)
|
|
{
|
|
default:
|
|
break;
|
|
|
|
#ifdef SHADERCOMPILER_ENABLED_DXCOMPILER
|
|
case ShaderFormat::HLSL6:
|
|
case ShaderFormat::SPIRV:
|
|
Compile_DXCompiler(input, output);
|
|
break;
|
|
#endif // SHADERCOMPILER_ENABLED_DXCOMPILER
|
|
|
|
#ifdef SHADERCOMPILER_ENABLED_D3DCOMPILER
|
|
case ShaderFormat::HLSL5:
|
|
Compile_D3DCompiler(input, output);
|
|
break;
|
|
#endif // SHADERCOMPILER_ENABLED_D3DCOMPILER
|
|
|
|
}
|
|
#endif // SHADERCOMPILER_ENABLED
|
|
}
|
|
|
|
constexpr const char* shadermetaextension = "wishadermeta";
|
|
bool SaveShaderAndMetadata(const std::string& shaderfilename, const CompilerOutput& output)
|
|
{
|
|
#ifdef SHADERCOMPILER_ENABLED
|
|
wi::helper::DirectoryCreate(wi::helper::GetDirectoryFromPath(shaderfilename));
|
|
|
|
wi::Archive dependencyLibrary(wi::helper::ReplaceExtension(shaderfilename, shadermetaextension), false);
|
|
if (dependencyLibrary.IsOpen())
|
|
{
|
|
std::string rootdir = dependencyLibrary.GetSourceDirectory();
|
|
wi::vector<std::string> dependencies = output.dependencies;
|
|
for (auto& x : dependencies)
|
|
{
|
|
wi::helper::MakePathRelative(rootdir, x);
|
|
}
|
|
dependencyLibrary << dependencies;
|
|
}
|
|
|
|
if (wi::helper::FileWrite(shaderfilename, output.shaderdata, output.shadersize))
|
|
{
|
|
return true;
|
|
}
|
|
#endif // SHADERCOMPILER_ENABLED
|
|
|
|
return false;
|
|
}
|
|
bool IsShaderOutdated(const std::string& shaderfilename)
|
|
{
|
|
#ifdef SHADERCOMPILER_ENABLED
|
|
std::string filepath = shaderfilename;
|
|
wi::helper::MakePathAbsolute(filepath);
|
|
if (!wi::helper::FileExists(filepath))
|
|
{
|
|
return true; // no shader file = outdated shader, apps can attempt to rebuild it
|
|
}
|
|
std::string dependencylibrarypath = wi::helper::ReplaceExtension(shaderfilename, shadermetaextension);
|
|
if (!wi::helper::FileExists(dependencylibrarypath))
|
|
{
|
|
return false; // no metadata file = no dependency, up to date (for example packaged builds)
|
|
}
|
|
|
|
const auto tim = std::filesystem::last_write_time(filepath);
|
|
|
|
wi::Archive dependencyLibrary(dependencylibrarypath);
|
|
if (dependencyLibrary.IsOpen())
|
|
{
|
|
std::string rootdir = dependencyLibrary.GetSourceDirectory();
|
|
wi::vector<std::string> dependencies;
|
|
dependencyLibrary >> dependencies;
|
|
|
|
for (auto& x : dependencies)
|
|
{
|
|
std::string dependencypath = rootdir + x;
|
|
wi::helper::MakePathAbsolute(dependencypath);
|
|
if (wi::helper::FileExists(dependencypath))
|
|
{
|
|
const auto dep_tim = std::filesystem::last_write_time(dependencypath);
|
|
|
|
if (tim < dep_tim)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif // SHADERCOMPILER_ENABLED
|
|
|
|
return false;
|
|
}
|
|
|
|
std::mutex locker;
|
|
wi::unordered_set<std::string> registered_shaders;
|
|
void RegisterShader(const std::string& shaderfilename)
|
|
{
|
|
#ifdef SHADERCOMPILER_ENABLED
|
|
std::scoped_lock lock(locker);
|
|
registered_shaders.insert(shaderfilename);
|
|
#endif // SHADERCOMPILER_ENABLED
|
|
}
|
|
size_t GetRegisteredShaderCount()
|
|
{
|
|
std::scoped_lock lock(locker);
|
|
return registered_shaders.size();
|
|
}
|
|
bool CheckRegisteredShadersOutdated()
|
|
{
|
|
#ifdef SHADERCOMPILER_ENABLED
|
|
for (auto& x : registered_shaders)
|
|
{
|
|
if (IsShaderOutdated(x))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
#endif // SHADERCOMPILER_ENABLED
|
|
return false;
|
|
}
|
|
}
|