使用Rust Vulkano库创建可导入PNG像素的GPU纹理图像失败求解
问题:Vulkano创建可接收PNG像素的GPU图像失败
我在用Vulkan的Rust绑定库Vulkano把PNG图像上传到GPU时,创建图像失败。当前使用的是commit为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();
运行时报错:
called
Result::unwrap()on anErrvalue: a non-validation error occurred: allocating memory for the image failed
我修改创建参数后出现了其他错误,请问怎么正确创建能接收PNG解码后的原始像素、并能用于采样器做纹理映射的图像?
解决方案
1. 修正内存分配逻辑错误
你的错误核心是同时要求内存必须HOST_VISIBLE且偏好DEVICE_LOCAL,但多数设备的DEVICE_LOCAL内存并不支持HOST_VISIBLE属性,直接导致内存分配失败。
如果需要从CPU上传像素数据,正确流程是分两步:
- 创建一块
HOST_VISIBLE+HOST_COHERENT的临时staging图像,用来存放解码后的PNG像素 - 创建一块
DEVICE_LOCAL的目标图像(用于采样),通过Vulkan传输命令把数据从staging图像复制到目标图像
2. 完整实现代码示例
步骤1:创建Staging图像(CPU上传用)
let staging_image = Image::new( memory_allocator.clone(), ImageCreateInfo { image_type: ImageType::Dim2d, format: Format::R8G8B8A8_UNORM, extent: [width, height, 1], usage: ImageUsage::TRANSFER_SRC, // 标记为传输源 ..Default::default() }, AllocationCreateInfo { memory_type_filter: MemoryTypeFilter { required_flags: MemoryPropertyFlags::HOST_VISIBLE | MemoryPropertyFlags::HOST_COHERENT, preferred_flags: MemoryPropertyFlags::empty(), ..Default::default() }, ..Default::default() }, ).unwrap();
步骤2:上传PNG像素到Staging图像
假设png_pixels是解码后的RGBA格式字节数组:
let mut mapping = staging_image.write().unwrap(); mapping.copy_from_slice(png_pixels);
步骤3:创建目标图像(采样用)
let target_image = Image::new( memory_allocator.clone(), ImageCreateInfo { image_type: ImageType::Dim2d, format: Format::R8G8B8A8_UNORM, extent: [width, height, 1], usage: ImageUsage::TRANSFER_DST | ImageUsage::SAMPLED, // 接收传输+可采样 ..Default::default() }, AllocationCreateInfo { memory_type_filter: MemoryTypeFilter { required_flags: MemoryPropertyFlags::DEVICE_LOCAL, preferred_flags: MemoryPropertyFlags::empty(), ..Default::default() }, ..Default::default() }, ).unwrap();
步骤4:提交数据复制命令
用命令缓冲区完成staging到目标图像的数据复制:
let command_buffer = AutoCommandBufferBuilder::primary( &command_buffer_allocator, queue.queue_family_index(), CommandBufferUsage::OneTimeSubmit, ) .unwrap() .copy_image_to_image( CopyImageToImageInfo { src: staging_image.as_view().unwrap(), dst: target_image.as_view().unwrap(), regions: [ImageCopy { src_subresource: ImageSubresourceLayers { aspect: ImageAspect::COLOR, mip_level: 0, base_array_layer: 0, layer_count: 1, }, src_offset: [0, 0, 0], dst_subresource: ImageSubresourceLayers { aspect: ImageAspect::COLOR, mip_level: 0, base_array_layer: 0, layer_count: 1, }, dst_offset: [0, 0, 0], extent: [width, height, 1], }], ..Default::default() }, ) .build() .unwrap(); // 提交命令并等待执行完成 let _ = queue.submit(SubmitInfo::new().command_buffers(&[command_buffer])).unwrap(); queue.wait_idle().unwrap();
3. 关键注意事项
- 不要强行要求
DEVICE_LOCAL内存同时具备HOST_VISIBLE属性,这在多数硬件上不支持,必须用staging资源中转 - staging图像必须添加
HOST_COHERENT标志,写入内存后无需手动flush就能让GPU读取到数据 - 目标图像的
usage必须包含TRANSFER_DST才能接收复制数据,SAMPLED保证能被采样器绑定使用
内容的提问来源于stack exchange,提问作者Mutant Bob
相关产品推荐
相关产品推荐

