OpenGL: создание прокси-пользователя для шейдера
До 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);
}
}
}
1 ответ
У вас может быть другая шейдерная программа, которую вы используете просто для "парковки" шейдера, когда он отсоединен от шейдера, над которым вы работаете. Это будет поддерживать ссылку, которая предотвращает его удаление навсегда.
Просто показывая расширенные части кода, с dummyProgram
будучи программой, которую вы создали исключительно для этой цели:
if (currVertexShader != 0) {
glAttachShader(dummyProgram, currVertexShader);
glDetachShader(currProgram, currVertexShader);
}
...
if (currVertexShader != 0) {
glAttachShader(currProgram, currVertexShader);
glDetachShader(dummyProgram, currVertexShader);
}
Это будет работать на основе следующего определения в спецификации (приложение D.1.2 в спецификации OpenGL 3.3):
Когда шейдерный объект или программный объект удаляется, он помечается для удаления, но его имя остается действительным до тех пор, пока базовый объект не может быть удален, поскольку он больше не используется. Шейдерный объект используется, когда он присоединен к любому программному объекту.
Поэтому достаточно просто присоединить шейдер к любой программе, чтобы он считался "используемым", и предотвратить его удаление. Если вам интересны более подробные сведения о тонких аспектах времени жизни программ / шейдеров, я написал ответ на предыдущий вопрос: glDeleteShader - порядок не имеет значения?,