我正在尝试使用 vulkan 的 vulkano rust 库将 PNG 上传到 GPU。我什至无法正确创建图像。
到目前为止,我拥有的非工作代码(使用 94f50f18bd25971ea123adb8b5782ad65a8f085c 提交,因为我最终需要 opengl 互操作)是
let extent = [width, height, 1];
let format = Format::R8G8B8A8_UNORM;
let image = Image::new(
memory_allocator.clone(),
ImageCreateInfo {
image_type: ImageType::Dim2d,
format,
extent,
usage: ImageUsage::TRANSFER_DST | ImageUsage::SAMPLED,
..Default::default()
},
AllocationCreateInfo {
memory_type_filter: MemoryTypeFilter {
required_flags: MemoryPropertyFlags::HOST_VISIBLE,
preferred_flags: MemoryPropertyFlags::DEVICE_LOCAL,
not_preferred_flags: MemoryPropertyFlags::empty(),
},
..Default::default()
},
)
.unwrap();
它失败并显示“在
Result::unwrap()
值上调用 Err
:发生非验证错误:为图像分配内存失败”。通过尝试不同的创建参数,我成功地得到了一些不同的错误。
创建可以接收原始像素(在我的情况下从 PNG 解码)然后与纹理映射多边形采样器一起使用的图像的正确技术是什么?
首先,您应该在尝试创建图像时验证宽度和高度均非零。
如果这不是原因,那么您可能应该在实例中启用验证层并在调试回调中放置一个断点,以便您可以看到底层系统遇到的错误阻止了图像创建。
其次,这个内存过滤器没有多大意义:
AllocationCreateInfo {
memory_type_filter: MemoryTypeFilter {
required_flags: MemoryPropertyFlags::HOST_VISIBLE,
preferred_flags: MemoryPropertyFlags::DEVICE_LOCAL,
not_preferred_flags: MemoryPropertyFlags::empty(),
},
..Default::default()
},
一般来说,出于性能原因,图像需要位于设备本地。所以你的首选和必需的标志向后看。但是,我明白你想要做什么,你需要能够将 PNG 数据从 CPU 内存复制到设备内存,为此你需要主机可见内存。
一般来说,Vulkan 环境大致分为两类,一类具有统一内存架构(移动设备和许多笔记本电脑),另一类具有离散内存架构(具有插件 GPU 卡的桌面系统)。
在统一内存架构上,您应该反转用于使
DEVICE_LOCAL
为必需且 HOST_VISIBLE
为首选的标志(如果您知道至少有一个支持它的内存池,则为必需)。理论上,您应该取回一些可以映射到 CPU 可见指针的内存,此时您可以复制 PNG 内容。
然而,在大多数桌面系统上,将图像(或缓冲区)加载到 GPU 中是一个多步骤过程,涉及创建两个对象,将它们称为 A 和 B。
A 实际的 GPU 资源,将具有
DEVICE_LOCAL
内存和另一个用于暂存的瞬态对象。
B 是使用
HOST_VISIBLE
作为所需内存类型创建的第二个对象(并且根本不指定 DEVICE_LOCAL
)。您创建它,将内存映射到 CPU,将数据复制到其中。
一旦A和B都被创建,您需要执行一个命令缓冲区,告诉驱动程序将B的内容复制到A。
命令缓冲区在 GPU 上完成后,您可以销毁对象 B 或将其重新用于其他副本(如果它足够通用)。