如何为D2Array上传尺寸小于4x4的BC7多级纹理(Mipmaps)?
问题:BC7压缩纹理在多层D2Array小尺寸Mipmap时的wgpu写入错误
我正在使用intel_tex_2 crate压缩图像,通过wgpu crate上传,纹理格式为Bc7RgbaUnorm。当D2Array层数大于1且mipmap尺寸小于4x4时出现问题:write_texture会尝试读取远超4x4块的数据。例如2x2x2纹理会报错:
Copy of 0..80 would end up overrunning the bounds of the Source buffer of size 32
每增加一层会多占用48字节。我尝试过多种填充方式,但显示均不正确。以下是最小复现示例:
Cargo.toml
[package] name = "test_bed" version = "0.1.0" edition = "2021" [dependencies] pollster = "0.3" winit = "0.30.3" wgpu = "0.20.1" intel_tex_2 = "0.4.0"
main.rs
use winit::{application::ApplicationHandler, event_loop::{ActiveEventLoop, EventLoop}, window::Window}; // 0.30.3 use wgpu::{InstanceDescriptor, InstanceFlags, Dx12Compiler, Gles3MinorVersion, Origin3d, Features}; // 0.20.1 use intel_tex_2::bc7; // 0.4.0 pub fn compression_test(device: &wgpu::Device, queue: &wgpu::Queue) { assert!(device.features().contains(Features::TEXTURE_COMPRESSION_BC)); // 3 mipmap levels let mut rgbas = vec![ Vec::new(), Vec::new(), Vec::new(), ]; // 4x4 top level let size = wgpu::Extent3d { width: 4, height: 4, depth_or_array_layers: 2, }; for _ in 0..size.depth_or_array_layers { rgbas[0].extend_from_slice(&BLUE4X4); rgbas[1].extend_from_slice(&GREEN2X2); rgbas[2].extend_from_slice(&RED1X1); } let texture = device.create_texture(&wgpu::TextureDescriptor { label: None, size, mip_level_count: rgbas.len() as u32, sample_count: 1, dimension: wgpu::TextureDimension::D2, format: wgpu::TextureFormat::Bc7RgbaUnormSrgb, usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST, view_formats: &[], }); for (mip_level, rgba) in rgbas.into_iter().enumerate() { let mip_size = size.mip_level_size(mip_level as u32, wgpu::TextureDimension::D2); let layer_bytes = (mip_size.width * mip_size.height * 4) as usize; let compressed_layer_bytes = bc7::calc_output_size(mip_size.width, mip_size.height).max(16); let compressed_layer_stride = layer_bytes.max(compressed_layer_bytes); let mut compressed = vec![0u8; rgba.len().max(compressed_layer_stride * mip_size.depth_or_array_layers as usize)]; let data_iter = rgba.chunks(layer_bytes); let compressed_iter = compressed.chunks_mut(compressed_layer_stride); for (data, compressed) in data_iter.zip(compressed_iter) { let surface = intel_tex_2::RgbaSurface { width: mip_size.width, height: mip_size.height, stride: mip_size.width * 4, data, }; bc7::compress_blocks_into(&bc7::opaque_ultra_fast_settings(), &surface, &mut compressed[..compressed_layer_bytes]); } let physical_size = mip_size.physical_size(wgpu::TextureFormat::Bc7RgbaUnormSrgb); println!("write: {} bytes, size {:?} padded to {:?}", compressed.len(), mip_size, physical_size); queue.write_texture( wgpu::ImageCopyTexture { aspect: wgpu::TextureAspect::All, texture: &texture, mip_level: mip_level as u32, origin: Origin3d::ZERO, }, &compressed, wgpu::ImageDataLayout { offset: 0, bytes_per_row: Some(4 * physical_size.width), rows_per_image: Some(physical_size.height), }, physical_size, ); } } fn main() { let event_loop: EventLoop<()> = EventLoop::with_user_event().build().unwrap(); let _ = event_loop.run_app(&mut AppHandler); } struct AppHandler; impl ApplicationHandler<()> for AppHandler { fn resumed(&mut self, event_loop: &ActiveEventLoop) { let window = window(event_loop); let (device, queue) = pollster::block_on(wgpu(window)); // 0.3.0 compression_test(&device, &queue); event_loop.exit(); } fn window_event(&mut self, _event_loop: &winit::event_loop::ActiveEventLoop, _window_id: winit::window::WindowId, _event: winit::event::WindowEvent) {} } pub fn window(event_loop: &ActiveEventLoop) -> Window { let window_attributes = Window::default_attributes(); event_loop.create_window(window_attributes).unwrap() } pub async fn wgpu(window: Window) -> (wgpu::Device, wgpu::Queue) { let window_size = window.inner_size(); // WGPU let instance = wgpu::Instance::new(InstanceDescriptor { backends: wgpu::Backends::DX12 | wgpu::Backends::METAL | wgpu::Backends::VULKAN | wgpu::Backends::GL, flags: InstanceFlags::default(), dx12_shader_compiler: Dx12Compiler::default(), gles_minor_version: Gles3MinorVersion::Automatic, }); let surface = instance.create_surface(window).unwrap(); let adapter = instance .request_adapter(&wgpu::RequestAdapterOptions { power_preference: wgpu::PowerPreference::HighPerformance, compatible_surface: Some(&surface), force_fallback_adapter: false, }).await.unwrap(); let required_features = wgpu::Features::default() | wgpu::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING | wgpu::Features::TEXTURE_BINDING_ARRAY | wgpu::Features::BUFFER_BINDING_ARRAY | wgpu::Features::DEPTH_CLIP_CONTROL | wgpu::Features::ADDRESS_MODE_CLAMP_TO_ZERO | wgpu::Features::ADDRESS_MODE_CLAMP_TO_BORDER | wgpu::Features::TEXTURE_COMPRESSION_BC; let (device, queue) = adapter.request_device( &wgpu::DeviceDescriptor { label: None, required_features, required_limits: adapter.limits(), }, None, ).await.unwrap(); let surface_capabilities = surface.get_capabilities(&adapter); let surface_format = surface_capabilities.formats.iter() .copied() .filter(|f| f.is_srgb()) .next() .unwrap_or(surface_capabilities.formats[0]); let config = wgpu::SurfaceConfiguration { usage: wgpu::TextureUsages::RENDER_ATTACHMENT, format: surface_format, width: window_size.width, height: window_size.height, present_mode: wgpu::PresentMode::Fifo, desired_maximum_frame_latency: 2, alpha_mode: surface_capabilities.alpha_modes[0], view_formats: vec![], }; surface.configure(&device, &config); (device, queue) } pub const BLUE4X4: [u8; 4 * 4 * 4] = [ 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, ]; pub const GREEN2X2: [u8; 2 * 2 * 4] = [ 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, ]; pub const RED1X1: [u8; 1 * 1 * 4] = [ 255, 0, 0, 255 ];
内容的提问来源于stack exchange,提问作者VrydayVrything
相关产品推荐
相关产品推荐

