2021-05-29 08:17:36 +10:00
|
|
|
// Copyright 2021 The piet-gpu authors.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// https://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
//
|
|
|
|
// Also licensed under MIT license, at your choice.
|
|
|
|
|
|
|
|
//! The generic trait for backends to implement.
|
|
|
|
|
2022-01-20 06:58:01 +11:00
|
|
|
use crate::{
|
2022-04-14 03:31:38 +10:00
|
|
|
BindType, BufferUsage, ComputePassDescriptor, Error, GpuInfo, ImageFormat, ImageLayout,
|
|
|
|
MapMode, SamplerParams,
|
2022-01-20 06:58:01 +11:00
|
|
|
};
|
2021-05-29 08:17:36 +10:00
|
|
|
|
|
|
|
pub trait Device: Sized {
|
|
|
|
type Buffer: 'static;
|
|
|
|
type Image;
|
|
|
|
type Pipeline;
|
|
|
|
type DescriptorSet;
|
|
|
|
type QueryPool;
|
|
|
|
type CmdBuf: CmdBuf<Self>;
|
|
|
|
type Fence;
|
|
|
|
type Semaphore;
|
|
|
|
type DescriptorSetBuilder: DescriptorSetBuilder<Self>;
|
|
|
|
type Sampler;
|
|
|
|
type ShaderSource: ?Sized;
|
|
|
|
|
|
|
|
/// Query the GPU info.
|
|
|
|
///
|
|
|
|
/// This method may be expensive, so the hub should call it once and retain
|
|
|
|
/// the info.
|
|
|
|
fn query_gpu_info(&self) -> GpuInfo;
|
|
|
|
|
|
|
|
fn create_buffer(&self, size: u64, usage: BufferUsage) -> Result<Self::Buffer, Error>;
|
|
|
|
|
|
|
|
/// Destroy a buffer.
|
|
|
|
///
|
|
|
|
/// The same safety requirements hold as in Vulkan: the buffer cannot be used
|
|
|
|
/// after this call, and all commands referencing this buffer must have completed.
|
|
|
|
///
|
|
|
|
/// Maybe doesn't need result return?
|
|
|
|
unsafe fn destroy_buffer(&self, buffer: &Self::Buffer) -> Result<(), Error>;
|
|
|
|
|
2022-01-20 06:58:01 +11:00
|
|
|
unsafe fn create_image2d(
|
|
|
|
&self,
|
|
|
|
width: u32,
|
|
|
|
height: u32,
|
|
|
|
format: ImageFormat,
|
|
|
|
) -> Result<Self::Image, Error>;
|
2021-05-29 08:17:36 +10:00
|
|
|
|
|
|
|
/// Destroy an image.
|
|
|
|
///
|
|
|
|
/// The same safety requirements hold as in Vulkan: the image cannot be used
|
|
|
|
/// after this call, and all commands referencing this image must have completed.
|
|
|
|
///
|
|
|
|
/// Use this only with images we created, not for swapchain images.
|
|
|
|
///
|
|
|
|
/// Maybe doesn't need result return?
|
|
|
|
unsafe fn destroy_image(&self, image: &Self::Image) -> Result<(), Error>;
|
|
|
|
|
2021-11-10 15:28:06 +11:00
|
|
|
/// Build a compute pipeline.
|
2021-05-29 08:17:36 +10:00
|
|
|
///
|
2021-11-10 15:28:06 +11:00
|
|
|
/// A pipeline is a bit of shader IR plus a signature for what kinds of resources
|
|
|
|
/// it expects.
|
|
|
|
unsafe fn create_compute_pipeline(
|
|
|
|
&self,
|
|
|
|
code: &Self::ShaderSource,
|
|
|
|
bind_types: &[BindType],
|
|
|
|
) -> Result<Self::Pipeline, Error>;
|
|
|
|
|
2021-11-11 07:29:40 +11:00
|
|
|
/// Start building a descriptor set.
|
|
|
|
///
|
2021-05-29 08:17:36 +10:00
|
|
|
/// A descriptor set is a binding of resources for a given pipeline.
|
|
|
|
unsafe fn descriptor_set_builder(&self) -> Self::DescriptorSetBuilder;
|
|
|
|
|
|
|
|
/// Create a descriptor set for a given pipeline, binding buffers and images.
|
|
|
|
///
|
|
|
|
/// This is provided as a convenience but will probably go away, as the functionality
|
|
|
|
/// is subsumed by the builder.
|
|
|
|
unsafe fn create_descriptor_set(
|
|
|
|
&self,
|
|
|
|
pipeline: &Self::Pipeline,
|
|
|
|
bufs: &[&Self::Buffer],
|
|
|
|
images: &[&Self::Image],
|
|
|
|
) -> Result<Self::DescriptorSet, Error> {
|
|
|
|
let mut builder = self.descriptor_set_builder();
|
|
|
|
builder.add_buffers(bufs);
|
|
|
|
builder.add_images(images);
|
|
|
|
builder.build(self, pipeline)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn create_cmd_buf(&self) -> Result<Self::CmdBuf, Error>;
|
|
|
|
|
2021-09-07 03:17:16 +10:00
|
|
|
/// If the command buffer was submitted, it must complete before this is called.
|
|
|
|
unsafe fn destroy_cmd_buf(&self, cmd_buf: Self::CmdBuf) -> Result<(), Error>;
|
|
|
|
|
2021-05-29 08:17:36 +10:00
|
|
|
fn create_query_pool(&self, n_queries: u32) -> Result<Self::QueryPool, Error>;
|
|
|
|
|
|
|
|
/// Get results from query pool, destroying it in the process.
|
|
|
|
///
|
|
|
|
/// The returned vector is one less than the number of queries; the first is used as
|
|
|
|
/// a baseline.
|
|
|
|
///
|
|
|
|
/// # Safety
|
|
|
|
/// All submitted commands that refer to this query pool must have completed.
|
|
|
|
unsafe fn fetch_query_pool(&self, pool: &Self::QueryPool) -> Result<Vec<f64>, Error>;
|
|
|
|
|
|
|
|
unsafe fn run_cmd_bufs(
|
|
|
|
&self,
|
|
|
|
cmd_buf: &[&Self::CmdBuf],
|
|
|
|
wait_semaphores: &[&Self::Semaphore],
|
|
|
|
signal_semaphores: &[&Self::Semaphore],
|
|
|
|
fence: Option<&mut Self::Fence>,
|
|
|
|
) -> Result<(), Error>;
|
|
|
|
|
2021-11-26 08:12:25 +11:00
|
|
|
/// Map the buffer into addressable memory.
|
2021-05-29 08:17:36 +10:00
|
|
|
///
|
|
|
|
/// # Safety
|
|
|
|
///
|
2021-11-26 08:12:25 +11:00
|
|
|
/// The buffer must be valid to access. The offset + size much be within the
|
|
|
|
/// buffer's allocation. The buffer must not already be mapped. Of course,
|
|
|
|
/// the usual safety rules apply to the returned pointer.
|
|
|
|
unsafe fn map_buffer(
|
2021-05-29 08:17:36 +10:00
|
|
|
&self,
|
|
|
|
buffer: &Self::Buffer,
|
|
|
|
offset: u64,
|
|
|
|
size: u64,
|
2021-11-26 08:12:25 +11:00
|
|
|
mode: MapMode,
|
|
|
|
) -> Result<*mut u8, Error>;
|
2021-05-29 08:17:36 +10:00
|
|
|
|
2021-11-26 08:12:25 +11:00
|
|
|
/// Map the buffer into addressable memory.
|
2021-05-29 08:17:36 +10:00
|
|
|
///
|
|
|
|
/// # Safety
|
|
|
|
///
|
2021-11-26 08:12:25 +11:00
|
|
|
/// The buffer must be mapped. The parameters must be the same as the map
|
|
|
|
/// call.
|
|
|
|
unsafe fn unmap_buffer(
|
2021-05-29 08:17:36 +10:00
|
|
|
&self,
|
|
|
|
buffer: &Self::Buffer,
|
|
|
|
offset: u64,
|
|
|
|
size: u64,
|
2021-11-26 08:12:25 +11:00
|
|
|
mode: MapMode,
|
2021-05-29 08:17:36 +10:00
|
|
|
) -> Result<(), Error>;
|
|
|
|
|
|
|
|
unsafe fn create_semaphore(&self) -> Result<Self::Semaphore, Error>;
|
|
|
|
unsafe fn create_fence(&self, signaled: bool) -> Result<Self::Fence, Error>;
|
2021-09-07 03:17:16 +10:00
|
|
|
unsafe fn destroy_fence(&self, fence: Self::Fence) -> Result<(), Error>;
|
2021-05-29 08:17:36 +10:00
|
|
|
unsafe fn wait_and_reset(&self, fences: Vec<&mut Self::Fence>) -> Result<(), Error>;
|
|
|
|
unsafe fn get_fence_status(&self, fence: &mut Self::Fence) -> Result<bool, Error>;
|
|
|
|
|
|
|
|
unsafe fn create_sampler(&self, params: SamplerParams) -> Result<Self::Sampler, Error>;
|
|
|
|
}
|
|
|
|
|
2022-04-14 03:31:38 +10:00
|
|
|
/// The trait implemented by backend command buffer implementations.
|
|
|
|
///
|
|
|
|
/// Valid encoding is represented by a state machine (currently not validated
|
|
|
|
/// but it is easy to imagine there might be at least debug validation). Most
|
|
|
|
/// methods are only valid in a particular state, and some move it to another
|
|
|
|
/// state.
|
2021-05-29 08:17:36 +10:00
|
|
|
pub trait CmdBuf<D: Device> {
|
2022-04-14 03:31:38 +10:00
|
|
|
/// Begin encoding.
|
|
|
|
///
|
|
|
|
/// State: init -> ready
|
2021-05-29 08:17:36 +10:00
|
|
|
unsafe fn begin(&mut self);
|
|
|
|
|
2022-04-14 03:31:38 +10:00
|
|
|
/// State: ready -> finished
|
2021-05-29 08:17:36 +10:00
|
|
|
unsafe fn finish(&mut self);
|
|
|
|
|
2021-10-22 12:07:46 +11:00
|
|
|
/// Return true if the command buffer is suitable for reuse.
|
|
|
|
unsafe fn reset(&mut self) -> bool;
|
|
|
|
|
2022-04-14 03:31:38 +10:00
|
|
|
/// Begin a compute pass.
|
|
|
|
///
|
|
|
|
/// State: ready -> in_compute_pass
|
|
|
|
unsafe fn begin_compute_pass(&mut self, desc: &ComputePassDescriptor);
|
|
|
|
|
|
|
|
/// Dispatch
|
|
|
|
///
|
|
|
|
/// State: in_compute_pass
|
2021-05-29 08:17:36 +10:00
|
|
|
unsafe fn dispatch(
|
|
|
|
&mut self,
|
|
|
|
pipeline: &D::Pipeline,
|
|
|
|
descriptor_set: &D::DescriptorSet,
|
|
|
|
workgroup_count: (u32, u32, u32),
|
|
|
|
workgroup_size: (u32, u32, u32),
|
|
|
|
);
|
|
|
|
|
2022-04-14 03:31:38 +10:00
|
|
|
/// State: in_compute_pass -> ready
|
|
|
|
unsafe fn end_compute_pass(&mut self);
|
|
|
|
|
2021-05-29 08:17:36 +10:00
|
|
|
/// Insert an execution and memory barrier.
|
|
|
|
///
|
|
|
|
/// Compute kernels (and other actions) after this barrier may read from buffers
|
|
|
|
/// that were written before this barrier.
|
|
|
|
unsafe fn memory_barrier(&mut self);
|
|
|
|
|
|
|
|
/// Insert a barrier for host access to buffers.
|
|
|
|
///
|
|
|
|
/// The host may read buffers written before this barrier, after the fence for
|
|
|
|
/// the command buffer is signaled.
|
|
|
|
///
|
|
|
|
/// See http://themaister.net/blog/2019/08/14/yet-another-blog-explaining-vulkan-synchronization/
|
|
|
|
/// ("Host memory reads") for an explanation of this barrier.
|
|
|
|
unsafe fn host_barrier(&mut self);
|
|
|
|
|
|
|
|
unsafe fn image_barrier(
|
|
|
|
&mut self,
|
|
|
|
image: &D::Image,
|
|
|
|
src_layout: ImageLayout,
|
|
|
|
dst_layout: ImageLayout,
|
|
|
|
);
|
|
|
|
|
|
|
|
/// Clear the buffer.
|
|
|
|
///
|
|
|
|
/// This is readily supported in Vulkan, but for portability it is remarkably
|
|
|
|
/// tricky (unimplemented in gfx-hal right now). Possibly best to write a compute
|
|
|
|
/// kernel, or organize the code not to need it.
|
2021-11-19 14:38:31 +11:00
|
|
|
unsafe fn clear_buffer(&mut self, buffer: &D::Buffer, size: Option<u64>);
|
2021-05-29 08:17:36 +10:00
|
|
|
|
2021-11-19 14:38:31 +11:00
|
|
|
unsafe fn copy_buffer(&mut self, src: &D::Buffer, dst: &D::Buffer);
|
2021-05-29 08:17:36 +10:00
|
|
|
|
2021-11-19 14:38:31 +11:00
|
|
|
unsafe fn copy_image_to_buffer(&mut self, src: &D::Image, dst: &D::Buffer);
|
2021-05-29 08:17:36 +10:00
|
|
|
|
2021-11-19 14:38:31 +11:00
|
|
|
unsafe fn copy_buffer_to_image(&mut self, src: &D::Buffer, dst: &D::Image);
|
2021-05-29 08:17:36 +10:00
|
|
|
|
|
|
|
// low portability, dx12 doesn't support it natively
|
2021-11-19 14:38:31 +11:00
|
|
|
unsafe fn blit_image(&mut self, src: &D::Image, dst: &D::Image);
|
2021-05-29 08:17:36 +10:00
|
|
|
|
|
|
|
/// Reset the query pool.
|
|
|
|
///
|
|
|
|
/// The query pool must be reset before each use, to avoid validation errors.
|
|
|
|
/// This is annoying, and we could tweak the API to make it implicit, doing
|
|
|
|
/// the reset before the first timestamp write.
|
|
|
|
unsafe fn reset_query_pool(&mut self, pool: &D::QueryPool);
|
|
|
|
|
|
|
|
unsafe fn write_timestamp(&mut self, pool: &D::QueryPool, query: u32);
|
|
|
|
|
|
|
|
/// Prepare the timestamps for reading. This isn't required on Vulkan but
|
|
|
|
/// is required on (at least) DX12.
|
|
|
|
unsafe fn finish_timestamps(&mut self, _pool: &D::QueryPool) {}
|
2022-01-21 13:30:30 +11:00
|
|
|
|
|
|
|
/// Begin a labeled section for debugging and profiling purposes.
|
2022-04-14 03:31:38 +10:00
|
|
|
unsafe fn begin_debug_label(&mut self, _label: &str) {}
|
2022-01-21 13:30:30 +11:00
|
|
|
|
|
|
|
/// End a section opened by `begin_debug_label`.
|
|
|
|
unsafe fn end_debug_label(&mut self) {}
|
2021-05-29 08:17:36 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
/// A builder for descriptor sets with more complex layouts.
|
|
|
|
///
|
|
|
|
/// Note: the order needs to match the pipeline building, and it also needs to
|
|
|
|
/// be buffers, then images, then textures.
|
|
|
|
pub trait DescriptorSetBuilder<D: Device> {
|
|
|
|
fn add_buffers(&mut self, buffers: &[&D::Buffer]);
|
|
|
|
/// Add an array of storage images.
|
|
|
|
///
|
|
|
|
/// The images need to be in `ImageLayout::General` layout.
|
|
|
|
fn add_images(&mut self, images: &[&D::Image]);
|
|
|
|
/// Add an array of textures.
|
|
|
|
///
|
|
|
|
/// The images need to be in `ImageLayout::ShaderRead` layout.
|
|
|
|
///
|
|
|
|
/// The same sampler is used for all textures, which is not very sophisticated;
|
|
|
|
/// we should have a way to vary the sampler.
|
|
|
|
fn add_textures(&mut self, images: &[&D::Image]);
|
|
|
|
unsafe fn build(self, device: &D, pipeline: &D::Pipeline) -> Result<D::DescriptorSet, Error>;
|
|
|
|
}
|