Я пытаюсь использовать ядро CUDA для изменения текстуры OpenGL, но у меня возникла странная проблема, когда мои вызовы surf2Dwrite()
, кажется, смешиваются с предыдущим содержимым текстуры, как вы можете видеть на изображении ниже.Деревянная текстура сзади - это то, что находится в текстуре, прежде чем модифицировать ее с моим ядром CUDA.Ожидаемый результат будет включать ТОЛЬКО цветовые градиенты, а не текстуру дерева за ним.Я не понимаю, почему происходит это смешивание.
Возможные проблемы / недоразумения
Я новичок в CUDA иOpenGL.Здесь я попытаюсь объяснить процесс мышления, который привел меня к этому коду:
- Я использую
cudaArray
для доступа к текстуре (а не, например, к массиву с плавающей точкой), потому что я читаючто лучше читать локальность кэша при чтении / записи текстуры. - Я использую поверхности, потому что где-то читал, что это единственный способ изменить
cudaArray
- Я хотел использовать поверхностьобъекты, которые я понимаю, как новый способ ведения дел.Старый способ - использовать поверхностные ссылки.
Некоторые возможные проблемы с моим кодом, которые я не знаю, как проверять / проверять:
- Я не согласен сформаты изображений?Может я где-то не указывал правильное количество бит / канал?Может быть, я должен использовать
float
с вместо unsigned char
с?
Сводка кода
Вы можете найти полный минимальный рабочий пример в этом GitHub Gist ,Это довольно долго из-за всех движущихся частей, но я постараюсь подвести итог.Я приветствую предложения о том, как сократить MWE.Общая структура выглядит следующим образом:
- создать текстуру OpenGL из локально сохраненного файла
- зарегистрировать текстуру в CUDA, используя
cudaGraphicsGLRegisterImage()
- call
cudaGraphicsSubResourceGetMappedArray()
чтобы получить cudaArray
, представляющий текстуру - , создайте
cudaSurfaceObject_t
, который я могу использовать для записи в cudaArray
- передачи объекта поверхности ядру, которое пишет в текстурус
surf2Dwrite()
- используйте текстуру для рисования прямоугольника на экране
Создание текстуры OpenGL
Я новичок в OpenGL, поэтому я используюРаздел «Текстуры» учебников LearnOpenGL в качестве отправной точки.Вот как я могу установить текстуру (используя библиотеку изображений stb_image.h
)
GLuint initTexturesGL(){
// load texture from file
int numChannels;
unsigned char *data = stbi_load("img/container.jpg", &g_imageWidth, &g_imageHeight, &numChannels, 4);
if(!data){
std::cerr << "Error: Failed to load texture image!" << std::endl;
exit(1);
}
// opengl texture
GLuint textureId;
glGenTextures(1, &textureId);
glBindTexture(GL_TEXTURE_2D, textureId);
// wrapping
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
// filtering
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// set texture image
glTexImage2D(
GL_TEXTURE_2D, // target
0, // mipmap level
GL_RGBA8, // internal format (#channels, #bits/channel, ...)
g_imageWidth, // width
g_imageHeight, // height
0, // border (must be zero)
GL_RGBA, // format of input image
GL_UNSIGNED_BYTE, // type
data // data
);
glGenerateMipmap(GL_TEXTURE_2D);
// unbind and free image
glBindTexture(GL_TEXTURE_2D, 0);
stbi_image_free(data);
return textureId;
}
CUDA Graphics Interop
После вызова функции выше, я регистрирую текстуру в CUDA:
void initTexturesCuda(GLuint textureId){
// register texture
HANDLE(cudaGraphicsGLRegisterImage(
&g_textureResource, // resource
textureId, // image
GL_TEXTURE_2D, // target
cudaGraphicsRegisterFlagsSurfaceLoadStore // flags
));
// resource description for surface
memset(&g_resourceDesc, 0, sizeof(g_resourceDesc));
g_resourceDesc.resType = cudaResourceTypeArray;
}
Render Loop
В каждом кадре я запускаю следующее, чтобы изменить текстуру и визуализировать изображение:
while(!glfwWindowShouldClose(window)){
// -- CUDA --
// map
HANDLE(cudaGraphicsMapResources(1, &g_textureResource));
HANDLE(cudaGraphicsSubResourceGetMappedArray(
&g_textureArray, // array through which to access subresource
g_textureResource, // mapped resource to access
0, // array index
0 // mipLevel
));
// create surface object (compute >= 3.0)
g_resourceDesc.res.array.array = g_textureArray;
HANDLE(cudaCreateSurfaceObject(&g_surfaceObj, &g_resourceDesc));
// run kernel
kernel<<<gridDim, blockDim>>>(g_surfaceObj, g_imageWidth, g_imageHeight);
// unmap
HANDLE(cudaGraphicsUnmapResources(1, &g_textureResource));
// --- OpenGL ---
// clear
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// use program
shader.use();
// triangle
glBindVertexArray(vao);
glBindTexture(GL_TEXTURE_2D, textureId);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
// glfw: swap buffers and poll i/o events
glfwSwapBuffers(window);
glfwPollEvents();
}
CUDA Kernel
Фактическая CUDAЯдро выглядит следующим образом:
__global__ void kernel(cudaSurfaceObject_t surface, int nx, int ny){
int x = blockIdx.x * blockDim.x + threadIdx.x;
int y = blockIdx.y * blockDim.y + threadIdx.y;
if(x < nx && y < ny){
uchar4 data = make_uchar4(x % 255,
y % 255,
0, 255);
surf2Dwrite(data, surface, x * sizeof(uchar4), y);
}
}