关于原子操作,还是先看一下OpenGL编程指南中的描述:
关于书中的描述个人的理解是:比如原先的操作是取出内存中的数,对数值进行操作,然后写回内存,在这一系列的操作的过程中,一个的线程可能在另一个线程写回内存之前对内存进行了读取,这样它读取的数值是不正确的。原子操作把这一系列的操作作为一个整体,就相当于瞬时完成了一系列的操作,一个线程操作的同时,别的线程没法在一个整体当中插入进来。但是多个线程可能同时读取,原子操作对这方面就是保证重新序列化并依次执行。
修改代码测试一下,先不用原子操作,看一下效果:
#include "vgl.h"
#include "vmath.h"
#include "LoadShaders.h"
#include "vbm.h"
#include <iostream>
using namespace std;
GLuint vbo;
GLuint ProgCompure;
GLuint tex;
GLuint atomic_counter_buffer;
void init()
{
glGenBuffers(1, &vbo);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, vbo);
glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(GLint) * 64, NULL, GL_DYNAMIC_COPY);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, vbo);
GLint *tempP = (GLint *)glMapBuffer(GL_SHADER_STORAGE_BUFFER, GL_WRITE_ONLY);
for (int i = 0; i < 64; ++i)
{
*(tempP++) = 0;
}
glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
// Create the atomic counter buffer
glGenBuffers(1, &atomic_counter_buffer);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, atomic_counter_buffer);
glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(GLuint), NULL, GL_DYNAMIC_COPY);
ShaderInfo csi[] = {
{ GL_COMPUTE_SHADER, "Computer.comp" },
{GL_NONE, NULL}
};
ProgCompure = LoadShaders(csi);
}
void display()
{
glUseProgram(ProgCompure);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, vbo);
// Reset atomic counter
GLuint * data;
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, atomic_counter_buffer);
data = (GLuint *)glMapBuffer(GL_SHADER_STORAGE_BUFFER, GL_WRITE_ONLY);
data[0] = 0;
glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
glDispatchCompute(1, 1, 1);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, vbo);
GLint *tempP = (GLint *)glMapBuffer(GL_SHADER_STORAGE_BUFFER, GL_READ_ONLY);
for (int i = 0; i < 8; ++i){
for (int j = 0; j < 8; ++j)
{
cout << *(tempP++) << " ";
}
cout << endl;
}
glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
}
void idle()
{
static unsigned int Time = GetTickCount();
unsigned int timeNow = GetTickCount();
if (timeNow - Time > 15)
{
Time = timeNow;
glutPostRedisplay();
}
else
{
Sleep(30);
}
}
int main(int argc, char *argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE | GLUT_STENCIL);
glutInitContextProfile(GLUT_CORE_PROFILE);
glutInitContextVersion(4, 3);
glutInitWindowSize(400, 400);
glutCreateWindow("Test OpenGL");
if (glewInit() != 0)
{
cout << "Can not initilizate GLEW" << endl;
return 1;
}
init();
glutIdleFunc(idle);
glutDisplayFunc(display);
glutMainLoop();
return 0;
}
#version 430 core
layout(local_size_x = 4,local_size_y = 4,local_size_z = 4) in;
layout(binding = 0) buffer outBuf
{
int datas[];
}Out;
layout(binding = 0) buffer countBuf
{
int datas[];
}Count;
void main()
{
//获取本地工作组在全局工作组中的索引
uint workGroupIndex = gl_WorkGroupID.z * gl_NumWorkGroups.x * gl_NumWorkGroups.y + gl_WorkGroupID.y * gl_NumWorkGroups.x + gl_WorkGroupID.x;
//一个本地工作组的运行次数
uint localWorkNum = gl_WorkGroupSize.x * gl_WorkGroupSize.y * gl_WorkGroupSize.z;
//当次运行的索引
uint index = workGroupIndex * localWorkNum + gl_LocalInvocationIndex;
Count.datas[0] = Count.datas[0] +1;
Out.datas[index] = Count.datas[0];
}
输出如图,计算着色器的多个工作组读的是同一个值,然后加1:
接着用原子操作看一下效果:
#include "vgl.h"
#include "vmath.h"
#include "LoadShaders.h"
#include "vbm.h"
#include <iostream>
using namespace std;
GLuint vbo;
GLuint ProgCompure;
GLuint tex;
GLuint atomic_counter_buffer;
void init()
{
glGenBuffers(1, &vbo);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, vbo);
glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(GLint) * 64, NULL, GL_DYNAMIC_COPY);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, vbo);
GLint *tempP = (GLint *)glMapBuffer(GL_SHADER_STORAGE_BUFFER, GL_WRITE_ONLY);
for (int i = 0; i < 64; ++i)
{
*(tempP++) = 0;
}
glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
// Create the atomic counter buffer
glGenBuffers(1, &atomic_counter_buffer);
glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, atomic_counter_buffer);
glBufferData(GL_ATOMIC_COUNTER_BUFFER, sizeof(GLuint), NULL, GL_DYNAMIC_COPY);
ShaderInfo csi[] = {
{ GL_COMPUTE_SHADER, "Computer.comp" },
{GL_NONE, NULL}
};
ProgCompure = LoadShaders(csi);
}
void display()
{
glUseProgram(ProgCompure);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, vbo);
// Reset atomic counter
GLuint * data;
glBindBufferBase(GL_ATOMIC_COUNTER_BUFFER, 1, atomic_counter_buffer);
data = (GLuint *)glMapBuffer(GL_ATOMIC_COUNTER_BUFFER, GL_WRITE_ONLY);
data[0] = 0;
glUnmapBuffer(GL_ATOMIC_COUNTER_BUFFER);
glDispatchCompute(1, 1, 1);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, vbo);
GLint *tempP = (GLint *)glMapBuffer(GL_SHADER_STORAGE_BUFFER, GL_READ_ONLY);
for (int i = 0; i < 8; ++i){
for (int j = 0; j < 8; ++j)
{
cout << *(tempP++) << " ";
}
cout << endl;
}
glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
}
void idle()
{
static unsigned int Time = GetTickCount();
unsigned int timeNow = GetTickCount();
if (timeNow - Time > 15)
{
Time = timeNow;
glutPostRedisplay();
}
else
{
Sleep(30);
}
}
int main(int argc, char *argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE | GLUT_STENCIL);
glutInitContextProfile(GLUT_CORE_PROFILE);
glutInitContextVersion(4, 3);
glutInitWindowSize(400, 400);
glutCreateWindow("Test OpenGL");
if (glewInit() != 0)
{
cout << "Can not initilizate GLEW" << endl;
return 1;
}
init();
glutIdleFunc(idle);
glutDisplayFunc(display);
glutMainLoop();
return 0;
}
#version 430 core
layout(local_size_x = 4,local_size_y = 4,local_size_z = 4) in;
layout(binding = 0) buffer outBuf
{
int datas[];
}Out;
layout (binding = 1, offset = 0) uniform atomic_uint TessCoord;
void main()
{
//获取本地工作组在全局工作组中的索引
uint workGroupIndex = gl_WorkGroupID.z * gl_NumWorkGroups.x * gl_NumWorkGroups.y + gl_WorkGroupID.y * gl_NumWorkGroups.x + gl_WorkGroupID.x;
//一个本地工作组的运行次数
uint localWorkNum = gl_WorkGroupSize.x * gl_WorkGroupSize.y * gl_WorkGroupSize.z;
//当次运行的索引
uint index = workGroupIndex * localWorkNum + gl_LocalInvocationIndex;
Out.datas[index] = int(atomicCounterIncrement(TessCoord));
}
现在就能保证正确的累加了。