[Bug report]: atomicCompSwap, wrong arguments order.

  1. Summary
  2. Driver version
  3. Example

Notice that I tried to fill in a bug report using the dedicated web page but the web page does not work.

  1. Summary

GLSL Spec (https://www.opengl.org/registry/doc/GLSLangSpec.4.50.diff.pdf page 176) says :
u/int atomicCompSwap(inout u/int mem, u/int compare, u/int data)
Compares the value of “compare” and the contents of “mem”. If the values are equal, the new value is given by “data”; otherwise, it is taken from the original contents of “mem”.
In practice, the two last arguments of atomicCompSwap seems to be swapped:
u/int atomicCompSwap(inout u/int mem, u/int data, u/int compare)

  1. Driver version

System: Linux (with Quadro K420)
Driver version: 367.57 (from debian rep) and 375.20

  1. Example

Here is a simple code to reproduce the problem :
We create a compute shader and change the content of a SSBO (initially filled with 0) using the atomicCompSwap instruction. The content of the SSBO is displayed before and after compute shader execution.

#include <GL/glew.h>
#include <GL/freeglut.h>
#include <iostream>

GLuint compute_program;
GLuint ssbo_id;

struct value {
  GLuint v0[1];
  GLuint v1[1];
};

struct value init_SSBO() {
  struct value p;
  p.v0[0] = 0;
  p.v1[0] = 0;
  return p;
}

void init_buffer() {
  glGenBuffers(1, &ssbo_id);
  glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo_id);
  glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(struct value), 0, GL_DYNAMIC_COPY);
  struct value l = init_SSBO();
  struct value *p = (struct value *) glMapBuffer(GL_SHADER_STORAGE_BUFFER, GL_WRITE_ONLY);
  p[0] = l;
  glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
  glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, ssbo_id);
  glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
}

void display_ssbo() {
  glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo_id);
  GLint *p = (GLint *) glMapBuffer(GL_SHADER_STORAGE_BUFFER, GL_READ_ONLY);
  std::cout << "v0: " << p[0] << " v1: " << p[1] << std::endl;
  glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
}

void test() {
  std::cout << "before\n";
  display_ssbo();

  glUseProgram(compute_program);
  glDispatchCompute(1, 1, 1);
  glMemoryBarrier(GL_ALL_BARRIER_BITS);

  std::cout << "after\n";
  display_ssbo();
}

void init_compute_shaders() {
  GLuint shader_id = glCreateShader(GL_COMPUTE_SHADER);
  std::string shader_src =
    "#version 450\n\
    layout (local_size_x = 1) in;\n\
    coherent layout(std430, binding = 1) buffer test_buffer\n\
    {\n\
    uint v0[1];\n\
    uint v1[1];\n\
    };\n\
    void write_buffer() {\n\
    atomicCompSwap(v0[0], 0, 0, 2);\n\
    atomicCompSwap(v1[0], 0, 3, 0);\n\
    }\n\
    void main() {\n\
    write_buffer();\n\
    }\n";
  const char *src = shader_src.c_str();
  glShaderSource(shader_id, 1, (const GLchar**)&(src), NULL);
  glCompileShader(shader_id);
  compute_program=glCreateProgram();
  glAttachShader(compute_program, shader_id);
  glLinkProgram(compute_program);
  return;
}

void init_gl() {

}

bool init_glew() {
  glewExperimental = GL_TRUE;
  GLenum err = glewInit();
  if (GLEW_OK != err) {
    std::cerr << "GlewInit fails: " << glewGetErrorString(err) << std::endl;
    return false;
  }
  return true;
}

void init_glut(int argc, char **argv) {
  glutInit(&argc, argv);
  glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
  glutInitContextVersion (4, 5);
  glutCreateWindow("Bug atomicCompSwap");
}

int main(int argc, char **argv) {
  init_glut(argc, argv);
  if (!init_glew())
    return -1;
  init_gl();
  init_compute_shaders();
  init_buffer();
  test();
  return 0;
}

A simple Makefile

CC = g++

CXXFLAGS = -std=c++11 -O3 -Wall
LDXXFLAGS = -lGL -lGLEW -lglut -lpthread

DIST_FILE = bug

all:$(OBJ_FILES) main.cpp
	$(CC) main.cpp -o $(DIST_FILE) $(LDXXFLAGS) $(CXXFLAGS)

clean:
	rm -f $(DIST_FILE)

v0 and v1 are filled with 0 and the two following instructions are executed:
1/atomicCompSwap(v0[0], 0, 0, 2);
2/atomicCompSwap(v1[0], 0, 3, 0);

If I am right, the expected output is:
before
v0: 0 v1: 0
after
v0: 2 v1: 0
(according to the spec, only 1/ can write)

The obtained output is:
before
v0: 0 v1: 0
after
v0: 0 v1: 3
(in practice only 2/ succeeded)

Hi,

I moved your thread to the Linux subforum.

atomicCompSwap(v0[0], 0, 0, 2);

I may be wrong, but the spec shows that this function only takes 3 arguments. Why are you giving it 4?

Hi,
Thank you for your reply.
You are right, my additional argument is the origin of the issue.
Sorry for the inconvenience
J.F.
(I fail to reproduce the situation that leads me to add this argument.)

Our GLSL compiler should probably have error out. We’re tracking that in bug 1849013.

Is there a way to follow this bug or is it private ?

It is private.