aboutsummaryrefslogtreecommitdiff
path: root/stockton-render/src/draw/draw_passes/ui.rs
diff options
context:
space:
mode:
Diffstat (limited to 'stockton-render/src/draw/draw_passes/ui.rs')
-rw-r--r--stockton-render/src/draw/draw_passes/ui.rs347
1 files changed, 347 insertions, 0 deletions
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)?])
+ }
+}