mirror of
https://github.com/vulkano-rs/vulkano.git
synced 2024-10-30 05:52:44 +00:00
* Merged `ImmutableBuffer` into `DeviceLocalBuffer` #1934 * Updated documentation
This commit is contained in:
parent
f74cd9f7bd
commit
77e59002de
@ -1,202 +0,0 @@
|
||||
// Copyright (c) 2020 The vulkano developers
|
||||
// Licensed under the Apache License, Version 2.0
|
||||
// <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
|
||||
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
|
||||
// at your option. All files in the project carrying such
|
||||
// notice may not be copied, modified, or distributed except
|
||||
// according to those terms.
|
||||
|
||||
// This example demonstrates how to initialize immutable buffers.
|
||||
|
||||
use vulkano::{
|
||||
buffer::{BufferUsage, CpuAccessibleBuffer, ImmutableBuffer},
|
||||
command_buffer::{AutoCommandBufferBuilder, CommandBufferUsage},
|
||||
descriptor_set::{PersistentDescriptorSet, WriteDescriptorSet},
|
||||
device::{
|
||||
physical::{PhysicalDevice, PhysicalDeviceType},
|
||||
Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo,
|
||||
},
|
||||
instance::{Instance, InstanceCreateInfo},
|
||||
pipeline::{ComputePipeline, Pipeline, PipelineBindPoint},
|
||||
sync::{self, GpuFuture},
|
||||
VulkanLibrary,
|
||||
};
|
||||
|
||||
fn main() {
|
||||
// The most part of this example is exactly the same as `basic-compute-shader`. You should read the
|
||||
// `basic-compute-shader` example if you haven't done so yet.
|
||||
|
||||
let library = VulkanLibrary::new().unwrap();
|
||||
let instance = Instance::new(
|
||||
library,
|
||||
InstanceCreateInfo {
|
||||
// Enable enumerating devices that use non-conformant vulkan implementations. (ex. MoltenVK)
|
||||
enumerate_portability: true,
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let device_extensions = DeviceExtensions {
|
||||
khr_storage_buffer_storage_class: true,
|
||||
..DeviceExtensions::none()
|
||||
};
|
||||
let (physical_device, queue_family) = PhysicalDevice::enumerate(&instance)
|
||||
.filter(|&p| p.supported_extensions().is_superset_of(&device_extensions))
|
||||
.filter_map(|p| {
|
||||
p.queue_families()
|
||||
.find(|&q| q.supports_compute())
|
||||
.map(|q| (p, q))
|
||||
})
|
||||
.min_by_key(|(p, _)| match p.properties().device_type {
|
||||
PhysicalDeviceType::DiscreteGpu => 0,
|
||||
PhysicalDeviceType::IntegratedGpu => 1,
|
||||
PhysicalDeviceType::VirtualGpu => 2,
|
||||
PhysicalDeviceType::Cpu => 3,
|
||||
PhysicalDeviceType::Other => 4,
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
println!(
|
||||
"Using device: {} (type: {:?})",
|
||||
physical_device.properties().device_name,
|
||||
physical_device.properties().device_type
|
||||
);
|
||||
|
||||
let (device, mut queues) = Device::new(
|
||||
physical_device,
|
||||
DeviceCreateInfo {
|
||||
enabled_extensions: device_extensions,
|
||||
queue_create_infos: vec![QueueCreateInfo::family(queue_family)],
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let queue = queues.next().unwrap();
|
||||
|
||||
let pipeline = {
|
||||
mod cs {
|
||||
vulkano_shaders::shader! {
|
||||
ty: "compute",
|
||||
src: "
|
||||
#version 450
|
||||
|
||||
layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in;
|
||||
|
||||
layout(set = 0, binding = 0) restrict buffer Data {
|
||||
uint data[];
|
||||
} data;
|
||||
|
||||
layout(set = 0, binding = 1) readonly restrict buffer ImmutableData {
|
||||
uint data;
|
||||
} immutable_data;
|
||||
|
||||
void main() {
|
||||
uint idx = gl_GlobalInvocationID.x;
|
||||
data.data[idx] *= 12;
|
||||
data.data[idx] += immutable_data.data;
|
||||
}"
|
||||
}
|
||||
}
|
||||
let shader = cs::load(device.clone()).unwrap();
|
||||
ComputePipeline::new(
|
||||
device.clone(),
|
||||
shader.entry_point("main").unwrap(),
|
||||
&(),
|
||||
None,
|
||||
|_| {},
|
||||
)
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
let data_buffer = {
|
||||
let data_iter = (0..65536u32).map(|n| n);
|
||||
CpuAccessibleBuffer::from_iter(device.clone(), BufferUsage::all(), false, data_iter)
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
// Create immutable buffer and initialize it
|
||||
let immutable_data_buffer = {
|
||||
// uninitialized(), uninitialized_array() and raw() return two things: the buffer,
|
||||
// and a special access that should be used for the initial upload to the buffer.
|
||||
let (immutable_data_buffer, immutable_data_buffer_init) = unsafe {
|
||||
ImmutableBuffer::<u32>::uninitialized(device.clone(), BufferUsage::all()).unwrap()
|
||||
};
|
||||
|
||||
// Build command buffer which initialize our buffer.
|
||||
let mut builder = AutoCommandBufferBuilder::primary(
|
||||
device.clone(),
|
||||
queue.family(),
|
||||
CommandBufferUsage::OneTimeSubmit,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Initializing a immutable buffer is done by coping data to
|
||||
// ImmutableBufferInitialization which is returned by a function we use to create buffer.
|
||||
// We can use copy_buffer(), fill_buffer() and some other functions that copies data to
|
||||
// buffer also.
|
||||
builder
|
||||
.update_buffer(&3u32, immutable_data_buffer_init, 0)
|
||||
.unwrap();
|
||||
|
||||
let command_buffer = builder.build().unwrap();
|
||||
|
||||
let future = sync::now(device.clone())
|
||||
.then_execute(queue.clone(), command_buffer)
|
||||
.unwrap()
|
||||
.then_signal_fence_and_flush()
|
||||
.unwrap();
|
||||
|
||||
future.wait(None).unwrap();
|
||||
|
||||
// Once a buffer is initialized, we no longer need ImmutableBufferInitialization.
|
||||
// So we return only the buffer.
|
||||
immutable_data_buffer
|
||||
};
|
||||
|
||||
let layout = pipeline.layout().set_layouts().get(0).unwrap();
|
||||
let set = PersistentDescriptorSet::new(
|
||||
layout.clone(),
|
||||
[
|
||||
WriteDescriptorSet::buffer(0, data_buffer.clone()),
|
||||
// Now you can just add immutable buffer like other buffers.
|
||||
WriteDescriptorSet::buffer(1, immutable_data_buffer.clone()),
|
||||
],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let mut builder = AutoCommandBufferBuilder::primary(
|
||||
device.clone(),
|
||||
queue.family(),
|
||||
CommandBufferUsage::OneTimeSubmit,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
builder
|
||||
.bind_pipeline_compute(pipeline.clone())
|
||||
.bind_descriptor_sets(
|
||||
PipelineBindPoint::Compute,
|
||||
pipeline.layout().clone(),
|
||||
0,
|
||||
set.clone(),
|
||||
)
|
||||
.dispatch([1024, 1, 1])
|
||||
.unwrap();
|
||||
|
||||
let command_buffer = builder.build().unwrap();
|
||||
|
||||
let future = sync::now(device.clone())
|
||||
.then_execute(queue.clone(), command_buffer)
|
||||
.unwrap()
|
||||
.then_signal_fence_and_flush()
|
||||
.unwrap();
|
||||
|
||||
future.wait(None).unwrap();
|
||||
|
||||
let data_buffer_content = data_buffer.read().unwrap();
|
||||
for n in 0..65536u32 {
|
||||
assert_eq!(data_buffer_content[n as usize], n * 12 + 3);
|
||||
}
|
||||
}
|
@ -17,10 +17,14 @@
|
||||
use super::{
|
||||
sys::{UnsafeBuffer, UnsafeBufferCreateInfo},
|
||||
BufferAccess, BufferAccessObject, BufferContents, BufferCreationError, BufferInner,
|
||||
BufferUsage, TypedBufferAccess,
|
||||
BufferUsage, CpuAccessibleBuffer, TypedBufferAccess,
|
||||
};
|
||||
use crate::{
|
||||
device::{physical::QueueFamily, Device, DeviceOwned},
|
||||
command_buffer::{
|
||||
AutoCommandBufferBuilder, CommandBufferBeginError, CommandBufferExecFuture,
|
||||
CommandBufferUsage, CopyBufferInfo, PrimaryAutoCommandBuffer, PrimaryCommandBuffer,
|
||||
},
|
||||
device::{physical::QueueFamily, Device, DeviceOwned, Queue},
|
||||
memory::{
|
||||
pool::{
|
||||
alloc_dedicated_with_exportable_fd, AllocFromRequirementsFilter, AllocLayout,
|
||||
@ -29,11 +33,13 @@ use crate::{
|
||||
DedicatedAllocation, DeviceMemoryAllocationError, DeviceMemoryExportError,
|
||||
ExternalMemoryHandleType, MemoryPool, MemoryRequirements,
|
||||
},
|
||||
sync::Sharing,
|
||||
sync::{NowFuture, Sharing},
|
||||
DeviceSize,
|
||||
};
|
||||
use core::fmt;
|
||||
use smallvec::SmallVec;
|
||||
use std::{
|
||||
error,
|
||||
fs::File,
|
||||
hash::{Hash, Hasher},
|
||||
marker::PhantomData,
|
||||
@ -164,6 +170,133 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: make this prettier
|
||||
type DeviceLocalBufferFromBufferFuture =
|
||||
CommandBufferExecFuture<NowFuture, PrimaryAutoCommandBuffer>;
|
||||
|
||||
impl<T> DeviceLocalBuffer<T>
|
||||
where
|
||||
T: BufferContents + ?Sized,
|
||||
{
|
||||
/// Builds a `DeviceLocalBuffer` that copies its data from another buffer.
|
||||
///
|
||||
/// This function returns two objects: the newly-created buffer, and a future representing
|
||||
/// the initial upload operation. In order to be allowed to use the `DeviceLocalBuffer`, you must
|
||||
/// either submit your operation after this future, or execute this future and wait for it to
|
||||
/// be finished before submitting your own operation.
|
||||
pub fn from_buffer<B>(
|
||||
source: Arc<B>,
|
||||
usage: BufferUsage,
|
||||
queue: Arc<Queue>,
|
||||
) -> Result<
|
||||
(Arc<DeviceLocalBuffer<T>>, DeviceLocalBufferFromBufferFuture),
|
||||
DeviceLocalBufferCreationError,
|
||||
>
|
||||
where
|
||||
B: TypedBufferAccess<Content = T> + 'static,
|
||||
{
|
||||
unsafe {
|
||||
// We automatically set `transfer_dst` to true in order to avoid annoying errors.
|
||||
let actual_usage = BufferUsage {
|
||||
transfer_dst: true,
|
||||
..usage
|
||||
};
|
||||
|
||||
let buffer = DeviceLocalBuffer::raw(
|
||||
source.device().clone(),
|
||||
source.size(),
|
||||
actual_usage,
|
||||
source.device().active_queue_families(),
|
||||
)?;
|
||||
|
||||
let mut cbb = AutoCommandBufferBuilder::primary(
|
||||
source.device().clone(),
|
||||
queue.family(),
|
||||
CommandBufferUsage::MultipleSubmit,
|
||||
)?;
|
||||
cbb.copy_buffer(CopyBufferInfo::buffers(source, buffer.clone()))
|
||||
.unwrap(); // TODO: return error?
|
||||
let cb = cbb.build().unwrap(); // TODO: return OomError
|
||||
|
||||
let future = match cb.execute(queue) {
|
||||
Ok(f) => f,
|
||||
Err(_) => unreachable!(),
|
||||
};
|
||||
|
||||
Ok((buffer, future))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DeviceLocalBuffer<T>
|
||||
where
|
||||
T: BufferContents,
|
||||
{
|
||||
/// Builds an `DeviceLocalBuffer` from some data.
|
||||
///
|
||||
/// This function builds a memory-mapped intermediate buffer, writes the data to it, builds a
|
||||
/// command buffer that copies from this intermediate buffer to the final buffer, and finally
|
||||
/// submits the command buffer as a future.
|
||||
///
|
||||
/// This function returns two objects: the newly-created buffer, and a future representing
|
||||
/// the initial upload operation. In order to be allowed to use the `DeviceLocalBuffer`, you must
|
||||
/// either submit your operation after this future, or execute this future and wait for it to
|
||||
/// be finished before submitting your own operation.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// - Panics if `T` has zero size.
|
||||
pub fn from_data(
|
||||
data: T,
|
||||
usage: BufferUsage,
|
||||
queue: Arc<Queue>,
|
||||
) -> Result<
|
||||
(Arc<DeviceLocalBuffer<T>>, DeviceLocalBufferFromBufferFuture),
|
||||
DeviceLocalBufferCreationError,
|
||||
> {
|
||||
let source = CpuAccessibleBuffer::from_data(
|
||||
queue.device().clone(),
|
||||
BufferUsage::transfer_src(),
|
||||
false,
|
||||
data,
|
||||
)?;
|
||||
DeviceLocalBuffer::from_buffer(source, usage, queue)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DeviceLocalBuffer<[T]>
|
||||
where
|
||||
[T]: BufferContents,
|
||||
{
|
||||
/// # Panics
|
||||
///
|
||||
/// - Panics if `T` has zero size.
|
||||
/// - Panics if `data` is empty.
|
||||
pub fn from_iter<D>(
|
||||
data: D,
|
||||
usage: BufferUsage,
|
||||
queue: Arc<Queue>,
|
||||
) -> Result<
|
||||
(
|
||||
Arc<DeviceLocalBuffer<[T]>>,
|
||||
DeviceLocalBufferFromBufferFuture,
|
||||
),
|
||||
DeviceLocalBufferCreationError,
|
||||
>
|
||||
where
|
||||
D: IntoIterator<Item = T>,
|
||||
D::IntoIter: ExactSizeIterator,
|
||||
{
|
||||
let source = CpuAccessibleBuffer::from_iter(
|
||||
queue.device().clone(),
|
||||
BufferUsage::transfer_src(),
|
||||
false,
|
||||
data,
|
||||
)?;
|
||||
DeviceLocalBuffer::from_buffer(source, usage, queue)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DeviceLocalBuffer<[T]>
|
||||
where
|
||||
[T]: BufferContents,
|
||||
@ -436,3 +569,133 @@ where
|
||||
self.size().hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum DeviceLocalBufferCreationError {
|
||||
DeviceMemoryAllocationError(DeviceMemoryAllocationError),
|
||||
CommandBufferBeginError(CommandBufferBeginError),
|
||||
}
|
||||
|
||||
impl error::Error for DeviceLocalBufferCreationError {
|
||||
#[inline]
|
||||
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
|
||||
match self {
|
||||
Self::DeviceMemoryAllocationError(err) => Some(err),
|
||||
Self::CommandBufferBeginError(err) => Some(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for DeviceLocalBufferCreationError {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::DeviceMemoryAllocationError(err) => err.fmt(f),
|
||||
Self::CommandBufferBeginError(err) => err.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DeviceMemoryAllocationError> for DeviceLocalBufferCreationError {
|
||||
#[inline]
|
||||
fn from(e: DeviceMemoryAllocationError) -> Self {
|
||||
Self::DeviceMemoryAllocationError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CommandBufferBeginError> for DeviceLocalBufferCreationError {
|
||||
#[inline]
|
||||
fn from(e: CommandBufferBeginError) -> Self {
|
||||
Self::CommandBufferBeginError(e)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::sync::GpuFuture;
|
||||
|
||||
#[test]
|
||||
fn from_data_working() {
|
||||
let (device, queue) = gfx_dev_and_queue!();
|
||||
|
||||
let (buffer, _) =
|
||||
DeviceLocalBuffer::from_data(12u32, BufferUsage::all(), queue.clone()).unwrap();
|
||||
|
||||
let destination =
|
||||
CpuAccessibleBuffer::from_data(device.clone(), BufferUsage::all(), false, 0).unwrap();
|
||||
|
||||
let mut cbb = AutoCommandBufferBuilder::primary(
|
||||
device.clone(),
|
||||
queue.family(),
|
||||
CommandBufferUsage::MultipleSubmit,
|
||||
)
|
||||
.unwrap();
|
||||
cbb.copy_buffer(CopyBufferInfo::buffers(buffer, destination.clone()))
|
||||
.unwrap();
|
||||
let _ = cbb
|
||||
.build()
|
||||
.unwrap()
|
||||
.execute(queue.clone())
|
||||
.unwrap()
|
||||
.then_signal_fence_and_flush()
|
||||
.unwrap();
|
||||
|
||||
let destination_content = destination.read().unwrap();
|
||||
assert_eq!(*destination_content, 12);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_iter_working() {
|
||||
let (device, queue) = gfx_dev_and_queue!();
|
||||
|
||||
let (buffer, _) = DeviceLocalBuffer::from_iter(
|
||||
(0..512u32).map(|n| n * 2),
|
||||
BufferUsage::all(),
|
||||
queue.clone(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let destination = CpuAccessibleBuffer::from_iter(
|
||||
device.clone(),
|
||||
BufferUsage::all(),
|
||||
false,
|
||||
(0..512).map(|_| 0u32),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let mut cbb = AutoCommandBufferBuilder::primary(
|
||||
device.clone(),
|
||||
queue.family(),
|
||||
CommandBufferUsage::MultipleSubmit,
|
||||
)
|
||||
.unwrap();
|
||||
cbb.copy_buffer(CopyBufferInfo::buffers(buffer, destination.clone()))
|
||||
.unwrap();
|
||||
let _ = cbb
|
||||
.build()
|
||||
.unwrap()
|
||||
.execute(queue.clone())
|
||||
.unwrap()
|
||||
.then_signal_fence_and_flush()
|
||||
.unwrap();
|
||||
|
||||
let destination_content = destination.read().unwrap();
|
||||
for (n, &v) in destination_content.iter().enumerate() {
|
||||
assert_eq!(n * 2, v as usize);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[allow(unused)]
|
||||
fn create_buffer_zero_size_data() {
|
||||
let (device, queue) = gfx_dev_and_queue!();
|
||||
|
||||
assert_should_panic!({
|
||||
DeviceLocalBuffer::from_data((), BufferUsage::all(), queue.clone()).unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: write tons of tests that try to exploit loopholes
|
||||
// this isn't possible yet because checks aren't correctly implemented yet
|
||||
}
|
||||
|
@ -1,785 +0,0 @@
|
||||
// Copyright (c) 2016 The vulkano developers
|
||||
// Licensed under the Apache License, Version 2.0
|
||||
// <LICENSE-APACHE or
|
||||
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT
|
||||
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
|
||||
// at your option. All files in the project carrying such
|
||||
// notice may not be copied, modified, or distributed except
|
||||
// according to those terms.
|
||||
|
||||
//! Buffer that is written once then read for as long as it is alive.
|
||||
//!
|
||||
//! Use this buffer when you have data that you never modify.
|
||||
//!
|
||||
//! Only the first ever command buffer that uses this buffer can write to it (for example by
|
||||
//! copying from another buffer). Any subsequent command buffer **must** only read from the buffer,
|
||||
//! or a panic will happen.
|
||||
//!
|
||||
//! The buffer will be stored in device-local memory if possible
|
||||
//!
|
||||
|
||||
use super::{
|
||||
sys::UnsafeBuffer, BufferAccess, BufferAccessObject, BufferContents, BufferInner, BufferUsage,
|
||||
CpuAccessibleBuffer,
|
||||
};
|
||||
use crate::{
|
||||
buffer::{sys::UnsafeBufferCreateInfo, BufferCreationError, TypedBufferAccess},
|
||||
command_buffer::{
|
||||
AutoCommandBufferBuilder, CommandBufferBeginError, CommandBufferExecFuture,
|
||||
CommandBufferUsage, CopyBufferInfo, PrimaryAutoCommandBuffer, PrimaryCommandBuffer,
|
||||
},
|
||||
device::{physical::QueueFamily, Device, DeviceOwned, Queue},
|
||||
memory::{
|
||||
pool::{
|
||||
AllocFromRequirementsFilter, AllocLayout, MappingRequirement, MemoryPoolAlloc,
|
||||
PotentialDedicatedAllocation, StdMemoryPoolAlloc,
|
||||
},
|
||||
DedicatedAllocation, DeviceMemoryAllocationError, MemoryPool,
|
||||
},
|
||||
sync::{NowFuture, Sharing},
|
||||
DeviceSize, OomError,
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
use std::{
|
||||
error, fmt,
|
||||
hash::{Hash, Hasher},
|
||||
marker::PhantomData,
|
||||
mem::size_of,
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
/// Buffer that is written once then read for as long as it is alive.
|
||||
#[derive(Debug)]
|
||||
pub struct ImmutableBuffer<T, A = PotentialDedicatedAllocation<StdMemoryPoolAlloc>>
|
||||
where
|
||||
T: BufferContents + ?Sized,
|
||||
{
|
||||
// Inner content.
|
||||
inner: Arc<UnsafeBuffer>,
|
||||
|
||||
// Memory allocated for the buffer.
|
||||
memory: A,
|
||||
|
||||
// Queue families allowed to access this buffer.
|
||||
queue_families: SmallVec<[u32; 4]>,
|
||||
|
||||
// Necessary to have the appropriate template parameter.
|
||||
marker: PhantomData<Box<T>>,
|
||||
}
|
||||
|
||||
// TODO: make this prettier
|
||||
type ImmutableBufferFromBufferFuture = CommandBufferExecFuture<NowFuture, PrimaryAutoCommandBuffer>;
|
||||
|
||||
impl<T> ImmutableBuffer<T>
|
||||
where
|
||||
T: BufferContents + ?Sized,
|
||||
{
|
||||
/// Builds an `ImmutableBuffer` that copies its data from another buffer.
|
||||
///
|
||||
/// This function returns two objects: the newly-created buffer, and a future representing
|
||||
/// the initial upload operation. In order to be allowed to use the `ImmutableBuffer`, you must
|
||||
/// either submit your operation after this future, or execute this future and wait for it to
|
||||
/// be finished before submitting your own operation.
|
||||
pub fn from_buffer<B>(
|
||||
source: Arc<B>,
|
||||
usage: BufferUsage,
|
||||
queue: Arc<Queue>,
|
||||
) -> Result<
|
||||
(Arc<ImmutableBuffer<T>>, ImmutableBufferFromBufferFuture),
|
||||
ImmutableBufferCreationError,
|
||||
>
|
||||
where
|
||||
B: TypedBufferAccess<Content = T> + 'static,
|
||||
{
|
||||
unsafe {
|
||||
// We automatically set `transfer_dst` to true in order to avoid annoying errors.
|
||||
let actual_usage = BufferUsage {
|
||||
transfer_dst: true,
|
||||
..usage
|
||||
};
|
||||
|
||||
let (buffer, init) = ImmutableBuffer::raw(
|
||||
source.device().clone(),
|
||||
source.size(),
|
||||
actual_usage,
|
||||
source.device().active_queue_families(),
|
||||
)?;
|
||||
|
||||
let mut cbb = AutoCommandBufferBuilder::primary(
|
||||
source.device().clone(),
|
||||
queue.family(),
|
||||
CommandBufferUsage::MultipleSubmit,
|
||||
)?;
|
||||
cbb.copy_buffer(CopyBufferInfo::buffers(source, init))
|
||||
.unwrap(); // TODO: return error?
|
||||
let cb = cbb.build().unwrap(); // TODO: return OomError
|
||||
|
||||
let future = match cb.execute(queue) {
|
||||
Ok(f) => f,
|
||||
Err(_) => unreachable!(),
|
||||
};
|
||||
|
||||
Ok((buffer, future))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ImmutableBuffer<T>
|
||||
where
|
||||
T: BufferContents,
|
||||
{
|
||||
/// Builds an `ImmutableBuffer` from some data.
|
||||
///
|
||||
/// This function builds a memory-mapped intermediate buffer, writes the data to it, builds a
|
||||
/// command buffer that copies from this intermediate buffer to the final buffer, and finally
|
||||
/// submits the command buffer as a future.
|
||||
///
|
||||
/// This function returns two objects: the newly-created buffer, and a future representing
|
||||
/// the initial upload operation. In order to be allowed to use the `ImmutableBuffer`, you must
|
||||
/// either submit your operation after this future, or execute this future and wait for it to
|
||||
/// be finished before submitting your own operation.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// - Panics if `T` has zero size.
|
||||
pub fn from_data(
|
||||
data: T,
|
||||
usage: BufferUsage,
|
||||
queue: Arc<Queue>,
|
||||
) -> Result<
|
||||
(Arc<ImmutableBuffer<T>>, ImmutableBufferFromBufferFuture),
|
||||
ImmutableBufferCreationError,
|
||||
> {
|
||||
let source = CpuAccessibleBuffer::from_data(
|
||||
queue.device().clone(),
|
||||
BufferUsage::transfer_src(),
|
||||
false,
|
||||
data,
|
||||
)?;
|
||||
ImmutableBuffer::from_buffer(source, usage, queue)
|
||||
}
|
||||
|
||||
/// Builds a new buffer with uninitialized data. Only allowed for sized data.
|
||||
///
|
||||
/// Returns two things: the buffer, and a special access that should be used for the initial
|
||||
/// upload to the buffer.
|
||||
///
|
||||
/// You will get an error if you try to use the buffer before using the initial upload access.
|
||||
/// However this function doesn't check whether you actually used this initial upload to fill
|
||||
/// the buffer like you're supposed to do.
|
||||
///
|
||||
/// You will also get an error if you try to get exclusive access to the final buffer.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// - The `ImmutableBufferInitialization` should be used to fill the buffer with some initial
|
||||
/// data, otherwise the content is undefined.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// - Panics if `T` has zero size.
|
||||
#[inline]
|
||||
pub unsafe fn uninitialized(
|
||||
device: Arc<Device>,
|
||||
usage: BufferUsage,
|
||||
) -> Result<
|
||||
(
|
||||
Arc<ImmutableBuffer<T>>,
|
||||
Arc<ImmutableBufferInitialization<T>>,
|
||||
),
|
||||
ImmutableBufferCreationError,
|
||||
> {
|
||||
ImmutableBuffer::raw(
|
||||
device.clone(),
|
||||
size_of::<T>() as DeviceSize,
|
||||
usage,
|
||||
device.active_queue_families(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ImmutableBuffer<[T]>
|
||||
where
|
||||
[T]: BufferContents,
|
||||
{
|
||||
/// # Panics
|
||||
///
|
||||
/// - Panics if `T` has zero size.
|
||||
/// - Panics if `data` is empty.
|
||||
pub fn from_iter<D>(
|
||||
data: D,
|
||||
usage: BufferUsage,
|
||||
queue: Arc<Queue>,
|
||||
) -> Result<
|
||||
(Arc<ImmutableBuffer<[T]>>, ImmutableBufferFromBufferFuture),
|
||||
ImmutableBufferCreationError,
|
||||
>
|
||||
where
|
||||
D: IntoIterator<Item = T>,
|
||||
D::IntoIter: ExactSizeIterator,
|
||||
{
|
||||
let source = CpuAccessibleBuffer::from_iter(
|
||||
queue.device().clone(),
|
||||
BufferUsage::transfer_src(),
|
||||
false,
|
||||
data,
|
||||
)?;
|
||||
ImmutableBuffer::from_buffer(source, usage, queue)
|
||||
}
|
||||
|
||||
/// Builds a new buffer with uninitialized data. Can be used for arrays.
|
||||
///
|
||||
/// Returns two things: the buffer, and a special access that should be used for the initial
|
||||
/// upload to the buffer.
|
||||
///
|
||||
/// You will get an error if you try to use the buffer before using the initial upload access.
|
||||
/// However this function doesn't check whether you actually used this initial upload to fill
|
||||
/// the buffer like you're supposed to do.
|
||||
///
|
||||
/// You will also get an error if you try to get exclusive access to the final buffer.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// - The `ImmutableBufferInitialization` should be used to fill the buffer with some initial
|
||||
/// data, otherwise the content is undefined.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// - Panics if `T` has zero size.
|
||||
/// - Panics if `len` is zero.
|
||||
#[inline]
|
||||
pub unsafe fn uninitialized_array(
|
||||
device: Arc<Device>,
|
||||
len: DeviceSize,
|
||||
usage: BufferUsage,
|
||||
) -> Result<
|
||||
(
|
||||
Arc<ImmutableBuffer<[T]>>,
|
||||
Arc<ImmutableBufferInitialization<[T]>>,
|
||||
),
|
||||
ImmutableBufferCreationError,
|
||||
> {
|
||||
ImmutableBuffer::raw(
|
||||
device.clone(),
|
||||
len * size_of::<T>() as DeviceSize,
|
||||
usage,
|
||||
device.active_queue_families(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ImmutableBuffer<T>
|
||||
where
|
||||
T: BufferContents + ?Sized,
|
||||
{
|
||||
/// Builds a new buffer without checking the size and granting free access for the initial
|
||||
/// upload.
|
||||
///
|
||||
/// Returns two things: the buffer, and a special access that should be used for the initial
|
||||
/// upload to the buffer.
|
||||
/// You will get an error if you try to use the buffer before using the initial upload access.
|
||||
/// However this function doesn't check whether you used this initial upload to fill the buffer.
|
||||
/// You will also get an error if you try to get exclusive access to the final buffer.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// - You must ensure that the size that you pass is correct for `T`.
|
||||
/// - The `ImmutableBufferInitialization` should be used to fill the buffer with some initial
|
||||
/// data.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// - Panics if `size` is zero.
|
||||
#[inline]
|
||||
pub unsafe fn raw<'a, I>(
|
||||
device: Arc<Device>,
|
||||
size: DeviceSize,
|
||||
usage: BufferUsage,
|
||||
queue_families: I,
|
||||
) -> Result<
|
||||
(
|
||||
Arc<ImmutableBuffer<T>>,
|
||||
Arc<ImmutableBufferInitialization<T>>,
|
||||
),
|
||||
ImmutableBufferCreationError,
|
||||
>
|
||||
where
|
||||
I: IntoIterator<Item = QueueFamily<'a>>,
|
||||
{
|
||||
let queue_families = queue_families.into_iter().map(|f| f.id()).collect();
|
||||
ImmutableBuffer::raw_impl(device, size, usage, queue_families)
|
||||
}
|
||||
|
||||
// Internal implementation of `raw`. This is separated from `raw` so that it doesn't need to be
|
||||
// inlined.
|
||||
unsafe fn raw_impl(
|
||||
device: Arc<Device>,
|
||||
size: DeviceSize,
|
||||
usage: BufferUsage,
|
||||
queue_families: SmallVec<[u32; 4]>,
|
||||
) -> Result<
|
||||
(
|
||||
Arc<ImmutableBuffer<T>>,
|
||||
Arc<ImmutableBufferInitialization<T>>,
|
||||
),
|
||||
ImmutableBufferCreationError,
|
||||
> {
|
||||
let buffer = match UnsafeBuffer::new(
|
||||
device.clone(),
|
||||
UnsafeBufferCreateInfo {
|
||||
sharing: if queue_families.len() >= 2 {
|
||||
Sharing::Concurrent(queue_families.clone())
|
||||
} else {
|
||||
Sharing::Exclusive
|
||||
},
|
||||
size,
|
||||
usage,
|
||||
..Default::default()
|
||||
},
|
||||
) {
|
||||
Ok(b) => b,
|
||||
Err(BufferCreationError::AllocError(err)) => return Err(err.into()),
|
||||
Err(_) => unreachable!(), // We don't use sparse binding, therefore the other
|
||||
// errors can't happen
|
||||
};
|
||||
let mem_reqs = buffer.memory_requirements();
|
||||
|
||||
let mem = MemoryPool::alloc_from_requirements(
|
||||
&Device::standard_pool(&device),
|
||||
&mem_reqs,
|
||||
AllocLayout::Linear,
|
||||
MappingRequirement::DoNotMap,
|
||||
Some(DedicatedAllocation::Buffer(&buffer)),
|
||||
|t| {
|
||||
if t.is_device_local() {
|
||||
AllocFromRequirementsFilter::Preferred
|
||||
} else {
|
||||
AllocFromRequirementsFilter::Allowed
|
||||
}
|
||||
},
|
||||
)?;
|
||||
debug_assert!((mem.offset() % mem_reqs.alignment) == 0);
|
||||
buffer.bind_memory(mem.memory(), mem.offset())?;
|
||||
|
||||
let final_buf = Arc::new(ImmutableBuffer {
|
||||
inner: buffer,
|
||||
memory: mem,
|
||||
queue_families: queue_families,
|
||||
marker: PhantomData,
|
||||
});
|
||||
|
||||
let initialization = Arc::new(ImmutableBufferInitialization {
|
||||
buffer: final_buf.clone(),
|
||||
});
|
||||
|
||||
Ok((final_buf, initialization))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, A> ImmutableBuffer<T, A>
|
||||
where
|
||||
T: BufferContents + ?Sized,
|
||||
{
|
||||
/// Returns the device used to create this buffer.
|
||||
#[inline]
|
||||
pub fn device(&self) -> &Arc<Device> {
|
||||
self.inner.device()
|
||||
}
|
||||
|
||||
/// Returns the queue families this buffer can be used on.
|
||||
// TODO: use a custom iterator
|
||||
#[inline]
|
||||
pub fn queue_families(&self) -> Vec<QueueFamily> {
|
||||
self.queue_families
|
||||
.iter()
|
||||
.map(|&num| {
|
||||
self.device()
|
||||
.physical_device()
|
||||
.queue_family_by_id(num)
|
||||
.unwrap()
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T, A> BufferAccess for ImmutableBuffer<T, A>
|
||||
where
|
||||
T: BufferContents + ?Sized,
|
||||
A: Send + Sync,
|
||||
{
|
||||
#[inline]
|
||||
fn inner(&self) -> BufferInner {
|
||||
BufferInner {
|
||||
buffer: &self.inner,
|
||||
offset: 0,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn size(&self) -> DeviceSize {
|
||||
self.inner.size()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, A> BufferAccessObject for Arc<ImmutableBuffer<T, A>>
|
||||
where
|
||||
T: BufferContents + ?Sized,
|
||||
A: Send + Sync + 'static,
|
||||
{
|
||||
#[inline]
|
||||
fn as_buffer_access_object(&self) -> Arc<dyn BufferAccess> {
|
||||
self.clone()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T, A> TypedBufferAccess for ImmutableBuffer<T, A>
|
||||
where
|
||||
T: BufferContents + ?Sized,
|
||||
A: Send + Sync,
|
||||
{
|
||||
type Content = T;
|
||||
}
|
||||
|
||||
unsafe impl<T, A> DeviceOwned for ImmutableBuffer<T, A>
|
||||
where
|
||||
T: BufferContents + ?Sized,
|
||||
{
|
||||
#[inline]
|
||||
fn device(&self) -> &Arc<Device> {
|
||||
self.inner.device()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, A> PartialEq for ImmutableBuffer<T, A>
|
||||
where
|
||||
T: BufferContents + ?Sized,
|
||||
A: Send + Sync,
|
||||
{
|
||||
#[inline]
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.inner() == other.inner() && self.size() == other.size()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, A> Eq for ImmutableBuffer<T, A>
|
||||
where
|
||||
T: BufferContents + ?Sized,
|
||||
A: Send + Sync,
|
||||
{
|
||||
}
|
||||
|
||||
impl<T, A> Hash for ImmutableBuffer<T, A>
|
||||
where
|
||||
T: BufferContents + ?Sized,
|
||||
A: Send + Sync,
|
||||
{
|
||||
#[inline]
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.inner().hash(state);
|
||||
self.size().hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
/// Access to the immutable buffer that can be used for the initial upload.
|
||||
#[derive(Debug)]
|
||||
pub struct ImmutableBufferInitialization<T, A = PotentialDedicatedAllocation<StdMemoryPoolAlloc>>
|
||||
where
|
||||
T: BufferContents + ?Sized,
|
||||
{
|
||||
buffer: Arc<ImmutableBuffer<T, A>>,
|
||||
}
|
||||
|
||||
unsafe impl<T, A> BufferAccess for ImmutableBufferInitialization<T, A>
|
||||
where
|
||||
T: BufferContents + ?Sized,
|
||||
A: Send + Sync,
|
||||
{
|
||||
#[inline]
|
||||
fn inner(&self) -> BufferInner {
|
||||
self.buffer.inner()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn size(&self) -> DeviceSize {
|
||||
self.buffer.size()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, A> BufferAccessObject for Arc<ImmutableBufferInitialization<T, A>>
|
||||
where
|
||||
T: BufferContents + ?Sized,
|
||||
A: Send + Sync + 'static,
|
||||
{
|
||||
#[inline]
|
||||
fn as_buffer_access_object(&self) -> Arc<dyn BufferAccess> {
|
||||
self.clone()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T, A> TypedBufferAccess for ImmutableBufferInitialization<T, A>
|
||||
where
|
||||
T: BufferContents + ?Sized,
|
||||
A: Send + Sync,
|
||||
{
|
||||
type Content = T;
|
||||
}
|
||||
|
||||
unsafe impl<T, A> DeviceOwned for ImmutableBufferInitialization<T, A>
|
||||
where
|
||||
T: BufferContents + ?Sized,
|
||||
{
|
||||
#[inline]
|
||||
fn device(&self) -> &Arc<Device> {
|
||||
self.buffer.inner.device()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, A> Clone for ImmutableBufferInitialization<T, A>
|
||||
where
|
||||
T: BufferContents + ?Sized,
|
||||
{
|
||||
#[inline]
|
||||
fn clone(&self) -> ImmutableBufferInitialization<T, A> {
|
||||
ImmutableBufferInitialization {
|
||||
buffer: self.buffer.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, A> PartialEq for ImmutableBufferInitialization<T, A>
|
||||
where
|
||||
T: BufferContents + ?Sized,
|
||||
A: Send + Sync,
|
||||
{
|
||||
#[inline]
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.inner() == other.inner() && self.size() == other.size()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, A> Eq for ImmutableBufferInitialization<T, A>
|
||||
where
|
||||
T: BufferContents + ?Sized,
|
||||
A: Send + Sync,
|
||||
{
|
||||
}
|
||||
|
||||
impl<T, A> Hash for ImmutableBufferInitialization<T, A>
|
||||
where
|
||||
T: BufferContents + ?Sized,
|
||||
A: Send + Sync,
|
||||
{
|
||||
#[inline]
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.inner().hash(state);
|
||||
self.size().hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum ImmutableBufferCreationError {
|
||||
DeviceMemoryAllocationError(DeviceMemoryAllocationError),
|
||||
CommandBufferBeginError(CommandBufferBeginError),
|
||||
}
|
||||
|
||||
impl error::Error for ImmutableBufferCreationError {
|
||||
#[inline]
|
||||
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
|
||||
match self {
|
||||
Self::DeviceMemoryAllocationError(err) => Some(err),
|
||||
Self::CommandBufferBeginError(err) => Some(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ImmutableBufferCreationError {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::DeviceMemoryAllocationError(err) => err.fmt(f),
|
||||
Self::CommandBufferBeginError(err) => err.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DeviceMemoryAllocationError> for ImmutableBufferCreationError {
|
||||
#[inline]
|
||||
fn from(err: DeviceMemoryAllocationError) -> Self {
|
||||
Self::DeviceMemoryAllocationError(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<OomError> for ImmutableBufferCreationError {
|
||||
#[inline]
|
||||
fn from(err: OomError) -> Self {
|
||||
Self::DeviceMemoryAllocationError(err.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CommandBufferBeginError> for ImmutableBufferCreationError {
|
||||
#[inline]
|
||||
fn from(err: CommandBufferBeginError) -> Self {
|
||||
Self::CommandBufferBeginError(err)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::sync::GpuFuture;
|
||||
|
||||
#[test]
|
||||
fn from_data_working() {
|
||||
let (device, queue) = gfx_dev_and_queue!();
|
||||
|
||||
let (buffer, _) =
|
||||
ImmutableBuffer::from_data(12u32, BufferUsage::all(), queue.clone()).unwrap();
|
||||
|
||||
let destination =
|
||||
CpuAccessibleBuffer::from_data(device.clone(), BufferUsage::all(), false, 0).unwrap();
|
||||
|
||||
let mut cbb = AutoCommandBufferBuilder::primary(
|
||||
device.clone(),
|
||||
queue.family(),
|
||||
CommandBufferUsage::MultipleSubmit,
|
||||
)
|
||||
.unwrap();
|
||||
cbb.copy_buffer(CopyBufferInfo::buffers(buffer, destination.clone()))
|
||||
.unwrap();
|
||||
let _ = cbb
|
||||
.build()
|
||||
.unwrap()
|
||||
.execute(queue.clone())
|
||||
.unwrap()
|
||||
.then_signal_fence_and_flush()
|
||||
.unwrap();
|
||||
|
||||
let destination_content = destination.read().unwrap();
|
||||
assert_eq!(*destination_content, 12);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_iter_working() {
|
||||
let (device, queue) = gfx_dev_and_queue!();
|
||||
|
||||
let (buffer, _) = ImmutableBuffer::from_iter(
|
||||
(0..512u32).map(|n| n * 2),
|
||||
BufferUsage::all(),
|
||||
queue.clone(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let destination = CpuAccessibleBuffer::from_iter(
|
||||
device.clone(),
|
||||
BufferUsage::all(),
|
||||
false,
|
||||
(0..512).map(|_| 0u32),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let mut cbb = AutoCommandBufferBuilder::primary(
|
||||
device.clone(),
|
||||
queue.family(),
|
||||
CommandBufferUsage::MultipleSubmit,
|
||||
)
|
||||
.unwrap();
|
||||
cbb.copy_buffer(CopyBufferInfo::buffers(buffer, destination.clone()))
|
||||
.unwrap();
|
||||
let _ = cbb
|
||||
.build()
|
||||
.unwrap()
|
||||
.execute(queue.clone())
|
||||
.unwrap()
|
||||
.then_signal_fence_and_flush()
|
||||
.unwrap();
|
||||
|
||||
let destination_content = destination.read().unwrap();
|
||||
for (n, &v) in destination_content.iter().enumerate() {
|
||||
assert_eq!(n * 2, v as usize);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn init_then_read_same_cb() {
|
||||
let (device, queue) = gfx_dev_and_queue!();
|
||||
|
||||
let (buffer, init) = unsafe {
|
||||
ImmutableBuffer::<u32>::uninitialized(device.clone(), BufferUsage::all()).unwrap()
|
||||
};
|
||||
|
||||
let source =
|
||||
CpuAccessibleBuffer::from_data(device.clone(), BufferUsage::all(), false, 0).unwrap();
|
||||
|
||||
let mut cbb = AutoCommandBufferBuilder::primary(
|
||||
device.clone(),
|
||||
queue.family(),
|
||||
CommandBufferUsage::MultipleSubmit,
|
||||
)
|
||||
.unwrap();
|
||||
cbb.copy_buffer(CopyBufferInfo::buffers(source.clone(), init))
|
||||
.unwrap()
|
||||
.copy_buffer(CopyBufferInfo::buffers(buffer, source.clone()))
|
||||
.unwrap();
|
||||
let _ = cbb
|
||||
.build()
|
||||
.unwrap()
|
||||
.execute(queue.clone())
|
||||
.unwrap()
|
||||
.then_signal_fence_and_flush()
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore] // TODO: doesn't work because the submit sync layer isn't properly implemented
|
||||
fn init_then_read_same_future() {
|
||||
let (device, queue) = gfx_dev_and_queue!();
|
||||
|
||||
let (buffer, init) = unsafe {
|
||||
ImmutableBuffer::<u32>::uninitialized(device.clone(), BufferUsage::all()).unwrap()
|
||||
};
|
||||
|
||||
let source =
|
||||
CpuAccessibleBuffer::from_data(device.clone(), BufferUsage::all(), false, 0).unwrap();
|
||||
|
||||
let mut cbb = AutoCommandBufferBuilder::primary(
|
||||
device.clone(),
|
||||
queue.family(),
|
||||
CommandBufferUsage::MultipleSubmit,
|
||||
)
|
||||
.unwrap();
|
||||
cbb.copy_buffer(CopyBufferInfo::buffers(source.clone(), init))
|
||||
.unwrap();
|
||||
let cb1 = cbb.build().unwrap();
|
||||
|
||||
let mut cbb = AutoCommandBufferBuilder::primary(
|
||||
device.clone(),
|
||||
queue.family(),
|
||||
CommandBufferUsage::MultipleSubmit,
|
||||
)
|
||||
.unwrap();
|
||||
cbb.copy_buffer(CopyBufferInfo::buffers(buffer, source.clone()))
|
||||
.unwrap();
|
||||
let cb2 = cbb.build().unwrap();
|
||||
|
||||
let _ = cb1
|
||||
.execute(queue.clone())
|
||||
.unwrap()
|
||||
.then_execute(queue.clone(), cb2)
|
||||
.unwrap()
|
||||
.then_signal_fence_and_flush()
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[allow(unused)]
|
||||
fn create_buffer_zero_size_data() {
|
||||
let (device, queue) = gfx_dev_and_queue!();
|
||||
|
||||
assert_should_panic!({
|
||||
ImmutableBuffer::from_data((), BufferUsage::all(), queue.clone()).unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: write tons of tests that try to exploit loopholes
|
||||
// this isn't possible yet because checks aren't correctly implemented yet
|
||||
}
|
@ -33,10 +33,6 @@
|
||||
//! usually located in video memory and whose content can't be directly accessed by your
|
||||
//! application. Accessing this buffer from the GPU is generally faster compared to accessing a
|
||||
//! CPU-accessible buffer.
|
||||
//! - An [`ImmutableBuffer`](crate::buffer::immutable::ImmutableBuffer) designates a buffer in video
|
||||
//! memory and whose content can only be written at creation. Compared to `DeviceLocalBuffer`,
|
||||
//! this buffer requires less CPU processing because we don't need to keep track of the reads
|
||||
//! and writes.
|
||||
//! - A [`CpuBufferPool`](crate::buffer::cpu_pool::CpuBufferPool) is a ring buffer that can be used to
|
||||
//! transfer data between the CPU and the GPU at a high rate.
|
||||
//! - A [`CpuAccessibleBuffer`](crate::buffer::cpu_access::CpuAccessibleBuffer) is a simple buffer that
|
||||
@ -45,12 +41,7 @@
|
||||
//! Here is a quick way to choose which buffer to use. Do you often need to read or write
|
||||
//! the content of the buffer? If so, use a `CpuBufferPool`. Otherwise, do you need to be able to
|
||||
//! modify the content of the buffer after its initialization? If so, use a `DeviceLocalBuffer`.
|
||||
//! If no to both questions, use an `ImmutableBuffer`.
|
||||
//!
|
||||
//! When deciding how your buffer is going to be used, don't forget that sometimes the best
|
||||
//! solution is to manipulate multiple buffers instead. For example if you need to update a buffer's
|
||||
//! content only from time to time, it may be a good idea to simply recreate a new `ImmutableBuffer`
|
||||
//! every time.
|
||||
//! Another example: if a buffer is under constant access by the GPU but you need to
|
||||
//! read its content on the CPU from time to time, it may be a good idea to use a
|
||||
//! `DeviceLocalBuffer` as the main buffer and a `CpuBufferPool` for when you need to read it.
|
||||
@ -87,7 +78,6 @@ pub use self::{
|
||||
cpu_access::CpuAccessibleBuffer,
|
||||
cpu_pool::CpuBufferPool,
|
||||
device_local::DeviceLocalBuffer,
|
||||
immutable::ImmutableBuffer,
|
||||
slice::BufferSlice,
|
||||
sys::{BufferCreationError, SparseLevel},
|
||||
traits::{
|
||||
@ -108,7 +98,6 @@ use std::mem::size_of;
|
||||
pub mod cpu_access;
|
||||
pub mod cpu_pool;
|
||||
pub mod device_local;
|
||||
pub mod immutable;
|
||||
pub mod sys;
|
||||
pub mod view;
|
||||
|
||||
|
@ -141,10 +141,10 @@ impl<T: ?Sized, B> BufferSlice<T, B> {
|
||||
/// ```
|
||||
/// # use std::sync::Arc;
|
||||
/// # use vulkano::buffer::BufferSlice;
|
||||
/// # use vulkano::buffer::immutable::ImmutableBuffer;
|
||||
/// # use vulkano::buffer::DeviceLocalBuffer;
|
||||
/// # struct VertexImpl;
|
||||
/// let blob_slice: Arc<BufferSlice<[u8], Arc<ImmutableBuffer<[u8]>>>> = return;
|
||||
/// let vertex_slice: Arc<BufferSlice<[VertexImpl], Arc<ImmutableBuffer<[u8]>>>> = unsafe {
|
||||
/// let blob_slice: Arc<BufferSlice<[u8], Arc<DeviceLocalBuffer<[u8]>>>> = return;
|
||||
/// let vertex_slice: Arc<BufferSlice<[VertexImpl], Arc<DeviceLocalBuffer<[u8]>>>> = unsafe {
|
||||
/// blob_slice.reinterpret::<[VertexImpl]>()
|
||||
/// };
|
||||
/// ```
|
||||
|
@ -19,7 +19,7 @@
|
||||
//!
|
||||
//! ```
|
||||
//! # use std::sync::Arc;
|
||||
//! use vulkano::buffer::immutable::ImmutableBuffer;
|
||||
//! use vulkano::buffer::DeviceLocalBuffer;
|
||||
//! use vulkano::buffer::BufferUsage;
|
||||
//! use vulkano::buffer::view::{BufferView, BufferViewCreateInfo};
|
||||
//! use vulkano::format::Format;
|
||||
@ -31,7 +31,7 @@
|
||||
//! .. BufferUsage::none()
|
||||
//! };
|
||||
//!
|
||||
//! let (buffer, _future) = ImmutableBuffer::<[u32]>::from_iter((0..128).map(|n| n), usage,
|
||||
//! let (buffer, _future) = DeviceLocalBuffer::<[u32]>::from_iter((0..128).map(|n| n), usage,
|
||||
//! queue.clone()).unwrap();
|
||||
//! let _view = BufferView::new(
|
||||
//! buffer,
|
||||
@ -462,9 +462,8 @@ impl Hash for dyn BufferViewAbstract {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::buffer::immutable::ImmutableBuffer;
|
||||
use crate::buffer::view::{BufferView, BufferViewCreateInfo, BufferViewCreationError};
|
||||
use crate::buffer::BufferUsage;
|
||||
use crate::buffer::{BufferUsage, DeviceLocalBuffer};
|
||||
use crate::format::Format;
|
||||
|
||||
#[test]
|
||||
@ -477,9 +476,12 @@ mod tests {
|
||||
..BufferUsage::none()
|
||||
};
|
||||
|
||||
let (buffer, _) =
|
||||
ImmutableBuffer::<[[u8; 4]]>::from_iter((0..128).map(|_| [0; 4]), usage, queue.clone())
|
||||
.unwrap();
|
||||
let (buffer, _) = DeviceLocalBuffer::<[[u8; 4]]>::from_iter(
|
||||
(0..128).map(|_| [0; 4]),
|
||||
usage,
|
||||
queue.clone(),
|
||||
)
|
||||
.unwrap();
|
||||
let view = BufferView::new(
|
||||
buffer,
|
||||
BufferViewCreateInfo {
|
||||
@ -500,9 +502,12 @@ mod tests {
|
||||
..BufferUsage::none()
|
||||
};
|
||||
|
||||
let (buffer, _) =
|
||||
ImmutableBuffer::<[[u8; 4]]>::from_iter((0..128).map(|_| [0; 4]), usage, queue.clone())
|
||||
.unwrap();
|
||||
let (buffer, _) = DeviceLocalBuffer::<[[u8; 4]]>::from_iter(
|
||||
(0..128).map(|_| [0; 4]),
|
||||
usage,
|
||||
queue.clone(),
|
||||
)
|
||||
.unwrap();
|
||||
BufferView::new(
|
||||
buffer,
|
||||
BufferViewCreateInfo {
|
||||
@ -524,7 +529,8 @@ mod tests {
|
||||
};
|
||||
|
||||
let (buffer, _) =
|
||||
ImmutableBuffer::<[u32]>::from_iter((0..128).map(|_| 0), usage, queue.clone()).unwrap();
|
||||
DeviceLocalBuffer::<[u32]>::from_iter((0..128).map(|_| 0), usage, queue.clone())
|
||||
.unwrap();
|
||||
BufferView::new(
|
||||
buffer,
|
||||
BufferViewCreateInfo {
|
||||
@ -540,7 +546,7 @@ mod tests {
|
||||
// `VK_FORMAT_R8G8B8A8_UNORM` guaranteed to be a supported format
|
||||
let (device, queue) = gfx_dev_and_queue!();
|
||||
|
||||
let (buffer, _) = ImmutableBuffer::<[[u8; 4]]>::from_iter(
|
||||
let (buffer, _) = DeviceLocalBuffer::<[[u8; 4]]>::from_iter(
|
||||
(0..128).map(|_| [0; 4]),
|
||||
BufferUsage::none(),
|
||||
queue.clone(),
|
||||
@ -569,7 +575,7 @@ mod tests {
|
||||
..BufferUsage::none()
|
||||
};
|
||||
|
||||
let (buffer, _) = ImmutableBuffer::<[[f64; 4]]>::from_iter(
|
||||
let (buffer, _) = DeviceLocalBuffer::<[[f64; 4]]>::from_iter(
|
||||
(0..128).map(|_| [0.0; 4]),
|
||||
usage,
|
||||
queue.clone(),
|
||||
|
@ -523,7 +523,7 @@ impl std::fmt::Debug for dyn Command {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{
|
||||
buffer::{BufferUsage, CpuAccessibleBuffer, ImmutableBuffer},
|
||||
buffer::{BufferUsage, CpuAccessibleBuffer, DeviceLocalBuffer},
|
||||
command_buffer::{
|
||||
pool::{CommandPool, CommandPoolBuilderAlloc},
|
||||
sys::CommandBufferBeginInfo,
|
||||
@ -571,7 +571,7 @@ mod tests {
|
||||
|
||||
// Create a tiny test buffer
|
||||
let (buf, future) =
|
||||
ImmutableBuffer::from_data(0u32, BufferUsage::transfer_dst(), queue.clone())
|
||||
DeviceLocalBuffer::from_data(0u32, BufferUsage::transfer_dst(), queue.clone())
|
||||
.unwrap();
|
||||
future
|
||||
.then_signal_fence_and_flush()
|
||||
|
Loading…
Reference in New Issue
Block a user