From 11bc0b30d2799420f98967b8f6aad62c31acbcb4 Mon Sep 17 00:00:00 2001 From: numzero Date: Sun, 25 Jan 2026 02:26:58 +0300 Subject: [PATCH] add basic rendering --- Cargo.lock | 154 ++++++++++++++++++++++++++---------------- Cargo.toml | 5 +- src/lib.rs | 41 +++++++---- src/main.rs | 2 +- src/particle.wgsl | 24 +++++++ src/render.rs | 146 +++++++++++++++++++++++++++++++++++++++ ui/Cargo.toml | 2 +- ui/src/lib.rs | 2 +- ui/src/main_window.ui | 4 +- 9 files changed, 300 insertions(+), 80 deletions(-) create mode 100644 src/particle.wgsl create mode 100644 src/render.rs diff --git a/Cargo.lock b/Cargo.lock index 5bcda38..67c224e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -31,6 +31,12 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + [[package]] name = "android-activity" version = "0.6.0" @@ -505,6 +511,9 @@ name = "glam" version = "0.30.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19fc433e8437a212d1b6f1e68c7824af3aed907da60afa994e7f542d18d12aa9" +dependencies = [ + "bytemuck", +] [[package]] name = "glow" @@ -527,34 +536,17 @@ dependencies = [ "gl_generator", ] -[[package]] -name = "gpu-alloc" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171" -dependencies = [ - "bitflags 2.10.0", - "gpu-alloc-types", -] - -[[package]] -name = "gpu-alloc-types" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4" -dependencies = [ - "bitflags 2.10.0", -] - [[package]] name = "gpu-allocator" -version = "0.27.0" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c151a2a5ef800297b4e79efa4f4bec035c5f51d5ae587287c9b952bdf734cacd" +checksum = "51255ea7cfaadb6c5f1528d43e92a82acb2b96c43365989a28b2d44ee38f8795" dependencies = [ + "ash", + "hashbrown 0.16.1", "log", "presser", - "thiserror 1.0.69", + "thiserror 2.0.18", "windows", ] @@ -605,6 +597,8 @@ version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" dependencies = [ + "allocator-api2", + "equivalent", "foldhash 0.2.0", ] @@ -781,9 +775,9 @@ dependencies = [ [[package]] name = "metal" -version = "0.32.0" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00c15a6f673ff72ddcc22394663290f870fb224c1bfce55734a75c414150e605" +checksum = "c7047791b5bc903b8cd963014b355f71dc9864a9a0b727057676c1dcae5cbc15" dependencies = [ "bitflags 2.10.0", "block", @@ -796,9 +790,9 @@ dependencies = [ [[package]] name = "naga" -version = "27.0.3" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "066cf25f0e8b11ee0df221219010f213ad429855f57c494f995590c861a9a7d8" +checksum = "618f667225063219ddfc61251087db8a9aec3c3f0950c916b614e403486f1135" dependencies = [ "arrayvec", "bit-set", @@ -1155,6 +1149,7 @@ dependencies = [ name = "particle_matter" version = "0.1.0" dependencies = [ + "bytemuck", "glam", "pollster", "wgpu", @@ -1910,12 +1905,13 @@ dependencies = [ [[package]] name = "wgpu" -version = "27.0.1" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfe68bac7cde125de7a731c3400723cadaaf1703795ad3f4805f187459cd7a77" +checksum = "f9cb534d5ffd109c7d1135f34cdae29e60eab94855a625dcfe1705f8bc7ad79f" dependencies = [ "arrayvec", "bitflags 2.10.0", + "bytemuck", "cfg-if", "cfg_aliases", "document-features", @@ -1939,9 +1935,9 @@ dependencies = [ [[package]] name = "wgpu-core" -version = "27.0.3" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27a75de515543b1897b26119f93731b385a19aea165a1ec5f0e3acecc229cae7" +checksum = "8bb4c8b5db5f00e56f1f08869d870a0dff7c8bc7ebc01091fec140b0cf0211a9" dependencies = [ "arrayvec", "bit-set", @@ -1971,36 +1967,36 @@ dependencies = [ [[package]] name = "wgpu-core-deps-apple" -version = "27.0.0" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0772ae958e9be0c729561d5e3fd9a19679bcdfb945b8b1a1969d9bfe8056d233" +checksum = "87b7b696b918f337c486bf93142454080a32a37832ba8a31e4f48221890047da" dependencies = [ "wgpu-hal", ] [[package]] name = "wgpu-core-deps-emscripten" -version = "27.0.0" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b06ac3444a95b0813ecfd81ddb2774b66220b264b3e2031152a4a29fda4da6b5" +checksum = "34b251c331f84feac147de3c4aa3aa45112622a95dd7ee1b74384fa0458dbd79" dependencies = [ "wgpu-hal", ] [[package]] name = "wgpu-core-deps-windows-linux-android" -version = "27.0.0" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71197027d61a71748e4120f05a9242b2ad142e3c01f8c1b47707945a879a03c3" +checksum = "68ca976e72b2c9964eb243e281f6ce7f14a514e409920920dcda12ae40febaae" dependencies = [ "wgpu-hal", ] [[package]] name = "wgpu-hal" -version = "27.0.4" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b21cb61c57ee198bc4aff71aeadff4cbb80b927beb912506af9c780d64313ce" +checksum = "293080d77fdd14d6b08a67c5487dfddbf874534bb7921526db56a7b75d7e3bef" dependencies = [ "android_system_properties", "arrayvec", @@ -2014,7 +2010,6 @@ dependencies = [ "core-graphics-types 0.2.0", "glow", "glutin_wgl_sys", - "gpu-alloc", "gpu-allocator", "gpu-descriptor", "hashbrown 0.16.1", @@ -2047,15 +2042,14 @@ dependencies = [ [[package]] name = "wgpu-types" -version = "27.0.1" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afdcf84c395990db737f2dd91628706cb31e86d72e53482320d368e52b5da5eb" +checksum = "e18308757e594ed2cd27dddbb16a139c42a683819d32a2e0b1b0167552f5840c" dependencies = [ "bitflags 2.10.0", "bytemuck", "js-sys", "log", - "thiserror 2.0.18", "web-sys", ] @@ -2070,32 +2064,54 @@ dependencies = [ [[package]] name = "windows" -version = "0.58.0" +version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" +dependencies = [ + "windows-collections", + "windows-core", + "windows-future", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" dependencies = [ "windows-core", - "windows-targets 0.52.6", ] [[package]] name = "windows-core" -version = "0.58.0" +version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ "windows-implement", "windows-interface", + "windows-link", "windows-result", "windows-strings", - "windows-targets 0.52.6", +] + +[[package]] +name = "windows-future" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" +dependencies = [ + "windows-core", + "windows-link", + "windows-threading", ] [[package]] name = "windows-implement" -version = "0.58.0" +version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", @@ -2104,9 +2120,9 @@ dependencies = [ [[package]] name = "windows-interface" -version = "0.58.0" +version = "0.59.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", @@ -2120,22 +2136,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] -name = "windows-result" -version = "0.2.0" +name = "windows-numerics" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" dependencies = [ - "windows-targets 0.52.6", + "windows-core", + "windows-link", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", ] [[package]] name = "windows-strings" -version = "0.1.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" dependencies = [ - "windows-result", - "windows-targets 0.52.6", + "windows-link", ] [[package]] @@ -2205,6 +2230,15 @@ dependencies = [ "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows-threading" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" +dependencies = [ + "windows-link", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" diff --git a/Cargo.toml b/Cargo.toml index 4a071ab..7cc561e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,8 @@ debug = false opt-level = 3 [dependencies] -glam = { version = "0.30.9" } +bytemuck = "1.24.0" +glam = { version = "0.30.9", features = ["bytemuck"] } pollster = "0.4.0" -wgpu = "27.0.1" +wgpu = "28.0.0" winit = "0.30.12" diff --git a/src/lib.rs b/src/lib.rs index 2518f00..b431d93 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,9 +1,10 @@ use std::error::Error; -use glam::{UVec2, Vec4}; +use glam::{UVec2, Vec4, vec2}; -const DEPTH_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth16Unorm; -const OUTPUT_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Bgra8UnormSrgb; +use crate::render::{DEPTH_FORMAT, OUTPUT_FORMAT}; + +mod render; #[derive(Debug, Clone, Copy)] #[repr(C)] @@ -23,6 +24,8 @@ pub struct Core { surface: wgpu::Surface<'static>, depth: wgpu::Texture, + pipeline: render::ParticlePipeline, + particle: render::ParticleMesh, } impl Core { @@ -34,6 +37,8 @@ impl Core { } = gpu; let depth = Self::create_depth_buffer(&device, pixel_size); + let pipeline = render::ParticlePipeline::new(&device); + let particle = render::ParticleMesh::new(&device, 17); queue.submit([]); // flush buffer updates Self::configure_surface(&surface, &device, pixel_size); @@ -43,6 +48,8 @@ impl Core { queue, surface, depth, + pipeline, + particle, } } @@ -53,7 +60,12 @@ impl Core { let h = size.height as f32; w / h }; - self.queue.submit([]); // flush buffer updates + let look = render::LookParams { + scale: vec2(0.01 / aspect, 0.01), + }; + + let particles = vec![vec2(0., 0.), vec2(20., 0.), vec2(0., 10.)]; + let particles = render::ParticleSet::new(&self.device, particles.as_slice()); let view = output.create_view(&wgpu::TextureViewDescriptor::default()); let depth_view = self @@ -77,17 +89,13 @@ impl Core { store: wgpu::StoreOp::Store, }, })], - depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment { - view: &depth_view, - depth_ops: Some(wgpu::Operations { - load: wgpu::LoadOp::Clear(1.), - store: wgpu::StoreOp::Discard, - }), - stencil_ops: None, - }), + depth_stencil_attachment: None, ..Default::default() }); + self.pipeline + .render(&mut pass, &look, &self.particle, &particles); + drop(pass); self.queue.submit(std::iter::once(encoder.finish())); } @@ -158,7 +166,14 @@ pub async fn init_gpu_inner( .await .unwrap(); let (device, queue) = adapter - .request_device(&wgpu::DeviceDescriptor::default()) + .request_device(&wgpu::DeviceDescriptor { + required_features: wgpu::Features::IMMEDIATES, + required_limits: wgpu::Limits { + max_immediate_size: 64, + ..Default::default() + }, + ..Default::default() + }) .await .unwrap(); Ok(Gpu { diff --git a/src/main.rs b/src/main.rs index 68d54c6..5c00311 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,7 @@ use std::sync::Arc; -use particle_matter::{Core, RedrawArgs, init_gpu_inner}; use glam::{uvec2, vec4}; +use particle_matter::{Core, RedrawArgs, init_gpu_inner}; use winit::{ application::ApplicationHandler, event::WindowEvent, diff --git a/src/particle.wgsl b/src/particle.wgsl new file mode 100644 index 0000000..3a95630 --- /dev/null +++ b/src/particle.wgsl @@ -0,0 +1,24 @@ +struct LookParams { + scale: vec2f, +} + +struct Varying { + @builtin(position) screen: vec4f, + @location(0) color: vec4f, +} + +var look: LookParams; + +@vertex +fn on_vertex( + @location(0) pos: vec2f, + @location(1) off: vec2f, +) -> Varying { + let vpos = pos + off; + return Varying(vec4f(look.scale * vpos, 0., 1.), vec4f(0.5)); +} + +@fragment +fn on_fragment(in: Varying) -> @location(0) vec4f { + return in.color; +} diff --git a/src/render.rs b/src/render.rs new file mode 100644 index 0000000..9da724d --- /dev/null +++ b/src/render.rs @@ -0,0 +1,146 @@ +use bytemuck::{Pod, Zeroable, cast_slice}; +use glam::{Vec2, vec2}; +use wgpu::util::DeviceExt as _; + +pub const OUTPUT_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Bgra8UnormSrgb; +pub const DEPTH_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth24Plus; + +static PARTICLE_SHADER: &str = include_str!("particle.wgsl"); + +pub struct ParticlePipeline { + pipeline: wgpu::RenderPipeline, +} + +#[derive(Debug, Clone, Copy, Zeroable, Pod)] +#[repr(C)] +pub struct LookParams { + pub scale: Vec2, +} + +pub struct ParticleMesh { + vertex_buffer: wgpu::Buffer, + vertex_count: u32, +} + +pub struct ParticleSet { + vertex_buffer: wgpu::Buffer, + vertex_count: u32, +} + +impl ParticleMesh { + pub fn new(device: &wgpu::Device, n: u32) -> Self { + assert!(n >= 3); + assert!(n < 1000); + let mut vertices: Vec = Vec::with_capacity(n as usize); + for k in 0..n { + let k2 = (k / 2) as i32; + let k3 = match k % 2 { + 0 => k2, + 1 => -1 - k2, + _ => unreachable!(), + }; + let phi = 2. * std::f32::consts::PI * (k3 as f32) / (n as f32); + let (y, x) = phi.sin_cos(); + vertices.push(vec2(x, y)); + } + let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: None, + usage: wgpu::BufferUsages::VERTEX, + contents: cast_slice(vertices.as_slice()), + }); + Self { + vertex_buffer, + vertex_count: n, + } + } +} + +impl ParticleSet { + pub fn new(device: &wgpu::Device, positions: &[Vec2]) -> Self { + let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: None, + usage: wgpu::BufferUsages::VERTEX, + contents: cast_slice(positions), + }); + Self { + vertex_buffer, + vertex_count: positions.len().try_into().unwrap(), + } + } +} + +impl ParticlePipeline { + pub fn new(device: &wgpu::Device) -> Self { + let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { + label: None, + source: wgpu::ShaderSource::Wgsl(PARTICLE_SHADER.into()), + }); + let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: None, + bind_group_layouts: &[], + immediate_size: 8, + }); + let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: None, + layout: Some(&layout), + vertex: wgpu::VertexState { + module: &shader, + entry_point: None, + compilation_options: wgpu::PipelineCompilationOptions::default(), + buffers: &[ + wgpu::VertexBufferLayout { + array_stride: size_of::() as u64, + step_mode: wgpu::VertexStepMode::Instance, + attributes: &[wgpu::VertexAttribute { + shader_location: 0, + offset: 0, + format: wgpu::VertexFormat::Float32x2, + }], + }, + wgpu::VertexBufferLayout { + array_stride: size_of::() as u64, + step_mode: wgpu::VertexStepMode::Vertex, + attributes: &[wgpu::VertexAttribute { + shader_location: 1, + offset: 0, + format: wgpu::VertexFormat::Float32x2, + }], + }, + ], + }, + primitive: wgpu::PrimitiveState { + topology: wgpu::PrimitiveTopology::TriangleStrip, + ..Default::default() + }, + depth_stencil: None, + multisample: wgpu::MultisampleState::default(), + fragment: Some(wgpu::FragmentState { + module: &shader, + entry_point: None, + compilation_options: wgpu::PipelineCompilationOptions::default(), + targets: &[Some(wgpu::ColorTargetState { + format: OUTPUT_FORMAT, + blend: Some(wgpu::BlendState::REPLACE), + write_mask: wgpu::ColorWrites::ALL, + })], + }), + multiview_mask: None, + cache: None, + }); + Self { pipeline } + } + + pub fn render<'a>( + &self, + pass: &mut wgpu::RenderPass, + params: &LookParams, + mesh: &ParticleMesh, + particles: &ParticleSet, + ) { + pass.set_pipeline(&self.pipeline); + pass.set_immediates(0, bytemuck::bytes_of(params)); + pass.set_vertex_buffer(0, particles.vertex_buffer.slice(..)); + pass.set_vertex_buffer(1, mesh.vertex_buffer.slice(..)); + pass.draw(0..mesh.vertex_count, 0..particles.vertex_count); + } +} diff --git a/ui/Cargo.toml b/ui/Cargo.toml index 525de92..bb9414a 100644 --- a/ui/Cargo.toml +++ b/ui/Cargo.toml @@ -12,4 +12,4 @@ particle_matter = {path = "../"} glam = { version = "0.30" } pollster = "0.4.0" raw-window-handle = "0.6.2" -wgpu = "27.0.1" +wgpu = "28.0.0" diff --git a/ui/src/lib.rs b/ui/src/lib.rs index 9cc3c9b..ba7d812 100644 --- a/ui/src/lib.rs +++ b/ui/src/lib.rs @@ -1,7 +1,7 @@ use std::{ffi::c_void, num::NonZero, ptr::NonNull}; -use particle_matter::{Core, RedrawArgs, init_gpu_inner}; use glam::{UVec2, uvec2}; +use particle_matter::{Core, RedrawArgs, init_gpu_inner}; use raw_window_handle::{RawDisplayHandle, RawWindowHandle, XcbDisplayHandle, XcbWindowHandle}; unsafe fn create_viewport( diff --git a/ui/src/main_window.ui b/ui/src/main_window.ui index b134512..89ab326 100644 --- a/ui/src/main_window.ui +++ b/ui/src/main_window.ui @@ -51,8 +51,8 @@ - 25 - 220 + 0 + 0 0