mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-11 21:58:31 +09:00
[Fix, Test] (MG_State, MG_Impl, MG_IntegrationTest): a double uniform initializer no longer reads zero
This commit is contained in:
@@ -850,7 +850,12 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
// vector per column - while the value glGetUniform* must return is tightly packed
|
// vector per column - while the value glGetUniform* must return is tightly packed
|
||||||
// columns * rows floats. Only mat4 is the same either way; every other shape needs the
|
// columns * rows floats. Only mat4 is the same either way; every other shape needs the
|
||||||
// padding undone, and the readback has to undo exactly what UniformMatrixfv_Object put
|
// padding undone, and the readback has to undo exactly what UniformMatrixfv_Object put
|
||||||
// there. Returns false when `ttype` is not a float matrix (nothing to unpack).
|
// there. Returns false when there is nothing here to unpack.
|
||||||
|
//
|
||||||
|
// A DOUBLE matrix is declined not because it is laid out differently - it is not, the
|
||||||
|
// demotion makes a dmat4 a mat4 in the shader and a mat4-shaped slot here - but because it
|
||||||
|
// is ROUTED differently: the caller's component-by-component EbtDouble branch has to widen
|
||||||
|
// each float back to the queried type, and it undoes the same padding itself.
|
||||||
Bool TryGatherFloatMatrixColumns(const glslang::TType* ttype, const char* pBase, void* params) {
|
Bool TryGatherFloatMatrixColumns(const glslang::TType* ttype, const char* pBase, void* params) {
|
||||||
if (ttype == nullptr || !ttype->isMatrix() || ttype->getBasicType() == glslang::EbtDouble) return false;
|
if (ttype == nullptr || !ttype->isMatrix() || ttype->getBasicType() == glslang::EbtDouble) return false;
|
||||||
const Int columns = ttype->getMatrixCols();
|
const Int columns = ttype->getMatrixCols();
|
||||||
|
|||||||
@@ -314,6 +314,45 @@ void main() {
|
|||||||
EXPECT_EQ(glGetError(), static_cast<GLenum>(GL_NO_ERROR));
|
EXPECT_EQ(glGetError(), static_cast<GLenum>(GL_NO_ERROR));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(DoublePrecisionScenario, ADoubleUniformKeepsItsDeclaredInitializer) {
|
||||||
|
if (!Ready()) return;
|
||||||
|
// A declared initializer is seeded straight into the uniform shadow at link, and the
|
||||||
|
// seeding used to skip 64-bit floats outright ("no 32-bit shadow encoding") - which
|
||||||
|
// was true before the demotion and silently left every such uniform reading zero.
|
||||||
|
const char* source = R"(#version 430 core
|
||||||
|
layout(local_size_x = 1) in;
|
||||||
|
uniform double uSeeded = 2.5lf;
|
||||||
|
uniform dvec3 uSeededVector = dvec3(4.0lf, 5.0lf, 6.0lf);
|
||||||
|
layout(std430, binding = 0) buffer Output {
|
||||||
|
float g_out[];
|
||||||
|
};
|
||||||
|
void main() {
|
||||||
|
g_out[0] = float(uSeeded);
|
||||||
|
g_out[1] = float(uSeededVector.x);
|
||||||
|
g_out[2] = float(uSeededVector.y);
|
||||||
|
g_out[3] = float(uSeededVector.z);
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
const GLuint program = CompileComputeProgram(source);
|
||||||
|
ASSERT_NE(program, 0u) << m_buildLog;
|
||||||
|
|
||||||
|
glUseProgram(program);
|
||||||
|
glDispatchCompute(1, 1, 1);
|
||||||
|
glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT);
|
||||||
|
std::vector<float> values(4, -1.0f);
|
||||||
|
glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_output);
|
||||||
|
glGetBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, 4 * sizeof(float), values.data());
|
||||||
|
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
|
||||||
|
glUseProgram(0);
|
||||||
|
glDeleteProgram(program);
|
||||||
|
|
||||||
|
EXPECT_FLOAT_EQ(values[0], 2.5f) << "scalar double initializer";
|
||||||
|
EXPECT_FLOAT_EQ(values[1], 4.0f) << "dvec3 initializer .x";
|
||||||
|
EXPECT_FLOAT_EQ(values[2], 5.0f) << "dvec3 initializer .y";
|
||||||
|
EXPECT_FLOAT_EQ(values[3], 6.0f) << "dvec3 initializer .z";
|
||||||
|
EXPECT_EQ(glGetError(), static_cast<GLenum>(GL_NO_ERROR));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(DoublePrecisionScenario, TheFp64ExtensionIsNotAdvertised) {
|
TEST_F(DoublePrecisionScenario, TheFp64ExtensionIsNotAdvertised) {
|
||||||
if (!Ready()) return;
|
if (!Ready()) return;
|
||||||
// The shader above compiled, linked and ran without the extension string, which is
|
// The shader above compiled, linked and ran without the extension string, which is
|
||||||
|
|||||||
@@ -165,10 +165,18 @@ namespace MobileGL::MG_State::GLState {
|
|||||||
const Int elements = init.arraySize;
|
const Int elements = init.arraySize;
|
||||||
if (componentsPerElement <= 0 || elements <= 0) continue;
|
if (componentsPerElement <= 0 || elements <= 0) continue;
|
||||||
|
|
||||||
const Bool isFloat = init.basicType == glslang::EbtFloat || init.basicType == glslang::EbtFloat16;
|
// EbtDouble belongs with the floats now, not with the skipped types: every 64-bit
|
||||||
|
// float in a shader is narrowed to 32 bits before the module reaches a backend
|
||||||
|
// (ShaderTranspiler::DemoteFloat64Pass), so a `uniform double d = 1.5;` has exactly
|
||||||
|
// the 32-bit shadow encoding a `uniform float` does - and glslang already folded its
|
||||||
|
// value into floatValues, which is a vector<double> either way. Leaving it out meant
|
||||||
|
// the initializer was silently dropped and the uniform came up zero.
|
||||||
|
const Bool isFloat = init.basicType == glslang::EbtFloat ||
|
||||||
|
init.basicType == glslang::EbtFloat16 ||
|
||||||
|
init.basicType == glslang::EbtDouble;
|
||||||
const Bool isInt = init.basicType == glslang::EbtInt || init.basicType == glslang::EbtUint ||
|
const Bool isInt = init.basicType == glslang::EbtInt || init.basicType == glslang::EbtUint ||
|
||||||
init.basicType == glslang::EbtBool;
|
init.basicType == glslang::EbtBool;
|
||||||
// Anything else (fp64, 64-bit integers) has no 32-bit shadow encoding here, and a
|
// Anything else (64-bit integers) has no 32-bit shadow encoding here, and a
|
||||||
// half-written uniform is worse than an untouched one.
|
// half-written uniform is worse than an untouched one.
|
||||||
if (!isFloat && !isInt) continue;
|
if (!isFloat && !isInt) continue;
|
||||||
const SizeT provided = isFloat ? init.floatValues.size() : init.intValues.size();
|
const SizeT provided = isFloat ? init.floatValues.size() : init.intValues.size();
|
||||||
|
|||||||
Reference in New Issue
Block a user