0

在 OpenGL 管道之前:我想为我渲染的一些对象使用特殊的顶点着色器。所以我想到了这个:

int currProgram = glGetInteger(GL_CURRENT_PROGRAM);
int currVertexShader = 0;
if (currProgram == 0) {
    glUseProgram(programName);
} else {
    currVertexShader = GLStatics.getShader(currProgram,
            GL_VERTEX_SHADER);
    if (currVertexShader != 0) {
        glDetachShader(currProgram, currVertexShader); // <-- problem here
    }
    glAttachShader(currProgram, shaderName);
    GLStatics.linkProgramSafe(currProgram);
}
// Actual render code
if (currProgram != 0) {
    glDetachShader(currProgram, shaderName); // Can safely detach
    if (currVertexShader != 0) {
        glAttachShader(currProgram, currVertexShader);
    }
    GLStatics.linkProgramSafe(currProgram);
}
glUseProgram(currProgram);

所以我必须静态 GLObjects: shaderName,这是我要使用的已编译顶点着色器,programName如果事先没有绑定其他程序,这是我绑定的程序。

我认为当真的有问题时这会运行良好。在执行代码之前在当前绑定程序的顶点着色器上调用 glDeleteShader() 时,着色器对象将被删除(在标记的行中)并且之后无法重新附加。

有没有一种简单的方法来解决这个问题(在高效的意义上很容易)?

为了完整起见,GLStatics课程:

public class GLStatics {
    public static ByteBuffer createDirectBuffer(int size) {
        return ByteBuffer.allocateDirect(size);
    }

    public static int createProgramSafe() {
        int programName = glCreateProgram();
        if (programName == 0) {
            throw new IllegalStateException(
                    "GL Error: Created Program is 0. Can't proceed.");
        }
        return programName;
    }

    public static int getShader(int program, int searchedType) {
        int shaderCount = glGetProgrami(program, GL_ATTACHED_SHADERS);
        IntBuffer attachedShaders = createDirectBuffer(shaderCount * 4)
                .asIntBuffer();
        IntBuffer count = createDirectBuffer(4).asIntBuffer();
        glGetAttachedShaders(program, count, attachedShaders);
        assert count.get() == shaderCount;
        for (int i = 0; i < shaderCount; ++i) {
            int shaderCandidate = attachedShaders.get();
            if (searchedType == glGetShaderi(shaderCandidate, GL_SHADER_TYPE)) {
                return shaderCandidate;
            }
        }
        return 0;
    }

    public static void linkProgramSafe(int program) {
        glLinkProgram(program);
        if (glGetProgrami(program, GL_LINK_STATUS) == GL_FALSE) {
            int errorLength = glGetProgrami(program, GL_INFO_LOG_LENGTH);
            String error = glGetProgramInfoLog(program, errorLength);
            throw new IllegalStateException(error);
        }

    }
}
4

1 回答 1

1

您可以有另一个着色器程序,仅用于“停放”着色器,同时它与您正在操作的着色器分离。这将保持一个引用,以防止它被永久删除。

仅显示代码的扩展部分,dummyProgram作为您专门为此目的创建的程序:

if (currVertexShader != 0) {
    glAttachShader(dummyProgram, currVertexShader);
    glDetachShader(currProgram, currVertexShader);
}
...
if (currVertexShader != 0) {
    glAttachShader(currProgram, currVertexShader);
    glDetachShader(dummyProgram, currVertexShader);
}

这将根据规范中的以下定义(OpenGL 3.3 规范中的附录 D.1.2)起作用:

当一个着色器对象或程序对象被删除时,它被标记为删除,但它的名称仍然有效,直到可以删除底层对象,因为它不再使用。着色器对象在附加到任何程序对象时正在使用中。

因此,只需将着色器附加到任何程序就足以让它被视为“正在使用”,并防止它被删除。如果您对有关程序/着色器生命周期的微妙方面的更多细节感兴趣,这是我写给较早问题的答案:glDeleteShader - 顺序无关吗?.

于 2014-07-22T04:31:12.230 回答