From 194943c12ccb2c3dd6fb5732c678566e47170bf9 Mon Sep 17 00:00:00 2001 From: Rukai Date: Tue, 18 Jun 2019 11:55:03 +1000 Subject: [PATCH] Full MSAA handling --- Cargo.lock | 20 ++--- wgpu-native/src/command/mod.rs | 124 ++++++++++++++++++++++++++++-- wgpu-native/src/command/render.rs | 8 +- wgpu-native/src/conv.rs | 2 +- wgpu-native/src/device.rs | 42 ++++++++-- wgpu-native/src/pipeline.rs | 1 + 6 files changed, 171 insertions(+), 26 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 61b743e116..18de8de20c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -378,15 +378,17 @@ dependencies = [ [[package]] name = "gfx-backend-vulkan" -version = "0.2.0" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "ash 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "core-graphics 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)", "derivative 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "gfx-hal 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "objc 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "winit 0.19.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -997,7 +999,7 @@ dependencies = [ [[package]] name = "rendy-descriptor" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "derivative 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1010,7 +1012,7 @@ dependencies = [ [[package]] name = "rendy-memory" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "colorful 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1294,13 +1296,13 @@ dependencies = [ "gfx-backend-empty 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "gfx-backend-gl 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "gfx-backend-metal 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "gfx-backend-vulkan 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gfx-backend-vulkan 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "gfx-hal 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rendy-descriptor 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rendy-memory 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rendy-descriptor 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rendy-memory 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", "vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "winit 0.19.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1485,7 +1487,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum gfx-backend-empty 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "48d970b730c75da0e4905380e9af664e0ef46c5c4643fbacf637e114df047fb5" "checksum gfx-backend-gl 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "387c0d9e9f9177478413af94e57580cb98fde035e08a723ed7e6b45b022f3092" "checksum gfx-backend-metal 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d39d1a05e08367faa2006316d4420439fc5f82de63bc8706bab50b4eba7760c" -"checksum gfx-backend-vulkan 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "40aed111bb822e01f3569345eefc732c5b557b19d7905b0a865e553cf4e4e7ea" +"checksum gfx-backend-vulkan 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c3947889fe783aa17c4ca184f18d1142e61a4aaa292715ada4b8bf75a6a6e1" "checksum gfx-hal 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c045161572372465ebbcb0d33ee937b6ed8874a193148c053688a400b92b197c" "checksum gfx_gl 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3e8a920f8f6c1025a7ddf9dd25502bf059506fd3cd765dfbe8dba0b56b7eeecb" "checksum gl_generator 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "39a23d5e872a275135d66895d954269cf5e8661d234eb1c2480f4ce0d586acbd" @@ -1551,8 +1553,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)" = "12229c14a0f65c4f1cb046a3b52047cdd9da1f4b30f8a39c5063c8bae515e252" "checksum relevant 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bbc232e13d37f4547f5b9b42a5efc380cabe5dbc1807f8b893580640b2ab0308" "checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5" -"checksum rendy-descriptor 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d994004622af2fe3848fde527258dd6bee8cf089a51e2e83fe5f2b7aeb09f6c0" -"checksum rendy-memory 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fbdab806e4d349037b60ff6a25dad1260da58eb4b43ecac31350443278d5c002" +"checksum rendy-descriptor 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "83249fea9ebf5ce1715b13158f5f0947d831b2df63809d41dac2501556635db8" +"checksum rendy-memory 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3b3846dfe67af528d47ef5fbf30aff05ab683ebf076971dd5a9293a9bf75a0c1" "checksum rustc-demangle 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "ccc78bfd5acd7bf3e89cffcf899e5cb1a52d6fafa8dec2739ad70c9577a57288" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" "checksum rusttype 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "25951e85bb2647960969f72c559392245a5bd07446a589390bf427dda31cdc4a" diff --git a/wgpu-native/src/command/mod.rs b/wgpu-native/src/command/mod.rs index 85747797a4..ddaa398cc4 100644 --- a/wgpu-native/src/command/mod.rs +++ b/wgpu-native/src/command/mod.rs @@ -39,11 +39,16 @@ use crate::{ #[cfg(feature = "local")] use crate::{ComputePassId, RenderPassId}; +use arrayvec::ArrayVec; use back::Backend; -use hal::{command::RawCommandBuffer, Device as _}; +use hal::{ + adapter::PhysicalDevice, + command::RawCommandBuffer, + Device as _ +}; use log::trace; -use std::{collections::hash_map::Entry, iter, mem, slice, thread::ThreadId}; +use std::{collections::hash_map::Entry, iter, mem, slice, ptr, thread::ThreadId}; #[repr(C)] #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] @@ -183,10 +188,24 @@ pub fn command_encoder_begin_render_pass( let mut extent = None; let mut barriers = Vec::new(); + let limits = HUB.adapters.read()[device.adapter_id].physical_device.limits(); + let samples_count_limit = limits.framebuffer_color_samples_count; + let color_attachments = unsafe { slice::from_raw_parts(desc.color_attachments, desc.color_attachments_length) }; let depth_stencil_attachment = unsafe { desc.depth_stencil_attachment.as_ref() }; + let sample_count = color_attachments.get(0).map(|at| view_guard[at.attachment].samples).unwrap_or(1); + assert!(sample_count & samples_count_limit != 0, "Attachment sample_count must be supported by physical device limits"); + for at in color_attachments.iter() { + let sample_count_check = view_guard[at.attachment].samples; + assert_eq!(sample_count_check, sample_count, "All attachments must have the same sample_count"); + + if let Some(resolve) = unsafe { at.resolve_target.as_ref() } { + assert_eq!(view_guard[*resolve].samples, 1, "All target_resolves must have a sample_count of 1"); + } + } + let rp_key = { let trackers = &mut cmb.trackers; let swap_chain_links = &mut cmb.swap_chain_links; @@ -237,7 +256,10 @@ pub fn command_encoder_begin_render_pass( } }); - let color_keys = color_attachments.iter().map(|at| { + let mut colors = ArrayVec::new(); + let mut resolves = ArrayVec::new(); + + for at in color_attachments { let view = trackers.views .use_extend(&*view_guard, at.attachment, (), ()) .unwrap(); @@ -284,17 +306,76 @@ pub fn command_encoder_begin_render_pass( hal::image::Layout::ColorAttachmentOptimal } }; - hal::pass::Attachment { + + colors.push(hal::pass::Attachment { format: Some(conv::map_texture_format(view.format)), samples: view.samples, ops: conv::map_load_store_ops(at.load_op, at.store_op), stencil_ops: hal::pass::AttachmentOps::DONT_CARE, layouts: old_layout .. hal::image::Layout::ColorAttachmentOptimal, + }); + + if let Some(resolve_target) = unsafe { at.resolve_target.as_ref() } { + let view = trackers.views + .use_extend(&*view_guard, *resolve_target, (), ()) + .unwrap(); + if let Some(ex) = extent { + assert_eq!(ex, view.extent); + } else { + extent = Some(view.extent); + } + + if view.is_owned_by_swap_chain { + let link = match texture_guard[view.texture_id.value].placement { + TexturePlacement::SwapChain(ref link) => SwapChainLink { + swap_chain_id: link.swap_chain_id.clone(), + epoch: *link.epoch.lock(), + image_index: link.image_index, + }, + TexturePlacement::Memory(_) | TexturePlacement::Void => unreachable!(), + }; + swap_chain_links.push(link); + } + + let old_layout = match trackers.textures.query( + view.texture_id.value, + view.range.clone(), + ) { + Some(usage) => { + conv::map_texture_state(usage, hal::format::Aspects::COLOR).1 + } + None => { + // Required sub-resources have inconsistent states, we need to + // issue individual barriers instead of relying on the render pass. + let (texture, pending) = trackers.textures.use_replace( + &*texture_guard, + view.texture_id.value, + view.range.clone(), + TextureUsage::OUTPUT_ATTACHMENT, + ); + barriers.extend(pending.map(|pending| hal::memory::Barrier::Image { + states: pending.to_states(), + target: &texture.raw, + families: None, + range: pending.selector, + })); + hal::image::Layout::ColorAttachmentOptimal + } + }; + + resolves.push(hal::pass::Attachment { + format: Some(conv::map_texture_format(view.format)), + samples: view.samples, + ops: hal::pass::AttachmentOps::new(hal::pass::AttachmentLoadOp::DontCare, hal::pass::AttachmentStoreOp::Store), + stencil_ops: hal::pass::AttachmentOps::DONT_CARE, + layouts: old_layout .. hal::image::Layout::ColorAttachmentOptimal, + }); } - }); + } RenderPassKey { - colors: color_keys.collect(), + colors, + resolves, depth_stencil, } }; @@ -313,22 +394,40 @@ pub fn command_encoder_begin_render_pass( let render_pass = match render_pass_cache.entry(rp_key.clone()) { Entry::Occupied(e) => e.into_mut(), Entry::Vacant(e) => { + let attachment_unused: u32 = !0; // TODO: Get from ash or expose in gfx-hal. Very important this remains a u32. + let color_ids = [ (0, hal::image::Layout::ColorAttachmentOptimal), (1, hal::image::Layout::ColorAttachmentOptimal), (2, hal::image::Layout::ColorAttachmentOptimal), (3, hal::image::Layout::ColorAttachmentOptimal), ]; + + let mut resolve_ids = ArrayVec::<[_; crate::device::MAX_COLOR_TARGETS]>::new(); + let mut attachment_index = color_attachments.len(); + if color_attachments.iter().any(|at| at.resolve_target != ptr::null()) { + for (i, at) in color_attachments.iter().enumerate() { + if at.resolve_target == ptr::null() { + resolve_ids.push((attachment_unused as usize, hal::image::Layout::ColorAttachmentOptimal)); + } else { + let sample_count_check = view_guard[color_attachments[i].attachment].samples; + assert!(sample_count_check > 1, "RenderPassColorAttachmentDescriptor with a resolve_target must have an attachment with sample_count > 1"); + resolve_ids.push((attachment_index, hal::image::Layout::ColorAttachmentOptimal)); + attachment_index += 1; + } + } + } + let depth_id = ( - color_attachments.len(), + attachment_index, hal::image::Layout::DepthStencilAttachmentOptimal, ); let subpass = hal::pass::SubpassDesc { colors: &color_ids[.. color_attachments.len()], + resolves: &resolve_ids, depth_stencil: depth_stencil_attachment.map(|_| &depth_id), inputs: &[], - resolves: &[], preserves: &[], }; @@ -345,6 +444,9 @@ pub fn command_encoder_begin_render_pass( let mut framebuffer_cache = device.framebuffers.lock(); let fb_key = FramebufferKey { colors: color_attachments.iter().map(|at| at.attachment).collect(), + resolves: color_attachments.iter().filter_map(|at| + unsafe { at.resolve_target.as_ref() }.cloned() + ).collect(), depth_stencil: depth_stencil_attachment.map(|at| at.attachment), }; let framebuffer = match framebuffer_cache.entry(fb_key) { @@ -437,6 +539,11 @@ pub fn command_encoder_begin_render_pass( .iter() .map(|at| view_guard[at.attachment].format) .collect(), + resolves: color_attachments + .iter() + .filter_map(|at| unsafe { at.resolve_target.as_ref() }) + .map(|resolve| view_guard[*resolve].format) + .collect(), depth_stencil: depth_stencil_attachment.map(|at| view_guard[at.attachment].format), }; @@ -447,6 +554,7 @@ pub fn command_encoder_begin_render_pass( ref_count: cmb.life_guard.ref_count.clone(), }, context, + sample_count, ) } diff --git a/wgpu-native/src/command/render.rs b/wgpu-native/src/command/render.rs index 94fafabdfd..912227751e 100644 --- a/wgpu-native/src/command/render.rs +++ b/wgpu-native/src/command/render.rs @@ -119,6 +119,7 @@ pub struct RenderPass { stencil_reference_status: OptionalState, index_state: IndexState, vertex_state: VertexState, + sample_count: u8, } impl RenderPass { @@ -126,6 +127,7 @@ impl RenderPass { raw: B::CommandBuffer, cmb_id: Stored, context: RenderPassContext, + sample_count: u8, ) -> Self { RenderPass { raw, @@ -145,6 +147,7 @@ impl RenderPass { vertex_limit: 0, instance_limit: 0, }, + sample_count, } } @@ -400,10 +403,11 @@ pub extern "C" fn wgpu_render_pass_set_pipeline( let pipeline_guard = HUB.render_pipelines.read(); let pipeline = &pipeline_guard[pipeline_id]; - assert_eq!( - pass.context, pipeline.pass_context, + assert!( + pass.context.compatible(&pipeline.pass_context), "The render pipeline is not compatible with the pass!" ); + assert_eq!(pipeline.sample_count, pass.sample_count, "The render pipeline and renderpass have mismatching sample_count"); pass.blend_color_status .require(pipeline.flags.contains(PipelineFlags::BLEND_COLOR)); diff --git a/wgpu-native/src/conv.rs b/wgpu-native/src/conv.rs index 47860b2318..5641a4fbe6 100644 --- a/wgpu-native/src/conv.rs +++ b/wgpu-native/src/conv.rs @@ -428,7 +428,7 @@ pub fn map_texture_dimension_size( D2 => { assert_eq!(depth, 1); assert!(sample_size == 1 || sample_size == 2 || sample_size == 4 - || sample_size == 8 || sample_size == 16 || sample_size == 32 || sample_size == 64, + || sample_size == 8 || sample_size == 16 || sample_size == 32, "Invalid sample_count of {}", sample_size); H::D2(width, height, checked_u32_as_u16(array_size), sample_size as u8) } diff --git a/wgpu-native/src/device.rs b/wgpu-native/src/device.rs index f11cabe85d..e4fc8c4dec 100644 --- a/wgpu-native/src/device.rs +++ b/wgpu-native/src/device.rs @@ -94,12 +94,20 @@ enum HostMap { #[derive(Clone, Debug, Hash, PartialEq)] pub(crate) struct AttachmentData { pub colors: ArrayVec<[T; MAX_COLOR_TARGETS]>, + pub resolves: ArrayVec<[T; MAX_COLOR_TARGETS]>, pub depth_stencil: Option, } impl Eq for AttachmentData {} impl AttachmentData { pub(crate) fn all(&self) -> impl Iterator { - self.colors.iter().chain(&self.depth_stencil) + self.colors.iter().chain(&self.resolves).chain(&self.depth_stencil) + } +} + +impl RenderPassContext { + // Assumed the renderpass only contains one subpass + pub(crate) fn compatible(&self, other: &RenderPassContext) -> bool { + self.colors == other.colors && self.depth_stencil == other.depth_stencil } } @@ -424,7 +432,7 @@ fn map_buffer( pub struct Device { pub(crate) raw: B::Device, - adapter_id: AdapterId, + pub(crate) adapter_id: AdapterId, pub(crate) queue_group: hal::QueueGroup, pub(crate) com_allocator: command::CommandAllocator, mem_allocator: Mutex>, @@ -1394,6 +1402,11 @@ pub fn device_create_render_pipeline( device_id: DeviceId, desc: &pipeline::RenderPipelineDescriptor, ) -> pipeline::RenderPipeline { + let sc = desc.sample_count; + assert!(sc == 1 || sc == 2 || sc == 4 || sc == 8 || sc == 16 || sc == 32, + "Invalid sample_count of {}", sc); + let sc = sc as u8; + let device_guard = HUB.devices.read(); let device = &device_guard[device_id]; let pipeline_layout_guard = HUB.pipeline_layouts.read(); @@ -1409,15 +1422,20 @@ pub fn device_create_render_pipeline( .iter() .map(|at| hal::pass::Attachment { format: Some(conv::map_texture_format(at.format)), - samples: desc.sample_count as u8, + samples: sc, ops: hal::pass::AttachmentOps::PRESERVE, stencil_ops: hal::pass::AttachmentOps::DONT_CARE, layouts: hal::image::Layout::General .. hal::image::Layout::General, }) .collect(), + // We can ignore the resolves as the vulkan specs says: + // As an additional special case, if two render passes have a single subpass, + // they are compatible even if they have different resolve attachment references + // or depth/stencil resolve modes but satisfy the other compatibility conditions. + resolves: ArrayVec::new(), depth_stencil: depth_stencil_state.map(|at| hal::pass::Attachment { format: Some(conv::map_texture_format(at.format)), - samples: desc.sample_count as u8, + samples: sc, ops: hal::pass::AttachmentOps::PRESERVE, stencil_ops: hal::pass::AttachmentOps::PRESERVE, layouts: hal::image::Layout::General .. hal::image::Layout::General, @@ -1434,6 +1452,7 @@ pub fn device_create_render_pipeline( (2, hal::image::Layout::ColorAttachmentOptimal), (3, hal::image::Layout::ColorAttachmentOptimal), ]; + let depth_id = ( desc.color_states_length, hal::image::Layout::DepthStencilAttachmentOptimal, @@ -1538,8 +1557,17 @@ pub fn device_create_render_pipeline( .map(conv::map_depth_stencil_state_descriptor) .unwrap_or_default(); - // TODO - let multisampling: Option = None; + let multisampling: Option = if sc == 1 { + None + } else { + Some(hal::pso::Multisampling { + rasterization_samples: sc, + sample_shading: None, + sample_mask: !0, + alpha_coverage: false, + alpha_to_one: false, + }) + }; // TODO let baked_states = hal::pso::BakedStates { @@ -1585,6 +1613,7 @@ pub fn device_create_render_pipeline( let pass_context = RenderPassContext { colors: color_states.iter().map(|state| state.format).collect(), + resolves: ArrayVec::new(), depth_stencil: depth_stencil_state.map(|state| state.format), }; @@ -1607,6 +1636,7 @@ pub fn device_create_render_pipeline( flags, index_format: desc.vertex_input.index_format, vertex_strides, + sample_count: sc, } } diff --git a/wgpu-native/src/pipeline.rs b/wgpu-native/src/pipeline.rs index 49dcf1162a..09477dc917 100644 --- a/wgpu-native/src/pipeline.rs +++ b/wgpu-native/src/pipeline.rs @@ -301,5 +301,6 @@ pub struct RenderPipeline { pub(crate) pass_context: RenderPassContext, pub(crate) flags: PipelineFlags, pub(crate) index_format: IndexFormat, + pub(crate) sample_count: u8, pub(crate) vertex_strides: Vec<(BufferAddress, InputStepMode)>, }