2013-06-06 2 views
3

bindless imageLoad 및 imageStore를 사용하는 프래그먼트 셰이더를 통해 3D 1 구성 요소 플로트 텍스처로 렌더링하는 코드를 너무 길게 썼습니다.nVidia OpenGL에서 glGetTexImage 및 imageStore를 혼합 할 때의 문제

코드가 제대로 작동하고 있습니다.

그런 다음 일부 GLSL 컴파일러 버그를 해결해야하므로 위의 3D 텍스처를 glGetTexImage를 통해 호스트로 다시 읽고 싶었습니다. 예, 저는 glMemoryBarrierEXT (GL_ALL_BARRIER_BITS)를했습니다. glGetTexLevelparameteriv() 및 일치하는 모든 것을 통해 텍스처 정보를 확인했습니다. OpenGL 오류가 있는지 확인한 적이 없었습니다.

슬프게도 glGetTexImage는 프래그먼트 셰이더에 의해 작성된 내용을 읽지 않습니다. 대신 glTexImage3D()를 호출하여 텍스처를 생성 할 때 가짜 값을 반환합니다.

예상되는 동작입니까? 설명서에 별도로 나와 있습니다.

실제로 glGetTexImage가 작동하는 경우, 3D 텍스처 (장치에 상주합니까?)에있는 데이터를 어떻게 다시 읽을 수 있습니까? 텍스처에 비거주 자료가있을 때와 마찬가지로 드라이버가이를 수행 할 수 있습니다. 확실히 glGetTexImage가 그렇게 작동 여부를했는데 만약 내가 요구 한


...이 간단한 일을 할 수있는 간단한 방법이있다. 여기

void Bindless3DArray::dump_array(Array3D<float> &out) 
{ 
bool was_mapped = m_image_mapped; 
if (was_mapped) 
    unmap_array();   // unmap array so it's accessible to opengl 

out.resize(m_depth, m_height, m_width); 

glBindTexture(GL_TEXTURE_3D, m_textureid); // from glGenTextures() 

#if 0 
int w,h,d; 
glGetTexLevelParameteriv(GL_TEXTURE_3D, 0, GL_TEXTURE_WIDTH, &w); 
glGetTexLevelParameteriv(GL_TEXTURE_3D, 0, GL_TEXTURE_HEIGHT, &h); 
glGetTexLevelParameteriv(GL_TEXTURE_3D, 0, GL_TEXTURE_DEPTH, &d); 
int internal_format; 
glGetTexLevelParameteriv(GL_TEXTURE_3D, 0, GL_TEXTURE_INTERNAL_FORMAT, &internal_format); 
int data_type_r, data_type_g; 
glGetTexLevelParameteriv(GL_TEXTURE_3D, 0, GL_TEXTURE_RED_TYPE, &data_type_r); 
glGetTexLevelParameteriv(GL_TEXTURE_3D, 0, GL_TEXTURE_GREEN_TYPE, &data_type_g); 
int size_r, size_g; 
glGetTexLevelParameteriv(GL_TEXTURE_3D, 0, GL_TEXTURE_RED_SIZE, &size_r); 
glGetTexLevelParameteriv(GL_TEXTURE_3D, 0, GL_TEXTURE_GREEN_SIZE, &size_g); 
#endif 

glGetTexImage(GL_TEXTURE_3D, 0, GL_RED, GL_FLOAT, &out(0,0,0)); 
glBindTexture(GL_TEXTURE_3D, 0); 
CHECK_GLERROR(); 

if (was_mapped) 
    map_array_to_cuda(); // restore state 
} 

bindless 배열 생성 코드 것 : 여기에 코드입니다

void Bindless3DArray::allocate(int w, int h, int d, ElementType t) 
{ 
if (!m_textureid) 
    glGenTextures(1, &m_textureid); 
m_type = t; 
m_width = w; 
m_height = h; 
m_depth = d; 

glBindTexture(GL_TEXTURE_3D, m_textureid); 
CHECK_GLERROR(); 
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAX_LEVEL, 0); // ensure only 1 miplevel is allocated 
CHECK_GLERROR(); 

Array3D<float> foo(d, h, w); 
// DEBUG -- glGetTexImage returns THIS data, not what's on device 
for (int z=0; z<m_depth; ++z) 
for (int y=0; y<m_height; ++y) 
for (int x=0; x<m_width; ++x) 
    foo(z,y,x) = 3.14159; 

//-- Texture creation 
if (t == ElementInteger) 
    glTexImage3D(GL_TEXTURE_3D, 0, GL_R32UI, w, h, d, 0, GL_RED_INTEGER, GL_INT, 0); 
else if (t == ElementFloat) 
    glTexImage3D(GL_TEXTURE_3D, 0, GL_R32F, w, h, d, 0, GL_RED, GL_FLOAT, &foo(0,0,0)); 
