This commit is contained in:
Oleg Sobolev 2024-06-06 22:40:28 +07:00
commit b8eea76bf9
8 changed files with 2602 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/target

2233
Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

12
Cargo.toml Normal file
View file

@ -0,0 +1,12 @@
[package]
name = "bezier"
version = "0.1.0"
edition = "2021"
[dependencies]
wgpu = "0.19.4"
winit = { version = "0.29.15", features = ["rwh_05"] }
env_logger = "*"
pollster = "*"
log = "0.4.21"
bytemuck = { version = "1.12", features = [ "derive" ] }

68
src/lib.rs Normal file
View file

@ -0,0 +1,68 @@
pub mod vertex;
mod state;
pub use vertex::Vertex;
use state::State;
use winit::{
event::{Event, KeyEvent, WindowEvent},
event_loop::EventLoop,
keyboard::{KeyCode, PhysicalKey},
window::WindowBuilder,
};
pub async fn run() {
env_logger::init();
let event_loop = EventLoop::new().unwrap();
event_loop.set_control_flow(winit::event_loop::ControlFlow::Poll);
let window = WindowBuilder::new().build(&event_loop).unwrap();
let window_ref = &window;
let mut state = State::new(&window).await;
let _ = event_loop.run(move |mut event, control_flow| match event {
Event::WindowEvent {
ref mut event,
window_id,
} if window_id == state.window().id() => {
if !state.input(event) {
match event {
WindowEvent::CloseRequested
| WindowEvent::KeyboardInput {
event:
KeyEvent {
physical_key: PhysicalKey::Code(KeyCode::Escape),
..
},
..
} => control_flow.exit(),
WindowEvent::Resized(physical_size) => {
state.resize(*physical_size);
}
WindowEvent::RedrawRequested => {
state.update();
match state.render() {
Ok(_) => {}
Err(wgpu::SurfaceError::Lost) => {} /*state.resize(state.size)*/,
Err(wgpu::SurfaceError::OutOfMemory) => control_flow.exit(),
Err(e) => eprintln!("{:?}", e),
};
window_ref.request_redraw();
}
_ => {}
}
}
}
_ => {}
});
}
// #[rustfmt::skip]
// pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4<f32> = cgmath::Matrix4::new(
// 1.0, 0.0, 0.0, 0.0,
// 0.0, 1.0, 0.0, 0.0,
// 0.0, 0.0, 0.5, 0.5,
// 0.0, 0.0, 0.0, 1.0,
// );

5
src/main.rs Normal file
View file

@ -0,0 +1,5 @@
use bezier::run;
fn main() {
pollster::block_on(run());
}

23
src/shader.wgsl Normal file
View file

@ -0,0 +1,23 @@
struct VertexInput {
@location(0) position: vec2<f32>,
}
struct VertexOutput {
@builtin(position) clip_position: vec4<f32>,
}
@vertex
fn vs_main(
@builtin(vertex_index) in_vertex_index: u32,
) -> VertexOutput {
var out: VertexOutput;
let x = f32(1 - i32(in_vertex_index)) * 0.5;
let y = f32(i32(in_vertex_index & 1u) * 2 - 1) * 0.5;
out.clip_position = vec4<f32>(x, y, 0.0, 1.0);
return out;
}
@fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
return vec4<f32>(0.3, 0.2, 0.1, 1.0);
}

232
src/state.rs Normal file
View file

