From e2e0881854e7dd795b031a5cce87a53d572b8ff9 Mon Sep 17 00:00:00 2001 From: ErikWDev Date: Sun, 1 Dec 2024 10:08:48 +0900 Subject: [PATCH 01/13] add sample_count to texture + msaa state to render pip desc, make compile --- blade-egui/src/lib.rs | 2 ++ blade-graphics/src/lib.rs | 20 ++++++++++++++++++++ blade-render/src/render/debug.rs | 2 ++ blade-render/src/render/dummy.rs | 3 +++ blade-render/src/render/env_map.rs | 1 + blade-render/src/render/mod.rs | 2 ++ blade-render/src/texture/mod.rs | 1 + examples/bunnymark/main.rs | 2 ++ examples/init/main.rs | 3 +++ examples/mini/main.rs | 1 + examples/particle/particle.rs | 1 + examples/ray-query/main.rs | 2 ++ 12 files changed, 40 insertions(+) diff --git a/blade-egui/src/lib.rs b/blade-egui/src/lib.rs index 7af3d5b..0f8b092 100644 --- a/blade-egui/src/lib.rs +++ b/blade-egui/src/lib.rs @@ -71,6 +71,7 @@ impl GuiTexture { mip_level_count: 1, dimension: blade_graphics::TextureDimension::D2, usage: blade_graphics::TextureUsage::COPY | blade_graphics::TextureUsage::RESOURCE, + sample_count: 1, }); let view = context.create_texture_view( allocation, @@ -161,6 +162,7 @@ impl GuiPainter { }), write_mask: blade_graphics::ColorWrites::all(), }], + multisample_state: blade_graphics::MultisampleState::default(), }); let belt = BufferBelt::new(BufferBeltDescriptor { diff --git a/blade-graphics/src/lib.rs b/blade-graphics/src/lib.rs index b85baf6..40b449a 100644 --- a/blade-graphics/src/lib.rs +++ b/blade-graphics/src/lib.rs @@ -402,6 +402,7 @@ pub struct TextureDesc<'a> { pub size: Extent, pub array_layer_count: u32, pub mip_level_count: u32, + pub sample_count: u32, pub dimension: TextureDimension, pub usage: TextureUsage, } @@ -1018,12 +1019,31 @@ pub struct RenderPipelineDesc<'a> { pub depth_stencil: Option, pub fragment: ShaderFunction<'a>, pub color_targets: &'a [ColorTargetState], + pub multisample_state: MultisampleState, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub struct MultisampleState { + pub sample_count: u32, + pub sample_mask: u64, + pub alpha_to_coverage: bool, +} + +impl Default for MultisampleState { + fn default() -> Self { + Self { + sample_count: 1, + sample_mask: !0, + alpha_to_coverage: false, + } + } } #[derive(Clone, Copy, Debug)] pub enum InitOp { Load, Clear(TextureColor), + DontCare, } #[derive(Clone, Copy, Debug)] diff --git a/blade-render/src/render/debug.rs b/blade-render/src/render/debug.rs index 06da080..027158c 100644 --- a/blade-render/src/render/debug.rs +++ b/blade-render/src/render/debug.rs @@ -95,6 +95,7 @@ fn create_draw_pipeline( blend: Some(blade_graphics::BlendState::ALPHA_BLENDING), write_mask: blade_graphics::ColorWrites::all(), }], + multisample_state: blade_graphics::MultisampleState::default(), }) } @@ -117,6 +118,7 @@ fn create_blit_pipeline( depth_stencil: None, fragment: shader.at("blit_fs"), color_targets: &[format.into()], + multisample_state: blade_graphics::MultisampleState::default(), }) } diff --git a/blade-render/src/render/dummy.rs b/blade-render/src/render/dummy.rs index f4c9fb4..7e6e75c 100644 --- a/blade-render/src/render/dummy.rs +++ b/blade-render/src/render/dummy.rs @@ -29,6 +29,7 @@ impl DummyResources { mip_level_count: 1, dimension: blade_graphics::TextureDimension::D2, usage: blade_graphics::TextureUsage::COPY | blade_graphics::TextureUsage::RESOURCE, + sample_count: 1, }); let white_view = gpu.create_texture_view( white_texture, @@ -47,6 +48,7 @@ impl DummyResources { mip_level_count: 1, dimension: blade_graphics::TextureDimension::D2, usage: blade_graphics::TextureUsage::COPY | blade_graphics::TextureUsage::RESOURCE, + sample_count: 1, }); let black_view = gpu.create_texture_view( black_texture, @@ -65,6 +67,7 @@ impl DummyResources { mip_level_count: 1, dimension: blade_graphics::TextureDimension::D2, usage: blade_graphics::TextureUsage::COPY | blade_graphics::TextureUsage::RESOURCE, + sample_count: 1, }); let red_view = gpu.create_texture_view( red_texture, diff --git a/blade-render/src/render/env_map.rs b/blade-render/src/render/env_map.rs index 979dfae..a87bf42 100644 --- a/blade-render/src/render/env_map.rs +++ b/blade-render/src/render/env_map.rs @@ -112,6 +112,7 @@ impl EnvironmentMap { array_layer_count: 1, mip_level_count, usage: blade_graphics::TextureUsage::RESOURCE | blade_graphics::TextureUsage::STORAGE, + sample_count: 1, }); self.weight_view = gpu.create_texture_view( self.weight_texture, diff --git a/blade-render/src/render/mod.rs b/blade-render/src/render/mod.rs index 43f92da..569c1cf 100644 --- a/blade-render/src/render/mod.rs +++ b/blade-render/src/render/mod.rs @@ -178,6 +178,7 @@ impl RenderTarget { array_layer_count: N as u32, mip_level_count: 1, usage: blade_graphics::TextureUsage::RESOURCE | blade_graphics::TextureUsage::STORAGE, + sample_count: 1, }); encoder.init_texture(texture); @@ -605,6 +606,7 @@ impl ShaderPipelines { fragment: shader.at("postfx_fs"), color_targets: &[info.format.into()], depth_stencil: None, + multisample_state: blade_graphics::MultisampleState::default(), }) } diff --git a/blade-render/src/texture/mod.rs b/blade-render/src/texture/mod.rs index 6e5192c..4a5b052 100644 --- a/blade-render/src/texture/mod.rs +++ b/blade-render/src/texture/mod.rs @@ -396,6 +396,7 @@ impl blade_asset::Baker for Baker { mip_level_count: image.mips.len() as u32, dimension: blade_graphics::TextureDimension::D2, usage: blade_graphics::TextureUsage::COPY | blade_graphics::TextureUsage::RESOURCE, + sample_count: 1, }); let view = self.gpu_context.create_texture_view( texture, diff --git a/examples/bunnymark/main.rs b/examples/bunnymark/main.rs index b9e8d32..e027c7e 100644 --- a/examples/bunnymark/main.rs +++ b/examples/bunnymark/main.rs @@ -123,6 +123,7 @@ impl Example { blend: Some(gpu::BlendState::ALPHA_BLENDING), write_mask: gpu::ColorWrites::default(), }], + multisample_state: gpu::MultisampleState::default(), }); let extent = gpu::Extent { @@ -138,6 +139,7 @@ impl Example { array_layer_count: 1, mip_level_count: 1, usage: gpu::TextureUsage::RESOURCE | gpu::TextureUsage::COPY, + sample_count: 1, }); let view = context.create_texture_view( texture, diff --git a/examples/init/main.rs b/examples/init/main.rs index a192139..5ffabc2 100644 --- a/examples/init/main.rs +++ b/examples/init/main.rs @@ -29,6 +29,7 @@ impl EnvMapSampler { mip_level_count: 1, dimension: gpu::TextureDimension::D2, usage: gpu::TextureUsage::TARGET, + sample_count: 1, }); let accum_view = context.create_texture_view( accum_texture, @@ -57,6 +58,7 @@ impl EnvMapSampler { blend: None, write_mask: gpu::ColorWrites::ALL, }], + multisample_state: gpu::MultisampleState::default(), }); let accum_pipeline = context.create_render_pipeline(gpu::RenderPipelineDesc { name: "env-accum", @@ -74,6 +76,7 @@ impl EnvMapSampler { blend: Some(gpu::BlendState::ADDITIVE), write_mask: gpu::ColorWrites::RED, }], + multisample_state: gpu::MultisampleState::default(), }); Self { diff --git a/examples/mini/main.rs b/examples/mini/main.rs index 223df8d..11299ae 100644 --- a/examples/mini/main.rs +++ b/examples/mini/main.rs @@ -59,6 +59,7 @@ fn main() { array_layer_count: 1, mip_level_count, usage: gpu::TextureUsage::RESOURCE | gpu::TextureUsage::STORAGE | gpu::TextureUsage::COPY, + sample_count: 1, }); let views = (0..mip_level_count) .map(|i| { diff --git a/examples/particle/particle.rs b/examples/particle/particle.rs index 7314514..2541d8d 100644 --- a/examples/particle/particle.rs +++ b/examples/particle/particle.rs @@ -112,6 +112,7 @@ impl System { write_mask: gpu::ColorWrites::default(), }], depth_stencil: None, + multisample_state: gpu::MultisampleState::default(), }); let wg_width = reset_pipeline.get_workgroup_size()[0] as usize; diff --git a/examples/ray-query/main.rs b/examples/ray-query/main.rs index 3fab8f2..b5c1039 100644 --- a/examples/ray-query/main.rs +++ b/examples/ray-query/main.rs @@ -82,6 +82,7 @@ impl Example { dimension: gpu::TextureDimension::D2, array_layer_count: 1, mip_level_count: 1, + sample_count: 1, usage: gpu::TextureUsage::RESOURCE | gpu::TextureUsage::STORAGE, }); let target_view = context.create_texture_view( @@ -115,6 +116,7 @@ impl Example { fragment: shader.at("draw_fs"), color_targets: &[surface.info().format.into()], depth_stencil: None, + multisample_state: Default::default(), }); let (indices, vertex_values) = From e0028d719c39683c3b2ea75e7bfb4d0b98effcbc Mon Sep 17 00:00:00 2001 From: ErikWDev Date: Mon, 2 Dec 2024 22:43:16 +0900 Subject: [PATCH 02/13] Implement msaa API + particle example use msaa --- Cargo.toml | 2 +- blade-egui/src/lib.rs | 11 ++- blade-graphics/src/gles/command.rs | 63 ++++++++++++ blade-graphics/src/gles/mod.rs | 4 + blade-graphics/src/gles/resource.rs | 59 +++++++++--- blade-graphics/src/metal/pipeline.rs | 2 + blade-graphics/src/metal/resource.rs | 7 +- blade-graphics/src/vulkan/command.rs | 24 +++++ blade-graphics/src/vulkan/pipeline.rs | 11 ++- blade-graphics/src/vulkan/resource.rs | 8 +- examples/particle/main.rs | 133 ++++++++++++++++++++++---- examples/particle/particle.rs | 19 ++-- examples/scene/main.rs | 2 +- src/lib.rs | 2 +- 14 files changed, 301 insertions(+), 46 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 061ebe6..272414a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,7 @@ profiling = "1" slab = "0.4" strum = { version = "0.25", features = ["derive"] } web-sys = "0.3.60" -winit = "0.30" +winit = { version = "0.30" } [lib] diff --git a/blade-egui/src/lib.rs b/blade-egui/src/lib.rs index 0f8b092..ac2abbe 100644 --- a/blade-egui/src/lib.rs +++ b/blade-egui/src/lib.rs @@ -129,7 +129,11 @@ impl GuiPainter { /// It supports renderpasses with only a color attachment, /// and this attachment format must be The `output_format`. #[profiling::function] - pub fn new(info: blade_graphics::SurfaceInfo, context: &blade_graphics::Context) -> Self { + pub fn new( + info: blade_graphics::SurfaceInfo, + context: &blade_graphics::Context, + sample_count: u32, + ) -> Self { let shader = context.create_shader(blade_graphics::ShaderDesc { source: SHADER_SOURCE, }); @@ -162,7 +166,10 @@ impl GuiPainter { }), write_mask: blade_graphics::ColorWrites::all(), }], - multisample_state: blade_graphics::MultisampleState::default(), + multisample_state: blade_graphics::MultisampleState { + sample_count, + ..Default::default() + }, }); let belt = BufferBelt::new(BufferBeltDescriptor { diff --git a/blade-graphics/src/gles/command.rs b/blade-graphics/src/gles/command.rs index 03001e6..d55dcce 100644 --- a/blade-graphics/src/gles/command.rs +++ b/blade-graphics/src/gles/command.rs @@ -185,6 +185,10 @@ impl super::CommandEncoder { if let crate::FinishOp::Discard = rt.finish_op { invalidate_attachments.push(attachment); } + if let crate::FinishOp::ResolveTo(to) = rt.finish_op { + self.commands + .push(super::Command::BlitFramebuffer { from: rt.view, to }); + } } if let Some(ref rt) = targets.depth_stencil { let attachment = match rt.view.aspects { @@ -806,6 +810,65 @@ impl super::Command { None, ); } + + Self::BlitFramebuffer { from, to } => { + /* + TODO(ErikWDev): Validate + */ + + let target_from = match from.inner { + super::TextureInner::Renderbuffer { raw } => raw, + _ => panic!("Unsupported resolve between non-renderbuffers"), + }; + let target_to = match to.inner { + super::TextureInner::Renderbuffer { raw } => raw, + _ => panic!("Unsupported resolve between non-renderbuffers"), + }; + + let framebuf_from = gl.create_framebuffer().unwrap(); + let framebuf_to = gl.create_framebuffer().unwrap(); + + gl.bind_framebuffer(glow::READ_FRAMEBUFFER, Some(framebuf_from)); + gl.framebuffer_renderbuffer( + glow::READ_FRAMEBUFFER, + glow::COLOR_ATTACHMENT0, // NOTE: Assuming color attachment + glow::RENDERBUFFER, + Some(target_from), + ); + + gl.bind_framebuffer(glow::DRAW_FRAMEBUFFER, Some(framebuf_to)); + gl.framebuffer_renderbuffer( + glow::DRAW_FRAMEBUFFER, + glow::COLOR_ATTACHMENT0, // NOTE: Assuming color attachment + glow::RENDERBUFFER, + Some(target_to), + ); + assert_eq!( + gl.check_framebuffer_status(glow::DRAW_FRAMEBUFFER), + glow::FRAMEBUFFER_COMPLETE, + "DRAW_FRAMEBUFFER is not complete" + ); + + gl.blit_framebuffer( + 0, + 0, + from.target_size[0] as _, + from.target_size[1] as _, + 0, + 0, + to.target_size[0] as _, + to.target_size[1] as _, + glow::COLOR_BUFFER_BIT, // NOTE: Assuming color + glow::NEAREST, + ); + + gl.bind_framebuffer(glow::READ_FRAMEBUFFER, None); + gl.bind_framebuffer(glow::DRAW_FRAMEBUFFER, None); + + gl.delete_framebuffer(framebuf_from); + gl.delete_framebuffer(framebuf_to); + } + Self::BindAttachment { attachment, ref view, diff --git a/blade-graphics/src/gles/mod.rs b/blade-graphics/src/gles/mod.rs index a56c9b2..3ea9c3a 100644 --- a/blade-graphics/src/gles/mod.rs +++ b/blade-graphics/src/gles/mod.rs @@ -274,6 +274,10 @@ enum Command { size: crate::Extent, }, ResetFramebuffer, + BlitFramebuffer { + from: TextureView, + to: TextureView, + }, BindAttachment { attachment: u32, view: TextureView, diff --git a/blade-graphics/src/gles/resource.rs b/blade-graphics/src/gles/resource.rs index 426a775..275a839 100644 --- a/blade-graphics/src/gles/resource.rs +++ b/blade-graphics/src/gles/resource.rs @@ -128,12 +128,24 @@ impl crate::traits::ResourceDevice for super::Context { let raw = unsafe { gl.create_renderbuffer().unwrap() }; unsafe { gl.bind_renderbuffer(glow::RENDERBUFFER, Some(raw)); - gl.renderbuffer_storage( - glow::RENDERBUFFER, - format_desc.internal, - desc.size.width as i32, - desc.size.height as i32, - ); + + if desc.sample_count <= 1 { + gl.renderbuffer_storage( + glow::RENDERBUFFER, + format_desc.internal, + desc.size.width as i32, + desc.size.height as i32, + ); + } else { + gl.renderbuffer_storage_multisample( + glow::RENDERBUFFER, + desc.sample_count as i32, + format_desc.internal, + desc.size.width as i32, + desc.size.height as i32, + ); + } + gl.bind_renderbuffer(glow::RENDERBUFFER, None); #[cfg(not(target_arch = "wasm32"))] if !desc.name.is_empty() && gl.supports_debug() { @@ -144,6 +156,7 @@ impl crate::traits::ResourceDevice for super::Context { ); } } + super::TextureInner::Renderbuffer { raw } } else { let raw = unsafe { gl.create_texture().unwrap() }; @@ -159,7 +172,11 @@ impl crate::traits::ResourceDevice for super::Context { if desc.array_layer_count > 1 { glow::TEXTURE_2D_ARRAY } else { - glow::TEXTURE_2D + if desc.sample_count <= 1 { + glow::TEXTURE_2D + } else { + glow::TEXTURE_2D_MULTISAMPLE + } } } crate::TextureDimension::D3 => glow::TEXTURE_3D, @@ -184,13 +201,27 @@ impl crate::traits::ResourceDevice for super::Context { ); } crate::TextureDimension::D2 => { - gl.tex_storage_2d( - target, - desc.mip_level_count as i32, - format_desc.internal, - desc.size.width as i32, - desc.size.height as i32, - ); + if desc.sample_count <= 1 { + gl.tex_storage_2d( + target, + desc.mip_level_count as i32, + format_desc.internal, + desc.size.width as i32, + desc.size.height as i32, + ); + } else { + /* + TODO(ErikWDev): How to set mip count and sample count? Not possible in gles? + */ + gl.tex_storage_2d_multisample( + target, + desc.sample_count as i32, + format_desc.internal, + desc.size.width as i32, + desc.size.height as i32, + true, + ); + } } crate::TextureDimension::D1 => { gl.tex_storage_1d( diff --git a/blade-graphics/src/metal/pipeline.rs b/blade-graphics/src/metal/pipeline.rs index 904412a..643cebd 100644 --- a/blade-graphics/src/metal/pipeline.rs +++ b/blade-graphics/src/metal/pipeline.rs @@ -420,6 +420,8 @@ impl crate::traits::ShaderDevice for super::Context { }, ); descriptor.set_vertex_function(Some(&vs.function)); + descriptor.set_raster_sample_count(desc.multisample_state.sample_count as _); + descriptor.set_alpha_to_coverage_enabled(desc.multisample_state.alpha_to_coverage); // Fragment shader let fs = self.load_shader( diff --git a/blade-graphics/src/metal/resource.rs b/blade-graphics/src/metal/resource.rs index 2b725b0..ee234c1 100644 --- a/blade-graphics/src/metal/resource.rs +++ b/blade-graphics/src/metal/resource.rs @@ -181,7 +181,11 @@ impl crate::traits::ResourceDevice for super::Context { if desc.array_layer_count > 1 { metal::MTLTextureType::D2Array } else { - metal::MTLTextureType::D2 + if desc.sample_count <= 1 { + metal::MTLTextureType::D2 + } else { + metal::MTLTextureType::D2Multisample + } } } crate::TextureDimension::D3 => metal::MTLTextureType::D3, @@ -198,6 +202,7 @@ impl crate::traits::ResourceDevice for super::Context { descriptor.set_array_length(desc.array_layer_count as u64); descriptor.set_mipmap_level_count(desc.mip_level_count as u64); descriptor.set_pixel_format(mtl_format); + descriptor.set_sample_count(desc.sample_count as _); descriptor.set_usage(mtl_usage); descriptor.set_storage_mode(metal::MTLStorageMode::Private); diff --git a/blade-graphics/src/vulkan/command.rs b/blade-graphics/src/vulkan/command.rs index b778686..c74b75e 100644 --- a/blade-graphics/src/vulkan/command.rs +++ b/blade-graphics/src/vulkan/command.rs @@ -190,10 +190,34 @@ fn map_render_target(rt: &crate::RenderTarget) -> vk::RenderingAttachmentInfo<'s }, } }; + vk_info.load_op = vk::AttachmentLoadOp::CLEAR; vk_info.clear_value = cv; } + if let crate::FinishOp::ResolveTo(resolve_view) = rt.finish_op { + vk_info = vk_info + .resolve_image_view(resolve_view.raw) + .resolve_image_layout(vk::ImageLayout::GENERAL) + .resolve_mode(vk::ResolveModeFlags::AVERAGE); + } + + vk_info.store_op = match rt.finish_op { + crate::FinishOp::Store => vk::AttachmentStoreOp::STORE, + crate::FinishOp::Discard => vk::AttachmentStoreOp::DONT_CARE, + crate::FinishOp::Ignore => vk::AttachmentStoreOp::DONT_CARE, + crate::FinishOp::ResolveTo(..) => { + /* + TODO: DONT_CARE is most optimal in many cases where the msaa texture itself is never read afterwards but only the resolved, + but how can the user specify this in blade? + https://docs.vulkan.org/samples/latest/samples/performance/msaa/README.html#_best_practice_summary + */ + + // vk::AttachmentStoreOp::STORE + vk::AttachmentStoreOp::DONT_CARE + } + }; + vk_info } diff --git a/blade-graphics/src/vulkan/pipeline.rs b/blade-graphics/src/vulkan/pipeline.rs index 8182aa8..fa07096 100644 --- a/blade-graphics/src/vulkan/pipeline.rs +++ b/blade-graphics/src/vulkan/pipeline.rs @@ -503,9 +503,16 @@ impl crate::traits::ShaderDevice for super::Context { .scissor_count(1) .viewport_count(1); - let vk_sample_mask = [1u32, 0]; + let vk_sample_mask = [ + desc.multisample_state.sample_mask as u32, + (desc.multisample_state.sample_mask >> 32) as u32, + ]; + let vk_multisample = vk::PipelineMultisampleStateCreateInfo::default() - .rasterization_samples(vk::SampleCountFlags::TYPE_1) + .rasterization_samples(vk::SampleCountFlags::from_raw( + desc.multisample_state.sample_count, + )) + .alpha_to_coverage_enable(desc.multisample_state.alpha_to_coverage) .sample_mask(&vk_sample_mask); let mut d_format = vk::Format::UNDEFINED; diff --git a/blade-graphics/src/vulkan/resource.rs b/blade-graphics/src/vulkan/resource.rs index 089219b..02b08b7 100644 --- a/blade-graphics/src/vulkan/resource.rs +++ b/blade-graphics/src/vulkan/resource.rs @@ -252,7 +252,7 @@ impl crate::traits::ResourceDevice for super::Context { let mut create_flags = vk::ImageCreateFlags::empty(); if desc.dimension == crate::TextureDimension::D2 && desc.size.depth % 6 == 0 - //&& desc.sample_count == 1 + && desc.sample_count == 1 && desc.size.width == desc.size.height { create_flags |= vk::ImageCreateFlags::CUBE_COMPATIBLE; @@ -265,13 +265,17 @@ impl crate::traits::ResourceDevice for super::Context { extent: super::map_extent_3d(&desc.size), mip_levels: desc.mip_level_count, array_layers: desc.array_layer_count, - samples: vk::SampleCountFlags::from_raw(1), // desc.sample_count + samples: vk::SampleCountFlags::from_raw(desc.sample_count), tiling: vk::ImageTiling::OPTIMAL, usage: map_texture_usage(desc.usage, desc.format.aspects()), sharing_mode: vk::SharingMode::EXCLUSIVE, ..Default::default() }; + /* + TODO(ErikWDev): Support lazily allocated texture with transient allocation for efficient msaa? + Measure bandwidth usage! + */ let raw = unsafe { self.device.core.create_image(&vk_info, None).unwrap() }; let requirements = unsafe { self.device.core.get_image_memory_requirements(raw) }; let allocation = self.allocate_memory(requirements, crate::Memory::Device); diff --git a/examples/particle/main.rs b/examples/particle/main.rs index e6a24c7..9175f05 100644 --- a/examples/particle/main.rs +++ b/examples/particle/main.rs @@ -4,6 +4,11 @@ use blade_graphics as gpu; mod particle; +// const SAMPLE_COUNT: u32 = 1; +// const SAMPLE_COUNT: u32 = 2; +const SAMPLE_COUNT: u32 = 4; +// const SAMPLE_COUNT: u32 = 8; + struct Example { command_encoder: gpu::CommandEncoder, prev_sync_point: Option, @@ -11,9 +16,71 @@ struct Example { surface: gpu::Surface, gui_painter: blade_egui::GuiPainter, particle_system: particle::System, + + msaa_texture: gpu::Texture, + msaa_view: gpu::TextureView, } impl Example { + fn resize(&mut self, size: winit::dpi::PhysicalSize) { + let config = Self::make_surface_config(size); + self.context.reconfigure_surface(&mut self.surface, config); + + let surface_info = self.surface.info(); + + if let Some(sp) = self.prev_sync_point.take() { + self.context.wait_for(&sp, !0); + } + self.context.destroy_texture_view(self.msaa_view); + self.context.destroy_texture(self.msaa_texture); + + // self.gui_painter.destroy(&self.context); + // self.gui_painter = blade_egui::GuiPainter::new(surface_info, &self.context, SAMPLE_COUNT); + + self.msaa_texture = self.context.create_texture(gpu::TextureDesc { + name: "msaa texture", + format: surface_info.format, + size: gpu::Extent { + width: size.width, + height: size.height, + depth: 1, + }, + sample_count: SAMPLE_COUNT, + dimension: gpu::TextureDimension::D2, + usage: gpu::TextureUsage::TARGET, + array_layer_count: 1, + mip_level_count: 1, + }); + self.msaa_view = self.context.create_texture_view( + self.msaa_texture, + gpu::TextureViewDesc { + name: "msaa texture view", + format: surface_info.format, + dimension: gpu::ViewDimension::D2, + subresources: &gpu::TextureSubresources { + base_mip_level: 0, + mip_level_count: None, + base_array_layer: 0, + array_layer_count: None, + }, + }, + ); + } + + fn make_surface_config(size: winit::dpi::PhysicalSize) -> gpu::SurfaceConfig { + log::info!("Window size: {:?}", size); + gpu::SurfaceConfig { + size: gpu::Extent { + width: size.width, + height: size.height, + depth: 1, + }, + usage: gpu::TextureUsage::TARGET, + display_sync: gpu::DisplaySync::Block, + ..Default::default() + } + } + fn new(window: &winit::window::Window) -> Self { let window_size = window.inner_size(); let context = unsafe { @@ -21,27 +88,18 @@ impl Example { presentation: true, validation: cfg!(debug_assertions), timing: true, - capture: true, + capture: false, + ..Default::default() }) .unwrap() }; - let surface_config = gpu::SurfaceConfig { - size: gpu::Extent { - width: window_size.width, - height: window_size.height, - depth: 1, - }, - usage: gpu::TextureUsage::TARGET, - display_sync: gpu::DisplaySync::Block, - ..Default::default() - }; let surface = context - .create_surface_configured(window, surface_config) + .create_surface_configured(window, Self::make_surface_config(window_size)) .unwrap(); let surface_info = surface.info(); - let gui_painter = blade_egui::GuiPainter::new(surface_info, &context); + let gui_painter = blade_egui::GuiPainter::new(surface_info, &context, SAMPLE_COUNT); let particle_system = particle::System::new( &context, particle::SystemDesc { @@ -49,6 +107,7 @@ impl Example { capacity: 100_000, draw_format: surface_info.format, }, + SAMPLE_COUNT, ); let mut command_encoder = context.create_command_encoder(gpu::CommandEncoderDesc { @@ -59,6 +118,35 @@ impl Example { particle_system.reset(&mut command_encoder); let sync_point = context.submit(&mut command_encoder); + let msaa_texture = context.create_texture(gpu::TextureDesc { + name: "msaa texture", + format: surface_info.format, + size: gpu::Extent { + width: window_size.width, + height: window_size.height, + depth: 1, + }, + sample_count: SAMPLE_COUNT, + dimension: gpu::TextureDimension::D2, + usage: gpu::TextureUsage::TARGET, + array_layer_count: 1, + mip_level_count: 1, + }); + let msaa_view = context.create_texture_view( + msaa_texture, + gpu::TextureViewDesc { + name: "msaa texture view", + format: surface_info.format, + dimension: gpu::ViewDimension::D2, + subresources: &gpu::TextureSubresources { + base_mip_level: 0, + mip_level_count: None, + base_array_layer: 0, + array_layer_count: None, + }, + }, + ); + Self { command_encoder, prev_sync_point: Some(sync_point), @@ -66,6 +154,8 @@ impl Example { surface, gui_painter, particle_system, + msaa_texture, + msaa_view, } } @@ -78,6 +168,9 @@ impl Example { self.gui_painter.destroy(&self.context); self.particle_system.destroy(&self.context); self.context.destroy_surface(&mut self.surface); + + self.context.destroy_texture_view(self.msaa_view); + self.context.destroy_texture(self.msaa_texture); } fn render( @@ -88,6 +181,7 @@ impl Example { ) { let frame = self.surface.acquire_frame(); self.command_encoder.start(); + self.command_encoder.init_texture(self.msaa_texture); self.command_encoder.init_texture(frame.texture()); self.gui_painter @@ -99,14 +193,15 @@ impl Example { "draw", gpu::RenderTargetSet { colors: &[gpu::RenderTarget { - view: frame.texture_view(), + view: self.msaa_view, init_op: gpu::InitOp::Clear(gpu::TextureColor::OpaqueBlack), - finish_op: gpu::FinishOp::Store, + finish_op: gpu::FinishOp::ResolveTo(frame.texture_view()), }], depth_stencil: None, }, ) { - self.particle_system.draw(&mut pass); + self.particle_system + .draw(&mut pass, screen_desc.physical_size); self.gui_painter .paint(&mut pass, gui_primitives, screen_desc, &self.context); } @@ -156,6 +251,7 @@ fn main() { winit::event::Event::AboutToWait => { window.request_redraw(); } + winit::event::Event::WindowEvent { event, .. } => { let response = egui_winit.on_window_event(&window, &event); if response.consumed { @@ -183,6 +279,11 @@ fn main() { winit::event::WindowEvent::CloseRequested => { target.exit(); } + + winit::event::WindowEvent::Resized(size) => { + example.resize(size); + } + winit::event::WindowEvent::RedrawRequested => { let raw_input = egui_winit.take_egui_input(&window); let egui_output = egui_winit.egui_ctx().run(raw_input, |egui_ctx| { diff --git a/examples/particle/particle.rs b/examples/particle/particle.rs index 2541d8d..376950d 100644 --- a/examples/particle/particle.rs +++ b/examples/particle/particle.rs @@ -71,7 +71,7 @@ struct DrawData { } impl System { - pub fn new(context: &gpu::Context, desc: SystemDesc) -> Self { + pub fn new(context: &gpu::Context, desc: SystemDesc, sample_count: u32) -> Self { let source = std::fs::read_to_string("examples/particle/particle.wgsl").unwrap(); let shader = context.create_shader(gpu::ShaderDesc { source: &source }); let particle_size = shader.get_struct_size("Particle"); @@ -112,7 +112,10 @@ impl System { write_mask: gpu::ColorWrites::default(), }], depth_stencil: None, - multisample_state: gpu::MultisampleState::default(), + multisample_state: gpu::MultisampleState { + sample_count, + ..Default::default() + }, }); let wg_width = reset_pipeline.get_workgroup_size()[0] as usize; @@ -199,7 +202,7 @@ impl System { } } - pub fn draw(&self, pass: &mut gpu::RenderCommandEncoder) { + pub fn draw(&self, pass: &mut gpu::RenderCommandEncoder, size: (u32, u32)) { let mut pc = pass.with(&self.draw_pipeline); pc.bind( 0, @@ -212,7 +215,11 @@ impl System { scale: 1.0, }, screen_center: [0.0; 2], - screen_extent: [1000.0; 2], + screen_extent: { + let ratio = size.0 as f32 / size.1 as f32; + + [1000.0 * ratio, 1000.0] + }, }, }, ); @@ -220,8 +227,8 @@ impl System { } pub fn add_gui(&mut self, ui: &mut egui::Ui) { - ui.add(egui::Slider::new(&mut self.params.life, 0.1..=10.0).text("life")); + ui.add(egui::Slider::new(&mut self.params.life, 0.1..=20.0).text("life")); ui.add(egui::Slider::new(&mut self.params.velocity, 1.0..=500.0).text("velocity")); - ui.add(egui::Slider::new(&mut self.params.scale, 0.1..=50.0).text("scale")); + ui.add(egui::Slider::new(&mut self.params.scale, 0.1..=70.0).text("scale")); } } diff --git a/examples/scene/main.rs b/examples/scene/main.rs index ddae495..e0134eb 100644 --- a/examples/scene/main.rs +++ b/examples/scene/main.rs @@ -228,7 +228,7 @@ impl Example { &render_config, ); pacer.end_frame(&context); - let gui_painter = blade_egui::GuiPainter::new(surface_info, &context); + let gui_painter = blade_egui::GuiPainter::new(surface_info, &context, 1); Self { scene_path: PathBuf::new(), diff --git a/src/lib.rs b/src/lib.rs index 2f754a4..7ca89ce 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -461,7 +461,7 @@ impl Engine { pacer.end_frame(&gpu_context); - let gui_painter = blade_egui::GuiPainter::new(surface_info, &gpu_context); + let gui_painter = blade_egui::GuiPainter::new(surface_info, &gpu_context, 1); let mut physics = Physics::default(); physics.debug_pipeline.mode = rapier3d::pipeline::DebugRenderMode::empty(); physics.integration_params.dt = config.time_step; From 34d7be61275dc9c58efcaee1f5ce00bde213a353 Mon Sep 17 00:00:00 2001 From: "Erik W. Gren" Date: Thu, 5 Dec 2024 10:45:09 +0100 Subject: [PATCH 03/13] no sample_count in egui --- Cargo.toml | 1 + blade-egui/src/lib.rs | 11 ++--------- examples/scene/main.rs | 2 +- src/lib.rs | 2 +- 4 files changed, 5 insertions(+), 11 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 272414a..f92be9b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,7 @@ slab = "0.4" strum = { version = "0.25", features = ["derive"] } web-sys = "0.3.60" winit = { version = "0.30" } +# winit = { version = "0.30", default-features=false, features=["x11", "rwh_06"] } [lib] diff --git a/blade-egui/src/lib.rs b/blade-egui/src/lib.rs index ac2abbe..cb8b500 100644 --- a/blade-egui/src/lib.rs +++ b/blade-egui/src/lib.rs @@ -129,11 +129,7 @@ impl GuiPainter { /// It supports renderpasses with only a color attachment, /// and this attachment format must be The `output_format`. #[profiling::function] - pub fn new( - info: blade_graphics::SurfaceInfo, - context: &blade_graphics::Context, - sample_count: u32, - ) -> Self { + pub fn new(info: blade_graphics::SurfaceInfo, context: &blade_graphics::Context) -> Self { let shader = context.create_shader(blade_graphics::ShaderDesc { source: SHADER_SOURCE, }); @@ -166,10 +162,7 @@ impl GuiPainter { }), write_mask: blade_graphics::ColorWrites::all(), }], - multisample_state: blade_graphics::MultisampleState { - sample_count, - ..Default::default() - }, + multisample_state: Default::default(), }); let belt = BufferBelt::new(BufferBeltDescriptor { diff --git a/examples/scene/main.rs b/examples/scene/main.rs index e0134eb..ddae495 100644 --- a/examples/scene/main.rs +++ b/examples/scene/main.rs @@ -228,7 +228,7 @@ impl Example { &render_config, ); pacer.end_frame(&context); - let gui_painter = blade_egui::GuiPainter::new(surface_info, &context, 1); + let gui_painter = blade_egui::GuiPainter::new(surface_info, &context); Self { scene_path: PathBuf::new(), diff --git a/src/lib.rs b/src/lib.rs index 7ca89ce..2f754a4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -461,7 +461,7 @@ impl Engine { pacer.end_frame(&gpu_context); - let gui_painter = blade_egui::GuiPainter::new(surface_info, &gpu_context, 1); + let gui_painter = blade_egui::GuiPainter::new(surface_info, &gpu_context); let mut physics = Physics::default(); physics.debug_pipeline.mode = rapier3d::pipeline::DebugRenderMode::empty(); physics.integration_params.dt = config.time_step; From ecf5d19f472e6cbd068d0e2b92032346ba49a5d8 Mon Sep 17 00:00:00 2001 From: "Erik W. Gren" Date: Thu, 5 Dec 2024 10:45:23 +0100 Subject: [PATCH 04/13] initial support for changing the msaa dynamically --- examples/particle/main.rs | 243 ++++++++++++++++++++++++-------------- 1 file changed, 153 insertions(+), 90 deletions(-) diff --git a/examples/particle/main.rs b/examples/particle/main.rs index 9175f05..7f10caf 100644 --- a/examples/particle/main.rs +++ b/examples/particle/main.rs @@ -4,23 +4,25 @@ use blade_graphics as gpu; mod particle; -// const SAMPLE_COUNT: u32 = 1; -// const SAMPLE_COUNT: u32 = 2; -const SAMPLE_COUNT: u32 = 4; -// const SAMPLE_COUNT: u32 = 8; - struct Example { command_encoder: gpu::CommandEncoder, prev_sync_point: Option, context: gpu::Context, surface: gpu::Surface, gui_painter: blade_egui::GuiPainter, + particle_system: particle::System, - msaa_texture: gpu::Texture, - msaa_view: gpu::TextureView, + sample_count: u32, + msaa_texture: Option, + msaa_view: Option, } +// pub const INITIAL_SAMPLE_COUNT: u32 = 1; +// pub const INITIAL_SAMPLE_COUNT: u32 = 2; +pub const INITIAL_SAMPLE_COUNT: u32 = 4; +// pub const INITIAL_SAMPLE_COUNT: u32 = 8; + impl Example { fn resize(&mut self, size: winit::dpi::PhysicalSize) { let config = Self::make_surface_config(size); @@ -31,40 +33,49 @@ impl Example { if let Some(sp) = self.prev_sync_point.take() { self.context.wait_for(&sp, !0); } - self.context.destroy_texture_view(self.msaa_view); - self.context.destroy_texture(self.msaa_texture); - // self.gui_painter.destroy(&self.context); - // self.gui_painter = blade_egui::GuiPainter::new(surface_info, &self.context, SAMPLE_COUNT); + if let Some(msaa_view) = self.msaa_view.take() { + self.context.destroy_texture_view(msaa_view); + } + if let Some(msaa_texture) = self.msaa_texture.take() { + self.context.destroy_texture(msaa_texture); + } + self.recreate_msaa_texutres_if_needed((size.width, size.height), surface_info.format); + } - self.msaa_texture = self.context.create_texture(gpu::TextureDesc { - name: "msaa texture", - format: surface_info.format, - size: gpu::Extent { - width: size.width, - height: size.height, - depth: 1, - }, - sample_count: SAMPLE_COUNT, - dimension: gpu::TextureDimension::D2, - usage: gpu::TextureUsage::TARGET, - array_layer_count: 1, - mip_level_count: 1, - }); - self.msaa_view = self.context.create_texture_view( - self.msaa_texture, - gpu::TextureViewDesc { - name: "msaa texture view", - format: surface_info.format, - dimension: gpu::ViewDimension::D2, - subresources: &gpu::TextureSubresources { - base_mip_level: 0, - mip_level_count: None, - base_array_layer: 0, - array_layer_count: None, + fn recreate_msaa_texutres_if_needed( + &mut self, + (width, height): (u32, u32), + format: gpu::TextureFormat, + ) { + if self.sample_count > 1 && self.msaa_texture.is_none() { + let msaa_texture = self.context.create_texture(gpu::TextureDesc { + name: "msaa texture", + format, + size: gpu::Extent { + width, + height, + depth: 1, }, - }, - ); + sample_count: self.sample_count, + dimension: gpu::TextureDimension::D2, + usage: gpu::TextureUsage::TARGET, + array_layer_count: 1, + mip_level_count: 1, + }); + let msaa_view = self.context.create_texture_view( + msaa_texture, + gpu::TextureViewDesc { + name: "msaa texture view", + format, + dimension: gpu::ViewDimension::D2, + subresources: &Default::default(), + }, + ); + + self.msaa_texture = Some(msaa_texture); + self.msaa_view = Some(msaa_view); + } } fn make_surface_config(size: winit::dpi::PhysicalSize) -> gpu::SurfaceConfig { @@ -99,7 +110,7 @@ impl Example { .unwrap(); let surface_info = surface.info(); - let gui_painter = blade_egui::GuiPainter::new(surface_info, &context, SAMPLE_COUNT); + let gui_painter = blade_egui::GuiPainter::new(surface_info, &context); let particle_system = particle::System::new( &context, particle::SystemDesc { @@ -107,7 +118,7 @@ impl Example { capacity: 100_000, draw_format: surface_info.format, }, - SAMPLE_COUNT, + INITIAL_SAMPLE_COUNT, ); let mut command_encoder = context.create_command_encoder(gpu::CommandEncoderDesc { @@ -118,35 +129,6 @@ impl Example { particle_system.reset(&mut command_encoder); let sync_point = context.submit(&mut command_encoder); - let msaa_texture = context.create_texture(gpu::TextureDesc { - name: "msaa texture", - format: surface_info.format, - size: gpu::Extent { - width: window_size.width, - height: window_size.height, - depth: 1, - }, - sample_count: SAMPLE_COUNT, - dimension: gpu::TextureDimension::D2, - usage: gpu::TextureUsage::TARGET, - array_layer_count: 1, - mip_level_count: 1, - }); - let msaa_view = context.create_texture_view( - msaa_texture, - gpu::TextureViewDesc { - name: "msaa texture view", - format: surface_info.format, - dimension: gpu::ViewDimension::D2, - subresources: &gpu::TextureSubresources { - base_mip_level: 0, - mip_level_count: None, - base_array_layer: 0, - array_layer_count: None, - }, - }, - ); - Self { command_encoder, prev_sync_point: Some(sync_point), @@ -154,8 +136,9 @@ impl Example { surface, gui_painter, particle_system, - msaa_texture, - msaa_view, + sample_count: INITIAL_SAMPLE_COUNT, + msaa_texture: None, + msaa_view: None, } } @@ -169,8 +152,12 @@ impl Example { self.particle_system.destroy(&self.context); self.context.destroy_surface(&mut self.surface); - self.context.destroy_texture_view(self.msaa_view); - self.context.destroy_texture(self.msaa_texture); + if let Some(msaa_view) = self.msaa_view.take() { + self.context.destroy_texture_view(msaa_view); + } + if let Some(msaa_texture) = self.msaa_texture.take() { + self.context.destroy_texture(msaa_texture); + } } fn render( @@ -179,32 +166,71 @@ impl Example { gui_textures: &egui::TexturesDelta, screen_desc: &blade_egui::ScreenDescriptor, ) { + self.recreate_msaa_texutres_if_needed( + screen_desc.physical_size, + self.surface.info().format, + ); + let frame = self.surface.acquire_frame(); + let frame_view = frame.texture_view(); self.command_encoder.start(); - self.command_encoder.init_texture(self.msaa_texture); + if let Some(msaa_texture) = self.msaa_texture { + self.command_encoder.init_texture(msaa_texture); + } self.command_encoder.init_texture(frame.texture()); self.gui_painter .update_textures(&mut self.command_encoder, gui_textures, &self.context); - self.particle_system.update(&mut self.command_encoder); - if let mut pass = self.command_encoder.render( - "draw", - gpu::RenderTargetSet { - colors: &[gpu::RenderTarget { - view: self.msaa_view, - init_op: gpu::InitOp::Clear(gpu::TextureColor::OpaqueBlack), - finish_op: gpu::FinishOp::ResolveTo(frame.texture_view()), - }], - depth_stencil: None, - }, - ) { - self.particle_system - .draw(&mut pass, screen_desc.physical_size); - self.gui_painter - .paint(&mut pass, gui_primitives, screen_desc, &self.context); + if self.sample_count <= 1 { + if let mut pass = self.command_encoder.render( + "draw particles and ui", + gpu::RenderTargetSet { + colors: &[gpu::RenderTarget { + view: frame_view, + init_op: gpu::InitOp::Clear(gpu::TextureColor::OpaqueBlack), + finish_op: gpu::FinishOp::Store, + }], + depth_stencil: None, + }, + ) { + self.particle_system + .draw(&mut pass, screen_desc.physical_size); + self.gui_painter + .paint(&mut pass, gui_primitives, screen_desc, &self.context); + } + } else { + if let mut pass = self.command_encoder.render( + "draw particles with msaa resolve", + gpu::RenderTargetSet { + colors: &[gpu::RenderTarget { + view: self.msaa_view.unwrap(), + init_op: gpu::InitOp::Clear(gpu::TextureColor::OpaqueBlack), + finish_op: gpu::FinishOp::ResolveTo(frame_view), + }], + depth_stencil: None, + }, + ) { + self.particle_system + .draw(&mut pass, screen_desc.physical_size); + } + if let mut pass = self.command_encoder.render( + "draw ui", + gpu::RenderTargetSet { + colors: &[gpu::RenderTarget { + view: frame_view, + init_op: gpu::InitOp::Load, + finish_op: gpu::FinishOp::Store, + }], + depth_stencil: None, + }, + ) { + self.gui_painter + .paint(&mut pass, gui_primitives, screen_desc, &self.context); + } } + self.command_encoder.present(frame); let sync_point = self.context.submit(&mut self.command_encoder); self.gui_painter.after_submit(&sync_point); @@ -216,8 +242,45 @@ impl Example { } fn add_gui(&mut self, ui: &mut egui::Ui) { + ui.add_space(5.0); ui.heading("Particle System"); self.particle_system.add_gui(ui); + + ui.add_space(5.0); + ui.heading("Rendering Settings"); + egui::ComboBox::new("msaa dropdown", "MSAA samples") + .selected_text(format!("x{}", self.sample_count)) + .show_ui(ui, |ui| { + for i in [1, 2, 4, 8] { + if ui + .selectable_value(&mut self.sample_count, i, format!("x{i}")) + .changed() + { + if let Some(sp) = self.prev_sync_point.take() { + self.context.wait_for(&sp, !0); + } + + self.particle_system.destroy(&self.context); + self.particle_system = particle::System::new( + &self.context, + particle::SystemDesc { + name: "particle system", + capacity: 100_000, + draw_format: gpu::TextureFormat::Bgra8Unorm, // TODO: get real surface format.. + }, + self.sample_count, + ); + if let Some(msaa_view) = self.msaa_view.take() { + self.context.destroy_texture_view(msaa_view); + } + if let Some(msaa_texture) = self.msaa_texture.take() { + self.context.destroy_texture(msaa_texture); + } + } + } + }); + + ui.add_space(5.0); ui.heading("Timings"); for (name, time) in self.command_encoder.timings() { let millis = time.as_secs_f32() * 1000.0; From 3a7b3516513aab28cded44c61f4ad4f70c60773a Mon Sep 17 00:00:00 2001 From: "Erik W. Gren" Date: Thu, 5 Dec 2024 10:45:46 +0100 Subject: [PATCH 05/13] support msaa 2d array textures --- blade-graphics/src/gles/resource.rs | 22 +++++++++++++++++----- blade-graphics/src/metal/resource.rs | 6 +++++- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/blade-graphics/src/gles/resource.rs b/blade-graphics/src/gles/resource.rs index 275a839..640d266 100644 --- a/blade-graphics/src/gles/resource.rs +++ b/blade-graphics/src/gles/resource.rs @@ -160,8 +160,13 @@ impl crate::traits::ResourceDevice for super::Context { super::TextureInner::Renderbuffer { raw } } else { let raw = unsafe { gl.create_texture().unwrap() }; + + let mut ignoring_sample_count = false; let target = match desc.dimension { crate::TextureDimension::D1 => { + if desc.sample_count > 1 { + log::warn!("Sample count is ignored: not supported for 1D textures",); + } if desc.array_layer_count > 1 { glow::TEXTURE_1D_ARRAY } else { @@ -170,7 +175,11 @@ impl crate::traits::ResourceDevice for super::Context { } crate::TextureDimension::D2 => { if desc.array_layer_count > 1 { - glow::TEXTURE_2D_ARRAY + if desc.sample_count <= 1 { + glow::TEXTURE_2D_ARRAY + } else { + glow::TEXTURE_2D_ARRAY_MULTISAMPLE + } } else { if desc.sample_count <= 1 { glow::TEXTURE_2D @@ -179,7 +188,12 @@ impl crate::traits::ResourceDevice for super::Context { } } } - crate::TextureDimension::D3 => glow::TEXTURE_3D, + crate::TextureDimension::D3 => { + if desc.sample_count > 1 { + log::warn!("Sample count is ignored: not supported for 3D textures",); + } + glow::TEXTURE_3D + } }; unsafe { @@ -210,9 +224,7 @@ impl crate::traits::ResourceDevice for super::Context { desc.size.height as i32, ); } else { - /* - TODO(ErikWDev): How to set mip count and sample count? Not possible in gles? - */ + assert_eq!(desc.mip_level_count, 1); gl.tex_storage_2d_multisample( target, desc.sample_count as i32, diff --git a/blade-graphics/src/metal/resource.rs b/blade-graphics/src/metal/resource.rs index ee234c1..2c08e12 100644 --- a/blade-graphics/src/metal/resource.rs +++ b/blade-graphics/src/metal/resource.rs @@ -179,7 +179,11 @@ impl crate::traits::ResourceDevice for super::Context { } crate::TextureDimension::D2 => { if desc.array_layer_count > 1 { - metal::MTLTextureType::D2Array + if desc.sample_count <= 1 { + metal::MTLTextureType::D2Array + } else { + metal::MTLTextureType::D2MultisampleArray + } } else { if desc.sample_count <= 1 { metal::MTLTextureType::D2 From f9c281edb8914bf131eb194dadd2fb7344640851 Mon Sep 17 00:00:00 2001 From: "Erik W. Gren" Date: Fri, 6 Dec 2024 12:58:12 +0100 Subject: [PATCH 06/13] make compile on metal + add missing sample_count dependency in texture view --- blade-graphics/src/lib.rs | 2 ++ blade-graphics/src/metal/command.rs | 2 ++ blade-graphics/src/metal/resource.rs | 20 ++++++++++++++++---- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/blade-graphics/src/lib.rs b/blade-graphics/src/lib.rs index 40b449a..795fc1c 100644 --- a/blade-graphics/src/lib.rs +++ b/blade-graphics/src/lib.rs @@ -1050,6 +1050,8 @@ pub enum InitOp { pub enum FinishOp { Store, Discard, + /// The resolved texture will be stored but it is undefined what + /// happens to the original render target ResolveTo(TextureView), Ignore, } diff --git a/blade-graphics/src/metal/command.rs b/blade-graphics/src/metal/command.rs index 4f148d6..ebd5dad 100644 --- a/blade-graphics/src/metal/command.rs +++ b/blade-graphics/src/metal/command.rs @@ -217,6 +217,7 @@ impl super::CommandEncoder { at_descriptor.set_clear_color(clear_color); metal::MTLLoadAction::Clear } + crate::InitOp::DontCare => metal::MTLLoadAction::DontCare, }; at_descriptor.set_load_action(load_action); @@ -247,6 +248,7 @@ impl super::CommandEncoder { at_descriptor.set_clear_depth(clear_depth); metal::MTLLoadAction::Clear } + crate::InitOp::DontCare => metal::MTLLoadAction::DontCare, }; let store_action = match rt.finish_op { crate::FinishOp::Store | crate::FinishOp::Ignore => { diff --git a/blade-graphics/src/metal/resource.rs b/blade-graphics/src/metal/resource.rs index 2c08e12..84e2a84 100644 --- a/blade-graphics/src/metal/resource.rs +++ b/blade-graphics/src/metal/resource.rs @@ -22,14 +22,26 @@ fn map_texture_usage(usage: crate::TextureUsage) -> metal::MTLTextureUsage { mtl_usage } -fn map_view_dimension(dimension: crate::ViewDimension) -> metal::MTLTextureType { +fn map_view_dimension(dimension: crate::ViewDimension, sample_count: u64) -> metal::MTLTextureType { use crate::ViewDimension as Vd; use metal::MTLTextureType::*; match dimension { Vd::D1 => D1, Vd::D1Array => D1Array, - Vd::D2 => D2, - Vd::D2Array => D2Array, + Vd::D2 => { + if sample_count <= 1 { + D2 + } else { + D2Multisample + } + } + Vd::D2Array => { + if sample_count <= 1 { + D2Array + } else { + D2MultisampleArray + } + } Vd::D3 => D3, Vd::Cube => Cube, Vd::CubeArray => CubeArray, @@ -234,7 +246,7 @@ impl crate::traits::ResourceDevice for super::Context { ) -> super::TextureView { let texture = texture.as_ref(); let mtl_format = super::map_texture_format(desc.format); - let mtl_type = map_view_dimension(desc.dimension); + let mtl_type = map_view_dimension(desc.dimension, texture.sample_count()); let mip_level_count = match desc.subresources.mip_level_count { Some(count) => count.get() as u64, None => texture.mipmap_level_count() - desc.subresources.base_mip_level as u64, From 58b57d77880fd36191271695d41c9aebff3e7954 Mon Sep 17 00:00:00 2001 From: "Erik W. Gren" Date: Fri, 6 Dec 2024 12:58:51 +0100 Subject: [PATCH 07/13] get real surface format when recreating particle system --- examples/particle/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/particle/main.rs b/examples/particle/main.rs index 7f10caf..28df139 100644 --- a/examples/particle/main.rs +++ b/examples/particle/main.rs @@ -251,7 +251,7 @@ impl Example { egui::ComboBox::new("msaa dropdown", "MSAA samples") .selected_text(format!("x{}", self.sample_count)) .show_ui(ui, |ui| { - for i in [1, 2, 4, 8] { + for i in [1, 2, 4] { if ui .selectable_value(&mut self.sample_count, i, format!("x{i}")) .changed() @@ -266,7 +266,7 @@ impl Example { particle::SystemDesc { name: "particle system", capacity: 100_000, - draw_format: gpu::TextureFormat::Bgra8Unorm, // TODO: get real surface format.. + draw_format: self.surface.info().format, }, self.sample_count, ); From c93df83be9109251d404ae169a133fdbeed3335d Mon Sep 17 00:00:00 2001 From: "Erik W. Gren" Date: Fri, 6 Dec 2024 13:04:10 +0100 Subject: [PATCH 08/13] use correct texture constant in gl --- blade-graphics/src/gles/resource.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/blade-graphics/src/gles/resource.rs b/blade-graphics/src/gles/resource.rs index 640d266..bad82b8 100644 --- a/blade-graphics/src/gles/resource.rs +++ b/blade-graphics/src/gles/resource.rs @@ -161,7 +161,6 @@ impl crate::traits::ResourceDevice for super::Context { } else { let raw = unsafe { gl.create_texture().unwrap() }; - let mut ignoring_sample_count = false; let target = match desc.dimension { crate::TextureDimension::D1 => { if desc.sample_count > 1 { @@ -178,7 +177,7 @@ impl crate::traits::ResourceDevice for super::Context { if desc.sample_count <= 1 { glow::TEXTURE_2D_ARRAY } else { - glow::TEXTURE_2D_ARRAY_MULTISAMPLE + glow::TEXTURE_2D_MULTISAMPLE_ARRAY } } else { if desc.sample_count <= 1 { From 4795021b45883fc1bf0eab3d9f2276f9e3d98048 Mon Sep 17 00:00:00 2001 From: "Erik W. Gren" Date: Fri, 6 Dec 2024 13:37:36 +0100 Subject: [PATCH 09/13] fix compiling egl gles on macos --- .gitignore | 3 +++ Cargo.toml | 2 ++ blade-graphics/src/gles/egl.rs | 3 ++- examples/particle/main.rs | 4 +++- 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 878b808..adac069 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,9 @@ /blade-asset/cooked /_failure.wgsl +libEGL.dylib +libGLESv2.dylib + .DS_Store /.vs /.vscode diff --git a/Cargo.toml b/Cargo.toml index f92be9b..562fdf7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -94,6 +94,8 @@ del-geo = "=0.1.29" egui-winit = "0.29" [target.'cfg(target_arch = "wasm32")'.dev-dependencies] +# see https://github.com/emilk/egui/issues/4270 +egui-winit = { version="0.29", default-features=false, features=["links"] } console_error_panic_hook = "0.1.7" console_log = "1" web-sys = { workspace = true, features = ["Window"] } diff --git a/blade-graphics/src/gles/egl.rs b/blade-graphics/src/gles/egl.rs index 9e9e5ce..70d6567 100644 --- a/blade-graphics/src/gles/egl.rs +++ b/blade-graphics/src/gles/egl.rs @@ -357,7 +357,8 @@ impl super::Context { let window_ptr = unsafe { use objc::{msg_send, runtime::Object, sel, sel_impl}; // ns_view always have a layer and don't need to verify that it exists. - let layer: *mut Object = msg_send![handle.ns_view.as_ptr(), layer]; + let layer: *mut Object = + msg_send![handle.ns_view.as_ptr() as *mut Object, layer]; layer as *mut ffi::c_void }; window_ptr diff --git a/examples/particle/main.rs b/examples/particle/main.rs index 28df139..67abbf9 100644 --- a/examples/particle/main.rs +++ b/examples/particle/main.rs @@ -371,7 +371,9 @@ fn main() { let control_flow = if let Some(repaint_after_instant) = std::time::Instant::now().checked_add(repaint_delay) { - winit::event_loop::ControlFlow::WaitUntil(repaint_after_instant) + winit::event_loop::ControlFlow::WaitUntil( + repaint_after_instant.into(), + ) } else { winit::event_loop::ControlFlow::Wait }; From 9d652a17fa2a03e21ffbf51a1a2e51af21b43154 Mon Sep 17 00:00:00 2001 From: "Erik W. Gren" Date: Sat, 7 Dec 2024 18:10:12 +0100 Subject: [PATCH 10/13] tmp --- Cargo.toml | 6 +++--- examples/particle/main.rs | 7 ++++++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 562fdf7..816adbd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,8 +28,8 @@ profiling = "1" slab = "0.4" strum = { version = "0.25", features = ["derive"] } web-sys = "0.3.60" -winit = { version = "0.30" } -# winit = { version = "0.30", default-features=false, features=["x11", "rwh_06"] } +# winit = { version = "0.30" } +winit = { version = "0.30", default-features=false, features=["x11", "rwh_06"] } [lib] @@ -91,7 +91,7 @@ del-geo = "=0.1.29" [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] # see https://github.com/emilk/egui/issues/4270 -egui-winit = "0.29" +egui-winit = { version="0.29", default-features=false, features=["links"] } [target.'cfg(target_arch = "wasm32")'.dev-dependencies] # see https://github.com/emilk/egui/issues/4270 diff --git a/examples/particle/main.rs b/examples/particle/main.rs index 67abbf9..d6a8659 100644 --- a/examples/particle/main.rs +++ b/examples/particle/main.rs @@ -242,7 +242,6 @@ impl Example { } fn add_gui(&mut self, ui: &mut egui::Ui) { - ui.add_space(5.0); ui.heading("Particle System"); self.particle_system.add_gui(ui); @@ -260,6 +259,7 @@ impl Example { self.context.wait_for(&sp, !0); } + let old_params = self.particle_system.params; self.particle_system.destroy(&self.context); self.particle_system = particle::System::new( &self.context, @@ -270,6 +270,8 @@ impl Example { }, self.sample_count, ); + self.particle_system.params = old_params; + if let Some(msaa_view) = self.msaa_view.take() { self.context.destroy_texture_view(msaa_view); } @@ -351,7 +353,10 @@ fn main() { let raw_input = egui_winit.take_egui_input(&window); let egui_output = egui_winit.egui_ctx().run(raw_input, |egui_ctx| { egui::SidePanel::left("info").show(egui_ctx, |ui| { + ui.add_space(5.0); example.add_gui(ui); + + ui.add_space(5.0); if ui.button("Quit").clicked() { target.exit(); } From e7d71488a5c49fd5594ff07aa2a867d40374b28b Mon Sep 17 00:00:00 2001 From: "Erik W. Gren" Date: Sat, 7 Dec 2024 22:03:31 +0100 Subject: [PATCH 11/13] support blitting to/from texture, not just renderbuffers --- blade-graphics/src/gles/command.rs | 66 +++++++++++++++++++----------- 1 file changed, 42 insertions(+), 24 deletions(-) diff --git a/blade-graphics/src/gles/command.rs b/blade-graphics/src/gles/command.rs index d55dcce..1f140c9 100644 --- a/blade-graphics/src/gles/command.rs +++ b/blade-graphics/src/gles/command.rs @@ -813,37 +813,55 @@ impl super::Command { Self::BlitFramebuffer { from, to } => { /* - TODO(ErikWDev): Validate + TODO: Framebuffers could be re-used instead of being created on the fly. + Currently deleted down below */ - - let target_from = match from.inner { - super::TextureInner::Renderbuffer { raw } => raw, - _ => panic!("Unsupported resolve between non-renderbuffers"), - }; - let target_to = match to.inner { - super::TextureInner::Renderbuffer { raw } => raw, - _ => panic!("Unsupported resolve between non-renderbuffers"), - }; - let framebuf_from = gl.create_framebuffer().unwrap(); let framebuf_to = gl.create_framebuffer().unwrap(); gl.bind_framebuffer(glow::READ_FRAMEBUFFER, Some(framebuf_from)); - gl.framebuffer_renderbuffer( - glow::READ_FRAMEBUFFER, - glow::COLOR_ATTACHMENT0, // NOTE: Assuming color attachment - glow::RENDERBUFFER, - Some(target_from), - ); + match from.inner { + super::TextureInner::Renderbuffer { raw } => { + gl.framebuffer_renderbuffer( + glow::READ_FRAMEBUFFER, + glow::COLOR_ATTACHMENT0, // NOTE: Assuming color attachment + glow::RENDERBUFFER, + Some(raw), + ); + } + super::TextureInner::Texture { raw, target } => { + gl.framebuffer_texture_2d( + glow::READ_FRAMEBUFFER, + glow::COLOR_ATTACHMENT0, + target, + Some(raw), + 0, + ); + } + } gl.bind_framebuffer(glow::DRAW_FRAMEBUFFER, Some(framebuf_to)); - gl.framebuffer_renderbuffer( - glow::DRAW_FRAMEBUFFER, - glow::COLOR_ATTACHMENT0, // NOTE: Assuming color attachment - glow::RENDERBUFFER, - Some(target_to), - ); - assert_eq!( + match to.inner { + super::TextureInner::Renderbuffer { raw } => { + gl.framebuffer_renderbuffer( + glow::DRAW_FRAMEBUFFER, + glow::COLOR_ATTACHMENT0, // NOTE: Assuming color attachment + glow::RENDERBUFFER, + Some(raw), + ); + } + super::TextureInner::Texture { raw, target } => { + gl.framebuffer_texture_2d( + glow::DRAW_FRAMEBUFFER, + glow::COLOR_ATTACHMENT0, + target, + Some(raw), + 0, + ); + } + } + + debug_assert_eq!( gl.check_framebuffer_status(glow::DRAW_FRAMEBUFFER), glow::FRAMEBUFFER_COMPLETE, "DRAW_FRAMEBUFFER is not complete" From 334c12743fbd86dc3c0a96417c9f1f2ffd4f5590 Mon Sep 17 00:00:00 2001 From: "Erik W. Gren" Date: Sat, 7 Dec 2024 22:03:52 +0100 Subject: [PATCH 12/13] ResolveTo docs --- blade-graphics/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blade-graphics/src/lib.rs b/blade-graphics/src/lib.rs index 795fc1c..6719137 100644 --- a/blade-graphics/src/lib.rs +++ b/blade-graphics/src/lib.rs @@ -1050,7 +1050,7 @@ pub enum InitOp { pub enum FinishOp { Store, Discard, - /// The resolved texture will be stored but it is undefined what + /// The texture specified here will be stored but it is undefined what /// happens to the original render target ResolveTo(TextureView), Ignore, From a63bc0d83452433acf73a11328bff83a696c89fa Mon Sep 17 00:00:00 2001 From: "Erik W. Gren" Date: Sat, 7 Dec 2024 22:05:07 +0100 Subject: [PATCH 13/13] winit --- Cargo.toml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 816adbd..d1cc0f6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,8 +28,7 @@ profiling = "1" slab = "0.4" strum = { version = "0.25", features = ["derive"] } web-sys = "0.3.60" -# winit = { version = "0.30" } -winit = { version = "0.30", default-features=false, features=["x11", "rwh_06"] } +winit = { version = "0.30" } [lib]