Vulkan:将3频道图片上传到设备

时间:2019-06-19 16:06:03

标签: vulkan

假设主机端有一个3通道映像(float或uint8),需要将其传输到设备映像。使用vkCmdCopyBufferToImage。对于设备映像的格式,我看到两个选项:

  1. 使用R32G32B32A32_SFLOAT / R8G8B8A8_SNORM并将有效PCI带宽减少25%,并处理主机上的额外通道(额外的CPU和/或RAM)。
  2. 使用不受广泛支持的R32G32B32_SFLOAT / R8G8B8_SNORM。

有什么办法可以在设备上保留4通道,但可以从3通道主机缓冲区中填充它吗?

2 个答案:

答案 0 :(得分:1)

是:逐个像素读取图像,获取每个3通道像素并写入4通道版本。然后将格式正确的4通道数据上传到Vulkan。

Vulkan不是OpenGL;它提供的(大部分)唯一功能是硬件功能:GPU为您完成的任务。硬件无法完成将3通道数据转换为正确格式的操作。所以Vulkan不这样做;那是你的工作。

答案 1 :(得分:0)

NicolBolas是正确的,它将需要硬件将非4通道图像自动传输到4通道图像存储器,或支持3通道图像纹理。但是,为什么不充分利用吞吐能力,并利用计算着色器将纹理用作着色器缓冲区,并以正确的格式读取数据,而不是像从主机通过图像逐像素复制那样缓慢地进行操作? ?

这是我的意思:

  • 您有一个3通道的图像,但这只是数据。

  • 您现在将其作为缓冲区上传到设备。

  • 在计算着色器中,从图像读取通道,然后直接在设备上将结果写到图像

使用图像的示例

如前所述,您还可以直接写入图像缓冲区,这意味着您不必为图像调用单独的缓冲区副本。

#version 450

// should be tightly packed if I read the spec correctly
// https://www.khronos.org/registry/OpenGL/specs/gl/glspec45.core.pdf
layout(std430, binding = 0) buffer in_3_channel 
{
    vec3 input_channel[ ];
};

layout (binding = 1, rgba32f) uniform image2D result_image;

layout (local_size_x = 256) in;

layout (binding = 2) uniform UBO 
{
    int image_size;
    int image_cols;
} ubo;


void main() 
{
    uint index = gl_GlobalInvocationID.x;
    if (index >= ubo.image_size){
        return; 
    }

    vec3 in_color = input_channel[index];
    vec4 out_color = vec4(in_color, 1.0);
    // using an image instead, we would write using this:
    int row = gl_GlobalInvocationID.x / image_cols;
    int col = gl_GlobalInvocationID.x % image_cols;
    ivec2 image_write_location = ivec2(row, col);
    imageStore(result_image, image_write_location, out_color);
}

使用缓冲区的示例:

#version 450

// should be tightly packed if I read the spec correctly
// https://www.khronos.org/registry/OpenGL/specs/gl/glspec45.core.pdf
layout(std430, binding = 0) buffer in_3_channel 
{
    vec3 input_channel[ ];
};

layout(std430, binding = 1) buffer out_4_channel{
    vec4 output_channel[ ];
};
//alternatively you could use a image buffer directly ex:
//
//layout (binding = 1, rgba32f) uniform image2D result_image;

layout (local_size_x = 256) in;

layout (binding = 2) uniform UBO 
{
    int image_size;
} ubo;


void main() 
{
    uint index = gl_GlobalInvocationID.x;
    if (index >= ubo.image_size){
        return; 
    }

    vec3 in_color = input_channel[index];
    vec4 out_color = vec4(in_color, 1.0);
    output_channel[index] = out_color;
    // using an image instead, we would write using this:
    // ivec2 image_write_location = ivec2(gl_GlobalInvocationID.x / image_cols, gl_GlobalInvocationID.x % image_cols);
    //imageStore(result_image, image_write_location, out_color);
}

如果您想将缓冲区复制到纹理,则可以将数据复制到图像,如下例所示:https://vulkan-tutorial.com/Texture_mapping/Images

    //staging buffer is your new converted RGB -> RGBA buffer. 
    transitionImageLayout(textureImage, VK_FORMAT_R32G32B32A32_SFLOAT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
        copyBufferToImage(stagingBuffer, textureImage, static_cast<uint32_t>(texWidth), static_cast<uint32_t>(texHeight));
    transitionImageLayout(textureImage, VK_FORMAT_R32G32B32A32_SFLOAT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);

结论

现在,您已经获得了主机->设备带宽的优势,利用了GPU的计算能力,并且仅在3通道内存的初始传输时接触了主机。