Specialize the gpu_access function ; one for buffers one for images

This commit is contained in:
Pierre Krieger 2016-02-19 21:19:41 +01:00
parent 04c462b5a2
commit 43d9c80888
5 changed files with 133 additions and 91 deletions

View File

@ -17,6 +17,7 @@
//!
use std::marker::PhantomData;
use std::mem;
use std::ops::Range;
use std::ptr;
use std::sync::Arc;
@ -39,6 +40,32 @@ use VulkanPointers;
use check_errors;
use vk;
pub unsafe trait BufferResource: Resource {
/// Instructs the resource that it is going to be used by the GPU soon in the future. The
/// function should block if the memory is currently being accessed by the CPU.
///
/// `write` indicates whether the GPU will write to the memory. If `false`, then it will only
/// be written.
///
/// `queue` is the queue where the command buffer that accesses the memory will be submitted.
/// If the `gpu_access` function submits something to that queue, it will thus be submitted
/// beforehand. This behavior can be used for example to submit sparse binding commands.
///
/// `fence` is a fence that will be signaled when this GPU access will stop. It should be
/// waited upon whenever the user wants to read this memory from the CPU. If `requires_fence`
/// returned false, then this value will be `None`.
///
/// `semaphore` is a semaphore that will be signaled when this GPU access will stop. This value
/// is intended to be returned later, in a follow-up call to `gpu_access`. If
/// `requires_semaphore` returned false, then this value will be `None`.
///
/// The function can return a semaphore which will be waited up by the GPU before the
/// work starts.
fn gpu_access(&self, write: bool, offset: usize, size: usize, queue: &mut Queue,
fence: Option<Arc<Fence>>, semaphore: Option<Arc<Semaphore>>)
-> Option<Arc<Semaphore>>;
}
pub struct Buffer<T: ?Sized, M> {
marker: PhantomData<T>,
inner: Inner<M>,
@ -205,6 +232,17 @@ impl<T: ?Sized, M> Buffer<T, M> {
pub fn write(&self) -> Result<ReadWrite, > {
}*/
/// Builds a slice without checking neither the type nor the range.
#[inline]
pub unsafe fn unchecked_slice<U: ?Sized>(&self, range: Range<usize>) -> BufferSlice<U, M> {
BufferSlice {
marker: PhantomData,
inner: &self.inner,
offset: range.start,
size: range.end - range.start,
}
}
}
impl<T, M> Buffer<[T], M> {
@ -230,12 +268,16 @@ unsafe impl<T: ?Sized, M> Resource for Buffer<T, M> where M: MemorySourceChunk {
fn sharing_mode(&self) -> &SharingMode {
&self.inner.sharing
}
}
unsafe impl<T: ?Sized, M> BufferResource for Buffer<T, M> where M: MemorySourceChunk {
#[inline]
fn gpu_access(&self, write: bool, queue: &mut Queue, fence: Option<Arc<Fence>>,
semaphore: Option<Arc<Semaphore>>) -> Option<Arc<Semaphore>>
fn gpu_access(&self, write: bool, offset: usize, size: usize, queue: &mut Queue,
fence: Option<Arc<Fence>>, semaphore: Option<Arc<Semaphore>>)
-> Option<Arc<Semaphore>>
{
self.inner.memory.gpu_access(write, ChunkRange::All, queue, fence, semaphore)
self.inner.memory.gpu_access(write, ChunkRange::Range { offset: offset, size: size },
queue, fence, semaphore)
}
}
@ -387,23 +429,6 @@ pub struct BufferSlice<'a, T: ?Sized + 'a, M: 'a> {
size: usize,
}
unsafe impl<'a, T: ?Sized + 'a, M: 'a> Resource for BufferSlice<'a, T, M>
where M: MemorySourceChunk
{
#[inline]
fn sharing_mode(&self) -> &SharingMode {
&self.inner.sharing
}
#[inline]
fn gpu_access(&self, write: bool, queue: &mut Queue, fence: Option<Arc<Fence>>,
semaphore: Option<Arc<Semaphore>>) -> Option<Arc<Semaphore>>
{
self.inner.memory.gpu_access(write, ChunkRange::Range { offset: self.offset, size: self.size },
queue, fence, semaphore)
}
}
impl<'a, T: ?Sized + 'a, M: 'a> BufferSlice<'a, T, M> {
/// Returns the offset of that slice within the buffer.
#[inline]

View File

@ -4,6 +4,7 @@ use std::sync::Arc;
use buffer::Buffer;
use buffer::BufferSlice;
use buffer::BufferResource;
use command_buffer::CommandBufferPool;
use command_buffer::DynamicState;
use device::Queue;
@ -36,10 +37,10 @@ pub struct InnerCommandBufferBuilder {
cmd: Option<vk::CommandBuffer>,
// List of all resources that are used by this command buffer.
resources: Vec<Arc<Resource>>,
buffer_resources: Vec<Arc<BufferResource>>,
// Same as `resources`. Should be merged with `resources` once Rust allows turning a
// `Arc<ImageResource>` into an `Arc<Resource>`.
// `Arc<ImageResource>` into an `Arc<BufferResource>`.
image_resources: Vec<Arc<ImageResource>>,
// List of pipelines that are used by this command buffer.
@ -100,7 +101,7 @@ impl InnerCommandBufferBuilder {
device: device.clone(),
pool: pool.clone(),
cmd: Some(cmd),
resources: Vec::new(),
buffer_resources: Vec::new(),
image_resources: Vec::new(),
pipelines: Vec::new(),
graphics_pipeline: None,
@ -124,7 +125,8 @@ impl InnerCommandBufferBuilder {
for cb in iter {
command_buffers.push(cb.cmd);
for p in cb.pipelines.iter() { self.pipelines.push(p.clone()); }
for r in cb.resources.iter() { self.resources.push(r.clone()); }
for r in cb.buffer_resources.iter() { self.buffer_resources.push(r.clone()); }
for r in cb.image_resources.iter() { self.image_resources.push(r.clone()); }
}
let vk = self.device.pointers();
@ -203,7 +205,7 @@ impl InnerCommandBufferBuilder {
assert!(size % 4 == 0);
assert!(buffer.usage_transfer_dest());
self.resources.push(buffer.clone());
self.buffer_resources.push(buffer.clone());
// FIXME: check that the queue family supports transfers
// FIXME: check queue family of the buffer
@ -251,8 +253,8 @@ impl InnerCommandBufferBuilder {
vk.CmdCopyBuffer(self.cmd.unwrap(), source.internal_object(),
destination.internal_object(), 1, &copy);
self.resources.push(source.clone());
self.resources.push(destination.clone());
self.buffer_resources.push(source.clone());
self.buffer_resources.push(destination.clone());
}
self
@ -427,7 +429,7 @@ impl InnerCommandBufferBuilder {
device: self.device.clone(),
pool: self.pool.clone(),
cmd: cmd,
resources: mem::replace(&mut self.resources, Vec::new()),
buffer_resources: mem::replace(&mut self.buffer_resources, Vec::new()),
image_resources: mem::replace(&mut self.image_resources, Vec::new()),
pipelines: mem::replace(&mut self.pipelines, Vec::new()),
})
@ -454,7 +456,7 @@ pub struct InnerCommandBuffer {
device: Arc<Device>,
pool: Arc<CommandBufferPool>,
cmd: vk::CommandBuffer,
resources: Vec<Arc<Resource>>,
buffer_resources: Vec<Arc<BufferResource>>,
image_resources: Vec<Arc<ImageResource>>,
pipelines: Vec<Arc<GenericPipeline>>,
}
@ -478,7 +480,7 @@ impl InnerCommandBuffer {
// FIXME: fence shouldn't be discarded, as it could be ignored by resources and
// destroyed while in use
let fence = if self.resources.iter().any(|r| r.requires_fence()) ||
let fence = if self.buffer_resources.iter().any(|r| r.requires_fence()) ||
self.image_resources.iter().any(|r| r.requires_fence())
{
Some(try!(Fence::new(queue.device())))
@ -496,33 +498,48 @@ impl InnerCommandBuffer {
// they should be included in a return value instead
// FIXME: same for post-semaphores
macro_rules! process {
($iter:expr) => (
for resource in $iter {
let post_semaphore = if resource.requires_semaphore() {
let semaphore = try!(Semaphore::new(queue.device()));
post_semaphores.push(semaphore.clone());
post_semaphores_ids.push(semaphore.internal_object());
Some(semaphore)
for resource in self.buffer_resources.iter() {
let post_semaphore = if resource.requires_semaphore() {
let semaphore = try!(Semaphore::new(queue.device()));
post_semaphores.push(semaphore.clone());
post_semaphores_ids.push(semaphore.internal_object());
Some(semaphore)
} else {
None
};
} else {
None
};
// FIXME: for the moment `write` is always true ; that shouldn't be the case
let sem = resource.gpu_access(true, queue, fence.clone(), post_semaphore);
// FIXME: for the moment `write` is always true ; that shouldn't be the case
// FIXME: wrong offset and size
let sem = resource.gpu_access(true, 0, 18, queue, fence.clone(), post_semaphore);
if let Some(s) = sem {
pre_semaphores_ids.push(s.internal_object());
pre_semaphores_stages.push(vk::PIPELINE_STAGE_TOP_OF_PIPE_BIT); // TODO:
pre_semaphores.push(s);
}
}
);
if let Some(s) = sem {
pre_semaphores_ids.push(s.internal_object());
pre_semaphores_stages.push(vk::PIPELINE_STAGE_TOP_OF_PIPE_BIT); // TODO:
pre_semaphores.push(s);
}
}
process!(self.resources.iter());
process!(self.image_resources.iter());
for resource in self.image_resources.iter() {
let post_semaphore = if resource.requires_semaphore() {
let semaphore = try!(Semaphore::new(queue.device()));
post_semaphores.push(semaphore.clone());
post_semaphores_ids.push(semaphore.internal_object());
Some(semaphore)
} else {
None
};
// FIXME: for the moment `write` is always true ; that shouldn't be the case
let sem = resource.gpu_access(true, queue, fence.clone(), post_semaphore);
if let Some(s) = sem {
pre_semaphores_ids.push(s.internal_object());
pre_semaphores_stages.push(vk::PIPELINE_STAGE_TOP_OF_PIPE_BIT); // TODO:
pre_semaphores.push(s);
}
}
let infos = vk::SubmitInfo {
sType: vk::STRUCTURE_TYPE_SUBMIT_INFO,

View File

@ -43,6 +43,29 @@ pub unsafe trait ImageResource: Resource {
/// command buffer, it is switched from this default layout to something else (if necessary),
/// then back again to the default.
fn default_layout(&self) -> Layout;
/// Instructs the resource that it is going to be used by the GPU soon in the future. The
/// function should block if the memory is currently being accessed by the CPU.
///
/// `write` indicates whether the GPU will write to the memory. If `false`, then it will only
/// be written.
///
/// `queue` is the queue where the command buffer that accesses the memory will be submitted.
/// If the `gpu_access` function submits something to that queue, it will thus be submitted
/// beforehand. This behavior can be used for example to submit sparse binding commands.
///
/// `fence` is a fence that will be signaled when this GPU access will stop. It should be
/// waited upon whenever the user wants to read this memory from the CPU. If `requires_fence`
/// returned false, then this value will be `None`.
///
/// `semaphore` is a semaphore that will be signaled when this GPU access will stop. This value
/// is intended to be returned later, in a follow-up call to `gpu_access`. If
/// `requires_semaphore` returned false, then this value will be `None`.
///
/// The function can return a semaphore which will be waited up by the GPU before the
/// work starts.
fn gpu_access(&self, write: bool, queue: &mut Queue, fence: Option<Arc<Fence>>,
semaphore: Option<Arc<Semaphore>>) -> Option<Arc<Semaphore>>;
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
@ -387,14 +410,6 @@ unsafe impl<Ty, F, M> Resource for Image<Ty, F, M>
fn sharing_mode(&self) -> &SharingMode {
&self.sharing
}
#[inline]
fn gpu_access(&self, write: bool, queue: &mut Queue, fence: Option<Arc<Fence>>,
semaphore: Option<Arc<Semaphore>>) -> Option<Arc<Semaphore>>
{
// FIXME: if the image is in its initial transition phase, we need to a semaphore
self.memory.gpu_access(write, ChunkRange::All, queue, fence, semaphore)
}
}
unsafe impl<Ty, F, M> ImageResource for Image<Ty, F, M>
@ -404,6 +419,14 @@ unsafe impl<Ty, F, M> ImageResource for Image<Ty, F, M>
fn default_layout(&self) -> Layout {
self.layout
}
#[inline]
fn gpu_access(&self, write: bool, queue: &mut Queue, fence: Option<Arc<Fence>>,
semaphore: Option<Arc<Semaphore>>) -> Option<Arc<Semaphore>>
{
// FIXME: if the image is in its initial transition phase, we need to a semaphore
self.memory.gpu_access(write, ChunkRange::All, queue, fence, semaphore)
}
}
impl<Ty, F, M> Drop for Image<Ty, F, M>
@ -665,13 +688,6 @@ unsafe impl<Ty, F, M> Resource for ImageView<Ty, F, M>
fn sharing_mode(&self) -> &SharingMode {
self.image.sharing_mode()
}
#[inline]
fn gpu_access(&self, write: bool, queue: &mut Queue, fence: Option<Arc<Fence>>,
semaphore: Option<Arc<Semaphore>>) -> Option<Arc<Semaphore>>
{
self.image.gpu_access(write, queue, fence, semaphore)
}
}
unsafe impl<Ty, F, M> ImageResource for ImageView<Ty, F, M>
@ -681,6 +697,13 @@ unsafe impl<Ty, F, M> ImageResource for ImageView<Ty, F, M>
fn default_layout(&self) -> Layout {
self.image.default_layout()
}
#[inline]
fn gpu_access(&self, write: bool, queue: &mut Queue, fence: Option<Arc<Fence>>,
semaphore: Option<Arc<Semaphore>>) -> Option<Arc<Semaphore>>
{
self.image.gpu_access(write, queue, fence, semaphore)
}
}
impl<Ty, F, M> Drop for ImageView<Ty, F, M> where Ty: ImageTypeMarker {

View File

@ -45,7 +45,7 @@ impl Swapchain {
///
/// See also the `Surface::get_capabilities` function which returns the values that are
/// supported by the implementation. All the parameters that you pass to `Swapchain::new`
/// must be supported.
/// must be supported.
///
/// The `clipped` parameter indicates whether the implementation is allowed to discard
/// rendering operations that affect regions of the surface which aren't visible. This is

View File

@ -39,29 +39,6 @@ pub unsafe trait Resource {
fn requires_semaphore(&self) -> bool {
true
}
/// Instructs the resource that it is going to be used by the GPU soon in the future. The
/// function should block if the memory is currently being accessed by the CPU.
///
/// `write` indicates whether the GPU will write to the memory. If `false`, then it will only
/// be written.
///
/// `queue` is the queue where the command buffer that accesses the memory will be submitted.
/// If the `gpu_access` function submits something to that queue, it will thus be submitted
/// beforehand. This behavior can be used for example to submit sparse binding commands.
///
/// `fence` is a fence that will be signaled when this GPU access will stop. It should be
/// waited upon whenever the user wants to read this memory from the CPU. If `requires_fence`
/// returned false, then this value will be `None`.
///
/// `semaphore` is a semaphore that will be signaled when this GPU access will stop. This value
/// is intended to be returned later, in a follow-up call to `gpu_access`. If
/// `requires_semaphore` returned false, then this value will be `None`.
///
/// The function can return a semaphore which will be waited up by the GPU before the
/// work starts.
fn gpu_access(&self, write: bool, queue: &mut Queue, fence: Option<Arc<Fence>>,
semaphore: Option<Arc<Semaphore>>) -> Option<Arc<Semaphore>>;
}
/// Declares in which queue(s) a resource can be used.