I'm generating noise into 3D texture in compute shader and then building mesh out of it on CPU. It works fine when I do that in the main loop, but I noticed that I'm only getting ~1% of noise filled on the first render. Here is minimal example, where I'm trying to fill 3D texture with ones in shader, but getting zeroes or noise in return:
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <stdio.h>
#include <stdlib.h>
void error(const char* message) { ... }
const char* slurp_file(const char* filename) { ... }
int main() {
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
GLFWwindow* window = glfwCreateWindow(100, 100, "", NULL, NULL);
glfwMakeContextCurrent(window);
gladLoadGL();
// create texture
GLuint texture;
glGenTextures(1, &texture);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_3D, texture);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
// allocate it
size_t size = 4;
glTexImage3D(GL_TEXTURE_3D, 0, GL_R32F, size, size, size, 0, GL_RED, GL_FLOAT, NULL);
// create shader
const char* shader_source = slurp_file("shaders/example.comp");
GLuint shader = glCreateShader(GL_COMPUTE_SHADER);
glShaderSource(shader, 1, &shader_source, NULL);
glCompileShader(shader);
// check it
int code;
glGetShaderiv(shader, GL_COMPILE_STATUS, &code);
if (!code) error("shader compilation failed");
// create program
int program = glCreateProgram();
glAttachShader(program, shader);
glLinkProgram(program);
// check it
glGetProgramiv(program, GL_LINK_STATUS, &code);
if (!code) error("program linkage failed");
// run shader
glUseProgram(program);
// i don't think it's needed, as 0 would be a default value
int uniform_loc = glGetUniformLocation(program, "img_output");
glUniform1i(uniform_loc, 0);
// dispatch and sync
glDispatchCompute(size, size, size);
// originally i had GL_TEXTURE_FETCH_BARRIER_BIT here
glMemoryBarrier(GL_ALL_BARRIER_BITS);
// read texture data
float* data = malloc(size * size * size * sizeof(float));
glBindImageTexture(0, texture, 0, GL_TRUE, 0, GL_READ_WRITE, GL_R32F);
glGetTexImage(GL_TEXTURE_3D, 0, GL_RED, GL_FLOAT, data);
// prints 0s or noise, but i expect all 1s
for (int i = 0; i < size * size * size; i++)
printf("%f ", data[i]);
}
Here is the shader:
#version 430
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
layout(binding = 0, r32f) writeonly uniform image3D img_output;
void main() {
ivec3 texture_coords = ivec3(gl_GlobalInvocationID.xyz);
vec4 pixel = vec4(1, 0, 0, 0);
imageStore(img_output, texture_coords, pixel);
}
Just adding sleep after glDispatchCompute
does nothing, so I think I'm missing some locking? Or maybe texture should be configured in some other way?
You need to bind the texture to the image unit before executing the compute shader. The binding between the texture object and the shader is established through the texture image unit. The shader knows the unit because you set the unit variable or specify the binding point with a layout qualifier, but you also need to bind the object to the unit:
glBindImageTexture(0, texture, 0, GL_TRUE, 0, GL_READ_WRITE, GL_R32F);
glDispatchCompute(size, size, size);