@ -0,0 +1,232 @@
use wgpu::{util::DeviceExt, ColorTargetState};
use crate::Vertex;
pub struct State<'window> {
window: &'window winit::window::Window,
surface_config: wgpu::SurfaceConfiguration,
surface: wgpu::Surface<'window>,
device: wgpu::Device,
queue: wgpu::Queue,
render_pipeline: wgpu::RenderPipeline,
vertex_buffer: wgpu::Buffer,
}
impl<'window> State<'window> {
pub async fn new(window: &'window winit::window::Window) -> Self {
let size = window.inner_size();
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
backends: wgpu::Backends::PRIMARY,
..Default::default()
});
let surface = instance.create_surface(window).unwrap();
let adapter = instance
.request_adapter(&wgpu::RequestAdapterOptionsBase {
power_preference: wgpu::PowerPreference::default(),
force_fallback_adapter: false,
compatible_surface: Some(&surface),
})
.await
.unwrap();
let (device, queue) = adapter
.request_device(
&wgpu::DeviceDescriptor {
label: Some("Wgpu device"),
required_features: wgpu::Features::empty(),
required_limits: wgpu::Limits::default(),
},
None,
)
.await
.unwrap();
let surface_config = Self::create_surface_config(&surface, &adapter, &size);
surface.configure(&device, &surface_config);
let shader_module = device.create_shader_module(wgpu::include_wgsl!("shader.wgsl"));
let vertices = [
Vertex::new_white([0.0, 0.0]),
Vertex::new_white([1.0, 0.0]),
Vertex::new_white([1.0, 1.0]),
];
let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Vertex Buffer"),
contents: bytemuck::cast_slice(&vertices),
usage: wgpu::BufferUsages::VERTEX,
});
let render_pipeline = Self::create_render_pipeline(&device, &shader_module, &surface_config);
Self {
window,
surface_config,
surface,
device,
queue,
render_pipeline,
vertex_buffer
}
}
pub fn render(&mut self) -> Result<(), wgpu::SurfaceError> {
let output = self.surface.get_current_texture().unwrap();
let view = output
.texture
.create_view(&wgpu::TextureViewDescriptor::default());
let mut encoder = self
.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("Command Encoder"),
});
self.render_pass(&mut encoder, &view);
self.queue.submit(std::iter::once(encoder.finish()));
output.present();
Ok(())
}
fn render_pass(&mut self, encoder: &mut wgpu::CommandEncoder, view: &wgpu::TextureView) {
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("Render Pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color {
r: 0.1,
g: 0.2,
b: 0.3,
a: 1.0,
}),
store: wgpu::StoreOp::Store,
},
})],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
});
render_pass.set_pipeline(&self.render_pipeline);
render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
render_pass.draw(0..3, 0..1);
}
pub fn window(&self) -> &winit::window::Window {
&self.window
}
pub fn input(&mut self, _event: &mut winit::event::WindowEvent) -> bool {
false
}
pub fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
if new_size.width == 0 && new_size.height == 0 {
return;
}
self.surface_config.height = new_size.height;
self.surface_config.width = new_size.width;
self.surface.configure(&self.device, &self.surface_config);
}
pub fn update(&mut self) {}
fn create_render_pipeline(device: &wgpu::Device, shader_module: &wgpu::ShaderModule, surface_config: &wgpu::SurfaceConfiguration) -> wgpu::RenderPipeline {
let vertex = Self::create_vertex_state(&shader_module);
let color_targets = Self::create_color_targets(&surface_config);
let fragment = Self::create_fragment_state(&shader_module, &color_targets);
let primitive = Self::create_primitive_state();
let multisample = Self::create_multisample_state();
let render_pipeline_layout = Self::create_pipeline_layout(&device);
device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Render Pipeline"),
layout: Some(&render_pipeline_layout),
vertex,
fragment: Some(fragment),
primitive,
depth_stencil: None,
multisample,
multiview: None,
})
}
fn create_pipeline_layout(device: &wgpu::Device) -> wgpu::PipelineLayout {
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Render Pipeline Layout"),
bind_group_layouts: &[],
push_constant_ranges: &[],
})
}
fn create_surface_config(surface: &wgpu::Surface, adapter: &wgpu::Adapter, size: &winit::dpi::PhysicalSize<u32>) -> wgpu::SurfaceConfiguration {
let surface_capabilities = surface.get_capabilities(&adapter);
let surface_format = surface_capabilities
.formats
.iter()
.find(|f| f.is_srgb())
.copied()
.unwrap_or(surface_capabilities.formats[0]);
wgpu::SurfaceConfiguration {
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
format: surface_format,
width: size.width,
height: size.height,
present_mode: wgpu::PresentMode::Fifo,
desired_maximum_frame_latency: 2,
alpha_mode: surface_capabilities.alpha_modes[0],
view_formats: vec![],
}
}
const VERTEX_BUFFERS: [wgpu::VertexBufferLayout<'static>; 1] = [Vertex::desc()];
fn create_vertex_state(shader_module: &wgpu::ShaderModule) -> wgpu::VertexState {
wgpu::VertexState {
module: shader_module,
entry_point: "vs_main",
buffers: &Self::VERTEX_BUFFERS,
}
}
fn create_fragment_state<'a>(shader_module: &'a wgpu::ShaderModule, targets: &'a [Option<ColorTargetState>]) -> wgpu::FragmentState<'a> {
wgpu::FragmentState {
module: shader_module,
entry_point: "fs_main",
targets,
}
}
fn create_color_targets(surface_config: &wgpu::SurfaceConfiguration) -> Vec<Option<ColorTargetState>> {
vec![Some(wgpu::ColorTargetState {
format: surface_config.format,
blend: Some(wgpu::BlendState::REPLACE),
write_mask: wgpu::ColorWrites::ALL,
})]
}
fn create_primitive_state() -> wgpu::PrimitiveState {
wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
strip_index_format: None,
front_face: wgpu::FrontFace::Ccw,
cull_mode: Some(wgpu::Face::Back),
polygon_mode: wgpu::PolygonMode::Fill,
unclipped_depth: false,
conservative: false,
}
}
fn create_multisample_state() -> wgpu::MultisampleState {
wgpu::MultisampleState { count: 1, mask: !0, alpha_to_coverage_enabled: false }
}
}

28
src/vertex.rs Normal file
View file

@ -0,0 +1,28 @@
#[repr(C)]
#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
pub struct Vertex {
position: [f32; 2],
color: [f32; 3],
}
impl Vertex {
pub fn new(position: [f32; 2], color: [f32; 3]) -> Vertex {
Vertex {position, color}
}
pub fn new_white(position: [f32; 2]) -> Vertex {
Vertex {position, color: [1.0, 1.0, 1.0]}
}
const ATTRIBS: [wgpu::VertexAttribute; 2] = wgpu::vertex_attr_array![0 => Float32x2, 1 => Float32x3];
pub const fn desc() -> wgpu::VertexBufferLayout<'static> {
use std::mem;
wgpu::VertexBufferLayout {
array_stride: mem::size_of::<Self>() as wgpu::BufferAddress,
step_mode: wgpu::VertexStepMode::Vertex,
attributes: &Self::ATTRIBS,
}
}
}