I am trying to render images with OpenGL using a compute shader. I create a compute shader and program for it, then a vertex shader and fragment shader, as well as a program for them. I then create a texture and two triangles and render the texture on the triangles. So far, I have been unable to get the shaders to do anything properly, since glCompileShader
seems to not catch when shaders compile incorrectly, so I can't tell what's wrong. I have added glGetError
calls after each of my compiling and linking steps to see what the problem might be, but none of them throw errors; all that happens is glUseProgram
raises an invalid operation
error.
renderShaderSource:
#version 460 core
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
layout(rgba32f, binding = 0) uniform image2D screen;
void main()
}
vertexShaderSource:
#version 460 core
layout (location = 0) in vec3 pos;
layout (location = 1) in vec2 uvs;
out vec2 UVs;
void main()
{
gl_Position = vec4(pos.x, pos.y, pos.z, 1.0);
UVs = uvs;
}
fragmentShaderSource:
#version 460 core
out vec4 FragColor;
uniform sampler2D screen;
in vec2 UVs;
void main()
{
FragColor = texture(screen, UVs);
}
Python code (with excess comments and irrelevant code removed):
class GLWidget(QOpenGLWidget):
width = 100
height = 100
def initializeGL(self):
print('--initializeGL--')
print('GL version', glGetString(GL_VERSION))
# Render shader
renderShader = glCreateShader(GL_COMPUTE_SHADER)
glShaderSource(renderShader, renderShaderSource)
glCompileShader(renderShader)
status = glGetError()
if status != GL_NO_ERROR: raise RuntimeError('Error {} compiling renderShader'.format(status))
# Render program
renderProgram = glCreateProgram()
glAttachShader(renderProgram, renderShader)
glLinkProgram(renderProgram)
status = glGetError()
if status != GL_NO_ERROR: raise RuntimeError('Error {} linking renderProgram'.format(status))
# Vertex shader
vertexShader = glCreateShader(GL_VERTEX_SHADER)
glShaderSource(vertexShader, vertexShaderSource)
glCompileShader(vertexShader)
status = glGetError()
if status != GL_NO_ERROR: raise RuntimeError('Error {} compiling vertexShader'.format(status))
# Fragment shader
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER)
glShaderSource(fragmentShader, fragmentShaderSource)
glCompileShader(fragmentShader)
status = glGetError()
if status != GL_NO_ERROR: raise RuntimeError('Error {} compiling fragmentShader'.format(status))
# Display program
displayProgram = glCreateProgram()
glAttachShader(displayProgram, vertexShader)
glAttachShader(displayProgram, fragmentShader)
glLinkProgram(displayProgram)
status = glGetError()
if status != GL_NO_ERROR: raise RuntimeError('Error {} linking displayProgram'.format(status))
# Texture to render to
screenTex = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, screenTex)
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA32F, self.width, self.height)
glBindImageTexture(0, screenTex, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA32F)
# Shape to render texture on
self.vertices = numpy.array([
-1.0, -1.0, 0.0, 0.0, 0.0,
-1.0, 1.0, 0.0, 0.0, 1.0,
1.0, 1.0, 0.0, 1.0, 1.0,
1.0, -1.0, 0.0, 1.0, 0.0
], dtype='float32')
# Buffers for rendering shape
vertexArray = glGenVertexArrays(1)
vertexBuffer = glGenBuffers(1)
glBindVertexArray(vertexArray)
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer)
glBufferData(GL_ARRAY_BUFFER, 4 * len(self.vertices), self.vertices, GL_STATIC_DRAW)
glVertexAttribPointer(0, 3, GL_FLOAT, False, 4 * 3, None)
glEnableVertexAttribArray(0)
# Unbind from the vertex buffer and vertex array
glBindBuffer(GL_ARRAY_BUFFER, 0)
glBindVertexArray(0)
self.renderProgram = renderProgram
self.displayProgram = displayProgram
self.vertexArray = vertexArray
self.vertexBuffer = vertexBuffer
def resizeGL(self, width, height):
print('--resizeGL--')
self.width = width
self.height = height
glViewport(0, 0, width, height)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
glOrtho(-1, 1, 1, -1, -1, 1)
def paintGL(self):
print('--paintGL--')
# Run render portion
glUseProgram(self.renderProgram)
glDispatchCompute(self.width, self.height, 1)
glMemoryBarrier(GL_ALL_BARRIER_BITS)
# Run display portion
glClearColor(0.0, 0.0, 0.0, 1.0)
glClear(GL_COLOR_BUFFER_BIT)
glUseProgram(self.displayProgram)
glBindVertexArray(self.vertexArray)
glDrawArrays(GL_TRIANGLES, 0, 6)
It's fairly clear that my compute shader (see renderShaderSource) is the problem in this scenario, I mean it's got mismatched { } things. But when I run the program, glUseProgram
errors instead of glGetError
:
--initUI--
--initializeGL--
GL version b'4.6 (Compatibility Profile) Mesa 21.2.6'
--resizeGL--
--paintGL--
Traceback (most recent call last):
File "gl.py", line 146, in paintGL
glUseProgram(self.renderProgram)
File "/home/aweso/.local/lib/python3.8/site-packages/OpenGL/platform/baseplatform.py", line 415, in __call__
return self( *args, **named )
File "/home/aweso/.local/lib/python3.8/site-packages/OpenGL/error.py", line 230, in glCheckError
raise self._errorClass(
OpenGL.error.GLError: GLError(
err = 1282,
description = b'invalid operation',
baseOperation = glUseProgram,
cArguments = (2,)
)
--paintGL--
Traceback (most recent call last):
File "gl.py", line 146, in paintGL
glUseProgram(self.renderProgram)
File "/home/aweso/.local/lib/python3.8/site-packages/OpenGL/error.py", line 230, in glCheckError
raise self._errorClass(
OpenGL.error.GLError: GLError(
err = 1282,
description = b'invalid operation',
baseOperation = glUseProgram,
cArguments = (2,)
)
Why did none of the glGetError
calls catch that the compute shader failed to compile?
Python 3.8 on Ubuntu 20.04, Intel Core i3-1005G1 with integrated GPU. OpenGL 4.6 Mesa 21.2.6. OpenGL viewport is a PyQt6 QOpenGLWidget
Shader compile errors can't be get with glGetError
. You have to call glGetShaderiv
/glGetShaderInfoLog
to get the compile errors. e.g.:
glCompileShader(renderShader)
if not glGetShaderiv(renderShader, GL_COMPILE_STATUS):
raise RuntimeError(glGetShaderInfoLog(renderShader).replace(b'\\n', b'\n'))
Program link errors can be get with glGetProgramiv
/glGetProgramInfoLog
. e.g.:
glLinkProgram(displayProgram)
if not glGetProgramiv(displayProgram, GL_LINK_STATUS):
raise RuntimeError(glGetProgramInfoLog(displayProgram).replace(b'\\n', b'\n'))
I also recommend Debug Output. e.g.:
@GLDEBUGPROC
def __CB_OpenGL_DebugMessage(source, type, id, severity, length, message, userParam):
msg = message[0:length]
print(msg.decode("utf-8"))
glDebugMessageCallback(__CB_OpenGL_DebugMessage, None)
glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, None, GL_TRUE)
glEnable(GL_DEBUG_OUTPUT)
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS)