使用几何着色器进行图层渲染似乎只输出图像数组的第一层

Layer rendering using geometry shader seems to only output the first layer of an image array

我正在尝试对图像阵列进行一些分层渲染,并在屏幕上显示其中一层。 但无论我做什么,只有第一层被正确渲染和显示,每次我尝试显示第一层以外的另一层时,我除了黑屏什么也得不到。 所以我试着打包了大部分必要的信息,希望有人能发现我的错误。

首先,我用这些参数创建了一个图像:

VkImageCreateInfo info{};
info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
info.imageType = VK_IMAGE_TYPE_2D;
info.format = VK_FORMAT_R8G8B8A8_UNORM;
info.extent = { WindowWidth, WindowHeight, 1 };
info.mipLevels = 1;
info.arrayLayers = 6;
info.samples = VK_SAMPLE_COUNT_1_BIT;
info.tiling = VK_IMAGE_TILING_OPTIMAL;
info.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;

VkImage myImage = createImage(info);

我省略了整个内存分配和绑定体操,因为我认为它与这里无关。

然后,我使用了两个图像视图: - 具有整个 {0, 6} 层范围的一个,将用于几何着色器中的层渲染 - 另一个只有一层,将用于在我选择的给定层获取数据

这是我创建它们的方式:

VkImageViewCreateInfo info{};
info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
info.image = myImage;
info.viewType = VK_IMAGE_VIEW_TYPE_2D;
info.format = VK_FORMAT_R8G8B8A8_UNORM;
info.components = Identity;
info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
info.subresourceRange.baseMipLevel = 0;
info.subresourceRange.levelCount = 1;
info.subresourceRange.baseArrayLayer = 0;
info.subresourceRange.layerCount = 6;

VkImageView imageView6Layers = createImageView(info);

VkImageViewCreateInfo info2 = info; 
info2.subresourceRange.baseArrayLayer = 1; // Target the layer 1 for example
info2.subresourceRange.layerCount = 1;

VkImageView imageView1Layer = createImageView(info2);

至此,我已经构建了两个渲染通道来划分过程:

这是第一个渲染通道(用于分层渲染):

// 6 layers image view
VkAttachmentDescription attachment{};  
attachment.format = VK_FORMAT_R8G8B8A8_UNORM;
attachment.samples = VK_SAMPLE_COUNT_1_BIT;
attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
attachment.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;

VkAttachmentReference colorAttachment{};
colorAttachment.attachment = 0;
colorAttachment.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;

VkSubpassDescription subpass{};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &colorAttachment;

VkRenderPassCreateInfo info{};
info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
info.attachmentCount = 1;
info.pAttachments = &attachment;
info.subpassCount = 1;
info.pSubpasses = &subpass;

VkRenderPass renderPass6Layers = createRenderPass(info);

这是第二个渲染过程(获取数据,并将其输出到交换链图像):

VkAttachmentDescription attachments[2]{};

