Fixes
This commit is contained in:
49
src/runtime/shader/RuntimeShaderArtifact.h
Normal file
49
src/runtime/shader/RuntimeShaderArtifact.h
Normal file
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
|
||||
#include "ShaderTypes.h"
|
||||
#include "FontAtlasBuilder.h"
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
struct RuntimeShaderPassArtifact
|
||||
{
|
||||
std::string passId;
|
||||
std::string fragmentShaderSource;
|
||||
std::vector<std::string> inputNames;
|
||||
std::string outputName;
|
||||
};
|
||||
|
||||
struct RuntimeTextTextureMetrics
|
||||
{
|
||||
float activeWidthScale = 1.0f;
|
||||
float aspect = 1.0f;
|
||||
};
|
||||
|
||||
struct RuntimePreparedTextTexture
|
||||
{
|
||||
std::string parameterId;
|
||||
std::string textValue;
|
||||
unsigned width = 0;
|
||||
unsigned height = 0;
|
||||
unsigned liveWidth = 1;
|
||||
std::shared_ptr<const std::vector<unsigned char>> rgbaPixels;
|
||||
};
|
||||
|
||||
struct RuntimeShaderArtifact
|
||||
{
|
||||
std::string layerId;
|
||||
std::string shaderId;
|
||||
std::string packageFingerprint;
|
||||
std::string displayName;
|
||||
std::string fragmentShaderSource;
|
||||
std::vector<RuntimeShaderPassArtifact> passes;
|
||||
std::string message;
|
||||
std::vector<ShaderParameterDefinition> parameterDefinitions;
|
||||
std::map<std::string, ShaderParameterValue> parameterValues;
|
||||
std::map<std::string, RuntimeTextTextureMetrics> textTextureMetrics;
|
||||
std::vector<RuntimePreparedTextTexture> preparedTextTextures;
|
||||
std::vector<RenderCadenceCompositor::FontAtlasBuildOutput> fontAtlases;
|
||||
};
|
||||
75
src/runtime/shader/RuntimeShaderBridge.cpp
Normal file
75
src/runtime/shader/RuntimeShaderBridge.cpp
Normal file
@@ -0,0 +1,75 @@
|
||||
#include "RuntimeShaderBridge.h"
|
||||
|
||||
#include <chrono>
|
||||
|
||||
RuntimeShaderBridge::~RuntimeShaderBridge()
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
|
||||
void RuntimeShaderBridge::Start(const std::string& shaderId, ArtifactCallback onArtifactReady, ErrorCallback onError)
|
||||
{
|
||||
Start(std::string(), shaderId, std::move(onArtifactReady), std::move(onError));
|
||||
}
|
||||
|
||||
void RuntimeShaderBridge::Start(const std::string& layerId, const std::string& shaderId, ArtifactCallback onArtifactReady, ErrorCallback onError)
|
||||
{
|
||||
Stop();
|
||||
if (shaderId.empty())
|
||||
return;
|
||||
|
||||
mLayerId = layerId;
|
||||
mOnArtifactReady = std::move(onArtifactReady);
|
||||
mOnError = std::move(onError);
|
||||
mStopping.store(false, std::memory_order_release);
|
||||
mFinished.store(false, std::memory_order_release);
|
||||
mCompiler.StartShaderBuild(shaderId);
|
||||
mThread = std::thread([this]() { ThreadMain(); });
|
||||
}
|
||||
|
||||
void RuntimeShaderBridge::RequestStop()
|
||||
{
|
||||
mStopping.store(true, std::memory_order_release);
|
||||
}
|
||||
|
||||
void RuntimeShaderBridge::Stop()
|
||||
{
|
||||
RequestStop();
|
||||
if (mThread.joinable())
|
||||
mThread.join();
|
||||
mCompiler.Stop();
|
||||
mLayerId.clear();
|
||||
mOnArtifactReady = ArtifactCallback();
|
||||
mOnError = ErrorCallback();
|
||||
mFinished.store(true, std::memory_order_release);
|
||||
}
|
||||
|
||||
bool RuntimeShaderBridge::CanStopWithoutWaiting() const
|
||||
{
|
||||
return mFinished.load(std::memory_order_acquire) && !mCompiler.Running();
|
||||
}
|
||||
|
||||
void RuntimeShaderBridge::ThreadMain()
|
||||
{
|
||||
while (!mStopping.load(std::memory_order_acquire))
|
||||
{
|
||||
RuntimeSlangShaderBuild build;
|
||||
if (mCompiler.TryConsume(build))
|
||||
{
|
||||
if (build.succeeded)
|
||||
{
|
||||
build.artifact.layerId = mLayerId;
|
||||
if (mOnArtifactReady)
|
||||
mOnArtifactReady(build.artifact);
|
||||
}
|
||||
else if (mOnError)
|
||||
{
|
||||
mOnError(build.message);
|
||||
}
|
||||
mFinished.store(true, std::memory_order_release);
|
||||
return;
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
||||
}
|
||||
mFinished.store(true, std::memory_order_release);
|
||||
}
|
||||
38
src/runtime/shader/RuntimeShaderBridge.h
Normal file
38
src/runtime/shader/RuntimeShaderBridge.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include "RuntimeShaderArtifact.h"
|
||||
#include "RuntimeSlangShaderCompiler.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
class RuntimeShaderBridge
|
||||
{
|
||||
public:
|
||||
using ArtifactCallback = std::function<void(const RuntimeShaderArtifact&)>;
|
||||
using ErrorCallback = std::function<void(const std::string&)>;
|
||||
|
||||
RuntimeShaderBridge() = default;
|
||||
RuntimeShaderBridge(const RuntimeShaderBridge&) = delete;
|
||||
RuntimeShaderBridge& operator=(const RuntimeShaderBridge&) = delete;
|
||||
~RuntimeShaderBridge();
|
||||
|
||||
void Start(const std::string& shaderId, ArtifactCallback onArtifactReady, ErrorCallback onError);
|
||||
void Start(const std::string& layerId, const std::string& shaderId, ArtifactCallback onArtifactReady, ErrorCallback onError);
|
||||
void RequestStop();
|
||||
void Stop();
|
||||
bool CanStopWithoutWaiting() const;
|
||||
|
||||
private:
|
||||
void ThreadMain();
|
||||
|
||||
RuntimeSlangShaderCompiler mCompiler;
|
||||
std::thread mThread;
|
||||
std::atomic<bool> mStopping{ false };
|
||||
std::atomic<bool> mFinished{ true };
|
||||
std::string mLayerId;
|
||||
ArtifactCallback mOnArtifactReady;
|
||||
ErrorCallback mOnError;
|
||||
};
|
||||
172
src/runtime/shader/RuntimeSlangShaderCompiler.cpp
Normal file
172
src/runtime/shader/RuntimeSlangShaderCompiler.cpp
Normal file
@@ -0,0 +1,172 @@
|
||||
#include "RuntimeSlangShaderCompiler.h"
|
||||
|
||||
#include "ShaderCompiler.h"
|
||||
#include "ShaderPackageRegistry.h"
|
||||
#include "ShaderTypes.h"
|
||||
#include "SupportedShaderCatalog.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
|
||||
namespace
|
||||
{
|
||||
std::filesystem::path FindRepoRoot()
|
||||
{
|
||||
std::filesystem::path current = std::filesystem::current_path();
|
||||
for (;;)
|
||||
{
|
||||
if (std::filesystem::exists(current / "shaders" / "happy-accident" / "shader.slang") &&
|
||||
std::filesystem::exists(current / "runtime" / "templates" / "shader_wrapper.slang.in"))
|
||||
{
|
||||
return current;
|
||||
}
|
||||
|
||||
const std::filesystem::path parent = current.parent_path();
|
||||
if (parent.empty() || parent == current)
|
||||
return std::filesystem::current_path();
|
||||
current = parent;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
RuntimeSlangShaderCompiler::~RuntimeSlangShaderCompiler()
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
|
||||
void RuntimeSlangShaderCompiler::StartHappyAccidentBuild()
|
||||
{
|
||||
StartShaderBuild("happy-accident");
|
||||
}
|
||||
|
||||
void RuntimeSlangShaderCompiler::StartShaderBuild(const std::string& shaderId)
|
||||
{
|
||||
if (mRunning.load(std::memory_order_acquire))
|
||||
return;
|
||||
|
||||
if (mThread.joinable())
|
||||
mThread.join();
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
mReadyBuild = RuntimeSlangShaderBuild();
|
||||
}
|
||||
|
||||
mRunning.store(true, std::memory_order_release);
|
||||
mThread = std::thread([this, shaderId]() {
|
||||
RuntimeSlangShaderBuild build = BuildShader(shaderId);
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
mReadyBuild = std::move(build);
|
||||
mReadyBuild.available = true;
|
||||
}
|
||||
mRunning.store(false, std::memory_order_release);
|
||||
});
|
||||
}
|
||||
|
||||
void RuntimeSlangShaderCompiler::Stop()
|
||||
{
|
||||
if (mThread.joinable())
|
||||
mThread.join();
|
||||
mRunning.store(false, std::memory_order_release);
|
||||
}
|
||||
|
||||
bool RuntimeSlangShaderCompiler::TryConsume(RuntimeSlangShaderBuild& build)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
if (!mReadyBuild.available)
|
||||
return false;
|
||||
|
||||
build = std::move(mReadyBuild);
|
||||
mReadyBuild = RuntimeSlangShaderBuild();
|
||||
return true;
|
||||
}
|
||||
|
||||
RuntimeSlangShaderBuild RuntimeSlangShaderCompiler::BuildShader(const std::string& shaderId) const
|
||||
{
|
||||
RuntimeSlangShaderBuild build;
|
||||
build.artifact.shaderId = shaderId;
|
||||
|
||||
try
|
||||
{
|
||||
const std::filesystem::path repoRoot = FindRepoRoot();
|
||||
const std::filesystem::path shaderDir = repoRoot / "shaders" / shaderId;
|
||||
const std::filesystem::path runtimeBuildDir = repoRoot / "runtime" / "generated" / "render-cadence-compositor";
|
||||
|
||||
ShaderPackageRegistry registry(0);
|
||||
ShaderPackage shaderPackage;
|
||||
std::string error;
|
||||
if (!registry.ParseManifest(shaderDir / "shader.json", shaderPackage, error))
|
||||
{
|
||||
build.succeeded = false;
|
||||
build.message = error.empty() ? "Shader manifest parse failed." : error;
|
||||
return build;
|
||||
}
|
||||
const RenderCadenceCompositor::ShaderSupportResult support =
|
||||
RenderCadenceCompositor::CheckStatelessSinglePassShaderSupport(shaderPackage);
|
||||
if (!support.supported)
|
||||
{
|
||||
build.succeeded = false;
|
||||
build.message = support.reason;
|
||||
return build;
|
||||
}
|
||||
|
||||
RenderCadenceCompositor::FontAtlasBuildConfig fontConfig;
|
||||
fontConfig.repoRoot = repoRoot;
|
||||
RenderCadenceCompositor::FontAtlasBuilder fontAtlasBuilder(fontConfig);
|
||||
std::vector<RenderCadenceCompositor::FontAtlasBuildOutput> fontAtlasOutputs;
|
||||
if (!fontAtlasBuilder.BuildPackageFontAtlases(shaderPackage, fontAtlasOutputs, error))
|
||||
{
|
||||
build.succeeded = false;
|
||||
build.message = error.empty() ? "Font atlas build failed." : error;
|
||||
return build;
|
||||
}
|
||||
|
||||
ShaderCompiler compiler(
|
||||
repoRoot,
|
||||
runtimeBuildDir / (shaderId + ".wrapper.slang"),
|
||||
runtimeBuildDir / (shaderId + ".generated.glsl"),
|
||||
runtimeBuildDir / (shaderId + ".patched.glsl"),
|
||||
0);
|
||||
|
||||
const auto start = std::chrono::steady_clock::now();
|
||||
for (const ShaderPassDefinition& pass : shaderPackage.passes)
|
||||
{
|
||||
std::string fragmentShaderSource;
|
||||
if (!compiler.BuildPassFragmentShaderSource(shaderPackage, pass, fragmentShaderSource, error))
|
||||
{
|
||||
build.succeeded = false;
|
||||
build.message = error.empty() ? "Slang compile failed." : error;
|
||||
return build;
|
||||
}
|
||||
|
||||
RuntimeShaderPassArtifact passArtifact;
|
||||
passArtifact.passId = pass.id;
|
||||
passArtifact.fragmentShaderSource = std::move(fragmentShaderSource);
|
||||
passArtifact.inputNames = pass.inputNames;
|
||||
passArtifact.outputName = pass.outputName;
|
||||
build.artifact.passes.push_back(std::move(passArtifact));
|
||||
}
|
||||
|
||||
const auto end = std::chrono::steady_clock::now();
|
||||
const double milliseconds = std::chrono::duration_cast<std::chrono::duration<double, std::milli>>(end - start).count();
|
||||
build.succeeded = true;
|
||||
build.artifact.shaderId = shaderPackage.id;
|
||||
build.artifact.packageFingerprint = RenderCadenceCompositor::ShaderPackageFingerprint(shaderPackage);
|
||||
build.artifact.displayName = shaderPackage.displayName;
|
||||
build.artifact.parameterDefinitions = shaderPackage.parameters;
|
||||
build.artifact.fontAtlases = std::move(fontAtlasOutputs);
|
||||
if (!build.artifact.passes.empty())
|
||||
build.artifact.fragmentShaderSource = build.artifact.passes.front().fragmentShaderSource;
|
||||
build.artifact.message = shaderPackage.displayName + " Slang compile completed in " + std::to_string(milliseconds) + " ms.";
|
||||
build.message = build.artifact.message;
|
||||
return build;
|
||||
}
|
||||
catch (const std::exception& exception)
|
||||
{
|
||||
build.succeeded = false;
|
||||
build.message = exception.what();
|
||||
return build;
|
||||
}
|
||||
}
|
||||
39
src/runtime/shader/RuntimeSlangShaderCompiler.h
Normal file
39
src/runtime/shader/RuntimeSlangShaderCompiler.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include "RuntimeShaderArtifact.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
struct RuntimeSlangShaderBuild
|
||||
{
|
||||
bool available = false;
|
||||
bool succeeded = false;
|
||||
RuntimeShaderArtifact artifact;
|
||||
std::string message;
|
||||
};
|
||||
|
||||
class RuntimeSlangShaderCompiler
|
||||
{
|
||||
public:
|
||||
RuntimeSlangShaderCompiler() = default;
|
||||
RuntimeSlangShaderCompiler(const RuntimeSlangShaderCompiler&) = delete;
|
||||
RuntimeSlangShaderCompiler& operator=(const RuntimeSlangShaderCompiler&) = delete;
|
||||
~RuntimeSlangShaderCompiler();
|
||||
|
||||
void StartHappyAccidentBuild();
|
||||
void StartShaderBuild(const std::string& shaderId);
|
||||
void Stop();
|
||||
bool TryConsume(RuntimeSlangShaderBuild& build);
|
||||
bool Running() const { return mRunning.load(std::memory_order_acquire); }
|
||||
|
||||
private:
|
||||
RuntimeSlangShaderBuild BuildShader(const std::string& shaderId) const;
|
||||
|
||||
std::thread mThread;
|
||||
std::atomic<bool> mRunning{ false };
|
||||
std::mutex mMutex;
|
||||
RuntimeSlangShaderBuild mReadyBuild;
|
||||
};
|
||||
Reference in New Issue
Block a user