diff --git a/src/renderer.rs b/src/renderer.rs new file mode 100644 index 0000000..2075f4e --- /dev/null +++ b/src/renderer.rs @@ -0,0 +1,150 @@ +use winit::{dpi::PhysicalSize, window::Window}; + +pub(crate) struct RenderContext { + pub instance: wgpu::Instance, + pub size: PhysicalSize, + pub surface: wgpu::Surface, + pub surface_config: wgpu::SurfaceConfiguration, + pub adapter: wgpu::Adapter, + pub device: wgpu::Device, + pub queue: wgpu::Queue, +} + +impl RenderContext { + pub async fn new(window: &Window) -> Self { + log::info!("Initialising WGPU context..."); + let instance = wgpu::Instance::new(wgpu::InstanceDescriptor { + backends: wgpu::Backends::VULKAN, + dx12_shader_compiler: Default::default(), + }); + + // To be able to start drawing we need a few things: + // - A surface + // - A GPU device to draw to the surface + // - A draw command queue + log::info!("Initialising window surface..."); + let surface = unsafe { instance.create_surface(&window) }.unwrap(); + + log::info!("Requesting GPU adapter..."); + let adapter = instance + .request_adapter(&wgpu::RequestAdapterOptions { + power_preference: wgpu::PowerPreference::HighPerformance, + force_fallback_adapter: false, + compatible_surface: Some(&surface), + }) + .await + .unwrap(); + + log::info!("Checking GPU adapter meets requirements"); + log::info!("Requesting GPU device..."); + let (device, queue) = adapter + .request_device( + &wgpu::DeviceDescriptor { + label: None, + features: wgpu::Features::empty(), + limits: wgpu::Limits::default(), + }, + None, + ) + .await + .unwrap(); + + log::info!("Configuring window surface..."); + let size = window.inner_size(); + let surface_config = surface + .get_default_config(&adapter, size.width, size.height) + .unwrap(); + surface.configure(&device, &surface_config); + + Self { + instance, + size, + surface, + surface_config, + adapter, + device, + queue, + } + } +} + +pub(crate) struct Renderer { + clear_color: wgpu::Color, + render_pipeline: wgpu::RenderPipeline, +} + +impl Renderer { + pub fn new(context: &RenderContext) -> Self { + log::info!("Creating render shader..."); + let shader_descriptor = wgpu::include_wgsl!("../assets/shaders/shader.wgsl"); + let shader = context.device.create_shader_module(shader_descriptor); + + log::info!("Creating render pipeline..."); + let render_pipeline = + context + .device + .create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: None, + layout: Some(&context.device.create_pipeline_layout( + &wgpu::PipelineLayoutDescriptor { + label: Some("draw"), + bind_group_layouts: &[], + push_constant_ranges: &[], + }, + )), + vertex: wgpu::VertexState { + module: &shader, + entry_point: "vertex", + buffers: &[], + }, + fragment: Some(wgpu::FragmentState { + module: &shader, + entry_point: "fragment", + targets: &[Some(context.surface_config.format.into())], + }), + primitive: wgpu::PrimitiveState::default(), + depth_stencil: None, + multisample: wgpu::MultisampleState::default(), + multiview: None, + }); + + let clear_color = wgpu::Color { + r: 255.0 / 255.0, + g: 216.0 / 255.0, + b: 102.0 / 255.0, + a: 1.0, + }; + + Self { + clear_color, + render_pipeline, + } + } + + pub fn render(&mut self, context: &RenderContext) { + let frame = context.surface.get_current_texture().unwrap(); + let view = frame + .texture + .create_view(&wgpu::TextureViewDescriptor::default()); + + let mut encoder = context + .device + .create_command_encoder(&wgpu::CommandEncoderDescriptor::default()); + + 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(self.clear_color), + store: true, + }, + })], + depth_stencil_attachment: None, + }); + + context.queue.submit(Some(encoder.finish())); + frame.present(); + } +}