假设主机端有一个3通道映像(float或uint8),需要将其传输到设备映像。使用vkCmdCopyBufferToImage
。对于设备映像的格式,我看到两个选项:
有什么办法可以在设备上保留4通道,但可以从3通道主机缓冲区中填充它吗?
答案 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通道内存的初始传输时接触了主机。