Formalize volume shader include context
This commit is contained in:
@@ -49,6 +49,7 @@ enum class ShaderLanguage : uint8_t;
|
||||
|
||||
struct ShaderCompileDesc {
|
||||
std::wstring fileName;
|
||||
std::vector<std::wstring> includeDirectories;
|
||||
std::vector<uint8_t> source;
|
||||
ShaderLanguage sourceLanguage = ShaderLanguage::Unknown;
|
||||
std::wstring entryPoint;
|
||||
@@ -323,6 +324,7 @@ struct GraphicsPipelineDesc {
|
||||
uint32_t renderTargetFormats[8] = { 0 }; // Format
|
||||
uint32_t depthStencilFormat = 0; // Format
|
||||
uint32_t sampleCount = 1;
|
||||
uint32_t sampleQuality = 0;
|
||||
};
|
||||
|
||||
struct RHIDeviceDesc {
|
||||
|
||||
@@ -603,10 +603,89 @@ std::string InjectMacrosIntoSource(const std::string& source, const std::vector<
|
||||
return macroBlock + source;
|
||||
}
|
||||
|
||||
std::filesystem::path MakeAbsoluteNormalizedPath(const std::filesystem::path& path) {
|
||||
if (path.empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::error_code ec;
|
||||
const std::filesystem::path absolutePath = std::filesystem::absolute(path, ec);
|
||||
if (ec) {
|
||||
return path.lexically_normal();
|
||||
}
|
||||
|
||||
return absolutePath.lexically_normal();
|
||||
}
|
||||
|
||||
std::string NormalizeDirectoryKey(const std::filesystem::path& path) {
|
||||
std::string key = path.generic_string();
|
||||
std::transform(key.begin(), key.end(), key.begin(), [](unsigned char ch) {
|
||||
return static_cast<char>(std::tolower(ch));
|
||||
});
|
||||
return key;
|
||||
}
|
||||
|
||||
std::vector<std::filesystem::path> CollectShaderIncludeDirectories(const ShaderCompileDesc& desc) {
|
||||
std::vector<std::filesystem::path> directories;
|
||||
directories.reserve(desc.includeDirectories.size() + (desc.fileName.empty() ? 0u : 1u));
|
||||
|
||||
auto appendDirectory = [&](const std::filesystem::path& candidate) {
|
||||
if (candidate.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const std::filesystem::path normalized = MakeAbsoluteNormalizedPath(candidate);
|
||||
if (normalized.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string key = NormalizeDirectoryKey(normalized);
|
||||
const auto existing = std::find_if(directories.begin(), directories.end(), [&](const std::filesystem::path& path) {
|
||||
return NormalizeDirectoryKey(path) == key;
|
||||
});
|
||||
if (existing == directories.end()) {
|
||||
directories.push_back(normalized);
|
||||
}
|
||||
};
|
||||
|
||||
if (!desc.fileName.empty()) {
|
||||
const std::filesystem::path sourcePath(desc.fileName);
|
||||
if (sourcePath.has_parent_path()) {
|
||||
appendDirectory(sourcePath.parent_path());
|
||||
}
|
||||
}
|
||||
|
||||
for (const std::wstring& includeDirectory : desc.includeDirectories) {
|
||||
appendDirectory(std::filesystem::path(includeDirectory));
|
||||
}
|
||||
|
||||
return directories;
|
||||
}
|
||||
|
||||
std::wstring BuildIncludeDirectoryArguments(const ShaderCompileDesc& desc) {
|
||||
std::wstring arguments;
|
||||
const std::vector<std::filesystem::path> includeDirectories = CollectShaderIncludeDirectories(desc);
|
||||
for (const std::filesystem::path& includeDirectory : includeDirectories) {
|
||||
arguments += L" -I \"" + includeDirectory.wstring() + L"\"";
|
||||
}
|
||||
|
||||
return arguments;
|
||||
}
|
||||
|
||||
std::wstring ResolveCompilerWorkingDirectory(const ShaderCompileDesc& desc) {
|
||||
const std::vector<std::filesystem::path> includeDirectories = CollectShaderIncludeDirectories(desc);
|
||||
if (!includeDirectories.empty()) {
|
||||
return includeDirectories.front().wstring();
|
||||
}
|
||||
|
||||
return std::wstring();
|
||||
}
|
||||
|
||||
bool RunProcessAndCapture(const std::wstring& executablePath,
|
||||
const std::wstring& arguments,
|
||||
DWORD& exitCode,
|
||||
std::string& output) {
|
||||
std::string& output,
|
||||
const wchar_t* workingDirectory = nullptr) {
|
||||
SECURITY_ATTRIBUTES securityAttributes = {};
|
||||
securityAttributes.nLength = sizeof(securityAttributes);
|
||||
securityAttributes.bInheritHandle = TRUE;
|
||||
@@ -638,7 +717,7 @@ bool RunProcessAndCapture(const std::wstring& executablePath,
|
||||
TRUE,
|
||||
CREATE_NO_WINDOW,
|
||||
nullptr,
|
||||
nullptr,
|
||||
workingDirectory,
|
||||
&startupInfo,
|
||||
&processInfo);
|
||||
|
||||
@@ -701,14 +780,22 @@ bool CompileGlslToSpirv(const ShaderCompileDesc& desc,
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::wstring includeArguments = BuildIncludeDirectoryArguments(desc);
|
||||
const std::wstring workingDirectory = ResolveCompilerWorkingDirectory(desc);
|
||||
const std::wstring arguments =
|
||||
SpirvTargetArguments(targetEnvironment) + L" -S " + ShaderStageArgument(type) +
|
||||
L" -e " + WidenAscii(entryPoint.c_str()) +
|
||||
includeArguments +
|
||||
L" -o \"" + tempOutputPath + L"\" \"" + tempSourcePath + L"\"";
|
||||
|
||||
DWORD exitCode = 0;
|
||||
std::string compilerOutput;
|
||||
const bool ranProcess = RunProcessAndCapture(validatorPath, arguments, exitCode, compilerOutput);
|
||||
const bool ranProcess = RunProcessAndCapture(
|
||||
validatorPath,
|
||||
arguments,
|
||||
exitCode,
|
||||
compilerOutput,
|
||||
workingDirectory.empty() ? nullptr : workingDirectory.c_str());
|
||||
|
||||
std::vector<uint8_t> bytes;
|
||||
const bool loadedOutput = ranProcess && exitCode == 0 && LoadBinaryFile(tempOutputPath, bytes);
|
||||
@@ -786,16 +873,24 @@ bool CompileHlslToSpirv(const ShaderCompileDesc& desc,
|
||||
const std::string profile = NarrowAscii(desc.profile);
|
||||
const std::wstring targetProfile =
|
||||
WidenAscii(profile.empty() ? "ps_6_0" : profile.c_str());
|
||||
const std::wstring includeArguments = BuildIncludeDirectoryArguments(desc);
|
||||
const std::wstring workingDirectory = ResolveCompilerWorkingDirectory(desc);
|
||||
|
||||
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()) +
|
||||
includeArguments +
|
||||
L" -Fo \"" + tempOutputPath + L"\" \"" + tempSourcePath + L"\"";
|
||||
|
||||
DWORD exitCode = 0;
|
||||
std::string compilerOutput;
|
||||
const bool ranProcess = RunProcessAndCapture(dxcPath, arguments, exitCode, compilerOutput);
|
||||
const bool ranProcess = RunProcessAndCapture(
|
||||
dxcPath,
|
||||
arguments,
|
||||
exitCode,
|
||||
compilerOutput,
|
||||
workingDirectory.empty() ? nullptr : workingDirectory.c_str());
|
||||
|
||||
std::vector<uint8_t> bytes;
|
||||
const bool loadedOutput = ranProcess && exitCode == 0 && LoadBinaryFile(tempOutputPath, bytes);
|
||||
@@ -861,15 +956,23 @@ bool CompileHlslToSpirv(const ShaderCompileDesc& desc,
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::wstring includeArguments = BuildIncludeDirectoryArguments(desc);
|
||||
const std::wstring workingDirectory = ResolveCompilerWorkingDirectory(desc);
|
||||
const std::wstring arguments =
|
||||
L"-D --auto-map-bindings " + SpirvTargetArguments(targetEnvironment) +
|
||||
L" -S " + ShaderStageArgument(type) +
|
||||
L" -e " + WidenAscii(entryPoint.c_str()) +
|
||||
includeArguments +
|
||||
L" -o \"" + tempOutputPath + L"\" \"" + tempSourcePath + L"\"";
|
||||
|
||||
DWORD exitCode = 0;
|
||||
std::string compilerOutput;
|
||||
const bool ranProcess = RunProcessAndCapture(validatorPath, arguments, exitCode, compilerOutput);
|
||||
const bool ranProcess = RunProcessAndCapture(
|
||||
validatorPath,
|
||||
arguments,
|
||||
exitCode,
|
||||
compilerOutput,
|
||||
workingDirectory.empty() ? nullptr : workingDirectory.c_str());
|
||||
|
||||
std::vector<uint8_t> bytes;
|
||||
const bool loadedOutput = ranProcess && exitCode == 0 && LoadBinaryFile(tempOutputPath, bytes);
|
||||
|
||||
448
engine/src/Rendering/Internal/ShaderVariantUtils.h
Normal file
448
engine/src/Rendering/Internal/ShaderVariantUtils.h
Normal file
@@ -0,0 +1,448 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/Core/Containers/String.h>
|
||||
#include <XCEngine/RHI/RHIEnums.h>
|
||||
#include <XCEngine/RHI/RHIPipelineState.h>
|
||||
#include <XCEngine/Rendering/Builtin/BuiltinPassLayoutUtils.h>
|
||||
#include <XCEngine/Resources/BuiltinResources.h>
|
||||
#include <XCEngine/Resources/Shader/Shader.h>
|
||||
|
||||
#include <filesystem>
|
||||
#include <regex>
|
||||
#include <string>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Rendering {
|
||||
namespace Internal {
|
||||
|
||||
inline Resources::ShaderBackend ToShaderBackend(RHI::RHIType backendType) {
|
||||
switch (backendType) {
|
||||
case RHI::RHIType::D3D12:
|
||||
return Resources::ShaderBackend::D3D12;
|
||||
case RHI::RHIType::Vulkan:
|
||||
return Resources::ShaderBackend::Vulkan;
|
||||
case RHI::RHIType::OpenGL:
|
||||
default:
|
||||
return Resources::ShaderBackend::OpenGL;
|
||||
}
|
||||
}
|
||||
|
||||
inline RHI::ShaderLanguage ToRHIShaderLanguage(Resources::ShaderLanguage language) {
|
||||
switch (language) {
|
||||
case Resources::ShaderLanguage::HLSL:
|
||||
return RHI::ShaderLanguage::HLSL;
|
||||
case Resources::ShaderLanguage::SPIRV:
|
||||
return RHI::ShaderLanguage::SPIRV;
|
||||
case Resources::ShaderLanguage::GLSL:
|
||||
default:
|
||||
return RHI::ShaderLanguage::GLSL;
|
||||
}
|
||||
}
|
||||
|
||||
inline std::wstring ToWideAscii(const Containers::String& value) {
|
||||
std::wstring wide;
|
||||
wide.reserve(value.Length());
|
||||
for (size_t index = 0; index < value.Length(); ++index) {
|
||||
wide.push_back(static_cast<wchar_t>(value[index]));
|
||||
}
|
||||
return wide;
|
||||
}
|
||||
|
||||
inline std::string ToStdString(const Containers::String& value) {
|
||||
return std::string(value.CStr(), value.Length());
|
||||
}
|
||||
|
||||
inline std::string EscapeRegexLiteral(const Containers::String& value) {
|
||||
std::string escaped;
|
||||
escaped.reserve(value.Length() * 2u);
|
||||
for (size_t index = 0; index < value.Length(); ++index) {
|
||||
const char ch = value[index];
|
||||
switch (ch) {
|
||||
case '\\':
|
||||
case '^':
|
||||
case '$':
|
||||
case '.':
|
||||
case '|':
|
||||
case '?':
|
||||
case '*':
|
||||
case '+':
|
||||
case '(':
|
||||
case ')':
|
||||
case '[':
|
||||
case ']':
|
||||
case '{':
|
||||
case '}':
|
||||
escaped.push_back('\\');
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
escaped.push_back(ch);
|
||||
}
|
||||
|
||||
return escaped;
|
||||
}
|
||||
|
||||
inline bool TryCollectShaderPassResourceBindings(
|
||||
const Resources::ShaderPass& pass,
|
||||
Containers::Array<Resources::ShaderResourceBindingDesc>& outBindings) {
|
||||
outBindings.Clear();
|
||||
|
||||
if (pass.resources.Empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
outBindings.Reserve(pass.resources.Size());
|
||||
for (const Resources::ShaderResourceBindingDesc& binding : pass.resources) {
|
||||
outBindings.PushBack(binding);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool TryRewriteHlslRegisterBindingWithName(
|
||||
std::string& sourceText,
|
||||
const Containers::String& declarationName,
|
||||
const char* registerPrefix,
|
||||
Core::uint32 bindingIndex,
|
||||
Core::uint32 setIndex,
|
||||
bool includeRegisterSpace,
|
||||
Resources::ShaderResourceType resourceType) {
|
||||
if (declarationName.Empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string registerClause =
|
||||
includeRegisterSpace
|
||||
? std::string("register(") + registerPrefix +
|
||||
std::to_string(bindingIndex) +
|
||||
", space" +
|
||||
std::to_string(setIndex) +
|
||||
")"
|
||||
: std::string("register(") + registerPrefix +
|
||||
std::to_string(bindingIndex) +
|
||||
")";
|
||||
const std::string escapedName = EscapeRegexLiteral(declarationName);
|
||||
|
||||
if (resourceType == Resources::ShaderResourceType::ConstantBuffer) {
|
||||
const std::regex pattern(
|
||||
"(cbuffer\\s+" + escapedName + "\\s*)(:\\s*register\\s*\\([^\\)]*\\))?(\\s*\\{)",
|
||||
std::regex::ECMAScript);
|
||||
const std::string rewritten =
|
||||
std::regex_replace(sourceText, pattern, "$1: " + registerClause + "$3");
|
||||
if (rewritten != sourceText) {
|
||||
sourceText = rewritten;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (resourceType == Resources::ShaderResourceType::StructuredBuffer ||
|
||||
resourceType == Resources::ShaderResourceType::RWStructuredBuffer) {
|
||||
const std::regex pattern(
|
||||
"((?:globallycoherent\\s+)?(?:StructuredBuffer|RWStructuredBuffer)\\s*<[^;\\r\\n>]+>\\s+" +
|
||||
escapedName + "\\s*)(:\\s*register\\s*\\([^\\)]*\\))?(\\s*;)",
|
||||
std::regex::ECMAScript);
|
||||
const std::string rewritten =
|
||||
std::regex_replace(sourceText, pattern, "$1: " + registerClause + "$3");
|
||||
if (rewritten != sourceText) {
|
||||
sourceText = rewritten;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (resourceType == Resources::ShaderResourceType::RawBuffer ||
|
||||
resourceType == Resources::ShaderResourceType::RWRawBuffer) {
|
||||
const std::regex pattern(
|
||||
"((?:globallycoherent\\s+)?(?:ByteAddressBuffer|RWByteAddressBuffer)\\s+" + escapedName +
|
||||
"\\s*)(:\\s*register\\s*\\([^\\)]*\\))?(\\s*;)",
|
||||
std::regex::ECMAScript);
|
||||
const std::string rewritten =
|
||||
std::regex_replace(sourceText, pattern, "$1: " + registerClause + "$3");
|
||||
if (rewritten != sourceText) {
|
||||
sourceText = rewritten;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::regex pattern(
|
||||
"((?:Texture2D|TextureCube|SamplerState|SamplerComparisonState)\\s+" + escapedName +
|
||||
"\\s*)(:\\s*register\\s*\\([^\\)]*\\))?(\\s*;)",
|
||||
std::regex::ECMAScript);
|
||||
const std::string rewritten =
|
||||
std::regex_replace(sourceText, pattern, "$1: " + registerClause + "$3");
|
||||
if (rewritten != sourceText) {
|
||||
sourceText = rewritten;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
inline const char* TryGetHlslRegisterPrefix(Resources::ShaderResourceType type) {
|
||||
switch (type) {
|
||||
case Resources::ShaderResourceType::ConstantBuffer:
|
||||
return "b";
|
||||
case Resources::ShaderResourceType::Texture2D:
|
||||
case Resources::ShaderResourceType::TextureCube:
|
||||
case Resources::ShaderResourceType::StructuredBuffer:
|
||||
case Resources::ShaderResourceType::RawBuffer:
|
||||
return "t";
|
||||
case Resources::ShaderResourceType::Sampler:
|
||||
return "s";
|
||||
case Resources::ShaderResourceType::RWStructuredBuffer:
|
||||
case Resources::ShaderResourceType::RWRawBuffer:
|
||||
return "u";
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool TryRewriteHlslRegisterBinding(
|
||||
std::string& sourceText,
|
||||
const Resources::ShaderResourceBindingDesc& binding,
|
||||
bool includeRegisterSpace) {
|
||||
const char* registerPrefix = TryGetHlslRegisterPrefix(binding.type);
|
||||
if (registerPrefix == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (TryRewriteHlslRegisterBindingWithName(
|
||||
sourceText,
|
||||
binding.name,
|
||||
registerPrefix,
|
||||
binding.binding,
|
||||
binding.set,
|
||||
includeRegisterSpace,
|
||||
binding.type)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool TryBuildRuntimeShaderBindings(
|
||||
const Resources::ShaderPass& pass,
|
||||
Resources::ShaderBackend backend,
|
||||
Containers::Array<Resources::ShaderResourceBindingDesc>& outBindings,
|
||||
bool& outIncludeRegisterSpace) {
|
||||
outBindings.Clear();
|
||||
outIncludeRegisterSpace = false;
|
||||
|
||||
if (!TryCollectShaderPassResourceBindings(pass, outBindings)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (backend == Resources::ShaderBackend::Vulkan) {
|
||||
outIncludeRegisterSpace = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (backend != Resources::ShaderBackend::D3D12 &&
|
||||
backend != Resources::ShaderBackend::OpenGL) {
|
||||
outBindings.Clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
Core::uint32 nextConstantBufferRegister = 0;
|
||||
Core::uint32 nextTextureRegister = 0;
|
||||
Core::uint32 nextSamplerRegister = 0;
|
||||
Core::uint32 nextUnorderedAccessRegister = 0;
|
||||
for (Resources::ShaderResourceBindingDesc& binding : outBindings) {
|
||||
binding.set = 0;
|
||||
switch (binding.type) {
|
||||
case Resources::ShaderResourceType::ConstantBuffer:
|
||||
binding.binding = nextConstantBufferRegister++;
|
||||
break;
|
||||
case Resources::ShaderResourceType::Texture2D:
|
||||
case Resources::ShaderResourceType::TextureCube:
|
||||
case Resources::ShaderResourceType::StructuredBuffer:
|
||||
case Resources::ShaderResourceType::RawBuffer:
|
||||
binding.binding = nextTextureRegister++;
|
||||
break;
|
||||
case Resources::ShaderResourceType::Sampler:
|
||||
binding.binding = nextSamplerRegister++;
|
||||
break;
|
||||
case Resources::ShaderResourceType::RWStructuredBuffer:
|
||||
case Resources::ShaderResourceType::RWRawBuffer:
|
||||
default:
|
||||
binding.binding = nextUnorderedAccessRegister++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline std::string BuildRuntimeShaderSource(
|
||||
const Resources::ShaderPass& pass,
|
||||
Resources::ShaderBackend backend,
|
||||
const Resources::ShaderStageVariant& variant) {
|
||||
std::string sourceText = ToStdString(variant.sourceCode);
|
||||
|
||||
if (variant.language != Resources::ShaderLanguage::HLSL ||
|
||||
backend == Resources::ShaderBackend::Generic) {
|
||||
return sourceText;
|
||||
}
|
||||
|
||||
Containers::Array<Resources::ShaderResourceBindingDesc> bindings;
|
||||
bool includeRegisterSpace = false;
|
||||
if (!TryBuildRuntimeShaderBindings(pass, backend, bindings, includeRegisterSpace)) {
|
||||
return sourceText;
|
||||
}
|
||||
|
||||
for (const Resources::ShaderResourceBindingDesc& binding : bindings) {
|
||||
TryRewriteHlslRegisterBinding(sourceText, binding, includeRegisterSpace);
|
||||
}
|
||||
|
||||
return sourceText;
|
||||
}
|
||||
|
||||
inline void AddShaderCompileMacro(
|
||||
RHI::ShaderCompileDesc& compileDesc,
|
||||
const wchar_t* name,
|
||||
const wchar_t* definition = L"1") {
|
||||
if (name == nullptr || *name == L'\0') {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const RHI::ShaderCompileMacro& existingMacro : compileDesc.macros) {
|
||||
if (existingMacro.name == name) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
RHI::ShaderCompileMacro macro = {};
|
||||
macro.name = name;
|
||||
macro.definition = definition != nullptr ? definition : L"";
|
||||
compileDesc.macros.push_back(std::move(macro));
|
||||
}
|
||||
|
||||
inline void InjectShaderBackendMacros(
|
||||
Resources::ShaderBackend backend,
|
||||
RHI::ShaderCompileDesc& compileDesc) {
|
||||
switch (backend) {
|
||||
case Resources::ShaderBackend::OpenGL:
|
||||
AddShaderCompileMacro(compileDesc, L"SHADER_API_GLCORE");
|
||||
AddShaderCompileMacro(compileDesc, L"UNITY_UV_STARTS_AT_TOP", L"0");
|
||||
AddShaderCompileMacro(compileDesc, L"UNITY_NEAR_CLIP_VALUE", L"-1");
|
||||
break;
|
||||
case Resources::ShaderBackend::Vulkan:
|
||||
AddShaderCompileMacro(compileDesc, L"SHADER_API_VULKAN");
|
||||
AddShaderCompileMacro(compileDesc, L"UNITY_UV_STARTS_AT_TOP", L"1");
|
||||
AddShaderCompileMacro(compileDesc, L"UNITY_NEAR_CLIP_VALUE", L"0");
|
||||
break;
|
||||
case Resources::ShaderBackend::D3D12:
|
||||
AddShaderCompileMacro(compileDesc, L"SHADER_API_D3D12");
|
||||
AddShaderCompileMacro(compileDesc, L"UNITY_UV_STARTS_AT_TOP", L"1");
|
||||
AddShaderCompileMacro(compileDesc, L"UNITY_NEAR_CLIP_VALUE", L"0");
|
||||
break;
|
||||
case Resources::ShaderBackend::Generic:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
inline void ApplyShaderStageVariant(
|
||||
const Resources::ShaderStageVariant& variant,
|
||||
RHI::ShaderCompileDesc& compileDesc) {
|
||||
compileDesc.fileName.clear();
|
||||
compileDesc.includeDirectories.clear();
|
||||
compileDesc.source.assign(
|
||||
variant.sourceCode.CStr(),
|
||||
variant.sourceCode.CStr() + variant.sourceCode.Length());
|
||||
compileDesc.sourceLanguage = ToRHIShaderLanguage(variant.language);
|
||||
compileDesc.entryPoint = ToWideAscii(variant.entryPoint);
|
||||
compileDesc.profile = ToWideAscii(variant.profile);
|
||||
}
|
||||
|
||||
inline std::wstring ResolveRuntimeShaderSourcePath(const Containers::String& shaderPath) {
|
||||
Containers::String resolvedPath = shaderPath;
|
||||
if (resolvedPath.Empty()) {
|
||||
return std::wstring();
|
||||
}
|
||||
|
||||
if (Resources::IsBuiltinShaderPath(resolvedPath)) {
|
||||
Containers::String builtinAssetPath;
|
||||
if (!Resources::TryResolveBuiltinShaderAssetPath(resolvedPath, builtinAssetPath)) {
|
||||
return std::wstring();
|
||||
}
|
||||
resolvedPath = builtinAssetPath;
|
||||
}
|
||||
|
||||
return ToWideAscii(resolvedPath);
|
||||
}
|
||||
|
||||
inline void ApplyShaderStageVariant(
|
||||
const Containers::String& shaderPath,
|
||||
const Resources::ShaderPass& pass,
|
||||
Resources::ShaderBackend backend,
|
||||
const Resources::ShaderStageVariant& variant,
|
||||
RHI::ShaderCompileDesc& compileDesc) {
|
||||
const std::string sourceText = BuildRuntimeShaderSource(pass, backend, variant);
|
||||
compileDesc.source.assign(sourceText.begin(), sourceText.end());
|
||||
compileDesc.fileName = ResolveRuntimeShaderSourcePath(shaderPath);
|
||||
compileDesc.includeDirectories.clear();
|
||||
if (!compileDesc.fileName.empty()) {
|
||||
const std::filesystem::path shaderFilePath(compileDesc.fileName);
|
||||
if (shaderFilePath.has_parent_path()) {
|
||||
compileDesc.includeDirectories.push_back(shaderFilePath.parent_path().wstring());
|
||||
}
|
||||
}
|
||||
compileDesc.sourceLanguage = ToRHIShaderLanguage(variant.language);
|
||||
compileDesc.entryPoint = ToWideAscii(variant.entryPoint);
|
||||
compileDesc.profile = ToWideAscii(variant.profile);
|
||||
InjectShaderBackendMacros(backend, compileDesc);
|
||||
}
|
||||
|
||||
inline void ApplyShaderStageVariant(
|
||||
const Resources::ShaderPass& pass,
|
||||
Resources::ShaderBackend backend,
|
||||
const Resources::ShaderStageVariant& variant,
|
||||
RHI::ShaderCompileDesc& compileDesc) {
|
||||
ApplyShaderStageVariant(Containers::String(), pass, backend, variant, compileDesc);
|
||||
}
|
||||
|
||||
inline Containers::String BuildShaderKeywordSignature(
|
||||
const Resources::ShaderKeywordSet& keywordSet) {
|
||||
Resources::ShaderKeywordSet normalizedKeywords = keywordSet;
|
||||
Resources::NormalizeShaderKeywordSetInPlace(normalizedKeywords);
|
||||
|
||||
Containers::String signature;
|
||||
for (size_t keywordIndex = 0; keywordIndex < normalizedKeywords.enabledKeywords.Size(); ++keywordIndex) {
|
||||
if (keywordIndex > 0) {
|
||||
signature += ";";
|
||||
}
|
||||
|
||||
signature += normalizedKeywords.enabledKeywords[keywordIndex];
|
||||
}
|
||||
|
||||
return signature;
|
||||
}
|
||||
|
||||
inline bool ShaderPassHasGraphicsVariants(
|
||||
const Resources::Shader& shader,
|
||||
const Containers::String& passName,
|
||||
Resources::ShaderBackend backend,
|
||||
const Resources::ShaderKeywordSet& enabledKeywords = Resources::ShaderKeywordSet()) {
|
||||
return shader.FindVariant(
|
||||
passName,
|
||||
Resources::ShaderType::Vertex,
|
||||
backend,
|
||||
enabledKeywords) != nullptr &&
|
||||
shader.FindVariant(
|
||||
passName,
|
||||
Resources::ShaderType::Fragment,
|
||||
backend,
|
||||
enabledKeywords) != nullptr;
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Rendering
|
||||
} // namespace XCEngine
|
||||
Reference in New Issue
Block a user