From bc6bb0c3df04fa2c5aaf398f92908110a3b2b6d4 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Wed, 16 Jun 2021 01:26:14 -0400 Subject: [PATCH] hal/vk: render passes and queries --- wgpu-hal/src/vulkan/adapter.rs | 2 + wgpu-hal/src/vulkan/command.rs | 122 ++++++++++++++++++++++++++++++-- wgpu-hal/src/vulkan/conv.rs | 42 +++++++++++ wgpu-hal/src/vulkan/device.rs | 109 +++++++++++++++++++++++++--- wgpu-hal/src/vulkan/instance.rs | 6 +- wgpu-hal/src/vulkan/mod.rs | 40 ++++++++++- 6 files changed, 304 insertions(+), 17 deletions(-) diff --git a/wgpu-hal/src/vulkan/adapter.rs b/wgpu-hal/src/vulkan/adapter.rs index 0fc2e8f861..674b2108fd 100644 --- a/wgpu-hal/src/vulkan/adapter.rs +++ b/wgpu-hal/src/vulkan/adapter.rs @@ -715,6 +715,7 @@ impl crate::Adapter for super::Adapter { let family_index = 0; //TODO let raw_queue = raw_device.get_device_queue(family_index, 0); + let shared = Arc::new(super::DeviceShared { raw: raw_device, instance: Arc::clone(&self.instance), @@ -727,6 +728,7 @@ impl crate::Adapter for super::Adapter { private_caps: self.private_caps.clone(), _timestamp_period: self.phd_capabilities.properties.limits.timestamp_period, render_passes: Mutex::new(Default::default()), + framebuffers: Mutex::new(Default::default()), }); let queue = super::Queue { raw: raw_queue, diff --git a/wgpu-hal/src/vulkan/command.rs b/wgpu-hal/src/vulkan/command.rs index 0f2538d7be..8ac7bf7c8b 100644 --- a/wgpu-hal/src/vulkan/command.rs +++ b/wgpu-hal/src/vulkan/command.rs @@ -1,9 +1,10 @@ use super::conv; +use arrayvec::ArrayVec; use ash::{version::DeviceV1_0, vk}; use inplace_it::inplace_or_alloc_from_iter; -use std::ops::Range; +use std::{mem, ops::Range}; const ALLOCATION_GRANULARITY: u32 = 16; const DST_IMAGE_LAYOUT: vk::ImageLayout = vk::ImageLayout::TRANSFER_DST_OPTIMAL; @@ -267,10 +268,33 @@ impl crate::CommandEncoder for super::CommandEncoder { }); } - unsafe fn begin_query(&mut self, set: &super::QuerySet, index: u32) {} - unsafe fn end_query(&mut self, set: &super::QuerySet, index: u32) {} - unsafe fn write_timestamp(&mut self, set: &super::QuerySet, index: u32) {} - unsafe fn reset_queries(&mut self, set: &super::QuerySet, range: Range) {} + unsafe fn begin_query(&mut self, set: &super::QuerySet, index: u32) { + self.device.raw.cmd_begin_query( + self.active, + set.raw, + index, + vk::QueryControlFlags::empty(), + ); + } + unsafe fn end_query(&mut self, set: &super::QuerySet, index: u32) { + self.device.raw.cmd_end_query(self.active, set.raw, index); + } + unsafe fn write_timestamp(&mut self, set: &super::QuerySet, index: u32) { + self.device.raw.cmd_write_timestamp( + self.active, + vk::PipelineStageFlags::BOTTOM_OF_PIPE, + set.raw, + index, + ); + } + unsafe fn reset_queries(&mut self, set: &super::QuerySet, range: Range) { + self.device.raw.cmd_reset_query_pool( + self.active, + set.raw, + range.start, + range.end - range.start, + ); + } unsafe fn copy_query_results( &mut self, set: &super::QuerySet, @@ -278,12 +302,96 @@ impl crate::CommandEncoder for super::CommandEncoder { buffer: &super::Buffer, offset: wgt::BufferAddress, ) { + self.device.raw.cmd_copy_query_pool_results( + self.active, + set.raw, + range.start, + range.end - range.start, + buffer.raw, + offset, + 0, + vk::QueryResultFlags::TYPE_64 | vk::QueryResultFlags::WAIT, + ); } // render - unsafe fn begin_render_pass(&mut self, desc: &crate::RenderPassDescriptor) {} - unsafe fn end_render_pass(&mut self) {} + unsafe fn begin_render_pass(&mut self, desc: &crate::RenderPassDescriptor) { + let mut vk_clear_values = ArrayVec::<[vk::ClearValue; super::MAX_TOTAL_ATTACHMENTS]>::new(); + let mut vk_image_views = ArrayVec::<[vk::ImageView; super::MAX_TOTAL_ATTACHMENTS]>::new(); + let mut rp_key = super::RenderPassKey::default(); + let mut fb_key = super::FramebufferKey::default(); + let caps = &self.device.private_caps; + + for cat in desc.color_attachments { + vk_clear_values.push(vk::ClearValue { + color: cat.make_vk_clear_color(), + }); + vk_image_views.push(cat.target.view.raw); + rp_key.colors.push(super::ColorAttachmentKey { + base: cat.target.make_attachment_key(cat.ops, caps), + resolve: cat + .resolve_target + .as_ref() + .map(|target| target.make_attachment_key(crate::AttachmentOp::STORE, caps)), + }); + fb_key.add(cat.target.view); + if let Some(ref at) = cat.resolve_target { + vk_clear_values.push(mem::zeroed()); + vk_image_views.push(at.view.raw); + fb_key.add(at.view); + } + } + if let Some(ref ds) = desc.depth_stencil_attachment { + vk_clear_values.push(vk::ClearValue { + depth_stencil: vk::ClearDepthStencilValue { + depth: ds.clear_value.0, + stencil: ds.clear_value.1, + }, + }); + vk_image_views.push(ds.target.view.raw); + rp_key.depth_stencil = Some(super::DepthStencilAttachmentKey { + base: ds.target.make_attachment_key(ds.depth_ops, caps), + stencil_ops: ds.stencil_ops, + }); + fb_key.add(ds.target.view); + } + rp_key.sample_count = fb_key.sample_count; + + let render_area = vk::Rect2D { + offset: vk::Offset2D { x: 0, y: 0 }, + extent: vk::Extent2D { + width: fb_key.extent.width, + height: fb_key.extent.height, + }, + }; + + let raw_pass = self.device.make_render_pass(rp_key).unwrap(); + + let raw_framebuffer = self + .device + .make_framebuffer(fb_key, raw_pass, desc.label) + .unwrap(); + + let mut vk_attachment_info = vk::RenderPassAttachmentBeginInfo::builder() + .attachments(&vk_image_views) + .build(); + let mut vk_info = vk::RenderPassBeginInfo::builder() + .render_pass(raw_pass) + .render_area(render_area) + .clear_values(&vk_clear_values) + .framebuffer(raw_framebuffer); + if caps.imageless_framebuffers { + vk_info = vk_info.push_next(&mut vk_attachment_info); + } + + self.device + .raw + .cmd_begin_render_pass(self.active, &vk_info, vk::SubpassContents::INLINE); + } + unsafe fn end_render_pass(&mut self) { + self.device.raw.cmd_end_render_pass(self.active); + } unsafe fn set_bind_group( &mut self, diff --git a/wgpu-hal/src/vulkan/conv.rs b/wgpu-hal/src/vulkan/conv.rs index e6c651b64e..cd7edc2224 100644 --- a/wgpu-hal/src/vulkan/conv.rs +++ b/wgpu-hal/src/vulkan/conv.rs @@ -110,6 +110,48 @@ impl super::PrivateCapabilities { } } +impl crate::Attachment<'_, super::Api> { + pub(super) fn make_attachment_key( + &self, + ops: crate::AttachmentOp, + caps: &super::PrivateCapabilities, + ) -> super::AttachmentKey { + super::AttachmentKey { + format: caps.map_texture_format(self.view.attachment.view_format), + layout_pre: derive_image_layout(self.boundary_usage.start), + layout_in: derive_image_layout(self.usage), + layout_post: derive_image_layout(self.boundary_usage.end), + ops, + } + } +} + +impl crate::ColorAttachment<'_, super::Api> { + pub(super) unsafe fn make_vk_clear_color(&self) -> vk::ClearColorValue { + let cv = &self.clear_value; + match self + .target + .view + .attachment + .view_format + .describe() + .sample_type + { + wgt::TextureSampleType::Float { .. } | wgt::TextureSampleType::Depth => { + vk::ClearColorValue { + float32: [cv.r as f32, cv.g as f32, cv.b as f32, cv.a as f32], + } + } + wgt::TextureSampleType::Sint => vk::ClearColorValue { + int32: [cv.r as i32, cv.g as i32, cv.b as i32, cv.a as i32], + }, + wgt::TextureSampleType::Uint => vk::ClearColorValue { + uint32: [cv.r as u32, cv.g as u32, cv.b as u32, cv.a as u32], + }, + } + } +} + pub fn derive_image_layout(usage: crate::TextureUse) -> vk::ImageLayout { match usage { crate::TextureUse::COPY_SRC => vk::ImageLayout::TRANSFER_SRC_OPTIMAL, diff --git a/wgpu-hal/src/vulkan/device.rs b/wgpu-hal/src/vulkan/device.rs index 446dff300b..6da056fa0c 100644 --- a/wgpu-hal/src/vulkan/device.rs +++ b/wgpu-hal/src/vulkan/device.rs @@ -5,7 +5,7 @@ use ash::{extensions::khr, version::DeviceV1_0, vk}; use inplace_it::inplace_or_alloc_from_iter; use parking_lot::Mutex; -use std::{collections::hash_map::Entry, ffi::CString, ptr, sync::Arc}; +use std::{cmp, collections::hash_map::Entry, ffi::CString, ptr, sync::Arc}; impl super::DeviceShared { unsafe fn set_object_name( @@ -148,6 +148,74 @@ impl super::DeviceShared { } }) } + + pub fn make_framebuffer( + &self, + key: super::FramebufferKey, + raw_pass: vk::RenderPass, + pass_label: crate::Label, + ) -> Result { + Ok(match self.framebuffers.lock().entry(key) { + Entry::Occupied(e) => *e.get(), + Entry::Vacant(e) => { + let vk_views = e + .key() + .attachments + .iter() + .map(|at| at.raw) + .collect::>(); + let vk_view_formats = e + .key() + .attachments + .iter() + .map(|at| self.private_caps.map_texture_format(at.view_format)) + .collect::>(); + let vk_image_infos = e + .key() + .attachments + .iter() + .enumerate() + .map(|(i, at)| { + vk::FramebufferAttachmentImageInfo::builder() + .usage(conv::map_texture_usage(at.texture_usage)) + .flags(at.raw_image_flags) + .width(e.key().extent.width) + .height(e.key().extent.height) + .layer_count(e.key().extent.depth_or_array_layers) + .view_formats(&vk_view_formats[i..i + 1]) + .build() + }) + .collect::>(); + + let mut vk_attachment_info = vk::FramebufferAttachmentsCreateInfo::builder() + .attachment_image_infos(&vk_image_infos) + .build(); + let mut vk_info = vk::FramebufferCreateInfo::builder() + .render_pass(raw_pass) + .width(e.key().extent.width) + .height(e.key().extent.height) + .layers(e.key().extent.depth_or_array_layers); + + if self.private_caps.imageless_framebuffers { + //TODO: https://github.com/MaikKlein/ash/issues/450 + vk_info = vk_info + .flags(vk::FramebufferCreateFlags::IMAGELESS_KHR) + .push_next(&mut vk_attachment_info); + vk_info.attachment_count = e.key().attachments.len() as u32; + } else { + vk_info = vk_info.attachments(&vk_views); + } + + *e.insert(unsafe { + let raw = self.raw.create_framebuffer(&vk_info, None).unwrap(); + if let Some(label) = pass_label { + self.set_object_name(vk::ObjectType::FRAMEBUFFER, raw, label); + } + raw + }) + } + }) + } } impl gpu_alloc::MemoryDevice for super::DeviceShared { @@ -432,7 +500,7 @@ impl super::Device { device: Arc::clone(&self.shared), fence, images, - format: config.format, + config: config.clone(), }) } } @@ -562,13 +630,13 @@ impl crate::Device for super::Device { desc: &crate::TextureDescriptor, ) -> Result { let (array_layer_count, vk_extent) = conv::map_extent(desc.size, desc.dimension); - let mut flags = vk::ImageCreateFlags::empty(); + let mut raw_flags = vk::ImageCreateFlags::empty(); if desc.dimension == wgt::TextureDimension::D2 && desc.size.depth_or_array_layers % 6 == 0 { - flags |= vk::ImageCreateFlags::CUBE_COMPATIBLE; + raw_flags |= vk::ImageCreateFlags::CUBE_COMPATIBLE; } let vk_info = vk::ImageCreateInfo::builder() - .flags(flags) + .flags(raw_flags) .image_type(conv::map_texture_dimension(desc.dimension)) .format(self.shared.private_caps.map_texture_format(desc.format)) .extent(vk_extent) @@ -605,9 +673,13 @@ impl crate::Device for super::Device { Ok(super::Texture { raw, block: Some(block), + usage: desc.usage, dim: desc.dimension, aspects: crate::FormatAspect::from(desc.format), format_info: desc.format.describe(), + sample_count: desc.sample_count, + size: desc.size, + raw_flags, }) } unsafe fn destroy_texture(&self, texture: super::Texture) { @@ -627,7 +699,6 @@ impl crate::Device for super::Device { .image(texture.raw) .view_type(conv::map_view_dimension(desc.dimension)) .format(self.shared.private_caps.map_texture_format(desc.format)) - //.components(conv::map_swizzle(swizzle)) .subresource_range(conv::map_subresource_range(&desc.range, texture.aspects)); let mut image_view_info; @@ -645,7 +716,29 @@ impl crate::Device for super::Device { .set_object_name(vk::ObjectType::IMAGE_VIEW, raw, label); } - Ok(super::TextureView { raw }) + let attachment = super::FramebufferAttachment { + raw: if self.shared.private_caps.imageless_framebuffers { + vk::ImageView::null() + } else { + raw + }, + texture_usage: texture.usage, + raw_image_flags: texture.raw_flags, + view_format: desc.format, + }; + let sample_count = texture.sample_count; + let render_size = wgt::Extent3d { + width: cmp::max(1, texture.size.width >> desc.range.base_mip_level), + height: cmp::max(1, texture.size.height >> desc.range.base_mip_level), + depth_or_array_layers: 1, + }; + + Ok(super::TextureView { + raw, + attachment, + sample_count, + render_size, + }) } unsafe fn destroy_texture_view(&self, view: super::TextureView) { self.shared.raw.destroy_image_view(view.raw, None); @@ -735,7 +828,7 @@ impl crate::Device for super::Device { let mut types = Vec::new(); for entry in desc.entries { let count = entry.count.map_or(1, |c| c.get()); - if entry.binding as usize > types.len() { + if entry.binding as usize >= types.len() { types.resize( entry.binding as usize + 1, vk::DescriptorType::INPUT_ATTACHMENT, diff --git a/wgpu-hal/src/vulkan/instance.rs b/wgpu-hal/src/vulkan/instance.rs index 047f89efa8..487dd5b665 100644 --- a/wgpu-hal/src/vulkan/instance.rs +++ b/wgpu-hal/src/vulkan/instance.rs @@ -639,9 +639,13 @@ impl crate::Surface for super::Surface { texture: super::Texture { raw: sc.images[index as usize], block: None, + usage: sc.config.usage, dim: wgt::TextureDimension::D2, aspects: crate::FormatAspect::COLOR, - format_info: sc.format.describe(), + format_info: sc.config.format.describe(), + sample_count: 1, + size: sc.config.extent, + raw_flags: vk::ImageCreateFlags::empty(), }, }; Ok(Some(crate::AcquiredSurfaceTexture { diff --git a/wgpu-hal/src/vulkan/mod.rs b/wgpu-hal/src/vulkan/mod.rs index d4fa6c93c5..275bd5488a 100644 --- a/wgpu-hal/src/vulkan/mod.rs +++ b/wgpu-hal/src/vulkan/mod.rs @@ -17,6 +17,7 @@ use ash::{ use parking_lot::Mutex; const MILLIS_TO_NANOS: u64 = 1_000_000; +const MAX_TOTAL_ATTACHMENTS: usize = crate::MAX_COLOR_TARGETS * 2 + 1; #[derive(Clone)] pub struct Api; @@ -72,7 +73,7 @@ struct Swapchain { device: Arc, fence: vk::Fence, images: Vec, - format: wgt::TextureFormat, + config: crate::SurfaceConfiguration, } pub struct Surface { @@ -172,6 +173,35 @@ struct RenderPassKey { sample_count: u32, } +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +struct FramebufferAttachment { + /// Can be NULL if the framebuffer is image-less + raw: vk::ImageView, + texture_usage: crate::TextureUse, + raw_image_flags: vk::ImageCreateFlags, + view_format: wgt::TextureFormat, +} + +#[derive(Clone, Eq, Default, Hash, PartialEq)] +struct FramebufferKey { + attachments: ArrayVec<[FramebufferAttachment; MAX_TOTAL_ATTACHMENTS]>, + extent: wgt::Extent3d, + sample_count: u32, +} + +impl FramebufferKey { + fn add(&mut self, view: &TextureView) { + self.extent.width = self.extent.width.max(view.render_size.width); + self.extent.height = self.extent.height.max(view.render_size.height); + self.extent.depth_or_array_layers = self + .extent + .depth_or_array_layers + .max(view.render_size.depth_or_array_layers); + self.sample_count = self.sample_count.max(view.sample_count); + self.attachments.push(view.attachment.clone()); + } +} + struct DeviceShared { raw: ash::Device, instance: Arc, @@ -182,6 +212,7 @@ struct DeviceShared { downlevel_flags: wgt::DownlevelFlags, private_caps: PrivateCapabilities, render_passes: Mutex>, + framebuffers: Mutex>, } pub struct Device { @@ -210,14 +241,21 @@ pub struct Buffer { pub struct Texture { raw: vk::Image, block: Option>, + usage: crate::TextureUse, dim: wgt::TextureDimension, aspects: crate::FormatAspect, format_info: wgt::TextureFormatInfo, + sample_count: u32, + size: wgt::Extent3d, + raw_flags: vk::ImageCreateFlags, } #[derive(Debug)] pub struct TextureView { raw: vk::ImageView, + attachment: FramebufferAttachment, + sample_count: u32, + render_size: wgt::Extent3d, } #[derive(Debug)]