diff options
author | tcmal <me@aria.rip> | 2024-08-25 17:44:23 +0100 |
---|---|---|
committer | tcmal <me@aria.rip> | 2024-08-25 17:44:23 +0100 |
commit | 6eb9083b1f2fec63e17dc8ef462ea89bae0854b5 (patch) | |
tree | 814f670c38040c37408db06b66c9cb24550393e9 /stockton-render/src/draw/ui | |
parent | 6ef351de4ba506e7f0f285569aa0e22255bb68c6 (diff) |
feat(render): ui draw pass
Diffstat (limited to 'stockton-render/src/draw/ui')
-rw-r--r-- | stockton-render/src/draw/ui/mod.rs | 59 | ||||
-rw-r--r-- | stockton-render/src/draw/ui/pipeline.rs | 294 | ||||
-rw-r--r-- | stockton-render/src/draw/ui/render.rs | 97 | ||||
-rwxr-xr-x | stockton-render/src/draw/ui/texture.rs | 67 |
4 files changed, 50 insertions, 467 deletions
diff --git a/stockton-render/src/draw/ui/mod.rs b/stockton-render/src/draw/ui/mod.rs index 5daa117..1b52753 100644 --- a/stockton-render/src/draw/ui/mod.rs +++ b/stockton-render/src/draw/ui/mod.rs @@ -1,11 +1,52 @@ -pub mod pipeline; -pub mod render; -pub mod texture; +use crate::draw::texture::{resolver::TextureResolver, LoadableImage}; +use egui::{CtxRef, Texture}; +use std::{convert::TryInto, sync::Arc}; -pub use pipeline::UiPipeline; -pub use render::do_render; -use stockton_types::Vector2; -pub use texture::{ensure_textures, UiTextures}; +pub struct UiTextures { + ctx: CtxRef, +} -#[derive(Debug)] -pub struct UiPoint(pub Vector2, pub Vector2, pub [f32; 4]); +impl TextureResolver for UiTextures { + type Image = Arc<Texture>; + fn resolve(&mut self, tex: u32) -> Option<Self::Image> { + if tex == 0 { + Some(self.ctx.texture()) + } else { + None + } + } +} + +impl UiTextures { + pub fn new(ctx: CtxRef) -> Self { + UiTextures { ctx } + } +} + +impl LoadableImage for Arc<Texture> { + fn width(&self) -> u32 { + self.width as u32 + } + fn height(&self) -> u32 { + self.height as u32 + } + fn copy_row(&self, y: u32, ptr: *mut u8) { + let row_size = self.width(); + let pixels = &self.pixels[(y * row_size) as usize..((y + 1) * row_size) as usize]; + + for (i, x) in pixels.iter().enumerate() { + unsafe { + *ptr.offset(i as isize * 4) = 255; + *ptr.offset((i as isize * 4) + 1) = 255; + *ptr.offset((i as isize * 4) + 2) = 255; + *ptr.offset((i as isize * 4) + 3) = *x; + } + } + } + + unsafe fn copy_into(&self, ptr: *mut u8, row_size: usize) { + for y in 0..self.height() { + self.copy_row(y, ptr.offset((row_size * y as usize).try_into().unwrap())); + } + } +} diff --git a/stockton-render/src/draw/ui/pipeline.rs b/stockton-render/src/draw/ui/pipeline.rs deleted file mode 100644 index dfa4e88..0000000 --- a/stockton-render/src/draw/ui/pipeline.rs +++ /dev/null @@ -1,294 +0,0 @@ -//! A complete graphics pipeline - -/// Entry point name for shaders -const ENTRY_NAME: &str = "main"; - -/// Source for vertex shader. TODO -const VERTEX_SOURCE: &str = include_str!("./data/stockton.vert"); - -/// Source for fragment shader. TODO -const FRAGMENT_SOURCE: &str = include_str!("./data/stockton.frag"); - -use std::{ - array::IntoIter, - iter::once, - mem::{size_of, ManuallyDrop}, -}; - -use crate::draw::target::SwapchainProperties; -use crate::{error::EnvironmentError, types::*}; -use anyhow::{Context, Result}; - -/// A complete 2D graphics pipeline and associated resources -pub struct UiPipeline { - /// Our main render pass - pub(crate) renderpass: ManuallyDrop<RenderPassT>, - - /// The layout of our main graphics pipeline - pub(crate) pipeline_layout: ManuallyDrop<PipelineLayoutT>, - - /// Our main graphics pipeline - pub(crate) pipeline: ManuallyDrop<GraphicsPipelineT>, - - /// The vertex shader module - pub(crate) vs_module: ManuallyDrop<ShaderModuleT>, - - /// The fragment shader module - pub(crate) fs_module: ManuallyDrop<ShaderModuleT>, -} - -impl UiPipeline { - pub fn new<'a, T: Iterator<Item = &'a DescriptorSetLayoutT>>( - device: &mut DeviceT, - extent: hal::image::Extent, - swapchain_properties: &SwapchainProperties, - set_layouts: T, - ) -> Result<Self> { - use hal::format::Format; - use hal::pso::*; - - // Renderpass - let renderpass = { - use hal::{ - image::{Access, Layout}, - memory::Dependencies, - pass::*, - }; - - let img_attachment = Attachment { - format: Some(swapchain_properties.format), - samples: 1, - ops: AttachmentOps::new(AttachmentLoadOp::Load, AttachmentStoreOp::Store), - stencil_ops: AttachmentOps::new( - AttachmentLoadOp::DontCare, - AttachmentStoreOp::DontCare, - ), - layouts: Layout::ColorAttachmentOptimal..Layout::Present, - }; - - let subpass = SubpassDesc { - colors: &[(0, Layout::ColorAttachmentOptimal)], - depth_stencil: None, - inputs: &[], - resolves: &[], - preserves: &[], - }; - - let external_dependency = SubpassDependency { - flags: Dependencies::empty(), - passes: None..Some(0), - stages: PipelineStage::COLOR_ATTACHMENT_OUTPUT - ..(PipelineStage::COLOR_ATTACHMENT_OUTPUT - | PipelineStage::EARLY_FRAGMENT_TESTS), - accesses: Access::empty() - ..(Access::COLOR_ATTACHMENT_READ | Access::COLOR_ATTACHMENT_WRITE), - }; - - unsafe { - device.create_render_pass( - IntoIter::new([img_attachment]), - IntoIter::new([subpass]), - IntoIter::new([external_dependency]), - ) - } - .context("Error creating render pass")? - }; - - // Subpass - let subpass = hal::pass::Subpass { - index: 0, - main_pass: &renderpass, - }; - - // Shader modules - let (vs_module, fs_module) = { - let mut compiler = shaderc::Compiler::new().ok_or(EnvironmentError::NoShaderC)?; - - let vertex_compile_artifact = compiler - .compile_into_spirv( - VERTEX_SOURCE, - shaderc::ShaderKind::Vertex, - "vertex_ui.vert", - ENTRY_NAME, - None, - ) - .context("Error compiling vertex shader")?; - - let fragment_compile_artifact = compiler - .compile_into_spirv( - FRAGMENT_SOURCE, - shaderc::ShaderKind::Fragment, - "fragment_ui.frag", - ENTRY_NAME, - None, - ) - .context("Error compiling fragment shader")?; - - // Make into shader module - unsafe { - ( - device - .create_shader_module(vertex_compile_artifact.as_binary()) - .context("Error creating vertex shader module")?, - device - .create_shader_module(fragment_compile_artifact.as_binary()) - .context("Error creating fragment shader module")?, - ) - } - }; - - // Shader entry points (ShaderStage) - let (vs_entry, fs_entry) = ( - EntryPoint::<back::Backend> { - entry: ENTRY_NAME, - module: &vs_module, - specialization: Specialization::default(), - }, - EntryPoint::<back::Backend> { - entry: ENTRY_NAME, - module: &fs_module, - specialization: Specialization::default(), - }, - ); - - // Rasterizer - let rasterizer = Rasterizer { - polygon_mode: PolygonMode::Fill, - cull_face: Face::NONE, - front_face: FrontFace::CounterClockwise, - depth_clamping: false, - depth_bias: None, - conservative: true, - line_width: State::Static(1.0), - }; - - // Depth stencil - let depth_stencil = DepthStencilDesc { - depth: None, - depth_bounds: false, - stencil: None, - }; - - // Pipeline layout - let layout = unsafe { - device.create_pipeline_layout(set_layouts, once((ShaderStageFlags::VERTEX, 0..8))) - } - .context("Error creating pipeline layout")?; - - // Colour blending - let blender = { - let blend_state = BlendState { - color: BlendOp::Add { - src: Factor::SrcAlpha, - dst: Factor::OneMinusSrcAlpha, - }, - alpha: BlendOp::Add { - src: Factor::SrcAlpha, - dst: Factor::OneMinusSrcAlpha, - }, - }; - - BlendDesc { - logic_op: None, - targets: vec![ColorBlendDesc { - mask: ColorMask::ALL, - blend: Some(blend_state), - }], - } - }; - - // Baked states - let baked_states = BakedStates { - viewport: Some(Viewport { - rect: extent.rect(), - depth: (0.0..1.0), - }), - scissor: None, - blend_constants: None, - depth_bounds: None, - }; - - // Primitive assembler - let primitive_assembler = PrimitiveAssemblerDesc::Vertex { - buffers: &[VertexBufferDesc { - binding: 0, - stride: (size_of::<f32>() * 8) as u32, - rate: VertexInputRate::Vertex, - }], - attributes: &[ - AttributeDesc { - location: 0, - binding: 0, - element: Element { - format: Format::Rg32Sfloat, - offset: 0, - }, - }, - AttributeDesc { - location: 1, - binding: 0, - element: Element { - format: Format::Rg32Sfloat, - offset: (size_of::<f32>() * 2) as u32, - }, - }, - AttributeDesc { - location: 2, - binding: 0, - element: Element { - format: Format::Rgba32Sfloat, - offset: (size_of::<f32>() * 4) as u32, - }, - }, - ], - input_assembler: InputAssemblerDesc::new(Primitive::TriangleList), - vertex: vs_entry, - tessellation: None, - geometry: None, - }; - - // Pipeline description - let pipeline_desc = GraphicsPipelineDesc { - label: Some("UI Pipeline"), - primitive_assembler, - rasterizer, - fragment: Some(fs_entry), - blender, - depth_stencil, - multisampling: None, - baked_states, - layout: &layout, - subpass, - flags: PipelineCreationFlags::empty(), - parent: BasePipeline::None, - }; - - // Pipeline - let pipeline = unsafe { device.create_graphics_pipeline(&pipeline_desc, None) } - .context("Error creating graphics pipeline")?; - - Ok(UiPipeline { - renderpass: ManuallyDrop::new(renderpass), - pipeline_layout: ManuallyDrop::new(layout), - pipeline: ManuallyDrop::new(pipeline), - vs_module: ManuallyDrop::new(vs_module), - fs_module: ManuallyDrop::new(fs_module), - }) - } - - /// Deactivate vulkan resources. Use before dropping - pub fn deactivate(self, device: &mut DeviceT) { - unsafe { - use core::ptr::read; - - device.destroy_render_pass(ManuallyDrop::into_inner(read(&self.renderpass))); - - device.destroy_shader_module(ManuallyDrop::into_inner(read(&self.vs_module))); - device.destroy_shader_module(ManuallyDrop::into_inner(read(&self.fs_module))); - - device.destroy_graphics_pipeline(ManuallyDrop::into_inner(read(&self.pipeline))); - - device.destroy_pipeline_layout(ManuallyDrop::into_inner(read(&self.pipeline_layout))); - } - } -} diff --git a/stockton-render/src/draw/ui/render.rs b/stockton-render/src/draw/ui/render.rs deleted file mode 100644 index 009a1b7..0000000 --- a/stockton-render/src/draw/ui/render.rs +++ /dev/null @@ -1,97 +0,0 @@ -use crate::draw::texture::TextureRepo; -use hal::pso::{Rect, ShaderStageFlags}; - -use super::UiPoint; -use crate::draw::buffers::DrawBuffers; -use crate::types::*; -use crate::UiState; -use anyhow::{anyhow, Result}; -use egui::{ClippedMesh, TextureId}; -use std::{array::IntoIter, convert::TryInto, iter::empty}; -use stockton_types::Vector2; - -pub fn do_render( - cmd_buffer: &mut CommandBufferT, - pipeline_layout: &PipelineLayoutT, - draw_buffers: &mut DrawBuffers<UiPoint>, - tex_repo: &mut TextureRepo, - ui: &mut UiState, -) -> Result<()> { - // TODO: Actual UI Rendering - let (_out, shapes) = ui.end_frame(); - let screen = ui.dimensions().ok_or(anyhow!("UI not set up properly."))?; - let shapes = ui.ctx().tessellate(shapes); - - for ClippedMesh(rect, tris) in shapes.iter() { - assert!(tris.texture_id == TextureId::Egui); - - // Copy triangles/indicies - for i in (0..tris.indices.len()).step_by(3) { - draw_buffers.index_buffer[i / 3] = ( - tris.indices[i].try_into()?, - tris.indices[i + 1].try_into()?, - tris.indices[i + 2].try_into()?, - ); - // eprintln!( - // "{} {}", - // tris.vertices[tris.indices[i] as usize].uv.x, - // tris.vertices[tris.indices[i] as usize].uv.y - // ); - // eprintln!( - // "{} {}", - // tris.vertices[tris.indices[i + 1] as usize].uv.x, - // tris.vertices[tris.indices[i + 1] as usize].uv.y - // ); - // eprintln!( - // "{} {}", - // tris.vertices[tris.indices[i + 2] as usize].uv.x, - // tris.vertices[tris.indices[i + 2] as usize].uv.y - // ); - } - for (i, vertex) in tris.vertices.iter().enumerate() { - draw_buffers.vertex_buffer[i] = UiPoint( - Vector2::new(vertex.pos.x, vertex.pos.y), - Vector2::new(vertex.uv.x, vertex.uv.y), - [ - vertex.color.r() as f32 / 255.0, - vertex.color.g() as f32 / 255.0, - vertex.color.b() as f32 / 255.0, - vertex.color.a() as f32 / 255.0, - ], - ); - } - // TODO: *Properly* deal with textures - if let Some(ds) = tex_repo.attempt_get_descriptor_set(0) { - unsafe { - cmd_buffer.push_graphics_constants( - pipeline_layout, - ShaderStageFlags::VERTEX, - 0, - &[screen.x.to_bits(), screen.y.to_bits()], - ); - - cmd_buffer.set_scissors( - 0, - IntoIter::new([Rect { - x: rect.min.x as i16, - y: rect.min.y as i16, - w: rect.width() as i16, - h: rect.height() as i16, - }]), - ); - cmd_buffer.bind_graphics_descriptor_sets( - pipeline_layout, - 0, - IntoIter::new([ds]), - empty(), - ); - // Call draw - cmd_buffer.draw_indexed(0..tris.indices.len() as u32, 0, 0..1); - } - } else { - // tex_repo.queue_load(0); - } - } - - Ok(()) -} diff --git a/stockton-render/src/draw/ui/texture.rs b/stockton-render/src/draw/ui/texture.rs deleted file mode 100755 index 7cba1ac..0000000 --- a/stockton-render/src/draw/ui/texture.rs +++ /dev/null @@ -1,67 +0,0 @@ -use crate::draw::texture::{resolver::TextureResolver, LoadableImage, TextureRepo}; -use crate::UiState; -use anyhow::Result; -use egui::{CtxRef, Texture}; -use log::debug; -use std::{convert::TryInto, sync::Arc}; - -pub struct UiTextures { - ctx: CtxRef, -} - -impl TextureResolver for UiTextures { - type Image = Arc<Texture>; - fn resolve(&mut self, tex: u32) -> Option<Self::Image> { - if tex == 0 { - Some(self.ctx.texture()) - } else { - None - } - } -} - -impl UiTextures { - pub fn new(ctx: CtxRef) -> Self { - UiTextures { ctx } - } -} - -impl LoadableImage for Arc<Texture> { - fn width(&self) -> u32 { - self.width as u32 - } - fn height(&self) -> u32 { - self.height as u32 - } - fn copy_row(&self, y: u32, ptr: *mut u8) { - let row_size = self.width(); - let pixels = &self.pixels[(y * row_size) as usize..((y + 1) * row_size) as usize]; - - for (i, x) in pixels.iter().enumerate() { - unsafe { - *ptr.offset(i as isize * 4) = 255; - *ptr.offset((i as isize * 4) + 1) = 255; - *ptr.offset((i as isize * 4) + 2) = 255; - *ptr.offset((i as isize * 4) + 3) = *x; - } - } - } - - unsafe fn copy_into(&self, ptr: *mut u8, row_size: usize) { - for y in 0..self.height() { - self.copy_row(y, ptr.offset((row_size * y as usize).try_into().unwrap())); - } - } -} - -pub fn ensure_textures(tex_repo: &mut TextureRepo, ui: &mut UiState) -> Result<()> { - let tex = ui.ctx().texture(); - - if tex.version != ui.last_tex_ver { - debug!("Queueing UI Texture reload"); - tex_repo.force_queue_load(0)?; - ui.last_tex_ver = tex.version; - } - - Ok(()) -} |