mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-11 13:48:30 +09:00
[Feat] (Program): implement GL_ARB_gl_spirv - glShaderBinary, glSpecializeShader and the SPIR_V_BINARY state, feeding the module into the ordinary compile pipeline
This commit is contained in:
@@ -15,7 +15,59 @@
|
||||
#include <MG_Util/ShaderTranspiler/Types.h>
|
||||
|
||||
namespace MobileGL::MG_State::GLState {
|
||||
void ShaderObject::SetSpirvBinary(Vector<Uint32>&& binary) {
|
||||
// A module replaces whatever this object stood for, so the compiled state of the old
|
||||
// source goes with it - including a compile still in flight.
|
||||
ReleaseCompileNode();
|
||||
m_spirvBinary = Move(binary);
|
||||
m_hasSpirvBinary = true;
|
||||
m_specializationFailed = false;
|
||||
m_specializationInfoLog.clear();
|
||||
m_source = MakeShared<const String>(String{});
|
||||
InvalidateCompiledState();
|
||||
}
|
||||
|
||||
void ShaderObject::SpecializeFromSpirv(String&& glsl) {
|
||||
ReleaseCompileNode();
|
||||
m_specializationFailed = false;
|
||||
m_specializationInfoLog.clear();
|
||||
// The GLSL the module specializes to enters the ORDINARY pipeline from here: preprocess,
|
||||
// glslang parse, reflection, transpile, both backends. Nothing downstream needs to know
|
||||
// the source was not written by the application - which is the whole reason this hop
|
||||
// exists, and the reason a SPIR-V program's GL-visible surface (uniform locations, block
|
||||
// indices, transform-feedback layout) is populated at all.
|
||||
m_source = MakeShared<const String>(Move(glsl));
|
||||
InvalidateCompiledState();
|
||||
Compile();
|
||||
}
|
||||
|
||||
void ShaderObject::RecordSpecializationFailure(String&& infoLog) {
|
||||
ReleaseCompileNode();
|
||||
m_source = MakeShared<const String>(String{});
|
||||
InvalidateCompiledState();
|
||||
m_specializationFailed = true;
|
||||
m_specializationInfoLog = Move(infoLog);
|
||||
}
|
||||
|
||||
void ShaderObject::SetShaderSource(const String& source) {
|
||||
// glShaderSource on a SPIR-V shader takes the object back to being a GLSL one, and
|
||||
// GL_SPIR_V_BINARY must then read FALSE (ARB_gl_spirv; gl4cGlSpirvTests'
|
||||
// spirv_modules_state_queries_test checks exactly this transition). The stored module goes
|
||||
// with the flag - re-specializing it would be re-specializing a shader the application has
|
||||
// already replaced. The memo below is skipped on purpose: the source may well be
|
||||
// byte-identical to the empty string this object has been holding, and keeping the
|
||||
// "compiled state" of that would keep the module's verdict too.
|
||||
if (m_hasSpirvBinary || m_specializationFailed) {
|
||||
m_hasSpirvBinary = false;
|
||||
m_spirvBinary.clear();
|
||||
m_spirvBinary.shrink_to_fit();
|
||||
m_specializationFailed = false;
|
||||
m_specializationInfoLog.clear();
|
||||
ReleaseCompileNode();
|
||||
m_source = MakeShared<const String>(source);
|
||||
InvalidateCompiledState();
|
||||
return;
|
||||
}
|
||||
// P0b layer 1. glShaderSource always REPLACES the source, but replacing it with a
|
||||
// byte-identical one cannot change what a compile would produce: the whole
|
||||
// pipeline (preprocess -> lexical checks -> glslang parse) is a pure function of
|
||||
@@ -36,6 +88,10 @@ namespace MobileGL::MG_State::GLState {
|
||||
}
|
||||
|
||||
void ShaderObject::SetShaderSource(String&& source) {
|
||||
if (m_hasSpirvBinary || m_specializationFailed) {
|
||||
SetShaderSource(static_cast<const String&>(source));
|
||||
return;
|
||||
}
|
||||
if (SourceMatchesCompiledState(source)) return;
|
||||
ReleaseCompileNode();
|
||||
m_source = MakeShared<const String>(Move(source));
|
||||
|
||||
@@ -65,6 +65,28 @@ namespace MobileGL {
|
||||
void SetShaderSource(const String& source);
|
||||
void SetShaderSource(String&& source);
|
||||
void Compile();
|
||||
|
||||
// ---- GL_ARB_gl_spirv ----
|
||||
// glShaderBinary(GL_SHADER_BINARY_FORMAT_SPIR_V): the object stops standing for a
|
||||
// GLSL source and starts standing for an application-supplied SPIR-V module. The
|
||||
// module is held verbatim until glSpecializeShader names an entry point for it -
|
||||
// ARB_gl_spirv makes the pair a two-step operation, and glCompileShader in between is
|
||||
// INVALID_OPERATION rather than a compile of anything.
|
||||
//
|
||||
// Both directions clear the other: glShaderSource on a SPIR-V shader takes it back to
|
||||
// being a GLSL shader with GL_SPIR_V_BINARY reading FALSE, which the conformance suite
|
||||
// checks explicitly.
|
||||
void SetSpirvBinary(Vector<Uint32>&& binary);
|
||||
Bool HasSpirvBinary() const { return m_hasSpirvBinary; }
|
||||
const Vector<Uint32>& GetSpirvBinary() const { return m_spirvBinary; }
|
||||
// glSpecializeShader's half: hand the object the GLSL its module specializes to and
|
||||
// let the ordinary pipeline compile it.
|
||||
void SpecializeFromSpirv(String&& glsl);
|
||||
// The other half: specialization itself failed (a bad entry point, a constant id the
|
||||
// module does not declare, a module spirv-val rejects). There is nothing to compile,
|
||||
// so the verdict is recorded directly - COMPILE_STATUS false with this log - and both
|
||||
// queries answer from it without touching the compile pipeline.
|
||||
void RecordSpecializationFailure(String&& infoLog);
|
||||
// Gives up this object's claim on its compile node, cancelling the node only if
|
||||
// this object was its LAST claimant. Called at the points where the object's
|
||||
// compiled state stops being observable through THIS name: a real source change,
|
||||
@@ -99,14 +121,16 @@ namespace MobileGL {
|
||||
const SharedPtr<const String>& GetShaderSourcePtr() const { return m_source; }
|
||||
|
||||
const SharedPtr<glslang::TShader>& GetCompiledShader() const { return Compiled().shader; }
|
||||
const String& GetInfoLog() const { return Compiled().infoLog; }
|
||||
const String& GetInfoLog() const {
|
||||
return m_specializationFailed ? m_specializationInfoLog : Compiled().infoLog;
|
||||
}
|
||||
// Explicit layout(location = N) qualifiers on this shader's default-block
|
||||
// uniforms, as glslang recorded them at the point its Vulkan-relaxed remap
|
||||
// discarded them (see CollectExplicitUniformLocations).
|
||||
const UnorderedMap<String, Int>& GetExplicitUniformLocations() const {
|
||||
return Compiled().explicitUniformLocations;
|
||||
}
|
||||
Bool GetCompileStatus() const { return Compiled().compileStatus; }
|
||||
Bool GetCompileStatus() const { return m_specializationFailed ? false : Compiled().compileStatus; }
|
||||
Bool GetDeleteStatus() const { return m_deleteStatus; }
|
||||
|
||||
// Blocks until a pending compile has published its artifacts. Public for the
|
||||
@@ -248,6 +272,18 @@ namespace MobileGL {
|
||||
// query optimistically for the current node. Cleared wherever the node
|
||||
// changes hands (AdoptCompileNode) or goes away (DropCompileNode).
|
||||
mutable Bool m_optimisticAnswerLatched = false;
|
||||
// The application-supplied SPIR-V module and the flag GL_SPIR_V_BINARY reports. The
|
||||
// module is kept after specialization too: glSpecializeShader may legally run again on
|
||||
// the same object with different constants, and the second call has to re-specialize
|
||||
// the ORIGINAL words rather than the ones the first call folded.
|
||||
Vector<Uint32> m_spirvBinary;
|
||||
Bool m_hasSpirvBinary = false;
|
||||
// A specialization that failed before any compile could start. Kept beside the
|
||||
// compile artifacts rather than inside them because there is no compile job to hang
|
||||
// it on - see RecordSpecializationFailure. Cleared by anything that gives the object
|
||||
// a new meaning (a new source, a new module, a fresh specialization).
|
||||
Bool m_specializationFailed = false;
|
||||
String m_specializationInfoLog;
|
||||
};
|
||||
} // namespace MG_State::GLState
|
||||
} // namespace MobileGL
|
||||
|
||||
Reference in New Issue
Block a user