diff --git a/Cargo.lock b/Cargo.lock index 0d54e9e..b73f2af 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -301,6 +301,49 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crossbeam-channel" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01a9af1f4c2ef74bb8aa1f7e19706bc72d03598c8a570bb5de72243c7a9d9d5a" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "memoffset 0.7.1", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" +dependencies = [ + "cfg-if", +] + [[package]] name = "crossfont" version = "0.5.1" @@ -606,6 +649,15 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + [[package]] name = "ident_case" version = "1.0.1" @@ -822,6 +874,7 @@ dependencies = [ "librashader-reflect", "librashader-runtime", "librashader-spirv-cross", + "rayon", "rustc-hash", "thiserror", "windows", @@ -841,6 +894,7 @@ dependencies = [ "librashader-reflect", "librashader-runtime", "librashader-spirv-cross", + "rayon", "rustc-hash", "thiserror", "windows", @@ -879,6 +933,7 @@ dependencies = [ "librashader-spirv-cross", "num", "raw-window-handle 0.5.0", + "rayon", "rustc-hash", "thiserror", "winit", @@ -947,6 +1002,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "memoffset" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" +dependencies = [ + "autocfg", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -1059,7 +1123,7 @@ dependencies = [ "bitflags", "cfg-if", "libc", - "memoffset", + "memoffset 0.6.5", ] [[package]] @@ -1072,7 +1136,7 @@ dependencies = [ "bitflags", "cfg-if", "libc", - "memoffset", + "memoffset 0.6.5", ] [[package]] @@ -1181,6 +1245,16 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_cpus" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +dependencies = [ + "hermit-abi", + "libc", +] + [[package]] name = "num_enum" version = "0.5.9" @@ -1353,6 +1427,28 @@ dependencies = [ "raw-window-handle 0.5.0", ] +[[package]] +name = "rayon" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db3a213adf02b3bcfd2d3846bb41cb22857d131789e01df434fb7e7bc0759b7" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "356a0625f1954f730c0201cdab48611198dc6ce21f4acff55089b5a78e6e835b" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "num_cpus", +] + [[package]] name = "redox_syscall" version = "0.2.16" diff --git a/librashader-reflect/src/back/cross.rs b/librashader-reflect/src/back/cross.rs index e1a944b..9f6dbd8 100644 --- a/librashader-reflect/src/back/cross.rs +++ b/librashader-reflect/src/back/cross.rs @@ -30,7 +30,7 @@ impl FromCompilation for GLSL { compile: GlslangCompilation, ) -> Result, ShaderReflectError> { Ok(CompilerBackend { - backend: GlslReflect::try_from(compile)?, + backend: GlslReflect::try_from(&compile)?, }) } } @@ -52,7 +52,7 @@ impl FromCompilation for HLSL { compile: GlslangCompilation, ) -> Result, ShaderReflectError> { Ok(CompilerBackend { - backend: HlslReflect::try_from(compile)?, + backend: HlslReflect::try_from(&compile)?, }) } } diff --git a/librashader-reflect/src/back/dxil.rs b/librashader-reflect/src/back/dxil.rs index dd9ed5a..9ee523d 100644 --- a/librashader-reflect/src/back/dxil.rs +++ b/librashader-reflect/src/back/dxil.rs @@ -24,9 +24,9 @@ impl FromCompilation for DXIL { fn from_compilation( compile: GlslangCompilation, ) -> Result, ShaderReflectError> { - let vertex = compile.vertex.as_binary().to_vec(); - let fragment = compile.fragment.as_binary().to_vec(); - let reflect = GlslReflect::try_from(compile)?; + let reflect = GlslReflect::try_from(&compile)?; + let vertex = compile.vertex; + let fragment = compile.fragment; Ok(CompilerBackend { // we can just reuse WriteSpirV as the backend. backend: WriteSpirV { diff --git a/librashader-reflect/src/back/spirv.rs b/librashader-reflect/src/back/spirv.rs index 9f02f1a..e317499 100644 --- a/librashader-reflect/src/back/spirv.rs +++ b/librashader-reflect/src/back/spirv.rs @@ -23,9 +23,9 @@ impl FromCompilation for SPIRV { fn from_compilation( compile: GlslangCompilation, ) -> Result, ShaderReflectError> { - let vertex = compile.vertex.as_binary().to_vec(); - let fragment = compile.fragment.as_binary().to_vec(); - let reflect = GlslReflect::try_from(compile)?; + let reflect = GlslReflect::try_from(&compile)?; + let vertex = compile.vertex; + let fragment = compile.fragment; Ok(CompilerBackend { backend: WriteSpirV { reflect, diff --git a/librashader-reflect/src/front/shaderc.rs b/librashader-reflect/src/front/shaderc.rs index 01693c8..252c762 100644 --- a/librashader-reflect/src/front/shaderc.rs +++ b/librashader-reflect/src/front/shaderc.rs @@ -1,11 +1,11 @@ use crate::error::ShaderCompileError; use librashader_preprocess::ShaderSource; -use shaderc::{CompilationArtifact, CompileOptions, Limit, ShaderKind}; +use shaderc::{CompileOptions, Limit, ShaderKind}; /// A reflectable shader compilation via glslang (shaderc). pub struct GlslangCompilation { - pub(crate) vertex: CompilationArtifact, - pub(crate) fragment: CompilationArtifact, + pub(crate) vertex: Vec, + pub(crate) fragment: Vec, } impl GlslangCompilation { @@ -137,6 +137,11 @@ pub(crate) fn compile_spirv( "main", Some(&options), )?; + + // shaderc has a GIL so Send is unsafe. + let vertex = Vec::from(vertex.as_binary()); + let fragment = Vec::from(fragment.as_binary()); + Ok(GlslangCompilation { vertex, fragment }) } diff --git a/librashader-reflect/src/reflect/cross.rs b/librashader-reflect/src/reflect/cross.rs index 41afa17..cfd374b 100644 --- a/librashader-reflect/src/reflect/cross.rs +++ b/librashader-reflect/src/reflect/cross.rs @@ -7,7 +7,7 @@ use crate::reflect::semantics::{ MAX_BINDINGS_COUNT, MAX_PUSH_BUFFER_SIZE, }; use crate::reflect::{align_uniform_size, ReflectShader}; -use std::ops::Deref; +use std::ops::{Deref, DerefMut}; use spirv_cross::hlsl::ShaderModel; use spirv_cross::spirv::{Ast, Decoration, Module, Resource, ShaderResources, Type}; @@ -18,6 +18,9 @@ use crate::back::targets::{GLSL, HLSL}; use crate::back::{CompileShader, ShaderCompilerOutput}; use crate::reflect::helper::{SemanticErrorBlame, TextureData, UboData}; +// This is "probably" OK. +unsafe impl Send for CrossReflect {} + pub(crate) struct CrossReflect where T: spirv_cross::spirv::Target, @@ -110,7 +113,7 @@ impl ValidateTypeSemantics for TextureSemantics { } } -impl TryFrom for CrossReflect +impl TryFrom<&GlslangCompilation> for CrossReflect where T: spirv_cross::spirv::Target, Ast: spirv_cross::spirv::Compile, @@ -118,9 +121,9 @@ where { type Error = ShaderReflectError; - fn try_from(value: GlslangCompilation) -> Result { - let vertex_module = Module::from_words(value.vertex.as_binary()); - let fragment_module = Module::from_words(value.fragment.as_binary()); + fn try_from(value: &GlslangCompilation) -> Result { + let vertex_module = Module::from_words(&value.vertex); + let fragment_module = Module::from_words(&value.fragment); let vertex = Ast::parse(&vertex_module)?; let fragment = Ast::parse(&fragment_module)?; @@ -878,7 +881,7 @@ mod test { ); } let spirv = GlslangCompilation::compile(&result).unwrap(); - let mut reflect = CrossReflect::::try_from(spirv).unwrap(); + let mut reflect = CrossReflect::::try_from(&spirv).unwrap(); let _shader_reflection = reflect .reflect( 0, diff --git a/librashader-reflect/src/reflect/naga.rs b/librashader-reflect/src/reflect/naga.rs index 23562f8..0967e85 100644 --- a/librashader-reflect/src/reflect/naga.rs +++ b/librashader-reflect/src/reflect/naga.rs @@ -23,16 +23,16 @@ impl TryFrom for NagaReflect { } } -impl TryFrom for NagaReflect { +impl TryFrom<&GlslangCompilation> for NagaReflect { type Error = ShaderReflectError; - fn try_from(value: GlslangCompilation) -> Result { + fn try_from(value: &GlslangCompilation) -> Result { let ops = Options::default(); let vertex = - naga::front::spv::Parser::new(value.vertex.as_binary().to_vec().into_iter(), &ops) + naga::front::spv::Parser::new(value.vertex.clone().into_iter(), &ops) .parse()?; let fragment = - naga::front::spv::Parser::new(value.fragment.as_binary().to_vec().into_iter(), &ops) + naga::front::spv::Parser::new(value.fragment.clone().into_iter(), &ops) .parse()?; Ok(NagaReflect { vertex, fragment }) } diff --git a/librashader-runtime-d3d11/Cargo.toml b/librashader-runtime-d3d11/Cargo.toml index 48e58fb..9af2f6f 100644 --- a/librashader-runtime-d3d11/Cargo.toml +++ b/librashader-runtime-d3d11/Cargo.toml @@ -22,6 +22,7 @@ spirv_cross = { package = "librashader-spirv-cross", version = "0.23" } rustc-hash = "1.1.0" bytemuck = "1.12.3" +rayon = "1.6.1" [target.'cfg(windows)'.dependencies.windows] version = "0.44.0" diff --git a/librashader-runtime-d3d11/src/filter_chain.rs b/librashader-runtime-d3d11/src/filter_chain.rs index 9eab31c..4558517 100644 --- a/librashader-runtime-d3d11/src/filter_chain.rs +++ b/librashader-runtime-d3d11/src/filter_chain.rs @@ -32,13 +32,14 @@ use windows::Win32::Graphics::Direct3D11::{ D3D11_TEXTURE2D_DESC, D3D11_USAGE_DEFAULT, D3D11_USAGE_DYNAMIC, }; use windows::Win32::Graphics::Dxgi::Common::DXGI_FORMAT_R8G8B8A8_UNORM; +use rayon::prelude::*; pub struct FilterMutable { pub(crate) passes_enabled: usize, pub(crate) parameters: FxHashMap, } -type ShaderPassMeta = ShaderPassArtifact>; +type ShaderPassMeta = ShaderPassArtifact + Send>; /// A Direct3D 11 filter chain. pub struct FilterChainD3D11 { @@ -220,81 +221,85 @@ impl FilterChainD3D11 { passes: Vec, semantics: &ShaderSemantics, ) -> error::Result> { - let mut filters = Vec::new(); + // access to ID3D11Device is thread safe. + let filters: Vec> = + passes.into_par_iter() + .enumerate() + .map(|(index, (config, source, mut reflect)) | { + let reflection = reflect.reflect(index, semantics)?; + let hlsl = reflect.compile(None)?; - for (index, (config, source, mut reflect)) in passes.into_iter().enumerate() { - let reflection = reflect.reflect(index, semantics)?; - let hlsl = reflect.compile(None)?; + let vertex_dxbc = + util::d3d_compile_shader(hlsl.vertex.as_bytes(), b"main\0", b"vs_5_0\0")?; + let vs = d3d11_compile_bound_shader( + device, + &vertex_dxbc, + None, + ID3D11Device::CreateVertexShader, + )?; - let vertex_dxbc = - util::d3d_compile_shader(hlsl.vertex.as_bytes(), b"main\0", b"vs_5_0\0")?; - let vs = d3d11_compile_bound_shader( - device, - &vertex_dxbc, - None, - ID3D11Device::CreateVertexShader, - )?; + let ia_desc = DrawQuad::get_spirv_cross_vbo_desc(); + let vao = util::d3d11_create_input_layout(device, &ia_desc, &vertex_dxbc)?; - let ia_desc = DrawQuad::get_spirv_cross_vbo_desc(); - let vao = util::d3d11_create_input_layout(device, &ia_desc, &vertex_dxbc)?; + let fragment_dxbc = + util::d3d_compile_shader(hlsl.fragment.as_bytes(), b"main\0", b"ps_5_0\0")?; + let ps = d3d11_compile_bound_shader( + device, + &fragment_dxbc, + None, + ID3D11Device::CreatePixelShader, + )?; - let fragment_dxbc = - util::d3d_compile_shader(hlsl.fragment.as_bytes(), b"main\0", b"ps_5_0\0")?; - let ps = d3d11_compile_bound_shader( - device, - &fragment_dxbc, - None, - ID3D11Device::CreatePixelShader, - )?; + let ubo_cbuffer = if let Some(ubo) = &reflection.ubo && ubo.size != 0 { + let buffer = FilterChainD3D11::create_constant_buffer(device, ubo.size)?; + Some(ConstantBufferBinding { + binding: ubo.binding, + size: ubo.size, + stage_mask: ubo.stage_mask, + buffer, + }) + } else { + None + }; - let ubo_cbuffer = if let Some(ubo) = &reflection.ubo && ubo.size != 0 { - let buffer = FilterChainD3D11::create_constant_buffer(device, ubo.size)?; - Some(ConstantBufferBinding { - binding: ubo.binding, - size: ubo.size, - stage_mask: ubo.stage_mask, - buffer, - }) - } else { - None - }; + let push_cbuffer = if let Some(push) = &reflection.push_constant && push.size != 0 { + let buffer = FilterChainD3D11::create_constant_buffer(device, push.size)?; + Some(ConstantBufferBinding { + binding: if ubo_cbuffer.is_some() { 1 } else { 0 }, + size: push.size, + stage_mask: push.stage_mask, + buffer, + }) + } else { + None + }; - let push_cbuffer = if let Some(push) = &reflection.push_constant && push.size != 0 { - let buffer = FilterChainD3D11::create_constant_buffer(device, push.size)?; - Some(ConstantBufferBinding { - binding: if ubo_cbuffer.is_some() { 1 } else { 0 }, - size: push.size, - stage_mask: push.stage_mask, - buffer, - }) - } else { - None - }; + let uniform_storage = UniformStorage::new( + reflection.ubo.as_ref().map_or(0, |ubo| ubo.size as usize), + reflection + .push_constant + .as_ref() + .map_or(0, |push| push.size as usize), + ); - let uniform_storage = UniformStorage::new( - reflection.ubo.as_ref().map_or(0, |ubo| ubo.size as usize), - reflection - .push_constant - .as_ref() - .map_or(0, |push| push.size as usize), - ); + let uniform_bindings = reflection.meta.create_binding_map(|param| param.offset()); - let uniform_bindings = reflection.meta.create_binding_map(|param| param.offset()); + Ok(FilterPass { + reflection, + vertex_shader: vs, + vertex_layout: vao, + pixel_shader: ps, + uniform_bindings, + uniform_storage, + uniform_buffer: ubo_cbuffer, + push_buffer: push_cbuffer, + source, + config: config.clone(), + }) + }).collect(); - filters.push(FilterPass { - reflection, - compiled: hlsl, - vertex_shader: vs, - vertex_layout: vao, - pixel_shader: ps, - uniform_bindings, - uniform_storage, - uniform_buffer: ubo_cbuffer, - push_buffer: push_cbuffer, - source, - config: config.clone(), - }) - } + let filters: error::Result> = filters.into_iter().collect(); + let filters = filters?; Ok(filters) } diff --git a/librashader-runtime-d3d11/src/filter_pass.rs b/librashader-runtime-d3d11/src/filter_pass.rs index 3c8e634..2a9d3bf 100644 --- a/librashader-runtime-d3d11/src/filter_pass.rs +++ b/librashader-runtime-d3d11/src/filter_pass.rs @@ -34,7 +34,6 @@ pub struct ConstantBufferBinding { // slang_process.cpp 141 pub struct FilterPass { pub reflection: ShaderReflection, - pub compiled: ShaderCompilerOutput, pub vertex_shader: ID3D11VertexShader, pub vertex_layout: ID3D11InputLayout, pub pixel_shader: ID3D11PixelShader, diff --git a/librashader-runtime-d3d11/src/lib.rs b/librashader-runtime-d3d11/src/lib.rs index c525f08..5f329df 100644 --- a/librashader-runtime-d3d11/src/lib.rs +++ b/librashader-runtime-d3d11/src/lib.rs @@ -38,7 +38,7 @@ mod tests { // "../test/slang-shaders/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV.slangp", // "../test/null.slangp", - const FILTER_PATH: &str = "../test/slang-shaders/bezel/koko-aio/monitor-bloom.slangp"; + const FILTER_PATH: &str = "../test/slang-shaders/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV.slangp"; const IMAGE_PATH: &str = "../test/finalfightlong.png"; #[test] fn triangle_d3d11_args() { @@ -84,7 +84,8 @@ mod tests { force_no_mipmaps: false, }), // replace below with 'None' for the triangle - None, + // None, + Some(Image::load(IMAGE_PATH, UVDirection::TopLeft).unwrap()) ) .unwrap(); // let sample = hello_triangle_old::d3d11_hello_triangle::Sample::new( diff --git a/librashader-runtime-vk/Cargo.toml b/librashader-runtime-vk/Cargo.toml index 244118e..3a68eda 100644 --- a/librashader-runtime-vk/Cargo.toml +++ b/librashader-runtime-vk/Cargo.toml @@ -25,6 +25,8 @@ bytemuck = "1.12.3" thiserror = "1.0.37" ash = { version = "0.37.1+1.3.235", features = ["linked", "debug"] } +rayon = "1.6.1" + [dev-dependencies] num = "0.4.0" glfw = "0.49.0" diff --git a/librashader-runtime-vk/src/filter_chain.rs b/librashader-runtime-vk/src/filter_chain.rs index b348612..39c59ac 100644 --- a/librashader-runtime-vk/src/filter_chain.rs +++ b/librashader-runtime-vk/src/filter_chain.rs @@ -31,6 +31,7 @@ use std::collections::VecDeque; use std::path::Path; use std::sync::Arc; +use rayon::prelude::*; /// A Vulkan device and metadata that is required by the shader runtime. pub struct VulkanObjects { pub(crate) device: Arc, @@ -39,7 +40,7 @@ pub struct VulkanObjects { pipeline_cache: vk::PipelineCache, } -type ShaderPassMeta = ShaderPassArtifact>; +type ShaderPassMeta = ShaderPassArtifact + Send>; /// A collection of handles needed to access the Vulkan instance. #[derive(Clone)] @@ -308,69 +309,66 @@ impl FilterChainVulkan { frames_in_flight: u32, use_render_pass: bool, ) -> error::Result> { - let mut filters = Vec::new(); let frames_in_flight = std::cmp::max(1, frames_in_flight); - // initialize passes - for (index, (config, source, mut reflect)) in passes.into_iter().enumerate() { - let reflection = reflect.reflect(index, semantics)?; - let spirv_words = reflect.compile(None)?; + let filters: Vec> = passes.into_par_iter() + .enumerate() + .map(|(index, (config, source, mut reflect))| { + let reflection = reflect.reflect(index, semantics)?; + let spirv_words = reflect.compile(None)?; - let ubo_size = reflection.ubo.as_ref().map_or(0, |ubo| ubo.size as usize); - let uniform_storage = UniformStorage::new_with_storage( - RawVulkanBuffer::new( + let ubo_size = reflection.ubo.as_ref().map_or(0, |ubo| ubo.size as usize); + 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() + .map_or(0, |push| push.size as usize), + ); + + let uniform_bindings = reflection.meta + .create_binding_map(|param| param.offset()); + + let render_pass_format = if !use_render_pass { + vk::Format::UNDEFINED + } else if let Some(format) = config.get_format_override() { + format.into() + } else if source.format != ImageFormat::Unknown { + source.format.into() + } else { + ImageFormat::R8G8B8A8Unorm.into() + }; + + let graphics_pipeline = VulkanGraphicsPipeline::new( &vulkan.device, - &vulkan.memory_properties, - vk::BufferUsageFlags::UNIFORM_BUFFER, - ubo_size, - )?, - reflection - .push_constant - .as_ref() - .map_or(0, |push| push.size as usize), - ); + &vulkan.pipeline_cache, + &spirv_words, + &reflection, + frames_in_flight, + render_pass_format, + )?; - let uniform_bindings = reflection.meta.create_binding_map(|param| param.offset()); - - let render_pass_format = if !use_render_pass { - vk::Format::UNDEFINED - } else if let Some(format) = config.get_format_override() { - format.into() - } else if source.format != ImageFormat::Unknown { - source.format.into() - } else { - ImageFormat::R8G8B8A8Unorm.into() - }; - - let graphics_pipeline = VulkanGraphicsPipeline::new( - &vulkan.device, - &vulkan.pipeline_cache, - &spirv_words, - &reflection, - frames_in_flight, - render_pass_format, - )?; - - // 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, - // compiled: spirv_words, - uniform_storage, - uniform_bindings, - source, - config, - graphics_pipeline, - // ubo_ring, - frames_in_flight, - }); - } + Ok(FilterPass { + device: vulkan.device.clone(), + reflection, + // compiled: spirv_words, + uniform_storage, + uniform_bindings, + source, + config, + graphics_pipeline, + // ubo_ring, + frames_in_flight, + }) + }).collect(); + let filters: error::Result> = filters.into_iter().collect(); + let filters = filters?; Ok(filters.into_boxed_slice()) } diff --git a/librashader-runtime-vk/src/vulkan_primitives.rs b/librashader-runtime-vk/src/vulkan_primitives.rs index 71e3fc9..35a9b10 100644 --- a/librashader-runtime-vk/src/vulkan_primitives.rs +++ b/librashader-runtime-vk/src/vulkan_primitives.rs @@ -134,6 +134,10 @@ impl<'a> Drop for VulkanBufferMapHandle<'a> { } } +/// SAFETY: Creating the pointer should be safe in multithreaded contexts. +/// +/// Mutation is guarded by DerefMut +unsafe impl Send for RawVulkanBuffer {} pub struct RawVulkanBuffer { buffer: ManuallyDrop, ptr: NonNull,