mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-11 05:38:31 +09:00
[Fix] (Tessellation): bound the pass-through control-stage cache and stop baking "draw nothing" for levels GL clamps
This commit is contained in:
@@ -12,6 +12,10 @@
|
||||
|
||||
#include "ShaderCompiler.h"
|
||||
|
||||
#include <format>
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "SpirvPasses/EliminateFloatEqualsZeroPass.h"
|
||||
#include "SpirvPasses/FlattenInterfaceStructPass.h"
|
||||
#include "SpirvPasses/RenameSamplerFunctionParameterPass.h"
|
||||
@@ -64,6 +68,30 @@
|
||||
namespace MobileGL {
|
||||
namespace MG_Util {
|
||||
namespace ShaderTranspiler {
|
||||
// Above every plausible GL_MAX_TESS_GEN_LEVEL (the GL core minimum is 64), so it lands on the
|
||||
// same clamped result the device's own maximum would. +inf has to reach the tessellator as
|
||||
// "as finely as possible", not as "discard".
|
||||
static constexpr const char* kClampedHighTessLevelLiteral = "65536.0";
|
||||
|
||||
String TessellationLevelLiteral(Float value) {
|
||||
// GL leaves a NaN level unspecified; 0.0 is the safe reading, and unlike "nan" it compiles.
|
||||
if (std::isnan(value)) return "0.0";
|
||||
// -inf is <= 0 and discards the patch, exactly like 0.0. +inf clamps to the maximum.
|
||||
if (std::isinf(value)) return value > 0.0f ? kClampedHighTessLevelLiteral : "0.0";
|
||||
|
||||
// Shortest round-trip, not a fixed six decimals: "{:.6f}" renders every level below ~5e-7
|
||||
// as "0.000000", which turns a positive level GL would clamp to 1 into a discarded patch.
|
||||
String text = std::format("{}", value);
|
||||
// ...but shortest round-trip spells an integral value as a bare digit sequence, which GLSL
|
||||
// reads as an INT literal, so the decimal point has to be put back when nothing else marks
|
||||
// the literal as floating point.
|
||||
if (text.find('.') == String::npos && text.find('e') == String::npos &&
|
||||
text.find('E') == String::npos) {
|
||||
text += ".0";
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
// `env` is the compile-time backend snapshot; null means "resolve from the live
|
||||
// backend", which is what the standalone/test entry points do. The pipeline always
|
||||
// passes one, so a worker never reaches pActiveBackendObject through here.
|
||||
|
||||
@@ -18,6 +18,18 @@
|
||||
namespace MobileGL {
|
||||
namespace MG_Util {
|
||||
namespace ShaderTranspiler {
|
||||
// A GLSL float literal for a tessellation level, for the pass-through tessellation
|
||||
// control stage both backends synthesize when a program has an evaluation stage and no
|
||||
// control stage. Shared so the two generators cannot disagree about what a level means.
|
||||
//
|
||||
// GL 4.6 core 11.2.2 discards a patch only when a relevant OUTER level is <= 0; every
|
||||
// other value is CLAMPED into [1, MAX_TESS_GEN_LEVEL]. So "draw nothing" is reserved
|
||||
// for the values that really mean it, and everything else has to survive the trip
|
||||
// through text: a shortest-round-trip spelling, because a fixed-decimal one flushes
|
||||
// small positive levels to zero, and always with a '.' or an exponent, because a bare
|
||||
// digit sequence is an INT literal and `gl_TessLevelOuter[0] = 1;` does not compile.
|
||||
String TessellationLevelLiteral(Float value);
|
||||
|
||||
class ShaderCompiler {
|
||||
public:
|
||||
static Result<SharedPtr<glslang::TShader>> CompileShader(const ShaderAttrib& attrib);
|
||||
|
||||
Reference in New Issue
Block a user