From e947271f9a0a16c1b8956bec592aec32098c4635 Mon Sep 17 00:00:00 2001 From: chyyran Date: Mon, 16 Jan 2023 00:16:31 -0500 Subject: [PATCH] vk: write uniforms directly to host-visible mapped memory --- librashader-runtime-vk/src/draw_quad.rs | 5 +- librashader-runtime-vk/src/filter_chain.rs | 27 +++-- librashader-runtime-vk/src/filter_pass.rs | 29 +++-- .../src/hello_triangle/pipeline.rs | 2 + librashader-runtime-vk/src/lib.rs | 1 - .../src/vulkan_primitives.rs | 104 ++++++++++++++++-- librashader-runtime-vk/src/vulkan_state.rs | 1 - librashader-runtime/src/binding.rs | 6 +- librashader-runtime/src/uniforms.rs | 73 ++++++++---- 9 files changed, 195 insertions(+), 53 deletions(-) diff --git a/librashader-runtime-vk/src/draw_quad.rs b/librashader-runtime-vk/src/draw_quad.rs index 418a0f4..7b8b4b3 100644 --- a/librashader-runtime-vk/src/draw_quad.rs +++ b/librashader-runtime-vk/src/draw_quad.rs @@ -1,6 +1,7 @@ use crate::error; use crate::vulkan_primitives::VulkanBuffer; use ash::vk; +use std::sync::Arc; #[rustfmt::skip] pub(crate) static VBO_DEFAULT_FINAL: &[f32; 16] = &[ @@ -13,12 +14,12 @@ pub(crate) static VBO_DEFAULT_FINAL: &[f32; 16] = &[ pub struct DrawQuad { buffer: VulkanBuffer, - device: ash::Device, + device: Arc, } impl DrawQuad { pub fn new( - device: &ash::Device, + device: &Arc, mem_props: &vk::PhysicalDeviceMemoryProperties, ) -> error::Result { let mut buffer = VulkanBuffer::new( diff --git a/librashader-runtime-vk/src/filter_chain.rs b/librashader-runtime-vk/src/filter_chain.rs index 90cfdf6..3e700f7 100644 --- a/librashader-runtime-vk/src/filter_chain.rs +++ b/librashader-runtime-vk/src/filter_chain.rs @@ -8,7 +8,8 @@ use crate::queue_selection::get_graphics_queue; use crate::render_target::{RenderTarget, DEFAULT_MVP}; use crate::samplers::SamplerSet; use crate::texture::{InputImage, OwnedImage, OwnedImageLayout, VulkanImage}; -use crate::ubo_ring::VkUboRing; +// use crate::ubo_ring::VkUboRing; +use crate::vulkan_primitives::RawVulkanBuffer; use crate::vulkan_state::VulkanGraphicsPipeline; use crate::{error, util}; use ash::vk; @@ -352,8 +353,13 @@ impl FilterChainVulkan { .as_ref() .map(|ubo| ubo.size as usize) .unwrap_or(0); - let uniform_storage = UniformStorage::new( - ubo_size, + let uniform_storage = UniformStorage::new_with_storage( + RawVulkanBuffer::new( + &vulkan.device, + &vulkan.memory_properties, + vk::BufferUsageFlags::UNIFORM_BUFFER, + ubo_size, + )?, reflection .push_constant .as_ref() @@ -383,13 +389,12 @@ impl FilterChainVulkan { frames_in_flight, )?; - let ubo_ring = VkUboRing::new( - &vulkan.device, - &vulkan.memory_properties, - frames_in_flight as usize, - ubo_size, - )?; - // shader_vulkan: 2026 + // let ubo_ring = VkUboRing::new( + // &vulkan.device, + // &vulkan.memory_properties, + // frames_in_flight as usize, + // ubo_size, + // )?; filters.push(FilterPass { device: vulkan.device.clone(), reflection, @@ -399,7 +404,7 @@ impl FilterChainVulkan { source, config, graphics_pipeline, - ubo_ring, + // ubo_ring, frames_in_flight, }); } diff --git a/librashader-runtime-vk/src/filter_pass.rs b/librashader-runtime-vk/src/filter_pass.rs index f0893e3..ed53784 100644 --- a/librashader-runtime-vk/src/filter_pass.rs +++ b/librashader-runtime-vk/src/filter_pass.rs @@ -2,7 +2,7 @@ use crate::filter_chain::FilterCommon; use crate::render_target::RenderTarget; use crate::samplers::SamplerSet; use crate::texture::InputImage; -use crate::ubo_ring::VkUboRing; +use crate::vulkan_primitives::RawVulkanBuffer; use crate::vulkan_state::VulkanGraphicsPipeline; use crate::{error, VulkanImage}; use ash::vk; @@ -14,7 +14,7 @@ use librashader_reflect::reflect::semantics::{ }; use librashader_reflect::reflect::ShaderReflection; use librashader_runtime::binding::{BindSemantics, TextureInput}; -use librashader_runtime::uniforms::{UniformStorage, UniformStorageAccess}; +use librashader_runtime::uniforms::{NoUniformBinder, UniformStorage, UniformStorageAccess}; use rustc_hash::FxHashMap; use std::sync::Arc; @@ -22,12 +22,12 @@ pub struct FilterPass { pub device: Arc, pub reflection: ShaderReflection, // pub(crate) compiled: ShaderCompilerOutput>, - pub(crate) uniform_storage: UniformStorage, + pub(crate) uniform_storage: UniformStorage, RawVulkanBuffer>, pub uniform_bindings: FxHashMap, pub source: ShaderSource, pub config: ShaderPassConfig, pub graphics_pipeline: VulkanGraphicsPipeline, - pub ubo_ring: VkUboRing, + // pub ubo_ring: VkUboRing, pub frames_in_flight: u32, } @@ -37,7 +37,7 @@ impl TextureInput for InputImage { } } -impl BindSemantics for FilterPass { +impl BindSemantics, RawVulkanBuffer> for FilterPass { type InputTexture = InputImage; type SamplerSet = SamplerSet; type DescriptorSet<'a> = vk::DescriptorSet; @@ -113,10 +113,21 @@ impl FilterPass { ); if let Some(ubo) = &self.reflection.ubo { - // shader_vulkan: 2554 (ra uses uses one big buffer) - // itll be simpler for us if we just use a RingBuffer tbh. - self.ubo_ring - .bind_to_descriptor_set(descriptor, ubo.binding, &self.uniform_storage)?; + // RetroArch uses uses one big buffer for this and handles alignment themselves. + // It's simpler here if we just use a buffer for each pass but not ideal. + // + // May want to switch to something like VMA in the future. + + // VkUboRing is no longer used, we just write directly to the storage now. + + // self.ubo_ring + // .bind_to_descriptor_set(descriptor, ubo.binding, &self.uniform_storage)?; + + self.uniform_storage.inner_ubo().bind_to_descriptor_set( + descriptor, + ubo.binding, + &self.uniform_storage, + )?; } output.output.begin_pass(cmd); diff --git a/librashader-runtime-vk/src/hello_triangle/pipeline.rs b/librashader-runtime-vk/src/hello_triangle/pipeline.rs index f797345..78fb98e 100644 --- a/librashader-runtime-vk/src/hello_triangle/pipeline.rs +++ b/librashader-runtime-vk/src/hello_triangle/pipeline.rs @@ -12,6 +12,8 @@ use std::mem::align_of; const ENTRY_POINT: &CStr = unsafe { CStr::from_bytes_with_nul_unchecked(b"main\0") }; #[derive(Default, Clone, Debug, Copy)] +#[repr(C)] +#[allow(dead_code)] struct Vertex { pos: [f32; 4], color: [f32; 4], diff --git a/librashader-runtime-vk/src/lib.rs b/librashader-runtime-vk/src/lib.rs index 2c7c8bb..f28562e 100644 --- a/librashader-runtime-vk/src/lib.rs +++ b/librashader-runtime-vk/src/lib.rs @@ -14,7 +14,6 @@ mod queue_selection; mod render_target; mod samplers; mod texture; -mod ubo_ring; mod util; mod vulkan_primitives; mod vulkan_state; diff --git a/librashader-runtime-vk/src/vulkan_primitives.rs b/librashader-runtime-vk/src/vulkan_primitives.rs index 348cf47..71e3fc9 100644 --- a/librashader-runtime-vk/src/vulkan_primitives.rs +++ b/librashader-runtime-vk/src/vulkan_primitives.rs @@ -1,6 +1,10 @@ use crate::{error, util}; use ash::vk; +use librashader_runtime::uniforms::UniformStorageAccess; use std::ffi::c_void; +use std::mem::ManuallyDrop; +use std::ops::{Deref, DerefMut}; +use std::ptr::NonNull; use std::sync::Arc; pub struct VulkanImageMemory { @@ -36,9 +40,9 @@ impl Drop for VulkanImageMemory { pub struct VulkanBuffer { pub handle: vk::Buffer, - device: ash::Device, - pub memory: vk::DeviceMemory, - pub size: vk::DeviceSize, + device: Arc, + memory: vk::DeviceMemory, + size: vk::DeviceSize, } pub struct VulkanBufferMapHandle<'a> { @@ -48,7 +52,7 @@ pub struct VulkanBufferMapHandle<'a> { impl VulkanBuffer { pub fn new( - device: &ash::Device, + device: &Arc, mem_props: &vk::PhysicalDeviceMemoryProperties, usage: vk::BufferUsageFlags, size: usize, @@ -112,13 +116,15 @@ impl Drop for VulkanBuffer { impl<'a> VulkanBufferMapHandle<'a> { pub unsafe fn copy_from(&mut self, offset: usize, src: &[u8]) { - std::ptr::copy_nonoverlapping( - src.as_ptr(), + let mut align = ash::util::Align::new( self.ptr .map_addr(|original| original.wrapping_add(offset)) .cast(), - src.len(), + std::mem::align_of::() as u64, + self.buffer.size, ); + + align.copy_from_slice(src); } } @@ -127,3 +133,87 @@ impl<'a> Drop for VulkanBufferMapHandle<'a> { unsafe { self.buffer.device.unmap_memory(self.buffer.memory) } } } + +pub struct RawVulkanBuffer { + buffer: ManuallyDrop, + ptr: NonNull, +} + +impl RawVulkanBuffer { + pub fn new( + device: &Arc, + mem_props: &vk::PhysicalDeviceMemoryProperties, + usage: vk::BufferUsageFlags, + size: usize, + ) -> error::Result { + let buffer = ManuallyDrop::new(VulkanBuffer::new(device, mem_props, usage, size)?); + let ptr = unsafe { + NonNull::new_unchecked(device.map_memory( + buffer.memory, + 0, + buffer.size, + vk::MemoryMapFlags::empty(), + )?) + }; + + Ok(RawVulkanBuffer { buffer, ptr }) + } + + pub fn bind_to_descriptor_set( + &self, + descriptor_set: vk::DescriptorSet, + binding: u32, + storage: &impl UniformStorageAccess, + ) -> error::Result<()> { + unsafe { + let buffer_info = [vk::DescriptorBufferInfo::builder() + .buffer(self.buffer.handle) + .offset(0) + .range(storage.ubo_slice().len() as vk::DeviceSize) + .build()]; + + let write_info = [vk::WriteDescriptorSet::builder() + .descriptor_type(vk::DescriptorType::UNIFORM_BUFFER) + .dst_set(descriptor_set) + .dst_binding(binding) + .dst_array_element(0) + .buffer_info(&buffer_info) + .build()]; + + self.buffer.device.update_descriptor_sets(&write_info, &[]) + } + Ok(()) + } +} + +impl Drop for RawVulkanBuffer { + fn drop(&mut self) { + unsafe { + self.buffer.device.unmap_memory(self.buffer.memory); + self.ptr = NonNull::dangling(); + if self.buffer.memory != vk::DeviceMemory::null() { + self.buffer.device.free_memory(self.buffer.memory, None); + } + + if self.buffer.handle != vk::Buffer::null() { + self.buffer.device.destroy_buffer(self.buffer.handle, None); + } + } + } +} + +impl Deref for RawVulkanBuffer { + type Target = [u8]; + + fn deref(&self) -> &Self::Target { + unsafe { std::slice::from_raw_parts(self.ptr.as_ptr().cast(), self.buffer.size as usize) } + } +} + +impl DerefMut for RawVulkanBuffer { + fn deref_mut(&mut self) -> &mut Self::Target { + unsafe { + std::slice::from_raw_parts_mut(self.ptr.as_ptr().cast(), self.buffer.size as usize) + } + } +} diff --git a/librashader-runtime-vk/src/vulkan_state.rs b/librashader-runtime-vk/src/vulkan_state.rs index 55f3894..415dde9 100644 --- a/librashader-runtime-vk/src/vulkan_state.rs +++ b/librashader-runtime-vk/src/vulkan_state.rs @@ -183,7 +183,6 @@ impl VulkanGraphicsPipeline { reflection: &ShaderReflection, replicas: u32, ) -> error::Result { - // shader_vulkan 1927 (init_pipeline_layout) let pipeline_layout = PipelineLayoutObjects::new(reflection, replicas, device)?; let input_assembly = vk::PipelineInputAssemblyStateCreateInfo::builder() diff --git a/librashader-runtime/src/binding.rs b/librashader-runtime/src/binding.rs index d8f0397..cdd7cb7 100644 --- a/librashader-runtime/src/binding.rs +++ b/librashader-runtime/src/binding.rs @@ -6,6 +6,7 @@ use librashader_reflect::reflect::semantics::{ }; use std::collections::HashMap; use std::hash::BuildHasher; +use std::ops::{Deref, DerefMut}; /// Trait for input textures used during uniform binding, pub trait TextureInput { @@ -47,8 +48,9 @@ where } /// Trait that abstracts binding of semantics to shader uniforms. -pub trait BindSemantics> +pub trait BindSemantics, S = Box<[u8]>> where + S: Deref + DerefMut, H: BindUniform, H: BindUniform, H: BindUniform, @@ -84,7 +86,7 @@ where fn bind_semantics<'a>( device: &Self::DeviceContext, sampler_set: &Self::SamplerSet, - uniform_storage: &mut UniformStorage, + uniform_storage: &mut UniformStorage, descriptor_set: &mut Self::DescriptorSet<'a>, mvp: &[f32; 16], frame_count: u32, diff --git a/librashader-runtime/src/uniforms.rs b/librashader-runtime/src/uniforms.rs index 9ff37b2..85d7263 100644 --- a/librashader-runtime/src/uniforms.rs +++ b/librashader-runtime/src/uniforms.rs @@ -1,5 +1,6 @@ use librashader_reflect::reflect::semantics::MemberOffset; use std::marker::PhantomData; +use std::ops::{Deref, DerefMut}; /// A scalar value that is valid as a uniform member pub trait UniformScalar: Copy + bytemuck::Pod {} @@ -36,7 +37,10 @@ pub trait UniformStorageAccess { fn push_slice(&self) -> &[u8]; } -impl UniformStorageAccess for UniformStorage { +impl UniformStorageAccess for UniformStorage +where + S: Deref + DerefMut, +{ fn ubo_pointer(&self) -> *const u8 { self.ubo.as_ptr() } @@ -64,24 +68,29 @@ impl BindUniform, T> for NoUniformBinder { } /// A helper to bind uniform variables to UBO or Push Constant Buffers. -pub struct UniformStorage> { - ubo: Box<[u8]>, +pub struct UniformStorage, S = Box<[u8]>> +where + S: Deref + DerefMut, +{ + ubo: S, push: Box<[u8]>, _h: PhantomData, _c: PhantomData, } -impl UniformStorage { - /// Create a new `UniformStorage` with the given size for UBO and Push Constant Buffer sizes. - pub fn new(ubo_size: usize, push_size: usize) -> Self { - UniformStorage { - ubo: vec![0u8; ubo_size].into_boxed_slice(), - push: vec![0u8; push_size].into_boxed_slice(), - _h: Default::default(), - _c: Default::default(), - } +impl UniformStorage +where + S: Deref + DerefMut, +{ + pub fn inner_ubo(&self) -> &S { + &self.ubo } +} +impl UniformStorage +where + S: Deref + DerefMut, +{ #[inline(always)] fn write_scalar_inner(buffer: &mut [u8], value: T, ctx: C) where @@ -100,8 +109,8 @@ impl UniformStorage { H: BindUniform, { let (buffer, offset) = match offset { - MemberOffset::Ubo(offset) => (&mut self.ubo, offset), - MemberOffset::PushConstant(offset) => (&mut self.push, offset), + MemberOffset::Ubo(offset) => (self.ubo.deref_mut(), offset), + MemberOffset::PushConstant(offset) => (self.push.deref_mut(), offset), }; Self::write_scalar_inner( @@ -110,10 +119,33 @@ impl UniformStorage { ctx, ) } + + /// Create a new `UniformStorage` with the given backing storage + pub fn new_with_storage(storage: S, push_size: usize) -> UniformStorage { + UniformStorage { + ubo: storage, + push: vec![0u8; push_size].into_boxed_slice(), + _h: Default::default(), + _c: Default::default(), + } + } } -impl UniformStorage +impl UniformStorage> { + /// Create a new `UniformStorage` with the given size for UBO and Push Constant Buffer sizes. + pub fn new(ubo_size: usize, push_size: usize) -> UniformStorage> { + UniformStorage { + ubo: vec![0u8; ubo_size].into_boxed_slice(), + push: vec![0u8; push_size].into_boxed_slice(), + _h: Default::default(), + _c: Default::default(), + } + } +} + +impl UniformStorage where + S: Deref + DerefMut, H: for<'a> BindUniform, { #[inline(always)] @@ -128,8 +160,8 @@ where #[inline(always)] pub fn bind_vec4(&mut self, offset: MemberOffset, value: impl Into<[f32; 4]>, ctx: C) { let (buffer, offset) = match offset { - MemberOffset::Ubo(offset) => (&mut self.ubo, offset), - MemberOffset::PushConstant(offset) => (&mut self.push, offset), + MemberOffset::Ubo(offset) => (self.ubo.deref_mut(), offset), + MemberOffset::PushConstant(offset) => (self.push.deref_mut(), offset), }; Self::write_vec4_inner( @@ -140,8 +172,9 @@ where } } -impl UniformStorage +impl UniformStorage where + S: Deref + DerefMut, H: for<'a> BindUniform, { #[inline(always)] @@ -156,8 +189,8 @@ where #[inline(always)] pub fn bind_mat4(&mut self, offset: MemberOffset, value: &[f32; 16], ctx: C) { let (buffer, offset) = match offset { - MemberOffset::Ubo(offset) => (&mut self.ubo, offset), - MemberOffset::PushConstant(offset) => (&mut self.push, offset), + MemberOffset::Ubo(offset) => (self.ubo.deref_mut(), offset), + MemberOffset::PushConstant(offset) => (self.push.deref_mut(), offset), }; Self::write_mat4_inner( &mut buffer[offset..][..16 * std::mem::size_of::()],