Skip to content

Commit 7861e9d

Browse files
authored
Fix OIT on Mac with explicit frag output binding (#332)
1 parent bb43937 commit 7861e9d

2 files changed

Lines changed: 31 additions & 0 deletions

File tree

common/src/backend/java/dev/engine_room/flywheel/backend/compile/PipelineCompiler.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,20 @@ static PipelineCompiler create(ShaderSources sources, Pipeline pipeline, List<So
181181
program.bindAttribLocation("_flw_aOverlay", 3);
182182
program.bindAttribLocation("_flw_aLight", 4);
183183
program.bindAttribLocation("_flw_aNormal", 5);
184+
185+
// Explicitly bind the fragment outputs to the color attachment slots the
186+
// OitFramebuffer draw buffers expect. Each OIT pass only declares one of these
187+
// output groups (guarded by #ifdefs), and binding a name that isn't present is a
188+
// no-op, so binding them all unconditionally is safe. Apple's GL driver assigns
189+
// implicit output locations in an order that doesn't match declaration order,
190+
// which scrambles the coefficient targets and makes OIT geometry invisible.
191+
program.bindFragDataLocation("_flw_outputColor", 0);
192+
program.bindFragDataLocation("_flw_depthRange_out", 0);
193+
program.bindFragDataLocation("_flw_coeffs0", 0);
194+
program.bindFragDataLocation("_flw_coeffs1", 1);
195+
program.bindFragDataLocation("_flw_coeffs2", 2);
196+
program.bindFragDataLocation("_flw_coeffs3", 3);
197+
program.bindFragDataLocation("_flw_accumulate", 0);
184198
})
185199
.postLink((key, program) -> {
186200
Uniforms.setUniformBlockBindings(program);

common/src/backend/java/dev/engine_room/flywheel/backend/gl/shader/GlProgram.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import static org.lwjgl.opengl.GL20.glUniform4f;
1111
import static org.lwjgl.opengl.GL20.glUniformMatrix3fv;
1212
import static org.lwjgl.opengl.GL20.glUniformMatrix4fv;
13+
import static org.lwjgl.opengl.GL30.glBindFragDataLocation;
1314
import static org.lwjgl.opengl.GL30.glUniform1ui;
1415
import static org.lwjgl.opengl.GL30.glUniform2ui;
1516
import static org.lwjgl.opengl.GL31.GL_INVALID_INDEX;
@@ -189,6 +190,22 @@ public void bindAttribLocation(String attribute, int binding) {
189190
glBindAttribLocation(handle(), binding, attribute);
190191
}
191192

193+
/**
194+
* Binds a fragment shader output variable to a specific color attachment (draw buffer) slot.
195+
*
196+
* <p>This must happen before the program is linked. Binding a name that isn't an active output
197+
* in the linked program is a no-op, so it's safe to bind every possible output unconditionally.
198+
*
199+
* <p>Without this, the GL implementation is free to assign output locations in any order it
200+
* likes when a fragment shader declares multiple outputs without explicit {@code layout(location)}
201+
* qualifiers. Most desktop drivers happen to assign them in declaration order, but Apple's legacy
202+
* OpenGL driver does not, which scrambles the OIT coefficient targets and makes the transparent
203+
* geometry vanish. Binding the locations explicitly is correct everywhere.
204+
*/
205+
public void bindFragDataLocation(String name, int binding) {
206+
glBindFragDataLocation(handle(), binding, name);
207+
}
208+
192209
@Override
193210
protected void deleteInternal(int handle) {
194211
glDeleteProgram(handle);

0 commit comments

Comments
 (0)