aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortcmal <me@aria.rip>2024-08-25 17:44:23 +0100
committertcmal <me@aria.rip>2024-08-25 17:44:23 +0100
commit6eb9083b1f2fec63e17dc8ef462ea89bae0854b5 (patch)
tree814f670c38040c37408db06b66c9cb24550393e9
parent6ef351de4ba506e7f0f285569aa0e22255bb68c6 (diff)
feat(render): ui draw pass
-rw-r--r--examples/render-quad/src/main.rs16
-rw-r--r--stockton-render/src/draw/buffers/dedicated_image.rs199
-rw-r--r--stockton-render/src/draw/builders/pipeline.rs1
-rw-r--r--stockton-render/src/draw/draw_passes/cons.rs45
-rw-r--r--stockton-render/src/draw/draw_passes/mod.rs2
-rw-r--r--stockton-render/src/draw/draw_passes/ui.rs347
-rw-r--r--stockton-render/src/draw/draw_passes/util.rs2
-rw-r--r--stockton-render/src/draw/ui/mod.rs59
-rw-r--r--stockton-render/src/draw/ui/pipeline.rs294
-rw-r--r--stockton-render/src/draw/ui/render.rs97
-rwxr-xr-xstockton-render/src/draw/ui/texture.rs67
-rw-r--r--stockton-render/src/window.rs4
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),