проблемы с смешиванием glGetTexImage и imageStore на nvidia opengl

Я написал некоторый код, слишком длинный, чтобы вставлять его сюда, который визуализирует плавающую текстуру компонента 3D 1 с помощью фрагментного шейдера, использующего безбиндинговые функции imageLoad и 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
}

Вот код, который создает массив без привязки:

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() для обратного чтения данных. К сожалению, я получаю только то, что загрузил при вызове выделения.

Программа рендеринга выглядит так

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();
}

и программа фрагмента:

#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
}

person Walt Donovan    schedule 06.06.2013    source источник
comment
Трудно сказать, в чем проблема, когда вы не показываете никакого фактического кода.   -  person Nicol Bolas    schedule 06.06.2013
comment
Как и просили, я добавил код.   -  person Walt Donovan    schedule 07.06.2013
comment
glMemoryBarrierEXT(GL_ALL_BARRIER_BITS); Это из EXT_shader_image_load_store. Но все остальное использует основные функции OpenGL 4.2/ARB_shader_image_load_store. У вас не должно быть EXT там.   -  person Nicol Bolas    schedule 07.06.2013
comment
Хорошо, спасибо, что заметили это.   -  person Walt Donovan    schedule 07.06.2013
comment
Я также получаю в значительной степени именно эту проблему (только я не использую привязку). Вы с тех пор нашли решение?   -  person Dylan    schedule 28.05.2014


Ответы (2)


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

Это не то, что означает discard. О, это значит. Но это также означает, что все записи загрузки/сохранения изображений также будут отброшены. Действительно, велика вероятность, что компилятор увидит этот оператор и просто ничего не сделает для всего фрагментного шейдера.

Если вы хотите просто выполнить фрагментный шейдер, вы можете использовать функцию GL 4.3 (доступную на вашем оборудовании NVIDIA) с пустой объект фреймбуфера. Или вы можете использовать вычислительный шейдер. Если вы еще не можете использовать GL 4.3, используйте маску записи, чтобы отключить запись всех цветов .

person Nicol Bolas    schedule 06.06.2013
comment
Интересно, что вы это говорите, потому что я уверен, что в imageStore действительно хранятся вещи. В любом случае, я попробую то, что вы предлагаете, и, если понадобится, отправлю сообщение об ошибке в nvidia. - person Walt Donovan; 07.06.2013
comment
@WaltDonovan: это не ошибка. Спецификация OpenGL требует этого. Если вы выдаете discard, то каждое видимое действие, которое делает фрагментный шейдер, должно отбрасываться. - person Nicol Bolas; 07.06.2013
comment
Прекрасно — я нашел эту единственную ссылку глубоко в спецификации GL_EXT_shader_image_load_store: (20) Что произойдет, если шейдер укажет хранилище изображений или атомарную операцию для уничтоженных/отброшенных пикселей? РЕШЕНО: при этом не происходит сохранения. Так что тот факт, что это, казалось, работало, на самом деле является еще одной ошибкой в ​​​​драйвере. - person Walt Donovan; 07.06.2013
comment
Дерьмо. Использование пустого FBO не решило проблему (и фактически дало идентичные результаты). Я закомментировал все сбросы. Есть идеи сейчас? - person Walt Donovan; 07.06.2013
comment
@WaltDonovan: Что происходит, когда вы не используете беспроводные технологии NVIDIA? - person Nicol Bolas; 07.06.2013
comment
Что ж, это была бы попытка №7 заставить этот код работать... Я предпочитаю подход без привязки, так как он ближе к c/c++. Надеюсь, я скоро получу от nvidia отчеты об ошибках, которые я отправил им, и я снова прочитаю о старом способе делать что-то, прежде чем привязываться. - person Walt Donovan; 07.06.2013

Как упоминает Николь выше, если вам нужны побочные эффекты только загрузки и сохранения изображения, правильным способом является использование пустого объекта кадрового буфера.

Ошибка смешивания glGetTexImage() и текстур без привязки на самом деле была ошибкой драйвера и была исправлена ​​в версии драйвера 335.23. Я зарегистрировал ошибку и подтвердил, что мой код теперь работает правильно.

Примечание. Я использую в коде пустые объекты буфера кадра и больше не использую «отбросить».

person Walt Donovan    schedule 29.05.2014