use anyhow::Result; use galactica_content::Content; use glyphon::{ Attrs, Buffer, Family, FontSystem, Metrics, Shaping, SwashCache, TextAtlas, TextRenderer, }; use std::rc::Rc; use crate::{ datastructs::{RenderState, VertexBuffers}, globaluniform::GlobalUniform, pipeline::PipelineBuilder, shaderprocessor::preprocess_shader, starfield::Starfield, texturearray::TextureArray, ui::UiManager, vertexbuffer::{ consts::{SPRITE_INDICES, SPRITE_VERTICES}, types::{ ObjectInstance, ParticleInstance, RadialBarInstance, StarfieldInstance, TexturedVertex, UiInstance, }, VertexBuffer, }, GPUState, }; impl GPUState { /// Make a new GPUState that draws on `window` pub async fn new(window: winit::window::Window, ct: &Content) -> Result { let window_size = window.inner_size(); let window_aspect = window_size.width as f32 / window_size.height as f32; let instance = wgpu::Instance::new(wgpu::InstanceDescriptor { backends: wgpu::Backends::all(), ..Default::default() }); let surface = unsafe { instance.create_surface(&window) }.unwrap(); // Basic setup let device; let queue; let config; { let adapter = instance .request_adapter(&wgpu::RequestAdapterOptions { power_preference: wgpu::PowerPreference::default(), compatible_surface: Some(&surface), force_fallback_adapter: false, }) .await .unwrap(); (device, queue) = adapter .request_device( &wgpu::DeviceDescriptor { features: wgpu::Features::TEXTURE_BINDING_ARRAY | wgpu::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING, // We may need limits if we compile for wasm limits: wgpu::Limits::default(), label: Some("gpu device"), }, None, ) .await .unwrap(); // Assume sRGB let surface_caps = surface.get_capabilities(&adapter); let surface_format = surface_caps .formats .iter() .copied() .filter(|f| f.is_srgb()) .filter(|f| f.has_stencil_aspect()) .next() .unwrap_or(surface_caps.formats[0]); config = wgpu::SurfaceConfiguration { usage: wgpu::TextureUsages::RENDER_ATTACHMENT, format: surface_format, width: window_size.width, height: window_size.height, present_mode: surface_caps.present_modes[0], alpha_mode: surface_caps.alpha_modes[0], view_formats: vec![], }; surface.configure(&device, &config); } let vertex_buffers = VertexBuffers { object_counter: 0, ui_counter: 0, particle_counter: 0, radialbar_counter: 0, object: Rc::new(VertexBuffer::new::( "object", &device, Some(SPRITE_VERTICES), Some(SPRITE_INDICES), galactica_constants::OBJECT_SPRITE_INSTANCE_LIMIT, )), starfield: Rc::new(VertexBuffer::new::( "starfield", &device, Some(SPRITE_VERTICES), Some(SPRITE_INDICES), galactica_constants::STARFIELD_SPRITE_INSTANCE_LIMIT, )), ui: Rc::new(VertexBuffer::new::( "ui", &device, Some(SPRITE_VERTICES), Some(SPRITE_INDICES), galactica_constants::UI_SPRITE_INSTANCE_LIMIT, )), particle: Rc::new(VertexBuffer::new::( "particle", &device, Some(SPRITE_VERTICES), Some(SPRITE_INDICES), galactica_constants::PARTICLE_SPRITE_INSTANCE_LIMIT, )), radialbar: Rc::new(VertexBuffer::new::( "radial bar", &device, Some(SPRITE_VERTICES), Some(SPRITE_INDICES), 10, )), }; // Load uniforms let global_uniform = GlobalUniform::new(&device); let texture_array = TextureArray::new(&device, &queue, ct)?; // Make sure these match the indices in each shader let bind_group_layouts = &[ &texture_array.bind_group_layout, &global_uniform.bind_group_layout, ]; // Text renderer let mut text_atlas = TextAtlas::new(&device, &queue, wgpu::TextureFormat::Bgra8UnormSrgb); let mut text_font_system = FontSystem::new(); let text_cache = SwashCache::new(); let text_renderer = TextRenderer::new( &mut text_atlas, &device, wgpu::MultisampleState::default(), None, ); let text_buffer = { let mut buffer = Buffer::new(&mut text_font_system, Metrics::new(30.0, 42.0)); buffer.set_size( &mut text_font_system, window_size.width as f32, window_size.height as f32, ); buffer.set_text(&mut text_font_system, "Hello world! 👋\nThis is rendered with 🦅 glyphon 🦁\nThe text below should be partially clipped.\na b c d e f g h i j k l m n o p q r s t u v w x y z", Attrs::new().family(Family::SansSerif), Shaping::Advanced); buffer.shape_until_scroll(&mut text_font_system); buffer }; // Create render pipelines let object_pipeline = PipelineBuilder::new("object", &device) .set_shader(&preprocess_shader( &include_str!(concat!( env!("CARGO_MANIFEST_DIR"), "/shaders/", "object.wgsl" )), &global_uniform, 1, )) .set_format(config.format) .set_triangle(true) .set_vertex_buffer(&vertex_buffers.object) .set_bind_group_layouts(bind_group_layouts) .build(); let starfield_pipeline = PipelineBuilder::new("starfield", &device) .set_shader(&preprocess_shader( &include_str!(concat!( env!("CARGO_MANIFEST_DIR"), "/shaders/", "starfield.wgsl" )), &global_uniform, 1, )) .set_format(config.format) .set_triangle(true) .set_vertex_buffer(&vertex_buffers.starfield) .set_bind_group_layouts(bind_group_layouts) .build(); let ui_pipeline = PipelineBuilder::new("ui", &device) .set_shader(&preprocess_shader( &include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/shaders/", "ui.wgsl")), &global_uniform, 1, )) .set_format(config.format) .set_triangle(true) .set_vertex_buffer(&vertex_buffers.ui) .set_bind_group_layouts(bind_group_layouts) .build(); let particle_pipeline = PipelineBuilder::new("particle", &device) .set_shader(&preprocess_shader( &include_str!(concat!( env!("CARGO_MANIFEST_DIR"), "/shaders/", "particle.wgsl" )), &global_uniform, 1, )) .set_format(config.format) .set_triangle(true) .set_vertex_buffer(&vertex_buffers.particle) .set_bind_group_layouts(bind_group_layouts) .build(); let radialbar_pipeline = PipelineBuilder::new("radialbar", &device) .set_shader(&preprocess_shader( &include_str!(concat!( env!("CARGO_MANIFEST_DIR"), "/shaders/", "radialbar.wgsl" )), &global_uniform, 1, )) .set_format(config.format) .set_triangle(true) .set_vertex_buffer(&vertex_buffers.radialbar) .set_bind_group_layouts(bind_group_layouts) .build(); let mut starfield = Starfield::new(); starfield.regenerate(); return Ok(Self { state: RenderState { queue, window, window_size, window_aspect, global_uniform, vertex_buffers, text_atlas, text_buffer, text_cache, text_font_system, text_renderer, }, ui: UiManager::new(), device, config, surface, starfield, texture_array, object_pipeline, starfield_pipeline, ui_pipeline, particle_pipeline, radialbar_pipeline, }); } }