rt: make runtimes thread safe and document thread safety guarantees

This commit is contained in:
chyyran 2023-02-10 01:29:49 -05:00
parent 7f17569b58
commit 512a4c0050
21 changed files with 142 additions and 88 deletions

1
Cargo.lock generated
View file

@ -940,6 +940,7 @@ dependencies = [
"librashader-reflect", "librashader-reflect",
"librashader-runtime", "librashader-runtime",
"librashader-spirv-cross", "librashader-spirv-cross",
"parking_lot",
"rayon", "rayon",
"rustc-hash", "rustc-hash",
"thiserror", "thiserror",

View file

@ -10,23 +10,21 @@ librashader (*/ˈli:brəʃeɪdɚ/*) is a preprocessor, compiler, and runtime for
![Nightly rust](https://img.shields.io/badge/rust-nightly-orange.svg) ![Nightly rust](https://img.shields.io/badge/rust-nightly-orange.svg)
## Supported Render APIs ## Supported Render APIs
librashader supports OpenGL 3, OpenGL 4.6, Vulkan, Direct3D 11, and Direct3D 12. Older versions librashader supports OpenGL 3, OpenGL 4.6, Vulkan, Direct3D 11, and Direct3D 12. Metal and WebGPU
of Direct3D and OpenGL, as well as Metal, are not supported (but pull-requests are welcome). are not currently supported (but pull-requests are welcome). librashader does not support legacy render
APIs such as older versions of OpenGL, or legacy versions of Direct3D.
| **API** | **Status** | **`librashader` feature** | | **API** | **Status** | **`librashader` feature** |
|-------------|------------|---------------------------| |-------------|------------|--------------------------|
| OpenGL 3.3+ | ✔ | `gl` | | OpenGL 3.3+ | ✔ | `gl` |
| OpenGL 4.6 | ✔ | `gl` | | OpenGL 4.6 | ✔ | `gl` |
| Vulkan | ✔ | `vk` | | Vulkan | ✔ | `vk` |
| Direct3D 11 | ✔ | `d3d11` | | Direct3D 11 | ✔ | `d3d11` |
| Direct3D 12 | ✔ | `d3d12` | | Direct3D 12 | ✔ | `d3d12` |
| OpenGL 2 | ❌ | |
| Direct3D 9 | ❌ | |
| Direct3D 10 | ❌ | |
| Metal | ❌ | | | Metal | ❌ | |
| WebGPU | ❌ | |
✔ = Render API is supported — ❌ Render API is not supported
✔ = Render API is supported — 🚧 = Support is in progress — ❌ Render API is not supported
## Usage ## Usage
librashader provides both a Rust API under the `librashader` crate, and a C API. Both APIs are first-class and fully supported. librashader provides both a Rust API under the `librashader` crate, and a C API. Both APIs are first-class and fully supported.
@ -68,9 +66,9 @@ is important to ensure that updates to librashader do not break existing consume
As of `0.1.0-rc.3`, the C ABI should be mostly stable. We reserve the right to make breaking changes before a numbered As of `0.1.0-rc.3`, the C ABI should be mostly stable. We reserve the right to make breaking changes before a numbered
release without following semantic versioning. release without following semantic versioning.
Linking against `librashader.h` directly is possible, but is not officially supported. You will need to ensure linkage Linking statically against `librashader.h` is possible, but is not officially supported. You will need to ensure
parameters are correct in order to successfully link with `librashader.lib` or `librashader.a`. The [corrosion](https://github.com/corrosion-rs/) linkage parameters are correct in order to successfully link with `librashader.lib` or `librashader.a`.
CMake package is highly recommended. The [corrosion](https://github.com/corrosion-rs/) CMake package is highly recommended.
### Examples ### Examples
@ -153,6 +151,13 @@ static GL_DEFAULT_MVP: &[f32; 16] = &[
]; ];
``` ```
### Thread safety
In general, it is **safe** to create a filter chain instance from a different thread, but drawing filter passes **must be
externally synchronized**. The exceptions to filter chain creation are in OpenGL, where creating the filter chain instance
is safe **if and only if** the thread local OpenGL context is initialized to the same context as the drawing thread, and
in Direct3D 11, where filter chain creation is thread-unsafe if the `ID3D11Device` was created with
`D3D11_CREATE_DEVICE_SINGLETHREADED`.
### Writing a librashader Runtime ### Writing a librashader Runtime
If you wish to contribute a runtime implementation not already available, see the [librashader-runtime](https://docs.rs/librashader-runtime/latest/librashader_runtime/) If you wish to contribute a runtime implementation not already available, see the [librashader-runtime](https://docs.rs/librashader-runtime/latest/librashader_runtime/)

View file

@ -831,6 +831,10 @@ libra_error_t libra_gl_filter_chain_create(libra_shader_preset_t *preset,
/// values for the model view projection matrix. /// values for the model view projection matrix.
/// - `opt` may be null, or if it is not null, must be an aligned pointer to a valid `frame_gl_opt_t` /// - `opt` may be null, or if it is not null, must be an aligned pointer to a valid `frame_gl_opt_t`
/// struct. /// struct.
/// - You must ensure that only one thread has access to `chain` before you call this function. Only one
/// thread at a time may call this function. The thread `libra_gl_filter_chain_frame` is called from
/// must have its thread-local OpenGL context initialized with the same context used to create
/// the filter chain.
libra_error_t libra_gl_filter_chain_frame(libra_gl_filter_chain_t *chain, libra_error_t libra_gl_filter_chain_frame(libra_gl_filter_chain_t *chain,
size_t frame_count, size_t frame_count,
struct libra_source_image_gl_t image, struct libra_source_image_gl_t image,
@ -925,6 +929,8 @@ libra_error_t libra_vk_filter_chain_create(struct libra_device_vk_t vulkan,
/// values for the model view projection matrix. /// values for the model view projection matrix.
/// - `opt` may be null, or if it is not null, must be an aligned pointer to a valid `frame_vk_opt_t` /// - `opt` may be null, or if it is not null, must be an aligned pointer to a valid `frame_vk_opt_t`
/// struct. /// struct.
/// - You must ensure that only one thread has access to `chain` before you call this function. Only one
/// thread at a time may call this function.
libra_error_t libra_vk_filter_chain_frame(libra_vk_filter_chain_t *chain, libra_error_t libra_vk_filter_chain_frame(libra_vk_filter_chain_t *chain,
VkCommandBuffer command_buffer, VkCommandBuffer command_buffer,
size_t frame_count, size_t frame_count,
@ -1015,6 +1021,8 @@ libra_error_t libra_d3d11_filter_chain_create(libra_shader_preset_t *preset,
/// struct. /// struct.
/// - `out` must not be null. /// - `out` must not be null.
/// - `image.handle` must not be null. /// - `image.handle` must not be null.
/// - You must ensure that only one thread has access to `chain` before you call this function. Only one
/// thread at a time may call this function.
libra_error_t libra_d3d11_filter_chain_frame(libra_d3d11_filter_chain_t *chain, libra_error_t libra_d3d11_filter_chain_frame(libra_d3d11_filter_chain_t *chain,
size_t frame_count, size_t frame_count,
struct libra_source_image_d3d11_t image, struct libra_source_image_d3d11_t image,
@ -1106,6 +1114,8 @@ libra_error_t libra_d3d12_filter_chain_create(libra_shader_preset_t *preset,
/// - `out` must be a descriptor handle to a render target view. /// - `out` must be a descriptor handle to a render target view.
/// - `image.resource` must not be null. /// - `image.resource` must not be null.
/// - `command_list` must not be null. /// - `command_list` must not be null.
/// - You must ensure that only one thread has access to `chain` before you call this function. Only one
/// thread at a time may call this function.
libra_error_t libra_d3d12_filter_chain_frame(libra_d3d12_filter_chain_t *chain, libra_error_t libra_d3d12_filter_chain_frame(libra_d3d12_filter_chain_t *chain,
const ID3D12GraphicsCommandList * command_list, const ID3D12GraphicsCommandList * command_list,
size_t frame_count, size_t frame_count,

View file

@ -51,6 +51,18 @@
//! There is a case to be made for skipping error checking for `*_filter_chain_frame` due to performance reasons, //! There is a case to be made for skipping error checking for `*_filter_chain_frame` due to performance reasons,
//! but only if you are certain that the safety invariants are upheld on each call. Failure to check for errors //! but only if you are certain that the safety invariants are upheld on each call. Failure to check for errors
//! may result in **undefined behaviour** stemming from failure to uphold safety invariants. //! may result in **undefined behaviour** stemming from failure to uphold safety invariants.
//!
//! ## Thread safety
//!
//! In general, it is **safe** to create a filter chain instance from a different thread, but drawing filter passes must be
//! synchronized externally. The exception to filter chain creation are in OpenGL, where creating the filter chain instance
//! is safe **if and only if** the thread local OpenGL context is initialized to the same context as the drawing thread, and
//! in Direct3D 11, where filter chain creation is unsafe if the `ID3D11Device` was created with
//! `D3D11_CREATE_DEVICE_SINGLETHREADED`.
//!
//! You must ensure that only thread has access to a created filter pass **before** you call `*_frame`. `*_frame` may only be
//! called from one thread at a time.
#![allow(non_camel_case_types)] #![allow(non_camel_case_types)]
#![feature(try_blocks)] #![feature(try_blocks)]
#![feature(pointer_is_aligned)] #![feature(pointer_is_aligned)]
@ -73,3 +85,6 @@ pub type LIBRASHADER_API_VERSION = usize;
/// The current version of the librashader API/ABI. /// The current version of the librashader API/ABI.
/// Pass this into `version` for config structs. /// Pass this into `version` for config structs.
pub const LIBRASHADER_CURRENT_VERSION: LIBRASHADER_API_VERSION = 0; pub const LIBRASHADER_CURRENT_VERSION: LIBRASHADER_API_VERSION = 0;
#[allow(dead_code)]
const fn assert_thread_safe<T: Send + Sync>() { }

View file

@ -7,6 +7,8 @@ use std::ffi::{c_char, CStr, CString};
use std::mem::MaybeUninit; use std::mem::MaybeUninit;
use std::ptr::NonNull; use std::ptr::NonNull;
const _: () = crate::assert_thread_safe::<ShaderPreset>();
/// A list of preset parameters. /// A list of preset parameters.
#[repr(C)] #[repr(C)]
pub struct libra_preset_param_list_t { pub struct libra_preset_param_list_t {

View file

@ -13,8 +13,8 @@ use windows::Win32::Graphics::Direct3D11::{
ID3D11Device, ID3D11RenderTargetView, ID3D11ShaderResourceView, ID3D11Device, ID3D11RenderTargetView, ID3D11ShaderResourceView,
}; };
pub use librashader::runtime::d3d11::capi::options::FilterChainOptionsD3D11; use librashader::runtime::d3d11::capi::options::FilterChainOptionsD3D11;
pub use librashader::runtime::d3d11::capi::options::FrameOptionsD3D11; use librashader::runtime::d3d11::capi::options::FrameOptionsD3D11;
use librashader::runtime::{FilterChainParameters, Size, Viewport}; use librashader::runtime::{FilterChainParameters, Size, Viewport};
use crate::LIBRASHADER_API_VERSION; use crate::LIBRASHADER_API_VERSION;
@ -142,6 +142,8 @@ extern_fn! {
/// struct. /// struct.
/// - `out` must not be null. /// - `out` must not be null.
/// - `image.handle` must not be null. /// - `image.handle` must not be null.
/// - You must ensure that only one thread has access to `chain` before you call this function. Only one
/// thread at a time may call this function.
fn libra_d3d11_filter_chain_frame( fn libra_d3d11_filter_chain_frame(
chain: *mut libra_d3d11_filter_chain_t, chain: *mut libra_d3d11_filter_chain_t,
frame_count: usize, frame_count: usize,

View file

@ -2,3 +2,4 @@
mod filter_chain; mod filter_chain;
pub use filter_chain::*; pub use filter_chain::*;
const _: () = crate::assert_thread_safe::<librashader::runtime::d3d11::FilterChain>();

View file

@ -13,8 +13,8 @@ use windows::Win32::Graphics::Direct3D12::{
}; };
use windows::Win32::Graphics::Dxgi::Common::DXGI_FORMAT; use windows::Win32::Graphics::Dxgi::Common::DXGI_FORMAT;
pub use librashader::runtime::d3d12::capi::options::FilterChainOptionsD3D12; use librashader::runtime::d3d12::capi::options::FilterChainOptionsD3D12;
pub use librashader::runtime::d3d12::capi::options::FrameOptionsD3D12; use librashader::runtime::d3d12::capi::options::FrameOptionsD3D12;
use librashader::runtime::d3d12::{D3D12InputImage, D3D12OutputView}; use librashader::runtime::d3d12::{D3D12InputImage, D3D12OutputView};
use librashader::runtime::{FilterChainParameters, Size, Viewport}; use librashader::runtime::{FilterChainParameters, Size, Viewport};
@ -159,6 +159,8 @@ extern_fn! {
/// - `out` must be a descriptor handle to a render target view. /// - `out` must be a descriptor handle to a render target view.
/// - `image.resource` must not be null. /// - `image.resource` must not be null.
/// - `command_list` must not be null. /// - `command_list` must not be null.
/// - You must ensure that only one thread has access to `chain` before you call this function. Only one
/// thread at a time may call this function.
fn libra_d3d12_filter_chain_frame( fn libra_d3d12_filter_chain_frame(
chain: *mut libra_d3d12_filter_chain_t, chain: *mut libra_d3d12_filter_chain_t,
command_list: ManuallyDrop<ID3D12GraphicsCommandList>, command_list: ManuallyDrop<ID3D12GraphicsCommandList>,

View file

@ -2,3 +2,4 @@
mod filter_chain; mod filter_chain;
pub use filter_chain::*; pub use filter_chain::*;
const _: () = crate::assert_thread_safe::<librashader::runtime::d3d12::FilterChain>();

View file

@ -10,8 +10,8 @@ use std::mem::MaybeUninit;
use std::ptr::NonNull; use std::ptr::NonNull;
use std::slice; use std::slice;
pub use librashader::runtime::gl::capi::options::FilterChainOptionsGL; use librashader::runtime::gl::capi::options::FilterChainOptionsGL;
pub use librashader::runtime::gl::capi::options::FrameOptionsGL; use librashader::runtime::gl::capi::options::FrameOptionsGL;
use librashader::runtime::FilterChainParameters; use librashader::runtime::FilterChainParameters;
use librashader::runtime::{Size, Viewport}; use librashader::runtime::{Size, Viewport};
use crate::LIBRASHADER_API_VERSION; use crate::LIBRASHADER_API_VERSION;
@ -162,6 +162,10 @@ extern_fn! {
/// values for the model view projection matrix. /// values for the model view projection matrix.
/// - `opt` may be null, or if it is not null, must be an aligned pointer to a valid `frame_gl_opt_t` /// - `opt` may be null, or if it is not null, must be an aligned pointer to a valid `frame_gl_opt_t`
/// struct. /// struct.
/// - You must ensure that only one thread has access to `chain` before you call this function. Only one
/// thread at a time may call this function. The thread `libra_gl_filter_chain_frame` is called from
/// must have its thread-local OpenGL context initialized with the same context used to create
/// the filter chain.
fn libra_gl_filter_chain_frame( fn libra_gl_filter_chain_frame(
chain: *mut libra_gl_filter_chain_t, chain: *mut libra_gl_filter_chain_t,
frame_count: usize, frame_count: usize,

View file

@ -2,3 +2,4 @@
mod filter_chain; mod filter_chain;
pub use filter_chain::*; pub use filter_chain::*;
const _: () = crate::assert_thread_safe::<librashader::runtime::gl::FilterChain>();

View file

@ -10,8 +10,8 @@ use std::mem::MaybeUninit;
use std::ptr::NonNull; use std::ptr::NonNull;
use std::slice; use std::slice;
pub use librashader::runtime::vk::capi::options::FilterChainOptionsVulkan; use librashader::runtime::vk::capi::options::FilterChainOptionsVulkan;
pub use librashader::runtime::vk::capi::options::FrameOptionsVulkan; use librashader::runtime::vk::capi::options::FrameOptionsVulkan;
use librashader::runtime::FilterChainParameters; use librashader::runtime::FilterChainParameters;
use librashader::runtime::{Size, Viewport}; use librashader::runtime::{Size, Viewport};
@ -182,6 +182,8 @@ extern_fn! {
/// values for the model view projection matrix. /// values for the model view projection matrix.
/// - `opt` may be null, or if it is not null, must be an aligned pointer to a valid `frame_vk_opt_t` /// - `opt` may be null, or if it is not null, must be an aligned pointer to a valid `frame_vk_opt_t`
/// struct. /// struct.
/// - You must ensure that only one thread has access to `chain` before you call this function. Only one
/// thread at a time may call this function.
fn libra_vk_filter_chain_frame( fn libra_vk_filter_chain_frame(
chain: *mut libra_vk_filter_chain_t, chain: *mut libra_vk_filter_chain_t,
command_buffer: vk::CommandBuffer, command_buffer: vk::CommandBuffer,

View file

@ -2,3 +2,4 @@
mod filter_chain; mod filter_chain;
pub use filter_chain::*; pub use filter_chain::*;
const _: () = crate::assert_thread_safe::<librashader::runtime::vk::FilterChain>();

View file

@ -19,6 +19,7 @@ librashader-reflect = { path = "../librashader-reflect", version = "0.1.0-rc.2",
librashader-runtime = { path = "../librashader-runtime", version = "0.1.0-rc.2" } librashader-runtime = { path = "../librashader-runtime", version = "0.1.0-rc.2" }
thiserror = "1.0.37" thiserror = "1.0.37"
spirv_cross = { package = "librashader-spirv-cross", version = "0.23" } spirv_cross = { package = "librashader-spirv-cross", version = "0.23" }
parking_lot = "0.12.1"
rustc-hash = "1.1.0" rustc-hash = "1.1.0"
bytemuck = { version = "1.12.3", features = ["derive"] } bytemuck = { version = "1.12.3", features = ["derive"] }

View file

@ -100,8 +100,10 @@ impl D3D12Buffer {
/// SAFETY: Creating the pointer should be safe in multithreaded contexts. /// SAFETY: Creating the pointer should be safe in multithreaded contexts.
/// ///
/// Mutation is guarded by DerefMut<Target=[u8]> /// Mutation is guarded by DerefMut<Target=[u8]>, so exclusive access is guaranteed.
/// We do not ever leak the pointer to C.
unsafe impl Send for RawD3D12Buffer {} unsafe impl Send for RawD3D12Buffer {}
unsafe impl Sync for RawD3D12Buffer {}
pub struct RawD3D12Buffer { pub struct RawD3D12Buffer {
buffer: ManuallyDrop<D3D12Buffer>, buffer: ManuallyDrop<D3D12Buffer>,
ptr: NonNull<c_void>, ptr: NonNull<c_void>,

View file

@ -1,10 +1,10 @@
use crate::error; use crate::error;
use bitvec::bitvec; use bitvec::bitvec;
use bitvec::boxed::BitBox; use bitvec::boxed::BitBox;
use std::cell::RefCell; use parking_lot::RwLock;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::ops::Deref; use std::ops::Deref;
use std::rc::Rc; use std::sync::Arc;
use crate::error::FilterChainError; use crate::error::FilterChainError;
use windows::Win32::Graphics::Direct3D12::{ use windows::Win32::Graphics::Direct3D12::{
@ -98,12 +98,12 @@ impl const D3D12HeapType for SamplerWorkHeap {
} }
} }
pub type D3D12DescriptorHeapSlot<T> = Rc<D3D12DescriptorHeapSlotInner<T>>; pub type D3D12DescriptorHeapSlot<T> = Arc<D3D12DescriptorHeapSlotInner<T>>;
pub struct D3D12DescriptorHeapSlotInner<T> { pub struct D3D12DescriptorHeapSlotInner<T> {
cpu_handle: D3D12_CPU_DESCRIPTOR_HANDLE, cpu_handle: D3D12_CPU_DESCRIPTOR_HANDLE,
gpu_handle: Option<D3D12_GPU_DESCRIPTOR_HANDLE>, gpu_handle: Option<D3D12_GPU_DESCRIPTOR_HANDLE>,
heap: Rc<RefCell<D3D12DescriptorHeapInner>>, heap: Arc<RwLock<D3D12DescriptorHeapInner>>,
slot: usize, slot: usize,
_pd: PhantomData<T>, _pd: PhantomData<T>,
} }
@ -117,7 +117,7 @@ impl<T> D3D12DescriptorHeapSlotInner<T> {
/// unsafe because type must match /// unsafe because type must match
pub unsafe fn copy_descriptor(&self, source: D3D12_CPU_DESCRIPTOR_HANDLE) { pub unsafe fn copy_descriptor(&self, source: D3D12_CPU_DESCRIPTOR_HANDLE) {
unsafe { unsafe {
let heap = self.heap.deref().borrow(); let heap = self.heap.deref().read();
heap.device heap.device
.CopyDescriptorsSimple(1, self.cpu_handle, source, heap.ty) .CopyDescriptorsSimple(1, self.cpu_handle, source, heap.ty)
@ -142,7 +142,7 @@ impl<T: D3D12ShaderVisibleHeapType> AsRef<D3D12_GPU_DESCRIPTOR_HANDLE>
impl<T: D3D12ShaderVisibleHeapType> From<&D3D12DescriptorHeap<T>> for ID3D12DescriptorHeap { impl<T: D3D12ShaderVisibleHeapType> From<&D3D12DescriptorHeap<T>> for ID3D12DescriptorHeap {
fn from(value: &D3D12DescriptorHeap<T>) -> Self { fn from(value: &D3D12DescriptorHeap<T>) -> Self {
value.0.borrow().heap.clone() value.0.read().heap.clone()
} }
} }
@ -159,7 +159,7 @@ struct D3D12DescriptorHeapInner {
map: BitBox, map: BitBox,
} }
pub struct D3D12DescriptorHeap<T>(Rc<RefCell<D3D12DescriptorHeapInner>>, PhantomData<T>); pub struct D3D12DescriptorHeap<T>(Arc<RwLock<D3D12DescriptorHeapInner>>, PhantomData<T>);
impl<T: D3D12HeapType> D3D12DescriptorHeap<T> { impl<T: D3D12HeapType> D3D12DescriptorHeap<T> {
pub fn new(device: &ID3D12Device, size: usize) -> error::Result<D3D12DescriptorHeap<T>> { pub fn new(device: &ID3D12Device, size: usize) -> error::Result<D3D12DescriptorHeap<T>> {
@ -171,7 +171,7 @@ impl<T: D3D12HeapType> D3D12DescriptorHeap<T> {
impl<T> D3D12DescriptorHeap<T> { impl<T> D3D12DescriptorHeap<T> {
/// Gets a cloned handle to the inner heap /// Gets a cloned handle to the inner heap
pub fn handle(&self) -> ID3D12DescriptorHeap { pub fn handle(&self) -> ID3D12DescriptorHeap {
let inner = self.0.borrow(); let inner = self.0.read();
inner.heap.clone() inner.heap.clone()
} }
@ -190,7 +190,7 @@ impl<T> D3D12DescriptorHeap<T> {
}; };
Ok(D3D12DescriptorHeap( Ok(D3D12DescriptorHeap(
Rc::new(RefCell::new(D3D12DescriptorHeapInner { Arc::new(RwLock::new(D3D12DescriptorHeapInner {
device: device.clone(), device: device.clone(),
heap, heap,
ty: desc.Type, ty: desc.Type,
@ -224,12 +224,12 @@ impl<T> D3D12DescriptorHeap<T> {
) { ) {
// has to be called right after creation. // has to be called right after creation.
assert_eq!( assert_eq!(
Rc::strong_count(&self.0), Arc::strong_count(&self.0),
1, 1,
"D3D12DescriptorHeap::suballocate can only be callled immediately after creation." "D3D12DescriptorHeap::suballocate can only be callled immediately after creation."
); );
let inner = Rc::try_unwrap(self.0) let inner = Arc::try_unwrap(self.0)
.expect("[d3d12] undefined behaviour to suballocate a descriptor heap with live descriptors.") .expect("[d3d12] undefined behaviour to suballocate a descriptor heap with live descriptors.")
.into_inner(); .into_inner();
@ -301,11 +301,11 @@ impl<T> D3D12DescriptorHeap<T> {
heaps heaps
.into_iter() .into_iter()
.map(|inner| { .map(|inner| {
D3D12DescriptorHeap(Rc::new(RefCell::new(inner)), PhantomData::default()) D3D12DescriptorHeap(Arc::new(RwLock::new(inner)), PhantomData::default())
}) })
.collect(), .collect(),
reserved_heap.map(|inner| { reserved_heap.map(|inner| {
D3D12DescriptorHeap(Rc::new(RefCell::new(inner)), PhantomData::default()) D3D12DescriptorHeap(Arc::new(RwLock::new(inner)), PhantomData::default())
}), }),
inner.heap, inner.heap,
) )
@ -314,7 +314,7 @@ impl<T> D3D12DescriptorHeap<T> {
pub fn alloc_slot(&mut self) -> error::Result<D3D12DescriptorHeapSlot<T>> { pub fn alloc_slot(&mut self) -> error::Result<D3D12DescriptorHeapSlot<T>> {
let mut handle = D3D12_CPU_DESCRIPTOR_HANDLE { ptr: 0 }; let mut handle = D3D12_CPU_DESCRIPTOR_HANDLE { ptr: 0 };
let mut inner = self.0.borrow_mut(); let mut inner = self.0.write();
for i in inner.start..inner.num_descriptors { for i in inner.start..inner.num_descriptors {
if !inner.map[i] { if !inner.map[i] {
inner.map.set(i, true); inner.map.set(i, true);
@ -327,10 +327,10 @@ impl<T> D3D12DescriptorHeap<T> {
ptr: (handle.ptr as u64 - inner.cpu_start.ptr as u64) + gpu_start.ptr, ptr: (handle.ptr as u64 - inner.cpu_start.ptr as u64) + gpu_start.ptr,
}); });
return Ok(Rc::new(D3D12DescriptorHeapSlotInner { return Ok(Arc::new(D3D12DescriptorHeapSlotInner {
cpu_handle: handle, cpu_handle: handle,
slot: i, slot: i,
heap: Rc::clone(&self.0), heap: Arc::clone(&self.0),
gpu_handle, gpu_handle,
_pd: Default::default(), _pd: Default::default(),
})); }));
@ -352,7 +352,7 @@ impl<T> D3D12DescriptorHeap<T> {
impl<T> Drop for D3D12DescriptorHeapSlotInner<T> { impl<T> Drop for D3D12DescriptorHeapSlotInner<T> {
fn drop(&mut self) { fn drop(&mut self) {
let mut inner = self.heap.borrow_mut(); let mut inner = self.heap.write();
inner.map.set(self.slot, false); inner.map.set(self.slot, false);
if inner.start > self.slot { if inner.start > self.slot {
inner.start = self.slot inner.start = self.slot

View file

@ -403,6 +403,8 @@ impl FilterChainD3D12 {
let filters: Vec<error::Result<_>> = passes.into_par_iter() let filters: Vec<error::Result<_>> = passes.into_par_iter()
.zip(hlsl_passes) .zip(hlsl_passes)
.zip(work_heaps)
.zip(sampler_work_heaps)
.enumerate() .enumerate()
.map_init( .map_init(
|| { || {
@ -411,8 +413,8 @@ impl FilterChainD3D12 {
let compiler: IDxcCompiler = unsafe { DxcCreateInstance(&CLSID_DxcCompiler)? }; let compiler: IDxcCompiler = unsafe { DxcCreateInstance(&CLSID_DxcCompiler)? };
Ok::<_, FilterChainError>((validator, library, compiler)) Ok::<_, FilterChainError>((validator, library, compiler))
}, },
|dxc, (index, ((config, source, mut dxil), |dxc, (index, ((((config, source, mut dxil),
(_, _, mut hlsl)))| { (_, _, mut hlsl)), mut texture_heap), mut sampler_heap))| {
let Ok((validator, library, compiler)) = dxc else { let Ok((validator, library, compiler)) = dxc else {
return Err(FilterChainError::Direct3DOperationError("Could not initialize DXC for thread")); return Err(FilterChainError::Direct3DOperationError("Could not initialize DXC for thread"));
}; };
@ -472,48 +474,55 @@ impl FilterChainD3D12 {
let uniform_bindings = reflection.meta.create_binding_map(|param| param.offset()); let uniform_bindings = reflection.meta.create_binding_map(|param| param.offset());
Ok((reflection,
uniform_bindings,
uniform_storage,
graphics_pipeline,
config,
source))
}).collect();
let filters: error::Result<Vec<_>> = filters.into_iter().collect();
let filters = filters?;
// Need to take care of the heaps in a single thread because [;16] is not sized..?
let filters: Vec<error::Result<FilterPass>> = filters
.into_iter()
.zip(work_heaps)
.zip(sampler_work_heaps)
.map(
|(
(
(reflection, uniform_bindings, uniform_storage, pipeline, config, source),
mut texture_heap,
),
mut sampler_heap,
)| {
let texture_heap = texture_heap.alloc_range()?; let texture_heap = texture_heap.alloc_range()?;
let sampler_heap = sampler_heap.alloc_range()?; let sampler_heap = sampler_heap.alloc_range()?;
Ok(FilterPass { Ok(FilterPass {
reflection, reflection,
uniform_bindings, uniform_bindings,
uniform_storage, uniform_storage,
pipeline, pipeline: graphics_pipeline,
config, config,
texture_heap, texture_heap,
sampler_heap, sampler_heap,
source, source,
}) })
},
) }).collect();
.collect();
let filters: error::Result<Vec<_>> = filters.into_iter().collect(); let filters: error::Result<Vec<_>> = filters.into_iter().collect();
let filters = filters?; let filters = filters?;
//
// // Need to take care of the heaps in a single thread because [;16] is not sized..?
// let filters: Vec<error::Result<FilterPass>> = filters
// .into_iter()
// .zip(work_heaps)
// .zip(sampler_work_heaps)
// .map(
// |(
// (
// (reflection, uniform_bindings, uniform_storage, pipeline, config, source),
// mut texture_heap,
// ),
// mut sampler_heap,
// )| {
// let texture_heap = texture_heap.alloc_range()?;
// let sampler_heap = sampler_heap.alloc_range()?;
// Ok(FilterPass {
// reflection,
// uniform_bindings,
// uniform_storage,
// pipeline,
// config,
// texture_heap,
// sampler_heap,
// source,
// })
// },
// )
// .collect();
// let filters: error::Result<Vec<_>> = filters.into_iter().collect();
// let filters = filters?;
// Panic SAFETY: mipmap_heap is always 1024 descriptors. // Panic SAFETY: mipmap_heap is always 1024 descriptors.
Ok(( Ok((

View file

@ -261,7 +261,6 @@ impl<T: GLInterface> FilterChainImpl<T> {
filters.push(FilterPass { filters.push(FilterPass {
reflection, reflection,
compiled: glsl,
program, program,
ubo_location, ubo_location,
ubo_ring, ubo_ring,

View file

@ -1,6 +1,4 @@
use gl::types::{GLint, GLsizei, GLuint}; use gl::types::{GLint, GLsizei, GLuint};
use librashader_reflect::back::cross::CrossGlslContext;
use librashader_reflect::back::ShaderCompilerOutput;
use librashader_reflect::reflect::ShaderReflection; use librashader_reflect::reflect::ShaderReflection;
use librashader_common::{ImageFormat, Size, Viewport}; use librashader_common::{ImageFormat, Size, Viewport};
@ -33,7 +31,6 @@ impl UniformOffset {
pub struct FilterPass<T: GLInterface> { pub struct FilterPass<T: GLInterface> {
pub reflection: ShaderReflection, pub reflection: ShaderReflection,
pub compiled: ShaderCompilerOutput<String, CrossGlslContext>,
pub program: GLuint, pub program: GLuint,
pub ubo_location: UniformLocation<GLuint>, pub ubo_location: UniformLocation<GLuint>,
pub ubo_ring: Option<T::UboRing>, pub ubo_ring: Option<T::UboRing>,

View file

@ -130,8 +130,10 @@ impl Drop for VulkanBuffer {
/// SAFETY: Creating the pointer should be safe in multithreaded contexts. /// SAFETY: Creating the pointer should be safe in multithreaded contexts.
/// ///
/// Mutation is guarded by DerefMut<Target=[u8]> /// Mutation is guarded by DerefMut<Target=[u8]>, so exclusive access is guaranteed.
/// We do not ever leak the pointer to C.
unsafe impl Send for RawVulkanBuffer {} unsafe impl Send for RawVulkanBuffer {}
unsafe impl Sync for RawVulkanBuffer {}
pub struct RawVulkanBuffer { pub struct RawVulkanBuffer {
buffer: ManuallyDrop<VulkanBuffer>, buffer: ManuallyDrop<VulkanBuffer>,
ptr: NonNull<c_void>, ptr: NonNull<c_void>,

View file

@ -31,11 +31,8 @@
//! | Vulkan | ✔ | `vk` | //! | Vulkan | ✔ | `vk` |
//! | Direct3D 11 | ✔ | `d3d11` | //! | Direct3D 11 | ✔ | `d3d11` |
//! | Direct3D 12 | ✔ | `d3d12` | //! | Direct3D 12 | ✔ | `d3d12` |
//! | OpenGL 2 | ❌ | |
//! | Direct3D 9 | ❌ | |
//! | Direct3D 10 | ❌ | |
//! | Metal | ❌ | | //! | Metal | ❌ | |
//! //! | WebGPU | ❌ | |
//! ## C API //! ## C API
//! For documentation on the librashader C API, see [librashader-capi](https://docs.rs/librashader-capi/latest/librashader_capi/), //! For documentation on the librashader C API, see [librashader-capi](https://docs.rs/librashader-capi/latest/librashader_capi/),
//! or [`librashader.h`](https://github.com/SnowflakePowered/librashader/blob/master/include/librashader.h). //! or [`librashader.h`](https://github.com/SnowflakePowered/librashader/blob/master/include/librashader.h).