Fix Vulkan unlit single-source HLSL compilation
This commit is contained in:
@@ -11,19 +11,56 @@ Shader "Builtin Unlit"
|
||||
{
|
||||
Name "Unlit"
|
||||
Tags { "LightMode" = "Unlit" }
|
||||
Resources
|
||||
{
|
||||
PerObjectConstants (ConstantBuffer, 0, 0) [Semantic(PerObject)]
|
||||
MaterialConstants (ConstantBuffer, 1, 0) [Semantic(Material)]
|
||||
BaseColorTexture (Texture2D, 2, 0) [Semantic(BaseColorTexture)]
|
||||
LinearClampSampler (Sampler, 3, 0) [Semantic(LinearClampSampler)]
|
||||
}
|
||||
Cull Back
|
||||
ZWrite On
|
||||
ZTest LEqual
|
||||
HLSLPROGRAM
|
||||
#pragma target 4.5
|
||||
#pragma vertex MainVS
|
||||
#pragma fragment MainPS
|
||||
#pragma backend D3D12 HLSL "unlit.vs.hlsl" "unlit.ps.hlsl" vs_5_0 ps_5_0
|
||||
#pragma backend OpenGL GLSL "unlit.vert.glsl" "unlit.frag.glsl"
|
||||
#pragma backend Vulkan GLSL "unlit.vert.vk.glsl" "unlit.frag.vk.glsl"
|
||||
cbuffer PerObjectConstants : register(b0)
|
||||
{
|
||||
float4x4 gProjectionMatrix;
|
||||
float4x4 gViewMatrix;
|
||||
float4x4 gModelMatrix;
|
||||
float4x4 gNormalMatrix;
|
||||
};
|
||||
|
||||
cbuffer MaterialConstants : register(b1)
|
||||
{
|
||||
float4 gBaseColorFactor;
|
||||
};
|
||||
|
||||
Texture2D BaseColorTexture : register(t0);
|
||||
SamplerState LinearClampSampler : register(s0);
|
||||
|
||||
struct VSInput
|
||||
{
|
||||
float3 position : POSITION;
|
||||
float3 normal : NORMAL;
|
||||
float2 texcoord : TEXCOORD0;
|
||||
};
|
||||
|
||||
struct PSInput
|
||||
{
|
||||
float4 position : SV_POSITION;
|
||||
float2 texcoord : TEXCOORD0;
|
||||
};
|
||||
|
||||
PSInput MainVS(VSInput input)
|
||||
{
|
||||
PSInput output;
|
||||
const float4 positionWS = mul(gModelMatrix, float4(input.position, 1.0f));
|
||||
const float4 positionVS = mul(gViewMatrix, positionWS);
|
||||
output.position = mul(gProjectionMatrix, positionVS);
|
||||
output.texcoord = input.texcoord;
|
||||
return output;
|
||||
}
|
||||
|
||||
float4 MainPS(PSInput input) : SV_TARGET
|
||||
{
|
||||
return BaseColorTexture.Sample(LinearClampSampler, input.texcoord) * gBaseColorFactor;
|
||||
}
|
||||
ENDHLSL
|
||||
}
|
||||
}
|
||||
|
||||
@@ -329,6 +329,49 @@ bool FindGlslangValidator(std::wstring& outPath) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FindDxcCompiler(std::wstring& outPath) {
|
||||
const std::wstring vulkanSdk = GetEnvironmentVariableValue(L"VULKAN_SDK");
|
||||
if (!vulkanSdk.empty()) {
|
||||
const std::filesystem::path sdkCandidate = std::filesystem::path(vulkanSdk) / L"Bin" / L"dxc.exe";
|
||||
if (std::filesystem::exists(sdkCandidate)) {
|
||||
outPath = sdkCandidate.wstring();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::filesystem::path> candidates;
|
||||
const std::filesystem::path sdkRoot = L"D:/VulkanSDK";
|
||||
if (std::filesystem::exists(sdkRoot) && std::filesystem::is_directory(sdkRoot)) {
|
||||
for (const auto& entry : std::filesystem::directory_iterator(sdkRoot)) {
|
||||
if (!entry.is_directory()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::filesystem::path candidate = entry.path() / L"Bin" / L"dxc.exe";
|
||||
if (std::filesystem::exists(candidate)) {
|
||||
candidates.push_back(candidate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!candidates.empty()) {
|
||||
std::sort(candidates.begin(), candidates.end(), [](const std::filesystem::path& lhs, const std::filesystem::path& rhs) {
|
||||
return lhs.wstring() > rhs.wstring();
|
||||
});
|
||||
outPath = candidates.front().wstring();
|
||||
return true;
|
||||
}
|
||||
|
||||
wchar_t buffer[MAX_PATH] = {};
|
||||
const DWORD length = SearchPathW(nullptr, L"dxc.exe", nullptr, MAX_PATH, buffer, nullptr);
|
||||
if (length > 0 && length < MAX_PATH) {
|
||||
outPath = buffer;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FindSpirvCross(std::wstring& outPath) {
|
||||
const std::wstring vulkanSdk = GetEnvironmentVariableValue(L"VULKAN_SDK");
|
||||
if (!vulkanSdk.empty()) {
|
||||
@@ -486,8 +529,35 @@ std::string InjectMacrosIntoSource(const std::string& source, const std::vector<
|
||||
macroBlock += '\n';
|
||||
}
|
||||
|
||||
if (source.rfind("#version", 0) == 0) {
|
||||
const size_t lineEnd = source.find('\n');
|
||||
size_t scan = 0;
|
||||
while (scan < source.size()) {
|
||||
while (scan < source.size() && std::isspace(static_cast<unsigned char>(source[scan])) != 0) {
|
||||
++scan;
|
||||
}
|
||||
|
||||
if (scan + 1 < source.size() && source[scan] == '/' && source[scan + 1] == '/') {
|
||||
const size_t lineEnd = source.find('\n', scan);
|
||||
if (lineEnd == std::string::npos) {
|
||||
return source + '\n' + macroBlock;
|
||||
}
|
||||
scan = lineEnd + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (scan + 1 < source.size() && source[scan] == '/' && source[scan + 1] == '*') {
|
||||
const size_t commentEnd = source.find("*/", scan + 2);
|
||||
if (commentEnd == std::string::npos) {
|
||||
break;
|
||||
}
|
||||
scan = commentEnd + 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (source.compare(scan, 8, "#version") == 0) {
|
||||
const size_t lineEnd = source.find('\n', scan);
|
||||
if (lineEnd != std::string::npos) {
|
||||
std::string result;
|
||||
result.reserve(source.size() + macroBlock.size());
|
||||
@@ -650,6 +720,86 @@ bool CompileHlslToSpirv(const ShaderCompileDesc& desc,
|
||||
const std::string& entryPoint,
|
||||
std::vector<uint32_t>& spirvWords,
|
||||
std::string* errorMessage) {
|
||||
auto tryCompileWithDxc = [&]() -> bool {
|
||||
if (targetEnvironment != SpirvTargetEnvironment::Vulkan) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::wstring dxcPath;
|
||||
if (!FindDxcCompiler(dxcPath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::wstring tempSourcePath;
|
||||
std::wstring tempOutputPath;
|
||||
if (!CreateTemporaryPath(ShaderSourceExtension(type, true).c_str(), tempSourcePath) ||
|
||||
!CreateTemporaryPath(L".spv", tempOutputPath)) {
|
||||
if (errorMessage != nullptr) {
|
||||
*errorMessage = "Failed to allocate temporary shader compiler files.";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string sourceWithMacros = InjectMacrosIntoSource(sourceText, desc.macros);
|
||||
const bool wroteSource = WriteTextFile(tempSourcePath, sourceWithMacros);
|
||||
if (!wroteSource) {
|
||||
DeleteFileW(tempSourcePath.c_str());
|
||||
DeleteFileW(tempOutputPath.c_str());
|
||||
if (errorMessage != nullptr) {
|
||||
*errorMessage = "Failed to write temporary HLSL shader file.";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string profile = NarrowAscii(desc.profile);
|
||||
const std::wstring targetProfile =
|
||||
WidenAscii(profile.empty() ? "ps_6_0" : profile.c_str());
|
||||
|
||||
const std::wstring arguments =
|
||||
L"-spirv -Zpc -fvk-use-dx-layout -fspv-target-env=vulkan1.0 " +
|
||||
std::wstring(L"-T ") + targetProfile +
|
||||
L" -E " + WidenAscii(entryPoint.c_str()) +
|
||||
L" -Fo \"" + tempOutputPath + L"\" \"" + tempSourcePath + L"\"";
|
||||
|
||||
DWORD exitCode = 0;
|
||||
std::string compilerOutput;
|
||||
const bool ranProcess = RunProcessAndCapture(dxcPath, arguments, exitCode, compilerOutput);
|
||||
|
||||
std::vector<uint8_t> bytes;
|
||||
const bool loadedOutput = ranProcess && exitCode == 0 && LoadBinaryFile(tempOutputPath, bytes);
|
||||
|
||||
DeleteFileW(tempSourcePath.c_str());
|
||||
DeleteFileW(tempOutputPath.c_str());
|
||||
|
||||
if (!ranProcess) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!loadedOutput) {
|
||||
if (errorMessage != nullptr) {
|
||||
*errorMessage = compilerOutput.empty()
|
||||
? std::string("dxc.exe failed to compile HLSL to SPIR-V for Vulkan.")
|
||||
: compilerOutput;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bytes.empty() || (bytes.size() % sizeof(uint32_t)) != 0) {
|
||||
if (errorMessage != nullptr) {
|
||||
*errorMessage = "Compiled SPIR-V payload size is invalid.";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
spirvWords.resize(bytes.size() / sizeof(uint32_t));
|
||||
std::memcpy(spirvWords.data(), bytes.data(), bytes.size());
|
||||
return true;
|
||||
};
|
||||
|
||||
if (tryCompileWithDxc()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::wstring validatorPath;
|
||||
if (!FindGlslangValidator(validatorPath)) {
|
||||
if (errorMessage != nullptr) {
|
||||
|
||||
Reference in New Issue
Block a user