我正在尝试在OpenGL中构建计算着色器执行骨架化算法。我已经在仅CPU版本中测试了该算法,在那儿是正确的。但是,在将其移植到计算着色器代码时遇到了一些麻烦。
问题是,无论我运行了多少次计算着色器,输出都不会在第一次调用后发生变化。实际上,如果我在while循环结束时取出检查,该程序将永远不会终止。
我有两个存储区域,可用于输入和输出。我正在尝试在主while循环中使用glBindBufferBase()进行技巧操作,在其中我将其中的两个交换(上一轮的输出成为本轮的输入)。参见main.cpp中的第270-318行。这样一来,我就不会多次在CPU和GPU之间来回复制数据。
我还重新设计了此版本以将IMG_1换回数据,然后使用以下命令调用glBufferData数据。它可以正常工作,但是执行所有这些CPU <-> GPU操作似乎效率很低。
所以,我的问题是:
1)我可以使用glBindBuffers来完成此技巧吗?我可以在其中交换它们,以便可以多次处理数据而无需移动数据回到CPU?在测试较小的问题(仅添加短数组)时,它奏效了。
2)如果技巧很好,那我在哪里出错了?
注意:此代码需要一个称为“ test.pgm”的640 x 400大小的.pgm(黑白图像)。您可以在GIMP中制作一个,但请确保将其另存为二进制而不是ASCII。
此代码是使用以下标志编译的:
g++ -g pgm.cpp main.cpp -lglut -lGLU -lGL -lm -lGLEW -o test
另外,请原谅我使用C ++但做了C风格的技巧。我在C上花费的时间比在C ++中花费的时间多。
main.cpp
// Include standard headers
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <math.h>
#include <errno.h>
//For stat
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
// Include GLEW
#include <GL/glew.h>
//Glut
#include <GL/glut.h>
//Project specific
#include "skeletonize.hpp"
#include "PGM.hpp"
// OpenGL shader info
GLuint programID;
GLuint output_image;
#define IMG_0 0
#define IMG_1 1
#define CMD 2
#define NUM_BUFS 3
#define CMD_BUF_WIDTH 0
#define CMD_BUF_HEIGHT 1
#define CMD_BUF_CMD 2
#define CMD_BUF_RESPONSE 3
#define CMD_BUF_LEN 4
#define CMD_EXPAND 1
#define CMD_THIN_N 2
#define CMD_THIN_S 3
#define CMD_THIN_E 4
#define CMD_THIN_W 5
#define CMD_NORMALIZE 6
#define CMD_REGULARIZE 7
#define INITIALIZED 0
#define NOT_FINISHED 1
GLuint computeProgram;
GLuint buffers[NUM_BUFS]; //SSBO objects, one for IMG_0, one for IMG_1, and one for commands/response
static GLchar* computeSource;
GLuint shaderProgram;
//TODO: don't really need 2 textures yet, but will eventually when doing overlay of original image.
GLuint textures[2];
GLchar* LoadSource(const char* pFile)
{
struct stat buf;
GLchar *source;
int fd;
if (stat(pFile, &buf) == -1)
{
printf("Error opening file\n");
printf("Error: %s\n", strerror(errno));
return NULL;
}
fd = open(pFile, O_RDONLY);
if (fd == -1)
{
printf("Error opening file. Error: %s\n", strerror(errno));
return NULL;
}
source = new GLchar[buf.st_size + 1];
if (read(fd, source, buf.st_size) == -1)
{
printf("Error reading file. Error: %s\n", strerror(errno));
delete[] source;
return NULL;
}
source[buf.st_size] = '#version 450 core
//#extension GL_NV_shader_buffer_load : enable
#define WIDTH 0 // Width of image
#define HEIGHT 1 // Height of image
#define CMD 2 // Command to execute
#define RESPONSE 3 // Response to command
#define BUF_LEN 4
#define CMD_UNUSED 1 // TODO: remove this. Will have to be mirroed in C code.
#define CMD_THIN_N 2
#define CMD_THIN_S 3
#define CMD_THIN_E 4
#define CMD_THIN_W 5
#define CMD_NORMALIZE 6
#define CMD_REGULARIZE 7
#define NOT_FINISHED 1
layout (local_size_x = 16, local_size_y = 16) in;
//layout (local_size_x = 1) in; //TODO: remove
layout (std430, binding = 0) buffer Cmd {
uint cmd_buf[BUF_LEN]; //Width, height, command, response
};
layout (std430, binding = 1) buffer Img1 {
uint image_0[];
};
layout (std430, binding = 2) buffer Img2 {
uint image_1[];
};
int sigma(uint data[9]) {
int i;
int sigma = 0;
// Assume 9 pixels, A0 (pixel of interest) -> A8
// In image, A0 is center
// 1 2 3
// 8 0 4
// 7 6 5
for (i=1; i < 9; i++)
{
sigma += int(data[i]);
}
return sigma;
}
int chi(uint data[9]) {
int chi;
// Assume 9 pixels, A0 (pixel of interest) -> A8
// 1 2 3
// 8 0 4
// 7 6 5
chi = int(data[1] != data[3]) +
int(data[3] != data[5]) +
int(data[5] != data[7]) +
int(data[7] != data[1]) +
2 * ( int((data[2] > data[1]) && (data[2] > data[3])) ) +
int((data[4] > data[3]) && (data[4] > data[5])) +
int((data[6] > data[5]) && (data[6] > data[7])) +
int((data[8] > data[7]) && (data[8] > data[1]));
return chi;
}
// 1 2 3
// 8 0 4
// 7 6 5
int getPos(in int x, int y) {
return y * int(cmd_buf[WIDTH]) + x;
}
uint getVal(in int pos) {
return image_0[ uint(pos) ];
}
int removePoint(uint neighborhood[9]) {
int x = int(gl_GlobalInvocationID.x);
int y = int(gl_GlobalInvocationID.y);
if (chi(neighborhood) == 2 && sigma(neighborhood) != 1) {
image_1[getPos(x, y)] = 0;
cmd_buf[RESPONSE] = NOT_FINISHED;
return 1;
}
else
{
//TODO: needed? Swapping back and forth between input and output should account for this
image_1[getPos(x,y)] = 1;
}
return 0;
}
void getNeighborhood(inout uint neighborhood[9]) {
int x = int(gl_GlobalInvocationID.x);
int y = int(gl_GlobalInvocationID.y);
int bottom = int(cmd_buf[WIDTH] * (cmd_buf[HEIGHT] - 1));
int pos = getPos(x, y);
int width = int(cmd_buf[WIDTH]);
int height = int(cmd_buf[HEIGHT]);
uint pixel;
int i = 0;
for (i=1; i < 9; i++) {
neighborhood[i] = 2;
}
if (pos < width) {
// Pixel on top, fill outiside image with zero
neighborhood[1] = 0;
neighborhood[2] = 0;
neighborhood[3] = 0;
}
if (pos % width == 0) {
// Pixel is on left edge. Fill area outside of image with zero
neighborhood[1] = 0;
neighborhood[8] = 0;
neighborhood[7] = 0;
}
if ((pos % width) == (width - 1)) {
// Pixel is on right edge.
neighborhood[3] = 0;
neighborhood[4] = 0;
neighborhood[5] = 0;
}
if (pos >= bottom) {
// Pixel is on bottom edge.
neighborhood[5] = 0;
neighborhood[6] = 0;
neighborhood[7] = 0;
}
// Get remaining pixels
for (i=1; i < 9; i++) {
if (neighborhood[i] == 2) {
switch (i) {
case 1:
// Upper left pixel
neighborhood[i] = getVal(pos - 1 - width);
break;
case 2:
// Upper middle pixel
neighborhood[i] = getVal(pos - width);
break;
case 3:
// Upper right pixel
neighborhood[i] = getVal(pos + 1 - width);
break;
case 4:
// Right pixel
neighborhood[i] = getVal(pos + 1);
break;
case 5:
// Bottom right pixel
neighborhood[i] = getVal(pos + width + 1);
break;
case 6:
// Bottom middle pixel
neighborhood[i] = getVal(pos + width);
break;
case 7:
// Bottom left pixel
neighborhood[i] = getVal(pos + width - 1);
break;
case 8:
// Left pixel
neighborhood[i] = getVal(pos - 1);
break;
}
}
}
}
void normalize() {
int x = int(gl_GlobalInvocationID.x);
int y = int(gl_GlobalInvocationID.y);
uint val = image_0[getPos(x, y)] == 0x0 ? 0 : 1;
image_0[getPos(x, y)] = val;
image_1[getPos(x, y)] = val;
}
void regularize() {
int x = int(gl_GlobalInvocationID.x);
int y = int(gl_GlobalInvocationID.y);
uint val = image_0[getPos(x, y)] == 0x0 ? 0 : 0xFFFFFFFF;
if (val != 0xFFFFFFFF)
{
cmd_buf[RESPONSE] = 99; //Test Value -- TODO: remove
}
image_1[getPos(x, y)] = val;
}
// North-South-East-West skeletonization
void skeleton() {
int x = int(gl_GlobalInvocationID.x);
int y = int(gl_GlobalInvocationID.y);
uint neighborhood[9];
neighborhood[0] = getVal(getPos(x, y));
// Only consider cases where the center is 1
if (neighborhood[0] != 1) {
return;
}
getNeighborhood(neighborhood);
switch (cmd_buf[CMD]) {
case CMD_THIN_N:
//north
if (neighborhood[2] == 0 && neighborhood[6] == 1) {
removePoint(neighborhood);
}
break;
case CMD_THIN_S:
//south
if (neighborhood[2] == 1 && neighborhood[6] == 0) {
removePoint(neighborhood);
}
break;
case CMD_THIN_E:
//east
if (neighborhood[4] == 0 && neighborhood[8] == 1) {
removePoint(neighborhood);
}
break;
case CMD_THIN_W:
//west
if (neighborhood[4] == 1 && neighborhood[8] == 0) {
removePoint(neighborhood);
}
break;
}
}
void main() {
switch (cmd_buf[CMD]) {
case CMD_THIN_N:
case CMD_THIN_S:
case CMD_THIN_E:
case CMD_THIN_W:
skeleton();
break;
case CMD_NORMALIZE:
normalize();
break;
case CMD_REGULARIZE:
regularize();
break;
}
}
'; //Shader compiler needs null to know end of input
return source;
}
// Shader sources
const GLchar* vertexSource =
"#version 450 core\n"
"in vec2 position;"
"in vec2 texcoord;"
"out vec2 Texcoord;"
"void main()"
"{"
" Texcoord = texcoord;"
" gl_Position = vec4(position, 0.0, 1.0);"
"}";
const GLchar* fragmentSource =
"#version 450 core\n"
"in vec2 Texcoord;"
"out vec4 outColor;"
"uniform sampler2D texData;"
"void main()"
"{"
" vec4 imColor = texture(texData, Texcoord);"
" outColor = vec4(0.0, imColor.r, 0.0, 1.0);"
//" outColor = texture(texData, Texcoord);"
//" outColor = vec4(1.0, 1.0, 0.0, 1.0);"
"}";
void checkError(int line)
{
GLint err;
do
{
err = glGetError();
switch (err)
{
case GL_NO_ERROR:
//printf("%d: No error\n", line);
break;
case GL_INVALID_ENUM:
printf("%d: Invalid enum!\n", line);
break;
case GL_INVALID_VALUE:
printf("%d: Invalid value\n", line);
break;
case GL_INVALID_OPERATION:
printf("%d: Invalid operation\n", line);
break;
case GL_INVALID_FRAMEBUFFER_OPERATION:
printf("%d: Invalid framebuffer operation\n", line);
break;
case GL_OUT_OF_MEMORY:
printf("%d: Out of memory\n", line);
break;
default:
printf("%d: glGetError default case. Should not happen!\n", line);
}
} while (err != GL_NO_ERROR);
}
void display()
{
glClearColor(0.0f, 0.0f, 1.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glFlush();
glutSwapBuffers();
}
void reshape(int width,int height)
{
double w2h = (height>0) ? (double)width/height : 1;
// Set viewport as entire window
glViewport(0,0, width,height);
}
void runComputeProgram(uint32_t *data, uint32_t *data2)
{
int width = 640;
int height = 400;
uint32_t *ptr;
uint32_t cmd[CMD_BUF_LEN];
computeSource = LoadSource("compute.shader");
if (computeSource == NULL)
{
return;
}
GLuint computeShader = glCreateShader(GL_COMPUTE_SHADER);
glShaderSource(computeShader, 1, &computeSource, NULL);
glCompileShader(computeShader);
computeProgram = glCreateProgram();
glAttachShader(computeProgram, computeShader);
glLinkProgram(computeProgram);
GLint status;
glGetProgramiv(computeProgram, GL_LINK_STATUS, &status);
if (status == GL_TRUE)
{
printf("link good\n");
}
else
{
printf("link bad\n");
GLchar log[4096];
GLsizei len;
glGetProgramInfoLog(computeProgram, 4096, &len, log);
printf("%s\n", log);
return;
}
// First action is to transform the image into binary values (0, 1)
cmd[CMD_BUF_CMD] = CMD_NORMALIZE;
cmd[CMD_BUF_WIDTH] = width;
cmd[CMD_BUF_HEIGHT] = height;
glGenBuffers(NUM_BUFS, buffers);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, buffers[CMD]);
glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(cmd), cmd, GL_DYNAMIC_DRAW);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, buffers[IMG_0]);
glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(uint32_t) * width * height, data, GL_DYNAMIC_DRAW);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, buffers[IMG_1]);
glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(uint32_t) * width * height, data2, GL_DYNAMIC_DRAW);
glUseProgram(computeProgram);
glDispatchCompute(width / 16, height / 16, 1);
glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, buffers[IMG_1]);
ptr = (uint32_t *)glMapBuffer(GL_SHADER_STORAGE_BUFFER, GL_READ_ONLY);
glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
// Rebind ptr for our while loop
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, buffers[CMD]);
//glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(cmd), cmd, GL_DYNAMIC_DRAW);
ptr = (uint32_t *)glMapBuffer(GL_SHADER_STORAGE_BUFFER, GL_READ_ONLY);
int i = 0;
do
{
printf("iteration: %d", i);
glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
cmd[CMD_BUF_RESPONSE] = INITIALIZED;
switch (i % 4)
{
case 0:
cmd[CMD_BUF_CMD] = CMD_THIN_N;
break;
case 1:
cmd[CMD_BUF_CMD] = CMD_THIN_S;
break;
case 2:
cmd[CMD_BUF_CMD] = CMD_THIN_E;
break;
case 3:
cmd[CMD_BUF_CMD] = CMD_THIN_W;
break;
}
glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(cmd), cmd, GL_DYNAMIC_DRAW);
glDispatchCompute(width / 16, height / 16, 1);
glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
if (i % 2 == 0)
{
printf("Input is now img_1. Output is img_0\n");
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, buffers[IMG_1]);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, buffers[IMG_0]);
checkError(__LINE__);
}
else
{
printf("Input is now img_0. Output is img_1\n");
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, buffers[IMG_0]);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, buffers[IMG_1]);
checkError(__LINE__);
}
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, buffers[CMD]);
ptr = (uint32_t *)glMapBuffer(GL_SHADER_STORAGE_BUFFER, GL_READ_ONLY);
printf("cmd issued at start: %d response: %d\n", ptr[CMD_BUF_CMD], ptr[CMD_BUF_RESPONSE]);
i++;
} while(ptr[CMD_BUF_RESPONSE] != INITIALIZED && i < 10); //Using i < 10, otherwise this never terminates
glUnmapBuffer(GL_SHADER_STORAGE_BUFFER); // Free ptr
// Transform Binary image (0, 1) to (0, 0xFFFFFFFF) values for texture display
cmd[CMD_BUF_CMD] = CMD_REGULARIZE;
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, buffers[CMD]);
glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(cmd), cmd, GL_DYNAMIC_DRAW);
glDispatchCompute(width / 16, height / 16, 1);
glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, buffers[CMD]);
ptr = (uint32_t *)glMapBuffer(GL_SHADER_STORAGE_BUFFER, GL_READ_ONLY);
printf("Regularize: cmd: %d width: %d height: %d response: %d\n", ptr[CMD_BUF_CMD], ptr[CMD_BUF_WIDTH], ptr[CMD_BUF_HEIGHT], ptr[CMD_BUF_RESPONSE]);
// Create texure
glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
glGenTextures(2, textures);
checkError(__LINE__);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, textures[0]);
checkError(__LINE__);
if (i % 2 == 0)
{
printf("output image is img_1\n");
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, buffers[IMG_1]);
ptr = (uint32_t *)glMapBuffer(GL_SHADER_STORAGE_BUFFER, GL_READ_ONLY);
}
else
{
printf("output image is img_0\n");
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, buffers[IMG_0]);
ptr = (uint32_t *)glMapBuffer(GL_SHADER_STORAGE_BUFFER, GL_READ_ONLY);
}
glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
glUseProgram(shaderProgram);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RED, GL_UNSIGNED_INT, ptr); //TODO: this is wrong. worry about later.
checkError(__LINE__);
glUniform1i(glGetUniformLocation(shaderProgram, "texData"), 0);
checkError(__LINE__);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
checkError(__LINE__);
}
void initGL()
{
// Vertices & texture init
GLuint vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
GLuint vbo;
glGenBuffers(1, &vbo);
GLfloat vertices[] = {
// X Y S T
-1.0f, 1.0f, 0.0f, 0.0f, // Top-left
1.0f, 1.0f, 1.0f, 0.0f, // Top-right
1.0f, -1.0f, 1.0f, 1.0f, // Bottom-right
-1.0f, -1.0f, 0.0f, 1.0f // Bottom-left
};
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
GLuint ebo;
glGenBuffers(1, &ebo);
GLuint elements[] = {
0, 1, 2,
2, 3, 0
};
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(elements), elements, GL_STATIC_DRAW);
// Create shaders
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexSource, NULL);
glCompileShader(vertexShader);
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentSource, NULL);
glCompileShader(fragmentShader);
shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glBindFragDataLocation(shaderProgram, 0, "outColor");
glLinkProgram(shaderProgram);
glUseProgram(shaderProgram);
// Vertex data specification
GLint posAttrib = glGetAttribLocation(shaderProgram, "position");
glEnableVertexAttribArray(posAttrib);
glVertexAttribPointer(posAttrib, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), 0);
GLint texAttrib = glGetAttribLocation(shaderProgram, "texcoord");
glEnableVertexAttribArray(texAttrib);
glVertexAttribPointer(texAttrib, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (void *)(2 * sizeof(GLfloat)));
checkError(__LINE__);
}
int main(int argc, char** argv)
{
// Image setup
PGM pgmImage;
pgmImage.ReadFile("test.pgm");
uint32_t *data = new uint32_t[pgmImage.GetHeight() * pgmImage.GetWidth()];
uint32_t *data2 = new uint32_t[pgmImage.GetHeight() * pgmImage.GetWidth()];
unsigned int size = pgmImage.GetHeight() * pgmImage.GetWidth();
uint8_t *pgmData = pgmImage.GetData();
for (int i=0; i < size; i++)
{
data[i] = pgmData[i];
}
int count = 0;
for (int i =0; i < pgmImage.GetHeight() * pgmImage.GetWidth(); i++)
{
if (data[i] == 0xFF)
{
count++;
}
}
printf("count: %d\n", count);
// Window Setup
glutInitWindowSize(640, 400);
glutInitWindowPosition (140, 140);
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);
glutInit(&argc, argv);
glutCreateWindow( "OpenGL Application" );
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glewExperimental = true;
if (glewInit() != GLEW_OK) {
fprintf(stderr, "Failed to initialize GLEW\n");
return -1;
}
initGL();
runComputeProgram(data, data2);
checkError(__LINE__);
glutMainLoop();
return 0;
}
compute.shader
#include "PGM.hpp"
#define PGM_HEADER "P5"
PGM::PGM()
{
mpData = NULL;
Clear();
}
PGM::~PGM()
{
Clear();
}
uint8_t* PGM::GetData()
{
return mpImgData;
}
uint16_t PGM::GetWidth()
{
return mWidth;
}
uint16_t PGM::GetHeight()
{
return mHeight;
}
uint8_t PGM::GetMaxWhite()
{
return mMaxWhite;
}
void PGM::Clear()
{
if (mpData != NULL)
{
delete[] mpData;
}
mpImgData = NULL;
mWidth = 0;
mHeight = 0;
mMaxWhite = 255;
}
// Finds the
int PGM::PopulateFields(size_t size)
{
int i;
bool EOL = false;
bool haveWhite = false;
bool comment = false;
if (mpData == NULL) { return -1; }
// Check header
if ((mpData[0] != 0x50) || (mpData[1] != 0x35)) { return -2; }
//Ignore the comment
//Start at 3rd position in file, after "P5" header
for (i = 2; i < size; i++)
{
if (mpData[i] == '#')
{
comment = true;
continue;
}
if (mpData[i] == 0x0A && comment == true)
{
comment = false;
break;
}
if (comment == true)
{
continue;
}
}
// Get width and height
i++;
sscanf((char *)&mpData[i], "%4" SCNu16 " %4" SCNu16, &mWidth, &mHeight);
for (i; i < size; i++)
{
//Move past the width and height we just found
if (mpData[i] == 0x0A && EOL == false)
{
EOL = true;
continue;
}
// If past the width and height, now at the range. Save it.
if (EOL == true && haveWhite == false)
{
sscanf((char *)&mpData[i], "%3" SCNu8, &mMaxWhite);
haveWhite = true;
}
if (haveWhite == true && mpData[i] == 0x0A)
{
i++; //Move to next element - start of the actual data
break;
}
}
if (i == size)
{
return -3; //Did not find the start of data.
}
mpImgData = &mpData[i];
return 0;
}
// Reads a PGM file. Returns 0 on success, other values on failure
int PGM::ReadFile(const char *pPath)
{
struct stat st;
int fd;
if (this->mpData != NULL)
{
Clear();
}
if (stat(pPath, &st) != 0)
{
return 1;
}
fd = open(pPath, O_RDONLY);
if (fd == -1)
{
return 1;
}
//this->mpData = (uint8_t *) malloc(st.st_size);
mpData = new uint8_t[st.st_size];
if (this->mpData == NULL)
{
return 2;
}
if (read(fd, this->mpData, st.st_size) == -1)
{
Clear();
}
close(fd);
PopulateFields(st.st_size);
return 0;
}
pgm.cpp
#ifndef __PGM_H__
#define __PGM_H__
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <inttypes.h>
class PGM
{
public:
int ReadFile(const char *pPath);
uint8_t* GetData();
uint16_t GetWidth();
uint16_t GetHeight();
uint8_t GetMaxWhite();
PGM();
~PGM();
private:
void Clear();
int PopulateFields(size_t size);
uint8_t *mpData;
uint8_t *mpImgData;
uint16_t mWidth;
uint16_t mHeight;
uint8_t mMaxWhite;
};
#endif // __PGM_H__
pgm.hpp
q4312078q
#1 楼
我找到了!问题源于交换中的问题。当我们交换缓冲区时,输出缓冲区成为输入,而输入缓冲区成为输出。但是,现在作为输出的缓冲区未更新为匹配!To illustrate:
Init:
In Out
1 1 1 1 1 1
1 1 1 1 1 1
1 1 1 1 1 1
First Iteration (remove north corners):
In Out
1 1 1 0 1 0
1 1 1 1 1 1
1 1 1 1 1 1
Swap
In Out
0 1 0 1 1 1
1 1 1 1 1 1
1 1 1 1 1 1
Second iteration (remove east corners):
In Out
0 1 0 1 1 1
1 1 1 1 1 1
1 1 1 1 1 0
... (and so on)
通过在计算着色器中修改skeleton()来将image_0复制到image_1,然后进行任何操作否则,该算法将正常工作。
void skeleton() {
int x = int(gl_GlobalInvocationID.x);
int y = int(gl_GlobalInvocationID.y);
uint neighborhood[9];
neighborhood[0] = getVal(getPos(x, y));
image_1[getPos(x, y)] = image_0[getPos(x, y)];
// Only consider cases where the center is 1
if (neighborhood[0] != 1) {
image_1[0] = 0;
return;
}
...
另外,我还清理了很多代码。例如,您可以在调度调用之前通过glBindBufferBase()设置输入和输出缓冲区。
评论
将所有数据保留在GPU上,并在执行操作时换入/换出缓冲区应该没问题。它称为“乒乓”,是图形中非常普遍的技术。我无法在您的代码中发现该错误,但是您的计算着色器非常复杂。我建议注释掉所有内容,除了对缓冲区的简单写操作外,看看可以使它工作。然后逐步将其重新构建起来,并在进行过程中对每块进行测试。这应该有助于缩小问题的范围。感谢您的关注!我将对其进行更多调查。我认为这是一个交换问题,因为如果我在每次迭代后打印出0x1的像素数,它就会变成(组成数字)1000、900、860、890、860、890、860、890。但是,如果我只需一遍又一遍地运行开关外壳的一部分,它就会单调减少直到不再。很高兴知道我对乒乓球并不疯狂! :)
有关信息:从堆栈溢出中交叉发布