From 93347346e8bd8f7412ae03a0858dd307a1df2e0d Mon Sep 17 00:00:00 2001 From: Micha White Date: Thu, 20 Oct 2022 20:39:44 -0400 Subject: Moved files into workspace --- src/renderer.rs | 410 -------------------------------------------------------- 1 file changed, 410 deletions(-) delete mode 100644 src/renderer.rs (limited to 'src/renderer.rs') diff --git a/src/renderer.rs b/src/renderer.rs deleted file mode 100644 index afcb92b..0000000 --- a/src/renderer.rs +++ /dev/null @@ -1,410 +0,0 @@ -use std::{convert::TryInto, num::NonZeroU32}; - -use crate::{ - vertex::SQUARE, Camera, Instance, InstanceBuffer, RenderWindowConfig, TextureAtlas, Vertex, -}; -use pollster::FutureExt; -use thiserror::Error; -use wgpu::{include_wgsl, util::DeviceExt}; -use winit::{ - dpi::PhysicalSize, - error::OsError, - event::{Event, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, - window::Window, -}; - -/// No device could be found which supports the given surface -#[derive(Clone, Copy, Debug, Error)] -#[error("No GPU could be found on this machine")] -pub struct NoGpuError { - /// Prevents this type from being constructed - _priv: (), -} - -impl NoGpuError { - /// Create a new error - const fn new() -> Self { - Self { _priv: () } - } -} - -#[derive(Debug, Error)] -pub enum NewRendererError { - #[error(transparent)] - NoGpu(#[from] NoGpuError), - #[error(transparent)] - // TODO better error - WindowInitError(#[from] OsError), -} - -// TODO make this Debug -pub struct Renderer { - // TODO move some of this data elsewhere - surface: wgpu::Surface, - surface_config: wgpu::SurfaceConfiguration, - supported_present_modes: Box<[wgpu::PresentMode]>, - device: wgpu::Device, - queue: wgpu::Queue, - render_pipeline: wgpu::RenderPipeline, - square_vertex_buffer: wgpu::Buffer, - square_vertices: u32, - instances: InstanceBuffer, - camera: Camera, - textures: TextureAtlas, - event_loop: Option>, - window: Window, -} - -fn get_adapter( - instance: &wgpu::Instance, - surface: &wgpu::Surface, - power_preference: wgpu::PowerPreference, -) -> Result { - let adapter = instance - .request_adapter(&wgpu::RequestAdapterOptions { - power_preference, - compatible_surface: Some(surface), - force_fallback_adapter: false, - }) - .block_on(); // TODO this takes too long - - let adapter = adapter.or_else(|| { - instance - .enumerate_adapters(wgpu::Backends::PRIMARY) - .find(|adapter| !surface.get_supported_formats(adapter).is_empty()) - }); - - adapter.ok_or(NoGpuError::new()) -} - -fn sprite_render_pipeline( - device: &wgpu::Device, - texture_format: wgpu::TextureFormat, - render_pipeline_layout: &wgpu::PipelineLayout, -) -> wgpu::RenderPipeline { - let shader = device.create_shader_module(include_wgsl!("../shaders/sprite.wgsl")); - - device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("Sprite Render Pipeline"), - layout: Some(render_pipeline_layout), - // information about the vertex shader - vertex: wgpu::VertexState { - module: &shader, - entry_point: "vs_main", - buffers: &[Vertex::desc(), Instance::desc()], - }, - // information about the fragment shader - fragment: Some(wgpu::FragmentState { - module: &shader, - entry_point: "fs_main", - targets: &[Some(wgpu::ColorTargetState { - format: texture_format, - blend: Some(wgpu::BlendState::ALPHA_BLENDING), - write_mask: wgpu::ColorWrites::ALL, - })], - }), - primitive: wgpu::PrimitiveState { - // don't render the back of a sprite - cull_mode: Some(wgpu::Face::Back), - ..Default::default() - }, - depth_stencil: None, - multisample: wgpu::MultisampleState::default(), - multiview: None, - }) -} - -impl Renderer { - /// Initializes the renderer - /// - /// # Errors - /// - /// Returns a [`NoGpu`] error if no device could be detected that can - /// display to the window - /// - /// # Panics - /// - /// This function **must** be called on the main thread, or else it may - /// panic on some platforms. - // TODO make it possible to use without a window (ie, use a bitmap in memory as a surface) - // TODO this function needs to be smaller - pub fn new(config: &RenderWindowConfig) -> Result { - #[cfg(feature = "profile-with-tracy")] - profiling::tracy_client::Client::start(); - profiling::register_thread!("main"); - - // build the window - let event_loop = EventLoop::new(); - let window = config.to_window().build(&event_loop)?; - let event_loop = Some(event_loop); - - // the instance's main purpose is to create an adapter and a surface - let instance = wgpu::Instance::new(wgpu::Backends::VULKAN); - - // the surface is the part of the screen we'll draw to - let surface = unsafe { instance.create_surface(&window) }; - - let power_preference = config.power_preference(); - - // the adapter is the handle to the GPU - let adapter = get_adapter(&instance, &surface, power_preference)?; - - // gets a connection to the device, as well as a handle to its command queue - // the options chosen here ensure that this is guaranteed to not panic - let (device, queue) = adapter - .request_device( - &wgpu::DeviceDescriptor { - features: wgpu::Features::empty(), - ..Default::default() - }, - None, - ) - .block_on() - .expect("there was no device with the selected features"); - - // configuration for the surface - let supported_present_modes = surface - .get_supported_present_modes(&adapter) - .into_boxed_slice(); - let supported_alpha_modes = surface - .get_supported_alpha_modes(&adapter) - .into_boxed_slice(); - let surface_config = config.to_surface_configuration( - &supported_present_modes, - &supported_alpha_modes, - surface.get_supported_formats(&adapter)[0], - ); - surface.configure(&device, &surface_config); - - // create the camera - let width = window.inner_size().width; - let height = window.inner_size().height; - let (camera, camera_bind_group_layout) = Camera::new(&device, width, height); - - // the vertex buffer used for rendering squares - let square_vertices = SQUARE - .len() - .try_into() - .expect("expected fewer than 3 billion vertices in a square"); - let square_vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("Square Vertex Buffer"), - contents: bytemuck::cast_slice(&SQUARE), - usage: wgpu::BufferUsages::VERTEX, - }); - - // create the instance buffer - let instances = InstanceBuffer::new(&device, config.instance_capacity); - - // TODO make this configurable - let (textures, texture_layout) = TextureAtlas::new( - &device, - window.inner_size().width, - window.inner_size().height, - ); - - let render_pipeline_layout = - device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some("Sprite Render Pipeline Layout"), - bind_group_layouts: &[&camera_bind_group_layout, &texture_layout], - push_constant_ranges: &[], - }); - - // set up a pipeline for sprite rendering - let render_pipeline = - sprite_render_pipeline(&device, surface_config.format, &render_pipeline_layout); - - Ok(Self { - surface, - surface_config, - supported_present_modes, - device, - queue, - render_pipeline, - square_vertex_buffer, - square_vertices, - instances, - camera, - textures, - event_loop, - window, - }) - } - - /// Reconfigure the surface - fn reconfigure(&mut self) { - self.surface.configure(&self.device, &self.surface_config); - } - - /// Resize just the renderer. The window will remain unchanged - fn resize_renderer(&mut self, size: PhysicalSize) { - if size.width == 0 || size.height == 0 { - log::error!("The window was somehow set to a size of zero"); - return; - } - - self.surface_config.height = size.height; - self.surface_config.width = size.width; - self.camera.set_size(size.width, size.height); - self.reconfigure(); - } - - /// Set the physical window and renderer size - pub fn resize(&mut self, width: NonZeroU32, height: NonZeroU32) { - let size = PhysicalSize::new(width.get(), height.get()); - self.window.set_inner_size(size); - self.resize_renderer(size); - } - - /// Set vsync on or off. See `[RenderWindowConfig::present_mode]` for more details. - pub fn set_vsync(&mut self, vsync: bool) { - self.surface_config.present_mode = - RenderWindowConfig::present_mode(vsync, &self.supported_present_modes); - self.reconfigure(); - } - - /// Set the window's title - pub fn set_title(&mut self, title: &str) { - self.window.set_title(title); - } - - /// The reference buffer - pub const fn instances(&self) -> &InstanceBuffer { - &self.instances - } - - /// The reference buffer - pub fn instances_mut(&mut self) -> &mut InstanceBuffer { - &mut self.instances - } - - /// Get the camera information - pub const fn camera(&self) -> &Camera { - &self.camera - } - - /// Get a mutable reference to the camera - pub fn camera_mut(&mut self) -> &mut Camera { - &mut self.camera - } - - /// Get a reference to the texture atlas - pub const fn textures(&self) -> &TextureAtlas { - &self.textures - } - - /// Get a mutable reference to the texture atlas - pub fn textures_mut(&mut self) -> &mut TextureAtlas { - &mut self.textures - } - - /// Renders a new frame to the window - /// - /// # Errors - /// - /// A number of problems could occur here. A timeout could occur while - /// trying to acquire the next frame. There may also be no more memory left - /// that can be used for the new frame. - #[profiling::function] - fn render(&mut self) -> Result<(), wgpu::SurfaceError> { - // the new texture we can render to - let output = self.surface.get_current_texture()?; - let view = output - .texture - .create_view(&wgpu::TextureViewDescriptor::default()); - - // this will allow us to send commands to the gpu - let mut encoder = self - .device - .create_command_encoder(&wgpu::CommandEncoderDescriptor { - label: Some("Render Encoder"), - }); - - let num_instances = self.instances.len(); - self.instances.fill_buffer(&self.device, &self.queue); - self.camera.refresh(&self.queue); - self.textures.fill_textures(&self.queue); - - { - profiling::scope!("encode render pass"); - 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::BLACK), - store: true, - }, - })], - depth_stencil_attachment: None, - }); - - render_pass.set_pipeline(&self.render_pipeline); - render_pass.set_bind_group(0, self.camera.bind_group(), &[]); - render_pass.set_bind_group(1, self.textures.bind_group(), &[]); - render_pass.set_vertex_buffer(0, self.square_vertex_buffer.slice(..)); - render_pass.set_vertex_buffer(1, self.instances.buffer_slice()); - render_pass.draw(0..self.square_vertices, 0..num_instances); - } - // the encoder can't finish building the command buffer until the - // render pass is dropped - - // submit the command buffer to the GPU - profiling::scope!("submit render"); - self.queue.submit(std::iter::once(encoder.finish())); - output.present(); - - Ok(()) - } - - /// Take the event loop out of the Renderer, without moving it - /// - /// # Panics - /// - /// This method must only be called once - // TODO This is a quick fix to get the event loop inside the renderer. - // In the future, we should make a separate struct that contains the - // renderer and the event loop, which we move the event loop out of - // while still being able to move the renderer. - fn event_loop(&mut self) -> EventLoop<()> { - self.event_loop.take().unwrap() - } - - /// Run the renderer indefinitely - pub fn run(mut self, f: &'static dyn Fn(&mut Self)) -> ! { - self.window.set_visible(true); - let event_loop = self.event_loop(); - event_loop.run(move |event, _, control_flow| match event { - Event::WindowEvent { window_id, event } => { - if window_id == self.window.id() { - match event { - WindowEvent::Resized(size) => self.resize_renderer(size), - WindowEvent::CloseRequested => { - *control_flow = ControlFlow::ExitWithCode(0); - } - _ => (), - } - } - } - Event::MainEventsCleared => { - f(&mut self); - match self.render() { - Ok(_) => {} - // reconfigure the surface if it's been lost - Err(wgpu::SurfaceError::Lost) => { - self.reconfigure(); - } - // if we ran out of memory, then we'll die - Err(wgpu::SurfaceError::OutOfMemory) => { - *control_flow = ControlFlow::ExitWithCode(1); - } - // otherwise, we'll just log the error - Err(e) => log::error!("{}", e), - }; - profiling::finish_frame!(); - } - _ => {} - }); - } -} -- cgit v1.2.3