-
Notifications
You must be signed in to change notification settings - Fork 51
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for presser
#138
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This adds optional support for
presser
under thepresser
feature.
Thanks! Can you describe in the description/commit, in a single line, what presser
provides for users and why it should be used in gpu-allocator
, without having them click through to the crate?
Depending on how this pans out we might want to land https://github.com/Traverse-Research/gpu-allocator/compare/uninit at some point or remove the currently-UB API altogether.
EDIT: One flaw here is that the API requires mapped memory to be initialized with &[u8]
, which presser
points out to be unsafe when having to transmute some repr(C)
struct to such a slice while containing padding. Perhaps we can implement these initialization functions on top of presser
too?
Doing this is quite straightforward for Vulkan, but there didn't seem to be an equivalent for the d3d12
Allocation
type and I'm not familiar enough with that api to say how that should be implemented
IIRC we have a d3d12::ID3D12Resource::Map()
call in our custom implementation. I'm not sure why this isn't provided by gpu-allocator
since this is an explicit (vkMapMemory()
) operation on Vulkan as well... @manon-traverse any idea, perhaps worth to add in a separate PR?
I could implement it if one told me how to acquire a mapped base pointer for some allocation...
gpu-allocator/examples/d3d12-visualization/imgui_renderer.rs
Lines 453 to 456 in 8844887
upload_buffer | |
.as_ref() | |
.unwrap() | |
.Map(0, std::ptr::null(), &mut mapped_ptr); |
(But in a separate, preliminary PR, if we end up going this route 😉)
Yeah so when looking through the d3d12 visualizer example, it seems like the main issue is that in order to get a mapped pointer you have to first create the actual The |
Co-authored-by: Marijn Suijten <[email protected]>
Right, |
Interesting. I'm sorta unconvinced that abstracting this is much of a benefit because it's so easy to make an initialized buffer uninit again by copying data that has padding bytes into it, as you mention. I'm also not sure exactly how that works in terms of whether data the gpu would write would make padding bytes uninit (I think it won't since it's FFI so compiler has to be conservative) for GPU-to-CPU. I think that case of a readback buffer is probably the most compelling use case for actually tracking the initialization state of the memory as a whole since that would allow actually-safe access as a And yeah, I think using presser to implement the initialization api would be a great fit, and I'm in favor of eventually removing the |
Not necessarily, it could require the user to do |
Meant in the theoretical case that we extend |
Ah right, makes sense :) |
Agreed. I'm inclined to just make
This is especially tricky since yes, you typically want to consume the data in some |
As discussed long ago, and recently in #138, it is undefined behaviour to create or transmute to `&[u8]` when the underlying data is possibly uninit. This also holds true for transmuting arbitrary `T: Copy` structures to `&[u8]` where eventual padding bytes are considered uninitialized, hence invalid for `u8`. Instead of coming up with a massive safety API that distinguishes between uninitialized and initialized buffers - which turn out to be really easy to invalidate by copying structures with padding bytes - place the onus on the user to keep track of initialization status by only ever providing mapped slices in an `unsafe` context. Users are expected to initialize the buffer using `ptr::copy(_nonoverlapping)()` when used from a CPU context instead of calling `.mapped_mut_slice()`, or switch to the new [presser] API from #138. [presser]: https://crates.io/crates/presser
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm very glad with this contribution since it's alwasy bothered me a little bit that we're effectively having to unsafe transmute our data to get it into gpu-allocator, however I have a few remarks that I'd like to share;
- I think this is important enough to require feature parity with Dx12, it doesn't have to appear in this PR, but I will block the release of a new
gpu-allocator
version until we've landed Dx12 support as well. - I would like us to come up with a plan so we can start deprecating the old way of doing things and slowly move towards having
presser
be the default API here, so I'm not sure if we should hide it behind a feature toggle in the first place.
Sounds good to me @Jasper-Bekkers! I'll clean this up a bit more based on that plan and can start experimenting with a dx12 api soon. |
Addressed Jasper's feedback and updated the original PR description with the new direction :) |
... github is being dumb and not letting me re-request from both of you at the same time I guess lol. |
/// | ||
/// This type should be acquired by calling [`Allocation::try_as_mapped_slab`]. | ||
pub struct MappedAllocationSlab<'a> { | ||
_borrowed_alloc: &'a mut Allocation, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we don't actually use _borrowed_alloc
then we can hold a PhantomData<&'a mut Allocation>
. It will give us the same ownership semantics without actually using any memory.
As discussed long ago, and recently in #138, it is undefined behaviour to create or transmute to `&[u8]` when the underlying data is possibly uninit. This also holds true for transmuting arbitrary `T: Copy` structures to `&[u8]` where eventual padding bytes are considered uninitialized, hence invalid for `u8`. Instead of coming up with a massive safety API that distinguishes between uninitialized and initialized buffers - which turn out to be really easy to invalidate by copying structures with padding bytes - place the onus on the user to keep track of initialization status by only ever providing mapped slices in an `unsafe` context. Users are expected to initialize the buffer using `ptr::copy(_nonoverlapping)()` when used from a CPU context instead of calling `.mapped_mut_slice()`, or switch to the new [presser] API from #138. [presser]: https://crates.io/crates/presser
|
For CPU-to-GPU staging & update buffers, one must copy data into them. This currently requires unsafe raw pointer copies or using an unsound byte-slice interface.
presser
solves this by providing helper functions for safely copying data into raw allocated buffers while validating layout requirements and not creating invalid references to uninitialised memory.Implementation
This PR adds support for
presser
in two ways, and for now only for Vulkan.Allocation
implementspresser::Slab
directly, meaning you can directly pass it into presser's helper functions. However, because not allAllocation
s are validSlab
s, if you attempt to use a non-mapped or too-large allocation as aSlab
, you'll cause a panic.Slab
in some way other than panicking,Allocation::try_as_mapped_slab
will optionally return aMappedAllocationSlab
which pre-checks its conditions and so infallibly implementsSlab
upon succeeding.The advantage of implementing
Slab
onAllocation
directly, in addition to the added level of convenience it provides, is that once EmbarkStudios/presser#5 lands and we upgrade to that version here, you'll be able to share an&Allocator
and use theread_X
helpers frompresser
from multiple places, rather than requiring&mut Allocator
everywhere.Future path to adoption
Allocation
type. I think this likely will look like adding amapped_ptr: Option<NonNull<c_void>>
field much like thevulkan::Allocation
has, and then apub fn map(&mut self, resource: *mut ID3D12Resource)
which would fill in that field. But, that's for another PR's discussion...gpu-allocator
release just with the copy apis.gpu-allocator
to a version0.4
which includes those changesgpu-allocator
(minor) release which adds#[deprecated]
attributes tomapped_slice[_mut]
, documentation detailing why they've been deprecated, and linking to an upgrade guide in some release notes or something (or just a document in the repo).presser
is actually replacing all use cases, do agpu-allocator
major release which actually removes the#[deprecated]
methods and links to the migration guide again in the changelog/release notes.