diff options
Diffstat (limited to 'client/src')
-rw-r--r-- | client/src/main.rs | 25 | ||||
-rw-r--r-- | client/src/renderer.rs | 158 | ||||
-rw-r--r-- | client/src/shader.wgsl | 11 | ||||
-rw-r--r-- | client/src/window.rs | 45 |
4 files changed, 239 insertions, 0 deletions
diff --git a/client/src/main.rs b/client/src/main.rs new file mode 100644 index 0000000..1aa05db --- /dev/null +++ b/client/src/main.rs @@ -0,0 +1,25 @@ +pub mod window; +pub mod renderer; + +use anyhow::Result; +use clap::Parser; +use std::net::{SocketAddr, TcpStream}; +use window::WindowState; +use winit::event_loop::EventLoop; + +#[derive(Parser)] +struct Args { + address: SocketAddr, +} + +fn main() -> Result<()> { + env_logger::init_from_env("LOG"); + let args = Args::parse(); + + let sock = TcpStream::connect(args.address)?; + + let evloop = EventLoop::new()?; + evloop.run_app(&mut WindowState::new())?; + + Ok(()) +} diff --git a/client/src/renderer.rs b/client/src/renderer.rs new file mode 100644 index 0000000..b21c139 --- /dev/null +++ b/client/src/renderer.rs @@ -0,0 +1,158 @@ +use anyhow::{Result, anyhow}; +use pollster::FutureExt; +use wgpu::{ + Backends, BindGroup, BindGroupDescriptor, BindGroupLayoutDescriptor, BlendState, Color, + ColorTargetState, ColorWrites, CommandEncoderDescriptor, Device, DeviceDescriptor, Features, + FragmentState, FrontFace, Instance, InstanceDescriptor, Limits, LoadOp, MaintainBase, + MultisampleState, Operations, PipelineCompilationOptions, PipelineLayoutDescriptor, + PolygonMode, PowerPreference, PrimitiveState, PrimitiveTopology, Queue, + RenderPassColorAttachment, RenderPassDescriptor, RenderPipeline, RenderPipelineDescriptor, + RequestAdapterOptions, StoreOp, Surface, SurfaceConfiguration, TextureViewDescriptor, + VertexState, include_wgsl, +}; +use winit::window::Window; + +pub struct Renderer<'a> { + surface: Surface<'a>, + pipeline: RenderPipeline, + bind_group: BindGroup, + queue: Queue, + device: Device, + surface_configuration: SurfaceConfiguration, +} +impl<'a> Renderer<'a> { + pub fn new(window: &'a Window) -> Result<Self> { + let instance = Instance::new(InstanceDescriptor { + backends: Backends::all(), + ..Default::default() + }); + + let surface = instance.create_surface(window)?; + let adapter = instance + .request_adapter(&RequestAdapterOptions { + compatible_surface: Some(&surface), + power_preference: PowerPreference::HighPerformance, + ..Default::default() + }) + .block_on() + .ok_or(anyhow!("no adapter found"))?; + + let (device, queue) = adapter + .request_device( + &DeviceDescriptor { + required_features: Features::default(), + required_limits: Limits::downlevel_defaults(), + ..Default::default() + }, + None, + ) + .block_on()?; + + let surface_configuration = surface + .get_default_config(&adapter, 256, 256) + .ok_or(anyhow!("no default config"))?; + + let module = device.create_shader_module(include_wgsl!("shader.wgsl")); + + let bind_group_layout = device.create_bind_group_layout(&BindGroupLayoutDescriptor { + entries: &[], + label: None, + }); + let bind_group = device.create_bind_group(&BindGroupDescriptor { + label: None, + layout: &bind_group_layout, + entries: &[], + }); + let pipeline_layout = device.create_pipeline_layout(&PipelineLayoutDescriptor { + label: None, + bind_group_layouts: &[&bind_group_layout], + push_constant_ranges: &[], + }); + let pipeline = device.create_render_pipeline(&RenderPipelineDescriptor { + label: None, + layout: Some(&pipeline_layout), + fragment: Some(FragmentState { + module: &module, + entry_point: Some("fs_main"), + targets: &[Some(ColorTargetState { + blend: Some(BlendState::PREMULTIPLIED_ALPHA_BLENDING), + format: surface_configuration.format, + write_mask: ColorWrites::all(), + })], + compilation_options: PipelineCompilationOptions::default(), + }), + vertex: VertexState { + module: &module, + entry_point: Some("vs_main"), + buffers: &[], + compilation_options: PipelineCompilationOptions::default(), + }, + primitive: PrimitiveState { + topology: PrimitiveTopology::TriangleList, + front_face: FrontFace::Ccw, + cull_mode: None, //Some(Face::Back), + polygon_mode: PolygonMode::Fill, + ..Default::default() + }, + depth_stencil: Default::default(), + multisample: MultisampleState::default(), + multiview: None, + cache: None, + }); + + surface.configure(&device, &surface_configuration); + + Ok(Self { + surface, + pipeline, + bind_group, + device, + queue, + surface_configuration, + }) + } + + pub fn resize(&mut self, width: u32, height: u32) { + self.surface_configuration.width = width; + self.surface_configuration.height = height; + self.surface + .configure(&self.device, &self.surface_configuration); + } + + pub fn draw(&mut self) -> Result<()> { + let target = self.surface.get_current_texture()?; + let target_view = target + .texture + .create_view(&TextureViewDescriptor::default()); + + let mut commands = self + .device + .create_command_encoder(&CommandEncoderDescriptor { label: None }); + + { + let mut rpass = commands.begin_render_pass(&RenderPassDescriptor { + label: None, + color_attachments: &[Some(RenderPassColorAttachment { + view: &target_view, + resolve_target: None, + ops: Operations { + store: StoreOp::Store, + load: LoadOp::Clear(Color::BLUE), + }, + })], + ..Default::default() + }); + + rpass.set_bind_group(0, &self.bind_group, &[]); + rpass.set_pipeline(&self.pipeline); + rpass.draw(0..3, 0..1); + } + + let i = self.queue.submit(Some(commands.finish())); + self.device.poll(MaintainBase::WaitForSubmissionIndex(i)); + + target.present(); + + Ok(()) + } +} diff --git a/client/src/shader.wgsl b/client/src/shader.wgsl new file mode 100644 index 0000000..859ffa4 --- /dev/null +++ b/client/src/shader.wgsl @@ -0,0 +1,11 @@ +@vertex +fn vs_main(@builtin(vertex_index) in_vertex_index: u32) -> @builtin(position) vec4<f32> { + let x = f32(i32(in_vertex_index) - 1); + let y = f32(i32(in_vertex_index & 1u) * 2 - 1); + return vec4<f32>(x, y, 0.0, 1.0); +} + +@fragment +fn fs_main() -> @location(0) vec4<f32> { + return vec4<f32>(1.0, 0.0, 0.0, 1.0); +}
\ No newline at end of file diff --git a/client/src/window.rs b/client/src/window.rs new file mode 100644 index 0000000..20300d5 --- /dev/null +++ b/client/src/window.rs @@ -0,0 +1,45 @@ +use crate::renderer::Renderer; +use winit::{ + application::ApplicationHandler, + event::WindowEvent, + event_loop::ActiveEventLoop, + window::{Window, WindowAttributes, WindowId}, +}; + +pub struct WindowState { + window: Option<(Window, Renderer<'static>)>, +} +impl WindowState { + pub fn new() -> Self { + Self { window: None } + } +} +impl ApplicationHandler for WindowState { + fn resumed(&mut self, event_loop: &ActiveEventLoop) { + let win = event_loop + .create_window(WindowAttributes::default().with_maximized(true)) + .unwrap(); + let ren = Renderer::new(unsafe { std::mem::transmute::<&Window, &'static Window>(&win) }) + .unwrap(); + self.window = Some((win, ren)) + } + fn window_event( + &mut self, + event_loop: &ActiveEventLoop, + _window_id: WindowId, + event: WindowEvent, + ) { + if let Some((_win, ren)) = &mut self.window { + match event { + WindowEvent::Resized(size) => { + ren.resize(size.width, size.height); + } + WindowEvent::RedrawRequested => ren.draw().unwrap(), + WindowEvent::CloseRequested => { + event_loop.exit(); + } + _ => (), + } + } + } +} |