summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTengu712 <genreihoutengu@gmail.com>2026-09-26 21:36:43 +0900
committerTengu712 <genreihoutengu@gmail.com>2026-09-26 21:36:43 +0900
commitf6d058be6639c8e052c5a38dea4f33d6b26ae20c (patch)
treef101cc3ff0a9c620274360672973943d710e9a9d /src
parentb27234e27bbaf4d7f7f3467de64bd95ff2145c0e (diff)
add texture
Diffstat (limited to 'src')
-rw-r--r--src/graphics.rs68
-rw-r--r--src/graphics/texture.rs108
-rw-r--r--src/shader.wgsl21
3 files changed, 191 insertions, 6 deletions
diff --git a/src/graphics.rs b/src/graphics.rs
index 6fa13b3..bbe07df 100644
--- a/src/graphics.rs
+++ b/src/graphics.rs
@@ -2,6 +2,10 @@ use std::{borrow::Cow, sync::Arc};
use wgpu::*;
use winit::{dpi::PhysicalSize, event_loop::OwnedDisplayHandle, window::Window};
+mod texture;
+
+use texture::TextureWrapper;
+
pub enum AcquireSurfaceTextureResult {
Ok(SurfaceTexture),
ShouldSkip,
@@ -16,6 +20,9 @@ pub struct Renderer {
view_format: TextureFormat,
present_mode: PresentMode,
pipeline: RenderPipeline,
+
+ font_tex: TextureWrapper,
+ fixed_bind_group: BindGroup,
}
impl Renderer {
@@ -26,7 +33,6 @@ impl Renderer {
let size = window.inner_size();
let instance = Instance::new(InstanceDescriptor::new_with_display_handle(display));
-
let surface = instance.create_surface(window).map_err(|e| e.to_string())?;
let adapter = instance
@@ -67,9 +73,30 @@ impl Renderer {
source: ShaderSource::Wgsl(Cow::Borrowed(include_str!("shader.wgsl"))),
});
+ let fixed_bind_group_layout = device.create_bind_group_layout(&BindGroupLayoutDescriptor {
+ label: None,
+ entries: &[
+ BindGroupLayoutEntry {
+ binding: 0,
+ visibility: ShaderStages::FRAGMENT,
+ ty: BindingType::Sampler(SamplerBindingType::Filtering),
+ count: None,
+ },
+ BindGroupLayoutEntry {
+ binding: 1,
+ visibility: ShaderStages::FRAGMENT,
+ ty: BindingType::Texture {
+ sample_type: TextureSampleType::Float { filterable: true },
+ view_dimension: TextureViewDimension::D2,
+ multisampled: false,
+ },
+ count: None,
+ },
+ ],
+ });
let pipeline_layout = device.create_pipeline_layout(&PipelineLayoutDescriptor {
label: None,
- bind_group_layouts: &[],
+ bind_group_layouts: &[Some(&fixed_bind_group_layout)],
immediate_size: 0,
});
let pipeline = device.create_render_pipeline(&RenderPipelineDescriptor {
@@ -98,6 +125,40 @@ impl Renderer {
cache: None,
});
+ // NOTE: 本ゲームではlinearなサンプラしか用意しないので、ここで1個だけ作成すれば良い。
+ let sampler = device.create_sampler(&SamplerDescriptor {
+ label: None,
+ address_mode_u: AddressMode::ClampToEdge,
+ address_mode_v: AddressMode::ClampToEdge,
+ address_mode_w: AddressMode::ClampToEdge,
+ mag_filter: FilterMode::Linear,
+ min_filter: FilterMode::Linear,
+ mipmap_filter: MipmapFilterMode::Linear,
+ ..Default::default()
+ });
+ // NOTE: 文字テクスチャアトラスは使い回すので、ここで1個だけ作成すれば良い。
+ let font_tex = TextureWrapper::new(
+ &device,
+ 128,
+ 128,
+ TextureFormat::R8Unorm,
+ TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST,
+ );
+ let fixed_bind_group = device.create_bind_group(&BindGroupDescriptor {
+ label: None,
+ layout: &fixed_bind_group_layout,
+ entries: &[
+ BindGroupEntry {
+ binding: 0,
+ resource: BindingResource::Sampler(&sampler),
+ },
+ BindGroupEntry {
+ binding: 1,
+ resource: BindingResource::TextureView(&font_tex.view),
+ },
+ ],
+ });
+
let v = Self {
device,
queue,
@@ -106,6 +167,8 @@ impl Renderer {
view_format,
present_mode,
pipeline,
+ font_tex,
+ fixed_bind_group,
};
v.configure_surface(size);
@@ -161,6 +224,7 @@ impl Renderer {
});
render_pass.set_pipeline(&self.pipeline);
+ render_pass.set_bind_group(0, &self.fixed_bind_group, &[]);
// TODO: 描画
diff --git a/src/graphics/texture.rs b/src/graphics/texture.rs
new file mode 100644
index 0000000..6273b6e
--- /dev/null
+++ b/src/graphics/texture.rs
@@ -0,0 +1,108 @@
+use wgpu::*;
+
+pub struct TextureWrapper {
+ pub width: u32,
+ pub height: u32,
+ pub block_size: u32,
+ pub texture: Texture,
+ pub view: TextureView,
+}
+
+impl TextureWrapper {
+ pub fn new(
+ device: &Device,
+ width: u32,
+ height: u32,
+ format: TextureFormat,
+ usage: TextureUsages,
+ ) -> Self {
+ let texture = device.create_texture(&TextureDescriptor {
+ label: None,
+ size: Extent3d {
+ width,
+ height,
+ depth_or_array_layers: 1,
+ },
+ mip_level_count: 1,
+ sample_count: 1,
+ dimension: TextureDimension::D2,
+ format,
+ usage,
+ view_formats: &[],
+ });
+ let view = texture.create_view(&TextureViewDescriptor::default());
+
+ // NOTE: Depth, Stencil, YUVでない限りはNoneにはならないが、念の為`1`にunwrapする。
+ let block_size = format.block_copy_size(None).unwrap_or(1);
+
+ Self {
+ width,
+ height,
+ block_size,
+ texture,
+ view,
+ }
+ }
+
+ /// テクスチャ全体を更新するメソッド
+ ///
+ /// * queue - queue
+ /// * data - 更新データ
+ ///
+ /// `data`がテクスチャサイズと一致しない場合は、クラッシュしないように不足分が白色で埋められる。
+ pub fn write(&self, queue: &Queue, data: &[u8]) {
+ self.write_partial(queue, data, Origin3d::ZERO, self.width, self.height);
+ }
+
+ /// テクスチャを部分的に更新するメソッド
+ ///
+ /// * queue - queue
+ /// * data - 更新データ
+ /// * origin - 更新領域の開始座標
+ /// * width - 更新領域の幅
+ /// * height - 更新領域の高さ
+ ///
+ /// `data`が更新領域サイズと一致しない場合は、クラッシュしないように不足分が白色で埋められる。
+ pub fn write_partial(
+ &self,
+ queue: &Queue,
+ data: &[u8],
+ origin: Origin3d,
+ width: u32,
+ height: u32,
+ ) {
+ let expected_len = (width * height * self.block_size) as usize;
+
+ // `data`が想定サイズより短い場合はエラーになるので不足分を白色で埋める。
+ let buffer;
+ let data = if data.len() >= expected_len {
+ data
+ } else {
+ let mut v = Vec::with_capacity(expected_len);
+ v.extend_from_slice(data);
+ v.resize(expected_len, 0xff);
+ buffer = v;
+ buffer.as_slice()
+ };
+
+ queue.write_texture(
+ TexelCopyTextureInfo {
+ texture: &self.texture,
+ mip_level: 0,
+ origin,
+ aspect: TextureAspect::All,
+ },
+ data,
+ TexelCopyBufferLayout {
+ offset: 0,
+ bytes_per_row: Some(width * self.block_size),
+ rows_per_image: Some(height),
+ },
+ Extent3d {
+ width,
+ height,
+ depth_or_array_layers: 1,
+ },
+ );
+ }
+}
diff --git a/src/shader.wgsl b/src/shader.wgsl
index 9f158f1..c154cc4 100644
--- a/src/shader.wgsl
+++ b/src/shader.wgsl
@@ -1,10 +1,23 @@
+@group(0) @binding(0) var tex_sampler: sampler;
+@group(0) @binding(1) var font_tex: texture_2d<f32>;
+
+struct VertexOutput {
+ @builtin(position) position: vec4<f32>,
+ @location(0) uv: vec2<f32>,
+}
+
@vertex
-fn vs_main(@builtin(vertex_index) in_vertex_index: u32) -> @builtin(position) vec4<f32> {
+fn vs_main(@builtin(vertex_index) in_vertex_index: u32) -> VertexOutput {
let uv = vec2<f32>(f32((in_vertex_index << 1) & 2), f32(in_vertex_index & 2)) / 2.0;
- return vec4<f32>(uv.x - 0.5, uv.y - 0.5, 0.0, 1.0);
+
+ var out: VertexOutput;
+ out.position = vec4<f32>(uv.x - 0.5, uv.y - 0.5, 0.0, 1.0);
+ out.uv = uv;
+
+ return out;
}
@fragment
-fn fs_main() -> @location(0) vec4<f32> {
- return vec4<f32>(1.0, 1.0, 1.0, 1.0);
+fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
+ return vec4<f32>(1.0, 1.0, 1.0, textureSample(font_tex, tex_sampler, in.uv).r);
}