else 
    throw "Invalid type for Bindless3DArray"; 
CHECK_GLERROR(); 

m_handle = glGetImageHandleNV(m_textureid, 0, true, 0, (t == ElementInteger) ? GL_R32UI : GL_R32F); 
glMakeImageHandleResidentNV(m_handle, GL_READ_WRITE); 
CHECK_GLERROR(); 

#ifdef USE_CUDA 
checkCuda(cudaGraphicsGLRegisterImage(&m_image_resource, m_textureid, GL_TEXTURE_3D, cudaGraphicsRegisterFlagsSurfaceLoadStore)); 
#endif 
} 

내가 OpenGL을 조각 프로그램을 통해 그것을 렌더링, 배열을 할당 한 다음 내가 dump_array 전화는() 읽기 다시 데이터. 슬프게도, 나는 allocate 호출에서로드 한 것을 얻는다.

void App::clear_deepz() 
{ 
deepz_clear_program.bind(); 

deepz_clear_program.setUniformValue("sentinel", SENTINEL); 
deepz_clear_program.setUniformValue("deepz", deepz_array.handle()); 
deepz_clear_program.setUniformValue("sem", semaphore_array.handle()); 

run_program(); 

glMemoryBarrierEXT(GL_ALL_BARRIER_BITS); 
// glMemoryBarrierEXT(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); 
// glMemoryBarrierEXT(GL_SHADER_GLOBAL_ACCESS_BARRIER_BIT_NV); 

deepz_clear_program.release(); 
} 

과 조각 프로그램처럼

렌더 프로그램은 같습니다 무엇 discard 수단이 아니다

#version 420\n 

in vec4 gl_FragCoord; 
uniform float sentinel; 
coherent uniform layout(size1x32) image3D deepz; 
coherent uniform layout(size1x32) uimage3D sem; 

void main(void) 
{ 
ivec3 coords = ivec3(gl_FragCoord.x, gl_FragCoord.y, 0); 
imageStore(deepz, coords, vec4(sentinel)); 
imageStore(sem, coords, ivec4(0)); 
discard; // don't write to FBO at all 
} 
+1

실제 코드를 표시하지 않을 때 문제가 무엇인지 말하기는 어렵습니다. –

+0

요청에 따라 코드를 추가했습니다. –

+0

'glMemoryBarrierEXT (GL_ALL_BARRIER_BITS);'이것은 EXT_shader_image_load_store에서 온 것입니다. 그러나 다른 모든 것들은 핵심 OpenGL 4.2/ARB_shader_image_load_store 기능을 사용하고 있습니다. EXT가 없어야합니다. –

답변

2
discard; // don't write to FBO at all 

합니다. 오, 그 뜻입니다. 그러나 은 모든 이미지로드/저장 기록이 삭제된다는 것을 의미합니다. 실제로 컴파일러는이 문장을보고 전체 프래그먼트 셰이더에 대해 아무 것도하지 않을 것입니다.

프래그먼트 셰이더를 실행하려면 empty framebuffer object을 갖는 GL 4.3 기능 (NVIDIA 하드웨어에서 사용 가능)을 사용할 수 있습니다. 또는 compute shader을 사용할 수도 있습니다. GL 4.3을 아직 사용할 수 없으면 write mask을 사용하여 모든 색상 쓰기를 끕니다.

+0

재미있는 것은 내가 imageStore가 실제로 물건을 저장했기 때문입니다. 그럼에도 불구하고, 나는 네가 제안한 것을 시도해보고 nvidia에 버그를 제출해야한다. –

+0

@WaltDonovan : 버그가 아닙니다. OpenGL 사양 *에는 this *가 필요합니다. 'discard '를 실행하면, fragment shader가 보이는 모든 동작을 버려야합니다. –

+0

Lovely -이 단일 참조를 GL_EXT_shader_image_load_store 사양 깊숙이 발견했습니다. (20) 셰이더가 이미지 저장소 나 원자 연산을 지정하면 어떻게 될까요? 죽거나 삭제 된 픽셀은 어떻게됩니까? • 해결 :이 문제가 발생하면 상점이 발생하지 않습니다. 그래서 그것이 실제로 작동하는 것처럼 보인 사실은 운전자의 또 다른 버그입니다. –

1

위의 Nicol에서 언급했듯이 이미지로드 및 저장의 부작용 만 원할 경우 적절한 방법은 빈 프레임 버퍼 객체를 사용하는 것입니다.

glGetTexImage()와 bindless 텍스처를 혼합 한 버그는 실제로 드라이버 버그 였고 드라이버 버전 335.23에서 수정되었습니다. 버그를 제기하고 코드가 제대로 작동하는지 확인했습니다.

참고 코드에서 빈 프레임 버퍼 개체를 사용하고 있으며 더 이상 "폐기"를 사용하지 마십시오.