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 | |
parent | 6ef351de4ba506e7f0f285569aa0e22255bb68c6 (diff) |
feat(render): ui draw pass
-rw-r--r-- | examples/render-quad/src/main.rs | 16 | ||||
-rw-r--r-- | stockton-render/src/draw/buffers/dedicated_image.rs | 199 | ||||
-rw-r--r-- | stockton-render/src/draw/builders/pipeline.rs | 1 | ||||
-rw-r--r-- | stockton-render/src/draw/draw_passes/cons.rs | 45 | ||||
-rw-r--r-- | stockton-render/src/draw/draw_passes/mod.rs | 2 | ||||
-rw-r--r-- | stockton-render/src/draw/draw_passes/ui.rs | 347 | ||||
-rw-r--r-- | stockton-render/src/draw/draw_passes/util.rs | 2 | ||||
-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 | ||||
-rw-r--r-- | stockton-render/src/window.rs | 4 |
12 files changed, 457 insertions, 676 deletions
diff --git a/examples/render-quad/src/main.rs b/examples/render-quad/src/main.rs index b16c3d0..681c2a1 100644 --- a/examples/render-quad/src/main.rs +++ b/examples/render-quad/src/main.rs @@ -13,7 +13,7 @@ use std::collections::BTreeMap; use std::sync::{Arc, RwLock}; use stockton_levels::parts::data::{Geometry, Vertex}; use stockton_levels::types::Rgba; -use stockton_render::draw::LevelDrawPass; +use stockton_render::draw::{ConsDrawPass, LevelDrawPass, UiDrawPass}; use winit::{event::Event, event_loop::EventLoop, window::WindowBuilder}; use egui::{containers::CentralPanel, Frame}; @@ -32,7 +32,7 @@ use stockton_types::{Session, Vector2, Vector3}; mod level; use level::*; -type Dp<'a> = LevelDrawPass<'a, DemoLevel>; +type Dp<'a> = ConsDrawPass<LevelDrawPass<'a, DemoLevel>, UiDrawPass<'a>>; #[derive(InputManager, Default, Clone, Debug)] struct MovementInputs { @@ -127,7 +127,7 @@ fn try_main<'a>() -> Result<()> { })); // Create the UI State - let mut ui = UiState::new(); + let ui = UiState::new(); // Create the input manager let manager = { @@ -155,6 +155,7 @@ fn try_main<'a>() -> Result<()> { resources.insert(manager); resources.insert(Timing::default()); resources.insert(Mouse::default()); + resources.insert(ui); }, move |schedule| { schedule @@ -187,14 +188,17 @@ fn try_main<'a>() -> Result<()> { )); // Create the renderer - let (renderer, tx): (Renderer<Dp<'static>>, _) = Renderer::new(&window, &session, player)?; + let (renderer, tx): (Renderer<Dp<'static>>, _) = + Renderer::new(&window, &session, (player, ()))?; let new_control_flow = renderer.update_control_flow.clone(); // Populate the initial UI state - ui.populate_initial_state(&renderer); + { + let ui = &mut session.resources.get_mut::<UiState>().unwrap(); + ui.populate_initial_state(&renderer); + } session.resources.insert(renderer); - session.resources.insert(ui); // Done loading - This is our main loop. // It just communicates events to the session and continuously ticks diff --git a/stockton-render/src/draw/buffers/dedicated_image.rs b/stockton-render/src/draw/buffers/dedicated_image.rs index 38a23c9..878d304 100644 --- a/stockton-render/src/draw/buffers/dedicated_image.rs +++ b/stockton-render/src/draw/buffers/dedicated_image.rs @@ -1,18 +1,16 @@ //! A dedicated image. Used for depth buffers. -use super::create_buffer; -use crate::draw::texture::{LoadableImage, PIXEL_SIZE}; +use crate::draw::texture::PIXEL_SIZE; use crate::types::*; -use std::{array::IntoIter, convert::TryInto, iter::empty, mem::ManuallyDrop}; +use std::mem::ManuallyDrop; use anyhow::{Context, Result}; use hal::{ - buffer::Usage as BufUsage, - format::{Aspects, Format, Swizzle}, + format::{Format, Swizzle}, image::{SubresourceRange, Usage, Usage as ImgUsage, ViewKind}, memory, - memory::{Properties, Segment}, + memory::Properties, MemoryTypeId, }; use thiserror::Error; @@ -122,195 +120,6 @@ impl DedicatedLoadedImage { }) } - /// Load the given image - pub fn load<T: LoadableImage>( - &mut self, - img: T, - device: &mut DeviceT, - adapter: &Adapter, - command_queue: &mut QueueT, - command_pool: &mut CommandPoolT, - ) -> Result<()> { - let initial_row_size = PIXEL_SIZE * img.width() as usize; - let limits = adapter.physical_device.properties().limits; - let row_alignment_mask = limits.optimal_buffer_copy_pitch_alignment as u32 - 1; - - let row_size = - ((initial_row_size as u32 + row_alignment_mask) & !row_alignment_mask) as usize; - let total_size = (row_size * (img.height() as usize)) as u64; - debug_assert!(row_size as usize >= initial_row_size); - - // Make a staging buffer - let (staging_buffer, mut staging_memory) = create_buffer( - device, - adapter, - BufUsage::TRANSFER_SRC, - memory::Properties::CPU_VISIBLE | memory::Properties::COHERENT, - total_size, - ) - .context("Error creating staging buffer")?; - - // Copy everything into it - unsafe { - let mapped_memory: *mut u8 = std::mem::transmute( - device - .map_memory( - &mut staging_memory, - Segment { - offset: 0, - size: None, - }, - ) - .context("Error mapping staging memory")?, - ); - - for y in 0..img.height() as usize { - let dest_base: isize = (y * row_size).try_into()?; - img.copy_row(y as u32, mapped_memory.offset(dest_base)); - } - - device.unmap_memory(&mut staging_memory); - } - - // Copy from staging to image memory - let buf = unsafe { - use hal::command::{BufferImageCopy, CommandBufferFlags}; - use hal::image::{Access, Extent, Layout, Offset, SubresourceLayers}; - use hal::memory::Barrier; - use hal::pso::PipelineStage; - - // Get a command buffer - let mut buf = command_pool.allocate_one(hal::command::Level::Primary); - buf.begin_primary(CommandBufferFlags::ONE_TIME_SUBMIT); - - // Setup the layout of our image for copying - let image_barrier = Barrier::Image { - states: (Access::empty(), Layout::Undefined) - ..(Access::TRANSFER_WRITE, Layout::TransferDstOptimal), - target: &(*self.image), - families: None, - range: SubresourceRange { - aspects: Aspects::COLOR, - level_start: 0, - level_count: Some(1), - layer_start: 0, - layer_count: Some(1), - }, - }; - buf.pipeline_barrier( - PipelineStage::TOP_OF_PIPE..PipelineStage::TRANSFER, - memory::Dependencies::empty(), - IntoIter::new([image_barrier]), - ); - - // Copy from buffer to image - buf.copy_buffer_to_image( - &staging_buffer, - &(*self.image), - Layout::TransferDstOptimal, - IntoIter::new([BufferImageCopy { - buffer_offset: 0, - buffer_width: (row_size / PIXEL_SIZE) as u32, - buffer_height: img.height(), - image_layers: SubresourceLayers { - aspects: Aspects::COLOR, - level: 0, - layers: 0..1, - }, - image_offset: Offset { x: 0, y: 0, z: 0 }, - image_extent: Extent { - width: img.width(), - height: img.height(), - depth: 1, - }, - }]), - ); - - // Setup the layout of our image for shaders - let image_barrier = Barrier::Image { - states: (Access::TRANSFER_WRITE, Layout::TransferDstOptimal) - ..(Access::SHADER_READ, Layout::ShaderReadOnlyOptimal), - target: &(*self.image), - families: None, - range: SubresourceRange { - aspects: Aspects::COLOR, - level_start: 0, - level_count: Some(1), - layer_start: 0, - layer_count: Some(1), - }, - }; - - buf.pipeline_barrier( - PipelineStage::TRANSFER..PipelineStage::FRAGMENT_SHADER, - memory::Dependencies::empty(), - IntoIter::new([image_barrier]), - ); - - buf.finish(); - - buf - }; - - // Submit our commands and wait for them to finish - unsafe { - let mut setup_finished = device - .create_fence(false) - .context("Error creating setup_finished fence")?; - command_queue.submit( - IntoIter::new([&buf]), - empty(), - empty(), - Some(&mut setup_finished), - ); - - device - .wait_for_fence(&setup_finished, core::u64::MAX) - .context("Error waiting for image load to finish")?; - device.destroy_fence(setup_finished); - }; - - // Clean up temp resources - unsafe { - command_pool.free(std::iter::once(buf)); - - device.free_memory(staging_memory); - device.destroy_buffer(staging_buffer); - } - - Ok(()) - } - - /// Load the given image into a new buffer - pub fn load_into_new<T: LoadableImage>( - img: T, - device: &mut DeviceT, - adapter: &Adapter, - command_queue: &mut QueueT, - command_pool: &mut CommandPoolT, - format: Format, - usage: Usage, - ) -> Result<DedicatedLoadedImage> { - let mut loaded_image = Self::new( - device, - adapter, - format, - usage | Usage::TRANSFER_DST, - SubresourceRange { - aspects: Aspects::COLOR, - level_start: 0, - level_count: Some(1), - layer_start: 0, - layer_count: Some(1), - }, - img.width() as usize, - img.height() as usize, - )?; - loaded_image.load(img, device, adapter, command_queue, command_pool)?; - - Ok(loaded_image) - } - /// Properly frees/destroys all the objects in this struct /// Dropping without doing this is a bad idea pub fn deactivate(self, device: &mut DeviceT) { diff --git a/stockton-render/src/draw/builders/pipeline.rs b/stockton-render/src/draw/builders/pipeline.rs index cdd9736..329fa7f 100644 --- a/stockton-render/src/draw/builders/pipeline.rs +++ b/stockton-render/src/draw/builders/pipeline.rs @@ -48,6 +48,7 @@ fn get_size(f: Format) -> u32 { Format::Rgb32Sfloat => 4 * 3, Format::R32Sint => 4, Format::Rg32Sfloat => 4 * 2, + Format::Rgba32Sfloat => 4 * 4, _ => unimplemented!("dont know size of format {:?}", f), } } diff --git a/stockton-render/src/draw/draw_passes/cons.rs b/stockton-render/src/draw/draw_passes/cons.rs index 542c26a..3209806 100644 --- a/stockton-render/src/draw/draw_passes/cons.rs +++ b/stockton-render/src/draw/draw_passes/cons.rs @@ -3,7 +3,7 @@ use std::sync::{Arc, RwLock}; -use super::DrawPass; +use super::{DrawPass, IntoDrawPass}; use crate::types::*; use stockton_types::Session; @@ -11,8 +11,8 @@ use anyhow::Result; /// One draw pass, then another. pub struct ConsDrawPass<A: DrawPass, B: DrawPass> { - a: A, - b: B, + pub a: A, + pub b: B, } impl<A: DrawPass, B: DrawPass> DrawPass for ConsDrawPass<A, B> { @@ -33,3 +33,42 @@ impl<A: DrawPass, B: DrawPass> DrawPass for ConsDrawPass<A, B> { self.b.deactivate(device) } } + +impl<A: DrawPass, B: DrawPass, IA: IntoDrawPass<A>, IB: IntoDrawPass<B>> + IntoDrawPass<ConsDrawPass<A, B>> for (IA, IB) +{ + fn init( + self, + session: &Session, + adapter: &Adapter, + device: Arc<RwLock<DeviceT>>, + queue_negotiator: &mut crate::draw::queue_negotiator::QueueNegotiator, + swapchain_properties: &crate::draw::target::SwapchainProperties, + ) -> Result<ConsDrawPass<A, B>> { + Ok(ConsDrawPass { + a: self.0.init( + session, + adapter, + device.clone(), + queue_negotiator, + swapchain_properties, + )?, + b: self.1.init( + session, + adapter, + device, + queue_negotiator, + swapchain_properties, + )?, + }) + } + + fn find_aux_queues<'a>( + adapter: &'a Adapter, + queue_negotiator: &mut crate::draw::queue_negotiator::QueueNegotiator, + ) -> Result<Vec<(&'a QueueFamilyT, Vec<f32>)>> { + let mut v = IA::find_aux_queues(adapter, queue_negotiator)?; + v.extend(IB::find_aux_queues(adapter, queue_negotiator)?); + Ok(v) + } +} diff --git a/stockton-render/src/draw/draw_passes/mod.rs b/stockton-render/src/draw/draw_passes/mod.rs index 1c99614..b18d50e 100644 --- a/stockton-render/src/draw/draw_passes/mod.rs +++ b/stockton-render/src/draw/draw_passes/mod.rs @@ -9,10 +9,12 @@ use anyhow::Result; mod cons; mod level; +mod ui; pub mod util; pub use cons::ConsDrawPass; pub use level::LevelDrawPass; +pub use ui::UiDrawPass; /// One of several 'passes' that draw on each frame. pub trait DrawPass { diff --git a/stockton-render/src/draw/draw_passes/ui.rs b/stockton-render/src/draw/draw_passes/ui.rs new file mode 100644 index 0000000..82ca4b1 --- /dev/null +++ b/stockton-render/src/draw/draw_passes/ui.rs @@ -0,0 +1,347 @@ +//! Minimal code for drawing any level, based on traits from stockton-levels + +use super::{util::TargetSpecificResources, DrawPass, IntoDrawPass}; +use crate::{ + draw::{ + buffers::{draw_buffers::DrawBuffers, ModifiableBuffer}, + builders::{ + pipeline::{ + CompletePipeline, PipelineSpecBuilder, VertexBufferSpec, + VertexPrimitiveAssemblerSpec, + }, + renderpass::RenderpassSpec, + shader::ShaderDesc, + }, + queue_negotiator::QueueNegotiator, + target::SwapchainProperties, + texture::{TexLoadQueue, TextureLoadConfig, TextureRepo}, + ui::UiTextures, + }, + error::{EnvironmentError, LockPoisoned}, + types::*, + UiState, +}; +use egui::{ClippedMesh, TextureId}; +use hal::{ + buffer::SubRange, + command::{ClearColor, ClearValue, RenderAttachmentInfo, SubpassContents}, + format::Format, + image::Layout, + pass::{Attachment, AttachmentLoadOp, AttachmentOps, AttachmentStoreOp}, + pso::{ + BlendDesc, BlendOp, BlendState, ColorBlendDesc, ColorMask, DepthStencilDesc, Face, Factor, + FrontFace, InputAssemblerDesc, LogicOp, PolygonMode, Primitive, Rasterizer, Rect, + ShaderStageFlags, State, VertexInputRate, + }, +}; +use shaderc::ShaderKind; +use stockton_types::{Session, Vector2}; + +use std::{ + array::IntoIter, + convert::TryInto, + iter::{empty, once}, + sync::{Arc, RwLock}, +}; + +use anyhow::{anyhow, Context, Result}; + +#[derive(Debug)] +pub struct UiPoint(pub Vector2, pub Vector2, pub [f32; 4]); + +/// Draw a Ui object +pub struct UiDrawPass<'a> { + pipeline: CompletePipeline, + repo: TextureRepo, + draw_buffers: DrawBuffers<'a, UiPoint>, + + framebuffers: TargetSpecificResources<FramebufferT>, +} + +impl<'a> DrawPass for UiDrawPass<'a> { + fn queue_draw( + &mut self, + session: &Session, + img_view: &ImageViewT, + cmd_buffer: &mut crate::types::CommandBufferT, + ) -> anyhow::Result<()> { + // We might have loaded more textures + self.repo.process_responses(); + + // Make sure we update the vertex buffers after they're written to, but before they're read from. + self.draw_buffers + .vertex_buffer + .record_commit_cmds(cmd_buffer)?; + self.draw_buffers + .index_buffer + .record_commit_cmds(cmd_buffer)?; + + // Get level & camera + let ui: &mut UiState = &mut session.resources.get_mut::<UiState>().unwrap(); + + // Get framebuffer and depth buffer + let fb = self.framebuffers.get_next(); + unsafe { + cmd_buffer.begin_render_pass( + &self.pipeline.renderpass, + fb, + self.pipeline.render_area, + vec![RenderAttachmentInfo { + image_view: img_view, + clear_value: ClearValue { + color: ClearColor { + float32: [0.0, 0.0, 0.0, 1.0], + }, + }, + }] + .into_iter(), + SubpassContents::Inline, + ); + cmd_buffer.bind_graphics_pipeline(&self.pipeline.pipeline); + + // Bind buffers + cmd_buffer.bind_vertex_buffers( + 0, + once(( + self.draw_buffers.vertex_buffer.get_buffer(), + SubRange { + offset: 0, + size: None, + }, + )), + ); + cmd_buffer.bind_index_buffer( + self.draw_buffers.index_buffer.get_buffer(), + SubRange { + offset: 0, + size: None, + }, + hal::IndexType::U16, + ); + } + + 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) { + self.draw_buffers.index_buffer[i / 3] = ( + tris.indices[i].try_into()?, + tris.indices[i + 1].try_into()?, + tris.indices[i + 2].try_into()?, + ); + } + for (i, vertex) in tris.vertices.iter().enumerate() { + self.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) = self.repo.attempt_get_descriptor_set(0) { + unsafe { + cmd_buffer.push_graphics_constants( + &self.pipeline.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( + &self.pipeline.pipeline_layout, + 0, + IntoIter::new([ds]), + empty(), + ); + // Call draw + cmd_buffer.draw_indexed(0..tris.indices.len() as u32, 0, 0..1); + } + } else { + self.repo.queue_load(0)?; + } + } + + unsafe { + cmd_buffer.end_render_pass(); + } + + Ok(()) + } + + fn deactivate(self, device_lock: &mut Arc<RwLock<DeviceT>>) -> Result<()> { + unsafe { + let mut device = device_lock.write().map_err(|_| LockPoisoned::Device)?; + self.pipeline.deactivate(&mut device); + self.draw_buffers.deactivate(&mut device); + for fb in self.framebuffers.dissolve() { + device.destroy_framebuffer(fb); + } + } + self.repo.deactivate(device_lock); + + Ok(()) + } +} + +impl<'a> IntoDrawPass<UiDrawPass<'a>> for () { + fn init( + self, + session: &Session, + adapter: &Adapter, + device_lock: Arc<RwLock<DeviceT>>, + queue_negotiator: &mut QueueNegotiator, + swapchain_properties: &SwapchainProperties, + ) -> Result<UiDrawPass<'a>> { + let spec = PipelineSpecBuilder::default() + .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(DepthStencilDesc { + depth: None, + depth_bounds: false, + stencil: None, + }) + .blender(BlendDesc { + logic_op: Some(LogicOp::Copy), + targets: vec![ColorBlendDesc { + mask: ColorMask::ALL, + blend: Some(BlendState { + color: BlendOp::Add { + src: Factor::SrcAlpha, + dst: Factor::OneMinusSrcAlpha, + }, + alpha: BlendOp::Add { + src: Factor::SrcAlpha, + dst: Factor::OneMinusSrcAlpha, + }, + }), + }], + }) + .primitive_assembler(VertexPrimitiveAssemblerSpec::with_buffers( + InputAssemblerDesc::new(Primitive::TriangleList), + vec![VertexBufferSpec { + attributes: vec![Format::Rg32Sfloat, Format::Rg32Sfloat, Format::Rgba32Sfloat], + rate: VertexInputRate::Vertex, + }], + )) + .shader_vertex(ShaderDesc { + source: include_str!("../ui/data/stockton.vert").to_string(), + entry: "main".to_string(), + kind: ShaderKind::Vertex, + }) + .shader_fragment(ShaderDesc { + source: include_str!("../ui/data/stockton.frag").to_string(), + entry: "main".to_string(), + kind: ShaderKind::Fragment, + }) + .push_constants(vec![(ShaderStageFlags::VERTEX, 0..8)]) + .renderpass(RenderpassSpec { + colors: vec![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::ColorAttachmentOptimal, + }], + depth: None, + inputs: vec![], + resolves: vec![], + preserves: vec![], + }) + .build() + .context("Error building pipeline")?; + + let ui: &mut UiState = &mut session.resources.get_mut::<UiState>().unwrap(); + let repo = TextureRepo::new( + device_lock.clone(), + queue_negotiator + .family::<TexLoadQueue>() + .ok_or(EnvironmentError::NoSuitableFamilies) + .context("Error finding texture queue")?, + queue_negotiator + .get_queue::<TexLoadQueue>() + .ok_or(EnvironmentError::NoQueues) + .context("Error finding texture queue")?, + adapter, + TextureLoadConfig { + resolver: UiTextures::new(ui.ctx().clone()), + filter: hal::image::Filter::Linear, + wrap_mode: hal::image::WrapMode::Clamp, + }, + ) + .context("Error creating texture repo")?; + + let (draw_buffers, pipeline, framebuffers) = { + let mut device = device_lock.write().map_err(|_| LockPoisoned::Device)?; + let draw_buffers = + DrawBuffers::new(&mut device, adapter).context("Error creating draw buffers")?; + let pipeline = spec + .build( + &mut device, + swapchain_properties.extent, + swapchain_properties, + once(&*repo.get_ds_layout()?), + ) + .context("Error building pipeline")?; + + let fat = swapchain_properties.framebuffer_attachment(); + let framebuffers = TargetSpecificResources::new( + || unsafe { + Ok(device.create_framebuffer( + &pipeline.renderpass, + IntoIter::new([fat.clone()]), + swapchain_properties.extent, + )?) + }, + swapchain_properties.image_count as usize, + )?; + (draw_buffers, pipeline, framebuffers) + }; + + Ok(UiDrawPass { + pipeline, + repo, + draw_buffers, + framebuffers, + }) + } + + fn find_aux_queues<'c>( + adapter: &'c Adapter, + queue_negotiator: &mut QueueNegotiator, + ) -> Result<Vec<(&'c QueueFamilyT, Vec<f32>)>> { + queue_negotiator.find(adapter, &TexLoadQueue)?; + + Ok(vec![queue_negotiator + .family_spec::<TexLoadQueue>(&adapter.queue_families, 1) + .ok_or(EnvironmentError::NoSuitableFamilies)?]) + } +} diff --git a/stockton-render/src/draw/draw_passes/util.rs b/stockton-render/src/draw/draw_passes/util.rs index a42f870..5a4eb1a 100644 --- a/stockton-render/src/draw/draw_passes/util.rs +++ b/stockton-render/src/draw/draw_passes/util.rs @@ -11,7 +11,7 @@ pub struct TargetSpecificResources<T> { impl<T> TargetSpecificResources<T> { /// Create a new set of resources, given a function to generate them and the count /// In most cases, count should be swapchain_properties.image_count - pub fn new<F>(generator: F, count: usize) -> Result<Self> + pub fn new<F>(mut generator: F, count: usize) -> Result<Self> where F: FnMut() -> Result<T>, { 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(()) -} diff --git a/stockton-render/src/window.rs b/stockton-render/src/window.rs index 62e43d3..87f3182 100644 --- a/stockton-render/src/window.rs +++ b/stockton-render/src/window.rs @@ -3,7 +3,6 @@ use egui::{Modifiers, Rect, Vec2}; use legion::systems::Runnable; use log::debug; - use egui::{CtxRef, Event, Output, Pos2, RawInput}; use epaint::ClippedShape; use log::error; @@ -73,8 +72,6 @@ impl WindowEvent { pub struct UiState { ctx: CtxRef, raw_input: RawInput, - pub(crate) last_tex_ver: u64, - frame_active: bool, modifiers: Modifiers, @@ -86,7 +83,6 @@ impl UiState { UiState { ctx: CtxRef::default(), raw_input: RawInput::default(), - last_tex_ver: 0, frame_active: false, modifiers: Default::default(), pointer_pos: Pos2::new(0.0, 0.0), |