dynamic uniform offsets

This commit is contained in:
Jonas H
2026-03-05 15:03:56 +01:00
parent 28f8c65571
commit 5e5c6a47e7
5 changed files with 187 additions and 43 deletions

View File

@@ -1,5 +1,7 @@
use crate::texture::{DitherTextures, FlowmapTexture}; use crate::texture::{DitherTextures, FlowmapTexture};
use std::num::NonZeroU64;
use super::types::Uniforms;
use super::Renderer; use super::Renderer;
impl Renderer impl Renderer
@@ -24,7 +26,11 @@ impl Renderer
entries: &[ entries: &[
wgpu::BindGroupEntry { wgpu::BindGroupEntry {
binding: 0, binding: 0,
resource: uniform_buffer.as_entire_binding(), resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
buffer: uniform_buffer,
offset: 0,
size: NonZeroU64::new(std::mem::size_of::<Uniforms>() as u64),
}),
}, },
wgpu::BindGroupEntry { wgpu::BindGroupEntry {
binding: 1, binding: 1,

View File

@@ -102,32 +102,31 @@ impl DebugOverlay
cache: None, cache: None,
}); });
let pipeline_snow_light = let pipeline_snow_light = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { label: Some("Snow Light Debug Pipeline"),
label: Some("Snow Light Debug Pipeline"), layout: Some(&snow_light_layout),
layout: Some(&snow_light_layout), vertex: wgpu::VertexState {
vertex: wgpu::VertexState { module: &shader,
module: &shader, entry_point: Some("vs_main"),
entry_point: Some("vs_main"), buffers: &[ScreenVertex::desc()],
buffers: &[ScreenVertex::desc()], compilation_options: Default::default(),
compilation_options: Default::default(), },
}, fragment: Some(wgpu::FragmentState {
fragment: Some(wgpu::FragmentState { module: &shader,
module: &shader, entry_point: Some("fs_snow_light"),
entry_point: Some("fs_snow_light"), targets: &[Some(color_target)],
targets: &[Some(color_target)], compilation_options: Default::default(),
compilation_options: Default::default(), }),
}), primitive: wgpu::PrimitiveState {
primitive: wgpu::PrimitiveState { topology: wgpu::PrimitiveTopology::TriangleList,
topology: wgpu::PrimitiveTopology::TriangleList, cull_mode: None,
cull_mode: None, ..Default::default()
..Default::default() },
}, depth_stencil: None,
depth_stencil: None, multisample: wgpu::MultisampleState::default(),
multisample: wgpu::MultisampleState::default(), multiview_mask: None,
multiview_mask: None, cache: None,
cache: None, });
});
Self { Self {
pipeline_shadow, pipeline_shadow,

View File

@@ -6,12 +6,19 @@ mod types;
pub use types::{DrawCall, Pipeline, Spotlight, SpotlightRaw, Uniforms, MAX_SPOTLIGHTS}; pub use types::{DrawCall, Pipeline, Spotlight, SpotlightRaw, Uniforms, MAX_SPOTLIGHTS};
use crate::entity::EntityHandle;
use crate::debug::DebugMode; use crate::debug::DebugMode;
use crate::postprocess::{create_blit_pipeline, create_fullscreen_quad, LowResFramebuffer}; use crate::postprocess::{create_blit_pipeline, create_fullscreen_quad, LowResFramebuffer};
use crate::texture::{DitherTextures, FlowmapTexture}; use crate::texture::{DitherTextures, FlowmapTexture};
use pipeline::{create_debug_lines_pipeline, create_main_pipeline, create_snow_clipmap_pipeline, use pipeline::{
create_wireframe_pipeline}; create_debug_lines_pipeline, create_main_pipeline, create_snow_clipmap_pipeline,
create_wireframe_pipeline,
};
use std::cell::RefCell; use std::cell::RefCell;
use std::num::NonZeroU64;
const MAX_DRAW_CALLS: usize = 64;
pub struct Renderer pub struct Renderer
{ {
@@ -62,6 +69,8 @@ pub struct Renderer
snow_light_accumulation: Option<crate::snow_light::SnowLightAccumulation>, snow_light_accumulation: Option<crate::snow_light::SnowLightAccumulation>,
snow_light_bound: bool, snow_light_bound: bool,
pub selected_entity: Option<EntityHandle>,
} }
impl Renderer impl Renderer
@@ -90,8 +99,9 @@ impl Renderer
.await .await
.map_err(|_| "Failed to find adapter")?; .map_err(|_| "Failed to find adapter")?;
let wireframe_supported = let wireframe_supported = adapter
adapter.features().contains(wgpu::Features::POLYGON_MODE_LINE); .features()
.contains(wgpu::Features::POLYGON_MODE_LINE);
let required_features = if wireframe_supported let required_features = if wireframe_supported
{ {
wgpu::Features::POLYGON_MODE_LINE wgpu::Features::POLYGON_MODE_LINE
@@ -129,7 +139,7 @@ impl Renderer
let uniform_buffer = device.create_buffer(&wgpu::BufferDescriptor { let uniform_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Uniform Buffer"), label: Some("Uniform Buffer"),
size: std::mem::size_of::<Uniforms>() as wgpu::BufferAddress, size: (std::mem::size_of::<Uniforms>() * MAX_DRAW_CALLS) as wgpu::BufferAddress,
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false, mapped_at_creation: false,
}); });
@@ -306,8 +316,8 @@ impl Renderer
visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT, visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Buffer { ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform, ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false, has_dynamic_offset: true,
min_binding_size: None, min_binding_size: NonZeroU64::new(std::mem::size_of::<Uniforms>() as u64),
}, },
count: None, count: None,
}, },
@@ -463,15 +473,22 @@ impl Renderer
let wireframe_pipeline = if wireframe_supported let wireframe_pipeline = if wireframe_supported
{ {
Some(create_wireframe_pipeline(&device, config.format, &bind_group_layout)) Some(create_wireframe_pipeline(
&device,
config.format,
&bind_group_layout,
))
} }
else else
{ {
None None
}; };
let debug_lines_pipeline = let debug_lines_pipeline = Some(create_debug_lines_pipeline(
Some(create_debug_lines_pipeline(&device, config.format, &bind_group_layout)); &device,
config.format,
&bind_group_layout,
));
let debug_overlay = Some(debug_overlay::DebugOverlay::new(&device, config.format)); let debug_overlay = Some(debug_overlay::DebugOverlay::new(&device, config.format));
@@ -533,6 +550,7 @@ impl Renderer
dummy_snow_light_sampler, dummy_snow_light_sampler,
snow_light_accumulation: None, snow_light_accumulation: None,
snow_light_bound: false, snow_light_bound: false,
selected_entity: None,
}) })
} }
@@ -585,6 +603,9 @@ impl Renderer
label: Some("Render Encoder"), label: Some("Render Encoder"),
}); });
let uniform_size = std::mem::size_of::<Uniforms>() as u64;
let mut uniform_slot: u64 = 0;
for (i, draw_call) in draw_calls.iter().enumerate() for (i, draw_call) in draw_calls.iter().enumerate()
{ {
let uniforms = Uniforms::new( let uniforms = Uniforms::new(
@@ -603,8 +624,15 @@ impl Renderer
&self.spotlights, &self.spotlights,
debug_mode.as_u32(), debug_mode.as_u32(),
); );
self.queue
.write_buffer(&self.uniform_buffer, 0, bytemuck::cast_slice(&[uniforms])); let offset = uniform_slot * uniform_size;
uniform_slot += 1;
self.queue.write_buffer(
&self.uniform_buffer,
offset,
bytemuck::cast_slice(&[uniforms]),
);
{ {
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
@@ -660,7 +688,7 @@ impl Renderer
.unwrap_or(&self.standard_pipeline), .unwrap_or(&self.standard_pipeline),
}; };
render_pass.set_pipeline(pipeline); render_pass.set_pipeline(pipeline);
render_pass.set_bind_group(0, &self.bind_group, &[]); render_pass.set_bind_group(0, &self.bind_group, &[offset as u32]);
if let Some(ref displacement_bind_group) = draw_call.displacement_bind_group if let Some(ref displacement_bind_group) = draw_call.displacement_bind_group
{ {
@@ -686,7 +714,10 @@ impl Renderer
{ {
for draw_call in draw_calls.iter() for draw_call in draw_calls.iter()
{ {
if matches!(draw_call.pipeline, Pipeline::SnowClipmap | Pipeline::DebugLines) if matches!(
draw_call.pipeline,
Pipeline::SnowClipmap | Pipeline::DebugLines
)
{ {
continue; continue;
} }
@@ -707,9 +738,11 @@ impl Renderer
&self.spotlights, &self.spotlights,
4, 4,
); );
let wire_offset = uniform_slot * uniform_size;
uniform_slot += 1;
self.queue.write_buffer( self.queue.write_buffer(
&self.uniform_buffer, &self.uniform_buffer,
0, wire_offset,
bytemuck::cast_slice(&[uniforms]), bytemuck::cast_slice(&[uniforms]),
); );
@@ -742,7 +775,7 @@ impl Renderer
}); });
wire_pass.set_pipeline(wireframe_pipeline); wire_pass.set_pipeline(wireframe_pipeline);
wire_pass.set_bind_group(0, &self.bind_group, &[]); wire_pass.set_bind_group(0, &self.bind_group, &[wire_offset as u32]);
wire_pass.set_vertex_buffer(0, draw_call.vertex_buffer.slice(..)); wire_pass.set_vertex_buffer(0, draw_call.vertex_buffer.slice(..));
if let Some(ref instance_buffer) = draw_call.instance_buffer if let Some(ref instance_buffer) = draw_call.instance_buffer
@@ -764,6 +797,99 @@ impl Renderer
} }
} }
if let Some(selected) = self.selected_entity
{
if let Some(ref wireframe_pipeline) = self.wireframe_pipeline
{
for draw_call in draw_calls.iter()
{
if draw_call.entity != Some(selected)
{
continue;
}
if matches!(
draw_call.pipeline,
Pipeline::SnowClipmap | Pipeline::DebugLines
)
{
continue;
}
let uniforms = Uniforms::new(
draw_call.model,
*view,
*projection,
&light_view_projections,
camera_position,
player_position,
self.terrain_height_scale,
time,
self.shadow_bias,
draw_call.tile_scale,
draw_call.enable_dissolve,
draw_call.enable_snow_light,
&self.spotlights,
4,
);
let sel_offset = uniform_slot * uniform_size;
uniform_slot += 1;
self.queue.write_buffer(
&self.uniform_buffer,
sel_offset,
bytemuck::cast_slice(&[uniforms]),
);
{
let mut sel_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("Selection Wireframe Pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &self.framebuffer.view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Load,
store: wgpu::StoreOp::Store,
},
depth_slice: None,
})],
depth_stencil_attachment: Some(
wgpu::RenderPassDepthStencilAttachment {
view: &self.framebuffer.depth_view,
depth_ops: Some(wgpu::Operations {
load: wgpu::LoadOp::Load,
store: wgpu::StoreOp::Store,
}),
stencil_ops: None,
},
),
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
sel_pass.set_pipeline(wireframe_pipeline);
sel_pass.set_bind_group(0, &self.bind_group, &[sel_offset as u32]);
sel_pass.set_vertex_buffer(0, draw_call.vertex_buffer.slice(..));
if let Some(ref instance_buffer) = draw_call.instance_buffer
{
sel_pass.set_vertex_buffer(1, instance_buffer.slice(..));
}
sel_pass.set_index_buffer(
draw_call.index_buffer.slice(..),
wgpu::IndexFormat::Uint32,
);
sel_pass.draw_indexed(
0..draw_call.num_indices,
0,
0..draw_call.num_instances,
);
}
}
}
}
if debug_mode == DebugMode::ShadowMap if debug_mode == DebugMode::ShadowMap
{ {
if let Some(ref overlay) = self.debug_overlay if let Some(ref overlay) = self.debug_overlay
@@ -1074,3 +1200,12 @@ pub fn update_spotlights(spotlights: Vec<Spotlight>)
renderer.spotlights = spotlights; renderer.spotlights = spotlights;
}); });
} }
pub fn set_selected_entity(entity: Option<EntityHandle>)
{
GLOBAL_RENDERER.with(|r| {
let mut renderer = r.borrow_mut();
let renderer = renderer.as_mut().expect("Renderer not set");
renderer.selected_entity = entity;
});
}

View File

@@ -1,6 +1,8 @@
use bytemuck::{Pod, Zeroable}; use bytemuck::{Pod, Zeroable};
use glam::Mat4; use glam::Mat4;
use crate::entity::EntityHandle;
pub const MAX_SPOTLIGHTS: usize = 4; pub const MAX_SPOTLIGHTS: usize = 4;
#[repr(C)] #[repr(C)]
@@ -156,4 +158,5 @@ pub struct DrawCall
pub enable_dissolve: bool, pub enable_dissolve: bool,
pub enable_snow_light: bool, pub enable_snow_light: bool,
pub displacement_bind_group: Option<wgpu::BindGroup>, pub displacement_bind_group: Option<wgpu::BindGroup>,
pub entity: Option<EntityHandle>,
} }

View File

@@ -60,6 +60,7 @@ pub fn render_system(world: &World) -> Vec<DrawCall>
enable_dissolve: mesh_component.enable_dissolve, enable_dissolve: mesh_component.enable_dissolve,
enable_snow_light: mesh_component.enable_snow_light, enable_snow_light: mesh_component.enable_snow_light,
displacement_bind_group: None, displacement_bind_group: None,
entity: Some(entity),
}) })
}) })
.collect() .collect()