engine: sync editor rendering and ui changes
This commit is contained in:
@@ -324,7 +324,6 @@ void Material::Release() {
|
||||
m_renderQueue = static_cast<Core::int32>(MaterialRenderQueue::Geometry);
|
||||
m_renderState = MaterialRenderState();
|
||||
m_hasRenderStateOverride = false;
|
||||
m_legacyShaderPassHint.Clear();
|
||||
m_tags.Clear();
|
||||
m_keywordSet.enabledKeywords.Clear();
|
||||
m_properties.Clear();
|
||||
@@ -363,24 +362,6 @@ void Material::SetRenderStateOverrideEnabled(bool enabled) {
|
||||
MarkChanged(false);
|
||||
}
|
||||
|
||||
void Material::SetLegacyShaderPassHint(const Containers::String& shaderPass) {
|
||||
m_legacyShaderPassHint = shaderPass;
|
||||
MarkChanged(false);
|
||||
}
|
||||
|
||||
void Material::ClearLegacyShaderPassHint() {
|
||||
if (m_legacyShaderPassHint.Empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_legacyShaderPassHint.Clear();
|
||||
MarkChanged(false);
|
||||
}
|
||||
|
||||
void Material::SetShaderPass(const Containers::String& shaderPass) {
|
||||
SetLegacyShaderPassHint(shaderPass);
|
||||
}
|
||||
|
||||
void Material::SetTag(const Containers::String& name, const Containers::String& value) {
|
||||
for (MaterialTagEntry& tag : m_tags) {
|
||||
if (tag.name == name) {
|
||||
@@ -1120,7 +1101,6 @@ void Material::UpdateMemorySize() {
|
||||
m_memorySize = m_constantBufferData.Size() +
|
||||
m_constantLayout.Size() * sizeof(MaterialConstantFieldDesc) +
|
||||
sizeof(MaterialRenderState) +
|
||||
m_legacyShaderPassHint.Length() +
|
||||
m_tags.Size() * sizeof(MaterialTagEntry) +
|
||||
m_keywordSet.enabledKeywords.Size() * sizeof(Containers::String) +
|
||||
m_textureBindings.Size() * sizeof(MaterialTextureBinding) +
|
||||
|
||||
@@ -42,10 +42,25 @@ bool TryParseAuthoringBoolDirectiveToken(const std::string& token, bool& outValu
|
||||
bool TryParseAuthoringCullMode(const std::string& token, MaterialCullMode& outMode);
|
||||
bool TryParseAuthoringComparisonFunc(const std::string& token, MaterialComparisonFunc& outFunc);
|
||||
bool TryParseAuthoringBlendFactor(const std::string& token, MaterialBlendFactor& outFactor);
|
||||
bool TryParseAuthoringBlendOp(const std::string& token, MaterialBlendOp& outOp);
|
||||
bool TryParseAuthoringStencilOp(const std::string& token, MaterialStencilOp& outOp);
|
||||
bool TryParseAuthoringColorMask(const std::string& token, Core::uint8& outMask);
|
||||
bool TryParseAuthoringBlendDirective(
|
||||
const std::vector<std::string>& tokens,
|
||||
MaterialRenderState& outState);
|
||||
bool TryParseAuthoringBlendOpDirective(
|
||||
const std::vector<std::string>& tokens,
|
||||
MaterialRenderState& outState);
|
||||
bool TryParseAuthoringOffsetDirective(
|
||||
const std::vector<std::string>& tokens,
|
||||
MaterialRenderState& outState);
|
||||
bool TryParseAuthoringStencilDirective(
|
||||
const std::vector<std::string>& tokens,
|
||||
MaterialStencilState& outState);
|
||||
bool TryParseAuthoringUsePassReference(
|
||||
const Containers::String& reference,
|
||||
Containers::String& outShaderName,
|
||||
Containers::String& outPassName);
|
||||
|
||||
void SetOrReplaceAuthoringTag(
|
||||
std::vector<ShaderTagIR>& tags,
|
||||
|
||||
@@ -1,518 +0,0 @@
|
||||
#include "ShaderManifestLoader.h"
|
||||
|
||||
#include "ShaderFileUtils.h"
|
||||
#include "ShaderRuntimeBuildUtils.h"
|
||||
#include "../ShaderSourceUtils.h"
|
||||
|
||||
#include <cctype>
|
||||
#include <filesystem>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Resources {
|
||||
|
||||
namespace {
|
||||
|
||||
bool FindValueStart(const std::string& json, const char* key, size_t& valuePos) {
|
||||
const std::string token = std::string("\"") + key + "\"";
|
||||
const size_t keyPos = json.find(token);
|
||||
if (keyPos == std::string::npos) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const size_t colonPos = json.find(':', keyPos + token.length());
|
||||
if (colonPos == std::string::npos) {
|
||||
return false;
|
||||
}
|
||||
|
||||
valuePos = SkipWhitespace(json, colonPos + 1);
|
||||
return valuePos < json.size();
|
||||
}
|
||||
|
||||
bool TryParseStringValue(const std::string& json, const char* key, Containers::String& outValue) {
|
||||
size_t valuePos = 0;
|
||||
if (!FindValueStart(json, key, valuePos)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ParseQuotedString(json, valuePos, outValue);
|
||||
}
|
||||
|
||||
bool TryExtractDelimitedValue(
|
||||
const std::string& json,
|
||||
const char* key,
|
||||
char openChar,
|
||||
char closeChar,
|
||||
std::string& outValue) {
|
||||
size_t valuePos = 0;
|
||||
if (!FindValueStart(json, key, valuePos) || valuePos >= json.size() || json[valuePos] != openChar) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool inString = false;
|
||||
bool escaped = false;
|
||||
int depth = 0;
|
||||
|
||||
for (size_t pos = valuePos; pos < json.size(); ++pos) {
|
||||
const char ch = json[pos];
|
||||
if (escaped) {
|
||||
escaped = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch == '\\') {
|
||||
escaped = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch == '"') {
|
||||
inString = !inString;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (inString) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch == openChar) {
|
||||
++depth;
|
||||
} else if (ch == closeChar) {
|
||||
--depth;
|
||||
if (depth == 0) {
|
||||
outValue = json.substr(valuePos, pos - valuePos + 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TryExtractObject(const std::string& json, const char* key, std::string& outObject) {
|
||||
return TryExtractDelimitedValue(json, key, '{', '}', outObject);
|
||||
}
|
||||
|
||||
bool TryExtractArray(const std::string& json, const char* key, std::string& outArray) {
|
||||
return TryExtractDelimitedValue(json, key, '[', ']', outArray);
|
||||
}
|
||||
|
||||
bool TryParseStringMapObject(
|
||||
const std::string& objectText,
|
||||
const std::function<void(const Containers::String&, const Containers::String&)>& onEntry) {
|
||||
if (!onEntry || objectText.empty() || objectText.front() != '{' || objectText.back() != '}') {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t pos = 1;
|
||||
while (pos < objectText.size()) {
|
||||
pos = SkipWhitespace(objectText, pos);
|
||||
if (pos >= objectText.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (objectText[pos] == '}') {
|
||||
return true;
|
||||
}
|
||||
|
||||
Containers::String key;
|
||||
if (!ParseQuotedString(objectText, pos, key, &pos)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
pos = SkipWhitespace(objectText, pos);
|
||||
if (pos >= objectText.size() || objectText[pos] != ':') {
|
||||
return false;
|
||||
}
|
||||
|
||||
pos = SkipWhitespace(objectText, pos + 1);
|
||||
Containers::String value;
|
||||
if (!ParseQuotedString(objectText, pos, value, &pos)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
onEntry(key, value);
|
||||
|
||||
pos = SkipWhitespace(objectText, pos);
|
||||
if (pos >= objectText.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (objectText[pos] == ',') {
|
||||
++pos;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (objectText[pos] == '}') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SplitTopLevelArrayElements(const std::string& arrayText, std::vector<std::string>& outElements) {
|
||||
outElements.clear();
|
||||
if (arrayText.size() < 2 || arrayText.front() != '[' || arrayText.back() != ']') {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool inString = false;
|
||||
bool escaped = false;
|
||||
int objectDepth = 0;
|
||||
int arrayDepth = 0;
|
||||
size_t elementStart = std::string::npos;
|
||||
|
||||
for (size_t pos = 1; pos + 1 < arrayText.size(); ++pos) {
|
||||
const char ch = arrayText[pos];
|
||||
if (escaped) {
|
||||
escaped = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch == '\\') {
|
||||
escaped = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch == '"') {
|
||||
if (elementStart == std::string::npos) {
|
||||
elementStart = pos;
|
||||
}
|
||||
inString = !inString;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (inString) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (std::isspace(static_cast<unsigned char>(ch)) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (elementStart == std::string::npos) {
|
||||
elementStart = pos;
|
||||
}
|
||||
|
||||
if (ch == '{') {
|
||||
++objectDepth;
|
||||
} else if (ch == '}') {
|
||||
--objectDepth;
|
||||
} else if (ch == '[') {
|
||||
++arrayDepth;
|
||||
} else if (ch == ']') {
|
||||
--arrayDepth;
|
||||
} else if (ch == ',' && objectDepth == 0 && arrayDepth == 0) {
|
||||
outElements.push_back(TrimCopy(arrayText.substr(elementStart, pos - elementStart)));
|
||||
elementStart = std::string::npos;
|
||||
}
|
||||
}
|
||||
|
||||
if (elementStart != std::string::npos) {
|
||||
outElements.push_back(TrimCopy(arrayText.substr(elementStart, arrayText.size() - 1 - elementStart)));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TryParseShaderKeywordsArray(
|
||||
const std::string& arrayText,
|
||||
ShaderKeywordSet& outKeywordSet) {
|
||||
outKeywordSet = {};
|
||||
|
||||
std::vector<std::string> keywordElements;
|
||||
if (!SplitTopLevelArrayElements(arrayText, keywordElements)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const std::string& keywordElement : keywordElements) {
|
||||
if (keywordElement.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Containers::String keyword;
|
||||
size_t nextPos = 0;
|
||||
if (!ParseQuotedString(keywordElement, 0, keyword, &nextPos)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (SkipWhitespace(keywordElement, nextPos) != keywordElement.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
outKeywordSet.enabledKeywords.PushBack(keyword);
|
||||
}
|
||||
|
||||
NormalizeShaderKeywordSetInPlace(outKeywordSet);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TryParseUnsignedValue(const std::string& json, const char* key, Core::uint32& outValue) {
|
||||
size_t valuePos = 0;
|
||||
if (!FindValueStart(json, key, valuePos)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t endPos = valuePos;
|
||||
while (endPos < json.size() && std::isdigit(static_cast<unsigned char>(json[endPos])) != 0) {
|
||||
++endPos;
|
||||
}
|
||||
|
||||
if (endPos == valuePos) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
outValue = static_cast<Core::uint32>(std::stoul(json.substr(valuePos, endPos - valuePos)));
|
||||
return true;
|
||||
} catch (...) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool LooksLikeShaderManifest(const std::string& sourceText) {
|
||||
const size_t firstContentPos = SkipWhitespace(sourceText, 0);
|
||||
return firstContentPos < sourceText.size() &&
|
||||
sourceText[firstContentPos] == '{' &&
|
||||
sourceText.find("\"passes\"") != std::string::npos;
|
||||
}
|
||||
|
||||
bool CollectShaderManifestDependencyPaths(
|
||||
const Containers::String& path,
|
||||
const std::string& jsonText,
|
||||
Containers::Array<Containers::String>& outDependencies) {
|
||||
outDependencies.Clear();
|
||||
|
||||
std::string passesArray;
|
||||
if (!TryExtractArray(jsonText, "passes", passesArray)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<std::string> passObjects;
|
||||
if (!SplitTopLevelArrayElements(passesArray, passObjects)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unordered_set<std::string> seenPaths;
|
||||
for (const std::string& passObject : passObjects) {
|
||||
std::string variantsArray;
|
||||
if (!TryExtractArray(passObject, "variants", variantsArray)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<std::string> variantObjects;
|
||||
if (!SplitTopLevelArrayElements(variantsArray, variantObjects)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const std::string& variantObject : variantObjects) {
|
||||
Containers::String sourcePath;
|
||||
if (!TryParseStringValue(variantObject, "source", sourcePath) &&
|
||||
!TryParseStringValue(variantObject, "sourcePath", sourcePath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const Containers::String resolvedPath = ResolveShaderDependencyPath(sourcePath, path);
|
||||
const std::string key = ToStdString(resolvedPath);
|
||||
if (!key.empty() && seenPaths.insert(key).second) {
|
||||
outDependencies.PushBack(resolvedPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
LoadResult LoadShaderManifest(
|
||||
const Containers::String& path,
|
||||
const std::string& jsonText) {
|
||||
std::string passesArray;
|
||||
if (!TryExtractArray(jsonText, "passes", passesArray)) {
|
||||
return LoadResult("Shader manifest is missing a valid passes array: " + path);
|
||||
}
|
||||
|
||||
std::vector<std::string> passObjects;
|
||||
if (!SplitTopLevelArrayElements(passesArray, passObjects) || passObjects.empty()) {
|
||||
return LoadResult("Shader manifest does not contain any pass objects: " + path);
|
||||
}
|
||||
|
||||
auto shader = std::make_unique<Shader>();
|
||||
IResource::ConstructParams params;
|
||||
params.path = path;
|
||||
params.guid = ResourceGUID::Generate(path);
|
||||
|
||||
Containers::String manifestName;
|
||||
if (TryParseStringValue(jsonText, "name", manifestName) && !manifestName.Empty()) {
|
||||
params.name = manifestName;
|
||||
} else {
|
||||
const std::filesystem::path shaderPath(path.CStr());
|
||||
const std::string stem = shaderPath.stem().generic_string();
|
||||
params.name = stem.empty() ? path : Containers::String(stem.c_str());
|
||||
}
|
||||
|
||||
shader->Initialize(params);
|
||||
|
||||
Containers::String fallback;
|
||||
if (TryParseStringValue(jsonText, "fallback", fallback)) {
|
||||
shader->SetFallback(fallback);
|
||||
}
|
||||
|
||||
std::string propertiesArray;
|
||||
if (TryExtractArray(jsonText, "properties", propertiesArray)) {
|
||||
std::vector<std::string> propertyObjects;
|
||||
if (!SplitTopLevelArrayElements(propertiesArray, propertyObjects)) {
|
||||
return LoadResult("Shader manifest properties array could not be parsed: " + path);
|
||||
}
|
||||
|
||||
for (const std::string& propertyObject : propertyObjects) {
|
||||
ShaderPropertyDesc property = {};
|
||||
if (!TryParseStringValue(propertyObject, "name", property.name) || property.name.Empty()) {
|
||||
return LoadResult("Shader manifest property is missing a valid name: " + path);
|
||||
}
|
||||
|
||||
Containers::String propertyTypeName;
|
||||
if (!TryParseStringValue(propertyObject, "type", propertyTypeName) ||
|
||||
!TryParseShaderPropertyType(propertyTypeName, property.type)) {
|
||||
return LoadResult("Shader manifest property has an invalid type: " + path);
|
||||
}
|
||||
|
||||
if (!TryParseStringValue(propertyObject, "displayName", property.displayName)) {
|
||||
property.displayName = property.name;
|
||||
}
|
||||
|
||||
if (!TryParseStringValue(propertyObject, "defaultValue", property.defaultValue)) {
|
||||
TryParseStringValue(propertyObject, "default", property.defaultValue);
|
||||
}
|
||||
|
||||
TryParseStringValue(propertyObject, "semantic", property.semantic);
|
||||
shader->AddProperty(property);
|
||||
}
|
||||
}
|
||||
|
||||
for (const std::string& passObject : passObjects) {
|
||||
Containers::String passName;
|
||||
if (!TryParseStringValue(passObject, "name", passName) || passName.Empty()) {
|
||||
return LoadResult("Shader manifest pass is missing a valid name: " + path);
|
||||
}
|
||||
|
||||
std::string tagsObject;
|
||||
if (TryExtractObject(passObject, "tags", tagsObject)) {
|
||||
if (!TryParseStringMapObject(
|
||||
tagsObject,
|
||||
[shaderPtr = shader.get(), &passName](const Containers::String& key, const Containers::String& value) {
|
||||
shaderPtr->SetPassTag(passName, key, value);
|
||||
})) {
|
||||
return LoadResult("Shader manifest pass tags could not be parsed: " + path);
|
||||
}
|
||||
}
|
||||
|
||||
std::string resourcesArray;
|
||||
if (TryExtractArray(passObject, "resources", resourcesArray)) {
|
||||
std::vector<std::string> resourceObjects;
|
||||
if (!SplitTopLevelArrayElements(resourcesArray, resourceObjects)) {
|
||||
return LoadResult("Shader manifest pass resources could not be parsed: " + path);
|
||||
}
|
||||
|
||||
for (const std::string& resourceObject : resourceObjects) {
|
||||
ShaderResourceBindingDesc resourceBinding = {};
|
||||
if (!TryParseStringValue(resourceObject, "name", resourceBinding.name) ||
|
||||
resourceBinding.name.Empty()) {
|
||||
return LoadResult("Shader manifest pass resource is missing a valid name: " + path);
|
||||
}
|
||||
|
||||
Containers::String resourceTypeName;
|
||||
if (!TryParseStringValue(resourceObject, "type", resourceTypeName) ||
|
||||
!TryParseShaderResourceType(resourceTypeName, resourceBinding.type)) {
|
||||
return LoadResult("Shader manifest pass resource has an invalid type: " + path);
|
||||
}
|
||||
|
||||
if (!TryParseUnsignedValue(resourceObject, "set", resourceBinding.set)) {
|
||||
return LoadResult("Shader manifest pass resource is missing a valid set: " + path);
|
||||
}
|
||||
if (!TryParseUnsignedValue(resourceObject, "binding", resourceBinding.binding)) {
|
||||
return LoadResult("Shader manifest pass resource is missing a valid binding: " + path);
|
||||
}
|
||||
|
||||
TryParseStringValue(resourceObject, "semantic", resourceBinding.semantic);
|
||||
shader->AddPassResourceBinding(passName, resourceBinding);
|
||||
}
|
||||
}
|
||||
|
||||
std::string variantsArray;
|
||||
if (!TryExtractArray(passObject, "variants", variantsArray)) {
|
||||
return LoadResult("Shader manifest pass is missing variants: " + path);
|
||||
}
|
||||
|
||||
std::vector<std::string> variantObjects;
|
||||
if (!SplitTopLevelArrayElements(variantsArray, variantObjects) || variantObjects.empty()) {
|
||||
return LoadResult("Shader manifest pass does not contain any variants: " + path);
|
||||
}
|
||||
|
||||
for (const std::string& variantObject : variantObjects) {
|
||||
ShaderStageVariant variant = {};
|
||||
|
||||
Containers::String stageName;
|
||||
if (!TryParseStringValue(variantObject, "stage", stageName) ||
|
||||
!TryParseShaderType(stageName, variant.stage)) {
|
||||
return LoadResult("Shader manifest variant has an invalid stage: " + path);
|
||||
}
|
||||
|
||||
Containers::String backendName;
|
||||
if (!TryParseStringValue(variantObject, "backend", backendName) ||
|
||||
!TryParseShaderBackend(backendName, variant.backend)) {
|
||||
return LoadResult("Shader manifest variant has an invalid backend: " + path);
|
||||
}
|
||||
|
||||
Containers::String languageName;
|
||||
if (!TryParseStringValue(variantObject, "language", languageName) ||
|
||||
!TryParseShaderLanguage(languageName, variant.language)) {
|
||||
return LoadResult("Shader manifest variant has an invalid language: " + path);
|
||||
}
|
||||
|
||||
Containers::String sourceCode;
|
||||
if (TryParseStringValue(variantObject, "sourceCode", sourceCode)) {
|
||||
variant.sourceCode = sourceCode;
|
||||
} else {
|
||||
Containers::String sourcePath;
|
||||
if (!TryParseStringValue(variantObject, "source", sourcePath) &&
|
||||
!TryParseStringValue(variantObject, "sourcePath", sourcePath)) {
|
||||
return LoadResult("Shader manifest variant is missing source/sourceCode: " + path);
|
||||
}
|
||||
|
||||
const Containers::String resolvedSourcePath = ResolveShaderDependencyPath(sourcePath, path);
|
||||
if (!ReadShaderTextFile(resolvedSourcePath, variant.sourceCode)) {
|
||||
return LoadResult("Failed to read shader variant source: " + resolvedSourcePath);
|
||||
}
|
||||
}
|
||||
|
||||
if (!TryParseStringValue(variantObject, "entryPoint", variant.entryPoint)) {
|
||||
variant.entryPoint = GetDefaultEntryPoint(variant.language, variant.stage);
|
||||
}
|
||||
|
||||
if (!TryParseStringValue(variantObject, "profile", variant.profile)) {
|
||||
variant.profile = GetDefaultProfile(variant.language, variant.backend, variant.stage);
|
||||
}
|
||||
|
||||
std::string keywordsArray;
|
||||
if (TryExtractArray(variantObject, "keywords", keywordsArray) &&
|
||||
!TryParseShaderKeywordsArray(keywordsArray, variant.requiredKeywords)) {
|
||||
return LoadResult("Shader manifest variant keywords could not be parsed: " + path);
|
||||
}
|
||||
|
||||
shader->AddPassVariant(passName, variant);
|
||||
}
|
||||
}
|
||||
|
||||
shader->m_memorySize = CalculateShaderMemorySize(*shader);
|
||||
return LoadResult(shader.release());
|
||||
}
|
||||
|
||||
} // namespace Resources
|
||||
} // namespace XCEngine
|
||||
@@ -1,24 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/Core/Containers/Array.h>
|
||||
#include <XCEngine/Core/Containers/String.h>
|
||||
#include <XCEngine/Core/IO/IResourceLoader.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Resources {
|
||||
|
||||
bool LooksLikeShaderManifest(const std::string& sourceText);
|
||||
|
||||
bool CollectShaderManifestDependencyPaths(
|
||||
const Containers::String& path,
|
||||
const std::string& jsonText,
|
||||
Containers::Array<Containers::String>& outDependencies);
|
||||
|
||||
LoadResult LoadShaderManifest(
|
||||
const Containers::String& path,
|
||||
const std::string& jsonText);
|
||||
|
||||
} // namespace Resources
|
||||
} // namespace XCEngine
|
||||
Reference in New Issue
Block a user