Moved ui rendering to pipeline

master
Mark 2024-01-02 09:23:38 -08:00
parent a85a0f8188
commit 2e6f79ea31
Signed by: Mark
GPG Key ID: C6D63995FE72FD80
6 changed files with 158 additions and 40 deletions

View File

@ -44,8 +44,11 @@ pub const CONTENT_ROOT: &'static str = "./content";
/// Root directory of game textures
pub const TEXTURE_ROOT: &'static str = "./assets/render";
/// We can draw at most this many sprites on the screen.
pub const SPRITE_INSTANCE_LIMIT: u64 = 500;
/// We can draw at most this many object sprites on the screen.
pub const OBJECT_SPRITE_INSTANCE_LIMIT: u64 = 500;
/// We can draw at most this many ui sprites on the screen.
pub const UI_SPRITE_INSTANCE_LIMIT: u64 = 100;
/// Must be small enough to fit in an i32
pub const STARFIELD_INSTANCE_LIMIT: u64 = STARFIELD_COUNT * 24;
pub const STARFIELD_SPRITE_INSTANCE_LIMIT: u64 = STARFIELD_COUNT * 24;

View File

@ -0,0 +1,72 @@
struct InstanceInput {
@location(2) transform_matrix_0: vec4<f32>,
@location(3) transform_matrix_1: vec4<f32>,
@location(4) transform_matrix_2: vec4<f32>,
@location(5) transform_matrix_3: vec4<f32>,
@location(6) texture_idx: u32,
};
struct VertexInput {
@location(0) position: vec3<f32>,
@location(1) texture_coords: vec2<f32>,
}
struct VertexOutput {
@builtin(position) position: vec4<f32>,
@location(0) texture_coords: vec2<f32>,
@location(1) index: u32,
}
@group(1) @binding(0)
var<uniform> global: GlobalUniform;
struct GlobalUniform {
camera_position: vec2<f32>,
camera_zoom: vec2<f32>,
camera_zoom_limits: vec2<f32>,
window_size: vec2<f32>,
window_aspect: vec2<f32>,
starfield_texture: vec2<u32>,
starfield_tile_size: vec2<f32>,
starfield_size_limits: vec2<f32>,
};
@group(0) @binding(0)
var texture_array: binding_array<texture_2d<f32>>;
@group(0) @binding(1)
var sampler_array: binding_array<sampler>;
@vertex
fn vertex_main(
vertex: VertexInput,
instance: InstanceInput,
) -> VertexOutput {
let transform = mat4x4<f32>(
instance.transform_matrix_0,
instance.transform_matrix_1,
instance.transform_matrix_2,
instance.transform_matrix_3,
);
var out: VertexOutput;
out.position = transform * vec4<f32>(vertex.position, 1.0);
out.texture_coords = vertex.texture_coords;
out.index = instance.texture_idx;
return out;
}
@fragment
fn fragment_main(in: VertexOutput) -> @location(0) vec4<f32> {
return textureSampleLevel(
texture_array[in.index],
sampler_array[0],
in.texture_coords,
0.0
).rgba;
}

View File