// Swapchain image
attachments[0].format = swpachainImageFormat;
attachments[0].samples = VK_SAMPLE_COUNT_1_BIT;
attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attachments[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
attachments[0].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;

// 1 layer image view
attachments[1].format = VK_FORMAT_R8G8B8A8_UNORM;
attachments[1].samples = VK_SAMPLE_COUNT_1_BIT;
attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attachments[1].initialLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
attachments[1].finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;

VkAttachmentReference colorAttachment{};
colorAttachment.attachment = 0;
colorAttachment.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;

VkAttachmentReference inputAttachment{};
inputAttachment.attachment = 1;
inputAttachment.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;

VkSubpassDescription subpass{};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &colorAttachment;
subpass.inputAttachmentCount = 1;
subpass.pInputAttachments = &inputAttachment;

VkRenderPassCreateInfo info{};
info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
info.attachmentCount = 2;
info.pAttachments = attachments;
info.subpassCount = 1;
info.pSubpasses = &subpass;

VkRenderPass renderPass1Layer = createRenderPass(info);

然后是第一个子通道中使用的着色器:

顶点着色器:

#version 450

layout (location = 0) in vec3 iModelPos;

void main() 
{   
    gl_Position = vec4(iModelPos, 1);
}

几何着色器:

#version 450

layout (triangles) in;
layout (triangle_strip, max_vertices = 3) out;

void main()
{
    for (int i = 0; i < 6; ++i)
    {
        for (int j = 0; j < gl_in.length(); j++)
        {
            gl_Layer = i;

            // Assume I have everything necessary to compute the matrix
            gl_Position = PVM * gl_in[j].gl_Position;

            EmitVertex();
        }

        EndPrimitive();
    }
}

片段着色器:

#version 450

layout (location = 0) out vec4 oColor;

void main() 
{
    oColor = vec4(1,1,1,1);
}

第二个子通道中使用的着色器:

顶点着色器(简单地在整个屏幕上输出一个四边形):

#version 450

layout (location = 0) in vec3 iModelPos;

void main() 
{
    gl_Position = vec4(iModelPos, 1);
}

片段着色器(在图像层中获取数据):

#version 450

// Assume it's bound here
layout (input_attachment_index = 0, set = 0, binding = 0) uniform subpassInput ImageLayer;

layout (location = 0) out vec4 oColor;

void main() 
{
    oColor = subpassLoad(ImageLayer);
}

最后是我的主图:

int main(void)
{
    /* ... */

    {
        VkCommandBufferBeginInfo info{};
        info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
        info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;

        vkBeginCommandBuffer(cmdBuffer, &info);
    }

    VkFramebuffer framebuffer6Layers{}
    {
        VkFramebufferCreateInfo info{};
        info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
        info.renderPass = renderPass6Layers;
        info.attachmentCount = 1;
        info.pAttachments = &imageView6Layers;
        info.width = WindowWidth;
        info.height = WindowHeight;
        info.layers = 6;

        framebuffer6Layers = createFramebuffer(info);
    }

    {
        VkClearValue clearValues = {0,0,0,1};

        VkRenderPassBeginInfo info{};
        info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
        info.renderPass = renderPass6Layers;
        info.framebuffer = framebuffer6Layers;
        info.renderArea = { WindowWidth, WindowHeight };
        info.clearValueCount = 1;
        info.pClearValues = &clearValues;

        vkCmdBeginRenderPass(cmdBuffer, &info, VK_SUBPASS_CONTENTS_INLINE);
    }

    /* binds */

    vkCmdDraw(cmdBuffer, objectVertices.count(), 1, 0, 0);

    vkCmdEndRenderPass(cmdBuffer);
    vkEndCommandBuffer(cmdBuffer);

    /* submit cmdBuffer and wait for rendering to finish */

    {
        VkCommandBufferBeginInfo info{};
        info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
        info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;

        vkBeginCommandBuffer(cmdBuffer, &info);
    }

    {
        VkImageMemoryBarrier barrier{};
        barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
        barrier.image = myImage;
        barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
        barrier.dstAccessMask = VK_ACCESS_INPUT_ATTACHMENT_READ_BIT;
        barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
        barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
        barrier.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
        barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
        barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
        barrier.subresourceRange.baseArrayLayer = 0;
        barrier.subresourceRange.layerCount = 6;
        barrier.subresourceRange.baseMipLevel = 0;
        barrier.subresourceRange.levelCount = 1;

        vkCmdPipelineBarrier(cmdBuffer, 
        VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 
        VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 
        0, 
        0, nullptr, 
        0, nullptr, 
        1, &barrier);
    }

    VkFramebuffer framebuffer1Layer{};
    {
        VkImageView attachments[2]{};
        attachments[0] = swapchainImageView;
        attachments[1] = imageView1Layer;

        VkFramebufferCreateInfo info{};
        info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
        info.renderPass = renderPass1Layer;
        info.attachmentCount = 2;
        info.pAttachments = attachments;
        info.width = WindowWidth;
        info.height = WindowHeight;
        info.layers = 1;

        framebuffer1Layer = createFramebuffer(info);
    }

    {
        VkClearValue clearValues = {0,0,0,1};

        VkRenderPassBeginInfo info{};
        info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
        info.renderPass = renderPass1Layer;
        info.framebuffer = framebuffer1Layer;
        info.renderArea = { WindowWidth, WindowHeight };
        info.clearValueCount = 1;
        info.pClearValues = &clearValues;

        vkCmdBeginRenderPass(cmdBuffer, &info, VK_SUBPASS_CONTENTS_INLINE);
    }

    /* binds */

    vkCmdDraw(quadVertices.count(), 1, 0, 0);

    vkCmdEndRenderPass(cmdBuffer);
    vkEndCommandBuffer(cmdBuffer);

    /* submit cmdBuffer, wait for rendering to finish and present the swapchain image */

    /* ... */
}

我没有包含一些信息,例如帧缓冲区创建。 感觉已经给了很多信息了,希望有大佬指点一下,或者指点一下。

编辑 1:

验证层正在运行,不输出任何内容。

编辑 2:

我发现出于某些原因我实际上总是只创建一个层的帧缓冲区,这解释了为什么验证层从不抱怨。

为了在帧缓冲区中使用正确的层数,我将过程分为两个渲染过程,并在两者之间添加了一个图像屏障(参见上面编辑的过程)。 但是它并没有解决问题,当我尝试显示另一个图像层而不是第一个图像层时仍然黑屏,并且在显示第一层时正确结果。

验证层现在也不抱怨了,我按照要求添加了帧缓冲区创建细节。

编辑 3:

我尝试使用组合图像采样器统一代替输入附件,但没有成功。当我尝试显示第一层时它仍然有效,但我从其他层得到相同的黑屏。

我从第二个渲染过程中删除了附件并因此更新了内存屏障。

这是第二个渲染通道的更新:

// Swapchain image
VkAttachmentDescription attachment{};
attachment.format = swpachainImageFormat;
attachment.samples = VK_SAMPLE_COUNT_1_BIT;
attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attachments.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;

VkAttachmentReference colorAttachment{};
colorAttachment.attachment = 0;
colorAttachment.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;

VkSubpassDescription subpass{};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &colorAttachment;

VkRenderPassCreateInfo info{};
info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
info.attachmentCount = 1;
info.pAttachments = &attachment;
info.subpassCount = 1;
info.pSubpasses = &subpass;

VkRenderPass renderPass1Layer = createRenderPass(info);

这是新的片段着色器:

// Assume it's bound here
layout (set = 0, binding = 0) uniform sampler2D ImageLayer;

layout (location = 0) out vec4 oColor;

void main() 
{
    // Assume that I have the viewport from an uniform buffer.
    const vec2 uv = gl_FragCoord.xy / Viewport;
    oColor = texture(ImageLayer, uv);
}

uv 值是正确的,当我尝试输出 vec4(uv, 0, 1) 时,我在屏幕上得到了预期的颜色,对于我尝试显示的每个图像层:

黑色,红色,

绿色、黄色

图像屏障的变化如下:

barrier.dstAccessMask = VK_ACCESS_UNIFORM_READ_BIT;

例如图像使用标志或描述符集布局也有一些变化,但我没有在此处包括它们。

我终于弄清楚出了什么问题。

layout (triangle_strip, max_vertices = 3) out;

我的几何着色器中的这条线导致了第三个顶点之后的其他顶点 默默丢弃。因此,我的着色器能够写入的唯一层是第一个层。

我已经解决了这个问题:

layout (triangle_strip, max_vertices = 18) out;

我想在 6 个图层上绘制一个三角形(3 个顶点),因此 max_vertices = 3 x 6 = 18。

另一个问题在我的编辑 3 中解决了。我的帧缓冲区只创建了一层而不是所需的 6 层,使得几何着色器 静默 只写在第一层上.似乎当您尝试在大于您在帧缓冲区中提供的层数的层上写入时,着色器会自动在层 0 上写入。