Skip to content

Commit b5c58cd

Browse files
mvaligurskyMartin Valigurskycursoragent
authored
fix: move clustered packed values out of light struct for Samsung WebGL (playcanvas#8461)
Store clustered packed `flags`, `anglesData`, and `colorBFlagsData` as module-scope temporaries instead of struct members to avoid observed precision/corruption issues on some Samsung devices while preserving decode behavior. Fixes playcanvas#7800. Co-authored-by: Martin Valigursky <mvaligursky@snapchat.com> Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent b61d27b commit b5c58cd

1 file changed

Lines changed: 20 additions & 23 deletions

File tree

src/scene/shader-lib/glsl/chunks/lit/frag/clusteredLight.js

Lines changed: 20 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,6 @@ uniform vec2 shadowAtlasParams;
4444
// it's sorted to have all vectors aligned to 4 floats to limit padding
4545
struct ClusterLightData {
4646
47-
// 32bit of flags
48-
uint flags;
49-
5047
// area light sizes / orientation
5148
vec3 halfWidth;
5249
@@ -88,16 +85,10 @@ struct ClusterLightData {
8885
// compressed biases, two haf-floats stored in a float
8986
float biasesData;
9087
91-
// blue color component and angle flags (as uint for efficient bit operations)
92-
uint colorBFlagsData;
93-
9488
// shadow bias values
9589
float shadowBias;
9690
float shadowNormalBias;
9791
98-
// compressed angles, two haf-floats stored in a float
99-
float anglesData;
100-
10192
// spot light inner and outer angle cosine
10293
float innerConeAngleCos;
10394
float outerConeAngleCos;
@@ -118,6 +109,12 @@ struct ClusterLightData {
118109
// shadow (spot light only) / cookie projection matrix
119110
mat4 lightProjectionMatrix;
120111
112+
// NOTE: On some Samsung devices, these values can suffer precision / corruption issues when stored
113+
// as members of ClusterLightData. Keep them as module-scope temporaries instead. See issue #7800.
114+
uint clusterLightData_flags; // 32bit of flags
115+
float clusterLightData_anglesData; // compressed angles, two haf-floats stored in a float
116+
uint clusterLightData_colorBFlagsData; // blue color component and angle flags (as uint for efficient bit operations)
117+
121118
vec4 sampleLightTextureF(const ClusterLightData clusterLightData, int index) {
122119
return texelFetch(lightsTexture, ivec2(index, clusterLightData.lightIndex), 0);
123120
}
@@ -131,13 +128,13 @@ void decodeClusterLightCore(inout ClusterLightData clusterLightData, int lightIn
131128
vec4 halfData = sampleLightTextureF(clusterLightData, {CLUSTER_TEXTURE_COLOR_ANGLES_BIAS});
132129
133130
// store floats we decode later as needed
134-
clusterLightData.anglesData = halfData.z;
131+
clusterLightData_anglesData = halfData.z;
135132
clusterLightData.biasesData = halfData.w;
136-
clusterLightData.colorBFlagsData = floatBitsToUint(halfData.y);
133+
clusterLightData_colorBFlagsData = floatBitsToUint(halfData.y);
137134
138135
// decompress color half-floats
139136
vec2 colorRG = unpackHalf2x16(floatBitsToUint(halfData.x));
140-
vec2 colorB_flags = unpackHalf2x16(clusterLightData.colorBFlagsData);
137+
vec2 colorB_flags = unpackHalf2x16(clusterLightData_colorBFlagsData);
141138
clusterLightData.color = vec3(colorRG, colorB_flags.x) * {LIGHT_COLOR_DIVIDER};
142139
143140
// position and range, full floats
@@ -152,21 +149,21 @@ void decodeClusterLightCore(inout ClusterLightData clusterLightData, int lightIn
152149
clusterLightData.direction = lightDir_Flags.xyz;
153150
154151
// 32bit flags
155-
clusterLightData.flags = floatBitsToUint(lightDir_Flags.w);
156-
clusterLightData.isSpot = (clusterLightData.flags & (1u << 30u)) != 0u;
157-
clusterLightData.shape = (clusterLightData.flags >> 28u) & 0x3u;
158-
clusterLightData.falloffModeLinear = (clusterLightData.flags & (1u << 27u)) == 0u;
159-
clusterLightData.shadowIntensity = float((clusterLightData.flags >> 0u) & 0xFFu) / 255.0;
160-
clusterLightData.cookieIntensity = float((clusterLightData.flags >> 8u) & 0xFFu) / 255.0;
161-
clusterLightData.isDynamic = (clusterLightData.flags & (1u << 22u)) != 0u;
162-
clusterLightData.isLightmapped = (clusterLightData.flags & (1u << 21u)) != 0u;
152+
clusterLightData_flags = floatBitsToUint(lightDir_Flags.w);
153+
clusterLightData.isSpot = (clusterLightData_flags & (1u << 30u)) != 0u;
154+
clusterLightData.shape = (clusterLightData_flags >> 28u) & 0x3u;
155+
clusterLightData.falloffModeLinear = (clusterLightData_flags & (1u << 27u)) == 0u;
156+
clusterLightData.shadowIntensity = float((clusterLightData_flags >> 0u) & 0xFFu) / 255.0;
157+
clusterLightData.cookieIntensity = float((clusterLightData_flags >> 8u) & 0xFFu) / 255.0;
158+
clusterLightData.isDynamic = (clusterLightData_flags & (1u << 22u)) != 0u;
159+
clusterLightData.isLightmapped = (clusterLightData_flags & (1u << 21u)) != 0u;
163160
}
164161
165162
void decodeClusterLightSpot(inout ClusterLightData clusterLightData) {
166163
// decompress spot light angles
167-
uint angleFlags = (clusterLightData.colorBFlagsData >> 16u) & 0xFFFFu; // Extract upper 16 bits as integer
164+
uint angleFlags = (clusterLightData_colorBFlagsData >> 16u) & 0xFFFFu; // Extract upper 16 bits as integer
168165
169-
vec2 angleValues = unpackHalf2x16(floatBitsToUint(clusterLightData.anglesData));
166+
vec2 angleValues = unpackHalf2x16(floatBitsToUint(clusterLightData_anglesData));
170167
float innerVal = angleValues.x;
171168
float outerVal = angleValues.y;
172169
@@ -207,7 +204,7 @@ void decodeClusterLightShadowData(inout ClusterLightData clusterLightData) {
207204
void decodeClusterLightCookieData(inout ClusterLightData clusterLightData) {
208205
209206
// extract channel mask from flags
210-
uint cookieFlags = (clusterLightData.flags >> 23u) & 0x0Fu; // 4bits, each bit enables a channel
207+
uint cookieFlags = (clusterLightData_flags >> 23u) & 0x0Fu; // 4bits, each bit enables a channel
211208
clusterLightData.cookieChannelMask = vec4(uvec4(cookieFlags) & uvec4(1u, 2u, 4u, 8u));
212209
clusterLightData.cookieChannelMask = step(1.0, clusterLightData.cookieChannelMask); // Normalize to 0.0 or 1.0
213210
}

0 commit comments

Comments
 (0)