@ -37,8 +37,9 @@ pub struct GPUState {
window_aspect: f32,
sprite_pipeline: wgpu::RenderPipeline,
object_pipeline: wgpu::RenderPipeline,
starfield_pipeline: wgpu::RenderPipeline,
ui_pipeline: wgpu::RenderPipeline,
starfield: Starfield,
texture_array: TextureArray,
@ -47,8 +48,9 @@ pub struct GPUState {
}
struct VertexBuffers {
sprite: Rc<VertexBuffer>,
object: Rc<VertexBuffer>,
starfield: Rc<VertexBuffer>,
ui: Rc<VertexBuffer>,
}
impl GPUState {
@ -117,12 +119,12 @@ impl GPUState {
}
let vertex_buffers = VertexBuffers {
sprite: Rc::new(VertexBuffer::new::<TexturedVertex, SpriteInstance>(
"sprite",
object: Rc::new(VertexBuffer::new::<TexturedVertex, SpriteInstance>(
"objecte",
&device,
Some(SPRITE_VERTICES),
Some(SPRITE_INDICES),
galactica_constants::SPRITE_INSTANCE_LIMIT,
galactica_constants::OBJECT_SPRITE_INSTANCE_LIMIT,
)),
starfield: Rc::new(VertexBuffer::new::<TexturedVertex, StarfieldInstance>(
@ -130,7 +132,15 @@ impl GPUState {
&device,
Some(SPRITE_VERTICES),
Some(SPRITE_INDICES),
galactica_constants::STARFIELD_INSTANCE_LIMIT,
galactica_constants::STARFIELD_SPRITE_INSTANCE_LIMIT,
)),
ui: Rc::new(VertexBuffer::new::<TexturedVertex, SpriteInstance>(
"ui",
&device,
Some(SPRITE_VERTICES),
Some(SPRITE_INDICES),
galactica_constants::UI_SPRITE_INSTANCE_LIMIT,
)),
};
@ -145,15 +155,15 @@ impl GPUState {
];
// Create render pipelines
let sprite_pipeline = PipelineBuilder::new("sprite", &device)
let object_pipeline = PipelineBuilder::new("object", &device)
.set_shader(include_str!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/shaders/",
"sprite.wgsl"
"object.wgsl"
)))
.set_format(config.format)
.set_triangle(true)
.set_vertex_buffer(&vertex_buffers.sprite)
.set_vertex_buffer(&vertex_buffers.object)
.set_bind_group_layouts(bind_group_layouts)
.build();
@ -169,6 +179,18 @@ impl GPUState {
.set_bind_group_layouts(bind_group_layouts)
.build();
let ui_pipeline = PipelineBuilder::new("ui", &device)
.set_shader(include_str!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/shaders/",
"ui.wgsl"
)))
.set_format(config.format)
.set_triangle(true)
.set_vertex_buffer(&vertex_buffers.ui)
.set_bind_group_layouts(bind_group_layouts)
.build();
let mut starfield = Starfield::new();
starfield.regenerate();
@ -182,8 +204,9 @@ impl GPUState {
window_size,
window_aspect,
sprite_pipeline,
object_pipeline,
starfield_pipeline,
ui_pipeline,
starfield,
texture_array,
@ -404,14 +427,14 @@ impl GPUState {
/// Will panic if SPRITE_INSTANCE_LIMIT is exceeded.
///
/// This is only called inside self.render()
fn make_sprite_instances(
fn update_sprite_instances(
&self,
camera_zoom: f32,
camera_pos: Point2<f32>,
objects: &Vec<ObjectSprite>,
ui: &Vec<UiSprite>,
) -> Vec<SpriteInstance> {
let mut instances: Vec<SpriteInstance> = Vec::new();
) -> (usize, usize) {
let mut object_instances: Vec<SpriteInstance> = Vec::new();
// Game coordinates (relative to camera) of ne and sw corners of screen.
// Used to skip off-screen sprites.
@ -419,20 +442,45 @@ impl GPUState {
let clip_sw = Point2::from((self.window_aspect, -1.0)) * camera_zoom;
for s in objects {
self.push_object_sprite(camera_zoom, camera_pos, &mut instances, clip_ne, clip_sw, s);
}
for s in ui {
self.push_ui_sprite(&mut instances, s);
self.push_object_sprite(
camera_zoom,
camera_pos,
&mut object_instances,
clip_ne,
clip_sw,
s,
);
}
// Enforce sprite limit
if instances.len() as u64 > galactica_constants::SPRITE_INSTANCE_LIMIT {
if object_instances.len() as u64 > galactica_constants::OBJECT_SPRITE_INSTANCE_LIMIT {
// TODO: no panic, handle this better.
panic!("Sprite limit exceeded!")
}
return instances;
self.queue.write_buffer(
&self.vertex_buffers.object.instances,
0,
bytemuck::cast_slice(&object_instances),
);
let mut ui_instances: Vec<SpriteInstance> = Vec::new();
for s in ui {
self.push_ui_sprite(&mut ui_instances, s);
}
if ui_instances.len() as u64 > galactica_constants::UI_SPRITE_INSTANCE_LIMIT {
panic!("Ui sprite limit exceeded!")
}
self.queue.write_buffer(
&self.vertex_buffers.ui.instances,
0,
bytemuck::cast_slice(&ui_instances),
);
return (object_instances.len(), ui_instances.len());
}
/// Make a StarfieldInstance for each star that needs to be drawn.
@ -510,13 +558,8 @@ impl GPUState {
);
// Create sprite instances
let sprite_instances =
self.make_sprite_instances(camera_zoom, camera_pos, object_sprites, ui_sprites);
self.queue.write_buffer(
&self.vertex_buffers.sprite.instances,
0,
bytemuck::cast_slice(&sprite_instances),
);
let (n_object, n_ui) =
self.update_sprite_instances(camera_zoom, camera_pos, object_sprites, ui_sprites);
// These should match the indices in each shader,
// and should each have a corresponding bind group layout.
@ -533,13 +576,14 @@ impl GPUState {
);
// Sprite pipeline
self.vertex_buffers.sprite.set_in_pass(&mut render_pass);
render_pass.set_pipeline(&self.sprite_pipeline);
render_pass.draw_indexed(
0..SPRITE_INDICES.len() as u32,
0,
0..sprite_instances.len() as _,
);
self.vertex_buffers.object.set_in_pass(&mut render_pass);
render_pass.set_pipeline(&self.object_pipeline);
render_pass.draw_indexed(0..SPRITE_INDICES.len() as u32, 0, 0..n_object as _);
// Ui pipeline
self.vertex_buffers.ui.set_in_pass(&mut render_pass);
render_pass.set_pipeline(&self.ui_pipeline);
render_pass.draw_indexed(0..SPRITE_INDICES.len() as u32, 0, 0..n_ui as _);
// begin_render_pass borrows encoder mutably, so we can't call finish()
// without dropping this variable.

View File

@ -94,7 +94,7 @@ impl Starfield {
while ((nw_tile.x * 2 + 1)
* (nw_tile.y * 2 + 1)
* galactica_constants::STARFIELD_COUNT as i32)
> galactica_constants::STARFIELD_INSTANCE_LIMIT as i32
> galactica_constants::STARFIELD_SPRITE_INSTANCE_LIMIT as i32
{
nw_tile -= Vector2::from((1, 1));
}
@ -119,7 +119,7 @@ impl Starfield {
}
// Enforce starfield limit
if instances.len() as u64 > galactica_constants::STARFIELD_INSTANCE_LIMIT {
if instances.len() as u64 > galactica_constants::STARFIELD_SPRITE_INSTANCE_LIMIT {
unreachable!("Starfield limit exceeded!")
}
self.instance_count = instances.len() as u32;

View File

@ -33,7 +33,6 @@ pub fn build_radar(
};
let m = d.magnitude() / radar_range;
let d = d / radar_range * (radar_size / 2.0);
println!("{:?}", d);
if m < 0.8 {
let texture = ct.get_texture_handle("ui::radarframe");
let dimensions = Point2 {