Vulkan / OpenGL - проблема с мозаичным изображением шейдера - PullRequest
0 голосов
/ 04 января 2019

Я использую Vulkan с вычислительными шейдерами GLSL для пост-обработки текстуры, визуализированной в OpenGL. По большей части он работает нормально, но когда разрешение текстуры по вертикали находится в определенных пределах, я вижу очень странные аномалии, в которых gl_GlobalInvocationID, похоже, испортился.

Пример вычислительного шейдера, который демонстрирует это выглядит как

#version 450
#extension GL_ARB_separate_shader_objects : enable

#define WORKGROUP_SIZE 32

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

layout(binding=0, rgba8) uniform image2D input_tex;
layout(binding=1, rgba8) uniform image2D output_tex;

void main()
{
    vec4 red = vec4(1.0, 0.0, 0.0, 1.0);
    vec4 black = vec4(0.0, 0.0, 0.0, 1.0);
    if(gl_GlobalInvocationID.x == 0 || gl_GlobalInvocationID.y == 0)
    {
        imageStore(output_tex, ivec2(gl_GlobalInvocationID.xy), red);
    }
    else
    {
        imageStore(output_tex, ivec2(gl_GlobalInvocationID.xy), black);
    }
}

В зависимости от разрешения по вертикали я получаю странные результаты: 482+, 241-256, 121-128, 55-64 работают нормально, но 257-481, 129-240, 65-121, <55 неверны. Смотри ниже. </p>

Рабочее разрешение по вертикали 482: 960x482 Working

Вертикальное разрешение 480 битых: 960x480 Not Working

Я создаю текстуры с помощью:

vk::ImageCreateInfo image_create_info(
    vk::ImageCreateFlags(),
    vk::ImageType::e2D,
    vk::Format::eR8G8B8A8Unorm,
    {width, height, 1},
    1, 
    1,
    vk::SampleCountFlagBits::e1,
    vk::ImageTiling::eOptimal,
    vk::ImageUsageFlagBits::eColorAttachment |
        vk::ImageUsageFlagBits::eSampled |
        vk::ImageUsageFlagBits::eTransferSrc |
        vk::ImageUsageFlagBits::eStorage
);

image = device.createImage(image_create_info);

vk::MemoryRequirements memory_requirements = device.getImageMemoryRequirements(image);
memory_size = memory_requirements.size;

vk::ExportMemoryAllocateInfo export_allocate_info(
    vk::ExternalMemoryHandleTypeFlagBits::eOpaqueFd);
vk::MemoryAllocateInfo memory_allocate_info(
    memory_size,
    memory_type // Standard way of finding this using eDeviceLocal
);
memory_allocate_info.pNext = &export_allocate_info;
device_memory = device.allocateMemory(memory_allocate_info);
device.bindImageMemory(image, device_memory, 0);

vk::MemoryGetFdInfoKHR memory_info(device_memory, vk::ExternalMemoryHandleTypeFlagBits::eOpaqueFd);
opaque_handle = device.getMemoryFdKHR(memory_info, dynamic_loader);

// Create OpenGL texture
glCreateTextures(GL_TEXTURE_2D, 1, &opengl_tex);
glCreateMemoryObjectsEXT(1, &opengl_memory);
glImportMemoryFdEXT(opengl_memory, memory_size, GL_HANDLE_TYPE_OPAQUE_FD_EXT, opaque_handle);
glTextureStorageMem2DEXT(opengl_tex, 1, GL_RGBA8, width, height, opengl_memory, 0);

Я отрисовываю текстуру в OpenGL, затем использую glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST), чтобы скопировать отрендеренное изображение сверху в opengl_tex, а затем выполняю буфер команд, содержащий вышеуказанный шейдер. После этого я откладываю текстуру и отображаю ее на экране в OpenGL. (Использование семафоров для синхронизации).

Создание буфера команд выглядит как

vk::CommandBufferBeginInfo begin_info(
    vk::CommandBufferUsageFlagBits());
command_buffer.begin(begin_info);
command_buffer.bindPipeline(vk::PipelineBindPoint::eCompute, pipeline);
command_buffer.bindDescriptorSets(vk::PipelineBindPoint::eCompute, pipeline_layout, 0, 1, &descriptor_set, 0, nullptr);
command_buffer.dispatch((uint32_t)ceil(width/float(WORKGROUP_SIZE), (uint32_t)ceil(height/float(WORKGROUP_SIZE)), 1);
command_buffer.end();

Я несколько раз проверял, что не где-то жестко кодирую размер (кроме размера рабочей группы).

Я также создал утилиту для чтения изображений в формате png, выполнения того же шейдера и записи выходных данных (в обход взаимодействия OpenGL), и это работает независимо от измерения.

У меня есть требование, чтобы иметь возможность обрабатывать изображение 480x480, поэтому любая помощь будет высоко ценится. Если вам нужна какая-либо другая информация, я могу предоставить больше кода.

(к сведению, я использую GTX 1080 с драйвером NVidia 410.78 для Linux и Vulkan 1.1.85.0)

EDIT: Используя следующий фрагмент шейдера:

vec4 color = vec4(gl_GlobalInvocationID.x/480.0, gl_GlobalInvocationID.y/480.0, 0.0, 1.0);
imageStore(output_tex, ivec2(gl_GlobalInvocationID.xy), color);

Я получаю результаты

480x482 изображение работает:

480x482 Working

480x480 изображение повреждено:

480x480 Broken

...