diff options
-rw-r--r-- | examples/render-quad/src/main.rs | 1 | ||||
-rw-r--r-- | stockton-render/src/level.rs | 293 | ||||
-rw-r--r-- | stockton-render/src/ui.rs | 205 | ||||
-rw-r--r-- | stockton-skeleton/src/draw_passes/cons.rs | 22 | ||||
-rw-r--r-- | stockton-skeleton/src/draw_passes/mod.rs | 8 | ||||
-rw-r--r-- | stockton-skeleton/src/lib.rs | 14 |
6 files changed, 320 insertions, 223 deletions
diff --git a/examples/render-quad/src/main.rs b/examples/render-quad/src/main.rs index 45c469a..0b7f3be 100644 --- a/examples/render-quad/src/main.rs +++ b/examples/render-quad/src/main.rs @@ -238,7 +238,6 @@ fn try_main() -> Result<()> { if let Some(we) = WindowEvent::from(&event) { tx.send(we).unwrap(); - println!("{:?}", we); if let WindowEvent::SizeChanged(_, _) = we { let r = renderer.take().unwrap(); match r.recreate_surface(&session) { diff --git a/stockton-render/src/level.rs b/stockton-render/src/level.rs index 29ab7c5..b1f7b7c 100644 --- a/stockton-render/src/level.rs +++ b/stockton-render/src/level.rs @@ -10,8 +10,8 @@ use stockton_skeleton::{ image::{BoundImageView, ImageSpec, DEPTH_RESOURCES}, }, builders::{ - AttachmentSpec, CompletePipeline, PipelineSpecBuilder, RenderpassSpec, ShaderDesc, - VertexBufferSpec, VertexPrimitiveAssemblerSpec, + AttachmentSpec, CompletePipeline, PipelineSpec, PipelineSpecBuilder, RenderpassSpec, + ShaderDesc, VertexBufferSpec, VertexPrimitiveAssemblerSpec, }, context::RenderingContext, draw_passes::{util::TargetSpecificResources, DrawPass, IntoDrawPass, PassPosition}, @@ -286,11 +286,41 @@ where } fn handle_surface_change( - &mut self, + mut self, _session: &Session, - _context: &mut RenderingContext, - ) -> Result<()> { - todo!() + context: &mut RenderingContext, + ) -> Result<Self> { + // TODO: Handle deactivation if any of this fails. + + // Recreate depth buffers + for db in self.depth_buffers.dissolve() { + db.deactivate_with_context(context); + } + self.depth_buffers = create_depth_buffers(context)?; + + { + let mut device = context.lock_device()?; + + // Recreate pipeline + self.pipeline.deactivate(&mut device); + self.pipeline = create_pipeline_spec::<P>(context)? + .build( + &mut device, + context.properties().extent, + once(&*self.repo.get_ds_layout()?), + ) + .context("Error building pipeline")?; + + // Recreate framebuffers + for fb in self.framebuffers.dissolve() { + unsafe { + device.destroy_framebuffer(fb); + } + } + self.framebuffers = create_framebuffers(&mut device, &self.pipeline, context)?; + } + + Ok(self) } } impl<'a, M> LevelDrawPass<'a, M> { @@ -335,93 +365,7 @@ where _session: &mut Session, context: &mut RenderingContext, ) -> Result<LevelDrawPass<'a, M>> { - let spec = PipelineSpecBuilder::default() - .rasterizer(Rasterizer { - polygon_mode: PolygonMode::Fill, - cull_face: Face::BACK, - front_face: FrontFace::CounterClockwise, - depth_clamping: false, - depth_bias: None, - conservative: true, - line_width: State::Static(1.0), - }) - .depth_stencil(DepthStencilDesc { - depth: Some(DepthTest { - fun: Comparison::Less, - write: true, - }), - 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::Rgb32Sfloat, Format::R32Sint, Format::Rg32Sfloat], - rate: VertexInputRate::Vertex, - }], - )) - .shader_vertex(ShaderDesc { - source: include_str!("./data/3d.vert").to_string(), - entry: "main".to_string(), - kind: ShaderKind::Vertex, - }) - .shader_fragment(ShaderDesc { - source: include_str!("./data/3d.frag").to_string(), - entry: "main".to_string(), - kind: ShaderKind::Fragment, - }) - .push_constants(vec![(ShaderStageFlags::VERTEX, 0..64)]) - .renderpass(RenderpassSpec { - colors: vec![AttachmentSpec { - attachment: Attachment { - format: Some(context.properties().color_format), - samples: 1, - ops: P::attachment_ops(), - stencil_ops: P::attachment_ops(), - layouts: P::layout_as_range(), - }, - - used_layout: Layout::ColorAttachmentOptimal, - }], - depth: Some(AttachmentSpec { - attachment: Attachment { - format: Some(context.properties().depth_format), - samples: 1, - ops: AttachmentOps::new( - AttachmentLoadOp::Clear, - AttachmentStoreOp::DontCare, - ), - stencil_ops: AttachmentOps::new( - AttachmentLoadOp::DontCare, - AttachmentStoreOp::DontCare, - ), - layouts: Layout::Undefined..Layout::DepthStencilAttachmentOptimal, - }, - used_layout: Layout::DepthStencilAttachmentOptimal, - }), - inputs: vec![], - resolves: vec![], - preserves: vec![], - }) - .build() - .context("Error building pipeline")?; - + let spec = create_pipeline_spec::<P>(context)?; let repo = TextureRepo::new::<_, TexLoadQueue>( context, TextureLoadConfig { @@ -430,7 +374,7 @@ where wrap_mode: WrapMode::Tile, }, ) - .context("Error createing texture repo")?; + .context("Error creating texture repo")?; let draw_buffers = DrawBuffers::from_context(context).context("Error creating draw buffers")?; @@ -444,40 +388,11 @@ where ) .context("Error building pipeline")?; - let fat = context.properties().swapchain_framebuffer_attachment(); - let dat = FramebufferAttachment { - usage: Usage::DEPTH_STENCIL_ATTACHMENT, - format: context.properties().depth_format, - view_caps: ViewCapabilities::empty(), - }; - let framebuffers = TargetSpecificResources::new( - || unsafe { - Ok(device.create_framebuffer( - &pipeline.renderpass, - IntoIter::new([fat.clone(), dat.clone()]), - context.properties().extent, - )?) - }, - context.properties().image_count as usize, - )?; + let framebuffers = create_framebuffers(&mut device, &pipeline, context)?; (pipeline, framebuffers) }; - let db_spec = ImageSpec { - width: context.properties().extent.width, - height: context.properties().extent.height, - format: context.properties().depth_format, - usage: Usage::DEPTH_STENCIL_ATTACHMENT, - resources: DEPTH_RESOURCES, - }; - let img_count = context.properties().image_count; - let depth_buffers = TargetSpecificResources::new( - || { - BoundImageView::from_context(context, &db_spec) - .context("Error creating depth buffer") - }, - img_count as usize, - )?; + let depth_buffers = create_depth_buffers(context)?; Ok(LevelDrawPass { pipeline, @@ -500,6 +415,30 @@ where } } +fn create_framebuffers( + device: &mut DeviceT, + pipeline: &CompletePipeline, + context: &RenderingContext, +) -> Result<TargetSpecificResources<FramebufferT>> { + let fat = context.properties().swapchain_framebuffer_attachment(); + let dat = FramebufferAttachment { + usage: Usage::DEPTH_STENCIL_ATTACHMENT, + format: context.properties().depth_format, + view_caps: ViewCapabilities::empty(), + }; + + TargetSpecificResources::new( + || unsafe { + Ok(device.create_framebuffer( + &pipeline.renderpass, + IntoIter::new([fat.clone(), dat.clone()]), + context.properties().extent, + )?) + }, + context.properties().image_count as usize, + ) +} + /// Indicates an issue with the level object being used #[derive(Debug, Error)] pub enum LevelError { @@ -518,3 +457,107 @@ fn euler_to_direction(euler: &Vector3) -> Vector3 { yaw.cos() * pitch.cos(), ) } + +fn create_pipeline_spec<P: PassPosition>(context: &RenderingContext) -> Result<PipelineSpec> { + Ok(PipelineSpecBuilder::default() + .rasterizer(Rasterizer { + polygon_mode: PolygonMode::Fill, + cull_face: Face::BACK, + front_face: FrontFace::CounterClockwise, + depth_clamping: false, + depth_bias: None, + conservative: true, + line_width: State::Static(1.0), + }) + .depth_stencil(DepthStencilDesc { + depth: Some(DepthTest { + fun: Comparison::Less, + write: true, + }), + 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::Rgb32Sfloat, Format::R32Sint, Format::Rg32Sfloat], + rate: VertexInputRate::Vertex, + }], + )) + .shader_vertex(ShaderDesc { + source: include_str!("./data/3d.vert").to_string(), + entry: "main".to_string(), + kind: ShaderKind::Vertex, + }) + .shader_fragment(ShaderDesc { + source: include_str!("./data/3d.frag").to_string(), + entry: "main".to_string(), + kind: ShaderKind::Fragment, + }) + .push_constants(vec![(ShaderStageFlags::VERTEX, 0..64)]) + .renderpass(RenderpassSpec { + colors: vec![AttachmentSpec { + attachment: Attachment { + format: Some(context.properties().color_format), + samples: 1, + ops: P::attachment_ops(), + stencil_ops: P::attachment_ops(), + layouts: P::layout_as_range(), + }, + + used_layout: Layout::ColorAttachmentOptimal, + }], + depth: Some(AttachmentSpec { + attachment: Attachment { + format: Some(context.properties().depth_format), + samples: 1, + ops: AttachmentOps::new(AttachmentLoadOp::Clear, AttachmentStoreOp::DontCare), + stencil_ops: AttachmentOps::new( + AttachmentLoadOp::DontCare, + AttachmentStoreOp::DontCare, + ), + layouts: Layout::Undefined..Layout::DepthStencilAttachmentOptimal, + }, + used_layout: Layout::DepthStencilAttachmentOptimal, + }), + inputs: vec![], + resolves: vec![], + preserves: vec![], + }) + .build() + .context("Error building pipeline")?) +} + +fn create_depth_buffers( + context: &mut RenderingContext, +) -> Result<TargetSpecificResources<BoundImageView<DepthBufferPool>>> { + let db_spec = ImageSpec { + width: context.properties().extent.width, + height: context.properties().extent.height, + format: context.properties().depth_format, + usage: Usage::DEPTH_STENCIL_ATTACHMENT, + resources: DEPTH_RESOURCES, + }; + let img_count = context.properties().image_count; + + TargetSpecificResources::new( + || BoundImageView::from_context(context, &db_spec).context("Error creating depth buffer"), + img_count as usize, + ) +} diff --git a/stockton-render/src/ui.rs b/stockton-render/src/ui.rs index 474c1dd..4149d71 100644 --- a/stockton-render/src/ui.rs +++ b/stockton-render/src/ui.rs @@ -4,8 +4,8 @@ use crate::window::UiState; use stockton_skeleton::{ buffers::draw::DrawBuffers, builders::{ - AttachmentSpec, CompletePipeline, PipelineSpecBuilder, RenderpassSpec, ShaderDesc, - VertexBufferSpec, VertexPrimitiveAssemblerSpec, + AttachmentSpec, CompletePipeline, PipelineSpec, PipelineSpecBuilder, RenderpassSpec, + ShaderDesc, VertexBufferSpec, VertexPrimitiveAssemblerSpec, }, context::RenderingContext, draw_passes::{util::TargetSpecificResources, DrawPass, IntoDrawPass, PassPosition}, @@ -212,84 +212,39 @@ impl<'a, P: PassPosition> DrawPass<P> for UiDrawPass<'a> { } fn handle_surface_change( - &mut self, + mut self, _session: &Session, - _context: &mut RenderingContext, - ) -> Result<()> { - todo!() + context: &mut RenderingContext, + ) -> Result<Self> { + { + let mut device = context.lock_device()?; + + // Recreate pipeline + self.pipeline.deactivate(&mut device); + self.pipeline = create_pipeline_spec::<P>(context)? + .build( + &mut device, + context.properties().extent, + once(&*self.repo.get_ds_layout()?), + ) + .context("Error building pipeline")?; + + // Recreate framebuffers + for fb in self.framebuffers.dissolve() { + unsafe { + device.destroy_framebuffer(fb); + } + } + self.framebuffers = create_framebuffers(&mut device, context, &self.pipeline)?; + } + + Ok(self) } } impl<'a, P: PassPosition> IntoDrawPass<UiDrawPass<'a>, P> for () { fn init(self, session: &mut Session, context: &mut RenderingContext) -> 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!("./data/ui.vert").to_string(), - entry: "main".to_string(), - kind: ShaderKind::Vertex, - }) - .shader_fragment(ShaderDesc { - source: include_str!("./data/ui.frag").to_string(), - entry: "main".to_string(), - kind: ShaderKind::Fragment, - }) - .push_constants(vec![(ShaderStageFlags::VERTEX, 0..8)]) - .renderpass(RenderpassSpec { - colors: vec![AttachmentSpec { - attachment: Attachment { - format: Some(context.properties().color_format), - samples: 1, - ops: P::attachment_ops(), - stencil_ops: P::attachment_ops(), - layouts: P::layout_as_range(), - }, - used_layout: Layout::ColorAttachmentOptimal, - }], - depth: None, - inputs: vec![], - resolves: vec![], - preserves: vec![], - }) - .dynamic_scissor(true) - .build() - .context("Error building pipeline")?; + let spec = create_pipeline_spec::<P>(context)?; let ui: &mut UiState = &mut session.resources.get_mut::<UiState>().unwrap(); let repo = TextureRepo::new::<_, TexLoadQueue>( @@ -316,17 +271,7 @@ impl<'a, P: PassPosition> IntoDrawPass<UiDrawPass<'a>, P> for () { ) .context("Error building pipeline")?; - let fat = context.properties().swapchain_framebuffer_attachment(); - let framebuffers = TargetSpecificResources::new( - || unsafe { - Ok(device.create_framebuffer( - &pipeline.renderpass, - IntoIter::new([fat.clone()]), - context.properties().extent, - )?) - }, - context.properties().image_count as usize, - )?; + let framebuffers = create_framebuffers(&mut device, context, &pipeline)?; (pipeline, framebuffers) }; @@ -348,6 +293,96 @@ impl<'a, P: PassPosition> IntoDrawPass<UiDrawPass<'a>, P> for () { } } +fn create_framebuffers( + device: &mut DeviceT, + context: &RenderingContext, + pipeline: &CompletePipeline, +) -> Result<TargetSpecificResources<FramebufferT>> { + let fat = context.properties().swapchain_framebuffer_attachment(); + + TargetSpecificResources::new( + || unsafe { + Ok(device.create_framebuffer( + &pipeline.renderpass, + IntoIter::new([fat.clone()]), + context.properties().extent, + )?) + }, + context.properties().image_count as usize, + ) +} + +fn create_pipeline_spec<P: PassPosition>(context: &RenderingContext) -> Result<PipelineSpec> { + 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!("./data/ui.vert").to_string(), + entry: "main".to_string(), + kind: ShaderKind::Vertex, + }) + .shader_fragment(ShaderDesc { + source: include_str!("./data/ui.frag").to_string(), + entry: "main".to_string(), + kind: ShaderKind::Fragment, + }) + .push_constants(vec![(ShaderStageFlags::VERTEX, 0..8)]) + .renderpass(RenderpassSpec { + colors: vec![AttachmentSpec { + attachment: Attachment { + format: Some(context.properties().color_format), + samples: 1, + ops: P::attachment_ops(), + stencil_ops: P::attachment_ops(), + layouts: P::layout_as_range(), + }, + used_layout: Layout::ColorAttachmentOptimal, + }], + depth: None, + inputs: vec![], + resolves: vec![], + preserves: vec![], + }) + .dynamic_scissor(true) + .build() + .context("Error building pipeline") +} + pub struct UiTexture(Arc<Texture>); pub struct UiTextures { diff --git a/stockton-skeleton/src/draw_passes/cons.rs b/stockton-skeleton/src/draw_passes/cons.rs index 51c06a6..dea42af 100644 --- a/stockton-skeleton/src/draw_passes/cons.rs +++ b/stockton-skeleton/src/draw_passes/cons.rs @@ -32,12 +32,26 @@ macro_rules! cons_shared_impl { } fn handle_surface_change( - &mut self, + mut self, session: &Session, context: &mut RenderingContext, - ) -> Result<()> { - self.a.handle_surface_change(session, context)?; - self.b.handle_surface_change(session, context) + ) -> Result<Self> { + match self.a.handle_surface_change(session, context) { + Ok(a) => self.a = a, + Err(e) => { + self.b.deactivate(context)?; + return Err(e); + } + } + match self.b.handle_surface_change(session, context) { + Ok(b) => self.b = b, + Err(e) => { + self.a.deactivate(context)?; + return Err(e); + } + } + + Ok(self) } }; } diff --git a/stockton-skeleton/src/draw_passes/mod.rs b/stockton-skeleton/src/draw_passes/mod.rs index cdc983f..5b138c2 100644 --- a/stockton-skeleton/src/draw_passes/mod.rs +++ b/stockton-skeleton/src/draw_passes/mod.rs @@ -27,11 +27,15 @@ pub trait DrawPass<P: PassPosition> { ) -> Result<()>; /// Called just after the surface changes (probably a resize). + /// This takes ownership and returns itself to ensure that the `DrawPass` is not called again if it fails. + /// This means you should deactivate as much as possible in case of an error. fn handle_surface_change( - &mut self, + self, session: &Session, context: &mut RenderingContext, - ) -> Result<()>; + ) -> Result<Self> + where + Self: Sized; /// Deactivate any vulkan parts that need to be deactivated fn deactivate(self, context: &mut RenderingContext) -> Result<()>; diff --git a/stockton-skeleton/src/lib.rs b/stockton-skeleton/src/lib.rs index 6260753..2b6fe70 100644 --- a/stockton-skeleton/src/lib.rs +++ b/stockton-skeleton/src/lib.rs @@ -35,7 +35,7 @@ pub struct Renderer<DP> { context: ManuallyDrop<RenderingContext>, /// The draw pass we're using - draw_pass: DP, + draw_pass: ManuallyDrop<DP>, } impl<DP: DrawPass<Singular>> Renderer<DP> { @@ -54,7 +54,7 @@ impl<DP: DrawPass<Singular>> Renderer<DP> { Ok(Renderer { context: ManuallyDrop::new(context), - draw_pass, + draw_pass: ManuallyDrop::new(draw_pass), }) } @@ -65,7 +65,7 @@ impl<DP: DrawPass<Singular>> Renderer<DP> { // Hence, we can always take from the ManuallyDrop unsafe { match ManuallyDrop::take(&mut self.context) - .draw_next_frame(session, &mut self.draw_pass) + .draw_next_frame(session, &mut *self.draw_pass) { Ok(c) => { self.context = ManuallyDrop::new(c); @@ -74,7 +74,7 @@ impl<DP: DrawPass<Singular>> Renderer<DP> { Err((_e, c)) => { // TODO: Try to detect if the error is actually surface related. let c = c.attempt_recovery()?; - match c.draw_next_frame(session, &mut self.draw_pass) { + match c.draw_next_frame(session, &mut *self.draw_pass) { Ok(c) => { self.context = ManuallyDrop::new(c); Ok(self) @@ -94,9 +94,11 @@ impl<DP: DrawPass<Singular>> Renderer<DP> { unsafe { let ctx = ManuallyDrop::take(&mut self.context).recreate_surface()?; self.context = ManuallyDrop::new(ctx); + + let dp = ManuallyDrop::take(&mut self.draw_pass) + .handle_surface_change(session, &mut self.context)?; + self.draw_pass = ManuallyDrop::new(dp); } - self.draw_pass - .handle_surface_change(session, &mut self.context)?; Ok(self) } |