285 lines
7.1 KiB
Rust
Raw Normal View History

2024-01-10 17:53:27 -08:00
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<Self> {
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::<TexturedVertex, ObjectInstance>(
"object",
&device,
Some(SPRITE_VERTICES),
Some(SPRITE_INDICES),
galactica_constants::OBJECT_SPRITE_INSTANCE_LIMIT,
)),
starfield: Rc::new(VertexBuffer::new::<TexturedVertex, StarfieldInstance>(
"starfield",
&device,
Some(SPRITE_VERTICES),
Some(SPRITE_INDICES),
galactica_constants::STARFIELD_SPRITE_INSTANCE_LIMIT,
)),
ui: Rc::new(VertexBuffer::new::<TexturedVertex, UiInstance>(
"ui",
&device,
Some(SPRITE_VERTICES),
Some(SPRITE_INDICES),
galactica_constants::UI_SPRITE_INSTANCE_LIMIT,
)),
particle: Rc::new(VertexBuffer::new::<TexturedVertex, ParticleInstance>(
"particle",
&device,
Some(SPRITE_VERTICES),
Some(SPRITE_INDICES),
galactica_constants::PARTICLE_SPRITE_INSTANCE_LIMIT,
)),
radialbar: Rc::new(VertexBuffer::new::<TexturedVertex, RadialBarInstance>(
"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,
});
}
}