Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,20 @@ static PipelineCompiler create(ShaderSources sources, Pipeline pipeline, List<So
program.bindAttribLocation("_flw_aOverlay", 3);
program.bindAttribLocation("_flw_aLight", 4);
program.bindAttribLocation("_flw_aNormal", 5);

// Explicitly bind the fragment outputs to the color attachment slots the
// OitFramebuffer draw buffers expect. Each OIT pass only declares one of these
// output groups (guarded by #ifdefs), and binding a name that isn't present is a
// no-op, so binding them all unconditionally is safe. Apple's GL driver assigns
// implicit output locations in an order that doesn't match declaration order,
// which scrambles the coefficient targets and makes OIT geometry invisible.
program.bindFragDataLocation("_flw_outputColor", 0);
program.bindFragDataLocation("_flw_depthRange_out", 0);
program.bindFragDataLocation("_flw_coeffs0", 0);
program.bindFragDataLocation("_flw_coeffs1", 1);
program.bindFragDataLocation("_flw_coeffs2", 2);
program.bindFragDataLocation("_flw_coeffs3", 3);
program.bindFragDataLocation("_flw_accumulate", 0);
})
.postLink((key, program) -> {
Uniforms.setUniformBlockBindings(program);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import static org.lwjgl.opengl.GL20.glUniform4f;
import static org.lwjgl.opengl.GL20.glUniformMatrix3fv;
import static org.lwjgl.opengl.GL20.glUniformMatrix4fv;
import static org.lwjgl.opengl.GL30.glBindFragDataLocation;
import static org.lwjgl.opengl.GL30.glUniform1ui;
import static org.lwjgl.opengl.GL30.glUniform2ui;
import static org.lwjgl.opengl.GL31.GL_INVALID_INDEX;
Expand Down Expand Up @@ -189,6 +190,22 @@ public void bindAttribLocation(String attribute, int binding) {
glBindAttribLocation(handle(), binding, attribute);
}

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

@Override
protected void deleteInternal(int handle) {
glDeleteProgram(handle);
Expand Down
Loading