rendering: add keyword-aware shader variant selection
This commit is contained in:
@@ -13,7 +13,7 @@ namespace Resources {
|
||||
constexpr Core::uint32 kTextureArtifactSchemaVersion = 1;
|
||||
constexpr Core::uint32 kMaterialArtifactSchemaVersion = 3;
|
||||
constexpr Core::uint32 kMeshArtifactSchemaVersion = 2;
|
||||
constexpr Core::uint32 kShaderArtifactSchemaVersion = 2;
|
||||
constexpr Core::uint32 kShaderArtifactSchemaVersion = 3;
|
||||
constexpr Core::uint32 kUIDocumentArtifactSchemaVersion = 2;
|
||||
|
||||
struct TextureArtifactHeader {
|
||||
@@ -73,7 +73,7 @@ struct MaterialPropertyArtifact {
|
||||
};
|
||||
|
||||
struct ShaderArtifactFileHeader {
|
||||
char magic[8] = { 'X', 'C', 'S', 'H', 'D', '0', '2', '\0' };
|
||||
char magic[8] = { 'X', 'C', 'S', 'H', 'D', '0', '3', '\0' };
|
||||
Core::uint32 schemaVersion = kShaderArtifactSchemaVersion;
|
||||
};
|
||||
|
||||
@@ -105,10 +105,18 @@ struct ShaderResourceArtifact {
|
||||
Core::uint32 binding = 0;
|
||||
};
|
||||
|
||||
struct ShaderVariantArtifactHeaderV2 {
|
||||
Core::uint32 stage = 0;
|
||||
Core::uint32 language = 0;
|
||||
Core::uint32 backend = 0;
|
||||
Core::uint64 compiledBinarySize = 0;
|
||||
};
|
||||
|
||||
struct ShaderVariantArtifactHeader {
|
||||
Core::uint32 stage = 0;
|
||||
Core::uint32 language = 0;
|
||||
Core::uint32 backend = 0;
|
||||
Core::uint32 keywordCount = 0;
|
||||
Core::uint64 compiledBinarySize = 0;
|
||||
};
|
||||
|
||||
|
||||
@@ -106,6 +106,7 @@ private:
|
||||
Resources::MaterialRenderState renderState;
|
||||
const Resources::Shader* shader = nullptr;
|
||||
Containers::String passName;
|
||||
Containers::String keywordSignature;
|
||||
uint32_t renderTargetCount = 0;
|
||||
uint32_t renderTargetFormat = 0;
|
||||
uint32_t depthStencilFormat = 0;
|
||||
@@ -114,6 +115,7 @@ private:
|
||||
return renderState == other.renderState &&
|
||||
shader == other.shader &&
|
||||
passName == other.passName &&
|
||||
keywordSignature == other.keywordSignature &&
|
||||
renderTargetCount == other.renderTargetCount &&
|
||||
renderTargetFormat == other.renderTargetFormat &&
|
||||
depthStencilFormat == other.depthStencilFormat;
|
||||
@@ -125,6 +127,7 @@ private:
|
||||
size_t hash = MaterialRenderStateHash()(key.renderState);
|
||||
hash ^= reinterpret_cast<size_t>(key.shader) + 0x9e3779b9u + (hash << 6) + (hash >> 2);
|
||||
hash ^= std::hash<Containers::String>{}(key.passName) + 0x9e3779b9u + (hash << 6) + (hash >> 2);
|
||||
hash ^= std::hash<Containers::String>{}(key.keywordSignature) + 0x9e3779b9u + (hash << 6) + (hash >> 2);
|
||||
hash ^= std::hash<uint32_t>{}(key.renderTargetCount) + 0x9e3779b9u + (hash << 6) + (hash >> 2);
|
||||
hash ^= std::hash<uint32_t>{}(key.renderTargetFormat) + 0x9e3779b9u + (hash << 6) + (hash >> 2);
|
||||
hash ^= std::hash<uint32_t>{}(key.depthStencilFormat) + 0x9e3779b9u + (hash << 6) + (hash >> 2);
|
||||
|
||||
@@ -192,11 +192,13 @@ private:
|
||||
Resources::MaterialRenderState renderState;
|
||||
const Resources::Shader* shader = nullptr;
|
||||
Containers::String passName;
|
||||
Containers::String keywordSignature;
|
||||
|
||||
bool operator==(const PipelineStateKey& other) const {
|
||||
return renderState == other.renderState &&
|
||||
shader == other.shader &&
|
||||
passName == other.passName;
|
||||
passName == other.passName &&
|
||||
keywordSignature == other.keywordSignature;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -209,6 +211,7 @@ private:
|
||||
|
||||
combine(reinterpret_cast<size_t>(key.shader));
|
||||
combine(std::hash<Containers::String>{}(key.passName));
|
||||
combine(std::hash<Containers::String>{}(key.keywordSignature));
|
||||
return hash;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -88,6 +88,7 @@ struct ShaderStageVariant {
|
||||
ShaderType stage = ShaderType::Fragment;
|
||||
ShaderLanguage language = ShaderLanguage::GLSL;
|
||||
ShaderBackend backend = ShaderBackend::Generic;
|
||||
ShaderKeywordSet requiredKeywords;
|
||||
Containers::String entryPoint;
|
||||
Containers::String profile;
|
||||
Containers::String sourceCode;
|
||||
@@ -167,7 +168,8 @@ public:
|
||||
const ShaderStageVariant* FindVariant(
|
||||
const Containers::String& passName,
|
||||
ShaderType stage,
|
||||
ShaderBackend backend = ShaderBackend::Generic) const;
|
||||
ShaderBackend backend = ShaderBackend::Generic,
|
||||
const ShaderKeywordSet& enabledKeywords = ShaderKeywordSet()) const;
|
||||
|
||||
class IRHIShader* GetRHIResource() const { return m_rhiResource; }
|
||||
void SetRHIResource(class IRHIShader* resource);
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
#include <XCEngine/Core/Containers/String.h>
|
||||
#include <XCEngine/Core/Types.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Resources {
|
||||
|
||||
@@ -30,5 +33,84 @@ struct ShaderKeywordSet {
|
||||
}
|
||||
};
|
||||
|
||||
inline bool IsShaderKeywordPlaceholderToken(const Containers::String& keyword) {
|
||||
return keyword == Containers::String("_") ||
|
||||
keyword == Containers::String("__");
|
||||
}
|
||||
|
||||
inline Containers::String NormalizeShaderKeywordToken(const Containers::String& keyword) {
|
||||
const Containers::String normalized = keyword.Trim();
|
||||
if (normalized.Empty() || IsShaderKeywordPlaceholderToken(normalized)) {
|
||||
return Containers::String();
|
||||
}
|
||||
|
||||
return normalized;
|
||||
}
|
||||
|
||||
inline bool CompareShaderKeywordTokens(
|
||||
const Containers::String& left,
|
||||
const Containers::String& right) {
|
||||
return std::strcmp(left.CStr(), right.CStr()) < 0;
|
||||
}
|
||||
|
||||
inline void NormalizeShaderKeywordSetInPlace(ShaderKeywordSet& keywordSet) {
|
||||
Containers::Array<Containers::String> normalizedKeywords;
|
||||
normalizedKeywords.Reserve(keywordSet.enabledKeywords.Size());
|
||||
|
||||
for (const Containers::String& keyword : keywordSet.enabledKeywords) {
|
||||
const Containers::String normalizedKeyword = NormalizeShaderKeywordToken(keyword);
|
||||
if (normalizedKeyword.Empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool alreadyExists = false;
|
||||
for (const Containers::String& existingKeyword : normalizedKeywords) {
|
||||
if (existingKeyword == normalizedKeyword) {
|
||||
alreadyExists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!alreadyExists) {
|
||||
normalizedKeywords.PushBack(normalizedKeyword);
|
||||
}
|
||||
}
|
||||
|
||||
std::sort(
|
||||
normalizedKeywords.begin(),
|
||||
normalizedKeywords.end(),
|
||||
CompareShaderKeywordTokens);
|
||||
keywordSet.enabledKeywords = normalizedKeywords;
|
||||
}
|
||||
|
||||
inline bool ShaderKeywordSetContains(
|
||||
const ShaderKeywordSet& keywordSet,
|
||||
const Containers::String& keyword) {
|
||||
const Containers::String normalizedKeyword = NormalizeShaderKeywordToken(keyword);
|
||||
if (normalizedKeyword.Empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const Containers::String& enabledKeyword : keywordSet.enabledKeywords) {
|
||||
if (enabledKeyword == normalizedKeyword) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool IsShaderKeywordSubset(
|
||||
const ShaderKeywordSet& requiredKeywords,
|
||||
const ShaderKeywordSet& enabledKeywords) {
|
||||
for (const Containers::String& keyword : requiredKeywords.enabledKeywords) {
|
||||
if (!ShaderKeywordSetContains(enabledKeywords, keyword)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Resources
|
||||
} // namespace XCEngine
|
||||
|
||||
Reference in New Issue
Block a user