// ARB_compute_shader shared[] array test case.
//
// When using arrays of shared variables, index expressions involving
// gl_LocalInvocationID lead to stores not being observed by subsequent
// loads to the same location.
#include <GL/glew.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef __linux__
# include <GL/glx.h>
# include <X11/Xlib.h>
#endif
#ifdef _WIN32
# include <Windows.h>
#endif
#define mChkGL(X) X; CheckGLError(__LINE__);
const GLchar *ComputeCode =
// This shader should write 1 to the second element of sharedBuf,
// then read that value back and write it to OutputBuffer.
// There is 1 work group of size 1. The code is guarded so that
// only gl_LocationInvocationID.x == 0 executes the test.
"#version 430 core \n"
" \n"
"layout(std430, binding = 0) buffer Output { int OutputBuffer[1]; }; \n"
"shared int sharedBuf[2]; \n"
" \n"
"layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; \n"
"void main() { \n"
" if(gl_LocalInvocationID.x == 0) { \n"
" /* Initialize second element with 0. */ \n"
" sharedBuf[1] = 0; \n"
" \n"
// Uncomment the following line to make validation pass.
// " sharedBuf[1] = 1; \n"
" \n"
" /* This store to the second element is not seen by the load. */ \n"
" sharedBuf[1 + gl_LocalInvocationID.x] = 1; \n"
" \n"
" /* Copy second element out for validation. */ \n"
" OutputBuffer[0] = sharedBuf[1]; \n"
" } \n"
"} \n"
;
void CheckGLError(int line) {
GLint glErr = glGetError();
if(glErr != GL_NO_ERROR) {
printf("OpenGL error %d at line %d\n", glErr, line);
exit(1);
}
}
int main() {
// Minimal OpenGL context setup.
#ifdef __linux__
Display *display = XOpenDisplay(NULL);
Window window = XCreateSimpleWindow(display, DefaultRootWindow(display), 0, 0, 1, 1, 0, 0, 0);
int visual[] = { GLX_RGBA, 0 };
XVisualInfo *vInfo = glXChooseVisual(display, DefaultScreen(display), visual);
GLXContext glCtx = glXCreateContext(display, vInfo, NULL, 1);
glXMakeCurrent(display, window, glCtx);
#endif
#ifdef _WIN32
HWND window = CreateWindow(L"edit", 0, 0, 0, 0, 1, 1, NULL, NULL, NULL, NULL);
HDC dc = GetDC(window);
PIXELFORMATDESCRIPTOR pfd;
memset(&pfd, 0, sizeof(pfd));
pfd.dwFlags = PFD_SUPPORT_OPENGL;
SetPixelFormat(dc, ChoosePixelFormat(dc, &pfd), &pfd);
HGLRC glCtx = wglCreateContext(dc);
wglMakeCurrent(dc, glCtx);
#endif
glewInit();
// Allocate buffer for both programs.
GLuint buffer;
mChkGL(glGenBuffers(1, &buffer));
mChkGL(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, buffer));
mChkGL(glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(int), NULL, GL_DYNAMIC_DRAW));
// Build compute shader.
GLuint shader = glCreateShader(GL_COMPUTE_SHADER);
mChkGL(glShaderSource(shader, 1, &ComputeCode, NULL));
mChkGL(glCompileShader(shader));
GLint compileLogSize;
mChkGL(glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &compileLogSize));
if(compileLogSize > 0) {
char *compileLog = new char[compileLogSize];
mChkGL(glGetShaderInfoLog(shader, compileLogSize, NULL, compileLog));
printf("%s", compileLog);
}
// Build compute program.
GLuint program = glCreateProgram();
mChkGL(glAttachShader(program, shader));
mChkGL(glLinkProgram(program));
GLint linkLogSize;
mChkGL(glGetProgramiv(program, GL_INFO_LOG_LENGTH, &linkLogSize));
if(linkLogSize > 0) {
char *linkLog = new char[linkLogSize];
mChkGL(glGetProgramInfoLog(program, linkLogSize, NULL, linkLog));
printf("%s", linkLog);
}
// Invoke compute program and check result.
mChkGL(glClearBufferData(GL_SHADER_STORAGE_BUFFER, GL_R32UI, GL_RED, GL_INT, NULL));
mChkGL(glUseProgram(program));
mChkGL(glDispatchCompute(1, 1, 1));
mChkGL(glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT));
int *bufferData = (int *)glMapBuffer(GL_SHADER_STORAGE_BUFFER, GL_READ_ONLY);
int bufferVal = *bufferData;
mChkGL(glUnmapBuffer(GL_SHADER_STORAGE_BUFFER));
printf("Validation %s\n", (bufferVal == 1) ? "PASSED" : "FAILED");
}