[英]CUDA/OpenGL Interop: Writing to surface object does not erase previous contents
我試圖使用CUDA內核修改OpenGL紋理,但是遇到一個奇怪的問題,我對surf2Dwrite()
調用似乎與紋理的先前內容混合在一起,如下圖所示。 背面的木質紋理是使用我的CUDA內核修改之前的紋理。 預期的輸出將僅包括顏色漸變,而不包括其后面的木材紋理。 我不明白為什么會這樣混合。
我是CUDA和OpenGL的新手。 在這里,我將解釋導致我編寫此代碼的思考過程:
cudaArray
來訪問紋理(而不是例如浮點數數組),因為我讀到,讀取/寫入紋理時,緩存局部性更好。 cudaArray
的唯一方法 我不知道如何檢查/測試的代碼可能存在的一些問題:
float
而不是unsigned char
? 您可以在此GitHub Gist中找到完整的最低限度的工作示例 。 由於所有的活動部件,時間很長,但我將嘗試進行總結。 我歡迎有關如何縮短MWE的建議。 總體結構如下:
cudaGraphicsGLRegisterImage()
向CUDA注冊紋理 cudaGraphicsSubResourceGetMappedArray()
以獲取表示紋理的cudaArray
cudaSurfaceObject_t
,我可以用它來寫cudaArray
surf2Dwrite()
寫入紋理 我是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注冊了紋理:
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;
}
在每一幀中,我運行以下命令來修改紋理並渲染圖像:
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內核如下:
__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);
}
}
如果我理解正確,則首先注冊紋理,將其映射一次,為表示映射紋理的數組創建一個表面對象,然后取消映射紋理。 在每一幀中,您都再次映射資源,要求提供代表映射紋理的數組,然后完全忽略該幀,並使用為您第一次映射資源時返回的數組創建的表面對象。 從文檔中 :
[…]每次映射
resource
時,array
設置的值可能會更改。
每次映射資源時,您都必須創建一個新的表面對象,因為您每次可能會得到一個不同的數組。 而且,根據我的經驗,實際上您會經常得到不同的人。 僅在數組實際更改時才創建一個新的表面對象可能是一件正確的事。 該文檔似乎允許這樣做,但是我從未嘗試過,所以我無法確定這是否肯定有效……
除此之外:您可以為紋理生成mipmap。 您只覆蓋了mip級別0。然后使用帶有三線性插值的mipmapping渲染紋理。 因此,我的猜測是,您恰好以與mip級別0的分辨率完全不匹配的分辨率渲染紋理,因此,最終將在級別0(您在其中編寫)和級別1(即其中寫入)之間進行插值是從原始紋理生成的)…
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.