summaryrefslogtreecommitdiff
path: root/src/graphics.rs
diff options
context:
space:
mode:
authorTengu712 <genreihoutengu@gmail.com>2026-10-01 01:53:23 +0900
committerTengu712 <genreihoutengu@gmail.com>2026-10-01 01:53:23 +0900
commit2c104d7936193bb19eb52b3e4d47657ffb18a9d7 (patch)
tree6f90ea406a1506bbb37ee3aea52a29278524074a /src/graphics.rs
parente3835c956011c217e498b272490005dca457d402 (diff)
render text
Diffstat (limited to 'src/graphics.rs')
-rw-r--r--src/graphics.rs93
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);