diff options
Diffstat (limited to 'src/graphics.rs')
| -rw-r--r-- | src/graphics.rs | 93 |
1 files changed, 88 insertions, 5 deletions
diff --git a/src/graphics.rs b/src/graphics.rs index 2b96a7a..bab9f05 100644 --- a/src/graphics.rs +++ b/src/graphics.rs @@ -1,12 +1,27 @@ -use std::{borrow::Cow, sync::Arc}; +use glam::{Vec2, Vec4}; +use std::{borrow::Cow, mem, slice, sync::Arc}; use wgpu::*; use winit::{dpi::PhysicalSize, event_loop::OwnedDisplayHandle, window::Window}; +pub mod command; mod font; mod texture; +use command::RenderCommand; use font::CharAtlas; +pub const INSTANCE_MAX_COUNT: u64 = 128; + +#[repr(C)] +struct InstanceData { + // 中央原点 左→右[-1, 1] 下→上[-1, 1] + position: Vec2, + // 拡大率 (width, height): [0, 1]^2 + scale: Vec2, + // (u, v, width, height): [0, 1]^4 + uv: Vec4, +} + pub enum AcquireSurfaceTextureResult { Ok(SurfaceTexture), ShouldSkip, @@ -22,6 +37,8 @@ pub struct Renderer { present_mode: PresentMode, pipeline: RenderPipeline, + instances: Buffer, + trans_bind_group: BindGroup, char_atlas: CharAtlas, fixed_bind_group: BindGroup, } @@ -61,6 +78,19 @@ impl Renderer { source: ShaderSource::Wgsl(Cow::Borrowed(include_str!("shader.wgsl"))), }); + let trans_bind_group_layout = device.create_bind_group_layout(&BindGroupLayoutDescriptor { + label: None, + entries: &[BindGroupLayoutEntry { + binding: 0, + visibility: ShaderStages::VERTEX, + ty: BindingType::Buffer { + ty: BufferBindingType::Storage { read_only: true }, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, + }], + }); let fixed_bind_group_layout = device.create_bind_group_layout(&BindGroupLayoutDescriptor { label: None, entries: &[ @@ -84,7 +114,10 @@ impl Renderer { }); let pipeline_layout = device.create_pipeline_layout(&PipelineLayoutDescriptor { label: None, - bind_group_layouts: &[Some(&fixed_bind_group_layout)], + bind_group_layouts: &[ + Some(&trans_bind_group_layout), + Some(&fixed_bind_group_layout), + ], immediate_size: 0, }); let pipeline = device.create_render_pipeline(&RenderPipelineDescriptor { @@ -117,6 +150,26 @@ impl Renderer { cache: None, }); + // NOTE: インスタンスバッファは固定長で使いまわすので、ここで1個だけ作成すれば良い。 + let instances = device.create_buffer(&BufferDescriptor { + label: None, + size: mem::size_of::<InstanceData>() as u64 * INSTANCE_MAX_COUNT, + usage: BufferUsages::STORAGE | BufferUsages::COPY_DST, + mapped_at_creation: false, + }); + let trans_bind_group = device.create_bind_group(&BindGroupDescriptor { + label: None, + layout: &trans_bind_group_layout, + entries: &[BindGroupEntry { + binding: 0, + resource: BindingResource::Buffer(BufferBinding { + buffer: &instances, + offset: 0, + size: None, + }), + }], + }); + // NOTE: 本ゲームではlinearなサンプラしか用意しないので、ここで1個だけ作成すれば良い。 let sampler = device.create_sampler(&SamplerDescriptor { label: None, @@ -153,6 +206,8 @@ impl Renderer { view_format, present_mode, pipeline, + instances, + trans_bind_group, char_atlas, fixed_bind_group, }; @@ -185,7 +240,12 @@ impl Renderer { } } - pub fn render(&mut self, window: &Window, current_texture: SurfaceTexture) { + pub fn render( + &mut self, + window: &Window, + current_texture: SurfaceTexture, + commands: &[RenderCommand], + ) { let surface_texture_view = current_texture.texture.create_view(&TextureViewDescriptor { format: Some(self.view_format), ..Default::default() @@ -210,9 +270,32 @@ impl Renderer { }); render_pass.set_pipeline(&self.pipeline); - render_pass.set_bind_group(0, &self.fixed_bind_group, &[]); + render_pass.set_bind_group(0, &self.trans_bind_group, &[]); + render_pass.set_bind_group(1, &self.fixed_bind_group, &[]); - // TODO: 描画 + let size = window.inner_size().to_logical::<f32>(window.scale_factor()); + let mut instances = Vec::new(); + for command in commands { + command.build( + size.width, + size.height, + window.scale_factor() as f32, + &self.queue, + &mut self.char_atlas, + &mut instances, + ); + // TODO: 今はfont_texしか使わないので1ドローコールで済むが、 + // bind groupを変える必要があればドローコールを切る。 + } + instances.truncate(INSTANCE_MAX_COUNT as usize); + let bytes = unsafe { + slice::from_raw_parts( + instances.as_ptr() as *const u8, + mem::size_of_val(instances.as_slice()), + ) + }; + self.queue.write_buffer(&self.instances, 0, bytes); + render_pass.draw(0..4, 0..instances.len() as u32); drop(render_pass); |
