From 296c7a2ef57c19cd43552f0ad61cfd3d573245f3 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Sun, 21 Jan 2024 10:54:04 -0800 Subject: [PATCH] Use an integer texture instead of a storage buffer --- bin/boilerplate.rs | 2 -- lib/ffi/src/lib.rs | 2 -- res/shader/surface.inc.wgsl | 11 ++---- src/render/terrain.rs | 72 +++++++++++++++++++++++++------------ 4 files changed, 52 insertions(+), 35 deletions(-) diff --git a/bin/boilerplate.rs b/bin/boilerplate.rs index 9c90273..a2c8473 100644 --- a/bin/boilerplate.rs +++ b/bin/boilerplate.rs @@ -90,8 +90,6 @@ impl Harness { Terrain::RayTraced { .. } | Terrain::Sliced { .. } | Terrain::Painted { .. } => { wgpu::Limits { max_texture_dimension_2d: adapter_limits.max_texture_dimension_2d, - max_storage_buffers_per_shader_stage: 1, - max_storage_buffer_binding_size: terrain_buffer_size, ..wgpu::Limits::downlevel_webgl2_defaults() } } diff --git a/lib/ffi/src/lib.rs b/lib/ffi/src/lib.rs index d67be92..1f1a297 100644 --- a/lib/ffi/src/lib.rs +++ b/lib/ffi/src/lib.rs @@ -234,8 +234,6 @@ pub extern "C" fn rv_init(desc: InitDescriptor) -> Option> let limits = wgpu::Limits { max_texture_dimension_2d: adapter_limits.max_texture_dimension_2d, - max_storage_buffers_per_shader_stage: 1, - max_storage_buffer_binding_size: 1 << 28, ..wgpu::Limits::downlevel_webgl2_defaults() }; diff --git a/res/shader/surface.inc.wgsl b/res/shader/surface.inc.wgsl index a1de43e..831e8db 100644 --- a/res/shader/surface.inc.wgsl +++ b/res/shader/surface.inc.wgsl @@ -5,12 +5,9 @@ struct SurfaceConstants { terrain_bits: u32, // low 4 bits = shift, high 4 bits = mask delta_mode: u32, // low 8 bits = power, higher 16 bits = mask }; -struct TerrainData { - inner: array, -}; @group(1) @binding(0) var u_Surface: SurfaceConstants; -@group(1) @binding(2) var b_Terrain: TerrainData; +@group(1) @binding(2) var t_Terrain: texture_2d; const c_DoubleLevelMask: u32 = 64u; const c_ShadowMask: u32 = 128u; @@ -50,9 +47,7 @@ fn get_map_coordinates(pos: vec2) -> vec2 { fn get_surface_impl(tci: vec2) -> Surface { var suf: Surface; - let tc_index = tci.y * i32(u_Surface.texture_scale.x) + tci.x; - let data_raw = b_Terrain.inner[tc_index / 2]; - let data = (vec4(data_raw) >> vec4(0u, 8u, 16u, 24u)) & vec4(0xFFu); + let data = textureLoad(t_Terrain, vec2(tci.x/2, tci.y), 0); suf.is_shadowed = (data.y & c_ShadowMask) != 0u; let scale = u_Surface.texture_scale.z / 256.0; @@ -74,7 +69,7 @@ fn get_surface_impl(tci: vec2) -> Surface { } suf.mid_alt = f32(mid) * scale; } else { - let subdata = select(data.xy, data.zw, (tc_index & 1) != 0); + let subdata = select(data.xy, data.zw, (tci.x & 1) != 0); let altitude = f32(subdata.x) * scale; let ty = get_terrain_type(subdata.y); suf.low_type = ty; diff --git a/src/render/terrain.rs b/src/render/terrain.rs index 873cf8e..742ea0f 100644 --- a/src/render/terrain.rs +++ b/src/render/terrain.rs @@ -298,7 +298,7 @@ pub struct Context { raytrace_geo: Geometry, kind: Kind, shadow_kind: ShadowKind, - terrain_buf: wgpu::Buffer, + terrain_texture: wgpu::Texture, palette_texture: wgpu::Texture, pub flood: Flood, pub dirty_rects: Vec, @@ -698,11 +698,18 @@ impl Context { }) .collect::>(); - let terrain_buf = gfx.device.create_buffer(&wgpu::BufferDescriptor { + let terrain_texture = gfx.device.create_texture(&wgpu::TextureDescriptor { label: Some("Terrain data"), - size: (extent.width * extent.height) as wgpu::BufferAddress * 2, - usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::STORAGE, - mapped_at_creation: false, + size: wgpu::Extent3d { + width: extent.width / 2, + ..extent + }, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: wgpu::TextureFormat::Rgba8Uint, + view_formats: &[], + usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST, }); let flood_texture = gfx.device.create_texture(&wgpu::TextureDescriptor { @@ -792,15 +799,11 @@ impl Context { // terrain data wgpu::BindGroupLayoutEntry { binding: 2, - visibility: if supports_vertex_storage { - wgpu::ShaderStages::all() - } else { - wgpu::ShaderStages::FRAGMENT | wgpu::ShaderStages::COMPUTE - }, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Storage { read_only: true }, - has_dynamic_offset: false, - min_binding_size: None, + visibility: wgpu::ShaderStages::all(), + ty: wgpu::BindingType::Texture { + view_dimension: wgpu::TextureViewDimension::D2, + sample_type: wgpu::TextureSampleType::Uint, + multisampled: false, }, count: None, }, @@ -881,7 +884,9 @@ impl Context { }, wgpu::BindGroupEntry { binding: 2, - resource: terrain_buf.as_entire_binding(), + resource: wgpu::BindingResource::TextureView( + &terrain_texture.create_view(&wgpu::TextureViewDescriptor::default()), + ), }, wgpu::BindGroupEntry { binding: 4, @@ -1365,7 +1370,7 @@ impl Context { raytrace_geo, kind, shadow_kind, - terrain_buf, + terrain_texture, palette_texture: palette.texture, flood: Flood { texture: flood_texture, @@ -1569,6 +1574,8 @@ impl Context { if !dr.need_upload { continue; } + //Note: we are uploading the whole rows currently. We could instead upload + // only the relevant sub-rectangle, but managing `bytes_per_row` becomes annoying. let total_size = dr.rect.h as wgpu::BufferAddress * level.size.0 as wgpu::BufferAddress * 2; @@ -1588,15 +1595,34 @@ impl Context { } } } - staging_buf.unmap(); - encoder.copy_buffer_to_buffer( - &staging_buf, - 0, - &self.terrain_buf, - dr.rect.y as wgpu::BufferAddress * level.size.0 as wgpu::BufferAddress * 2, - total_size, + + encoder.copy_buffer_to_texture( + wgpu::ImageCopyBuffer { + buffer: &staging_buf, + layout: wgpu::ImageDataLayout { + offset: 0, + bytes_per_row: Some(level.size.0 as u32 * 2), + rows_per_image: None, + }, + }, + wgpu::ImageCopyTexture { + texture: &self.terrain_texture, + mip_level: 0, + origin: wgpu::Origin3d { + x: 0, + y: dr.rect.y as u32, + z: 0, + }, + aspect: wgpu::TextureAspect::All, + }, + wgpu::Extent3d { + width: level.size.0 as u32 / 2, + height: dr.rect.h as u32, + depth_or_array_layers: 1, + }, ); + staging_buf.unmap(); dr.need_upload = false; }