Сбой записи из вычислительного шейдера в постоянно отображаемый SSBO - PullRequest
0 голосов
/ 05 июля 2018

Я пытаюсь записать в SSBO с вычислительным шейдером и прочитать данные обратно на процессоре.

Вычислительный шейдер - просто игрушечный пример 1x1x1, который записывает 24 числа с плавающей запятой:

#version 450 core

layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;

layout (std430, binding = 0) buffer particles {
    float Particle[];
};

void main() {
    for (int i = 0; i < 24; ++i) {
        Particle[i] = i + 1;
    }
}

Вот так я запускаю шейдер и читаю данные:

val bufferFlags = GL_MAP_READ_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT
val bufferSize = 24 * 4
val bufferId = glCreateBuffers()

glNamedBufferStorage(bufferId, bufferSize, bufferFlags)

val mappedBuffer = glMapNamedBufferRange(bufferId, 0, bufferSize, bufferFlags)

mappedBuffer.rewind()
val mappedFloatBuffer = mappedBuffer.asFloatBuffer()

mappedFloatBuffer.rewind()

val ssboIndex = glGetProgramResourceIndex(progId, GL_SHADER_STORAGE_BLOCK, "particles")

val props = Array(GL_BUFFER_BINDING)
val params = Array(-1)
glGetProgramResourceiv(progId, GL_SHADER_STORAGE_BLOCK, ssboIndex, props, null, params)

glBindBufferBase(GL_SHADER_STORAGE_BUFFER, params(0), bufferId)

glUseProgram(progId)

val sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0)
glDispatchCompute(1, 1, 1)

glClientWaitSync(sync, 0, 1000000000) match {
  case GL_TIMEOUT_EXPIRED =>
    println("Timeout expired")

  case GL_WAIT_FAILED =>
    println("Wait failed. " + glGetError())

  case _ =>
    println("Result:")

    while(mappedFloatBuffer.hasRemaining) {
      println(mappedFloatBuffer.get())
    }
}

Я ожидаю, что он напечатает числа от 1 до 24, но вместо этого он печатает 24 нуля. Используя процессор, я могу читать и записывать (если установлен GL_MAP_WRITE_BIT) в буфер. То же самое происходит, если я не использую DSA (вместо glBindBuffer / glBufferStorage / glMapBufferRange). Однако, если буфер не отображается во время работы шейдера, и я отображаю его только перед печатью содержимого, все работает правильно. Разве это не то, для чего постоянно отображаются буферы? Так что я могу сохранить его на карте, пока его использует графический процессор?

Я проверил на наличие ошибок, с glGetError, а также с более новыми выходными данными отладки, но я не получаю никаких.

Здесь (pastebin) - полностью рабочий пример. Вам нужно LWJGL , чтобы запустить его.

1 Ответ

0 голосов
/ 05 июля 2018

В вашем коде есть ряд проблем.

Сначала вы ставите синхронизацию ограждения перед командой, с которой хотите синхронизироваться. Синхронизация с забором синхронизируется со всеми командами, выполненными до забор, а не после. Если вы хотите синхронизироваться с выполнением вычислительного шейдера, то вам нужно вставить забор после вызова отправки, а не до.

Во-вторых, синхронизации недостаточно. Записи в SSBO непоследовательны, поэтому вы должны следовать правилам некогерентных обращений к памяти , чтобы сделать их видимыми для вас. В этом случае вам нужно вставить соответствующий барьер памяти между операцией вычисления и при попытке чтения из буфера с помощью glMemoryBarrier. Поскольку вы читаете данные с помощью картографирования, правильный барьер для использования - GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT.

Ваш код работает, когда вы используете непостоянное отображение, но это всего лишь внешний вид. Это все еще неопределенное поведение из-за неправильного некогерентного доступа к памяти (то есть: отсутствие барьера памяти). Так уж сложилось, что UB делает то, что вы хотите ... в этом случае.

...