use anyhow::Result; use galactica_content::Content; use galactica_util::constants::{ OBJECT_SPRITE_INSTANCE_LIMIT, PARTICLE_SPRITE_INSTANCE_LIMIT, UI_SPRITE_INSTANCE_LIMIT, }; use glyphon::{FontSystem, 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), OBJECT_SPRITE_INSTANCE_LIMIT, )), starfield: Rc::new(VertexBuffer::new::( "starfield", &device, Some(SPRITE_VERTICES), Some(SPRITE_INDICES), ct.get_config().starfield_instance_limit, )), ui: Rc::new(VertexBuffer::new::( "ui", &device, Some(SPRITE_VERTICES), Some(SPRITE_INDICES), UI_SPRITE_INSTANCE_LIMIT, )), particle: Rc::new(VertexBuffer::new::( "particle", &device, Some(SPRITE_VERTICES), Some(SPRITE_INDICES), 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_with_locale_and_db( "en-US".to_string(), glyphon::fontdb::Database::new(), ); let conf = ct.get_config(); for font in &conf.font_files { text_font_system.db_mut().load_font_file(font)?; } // TODO: nice error if no family with this name is found text_font_system .db_mut() .set_sans_serif_family(conf.font_sans.clone()); text_font_system .db_mut() .set_serif_family(conf.font_serif.clone()); text_font_system .db_mut() .set_monospace_family(conf.font_mono.clone()); //text_font_system // .db_mut() // .set_cursive_family(conf.font_cursive.clone()); //text_font_system // .db_mut() // .set_fantasy_family(conf.font_fantasy.clone()); let text_cache = SwashCache::new(); let text_renderer = TextRenderer::new( &mut text_atlas, &device, wgpu::MultisampleState::default(), None, ); // 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(ct); let mut state = RenderState { queue, window, window_size, window_aspect, global_uniform, vertex_buffers, text_atlas, text_cache, text_font_system, text_renderer, }; return Ok(Self { ui: UiManager::new(&mut state), device, config, surface, starfield, texture_array, object_pipeline, starfield_pipeline, ui_pipeline, particle_pipeline, radialbar_pipeline, state, }); } }