mirror of
https://github.com/vulkano-rs/vulkano.git
synced 2024-11-22 14:56:42 +00:00
Merge branch 'master' into moltenvk
This commit is contained in:
commit
41eb21c291
@ -100,7 +100,7 @@ fn main() {
|
||||
// Create an image in order to generate some additional logging:
|
||||
let pixel_format = Format::R8G8B8A8Uint;
|
||||
let dimensions = Dimensions::Dim2d { width: 4096, height: 4096 };
|
||||
ImmutableImage::new(&device, dimensions, pixel_format, Some(queue.family())).unwrap();
|
||||
ImmutableImage::new(device.clone(), dimensions, pixel_format, Some(queue.family())).unwrap();
|
||||
|
||||
// (At this point you should see a bunch of messages printed to the terminal window - have fun debugging!)
|
||||
}
|
||||
|
@ -34,9 +34,9 @@ fn main() {
|
||||
println!("Using device: {} (type: {:?})", physical.name(), physical.ty());
|
||||
|
||||
let events_loop = winit::EventsLoop::new();
|
||||
let window = winit::WindowBuilder::new().build_vk_surface(&events_loop, &instance).unwrap();
|
||||
let window = winit::WindowBuilder::new().build_vk_surface(&events_loop, instance.clone()).unwrap();
|
||||
|
||||
let queue = physical.queue_families().find(|q| q.supports_graphics() &&
|
||||
let queue = physical.queue_families().find(|&q| q.supports_graphics() &&
|
||||
window.surface().is_supported(q).unwrap_or(false))
|
||||
.expect("couldn't find a graphical queue family");
|
||||
|
||||
@ -50,15 +50,15 @@ fn main() {
|
||||
let queue = queues.next().unwrap();
|
||||
|
||||
let (swapchain, images) = {
|
||||
let caps = window.surface().get_capabilities(&physical).expect("failed to get surface capabilities");
|
||||
let caps = window.surface().capabilities(physical).expect("failed to get surface capabilities");
|
||||
|
||||
let dimensions = caps.current_extent.unwrap_or([1280, 1024]);
|
||||
let present = caps.present_modes.iter().next().unwrap();
|
||||
let usage = caps.supported_usage_flags;
|
||||
|
||||
vulkano::swapchain::Swapchain::new(&device, &window.surface(), caps.min_image_count,
|
||||
vulkano::swapchain::Swapchain::new(device.clone(), window.surface().clone(), caps.min_image_count,
|
||||
vulkano::format::B8G8R8A8Srgb, dimensions, 1,
|
||||
&usage, &queue, vulkano::swapchain::SurfaceTransform::Identity,
|
||||
usage, &queue, vulkano::swapchain::SurfaceTransform::Identity,
|
||||
vulkano::swapchain::CompositeAlpha::Opaque,
|
||||
present, true, None).expect("failed to create swapchain")
|
||||
};
|
||||
@ -69,7 +69,7 @@ fn main() {
|
||||
impl_vertex!(Vertex, position);
|
||||
|
||||
let vertex_buffer = vulkano::buffer::cpu_access::CpuAccessibleBuffer::<[Vertex]>
|
||||
::from_iter(&device, &vulkano::buffer::BufferUsage::all(),
|
||||
::from_iter(device.clone(), vulkano::buffer::BufferUsage::all(),
|
||||
Some(queue.family()), [
|
||||
Vertex { position: [-0.5, -0.5 ] },
|
||||
Vertex { position: [-0.5, 0.5 ] },
|
||||
@ -88,7 +88,7 @@ fn main() {
|
||||
color: {
|
||||
load: Clear,
|
||||
store: Store,
|
||||
format: images[0].format(),
|
||||
format: swapchain.format(),
|
||||
samples: 1,
|
||||
}
|
||||
},
|
||||
@ -99,7 +99,7 @@ fn main() {
|
||||
).unwrap()
|
||||
);
|
||||
|
||||
let texture = vulkano::image::immutable::ImmutableImage::new(&device, vulkano::image::Dimensions::Dim2d { width: 93, height: 93 },
|
||||
let texture = vulkano::image::immutable::ImmutableImage::new(device.clone(), vulkano::image::Dimensions::Dim2d { width: 93, height: 93 },
|
||||
vulkano::format::R8G8B8A8Unorm, Some(queue.family())).unwrap();
|
||||
|
||||
|
||||
@ -112,20 +112,20 @@ fn main() {
|
||||
|
||||
// TODO: staging buffer instead
|
||||
vulkano::buffer::cpu_access::CpuAccessibleBuffer::<[[u8; 4]]>
|
||||
::from_iter(&device, &vulkano::buffer::BufferUsage::all(),
|
||||
::from_iter(device.clone(), vulkano::buffer::BufferUsage::all(),
|
||||
Some(queue.family()), image_data_chunks)
|
||||
.expect("failed to create buffer")
|
||||
};
|
||||
|
||||
|
||||
let sampler = vulkano::sampler::Sampler::new(&device, vulkano::sampler::Filter::Linear,
|
||||
let sampler = vulkano::sampler::Sampler::new(device.clone(), vulkano::sampler::Filter::Linear,
|
||||
vulkano::sampler::Filter::Linear, vulkano::sampler::MipmapMode::Nearest,
|
||||
vulkano::sampler::SamplerAddressMode::Repeat,
|
||||
vulkano::sampler::SamplerAddressMode::Repeat,
|
||||
vulkano::sampler::SamplerAddressMode::Repeat,
|
||||
0.0, 1.0, 0.0, 0.0).unwrap();
|
||||
|
||||
let pipeline = Arc::new(vulkano::pipeline::GraphicsPipeline::new(&device, vulkano::pipeline::GraphicsPipelineParams {
|
||||
let pipeline = Arc::new(vulkano::pipeline::GraphicsPipeline::new(device.clone(), vulkano::pipeline::GraphicsPipelineParams {
|
||||
vertex_input: vulkano::pipeline::vertex::SingleBufferDefinition::new(),
|
||||
vertex_shader: vs.main_entry_point(),
|
||||
input_assembly: vulkano::pipeline::input_assembly::InputAssembly {
|
||||
@ -157,21 +157,16 @@ fn main() {
|
||||
}));
|
||||
|
||||
let framebuffers = images.iter().map(|image| {
|
||||
let attachments = renderpass.desc().start_attachments()
|
||||
.color(image.clone());
|
||||
let dimensions = [image.dimensions()[0], image.dimensions()[1], 1];
|
||||
|
||||
vulkano::framebuffer::Framebuffer::new(renderpass.clone(), dimensions, attachments).unwrap()
|
||||
Arc::new(vulkano::framebuffer::Framebuffer::start(renderpass.clone())
|
||||
.add(image.clone()).unwrap().build().unwrap())
|
||||
}).collect::<Vec<_>>();
|
||||
|
||||
let mut submissions: Vec<Box<GpuFuture>> = Vec::new();
|
||||
let mut previous_frame_end = Box::new(vulkano::sync::now(device.clone())) as Box<GpuFuture>;
|
||||
|
||||
loop {
|
||||
while submissions.len() >= 4 {
|
||||
submissions.remove(0);
|
||||
}
|
||||
previous_frame_end.cleanup_finished();
|
||||
|
||||
let (image_num, future) = swapchain.acquire_next_image(Duration::new(10, 0)).unwrap();
|
||||
let (image_num, future) = vulkano::swapchain::acquire_next_image(swapchain.clone(), Duration::new(10, 0)).unwrap();
|
||||
|
||||
let cb = vulkano::command_buffer::AutoCommandBufferBuilder::new(device.clone(), queue.family())
|
||||
.unwrap()
|
||||
@ -180,18 +175,17 @@ fn main() {
|
||||
//.clear_color_image(&texture, [0.0, 1.0, 0.0, 1.0])
|
||||
.begin_render_pass(
|
||||
framebuffers[image_num].clone(), false,
|
||||
renderpass.desc().start_clear_values()
|
||||
.color([0.0, 0.0, 1.0, 1.0])).unwrap()
|
||||
vec![[0.0, 0.0, 1.0, 1.0].into()]).unwrap()
|
||||
.draw(pipeline.clone(), vulkano::command_buffer::DynamicState::none(), vertex_buffer.clone(),
|
||||
set.clone(), ()).unwrap()
|
||||
.end_render_pass().unwrap()
|
||||
.build().unwrap();
|
||||
|
||||
let future = future
|
||||
let future = previous_frame_end.join(future)
|
||||
.then_execute(queue.clone(), cb).unwrap()
|
||||
.then_swapchain_present(queue.clone(), swapchain.clone(), image_num)
|
||||
.then_signal_fence_and_flush().unwrap();
|
||||
submissions.push(Box::new(future) as Box<_>);
|
||||
previous_frame_end = Box::new(future) as Box<_>;
|
||||
|
||||
let mut done = false;
|
||||
events_loop.poll_events(|ev| {
|
||||
|
@ -38,9 +38,9 @@ fn main() {
|
||||
println!("Using device: {} (type: {:?})", physical.name(), physical.ty());
|
||||
|
||||
let events_loop = winit::EventsLoop::new();
|
||||
let window = winit::WindowBuilder::new().build_vk_surface(&events_loop, &instance).unwrap();
|
||||
let window = winit::WindowBuilder::new().build_vk_surface(&events_loop, instance.clone()).unwrap();
|
||||
|
||||
let queue = physical.queue_families().find(|q| q.supports_graphics() &&
|
||||
let queue = physical.queue_families().find(|&q| q.supports_graphics() &&
|
||||
window.surface().is_supported(q).unwrap_or(false))
|
||||
.expect("couldn't find a graphical queue family");
|
||||
|
||||
@ -55,32 +55,32 @@ fn main() {
|
||||
let queue = queues.next().unwrap();
|
||||
|
||||
let (swapchain, images) = {
|
||||
let caps = window.surface().get_capabilities(&physical).expect("failed to get surface capabilities");
|
||||
let caps = window.surface().capabilities(physical).expect("failed to get surface capabilities");
|
||||
|
||||
let dimensions = caps.current_extent.unwrap_or([1280, 1024]);
|
||||
let present = caps.present_modes.iter().next().unwrap();
|
||||
let usage = caps.supported_usage_flags;
|
||||
let format = caps.supported_formats[0].0;
|
||||
|
||||
vulkano::swapchain::Swapchain::new(&device, &window.surface(), caps.min_image_count, format, dimensions, 1,
|
||||
&usage, &queue, vulkano::swapchain::SurfaceTransform::Identity,
|
||||
vulkano::swapchain::Swapchain::new(device.clone(), window.surface().clone(), caps.min_image_count, format, dimensions, 1,
|
||||
usage, &queue, vulkano::swapchain::SurfaceTransform::Identity,
|
||||
vulkano::swapchain::CompositeAlpha::Opaque,
|
||||
present, true, None).expect("failed to create swapchain")
|
||||
};
|
||||
|
||||
|
||||
let depth_buffer = vulkano::image::attachment::AttachmentImage::transient(&device, images[0].dimensions(), vulkano::format::D16Unorm).unwrap().access();
|
||||
let depth_buffer = vulkano::image::attachment::AttachmentImage::transient(device.clone(), images[0].dimensions(), vulkano::format::D16Unorm).unwrap();
|
||||
|
||||
let vertex_buffer = vulkano::buffer::cpu_access::CpuAccessibleBuffer
|
||||
::from_iter(&device, &vulkano::buffer::BufferUsage::all(), Some(queue.family()), examples::VERTICES.iter().cloned())
|
||||
::from_iter(device.clone(), vulkano::buffer::BufferUsage::all(), Some(queue.family()), examples::VERTICES.iter().cloned())
|
||||
.expect("failed to create buffer");
|
||||
|
||||
let normals_buffer = vulkano::buffer::cpu_access::CpuAccessibleBuffer
|
||||
::from_iter(&device, &vulkano::buffer::BufferUsage::all(), Some(queue.family()), examples::NORMALS.iter().cloned())
|
||||
::from_iter(device.clone(), vulkano::buffer::BufferUsage::all(), Some(queue.family()), examples::NORMALS.iter().cloned())
|
||||
.expect("failed to create buffer");
|
||||
|
||||
let index_buffer = vulkano::buffer::cpu_access::CpuAccessibleBuffer
|
||||
::from_iter(&device, &vulkano::buffer::BufferUsage::all(), Some(queue.family()), examples::INDICES.iter().cloned())
|
||||
::from_iter(device.clone(), vulkano::buffer::BufferUsage::all(), Some(queue.family()), examples::INDICES.iter().cloned())
|
||||
.expect("failed to create buffer");
|
||||
|
||||
// note: this teapot was meant for OpenGL where the origin is at the lower left
|
||||
@ -90,7 +90,7 @@ fn main() {
|
||||
let scale = cgmath::Matrix4::from_scale(0.01);
|
||||
|
||||
let uniform_buffer = vulkano::buffer::cpu_access::CpuAccessibleBuffer::<vs::ty::Data>
|
||||
::from_data(&device, &vulkano::buffer::BufferUsage::all(), Some(queue.family()),
|
||||
::from_data(device.clone(), vulkano::buffer::BufferUsage::all(), Some(queue.family()),
|
||||
vs::ty::Data {
|
||||
world : <cgmath::Matrix4<f32> as cgmath::SquareMatrix>::identity().into(),
|
||||
view : (view * scale).into(),
|
||||
@ -107,13 +107,13 @@ fn main() {
|
||||
color: {
|
||||
load: Clear,
|
||||
store: Store,
|
||||
format: images[0].format(),
|
||||
format: swapchain.format(),
|
||||
samples: 1,
|
||||
},
|
||||
depth: {
|
||||
load: Clear,
|
||||
store: DontCare,
|
||||
format: vulkano::image::ImageAccess::format(&depth_buffer),
|
||||
format: vulkano::format::Format::D16Unorm,
|
||||
samples: 1,
|
||||
}
|
||||
},
|
||||
@ -124,7 +124,7 @@ fn main() {
|
||||
).unwrap()
|
||||
);
|
||||
|
||||
let pipeline = Arc::new(vulkano::pipeline::GraphicsPipeline::new(&device, vulkano::pipeline::GraphicsPipelineParams {
|
||||
let pipeline = Arc::new(vulkano::pipeline::GraphicsPipeline::new(device.clone(), vulkano::pipeline::GraphicsPipelineParams {
|
||||
vertex_input: vulkano::pipeline::vertex::TwoBuffersDefinition::new(),
|
||||
vertex_shader: vs.main_entry_point(),
|
||||
input_assembly: vulkano::pipeline::input_assembly::InputAssembly::triangle_list(),
|
||||
@ -153,20 +153,17 @@ fn main() {
|
||||
}));
|
||||
|
||||
let framebuffers = images.iter().map(|image| {
|
||||
let attachments = renderpass.desc().start_attachments()
|
||||
.color(image.clone()).depth(depth_buffer.clone());
|
||||
let dimensions = [image.dimensions()[0], image.dimensions()[1], 1];
|
||||
|
||||
vulkano::framebuffer::Framebuffer::new(renderpass.clone(), dimensions, attachments).unwrap()
|
||||
Arc::new(vulkano::framebuffer::Framebuffer::start(renderpass.clone())
|
||||
.add(image.clone()).unwrap()
|
||||
.add(depth_buffer.clone()).unwrap()
|
||||
.build().unwrap())
|
||||
}).collect::<Vec<_>>();
|
||||
|
||||
|
||||
let mut submissions: Vec<Box<GpuFuture>> = Vec::new();
|
||||
let mut previous_frame = Box::new(vulkano::sync::now(device.clone())) as Box<GpuFuture>;
|
||||
|
||||
loop {
|
||||
while submissions.len() >= 4 {
|
||||
submissions.remove(0);
|
||||
}
|
||||
previous_frame.cleanup_finished();
|
||||
|
||||
{
|
||||
// aquiring write lock for the uniform buffer
|
||||
@ -179,13 +176,15 @@ fn main() {
|
||||
buffer_content.world = cgmath::Matrix4::from(rotation).into();
|
||||
}
|
||||
|
||||
let (image_num, future) = swapchain.acquire_next_image(std::time::Duration::new(1, 0)).unwrap();
|
||||
let (image_num, acquire_future) = vulkano::swapchain::acquire_next_image(swapchain.clone(), std::time::Duration::new(1, 0)).unwrap();
|
||||
|
||||
let command_buffer = vulkano::command_buffer::AutoCommandBufferBuilder::new(device.clone(), queue.family()).unwrap()
|
||||
.begin_render_pass(
|
||||
framebuffers[image_num].clone(), false,
|
||||
renderpass.desc().start_clear_values()
|
||||
.color([0.0, 0.0, 1.0, 1.0]).depth((1f32))).unwrap()
|
||||
vec![
|
||||
[0.0, 0.0, 1.0, 1.0].into(),
|
||||
1f32.into()
|
||||
]).unwrap()
|
||||
.draw_indexed(
|
||||
pipeline.clone(), vulkano::command_buffer::DynamicState::none(),
|
||||
(vertex_buffer.clone(), normals_buffer.clone()),
|
||||
@ -193,11 +192,11 @@ fn main() {
|
||||
.end_render_pass().unwrap()
|
||||
.build().unwrap();
|
||||
|
||||
let future = future
|
||||
let future = previous_frame.join(acquire_future)
|
||||
.then_execute(queue.clone(), command_buffer).unwrap()
|
||||
.then_swapchain_present(queue.clone(), swapchain.clone(), image_num)
|
||||
.then_signal_fence_and_flush().unwrap();
|
||||
submissions.push(Box::new(future) as Box<_>);
|
||||
previous_frame = Box::new(future) as Box<_>;
|
||||
|
||||
let mut done = false;
|
||||
events_loop.poll_events(|ev| {
|
||||
|
@ -53,8 +53,10 @@ use vulkano::pipeline::vertex::SingleBufferDefinition;
|
||||
use vulkano::pipeline::viewport::ViewportsState;
|
||||
use vulkano::pipeline::viewport::Viewport;
|
||||
use vulkano::pipeline::viewport::Scissor;
|
||||
use vulkano::swapchain;
|
||||
use vulkano::swapchain::SurfaceTransform;
|
||||
use vulkano::swapchain::Swapchain;
|
||||
use vulkano::sync::now;
|
||||
use vulkano::sync::GpuFuture;
|
||||
|
||||
use std::sync::Arc;
|
||||
@ -105,7 +107,7 @@ fn main() {
|
||||
// This returns a `vulkano_win::Window` object that contains both a cross-platform winit
|
||||
// window and a cross-platform Vulkan surface that represents the surface of the window.
|
||||
let events_loop = winit::EventsLoop::new();
|
||||
let window = winit::WindowBuilder::new().build_vk_surface(&events_loop, &instance).unwrap();
|
||||
let window = winit::WindowBuilder::new().build_vk_surface(&events_loop, instance.clone()).unwrap();
|
||||
|
||||
// The next step is to choose which GPU queue will execute our draw commands.
|
||||
//
|
||||
@ -117,7 +119,7 @@ fn main() {
|
||||
// queue to handle data transfers in parallel. In this example we only use one queue.
|
||||
//
|
||||
// We have to choose which queues to use early on, because we will need this info very soon.
|
||||
let queue = physical.queue_families().find(|q| {
|
||||
let queue = physical.queue_families().find(|&q| {
|
||||
// We take the first queue that supports drawing to our window.
|
||||
q.supports_graphics() && window.surface().is_supported(q).unwrap_or(false)
|
||||
}).expect("couldn't find a graphical queue family");
|
||||
@ -162,7 +164,7 @@ fn main() {
|
||||
let (swapchain, images) = {
|
||||
// Querying the capabilities of the surface. When we create the swapchain we can only
|
||||
// pass values that are allowed by the capabilities.
|
||||
let caps = window.surface().get_capabilities(&physical)
|
||||
let caps = window.surface().capabilities(physical)
|
||||
.expect("failed to get surface capabilities");
|
||||
|
||||
// We choose the dimensions of the swapchain to match the current dimensions of the window.
|
||||
@ -183,8 +185,8 @@ fn main() {
|
||||
let format = caps.supported_formats[0].0;
|
||||
|
||||
// Please take a look at the docs for the meaning of the parameters we didn't mention.
|
||||
Swapchain::new(&device, &window.surface(), caps.min_image_count, format, dimensions, 1,
|
||||
&caps.supported_usage_flags, &queue, SurfaceTransform::Identity, alpha,
|
||||
Swapchain::new(device.clone(), window.surface().clone(), caps.min_image_count, format, dimensions, 1,
|
||||
caps.supported_usage_flags, &queue, SurfaceTransform::Identity, alpha,
|
||||
present, true, None).expect("failed to create swapchain")
|
||||
};
|
||||
|
||||
@ -194,7 +196,7 @@ fn main() {
|
||||
struct Vertex { position: [f32; 2] }
|
||||
impl_vertex!(Vertex, position);
|
||||
|
||||
CpuAccessibleBuffer::from_iter(&device, &BufferUsage::all(), Some(queue.family()), [
|
||||
CpuAccessibleBuffer::from_iter(device.clone(), BufferUsage::all(), Some(queue.family()), [
|
||||
Vertex { position: [-0.5, -0.25] },
|
||||
Vertex { position: [0.0, 0.5] },
|
||||
Vertex { position: [0.25, -0.1] }
|
||||
@ -244,7 +246,7 @@ fn main() {
|
||||
// of your structs that implements the `FormatDesc` trait). Here we use the
|
||||
// generic `vulkano::format::Format` enum because we don't know the format in
|
||||
// advance.
|
||||
format: images[0].format(),
|
||||
format: swapchain.format(),
|
||||
// TODO:
|
||||
samples: 1,
|
||||
}
|
||||
@ -259,7 +261,7 @@ fn main() {
|
||||
|
||||
// Before we draw we have to create what is called a pipeline. This is similar to an OpenGL
|
||||
// program, but much more specific.
|
||||
let pipeline = Arc::new(GraphicsPipeline::new(&device, GraphicsPipelineParams {
|
||||
let pipeline = Arc::new(GraphicsPipeline::new(device.clone(), GraphicsPipelineParams {
|
||||
// We need to indicate the layout of the vertices.
|
||||
// The type `SingleBufferDefinition` actually contains a template parameter corresponding
|
||||
// to the type of each vertex. But in this code it is automatically inferred.
|
||||
@ -323,22 +325,9 @@ fn main() {
|
||||
// Since we need to draw to multiple images, we are going to create a different framebuffer for
|
||||
// each image.
|
||||
let framebuffers = images.iter().map(|image| {
|
||||
// When we create the framebuffer we need to pass the actual list of images for the
|
||||
// framebuffer's attachments.
|
||||
//
|
||||
// The type of data that corresponds to this list depends on the way you created the
|
||||
// render pass. With the `single_pass_renderpass!` macro you need to call
|
||||
// `.desc().start_attachments()`. The returned object will have a method whose name is the
|
||||
// name of the first attachment. When called, it returns an object that will have a method
|
||||
// whose name is the name of the second attachment. And so on. Only the object returned
|
||||
// by the method of the last attachment can be passed to `Framebuffer::new`.
|
||||
let attachments = render_pass.desc().start_attachments().color(image.clone());
|
||||
|
||||
// Actually creating the framebuffer. Note that we have to pass the dimensions of the
|
||||
// framebuffer. These dimensions must be inferior or equal to the intersection of the
|
||||
// dimensions of all the attachments.
|
||||
let dimensions = [image.dimensions()[0], image.dimensions()[1], 1];
|
||||
Framebuffer::new(render_pass.clone(), dimensions, attachments).unwrap()
|
||||
Arc::new(Framebuffer::start(render_pass.clone())
|
||||
.add(image.clone()).unwrap()
|
||||
.build().unwrap())
|
||||
}).collect::<Vec<_>>();
|
||||
|
||||
// Initialization is finally finished!
|
||||
@ -348,15 +337,15 @@ fn main() {
|
||||
// they are in use by the GPU.
|
||||
//
|
||||
// Destroying the `GpuFuture` blocks until the GPU is finished executing it. In order to avoid
|
||||
// that, we store them in a `Vec` and clean them from time to time.
|
||||
let mut submissions: Vec<Box<GpuFuture>> = Vec::new();
|
||||
// that, we store the submission of the previous frame here.
|
||||
let mut previous_frame_end = Box::new(now(device.clone())) as Box<GpuFuture>;
|
||||
|
||||
loop {
|
||||
// Clearing the old submissions by keeping alive only the ones which probably aren't
|
||||
// finished.
|
||||
while submissions.len() >= 4 {
|
||||
submissions.remove(0);
|
||||
}
|
||||
// It is important to call this function from time to time, otherwise resources will keep
|
||||
// accumulating and you will eventually reach an out of memory error.
|
||||
// Calling this function polls various fences in order to determine what the GPU has
|
||||
// already processed, and frees the resources that are no longer needed.
|
||||
previous_frame_end.cleanup_finished();
|
||||
|
||||
// Before we can draw on the output, we have to *acquire* an image from the swapchain. If
|
||||
// no image is available (which happens if you submit draw commands too quickly), then the
|
||||
@ -365,7 +354,8 @@ fn main() {
|
||||
//
|
||||
// This function can block if no image is available. The parameter is a timeout after
|
||||
// which the function call will return an error.
|
||||
let (image_num, future) = swapchain.acquire_next_image(Duration::new(1, 0)).unwrap();
|
||||
let (image_num, acquire_future) = swapchain::acquire_next_image(swapchain.clone(),
|
||||
Duration::new(1, 0)).unwrap();
|
||||
|
||||
// In order to draw, we have to build a *command buffer*. The command buffer object holds
|
||||
// the list of commands that are going to be executed.
|
||||
@ -385,7 +375,7 @@ fn main() {
|
||||
// is similar to the list of attachments when building the framebuffers, except that
|
||||
// only the attachments that use `load: Clear` appear in the list.
|
||||
.begin_render_pass(framebuffers[image_num].clone(), false,
|
||||
render_pass.desc().start_clear_values().color([0.0, 0.0, 1.0, 1.0]))
|
||||
vec![[0.0, 0.0, 1.0, 1.0].into()])
|
||||
.unwrap()
|
||||
|
||||
// We are now inside the first subpass of the render pass. We add a draw command.
|
||||
@ -404,7 +394,7 @@ fn main() {
|
||||
// Finish building the command buffer by calling `build`.
|
||||
.build().unwrap();
|
||||
|
||||
let future = future
|
||||
let future = previous_frame_end.join(acquire_future)
|
||||
.then_execute(queue.clone(), command_buffer).unwrap()
|
||||
|
||||
// The color output is now expected to contain our triangle. But in order to show it on
|
||||
@ -415,7 +405,7 @@ fn main() {
|
||||
// the GPU has finished executing the command buffer that draws the triangle.
|
||||
.then_swapchain_present(queue.clone(), swapchain.clone(), image_num)
|
||||
.then_signal_fence_and_flush().unwrap();
|
||||
submissions.push(Box::new(future) as Box<_>);
|
||||
previous_frame_end = Box::new(future) as Box<_>;
|
||||
|
||||
// Note that in more complex programs it is likely that one of `acquire_next_image`,
|
||||
// `command_buffer::submit`, or `present` will block for some time. This happens when the
|
||||
|
@ -52,11 +52,11 @@ pub fn required_extensions() -> InstanceExtensions {
|
||||
}
|
||||
|
||||
pub trait VkSurfaceBuild {
|
||||
fn build_vk_surface(self, events_loop: &EventsLoop, instance: &Arc<Instance>) -> Result<Window, CreationError>;
|
||||
fn build_vk_surface(self, events_loop: &EventsLoop, instance: Arc<Instance>) -> Result<Window, CreationError>;
|
||||
}
|
||||
|
||||
impl VkSurfaceBuild for WindowBuilder {
|
||||
fn build_vk_surface(self, events_loop: &EventsLoop, instance: &Arc<Instance>) -> Result<Window, CreationError> {
|
||||
fn build_vk_surface(self, events_loop: &EventsLoop, instance: Arc<Instance>) -> Result<Window, CreationError> {
|
||||
let window = try!(self.build(events_loop));
|
||||
let surface = try!(unsafe { winit_to_surface(instance, &window) });
|
||||
|
||||
@ -133,7 +133,7 @@ impl From<WindowCreationError> for CreationError {
|
||||
}
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
unsafe fn winit_to_surface(instance: &Arc<Instance>,
|
||||
unsafe fn winit_to_surface(instance: Arc<Instance>,
|
||||
win: &winit::Window)
|
||||
-> Result<Arc<Surface>, SurfaceCreationError> {
|
||||
use winit::os::android::WindowExt;
|
||||
@ -141,7 +141,7 @@ unsafe fn winit_to_surface(instance: &Arc<Instance>,
|
||||
}
|
||||
|
||||
#[cfg(all(unix, not(target_os = "android"), not(target_os = "macos")))]
|
||||
unsafe fn winit_to_surface(instance: &Arc<Instance>,
|
||||
unsafe fn winit_to_surface(instance: Arc<Instance>,
|
||||
win: &winit::Window)
|
||||
-> Result<Arc<Surface>, SurfaceCreationError> {
|
||||
use winit::os::unix::WindowExt;
|
||||
@ -164,7 +164,7 @@ unsafe fn winit_to_surface(instance: &Arc<Instance>,
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
unsafe fn winit_to_surface(instance: &Arc<Instance>,
|
||||
unsafe fn winit_to_surface(instance: Arc<Instance>,
|
||||
win: &winit::Window)
|
||||
-> Result<Arc<Surface>, SurfaceCreationError> {
|
||||
use winit::os::windows::WindowExt;
|
||||
@ -174,7 +174,7 @@ unsafe fn winit_to_surface(instance: &Arc<Instance>,
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
unsafe fn winit_to_surface(instance: &Arc<Instance>, win: &winit::Window)
|
||||
unsafe fn winit_to_surface(instance: Arc<Instance>, win: &winit::Window)
|
||||
-> Result<Arc<Surface>, SurfaceCreationError>
|
||||
{
|
||||
use winit::os::macos::WindowExt;
|
||||
|
@ -31,7 +31,7 @@ use smallvec::SmallVec;
|
||||
use buffer::sys::BufferCreationError;
|
||||
use buffer::sys::SparseLevel;
|
||||
use buffer::sys::UnsafeBuffer;
|
||||
use buffer::sys::Usage;
|
||||
use buffer::BufferUsage;
|
||||
use buffer::traits::BufferAccess;
|
||||
use buffer::traits::BufferInner;
|
||||
use buffer::traits::Buffer;
|
||||
@ -47,6 +47,7 @@ use memory::pool::AllocLayout;
|
||||
use memory::pool::MemoryPool;
|
||||
use memory::pool::MemoryPoolAlloc;
|
||||
use memory::pool::StdMemoryPool;
|
||||
use sync::AccessError;
|
||||
use sync::Sharing;
|
||||
use sync::AccessFlagBits;
|
||||
use sync::PipelineStages;
|
||||
@ -77,7 +78,7 @@ impl<T> CpuAccessibleBuffer<T> {
|
||||
/// Deprecated. Use `from_data` instead.
|
||||
#[deprecated]
|
||||
#[inline]
|
||||
pub fn new<'a, I>(device: &Arc<Device>, usage: &Usage, queue_families: I)
|
||||
pub fn new<'a, I>(device: Arc<Device>, usage: BufferUsage, queue_families: I)
|
||||
-> Result<Arc<CpuAccessibleBuffer<T>>, OomError>
|
||||
where I: IntoIterator<Item = QueueFamily<'a>>
|
||||
{
|
||||
@ -87,7 +88,7 @@ impl<T> CpuAccessibleBuffer<T> {
|
||||
}
|
||||
|
||||
/// Builds a new buffer with some data in it. Only allowed for sized data.
|
||||
pub fn from_data<'a, I>(device: &Arc<Device>, usage: &Usage, queue_families: I, data: T)
|
||||
pub fn from_data<'a, I>(device: Arc<Device>, usage: BufferUsage, queue_families: I, data: T)
|
||||
-> Result<Arc<CpuAccessibleBuffer<T>>, OomError>
|
||||
where I: IntoIterator<Item = QueueFamily<'a>>,
|
||||
T: Content + 'static,
|
||||
@ -112,7 +113,7 @@ impl<T> CpuAccessibleBuffer<T> {
|
||||
|
||||
/// Builds a new uninitialized buffer. Only allowed for sized data.
|
||||
#[inline]
|
||||
pub unsafe fn uninitialized<'a, I>(device: &Arc<Device>, usage: &Usage, queue_families: I)
|
||||
pub unsafe fn uninitialized<'a, I>(device: Arc<Device>, usage: BufferUsage, queue_families: I)
|
||||
-> Result<Arc<CpuAccessibleBuffer<T>>, OomError>
|
||||
where I: IntoIterator<Item = QueueFamily<'a>>
|
||||
{
|
||||
@ -123,7 +124,7 @@ impl<T> CpuAccessibleBuffer<T> {
|
||||
impl<T> CpuAccessibleBuffer<[T]> {
|
||||
/// Builds a new buffer that contains an array `T`. The initial data comes from an iterator
|
||||
/// that produces that list of Ts.
|
||||
pub fn from_iter<'a, I, Q>(device: &Arc<Device>, usage: &Usage, queue_families: Q, data: I)
|
||||
pub fn from_iter<'a, I, Q>(device: Arc<Device>, usage: BufferUsage, queue_families: Q, data: I)
|
||||
-> Result<Arc<CpuAccessibleBuffer<[T]>>, OomError>
|
||||
where I: ExactSizeIterator<Item = T>,
|
||||
T: Content + 'static,
|
||||
@ -154,7 +155,7 @@ impl<T> CpuAccessibleBuffer<[T]> {
|
||||
// TODO: remove
|
||||
#[inline]
|
||||
#[deprecated]
|
||||
pub fn array<'a, I>(device: &Arc<Device>, len: usize, usage: &Usage, queue_families: I)
|
||||
pub fn array<'a, I>(device: Arc<Device>, len: usize, usage: BufferUsage, queue_families: I)
|
||||
-> Result<Arc<CpuAccessibleBuffer<[T]>>, OomError>
|
||||
where I: IntoIterator<Item = QueueFamily<'a>>
|
||||
{
|
||||
@ -165,7 +166,7 @@ impl<T> CpuAccessibleBuffer<[T]> {
|
||||
|
||||
/// Builds a new buffer. Can be used for arrays.
|
||||
#[inline]
|
||||
pub unsafe fn uninitialized_array<'a, I>(device: &Arc<Device>, len: usize, usage: &Usage,
|
||||
pub unsafe fn uninitialized_array<'a, I>(device: Arc<Device>, len: usize, usage: BufferUsage,
|
||||
queue_families: I)
|
||||
-> Result<Arc<CpuAccessibleBuffer<[T]>>, OomError>
|
||||
where I: IntoIterator<Item = QueueFamily<'a>>
|
||||
@ -181,7 +182,7 @@ impl<T: ?Sized> CpuAccessibleBuffer<T> {
|
||||
///
|
||||
/// You must ensure that the size that you pass is correct for `T`.
|
||||
///
|
||||
pub unsafe fn raw<'a, I>(device: &Arc<Device>, size: usize, usage: &Usage, queue_families: I)
|
||||
pub unsafe fn raw<'a, I>(device: Arc<Device>, size: usize, usage: BufferUsage, queue_families: I)
|
||||
-> Result<Arc<CpuAccessibleBuffer<T>>, OomError>
|
||||
where I: IntoIterator<Item = QueueFamily<'a>>
|
||||
{
|
||||
@ -195,7 +196,7 @@ impl<T: ?Sized> CpuAccessibleBuffer<T> {
|
||||
Sharing::Exclusive
|
||||
};
|
||||
|
||||
match UnsafeBuffer::new(device, size, &usage, sharing, SparseLevel::none()) {
|
||||
match UnsafeBuffer::new(device.clone(), size, usage, sharing, SparseLevel::none()) {
|
||||
Ok(b) => b,
|
||||
Err(BufferCreationError::OomError(err)) => return Err(err),
|
||||
Err(_) => unreachable!() // We don't use sparse binding, therefore the other
|
||||
@ -208,7 +209,7 @@ impl<T: ?Sized> CpuAccessibleBuffer<T> {
|
||||
.filter(|t| t.is_host_visible())
|
||||
.next().unwrap(); // Vk specs guarantee that this can't fail
|
||||
|
||||
let mem = try!(MemoryPool::alloc(&Device::standard_pool(device), mem_ty,
|
||||
let mem = try!(MemoryPool::alloc(&Device::standard_pool(&device), mem_ty,
|
||||
mem_reqs.size, mem_reqs.alignment, AllocLayout::Linear));
|
||||
debug_assert!((mem.offset() % mem_reqs.alignment) == 0);
|
||||
debug_assert!(mem.mapped_memory().is_some());
|
||||
@ -325,8 +326,8 @@ unsafe impl<T: ?Sized, A> BufferAccess for CpuAccessibleBuffer<T, A>
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_gpu_lock(&self, exclusive_access: bool, queue: &Queue) -> bool {
|
||||
true // FIXME:
|
||||
fn try_gpu_lock(&self, exclusive_access: bool, queue: &Queue) -> Result<(), AccessError> {
|
||||
Ok(()) // FIXME:
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -21,7 +21,7 @@ use smallvec::SmallVec;
|
||||
use buffer::sys::BufferCreationError;
|
||||
use buffer::sys::SparseLevel;
|
||||
use buffer::sys::UnsafeBuffer;
|
||||
use buffer::sys::Usage;
|
||||
use buffer::BufferUsage;
|
||||
use buffer::traits::BufferAccess;
|
||||
use buffer::traits::BufferInner;
|
||||
use buffer::traits::Buffer;
|
||||
@ -35,6 +35,7 @@ use memory::pool::AllocLayout;
|
||||
use memory::pool::MemoryPool;
|
||||
use memory::pool::MemoryPoolAlloc;
|
||||
use memory::pool::StdMemoryPool;
|
||||
use sync::AccessError;
|
||||
use sync::Sharing;
|
||||
|
||||
use OomError;
|
||||
@ -43,7 +44,7 @@ use OomError;
|
||||
///
|
||||
/// This buffer is especially suitable when you want to upload or download some data at each frame.
|
||||
///
|
||||
/// # Usage
|
||||
/// # BufferUsage
|
||||
///
|
||||
/// A `CpuBufferPool` is a bit similar to a `Vec`. You start by creating an empty pool, then you
|
||||
/// grab elements from the pool and use them, and if the pool is full it will automatically grow
|
||||
@ -71,7 +72,7 @@ pub struct CpuBufferPool<T: ?Sized, A = Arc<StdMemoryPool>> where A: MemoryPool
|
||||
one_size: usize,
|
||||
|
||||
// Buffer usage.
|
||||
usage: Usage,
|
||||
usage: BufferUsage,
|
||||
|
||||
// Queue families allowed to access this buffer.
|
||||
queue_families: SmallVec<[u32; 4]>,
|
||||
@ -131,7 +132,7 @@ pub struct CpuBufferPoolSubbuffer<T: ?Sized, A> where A: MemoryPool {
|
||||
|
||||
impl<T> CpuBufferPool<T> {
|
||||
#[inline]
|
||||
pub fn new<'a, I>(device: Arc<Device>, usage: &Usage, queue_families: I)
|
||||
pub fn new<'a, I>(device: Arc<Device>, usage: BufferUsage, queue_families: I)
|
||||
-> CpuBufferPool<T>
|
||||
where I: IntoIterator<Item = QueueFamily<'a>>
|
||||
{
|
||||
@ -146,13 +147,13 @@ impl<T> CpuBufferPool<T> {
|
||||
/// family accesses.
|
||||
#[inline]
|
||||
pub fn upload(device: Arc<Device>) -> CpuBufferPool<T> {
|
||||
CpuBufferPool::new(device, &Usage::transfer_source(), iter::empty())
|
||||
CpuBufferPool::new(device, BufferUsage::transfer_source(), iter::empty())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> CpuBufferPool<[T]> {
|
||||
#[inline]
|
||||
pub fn array<'a, I>(device: Arc<Device>, len: usize, usage: &Usage, queue_families: I)
|
||||
pub fn array<'a, I>(device: Arc<Device>, len: usize, usage: BufferUsage, queue_families: I)
|
||||
-> CpuBufferPool<[T]>
|
||||
where I: IntoIterator<Item = QueueFamily<'a>>
|
||||
{
|
||||
@ -164,7 +165,7 @@ impl<T> CpuBufferPool<[T]> {
|
||||
|
||||
impl<T: ?Sized> CpuBufferPool<T> {
|
||||
pub unsafe fn raw<'a, I>(device: Arc<Device>, one_size: usize,
|
||||
usage: &Usage, queue_families: I) -> CpuBufferPool<T>
|
||||
usage: BufferUsage, queue_families: I) -> CpuBufferPool<T>
|
||||
where I: IntoIterator<Item = QueueFamily<'a>>
|
||||
{
|
||||
let queue_families = queue_families.into_iter().map(|f| f.id())
|
||||
@ -265,7 +266,7 @@ impl<T, A> CpuBufferPool<T, A> where A: MemoryPool {
|
||||
None => return Err(OomError::OutOfDeviceMemory),
|
||||
};
|
||||
|
||||
match UnsafeBuffer::new(&self.device, total_size, &self.usage, sharing, SparseLevel::none()) {
|
||||
match UnsafeBuffer::new(self.device.clone(), total_size, self.usage, sharing, SparseLevel::none()) {
|
||||
Ok(b) => b,
|
||||
Err(BufferCreationError::OomError(err)) => return Err(err),
|
||||
Err(_) => unreachable!() // We don't use sparse binding, therefore the other
|
||||
@ -436,15 +437,15 @@ unsafe impl<T: ?Sized, A> BufferAccess for CpuBufferPoolSubbuffer<T, A>
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_gpu_lock(&self, _: bool, _: &Queue) -> bool {
|
||||
fn try_gpu_lock(&self, _: bool, _: &Queue) -> Result<(), AccessError> {
|
||||
let in_use = &self.buffer.subbuffers[self.subbuffer_index].num_gpu_accesses;
|
||||
if in_use.compare_and_swap(0, 1, Ordering::SeqCst) != 0 {
|
||||
return false;
|
||||
return Err(AccessError::AlreadyInUse);
|
||||
}
|
||||
|
||||
let was_locked = self.gpu_locked.swap(true, Ordering::SeqCst);
|
||||
debug_assert!(!was_locked);
|
||||
true
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -22,7 +22,7 @@ use smallvec::SmallVec;
|
||||
use buffer::sys::BufferCreationError;
|
||||
use buffer::sys::SparseLevel;
|
||||
use buffer::sys::UnsafeBuffer;
|
||||
use buffer::sys::Usage;
|
||||
use buffer::BufferUsage;
|
||||
use buffer::traits::BufferAccess;
|
||||
use buffer::traits::BufferInner;
|
||||
use buffer::traits::Buffer;
|
||||
@ -36,6 +36,7 @@ use memory::pool::AllocLayout;
|
||||
use memory::pool::MemoryPool;
|
||||
use memory::pool::MemoryPoolAlloc;
|
||||
use memory::pool::StdMemoryPool;
|
||||
use sync::AccessError;
|
||||
use sync::Sharing;
|
||||
|
||||
use OomError;
|
||||
@ -63,7 +64,7 @@ pub struct DeviceLocalBuffer<T: ?Sized, A = Arc<StdMemoryPool>> where A: MemoryP
|
||||
impl<T> DeviceLocalBuffer<T> {
|
||||
/// Builds a new buffer. Only allowed for sized data.
|
||||
#[inline]
|
||||
pub fn new<'a, I>(device: &Arc<Device>, usage: &Usage, queue_families: I)
|
||||
pub fn new<'a, I>(device: Arc<Device>, usage: BufferUsage, queue_families: I)
|
||||
-> Result<Arc<DeviceLocalBuffer<T>>, OomError>
|
||||
where I: IntoIterator<Item = QueueFamily<'a>>
|
||||
{
|
||||
@ -76,7 +77,7 @@ impl<T> DeviceLocalBuffer<T> {
|
||||
impl<T> DeviceLocalBuffer<[T]> {
|
||||
/// Builds a new buffer. Can be used for arrays.
|
||||
#[inline]
|
||||
pub fn array<'a, I>(device: &Arc<Device>, len: usize, usage: &Usage, queue_families: I)
|
||||
pub fn array<'a, I>(device: Arc<Device>, len: usize, usage: BufferUsage, queue_families: I)
|
||||
-> Result<Arc<DeviceLocalBuffer<[T]>>, OomError>
|
||||
where I: IntoIterator<Item = QueueFamily<'a>>
|
||||
{
|
||||
@ -93,7 +94,7 @@ impl<T: ?Sized> DeviceLocalBuffer<T> {
|
||||
///
|
||||
/// You must ensure that the size that you pass is correct for `T`.
|
||||
///
|
||||
pub unsafe fn raw<'a, I>(device: &Arc<Device>, size: usize, usage: &Usage, queue_families: I)
|
||||
pub unsafe fn raw<'a, I>(device: Arc<Device>, size: usize, usage: BufferUsage, queue_families: I)
|
||||
-> Result<Arc<DeviceLocalBuffer<T>>, OomError>
|
||||
where I: IntoIterator<Item = QueueFamily<'a>>
|
||||
{
|
||||
@ -107,7 +108,7 @@ impl<T: ?Sized> DeviceLocalBuffer<T> {
|
||||
Sharing::Exclusive
|
||||
};
|
||||
|
||||
match UnsafeBuffer::new(device, size, &usage, sharing, SparseLevel::none()) {
|
||||
match UnsafeBuffer::new(device.clone(), size, usage, sharing, SparseLevel::none()) {
|
||||
Ok(b) => b,
|
||||
Err(BufferCreationError::OomError(err)) => return Err(err),
|
||||
Err(_) => unreachable!() // We don't use sparse binding, therefore the other
|
||||
@ -124,7 +125,7 @@ impl<T: ?Sized> DeviceLocalBuffer<T> {
|
||||
device_local.chain(any).next().unwrap()
|
||||
};
|
||||
|
||||
let mem = try!(MemoryPool::alloc(&Device::standard_pool(device), mem_ty,
|
||||
let mem = try!(MemoryPool::alloc(&Device::standard_pool(&device), mem_ty,
|
||||
mem_reqs.size, mem_reqs.alignment, AllocLayout::Linear));
|
||||
debug_assert!((mem.offset() % mem_reqs.alignment) == 0);
|
||||
try!(buffer.bind_memory(mem.memory(), mem.offset()));
|
||||
@ -204,7 +205,7 @@ unsafe impl<P, T: ?Sized, A> BufferAccess for DeviceLocalBufferAccess<P>
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_gpu_lock(&self, _: bool, _: &Queue) -> bool {
|
||||
fn try_gpu_lock(&self, _: bool, _: &Queue) -> Result<(), AccessError> {
|
||||
// FIXME: not implemented correctly
|
||||
/*let val = self.0.gpu_lock.fetch_add(1, Ordering::SeqCst);
|
||||
if val == 1 {
|
||||
@ -213,7 +214,7 @@ unsafe impl<P, T: ?Sized, A> BufferAccess for DeviceLocalBufferAccess<P>
|
||||
self.0.gpu_lock.fetch_sub(1, Ordering::SeqCst);
|
||||
false
|
||||
}*/
|
||||
true
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -32,7 +32,7 @@ use buffer::CpuAccessibleBuffer;
|
||||
use buffer::sys::BufferCreationError;
|
||||
use buffer::sys::SparseLevel;
|
||||
use buffer::sys::UnsafeBuffer;
|
||||
use buffer::sys::Usage;
|
||||
use buffer::BufferUsage;
|
||||
use buffer::traits::BufferAccess;
|
||||
use buffer::traits::BufferInner;
|
||||
use buffer::traits::Buffer;
|
||||
@ -54,6 +54,7 @@ use memory::pool::AllocLayout;
|
||||
use memory::pool::MemoryPool;
|
||||
use memory::pool::MemoryPoolAlloc;
|
||||
use memory::pool::StdMemoryPoolAlloc;
|
||||
use sync::AccessError;
|
||||
use sync::NowFuture;
|
||||
use sync::Sharing;
|
||||
|
||||
@ -93,12 +94,12 @@ impl<T: ?Sized> ImmutableBuffer<T> {
|
||||
/// 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_data<'a, I>(data: T, usage: &Usage, queue_families: I, queue: Arc<Queue>)
|
||||
pub fn from_data<'a, I>(data: T, usage: BufferUsage, queue_families: I, queue: Arc<Queue>)
|
||||
-> Result<(Arc<ImmutableBuffer<T>>, ImmutableBufferFromBufferFuture), OomError>
|
||||
where I: IntoIterator<Item = QueueFamily<'a>>,
|
||||
T: 'static + Send + Sync + Sized,
|
||||
{
|
||||
let source = CpuAccessibleBuffer::from_data(queue.device(), &Usage::transfer_source(),
|
||||
let source = CpuAccessibleBuffer::from_data(queue.device().clone(), BufferUsage::transfer_source(),
|
||||
iter::once(queue.family()), data)?;
|
||||
ImmutableBuffer::from_buffer(source, usage, queue_families, queue)
|
||||
}
|
||||
@ -109,7 +110,7 @@ impl<T: ?Sized> ImmutableBuffer<T> {
|
||||
/// 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<'a, B, I>(source: B, usage: &Usage, queue_families: I, queue: Arc<Queue>)
|
||||
pub fn from_buffer<'a, B, I>(source: B, usage: BufferUsage, queue_families: I, queue: Arc<Queue>)
|
||||
-> Result<(Arc<ImmutableBuffer<T>>, ImmutableBufferFromBufferFuture), OomError>
|
||||
where B: Buffer + TypedBuffer<Content = T> + DeviceOwned, // TODO: remove + DeviceOwned once Buffer requires it
|
||||
B::Access: 'static + Clone + Send + Sync,
|
||||
@ -140,7 +141,7 @@ impl<T: ?Sized> ImmutableBuffer<T> {
|
||||
}
|
||||
|
||||
/// Builds an `ImmutableBuffer` that copies its data from another buffer.
|
||||
pub fn from_buffer_with_builder<'a, B, I, Cb, O>(source: B, usage: &Usage, queue_families: I,
|
||||
pub fn from_buffer_with_builder<'a, B, I, Cb, O>(source: B, usage: BufferUsage, queue_families: I,
|
||||
builder: Cb)
|
||||
-> Result<(Arc<ImmutableBuffer<T>>, O), ImmutableBufferFromBufferWithBuilderError>
|
||||
where B: Buffer + TypedBuffer<Content = T> + DeviceOwned, // TODO: remove + DeviceOwned once Buffer requires it
|
||||
@ -150,13 +151,13 @@ impl<T: ?Sized> ImmutableBuffer<T> {
|
||||
{
|
||||
unsafe {
|
||||
// We automatically set `transfer_dest` to true in order to avoid annoying errors.
|
||||
let actual_usage = Usage {
|
||||
let actual_usage = BufferUsage {
|
||||
transfer_dest: true,
|
||||
.. *usage
|
||||
.. usage
|
||||
};
|
||||
|
||||
let (buffer, init) = ImmutableBuffer::raw(source.device().clone(), source.size(),
|
||||
&actual_usage, queue_families)?;
|
||||
actual_usage, queue_families)?;
|
||||
|
||||
let builder = builder.copy_buffer(source, init)?;
|
||||
Ok((buffer, builder))
|
||||
@ -182,7 +183,7 @@ impl<T> ImmutableBuffer<T> {
|
||||
/// data, otherwise the content is undefined.
|
||||
///
|
||||
#[inline]
|
||||
pub unsafe fn uninitialized<'a, I>(device: Arc<Device>, usage: &Usage, queue_families: I)
|
||||
pub unsafe fn uninitialized<'a, I>(device: Arc<Device>, usage: BufferUsage, queue_families: I)
|
||||
-> Result<(Arc<ImmutableBuffer<T>>, ImmutableBufferInitialization<T>), OomError>
|
||||
where I: IntoIterator<Item = QueueFamily<'a>>
|
||||
{
|
||||
@ -191,13 +192,13 @@ impl<T> ImmutableBuffer<T> {
|
||||
}
|
||||
|
||||
impl<T> ImmutableBuffer<[T]> {
|
||||
pub fn from_iter<'a, D, I>(data: D, usage: &Usage, queue_families: I, queue: Arc<Queue>)
|
||||
pub fn from_iter<'a, D, I>(data: D, usage: BufferUsage, queue_families: I, queue: Arc<Queue>)
|
||||
-> Result<(Arc<ImmutableBuffer<[T]>>, ImmutableBufferFromBufferFuture), OomError>
|
||||
where I: IntoIterator<Item = QueueFamily<'a>>,
|
||||
D: ExactSizeIterator<Item = T>,
|
||||
T: 'static + Send + Sync + Sized,
|
||||
{
|
||||
let source = CpuAccessibleBuffer::from_iter(queue.device(), &Usage::transfer_source(),
|
||||
let source = CpuAccessibleBuffer::from_iter(queue.device().clone(), BufferUsage::transfer_source(),
|
||||
iter::once(queue.family()), data)?;
|
||||
ImmutableBuffer::from_buffer(source, usage, queue_families, queue)
|
||||
}
|
||||
@ -219,7 +220,7 @@ impl<T> ImmutableBuffer<[T]> {
|
||||
/// data, otherwise the content is undefined.
|
||||
///
|
||||
#[inline]
|
||||
pub unsafe fn uninitialized_array<'a, I>(device: Arc<Device>, len: usize, usage: &Usage,
|
||||
pub unsafe fn uninitialized_array<'a, I>(device: Arc<Device>, len: usize, usage: BufferUsage,
|
||||
queue_families: I)
|
||||
-> Result<(Arc<ImmutableBuffer<[T]>>, ImmutableBufferInitialization<[T]>), OomError>
|
||||
where I: IntoIterator<Item = QueueFamily<'a>>
|
||||
@ -245,7 +246,7 @@ impl<T: ?Sized> ImmutableBuffer<T> {
|
||||
/// data.
|
||||
///
|
||||
#[inline]
|
||||
pub unsafe fn raw<'a, I>(device: Arc<Device>, size: usize, usage: &Usage, queue_families: I)
|
||||
pub unsafe fn raw<'a, I>(device: Arc<Device>, size: usize, usage: BufferUsage, queue_families: I)
|
||||
-> Result<(Arc<ImmutableBuffer<T>>, ImmutableBufferInitialization<T>), OomError>
|
||||
where I: IntoIterator<Item = QueueFamily<'a>>
|
||||
{
|
||||
@ -255,7 +256,7 @@ impl<T: ?Sized> ImmutableBuffer<T> {
|
||||
|
||||
// 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: usize, usage: &Usage,
|
||||
unsafe fn raw_impl(device: Arc<Device>, size: usize, usage: BufferUsage,
|
||||
queue_families: SmallVec<[u32; 4]>)
|
||||
-> Result<(Arc<ImmutableBuffer<T>>, ImmutableBufferInitialization<T>), OomError>
|
||||
{
|
||||
@ -266,7 +267,7 @@ impl<T: ?Sized> ImmutableBuffer<T> {
|
||||
Sharing::Exclusive
|
||||
};
|
||||
|
||||
match UnsafeBuffer::new(&device, size, &usage, sharing, SparseLevel::none()) {
|
||||
match UnsafeBuffer::new(device.clone(), size, usage, sharing, SparseLevel::none()) {
|
||||
Ok(b) => b,
|
||||
Err(BufferCreationError::OomError(err)) => return Err(err),
|
||||
Err(_) => unreachable!() // We don't use sparse binding, therefore the other
|
||||
@ -355,16 +356,16 @@ unsafe impl<T: ?Sized, A> BufferAccess for ImmutableBuffer<T, A> {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_gpu_lock(&self, exclusive_access: bool, queue: &Queue) -> bool {
|
||||
fn try_gpu_lock(&self, exclusive_access: bool, queue: &Queue) -> Result<(), AccessError> {
|
||||
if exclusive_access {
|
||||
return false;
|
||||
return Err(AccessError::ExclusiveDenied);
|
||||
}
|
||||
|
||||
if !self.initialized.load(Ordering::Relaxed) {
|
||||
return false;
|
||||
return Err(AccessError::BufferNotInitialized);
|
||||
}
|
||||
|
||||
true
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@ -402,8 +403,12 @@ unsafe impl<T: ?Sized, A> BufferAccess for ImmutableBufferInitialization<T, A> {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_gpu_lock(&self, exclusive_access: bool, queue: &Queue) -> bool {
|
||||
!self.used.compare_and_swap(false, true, Ordering::Relaxed)
|
||||
fn try_gpu_lock(&self, exclusive_access: bool, queue: &Queue) -> Result<(), AccessError> {
|
||||
if !self.used.compare_and_swap(false, true, Ordering::Relaxed) {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(AccessError::AlreadyInUse)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@ -517,7 +522,7 @@ mod tests {
|
||||
use std::iter;
|
||||
use buffer::cpu_access::CpuAccessibleBuffer;
|
||||
use buffer::immutable::ImmutableBuffer;
|
||||
use buffer::sys::Usage;
|
||||
use buffer::BufferUsage;
|
||||
use command_buffer::AutoCommandBufferBuilder;
|
||||
use command_buffer::CommandBuffer;
|
||||
use command_buffer::CommandBufferBuilder;
|
||||
@ -527,11 +532,11 @@ mod tests {
|
||||
fn from_data_working() {
|
||||
let (device, queue) = gfx_dev_and_queue!();
|
||||
|
||||
let (buffer, _) = ImmutableBuffer::from_data(12u32, &Usage::all(),
|
||||
let (buffer, _) = ImmutableBuffer::from_data(12u32, BufferUsage::all(),
|
||||
iter::once(queue.family()),
|
||||
queue.clone()).unwrap();
|
||||
|
||||
let dest = CpuAccessibleBuffer::from_data(&device, &Usage::all(),
|
||||
let dest = CpuAccessibleBuffer::from_data(device.clone(), BufferUsage::all(),
|
||||
iter::once(queue.family()), 0).unwrap();
|
||||
|
||||
let _ = AutoCommandBufferBuilder::new(device.clone(), queue.family()).unwrap()
|
||||
@ -548,11 +553,11 @@ mod tests {
|
||||
fn from_iter_working() {
|
||||
let (device, queue) = gfx_dev_and_queue!();
|
||||
|
||||
let (buffer, _) = ImmutableBuffer::from_iter((0 .. 512u32).map(|n| n * 2), &Usage::all(),
|
||||
let (buffer, _) = ImmutableBuffer::from_iter((0 .. 512u32).map(|n| n * 2), BufferUsage::all(),
|
||||
iter::once(queue.family()),
|
||||
queue.clone()).unwrap();
|
||||
|
||||
let dest = CpuAccessibleBuffer::from_iter(&device, &Usage::all(),
|
||||
let dest = CpuAccessibleBuffer::from_iter(device.clone(), BufferUsage::all(),
|
||||
iter::once(queue.family()),
|
||||
(0 .. 512).map(|_| 0u32)).unwrap();
|
||||
|
||||
@ -573,7 +578,7 @@ mod tests {
|
||||
fn writing_forbidden() {
|
||||
let (device, queue) = gfx_dev_and_queue!();
|
||||
|
||||
let (buffer, _) = ImmutableBuffer::from_data(12u32, &Usage::all(),
|
||||
let (buffer, _) = ImmutableBuffer::from_data(12u32, BufferUsage::all(),
|
||||
iter::once(queue.family()),
|
||||
queue.clone()).unwrap();
|
||||
|
||||
@ -590,11 +595,11 @@ mod tests {
|
||||
let (device, queue) = gfx_dev_and_queue!();
|
||||
|
||||
let (buffer, _) = unsafe {
|
||||
ImmutableBuffer::<u32>::uninitialized(device.clone(), &Usage::all(),
|
||||
ImmutableBuffer::<u32>::uninitialized(device.clone(), BufferUsage::all(),
|
||||
iter::once(queue.family())).unwrap()
|
||||
};
|
||||
|
||||
let src = CpuAccessibleBuffer::from_data(&device, &Usage::all(),
|
||||
let src = CpuAccessibleBuffer::from_data(device.clone(), BufferUsage::all(),
|
||||
iter::once(queue.family()), 0).unwrap();
|
||||
|
||||
let _ = AutoCommandBufferBuilder::new(device.clone(), queue.family()).unwrap()
|
||||
@ -609,11 +614,11 @@ mod tests {
|
||||
let (device, queue) = gfx_dev_and_queue!();
|
||||
|
||||
let (buffer, init) = unsafe {
|
||||
ImmutableBuffer::<u32>::uninitialized(device.clone(), &Usage::all(),
|
||||
ImmutableBuffer::<u32>::uninitialized(device.clone(), BufferUsage::all(),
|
||||
iter::once(queue.family())).unwrap()
|
||||
};
|
||||
|
||||
let src = CpuAccessibleBuffer::from_data(&device, &Usage::all(),
|
||||
let src = CpuAccessibleBuffer::from_data(device.clone(), BufferUsage::all(),
|
||||
iter::once(queue.family()), 0).unwrap();
|
||||
|
||||
let _ = AutoCommandBufferBuilder::new(device.clone(), queue.family()).unwrap()
|
||||
@ -630,11 +635,11 @@ mod tests {
|
||||
let (device, queue) = gfx_dev_and_queue!();
|
||||
|
||||
let (buffer, init) = unsafe {
|
||||
ImmutableBuffer::<u32>::uninitialized(device.clone(), &Usage::all(),
|
||||
ImmutableBuffer::<u32>::uninitialized(device.clone(), BufferUsage::all(),
|
||||
iter::once(queue.family())).unwrap()
|
||||
};
|
||||
|
||||
let src = CpuAccessibleBuffer::from_data(&device, &Usage::all(),
|
||||
let src = CpuAccessibleBuffer::from_data(device.clone(), BufferUsage::all(),
|
||||
iter::once(queue.family()), 0).unwrap();
|
||||
|
||||
let cb1 = AutoCommandBufferBuilder::new(device.clone(), queue.family()).unwrap()
|
||||
|
@ -68,12 +68,12 @@ pub use self::device_local::DeviceLocalBuffer;
|
||||
pub use self::immutable::ImmutableBuffer;
|
||||
pub use self::slice::BufferSlice;
|
||||
pub use self::sys::BufferCreationError;
|
||||
pub use self::sys::Usage as BufferUsage;
|
||||
pub use self::traits::BufferAccess;
|
||||
pub use self::traits::BufferInner;
|
||||
pub use self::traits::Buffer;
|
||||
pub use self::traits::TypedBuffer;
|
||||
pub use self::traits::TypedBufferAccess;
|
||||
pub use self::usage::BufferUsage;
|
||||
pub use self::view::BufferView;
|
||||
pub use self::view::BufferViewRef;
|
||||
|
||||
@ -86,3 +86,4 @@ pub mod view;
|
||||
|
||||
mod slice;
|
||||
mod traits;
|
||||
mod usage;
|
||||
|
@ -20,6 +20,7 @@ use buffer::traits::Buffer;
|
||||
use device::Device;
|
||||
use device::DeviceOwned;
|
||||
use device::Queue;
|
||||
use sync::AccessError;
|
||||
|
||||
/// A subpart of a buffer.
|
||||
///
|
||||
@ -241,7 +242,7 @@ unsafe impl<T: ?Sized, B> BufferAccess for BufferSlice<T, B> where B: BufferAcce
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_gpu_lock(&self, exclusive_access: bool, queue: &Queue) -> bool {
|
||||
fn try_gpu_lock(&self, exclusive_access: bool, queue: &Queue) -> Result<(), AccessError> {
|
||||
self.resource.try_gpu_lock(exclusive_access, queue)
|
||||
}
|
||||
|
||||
|
@ -31,6 +31,8 @@ use std::ptr;
|
||||
use std::sync::Arc;
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use buffer::BufferUsage;
|
||||
use buffer::usage::usage_to_bits;
|
||||
use device::Device;
|
||||
use device::DeviceOwned;
|
||||
use memory::DeviceMemory;
|
||||
@ -62,14 +64,14 @@ impl UnsafeBuffer {
|
||||
/// Panics if `sparse.sparse` is false and `sparse.sparse_residency` or
|
||||
/// `sparse.sparse_aliased` is true.
|
||||
///
|
||||
pub unsafe fn new<'a, I>(device: &Arc<Device>, size: usize, usage: &Usage, sharing: Sharing<I>,
|
||||
sparse: SparseLevel)
|
||||
pub unsafe fn new<'a, I>(device: Arc<Device>, size: usize, usage: BufferUsage,
|
||||
sharing: Sharing<I>, sparse: SparseLevel)
|
||||
-> Result<(UnsafeBuffer, MemoryRequirements), BufferCreationError>
|
||||
where I: Iterator<Item = u32>
|
||||
{
|
||||
let vk = device.pointers();
|
||||
|
||||
let usage_bits = usage.to_usage_bits();
|
||||
let usage_bits = usage_to_bits(usage);
|
||||
|
||||
// Checking sparse features.
|
||||
assert!(sparse.sparse || !sparse.sparse_residency, "Can't enable sparse residency without \
|
||||
@ -301,170 +303,6 @@ impl SparseLevel {
|
||||
}
|
||||
}
|
||||
|
||||
/// Describes how a buffer is going to be used. This is **not** an optimization.
|
||||
///
|
||||
/// If you try to use a buffer in a way that you didn't declare, a panic will happen.
|
||||
///
|
||||
/// Some methods are provided to build `Usage` structs for some common situations. However
|
||||
/// there is no restriction in the combination of usages that can be enabled.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct Usage {
|
||||
pub transfer_source: bool,
|
||||
pub transfer_dest: bool,
|
||||
pub uniform_texel_buffer: bool,
|
||||
pub storage_texel_buffer: bool,
|
||||
pub uniform_buffer: bool,
|
||||
pub storage_buffer: bool,
|
||||
pub index_buffer: bool,
|
||||
pub vertex_buffer: bool,
|
||||
pub indirect_buffer: bool,
|
||||
}
|
||||
|
||||
impl Usage {
|
||||
/// Builds a `Usage` with all values set to false.
|
||||
#[inline]
|
||||
pub fn none() -> Usage {
|
||||
Usage {
|
||||
transfer_source: false,
|
||||
transfer_dest: false,
|
||||
uniform_texel_buffer: false,
|
||||
storage_texel_buffer: false,
|
||||
uniform_buffer: false,
|
||||
storage_buffer: false,
|
||||
index_buffer: false,
|
||||
vertex_buffer: false,
|
||||
indirect_buffer: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds a `Usage` with all values set to true. Can be used for quick prototyping.
|
||||
#[inline]
|
||||
pub fn all() -> Usage {
|
||||
Usage {
|
||||
transfer_source: true,
|
||||
transfer_dest: true,
|
||||
uniform_texel_buffer: true,
|
||||
storage_texel_buffer: true,
|
||||
uniform_buffer: true,
|
||||
storage_buffer: true,
|
||||
index_buffer: true,
|
||||
vertex_buffer: true,
|
||||
indirect_buffer: true,
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds a `Usage` with `transfer_source` set to true and the rest to false.
|
||||
#[inline]
|
||||
pub fn transfer_source() -> Usage {
|
||||
Usage {
|
||||
transfer_source: true,
|
||||
.. Usage::none()
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds a `Usage` with `transfer_dest` set to true and the rest to false.
|
||||
#[inline]
|
||||
pub fn transfer_dest() -> Usage {
|
||||
Usage {
|
||||
transfer_dest: true,
|
||||
.. Usage::none()
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds a `Usage` with `vertex_buffer` set to true and the rest to false.
|
||||
#[inline]
|
||||
pub fn vertex_buffer() -> Usage {
|
||||
Usage {
|
||||
vertex_buffer: true,
|
||||
.. Usage::none()
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds a `Usage` with `vertex_buffer` and `transfer_dest` set to true and the rest to false.
|
||||
#[inline]
|
||||
pub fn vertex_buffer_transfer_dest() -> Usage {
|
||||
Usage {
|
||||
vertex_buffer: true,
|
||||
transfer_dest: true,
|
||||
.. Usage::none()
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds a `Usage` with `index_buffer` set to true and the rest to false.
|
||||
#[inline]
|
||||
pub fn index_buffer() -> Usage {
|
||||
Usage {
|
||||
index_buffer: true,
|
||||
.. Usage::none()
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds a `Usage` with `index_buffer` and `transfer_dest` set to true and the rest to false.
|
||||
#[inline]
|
||||
pub fn index_buffer_transfer_dest() -> Usage {
|
||||
Usage {
|
||||
index_buffer: true,
|
||||
transfer_dest: true,
|
||||
.. Usage::none()
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds a `Usage` with `uniform_buffer` set to true and the rest to false.
|
||||
#[inline]
|
||||
pub fn uniform_buffer() -> Usage {
|
||||
Usage {
|
||||
uniform_buffer: true,
|
||||
.. Usage::none()
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds a `Usage` with `uniform_buffer` and `transfer_dest` set to true and the rest
|
||||
/// to false.
|
||||
#[inline]
|
||||
pub fn uniform_buffer_transfer_dest() -> Usage {
|
||||
Usage {
|
||||
uniform_buffer: true,
|
||||
transfer_dest: true,
|
||||
.. Usage::none()
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds a `Usage` with `indirect_buffer` set to true and the rest to false.
|
||||
#[inline]
|
||||
pub fn indirect_buffer() -> Usage {
|
||||
Usage {
|
||||
indirect_buffer: true,
|
||||
.. Usage::none()
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds a `Usage` with `indirect_buffer` and `transfer_dest` set to true and the rest
|
||||
/// to false.
|
||||
#[inline]
|
||||
pub fn indirect_buffer_transfer_dest() -> Usage {
|
||||
Usage {
|
||||
indirect_buffer: true,
|
||||
transfer_dest: true,
|
||||
.. Usage::none()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn to_usage_bits(&self) -> vk::BufferUsageFlagBits {
|
||||
let mut result = 0;
|
||||
if self.transfer_source { result |= vk::BUFFER_USAGE_TRANSFER_SRC_BIT; }
|
||||
if self.transfer_dest { result |= vk::BUFFER_USAGE_TRANSFER_DST_BIT; }
|
||||
if self.uniform_texel_buffer { result |= vk::BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT; }
|
||||
if self.storage_texel_buffer { result |= vk::BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT; }
|
||||
if self.uniform_buffer { result |= vk::BUFFER_USAGE_UNIFORM_BUFFER_BIT; }
|
||||
if self.storage_buffer { result |= vk::BUFFER_USAGE_STORAGE_BUFFER_BIT; }
|
||||
if self.index_buffer { result |= vk::BUFFER_USAGE_INDEX_BUFFER_BIT; }
|
||||
if self.vertex_buffer { result |= vk::BUFFER_USAGE_VERTEX_BUFFER_BIT; }
|
||||
if self.indirect_buffer { result |= vk::BUFFER_USAGE_INDIRECT_BUFFER_BIT; }
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
/// Error that can happen when creating a buffer.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum BufferCreationError {
|
||||
@ -536,7 +374,7 @@ mod tests {
|
||||
use super::BufferCreationError;
|
||||
use super::SparseLevel;
|
||||
use super::UnsafeBuffer;
|
||||
use super::Usage;
|
||||
use super::BufferUsage;
|
||||
|
||||
use device::Device;
|
||||
use device::DeviceOwned;
|
||||
@ -546,7 +384,7 @@ mod tests {
|
||||
fn create() {
|
||||
let (device, _) = gfx_dev_and_queue!();
|
||||
let (buf, reqs) = unsafe {
|
||||
UnsafeBuffer::new(&device, 128, &Usage::all(), Sharing::Exclusive::<Empty<_>>,
|
||||
UnsafeBuffer::new(device.clone(), 128, BufferUsage::all(), Sharing::Exclusive::<Empty<_>>,
|
||||
SparseLevel::none())
|
||||
}.unwrap();
|
||||
|
||||
@ -561,7 +399,7 @@ mod tests {
|
||||
let (device, _) = gfx_dev_and_queue!();
|
||||
let sparse = SparseLevel { sparse: false, sparse_residency: true, sparse_aliased: false };
|
||||
let _ = unsafe {
|
||||
UnsafeBuffer::new(&device, 128, &Usage::all(), Sharing::Exclusive::<Empty<_>>,
|
||||
UnsafeBuffer::new(device, 128, BufferUsage::all(), Sharing::Exclusive::<Empty<_>>,
|
||||
sparse)
|
||||
};
|
||||
}
|
||||
@ -572,7 +410,7 @@ mod tests {
|
||||
let (device, _) = gfx_dev_and_queue!();
|
||||
let sparse = SparseLevel { sparse: false, sparse_residency: false, sparse_aliased: true };
|
||||
let _ = unsafe {
|
||||
UnsafeBuffer::new(&device, 128, &Usage::all(), Sharing::Exclusive::<Empty<_>>,
|
||||
UnsafeBuffer::new(device, 128, BufferUsage::all(), Sharing::Exclusive::<Empty<_>>,
|
||||
sparse)
|
||||
};
|
||||
}
|
||||
@ -582,7 +420,7 @@ mod tests {
|
||||
let (device, _) = gfx_dev_and_queue!();
|
||||
let sparse = SparseLevel { sparse: true, sparse_residency: false, sparse_aliased: false };
|
||||
unsafe {
|
||||
match UnsafeBuffer::new(&device, 128, &Usage::all(), Sharing::Exclusive::<Empty<_>>,
|
||||
match UnsafeBuffer::new(device, 128, BufferUsage::all(), Sharing::Exclusive::<Empty<_>>,
|
||||
sparse)
|
||||
{
|
||||
Err(BufferCreationError::SparseBindingFeatureNotEnabled) => (),
|
||||
@ -596,7 +434,7 @@ mod tests {
|
||||
let (device, _) = gfx_dev_and_queue!(sparse_binding);
|
||||
let sparse = SparseLevel { sparse: true, sparse_residency: true, sparse_aliased: false };
|
||||
unsafe {
|
||||
match UnsafeBuffer::new(&device, 128, &Usage::all(), Sharing::Exclusive::<Empty<_>>,
|
||||
match UnsafeBuffer::new(device, 128, BufferUsage::all(), Sharing::Exclusive::<Empty<_>>,
|
||||
sparse)
|
||||
{
|
||||
Err(BufferCreationError::SparseResidencyBufferFeatureNotEnabled) => (),
|
||||
@ -610,7 +448,7 @@ mod tests {
|
||||
let (device, _) = gfx_dev_and_queue!(sparse_binding);
|
||||
let sparse = SparseLevel { sparse: true, sparse_residency: false, sparse_aliased: true };
|
||||
unsafe {
|
||||
match UnsafeBuffer::new(&device, 128, &Usage::all(), Sharing::Exclusive::<Empty<_>>,
|
||||
match UnsafeBuffer::new(device, 128, BufferUsage::all(), Sharing::Exclusive::<Empty<_>>,
|
||||
sparse)
|
||||
{
|
||||
Err(BufferCreationError::SparseResidencyAliasedFeatureNotEnabled) => (),
|
||||
|
@ -15,6 +15,7 @@ use device::DeviceOwned;
|
||||
use device::Queue;
|
||||
use image::ImageAccess;
|
||||
use memory::Content;
|
||||
use sync::AccessError;
|
||||
|
||||
use SafeDeref;
|
||||
use VulkanObject;
|
||||
@ -246,8 +247,7 @@ pub unsafe trait BufferAccess: DeviceOwned {
|
||||
/// The only way to know that the GPU has stopped accessing a queue is when the buffer object
|
||||
/// gets destroyed. Therefore you are encouraged to use temporary objects or handles (similar
|
||||
/// to a lock) in order to represent a GPU access.
|
||||
// TODO: return Result?
|
||||
fn try_gpu_lock(&self, exclusive_access: bool, queue: &Queue) -> bool;
|
||||
fn try_gpu_lock(&self, exclusive_access: bool, queue: &Queue) -> Result<(), AccessError>;
|
||||
|
||||
/// Locks the resource for usage on the GPU. Supposes that the resource is already locked, and
|
||||
/// simply increases the lock by one.
|
||||
@ -290,7 +290,7 @@ unsafe impl<T> BufferAccess for T where T: SafeDeref, T::Target: BufferAccess {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_gpu_lock(&self, exclusive_access: bool, queue: &Queue) -> bool {
|
||||
fn try_gpu_lock(&self, exclusive_access: bool, queue: &Queue) -> Result<(), AccessError> {
|
||||
(**self).try_gpu_lock(exclusive_access, queue)
|
||||
}
|
||||
|
||||
|
196
vulkano/src/buffer/usage.rs
Normal file
196
vulkano/src/buffer/usage.rs
Normal file
@ -0,0 +1,196 @@
|
||||
// Copyright (c) 2016 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.
|
||||
|
||||
use std::ops::BitOr;
|
||||
use vk;
|
||||
|
||||
/// Describes how a buffer is going to be used. This is **not** an optimization.
|
||||
///
|
||||
/// If you try to use a buffer in a way that you didn't declare, a panic will happen.
|
||||
///
|
||||
/// Some methods are provided to build `BufferUsage` structs for some common situations. However
|
||||
/// there is no restriction in the combination of BufferUsages that can be enabled.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct BufferUsage {
|
||||
pub transfer_source: bool,
|
||||
pub transfer_dest: bool,
|
||||
pub uniform_texel_buffer: bool,
|
||||
pub storage_texel_buffer: bool,
|
||||
pub uniform_buffer: bool,
|
||||
pub storage_buffer: bool,
|
||||
pub index_buffer: bool,
|
||||
pub vertex_buffer: bool,
|
||||
pub indirect_buffer: bool,
|
||||
}
|
||||
|
||||
impl BufferUsage {
|
||||
/// Builds a `BufferUsage` with all values set to false.
|
||||
#[inline]
|
||||
pub fn none() -> BufferUsage {
|
||||
BufferUsage {
|
||||
transfer_source: false,
|
||||
transfer_dest: false,
|
||||
uniform_texel_buffer: false,
|
||||
storage_texel_buffer: false,
|
||||
uniform_buffer: false,
|
||||
storage_buffer: false,
|
||||
index_buffer: false,
|
||||
vertex_buffer: false,
|
||||
indirect_buffer: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds a `BufferUsage` with all values set to true. Can be used for quick prototyping.
|
||||
#[inline]
|
||||
pub fn all() -> BufferUsage {
|
||||
BufferUsage {
|
||||
transfer_source: true,
|
||||
transfer_dest: true,
|
||||
uniform_texel_buffer: true,
|
||||
storage_texel_buffer: true,
|
||||
uniform_buffer: true,
|
||||
storage_buffer: true,
|
||||
index_buffer: true,
|
||||
vertex_buffer: true,
|
||||
indirect_buffer: true,
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds a `BufferUsage` with `transfer_source` set to true and the rest to false.
|
||||
#[inline]
|
||||
pub fn transfer_source() -> BufferUsage {
|
||||
BufferUsage {
|
||||
transfer_source: true,
|
||||
.. BufferUsage::none()
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds a `BufferUsage` with `transfer_dest` set to true and the rest to false.
|
||||
#[inline]
|
||||
pub fn transfer_dest() -> BufferUsage {
|
||||
BufferUsage {
|
||||
transfer_dest: true,
|
||||
.. BufferUsage::none()
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds a `BufferUsage` with `vertex_buffer` set to true and the rest to false.
|
||||
#[inline]
|
||||
pub fn vertex_buffer() -> BufferUsage {
|
||||
BufferUsage {
|
||||
vertex_buffer: true,
|
||||
.. BufferUsage::none()
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds a `BufferUsage` with `vertex_buffer` and `transfer_dest` set to true and the rest
|
||||
/// to false.
|
||||
#[inline]
|
||||
pub fn vertex_buffer_transfer_dest() -> BufferUsage {
|
||||
BufferUsage {
|
||||
vertex_buffer: true,
|
||||
transfer_dest: true,
|
||||
.. BufferUsage::none()
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds a `BufferUsage` with `index_buffer` set to true and the rest to false.
|
||||
#[inline]
|
||||
pub fn index_buffer() -> BufferUsage {
|
||||
BufferUsage {
|
||||
index_buffer: true,
|
||||
.. BufferUsage::none()
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds a `BufferUsage` with `index_buffer` and `transfer_dest` set to true and the rest to false.
|
||||
#[inline]
|
||||
pub fn index_buffer_transfer_dest() -> BufferUsage {
|
||||
BufferUsage {
|
||||
index_buffer: true,
|
||||
transfer_dest: true,
|
||||
.. BufferUsage::none()
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds a `BufferUsage` with `uniform_buffer` set to true and the rest to false.
|
||||
#[inline]
|
||||
pub fn uniform_buffer() -> BufferUsage {
|
||||
BufferUsage {
|
||||
uniform_buffer: true,
|
||||
.. BufferUsage::none()
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds a `BufferUsage` with `uniform_buffer` and `transfer_dest` set to true and the rest
|
||||
/// to false.
|
||||
#[inline]
|
||||
pub fn uniform_buffer_transfer_dest() -> BufferUsage {
|
||||
BufferUsage {
|
||||
uniform_buffer: true,
|
||||
transfer_dest: true,
|
||||
.. BufferUsage::none()
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds a `BufferUsage` with `indirect_buffer` set to true and the rest to false.
|
||||
#[inline]
|
||||
pub fn indirect_buffer() -> BufferUsage {
|
||||
BufferUsage {
|
||||
indirect_buffer: true,
|
||||
.. BufferUsage::none()
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds a `BufferUsage` with `indirect_buffer` and `transfer_dest` set to true and the rest
|
||||
/// to false.
|
||||
#[inline]
|
||||
pub fn indirect_buffer_transfer_dest() -> BufferUsage {
|
||||
BufferUsage {
|
||||
indirect_buffer: true,
|
||||
transfer_dest: true,
|
||||
.. BufferUsage::none()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BitOr for BufferUsage {
|
||||
type Output = Self;
|
||||
|
||||
#[inline]
|
||||
fn bitor(self, rhs: Self) -> Self {
|
||||
BufferUsage {
|
||||
transfer_source: self.transfer_source || rhs.transfer_source,
|
||||
transfer_dest: self.transfer_dest || rhs.transfer_dest,
|
||||
uniform_texel_buffer: self.uniform_texel_buffer || rhs.uniform_texel_buffer,
|
||||
storage_texel_buffer: self.storage_texel_buffer || rhs.storage_texel_buffer,
|
||||
uniform_buffer: self.uniform_buffer || rhs.uniform_buffer,
|
||||
storage_buffer: self.storage_buffer || rhs.storage_buffer,
|
||||
index_buffer: self.index_buffer || rhs.index_buffer,
|
||||
vertex_buffer: self.vertex_buffer || rhs.vertex_buffer,
|
||||
indirect_buffer: self.indirect_buffer || rhs.indirect_buffer,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Turns a `BufferUsage` into raw bits.
|
||||
#[inline]
|
||||
pub fn usage_to_bits(usage: BufferUsage) -> vk::BufferUsageFlagBits {
|
||||
let mut result = 0;
|
||||
if usage.transfer_source { result |= vk::BUFFER_USAGE_TRANSFER_SRC_BIT; }
|
||||
if usage.transfer_dest { result |= vk::BUFFER_USAGE_TRANSFER_DST_BIT; }
|
||||
if usage.uniform_texel_buffer { result |= vk::BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT; }
|
||||
if usage.storage_texel_buffer { result |= vk::BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT; }
|
||||
if usage.uniform_buffer { result |= vk::BUFFER_USAGE_UNIFORM_BUFFER_BIT; }
|
||||
if usage.storage_buffer { result |= vk::BUFFER_USAGE_STORAGE_BUFFER_BIT; }
|
||||
if usage.index_buffer { result |= vk::BUFFER_USAGE_INDEX_BUFFER_BIT; }
|
||||
if usage.vertex_buffer { result |= vk::BUFFER_USAGE_VERTEX_BUFFER_BIT; }
|
||||
if usage.indirect_buffer { result |= vk::BUFFER_USAGE_INDIRECT_BUFFER_BIT; }
|
||||
result
|
||||
}
|
@ -20,18 +20,18 @@
|
||||
//! ```
|
||||
//! # use std::sync::Arc;
|
||||
//! use vulkano::buffer::immutable::ImmutableBuffer;
|
||||
//! use vulkano::buffer::sys::Usage;
|
||||
//! use vulkano::buffer::BufferUsage;
|
||||
//! use vulkano::buffer::BufferView;
|
||||
//! use vulkano::format;
|
||||
//!
|
||||
//! # let device: Arc<vulkano::device::Device> = return;
|
||||
//! # let queue: Arc<vulkano::device::Queue> = return;
|
||||
//! let usage = Usage {
|
||||
//! let usage = BufferUsage {
|
||||
//! storage_texel_buffer: true,
|
||||
//! .. Usage::none()
|
||||
//! .. BufferUsage::none()
|
||||
//! };
|
||||
//!
|
||||
//! let (buffer, _future) = ImmutableBuffer::<[u32]>::from_iter((0..128).map(|n| n), &usage,
|
||||
//! let (buffer, _future) = ImmutableBuffer::<[u32]>::from_iter((0..128).map(|n| n), usage,
|
||||
//! Some(queue.family()),
|
||||
//! queue.clone()).unwrap();
|
||||
//! let _view = BufferView::new(buffer, format::R32Uint).unwrap();
|
||||
@ -321,7 +321,7 @@ impl From<Error> for BufferViewCreationError {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use buffer::BufferView;
|
||||
use buffer::sys::Usage;
|
||||
use buffer::BufferUsage;
|
||||
use buffer::view::BufferViewCreationError;
|
||||
use buffer::immutable::ImmutableBuffer;
|
||||
use format;
|
||||
@ -331,12 +331,12 @@ mod tests {
|
||||
// `VK_FORMAT_R8G8B8A8_UNORM` guaranteed to be a supported format
|
||||
let (device, queue) = gfx_dev_and_queue!();
|
||||
|
||||
let usage = Usage {
|
||||
let usage = BufferUsage {
|
||||
uniform_texel_buffer: true,
|
||||
.. Usage::none()
|
||||
.. BufferUsage::none()
|
||||
};
|
||||
|
||||
let (buffer, _) = ImmutableBuffer::<[[u8; 4]]>::from_iter((0..128).map(|_| [0; 4]), &usage,
|
||||
let (buffer, _) = ImmutableBuffer::<[[u8; 4]]>::from_iter((0..128).map(|_| [0; 4]), usage,
|
||||
Some(queue.family()), queue.clone()).unwrap();
|
||||
let view = BufferView::new(buffer, format::R8G8B8A8Unorm).unwrap();
|
||||
|
||||
@ -348,12 +348,12 @@ mod tests {
|
||||
// `VK_FORMAT_R8G8B8A8_UNORM` guaranteed to be a supported format
|
||||
let (device, queue) = gfx_dev_and_queue!();
|
||||
|
||||
let usage = Usage {
|
||||
let usage = BufferUsage {
|
||||
storage_texel_buffer: true,
|
||||
.. Usage::none()
|
||||
.. BufferUsage::none()
|
||||
};
|
||||
|
||||
let (buffer, _) = ImmutableBuffer::<[[u8; 4]]>::from_iter((0..128).map(|_| [0; 4]), &usage,
|
||||
let (buffer, _) = ImmutableBuffer::<[[u8; 4]]>::from_iter((0..128).map(|_| [0; 4]), usage,
|
||||
Some(queue.family()),
|
||||
queue.clone()).unwrap();
|
||||
let view = BufferView::new(buffer, format::R8G8B8A8Unorm).unwrap();
|
||||
@ -366,12 +366,12 @@ mod tests {
|
||||
// `VK_FORMAT_R32_UINT` guaranteed to be a supported format for atomics
|
||||
let (device, queue) = gfx_dev_and_queue!();
|
||||
|
||||
let usage = Usage {
|
||||
let usage = BufferUsage {
|
||||
storage_texel_buffer: true,
|
||||
.. Usage::none()
|
||||
.. BufferUsage::none()
|
||||
};
|
||||
|
||||
let (buffer, _) = ImmutableBuffer::<[u32]>::from_iter((0..128).map(|_| 0), &usage,
|
||||
let (buffer, _) = ImmutableBuffer::<[u32]>::from_iter((0..128).map(|_| 0), usage,
|
||||
Some(queue.family()),
|
||||
queue.clone()).unwrap();
|
||||
let view = BufferView::new(buffer, format::R32Uint).unwrap();
|
||||
@ -386,7 +386,7 @@ mod tests {
|
||||
let (device, queue) = gfx_dev_and_queue!();
|
||||
|
||||
let (buffer, _) = ImmutableBuffer::<[[u8; 4]]>::from_iter((0..128).map(|_| [0; 4]),
|
||||
&Usage::none(),
|
||||
BufferUsage::none(),
|
||||
Some(queue.family()),
|
||||
queue.clone()).unwrap();
|
||||
|
||||
@ -400,14 +400,14 @@ mod tests {
|
||||
fn unsupported_format() {
|
||||
let (device, queue) = gfx_dev_and_queue!();
|
||||
|
||||
let usage = Usage {
|
||||
let usage = BufferUsage {
|
||||
uniform_texel_buffer: true,
|
||||
storage_texel_buffer: true,
|
||||
.. Usage::none()
|
||||
.. BufferUsage::none()
|
||||
};
|
||||
|
||||
let (buffer, _) = ImmutableBuffer::<[[f64; 4]]>::from_iter((0..128).map(|_| [0.0; 4]),
|
||||
&usage, Some(queue.family()),
|
||||
usage, Some(queue.family()),
|
||||
queue.clone()).unwrap();
|
||||
|
||||
// TODO: what if R64G64B64A64Sfloat is supported?
|
||||
|
@ -24,7 +24,7 @@ use command_buffer::pool::StandardCommandPool;
|
||||
use device::Device;
|
||||
use device::DeviceOwned;
|
||||
use device::Queue;
|
||||
use image::Layout;
|
||||
use image::ImageLayout;
|
||||
use image::ImageAccess;
|
||||
use instance::QueueFamily;
|
||||
use sync::AccessCheckError;
|
||||
@ -108,7 +108,7 @@ unsafe impl<P> CommandBuffer for AutoCommandBufferBuilder<P>
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn check_image_access(&self, image: &ImageAccess, layout: Layout, exclusive: bool, queue: &Queue)
|
||||
fn check_image_access(&self, image: &ImageAccess, layout: ImageLayout, exclusive: bool, queue: &Queue)
|
||||
-> Result<Option<(PipelineStages, AccessFlagBits)>, AccessCheckError>
|
||||
{
|
||||
self.inner.check_image_access(image, layout, exclusive, queue)
|
||||
|
@ -22,7 +22,7 @@ use command_buffer::CommandBufferExecError;
|
||||
use device::Device;
|
||||
use device::DeviceOwned;
|
||||
use device::Queue;
|
||||
use image::Layout;
|
||||
use image::ImageLayout;
|
||||
use image::ImageAccess;
|
||||
use instance::QueueFamily;
|
||||
use sync::AccessCheckError;
|
||||
@ -68,7 +68,7 @@ unsafe impl<I> CommandBuffer for AbstractStorageLayer<I> where I: CommandBuffer
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn check_image_access(&self, image: &ImageAccess, layout: Layout, exclusive: bool, queue: &Queue)
|
||||
fn check_image_access(&self, image: &ImageAccess, layout: ImageLayout, exclusive: bool, queue: &Queue)
|
||||
-> Result<Option<(PipelineStages, AccessFlagBits)>, AccessCheckError>
|
||||
{
|
||||
self.inner.check_image_access(image, layout, exclusive, queue)
|
||||
|
@ -22,7 +22,7 @@ use command_buffer::CommandBufferBuilder;
|
||||
use command_buffer::CommandBufferExecError;
|
||||
use command_buffer::commands_raw;
|
||||
use framebuffer::FramebufferAbstract;
|
||||
use image::Layout;
|
||||
use image::ImageLayout;
|
||||
use image::ImageAccess;
|
||||
use instance::QueueFamily;
|
||||
use device::Device;
|
||||
@ -159,8 +159,8 @@ struct ResourceEntry {
|
||||
final_stages: PipelineStages,
|
||||
final_access: AccessFlagBits,
|
||||
exclusive: bool,
|
||||
initial_layout: Layout,
|
||||
final_layout: Layout,
|
||||
initial_layout: ImageLayout,
|
||||
final_layout: ImageLayout,
|
||||
}
|
||||
|
||||
impl<I> SubmitSyncBuilderLayer<I> {
|
||||
@ -187,8 +187,8 @@ impl<I> SubmitSyncBuilderLayer<I> {
|
||||
final_stages: stages,
|
||||
final_access: access,
|
||||
exclusive: exclusive,
|
||||
initial_layout: Layout::Undefined,
|
||||
final_layout: Layout::Undefined,
|
||||
initial_layout: ImageLayout::Undefined,
|
||||
final_layout: ImageLayout::Undefined,
|
||||
});
|
||||
},
|
||||
|
||||
@ -198,7 +198,7 @@ impl<I> SubmitSyncBuilderLayer<I> {
|
||||
entry.final_stages = entry.final_stages | stages;
|
||||
entry.final_access = entry.final_access | access;
|
||||
entry.exclusive = entry.exclusive || exclusive;
|
||||
entry.final_layout = Layout::Undefined;
|
||||
entry.final_layout = ImageLayout::Undefined;
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -250,14 +250,14 @@ impl<I> SubmitSyncBuilderLayer<I> {
|
||||
// TODO: slow
|
||||
for index in 0 .. FramebufferAbstract::attachments(framebuffer).len() {
|
||||
let key = Key::FramebufferAttachment(Box::new(framebuffer.clone()), index as u32);
|
||||
let desc = framebuffer.attachment(index).expect("Wrong implementation of FramebufferAbstract trait");
|
||||
let desc = framebuffer.attachment_desc(index).expect("Wrong implementation of FramebufferAbstract trait");
|
||||
let image = FramebufferAbstract::attachments(framebuffer)[index];
|
||||
|
||||
let initial_layout = match self.behavior {
|
||||
SubmitSyncBuilderLayerBehavior::Explicit => desc.initial_layout,
|
||||
SubmitSyncBuilderLayerBehavior::UseLayoutHint => {
|
||||
match desc.initial_layout {
|
||||
Layout::Undefined | Layout::Preinitialized => desc.initial_layout,
|
||||
ImageLayout::Undefined | ImageLayout::Preinitialized => desc.initial_layout,
|
||||
_ => image.parent().initial_layout_requirement(),
|
||||
}
|
||||
},
|
||||
@ -267,7 +267,7 @@ impl<I> SubmitSyncBuilderLayer<I> {
|
||||
SubmitSyncBuilderLayerBehavior::Explicit => desc.final_layout,
|
||||
SubmitSyncBuilderLayerBehavior::UseLayoutHint => {
|
||||
match desc.final_layout {
|
||||
Layout::Undefined | Layout::Preinitialized => desc.final_layout,
|
||||
ImageLayout::Undefined | ImageLayout::Preinitialized => desc.final_layout,
|
||||
_ => image.parent().final_layout_requirement(),
|
||||
}
|
||||
},
|
||||
@ -570,7 +570,7 @@ unsafe impl<I, O, B> AddCommand<commands_raw::CmdDrawIndirectRaw<B>> for SubmitS
|
||||
|
||||
#[inline]
|
||||
fn add(mut self, command: commands_raw::CmdDrawIndirectRaw<B>) -> Result<Self::Out, CommandAddError> {
|
||||
self.add_buffer(command.buffer(), true,
|
||||
self.add_buffer(command.buffer(), false,
|
||||
PipelineStages { draw_indirect: true, .. PipelineStages::none() },
|
||||
AccessFlagBits { indirect_command_read: true, .. AccessFlagBits::none() });
|
||||
|
||||
@ -741,7 +741,7 @@ unsafe impl<I> CommandBuffer for SubmitSyncLayer<I> where I: CommandBuffer {
|
||||
for (key, entry) in self.resources.iter() {
|
||||
match key {
|
||||
&Key::Buffer(ref buf) => {
|
||||
let err = match future.check_buffer_access(&buf, entry.exclusive, queue) {
|
||||
let prev_err = match future.check_buffer_access(&buf, entry.exclusive, queue) {
|
||||
Ok(_) => {
|
||||
unsafe { buf.increase_gpu_lock(); }
|
||||
continue;
|
||||
@ -749,17 +749,16 @@ unsafe impl<I> CommandBuffer for SubmitSyncLayer<I> where I: CommandBuffer {
|
||||
Err(err) => err
|
||||
};
|
||||
|
||||
if !buf.try_gpu_lock(entry.exclusive, queue) {
|
||||
match err {
|
||||
AccessCheckError::Unknown => panic!(), // TODO: use the err returned by try_gpu_lock
|
||||
AccessCheckError::Denied(err) => return Err(err.into()),
|
||||
}
|
||||
match (buf.try_gpu_lock(entry.exclusive, queue), prev_err) {
|
||||
(Ok(_), _) => (),
|
||||
(Err(err), AccessCheckError::Unknown) => return Err(err.into()),
|
||||
(_, AccessCheckError::Denied(err)) => return Err(err.into()),
|
||||
}
|
||||
},
|
||||
|
||||
&Key::Image(ref img) => {
|
||||
let err = match future.check_image_access(img, entry.initial_layout,
|
||||
entry.exclusive, queue)
|
||||
let prev_err = match future.check_image_access(img, entry.initial_layout,
|
||||
entry.exclusive, queue)
|
||||
{
|
||||
Ok(_) => {
|
||||
unsafe { img.increase_gpu_lock(); }
|
||||
@ -768,19 +767,18 @@ unsafe impl<I> CommandBuffer for SubmitSyncLayer<I> where I: CommandBuffer {
|
||||
Err(err) => err
|
||||
};
|
||||
|
||||
if !img.try_gpu_lock(entry.exclusive, queue) {
|
||||
match err {
|
||||
AccessCheckError::Unknown => panic!(), // TODO: use the err returned by try_gpu_lock
|
||||
AccessCheckError::Denied(err) => return Err(err.into()),
|
||||
}
|
||||
match (img.try_gpu_lock(entry.exclusive, queue), prev_err) {
|
||||
(Ok(_), _) => (),
|
||||
(Err(err), AccessCheckError::Unknown) => return Err(err.into()),
|
||||
(_, AccessCheckError::Denied(err)) => return Err(err.into()),
|
||||
}
|
||||
},
|
||||
|
||||
&Key::FramebufferAttachment(ref fb, idx) => {
|
||||
let img = fb.attachments()[idx as usize].parent();
|
||||
|
||||
let err = match future.check_image_access(img, entry.initial_layout,
|
||||
entry.exclusive, queue)
|
||||
let prev_err = match future.check_image_access(img, entry.initial_layout,
|
||||
entry.exclusive, queue)
|
||||
{
|
||||
Ok(_) => {
|
||||
unsafe { img.increase_gpu_lock(); }
|
||||
@ -789,11 +787,12 @@ unsafe impl<I> CommandBuffer for SubmitSyncLayer<I> where I: CommandBuffer {
|
||||
Err(err) => err
|
||||
};
|
||||
|
||||
if !img.try_gpu_lock(entry.exclusive, queue) {
|
||||
match err {
|
||||
AccessCheckError::Unknown => panic!(), // TODO: use the err returned by try_gpu_lock
|
||||
AccessCheckError::Denied(err) => return Err(err.into()),
|
||||
}
|
||||
// FIXME: this is bad because dropping the submit sync layer doesn't drop the
|
||||
// attachments of the framebuffer, meaning that they will stay locked
|
||||
match (img.try_gpu_lock(entry.exclusive, queue), prev_err) {
|
||||
(Ok(_), _) => (),
|
||||
(Err(err), AccessCheckError::Unknown) => return Err(err.into()),
|
||||
(_, AccessCheckError::Denied(err)) => return Err(err.into()),
|
||||
}
|
||||
},
|
||||
}
|
||||
@ -830,7 +829,7 @@ unsafe impl<I> CommandBuffer for SubmitSyncLayer<I> where I: CommandBuffer {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn check_image_access(&self, image: &ImageAccess, layout: Layout, exclusive: bool, queue: &Queue)
|
||||
fn check_image_access(&self, image: &ImageAccess, layout: ImageLayout, exclusive: bool, queue: &Queue)
|
||||
-> Result<Option<(PipelineStages, AccessFlagBits)>, AccessCheckError>
|
||||
{
|
||||
// TODO: check the queue family
|
||||
@ -844,7 +843,7 @@ unsafe impl<I> CommandBuffer for SubmitSyncLayer<I> where I: CommandBuffer {
|
||||
continue;
|
||||
}
|
||||
|
||||
if layout != Layout::Undefined && value.final_layout != layout {
|
||||
if layout != ImageLayout::Undefined && value.final_layout != layout {
|
||||
return Err(AccessCheckError::Denied(AccessError::UnexpectedImageLayout {
|
||||
allowed: value.final_layout,
|
||||
requested: layout,
|
||||
|
@ -28,7 +28,7 @@ use framebuffer::FramebufferAbstract;
|
||||
use framebuffer::RenderPass;
|
||||
use framebuffer::RenderPassAbstract;
|
||||
use framebuffer::Subpass;
|
||||
use image::Layout;
|
||||
use image::ImageLayout;
|
||||
use image::ImageAccess;
|
||||
use instance::QueueFamily;
|
||||
use sync::AccessCheckError;
|
||||
@ -317,7 +317,7 @@ unsafe impl<P> CommandBuffer for UnsafeCommandBuffer<P> where P: CommandPool {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn check_image_access(&self, image: &ImageAccess, layout: Layout, exclusive: bool, queue: &Queue)
|
||||
fn check_image_access(&self, image: &ImageAccess, layout: ImageLayout, exclusive: bool, queue: &Queue)
|
||||
-> Result<Option<(PipelineStages, AccessFlagBits)>, AccessCheckError>
|
||||
{
|
||||
Err(AccessCheckError::Unknown)
|
||||
|
@ -144,7 +144,7 @@ mod tests {
|
||||
fn basic() {
|
||||
let (device, queue) = gfx_dev_and_queue!();
|
||||
|
||||
let buffer = CpuAccessibleBuffer::from_data(&device, &BufferUsage::transfer_dest(),
|
||||
let buffer = CpuAccessibleBuffer::from_data(&device, BufferUsage::transfer_dest(),
|
||||
Some(queue.family()), 0u32).unwrap();
|
||||
|
||||
let _ = PrimaryCbBuilder::new(&device, queue.family())
|
||||
|
@ -20,7 +20,7 @@ use command_buffer::cb::AddCommand;
|
||||
use command_buffer::cb::UnsafeCommandBufferBuilder;
|
||||
use command_buffer::pool::CommandPool;
|
||||
use image::ImageAccess;
|
||||
use image::Layout;
|
||||
use image::ImageLayout;
|
||||
use sync::AccessFlagBits;
|
||||
use sync::PipelineStages;
|
||||
|
||||
@ -102,8 +102,8 @@ impl<'a> CmdPipelineBarrier<'a> {
|
||||
self.dependency_flags = 0;
|
||||
}
|
||||
|
||||
self.src_stage_mask |= source.into();
|
||||
self.dst_stage_mask |= dest.into();
|
||||
self.src_stage_mask |= Into::<vk::PipelineStageFlags>::into(source);
|
||||
self.dst_stage_mask |= Into::<vk::PipelineStageFlags>::into(dest);
|
||||
}
|
||||
|
||||
/// Adds a memory barrier. This means that all the memory writes by the given source stages
|
||||
@ -197,7 +197,7 @@ impl<'a> CmdPipelineBarrier<'a> {
|
||||
pub unsafe fn add_image_memory_barrier<I: ?Sized>(&mut self, image: &'a I, mipmaps: Range<u32>,
|
||||
layers: Range<u32>, source_stage: PipelineStages, source_access: AccessFlagBits,
|
||||
dest_stage: PipelineStages, dest_access: AccessFlagBits, by_region: bool,
|
||||
queue_transfer: Option<(u32, u32)>, current_layout: Layout, new_layout: Layout)
|
||||
queue_transfer: Option<(u32, u32)>, current_layout: ImageLayout, new_layout: ImageLayout)
|
||||
where I: ImageAccess
|
||||
{
|
||||
self.add_execution_dependency(source_stage, dest_stage, by_region);
|
||||
|
@ -22,7 +22,7 @@ use command_buffer::submit::SubmitCommandBufferBuilder;
|
||||
use device::Device;
|
||||
use device::DeviceOwned;
|
||||
use device::Queue;
|
||||
use image::Layout;
|
||||
use image::ImageLayout;
|
||||
use image::ImageAccess;
|
||||
use instance::QueueFamily;
|
||||
use sync::now;
|
||||
@ -136,7 +136,7 @@ pub unsafe trait CommandBuffer: DeviceOwned {
|
||||
fn check_buffer_access(&self, buffer: &BufferAccess, exclusive: bool, queue: &Queue)
|
||||
-> Result<Option<(PipelineStages, AccessFlagBits)>, AccessCheckError>;
|
||||
|
||||
fn check_image_access(&self, image: &ImageAccess, layout: Layout, exclusive: bool, queue: &Queue)
|
||||
fn check_image_access(&self, image: &ImageAccess, layout: ImageLayout, exclusive: bool, queue: &Queue)
|
||||
-> Result<Option<(PipelineStages, AccessFlagBits)>, AccessCheckError>;
|
||||
|
||||
// FIXME: lots of other methods
|
||||
@ -174,7 +174,7 @@ unsafe impl<T> CommandBuffer for T where T: SafeDeref, T::Target: CommandBuffer
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn check_image_access(&self, image: &ImageAccess, layout: Layout, exclusive: bool, queue: &Queue)
|
||||
fn check_image_access(&self, image: &ImageAccess, layout: ImageLayout, exclusive: bool, queue: &Queue)
|
||||
-> Result<Option<(PipelineStages, AccessFlagBits)>, AccessCheckError>
|
||||
{
|
||||
(**self).check_image_access(image, layout, exclusive, queue)
|
||||
@ -284,7 +284,7 @@ unsafe impl<F, Cb> GpuFuture for CommandBufferExecFuture<F, Cb>
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn check_image_access(&self, image: &ImageAccess, layout: Layout, exclusive: bool, queue: &Queue)
|
||||
fn check_image_access(&self, image: &ImageAccess, layout: ImageLayout, exclusive: bool, queue: &Queue)
|
||||
-> Result<Option<(PipelineStages, AccessFlagBits)>, AccessCheckError>
|
||||
{
|
||||
match self.command_buffer.check_image_access(image, layout, exclusive, queue) {
|
||||
|
@ -27,7 +27,7 @@ use device::Device;
|
||||
use device::DeviceOwned;
|
||||
use image::ImageAccess;
|
||||
use image::ImageView;
|
||||
use image::sys::Layout;
|
||||
use image::ImageLayout;
|
||||
use sampler::Sampler;
|
||||
use sync::AccessFlagBits;
|
||||
use sync::PipelineStages;
|
||||
@ -290,7 +290,7 @@ unsafe impl<L, R, T> SimpleDescriptorSetImageExt<L, R> for T
|
||||
num_mipmaps: 1, // FIXME:
|
||||
first_layer: 0, // FIXME:
|
||||
num_layers: 1, // FIXME:
|
||||
layout: Layout::General, // FIXME:
|
||||
layout: ImageLayout::General, // FIXME:
|
||||
stage: PipelineStages::none(), // FIXME:
|
||||
access: AccessFlagBits::none(), // FIXME:
|
||||
})
|
||||
@ -332,7 +332,7 @@ unsafe impl<L, R, T> SimpleDescriptorSetImageExt<L, R> for (T, Arc<Sampler>)
|
||||
num_mipmaps: 1, // FIXME:
|
||||
first_layer: 0, // FIXME:
|
||||
num_layers: 1, // FIXME:
|
||||
layout: Layout::General, // FIXME:
|
||||
layout: ImageLayout::General, // FIXME:
|
||||
stage: PipelineStages::none(), // FIXME:
|
||||
access: AccessFlagBits::none(), // FIXME:
|
||||
})
|
||||
@ -375,7 +375,7 @@ unsafe impl<L, R, T> SimpleDescriptorSetImageExt<L, R> for Vec<(T, Arc<Sampler>)
|
||||
num_mipmaps: 1, // FIXME:
|
||||
first_layer: 0, // FIXME:
|
||||
num_layers: 1, // FIXME:
|
||||
layout: Layout::General, // FIXME:
|
||||
layout: ImageLayout::General, // FIXME:
|
||||
stage: PipelineStages::none(), // FIXME:
|
||||
access: AccessFlagBits::none(), // FIXME:
|
||||
});
|
||||
@ -475,7 +475,7 @@ pub struct SimpleDescriptorSetImg<I> {
|
||||
num_mipmaps: u32,
|
||||
first_layer: u32,
|
||||
num_layers: u32,
|
||||
layout: Layout,
|
||||
layout: ImageLayout,
|
||||
stage: PipelineStages,
|
||||
access: AccessFlagBits,
|
||||
}
|
||||
|
@ -547,7 +547,7 @@ impl UnsafeDescriptorSet {
|
||||
/// command buffer contains a pointer/reference to a descriptor set, it is illegal to write
|
||||
/// to it.
|
||||
///
|
||||
pub unsafe fn write<I>(&mut self, device: &Arc<Device>, writes: I)
|
||||
pub unsafe fn write<I>(&mut self, device: &Device, writes: I)
|
||||
where I: Iterator<Item = DescriptorWrite>
|
||||
{
|
||||
let vk = device.pointers();
|
||||
|
@ -23,7 +23,7 @@ use descriptor::pipeline_layout::PipelineLayoutDescPcRange;
|
||||
/// use vulkano::descriptor::pipeline_layout::PipelineLayoutDesc;
|
||||
///
|
||||
/// # let device: Arc<Device> = return;
|
||||
/// let pipeline_layout = EmptyPipelineDesc.build(&device).unwrap();
|
||||
/// let pipeline_layout = EmptyPipelineDesc.build(device.clone()).unwrap();
|
||||
/// ```
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct EmptyPipelineDesc;
|
||||
|
@ -48,7 +48,7 @@ impl<L> PipelineLayout<L> where L: PipelineLayoutDesc {
|
||||
/// - Panics if one of the layout returned by `provided_set_layout()` belongs to a different
|
||||
/// device than the one passed as parameter.
|
||||
#[inline]
|
||||
pub fn new(device: &Arc<Device>, desc: L)
|
||||
pub fn new(device: Arc<Device>, desc: L)
|
||||
-> Result<PipelineLayout<L>, PipelineLayoutCreationError>
|
||||
{
|
||||
let vk = device.pointers();
|
||||
|
@ -99,7 +99,7 @@ pub unsafe trait PipelineLayoutDesc {
|
||||
///
|
||||
/// > **Note**: This is just a shortcut for `PipelineLayout::new`.
|
||||
#[inline]
|
||||
fn build(self, device: &Arc<Device>)
|
||||
fn build(self, device: Arc<Device>)
|
||||
-> Result<PipelineLayout<Self>, PipelineLayoutCreationError>
|
||||
where Self: Sized
|
||||
{
|
||||
|
@ -299,29 +299,20 @@ impl Device {
|
||||
Ok((device, output_queues))
|
||||
}
|
||||
|
||||
/// See the docs of wait().
|
||||
// FIXME: must synchronize all queuees
|
||||
#[inline]
|
||||
pub fn wait_raw(&self) -> Result<(), OomError> {
|
||||
unsafe {
|
||||
try!(check_errors(self.vk.DeviceWaitIdle(self.device)));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Waits until all work on this device has finished. You should never need to call
|
||||
/// this function, but it can be useful for debugging or benchmarking purposes.
|
||||
///
|
||||
/// This is the Vulkan equivalent of `glFinish`.
|
||||
/// > **Note**: This is the Vulkan equivalent of OpenGL's `glFinish`.
|
||||
///
|
||||
/// # Panic
|
||||
/// # Safety
|
||||
///
|
||||
/// - Panics if the device or host ran out of memory.
|
||||
/// This function is not thread-safe. You must not submit anything to any of the queue
|
||||
/// of the device (either explicitely or implicitely, for example with a future's destructor)
|
||||
/// while this function is waiting.
|
||||
///
|
||||
// FIXME: must synchronize all queuees
|
||||
#[inline]
|
||||
pub fn wait(&self) {
|
||||
self.wait_raw().unwrap();
|
||||
pub unsafe fn wait(&self) -> Result<(), OomError> {
|
||||
try!(check_errors(self.vk.DeviceWaitIdle(self.device)));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns the instance used to create this device.
|
||||
@ -357,7 +348,7 @@ impl Device {
|
||||
}
|
||||
|
||||
// The weak pointer is empty, so we create the pool.
|
||||
let new_pool = StdMemoryPool::new(me);
|
||||
let new_pool = StdMemoryPool::new(me.clone());
|
||||
*pool = Arc::downgrade(&new_pool);
|
||||
new_pool
|
||||
}
|
||||
|
@ -7,43 +7,20 @@
|
||||
// notice may not be copied, modified, or distributed except
|
||||
// according to those terms.
|
||||
|
||||
use std::cmp;
|
||||
use std::sync::Arc;
|
||||
use SafeDeref;
|
||||
use image::ImageViewAccess;
|
||||
use image::sys::UnsafeImageView;
|
||||
//use sync::AccessFlagBits;
|
||||
//use sync::PipelineStages;
|
||||
|
||||
/// A list of attachments.
|
||||
// TODO: rework this trait
|
||||
pub unsafe trait AttachmentsList {
|
||||
/// Returns the image views of this list.
|
||||
// TODO: better return type
|
||||
fn raw_image_view_handles(&self) -> Vec<&UnsafeImageView>;
|
||||
|
||||
/// Returns the dimensions of the intersection of the views. Returns `None` if the list is
|
||||
/// empty.
|
||||
///
|
||||
/// For example if one view is 256x256x2 and another one is 128x512x3, then this function
|
||||
/// should return 128x256x2.
|
||||
fn intersection_dimensions(&self) -> Option<[u32; 3]>;
|
||||
|
||||
// TODO: meh for API
|
||||
fn as_image_view_accesses(&self) -> Vec<&ImageViewAccess>;
|
||||
}
|
||||
|
||||
unsafe impl<T> AttachmentsList for T where T: SafeDeref, T::Target: AttachmentsList {
|
||||
#[inline]
|
||||
fn raw_image_view_handles(&self) -> Vec<&UnsafeImageView> {
|
||||
(**self).raw_image_view_handles()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn intersection_dimensions(&self) -> Option<[u32; 3]> {
|
||||
(**self).intersection_dimensions()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn as_image_view_accesses(&self) -> Vec<&ImageViewAccess> {
|
||||
(**self).as_image_view_accesses()
|
||||
@ -51,16 +28,6 @@ unsafe impl<T> AttachmentsList for T where T: SafeDeref, T::Target: AttachmentsL
|
||||
}
|
||||
|
||||
unsafe impl AttachmentsList for () {
|
||||
#[inline]
|
||||
fn raw_image_view_handles(&self) -> Vec<&UnsafeImageView> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn intersection_dimensions(&self) -> Option<[u32; 3]> {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn as_image_view_accesses(&self) -> Vec<&ImageViewAccess> {
|
||||
vec![]
|
||||
@ -68,106 +35,19 @@ unsafe impl AttachmentsList for () {
|
||||
}
|
||||
|
||||
unsafe impl AttachmentsList for Vec<Arc<ImageViewAccess + Send + Sync>> {
|
||||
#[inline]
|
||||
fn raw_image_view_handles(&self) -> Vec<&UnsafeImageView> {
|
||||
self.iter().map(|img| img.inner()).collect()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn intersection_dimensions(&self) -> Option<[u32; 3]> {
|
||||
let mut dims = None;
|
||||
|
||||
for view in self.iter() {
|
||||
debug_assert_eq!(view.dimensions().depth(), 1);
|
||||
|
||||
match dims {
|
||||
None => {
|
||||
dims = Some([
|
||||
view.dimensions().width(),
|
||||
view.dimensions().height(),
|
||||
view.dimensions().array_layers()
|
||||
]);
|
||||
},
|
||||
Some(ref mut d) => {
|
||||
d[0] = cmp::min(view.dimensions().width(), d[0]);
|
||||
d[1] = cmp::min(view.dimensions().height(), d[1]);
|
||||
d[2] = cmp::min(view.dimensions().array_layers(), d[2]);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
dims
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn as_image_view_accesses(&self) -> Vec<&ImageViewAccess> {
|
||||
self.iter().map(|p| &**p as &ImageViewAccess).collect()
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_into_atch_list {
|
||||
($first:ident $(, $rest:ident)*) => (
|
||||
unsafe impl<$first $(, $rest)*> AttachmentsList for ($first, $($rest),*)
|
||||
where $first: ImageViewAccess,
|
||||
$($rest: ImageViewAccess,)*
|
||||
{
|
||||
#[inline]
|
||||
#[allow(non_snake_case)]
|
||||
fn raw_image_view_handles(&self) -> Vec<&UnsafeImageView> {
|
||||
let &(ref $first, $(ref $rest,)*) = self;
|
||||
|
||||
vec![
|
||||
&$first.inner(),
|
||||
$(
|
||||
&$rest.inner(),
|
||||
)*
|
||||
]
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[allow(non_snake_case)]
|
||||
fn intersection_dimensions(&self) -> Option<[u32; 3]> {
|
||||
let &(ref $first, $(ref $rest,)*) = self;
|
||||
|
||||
let dims = {
|
||||
let d = $first.dimensions();
|
||||
debug_assert_eq!(d.depth(), 1);
|
||||
[d.width(), d.height(), d.array_layers()]
|
||||
};
|
||||
|
||||
$(
|
||||
let dims = {
|
||||
let d = $rest.dimensions();
|
||||
debug_assert_eq!(d.depth(), 1);
|
||||
[
|
||||
cmp::min(d.width(), dims[0]),
|
||||
cmp::min(d.height(), dims[1]),
|
||||
cmp::min(d.array_layers(), dims[2])
|
||||
]
|
||||
};
|
||||
)*
|
||||
|
||||
Some(dims)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[allow(non_snake_case)]
|
||||
fn as_image_view_accesses(&self) -> Vec<&ImageViewAccess> {
|
||||
let &(ref $first, $(ref $rest,)*) = self;
|
||||
|
||||
vec![
|
||||
&*$first,
|
||||
$(
|
||||
&*$rest,
|
||||
)*
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
impl_into_atch_list!($($rest),*);
|
||||
);
|
||||
|
||||
() => ();
|
||||
unsafe impl<A, B> AttachmentsList for (A, B)
|
||||
where A: AttachmentsList, B: ImageViewAccess
|
||||
{
|
||||
#[inline]
|
||||
fn as_image_view_accesses(&self) -> Vec<&ImageViewAccess> {
|
||||
let mut list = self.0.as_image_view_accesses();
|
||||
list.push(&self.1);
|
||||
list
|
||||
}
|
||||
}
|
||||
|
||||
impl_into_atch_list!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z);
|
||||
|
232
vulkano/src/framebuffer/compat_atch.rs
Normal file
232
vulkano/src/framebuffer/compat_atch.rs
Normal file
@ -0,0 +1,232 @@
|
||||
// Copyright (c) 2017 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 module contains the `ensure_image_view_compatible` function, which verifies whether
|
||||
//! an image view can be used as a render pass attachment.
|
||||
|
||||
use std::error;
|
||||
use std::fmt;
|
||||
use format::Format;
|
||||
use framebuffer::RenderPassDesc;
|
||||
use image::ImageViewAccess;
|
||||
|
||||
/// Checks whether the given image view is allowed to be the nth attachment of the given render
|
||||
/// pass.
|
||||
///
|
||||
/// # Panic
|
||||
///
|
||||
/// Panicks if the attachment number is out of range.
|
||||
// TODO: add a specializable trait instead, that uses this function
|
||||
// TODO: ImageView instead of ImageViewAccess?
|
||||
pub fn ensure_image_view_compatible<Rp, I>(render_pass: &Rp, attachment_num: usize, image: &I)
|
||||
-> Result<(), IncompatibleRenderPassAttachmentError>
|
||||
where Rp: ?Sized + RenderPassDesc,
|
||||
I: ?Sized + ImageViewAccess
|
||||
{
|
||||
let attachment_desc = render_pass.attachment_desc(attachment_num)
|
||||
.expect("Attachment num out of range");
|
||||
|
||||
if image.format() != attachment_desc.format {
|
||||
return Err(IncompatibleRenderPassAttachmentError::FormatMismatch {
|
||||
expected: attachment_desc.format,
|
||||
obtained: image.format(),
|
||||
});
|
||||
}
|
||||
|
||||
if image.samples() != attachment_desc.samples {
|
||||
return Err(IncompatibleRenderPassAttachmentError::SamplesMismatch {
|
||||
expected: attachment_desc.samples,
|
||||
obtained: image.samples(),
|
||||
});
|
||||
}
|
||||
|
||||
if !image.identity_swizzle() {
|
||||
return Err(IncompatibleRenderPassAttachmentError::NotIdentitySwizzled);
|
||||
}
|
||||
|
||||
for subpass_num in 0 .. render_pass.num_subpasses() {
|
||||
let subpass = render_pass.subpass_desc(subpass_num).expect("Subpass num out of range ; \
|
||||
wrong RenderPassDesc trait impl");
|
||||
|
||||
if subpass.color_attachments.iter().any(|&(n, _)| n == attachment_num) {
|
||||
debug_assert!(image.parent().has_color()); // Was normally checked by the render pass.
|
||||
if !image.parent().inner().usage_color_attachment() {
|
||||
return Err(IncompatibleRenderPassAttachmentError::MissingColorAttachmentUsage);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some((ds, _)) = subpass.depth_stencil {
|
||||
if ds == attachment_num {
|
||||
// Was normally checked by the render pass.
|
||||
debug_assert!(image.parent().has_depth() || image.parent().has_stencil());
|
||||
if !image.parent().inner().usage_depth_stencil_attachment() {
|
||||
return Err(IncompatibleRenderPassAttachmentError::MissingDepthStencilAttachmentUsage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if subpass.input_attachments.iter().any(|&(n, _)| n == attachment_num) {
|
||||
if !image.parent().inner().usage_input_attachment() {
|
||||
return Err(IncompatibleRenderPassAttachmentError::MissingInputAttachmentUsage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: consider forbidding LoadOp::Load if image is transient
|
||||
|
||||
// TODO: are all image layouts allowed? check this
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Error that can happen when an image is not compatible with a render pass attachment slot.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum IncompatibleRenderPassAttachmentError {
|
||||
/// The image format expected by the render pass doesn't match the actual format of
|
||||
/// the image.
|
||||
FormatMismatch {
|
||||
/// Format expected by the render pass.
|
||||
expected: Format,
|
||||
/// Format of the image.
|
||||
obtained: Format,
|
||||
},
|
||||
|
||||
/// The number of samples expected by the render pass doesn't match the number of samples of
|
||||
/// the image.
|
||||
SamplesMismatch {
|
||||
/// Number of samples expected by the render pass.
|
||||
expected: u32,
|
||||
/// Number of samples of the image.
|
||||
obtained: u32,
|
||||
},
|
||||
|
||||
/// The image view has a component swizzle that is different from identity.
|
||||
NotIdentitySwizzled,
|
||||
|
||||
/// The image is used as a color attachment but is missing the color attachment usage.
|
||||
MissingColorAttachmentUsage,
|
||||
|
||||
/// The image is used as a depth/stencil attachment but is missing the depth-stencil attachment
|
||||
/// usage.
|
||||
MissingDepthStencilAttachmentUsage,
|
||||
|
||||
/// The image is used as an input attachment but is missing the input attachment usage.
|
||||
MissingInputAttachmentUsage,
|
||||
}
|
||||
|
||||
impl error::Error for IncompatibleRenderPassAttachmentError {
|
||||
#[inline]
|
||||
fn description(&self) -> &str {
|
||||
match *self {
|
||||
IncompatibleRenderPassAttachmentError::FormatMismatch { .. } => {
|
||||
"mismatch between the format expected by the render pass and the actual format"
|
||||
},
|
||||
IncompatibleRenderPassAttachmentError::SamplesMismatch { .. } => {
|
||||
"mismatch between the number of samples expected by the render pass and the actual \
|
||||
number of samples"
|
||||
},
|
||||
IncompatibleRenderPassAttachmentError::NotIdentitySwizzled => {
|
||||
"the image view does not use identity swizzling"
|
||||
},
|
||||
IncompatibleRenderPassAttachmentError::MissingColorAttachmentUsage => {
|
||||
"the image is used as a color attachment but is missing the color attachment usage"
|
||||
},
|
||||
IncompatibleRenderPassAttachmentError::MissingDepthStencilAttachmentUsage => {
|
||||
"the image is used as a depth/stencil attachment but is missing the depth-stencil \
|
||||
attachment usage"
|
||||
},
|
||||
IncompatibleRenderPassAttachmentError::MissingInputAttachmentUsage => {
|
||||
"the image is used as an input attachment but is missing the input \
|
||||
attachment usage"
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for IncompatibleRenderPassAttachmentError {
|
||||
#[inline]
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
write!(fmt, "{}", error::Error::description(self))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use format::Format;
|
||||
use framebuffer::EmptySinglePassRenderPassDesc;
|
||||
use image::AttachmentImage;
|
||||
use image::ImageView;
|
||||
use super::ensure_image_view_compatible;
|
||||
use super::IncompatibleRenderPassAttachmentError;
|
||||
|
||||
#[test]
|
||||
fn basic_ok() {
|
||||
let (device, _) = gfx_dev_and_queue!();
|
||||
|
||||
let rp = single_pass_renderpass!(device.clone(),
|
||||
attachments: {
|
||||
color: {
|
||||
load: Clear,
|
||||
store: Store,
|
||||
format: Format::R8G8B8A8Unorm,
|
||||
samples: 1,
|
||||
}
|
||||
},
|
||||
pass: {
|
||||
color: [color],
|
||||
depth_stencil: {}
|
||||
}
|
||||
).unwrap();
|
||||
|
||||
let img = AttachmentImage::new(device, [128, 128], Format::R8G8B8A8Unorm).unwrap();
|
||||
|
||||
ensure_image_view_compatible(&rp, 0, &img.access()).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_mismatch() {
|
||||
let (device, _) = gfx_dev_and_queue!();
|
||||
|
||||
let rp = single_pass_renderpass!(device.clone(),
|
||||
attachments: {
|
||||
color: {
|
||||
load: Clear,
|
||||
store: Store,
|
||||
format: Format::R16G16Sfloat,
|
||||
samples: 1,
|
||||
}
|
||||
},
|
||||
pass: {
|
||||
color: [color],
|
||||
depth_stencil: {}
|
||||
}
|
||||
).unwrap();
|
||||
|
||||
let img = AttachmentImage::new(device, [128, 128], Format::R8G8B8A8Unorm).unwrap();
|
||||
|
||||
match ensure_image_view_compatible(&rp, 0, &img.access()) {
|
||||
Err(IncompatibleRenderPassAttachmentError::FormatMismatch {
|
||||
expected: Format::R16G16Sfloat, obtained: Format::R8G8B8A8Unorm }) => (),
|
||||
e => panic!("{:?}", e)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Attachment num out of range")]
|
||||
fn attachment_out_of_range() {
|
||||
let (device, _) = gfx_dev_and_queue!();
|
||||
|
||||
let rp = EmptySinglePassRenderPassDesc;
|
||||
let img = AttachmentImage::new(device, [128, 128], Format::R8G8B8A8Unorm).unwrap();
|
||||
|
||||
let _ = ensure_image_view_compatible(&rp, 0, &img.access());
|
||||
}
|
||||
|
||||
// TODO: more tests
|
||||
}
|
@ -14,12 +14,10 @@ use format::ClearValue;
|
||||
use format::Format;
|
||||
use format::FormatTy;
|
||||
use framebuffer::RenderPass;
|
||||
use framebuffer::RenderPassDescAttachmentsList;
|
||||
use framebuffer::RenderPassDescClearValues;
|
||||
use framebuffer::RenderPassCompatible;
|
||||
use framebuffer::RenderPassCreationError;
|
||||
use image::Layout as ImageLayout;
|
||||
use image::ImageViewAccess;
|
||||
use image::ImageLayout as ImageLayout;
|
||||
use sync::AccessFlagBits;
|
||||
use sync::PipelineStages;
|
||||
|
||||
@ -44,42 +42,46 @@ use vk;
|
||||
/// - The provided methods shouldn't be overriden with fancy implementations. For example
|
||||
/// `build_render_pass` must build a render pass from the description and not a different one.
|
||||
///
|
||||
pub unsafe trait RenderPassDesc: RenderPassDescClearValues<Vec<ClearValue>> +
|
||||
RenderPassDescAttachmentsList<Vec<Arc<ImageViewAccess + Send + Sync>>>
|
||||
{
|
||||
pub unsafe trait RenderPassDesc: RenderPassDescClearValues<Vec<ClearValue>> {
|
||||
/// Returns the number of attachments of the render pass.
|
||||
fn num_attachments(&self) -> usize;
|
||||
|
||||
/// Returns the description of an attachment.
|
||||
///
|
||||
/// Returns `None` if `num` is superior to `num_attachments()`.
|
||||
fn attachment(&self, num: usize) -> Option<LayoutAttachmentDescription>;
|
||||
fn attachment_desc(&self, num: usize) -> Option<LayoutAttachmentDescription>;
|
||||
|
||||
/// Returns an iterator to the list of attachments.
|
||||
#[inline]
|
||||
fn attachments(&self) -> RenderPassDescAttachments<Self> where Self: Sized {
|
||||
fn attachment_descs(&self) -> RenderPassDescAttachments<Self> where Self: Sized {
|
||||
RenderPassDescAttachments { render_pass: self, num: 0 }
|
||||
}
|
||||
|
||||
/// Returns the number of subpasses of the render pass.
|
||||
fn num_subpasses(&self) -> usize;
|
||||
/// Returns the description of a suvpass.
|
||||
|
||||
/// Returns the description of a subpass.
|
||||
///
|
||||
/// Returns `None` if `num` is superior to `num_subpasses()`.
|
||||
fn subpass(&self, num: usize) -> Option<LayoutPassDescription>;
|
||||
fn subpass_desc(&self, num: usize) -> Option<LayoutPassDescription>;
|
||||
|
||||
/// Returns an iterator to the list of subpasses.
|
||||
#[inline]
|
||||
fn subpasses(&self) -> RenderPassDescSubpasses<Self> where Self: Sized {
|
||||
fn subpass_descs(&self) -> RenderPassDescSubpasses<Self> where Self: Sized {
|
||||
RenderPassDescSubpasses { render_pass: self, num: 0 }
|
||||
}
|
||||
|
||||
/// Returns the number of dependencies of the render pass.
|
||||
fn num_dependencies(&self) -> usize;
|
||||
|
||||
/// Returns the description of a dependency.
|
||||
///
|
||||
/// Returns `None` if `num` is superior to `num_dependencies()`.
|
||||
fn dependency(&self, num: usize) -> Option<LayoutPassDependencyDescription>;
|
||||
fn dependency_desc(&self, num: usize) -> Option<LayoutPassDependencyDescription>;
|
||||
|
||||
/// Returns an iterator to the list of dependencies.
|
||||
#[inline]
|
||||
fn dependencies(&self) -> RenderPassDescDependencies<Self> where Self: Sized {
|
||||
fn dependency_descs(&self) -> RenderPassDescDependencies<Self> where Self: Sized {
|
||||
RenderPassDescDependencies { render_pass: self, num: 0 }
|
||||
}
|
||||
|
||||
@ -112,17 +114,17 @@ pub unsafe trait RenderPassDesc: RenderPassDescClearValues<Vec<ClearValue>> +
|
||||
/// Returns the number of color attachments of a subpass. Returns `None` if out of range.
|
||||
#[inline]
|
||||
fn num_color_attachments(&self, subpass: u32) -> Option<u32> {
|
||||
(&self).subpasses().skip(subpass as usize).next().map(|p| p.color_attachments.len() as u32)
|
||||
(&self).subpass_descs().skip(subpass as usize).next().map(|p| p.color_attachments.len() as u32)
|
||||
}
|
||||
|
||||
/// Returns the number of samples of the attachments of a subpass. Returns `None` if out of
|
||||
/// range or if the subpass has no attachment. TODO: return an enum instead?
|
||||
#[inline]
|
||||
fn num_samples(&self, subpass: u32) -> Option<u32> {
|
||||
(&self).subpasses().skip(subpass as usize).next().and_then(|p| {
|
||||
(&self).subpass_descs().skip(subpass as usize).next().and_then(|p| {
|
||||
// TODO: chain input attachments as well?
|
||||
p.color_attachments.iter().cloned().chain(p.depth_stencil.clone().into_iter())
|
||||
.filter_map(|a| (&self).attachments().skip(a.0).next())
|
||||
.filter_map(|a| (&self).attachment_descs().skip(a.0).next())
|
||||
.next().map(|a| a.samples)
|
||||
})
|
||||
}
|
||||
@ -131,13 +133,13 @@ pub unsafe trait RenderPassDesc: RenderPassDescClearValues<Vec<ClearValue>> +
|
||||
/// second element is `true` if there's a stencil attachment. Returns `None` if out of range.
|
||||
#[inline]
|
||||
fn has_depth_stencil_attachment(&self, subpass: u32) -> Option<(bool, bool)> {
|
||||
(&self).subpasses().skip(subpass as usize).next().map(|p| {
|
||||
(&self).subpass_descs().skip(subpass as usize).next().map(|p| {
|
||||
let atch_num = match p.depth_stencil {
|
||||
Some((d, _)) => d,
|
||||
None => return (false, false)
|
||||
};
|
||||
|
||||
match (&self).attachments().skip(atch_num).next().unwrap().format.ty() {
|
||||
match (&self).attachment_descs().skip(atch_num).next().unwrap().format.ty() {
|
||||
FormatTy::Depth => (true, false),
|
||||
FormatTy::Stencil => (false, true),
|
||||
FormatTy::DepthStencil => (true, true),
|
||||
@ -149,13 +151,13 @@ pub unsafe trait RenderPassDesc: RenderPassDescClearValues<Vec<ClearValue>> +
|
||||
/// Returns true if a subpass has a depth attachment or a depth-stencil attachment.
|
||||
#[inline]
|
||||
fn has_depth(&self, subpass: u32) -> Option<bool> {
|
||||
(&self).subpasses().skip(subpass as usize).next().map(|p| {
|
||||
(&self).subpass_descs().skip(subpass as usize).next().map(|p| {
|
||||
let atch_num = match p.depth_stencil {
|
||||
Some((d, _)) => d,
|
||||
None => return false
|
||||
};
|
||||
|
||||
match (&self).attachments().skip(atch_num).next().unwrap().format.ty() {
|
||||
match (&self).attachment_descs().skip(atch_num).next().unwrap().format.ty() {
|
||||
FormatTy::Depth => true,
|
||||
FormatTy::Stencil => false,
|
||||
FormatTy::DepthStencil => true,
|
||||
@ -168,7 +170,7 @@ pub unsafe trait RenderPassDesc: RenderPassDescClearValues<Vec<ClearValue>> +
|
||||
/// layout is not `DepthStencilReadOnlyOptimal`.
|
||||
#[inline]
|
||||
fn has_writable_depth(&self, subpass: u32) -> Option<bool> {
|
||||
(&self).subpasses().skip(subpass as usize).next().map(|p| {
|
||||
(&self).subpass_descs().skip(subpass as usize).next().map(|p| {
|
||||
let atch_num = match p.depth_stencil {
|
||||
Some((d, l)) => {
|
||||
if l == ImageLayout::DepthStencilReadOnlyOptimal { return false; }
|
||||
@ -177,7 +179,7 @@ pub unsafe trait RenderPassDesc: RenderPassDescClearValues<Vec<ClearValue>> +
|
||||
None => return false
|
||||
};
|
||||
|
||||
match (&self).attachments().skip(atch_num).next().unwrap().format.ty() {
|
||||
match (&self).attachment_descs().skip(atch_num).next().unwrap().format.ty() {
|
||||
FormatTy::Depth => true,
|
||||
FormatTy::Stencil => false,
|
||||
FormatTy::DepthStencil => true,
|
||||
@ -189,13 +191,13 @@ pub unsafe trait RenderPassDesc: RenderPassDescClearValues<Vec<ClearValue>> +
|
||||
/// Returns true if a subpass has a stencil attachment or a depth-stencil attachment.
|
||||
#[inline]
|
||||
fn has_stencil(&self, subpass: u32) -> Option<bool> {
|
||||
(&self).subpasses().skip(subpass as usize).next().map(|p| {
|
||||
(&self).subpass_descs().skip(subpass as usize).next().map(|p| {
|
||||
let atch_num = match p.depth_stencil {
|
||||
Some((d, _)) => d,
|
||||
None => return false
|
||||
};
|
||||
|
||||
match (&self).attachments().skip(atch_num).next().unwrap().format.ty() {
|
||||
match (&self).attachment_descs().skip(atch_num).next().unwrap().format.ty() {
|
||||
FormatTy::Depth => false,
|
||||
FormatTy::Stencil => true,
|
||||
FormatTy::DepthStencil => true,
|
||||
@ -208,7 +210,7 @@ pub unsafe trait RenderPassDesc: RenderPassDescClearValues<Vec<ClearValue>> +
|
||||
/// layout is not `DepthStencilReadOnlyOptimal`.
|
||||
#[inline]
|
||||
fn has_writable_stencil(&self, subpass: u32) -> Option<bool> {
|
||||
(&self).subpasses().skip(subpass as usize).next().map(|p| {
|
||||
(&self).subpass_descs().skip(subpass as usize).next().map(|p| {
|
||||
let atch_num = match p.depth_stencil {
|
||||
Some((d, l)) => {
|
||||
if l == ImageLayout::DepthStencilReadOnlyOptimal { return false; }
|
||||
@ -217,7 +219,7 @@ pub unsafe trait RenderPassDesc: RenderPassDescClearValues<Vec<ClearValue>> +
|
||||
None => return false
|
||||
};
|
||||
|
||||
match (&self).attachments().skip(atch_num).next().unwrap().format.ty() {
|
||||
match (&self).attachment_descs().skip(atch_num).next().unwrap().format.ty() {
|
||||
FormatTy::Depth => false,
|
||||
FormatTy::Stencil => true,
|
||||
FormatTy::DepthStencil => true,
|
||||
@ -234,8 +236,8 @@ unsafe impl<T> RenderPassDesc for T where T: SafeDeref, T::Target: RenderPassDes
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn attachment(&self, num: usize) -> Option<LayoutAttachmentDescription> {
|
||||
(**self).attachment(num)
|
||||
fn attachment_desc(&self, num: usize) -> Option<LayoutAttachmentDescription> {
|
||||
(**self).attachment_desc(num)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@ -244,8 +246,8 @@ unsafe impl<T> RenderPassDesc for T where T: SafeDeref, T::Target: RenderPassDes
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn subpass(&self, num: usize) -> Option<LayoutPassDescription> {
|
||||
(**self).subpass(num)
|
||||
fn subpass_desc(&self, num: usize) -> Option<LayoutPassDescription> {
|
||||
(**self).subpass_desc(num)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@ -254,8 +256,8 @@ unsafe impl<T> RenderPassDesc for T where T: SafeDeref, T::Target: RenderPassDes
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn dependency(&self, num: usize) -> Option<LayoutPassDependencyDescription> {
|
||||
(**self).dependency(num)
|
||||
fn dependency_desc(&self, num: usize) -> Option<LayoutPassDependencyDescription> {
|
||||
(**self).dependency_desc(num)
|
||||
}
|
||||
}
|
||||
|
||||
@ -273,7 +275,7 @@ impl<'a, R: ?Sized + 'a> Iterator for RenderPassDescAttachments<'a, R> where R:
|
||||
if self.num < self.render_pass.num_attachments() {
|
||||
let n = self.num;
|
||||
self.num += 1;
|
||||
Some(self.render_pass.attachment(n).expect("Wrong RenderPassDesc implementation"))
|
||||
Some(self.render_pass.attachment_desc(n).expect("Wrong RenderPassDesc implementation"))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@ -294,7 +296,7 @@ impl<'a, R: ?Sized + 'a> Iterator for RenderPassDescSubpasses<'a, R> where R: Re
|
||||
if self.num < self.render_pass.num_subpasses() {
|
||||
let n = self.num;
|
||||
self.num += 1;
|
||||
Some(self.render_pass.subpass(n).expect("Wrong RenderPassDesc implementation"))
|
||||
Some(self.render_pass.subpass_desc(n).expect("Wrong RenderPassDesc implementation"))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@ -315,7 +317,7 @@ impl<'a, R: ?Sized + 'a> Iterator for RenderPassDescDependencies<'a, R> where R:
|
||||
if self.num < self.render_pass.num_dependencies() {
|
||||
let n = self.num;
|
||||
self.num += 1;
|
||||
Some(self.render_pass.dependency(n).expect("Wrong RenderPassDesc implementation"))
|
||||
Some(self.render_pass.dependency_desc(n).expect("Wrong RenderPassDesc implementation"))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -8,17 +8,12 @@
|
||||
// according to those terms.
|
||||
|
||||
use std::iter;
|
||||
use std::sync::Arc;
|
||||
use format::ClearValue;
|
||||
use framebuffer::AttachmentsList;
|
||||
use framebuffer::FramebufferCreationError;
|
||||
use framebuffer::RenderPassDesc;
|
||||
use framebuffer::RenderPassDescAttachmentsList;
|
||||
use framebuffer::RenderPassDescClearValues;
|
||||
use framebuffer::LayoutAttachmentDescription;
|
||||
use framebuffer::LayoutPassDescription;
|
||||
use framebuffer::LayoutPassDependencyDescription;
|
||||
use image::ImageViewAccess;
|
||||
|
||||
/// Description of an empty render pass.
|
||||
///
|
||||
@ -49,7 +44,7 @@ unsafe impl RenderPassDesc for EmptySinglePassRenderPassDesc {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn attachment(&self, num: usize) -> Option<LayoutAttachmentDescription> {
|
||||
fn attachment_desc(&self, num: usize) -> Option<LayoutAttachmentDescription> {
|
||||
None
|
||||
}
|
||||
|
||||
@ -59,7 +54,7 @@ unsafe impl RenderPassDesc for EmptySinglePassRenderPassDesc {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn subpass(&self, num: usize) -> Option<LayoutPassDescription> {
|
||||
fn subpass_desc(&self, num: usize) -> Option<LayoutPassDescription> {
|
||||
if num == 0 {
|
||||
Some(LayoutPassDescription {
|
||||
color_attachments: vec![],
|
||||
@ -79,7 +74,7 @@ unsafe impl RenderPassDesc for EmptySinglePassRenderPassDesc {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn dependency(&self, num: usize) -> Option<LayoutPassDependencyDescription> {
|
||||
fn dependency_desc(&self, num: usize) -> Option<LayoutPassDependencyDescription> {
|
||||
None
|
||||
}
|
||||
|
||||
@ -143,24 +138,6 @@ unsafe impl RenderPassDesc for EmptySinglePassRenderPassDesc {
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl RenderPassDescAttachmentsList<Vec<Arc<ImageViewAccess + Send + Sync>>> for EmptySinglePassRenderPassDesc {
|
||||
#[inline]
|
||||
fn check_attachments_list(&self, list: Vec<Arc<ImageViewAccess + Send + Sync>>) -> Result<Box<AttachmentsList + Send + Sync>, FramebufferCreationError> {
|
||||
if list.is_empty() {
|
||||
Ok(Box::new(()) as Box<_>)
|
||||
} else {
|
||||
panic!() // FIXME: return error instead
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl RenderPassDescAttachmentsList<()> for EmptySinglePassRenderPassDesc {
|
||||
#[inline]
|
||||
fn check_attachments_list(&self, list: ()) -> Result<Box<AttachmentsList + Send + Sync>, FramebufferCreationError> {
|
||||
Ok(Box::new(()) as Box<_>)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl RenderPassDescClearValues<Vec<ClearValue>> for EmptySinglePassRenderPassDesc {
|
||||
#[inline]
|
||||
fn convert_clear_values(&self, values: Vec<ClearValue>) -> Box<Iterator<Item = ClearValue>> {
|
||||
|
@ -7,6 +7,7 @@
|
||||
// notice may not be copied, modified, or distributed except
|
||||
// according to those terms.
|
||||
|
||||
use std::cmp;
|
||||
use std::error;
|
||||
use std::fmt;
|
||||
use std::marker::PhantomData;
|
||||
@ -20,14 +21,16 @@ use device::DeviceOwned;
|
||||
use format::ClearValue;
|
||||
use framebuffer::AttachmentsList;
|
||||
use framebuffer::FramebufferAbstract;
|
||||
use framebuffer::IncompatibleRenderPassAttachmentError;
|
||||
use framebuffer::LayoutAttachmentDescription;
|
||||
use framebuffer::LayoutPassDependencyDescription;
|
||||
use framebuffer::LayoutPassDescription;
|
||||
use framebuffer::RenderPassAbstract;
|
||||
use framebuffer::RenderPassDescClearValues;
|
||||
use framebuffer::RenderPassDescAttachmentsList;
|
||||
use framebuffer::RenderPassDesc;
|
||||
use framebuffer::RenderPassSys;
|
||||
use framebuffer::ensure_image_view_compatible;
|
||||
use image::ImageView;
|
||||
use image::ImageViewAccess;
|
||||
|
||||
use Error;
|
||||
@ -37,88 +40,47 @@ use VulkanPointers;
|
||||
use check_errors;
|
||||
use vk;
|
||||
|
||||
/// Contains the list of images attached to a render pass.
|
||||
/// Contains a render pass and the image views that are attached to it.
|
||||
///
|
||||
/// Creating a framebuffer is done by passing the render pass object, the dimensions of the
|
||||
/// framebuffer, and the list of attachments to `Framebuffer::new()`.
|
||||
/// Creating a framebuffer is done by calling `Framebuffer::start`, which returns a
|
||||
/// `FramebufferBuilder` object. You can then add the framebuffer attachments one by one by
|
||||
/// calling `add(image)`. When you are done, call `build()`.
|
||||
///
|
||||
/// Just like all render pass objects implement the `RenderPassAbstract` trait, all framebuffer
|
||||
/// objects implement the `FramebufferAbstract` trait. This means that you can cast any
|
||||
/// `Arc<Framebuffer<..>>` into an `Arc<FramebufferAbstract + Send + Sync>` for easier storage.
|
||||
/// Both the `add` and the `build` functions perform various checks to make sure that the number
|
||||
/// of images is correct and that each image is compatible with the attachment definition in the
|
||||
/// render pass.
|
||||
///
|
||||
/// ## With a generic list of attachments
|
||||
///
|
||||
/// The list of attachments passed to `Framebuffer::new()` can be of various types, but one of the
|
||||
/// possibilities is to pass an object of type `Vec<Arc<ImageView + Send + Sync>>`.
|
||||
///
|
||||
/// > **Note**: If you access a render pass object through the `RenderPassAbstract` trait, passing
|
||||
/// > a `Vec<Arc<ImageView + Send + Sync>>` is the only possible method.
|
||||
///
|
||||
/// The framebuffer constructor will perform various checks to make sure that the number of images
|
||||
/// is correct and that each image can be used with this render pass.
|
||||
///
|
||||
/// ```ignore // FIXME: unignore
|
||||
/// ```
|
||||
/// # use std::sync::Arc;
|
||||
/// # use vulkano::framebuffer::RenderPassAbstract;
|
||||
/// use vulkano::framebuffer::Framebuffer;
|
||||
///
|
||||
/// # let render_pass: Arc<RenderPassAbstract + Send + Sync> = return;
|
||||
/// # let my_image: Arc<vulkano::image::ImageViewAccess> = return;
|
||||
/// // let render_pass: Arc<RenderPassAbstract + Send + Sync> = ...;
|
||||
/// let framebuffer = Framebuffer::new(render_pass.clone(), [1024, 768, 1],
|
||||
/// vec![my_image.clone() as Arc<_>]).unwrap();
|
||||
/// # let my_image: Arc<vulkano::image::AttachmentImage<vulkano::format::Format>> = return;
|
||||
/// // let render_pass: Arc<_> = ...;
|
||||
/// let framebuffer = Framebuffer::start(render_pass.clone())
|
||||
/// .add(my_image).unwrap()
|
||||
/// .build().unwrap();
|
||||
/// ```
|
||||
///
|
||||
/// ## With a specialized list of attachments
|
||||
/// Just like render pass objects implement the `RenderPassAbstract` trait, all framebuffer
|
||||
/// objects implement the `FramebufferAbstract` trait. This means that you can cast any
|
||||
/// `Arc<Framebuffer<..>>` into an `Arc<FramebufferAbstract + Send + Sync>` for easier storage.
|
||||
///
|
||||
/// The list of attachments can also be of any type `T`, as long as the render pass description
|
||||
/// implements the trait `RenderPassDescAttachmentsList<T>`.
|
||||
/// ## Framebuffer dimensions
|
||||
///
|
||||
/// For example if you pass a render pass object that implements
|
||||
/// `RenderPassDescAttachmentsList<Foo>`, then you can pass a `Foo` as the list of attachments.
|
||||
/// If you use `Framebuffer::start()` to create a framebuffer then vulkano will automatically
|
||||
/// make sure that all the attachments have the same dimensions, as this is the most common
|
||||
/// situation.
|
||||
///
|
||||
/// > **Note**: The reason why `Vec<Arc<ImageView + Send + Sync>>` always works (see previous section) is that
|
||||
/// > render pass descriptions are required to always implement
|
||||
/// > `RenderPassDescAttachmentsList<Vec<Arc<ImageViewAccess + Send + Sync>>>`.
|
||||
/// Alternatively you can also use `with_intersecting_dimensions`, in which case the dimensions of
|
||||
/// the framebuffer will be the intersection of the dimensions of all attachments, or
|
||||
/// `with_dimensions` if you want to specify exact dimensions. If you use `with_dimensions`, you
|
||||
/// are allowed to attach images that are larger than these dimensions.
|
||||
///
|
||||
/// When it comes to the `single_pass_renderpass!` and `ordered_passes_renderpass!` macros, you can
|
||||
/// build a list of attachments by calling `start_attachments()` on the render pass description,
|
||||
/// which will return an object that has a method whose name is the name of the first attachment
|
||||
/// and that can be used to specify it. This method will return another object that has a method
|
||||
/// whose name is the name of the second attachment, and so on. See the documentation of the macros
|
||||
/// for more details. TODO: put link here
|
||||
/// If the dimensions of the framebuffer don't match the dimensions of one of its attachment, then
|
||||
/// only the top-left hand corner of the image will be drawn to.
|
||||
///
|
||||
/// ```ignore // FIXME: unignore
|
||||
/// # #[macro_use] extern crate vulkano;
|
||||
/// # fn main() {
|
||||
/// # let device: std::sync::Arc<vulkano::device::Device> = return;
|
||||
/// use std::sync::Arc;
|
||||
/// use vulkano::format::Format;
|
||||
/// use vulkano::framebuffer::Framebuffer;
|
||||
///
|
||||
/// let render_pass = single_pass_renderpass!(device.clone(),
|
||||
/// attachments: {
|
||||
/// // `foo` is a custom name we give to the first and only attachment.
|
||||
/// foo: {
|
||||
/// load: Clear,
|
||||
/// store: Store,
|
||||
/// format: Format::R8G8B8A8Unorm,
|
||||
/// samples: 1,
|
||||
/// }
|
||||
/// },
|
||||
/// pass: {
|
||||
/// color: [foo], // Repeat the attachment name here.
|
||||
/// depth_stencil: {}
|
||||
/// }
|
||||
/// ).unwrap();
|
||||
///
|
||||
/// # let my_image: Arc<vulkano::image::ImageViewAccess> = return;
|
||||
/// let framebuffer = {
|
||||
/// let atch = render_pass.desc().start_attachments().foo(my_image.clone() as Arc<_>);
|
||||
/// Framebuffer::new(render_pass, [1024, 768, 1], atch).unwrap()
|
||||
/// };
|
||||
/// # }
|
||||
/// ```
|
||||
#[derive(Debug)]
|
||||
pub struct Framebuffer<Rp, A> {
|
||||
device: Arc<Device>,
|
||||
@ -128,24 +90,199 @@ pub struct Framebuffer<Rp, A> {
|
||||
resources: A,
|
||||
}
|
||||
|
||||
impl<Rp> Framebuffer<Rp, Box<AttachmentsList + Send + Sync>> {
|
||||
/// Builds a new framebuffer.
|
||||
///
|
||||
/// The `attachments` parameter depends on which render pass implementation is used.
|
||||
// TODO: allow ImageView
|
||||
pub fn new<Ia>(render_pass: Rp, dimensions: [u32; 3], attachments: Ia)
|
||||
-> Result<Arc<Framebuffer<Rp, Box<AttachmentsList + Send + Sync>>>, FramebufferCreationError>
|
||||
where Rp: RenderPassAbstract + RenderPassDescAttachmentsList<Ia>
|
||||
{
|
||||
let device = render_pass.device().clone();
|
||||
impl<Rp> Framebuffer<Rp, ()> {
|
||||
/// Starts building a framebuffer.
|
||||
pub fn start(render_pass: Rp) -> FramebufferBuilder<Rp, ()> {
|
||||
FramebufferBuilder {
|
||||
render_pass: render_pass,
|
||||
raw_ids: SmallVec::new(),
|
||||
dimensions: FramebufferBuilderDimensions::AutoIdentical(None),
|
||||
attachments: (),
|
||||
}
|
||||
}
|
||||
|
||||
// This function call is supposed to check whether the attachments are valid.
|
||||
// For more safety, we do some additional `debug_assert`s below.
|
||||
let attachments = try!(render_pass.check_attachments_list(attachments));
|
||||
/// Starts building a framebuffer. The dimensions of the framebuffer will automatically be
|
||||
/// the intersection of the dimensions of all the attachments.
|
||||
pub fn with_intersecting_dimensions(render_pass: Rp) -> FramebufferBuilder<Rp, ()> {
|
||||
FramebufferBuilder {
|
||||
render_pass: render_pass,
|
||||
raw_ids: SmallVec::new(),
|
||||
dimensions: FramebufferBuilderDimensions::AutoSmaller(None),
|
||||
attachments: (),
|
||||
}
|
||||
}
|
||||
|
||||
/// Starts building a framebuffer.
|
||||
pub fn with_dimensions(render_pass: Rp, dimensions: [u32; 3]) -> FramebufferBuilder<Rp, ()> {
|
||||
FramebufferBuilder {
|
||||
render_pass: render_pass,
|
||||
raw_ids: SmallVec::new(),
|
||||
dimensions: FramebufferBuilderDimensions::Specific(dimensions),
|
||||
attachments: (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Prototype of a framebuffer.
|
||||
pub struct FramebufferBuilder<Rp, A> {
|
||||
render_pass: Rp,
|
||||
raw_ids: SmallVec<[vk::ImageView; 8]>,
|
||||
dimensions: FramebufferBuilderDimensions,
|
||||
attachments: A,
|
||||
}
|
||||
|
||||
impl<Rp, A> fmt::Debug for FramebufferBuilder<Rp, A> where Rp: fmt::Debug, A: fmt::Debug {
|
||||
#[inline]
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
fmt.debug_struct("FramebufferBuilder")
|
||||
.field("render_pass", &self.render_pass)
|
||||
.field("dimensions", &self.dimensions)
|
||||
.field("attachments", &self.attachments)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum FramebufferBuilderDimensions {
|
||||
AutoIdentical(Option<[u32; 3]>),
|
||||
AutoSmaller(Option<[u32; 3]>),
|
||||
Specific([u32; 3]),
|
||||
}
|
||||
|
||||
impl<Rp, A> FramebufferBuilder<Rp, A>
|
||||
where Rp: RenderPassAbstract,
|
||||
A: AttachmentsList,
|
||||
{
|
||||
/// Appends an attachment to the prototype of the framebuffer.
|
||||
///
|
||||
/// Attachments must be added in the same order as the one defined in the render pass.
|
||||
pub fn add<T>(self, attachment: T)
|
||||
-> Result<FramebufferBuilder<Rp, (A, T::Access)>, FramebufferCreationError>
|
||||
where T: ImageView
|
||||
{
|
||||
let access = attachment.access();
|
||||
|
||||
if self.raw_ids.len() >= self.render_pass.num_attachments() {
|
||||
return Err(FramebufferCreationError::AttachmentsCountMismatch {
|
||||
expected: self.render_pass.num_attachments(),
|
||||
obtained: self.raw_ids.len() + 1,
|
||||
});
|
||||
}
|
||||
|
||||
match ensure_image_view_compatible(&self.render_pass, self.raw_ids.len(), &access) {
|
||||
Ok(()) => (),
|
||||
Err(err) => return Err(FramebufferCreationError::IncompatibleAttachment(err))
|
||||
};
|
||||
|
||||
let img_dims = access.dimensions();
|
||||
debug_assert_eq!(img_dims.depth(), 1);
|
||||
|
||||
let dimensions = match self.dimensions {
|
||||
FramebufferBuilderDimensions::AutoIdentical(None) => {
|
||||
let dims = [img_dims.width(), img_dims.height(), img_dims.array_layers()];
|
||||
FramebufferBuilderDimensions::AutoIdentical(Some(dims))
|
||||
},
|
||||
FramebufferBuilderDimensions::AutoIdentical(Some(current)) => {
|
||||
if img_dims.width() != current[0] || img_dims.height() != current[1] ||
|
||||
img_dims.array_layers() != current[2]
|
||||
{
|
||||
return Err(FramebufferCreationError::AttachmentDimensionsIncompatible {
|
||||
expected: current,
|
||||
obtained: [img_dims.width(), img_dims.height(), img_dims.array_layers()]
|
||||
});
|
||||
}
|
||||
|
||||
FramebufferBuilderDimensions::AutoIdentical(Some(current))
|
||||
}
|
||||
FramebufferBuilderDimensions::AutoSmaller(None) => {
|
||||
let dims = [img_dims.width(), img_dims.height(), img_dims.array_layers()];
|
||||
FramebufferBuilderDimensions::AutoSmaller(Some(dims))
|
||||
},
|
||||
FramebufferBuilderDimensions::AutoSmaller(Some(current)) => {
|
||||
let new_dims = [
|
||||
cmp::min(current[0], img_dims.width()),
|
||||
cmp::min(current[1], img_dims.height()),
|
||||
cmp::min(current[2], img_dims.array_layers())
|
||||
];
|
||||
|
||||
FramebufferBuilderDimensions::AutoSmaller(Some(new_dims))
|
||||
},
|
||||
FramebufferBuilderDimensions::Specific(current) => {
|
||||
if img_dims.width() < current[0] || img_dims.height() < current[1] ||
|
||||
img_dims.array_layers() < current[2]
|
||||
{
|
||||
return Err(FramebufferCreationError::AttachmentDimensionsIncompatible {
|
||||
expected: current,
|
||||
obtained: [img_dims.width(), img_dims.height(), img_dims.array_layers()]
|
||||
});
|
||||
}
|
||||
|
||||
FramebufferBuilderDimensions::Specific([
|
||||
img_dims.width(),
|
||||
img_dims.height(),
|
||||
img_dims.array_layers()
|
||||
])
|
||||
}
|
||||
};
|
||||
|
||||
let mut raw_ids = self.raw_ids;
|
||||
raw_ids.push(access.inner().internal_object());
|
||||
|
||||
Ok(FramebufferBuilder {
|
||||
render_pass: self.render_pass,
|
||||
raw_ids: raw_ids,
|
||||
dimensions: dimensions,
|
||||
attachments: (self.attachments, access),
|
||||
})
|
||||
}
|
||||
|
||||
/// Turns this builder into a `FramebufferBuilder<Rp, Box<AttachmentsList>>`.
|
||||
///
|
||||
/// This allows you to store the builder in situations where you don't know in advance the
|
||||
/// number of attachments.
|
||||
///
|
||||
/// > **Note**: This is a very rare corner case and you shouldn't have to use this function
|
||||
/// > in most situations.
|
||||
#[inline]
|
||||
pub fn boxed(self) -> FramebufferBuilder<Rp, Box<AttachmentsList>>
|
||||
where A: 'static
|
||||
{
|
||||
FramebufferBuilder {
|
||||
render_pass: self.render_pass,
|
||||
raw_ids: self.raw_ids,
|
||||
dimensions: self.dimensions,
|
||||
attachments: Box::new(self.attachments) as Box<_>,
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds the framebuffer.
|
||||
pub fn build(self) -> Result<Framebuffer<Rp, A>, FramebufferCreationError> {
|
||||
let device = self.render_pass.device().clone();
|
||||
|
||||
// Check the number of attachments.
|
||||
if self.raw_ids.len() != self.render_pass.num_attachments() {
|
||||
return Err(FramebufferCreationError::AttachmentsCountMismatch {
|
||||
expected: self.render_pass.num_attachments(),
|
||||
obtained: self.raw_ids.len(),
|
||||
});
|
||||
}
|
||||
|
||||
// Compute the dimensions.
|
||||
let dimensions = match self.dimensions {
|
||||
FramebufferBuilderDimensions::Specific(dims) |
|
||||
FramebufferBuilderDimensions::AutoIdentical(Some(dims)) |
|
||||
FramebufferBuilderDimensions::AutoSmaller(Some(dims)) => {
|
||||
dims
|
||||
},
|
||||
FramebufferBuilderDimensions::AutoIdentical(None) |
|
||||
FramebufferBuilderDimensions::AutoSmaller(None) => {
|
||||
return Err(FramebufferCreationError::CantDetermineDimensions);
|
||||
},
|
||||
};
|
||||
|
||||
// Checking the dimensions against the limits.
|
||||
{
|
||||
let limits = render_pass.device().physical_device().limits();
|
||||
let limits = device.physical_device().limits();
|
||||
let limits = [limits.max_framebuffer_width(), limits.max_framebuffer_height(),
|
||||
limits.max_framebuffer_layers()];
|
||||
if dimensions[0] > limits[0] || dimensions[1] > limits[1] ||
|
||||
@ -155,28 +292,16 @@ impl<Rp> Framebuffer<Rp, Box<AttachmentsList + Send + Sync>> {
|
||||
}
|
||||
}
|
||||
|
||||
// Checking the dimensions against the attachments.
|
||||
if let Some(dims_constraints) = attachments.intersection_dimensions() {
|
||||
if dims_constraints[0] < dimensions[0] || dims_constraints[1] < dimensions[1] ||
|
||||
dims_constraints[2] < dimensions[2]
|
||||
{
|
||||
return Err(FramebufferCreationError::AttachmentTooSmall);
|
||||
}
|
||||
}
|
||||
|
||||
let ids: SmallVec<[vk::ImageView; 8]> =
|
||||
attachments.raw_image_view_handles().into_iter().map(|v| v.internal_object()).collect();
|
||||
|
||||
let framebuffer = unsafe {
|
||||
let vk = render_pass.device().pointers();
|
||||
let vk = device.pointers();
|
||||
|
||||
let infos = vk::FramebufferCreateInfo {
|
||||
sType: vk::STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
|
||||
pNext: ptr::null(),
|
||||
flags: 0, // reserved
|
||||
renderPass: render_pass.inner().internal_object(),
|
||||
attachmentCount: ids.len() as u32,
|
||||
pAttachments: ids.as_ptr(),
|
||||
renderPass: self.render_pass.inner().internal_object(),
|
||||
attachmentCount: self.raw_ids.len() as u32,
|
||||
pAttachments: self.raw_ids.as_ptr(),
|
||||
width: dimensions[0],
|
||||
height: dimensions[1],
|
||||
layers: dimensions[2],
|
||||
@ -188,13 +313,13 @@ impl<Rp> Framebuffer<Rp, Box<AttachmentsList + Send + Sync>> {
|
||||
output
|
||||
};
|
||||
|
||||
Ok(Arc::new(Framebuffer {
|
||||
Ok(Framebuffer {
|
||||
device: device,
|
||||
render_pass: render_pass,
|
||||
render_pass: self.render_pass,
|
||||
framebuffer: framebuffer,
|
||||
dimensions: dimensions,
|
||||
resources: attachments,
|
||||
}))
|
||||
resources: self.attachments,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -263,8 +388,8 @@ unsafe impl<Rp, A> RenderPassDesc for Framebuffer<Rp, A> where Rp: RenderPassDes
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn attachment(&self, num: usize) -> Option<LayoutAttachmentDescription> {
|
||||
self.render_pass.attachment(num)
|
||||
fn attachment_desc(&self, num: usize) -> Option<LayoutAttachmentDescription> {
|
||||
self.render_pass.attachment_desc(num)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@ -273,8 +398,8 @@ unsafe impl<Rp, A> RenderPassDesc for Framebuffer<Rp, A> where Rp: RenderPassDes
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn subpass(&self, num: usize) -> Option<LayoutPassDescription> {
|
||||
self.render_pass.subpass(num)
|
||||
fn subpass_desc(&self, num: usize) -> Option<LayoutPassDescription> {
|
||||
self.render_pass.subpass_desc(num)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@ -283,17 +408,8 @@ unsafe impl<Rp, A> RenderPassDesc for Framebuffer<Rp, A> where Rp: RenderPassDes
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn dependency(&self, num: usize) -> Option<LayoutPassDependencyDescription> {
|
||||
self.render_pass.dependency(num)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<At, Rp, A> RenderPassDescAttachmentsList<At> for Framebuffer<Rp, A>
|
||||
where Rp: RenderPassDescAttachmentsList<At>
|
||||
{
|
||||
#[inline]
|
||||
fn check_attachments_list(&self, atch: At) -> Result<Box<AttachmentsList + Send + Sync>, FramebufferCreationError> {
|
||||
self.render_pass.check_attachments_list(atch)
|
||||
fn dependency_desc(&self, num: usize) -> Option<LayoutPassDependencyDescription> {
|
||||
self.render_pass.dependency_desc(num)
|
||||
}
|
||||
}
|
||||
|
||||
@ -344,17 +460,30 @@ unsafe impl<'a> VulkanObject for FramebufferSys<'a> {
|
||||
}
|
||||
|
||||
/// Error that can happen when creating a framebuffer object.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
#[repr(u32)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum FramebufferCreationError {
|
||||
/// Out of memory.
|
||||
OomError(OomError),
|
||||
/// The requested dimensions exceed the device's limits.
|
||||
DimensionsTooLarge,
|
||||
/// One of the attachments has a component swizzle that is different from identity.
|
||||
AttachmentNotIdentitySwizzled,
|
||||
/// One of the attachments is too small compared to the requested framebuffer dimensions.
|
||||
AttachmentTooSmall,
|
||||
/// The attachment has a size that isn't compatible with the requested framebuffer dimensions.
|
||||
AttachmentDimensionsIncompatible {
|
||||
/// Expected dimensions.
|
||||
expected: [u32; 3],
|
||||
/// Attachment dimensions.
|
||||
obtained: [u32; 3],
|
||||
},
|
||||
/// The number of attachments doesn't match the number expected by the render pass.
|
||||
AttachmentsCountMismatch {
|
||||
/// Expected number of attachments.
|
||||
expected: usize,
|
||||
/// Number of attachments that were given.
|
||||
obtained: usize,
|
||||
},
|
||||
/// One of the images cannot be used as the requested attachment.
|
||||
IncompatibleAttachment(IncompatibleRenderPassAttachmentError),
|
||||
/// The framebuffer has no attachment and no dimension was specified.
|
||||
CantDetermineDimensions,
|
||||
}
|
||||
|
||||
impl From<OomError> for FramebufferCreationError {
|
||||
@ -371,12 +500,17 @@ impl error::Error for FramebufferCreationError {
|
||||
FramebufferCreationError::OomError(_) => "no memory available",
|
||||
FramebufferCreationError::DimensionsTooLarge => "the dimensions of the framebuffer \
|
||||
are too large",
|
||||
FramebufferCreationError::AttachmentNotIdentitySwizzled => {
|
||||
"one of the attachments has a component swizzle that is different from identity"
|
||||
FramebufferCreationError::AttachmentDimensionsIncompatible { .. } => {
|
||||
"the attachment has a size that isn't compatible with the framebuffer dimensions"
|
||||
},
|
||||
FramebufferCreationError::AttachmentTooSmall => {
|
||||
"one of the attachments is too small compared to the requested framebuffer \
|
||||
dimensions"
|
||||
FramebufferCreationError::AttachmentsCountMismatch { .. } => {
|
||||
"the number of attachments doesn't match the number expected by the render pass"
|
||||
},
|
||||
FramebufferCreationError::IncompatibleAttachment(_) => {
|
||||
"one of the images cannot be used as the requested attachment"
|
||||
},
|
||||
FramebufferCreationError::CantDetermineDimensions => {
|
||||
"the framebuffer has no attachment and no dimension was specified"
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -385,6 +519,7 @@ impl error::Error for FramebufferCreationError {
|
||||
fn cause(&self) -> Option<&error::Error> {
|
||||
match *self {
|
||||
FramebufferCreationError::OomError(ref err) => Some(err),
|
||||
FramebufferCreationError::IncompatibleAttachment(ref err) => Some(err),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@ -404,70 +539,314 @@ impl From<Error> for FramebufferCreationError {
|
||||
}
|
||||
}
|
||||
|
||||
/* FIXME: restore
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use format::R8G8B8A8Unorm;
|
||||
use std::sync::Arc;
|
||||
use format::Format;
|
||||
use framebuffer::EmptySinglePassRenderPassDesc;
|
||||
use framebuffer::Framebuffer;
|
||||
use framebuffer::FramebufferCreationError;
|
||||
use framebuffer::RenderPassDesc;
|
||||
use image::attachment::AttachmentImage;
|
||||
|
||||
#[test]
|
||||
fn simple_create() {
|
||||
let (device, _) = gfx_dev_and_queue!();
|
||||
|
||||
let render_pass = single_pass_renderpass! {
|
||||
let render_pass = Arc::new(single_pass_renderpass!(device.clone(),
|
||||
attachments: {
|
||||
color: {
|
||||
load: Clear,
|
||||
store: DontCare,
|
||||
format: R8G8B8A8Unorm,
|
||||
format: Format::R8G8B8A8Unorm,
|
||||
samples: 1,
|
||||
}
|
||||
},
|
||||
pass: {
|
||||
color: [color],
|
||||
depth_stencil: {}
|
||||
}
|
||||
}.unwrap();
|
||||
).unwrap());
|
||||
|
||||
let image = AttachmentImage::new(&device, [1024, 768], R8G8B8A8Unorm).unwrap();
|
||||
|
||||
let _ = Framebuffer::new(render_pass, [1024, 768, 1], example::AList {
|
||||
color: image.clone()
|
||||
}).unwrap();
|
||||
let image = AttachmentImage::new(device.clone(), [1024, 768],
|
||||
Format::R8G8B8A8Unorm).unwrap();
|
||||
let _ = Framebuffer::start(render_pass).add(image.clone()).unwrap().build().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn framebuffer_too_large() {
|
||||
fn check_device_limits() {
|
||||
let (device, _) = gfx_dev_and_queue!();
|
||||
|
||||
let render_pass = example::CustomRenderPass::new(&device, &example::Formats {
|
||||
color: (R8G8B8A8Unorm, 1)
|
||||
}).unwrap();
|
||||
|
||||
let image = AttachmentImage::new(&device, [1024, 768], R8G8B8A8Unorm).unwrap();
|
||||
|
||||
let alist = example::AList { color: image.clone() };
|
||||
match Framebuffer::new(render_pass, [0xffffffff, 0xffffffff, 0xffffffff], alist) {
|
||||
let rp = EmptySinglePassRenderPassDesc.build_render_pass(device).unwrap();
|
||||
let res = Framebuffer::with_dimensions(rp, [0xffffffff, 0xffffffff, 0xffffffff]).build();
|
||||
match res {
|
||||
Err(FramebufferCreationError::DimensionsTooLarge) => (),
|
||||
_ => panic!()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn attachment_too_small() {
|
||||
fn attachment_format_mismatch() {
|
||||
let (device, _) = gfx_dev_and_queue!();
|
||||
|
||||
let render_pass = example::CustomRenderPass::new(&device, &example::Formats {
|
||||
color: (R8G8B8A8Unorm, 1)
|
||||
}).unwrap();
|
||||
let render_pass = Arc::new(single_pass_renderpass!(device.clone(),
|
||||
attachments: {
|
||||
color: {
|
||||
load: Clear,
|
||||
store: DontCare,
|
||||
format: Format::R8G8B8A8Unorm,
|
||||
samples: 1,
|
||||
}
|
||||
},
|
||||
pass: {
|
||||
color: [color],
|
||||
depth_stencil: {}
|
||||
}
|
||||
).unwrap());
|
||||
|
||||
let image = AttachmentImage::new(&device, [512, 512], R8G8B8A8Unorm).unwrap();
|
||||
let image = AttachmentImage::new(device.clone(), [1024, 768],
|
||||
Format::R8Unorm).unwrap();
|
||||
|
||||
let alist = example::AList { color: image.clone() };
|
||||
match Framebuffer::new(render_pass, [600, 600, 1], alist) {
|
||||
Err(FramebufferCreationError::AttachmentTooSmall) => (),
|
||||
match Framebuffer::start(render_pass).add(image.clone()) {
|
||||
Err(FramebufferCreationError::IncompatibleAttachment(_)) => (),
|
||||
_ => panic!()
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
// TODO: check samples mismatch
|
||||
|
||||
#[test]
|
||||
fn attachment_dims_larger_than_specified_valid() {
|
||||
let (device, _) = gfx_dev_and_queue!();
|
||||
|
||||
let render_pass = Arc::new(single_pass_renderpass!(device.clone(),
|
||||
attachments: {
|
||||
color: {
|
||||
load: Clear,
|
||||
store: DontCare,
|
||||
format: Format::R8G8B8A8Unorm,
|
||||
samples: 1,
|
||||
}
|
||||
},
|
||||
pass: {
|
||||
color: [color],
|
||||
depth_stencil: {}
|
||||
}
|
||||
).unwrap());
|
||||
|
||||
let img = AttachmentImage::new(device.clone(), [600, 600], Format::R8G8B8A8Unorm).unwrap();
|
||||
|
||||
let _ = Framebuffer::with_dimensions(render_pass, [512, 512, 1])
|
||||
.add(img).unwrap()
|
||||
.build().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn attachment_dims_smaller_than_specified() {
|
||||
let (device, _) = gfx_dev_and_queue!();
|
||||
|
||||
let render_pass = Arc::new(single_pass_renderpass!(device.clone(),
|
||||
attachments: {
|
||||
color: {
|
||||
load: Clear,
|
||||
store: DontCare,
|
||||
format: Format::R8G8B8A8Unorm,
|
||||
samples: 1,
|
||||
}
|
||||
},
|
||||
pass: {
|
||||
color: [color],
|
||||
depth_stencil: {}
|
||||
}
|
||||
).unwrap());
|
||||
|
||||
let img = AttachmentImage::new(device.clone(), [512, 700], Format::R8G8B8A8Unorm).unwrap();
|
||||
|
||||
match Framebuffer::with_dimensions(render_pass, [600, 600, 1]).add(img) {
|
||||
Err(FramebufferCreationError::AttachmentDimensionsIncompatible { expected, obtained }) => {
|
||||
assert_eq!(expected, [600, 600, 1]);
|
||||
assert_eq!(obtained, [512, 700, 1]);
|
||||
},
|
||||
_ => panic!()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multi_attachments_dims_not_identical() {
|
||||
let (device, _) = gfx_dev_and_queue!();
|
||||
|
||||
let render_pass = Arc::new(single_pass_renderpass!(device.clone(),
|
||||
attachments: {
|
||||
a: {
|
||||
load: Clear,
|
||||
store: DontCare,
|
||||
format: Format::R8G8B8A8Unorm,
|
||||
samples: 1,
|
||||
},
|
||||
b: {
|
||||
load: Clear,
|
||||
store: DontCare,
|
||||
format: Format::R8G8B8A8Unorm,
|
||||
samples: 1,
|
||||
}
|
||||
},
|
||||
pass: {
|
||||
color: [a, b],
|
||||
depth_stencil: {}
|
||||
}
|
||||
).unwrap());
|
||||
|
||||
let a = AttachmentImage::new(device.clone(), [512, 512], Format::R8G8B8A8Unorm).unwrap();
|
||||
let b = AttachmentImage::new(device.clone(), [512, 513], Format::R8G8B8A8Unorm).unwrap();
|
||||
|
||||
match Framebuffer::start(render_pass).add(a).unwrap().add(b) {
|
||||
Err(FramebufferCreationError::AttachmentDimensionsIncompatible { expected, obtained }) => {
|
||||
assert_eq!(expected, [512, 512, 1]);
|
||||
assert_eq!(obtained, [512, 513, 1]);
|
||||
},
|
||||
_ => panic!()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multi_attachments_auto_smaller() {
|
||||
let (device, _) = gfx_dev_and_queue!();
|
||||
|
||||
let render_pass = Arc::new(single_pass_renderpass!(device.clone(),
|
||||
attachments: {
|
||||
a: {
|
||||
load: Clear,
|
||||
store: DontCare,
|
||||
format: Format::R8G8B8A8Unorm,
|
||||
samples: 1,
|
||||
},
|
||||
b: {
|
||||
load: Clear,
|
||||
store: DontCare,
|
||||
format: Format::R8G8B8A8Unorm,
|
||||
samples: 1,
|
||||
}
|
||||
},
|
||||
pass: {
|
||||
color: [a, b],
|
||||
depth_stencil: {}
|
||||
}
|
||||
).unwrap());
|
||||
|
||||
let a = AttachmentImage::new(device.clone(), [256, 512], Format::R8G8B8A8Unorm).unwrap();
|
||||
let b = AttachmentImage::new(device.clone(), [512, 128], Format::R8G8B8A8Unorm).unwrap();
|
||||
|
||||
let fb = Framebuffer::with_intersecting_dimensions(render_pass)
|
||||
.add(a).unwrap()
|
||||
.add(b).unwrap()
|
||||
.build().unwrap();
|
||||
|
||||
match (fb.width(), fb.height(), fb.layers()) {
|
||||
(256, 128, 1) => (),
|
||||
_ => panic!()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn not_enough_attachments() {
|
||||
let (device, _) = gfx_dev_and_queue!();
|
||||
|
||||
let render_pass = Arc::new(single_pass_renderpass!(device.clone(),
|
||||
attachments: {
|
||||
a: {
|
||||
load: Clear,
|
||||
store: DontCare,
|
||||
format: Format::R8G8B8A8Unorm,
|
||||
samples: 1,
|
||||
},
|
||||
b: {
|
||||
load: Clear,
|
||||
store: DontCare,
|
||||
format: Format::R8G8B8A8Unorm,
|
||||
samples: 1,
|
||||
}
|
||||
},
|
||||
pass: {
|
||||
color: [a, b],
|
||||
depth_stencil: {}
|
||||
}
|
||||
).unwrap());
|
||||
|
||||
let img = AttachmentImage::new(device.clone(), [256, 512], Format::R8G8B8A8Unorm).unwrap();
|
||||
|
||||
let res = Framebuffer::with_intersecting_dimensions(render_pass)
|
||||
.add(img).unwrap()
|
||||
.build();
|
||||
|
||||
match res {
|
||||
Err(FramebufferCreationError::AttachmentsCountMismatch { expected: 2,
|
||||
obtained: 1 }) => (),
|
||||
_ => panic!()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn too_many_attachments() {
|
||||
let (device, _) = gfx_dev_and_queue!();
|
||||
|
||||
let render_pass = Arc::new(single_pass_renderpass!(device.clone(),
|
||||
attachments: {
|
||||
a: {
|
||||
load: Clear,
|
||||
store: DontCare,
|
||||
format: Format::R8G8B8A8Unorm,
|
||||
samples: 1,
|
||||
}
|
||||
},
|
||||
pass: {
|
||||
color: [a],
|
||||
depth_stencil: {}
|
||||
}
|
||||
).unwrap());
|
||||
|
||||
let a = AttachmentImage::new(device.clone(), [256, 512], Format::R8G8B8A8Unorm).unwrap();
|
||||
let b = AttachmentImage::new(device.clone(), [256, 512], Format::R8G8B8A8Unorm).unwrap();
|
||||
|
||||
let res = Framebuffer::with_intersecting_dimensions(render_pass)
|
||||
.add(a).unwrap()
|
||||
.add(b);
|
||||
|
||||
match res {
|
||||
Err(FramebufferCreationError::AttachmentsCountMismatch { expected: 1,
|
||||
obtained: 2 }) => (),
|
||||
_ => panic!()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_working() {
|
||||
let (device, _) = gfx_dev_and_queue!();
|
||||
|
||||
let rp = EmptySinglePassRenderPassDesc.build_render_pass(device).unwrap();
|
||||
let _ = Framebuffer::with_dimensions(rp, [512, 512, 1]).build().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cant_determine_dimensions_auto() {
|
||||
let (device, _) = gfx_dev_and_queue!();
|
||||
|
||||
let rp = EmptySinglePassRenderPassDesc.build_render_pass(device).unwrap();
|
||||
let res = Framebuffer::start(rp).build();
|
||||
match res {
|
||||
Err(FramebufferCreationError::CantDetermineDimensions) => (),
|
||||
_ => panic!()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cant_determine_dimensions_intersect() {
|
||||
let (device, _) = gfx_dev_and_queue!();
|
||||
|
||||
let rp = EmptySinglePassRenderPassDesc.build_render_pass(device).unwrap();
|
||||
let res = Framebuffer::with_intersecting_dimensions(rp).build();
|
||||
match res {
|
||||
Err(FramebufferCreationError::CantDetermineDimensions) => (),
|
||||
_ => panic!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -65,19 +65,14 @@ macro_rules! ordered_passes_renderpass {
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use std::sync::Arc;
|
||||
use $crate::format::ClearValue;
|
||||
use $crate::format::Format;
|
||||
use $crate::framebuffer::AttachmentsList;
|
||||
use $crate::framebuffer::FramebufferCreationError;
|
||||
use $crate::framebuffer::RenderPassDesc;
|
||||
use $crate::framebuffer::RenderPassDescAttachmentsList;
|
||||
use $crate::framebuffer::RenderPassDescClearValues;
|
||||
use $crate::framebuffer::LayoutAttachmentDescription;
|
||||
use $crate::framebuffer::LayoutPassDescription;
|
||||
use $crate::framebuffer::LayoutPassDependencyDescription;
|
||||
use $crate::image::Layout;
|
||||
use $crate::image::ImageViewAccess;
|
||||
use $crate::image::ImageLayout;
|
||||
use $crate::sync::AccessFlagBits;
|
||||
use $crate::sync::PipelineStages;
|
||||
|
||||
@ -87,37 +82,6 @@ macro_rules! ordered_passes_renderpass {
|
||||
)*
|
||||
}
|
||||
|
||||
impl CustomRenderPassDesc {
|
||||
#[inline]
|
||||
pub fn start_attachments(&self) -> atch::AttachmentsStart {
|
||||
atch::AttachmentsStart
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn start_clear_values(&self) -> cv::ClearValuesStart {
|
||||
cv::ClearValuesStart
|
||||
}
|
||||
}
|
||||
|
||||
pub mod atch {
|
||||
use $crate::framebuffer::AttachmentsList;
|
||||
use $crate::framebuffer::FramebufferCreationError;
|
||||
use $crate::framebuffer::RenderPassDescAttachmentsList;
|
||||
use $crate::image::traits::ImageViewAccess;
|
||||
use super::CustomRenderPassDesc;
|
||||
pub struct AttachmentsStart;
|
||||
ordered_passes_renderpass!{[] __impl_attachments__ [] [] [$($atch_name),*] [A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z]}
|
||||
}
|
||||
|
||||
pub mod cv {
|
||||
use std::iter;
|
||||
use $crate::format::ClearValue;
|
||||
use $crate::framebuffer::RenderPassDescClearValues;
|
||||
use super::CustomRenderPassDesc;
|
||||
pub struct ClearValuesStart;
|
||||
ordered_passes_renderpass!{[] __impl_clear_values__ [] [] [$($atch_name: $load),*] [A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z]}
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
unsafe impl RenderPassDesc for CustomRenderPassDesc {
|
||||
#[inline]
|
||||
@ -126,7 +90,7 @@ macro_rules! ordered_passes_renderpass {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn attachment(&self, id: usize) -> Option<LayoutAttachmentDescription> {
|
||||
fn attachment_desc(&self, id: usize) -> Option<LayoutAttachmentDescription> {
|
||||
attachment(self, id)
|
||||
}
|
||||
|
||||
@ -136,7 +100,7 @@ macro_rules! ordered_passes_renderpass {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn subpass(&self, id: usize) -> Option<LayoutPassDescription> {
|
||||
fn subpass_desc(&self, id: usize) -> Option<LayoutPassDescription> {
|
||||
subpass(id)
|
||||
}
|
||||
|
||||
@ -146,7 +110,7 @@ macro_rules! ordered_passes_renderpass {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn dependency(&self, id: usize) -> Option<LayoutPassDependencyDescription> {
|
||||
fn dependency_desc(&self, id: usize) -> Option<LayoutPassDependencyDescription> {
|
||||
dependency(id)
|
||||
}
|
||||
}
|
||||
@ -158,14 +122,6 @@ macro_rules! ordered_passes_renderpass {
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl RenderPassDescAttachmentsList<Vec<Arc<ImageViewAccess + Send + Sync>>> for CustomRenderPassDesc {
|
||||
fn check_attachments_list(&self, list: Vec<Arc<ImageViewAccess + Send + Sync>>) -> Result<Box<AttachmentsList + Send + Sync>, FramebufferCreationError> {
|
||||
// FIXME: correct safety checks
|
||||
assert_eq!(list.len(), self.num_attachments());
|
||||
Ok(Box::new(list) as Box<_>)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn num_attachments() -> usize {
|
||||
#![allow(unused_assignments)]
|
||||
@ -233,19 +189,19 @@ macro_rules! ordered_passes_renderpass {
|
||||
if id == cur_pass_num {
|
||||
let mut depth = None;
|
||||
$(
|
||||
depth = Some(($depth_atch, Layout::DepthStencilAttachmentOptimal));
|
||||
depth = Some(($depth_atch, ImageLayout::DepthStencilAttachmentOptimal));
|
||||
)*
|
||||
|
||||
return Some(LayoutPassDescription {
|
||||
color_attachments: vec![
|
||||
$(
|
||||
($color_atch, Layout::ColorAttachmentOptimal)
|
||||
($color_atch, ImageLayout::ColorAttachmentOptimal)
|
||||
),*
|
||||
],
|
||||
depth_stencil: depth,
|
||||
input_attachments: vec![
|
||||
$(
|
||||
($input_atch, Layout::ShaderReadOnlyOptimal)
|
||||
($input_atch, ImageLayout::ShaderReadOnlyOptimal)
|
||||
),*
|
||||
],
|
||||
resolve_attachments: vec![],
|
||||
@ -291,7 +247,7 @@ macro_rules! ordered_passes_renderpass {
|
||||
/// Returns the initial and final layout of an attachment, given its num.
|
||||
///
|
||||
/// The value always correspond to the first and last usages of an attachment.
|
||||
fn attachment_layouts(num: usize) -> (Layout, Layout) {
|
||||
fn attachment_layouts(num: usize) -> (ImageLayout, ImageLayout) {
|
||||
#![allow(unused_assignments)]
|
||||
#![allow(unused_mut)]
|
||||
#![allow(unused_variables)]
|
||||
@ -309,40 +265,40 @@ macro_rules! ordered_passes_renderpass {
|
||||
$(
|
||||
if $depth_atch == num {
|
||||
if initial_layout.is_none() {
|
||||
initial_layout = Some(Layout::DepthStencilAttachmentOptimal);
|
||||
initial_layout = Some(ImageLayout::DepthStencilAttachmentOptimal);
|
||||
}
|
||||
final_layout = Some(Layout::DepthStencilAttachmentOptimal);
|
||||
final_layout = Some(ImageLayout::DepthStencilAttachmentOptimal);
|
||||
}
|
||||
)*
|
||||
|
||||
$(
|
||||
if $color_atch == num {
|
||||
if initial_layout.is_none() {
|
||||
initial_layout = Some(Layout::ColorAttachmentOptimal);
|
||||
initial_layout = Some(ImageLayout::ColorAttachmentOptimal);
|
||||
}
|
||||
final_layout = Some(Layout::ColorAttachmentOptimal);
|
||||
final_layout = Some(ImageLayout::ColorAttachmentOptimal);
|
||||
}
|
||||
)*
|
||||
|
||||
$(
|
||||
if $input_atch == num {
|
||||
if initial_layout.is_none() {
|
||||
initial_layout = Some(Layout::ShaderReadOnlyOptimal);
|
||||
initial_layout = Some(ImageLayout::ShaderReadOnlyOptimal);
|
||||
}
|
||||
final_layout = Some(Layout::ShaderReadOnlyOptimal);
|
||||
final_layout = Some(ImageLayout::ShaderReadOnlyOptimal);
|
||||
}
|
||||
)*
|
||||
})*
|
||||
|
||||
$(if $atch_name == num {
|
||||
// If the clear OP is Clear or DontCare, default to the Undefined layout.
|
||||
if initial_layout == Some(Layout::DepthStencilAttachmentOptimal) ||
|
||||
initial_layout == Some(Layout::ColorAttachmentOptimal)
|
||||
if initial_layout == Some(ImageLayout::DepthStencilAttachmentOptimal) ||
|
||||
initial_layout == Some(ImageLayout::ColorAttachmentOptimal)
|
||||
{
|
||||
if $crate::framebuffer::LoadOp::$load == $crate::framebuffer::LoadOp::Clear ||
|
||||
$crate::framebuffer::LoadOp::$load == $crate::framebuffer::LoadOp::DontCare
|
||||
{
|
||||
initial_layout = Some(Layout::Undefined);
|
||||
initial_layout = Some(ImageLayout::Undefined);
|
||||
}
|
||||
}
|
||||
|
||||
@ -360,178 +316,4 @@ macro_rules! ordered_passes_renderpass {
|
||||
)*
|
||||
}.build_render_pass($device)
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
([] __impl_attachments__ [] [] [] [$($params:ident),*]) => {
|
||||
unsafe impl RenderPassDescAttachmentsList<AttachmentsStart> for CustomRenderPassDesc {
|
||||
type List = ();
|
||||
|
||||
fn check_attachments_list(&self, attachments: AttachmentsStart) -> Result<(), FramebufferCreationError> {
|
||||
Ok(()) // FIXME:
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
([] __impl_attachments__ [] [] [$next:ident $(, $rest:ident)*] [$first_param:ident, $($rest_params:ident),*]) => {
|
||||
pub struct $next<$first_param> {
|
||||
current: $first_param,
|
||||
}
|
||||
|
||||
impl AttachmentsStart {
|
||||
pub fn $next<$first_param>(self, next: $first_param) -> $next<$first_param> {
|
||||
$next {
|
||||
current: next,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<$first_param> $next<$first_param> {
|
||||
fn check_attachments_list(self) -> Result<($first_param,), FramebufferCreationError> {
|
||||
Ok((self.current,)) // FIXME: check attachment
|
||||
}
|
||||
}
|
||||
|
||||
ordered_passes_renderpass!{[] __impl_attachments__ [$next] [$first_param] [$($rest),*] [$($rest_params),*]}
|
||||
};
|
||||
|
||||
([] __impl_attachments__ [$prev:ident] [$($prev_params:ident),*] [] [$($params:ident),*]) => {
|
||||
unsafe impl<$($prev_params),*> RenderPassDescAttachmentsList<$prev<$($prev_params),*>> for CustomRenderPassDesc
|
||||
where $($prev_params: ImageViewAccess + Send + Sync + 'static),*
|
||||
{
|
||||
//type List = ($($prev_params,)*);
|
||||
|
||||
fn check_attachments_list(&self, attachments: $prev<$($prev_params,)*>) -> Result<Box<AttachmentsList + Send + Sync>, FramebufferCreationError> {
|
||||
Ok(Box::new(try!(attachments.check_attachments_list())))
|
||||
|
||||
// FIXME:
|
||||
/*$({
|
||||
if !l.$atch_name.identity_swizzle() {
|
||||
return Err(FramebufferCreationError::AttachmentNotIdentitySwizzled);
|
||||
}
|
||||
})**/
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
([] __impl_attachments__ [$prev:ident] [$($prev_params:ident),*] [$next:ident $(, $rest:ident)*] [$first_param:ident, $($rest_params:ident),*]) => {
|
||||
pub struct $next<$($prev_params,)* $first_param> {
|
||||
prev: $prev<$($prev_params),*>,
|
||||
current: $first_param,
|
||||
}
|
||||
|
||||
impl<$($prev_params),*> $prev<$($prev_params),*> {
|
||||
pub fn $next<$first_param>(self, next: $first_param) -> $next<$($prev_params,)* $first_param> {
|
||||
$next {
|
||||
prev: self,
|
||||
current: next,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<$($prev_params,)* $first_param> $next<$($prev_params,)* $first_param> {
|
||||
fn check_attachments_list(self) -> Result<($($prev_params,)* $first_param), FramebufferCreationError> {
|
||||
let ($($prev_params,)*) = try!(self.prev.check_attachments_list());
|
||||
// FIXME: check attachment
|
||||
Ok(($($prev_params,)* self.current))
|
||||
}
|
||||
}
|
||||
|
||||
ordered_passes_renderpass!{[] __impl_attachments__ [$next] [$($prev_params,)* $first_param] [$($rest),*] [$($rest_params),*]}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
([] __impl_clear_values__ [] [] [] [$($params:ident),*]) => {
|
||||
unsafe impl RenderPassDescClearValues<ClearValuesStart> for CustomRenderPassDesc {
|
||||
#[inline]
|
||||
fn convert_clear_values(&self, values: ClearValuesStart) -> Box<Iterator<Item = ClearValue>> {
|
||||
Box::new(iter::empty())
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
([] __impl_clear_values__ [] [] [$next:ident: Clear $(, $rest:ident: $rest_load:ident)*] [$first_param:ident, $($rest_params:ident),*]) => {
|
||||
pub struct $next<$first_param> {
|
||||
current: $first_param,
|
||||
}
|
||||
|
||||
impl ClearValuesStart {
|
||||
pub fn $next<$first_param>(self, next: $first_param) -> $next<$first_param> {
|
||||
$next {
|
||||
current: next,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<$first_param> $next<$first_param>
|
||||
where $first_param: Into<ClearValue>
|
||||
{
|
||||
#[inline]
|
||||
fn convert_clear_values(self) -> iter::Once<ClearValue> {
|
||||
// FIXME: check format
|
||||
iter::once(self.current.into())
|
||||
}
|
||||
}
|
||||
|
||||
ordered_passes_renderpass!{[] __impl_clear_values__ [$next] [$first_param] [$($rest: $rest_load),*] [$($rest_params),*]}
|
||||
};
|
||||
|
||||
([] __impl_clear_values__ [] [] [$next:ident: $other:ident $(, $rest:ident: $rest_load:ident)*] [$first_param:ident, $($rest_params:ident),*]) => {
|
||||
ordered_passes_renderpass!{[] __impl_clear_values__ [] [] [$($rest: $rest_load),*] [$first_param, $($rest_params),*]}
|
||||
};
|
||||
|
||||
([] __impl_clear_values__ [$prev:ident] [$($prev_params:ident),*] [] [$($params:ident),*]) => {
|
||||
unsafe impl<$($prev_params),*> RenderPassDescClearValues<$prev<$($prev_params),*>> for CustomRenderPassDesc
|
||||
where $($prev_params: Into<ClearValue>),*
|
||||
{
|
||||
#[inline]
|
||||
fn convert_clear_values(&self, values: $prev<$($prev_params,)*>) -> Box<Iterator<Item = ClearValue>> {
|
||||
Box::new(values.convert_clear_values())
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
([] __impl_clear_values__ [$prev:ident] [$($prev_params:ident),*] [$next:ident: Clear $(, $rest:ident: $rest_load:ident)*] [$first_param:ident, $($rest_params:ident),*]) => {
|
||||
pub struct $next<$($prev_params,)* $first_param> {
|
||||
prev: $prev<$($prev_params,)*>,
|
||||
current: $first_param,
|
||||
}
|
||||
|
||||
impl<$($prev_params,)*> $prev<$($prev_params,)*> {
|
||||
pub fn $next<$first_param>(self, next: $first_param) -> $next<$($prev_params,)* $first_param> {
|
||||
$next {
|
||||
prev: self,
|
||||
current: next,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<$($prev_params,)* $first_param> $next<$($prev_params,)* $first_param>
|
||||
where $first_param: Into<ClearValue>
|
||||
$(, $prev_params: Into<ClearValue>)*
|
||||
{
|
||||
#[inline]
|
||||
fn convert_clear_values(self) -> Box<Iterator<Item = ClearValue>> {
|
||||
// TODO: subopptimal iterator
|
||||
let prev = self.prev.convert_clear_values();
|
||||
// FIXME: check format
|
||||
Box::new(prev.chain(iter::once(self.current.into())))
|
||||
}
|
||||
}
|
||||
|
||||
ordered_passes_renderpass!{[] __impl_clear_values__ [$next] [$($prev_params,)* $first_param] [$($rest: $rest_load),*] [$($rest_params),*]}
|
||||
};
|
||||
|
||||
([] __impl_clear_values__ [$prev:ident] [$($prev_params:ident),*] [$next:ident: $other:ident $(, $rest:ident: $rest_load:ident)*] [$first_param:ident, $($rest_params:ident),*]) => {
|
||||
ordered_passes_renderpass!{[] __impl_clear_values__ [$prev] [$($prev_params,)*] [$($rest: $rest_load),*] [$first_param, $($rest_params),*]}
|
||||
};
|
||||
}
|
||||
|
@ -91,6 +91,8 @@
|
||||
//!
|
||||
|
||||
pub use self::attachments_list::AttachmentsList;
|
||||
pub use self::compat_atch::ensure_image_view_compatible;
|
||||
pub use self::compat_atch::IncompatibleRenderPassAttachmentError;
|
||||
pub use self::desc::LayoutAttachmentDescription;
|
||||
pub use self::desc::LayoutPassDescription;
|
||||
pub use self::desc::LayoutPassDependencyDescription;
|
||||
@ -102,6 +104,7 @@ pub use self::desc::StoreOp;
|
||||
pub use self::desc::LoadOp;
|
||||
pub use self::empty::EmptySinglePassRenderPassDesc;
|
||||
pub use self::framebuffer::Framebuffer;
|
||||
pub use self::framebuffer::FramebufferBuilder;
|
||||
pub use self::framebuffer::FramebufferCreationError;
|
||||
pub use self::framebuffer::FramebufferSys;
|
||||
pub use self::sys::RenderPass;
|
||||
@ -110,7 +113,6 @@ pub use self::sys::RenderPassSys;
|
||||
pub use self::traits::FramebufferAbstract;
|
||||
pub use self::traits::RenderPassDescClearValues;
|
||||
pub use self::traits::RenderPassCompatible;
|
||||
pub use self::traits::RenderPassDescAttachmentsList;
|
||||
pub use self::traits::RenderPassAbstract;
|
||||
pub use self::traits::RenderPassSubpassInterface;
|
||||
pub use self::traits::Subpass;
|
||||
@ -118,6 +120,7 @@ pub use self::traits::Subpass;
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
mod attachments_list;
|
||||
mod compat_atch;
|
||||
mod desc;
|
||||
mod empty;
|
||||
mod framebuffer;
|
||||
|
@ -19,15 +19,12 @@ use smallvec::SmallVec;
|
||||
use device::Device;
|
||||
use device::DeviceOwned;
|
||||
use format::ClearValue;
|
||||
use framebuffer::AttachmentsList;
|
||||
use framebuffer::EmptySinglePassRenderPassDesc;
|
||||
use framebuffer::FramebufferCreationError;
|
||||
use framebuffer::LayoutAttachmentDescription;
|
||||
use framebuffer::LayoutPassDependencyDescription;
|
||||
use framebuffer::LayoutPassDescription;
|
||||
use framebuffer::LoadOp;
|
||||
use framebuffer::RenderPassDescClearValues;
|
||||
use framebuffer::RenderPassDescAttachmentsList;
|
||||
use framebuffer::RenderPassDesc;
|
||||
use framebuffer::RenderPassAbstract;
|
||||
|
||||
@ -73,12 +70,12 @@ impl<D> RenderPass<D> where D: RenderPassDesc {
|
||||
// If the first use of an attachment in this render pass is as an input attachment, and
|
||||
// the attachment is not also used as a color or depth/stencil attachment in the same
|
||||
// subpass, then loadOp must not be VK_ATTACHMENT_LOAD_OP_CLEAR
|
||||
debug_assert!(description.attachments().enumerate().all(|(atch_num, attachment)| {
|
||||
debug_assert!(description.attachment_descs().enumerate().all(|(atch_num, attachment)| {
|
||||
if attachment.load != LoadOp::Clear {
|
||||
return true;
|
||||
}
|
||||
|
||||
for p in description.subpasses() {
|
||||
for p in description.subpass_descs() {
|
||||
if p.color_attachments.iter().find(|&&(a, _)| a == atch_num).is_some() { return true; }
|
||||
if let Some((a, _)) = p.depth_stencil { if a == atch_num { return true; } }
|
||||
if p.input_attachments.iter().find(|&&(a, _)| a == atch_num).is_some() { return false; }
|
||||
@ -87,7 +84,7 @@ impl<D> RenderPass<D> where D: RenderPassDesc {
|
||||
true
|
||||
}));
|
||||
|
||||
let attachments = description.attachments().map(|attachment| {
|
||||
let attachments = description.attachment_descs().map(|attachment| {
|
||||
debug_assert!(attachment.samples.is_power_of_two());
|
||||
|
||||
vk::AttachmentDescription {
|
||||
@ -109,7 +106,7 @@ impl<D> RenderPass<D> where D: RenderPassDesc {
|
||||
// This block allocates, for each pass, in order, all color attachment references, then all
|
||||
// input attachment references, then all resolve attachment references, then the depth
|
||||
// stencil attachment reference.
|
||||
let attachment_references = description.subpasses().flat_map(|pass| {
|
||||
let attachment_references = description.subpass_descs().flat_map(|pass| {
|
||||
// Performing some validation with debug asserts.
|
||||
debug_assert!(pass.resolve_attachments.is_empty() ||
|
||||
pass.resolve_attachments.len() == pass.color_attachments.len());
|
||||
@ -171,7 +168,7 @@ impl<D> RenderPass<D> where D: RenderPassDesc {
|
||||
// Same as `attachment_references` but only for the preserve attachments.
|
||||
// This is separate because attachment references are u32s and not `vkAttachmentReference`
|
||||
// structs.
|
||||
let preserve_attachments_references = description.subpasses().flat_map(|pass| {
|
||||
let preserve_attachments_references = description.subpass_descs().flat_map(|pass| {
|
||||
pass.preserve_attachments.into_iter().map(|offset| offset as u32)
|
||||
}).collect::<SmallVec<[_; 16]>>();
|
||||
|
||||
@ -184,7 +181,7 @@ impl<D> RenderPass<D> where D: RenderPassDesc {
|
||||
let mut preserve_ref_index = 0usize;
|
||||
let mut out: SmallVec<[_; 16]> = SmallVec::new();
|
||||
|
||||
for pass in description.subpasses() {
|
||||
for pass in description.subpass_descs() {
|
||||
if pass.color_attachments.len() as u32 >
|
||||
device.physical_device().limits().max_color_attachments()
|
||||
{
|
||||
@ -235,7 +232,7 @@ impl<D> RenderPass<D> where D: RenderPassDesc {
|
||||
out
|
||||
};
|
||||
|
||||
let dependencies = description.dependencies().map(|dependency| {
|
||||
let dependencies = description.dependency_descs().map(|dependency| {
|
||||
debug_assert!(dependency.source_subpass < passes.len());
|
||||
debug_assert!(dependency.destination_subpass < passes.len());
|
||||
|
||||
@ -336,8 +333,8 @@ unsafe impl<D> RenderPassDesc for RenderPass<D> where D: RenderPassDesc {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn attachment(&self, num: usize) -> Option<LayoutAttachmentDescription> {
|
||||
self.desc.attachment(num)
|
||||
fn attachment_desc(&self, num: usize) -> Option<LayoutAttachmentDescription> {
|
||||
self.desc.attachment_desc(num)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@ -346,8 +343,8 @@ unsafe impl<D> RenderPassDesc for RenderPass<D> where D: RenderPassDesc {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn subpass(&self, num: usize) -> Option<LayoutPassDescription> {
|
||||
self.desc.subpass(num)
|
||||
fn subpass_desc(&self, num: usize) -> Option<LayoutPassDescription> {
|
||||
self.desc.subpass_desc(num)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@ -356,17 +353,8 @@ unsafe impl<D> RenderPassDesc for RenderPass<D> where D: RenderPassDesc {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn dependency(&self, num: usize) -> Option<LayoutPassDependencyDescription> {
|
||||
self.desc.dependency(num)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<A, D> RenderPassDescAttachmentsList<A> for RenderPass<D>
|
||||
where D: RenderPassDescAttachmentsList<A>
|
||||
{
|
||||
#[inline]
|
||||
fn check_attachments_list(&self, atch: A) -> Result<Box<AttachmentsList + Send + Sync>, FramebufferCreationError> {
|
||||
self.desc.check_attachments_list(atch)
|
||||
fn dependency_desc(&self, num: usize) -> Option<LayoutPassDependencyDescription> {
|
||||
self.desc.dependency_desc(num)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,8 +9,6 @@
|
||||
|
||||
use device::DeviceOwned;
|
||||
use format::ClearValue;
|
||||
use framebuffer::AttachmentsList;
|
||||
use framebuffer::FramebufferCreationError;
|
||||
use framebuffer::FramebufferSys;
|
||||
use framebuffer::RenderPassDesc;
|
||||
use framebuffer::RenderPassSys;
|
||||
@ -108,40 +106,6 @@ unsafe impl<T> RenderPassAbstract for T where T: SafeDeref, T::Target: RenderPas
|
||||
}
|
||||
}
|
||||
|
||||
/// Extension trait for `RenderPassDesc`. Defines which types are allowed as an attachments list.
|
||||
///
|
||||
/// When the user creates a framebuffer, they need to pass a render pass object and a list of
|
||||
/// attachments. In order for it to work, the render pass object must implement
|
||||
/// `RenderPassDescAttachmentsList<A>` where `A` is the type of the list of attachments.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This trait is unsafe because it's the job of the implementation to check whether the
|
||||
/// attachments list is correct. What needs to be checked:
|
||||
///
|
||||
/// - That the attachments' format and samples count match the render pass layout.
|
||||
/// - That the attachments have been created with the proper usage flags.
|
||||
/// - That the attachments only expose one mipmap.
|
||||
/// - That the attachments use identity components swizzling.
|
||||
/// TODO: more stuff with aliasing
|
||||
///
|
||||
pub unsafe trait RenderPassDescAttachmentsList<A> {
|
||||
/// Decodes a `A` into a list of attachments.
|
||||
///
|
||||
/// Checks that the attachments match the render pass, and returns a list. Returns an error if
|
||||
/// one of the attachments is wrong.
|
||||
fn check_attachments_list(&self, A) -> Result<Box<AttachmentsList + Send + Sync>, FramebufferCreationError>;
|
||||
}
|
||||
|
||||
unsafe impl<A, T> RenderPassDescAttachmentsList<A> for T
|
||||
where T: SafeDeref, T::Target: RenderPassDescAttachmentsList<A>
|
||||
{
|
||||
#[inline]
|
||||
fn check_attachments_list(&self, atch: A) -> Result<Box<AttachmentsList + Send + Sync>, FramebufferCreationError> {
|
||||
(**self).check_attachments_list(atch)
|
||||
}
|
||||
}
|
||||
|
||||
/// Extension trait for `RenderPassDesc`. Defines which types are allowed as a list of clear values.
|
||||
///
|
||||
/// When the user enters a render pass, they need to pass a list of clear values to apply to
|
||||
@ -203,7 +167,7 @@ unsafe impl<A, B: ?Sized> RenderPassSubpassInterface<B> for A
|
||||
where A: RenderPassDesc, B: ShaderInterfaceDef
|
||||
{
|
||||
fn is_compatible_with(&self, subpass: u32, other: &B) -> bool {
|
||||
let pass_descr = match RenderPassDesc::subpasses(self).skip(subpass as usize).next() {
|
||||
let pass_descr = match RenderPassDesc::subpass_descs(self).skip(subpass as usize).next() {
|
||||
Some(s) => s,
|
||||
None => return false,
|
||||
};
|
||||
@ -215,7 +179,7 @@ unsafe impl<A, B: ?Sized> RenderPassSubpassInterface<B> for A
|
||||
None => return false,
|
||||
};
|
||||
|
||||
let attachment_desc = (&self).attachments().skip(attachment_id).next().unwrap();
|
||||
let attachment_desc = (&self).attachment_descs().skip(attachment_id).next().unwrap();
|
||||
|
||||
// FIXME: compare formats depending on the number of components and data type
|
||||
/*if attachment_desc.format != element.format {
|
||||
|
@ -23,10 +23,10 @@ use image::Dimensions;
|
||||
use image::ImageDimensions;
|
||||
use image::ViewType;
|
||||
use image::sys::ImageCreationError;
|
||||
use image::sys::Layout;
|
||||
use image::ImageLayout;
|
||||
use image::ImageUsage;
|
||||
use image::sys::UnsafeImage;
|
||||
use image::sys::UnsafeImageView;
|
||||
use image::sys::Usage;
|
||||
use image::traits::ImageAccess;
|
||||
use image::traits::ImageClearValue;
|
||||
use image::traits::ImageContent;
|
||||
@ -36,7 +36,8 @@ use image::traits::ImageView;
|
||||
use memory::pool::AllocLayout;
|
||||
use memory::pool::MemoryPool;
|
||||
use memory::pool::MemoryPoolAlloc;
|
||||
use memory::pool::StdMemoryPool;
|
||||
use memory::pool::StdMemoryPoolAlloc;
|
||||
use sync::AccessError;
|
||||
use sync::Sharing;
|
||||
|
||||
/// ImageAccess whose purpose is to be used as a framebuffer attachment.
|
||||
@ -69,7 +70,7 @@ use sync::Sharing;
|
||||
///
|
||||
// TODO: forbid reading transient images outside render passes?
|
||||
#[derive(Debug)]
|
||||
pub struct AttachmentImage<F, A = Arc<StdMemoryPool>> where A: MemoryPool {
|
||||
pub struct AttachmentImage<F = Format, A = StdMemoryPoolAlloc> {
|
||||
// Inner implementation.
|
||||
image: UnsafeImage,
|
||||
|
||||
@ -77,14 +78,14 @@ pub struct AttachmentImage<F, A = Arc<StdMemoryPool>> where A: MemoryPool {
|
||||
view: UnsafeImageView,
|
||||
|
||||
// Memory used to back the image.
|
||||
memory: A::Alloc,
|
||||
memory: A,
|
||||
|
||||
// Format.
|
||||
format: F,
|
||||
|
||||
// Layout to use when the image is used as a framebuffer attachment.
|
||||
// Must be either "depth-stencil optimal" or "color optimal".
|
||||
attachment_layout: Layout,
|
||||
attachment_layout: ImageLayout,
|
||||
|
||||
// Number of times this image is locked on the GPU side.
|
||||
gpu_lock: AtomicUsize,
|
||||
@ -96,16 +97,16 @@ impl<F> AttachmentImage<F> {
|
||||
/// Returns an error if the dimensions are too large or if the backend doesn't support this
|
||||
/// format as a framebuffer attachment.
|
||||
#[inline]
|
||||
pub fn new(device: &Arc<Device>, dimensions: [u32; 2], format: F)
|
||||
pub fn new(device: Arc<Device>, dimensions: [u32; 2], format: F)
|
||||
-> Result<Arc<AttachmentImage<F>>, ImageCreationError>
|
||||
where F: FormatDesc
|
||||
{
|
||||
AttachmentImage::new_impl(device, dimensions, format, Usage::none())
|
||||
AttachmentImage::new_impl(device, dimensions, format, ImageUsage::none())
|
||||
}
|
||||
|
||||
/// Same as `new`, but lets you specify additional usages.
|
||||
#[inline]
|
||||
pub fn with_usage(device: &Arc<Device>, dimensions: [u32; 2], format: F, usage: Usage)
|
||||
pub fn with_usage(device: Arc<Device>, dimensions: [u32; 2], format: F, usage: ImageUsage)
|
||||
-> Result<Arc<AttachmentImage<F>>, ImageCreationError>
|
||||
where F: FormatDesc
|
||||
{
|
||||
@ -117,19 +118,19 @@ impl<F> AttachmentImage<F> {
|
||||
/// A transient image is special because its content is undefined outside of a render pass.
|
||||
/// This means that the implementation has the possibility to not allocate any memory for it.
|
||||
#[inline]
|
||||
pub fn transient(device: &Arc<Device>, dimensions: [u32; 2], format: F)
|
||||
pub fn transient(device: Arc<Device>, dimensions: [u32; 2], format: F)
|
||||
-> Result<Arc<AttachmentImage<F>>, ImageCreationError>
|
||||
where F: FormatDesc
|
||||
{
|
||||
let base_usage = Usage {
|
||||
let base_usage = ImageUsage {
|
||||
transient_attachment: true,
|
||||
.. Usage::none()
|
||||
.. ImageUsage::none()
|
||||
};
|
||||
|
||||
AttachmentImage::new_impl(device, dimensions, format, base_usage)
|
||||
}
|
||||
|
||||
fn new_impl(device: &Arc<Device>, dimensions: [u32; 2], format: F, base_usage: Usage)
|
||||
fn new_impl(device: Arc<Device>, dimensions: [u32; 2], format: F, base_usage: ImageUsage)
|
||||
-> Result<Arc<AttachmentImage<F>>, ImageCreationError>
|
||||
where F: FormatDesc
|
||||
{
|
||||
@ -143,14 +144,14 @@ impl<F> AttachmentImage<F> {
|
||||
_ => false
|
||||
};
|
||||
|
||||
let usage = Usage {
|
||||
let usage = ImageUsage {
|
||||
color_attachment: !is_depth,
|
||||
depth_stencil_attachment: is_depth,
|
||||
.. base_usage
|
||||
};
|
||||
|
||||
let (image, mem_reqs) = unsafe {
|
||||
try!(UnsafeImage::new(device, &usage, format.format(),
|
||||
try!(UnsafeImage::new(device.clone(), usage, format.format(),
|
||||
ImageDimensions::Dim2d { width: dimensions[0], height: dimensions[1], array_layers: 1, cubemap_compatible: false },
|
||||
1, 1, Sharing::Exclusive::<Empty<u32>>, false, false))
|
||||
};
|
||||
@ -164,7 +165,7 @@ impl<F> AttachmentImage<F> {
|
||||
device_local.chain(any).next().unwrap()
|
||||
};
|
||||
|
||||
let mem = try!(MemoryPool::alloc(&Device::standard_pool(device), mem_ty,
|
||||
let mem = try!(MemoryPool::alloc(&Device::standard_pool(&device), mem_ty,
|
||||
mem_reqs.size, mem_reqs.alignment, AllocLayout::Optimal));
|
||||
debug_assert!((mem.offset() % mem_reqs.alignment) == 0);
|
||||
unsafe { try!(image.bind_memory(mem.memory(), mem.offset())); }
|
||||
@ -178,14 +179,14 @@ impl<F> AttachmentImage<F> {
|
||||
view: view,
|
||||
memory: mem,
|
||||
format: format,
|
||||
attachment_layout: if is_depth { Layout::DepthStencilAttachmentOptimal }
|
||||
else { Layout::ColorAttachmentOptimal },
|
||||
attachment_layout: if is_depth { ImageLayout::DepthStencilAttachmentOptimal }
|
||||
else { ImageLayout::ColorAttachmentOptimal },
|
||||
gpu_lock: AtomicUsize::new(0),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, A> AttachmentImage<F, A> where A: MemoryPool {
|
||||
impl<F, A> AttachmentImage<F, A> {
|
||||
/// Returns the dimensions of the image.
|
||||
#[inline]
|
||||
pub fn dimensions(&self) -> [u32; 2] {
|
||||
@ -195,13 +196,13 @@ impl<F, A> AttachmentImage<F, A> where A: MemoryPool {
|
||||
}
|
||||
|
||||
/// GPU access to an attachment image.
|
||||
pub struct AttachmentImageAccess<F, A> where A: MemoryPool {
|
||||
pub struct AttachmentImageAccess<F, A> {
|
||||
img: Arc<AttachmentImage<F, A>>,
|
||||
// True if `try_gpu_lock` was already called on it.
|
||||
already_locked: AtomicBool,
|
||||
}
|
||||
|
||||
impl<F, A> Clone for AttachmentImageAccess<F, A> where A: MemoryPool {
|
||||
impl<F, A> Clone for AttachmentImageAccess<F, A> {
|
||||
#[inline]
|
||||
fn clone(&self) -> AttachmentImageAccess<F, A> {
|
||||
AttachmentImageAccess {
|
||||
@ -212,8 +213,7 @@ impl<F, A> Clone for AttachmentImageAccess<F, A> where A: MemoryPool {
|
||||
}
|
||||
|
||||
unsafe impl<F, A> ImageAccess for AttachmentImageAccess<F, A>
|
||||
where F: 'static + Send + Sync,
|
||||
A: MemoryPool
|
||||
where F: 'static + Send + Sync
|
||||
{
|
||||
#[inline]
|
||||
fn inner(&self) -> &UnsafeImage {
|
||||
@ -221,12 +221,12 @@ unsafe impl<F, A> ImageAccess for AttachmentImageAccess<F, A>
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn initial_layout_requirement(&self) -> Layout {
|
||||
fn initial_layout_requirement(&self) -> ImageLayout {
|
||||
self.img.attachment_layout
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn final_layout_requirement(&self) -> Layout {
|
||||
fn final_layout_requirement(&self) -> ImageLayout {
|
||||
self.img.attachment_layout
|
||||
}
|
||||
|
||||
@ -236,25 +236,30 @@ unsafe impl<F, A> ImageAccess for AttachmentImageAccess<F, A>
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_gpu_lock(&self, _: bool, _: &Queue) -> bool {
|
||||
if self.already_locked.swap(true, Ordering::SeqCst) == true {
|
||||
fn try_gpu_lock(&self, _: bool, _: &Queue) -> Result<(), AccessError> {
|
||||
// FIXME: uncomment when it's working
|
||||
// the problem is in the submit sync layer which locks framebuffer attachments and
|
||||
// keeps them locked even after destruction
|
||||
Ok(())
|
||||
/*if self.already_locked.swap(true, Ordering::SeqCst) == true {
|
||||
return false;
|
||||
}
|
||||
|
||||
self.img.gpu_lock.compare_and_swap(0, 1, Ordering::SeqCst) == 0
|
||||
self.img.gpu_lock.compare_and_swap(0, 1, Ordering::SeqCst) == 0*/
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn increase_gpu_lock(&self) {
|
||||
debug_assert!(self.already_locked.load(Ordering::SeqCst));
|
||||
// FIXME: uncomment when it's working
|
||||
// the problem is in the submit sync layer which locks framebuffer attachments and
|
||||
// keeps them locked even after destruction
|
||||
/*debug_assert!(self.already_locked.load(Ordering::SeqCst));
|
||||
let val = self.img.gpu_lock.fetch_add(1, Ordering::SeqCst);
|
||||
debug_assert!(val >= 1);
|
||||
debug_assert!(val >= 1);*/
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, A> Drop for AttachmentImageAccess<F, A>
|
||||
where A: MemoryPool
|
||||
{
|
||||
impl<F, A> Drop for AttachmentImageAccess<F, A> {
|
||||
fn drop(&mut self) {
|
||||
if self.already_locked.load(Ordering::SeqCst) {
|
||||
let prev_val = self.img.gpu_lock.fetch_sub(1, Ordering::SeqCst);
|
||||
@ -264,8 +269,7 @@ impl<F, A> Drop for AttachmentImageAccess<F, A>
|
||||
}
|
||||
|
||||
unsafe impl<F, A> ImageClearValue<F::ClearValue> for AttachmentImageAccess<F, A>
|
||||
where F: FormatDesc + 'static + Send + Sync,
|
||||
A: MemoryPool
|
||||
where F: FormatDesc + 'static + Send + Sync
|
||||
{
|
||||
#[inline]
|
||||
fn decode(&self, value: F::ClearValue) -> Option<ClearValue> {
|
||||
@ -274,8 +278,7 @@ unsafe impl<F, A> ImageClearValue<F::ClearValue> for AttachmentImageAccess<F, A>
|
||||
}
|
||||
|
||||
unsafe impl<P, F, A> ImageContent<P> for AttachmentImageAccess<F, A>
|
||||
where F: 'static + Send + Sync,
|
||||
A: MemoryPool
|
||||
where F: 'static + Send + Sync
|
||||
{
|
||||
#[inline]
|
||||
fn matches_format(&self) -> bool {
|
||||
@ -284,7 +287,7 @@ unsafe impl<P, F, A> ImageContent<P> for AttachmentImageAccess<F, A>
|
||||
}
|
||||
|
||||
unsafe impl<F, A> Image for Arc<AttachmentImage<F, A>>
|
||||
where F: 'static + Send + Sync, A: MemoryPool
|
||||
where F: 'static + Send + Sync
|
||||
{
|
||||
type Access = AttachmentImageAccess<F, A>;
|
||||
|
||||
@ -313,7 +316,7 @@ unsafe impl<F, A> Image for Arc<AttachmentImage<F, A>>
|
||||
}
|
||||
|
||||
unsafe impl<F, A> ImageView for Arc<AttachmentImage<F, A>>
|
||||
where F: 'static + Send + Sync, A: MemoryPool
|
||||
where F: 'static + Send + Sync
|
||||
{
|
||||
type Access = AttachmentImageAccess<F, A>;
|
||||
|
||||
@ -327,7 +330,7 @@ unsafe impl<F, A> ImageView for Arc<AttachmentImage<F, A>>
|
||||
}
|
||||
|
||||
unsafe impl<F, A> ImageViewAccess for AttachmentImageAccess<F, A>
|
||||
where F: 'static + Send + Sync, A: MemoryPool
|
||||
where F: 'static + Send + Sync
|
||||
{
|
||||
#[inline]
|
||||
fn parent(&self) -> &ImageAccess {
|
||||
@ -346,23 +349,23 @@ unsafe impl<F, A> ImageViewAccess for AttachmentImageAccess<F, A>
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn descriptor_set_storage_image_layout(&self) -> Layout {
|
||||
Layout::ShaderReadOnlyOptimal
|
||||
fn descriptor_set_storage_image_layout(&self) -> ImageLayout {
|
||||
ImageLayout::ShaderReadOnlyOptimal
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn descriptor_set_combined_image_sampler_layout(&self) -> Layout {
|
||||
Layout::ShaderReadOnlyOptimal
|
||||
fn descriptor_set_combined_image_sampler_layout(&self) -> ImageLayout {
|
||||
ImageLayout::ShaderReadOnlyOptimal
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn descriptor_set_sampled_image_layout(&self) -> Layout {
|
||||
Layout::ShaderReadOnlyOptimal
|
||||
fn descriptor_set_sampled_image_layout(&self) -> ImageLayout {
|
||||
ImageLayout::ShaderReadOnlyOptimal
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn descriptor_set_input_attachment_layout(&self) -> Layout {
|
||||
Layout::ShaderReadOnlyOptimal
|
||||
fn descriptor_set_input_attachment_layout(&self) -> ImageLayout {
|
||||
ImageLayout::ShaderReadOnlyOptimal
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@ -379,18 +382,18 @@ mod tests {
|
||||
#[test]
|
||||
fn create_regular() {
|
||||
let (device, _) = gfx_dev_and_queue!();
|
||||
let _img = AttachmentImage::new(&device, [32, 32], Format::R8G8B8A8Unorm).unwrap();
|
||||
let _img = AttachmentImage::new(device, [32, 32], Format::R8G8B8A8Unorm).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create_transient() {
|
||||
let (device, _) = gfx_dev_and_queue!();
|
||||
let _img = AttachmentImage::transient(&device, [32, 32], Format::R8G8B8A8Unorm).unwrap();
|
||||
let _img = AttachmentImage::transient(device, [32, 32], Format::R8G8B8A8Unorm).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn d16_unorm_always_supported() {
|
||||
let (device, _) = gfx_dev_and_queue!();
|
||||
let _img = AttachmentImage::new(&device, [32, 32], Format::D16Unorm).unwrap();
|
||||
let _img = AttachmentImage::new(device, [32, 32], Format::D16Unorm).unwrap();
|
||||
}
|
||||
}
|
||||
|
@ -18,10 +18,10 @@ use image::Dimensions;
|
||||
use image::ImageDimensions;
|
||||
use image::MipmapsCount;
|
||||
use image::sys::ImageCreationError;
|
||||
use image::sys::Layout;
|
||||
use image::ImageLayout;
|
||||
use image::ImageUsage;
|
||||
use image::sys::UnsafeImage;
|
||||
use image::sys::UnsafeImageView;
|
||||
use image::sys::Usage;
|
||||
use image::traits::ImageAccess;
|
||||
use image::traits::ImageContent;
|
||||
use image::traits::ImageViewAccess;
|
||||
@ -32,6 +32,7 @@ use memory::pool::AllocLayout;
|
||||
use memory::pool::MemoryPool;
|
||||
use memory::pool::MemoryPoolAlloc;
|
||||
use memory::pool::StdMemoryPool;
|
||||
use sync::AccessError;
|
||||
use sync::Sharing;
|
||||
|
||||
/// Image whose purpose is to be used for read-only purposes. You can write to the image once,
|
||||
@ -50,7 +51,7 @@ impl<F> ImmutableImage<F> {
|
||||
/// Builds a new immutable image.
|
||||
// TODO: one mipmap is probably not a great default
|
||||
#[inline]
|
||||
pub fn new<'a, I>(device: &Arc<Device>, dimensions: Dimensions, format: F, queue_families: I)
|
||||
pub fn new<'a, I>(device: Arc<Device>, dimensions: Dimensions, format: F, queue_families: I)
|
||||
-> Result<Arc<ImmutableImage<F>>, ImageCreationError>
|
||||
where F: FormatDesc, I: IntoIterator<Item = QueueFamily<'a>>
|
||||
{
|
||||
@ -58,16 +59,16 @@ impl<F> ImmutableImage<F> {
|
||||
}
|
||||
|
||||
/// Builds a new immutable image with the given number of mipmaps.
|
||||
pub fn with_mipmaps<'a, I, M>(device: &Arc<Device>, dimensions: Dimensions, format: F,
|
||||
pub fn with_mipmaps<'a, I, M>(device: Arc<Device>, dimensions: Dimensions, format: F,
|
||||
mipmaps: M, queue_families: I)
|
||||
-> Result<Arc<ImmutableImage<F>>, ImageCreationError>
|
||||
where F: FormatDesc, I: IntoIterator<Item = QueueFamily<'a>>, M: Into<MipmapsCount>
|
||||
{
|
||||
let usage = Usage {
|
||||
let usage = ImageUsage {
|
||||
transfer_source: true, // for blits
|
||||
transfer_dest: true,
|
||||
sampled: true,
|
||||
.. Usage::none()
|
||||
.. ImageUsage::none()
|
||||
};
|
||||
|
||||
let queue_families = queue_families.into_iter().map(|f| f.id())
|
||||
@ -80,7 +81,7 @@ impl<F> ImmutableImage<F> {
|
||||
Sharing::Exclusive
|
||||
};
|
||||
|
||||
try!(UnsafeImage::new(device, &usage, format.format(), dimensions.to_image_dimensions(),
|
||||
try!(UnsafeImage::new(device.clone(), usage, format.format(), dimensions.to_image_dimensions(),
|
||||
1, mipmaps, sharing, false, false))
|
||||
};
|
||||
|
||||
@ -93,7 +94,7 @@ impl<F> ImmutableImage<F> {
|
||||
device_local.chain(any).next().unwrap()
|
||||
};
|
||||
|
||||
let mem = try!(MemoryPool::alloc(&Device::standard_pool(device), mem_ty,
|
||||
let mem = try!(MemoryPool::alloc(&Device::standard_pool(&device), mem_ty,
|
||||
mem_reqs.size, mem_reqs.alignment, AllocLayout::Optimal));
|
||||
debug_assert!((mem.offset() % mem_reqs.alignment) == 0);
|
||||
unsafe { try!(image.bind_memory(mem.memory(), mem.offset())); }
|
||||
@ -173,13 +174,13 @@ unsafe impl<F, A> ImageAccess for ImmutableImage<F, A> where F: 'static + Send +
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn initial_layout_requirement(&self) -> Layout {
|
||||
Layout::ShaderReadOnlyOptimal // TODO: ?
|
||||
fn initial_layout_requirement(&self) -> ImageLayout {
|
||||
ImageLayout::ShaderReadOnlyOptimal // TODO: ?
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn final_layout_requirement(&self) -> Layout {
|
||||
Layout::ShaderReadOnlyOptimal // TODO: ?
|
||||
fn final_layout_requirement(&self) -> ImageLayout {
|
||||
ImageLayout::ShaderReadOnlyOptimal // TODO: ?
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@ -188,8 +189,8 @@ unsafe impl<F, A> ImageAccess for ImmutableImage<F, A> where F: 'static + Send +
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_gpu_lock(&self, exclusive_access: bool, queue: &Queue) -> bool {
|
||||
true // FIXME:
|
||||
fn try_gpu_lock(&self, exclusive_access: bool, queue: &Queue) -> Result<(), AccessError> {
|
||||
Ok(()) // FIXME:
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@ -226,23 +227,23 @@ unsafe impl<F: 'static, A> ImageViewAccess for ImmutableImage<F, A>
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn descriptor_set_storage_image_layout(&self) -> Layout {
|
||||
Layout::ShaderReadOnlyOptimal
|
||||
fn descriptor_set_storage_image_layout(&self) -> ImageLayout {
|
||||
ImageLayout::ShaderReadOnlyOptimal
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn descriptor_set_combined_image_sampler_layout(&self) -> Layout {
|
||||
Layout::ShaderReadOnlyOptimal
|
||||
fn descriptor_set_combined_image_sampler_layout(&self) -> ImageLayout {
|
||||
ImageLayout::ShaderReadOnlyOptimal
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn descriptor_set_sampled_image_layout(&self) -> Layout {
|
||||
Layout::ShaderReadOnlyOptimal
|
||||
fn descriptor_set_sampled_image_layout(&self) -> ImageLayout {
|
||||
ImageLayout::ShaderReadOnlyOptimal
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn descriptor_set_input_attachment_layout(&self) -> Layout {
|
||||
Layout::ShaderReadOnlyOptimal
|
||||
fn descriptor_set_input_attachment_layout(&self) -> ImageLayout {
|
||||
ImageLayout::ShaderReadOnlyOptimal
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
42
vulkano/src/image/layout.rs
Normal file
42
vulkano/src/image/layout.rs
Normal file
@ -0,0 +1,42 @@
|
||||
// Copyright (c) 2016 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.
|
||||
|
||||
use vk;
|
||||
|
||||
/// Layout of an image.
|
||||
///
|
||||
/// > **Note**: In vulkano, image layouts are mostly a low-level detail. You can ignore them,
|
||||
/// > unless you use an unsafe function that states in its documentation that you must take care of
|
||||
/// > an image's layout.
|
||||
///
|
||||
/// In the Vulkan API, each mipmap level of each array layer is in one of the layouts of this enum.
|
||||
///
|
||||
/// Unless you use some short of high-level shortcut function, an image always starts in either
|
||||
/// the `Undefined` or the `Preinitialized` layout.
|
||||
/// Before you can use an image for a given purpose, you must ensure that the image in question is
|
||||
/// in the layout required for that purpose. For example if you want to write data to an image, you
|
||||
/// must first transition the image to the `TransferDstOptimal` layout. The `General` layout can
|
||||
/// also be used as a general-purpose fit-all layout, but using it will result in slower operations.
|
||||
///
|
||||
/// Transitionning between layouts can only be done through a GPU-side operation that is part of
|
||||
/// a command buffer.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[repr(u32)]
|
||||
pub enum ImageLayout {
|
||||
Undefined = vk::IMAGE_LAYOUT_UNDEFINED,
|
||||
General = vk::IMAGE_LAYOUT_GENERAL,
|
||||
ColorAttachmentOptimal = vk::IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
||||
DepthStencilAttachmentOptimal = vk::IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
|
||||
DepthStencilReadOnlyOptimal = vk::IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL,
|
||||
ShaderReadOnlyOptimal = vk::IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
||||
TransferSrcOptimal = vk::IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||
TransferDstOptimal = vk::IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
Preinitialized = vk::IMAGE_LAYOUT_PREINITIALIZED,
|
||||
PresentSrc = vk::IMAGE_LAYOUT_PRESENT_SRC_KHR,
|
||||
}
|
@ -48,22 +48,24 @@
|
||||
|
||||
pub use self::attachment::AttachmentImage;
|
||||
pub use self::immutable::ImmutableImage;
|
||||
pub use self::layout::ImageLayout;
|
||||
pub use self::storage::StorageImage;
|
||||
pub use self::swapchain::SwapchainImage;
|
||||
pub use self::sys::ImageCreationError;
|
||||
pub use self::sys::Layout;
|
||||
pub use self::sys::Usage;
|
||||
pub use self::traits::ImageAccess;
|
||||
pub use self::traits::ImageViewAccess;
|
||||
pub use self::traits::Image;
|
||||
pub use self::traits::ImageView;
|
||||
pub use self::usage::ImageUsage;
|
||||
|
||||
pub mod attachment; // TODO: make private
|
||||
pub mod immutable; // TODO: make private
|
||||
mod layout;
|
||||
mod storage;
|
||||
pub mod swapchain; // TODO: make private
|
||||
pub mod sys;
|
||||
pub mod traits;
|
||||
mod usage;
|
||||
|
||||
/// Specifies how many mipmaps must be allocated.
|
||||
///
|
||||
|
@ -22,10 +22,10 @@ use format::Format;
|
||||
use image::Dimensions;
|
||||
use image::ImageDimensions;
|
||||
use image::sys::ImageCreationError;
|
||||
use image::sys::Layout;
|
||||
use image::ImageLayout;
|
||||
use image::ImageUsage;
|
||||
use image::sys::UnsafeImage;
|
||||
use image::sys::UnsafeImageView;
|
||||
use image::sys::Usage;
|
||||
use image::traits::ImageAccess;
|
||||
use image::traits::ImageClearValue;
|
||||
use image::traits::ImageContent;
|
||||
@ -37,6 +37,7 @@ use memory::pool::AllocLayout;
|
||||
use memory::pool::MemoryPool;
|
||||
use memory::pool::MemoryPoolAlloc;
|
||||
use memory::pool::StdMemoryPool;
|
||||
use sync::AccessError;
|
||||
use sync::Sharing;
|
||||
|
||||
/// General-purpose image in device memory. Can be used for any usage, but will be slower than a
|
||||
@ -67,7 +68,7 @@ pub struct StorageImage<F, A = Arc<StdMemoryPool>> where A: MemoryPool {
|
||||
|
||||
impl<F> StorageImage<F> {
|
||||
/// Creates a new image with the given dimensions and format.
|
||||
pub fn new<'a, I>(device: &Arc<Device>, dimensions: Dimensions, format: F, queue_families: I)
|
||||
pub fn new<'a, I>(device: Arc<Device>, dimensions: Dimensions, format: F, queue_families: I)
|
||||
-> Result<Arc<StorageImage<F>>, ImageCreationError>
|
||||
where F: FormatDesc,
|
||||
I: IntoIterator<Item = QueueFamily<'a>>
|
||||
@ -80,7 +81,7 @@ impl<F> StorageImage<F> {
|
||||
_ => false
|
||||
};
|
||||
|
||||
let usage = Usage {
|
||||
let usage = ImageUsage {
|
||||
transfer_source: true,
|
||||
transfer_dest: true,
|
||||
sampled: true,
|
||||
@ -101,7 +102,7 @@ impl<F> StorageImage<F> {
|
||||
Sharing::Exclusive
|
||||
};
|
||||
|
||||
try!(UnsafeImage::new(device, &usage, format.format(), dimensions.to_image_dimensions(),
|
||||
try!(UnsafeImage::new(device.clone(), usage, format.format(), dimensions.to_image_dimensions(),
|
||||
1, 1, Sharing::Exclusive::<Empty<u32>>, false, false))
|
||||
};
|
||||
|
||||
@ -114,7 +115,7 @@ impl<F> StorageImage<F> {
|
||||
device_local.chain(any).next().unwrap()
|
||||
};
|
||||
|
||||
let mem = try!(MemoryPool::alloc(&Device::standard_pool(device), mem_ty,
|
||||
let mem = try!(MemoryPool::alloc(&Device::standard_pool(&device), mem_ty,
|
||||
mem_reqs.size, mem_reqs.alignment, AllocLayout::Optimal));
|
||||
debug_assert!((mem.offset() % mem_reqs.alignment) == 0);
|
||||
unsafe { try!(image.bind_memory(mem.memory(), mem.offset())); }
|
||||
@ -190,13 +191,13 @@ unsafe impl<F, A> ImageAccess for StorageImage<F, A> where F: 'static + Send + S
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn initial_layout_requirement(&self) -> Layout {
|
||||
Layout::General
|
||||
fn initial_layout_requirement(&self) -> ImageLayout {
|
||||
ImageLayout::General
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn final_layout_requirement(&self) -> Layout {
|
||||
Layout::General
|
||||
fn final_layout_requirement(&self) -> ImageLayout {
|
||||
ImageLayout::General
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@ -205,13 +206,13 @@ unsafe impl<F, A> ImageAccess for StorageImage<F, A> where F: 'static + Send + S
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_gpu_lock(&self, _: bool, _: &Queue) -> bool {
|
||||
fn try_gpu_lock(&self, _: bool, _: &Queue) -> Result<(), AccessError> {
|
||||
let val = self.gpu_lock.fetch_add(1, Ordering::SeqCst);
|
||||
if val == 1 {
|
||||
true
|
||||
Ok(())
|
||||
} else {
|
||||
self.gpu_lock.fetch_sub(1, Ordering::SeqCst);
|
||||
false
|
||||
Err(AccessError::AlreadyInUse)
|
||||
}
|
||||
}
|
||||
|
||||
@ -259,23 +260,23 @@ unsafe impl<F, A> ImageViewAccess for StorageImage<F, A>
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn descriptor_set_storage_image_layout(&self) -> Layout {
|
||||
Layout::General
|
||||
fn descriptor_set_storage_image_layout(&self) -> ImageLayout {
|
||||
ImageLayout::General
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn descriptor_set_combined_image_sampler_layout(&self) -> Layout {
|
||||
Layout::General
|
||||
fn descriptor_set_combined_image_sampler_layout(&self) -> ImageLayout {
|
||||
ImageLayout::General
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn descriptor_set_sampled_image_layout(&self) -> Layout {
|
||||
Layout::General
|
||||
fn descriptor_set_sampled_image_layout(&self) -> ImageLayout {
|
||||
ImageLayout::General
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn descriptor_set_input_attachment_layout(&self) -> Layout {
|
||||
Layout::General
|
||||
fn descriptor_set_input_attachment_layout(&self) -> ImageLayout {
|
||||
ImageLayout::General
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@ -293,7 +294,7 @@ mod tests {
|
||||
#[test]
|
||||
fn create() {
|
||||
let (device, queue) = gfx_dev_and_queue!();
|
||||
let _img = StorageImage::new(&device, Dimensions::Dim2d { width: 32, height: 32 },
|
||||
let _img = StorageImage::new(device, Dimensions::Dim2d { width: 32, height: 32 },
|
||||
Format::R8G8B8A8Unorm, Some(queue.family())).unwrap();
|
||||
}
|
||||
}
|
||||
|
@ -22,10 +22,11 @@ use image::traits::ImageContent;
|
||||
use image::traits::ImageViewAccess;
|
||||
use image::traits::Image;
|
||||
use image::traits::ImageView;
|
||||
use image::sys::Layout;
|
||||
use image::ImageLayout;
|
||||
use image::sys::UnsafeImage;
|
||||
use image::sys::UnsafeImageView;
|
||||
use swapchain::Swapchain;
|
||||
use sync::AccessError;
|
||||
|
||||
use OomError;
|
||||
|
||||
@ -42,30 +43,27 @@ use OomError;
|
||||
/// method on the swapchain), which will have the effect of showing the content of the image to
|
||||
/// the screen. Once an image has been presented, it can no longer be used unless it is acquired
|
||||
/// again.
|
||||
// TODO: #[derive(Debug)] (needs https://github.com/aturon/crossbeam/issues/62)
|
||||
// TODO: #[derive(Debug)]
|
||||
pub struct SwapchainImage {
|
||||
image: UnsafeImage,
|
||||
view: UnsafeImageView,
|
||||
format: Format,
|
||||
swapchain: Arc<Swapchain>,
|
||||
id: u32,
|
||||
image_offset: usize,
|
||||
view: UnsafeImageView,
|
||||
}
|
||||
|
||||
impl SwapchainImage {
|
||||
/// Builds a `SwapchainImage` from raw components.
|
||||
///
|
||||
/// This is an internal method that you shouldn't call.
|
||||
pub unsafe fn from_raw(image: UnsafeImage, format: Format, swapchain: &Arc<Swapchain>, id: u32)
|
||||
pub unsafe fn from_raw(swapchain: Arc<Swapchain>, id: usize)
|
||||
-> Result<Arc<SwapchainImage>, OomError>
|
||||
{
|
||||
let image = swapchain.raw_image(id).unwrap();
|
||||
let view = try!(UnsafeImageView::raw(&image, ViewType::Dim2d, 0 .. 1, 0 .. 1));
|
||||
|
||||
Ok(Arc::new(SwapchainImage {
|
||||
image: image,
|
||||
view: view,
|
||||
format: format,
|
||||
swapchain: swapchain.clone(),
|
||||
id: id,
|
||||
image_offset: id,
|
||||
view: view,
|
||||
}))
|
||||
}
|
||||
|
||||
@ -74,49 +72,47 @@ impl SwapchainImage {
|
||||
/// A `SwapchainImage` is always two-dimensional.
|
||||
#[inline]
|
||||
pub fn dimensions(&self) -> [u32; 2] {
|
||||
let dims = self.image.dimensions();
|
||||
let dims = self.my_image().dimensions();
|
||||
[dims.width(), dims.height()]
|
||||
}
|
||||
|
||||
/// Returns the format of the image.
|
||||
// TODO: return `ColorFormat` or something like this instead, for stronger typing
|
||||
#[inline]
|
||||
pub fn format(&self) -> Format {
|
||||
self.format
|
||||
}
|
||||
|
||||
/// Returns the swapchain this image belongs to.
|
||||
#[inline]
|
||||
pub fn swapchain(&self) -> &Arc<Swapchain> {
|
||||
&self.swapchain
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn my_image(&self) -> &UnsafeImage {
|
||||
self.swapchain.raw_image(self.image_offset).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl ImageAccess for SwapchainImage {
|
||||
#[inline]
|
||||
fn inner(&self) -> &UnsafeImage {
|
||||
&self.image
|
||||
self.my_image()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn initial_layout_requirement(&self) -> Layout {
|
||||
Layout::PresentSrc
|
||||
fn initial_layout_requirement(&self) -> ImageLayout {
|
||||
ImageLayout::PresentSrc
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn final_layout_requirement(&self) -> Layout {
|
||||
Layout::PresentSrc
|
||||
fn final_layout_requirement(&self) -> ImageLayout {
|
||||
ImageLayout::PresentSrc
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn conflict_key(&self, _: u32, _: u32, _: u32, _: u32) -> u64 {
|
||||
self.image.key()
|
||||
self.my_image().key()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_gpu_lock(&self, _: bool, _: &Queue) -> bool {
|
||||
fn try_gpu_lock(&self, _: bool, _: &Queue) -> Result<(), AccessError> {
|
||||
// Swapchain image are only accessible after being acquired.
|
||||
false
|
||||
Err(AccessError::SwapchainImageAcquireOnly)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@ -128,7 +124,7 @@ unsafe impl ImageClearValue<<Format as FormatDesc>::ClearValue> for SwapchainIma
|
||||
{
|
||||
#[inline]
|
||||
fn decode(&self, value: <Format as FormatDesc>::ClearValue) -> Option<ClearValue> {
|
||||
Some(self.format.decode_clear_value(value))
|
||||
Some(self.swapchain.format().decode_clear_value(value))
|
||||
}
|
||||
}
|
||||
|
||||
@ -147,8 +143,8 @@ unsafe impl ImageViewAccess for SwapchainImage {
|
||||
|
||||
#[inline]
|
||||
fn dimensions(&self) -> Dimensions {
|
||||
let dims = self.image.dimensions();
|
||||
Dimensions::Dim2d { width: dims.width(), height: dims.height() }
|
||||
let dims = self.swapchain.dimensions();
|
||||
Dimensions::Dim2d { width: dims[0], height: dims[1] }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@ -157,23 +153,23 @@ unsafe impl ImageViewAccess for SwapchainImage {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn descriptor_set_storage_image_layout(&self) -> Layout {
|
||||
Layout::ShaderReadOnlyOptimal
|
||||
fn descriptor_set_storage_image_layout(&self) -> ImageLayout {
|
||||
ImageLayout::ShaderReadOnlyOptimal
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn descriptor_set_combined_image_sampler_layout(&self) -> Layout {
|
||||
Layout::ShaderReadOnlyOptimal
|
||||
fn descriptor_set_combined_image_sampler_layout(&self) -> ImageLayout {
|
||||
ImageLayout::ShaderReadOnlyOptimal
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn descriptor_set_sampled_image_layout(&self) -> Layout {
|
||||
Layout::ShaderReadOnlyOptimal
|
||||
fn descriptor_set_sampled_image_layout(&self) -> ImageLayout {
|
||||
ImageLayout::ShaderReadOnlyOptimal
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn descriptor_set_input_attachment_layout(&self) -> Layout {
|
||||
Layout::ShaderReadOnlyOptimal
|
||||
fn descriptor_set_input_attachment_layout(&self) -> ImageLayout {
|
||||
ImageLayout::ShaderReadOnlyOptimal
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@ -192,17 +188,17 @@ unsafe impl Image for SwapchainImage {
|
||||
|
||||
#[inline]
|
||||
fn format(&self) -> Format {
|
||||
self.image.format()
|
||||
self.my_image().format()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn samples(&self) -> u32 {
|
||||
self.image.samples()
|
||||
self.my_image().samples()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn dimensions(&self) -> ImageDimensions {
|
||||
self.image.dimensions()
|
||||
self.my_image().dimensions()
|
||||
}
|
||||
}
|
||||
|
||||
@ -224,17 +220,17 @@ unsafe impl Image for Arc<SwapchainImage> {
|
||||
|
||||
#[inline]
|
||||
fn format(&self) -> Format {
|
||||
self.image.format()
|
||||
self.my_image().format()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn samples(&self) -> u32 {
|
||||
self.image.samples()
|
||||
self.my_image().samples()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn dimensions(&self) -> ImageDimensions {
|
||||
self.image.dimensions()
|
||||
self.my_image().dimensions()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25,6 +25,7 @@ use device::Device;
|
||||
use format::Format;
|
||||
use format::FormatTy;
|
||||
use image::ImageDimensions;
|
||||
use image::ImageUsage;
|
||||
use image::MipmapsCount;
|
||||
use image::ViewType;
|
||||
use memory::DeviceMemory;
|
||||
@ -78,7 +79,7 @@ impl UnsafeImage {
|
||||
/// - Panics if the number of samples is 0.
|
||||
///
|
||||
#[inline]
|
||||
pub unsafe fn new<'a, Mi, I>(device: &Arc<Device>, usage: &Usage, format: Format,
|
||||
pub unsafe fn new<'a, Mi, I>(device: Arc<Device>, usage: ImageUsage, format: Format,
|
||||
dimensions: ImageDimensions, num_samples: u32, mipmaps: Mi,
|
||||
sharing: Sharing<I>, linear_tiling: bool,
|
||||
preinitialized_layout: bool)
|
||||
@ -95,7 +96,7 @@ impl UnsafeImage {
|
||||
}
|
||||
|
||||
// Non-templated version to avoid inlining and improve compile times.
|
||||
unsafe fn new_impl(device: &Arc<Device>, usage: &Usage, format: Format,
|
||||
unsafe fn new_impl(device: Arc<Device>, usage: ImageUsage, format: Format,
|
||||
dimensions: ImageDimensions, num_samples: u32, mipmaps: MipmapsCount,
|
||||
(sh_mode, sh_indices): (vk::SharingMode, SmallVec<[u32; 8]>),
|
||||
linear_tiling: bool, preinitialized_layout: bool)
|
||||
@ -153,7 +154,7 @@ impl UnsafeImage {
|
||||
// If `transient_attachment` is true, then only `color_attachment`,
|
||||
// `depth_stencil_attachment` and `input_attachment` can be true as well.
|
||||
if usage.transient_attachment {
|
||||
let u = Usage {
|
||||
let u = ImageUsage {
|
||||
transient_attachment: false,
|
||||
color_attachment: false,
|
||||
depth_stencil_attachment: false,
|
||||
@ -161,7 +162,7 @@ impl UnsafeImage {
|
||||
.. usage.clone()
|
||||
};
|
||||
|
||||
if u != Usage::none() {
|
||||
if u != ImageUsage::none() {
|
||||
return Err(ImageCreationError::UnsupportedUsage);
|
||||
}
|
||||
}
|
||||
@ -451,7 +452,7 @@ impl UnsafeImage {
|
||||
/// Creates an image from a raw handle. The image won't be destroyed.
|
||||
///
|
||||
/// This function is for example used at the swapchain's initialization.
|
||||
pub unsafe fn from_raw(device: &Arc<Device>, handle: u64, usage: u32, format: Format,
|
||||
pub unsafe fn from_raw(device: Arc<Device>, handle: u64, usage: u32, format: Format,
|
||||
dimensions: ImageDimensions, samples: u32, mipmaps: u32)
|
||||
-> UnsafeImage
|
||||
{
|
||||
@ -972,135 +973,6 @@ impl Drop for UnsafeImageView {
|
||||
}
|
||||
}
|
||||
|
||||
/// Describes how an image is going to be used. This is **not** an optimization.
|
||||
///
|
||||
/// If you try to use an image in a way that you didn't declare, a panic will happen.
|
||||
///
|
||||
/// If `transient_attachment` is true, then only `color_attachment`, `depth_stencil_attachment`
|
||||
/// and `input_attachment` can be true as well. The rest must be false or an error will be returned
|
||||
/// when creating the image.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Usage {
|
||||
/// Can be used a source for transfers. Includes blits.
|
||||
pub transfer_source: bool,
|
||||
|
||||
/// Can be used a destination for transfers. Includes blits.
|
||||
pub transfer_dest: bool,
|
||||
|
||||
/// Can be sampled from a shader.
|
||||
pub sampled: bool,
|
||||
|
||||
/// Can be used as an image storage in a shader.
|
||||
pub storage: bool,
|
||||
|
||||
/// Can be attached as a color attachment to a framebuffer.
|
||||
pub color_attachment: bool,
|
||||
|
||||
/// Can be attached as a depth, stencil or depth-stencil attachment to a framebuffer.
|
||||
pub depth_stencil_attachment: bool,
|
||||
|
||||
/// Indicates that this image will only ever be used as a temporary framebuffer attachment.
|
||||
/// As soon as you leave a render pass, the content of transient images becomes undefined.
|
||||
///
|
||||
/// This is a hint to the Vulkan implementation that it may not need allocate any memory for
|
||||
/// this image if the image can live entirely in some cache.
|
||||
pub transient_attachment: bool,
|
||||
|
||||
/// Can be used as an input attachment. In other words, you can draw to it in a subpass then
|
||||
/// read from it in a following pass.
|
||||
pub input_attachment: bool,
|
||||
}
|
||||
|
||||
impl Usage {
|
||||
/// Builds a `Usage` with all values set to true. Note that using the returned value will
|
||||
/// produce an error because of `transient_attachment` being true.
|
||||
#[inline]
|
||||
pub fn all() -> Usage {
|
||||
Usage {
|
||||
transfer_source: true,
|
||||
transfer_dest: true,
|
||||
sampled: true,
|
||||
storage: true,
|
||||
color_attachment: true,
|
||||
depth_stencil_attachment: true,
|
||||
transient_attachment: true,
|
||||
input_attachment: true,
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds a `Usage` with all values set to false. Useful as a default value.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use vulkano::image::Usage as ImageUsage;
|
||||
///
|
||||
/// let _usage = ImageUsage {
|
||||
/// transfer_dest: true,
|
||||
/// sampled: true,
|
||||
/// .. ImageUsage::none()
|
||||
/// };
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn none() -> Usage {
|
||||
Usage {
|
||||
transfer_source: false,
|
||||
transfer_dest: false,
|
||||
sampled: false,
|
||||
storage: false,
|
||||
color_attachment: false,
|
||||
depth_stencil_attachment: false,
|
||||
transient_attachment: false,
|
||||
input_attachment: false,
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
pub fn to_usage_bits(&self) -> vk::ImageUsageFlagBits {
|
||||
let mut result = 0;
|
||||
if self.transfer_source { result |= vk::IMAGE_USAGE_TRANSFER_SRC_BIT; }
|
||||
if self.transfer_dest { result |= vk::IMAGE_USAGE_TRANSFER_DST_BIT; }
|
||||
if self.sampled { result |= vk::IMAGE_USAGE_SAMPLED_BIT; }
|
||||
if self.storage { result |= vk::IMAGE_USAGE_STORAGE_BIT; }
|
||||
if self.color_attachment { result |= vk::IMAGE_USAGE_COLOR_ATTACHMENT_BIT; }
|
||||
if self.depth_stencil_attachment { result |= vk::IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; }
|
||||
if self.transient_attachment { result |= vk::IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT; }
|
||||
if self.input_attachment { result |= vk::IMAGE_USAGE_INPUT_ATTACHMENT_BIT; }
|
||||
result
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[doc(hidden)]
|
||||
pub fn from_bits(val: u32) -> Usage {
|
||||
Usage {
|
||||
transfer_source: (val & vk::IMAGE_USAGE_TRANSFER_SRC_BIT) != 0,
|
||||
transfer_dest: (val & vk::IMAGE_USAGE_TRANSFER_DST_BIT) != 0,
|
||||
sampled: (val & vk::IMAGE_USAGE_SAMPLED_BIT) != 0,
|
||||
storage: (val & vk::IMAGE_USAGE_STORAGE_BIT) != 0,
|
||||
color_attachment: (val & vk::IMAGE_USAGE_COLOR_ATTACHMENT_BIT) != 0,
|
||||
depth_stencil_attachment: (val & vk::IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0,
|
||||
transient_attachment: (val & vk::IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT) != 0,
|
||||
input_attachment: (val & vk::IMAGE_USAGE_INPUT_ATTACHMENT_BIT) != 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[repr(u32)]
|
||||
pub enum Layout {
|
||||
Undefined = vk::IMAGE_LAYOUT_UNDEFINED,
|
||||
General = vk::IMAGE_LAYOUT_GENERAL,
|
||||
ColorAttachmentOptimal = vk::IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
||||
DepthStencilAttachmentOptimal = vk::IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
|
||||
DepthStencilReadOnlyOptimal = vk::IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL,
|
||||
ShaderReadOnlyOptimal = vk::IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
||||
TransferSrcOptimal = vk::IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||
TransferDstOptimal = vk::IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
Preinitialized = vk::IMAGE_LAYOUT_PREINITIALIZED,
|
||||
PresentSrc = vk::IMAGE_LAYOUT_PRESENT_SRC_KHR,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::iter::Empty;
|
||||
@ -1108,7 +980,7 @@ mod tests {
|
||||
|
||||
use super::ImageCreationError;
|
||||
use super::UnsafeImage;
|
||||
use super::Usage;
|
||||
use super::ImageUsage;
|
||||
|
||||
use image::ImageDimensions;
|
||||
use format::Format;
|
||||
@ -1118,13 +990,13 @@ mod tests {
|
||||
fn create_sampled() {
|
||||
let (device, _) = gfx_dev_and_queue!();
|
||||
|
||||
let usage = Usage {
|
||||
let usage = ImageUsage {
|
||||
sampled: true,
|
||||
.. Usage::none()
|
||||
.. ImageUsage::none()
|
||||
};
|
||||
|
||||
let (_img, _) = unsafe {
|
||||
UnsafeImage::new(&device, &usage, Format::R8G8B8A8Unorm,
|
||||
UnsafeImage::new(device, usage, Format::R8G8B8A8Unorm,
|
||||
ImageDimensions::Dim2d { width: 32, height: 32, array_layers: 1,
|
||||
cubemap_compatible: false }, 1, 1,
|
||||
Sharing::Exclusive::<Empty<_>>, false, false)
|
||||
@ -1135,14 +1007,14 @@ mod tests {
|
||||
fn create_transient() {
|
||||
let (device, _) = gfx_dev_and_queue!();
|
||||
|
||||
let usage = Usage {
|
||||
let usage = ImageUsage {
|
||||
transient_attachment: true,
|
||||
color_attachment: true,
|
||||
.. Usage::none()
|
||||
.. ImageUsage::none()
|
||||
};
|
||||
|
||||
let (_img, _) = unsafe {
|
||||
UnsafeImage::new(&device, &usage, Format::R8G8B8A8Unorm,
|
||||
UnsafeImage::new(device, usage, Format::R8G8B8A8Unorm,
|
||||
ImageDimensions::Dim2d { width: 32, height: 32, array_layers: 1,
|
||||
cubemap_compatible: false }, 1, 1,
|
||||
Sharing::Exclusive::<Empty<_>>, false, false)
|
||||
@ -1153,13 +1025,13 @@ mod tests {
|
||||
fn zero_sample() {
|
||||
let (device, _) = gfx_dev_and_queue!();
|
||||
|
||||
let usage = Usage {
|
||||
let usage = ImageUsage {
|
||||
sampled: true,
|
||||
.. Usage::none()
|
||||
.. ImageUsage::none()
|
||||
};
|
||||
|
||||
let res = unsafe {
|
||||
UnsafeImage::new(&device, &usage, Format::R8G8B8A8Unorm,
|
||||
UnsafeImage::new(device, usage, Format::R8G8B8A8Unorm,
|
||||
ImageDimensions::Dim2d { width: 32, height: 32, array_layers: 1,
|
||||
cubemap_compatible: false }, 0, 1,
|
||||
Sharing::Exclusive::<Empty<_>>, false, false)
|
||||
@ -1175,13 +1047,13 @@ mod tests {
|
||||
fn non_po2_sample() {
|
||||
let (device, _) = gfx_dev_and_queue!();
|
||||
|
||||
let usage = Usage {
|
||||
let usage = ImageUsage {
|
||||
sampled: true,
|
||||
.. Usage::none()
|
||||
.. ImageUsage::none()
|
||||
};
|
||||
|
||||
let res = unsafe {
|
||||
UnsafeImage::new(&device, &usage, Format::R8G8B8A8Unorm,
|
||||
UnsafeImage::new(device, usage, Format::R8G8B8A8Unorm,
|
||||
ImageDimensions::Dim2d { width: 32, height: 32, array_layers: 1,
|
||||
cubemap_compatible: false }, 5, 1,
|
||||
Sharing::Exclusive::<Empty<_>>, false, false)
|
||||
@ -1197,13 +1069,13 @@ mod tests {
|
||||
fn zero_mipmap() {
|
||||
let (device, _) = gfx_dev_and_queue!();
|
||||
|
||||
let usage = Usage {
|
||||
let usage = ImageUsage {
|
||||
sampled: true,
|
||||
.. Usage::none()
|
||||
.. ImageUsage::none()
|
||||
};
|
||||
|
||||
let res = unsafe {
|
||||
UnsafeImage::new(&device, &usage, Format::R8G8B8A8Unorm,
|
||||
UnsafeImage::new(device, usage, Format::R8G8B8A8Unorm,
|
||||
ImageDimensions::Dim2d { width: 32, height: 32, array_layers: 1,
|
||||
cubemap_compatible: false }, 1, 0,
|
||||
Sharing::Exclusive::<Empty<_>>, false, false)
|
||||
@ -1220,13 +1092,13 @@ mod tests {
|
||||
fn mipmaps_too_high() {
|
||||
let (device, _) = gfx_dev_and_queue!();
|
||||
|
||||
let usage = Usage {
|
||||
let usage = ImageUsage {
|
||||
sampled: true,
|
||||
.. Usage::none()
|
||||
.. ImageUsage::none()
|
||||
};
|
||||
|
||||
let res = unsafe {
|
||||
UnsafeImage::new(&device, &usage, Format::R8G8B8A8Unorm,
|
||||
UnsafeImage::new(device, usage, Format::R8G8B8A8Unorm,
|
||||
ImageDimensions::Dim2d { width: 32, height: 32, array_layers: 1,
|
||||
cubemap_compatible: false }, 1, u32::MAX,
|
||||
Sharing::Exclusive::<Empty<_>>, false, false)
|
||||
@ -1245,13 +1117,13 @@ mod tests {
|
||||
fn shader_storage_image_multisample() {
|
||||
let (device, _) = gfx_dev_and_queue!();
|
||||
|
||||
let usage = Usage {
|
||||
let usage = ImageUsage {
|
||||
storage: true,
|
||||
.. Usage::none()
|
||||
.. ImageUsage::none()
|
||||
};
|
||||
|
||||
let res = unsafe {
|
||||
UnsafeImage::new(&device, &usage, Format::R8G8B8A8Unorm,
|
||||
UnsafeImage::new(device, usage, Format::R8G8B8A8Unorm,
|
||||
ImageDimensions::Dim2d { width: 32, height: 32, array_layers: 1,
|
||||
cubemap_compatible: false }, 2, 1,
|
||||
Sharing::Exclusive::<Empty<_>>, false, false)
|
||||
@ -1268,13 +1140,13 @@ mod tests {
|
||||
fn compressed_not_color_attachment() {
|
||||
let (device, _) = gfx_dev_and_queue!();
|
||||
|
||||
let usage = Usage {
|
||||
let usage = ImageUsage {
|
||||
color_attachment: true,
|
||||
.. Usage::none()
|
||||
.. ImageUsage::none()
|
||||
};
|
||||
|
||||
let res = unsafe {
|
||||
UnsafeImage::new(&device, &usage, Format::ASTC_5x4UnormBlock,
|
||||
UnsafeImage::new(device, usage, Format::ASTC_5x4UnormBlock,
|
||||
ImageDimensions::Dim2d { width: 32, height: 32, array_layers: 1,
|
||||
cubemap_compatible: false }, 1, u32::MAX,
|
||||
Sharing::Exclusive::<Empty<_>>, false, false)
|
||||
@ -1291,14 +1163,14 @@ mod tests {
|
||||
fn transient_forbidden_with_some_usages() {
|
||||
let (device, _) = gfx_dev_and_queue!();
|
||||
|
||||
let usage = Usage {
|
||||
let usage = ImageUsage {
|
||||
transient_attachment: true,
|
||||
sampled: true,
|
||||
.. Usage::none()
|
||||
.. ImageUsage::none()
|
||||
};
|
||||
|
||||
let res = unsafe {
|
||||
UnsafeImage::new(&device, &usage, Format::R8G8B8A8Unorm,
|
||||
UnsafeImage::new(device, usage, Format::R8G8B8A8Unorm,
|
||||
ImageDimensions::Dim2d { width: 32, height: 32, array_layers: 1,
|
||||
cubemap_compatible: false }, 1, 1,
|
||||
Sharing::Exclusive::<Empty<_>>, false, false)
|
||||
@ -1314,13 +1186,13 @@ mod tests {
|
||||
fn cubecompatible_dims_mismatch() {
|
||||
let (device, _) = gfx_dev_and_queue!();
|
||||
|
||||
let usage = Usage {
|
||||
let usage = ImageUsage {
|
||||
sampled: true,
|
||||
.. Usage::none()
|
||||
.. ImageUsage::none()
|
||||
};
|
||||
|
||||
let res = unsafe {
|
||||
UnsafeImage::new(&device, &usage, Format::R8G8B8A8Unorm,
|
||||
UnsafeImage::new(device, usage, Format::R8G8B8A8Unorm,
|
||||
ImageDimensions::Dim2d { width: 32, height: 64, array_layers: 1,
|
||||
cubemap_compatible: true }, 1, 1,
|
||||
Sharing::Exclusive::<Empty<_>>, false, false)
|
||||
|
@ -19,10 +19,11 @@ use format::PossibleStencilFormatDesc;
|
||||
use format::PossibleDepthStencilFormatDesc;
|
||||
use image::Dimensions;
|
||||
use image::ImageDimensions;
|
||||
use image::sys::Layout;
|
||||
use image::ImageLayout;
|
||||
use image::sys::UnsafeImage;
|
||||
use image::sys::UnsafeImageView;
|
||||
use sampler::Sampler;
|
||||
use sync::AccessError;
|
||||
|
||||
use SafeDeref;
|
||||
use VulkanObject;
|
||||
@ -110,10 +111,10 @@ pub unsafe trait ImageAccess {
|
||||
}
|
||||
|
||||
/// Returns the layout that the image has when it is first used in a primary command buffer.
|
||||
fn initial_layout_requirement(&self) -> Layout;
|
||||
fn initial_layout_requirement(&self) -> ImageLayout;
|
||||
|
||||
/// Returns the layout that the image must be returned to before the end of the command buffer.
|
||||
fn final_layout_requirement(&self) -> Layout;
|
||||
fn final_layout_requirement(&self) -> ImageLayout;
|
||||
|
||||
/// Wraps around this `ImageAccess` and returns an identical `ImageAccess` but whose initial
|
||||
/// layout requirement is either `Undefined` or `Preinitialized`.
|
||||
@ -208,7 +209,7 @@ pub unsafe trait ImageAccess {
|
||||
/// The only way to know that the GPU has stopped accessing a queue is when the image object
|
||||
/// gets destroyed. Therefore you are encouraged to use temporary objects or handles (similar
|
||||
/// to a lock) in order to represent a GPU access.
|
||||
fn try_gpu_lock(&self, exclusive_access: bool, queue: &Queue) -> bool;
|
||||
fn try_gpu_lock(&self, exclusive_access: bool, queue: &Queue) -> Result<(), AccessError>;
|
||||
|
||||
/// Locks the resource for usage on the GPU. Supposes that the resource is already locked, and
|
||||
/// simply increases the lock by one.
|
||||
@ -224,12 +225,12 @@ unsafe impl<T> ImageAccess for T where T: SafeDeref, T::Target: ImageAccess {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn initial_layout_requirement(&self) -> Layout {
|
||||
fn initial_layout_requirement(&self) -> ImageLayout {
|
||||
(**self).initial_layout_requirement()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn final_layout_requirement(&self) -> Layout {
|
||||
fn final_layout_requirement(&self) -> ImageLayout {
|
||||
(**self).final_layout_requirement()
|
||||
}
|
||||
|
||||
@ -241,7 +242,7 @@ unsafe impl<T> ImageAccess for T where T: SafeDeref, T::Target: ImageAccess {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_gpu_lock(&self, exclusive_access: bool, queue: &Queue) -> bool {
|
||||
fn try_gpu_lock(&self, exclusive_access: bool, queue: &Queue) -> Result<(), AccessError> {
|
||||
(**self).try_gpu_lock(exclusive_access, queue)
|
||||
}
|
||||
|
||||
@ -268,16 +269,16 @@ unsafe impl<I> ImageAccess for ImageAccessFromUndefinedLayout<I>
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn initial_layout_requirement(&self) -> Layout {
|
||||
fn initial_layout_requirement(&self) -> ImageLayout {
|
||||
if self.preinitialized {
|
||||
Layout::Preinitialized
|
||||
ImageLayout::Preinitialized
|
||||
} else {
|
||||
Layout::Undefined
|
||||
ImageLayout::Undefined
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn final_layout_requirement(&self) -> Layout {
|
||||
fn final_layout_requirement(&self) -> ImageLayout {
|
||||
self.image.final_layout_requirement()
|
||||
}
|
||||
|
||||
@ -289,7 +290,7 @@ unsafe impl<I> ImageAccess for ImageAccessFromUndefinedLayout<I>
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_gpu_lock(&self, exclusive_access: bool, queue: &Queue) -> bool {
|
||||
fn try_gpu_lock(&self, exclusive_access: bool, queue: &Queue) -> Result<(), AccessError> {
|
||||
self.image.try_gpu_lock(exclusive_access, queue)
|
||||
}
|
||||
|
||||
@ -342,13 +343,13 @@ pub unsafe trait ImageViewAccess {
|
||||
}
|
||||
|
||||
/// Returns the image layout to use in a descriptor with the given subresource.
|
||||
fn descriptor_set_storage_image_layout(&self) -> Layout;
|
||||
fn descriptor_set_storage_image_layout(&self) -> ImageLayout;
|
||||
/// Returns the image layout to use in a descriptor with the given subresource.
|
||||
fn descriptor_set_combined_image_sampler_layout(&self) -> Layout;
|
||||
fn descriptor_set_combined_image_sampler_layout(&self) -> ImageLayout;
|
||||
/// Returns the image layout to use in a descriptor with the given subresource.
|
||||
fn descriptor_set_sampled_image_layout(&self) -> Layout;
|
||||
fn descriptor_set_sampled_image_layout(&self) -> ImageLayout;
|
||||
/// Returns the image layout to use in a descriptor with the given subresource.
|
||||
fn descriptor_set_input_attachment_layout(&self) -> Layout;
|
||||
fn descriptor_set_input_attachment_layout(&self) -> ImageLayout;
|
||||
|
||||
/// Returns true if the view doesn't use components swizzling.
|
||||
///
|
||||
@ -383,19 +384,19 @@ unsafe impl<T> ImageViewAccess for T where T: SafeDeref, T::Target: ImageViewAcc
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn descriptor_set_storage_image_layout(&self) -> Layout {
|
||||
fn descriptor_set_storage_image_layout(&self) -> ImageLayout {
|
||||
(**self).descriptor_set_storage_image_layout()
|
||||
}
|
||||
#[inline]
|
||||
fn descriptor_set_combined_image_sampler_layout(&self) -> Layout {
|
||||
fn descriptor_set_combined_image_sampler_layout(&self) -> ImageLayout {
|
||||
(**self).descriptor_set_combined_image_sampler_layout()
|
||||
}
|
||||
#[inline]
|
||||
fn descriptor_set_sampled_image_layout(&self) -> Layout {
|
||||
fn descriptor_set_sampled_image_layout(&self) -> ImageLayout {
|
||||
(**self).descriptor_set_sampled_image_layout()
|
||||
}
|
||||
#[inline]
|
||||
fn descriptor_set_input_attachment_layout(&self) -> Layout {
|
||||
fn descriptor_set_input_attachment_layout(&self) -> ImageLayout {
|
||||
(**self).descriptor_set_input_attachment_layout()
|
||||
}
|
||||
|
||||
@ -411,5 +412,5 @@ unsafe impl<T> ImageViewAccess for T where T: SafeDeref, T::Target: ImageViewAcc
|
||||
}
|
||||
|
||||
pub unsafe trait AttachmentImageView: ImageViewAccess {
|
||||
fn accept(&self, initial_layout: Layout, final_layout: Layout) -> bool;
|
||||
fn accept(&self, initial_layout: ImageLayout, final_layout: ImageLayout) -> bool;
|
||||
}
|
||||
|
145
vulkano/src/image/usage.rs
Normal file
145
vulkano/src/image/usage.rs
Normal file
@ -0,0 +1,145 @@
|
||||
// Copyright (c) 2016 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.
|
||||
|
||||
use std::ops::BitOr;
|
||||
use vk;
|
||||
|
||||
/// Describes how an image is going to be used. This is **not** an optimization.
|
||||
///
|
||||
/// If you try to use an image in a way that you didn't declare, a panic will happen.
|
||||
///
|
||||
/// If `transient_attachment` is true, then only `color_attachment`, `depth_stencil_attachment`
|
||||
/// and `input_attachment` can be true as well. The rest must be false or an error will be returned
|
||||
/// when creating the image.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct ImageUsage {
|
||||
/// Can be used a source for transfers. Includes blits.
|
||||
pub transfer_source: bool,
|
||||
|
||||
/// Can be used a destination for transfers. Includes blits.
|
||||
pub transfer_dest: bool,
|
||||
|
||||
/// Can be sampled from a shader.
|
||||
pub sampled: bool,
|
||||
|
||||
/// Can be used as an image storage in a shader.
|
||||
pub storage: bool,
|
||||
|
||||
/// Can be attached as a color attachment to a framebuffer.
|
||||
pub color_attachment: bool,
|
||||
|
||||
/// Can be attached as a depth, stencil or depth-stencil attachment to a framebuffer.
|
||||
pub depth_stencil_attachment: bool,
|
||||
|
||||
/// Indicates that this image will only ever be used as a temporary framebuffer attachment.
|
||||
/// As soon as you leave a render pass, the content of transient images becomes undefined.
|
||||
///
|
||||
/// This is a hint to the Vulkan implementation that it may not need allocate any memory for
|
||||
/// this image if the image can live entirely in some cache.
|
||||
pub transient_attachment: bool,
|
||||
|
||||
/// Can be used as an input attachment. In other words, you can draw to it in a subpass then
|
||||
/// read from it in a following pass.
|
||||
pub input_attachment: bool,
|
||||
}
|
||||
|
||||
impl ImageUsage {
|
||||
/// Builds a `ImageUsage` with all values set to true. Note that using the returned value will
|
||||
/// produce an error because of `transient_attachment` being true.
|
||||
#[inline]
|
||||
pub fn all() -> ImageUsage {
|
||||
ImageUsage {
|
||||
transfer_source: true,
|
||||
transfer_dest: true,
|
||||
sampled: true,
|
||||
storage: true,
|
||||
color_attachment: true,
|
||||
depth_stencil_attachment: true,
|
||||
transient_attachment: true,
|
||||
input_attachment: true,
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds a `ImageUsage` with all values set to false. Useful as a default value.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use vulkano::image::ImageUsage as ImageUsage;
|
||||
///
|
||||
/// let _usage = ImageUsage {
|
||||
/// transfer_dest: true,
|
||||
/// sampled: true,
|
||||
/// .. ImageUsage::none()
|
||||
/// };
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn none() -> ImageUsage {
|
||||
ImageUsage {
|
||||
transfer_source: false,
|
||||
transfer_dest: false,
|
||||
sampled: false,
|
||||
storage: false,
|
||||
color_attachment: false,
|
||||
depth_stencil_attachment: false,
|
||||
transient_attachment: false,
|
||||
input_attachment: false,
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: these functions shouldn't be public-hidden
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
pub fn to_usage_bits(&self) -> vk::ImageUsageFlagBits {
|
||||
let mut result = 0;
|
||||
if self.transfer_source { result |= vk::IMAGE_USAGE_TRANSFER_SRC_BIT; }
|
||||
if self.transfer_dest { result |= vk::IMAGE_USAGE_TRANSFER_DST_BIT; }
|
||||
if self.sampled { result |= vk::IMAGE_USAGE_SAMPLED_BIT; }
|
||||
if self.storage { result |= vk::IMAGE_USAGE_STORAGE_BIT; }
|
||||
if self.color_attachment { result |= vk::IMAGE_USAGE_COLOR_ATTACHMENT_BIT; }
|
||||
if self.depth_stencil_attachment { result |= vk::IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; }
|
||||
if self.transient_attachment { result |= vk::IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT; }
|
||||
if self.input_attachment { result |= vk::IMAGE_USAGE_INPUT_ATTACHMENT_BIT; }
|
||||
result
|
||||
}
|
||||
|
||||
// TODO: these functions shouldn't be public-hidden
|
||||
#[inline]
|
||||
#[doc(hidden)]
|
||||
pub fn from_bits(val: u32) -> ImageUsage {
|
||||
ImageUsage {
|
||||
transfer_source: (val & vk::IMAGE_USAGE_TRANSFER_SRC_BIT) != 0,
|
||||
transfer_dest: (val & vk::IMAGE_USAGE_TRANSFER_DST_BIT) != 0,
|
||||
sampled: (val & vk::IMAGE_USAGE_SAMPLED_BIT) != 0,
|
||||
storage: (val & vk::IMAGE_USAGE_STORAGE_BIT) != 0,
|
||||
color_attachment: (val & vk::IMAGE_USAGE_COLOR_ATTACHMENT_BIT) != 0,
|
||||
depth_stencil_attachment: (val & vk::IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0,
|
||||
transient_attachment: (val & vk::IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT) != 0,
|
||||
input_attachment: (val & vk::IMAGE_USAGE_INPUT_ATTACHMENT_BIT) != 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BitOr for ImageUsage {
|
||||
type Output = Self;
|
||||
|
||||
#[inline]
|
||||
fn bitor(self, rhs: Self) -> Self {
|
||||
ImageUsage {
|
||||
transfer_source: self.transfer_source || rhs.transfer_source,
|
||||
transfer_dest: self.transfer_dest || rhs.transfer_dest,
|
||||
sampled: self.sampled || rhs.sampled,
|
||||
storage: self.storage || rhs.storage,
|
||||
color_attachment: self.color_attachment || rhs.color_attachment,
|
||||
depth_stencil_attachment: self.depth_stencil_attachment || rhs.depth_stencil_attachment,
|
||||
transient_attachment: self.transient_attachment || rhs.transient_attachment,
|
||||
input_attachment: self.input_attachment || rhs.input_attachment,
|
||||
}
|
||||
}
|
||||
}
|
@ -417,6 +417,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore] // TODO: test fails for now on Mesa+Intel
|
||||
fn oom_multi() {
|
||||
let (device, _) = gfx_dev_and_queue!();
|
||||
let mem_ty = device.physical_device().memory_types().filter(|m| !m.is_lazily_allocated())
|
||||
|
@ -36,7 +36,7 @@ impl StdHostVisibleMemoryTypePool {
|
||||
/// - Panics if the `device` and `memory_type` don't belong to the same physical device.
|
||||
///
|
||||
#[inline]
|
||||
pub fn new(device: &Arc<Device>, memory_type: MemoryType)
|
||||
pub fn new(device: Arc<Device>, memory_type: MemoryType)
|
||||
-> Arc<StdHostVisibleMemoryTypePool>
|
||||
{
|
||||
assert_eq!(&**device.physical_device().instance() as *const Instance,
|
||||
|
@ -35,7 +35,7 @@ impl StdNonHostVisibleMemoryTypePool {
|
||||
/// - Panics if the `device` and `memory_type` don't belong to the same physical device.
|
||||
///
|
||||
#[inline]
|
||||
pub fn new(device: &Arc<Device>, memory_type: MemoryType)
|
||||
pub fn new(device: Arc<Device>, memory_type: MemoryType)
|
||||
-> Arc<StdNonHostVisibleMemoryTypePool>
|
||||
{
|
||||
assert_eq!(&**device.physical_device().instance() as *const Instance,
|
||||
|
@ -38,7 +38,7 @@ pub struct StdMemoryPool {
|
||||
impl StdMemoryPool {
|
||||
/// Creates a new pool.
|
||||
#[inline]
|
||||
pub fn new(device: &Arc<Device>) -> Arc<StdMemoryPool> {
|
||||
pub fn new(device: Arc<Device>) -> Arc<StdMemoryPool> {
|
||||
let cap = device.physical_device().memory_types().len();
|
||||
let hasher = BuildHasherDefault::<FnvHasher>::default();
|
||||
|
||||
@ -76,14 +76,14 @@ unsafe impl MemoryPool for Arc<StdMemoryPool> {
|
||||
Entry::Vacant(entry) => {
|
||||
match memory_type.is_host_visible() {
|
||||
true => {
|
||||
let pool = StdHostVisibleMemoryTypePool::new(&self.device, memory_type);
|
||||
let pool = StdHostVisibleMemoryTypePool::new(self.device.clone(), memory_type);
|
||||
entry.insert(Pool::HostVisible(pool.clone()));
|
||||
let alloc = try!(StdHostVisibleMemoryTypePool::alloc(&pool, size, alignment));
|
||||
let inner = StdMemoryPoolAllocInner::HostVisible(alloc);
|
||||
Ok(StdMemoryPoolAlloc { inner: inner, pool: self.clone() })
|
||||
},
|
||||
false => {
|
||||
let pool = StdNonHostVisibleMemoryTypePool::new(&self.device, memory_type);
|
||||
let pool = StdNonHostVisibleMemoryTypePool::new(self.device.clone(), memory_type);
|
||||
entry.insert(Pool::NonHostVisible(pool.clone()));
|
||||
let alloc = try!(StdNonHostVisibleMemoryTypePool::alloc(&pool, size, alignment));
|
||||
let inner = StdMemoryPoolAllocInner::NonHostVisible(alloc);
|
||||
|
@ -76,13 +76,13 @@ impl PipelineCache {
|
||||
///
|
||||
/// let cache = if let Some(data) = data {
|
||||
/// // This is unsafe because there is no way to be sure that the file contains valid data.
|
||||
/// unsafe { PipelineCache::with_data(&device, &data).unwrap() }
|
||||
/// unsafe { PipelineCache::with_data(device.clone(), &data).unwrap() }
|
||||
/// } else {
|
||||
/// PipelineCache::empty(&device).unwrap()
|
||||
/// PipelineCache::empty(device.clone()).unwrap()
|
||||
/// };
|
||||
/// ```
|
||||
#[inline]
|
||||
pub unsafe fn with_data(device: &Arc<Device>, initial_data: &[u8])
|
||||
pub unsafe fn with_data(device: Arc<Device>, initial_data: &[u8])
|
||||
-> Result<Arc<PipelineCache>, OomError>
|
||||
{
|
||||
PipelineCache::new_impl(device, Some(initial_data))
|
||||
@ -97,15 +97,15 @@ impl PipelineCache {
|
||||
/// # use vulkano::device::Device;
|
||||
/// use vulkano::pipeline::cache::PipelineCache;
|
||||
/// # let device: Arc<Device> = return;
|
||||
/// let cache = PipelineCache::empty(&device).unwrap();
|
||||
/// let cache = PipelineCache::empty(device.clone()).unwrap();
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn empty(device: &Arc<Device>) -> Result<Arc<PipelineCache>, OomError> {
|
||||
pub fn empty(device: Arc<Device>) -> Result<Arc<PipelineCache>, OomError> {
|
||||
unsafe { PipelineCache::new_impl(device, None) }
|
||||
}
|
||||
|
||||
// Actual implementation of the constructor.
|
||||
unsafe fn new_impl(device: &Arc<Device>, initial_data: Option<&[u8]>)
|
||||
unsafe fn new_impl(device: Arc<Device>, initial_data: Option<&[u8]>)
|
||||
-> Result<Arc<PipelineCache>, OomError>
|
||||
{
|
||||
let vk = device.pointers();
|
||||
@ -232,7 +232,7 @@ mod tests {
|
||||
#[should_panic]
|
||||
fn merge_self_forbidden() {
|
||||
let (device, queue) = gfx_dev_and_queue!();
|
||||
let pipeline = PipelineCache::empty(&device).unwrap();
|
||||
let pipeline = PipelineCache::empty(device).unwrap();
|
||||
pipeline.merge(&[&pipeline]).unwrap();
|
||||
}
|
||||
}
|
||||
|
@ -56,7 +56,7 @@ struct Inner {
|
||||
|
||||
impl ComputePipeline<()> {
|
||||
/// Builds a new `ComputePipeline`.
|
||||
pub fn new<Css, Csl>(device: &Arc<Device>, shader: &ComputeShaderEntryPoint<Css, Csl>,
|
||||
pub fn new<Css, Csl>(device: Arc<Device>, shader: &ComputeShaderEntryPoint<Css, Csl>,
|
||||
specialization: &Css)
|
||||
-> Result<ComputePipeline<PipelineLayout<Csl>>, ComputePipelineCreationError>
|
||||
where Csl: PipelineLayoutDescNames + Clone,
|
||||
@ -64,7 +64,7 @@ impl ComputePipeline<()> {
|
||||
{
|
||||
let vk = device.pointers();
|
||||
|
||||
let pipeline_layout = shader.layout().clone().build(device).unwrap(); // TODO: error
|
||||
let pipeline_layout = shader.layout().clone().build(device.clone()).unwrap(); // TODO: error
|
||||
|
||||
PipelineLayoutSuperset::ensure_superset_of(pipeline_layout.desc(), shader.layout())?;
|
||||
|
||||
|
@ -33,14 +33,11 @@ use descriptor::pipeline_layout::PipelineLayoutNotSupersetError;
|
||||
use descriptor::pipeline_layout::PipelineLayoutSys;
|
||||
use descriptor::pipeline_layout::EmptyPipelineDesc;
|
||||
use format::ClearValue;
|
||||
use framebuffer::AttachmentsList;
|
||||
use framebuffer::LayoutAttachmentDescription;
|
||||
use framebuffer::LayoutPassDescription;
|
||||
use framebuffer::LayoutPassDependencyDescription;
|
||||
use framebuffer::FramebufferCreationError;
|
||||
use framebuffer::RenderPassAbstract;
|
||||
use framebuffer::RenderPassDesc;
|
||||
use framebuffer::RenderPassDescAttachmentsList;
|
||||
use framebuffer::RenderPassDescClearValues;
|
||||
use framebuffer::RenderPassSubpassInterface;
|
||||
use framebuffer::RenderPassSys;
|
||||
@ -184,7 +181,7 @@ impl<Vdef, Rp> GraphicsPipeline<Vdef, (), Rp>
|
||||
/// the other constructors for other possibilities.
|
||||
#[inline]
|
||||
pub fn new<'a, Vsp, Vi, Vo, Vl, Fs, Fi, Fo, Fl>
|
||||
(device: &Arc<Device>,
|
||||
(device: Arc<Device>,
|
||||
params: GraphicsPipelineParams<'a, Vdef, Vsp, Vi, Vo, Vl, (), (), (), EmptyPipelineDesc,
|
||||
(), (), (), EmptyPipelineDesc, (), (), (), EmptyPipelineDesc,
|
||||
Fs, Fi, Fo, Fl, Rp>)
|
||||
@ -203,7 +200,7 @@ impl<Vdef, Rp> GraphicsPipeline<Vdef, (), Rp>
|
||||
|
||||
let pl = params.vertex_shader.layout().clone()
|
||||
.union(params.fragment_shader.layout().clone())
|
||||
.build(device).unwrap(); // TODO: error
|
||||
.build(device.clone()).unwrap(); // TODO: error
|
||||
|
||||
GraphicsPipeline::new_inner::<_, _, _, _, (), (), (), EmptyPipelineDesc, (), (), (),
|
||||
EmptyPipelineDesc, (), (), (), EmptyPipelineDesc, _, _, _, _>
|
||||
@ -219,7 +216,7 @@ impl<Vdef, Rp> GraphicsPipeline<Vdef, (), Rp>
|
||||
/// shader. See the other constructors for other possibilities.
|
||||
#[inline]
|
||||
pub fn with_geometry_shader<'a, Vsp, Vi, Vo, Vl, Gsp, Gi, Go, Gl, Fs, Fi, Fo, Fl>
|
||||
(device: &Arc<Device>,
|
||||
(device: Arc<Device>,
|
||||
params: GraphicsPipelineParams<'a, Vdef, Vsp, Vi, Vo, Vl, (), (), (), EmptyPipelineDesc,
|
||||
(), (), (), EmptyPipelineDesc, Gsp, Gi, Go, Gl, Fs, Fi,
|
||||
Fo, Fl, Rp>)
|
||||
@ -252,9 +249,9 @@ impl<Vdef, Rp> GraphicsPipeline<Vdef, (), Rp>
|
||||
let pl = params.vertex_shader.layout().clone()
|
||||
.union(params.fragment_shader.layout().clone())
|
||||
.union(params.geometry_shader.as_ref().unwrap().layout().clone()) // FIXME: unwrap()
|
||||
.build(device).unwrap(); // TODO: error
|
||||
.build(device.clone()).unwrap(); // TODO: error
|
||||
|
||||
GraphicsPipeline::new_inner(device, params, pl)
|
||||
GraphicsPipeline::new_inner(device.clone(), params, pl)
|
||||
}
|
||||
|
||||
/// Builds a new graphics pipeline object with tessellation shaders.
|
||||
@ -268,7 +265,7 @@ impl<Vdef, Rp> GraphicsPipeline<Vdef, (), Rp>
|
||||
#[inline]
|
||||
pub fn with_tessellation<'a, Vsp, Vi, Vo, Vl, Tcs, Tci, Tco, Tcl, Tes, Tei, Teo, Tel, Fs, Fi,
|
||||
Fo, Fl>
|
||||
(device: &Arc<Device>,
|
||||
(device: Arc<Device>,
|
||||
params: GraphicsPipelineParams<'a, Vdef, Vsp, Vi, Vo, Vl, Tcs, Tci, Tco, Tcl, Tes,
|
||||
Tei, Teo, Tel, (), (), (), EmptyPipelineDesc, Fs, Fi,
|
||||
Fo, Fl, Rp>)
|
||||
@ -308,7 +305,7 @@ impl<Vdef, Rp> GraphicsPipeline<Vdef, (), Rp>
|
||||
.union(params.fragment_shader.layout().clone())
|
||||
.union(params.tessellation.as_ref().unwrap().tessellation_control_shader.layout().clone()) // FIXME: unwrap()
|
||||
.union(params.tessellation.as_ref().unwrap().tessellation_evaluation_shader.layout().clone()) // FIXME: unwrap()
|
||||
.build(device).unwrap(); // TODO: error
|
||||
.build(device.clone()).unwrap(); // TODO: error
|
||||
|
||||
GraphicsPipeline::new_inner(device, params, pl)
|
||||
}
|
||||
@ -324,7 +321,7 @@ impl<Vdef, Rp> GraphicsPipeline<Vdef, (), Rp>
|
||||
#[inline]
|
||||
pub fn with_tessellation_and_geometry<'a, Vsp, Vi, Vo, Vl, Tcs, Tci, Tco, Tcl, Tes, Tei, Teo, Tel, Gsp, Gi,
|
||||
Go, Gl, Fs, Fi, Fo, Fl>
|
||||
(device: &Arc<Device>,
|
||||
(device: Arc<Device>,
|
||||
params: GraphicsPipelineParams<'a, Vdef, Vsp, Vi, Vo, Vl, Tcs, Tci, Tco, Tcl, Tes,
|
||||
Tei, Teo, Tel, Gsp, Gi, Go, Gl, Fs, Fi,
|
||||
Fo, Fl, Rp>)
|
||||
@ -377,7 +374,7 @@ impl<Vdef, Rp> GraphicsPipeline<Vdef, (), Rp>
|
||||
.union(params.tessellation.as_ref().unwrap().tessellation_control_shader.layout().clone()) // FIXME: unwrap()
|
||||
.union(params.tessellation.as_ref().unwrap().tessellation_evaluation_shader.layout().clone()) // FIXME: unwrap()
|
||||
.union(params.geometry_shader.as_ref().unwrap().layout().clone()) // FIXME: unwrap()
|
||||
.build(device).unwrap(); // TODO: error
|
||||
.build(device.clone()).unwrap(); // TODO: error
|
||||
|
||||
GraphicsPipeline::new_inner(device, params, pl)
|
||||
}
|
||||
@ -388,7 +385,7 @@ impl<Vdef, L, Rp> GraphicsPipeline<Vdef, L, Rp>
|
||||
{
|
||||
fn new_inner<'a, Vsp, Vi, Vo, Vl, Tcs, Tci, Tco, Tcl, Tes, Tei, Teo, Tel, Gsp, Gi, Go, Gl, Fs,
|
||||
Fi, Fo, Fl>
|
||||
(device: &Arc<Device>,
|
||||
(device: Arc<Device>,
|
||||
params: GraphicsPipelineParams<'a, Vdef, Vsp, Vi, Vo, Vl, Tcs, Tci, Tco, Tcl, Tes,
|
||||
Tei, Teo, Tel, Gsp, Gi, Go, Gl, Fs, Fi, Fo, Fl, Rp>,
|
||||
pipeline_layout: L)
|
||||
@ -1153,8 +1150,8 @@ unsafe impl<Mv, L, Rp> RenderPassDesc for GraphicsPipeline<Mv, L, Rp>
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn attachment(&self, num: usize) -> Option<LayoutAttachmentDescription> {
|
||||
self.render_pass.attachment(num)
|
||||
fn attachment_desc(&self, num: usize) -> Option<LayoutAttachmentDescription> {
|
||||
self.render_pass.attachment_desc(num)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@ -1163,8 +1160,8 @@ unsafe impl<Mv, L, Rp> RenderPassDesc for GraphicsPipeline<Mv, L, Rp>
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn subpass(&self, num: usize) -> Option<LayoutPassDescription> {
|
||||
self.render_pass.subpass(num)
|
||||
fn subpass_desc(&self, num: usize) -> Option<LayoutPassDescription> {
|
||||
self.render_pass.subpass_desc(num)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@ -1173,17 +1170,8 @@ unsafe impl<Mv, L, Rp> RenderPassDesc for GraphicsPipeline<Mv, L, Rp>
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn dependency(&self, num: usize) -> Option<LayoutPassDependencyDescription> {
|
||||
self.render_pass.dependency(num)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<A, Mv, L, Rp> RenderPassDescAttachmentsList<A> for GraphicsPipeline<Mv, L, Rp>
|
||||
where Rp: RenderPassDescAttachmentsList<A>
|
||||
{
|
||||
#[inline]
|
||||
fn check_attachments_list(&self, atch: A) -> Result<Box<AttachmentsList + Send + Sync>, FramebufferCreationError> {
|
||||
self.render_pass.check_attachments_list(atch)
|
||||
fn dependency_desc(&self, num: usize) -> Option<LayoutPassDependencyDescription> {
|
||||
self.render_pass.dependency_desc(num)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,664 +0,0 @@
|
||||
// Copyright (c) 2016 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.
|
||||
|
||||
//! # Vertex sources definition
|
||||
//!
|
||||
//! When you create a graphics pipeline object, you need to pass an object which indicates the
|
||||
//! layout of the vertex buffer(s) that will serve as input for the vertex shader. This is done
|
||||
//! by passing an implementation of the `VertexDefinition` trait.
|
||||
//!
|
||||
//! In addition to this, the object that you pass when you create the graphics pipeline must also
|
||||
//! implement the `VertexSource` trait. This trait has a template parameter which corresponds to the
|
||||
//! list of vertex buffers.
|
||||
//!
|
||||
//! The vulkano library provides some structs that already implement these traits.
|
||||
//! The most common situation is a single vertex buffer and no instancing, in which case you can
|
||||
//! pass a `SingleBufferDefinition` when you create the pipeline.
|
||||
//!
|
||||
//! # Implementing `Vertex`
|
||||
//!
|
||||
//! The implementations of the `VertexDefinition` trait that are provided by vulkano (like
|
||||
//! `SingleBufferDefinition`) require you to use a buffer whose content is `[V]` where `V`
|
||||
//! implements the `Vertex` trait.
|
||||
//!
|
||||
//! The `Vertex` trait is unsafe, but can be implemented on a struct with the `impl_vertex!`
|
||||
//! macro.
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! ```ignore // TODO:
|
||||
//! # #[macro_use] extern crate vulkano
|
||||
//! # fn main() {
|
||||
//! # use std::sync::Arc;
|
||||
//! # use vulkano::device::Device;
|
||||
//! # use vulkano::device::Queue;
|
||||
//! use vulkano::buffer::BufferAccess;
|
||||
//! use vulkano::buffer::Usage as BufferUsage;
|
||||
//! use vulkano::memory::HostVisible;
|
||||
//! use vulkano::pipeline::vertex::;
|
||||
//! # let device: Arc<Device> = return;
|
||||
//! # let queue: Arc<Queue> = return;
|
||||
//!
|
||||
//! struct Vertex {
|
||||
//! position: [f32; 2]
|
||||
//! }
|
||||
//!
|
||||
//! impl_vertex!(Vertex, position);
|
||||
//!
|
||||
//! let usage = BufferUsage {
|
||||
//! vertex_buffer: true,
|
||||
//! .. BufferUsage::none()
|
||||
//! };
|
||||
//!
|
||||
//! let vertex_buffer = BufferAccess::<[Vertex], _>::array(&device, 128, &usage, HostVisible, &queue)
|
||||
//! .expect("failed to create buffer");
|
||||
//!
|
||||
//! // TODO: finish example
|
||||
//! # }
|
||||
//! ```
|
||||
|
||||
use std::error;
|
||||
use std::fmt;
|
||||
use std::marker::PhantomData;
|
||||
use std::mem;
|
||||
use std::option::IntoIter as OptionIntoIter;
|
||||
use std::sync::Arc;
|
||||
use std::vec::IntoIter as VecIntoIter;
|
||||
|
||||
use buffer::BufferAccess;
|
||||
use buffer::BufferInner;
|
||||
use buffer::TypedBufferAccess;
|
||||
use format::Format;
|
||||
use pipeline::shader::ShaderInterfaceDef;
|
||||
use SafeDeref;
|
||||
use vk;
|
||||
|
||||
/// How the vertex source should be unrolled.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[repr(u32)]
|
||||
pub enum InputRate {
|
||||
/// Each element of the source corresponds to a vertex.
|
||||
Vertex = vk::VERTEX_INPUT_RATE_VERTEX,
|
||||
/// Each element of the source corresponds to an instance.
|
||||
Instance = vk::VERTEX_INPUT_RATE_INSTANCE,
|
||||
}
|
||||
|
||||
/// Describes an individual `Vertex`. In other words a collection of attributes that can be read
|
||||
/// from a vertex shader.
|
||||
///
|
||||
/// At this stage, the vertex is in a "raw" format. For example a `[f32; 4]` can match both a
|
||||
/// `vec4` or a `float[4]`. The way the things are binded depends on the shader.
|
||||
pub unsafe trait Vertex: 'static + Send + Sync {
|
||||
/// Returns the characteristics of a vertex member by its name.
|
||||
fn member(name: &str) -> Option<VertexMemberInfo>;
|
||||
}
|
||||
|
||||
unsafe impl Vertex for () {
|
||||
#[inline]
|
||||
fn member(_: &str) -> Option<VertexMemberInfo> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Information about a member of a vertex struct.
|
||||
pub struct VertexMemberInfo {
|
||||
/// Offset of the member in bytes from the start of the struct.
|
||||
pub offset: usize,
|
||||
/// Type of data. This is used to check that the interface is matching.
|
||||
pub ty: VertexMemberTy,
|
||||
/// Number of consecutive elements of that type.
|
||||
pub array_size: usize,
|
||||
}
|
||||
|
||||
/// Type of a member of a vertex struct.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum VertexMemberTy {
|
||||
I8,
|
||||
U8,
|
||||
I16,
|
||||
U16,
|
||||
I32,
|
||||
U32,
|
||||
F32,
|
||||
F64,
|
||||
}
|
||||
|
||||
impl VertexMemberTy {
|
||||
/// Returns true if a combination of `(type, array_size)` matches a format.
|
||||
#[inline]
|
||||
pub fn matches(&self, array_size: usize, format: Format, num_locs: u32) -> bool {
|
||||
// TODO: implement correctly
|
||||
let my_size = match *self {
|
||||
VertexMemberTy::I8 => 1,
|
||||
VertexMemberTy::U8 => 1,
|
||||
VertexMemberTy::I16 => 2,
|
||||
VertexMemberTy::U16 => 2,
|
||||
VertexMemberTy::I32 => 4,
|
||||
VertexMemberTy::U32 => 4,
|
||||
VertexMemberTy::F32 => 4,
|
||||
VertexMemberTy::F64 => 8,
|
||||
};
|
||||
|
||||
let format_size = match format.size() {
|
||||
None => return false,
|
||||
Some(s) => s,
|
||||
};
|
||||
|
||||
array_size * my_size == format_size * num_locs as usize
|
||||
}
|
||||
}
|
||||
|
||||
/// Information about a single attribute within a vertex.
|
||||
/// TODO: change that API
|
||||
pub struct AttributeInfo {
|
||||
/// Number of bytes between the start of a vertex and the location of attribute.
|
||||
pub offset: usize,
|
||||
/// VertexMember type of the attribute.
|
||||
pub format: Format,
|
||||
}
|
||||
|
||||
/// Trait for types that describe the definition of the vertex input used by a graphics pipeline.
|
||||
pub unsafe trait VertexDefinition<I>: VertexSource<Vec<Arc<BufferAccess + Send + Sync>>> {
|
||||
/// Iterator that returns the offset, the stride (in bytes) and input rate of each buffer.
|
||||
type BuffersIter: ExactSizeIterator<Item = (u32, usize, InputRate)>;
|
||||
/// Iterator that returns the attribute location, buffer id, and infos.
|
||||
type AttribsIter: ExactSizeIterator<Item = (u32, u32, AttributeInfo)>;
|
||||
|
||||
/// Builds the vertex definition to use to link this definition to a vertex shader's input
|
||||
/// interface.
|
||||
fn definition(&self, interface: &I) -> Result<(Self::BuffersIter, Self::AttribsIter),
|
||||
IncompatibleVertexDefinitionError>;
|
||||
}
|
||||
|
||||
unsafe impl<I, T> VertexDefinition<I> for T where T: SafeDeref, T::Target: VertexDefinition<I> {
|
||||
type BuffersIter = <T::Target as VertexDefinition<I>>::BuffersIter;
|
||||
type AttribsIter = <T::Target as VertexDefinition<I>>::AttribsIter;
|
||||
|
||||
#[inline]
|
||||
fn definition(&self, interface: &I) -> Result<(Self::BuffersIter, Self::AttribsIter),
|
||||
IncompatibleVertexDefinitionError>
|
||||
{
|
||||
(**self).definition(interface)
|
||||
}
|
||||
}
|
||||
|
||||
/// Error that can happen when the vertex definition doesn't match the input of the vertex shader.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum IncompatibleVertexDefinitionError {
|
||||
/// An attribute of the vertex shader is missing in the vertex source.
|
||||
MissingAttribute {
|
||||
/// Name of the missing attribute.
|
||||
attribute: String,
|
||||
},
|
||||
|
||||
/// The format of an attribute does not match.
|
||||
FormatMismatch {
|
||||
/// Name of the attribute.
|
||||
attribute: String,
|
||||
/// The format in the vertex shader.
|
||||
shader: (Format, usize),
|
||||
/// The format in the vertex definition.
|
||||
definition: (VertexMemberTy, usize),
|
||||
},
|
||||
}
|
||||
|
||||
impl error::Error for IncompatibleVertexDefinitionError {
|
||||
#[inline]
|
||||
fn description(&self) -> &str {
|
||||
match *self {
|
||||
IncompatibleVertexDefinitionError::MissingAttribute { .. } => "an attribute is missing",
|
||||
IncompatibleVertexDefinitionError::FormatMismatch { .. } => {
|
||||
"the format of an attribute does not match"
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for IncompatibleVertexDefinitionError {
|
||||
#[inline]
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
write!(fmt, "{}", error::Error::description(self))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Extension trait of `VertexDefinition`. The `L` parameter is an acceptable vertex source for this
|
||||
/// vertex definition.
|
||||
pub unsafe trait VertexSource<L> {
|
||||
/// Checks and returns the list of buffers with offsets, number of vertices and number of instances.
|
||||
// TODO: return error if problem
|
||||
// TODO: better than a Vec
|
||||
// TODO: return a struct instead
|
||||
fn decode<'l>(&self, &'l L) -> (Vec<BufferInner<'l>>, usize, usize);
|
||||
}
|
||||
|
||||
unsafe impl<L, T> VertexSource<L> for T where T: SafeDeref, T::Target: VertexSource<L> {
|
||||
#[inline]
|
||||
fn decode<'l>(&self, list: &'l L) -> (Vec<BufferInner<'l>>, usize, usize) {
|
||||
(**self).decode(list)
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation of `VertexDefinition` for a single vertex buffer.
|
||||
pub struct SingleBufferDefinition<T>(pub PhantomData<T>);
|
||||
|
||||
impl<T> SingleBufferDefinition<T> {
|
||||
#[inline]
|
||||
pub fn new() -> SingleBufferDefinition<T> { SingleBufferDefinition(PhantomData) }
|
||||
}
|
||||
|
||||
unsafe impl<T, I> VertexDefinition<I> for SingleBufferDefinition<T>
|
||||
where T: Vertex, I: ShaderInterfaceDef
|
||||
{
|
||||
type BuffersIter = OptionIntoIter<(u32, usize, InputRate)>;
|
||||
type AttribsIter = VecIntoIter<(u32, u32, AttributeInfo)>;
|
||||
|
||||
fn definition(&self, interface: &I) -> Result<(Self::BuffersIter, Self::AttribsIter),
|
||||
IncompatibleVertexDefinitionError>
|
||||
{
|
||||
let attrib = {
|
||||
let mut attribs = Vec::with_capacity(interface.elements().len());
|
||||
for e in interface.elements() {
|
||||
let name = e.name.as_ref().unwrap();
|
||||
|
||||
let infos = match <T as Vertex>::member(name) {
|
||||
Some(m) => m,
|
||||
None => return Err(IncompatibleVertexDefinitionError::MissingAttribute {
|
||||
attribute: name.clone().into_owned()
|
||||
})
|
||||
};
|
||||
|
||||
if !infos.ty.matches(infos.array_size, e.format,
|
||||
e.location.end - e.location.start)
|
||||
{
|
||||
return Err(IncompatibleVertexDefinitionError::FormatMismatch {
|
||||
attribute: name.clone().into_owned(),
|
||||
shader: (e.format, (e.location.end - e.location.start) as usize),
|
||||
definition: (infos.ty, infos.array_size),
|
||||
})
|
||||
}
|
||||
|
||||
let mut offset = infos.offset;
|
||||
for loc in e.location.clone() {
|
||||
attribs.push((loc, 0, AttributeInfo { offset: offset, format: e.format }));
|
||||
offset += e.format.size().unwrap();
|
||||
}
|
||||
}
|
||||
attribs
|
||||
}.into_iter(); // TODO: meh
|
||||
|
||||
let buffers = Some((0, mem::size_of::<T>(), InputRate::Vertex)).into_iter();
|
||||
Ok((buffers, attrib))
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<V> VertexSource<Vec<Arc<BufferAccess + Send + Sync>>> for SingleBufferDefinition<V>
|
||||
where V: Vertex
|
||||
{
|
||||
#[inline]
|
||||
fn decode<'l>(&self, source: &'l Vec<Arc<BufferAccess + Send + Sync>>) -> (Vec<BufferInner<'l>>, usize, usize) {
|
||||
// FIXME: safety
|
||||
assert_eq!(source.len(), 1);
|
||||
let len = source[0].size() / mem::size_of::<V>();
|
||||
(vec![source[0].inner()], len, 1)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a, B, V> VertexSource<B> for SingleBufferDefinition<V>
|
||||
where B: TypedBufferAccess<Content = [V]>, V: Vertex
|
||||
{
|
||||
#[inline]
|
||||
fn decode<'l>(&self, source: &'l B) -> (Vec<BufferInner<'l>>, usize, usize) {
|
||||
(vec![source.inner()], source.len(), 1)
|
||||
}
|
||||
}
|
||||
|
||||
/// Unstable.
|
||||
// TODO: shouldn't be just `Two` but `Multi`
|
||||
pub struct TwoBuffersDefinition<T, U>(pub PhantomData<(T, U)>);
|
||||
|
||||
impl<T, U> TwoBuffersDefinition<T, U> {
|
||||
#[inline]
|
||||
pub fn new() -> TwoBuffersDefinition<T, U> { TwoBuffersDefinition(PhantomData) }
|
||||
}
|
||||
|
||||
unsafe impl<T, U, I> VertexDefinition<I> for TwoBuffersDefinition<T, U>
|
||||
where T: Vertex, U: Vertex, I: ShaderInterfaceDef
|
||||
{
|
||||
type BuffersIter = VecIntoIter<(u32, usize, InputRate)>;
|
||||
type AttribsIter = VecIntoIter<(u32, u32, AttributeInfo)>;
|
||||
|
||||
fn definition(&self, interface: &I) -> Result<(Self::BuffersIter, Self::AttribsIter),
|
||||
IncompatibleVertexDefinitionError>
|
||||
{
|
||||
let attrib = {
|
||||
let mut attribs = Vec::with_capacity(interface.elements().len());
|
||||
for e in interface.elements() {
|
||||
let name = e.name.as_ref().unwrap();
|
||||
|
||||
let (infos, buf_offset) = if let Some(infos) = <T as Vertex>::member(name) {
|
||||
(infos, 0)
|
||||
} else if let Some(infos) = <U as Vertex>::member(name) {
|
||||
(infos, 1)
|
||||
} else {
|
||||
return Err(IncompatibleVertexDefinitionError::MissingAttribute {
|
||||
attribute: name.clone().into_owned()
|
||||
});
|
||||
};
|
||||
|
||||
if !infos.ty.matches(infos.array_size, e.format,
|
||||
e.location.end - e.location.start)
|
||||
{
|
||||
return Err(IncompatibleVertexDefinitionError::FormatMismatch {
|
||||
attribute: name.clone().into_owned(),
|
||||
shader: (e.format, (e.location.end - e.location.start) as usize),
|
||||
definition: (infos.ty, infos.array_size),
|
||||
})
|
||||
}
|
||||
|
||||
let mut offset = infos.offset;
|
||||
for loc in e.location.clone() {
|
||||
attribs.push((loc, buf_offset, AttributeInfo { offset: offset, format: e.format }));
|
||||
offset += e.format.size().unwrap();
|
||||
}
|
||||
}
|
||||
attribs
|
||||
}.into_iter(); // TODO: meh
|
||||
|
||||
let buffers = vec![
|
||||
(0, mem::size_of::<T>(), InputRate::Vertex),
|
||||
(1, mem::size_of::<U>(), InputRate::Vertex)
|
||||
].into_iter();
|
||||
|
||||
Ok((buffers, attrib))
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T, U> VertexSource<Vec<Arc<BufferAccess + Send + Sync>>> for TwoBuffersDefinition<T, U>
|
||||
where T: Vertex, U: Vertex
|
||||
{
|
||||
#[inline]
|
||||
fn decode<'l>(&self, source: &'l Vec<Arc<BufferAccess + Send + Sync>>) -> (Vec<BufferInner<'l>>, usize, usize) {
|
||||
unimplemented!() // FIXME: implement
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a, T, U, Bt, Bu> VertexSource<(Bt, Bu)> for TwoBuffersDefinition<T, U>
|
||||
where T: Vertex, Bt: TypedBufferAccess<Content = [T]>,
|
||||
U: Vertex, Bu: TypedBufferAccess<Content = [U]>
|
||||
{
|
||||
#[inline]
|
||||
fn decode<'l>(&self, source: &'l (Bt, Bu)) -> (Vec<BufferInner<'l>>, usize, usize) {
|
||||
let vertices = [source.0.len(), source.1.len()].iter().cloned().min().unwrap();
|
||||
(vec![source.0.inner(), source.1.inner()], vertices, 1)
|
||||
}
|
||||
}
|
||||
|
||||
/// Unstable.
|
||||
// TODO: bad way to do things
|
||||
pub struct OneVertexOneInstanceDefinition<T, U>(pub PhantomData<(T, U)>);
|
||||
|
||||
impl<T, U> OneVertexOneInstanceDefinition<T, U> {
|
||||
#[inline]
|
||||
pub fn new() -> OneVertexOneInstanceDefinition<T, U> { OneVertexOneInstanceDefinition(PhantomData) }
|
||||
}
|
||||
|
||||
unsafe impl<T, U, I> VertexDefinition<I> for OneVertexOneInstanceDefinition<T, U>
|
||||
where T: Vertex, U: Vertex, I: ShaderInterfaceDef
|
||||
{
|
||||
type BuffersIter = VecIntoIter<(u32, usize, InputRate)>;
|
||||
type AttribsIter = VecIntoIter<(u32, u32, AttributeInfo)>;
|
||||
|
||||
fn definition(&self, interface: &I) -> Result<(Self::BuffersIter, Self::AttribsIter),
|
||||
IncompatibleVertexDefinitionError>
|
||||
{
|
||||
let attrib = {
|
||||
let mut attribs = Vec::with_capacity(interface.elements().len());
|
||||
for e in interface.elements() {
|
||||
let name = e.name.as_ref().unwrap();
|
||||
|
||||
let (infos, buf_offset) = if let Some(infos) = <T as Vertex>::member(name) {
|
||||
(infos, 0)
|
||||
} else if let Some(infos) = <U as Vertex>::member(name) {
|
||||
(infos, 1)
|
||||
} else {
|
||||
return Err(IncompatibleVertexDefinitionError::MissingAttribute {
|
||||
attribute: name.clone().into_owned()
|
||||
});
|
||||
};
|
||||
|
||||
if !infos.ty.matches(infos.array_size, e.format,
|
||||
e.location.end - e.location.start)
|
||||
{
|
||||
return Err(IncompatibleVertexDefinitionError::FormatMismatch {
|
||||
attribute: name.clone().into_owned(),
|
||||
shader: (e.format, (e.location.end - e.location.start) as usize),
|
||||
definition: (infos.ty, infos.array_size),
|
||||
})
|
||||
}
|
||||
|
||||
let mut offset = infos.offset;
|
||||
for loc in e.location.clone() {
|
||||
attribs.push((loc, buf_offset, AttributeInfo { offset: offset, format: e.format }));
|
||||
offset += e.format.size().unwrap();
|
||||
}
|
||||
}
|
||||
attribs
|
||||
}.into_iter(); // TODO: meh
|
||||
|
||||
let buffers = vec![
|
||||
(0, mem::size_of::<T>(), InputRate::Vertex),
|
||||
(1, mem::size_of::<U>(), InputRate::Instance)
|
||||
].into_iter();
|
||||
|
||||
Ok((buffers, attrib))
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T, U> VertexSource<Vec<Arc<BufferAccess + Send + Sync>>> for OneVertexOneInstanceDefinition<T, U>
|
||||
where T: Vertex, U: Vertex
|
||||
{
|
||||
#[inline]
|
||||
fn decode<'l>(&self, source: &'l Vec<Arc<BufferAccess + Send + Sync>>) -> (Vec<BufferInner<'l>>, usize, usize) {
|
||||
// FIXME: safety
|
||||
assert_eq!(source.len(), 2);
|
||||
let len = source[0].size() / mem::size_of::<T>();
|
||||
let inst = source[0].size() / mem::size_of::<U>();
|
||||
(vec![source[0].inner(), source[1].inner()], len, inst)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a, T, U, Bt, Bu> VertexSource<(Bt, Bu)> for OneVertexOneInstanceDefinition<T, U>
|
||||
where T: Vertex, Bt: TypedBufferAccess<Content = [T]>,
|
||||
U: Vertex, Bu: TypedBufferAccess<Content = [U]>
|
||||
{
|
||||
#[inline]
|
||||
fn decode<'l>(&self, source: &'l (Bt, Bu)) -> (Vec<BufferInner<'l>>, usize, usize) {
|
||||
(vec![source.0.inner(), source.1.inner()], source.0.len(), source.1.len())
|
||||
}
|
||||
}
|
||||
|
||||
/// Implements the `Vertex` trait on a struct.
|
||||
// TODO: add example
|
||||
#[macro_export]
|
||||
macro_rules! impl_vertex {
|
||||
($out:ident $(, $member:ident)*) => (
|
||||
#[allow(unsafe_code)]
|
||||
unsafe impl $crate::pipeline::vertex::Vertex for $out {
|
||||
#[inline(always)]
|
||||
fn member(name: &str) -> Option<$crate::pipeline::vertex::VertexMemberInfo> {
|
||||
use std::ptr;
|
||||
#[allow(unused_imports)]
|
||||
use $crate::format::Format;
|
||||
use $crate::pipeline::vertex::VertexMemberInfo;
|
||||
use $crate::pipeline::vertex::VertexMemberTy;
|
||||
use $crate::pipeline::vertex::VertexMember;
|
||||
|
||||
$(
|
||||
if name == stringify!($member) {
|
||||
let (ty, array_size) = unsafe {
|
||||
#[inline] fn f<T: VertexMember>(_: &T) -> (VertexMemberTy, usize)
|
||||
{ T::format() }
|
||||
let dummy: *const $out = ptr::null();
|
||||
f(&(&*dummy).$member)
|
||||
};
|
||||
|
||||
return Some(VertexMemberInfo {
|
||||
offset: unsafe {
|
||||
let dummy: *const $out = ptr::null();
|
||||
let member = (&(&*dummy).$member) as *const _;
|
||||
member as usize
|
||||
},
|
||||
|
||||
ty: ty,
|
||||
array_size: array_size,
|
||||
});
|
||||
}
|
||||
)*
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/// Trait for data types that can be used as vertex members. Used by the `impl_vertex!` macro.
|
||||
pub unsafe trait VertexMember {
|
||||
/// Returns the format and array size of the member.
|
||||
fn format() -> (VertexMemberTy, usize);
|
||||
}
|
||||
|
||||
unsafe impl VertexMember for i8 {
|
||||
#[inline]
|
||||
fn format() -> (VertexMemberTy, usize) {
|
||||
(VertexMemberTy::I8, 1)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl VertexMember for u8 {
|
||||
#[inline]
|
||||
fn format() -> (VertexMemberTy, usize) {
|
||||
(VertexMemberTy::U8, 1)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl VertexMember for i16 {
|
||||
#[inline]
|
||||
fn format() -> (VertexMemberTy, usize) {
|
||||
(VertexMemberTy::I16, 1)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl VertexMember for u16 {
|
||||
#[inline]
|
||||
fn format() -> (VertexMemberTy, usize) {
|
||||
(VertexMemberTy::U16, 1)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl VertexMember for i32 {
|
||||
#[inline]
|
||||
fn format() -> (VertexMemberTy, usize) {
|
||||
(VertexMemberTy::I32, 1)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl VertexMember for u32 {
|
||||
#[inline]
|
||||
fn format() -> (VertexMemberTy, usize) {
|
||||
(VertexMemberTy::U32, 1)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl VertexMember for f32 {
|
||||
#[inline]
|
||||
fn format() -> (VertexMemberTy, usize) {
|
||||
(VertexMemberTy::F32, 1)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl VertexMember for f64 {
|
||||
#[inline]
|
||||
fn format() -> (VertexMemberTy, usize) {
|
||||
(VertexMemberTy::F64, 1)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T> VertexMember for (T,)
|
||||
where T: VertexMember
|
||||
{
|
||||
#[inline]
|
||||
fn format() -> (VertexMemberTy, usize) {
|
||||
<T as VertexMember>::format()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T> VertexMember for (T, T)
|
||||
where T: VertexMember
|
||||
{
|
||||
#[inline]
|
||||
fn format() -> (VertexMemberTy, usize) {
|
||||
let (ty, sz) = <T as VertexMember>::format();
|
||||
(ty, sz * 2)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T> VertexMember for (T, T, T)
|
||||
where T: VertexMember
|
||||
{
|
||||
#[inline]
|
||||
fn format() -> (VertexMemberTy, usize) {
|
||||
let (ty, sz) = <T as VertexMember>::format();
|
||||
(ty, sz * 3)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T> VertexMember for (T, T, T, T)
|
||||
where T: VertexMember
|
||||
{
|
||||
#[inline]
|
||||
fn format() -> (VertexMemberTy, usize) {
|
||||
let (ty, sz) = <T as VertexMember>::format();
|
||||
(ty, sz * 4)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_vm_array {
|
||||
($sz:expr) => (
|
||||
unsafe impl<T> VertexMember for [T; $sz]
|
||||
where T: VertexMember
|
||||
{
|
||||
#[inline]
|
||||
fn format() -> (VertexMemberTy, usize) {
|
||||
let (ty, sz) = <T as VertexMember>::format();
|
||||
(ty, sz * $sz)
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
impl_vm_array!(1);
|
||||
impl_vm_array!(2);
|
||||
impl_vm_array!(3);
|
||||
impl_vm_array!(4);
|
||||
impl_vm_array!(5);
|
||||
impl_vm_array!(6);
|
||||
impl_vm_array!(7);
|
||||
impl_vm_array!(8);
|
||||
impl_vm_array!(9);
|
||||
impl_vm_array!(10);
|
||||
impl_vm_array!(11);
|
||||
impl_vm_array!(12);
|
||||
impl_vm_array!(13);
|
||||
impl_vm_array!(14);
|
||||
impl_vm_array!(15);
|
||||
impl_vm_array!(16);
|
||||
impl_vm_array!(32);
|
||||
impl_vm_array!(64);
|
120
vulkano/src/pipeline/vertex/definition.rs
Normal file
120
vulkano/src/pipeline/vertex/definition.rs
Normal file
@ -0,0 +1,120 @@
|
||||
// Copyright (c) 2016 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.
|
||||
|
||||
use std::error;
|
||||
use std::fmt;
|
||||
use std::sync::Arc;
|
||||
|
||||
use buffer::BufferAccess;
|
||||
use buffer::BufferInner;
|
||||
use format::Format;
|
||||
use pipeline::vertex::VertexMemberTy;
|
||||
use SafeDeref;
|
||||
use vk;
|
||||
|
||||
/// Trait for types that describe the definition of the vertex input used by a graphics pipeline.
|
||||
pub unsafe trait VertexDefinition<I>: VertexSource<Vec<Arc<BufferAccess + Send + Sync>>> {
|
||||
/// Iterator that returns the offset, the stride (in bytes) and input rate of each buffer.
|
||||
type BuffersIter: ExactSizeIterator<Item = (u32, usize, InputRate)>;
|
||||
/// Iterator that returns the attribute location, buffer id, and infos.
|
||||
type AttribsIter: ExactSizeIterator<Item = (u32, u32, AttributeInfo)>;
|
||||
|
||||
/// Builds the vertex definition to use to link this definition to a vertex shader's input
|
||||
/// interface.
|
||||
fn definition(&self, interface: &I) -> Result<(Self::BuffersIter, Self::AttribsIter),
|
||||
IncompatibleVertexDefinitionError>;
|
||||
}
|
||||
|
||||
unsafe impl<I, T> VertexDefinition<I> for T where T: SafeDeref, T::Target: VertexDefinition<I> {
|
||||
type BuffersIter = <T::Target as VertexDefinition<I>>::BuffersIter;
|
||||
type AttribsIter = <T::Target as VertexDefinition<I>>::AttribsIter;
|
||||
|
||||
#[inline]
|
||||
fn definition(&self, interface: &I) -> Result<(Self::BuffersIter, Self::AttribsIter),
|
||||
IncompatibleVertexDefinitionError>
|
||||
{
|
||||
(**self).definition(interface)
|
||||
}
|
||||
}
|
||||
|
||||
/// How the vertex source should be unrolled.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[repr(u32)]
|
||||
pub enum InputRate {
|
||||
/// Each element of the source corresponds to a vertex.
|
||||
Vertex = vk::VERTEX_INPUT_RATE_VERTEX,
|
||||
/// Each element of the source corresponds to an instance.
|
||||
Instance = vk::VERTEX_INPUT_RATE_INSTANCE,
|
||||
}
|
||||
|
||||
/// Information about a single attribute within a vertex.
|
||||
/// TODO: change that API
|
||||
pub struct AttributeInfo {
|
||||
/// Number of bytes between the start of a vertex and the location of attribute.
|
||||
pub offset: usize,
|
||||
/// VertexMember type of the attribute.
|
||||
pub format: Format,
|
||||
}
|
||||
|
||||
/// Error that can happen when the vertex definition doesn't match the input of the vertex shader.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum IncompatibleVertexDefinitionError {
|
||||
/// An attribute of the vertex shader is missing in the vertex source.
|
||||
MissingAttribute {
|
||||
/// Name of the missing attribute.
|
||||
attribute: String,
|
||||
},
|
||||
|
||||
/// The format of an attribute does not match.
|
||||
FormatMismatch {
|
||||
/// Name of the attribute.
|
||||
attribute: String,
|
||||
/// The format in the vertex shader.
|
||||
shader: (Format, usize),
|
||||
/// The format in the vertex definition.
|
||||
definition: (VertexMemberTy, usize),
|
||||
},
|
||||
}
|
||||
|
||||
impl error::Error for IncompatibleVertexDefinitionError {
|
||||
#[inline]
|
||||
fn description(&self) -> &str {
|
||||
match *self {
|
||||
IncompatibleVertexDefinitionError::MissingAttribute { .. } => "an attribute is missing",
|
||||
IncompatibleVertexDefinitionError::FormatMismatch { .. } => {
|
||||
"the format of an attribute does not match"
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for IncompatibleVertexDefinitionError {
|
||||
#[inline]
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
write!(fmt, "{}", error::Error::description(self))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Extension trait of `VertexDefinition`. The `L` parameter is an acceptable vertex source for this
|
||||
/// vertex definition.
|
||||
pub unsafe trait VertexSource<L> {
|
||||
/// Checks and returns the list of buffers with offsets, number of vertices and number of instances.
|
||||
// TODO: return error if problem
|
||||
// TODO: better than a Vec
|
||||
// TODO: return a struct instead
|
||||
fn decode<'l>(&self, &'l L) -> (Vec<BufferInner<'l>>, usize, usize);
|
||||
}
|
||||
|
||||
unsafe impl<L, T> VertexSource<L> for T where T: SafeDeref, T::Target: VertexSource<L> {
|
||||
#[inline]
|
||||
fn decode<'l>(&self, list: &'l L) -> (Vec<BufferInner<'l>>, usize, usize) {
|
||||
(**self).decode(list)
|
||||
}
|
||||
}
|
188
vulkano/src/pipeline/vertex/impl_vertex.rs
Normal file
188
vulkano/src/pipeline/vertex/impl_vertex.rs
Normal file
@ -0,0 +1,188 @@
|
||||
// Copyright (c) 2017 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.
|
||||
|
||||
use pipeline::vertex::VertexMemberTy;
|
||||
|
||||
/// Implements the `Vertex` trait on a struct.
|
||||
// TODO: add example
|
||||
#[macro_export]
|
||||
macro_rules! impl_vertex {
|
||||
($out:ident $(, $member:ident)*) => (
|
||||
#[allow(unsafe_code)]
|
||||
unsafe impl $crate::pipeline::vertex::Vertex for $out {
|
||||
#[inline(always)]
|
||||
fn member(name: &str) -> Option<$crate::pipeline::vertex::VertexMemberInfo> {
|
||||
use std::ptr;
|
||||
#[allow(unused_imports)]
|
||||
use $crate::format::Format;
|
||||
use $crate::pipeline::vertex::VertexMemberInfo;
|
||||
use $crate::pipeline::vertex::VertexMemberTy;
|
||||
use $crate::pipeline::vertex::VertexMember;
|
||||
|
||||
$(
|
||||
if name == stringify!($member) {
|
||||
let (ty, array_size) = unsafe {
|
||||
#[inline] fn f<T: VertexMember>(_: &T) -> (VertexMemberTy, usize)
|
||||
{ T::format() }
|
||||
let dummy: *const $out = ptr::null();
|
||||
f(&(&*dummy).$member)
|
||||
};
|
||||
|
||||
return Some(VertexMemberInfo {
|
||||
offset: unsafe {
|
||||
let dummy: *const $out = ptr::null();
|
||||
let member = (&(&*dummy).$member) as *const _;
|
||||
member as usize
|
||||
},
|
||||
|
||||
ty: ty,
|
||||
array_size: array_size,
|
||||
});
|
||||
}
|
||||
)*
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/// Trait for data types that can be used as vertex members. Used by the `impl_vertex!` macro.
|
||||
pub unsafe trait VertexMember {
|
||||
/// Returns the format and array size of the member.
|
||||
fn format() -> (VertexMemberTy, usize);
|
||||
}
|
||||
|
||||
unsafe impl VertexMember for i8 {
|
||||
#[inline]
|
||||
fn format() -> (VertexMemberTy, usize) {
|
||||
(VertexMemberTy::I8, 1)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl VertexMember for u8 {
|
||||
#[inline]
|
||||
fn format() -> (VertexMemberTy, usize) {
|
||||
(VertexMemberTy::U8, 1)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl VertexMember for i16 {
|
||||
#[inline]
|
||||
fn format() -> (VertexMemberTy, usize) {
|
||||
(VertexMemberTy::I16, 1)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl VertexMember for u16 {
|
||||
#[inline]
|
||||
fn format() -> (VertexMemberTy, usize) {
|
||||
(VertexMemberTy::U16, 1)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl VertexMember for i32 {
|
||||
#[inline]
|
||||
fn format() -> (VertexMemberTy, usize) {
|
||||
(VertexMemberTy::I32, 1)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl VertexMember for u32 {
|
||||
#[inline]
|
||||
fn format() -> (VertexMemberTy, usize) {
|
||||
(VertexMemberTy::U32, 1)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl VertexMember for f32 {
|
||||
#[inline]
|
||||
fn format() -> (VertexMemberTy, usize) {
|
||||
(VertexMemberTy::F32, 1)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl VertexMember for f64 {
|
||||
#[inline]
|
||||
fn format() -> (VertexMemberTy, usize) {
|
||||
(VertexMemberTy::F64, 1)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T> VertexMember for (T,)
|
||||
where T: VertexMember
|
||||
{
|
||||
#[inline]
|
||||
fn format() -> (VertexMemberTy, usize) {
|
||||
<T as VertexMember>::format()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T> VertexMember for (T, T)
|
||||
where T: VertexMember
|
||||
{
|
||||
#[inline]
|
||||
fn format() -> (VertexMemberTy, usize) {
|
||||
let (ty, sz) = <T as VertexMember>::format();
|
||||
(ty, sz * 2)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T> VertexMember for (T, T, T)
|
||||
where T: VertexMember
|
||||
{
|
||||
#[inline]
|
||||
fn format() -> (VertexMemberTy, usize) {
|
||||
let (ty, sz) = <T as VertexMember>::format();
|
||||
(ty, sz * 3)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T> VertexMember for (T, T, T, T)
|
||||
where T: VertexMember
|
||||
{
|
||||
#[inline]
|
||||
fn format() -> (VertexMemberTy, usize) {
|
||||
let (ty, sz) = <T as VertexMember>::format();
|
||||
(ty, sz * 4)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_vm_array {
|
||||
($sz:expr) => (
|
||||
unsafe impl<T> VertexMember for [T; $sz]
|
||||
where T: VertexMember
|
||||
{
|
||||
#[inline]
|
||||
fn format() -> (VertexMemberTy, usize) {
|
||||
let (ty, sz) = <T as VertexMember>::format();
|
||||
(ty, sz * $sz)
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
impl_vm_array!(1);
|
||||
impl_vm_array!(2);
|
||||
impl_vm_array!(3);
|
||||
impl_vm_array!(4);
|
||||
impl_vm_array!(5);
|
||||
impl_vm_array!(6);
|
||||
impl_vm_array!(7);
|
||||
impl_vm_array!(8);
|
||||
impl_vm_array!(9);
|
||||
impl_vm_array!(10);
|
||||
impl_vm_array!(11);
|
||||
impl_vm_array!(12);
|
||||
impl_vm_array!(13);
|
||||
impl_vm_array!(14);
|
||||
impl_vm_array!(15);
|
||||
impl_vm_array!(16);
|
||||
impl_vm_array!(32);
|
||||
impl_vm_array!(64);
|
84
vulkano/src/pipeline/vertex/mod.rs
Normal file
84
vulkano/src/pipeline/vertex/mod.rs
Normal file
@ -0,0 +1,84 @@
|
||||
// Copyright (c) 2016 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.
|
||||
|
||||
//! # Vertex sources definition
|
||||
//!
|
||||
//! When you create a graphics pipeline object, you need to pass an object which indicates the
|
||||
//! layout of the vertex buffer(s) that will serve as input for the vertex shader. This is done
|
||||
//! by passing an implementation of the `VertexDefinition` trait.
|
||||
//!
|
||||
//! In addition to this, the object that you pass when you create the graphics pipeline must also
|
||||
//! implement the `VertexSource` trait. This trait has a template parameter which corresponds to the
|
||||
//! list of vertex buffers.
|
||||
//!
|
||||
//! The vulkano library provides some structs that already implement these traits.
|
||||
//! The most common situation is a single vertex buffer and no instancing, in which case you can
|
||||
//! pass a `SingleBufferDefinition` when you create the pipeline.
|
||||
//!
|
||||
//! # Implementing `Vertex`
|
||||
//!
|
||||
//! The implementations of the `VertexDefinition` trait that are provided by vulkano (like
|
||||
//! `SingleBufferDefinition`) require you to use a buffer whose content is `[V]` where `V`
|
||||
//! implements the `Vertex` trait.
|
||||
//!
|
||||
//! The `Vertex` trait is unsafe, but can be implemented on a struct with the `impl_vertex!`
|
||||
//! macro.
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! ```ignore // TODO:
|
||||
//! # #[macro_use] extern crate vulkano
|
||||
//! # fn main() {
|
||||
//! # use std::sync::Arc;
|
||||
//! # use vulkano::device::Device;
|
||||
//! # use vulkano::device::Queue;
|
||||
//! use vulkano::buffer::BufferAccess;
|
||||
//! use vulkano::buffer::BufferUsage;
|
||||
//! use vulkano::memory::HostVisible;
|
||||
//! use vulkano::pipeline::vertex::;
|
||||
//! # let device: Arc<Device> = return;
|
||||
//! # let queue: Arc<Queue> = return;
|
||||
//!
|
||||
//! struct Vertex {
|
||||
//! position: [f32; 2]
|
||||
//! }
|
||||
//!
|
||||
//! impl_vertex!(Vertex, position);
|
||||
//!
|
||||
//! let usage = BufferUsage {
|
||||
//! vertex_buffer: true,
|
||||
//! .. BufferUsage::none()
|
||||
//! };
|
||||
//!
|
||||
//! let vertex_buffer = BufferAccess::<[Vertex], _>::array(&device, 128, &usage, HostVisible, &queue)
|
||||
//! .expect("failed to create buffer");
|
||||
//!
|
||||
//! // TODO: finish example
|
||||
//! # }
|
||||
//! ```
|
||||
|
||||
pub use self::definition::AttributeInfo;
|
||||
pub use self::definition::IncompatibleVertexDefinitionError;
|
||||
pub use self::definition::InputRate;
|
||||
pub use self::definition::VertexDefinition;
|
||||
pub use self::definition::VertexSource;
|
||||
pub use self::impl_vertex::VertexMember;
|
||||
pub use self::one_one::OneVertexOneInstanceDefinition;
|
||||
pub use self::single::SingleBufferDefinition;
|
||||
pub use self::two::TwoBuffersDefinition;
|
||||
pub use self::vertex::Vertex;
|
||||
pub use self::vertex::VertexMemberInfo;
|
||||
pub use self::vertex::VertexMemberTy;
|
||||
|
||||
mod definition;
|
||||
mod impl_vertex;
|
||||
mod one_one;
|
||||
mod single;
|
||||
mod two;
|
||||
mod vertex;
|
108
vulkano/src/pipeline/vertex/one_one.rs
Normal file
108
vulkano/src/pipeline/vertex/one_one.rs
Normal file
@ -0,0 +1,108 @@
|
||||
// Copyright (c) 2016 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.
|
||||
|
||||
use std::marker::PhantomData;
|
||||
use std::mem;
|
||||
use std::sync::Arc;
|
||||
use std::vec::IntoIter as VecIntoIter;
|
||||
|
||||
use buffer::BufferAccess;
|
||||
use buffer::BufferInner;
|
||||
use buffer::TypedBufferAccess;
|
||||
use pipeline::shader::ShaderInterfaceDef;
|
||||
use pipeline::vertex::AttributeInfo;
|
||||
use pipeline::vertex::IncompatibleVertexDefinitionError;
|
||||
use pipeline::vertex::InputRate;
|
||||
use pipeline::vertex::Vertex;
|
||||
use pipeline::vertex::VertexDefinition;
|
||||
use pipeline::vertex::VertexSource;
|
||||
|
||||
/// Unstable.
|
||||
// TODO: bad way to do things
|
||||
pub struct OneVertexOneInstanceDefinition<T, U>(pub PhantomData<(T, U)>);
|
||||
|
||||
impl<T, U> OneVertexOneInstanceDefinition<T, U> {
|
||||
#[inline]
|
||||
pub fn new() -> OneVertexOneInstanceDefinition<T, U> { OneVertexOneInstanceDefinition(PhantomData) }
|
||||
}
|
||||
|
||||
unsafe impl<T, U, I> VertexDefinition<I> for OneVertexOneInstanceDefinition<T, U>
|
||||
where T: Vertex, U: Vertex, I: ShaderInterfaceDef
|
||||
{
|
||||
type BuffersIter = VecIntoIter<(u32, usize, InputRate)>;
|
||||
type AttribsIter = VecIntoIter<(u32, u32, AttributeInfo)>;
|
||||
|
||||
fn definition(&self, interface: &I) -> Result<(Self::BuffersIter, Self::AttribsIter),
|
||||
IncompatibleVertexDefinitionError>
|
||||
{
|
||||
let attrib = {
|
||||
let mut attribs = Vec::with_capacity(interface.elements().len());
|
||||
for e in interface.elements() {
|
||||
let name = e.name.as_ref().unwrap();
|
||||
|
||||
let (infos, buf_offset) = if let Some(infos) = <T as Vertex>::member(name) {
|
||||
(infos, 0)
|
||||
} else if let Some(infos) = <U as Vertex>::member(name) {
|
||||
(infos, 1)
|
||||
} else {
|
||||
return Err(IncompatibleVertexDefinitionError::MissingAttribute {
|
||||
attribute: name.clone().into_owned()
|
||||
});
|
||||
};
|
||||
|
||||
if !infos.ty.matches(infos.array_size, e.format,
|
||||
e.location.end - e.location.start)
|
||||
{
|
||||
return Err(IncompatibleVertexDefinitionError::FormatMismatch {
|
||||
attribute: name.clone().into_owned(),
|
||||
shader: (e.format, (e.location.end - e.location.start) as usize),
|
||||
definition: (infos.ty, infos.array_size),
|
||||
})
|
||||
}
|
||||
|
||||
let mut offset = infos.offset;
|
||||
for loc in e.location.clone() {
|
||||
attribs.push((loc, buf_offset, AttributeInfo { offset: offset, format: e.format }));
|
||||
offset += e.format.size().unwrap();
|
||||
}
|
||||
}
|
||||
attribs
|
||||
}.into_iter(); // TODO: meh
|
||||
|
||||
let buffers = vec![
|
||||
(0, mem::size_of::<T>(), InputRate::Vertex),
|
||||
(1, mem::size_of::<U>(), InputRate::Instance)
|
||||
].into_iter();
|
||||
|
||||
Ok((buffers, attrib))
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T, U> VertexSource<Vec<Arc<BufferAccess + Send + Sync>>> for OneVertexOneInstanceDefinition<T, U>
|
||||
where T: Vertex, U: Vertex
|
||||
{
|
||||
#[inline]
|
||||
fn decode<'l>(&self, source: &'l Vec<Arc<BufferAccess + Send + Sync>>) -> (Vec<BufferInner<'l>>, usize, usize) {
|
||||
// FIXME: safety
|
||||
assert_eq!(source.len(), 2);
|
||||
let len = source[0].size() / mem::size_of::<T>();
|
||||
let inst = source[0].size() / mem::size_of::<U>();
|
||||
(vec![source[0].inner(), source[1].inner()], len, inst)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a, T, U, Bt, Bu> VertexSource<(Bt, Bu)> for OneVertexOneInstanceDefinition<T, U>
|
||||
where T: Vertex, Bt: TypedBufferAccess<Content = [T]>,
|
||||
U: Vertex, Bu: TypedBufferAccess<Content = [U]>
|
||||
{
|
||||
#[inline]
|
||||
fn decode<'l>(&self, source: &'l (Bt, Bu)) -> (Vec<BufferInner<'l>>, usize, usize) {
|
||||
(vec![source.0.inner(), source.1.inner()], source.0.len(), source.1.len())
|
||||
}
|
||||
}
|
99
vulkano/src/pipeline/vertex/single.rs
Normal file
99
vulkano/src/pipeline/vertex/single.rs
Normal file
@ -0,0 +1,99 @@
|
||||
// Copyright (c) 2016 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.
|
||||
|
||||
use std::marker::PhantomData;
|
||||
use std::mem;
|
||||
use std::option::IntoIter as OptionIntoIter;
|
||||
use std::sync::Arc;
|
||||
use std::vec::IntoIter as VecIntoIter;
|
||||
|
||||
use buffer::BufferAccess;
|
||||
use buffer::BufferInner;
|
||||
use buffer::TypedBufferAccess;
|
||||
use pipeline::shader::ShaderInterfaceDef;
|
||||
use pipeline::vertex::AttributeInfo;
|
||||
use pipeline::vertex::IncompatibleVertexDefinitionError;
|
||||
use pipeline::vertex::InputRate;
|
||||
use pipeline::vertex::Vertex;
|
||||
use pipeline::vertex::VertexDefinition;
|
||||
use pipeline::vertex::VertexSource;
|
||||
|
||||
/// Implementation of `VertexDefinition` for a single vertex buffer.
|
||||
pub struct SingleBufferDefinition<T>(pub PhantomData<T>);
|
||||
|
||||
impl<T> SingleBufferDefinition<T> {
|
||||
#[inline]
|
||||
pub fn new() -> SingleBufferDefinition<T> { SingleBufferDefinition(PhantomData) }
|
||||
}
|
||||
|
||||
unsafe impl<T, I> VertexDefinition<I> for SingleBufferDefinition<T>
|
||||
where T: Vertex, I: ShaderInterfaceDef
|
||||
{
|
||||
type BuffersIter = OptionIntoIter<(u32, usize, InputRate)>;
|
||||
type AttribsIter = VecIntoIter<(u32, u32, AttributeInfo)>;
|
||||
|
||||
fn definition(&self, interface: &I) -> Result<(Self::BuffersIter, Self::AttribsIter),
|
||||
IncompatibleVertexDefinitionError>
|
||||
{
|
||||
let attrib = {
|
||||
let mut attribs = Vec::with_capacity(interface.elements().len());
|
||||
for e in interface.elements() {
|
||||
let name = e.name.as_ref().unwrap();
|
||||
|
||||
let infos = match <T as Vertex>::member(name) {
|
||||
Some(m) => m,
|
||||
None => return Err(IncompatibleVertexDefinitionError::MissingAttribute {
|
||||
attribute: name.clone().into_owned()
|
||||
})
|
||||
};
|
||||
|
||||
if !infos.ty.matches(infos.array_size, e.format,
|
||||
e.location.end - e.location.start)
|
||||
{
|
||||
return Err(IncompatibleVertexDefinitionError::FormatMismatch {
|
||||
attribute: name.clone().into_owned(),
|
||||
shader: (e.format, (e.location.end - e.location.start) as usize),
|
||||
definition: (infos.ty, infos.array_size),
|
||||
})
|
||||
}
|
||||
|
||||
let mut offset = infos.offset;
|
||||
for loc in e.location.clone() {
|
||||
attribs.push((loc, 0, AttributeInfo { offset: offset, format: e.format }));
|
||||
offset += e.format.size().unwrap();
|
||||
}
|
||||
}
|
||||
attribs
|
||||
}.into_iter(); // TODO: meh
|
||||
|
||||
let buffers = Some((0, mem::size_of::<T>(), InputRate::Vertex)).into_iter();
|
||||
Ok((buffers, attrib))
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<V> VertexSource<Vec<Arc<BufferAccess + Send + Sync>>> for SingleBufferDefinition<V>
|
||||
where V: Vertex
|
||||
{
|
||||
#[inline]
|
||||
fn decode<'l>(&self, source: &'l Vec<Arc<BufferAccess + Send + Sync>>) -> (Vec<BufferInner<'l>>, usize, usize) {
|
||||
// FIXME: safety
|
||||
assert_eq!(source.len(), 1);
|
||||
let len = source[0].size() / mem::size_of::<V>();
|
||||
(vec![source[0].inner()], len, 1)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a, B, V> VertexSource<B> for SingleBufferDefinition<V>
|
||||
where B: TypedBufferAccess<Content = [V]>, V: Vertex
|
||||
{
|
||||
#[inline]
|
||||
fn decode<'l>(&self, source: &'l B) -> (Vec<BufferInner<'l>>, usize, usize) {
|
||||
(vec![source.inner()], source.len(), 1)
|
||||
}
|
||||
}
|
105
vulkano/src/pipeline/vertex/two.rs
Normal file
105
vulkano/src/pipeline/vertex/two.rs
Normal file
@ -0,0 +1,105 @@
|
||||
// Copyright (c) 2016 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.
|
||||
|
||||
use std::marker::PhantomData;
|
||||
use std::mem;
|
||||
use std::sync::Arc;
|
||||
use std::vec::IntoIter as VecIntoIter;
|
||||
|
||||
use buffer::BufferAccess;
|
||||
use buffer::BufferInner;
|
||||
use buffer::TypedBufferAccess;
|
||||
use pipeline::shader::ShaderInterfaceDef;
|
||||
use pipeline::vertex::AttributeInfo;
|
||||
use pipeline::vertex::IncompatibleVertexDefinitionError;
|
||||
use pipeline::vertex::InputRate;
|
||||
use pipeline::vertex::Vertex;
|
||||
use pipeline::vertex::VertexDefinition;
|
||||
use pipeline::vertex::VertexSource;
|
||||
|
||||
/// Unstable.
|
||||
// TODO: shouldn't be just `Two` but `Multi`
|
||||
pub struct TwoBuffersDefinition<T, U>(pub PhantomData<(T, U)>);
|
||||
|
||||
impl<T, U> TwoBuffersDefinition<T, U> {
|
||||
#[inline]
|
||||
pub fn new() -> TwoBuffersDefinition<T, U> { TwoBuffersDefinition(PhantomData) }
|
||||
}
|
||||
|
||||
unsafe impl<T, U, I> VertexDefinition<I> for TwoBuffersDefinition<T, U>
|
||||
where T: Vertex, U: Vertex, I: ShaderInterfaceDef
|
||||
{
|
||||
type BuffersIter = VecIntoIter<(u32, usize, InputRate)>;
|
||||
type AttribsIter = VecIntoIter<(u32, u32, AttributeInfo)>;
|
||||
|
||||
fn definition(&self, interface: &I) -> Result<(Self::BuffersIter, Self::AttribsIter),
|
||||
IncompatibleVertexDefinitionError>
|
||||
{
|
||||
let attrib = {
|
||||
let mut attribs = Vec::with_capacity(interface.elements().len());
|
||||
for e in interface.elements() {
|
||||
let name = e.name.as_ref().unwrap();
|
||||
|
||||
let (infos, buf_offset) = if let Some(infos) = <T as Vertex>::member(name) {
|
||||
(infos, 0)
|
||||
} else if let Some(infos) = <U as Vertex>::member(name) {
|
||||
(infos, 1)
|
||||
} else {
|
||||
return Err(IncompatibleVertexDefinitionError::MissingAttribute {
|
||||
attribute: name.clone().into_owned()
|
||||
});
|
||||
};
|
||||
|
||||
if !infos.ty.matches(infos.array_size, e.format,
|
||||
e.location.end - e.location.start)
|
||||
{
|
||||
return Err(IncompatibleVertexDefinitionError::FormatMismatch {
|
||||
attribute: name.clone().into_owned(),
|
||||
shader: (e.format, (e.location.end - e.location.start) as usize),
|
||||
definition: (infos.ty, infos.array_size),
|
||||
})
|
||||
}
|
||||
|
||||
let mut offset = infos.offset;
|
||||
for loc in e.location.clone() {
|
||||
attribs.push((loc, buf_offset, AttributeInfo { offset: offset, format: e.format }));
|
||||
offset += e.format.size().unwrap();
|
||||
}
|
||||
}
|
||||
attribs
|
||||
}.into_iter(); // TODO: meh
|
||||
|
||||
let buffers = vec![
|
||||
(0, mem::size_of::<T>(), InputRate::Vertex),
|
||||
(1, mem::size_of::<U>(), InputRate::Vertex)
|
||||
].into_iter();
|
||||
|
||||
Ok((buffers, attrib))
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T, U> VertexSource<Vec<Arc<BufferAccess + Send + Sync>>> for TwoBuffersDefinition<T, U>
|
||||
where T: Vertex, U: Vertex
|
||||
{
|
||||
#[inline]
|
||||
fn decode<'l>(&self, source: &'l Vec<Arc<BufferAccess + Send + Sync>>) -> (Vec<BufferInner<'l>>, usize, usize) {
|
||||
unimplemented!() // FIXME: implement
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a, T, U, Bt, Bu> VertexSource<(Bt, Bu)> for TwoBuffersDefinition<T, U>
|
||||
where T: Vertex, Bt: TypedBufferAccess<Content = [T]>,
|
||||
U: Vertex, Bu: TypedBufferAccess<Content = [U]>
|
||||
{
|
||||
#[inline]
|
||||
fn decode<'l>(&self, source: &'l (Bt, Bu)) -> (Vec<BufferInner<'l>>, usize, usize) {
|
||||
let vertices = [source.0.len(), source.1.len()].iter().cloned().min().unwrap();
|
||||
(vec![source.0.inner(), source.1.inner()], vertices, 1)
|
||||
}
|
||||
}
|
76
vulkano/src/pipeline/vertex/vertex.rs
Normal file
76
vulkano/src/pipeline/vertex/vertex.rs
Normal file
@ -0,0 +1,76 @@
|
||||
// Copyright (c) 2017 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.
|
||||
|
||||
use format::Format;
|
||||
|
||||
/// Describes an individual `Vertex`. In other words a collection of attributes that can be read
|
||||
/// from a vertex shader.
|
||||
///
|
||||
/// At this stage, the vertex is in a "raw" format. For example a `[f32; 4]` can match both a
|
||||
/// `vec4` or a `float[4]`. The way the things are binded depends on the shader.
|
||||
pub unsafe trait Vertex: 'static + Send + Sync {
|
||||
/// Returns the characteristics of a vertex member by its name.
|
||||
fn member(name: &str) -> Option<VertexMemberInfo>;
|
||||
}
|
||||
|
||||
unsafe impl Vertex for () {
|
||||
#[inline]
|
||||
fn member(_: &str) -> Option<VertexMemberInfo> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Information about a member of a vertex struct.
|
||||
pub struct VertexMemberInfo {
|
||||
/// Offset of the member in bytes from the start of the struct.
|
||||
pub offset: usize,
|
||||
/// Type of data. This is used to check that the interface is matching.
|
||||
pub ty: VertexMemberTy,
|
||||
/// Number of consecutive elements of that type.
|
||||
pub array_size: usize,
|
||||
}
|
||||
|
||||
/// Type of a member of a vertex struct.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum VertexMemberTy {
|
||||
I8,
|
||||
U8,
|
||||
I16,
|
||||
U16,
|
||||
I32,
|
||||
U32,
|
||||
F32,
|
||||
F64,
|
||||
}
|
||||
|
||||
impl VertexMemberTy {
|
||||
/// Returns true if a combination of `(type, array_size)` matches a format.
|
||||
#[inline]
|
||||
pub fn matches(&self, array_size: usize, format: Format, num_locs: u32) -> bool {
|
||||
// TODO: implement correctly
|
||||
let my_size = match *self {
|
||||
VertexMemberTy::I8 => 1,
|
||||
VertexMemberTy::U8 => 1,
|
||||
VertexMemberTy::I16 => 2,
|
||||
VertexMemberTy::U16 => 2,
|
||||
VertexMemberTy::I32 => 4,
|
||||
VertexMemberTy::U32 => 4,
|
||||
VertexMemberTy::F32 => 4,
|
||||
VertexMemberTy::F64 => 8,
|
||||
};
|
||||
|
||||
let format_size = match format.size() {
|
||||
None => return false,
|
||||
Some(s) => s,
|
||||
};
|
||||
|
||||
array_size * my_size == format_size * num_locs as usize
|
||||
}
|
||||
}
|
@ -241,11 +241,11 @@ pub struct OcclusionQueriesPool {
|
||||
|
||||
impl OcclusionQueriesPool {
|
||||
/// See the docs of new().
|
||||
pub fn raw(device: &Arc<Device>, num_slots: u32)
|
||||
pub fn raw(device: Arc<Device>, num_slots: u32)
|
||||
-> Result<OcclusionQueriesPool, OomError>
|
||||
{
|
||||
Ok(OcclusionQueriesPool {
|
||||
inner: match UnsafeQueryPool::new(device.clone(), QueryType::Occlusion, num_slots) {
|
||||
inner: match UnsafeQueryPool::new(device, QueryType::Occlusion, num_slots) {
|
||||
Ok(q) => q,
|
||||
Err(QueryPoolCreationError::OomError(err)) => return Err(err),
|
||||
Err(QueryPoolCreationError::PipelineStatisticsQueryFeatureNotEnabled) => {
|
||||
@ -262,7 +262,7 @@ impl OcclusionQueriesPool {
|
||||
/// - Panics if the device or host ran out of memory.
|
||||
///
|
||||
#[inline]
|
||||
pub fn new(device: &Arc<Device>, num_slots: u32)
|
||||
pub fn new(device: Arc<Device>, num_slots: u32)
|
||||
-> Arc<OcclusionQueriesPool>
|
||||
{
|
||||
Arc::new(OcclusionQueriesPool::raw(device, num_slots).unwrap())
|
||||
@ -292,7 +292,7 @@ mod tests {
|
||||
#[test]
|
||||
fn occlusion_create() {
|
||||
let (device, _) = gfx_dev_and_queue!();
|
||||
let _ = OcclusionQueriesPool::new(&device, 256);
|
||||
let _ = OcclusionQueriesPool::new(device, 256);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -25,7 +25,7 @@
|
||||
//! use vulkano::sampler::Sampler;
|
||||
//!
|
||||
//! # let device: std::sync::Arc<vulkano::device::Device> = return;
|
||||
//! let _sampler = Sampler::simple_repeat_linear_no_mipmap(&device);
|
||||
//! let _sampler = Sampler::simple_repeat_linear_no_mipmap(device.clone());
|
||||
//! ```
|
||||
//!
|
||||
//! More detailed sampler creation:
|
||||
@ -34,7 +34,8 @@
|
||||
//! use vulkano::sampler;
|
||||
//!
|
||||
//! # let device: std::sync::Arc<vulkano::device::Device> = return;
|
||||
//! let _sampler = sampler::Sampler::new(&device, sampler::Filter::Linear, sampler::Filter::Linear,
|
||||
//! let _sampler = sampler::Sampler::new(device.clone(), sampler::Filter::Linear,
|
||||
//! sampler::Filter::Linear,
|
||||
//! sampler::MipmapMode::Nearest,
|
||||
//! sampler::SamplerAddressMode::Repeat,
|
||||
//! sampler::SamplerAddressMode::Repeat,
|
||||
@ -98,7 +99,7 @@ impl Sampler {
|
||||
/// - Panics if out of memory or the maximum number of samplers has exceeded.
|
||||
///
|
||||
#[inline]
|
||||
pub fn simple_repeat_linear(device: &Arc<Device>) -> Arc<Sampler> {
|
||||
pub fn simple_repeat_linear(device: Arc<Device>) -> Arc<Sampler> {
|
||||
Sampler::new(device, Filter::Linear, Filter::Linear, MipmapMode::Linear,
|
||||
SamplerAddressMode::Repeat, SamplerAddressMode::Repeat,
|
||||
SamplerAddressMode::Repeat, 0.0, 1.0, 0.0, 1_000.0).unwrap()
|
||||
@ -114,7 +115,7 @@ impl Sampler {
|
||||
/// - Panics if out of memory or the maximum number of samplers has exceeded.
|
||||
///
|
||||
#[inline]
|
||||
pub fn simple_repeat_linear_no_mipmap(device: &Arc<Device>) -> Arc<Sampler> {
|
||||
pub fn simple_repeat_linear_no_mipmap(device: Arc<Device>) -> Arc<Sampler> {
|
||||
Sampler::new(device, Filter::Linear, Filter::Linear, MipmapMode::Nearest,
|
||||
SamplerAddressMode::Repeat, SamplerAddressMode::Repeat,
|
||||
SamplerAddressMode::Repeat, 0.0, 1.0, 0.0, 1.0).unwrap()
|
||||
@ -146,7 +147,7 @@ impl Sampler {
|
||||
/// - Panics if `min_lod > max_lod`.
|
||||
///
|
||||
#[inline(always)]
|
||||
pub fn new(device: &Arc<Device>, mag_filter: Filter, min_filter: Filter,
|
||||
pub fn new(device: Arc<Device>, mag_filter: Filter, min_filter: Filter,
|
||||
mipmap_mode: MipmapMode, address_u: SamplerAddressMode,
|
||||
address_v: SamplerAddressMode, address_w: SamplerAddressMode, mip_lod_bias: f32,
|
||||
max_anisotropy: f32, min_lod: f32, max_lod: f32)
|
||||
@ -173,7 +174,7 @@ impl Sampler {
|
||||
/// Same panic reasons as `new`.
|
||||
///
|
||||
#[inline(always)]
|
||||
pub fn compare(device: &Arc<Device>, mag_filter: Filter, min_filter: Filter,
|
||||
pub fn compare(device: Arc<Device>, mag_filter: Filter, min_filter: Filter,
|
||||
mipmap_mode: MipmapMode, address_u: SamplerAddressMode,
|
||||
address_v: SamplerAddressMode, address_w: SamplerAddressMode, mip_lod_bias: f32,
|
||||
max_anisotropy: f32, min_lod: f32, max_lod: f32, compare: Compare)
|
||||
@ -183,7 +184,7 @@ impl Sampler {
|
||||
address_w, mip_lod_bias, max_anisotropy, min_lod, max_lod, Some(compare))
|
||||
}
|
||||
|
||||
fn new_impl(device: &Arc<Device>, mag_filter: Filter, min_filter: Filter,
|
||||
fn new_impl(device: Arc<Device>, mag_filter: Filter, min_filter: Filter,
|
||||
mipmap_mode: MipmapMode, address_u: SamplerAddressMode,
|
||||
address_v: SamplerAddressMode, address_w: SamplerAddressMode, mip_lod_bias: f32,
|
||||
max_anisotropy: f32, min_lod: f32, max_lod: f32, compare: Option<Compare>)
|
||||
@ -309,7 +310,7 @@ impl Sampler {
|
||||
///
|
||||
/// - Panics if multiple `ClampToBorder` values are passed and the border color is different.
|
||||
///
|
||||
pub fn unnormalized(device: &Arc<Device>, filter: Filter,
|
||||
pub fn unnormalized(device: Arc<Device>, filter: Filter,
|
||||
address_u: UnnormalizedSamplerAddressMode,
|
||||
address_v: UnnormalizedSamplerAddressMode)
|
||||
-> Result<Arc<Sampler>, SamplerCreationError>
|
||||
@ -672,7 +673,7 @@ mod tests {
|
||||
fn create_regular() {
|
||||
let (device, queue) = gfx_dev_and_queue!();
|
||||
|
||||
let s = sampler::Sampler::new(&device, sampler::Filter::Linear, sampler::Filter::Linear,
|
||||
let s = sampler::Sampler::new(device, sampler::Filter::Linear, sampler::Filter::Linear,
|
||||
sampler::MipmapMode::Nearest,
|
||||
sampler::SamplerAddressMode::Repeat,
|
||||
sampler::SamplerAddressMode::Repeat,
|
||||
@ -686,7 +687,7 @@ mod tests {
|
||||
fn create_compare() {
|
||||
let (device, queue) = gfx_dev_and_queue!();
|
||||
|
||||
let s = sampler::Sampler::compare(&device, sampler::Filter::Linear, sampler::Filter::Linear,
|
||||
let s = sampler::Sampler::compare(device, sampler::Filter::Linear, sampler::Filter::Linear,
|
||||
sampler::MipmapMode::Nearest,
|
||||
sampler::SamplerAddressMode::Repeat,
|
||||
sampler::SamplerAddressMode::Repeat,
|
||||
@ -701,7 +702,7 @@ mod tests {
|
||||
fn create_unnormalized() {
|
||||
let (device, queue) = gfx_dev_and_queue!();
|
||||
|
||||
let s = sampler::Sampler::unnormalized(&device, sampler::Filter::Linear,
|
||||
let s = sampler::Sampler::unnormalized(device, sampler::Filter::Linear,
|
||||
sampler::UnnormalizedSamplerAddressMode::ClampToEdge,
|
||||
sampler::UnnormalizedSamplerAddressMode::ClampToEdge)
|
||||
.unwrap();
|
||||
@ -713,13 +714,13 @@ mod tests {
|
||||
#[test]
|
||||
fn simple_repeat_linear() {
|
||||
let (device, queue) = gfx_dev_and_queue!();
|
||||
let _ = sampler::Sampler::simple_repeat_linear(&device);
|
||||
let _ = sampler::Sampler::simple_repeat_linear(device);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_repeat_linear_no_mipmap() {
|
||||
let (device, queue) = gfx_dev_and_queue!();
|
||||
let _ = sampler::Sampler::simple_repeat_linear_no_mipmap(&device);
|
||||
let _ = sampler::Sampler::simple_repeat_linear_no_mipmap(device);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -727,7 +728,7 @@ mod tests {
|
||||
fn min_lod_inferior() {
|
||||
let (device, queue) = gfx_dev_and_queue!();
|
||||
|
||||
let _ = sampler::Sampler::new(&device, sampler::Filter::Linear, sampler::Filter::Linear,
|
||||
let _ = sampler::Sampler::new(device, sampler::Filter::Linear, sampler::Filter::Linear,
|
||||
sampler::MipmapMode::Nearest,
|
||||
sampler::SamplerAddressMode::Repeat,
|
||||
sampler::SamplerAddressMode::Repeat,
|
||||
@ -739,7 +740,7 @@ mod tests {
|
||||
fn max_anisotropy() {
|
||||
let (device, queue) = gfx_dev_and_queue!();
|
||||
|
||||
let _ = sampler::Sampler::new(&device, sampler::Filter::Linear, sampler::Filter::Linear,
|
||||
let _ = sampler::Sampler::new(device, sampler::Filter::Linear, sampler::Filter::Linear,
|
||||
sampler::MipmapMode::Nearest,
|
||||
sampler::SamplerAddressMode::Repeat,
|
||||
sampler::SamplerAddressMode::Repeat,
|
||||
@ -754,7 +755,7 @@ mod tests {
|
||||
let b1 = sampler::BorderColor::IntTransparentBlack;
|
||||
let b2 = sampler::BorderColor::FloatOpaqueWhite;
|
||||
|
||||
let _ = sampler::Sampler::new(&device, sampler::Filter::Linear, sampler::Filter::Linear,
|
||||
let _ = sampler::Sampler::new(device, sampler::Filter::Linear, sampler::Filter::Linear,
|
||||
sampler::MipmapMode::Nearest,
|
||||
sampler::SamplerAddressMode::ClampToBorder(b1),
|
||||
sampler::SamplerAddressMode::ClampToBorder(b2),
|
||||
@ -765,7 +766,7 @@ mod tests {
|
||||
fn anisotropy_feature() {
|
||||
let (device, queue) = gfx_dev_and_queue!();
|
||||
|
||||
let r = sampler::Sampler::new(&device, sampler::Filter::Linear, sampler::Filter::Linear,
|
||||
let r = sampler::Sampler::new(device, sampler::Filter::Linear, sampler::Filter::Linear,
|
||||
sampler::MipmapMode::Nearest,
|
||||
sampler::SamplerAddressMode::Repeat,
|
||||
sampler::SamplerAddressMode::Repeat,
|
||||
@ -781,7 +782,7 @@ mod tests {
|
||||
fn anisotropy_limit() {
|
||||
let (device, queue) = gfx_dev_and_queue!(sampler_anisotropy);
|
||||
|
||||
let r = sampler::Sampler::new(&device, sampler::Filter::Linear, sampler::Filter::Linear,
|
||||
let r = sampler::Sampler::new(device, sampler::Filter::Linear, sampler::Filter::Linear,
|
||||
sampler::MipmapMode::Nearest,
|
||||
sampler::SamplerAddressMode::Repeat,
|
||||
sampler::SamplerAddressMode::Repeat,
|
||||
@ -798,7 +799,7 @@ mod tests {
|
||||
fn mip_lod_bias_limit() {
|
||||
let (device, queue) = gfx_dev_and_queue!();
|
||||
|
||||
let r = sampler::Sampler::new(&device, sampler::Filter::Linear, sampler::Filter::Linear,
|
||||
let r = sampler::Sampler::new(device, sampler::Filter::Linear, sampler::Filter::Linear,
|
||||
sampler::MipmapMode::Nearest,
|
||||
sampler::SamplerAddressMode::Repeat,
|
||||
sampler::SamplerAddressMode::Repeat,
|
||||
@ -815,7 +816,7 @@ mod tests {
|
||||
fn sampler_mirror_clamp_to_edge_extension() {
|
||||
let (device, queue) = gfx_dev_and_queue!();
|
||||
|
||||
let r = sampler::Sampler::new(&device, sampler::Filter::Linear, sampler::Filter::Linear,
|
||||
let r = sampler::Sampler::new(device, sampler::Filter::Linear, sampler::Filter::Linear,
|
||||
sampler::MipmapMode::Nearest,
|
||||
sampler::SamplerAddressMode::MirrorClampToEdge,
|
||||
sampler::SamplerAddressMode::MirrorClampToEdge,
|
||||
|
500
vulkano/src/swapchain/capabilities.rs
Normal file
500
vulkano/src/swapchain/capabilities.rs
Normal file
@ -0,0 +1,500 @@
|
||||
// Copyright (c) 2016 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.
|
||||
|
||||
use format::Format;
|
||||
use image::ImageUsage;
|
||||
use vk;
|
||||
|
||||
/// The capabilities of a surface when used by a physical device.
|
||||
///
|
||||
/// You have to match these capabilities when you create a swapchain.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Capabilities {
|
||||
/// Minimum number of images that must be present in the swapchain.
|
||||
pub min_image_count: u32,
|
||||
|
||||
/// Maximum number of images that must be present in the swapchain, or `None` if there is no
|
||||
/// maximum value. Note that "no maximum" doesn't mean that you can set a very high value, as
|
||||
/// you may still get out of memory errors.
|
||||
pub max_image_count: Option<u32>,
|
||||
|
||||
/// The current dimensions of the surface. `None` means that the surface's dimensions will
|
||||
/// depend on the dimensions of the swapchain that you are going to create.
|
||||
pub current_extent: Option<[u32; 2]>,
|
||||
|
||||
/// Minimum width and height of a swapchain that uses this surface.
|
||||
pub min_image_extent: [u32; 2],
|
||||
|
||||
/// Maximum width and height of a swapchain that uses this surface.
|
||||
pub max_image_extent: [u32; 2],
|
||||
|
||||
/// Maximum number of image layers if you create an image array. The minimum is 1.
|
||||
pub max_image_array_layers: u32,
|
||||
|
||||
/// List of transforms supported for the swapchain.
|
||||
pub supported_transforms: SupportedSurfaceTransforms,
|
||||
|
||||
/// Current transform used by the surface.
|
||||
pub current_transform: SurfaceTransform,
|
||||
|
||||
/// List of composite alpha modes supports for the swapchain.
|
||||
pub supported_composite_alpha: SupportedCompositeAlpha,
|
||||
|
||||
/// List of image usages that are supported for images of the swapchain. Only
|
||||
/// the `color_attachment` usage is guaranteed to be supported.
|
||||
pub supported_usage_flags: ImageUsage,
|
||||
|
||||
/// List of formats supported for the swapchain.
|
||||
pub supported_formats: Vec<(Format, ColorSpace)>, // TODO: https://github.com/KhronosGroup/Vulkan-Docs/issues/207
|
||||
|
||||
/// List of present modes that are supported. `Fifo` is always guaranteed to be supported.
|
||||
pub present_modes: SupportedPresentModes,
|
||||
}
|
||||
|
||||
/// The way presenting a swapchain is accomplished.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
#[repr(u32)]
|
||||
pub enum PresentMode {
|
||||
/// Immediately shows the image to the user. May result in visible tearing.
|
||||
Immediate = vk::PRESENT_MODE_IMMEDIATE_KHR,
|
||||
|
||||
/// The action of presenting an image puts it in wait. When the next vertical blanking period
|
||||
/// happens, the waiting image is effectively shown to the user. If an image is presented while
|
||||
/// another one is waiting, it is replaced.
|
||||
Mailbox = vk::PRESENT_MODE_MAILBOX_KHR,
|
||||
|
||||
/// The action of presenting an image adds it to a queue of images. At each vertical blanking
|
||||
/// period, the queue is poped and an image is presented.
|
||||
///
|
||||
/// Guaranteed to be always supported.
|
||||
///
|
||||
/// This is the equivalent of OpenGL's `SwapInterval` with a value of 1.
|
||||
Fifo = vk::PRESENT_MODE_FIFO_KHR,
|
||||
|
||||
/// Same as `Fifo`, except that if the queue was empty during the previous vertical blanking
|
||||
/// period then it is equivalent to `Immediate`.
|
||||
///
|
||||
/// This is the equivalent of OpenGL's `SwapInterval` with a value of -1.
|
||||
Relaxed = vk::PRESENT_MODE_FIFO_RELAXED_KHR,
|
||||
}
|
||||
|
||||
/// List of `PresentMode`s that are supported.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub struct SupportedPresentModes {
|
||||
pub immediate: bool,
|
||||
pub mailbox: bool,
|
||||
pub fifo: bool,
|
||||
pub relaxed: bool,
|
||||
}
|
||||
|
||||
pub fn supported_present_modes_from_list<I>(elem: I) -> SupportedPresentModes
|
||||
where I: Iterator<Item = vk::PresentModeKHR>
|
||||
{
|
||||
let mut result = SupportedPresentModes::none();
|
||||
for e in elem {
|
||||
match e {
|
||||
vk::PRESENT_MODE_IMMEDIATE_KHR => result.immediate = true,
|
||||
vk::PRESENT_MODE_MAILBOX_KHR => result.mailbox = true,
|
||||
vk::PRESENT_MODE_FIFO_KHR => result.fifo = true,
|
||||
vk::PRESENT_MODE_FIFO_RELAXED_KHR => result.relaxed = true,
|
||||
_ => panic!("Wrong value for vk::PresentModeKHR")
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
impl SupportedPresentModes {
|
||||
/// Builds a `SupportedPresentModes` with all fields set to false.
|
||||
#[inline]
|
||||
pub fn none() -> SupportedPresentModes {
|
||||
SupportedPresentModes {
|
||||
immediate: false,
|
||||
mailbox: false,
|
||||
fifo: false,
|
||||
relaxed: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if the given present mode is in this list of supported modes.
|
||||
#[inline]
|
||||
pub fn supports(&self, mode: PresentMode) -> bool {
|
||||
match mode {
|
||||
PresentMode::Immediate => self.immediate,
|
||||
PresentMode::Mailbox => self.mailbox,
|
||||
PresentMode::Fifo => self.fifo,
|
||||
PresentMode::Relaxed => self.relaxed,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an iterator to the list of supported present modes.
|
||||
#[inline]
|
||||
pub fn iter(&self) -> SupportedPresentModesIter {
|
||||
SupportedPresentModesIter(self.clone())
|
||||
}
|
||||
}
|
||||
|
||||
/// Enumeration of the `PresentMode`s that are supported.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub struct SupportedPresentModesIter(SupportedPresentModes);
|
||||
|
||||
impl Iterator for SupportedPresentModesIter {
|
||||
type Item = PresentMode;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<PresentMode> {
|
||||
if self.0.immediate { self.0.immediate = false; return Some(PresentMode::Immediate); }
|
||||
if self.0.mailbox { self.0.mailbox = false; return Some(PresentMode::Mailbox); }
|
||||
if self.0.fifo { self.0.fifo = false; return Some(PresentMode::Fifo); }
|
||||
if self.0.relaxed { self.0.relaxed = false; return Some(PresentMode::Relaxed); }
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// A transformation to apply to the image before showing it on the screen.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
#[repr(u32)]
|
||||
pub enum SurfaceTransform {
|
||||
/// Don't transform the image.
|
||||
Identity = vk::SURFACE_TRANSFORM_IDENTITY_BIT_KHR,
|
||||
/// Rotate 90 degrees.
|
||||
Rotate90 = vk::SURFACE_TRANSFORM_ROTATE_90_BIT_KHR,
|
||||
/// Rotate 180 degrees.
|
||||
Rotate180 = vk::SURFACE_TRANSFORM_ROTATE_180_BIT_KHR,
|
||||
/// Rotate 270 degrees.
|
||||
Rotate270 = vk::SURFACE_TRANSFORM_ROTATE_270_BIT_KHR,
|
||||
/// Mirror the image horizontally.
|
||||
HorizontalMirror = vk::SURFACE_TRANSFORM_HORIZONTAL_MIRROR_BIT_KHR,
|
||||
/// Mirror the image horizontally and rotate 90 degrees.
|
||||
HorizontalMirrorRotate90 = vk::SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR,
|
||||
/// Mirror the image horizontally and rotate 180 degrees.
|
||||
HorizontalMirrorRotate180 = vk::SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_180_BIT_KHR,
|
||||
/// Mirror the image horizontally and rotate 270 degrees.
|
||||
HorizontalMirrorRotate270 = vk::SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR,
|
||||
/// Let the operating system or driver implementation choose.
|
||||
Inherit = vk::SURFACE_TRANSFORM_INHERIT_BIT_KHR,
|
||||
}
|
||||
|
||||
/// How the alpha values of the pixels of the window are treated.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
#[repr(u32)]
|
||||
pub enum CompositeAlpha {
|
||||
/// The alpha channel of the image is ignored. All the pixels are considered as if they have a
|
||||
/// value of 1.0.
|
||||
Opaque = vk::COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
|
||||
|
||||
/// The alpha channel of the image is respected. The color channels are expected to have
|
||||
/// already been multiplied by the alpha value.
|
||||
PreMultiplied = vk::COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR,
|
||||
|
||||
/// The alpha channel of the image is respected. The color channels will be multiplied by the
|
||||
/// alpha value by the compositor before being added to what is behind.
|
||||
PostMultiplied = vk::COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR,
|
||||
|
||||
/// Let the operating system or driver implementation choose.
|
||||
Inherit = vk::COMPOSITE_ALPHA_INHERIT_BIT_KHR,
|
||||
}
|
||||
|
||||
/// List of supported composite alpha modes.
|
||||
///
|
||||
/// See the docs of `CompositeAlpha`.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct SupportedCompositeAlpha {
|
||||
pub opaque: bool,
|
||||
pub pre_multiplied: bool,
|
||||
pub post_multiplied: bool,
|
||||
pub inherit: bool,
|
||||
}
|
||||
|
||||
pub fn supported_composite_alpha_from_bits(val: u32) -> SupportedCompositeAlpha {
|
||||
let mut result = SupportedCompositeAlpha::none();
|
||||
if (val & vk::COMPOSITE_ALPHA_OPAQUE_BIT_KHR) != 0 { result.opaque = true; }
|
||||
if (val & vk::COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR) != 0 { result.pre_multiplied = true; }
|
||||
if (val & vk::COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR) != 0 { result.post_multiplied = true; }
|
||||
if (val & vk::COMPOSITE_ALPHA_INHERIT_BIT_KHR) != 0 { result.inherit = true; }
|
||||
result
|
||||
}
|
||||
|
||||
impl SupportedCompositeAlpha {
|
||||
/// Builds a `SupportedCompositeAlpha` with all fields set to false.
|
||||
#[inline]
|
||||
pub fn none() -> SupportedCompositeAlpha {
|
||||
SupportedCompositeAlpha {
|
||||
opaque: false,
|
||||
pre_multiplied: false,
|
||||
post_multiplied: false,
|
||||
inherit: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if the given `CompositeAlpha` is in this list.
|
||||
#[inline]
|
||||
pub fn supports(&self, value: CompositeAlpha) -> bool {
|
||||
match value {
|
||||
CompositeAlpha::Opaque => self.opaque,
|
||||
CompositeAlpha::PreMultiplied => self.pre_multiplied,
|
||||
CompositeAlpha::PostMultiplied => self.post_multiplied,
|
||||
CompositeAlpha::Inherit => self.inherit,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an iterator to the list of supported composite alpha.
|
||||
#[inline]
|
||||
pub fn iter(&self) -> SupportedCompositeAlphaIter {
|
||||
SupportedCompositeAlphaIter(self.clone())
|
||||
}
|
||||
}
|
||||
|
||||
/// Enumeration of the `CompositeAlpha` that are supported.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub struct SupportedCompositeAlphaIter(SupportedCompositeAlpha);
|
||||
|
||||
impl Iterator for SupportedCompositeAlphaIter {
|
||||
type Item = CompositeAlpha;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<CompositeAlpha> {
|
||||
if self.0.opaque { self.0.opaque = false; return Some(CompositeAlpha::Opaque); }
|
||||
if self.0.pre_multiplied { self.0.pre_multiplied = false; return Some(CompositeAlpha::PreMultiplied); }
|
||||
if self.0.post_multiplied { self.0.post_multiplied = false; return Some(CompositeAlpha::PostMultiplied); }
|
||||
if self.0.inherit { self.0.inherit = false; return Some(CompositeAlpha::Inherit); }
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// List of supported composite alpha modes.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub struct SupportedSurfaceTransforms {
|
||||
pub identity: bool,
|
||||
pub rotate90: bool,
|
||||
pub rotate180: bool,
|
||||
pub rotate270: bool,
|
||||
pub horizontal_mirror: bool,
|
||||
pub horizontal_mirror_rotate90: bool,
|
||||
pub horizontal_mirror_rotate180: bool,
|
||||
pub horizontal_mirror_rotate270: bool,
|
||||
pub inherit: bool,
|
||||
}
|
||||
|
||||
pub fn surface_transforms_from_bits(val: vk::SurfaceTransformFlagsKHR) -> SupportedSurfaceTransforms {
|
||||
macro_rules! v {
|
||||
($val:expr, $out:ident, $e:expr, $f:ident) => (
|
||||
if ($val & $e) != 0 { $out.$f = true; }
|
||||
);
|
||||
}
|
||||
|
||||
let mut result = SupportedSurfaceTransforms::none();
|
||||
v!(val, result, vk::SURFACE_TRANSFORM_IDENTITY_BIT_KHR, identity);
|
||||
v!(val, result, vk::SURFACE_TRANSFORM_ROTATE_90_BIT_KHR, rotate90);
|
||||
v!(val, result, vk::SURFACE_TRANSFORM_ROTATE_180_BIT_KHR, rotate180);
|
||||
v!(val, result, vk::SURFACE_TRANSFORM_ROTATE_270_BIT_KHR, rotate270);
|
||||
v!(val, result, vk::SURFACE_TRANSFORM_HORIZONTAL_MIRROR_BIT_KHR, horizontal_mirror);
|
||||
v!(val, result, vk::SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR,
|
||||
horizontal_mirror_rotate90);
|
||||
v!(val, result, vk::SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_180_BIT_KHR,
|
||||
horizontal_mirror_rotate180);
|
||||
v!(val, result, vk::SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR,
|
||||
horizontal_mirror_rotate270);
|
||||
v!(val, result, vk::SURFACE_TRANSFORM_INHERIT_BIT_KHR, inherit);
|
||||
result
|
||||
}
|
||||
|
||||
impl SupportedSurfaceTransforms {
|
||||
/// Builds a `SupportedSurfaceTransforms` with all fields set to false.
|
||||
#[inline]
|
||||
pub fn none() -> SupportedSurfaceTransforms {
|
||||
SupportedSurfaceTransforms {
|
||||
identity: false,
|
||||
rotate90: false,
|
||||
rotate180: false,
|
||||
rotate270: false,
|
||||
horizontal_mirror: false,
|
||||
horizontal_mirror_rotate90: false,
|
||||
horizontal_mirror_rotate180: false,
|
||||
horizontal_mirror_rotate270: false,
|
||||
inherit: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if the given `SurfaceTransform` is in this list.
|
||||
#[inline]
|
||||
pub fn supports(&self, value: SurfaceTransform) -> bool {
|
||||
match value {
|
||||
SurfaceTransform::Identity => self.identity,
|
||||
SurfaceTransform::Rotate90 => self.rotate90,
|
||||
SurfaceTransform::Rotate180 => self.rotate180,
|
||||
SurfaceTransform::Rotate270 => self.rotate270,
|
||||
SurfaceTransform::HorizontalMirror => self.horizontal_mirror,
|
||||
SurfaceTransform::HorizontalMirrorRotate90 => self.horizontal_mirror_rotate90,
|
||||
SurfaceTransform::HorizontalMirrorRotate180 => self.horizontal_mirror_rotate180,
|
||||
SurfaceTransform::HorizontalMirrorRotate270 => self.horizontal_mirror_rotate270,
|
||||
SurfaceTransform::Inherit => self.inherit,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an iterator to the list of supported composite alpha.
|
||||
#[inline]
|
||||
pub fn iter(&self) -> SupportedSurfaceTransformsIter {
|
||||
SupportedSurfaceTransformsIter(self.clone())
|
||||
}
|
||||
}
|
||||
|
||||
/// Enumeration of the `SurfaceTransform` that are supported.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub struct SupportedSurfaceTransformsIter(SupportedSurfaceTransforms);
|
||||
|
||||
impl Iterator for SupportedSurfaceTransformsIter {
|
||||
type Item = SurfaceTransform;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<SurfaceTransform> {
|
||||
if self.0.identity { self.0.identity = false; return Some(SurfaceTransform::Identity); }
|
||||
if self.0.rotate90 { self.0.rotate90 = false; return Some(SurfaceTransform::Rotate90); }
|
||||
if self.0.rotate180 { self.0.rotate180 = false; return Some(SurfaceTransform::Rotate180); }
|
||||
if self.0.rotate270 { self.0.rotate270 = false; return Some(SurfaceTransform::Rotate270); }
|
||||
if self.0.horizontal_mirror { self.0.horizontal_mirror = false; return Some(SurfaceTransform::HorizontalMirror); }
|
||||
if self.0.horizontal_mirror_rotate90 { self.0.horizontal_mirror_rotate90 = false; return Some(SurfaceTransform::HorizontalMirrorRotate90); }
|
||||
if self.0.horizontal_mirror_rotate180 { self.0.horizontal_mirror_rotate180 = false; return Some(SurfaceTransform::HorizontalMirrorRotate180); }
|
||||
if self.0.horizontal_mirror_rotate270 { self.0.horizontal_mirror_rotate270 = false; return Some(SurfaceTransform::HorizontalMirrorRotate270); }
|
||||
if self.0.inherit { self.0.inherit = false; return Some(SurfaceTransform::Inherit); }
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for SurfaceTransform {
|
||||
#[inline]
|
||||
fn default() -> SurfaceTransform {
|
||||
SurfaceTransform::Identity
|
||||
}
|
||||
}
|
||||
|
||||
/// How the presentation engine should interpret the data.
|
||||
///
|
||||
/// # A quick lesson about color spaces
|
||||
///
|
||||
/// ## What is a color space?
|
||||
///
|
||||
/// Each pixel of a monitor is made of three components: one red, one green, and one blue. In the
|
||||
/// past, computers would simply send to the monitor the intensity of each of the three components.
|
||||
///
|
||||
/// This proved to be problematic, because depending on the brand of the monitor the colors would
|
||||
/// not exactly be the same. For example on some monitors, a value of `[1.0, 0.0, 0.0]` would be a
|
||||
/// bit more orange than on others.
|
||||
///
|
||||
/// In order to standardize this, there exist what are called *color spaces*: sRGB, AdobeRGB,
|
||||
/// DCI-P3, scRGB, etc. When you manipulate RGB values in a specific color space, these values have
|
||||
/// a precise absolute meaning in terms of color, that is the same across all systems and monitors.
|
||||
///
|
||||
/// > **Note**: Color spaces are orthogonal to concept of RGB. *RGB* only indicates what is the
|
||||
/// > representation of the data, but not how it is interpreted. You can think of this a bit like
|
||||
/// > text encoding. An *RGB* value is a like a byte, in other words it is the medium by which
|
||||
/// > values are communicated, and a *color space* is like a text encoding (eg. UTF-8), in other
|
||||
/// > words it is the way the value should be interpreted.
|
||||
///
|
||||
/// The most commonly used color space today is sRGB. Most monitors today use this color space,
|
||||
/// and most images files are encoded in this color space.
|
||||
///
|
||||
/// ## Pixel formats and linear vs non-linear
|
||||
///
|
||||
/// In Vulkan all images have a specific format in which the data is stored. The data of an image
|
||||
/// consists of pixels in RGB but contains no information about the color space (or lack thereof)
|
||||
/// of these pixels. You are free to store them in whatever color space you want.
|
||||
///
|
||||
/// But one big practical problem with color spaces is that they are sometimes not linear, and in
|
||||
/// particular the popular sRGB color space is not linear. In a non-linear color space, a value of
|
||||
/// `[0.6, 0.6, 0.6]` for example is **not** twice as bright as a value of `[0.3, 0.3, 0.3]`. This
|
||||
/// is problematic, because operations such as taking the average of two colors or calculating the
|
||||
/// lighting of a texture with a dot product are mathematically incorrect and will produce
|
||||
/// incorrect colors.
|
||||
///
|
||||
/// > **Note**: If the texture format has an alpha component, it is not affected by the color space
|
||||
/// > and always behaves linearly.
|
||||
///
|
||||
/// In order to solve this Vulkan also provides image formats with the `Srgb` suffix, which are
|
||||
/// expected to contain RGB data in the sRGB color space. When you sample an image with such a
|
||||
/// format from a shader, the implementation will automatically turn the pixel values into a linear
|
||||
/// color space that is suitable for linear operations (such as additions or multiplications).
|
||||
/// When you write to a framebuffer attachment with such a format, the implementation will
|
||||
/// automatically perform the opposite conversion. These conversions are most of the time performed
|
||||
/// by the hardware and incur no additional cost.
|
||||
///
|
||||
/// ## Color space of the swapchain
|
||||
///
|
||||
/// The color space that you specify when you create a swapchain is how the implementation will
|
||||
/// interpret the raw data inside of the image.
|
||||
///
|
||||
/// > **Note**: The implementation can choose to send the data in the swapchain image directly to
|
||||
/// > the monitor, but it can also choose to write it in an intermediary buffer that is then read
|
||||
/// > by the operating system or windowing system. Therefore the color space that the
|
||||
/// > implementation supports is not necessarily the same as the one supported by the monitor.
|
||||
///
|
||||
/// It is *your* job to ensure that the data in the swapchain image is in the color space
|
||||
/// that is specified here, otherwise colors will be incorrect.
|
||||
/// The implementation will never perform any additional automatic conversion after the colors have
|
||||
/// been written to the swapchain image.
|
||||
///
|
||||
/// # How do I handle this correctly?
|
||||
///
|
||||
/// The easiest way to handle color spaces in a cross-platform program is:
|
||||
///
|
||||
/// - Always request the `SrgbNonLinear` color space when creating the swapchain.
|
||||
/// - Make sure that all your image files use the sRGB color space, and load them in images whose
|
||||
/// format has the `Srgb` suffix. Only use non-sRGB image formats for intermediary computations
|
||||
/// or to store non-color data.
|
||||
/// - Swapchain images should have a format with the `Srgb` suffix.
|
||||
///
|
||||
/// > **Note**: It is unclear whether the `SrgbNonLinear` color space is always supported by the
|
||||
/// > the implementation or not. See https://github.com/KhronosGroup/Vulkan-Docs/issues/442.
|
||||
///
|
||||
/// > **Note**: Lots of developers are confused by color spaces. You can sometimes find articles
|
||||
/// > talking about gamma correction and suggestion to put your colors to the power 2.2 for
|
||||
/// > example. These are all hacks and you should use the sRGB pixel formats intead.
|
||||
///
|
||||
/// If you follow these three rules, then everything should render the same way on all platforms.
|
||||
///
|
||||
/// Additionally you can try detect whether the implementation supports any additional color space
|
||||
/// and perform a manual conversion to that color space from inside your shader.
|
||||
///
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
#[repr(u32)]
|
||||
pub enum ColorSpace {
|
||||
SrgbNonLinear = vk::COLOR_SPACE_SRGB_NONLINEAR_KHR,
|
||||
DisplayP3Linear = vk::COLOR_SPACE_DISPLAY_P3_LINEAR_EXT,
|
||||
DisplayP3NonLinear = vk::COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT,
|
||||
ScrgbLinear = vk::COLOR_SPACE_SCRGB_LINEAR_EXT,
|
||||
ScrgbNonLinear = vk::COLOR_SPACE_SCRGB_NONLINEAR_EXT,
|
||||
DciP3Linear = vk::COLOR_SPACE_DCI_P3_LINEAR_EXT,
|
||||
DciP3NonLinear = vk::COLOR_SPACE_DCI_P3_NONLINEAR_EXT,
|
||||
Bt709Linear = vk::COLOR_SPACE_BT709_LINEAR_EXT,
|
||||
Bt709NonLinear = vk::COLOR_SPACE_BT709_NONLINEAR_EXT,
|
||||
Bt2020Linear = vk::COLOR_SPACE_BT2020_LINEAR_EXT,
|
||||
Bt2020NonLinear = vk::COLOR_SPACE_BT2020_NONLINEAR_EXT,
|
||||
AdobeRgbLinear = vk::COLOR_SPACE_ADOBERGB_LINEAR_EXT,
|
||||
AdobeRgbNonLinear = vk::COLOR_SPACE_ADOBERGB_NONLINEAR_EXT,
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn color_space_from_num(val: u32) -> ColorSpace {
|
||||
match val {
|
||||
vk::COLOR_SPACE_SRGB_NONLINEAR_KHR => ColorSpace::SrgbNonLinear,
|
||||
vk::COLOR_SPACE_DISPLAY_P3_LINEAR_EXT => ColorSpace::DisplayP3Linear,
|
||||
vk::COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT => ColorSpace::DisplayP3NonLinear,
|
||||
vk::COLOR_SPACE_SCRGB_LINEAR_EXT => ColorSpace::ScrgbLinear,
|
||||
vk::COLOR_SPACE_SCRGB_NONLINEAR_EXT => ColorSpace::ScrgbNonLinear,
|
||||
vk::COLOR_SPACE_DCI_P3_LINEAR_EXT => ColorSpace::DciP3Linear,
|
||||
vk::COLOR_SPACE_DCI_P3_NONLINEAR_EXT => ColorSpace::DciP3NonLinear,
|
||||
vk::COLOR_SPACE_BT709_LINEAR_EXT => ColorSpace::Bt709Linear,
|
||||
vk::COLOR_SPACE_BT709_NONLINEAR_EXT => ColorSpace::Bt709NonLinear,
|
||||
vk::COLOR_SPACE_BT2020_LINEAR_EXT => ColorSpace::Bt2020Linear,
|
||||
vk::COLOR_SPACE_BT2020_NONLINEAR_EXT => ColorSpace::Bt2020NonLinear,
|
||||
vk::COLOR_SPACE_ADOBERGB_LINEAR_EXT => ColorSpace::AdobeRgbLinear,
|
||||
vk::COLOR_SPACE_ADOBERGB_NONLINEAR_EXT => ColorSpace::AdobeRgbNonLinear,
|
||||
_ => panic!("Wrong value for color space enum")
|
||||
}
|
||||
}
|
@ -35,6 +35,7 @@ use std::vec::IntoIter;
|
||||
|
||||
use instance::Instance;
|
||||
use instance::PhysicalDevice;
|
||||
use swapchain::capabilities;
|
||||
use swapchain::SupportedSurfaceTransforms;
|
||||
|
||||
use check_errors;
|
||||
@ -233,7 +234,7 @@ impl Display {
|
||||
/// Returns the transforms supported by this display.
|
||||
#[inline]
|
||||
pub fn supported_transforms(&self) -> SupportedSurfaceTransforms {
|
||||
SupportedSurfaceTransforms::from_bits(self.properties.supportedTransforms)
|
||||
capabilities::surface_transforms_from_bits(self.properties.supportedTransforms)
|
||||
}
|
||||
|
||||
/// Returns true if TODO.
|
||||
|
@ -72,10 +72,10 @@
|
||||
//! };
|
||||
//!
|
||||
//! # fn build_window() -> *const u32 { ptr::null() }
|
||||
//! let window = build_window();
|
||||
//! let window = build_window(); // Third-party function, not provided by vulkano
|
||||
//! let _surface = unsafe {
|
||||
//! let hinstance: *const () = ptr::null(); // Windows-specific object
|
||||
//! Surface::from_hwnd(&instance, hinstance, window).unwrap()
|
||||
//! Surface::from_hwnd(instance.clone(), hinstance, window).unwrap()
|
||||
//! };
|
||||
//! ```
|
||||
//!
|
||||
@ -124,7 +124,7 @@
|
||||
//! Once you created a swapchain and retreived all the images that belong to it (see previous
|
||||
//! section), you can draw on it. This is done in three steps:
|
||||
//!
|
||||
//! - Call `Swapchain::acquire_next_image`. This function will return the index of the image
|
||||
//! - Call `swapchain::acquire_next_image`. This function will return the index of the image
|
||||
//! (within the list returned by `Swapchain::new`) that is available to draw, plus a future
|
||||
//! representing the moment when the GPU will gain access to that image.
|
||||
//! - Draw on that image just like you would draw to any other image (see the documentation of
|
||||
@ -136,9 +136,9 @@
|
||||
//!
|
||||
//! TODO: add example here
|
||||
//! loop {
|
||||
//! let index = swapchain.acquire_next_image(Duration::from_millis(500)).unwrap();
|
||||
//! let index = swapchain::acquire_next_image(Duration::from_millis(500)).unwrap();
|
||||
//! draw(images[index]);
|
||||
//! swapchain.present(queue, index).unwrap();
|
||||
//! swapchain::present(queue, index).unwrap();
|
||||
//! }
|
||||
//!
|
||||
//! ## Recreating a swapchain
|
||||
@ -156,6 +156,7 @@
|
||||
//!
|
||||
//! ```
|
||||
//! # use std::time::Duration;
|
||||
//! use vulkano::swapchain;
|
||||
//! use vulkano::swapchain::AcquireError;
|
||||
//! use vulkano::sync::GpuFuture;
|
||||
//!
|
||||
@ -172,7 +173,7 @@
|
||||
//!
|
||||
//! let (ref swapchain, ref _images) = swapchain;
|
||||
//!
|
||||
//! let (index, acq_future) = match swapchain.acquire_next_image(Duration::from_millis(500)) {
|
||||
//! let (index, acq_future) = match swapchain::acquire_next_image(swapchain.clone(), Duration::from_millis(500)) {
|
||||
//! Ok(r) => r,
|
||||
//! Err(AcquireError::OutOfDate) => { recreate_swapchain = true; continue; },
|
||||
//! Err(err) => panic!("{:?}", err)
|
||||
@ -191,137 +192,35 @@
|
||||
//!
|
||||
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use vk;
|
||||
|
||||
pub use self::surface::Capabilities;
|
||||
pub use self::capabilities::Capabilities;
|
||||
pub use self::capabilities::PresentMode;
|
||||
pub use self::capabilities::SupportedPresentModes;
|
||||
pub use self::capabilities::SupportedPresentModesIter;
|
||||
pub use self::capabilities::SurfaceTransform;
|
||||
pub use self::capabilities::CompositeAlpha;
|
||||
pub use self::capabilities::SupportedCompositeAlpha;
|
||||
pub use self::capabilities::SupportedCompositeAlphaIter;
|
||||
pub use self::capabilities::ColorSpace;
|
||||
pub use self::capabilities::SupportedSurfaceTransforms;
|
||||
pub use self::capabilities::SupportedSurfaceTransformsIter;
|
||||
pub use self::surface::Surface;
|
||||
pub use self::surface::PresentMode;
|
||||
pub use self::surface::SupportedPresentModes;
|
||||
pub use self::surface::SupportedPresentModesIter;
|
||||
pub use self::surface::SurfaceTransform;
|
||||
pub use self::surface::CompositeAlpha;
|
||||
pub use self::surface::SupportedCompositeAlpha;
|
||||
pub use self::surface::SupportedCompositeAlphaIter;
|
||||
pub use self::surface::ColorSpace;
|
||||
pub use self::surface::SurfaceCreationError;
|
||||
pub use self::swapchain::AcquireError;
|
||||
pub use self::swapchain::PresentFuture;
|
||||
pub use self::swapchain::Swapchain;
|
||||
pub use self::swapchain::SwapchainAcquireFuture;
|
||||
pub use self::swapchain::acquire_next_image;
|
||||
pub use self::swapchain::present;
|
||||
|
||||
mod capabilities;
|
||||
pub mod display;
|
||||
mod surface;
|
||||
mod swapchain;
|
||||
|
||||
/// List of supported composite alpha modes.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub struct SupportedSurfaceTransforms {
|
||||
pub identity: bool,
|
||||
pub rotate90: bool,
|
||||
pub rotate180: bool,
|
||||
pub rotate270: bool,
|
||||
pub horizontal_mirror: bool,
|
||||
pub horizontal_mirror_rotate90: bool,
|
||||
pub horizontal_mirror_rotate180: bool,
|
||||
pub horizontal_mirror_rotate270: bool,
|
||||
pub inherit: bool,
|
||||
}
|
||||
|
||||
impl SupportedSurfaceTransforms {
|
||||
/// Builds a `SupportedSurfaceTransforms` with all fields set to false.
|
||||
#[inline]
|
||||
pub fn none() -> SupportedSurfaceTransforms {
|
||||
SupportedSurfaceTransforms {
|
||||
identity: false,
|
||||
rotate90: false,
|
||||
rotate180: false,
|
||||
rotate270: false,
|
||||
horizontal_mirror: false,
|
||||
horizontal_mirror_rotate90: false,
|
||||
horizontal_mirror_rotate180: false,
|
||||
horizontal_mirror_rotate270: false,
|
||||
inherit: false,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn from_bits(val: vk::SurfaceTransformFlagsKHR) -> SupportedSurfaceTransforms {
|
||||
macro_rules! v {
|
||||
($val:expr, $out:ident, $e:expr, $f:ident) => (
|
||||
if ($val & $e) != 0 { $out.$f = true; }
|
||||
);
|
||||
}
|
||||
|
||||
let mut result = SupportedSurfaceTransforms::none();
|
||||
v!(val, result, vk::SURFACE_TRANSFORM_IDENTITY_BIT_KHR, identity);
|
||||
v!(val, result, vk::SURFACE_TRANSFORM_ROTATE_90_BIT_KHR, rotate90);
|
||||
v!(val, result, vk::SURFACE_TRANSFORM_ROTATE_180_BIT_KHR, rotate180);
|
||||
v!(val, result, vk::SURFACE_TRANSFORM_ROTATE_270_BIT_KHR, rotate270);
|
||||
v!(val, result, vk::SURFACE_TRANSFORM_HORIZONTAL_MIRROR_BIT_KHR, horizontal_mirror);
|
||||
v!(val, result, vk::SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR,
|
||||
horizontal_mirror_rotate90);
|
||||
v!(val, result, vk::SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_180_BIT_KHR,
|
||||
horizontal_mirror_rotate180);
|
||||
v!(val, result, vk::SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR,
|
||||
horizontal_mirror_rotate270);
|
||||
v!(val, result, vk::SURFACE_TRANSFORM_INHERIT_BIT_KHR, inherit);
|
||||
result
|
||||
}
|
||||
|
||||
/// Returns true if the given `SurfaceTransform` is in this list.
|
||||
#[inline]
|
||||
pub fn supports(&self, value: SurfaceTransform) -> bool {
|
||||
match value {
|
||||
SurfaceTransform::Identity => self.identity,
|
||||
SurfaceTransform::Rotate90 => self.rotate90,
|
||||
SurfaceTransform::Rotate180 => self.rotate180,
|
||||
SurfaceTransform::Rotate270 => self.rotate270,
|
||||
SurfaceTransform::HorizontalMirror => self.horizontal_mirror,
|
||||
SurfaceTransform::HorizontalMirrorRotate90 => self.horizontal_mirror_rotate90,
|
||||
SurfaceTransform::HorizontalMirrorRotate180 => self.horizontal_mirror_rotate180,
|
||||
SurfaceTransform::HorizontalMirrorRotate270 => self.horizontal_mirror_rotate270,
|
||||
SurfaceTransform::Inherit => self.inherit,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an iterator to the list of supported composite alpha.
|
||||
#[inline]
|
||||
pub fn iter(&self) -> SupportedSurfaceTransformsIter {
|
||||
SupportedSurfaceTransformsIter(self.clone())
|
||||
}
|
||||
}
|
||||
|
||||
/// Enumeration of the `SurfaceTransform` that are supported.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub struct SupportedSurfaceTransformsIter(SupportedSurfaceTransforms);
|
||||
|
||||
impl Iterator for SupportedSurfaceTransformsIter {
|
||||
type Item = SurfaceTransform;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<SurfaceTransform> {
|
||||
if self.0.identity { self.0.identity = false; return Some(SurfaceTransform::Identity); }
|
||||
if self.0.rotate90 { self.0.rotate90 = false; return Some(SurfaceTransform::Rotate90); }
|
||||
if self.0.rotate180 { self.0.rotate180 = false; return Some(SurfaceTransform::Rotate180); }
|
||||
if self.0.rotate270 { self.0.rotate270 = false; return Some(SurfaceTransform::Rotate270); }
|
||||
if self.0.horizontal_mirror { self.0.horizontal_mirror = false; return Some(SurfaceTransform::HorizontalMirror); }
|
||||
if self.0.horizontal_mirror_rotate90 { self.0.horizontal_mirror_rotate90 = false; return Some(SurfaceTransform::HorizontalMirrorRotate90); }
|
||||
if self.0.horizontal_mirror_rotate180 { self.0.horizontal_mirror_rotate180 = false; return Some(SurfaceTransform::HorizontalMirrorRotate180); }
|
||||
if self.0.horizontal_mirror_rotate270 { self.0.horizontal_mirror_rotate270 = false; return Some(SurfaceTransform::HorizontalMirrorRotate270); }
|
||||
if self.0.inherit { self.0.inherit = false; return Some(SurfaceTransform::Inherit); }
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for SurfaceTransform {
|
||||
#[inline]
|
||||
fn default() -> SurfaceTransform {
|
||||
SurfaceTransform::Identity
|
||||
}
|
||||
}
|
||||
|
||||
/// Internal trait so that creating/destroying a swapchain can access the surface's "has_swapchain"
|
||||
/// flag.
|
||||
// TODO: use pub(crate) maybe?
|
||||
unsafe trait SurfaceSwapchainLock {
|
||||
fn flag(&self) -> &AtomicBool;
|
||||
}
|
||||
|
@ -16,12 +16,13 @@ use std::sync::Arc;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
|
||||
use format::Format;
|
||||
use image::Usage as ImageUsage;
|
||||
use image::ImageUsage;
|
||||
use instance::Instance;
|
||||
use instance::PhysicalDevice;
|
||||
use instance::QueueFamily;
|
||||
use swapchain::capabilities;
|
||||
use swapchain::Capabilities;
|
||||
use swapchain::SurfaceSwapchainLock;
|
||||
use swapchain::SupportedSurfaceTransforms;
|
||||
use swapchain::display::DisplayMode;
|
||||
use swapchain::display::DisplayPlane;
|
||||
|
||||
@ -104,7 +105,7 @@ impl Surface {
|
||||
///
|
||||
/// The caller must ensure that the `hinstance` and the `hwnd` are both correct and stay
|
||||
/// alive for the entire lifetime of the surface.
|
||||
pub unsafe fn from_hwnd<T, U>(instance: &Arc<Instance>, hinstance: *const T, hwnd: *const U)
|
||||
pub unsafe fn from_hwnd<T, U>(instance: Arc<Instance>, hinstance: *const T, hwnd: *const U)
|
||||
-> Result<Arc<Surface>, SurfaceCreationError>
|
||||
{
|
||||
let vk = instance.pointers();
|
||||
@ -143,7 +144,7 @@ impl Surface {
|
||||
///
|
||||
/// The caller must ensure that the `connection` and the `window` are both correct and stay
|
||||
/// alive for the entire lifetime of the surface.
|
||||
pub unsafe fn from_xcb<C>(instance: &Arc<Instance>, connection: *const C, window: u32)
|
||||
pub unsafe fn from_xcb<C>(instance: Arc<Instance>, connection: *const C, window: u32)
|
||||
-> Result<Arc<Surface>, SurfaceCreationError>
|
||||
{
|
||||
let vk = instance.pointers();
|
||||
@ -182,7 +183,7 @@ impl Surface {
|
||||
///
|
||||
/// The caller must ensure that the `display` and the `window` are both correct and stay
|
||||
/// alive for the entire lifetime of the surface.
|
||||
pub unsafe fn from_xlib<D>(instance: &Arc<Instance>, display: *const D, window: c_ulong)
|
||||
pub unsafe fn from_xlib<D>(instance: Arc<Instance>, display: *const D, window: c_ulong)
|
||||
-> Result<Arc<Surface>, SurfaceCreationError>
|
||||
{
|
||||
let vk = instance.pointers();
|
||||
@ -221,7 +222,7 @@ impl Surface {
|
||||
///
|
||||
/// The caller must ensure that the `display` and the `surface` are both correct and stay
|
||||
/// alive for the entire lifetime of the surface.
|
||||
pub unsafe fn from_wayland<D, S>(instance: &Arc<Instance>, display: *const D, surface: *const S)
|
||||
pub unsafe fn from_wayland<D, S>(instance: Arc<Instance>, display: *const D, surface: *const S)
|
||||
-> Result<Arc<Surface>, SurfaceCreationError>
|
||||
{
|
||||
let vk = instance.pointers();
|
||||
@ -261,7 +262,7 @@ impl Surface {
|
||||
///
|
||||
/// The caller must ensure that the `connection` and the `surface` are both correct and stay
|
||||
/// alive for the entire lifetime of the surface.
|
||||
pub unsafe fn from_mir<C, S>(instance: &Arc<Instance>, connection: *const C, surface: *const S)
|
||||
pub unsafe fn from_mir<C, S>(instance: Arc<Instance>, connection: *const C, surface: *const S)
|
||||
-> Result<Arc<Surface>, SurfaceCreationError>
|
||||
{
|
||||
let vk = instance.pointers();
|
||||
@ -298,7 +299,7 @@ impl Surface {
|
||||
///
|
||||
/// The caller must ensure that the `window` is correct and stays alive for the entire
|
||||
/// lifetime of the surface.
|
||||
pub unsafe fn from_anativewindow<T>(instance: &Arc<Instance>, window: *const T)
|
||||
pub unsafe fn from_anativewindow<T>(instance: Arc<Instance>, window: *const T)
|
||||
-> Result<Arc<Surface>, SurfaceCreationError>
|
||||
{
|
||||
let vk = instance.pointers();
|
||||
@ -408,7 +409,7 @@ impl Surface {
|
||||
///
|
||||
/// The caller must ensure that the `window` is correct and stays alive for the entire
|
||||
/// lifetime of the surface.
|
||||
pub unsafe fn from_vi_surface<T>(instance: &Arc<Instance>, window: *const T)
|
||||
pub unsafe fn from_vi_surface<T>(instance: Arc<Instance>, window: *const T)
|
||||
-> Result<Arc<Surface>, SurfaceCreationError>
|
||||
{
|
||||
let vk = instance.pointers();
|
||||
@ -439,7 +440,8 @@ impl Surface {
|
||||
}
|
||||
|
||||
/// Returns true if the given queue family can draw on this surface.
|
||||
pub fn is_supported(&self, queue: &QueueFamily) -> Result<bool, OomError> {
|
||||
// FIXME: vulkano doesn't check this for the moment!
|
||||
pub fn is_supported(&self, queue: QueueFamily) -> Result<bool, CapabilitiesError> {
|
||||
unsafe {
|
||||
let vk = self.instance.pointers();
|
||||
|
||||
@ -452,15 +454,22 @@ impl Surface {
|
||||
}
|
||||
}
|
||||
|
||||
#[deprecated = "Renamed to `capabilities`"]
|
||||
#[inline]
|
||||
pub fn get_capabilities(&self, device: &PhysicalDevice) -> Result<Capabilities, OomError> {
|
||||
Ok(self.capabilities(*device).unwrap())
|
||||
}
|
||||
|
||||
/// Retreives the capabilities of a surface when used by a certain device.
|
||||
///
|
||||
/// # Panic
|
||||
///
|
||||
/// - Panics if the device and the surface don't belong to the same instance.
|
||||
///
|
||||
pub fn get_capabilities(&self, device: &PhysicalDevice) -> Result<Capabilities, OomError> { // TODO: wrong error type
|
||||
pub fn capabilities(&self, device: PhysicalDevice) -> Result<Capabilities, CapabilitiesError> {
|
||||
unsafe {
|
||||
assert_eq!(&*self.instance as *const _, &**device.instance() as *const _);
|
||||
assert_eq!(&*self.instance as *const _, &**device.instance() as *const _,
|
||||
"Instance mismatch in Surface::capabilities");
|
||||
|
||||
let vk = self.instance.pointers();
|
||||
|
||||
@ -510,7 +519,7 @@ impl Surface {
|
||||
// https://bugs.freedesktop.org/show_bug.cgi?id=97153
|
||||
// debug_assert!(modes.iter().find(|&&m| m == vk::PRESENT_MODE_FIFO_KHR).is_some());
|
||||
debug_assert!(modes.iter().count() > 0);
|
||||
SupportedPresentModes::from_list(modes.into_iter())
|
||||
capabilities::supported_present_modes_from_list(modes.into_iter())
|
||||
};
|
||||
|
||||
Ok(Capabilities {
|
||||
@ -527,16 +536,16 @@ impl Surface {
|
||||
min_image_extent: [caps.minImageExtent.width, caps.minImageExtent.height],
|
||||
max_image_extent: [caps.maxImageExtent.width, caps.maxImageExtent.height],
|
||||
max_image_array_layers: caps.maxImageArrayLayers,
|
||||
supported_transforms: SupportedSurfaceTransforms::from_bits(caps.supportedTransforms),
|
||||
current_transform: SupportedSurfaceTransforms::from_bits(caps.supportedTransforms).iter().next().unwrap(), // TODO:
|
||||
supported_composite_alpha: SupportedCompositeAlpha::from_bits(caps.supportedCompositeAlpha),
|
||||
supported_transforms: capabilities::surface_transforms_from_bits(caps.supportedTransforms),
|
||||
current_transform: capabilities::surface_transforms_from_bits(caps.currentTransform).iter().next().unwrap(), // TODO:
|
||||
supported_composite_alpha: capabilities::supported_composite_alpha_from_bits(caps.supportedCompositeAlpha),
|
||||
supported_usage_flags: {
|
||||
let usage = ImageUsage::from_bits(caps.supportedUsageFlags);
|
||||
debug_assert!(usage.color_attachment); // specs say that this must be true
|
||||
usage
|
||||
},
|
||||
supported_formats: formats.into_iter().map(|f| {
|
||||
(Format::from_num(f.format).unwrap(), ColorSpace::from_num(f.colorSpace))
|
||||
(Format::from_num(f.format).unwrap(), capabilities::color_space_from_num(f.colorSpace))
|
||||
}).collect(),
|
||||
present_modes: modes,
|
||||
})
|
||||
@ -634,386 +643,57 @@ impl From<Error> for SurfaceCreationError {
|
||||
}
|
||||
}
|
||||
|
||||
/// The capabilities of a surface when used by a physical device.
|
||||
///
|
||||
/// You have to match these capabilities when you create a swapchain.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Capabilities {
|
||||
/// Minimum number of images that must be present in the swapchain.
|
||||
pub min_image_count: u32,
|
||||
|
||||
/// Maximum number of images that must be present in the swapchain, or `None` if there is no
|
||||
/// maximum value. Note that "no maximum" doesn't mean that you can set a very high value, as
|
||||
/// you may still get out of memory errors.
|
||||
pub max_image_count: Option<u32>,
|
||||
|
||||
/// The current dimensions of the surface. `None` means that the surface's dimensions will
|
||||
/// depend on the dimensions of the swapchain that you are going to create.
|
||||
pub current_extent: Option<[u32; 2]>,
|
||||
|
||||
/// Minimum width and height of a swapchain that uses this surface.
|
||||
pub min_image_extent: [u32; 2],
|
||||
|
||||
/// Maximum width and height of a swapchain that uses this surface.
|
||||
pub max_image_extent: [u32; 2],
|
||||
|
||||
/// Maximum number of image layers if you create an image array. The minimum is 1.
|
||||
pub max_image_array_layers: u32,
|
||||
|
||||
/// List of transforms supported for the swapchain.
|
||||
pub supported_transforms: SupportedSurfaceTransforms,
|
||||
|
||||
/// Current transform used by the surface.
|
||||
pub current_transform: SurfaceTransform,
|
||||
|
||||
/// List of composite alpha modes supports for the swapchain.
|
||||
pub supported_composite_alpha: SupportedCompositeAlpha,
|
||||
|
||||
/// List of image usages that are supported for images of the swapchain. Only
|
||||
/// the `color_attachment` usage is guaranteed to be supported.
|
||||
pub supported_usage_flags: ImageUsage,
|
||||
|
||||
/// List of formats supported for the swapchain.
|
||||
pub supported_formats: Vec<(Format, ColorSpace)>, // TODO: https://github.com/KhronosGroup/Vulkan-Docs/issues/207
|
||||
|
||||
/// List of present modes that are supported. `Fifo` is always guaranteed to be supported.
|
||||
pub present_modes: SupportedPresentModes,
|
||||
}
|
||||
|
||||
/// The way presenting a swapchain is accomplished.
|
||||
/// Error that can happen when retreiving a surface's capabilities.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
#[repr(u32)]
|
||||
pub enum PresentMode {
|
||||
/// Immediately shows the image to the user. May result in visible tearing.
|
||||
Immediate = vk::PRESENT_MODE_IMMEDIATE_KHR,
|
||||
pub enum CapabilitiesError {
|
||||
/// Not enough memory.
|
||||
OomError(OomError),
|
||||
|
||||
/// The action of presenting an image puts it in wait. When the next vertical blanking period
|
||||
/// happens, the waiting image is effectively shown to the user. If an image is presented while
|
||||
/// another one is waiting, it is replaced.
|
||||
Mailbox = vk::PRESENT_MODE_MAILBOX_KHR,
|
||||
|
||||
/// The action of presenting an image adds it to a queue of images. At each vertical blanking
|
||||
/// period, the queue is poped and an image is presented.
|
||||
///
|
||||
/// Guaranteed to be always supported.
|
||||
///
|
||||
/// This is the equivalent of OpenGL's `SwapInterval` with a value of 1.
|
||||
Fifo = vk::PRESENT_MODE_FIFO_KHR,
|
||||
|
||||
/// Same as `Fifo`, except that if the queue was empty during the previous vertical blanking
|
||||
/// period then it is equivalent to `Immediate`.
|
||||
///
|
||||
/// This is the equivalent of OpenGL's `SwapInterval` with a value of -1.
|
||||
Relaxed = vk::PRESENT_MODE_FIFO_RELAXED_KHR,
|
||||
/// The surface is no longer accessible and must be recreated.
|
||||
SurfaceLost,
|
||||
}
|
||||
|
||||
/// List of `PresentMode`s that are supported.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub struct SupportedPresentModes {
|
||||
pub immediate: bool,
|
||||
pub mailbox: bool,
|
||||
pub fifo: bool,
|
||||
pub relaxed: bool,
|
||||
}
|
||||
|
||||
impl SupportedPresentModes {
|
||||
/// Builds a `SupportedPresentModes` with all fields set to false.
|
||||
impl error::Error for CapabilitiesError {
|
||||
#[inline]
|
||||
pub fn none() -> SupportedPresentModes {
|
||||
SupportedPresentModes {
|
||||
immediate: false,
|
||||
mailbox: false,
|
||||
fifo: false,
|
||||
relaxed: false,
|
||||
fn description(&self) -> &str {
|
||||
match *self {
|
||||
CapabilitiesError::OomError(_) => "not enough memory",
|
||||
CapabilitiesError::SurfaceLost => "the surface is no longer valid",
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn from_list<I>(elem: I) -> SupportedPresentModes where I: Iterator<Item = vk::PresentModeKHR> {
|
||||
let mut result = SupportedPresentModes::none();
|
||||
for e in elem {
|
||||
match e {
|
||||
vk::PRESENT_MODE_IMMEDIATE_KHR => result.immediate = true,
|
||||
vk::PRESENT_MODE_MAILBOX_KHR => result.mailbox = true,
|
||||
vk::PRESENT_MODE_FIFO_KHR => result.fifo = true,
|
||||
vk::PRESENT_MODE_FIFO_RELAXED_KHR => result.relaxed = true,
|
||||
_ => panic!("Wrong value for vk::PresentModeKHR")
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
/// Returns true if the given present mode is in this list of supported modes.
|
||||
#[inline]
|
||||
pub fn supports(&self, mode: PresentMode) -> bool {
|
||||
match mode {
|
||||
PresentMode::Immediate => self.immediate,
|
||||
PresentMode::Mailbox => self.mailbox,
|
||||
PresentMode::Fifo => self.fifo,
|
||||
PresentMode::Relaxed => self.relaxed,
|
||||
fn cause(&self) -> Option<&error::Error> {
|
||||
match *self {
|
||||
CapabilitiesError::OomError(ref err) => Some(err),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an iterator to the list of supported present modes.
|
||||
impl fmt::Display for CapabilitiesError {
|
||||
#[inline]
|
||||
pub fn iter(&self) -> SupportedPresentModesIter {
|
||||
SupportedPresentModesIter(self.clone())
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
write!(fmt, "{}", error::Error::description(self))
|
||||
}
|
||||
}
|
||||
|
||||
/// Enumeration of the `PresentMode`s that are supported.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub struct SupportedPresentModesIter(SupportedPresentModes);
|
||||
|
||||
impl Iterator for SupportedPresentModesIter {
|
||||
type Item = PresentMode;
|
||||
|
||||
impl From<OomError> for CapabilitiesError {
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<PresentMode> {
|
||||
if self.0.immediate { self.0.immediate = false; return Some(PresentMode::Immediate); }
|
||||
if self.0.mailbox { self.0.mailbox = false; return Some(PresentMode::Mailbox); }
|
||||
if self.0.fifo { self.0.fifo = false; return Some(PresentMode::Fifo); }
|
||||
if self.0.relaxed { self.0.relaxed = false; return Some(PresentMode::Relaxed); }
|
||||
None
|
||||
fn from(err: OomError) -> CapabilitiesError {
|
||||
CapabilitiesError::OomError(err)
|
||||
}
|
||||
}
|
||||
|
||||
/// A transformation to apply to the image before showing it on the screen.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
#[repr(u32)]
|
||||
pub enum SurfaceTransform {
|
||||
/// Don't transform the image.
|
||||
Identity = vk::SURFACE_TRANSFORM_IDENTITY_BIT_KHR,
|
||||
/// Rotate 90 degrees.
|
||||
Rotate90 = vk::SURFACE_TRANSFORM_ROTATE_90_BIT_KHR,
|
||||
/// Rotate 180 degrees.
|
||||
Rotate180 = vk::SURFACE_TRANSFORM_ROTATE_180_BIT_KHR,
|
||||
/// Rotate 270 degrees.
|
||||
Rotate270 = vk::SURFACE_TRANSFORM_ROTATE_270_BIT_KHR,
|
||||
/// Mirror the image horizontally.
|
||||
HorizontalMirror = vk::SURFACE_TRANSFORM_HORIZONTAL_MIRROR_BIT_KHR,
|
||||
/// Mirror the image horizontally and rotate 90 degrees.
|
||||
HorizontalMirrorRotate90 = vk::SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR,
|
||||
/// Mirror the image horizontally and rotate 180 degrees.
|
||||
HorizontalMirrorRotate180 = vk::SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_180_BIT_KHR,
|
||||
/// Mirror the image horizontally and rotate 270 degrees.
|
||||
HorizontalMirrorRotate270 = vk::SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR,
|
||||
/// Let the operating system or driver implementation choose.
|
||||
Inherit = vk::SURFACE_TRANSFORM_INHERIT_BIT_KHR,
|
||||
}
|
||||
|
||||
/// How the alpha values of the pixels of the window are treated.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
#[repr(u32)]
|
||||
pub enum CompositeAlpha {
|
||||
/// The alpha channel of the image is ignored. All the pixels are considered as if they have a
|
||||
/// value of 1.0.
|
||||
Opaque = vk::COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
|
||||
|
||||
/// The alpha channel of the image is respected. The color channels are expected to have
|
||||
/// already been multiplied by the alpha value.
|
||||
PreMultiplied = vk::COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR,
|
||||
|
||||
/// The alpha channel of the image is respected. The color channels will be multiplied by the
|
||||
/// alpha value by the compositor before being added to what is behind.
|
||||
PostMultiplied = vk::COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR,
|
||||
|
||||
/// Let the operating system or driver implementation choose.
|
||||
Inherit = vk::COMPOSITE_ALPHA_INHERIT_BIT_KHR,
|
||||
}
|
||||
|
||||
/// List of supported composite alpha modes.
|
||||
///
|
||||
/// See the docs of `CompositeAlpha`.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct SupportedCompositeAlpha {
|
||||
pub opaque: bool,
|
||||
pub pre_multiplied: bool,
|
||||
pub post_multiplied: bool,
|
||||
pub inherit: bool,
|
||||
}
|
||||
|
||||
impl SupportedCompositeAlpha {
|
||||
/// Builds a `SupportedCompositeAlpha` with all fields set to false.
|
||||
impl From<Error> for CapabilitiesError {
|
||||
#[inline]
|
||||
pub fn none() -> SupportedCompositeAlpha {
|
||||
SupportedCompositeAlpha {
|
||||
opaque: false,
|
||||
pre_multiplied: false,
|
||||
post_multiplied: false,
|
||||
inherit: false,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn from_bits(val: u32) -> SupportedCompositeAlpha {
|
||||
let mut result = SupportedCompositeAlpha::none();
|
||||
if (val & vk::COMPOSITE_ALPHA_OPAQUE_BIT_KHR) != 0 { result.opaque = true; }
|
||||
if (val & vk::COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR) != 0 { result.pre_multiplied = true; }
|
||||
if (val & vk::COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR) != 0 { result.post_multiplied = true; }
|
||||
if (val & vk::COMPOSITE_ALPHA_INHERIT_BIT_KHR) != 0 { result.inherit = true; }
|
||||
result
|
||||
}
|
||||
|
||||
/// Returns true if the given `CompositeAlpha` is in this list.
|
||||
#[inline]
|
||||
pub fn supports(&self, value: CompositeAlpha) -> bool {
|
||||
match value {
|
||||
CompositeAlpha::Opaque => self.opaque,
|
||||
CompositeAlpha::PreMultiplied => self.pre_multiplied,
|
||||
CompositeAlpha::PostMultiplied => self.post_multiplied,
|
||||
CompositeAlpha::Inherit => self.inherit,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an iterator to the list of supported composite alpha.
|
||||
#[inline]
|
||||
pub fn iter(&self) -> SupportedCompositeAlphaIter {
|
||||
SupportedCompositeAlphaIter(self.clone())
|
||||
}
|
||||
}
|
||||
|
||||
/// Enumeration of the `CompositeAlpha` that are supported.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub struct SupportedCompositeAlphaIter(SupportedCompositeAlpha);
|
||||
|
||||
impl Iterator for SupportedCompositeAlphaIter {
|
||||
type Item = CompositeAlpha;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<CompositeAlpha> {
|
||||
if self.0.opaque { self.0.opaque = false; return Some(CompositeAlpha::Opaque); }
|
||||
if self.0.pre_multiplied { self.0.pre_multiplied = false; return Some(CompositeAlpha::PreMultiplied); }
|
||||
if self.0.post_multiplied { self.0.post_multiplied = false; return Some(CompositeAlpha::PostMultiplied); }
|
||||
if self.0.inherit { self.0.inherit = false; return Some(CompositeAlpha::Inherit); }
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// How the presentation engine should interpret the data.
|
||||
///
|
||||
/// # A quick lesson about color spaces
|
||||
///
|
||||
/// ## What is a color space?
|
||||
///
|
||||
/// Each pixel of a monitor is made of three components: one red, one green, and one blue. In the
|
||||
/// past, computers would simply send to the monitor the intensity of each of the three components.
|
||||
///
|
||||
/// This proved to be problematic, because depending on the brand of the monitor the colors would
|
||||
/// not exactly be the same. For example on some monitors, a value of `[1.0, 0.0, 0.0]` would be a
|
||||
/// bit more orange than on others.
|
||||
///
|
||||
/// In order to standardize this, there exist what are called *color spaces*: sRGB, AdobeRGB,
|
||||
/// DCI-P3, scRGB, etc. When you manipulate RGB values in a specific color space, these values have
|
||||
/// a precise absolute meaning in terms of color, that is the same across all systems and monitors.
|
||||
///
|
||||
/// > **Note**: Color spaces are orthogonal to concept of RGB. *RGB* only indicates what is the
|
||||
/// > representation of the data, but not how it is interpreted. You can think of this a bit like
|
||||
/// > text encoding. An *RGB* value is a like a byte, in other words it is the medium by which
|
||||
/// > values are communicated, and a *color space* is like a text encoding (eg. UTF-8), in other
|
||||
/// > words it is the way the value should be interpreted.
|
||||
///
|
||||
/// The most commonly used color space today is sRGB. Most monitors today use this color space,
|
||||
/// and most images files are encoded in this color space.
|
||||
///
|
||||
/// ## Pixel formats and linear vs non-linear
|
||||
///
|
||||
/// In Vulkan all images have a specific format in which the data is stored. The data of an image
|
||||
/// consists of pixels in RGB but contains no information about the color space (or lack thereof)
|
||||
/// of these pixels. You are free to store them in whatever color space you want.
|
||||
///
|
||||
/// But one big practical problem with color spaces is that they are sometimes not linear, and in
|
||||
/// particular the popular sRGB color space is not linear. In a non-linear color space, a value of
|
||||
/// `[0.6, 0.6, 0.6]` for example is **not** twice as bright as a value of `[0.3, 0.3, 0.3]`. This
|
||||
/// is problematic, because operations such as taking the average of two colors or calculating the
|
||||
/// lighting of a texture with a dot product are mathematically incorrect and will produce
|
||||
/// incorrect colors.
|
||||
///
|
||||
/// > **Note**: If the texture format has an alpha component, it is not affected by the color space
|
||||
/// > and always behaves linearly.
|
||||
///
|
||||
/// In order to solve this Vulkan also provides image formats with the `Srgb` suffix, which are
|
||||
/// expected to contain RGB data in the sRGB color space. When you sample an image with such a
|
||||
/// format from a shader, the implementation will automatically turn the pixel values into a linear
|
||||
/// color space that is suitable for linear operations (such as additions or multiplications).
|
||||
/// When you write to a framebuffer attachment with such a format, the implementation will
|
||||
/// automatically perform the opposite conversion. These conversions are most of the time performed
|
||||
/// by the hardware and incur no additional cost.
|
||||
///
|
||||
/// ## Color space of the swapchain
|
||||
///
|
||||
/// The color space that you specify when you create a swapchain is how the implementation will
|
||||
/// interpret the raw data inside of the image.
|
||||
///
|
||||
/// > **Note**: The implementation can choose to send the data in the swapchain image directly to
|
||||
/// > the monitor, but it can also choose to write it in an intermediary buffer that is then read
|
||||
/// > by the operating system or windowing system. Therefore the color space that the
|
||||
/// > implementation supports is not necessarily the same as the one supported by the monitor.
|
||||
///
|
||||
/// It is *your* job to ensure that the data in the swapchain image is in the color space
|
||||
/// that is specified here, otherwise colors will be incorrect.
|
||||
/// The implementation will never perform any additional automatic conversion after the colors have
|
||||
/// been written to the swapchain image.
|
||||
///
|
||||
/// # How do I handle this correctly?
|
||||
///
|
||||
/// The easiest way to handle color spaces in a cross-platform program is:
|
||||
///
|
||||
/// - Always request the `SrgbNonLinear` color space when creating the swapchain.
|
||||
/// - Make sure that all your image files use the sRGB color space, and load them in images whose
|
||||
/// format has the `Srgb` suffix. Only use non-sRGB image formats for intermediary computations
|
||||
/// or to store non-color data.
|
||||
/// - Swapchain images should have a format with the `Srgb` suffix.
|
||||
///
|
||||
/// > **Note**: It is unclear whether the `SrgbNonLinear` color space is always supported by the
|
||||
/// > the implementation or not. See https://github.com/KhronosGroup/Vulkan-Docs/issues/442.
|
||||
///
|
||||
/// > **Note**: Lots of developers are confused by color spaces. You can sometimes find articles
|
||||
/// > talking about gamma correction and suggestion to put your colors to the power 2.2 for
|
||||
/// > example. These are all hacks and you should use the sRGB pixel formats intead.
|
||||
///
|
||||
/// If you follow these three rules, then everything should render the same way on all platforms.
|
||||
///
|
||||
/// Additionally you can try detect whether the implementation supports any additional color space
|
||||
/// and perform a manual conversion to that color space from inside your shader.
|
||||
///
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
#[repr(u32)]
|
||||
pub enum ColorSpace {
|
||||
SrgbNonLinear = vk::COLOR_SPACE_SRGB_NONLINEAR_KHR,
|
||||
DisplayP3Linear = vk::COLOR_SPACE_DISPLAY_P3_LINEAR_EXT,
|
||||
DisplayP3NonLinear = vk::COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT,
|
||||
ScrgbLinear = vk::COLOR_SPACE_SCRGB_LINEAR_EXT,
|
||||
ScrgbNonLinear = vk::COLOR_SPACE_SCRGB_NONLINEAR_EXT,
|
||||
DciP3Linear = vk::COLOR_SPACE_DCI_P3_LINEAR_EXT,
|
||||
DciP3NonLinear = vk::COLOR_SPACE_DCI_P3_NONLINEAR_EXT,
|
||||
Bt709Linear = vk::COLOR_SPACE_BT709_LINEAR_EXT,
|
||||
Bt709NonLinear = vk::COLOR_SPACE_BT709_NONLINEAR_EXT,
|
||||
Bt2020Linear = vk::COLOR_SPACE_BT2020_LINEAR_EXT,
|
||||
Bt2020NonLinear = vk::COLOR_SPACE_BT2020_NONLINEAR_EXT,
|
||||
AdobeRgbLinear = vk::COLOR_SPACE_ADOBERGB_LINEAR_EXT,
|
||||
AdobeRgbNonLinear = vk::COLOR_SPACE_ADOBERGB_NONLINEAR_EXT,
|
||||
}
|
||||
|
||||
impl ColorSpace {
|
||||
#[inline]
|
||||
fn from_num(val: u32) -> ColorSpace {
|
||||
match val {
|
||||
vk::COLOR_SPACE_SRGB_NONLINEAR_KHR => ColorSpace::SrgbNonLinear,
|
||||
vk::COLOR_SPACE_DISPLAY_P3_LINEAR_EXT => ColorSpace::DisplayP3Linear,
|
||||
vk::COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT => ColorSpace::DisplayP3NonLinear,
|
||||
vk::COLOR_SPACE_SCRGB_LINEAR_EXT => ColorSpace::ScrgbLinear,
|
||||
vk::COLOR_SPACE_SCRGB_NONLINEAR_EXT => ColorSpace::ScrgbNonLinear,
|
||||
vk::COLOR_SPACE_DCI_P3_LINEAR_EXT => ColorSpace::DciP3Linear,
|
||||
vk::COLOR_SPACE_DCI_P3_NONLINEAR_EXT => ColorSpace::DciP3NonLinear,
|
||||
vk::COLOR_SPACE_BT709_LINEAR_EXT => ColorSpace::Bt709Linear,
|
||||
vk::COLOR_SPACE_BT709_NONLINEAR_EXT => ColorSpace::Bt709NonLinear,
|
||||
vk::COLOR_SPACE_BT2020_LINEAR_EXT => ColorSpace::Bt2020Linear,
|
||||
vk::COLOR_SPACE_BT2020_NONLINEAR_EXT => ColorSpace::Bt2020NonLinear,
|
||||
vk::COLOR_SPACE_ADOBERGB_LINEAR_EXT => ColorSpace::AdobeRgbLinear,
|
||||
vk::COLOR_SPACE_ADOBERGB_NONLINEAR_EXT => ColorSpace::AdobeRgbNonLinear,
|
||||
_ => panic!("Wrong value for color space enum")
|
||||
fn from(err: Error) -> CapabilitiesError {
|
||||
match err {
|
||||
err @ Error::OutOfHostMemory => CapabilitiesError::OomError(OomError::from(err)),
|
||||
err @ Error::OutOfDeviceMemory => CapabilitiesError::OomError(OomError::from(err)),
|
||||
Error::SurfaceLost => CapabilitiesError::SurfaceLost,
|
||||
_ => panic!("unexpected error: {:?}", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1027,7 +707,7 @@ mod tests {
|
||||
#[test]
|
||||
fn khr_win32_surface_ext_missing() {
|
||||
let instance = instance!();
|
||||
match unsafe { Surface::from_hwnd(&instance, ptr::null::<u8>(), ptr::null::<u8>()) } {
|
||||
match unsafe { Surface::from_hwnd(instance, ptr::null::<u8>(), ptr::null::<u8>()) } {
|
||||
Err(SurfaceCreationError::MissingExtension { .. }) => (),
|
||||
_ => panic!()
|
||||
}
|
||||
@ -1036,7 +716,7 @@ mod tests {
|
||||
#[test]
|
||||
fn khr_xcb_surface_ext_missing() {
|
||||
let instance = instance!();
|
||||
match unsafe { Surface::from_xcb(&instance, ptr::null::<u8>(), 0) } {
|
||||
match unsafe { Surface::from_xcb(instance, ptr::null::<u8>(), 0) } {
|
||||
Err(SurfaceCreationError::MissingExtension { .. }) => (),
|
||||
_ => panic!()
|
||||
}
|
||||
@ -1045,7 +725,7 @@ mod tests {
|
||||
#[test]
|
||||
fn khr_xlib_surface_ext_missing() {
|
||||
let instance = instance!();
|
||||
match unsafe { Surface::from_xlib(&instance, ptr::null::<u8>(), 0) } {
|
||||
match unsafe { Surface::from_xlib(instance, ptr::null::<u8>(), 0) } {
|
||||
Err(SurfaceCreationError::MissingExtension { .. }) => (),
|
||||
_ => panic!()
|
||||
}
|
||||
@ -1054,7 +734,7 @@ mod tests {
|
||||
#[test]
|
||||
fn khr_wayland_surface_ext_missing() {
|
||||
let instance = instance!();
|
||||
match unsafe { Surface::from_wayland(&instance, ptr::null::<u8>(), ptr::null::<u8>()) } {
|
||||
match unsafe { Surface::from_wayland(instance, ptr::null::<u8>(), ptr::null::<u8>()) } {
|
||||
Err(SurfaceCreationError::MissingExtension { .. }) => (),
|
||||
_ => panic!()
|
||||
}
|
||||
@ -1063,7 +743,7 @@ mod tests {
|
||||
#[test]
|
||||
fn khr_mir_surface_ext_missing() {
|
||||
let instance = instance!();
|
||||
match unsafe { Surface::from_mir(&instance, ptr::null::<u8>(), ptr::null::<u8>()) } {
|
||||
match unsafe { Surface::from_mir(instance, ptr::null::<u8>(), ptr::null::<u8>()) } {
|
||||
Err(SurfaceCreationError::MissingExtension { .. }) => (),
|
||||
_ => panic!()
|
||||
}
|
||||
@ -1072,7 +752,7 @@ mod tests {
|
||||
#[test]
|
||||
fn khr_android_surface_ext_missing() {
|
||||
let instance = instance!();
|
||||
match unsafe { Surface::from_anativewindow(&instance, ptr::null::<u8>()) } {
|
||||
match unsafe { Surface::from_anativewindow(instance, ptr::null::<u8>()) } {
|
||||
Err(SurfaceCreationError::MissingExtension { .. }) => (),
|
||||
_ => panic!()
|
||||
}
|
||||
|
@ -13,7 +13,6 @@ use std::mem;
|
||||
use std::ptr;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
use std::sync::Weak;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::time::Duration;
|
||||
@ -29,9 +28,9 @@ use format::Format;
|
||||
use format::FormatDesc;
|
||||
use image::ImageAccess;
|
||||
use image::ImageDimensions;
|
||||
use image::Layout;
|
||||
use image::ImageLayout;
|
||||
use image::ImageUsage;
|
||||
use image::sys::UnsafeImage;
|
||||
use image::sys::Usage as ImageUsage;
|
||||
use image::swapchain::SwapchainImage;
|
||||
use swapchain::ColorSpace;
|
||||
use swapchain::CompositeAlpha;
|
||||
@ -56,13 +55,101 @@ use VulkanObject;
|
||||
use VulkanPointers;
|
||||
use vk;
|
||||
|
||||
/// Tries to take ownership of an image in order to draw on it.
|
||||
///
|
||||
/// The function returns the index of the image in the array of images that was returned
|
||||
/// when creating the swapchain, plus a future that represents the moment when the image will
|
||||
/// become available from the GPU (which may not be *immediately*).
|
||||
///
|
||||
/// If you try to draw on an image without acquiring it first, the execution will block. (TODO
|
||||
/// behavior may change).
|
||||
// TODO: has to make sure vkQueuePresent is called, because calling acquire_next_image many
|
||||
// times in a row is an error
|
||||
// TODO: change timeout to `Option<Duration>`.
|
||||
pub fn acquire_next_image(swapchain: Arc<Swapchain>, timeout: Duration)
|
||||
-> Result<(usize, SwapchainAcquireFuture), AcquireError>
|
||||
{
|
||||
unsafe {
|
||||
// Check that this is not an old swapchain. From specs:
|
||||
// > swapchain must not have been replaced by being passed as the
|
||||
// > VkSwapchainCreateInfoKHR::oldSwapchain value to vkCreateSwapchainKHR
|
||||
let stale = swapchain.stale.lock().unwrap();
|
||||
if *stale {
|
||||
return Err(AcquireError::OutOfDate);
|
||||
}
|
||||
|
||||
let vk = swapchain.device.pointers();
|
||||
|
||||
let semaphore = try!(Semaphore::new(swapchain.device.clone()));
|
||||
|
||||
let timeout_ns = timeout.as_secs().saturating_mul(1_000_000_000)
|
||||
.saturating_add(timeout.subsec_nanos() as u64);
|
||||
|
||||
let mut out = mem::uninitialized();
|
||||
let r = try!(check_errors(vk.AcquireNextImageKHR(swapchain.device.internal_object(),
|
||||
swapchain.swapchain, timeout_ns,
|
||||
semaphore.internal_object(), 0,
|
||||
&mut out)));
|
||||
|
||||
let id = match r {
|
||||
Success::Success => out as usize,
|
||||
Success::Suboptimal => out as usize, // TODO: give that info to the user
|
||||
Success::NotReady => return Err(AcquireError::Timeout),
|
||||
Success::Timeout => return Err(AcquireError::Timeout),
|
||||
s => panic!("unexpected success value: {:?}", s)
|
||||
};
|
||||
|
||||
Ok((id, SwapchainAcquireFuture {
|
||||
swapchain: swapchain.clone(), // TODO: don't clone
|
||||
semaphore: semaphore,
|
||||
image_id: id,
|
||||
finished: AtomicBool::new(false),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
/// Presents an image on the screen.
|
||||
///
|
||||
/// The parameter is the same index as what `acquire_next_image` returned. The image must
|
||||
/// have been acquired first.
|
||||
///
|
||||
/// The actual behavior depends on the present mode that you passed when creating the
|
||||
/// swapchain.
|
||||
pub fn present<F>(swapchain: Arc<Swapchain>, before: F, queue: Arc<Queue>, index: usize)
|
||||
-> PresentFuture<F>
|
||||
where F: GpuFuture
|
||||
{
|
||||
assert!(index < swapchain.images.len());
|
||||
|
||||
// TODO: restore this check with a dummy ImageAccess implementation
|
||||
/*let swapchain_image = me.images.lock().unwrap().get(index).unwrap().0.upgrade().unwrap(); // TODO: return error instead
|
||||
// Normally if `check_image_access` returns false we're supposed to call the `gpu_access`
|
||||
// function on the image instead. But since we know that this method on `SwapchainImage`
|
||||
// always returns false anyway (by design), we don't need to do it.
|
||||
assert!(before.check_image_access(&swapchain_image, ImageLayout::PresentSrc, true, &queue).is_ok()); // TODO: return error instead*/
|
||||
|
||||
PresentFuture {
|
||||
previous: before,
|
||||
queue: queue,
|
||||
swapchain: swapchain,
|
||||
image_id: index,
|
||||
flushed: AtomicBool::new(false),
|
||||
finished: AtomicBool::new(false),
|
||||
}
|
||||
}
|
||||
|
||||
/// Contains the swapping system and the images that can be shown on a surface.
|
||||
// TODO: #[derive(Debug)] (waiting on https://github.com/aturon/crossbeam/issues/62)
|
||||
pub struct Swapchain {
|
||||
// The Vulkan device this swapchain was created with.
|
||||
device: Arc<Device>,
|
||||
// The surface, which we need to keep alive.
|
||||
surface: Arc<Surface>,
|
||||
// The swapchain object.
|
||||
swapchain: vk::SwapchainKHR,
|
||||
|
||||
// The images of this swapchain.
|
||||
images: Vec<ImageEntry>,
|
||||
|
||||
// If true, that means we have used this swapchain to recreate a new swapchain. The current
|
||||
// swapchain can no longer be used for anything except presenting already-acquired images.
|
||||
//
|
||||
@ -82,9 +169,12 @@ pub struct Swapchain {
|
||||
alpha: CompositeAlpha,
|
||||
mode: PresentMode,
|
||||
clipped: bool,
|
||||
}
|
||||
|
||||
// TODO: meh for Mutex
|
||||
images: Mutex<Vec<(Weak<SwapchainImage>, bool)>>,
|
||||
struct ImageEntry {
|
||||
image: UnsafeImage,
|
||||
// If true, then the image is still in the undefined layout and must be transitionned.
|
||||
undefined_layout: AtomicBool,
|
||||
}
|
||||
|
||||
impl Swapchain {
|
||||
@ -110,9 +200,10 @@ impl Swapchain {
|
||||
///
|
||||
// TODO: remove `old_swapchain` parameter and add another function `with_old_swapchain`.
|
||||
// TODO: add `ColorSpace` parameter
|
||||
// TODO: isn't it unsafe to take the surface through an Arc when it comes to vulkano-win?
|
||||
#[inline]
|
||||
pub fn new<F, S>(device: &Arc<Device>, surface: &Arc<Surface>, num_images: u32, format: F,
|
||||
dimensions: [u32; 2], layers: u32, usage: &ImageUsage, sharing: S,
|
||||
pub fn new<F, S>(device: Arc<Device>, surface: Arc<Surface>, num_images: u32, format: F,
|
||||
dimensions: [u32; 2], layers: u32, usage: ImageUsage, sharing: S,
|
||||
transform: SurfaceTransform, alpha: CompositeAlpha, mode: PresentMode,
|
||||
clipped: bool, old_swapchain: Option<&Arc<Swapchain>>)
|
||||
-> Result<(Arc<Swapchain>, Vec<Arc<SwapchainImage>>), OomError>
|
||||
@ -127,16 +218,14 @@ impl Swapchain {
|
||||
pub fn recreate_with_dimension(&self, dimensions: [u32; 2])
|
||||
-> Result<(Arc<Swapchain>, Vec<Arc<SwapchainImage>>), OomError>
|
||||
{
|
||||
Swapchain::new_inner(&self.device, &self.surface, self.num_images, self.format,
|
||||
self.color_space, dimensions, self.layers, &self.usage,
|
||||
Swapchain::new_inner(self.device.clone(), self.surface.clone(), self.num_images,
|
||||
self.format, self.color_space, dimensions, self.layers, self.usage,
|
||||
self.sharing.clone(), self.transform, self.alpha, self.mode,
|
||||
self.clipped, Some(self))
|
||||
}
|
||||
|
||||
// TODO: images layouts should always be set to "PRESENT", since we have no way to switch the
|
||||
// layout at present time
|
||||
fn new_inner(device: &Arc<Device>, surface: &Arc<Surface>, num_images: u32, format: Format,
|
||||
color_space: ColorSpace, dimensions: [u32; 2], layers: u32, usage: &ImageUsage,
|
||||
fn new_inner(device: Arc<Device>, surface: Arc<Surface>, num_images: u32, format: Format,
|
||||
color_space: ColorSpace, dimensions: [u32; 2], layers: u32, usage: ImageUsage,
|
||||
sharing: SharingMode, transform: SurfaceTransform, alpha: CompositeAlpha,
|
||||
mode: PresentMode, clipped: bool, old_swapchain: Option<&Swapchain>)
|
||||
-> Result<(Arc<Swapchain>, Vec<Arc<SwapchainImage>>), OomError>
|
||||
@ -159,13 +248,13 @@ impl Swapchain {
|
||||
|
||||
// If we recreate a swapchain, make sure that the surface is the same.
|
||||
if let Some(sc) = old_swapchain {
|
||||
// TODO: return proper error instead of panicing?
|
||||
assert_eq!(surface.internal_object(), sc.surface.internal_object());
|
||||
assert_eq!(surface.internal_object(), sc.surface.internal_object(),
|
||||
"Surface mismatch between old and new swapchain");
|
||||
}
|
||||
|
||||
// Checking that the surface doesn't already have a swapchain.
|
||||
if old_swapchain.is_none() {
|
||||
// TODO: return proper error instead of panicing?
|
||||
// TODO: return proper error instead of panicking
|
||||
let has_already = surface.flag().swap(true, Ordering::AcqRel);
|
||||
if has_already { panic!("The surface already has a swapchain alive"); }
|
||||
}
|
||||
@ -218,10 +307,42 @@ impl Swapchain {
|
||||
output
|
||||
};
|
||||
|
||||
let image_handles = unsafe {
|
||||
let mut num = 0;
|
||||
try!(check_errors(vk.GetSwapchainImagesKHR(device.internal_object(),
|
||||
swapchain, &mut num,
|
||||
ptr::null_mut())));
|
||||
|
||||
let mut images = Vec::with_capacity(num as usize);
|
||||
try!(check_errors(vk.GetSwapchainImagesKHR(device.internal_object(),
|
||||
swapchain, &mut num,
|
||||
images.as_mut_ptr())));
|
||||
images.set_len(num as usize);
|
||||
images
|
||||
};
|
||||
|
||||
let images = image_handles.into_iter().enumerate().map(|(id, image)| unsafe {
|
||||
let dims = ImageDimensions::Dim2d {
|
||||
width: dimensions[0],
|
||||
height: dimensions[1],
|
||||
array_layers: layers,
|
||||
cubemap_compatible: false,
|
||||
};
|
||||
|
||||
let img = UnsafeImage::from_raw(device.clone(), image, usage.to_usage_bits(), format,
|
||||
dims, 1, 1);
|
||||
|
||||
ImageEntry {
|
||||
image: img,
|
||||
undefined_layout: AtomicBool::new(true)
|
||||
}
|
||||
}).collect::<Vec<_>>();
|
||||
|
||||
let swapchain = Arc::new(Swapchain {
|
||||
device: device.clone(),
|
||||
surface: surface.clone(),
|
||||
swapchain: swapchain,
|
||||
images: images,
|
||||
stale: Mutex::new(false),
|
||||
num_images: num_images,
|
||||
format: format,
|
||||
@ -234,113 +355,21 @@ impl Swapchain {
|
||||
alpha: alpha,
|
||||
mode: mode,
|
||||
clipped: clipped,
|
||||
images: Mutex::new(Vec::new()), // Filled below.
|
||||
});
|
||||
|
||||
let images = unsafe {
|
||||
let mut num = 0;
|
||||
try!(check_errors(vk.GetSwapchainImagesKHR(device.internal_object(),
|
||||
swapchain.swapchain, &mut num,
|
||||
ptr::null_mut())));
|
||||
|
||||
let mut images = Vec::with_capacity(num as usize);
|
||||
try!(check_errors(vk.GetSwapchainImagesKHR(device.internal_object(),
|
||||
swapchain.swapchain, &mut num,
|
||||
images.as_mut_ptr())));
|
||||
images.set_len(num as usize);
|
||||
images
|
||||
};
|
||||
|
||||
let images = images.into_iter().enumerate().map(|(id, image)| unsafe {
|
||||
let unsafe_image = UnsafeImage::from_raw(device, image, usage.to_usage_bits(), format,
|
||||
ImageDimensions::Dim2d { width: dimensions[0], height: dimensions[1], array_layers: 1, cubemap_compatible: false }, 1, 1);
|
||||
SwapchainImage::from_raw(unsafe_image, format, &swapchain, id as u32).unwrap() // TODO: propagate error
|
||||
}).collect::<Vec<_>>();
|
||||
|
||||
*swapchain.images.lock().unwrap() = images.iter().map(|i| (Arc::downgrade(i), true)).collect();
|
||||
Ok((swapchain, images))
|
||||
}
|
||||
|
||||
/// Tries to take ownership of an image in order to draw on it.
|
||||
///
|
||||
/// The function returns the index of the image in the array of images that was returned
|
||||
/// when creating the swapchain, plus a future that represents the moment when the image will
|
||||
/// become available from the GPU (which may not be *immediately*).
|
||||
///
|
||||
/// If you try to draw on an image without acquiring it first, the execution will block. (TODO
|
||||
/// behavior may change).
|
||||
// TODO: has to make sure vkQueuePresent is called, because calling acquire_next_image many
|
||||
// times in a row is an error
|
||||
// TODO: swapchain must not have been replaced by being passed as the VkSwapchainCreateInfoKHR::oldSwapchain value to vkCreateSwapchainKHR
|
||||
// TODO: change timeout to `Option<Duration>`.
|
||||
pub fn acquire_next_image(&self, timeout: Duration) -> Result<(usize, SwapchainAcquireFuture), AcquireError> {
|
||||
unsafe {
|
||||
let stale = self.stale.lock().unwrap();
|
||||
if *stale {
|
||||
return Err(AcquireError::OutOfDate);
|
||||
let swapchain_images = (0 .. swapchain.images.len()).map(|n| {
|
||||
unsafe {
|
||||
SwapchainImage::from_raw(swapchain.clone(), n).unwrap() // TODO: propagate error
|
||||
}
|
||||
}).collect();
|
||||
|
||||
let vk = self.device.pointers();
|
||||
|
||||
let semaphore = try!(Semaphore::new(self.device.clone()));
|
||||
|
||||
let timeout_ns = timeout.as_secs().saturating_mul(1_000_000_000)
|
||||
.saturating_add(timeout.subsec_nanos() as u64);
|
||||
|
||||
let mut out = mem::uninitialized();
|
||||
let r = try!(check_errors(vk.AcquireNextImageKHR(self.device.internal_object(),
|
||||
self.swapchain, timeout_ns,
|
||||
semaphore.internal_object(), 0,
|
||||
&mut out)));
|
||||
|
||||
let id = match r {
|
||||
Success::Success => out as usize,
|
||||
Success::Suboptimal => out as usize, // TODO: give that info to the user
|
||||
Success::NotReady => return Err(AcquireError::Timeout),
|
||||
Success::Timeout => return Err(AcquireError::Timeout),
|
||||
s => panic!("unexpected success value: {:?}", s)
|
||||
};
|
||||
|
||||
let mut images = self.images.lock().unwrap();
|
||||
let undefined_layout = mem::replace(&mut images.get_mut(id).unwrap().1, false);
|
||||
|
||||
Ok((id, SwapchainAcquireFuture {
|
||||
semaphore: semaphore,
|
||||
id: id,
|
||||
image: images.get(id).unwrap().0.clone(),
|
||||
finished: AtomicBool::new(false),
|
||||
undefined_layout: undefined_layout,
|
||||
}))
|
||||
}
|
||||
Ok((swapchain, swapchain_images))
|
||||
}
|
||||
|
||||
/// Presents an image on the screen.
|
||||
///
|
||||
/// The parameter is the same index as what `acquire_next_image` returned. The image must
|
||||
/// have been acquired first.
|
||||
///
|
||||
/// The actual behavior depends on the present mode that you passed when creating the
|
||||
/// swapchain.
|
||||
// TODO: use another API, since taking by Arc is meh
|
||||
pub fn present<F>(me: Arc<Self>, before: F, queue: Arc<Queue>, index: usize)
|
||||
-> PresentFuture<F>
|
||||
where F: GpuFuture
|
||||
{
|
||||
assert!(index < me.num_images as usize);
|
||||
|
||||
let swapchain_image = me.images.lock().unwrap().get(index).unwrap().0.upgrade().unwrap(); // TODO: return error instead
|
||||
// Normally if `check_image_access` returns false we're supposed to call the `gpu_access`
|
||||
// function on the image instead. But since we know that this method on `SwapchainImage`
|
||||
// always returns false anyway (by design), we don't need to do it.
|
||||
assert!(before.check_image_access(&swapchain_image, Layout::PresentSrc, true, &queue).is_ok()); // TODO: return error instead
|
||||
|
||||
PresentFuture {
|
||||
previous: before,
|
||||
queue: queue,
|
||||
swapchain: me,
|
||||
image_id: index as u32,
|
||||
finished: AtomicBool::new(false),
|
||||
}
|
||||
/// Returns of the images that belong to this swapchain.
|
||||
#[inline]
|
||||
pub fn raw_image(&self, offset: usize) -> Option<&UnsafeImage> {
|
||||
self.images.get(offset).map(|i| &i.image)
|
||||
}
|
||||
|
||||
/// Returns the number of images of the swapchain.
|
||||
@ -348,7 +377,7 @@ impl Swapchain {
|
||||
/// See the documentation of `Swapchain::new`.
|
||||
#[inline]
|
||||
pub fn num_images(&self) -> u32 {
|
||||
self.num_images
|
||||
self.images.len() as u32
|
||||
}
|
||||
|
||||
/// Returns the format of the images of the swapchain.
|
||||
@ -438,25 +467,23 @@ impl Drop for Swapchain {
|
||||
/// Represents the moment when the GPU will have access to a swapchain image.
|
||||
#[must_use]
|
||||
pub struct SwapchainAcquireFuture {
|
||||
swapchain: Arc<Swapchain>,
|
||||
image_id: usize,
|
||||
semaphore: Semaphore,
|
||||
id: usize,
|
||||
image: Weak<SwapchainImage>,
|
||||
finished: AtomicBool,
|
||||
// If true, then the acquired image is still in the undefined layout and must be transitionned.
|
||||
undefined_layout: bool,
|
||||
}
|
||||
|
||||
impl SwapchainAcquireFuture {
|
||||
/// Returns the index of the image in the list of images returned when creating the swapchain.
|
||||
#[inline]
|
||||
pub fn image_id(&self) -> usize {
|
||||
self.id
|
||||
self.image_id
|
||||
}
|
||||
|
||||
/// Returns the acquired image.
|
||||
/// Returns the corresponding swapchain.
|
||||
#[inline]
|
||||
pub fn image(&self) -> Option<Arc<SwapchainImage>> {
|
||||
self.image.upgrade()
|
||||
pub fn swapchain(&self) -> &Arc<Swapchain> {
|
||||
&self.swapchain
|
||||
}
|
||||
}
|
||||
|
||||
@ -500,34 +527,30 @@ unsafe impl GpuFuture for SwapchainAcquireFuture {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn check_image_access(&self, image: &ImageAccess, layout: Layout, exclusive: bool, queue: &Queue)
|
||||
fn check_image_access(&self, image: &ImageAccess, layout: ImageLayout, exclusive: bool, queue: &Queue)
|
||||
-> Result<Option<(PipelineStages, AccessFlagBits)>, AccessCheckError>
|
||||
{
|
||||
if let Some(sc_img) = self.image.upgrade() {
|
||||
if sc_img.inner().internal_object() != image.inner().internal_object() {
|
||||
return Err(AccessCheckError::Unknown);
|
||||
}
|
||||
|
||||
if self.undefined_layout && layout != Layout::Undefined {
|
||||
return Err(AccessCheckError::Denied(AccessError::ImageNotInitialized {
|
||||
requested: layout
|
||||
}));
|
||||
}
|
||||
|
||||
if layout != Layout::Undefined && layout != Layout::PresentSrc {
|
||||
return Err(AccessCheckError::Denied(AccessError::UnexpectedImageLayout {
|
||||
allowed: Layout::PresentSrc,
|
||||
requested: layout,
|
||||
}));
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
|
||||
} else {
|
||||
// The swapchain image no longer exists, therefore the `image` parameter received by
|
||||
// this function cannot possibly be the swapchain image.
|
||||
Err(AccessCheckError::Unknown)
|
||||
let swapchain_image = self.swapchain.raw_image(self.image_id).unwrap();
|
||||
if swapchain_image.internal_object() != image.inner().internal_object() {
|
||||
return Err(AccessCheckError::Unknown);
|
||||
}
|
||||
|
||||
if self.swapchain.images[self.image_id].undefined_layout.load(Ordering::Relaxed) &&
|
||||
layout != ImageLayout::Undefined
|
||||
{
|
||||
return Err(AccessCheckError::Denied(AccessError::ImageNotInitialized {
|
||||
requested: layout
|
||||
}));
|
||||
}
|
||||
|
||||
if layout != ImageLayout::Undefined && layout != ImageLayout::PresentSrc {
|
||||
return Err(AccessCheckError::Denied(AccessError::UnexpectedImageLayout {
|
||||
allowed: ImageLayout::PresentSrc,
|
||||
requested: layout,
|
||||
}));
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
@ -629,10 +652,29 @@ pub struct PresentFuture<P> where P: GpuFuture {
|
||||
previous: P,
|
||||
queue: Arc<Queue>,
|
||||
swapchain: Arc<Swapchain>,
|
||||
image_id: u32,
|
||||
image_id: usize,
|
||||
// True if `flush()` has been called on the future, which means that the present command has
|
||||
// been submitted.
|
||||
flushed: AtomicBool,
|
||||
// True if `signal_finished()` has been called on the future, which means that the future has
|
||||
// been submitted and has already been processed by the GPU.
|
||||
finished: AtomicBool,
|
||||
}
|
||||
|
||||
impl<P> PresentFuture<P> where P: GpuFuture {
|
||||
/// Returns the index of the image in the list of images returned when creating the swapchain.
|
||||
#[inline]
|
||||
pub fn image_id(&self) -> usize {
|
||||
self.image_id
|
||||
}
|
||||
|
||||
/// Returns the corresponding swapchain.
|
||||
#[inline]
|
||||
pub fn swapchain(&self) -> &Arc<Swapchain> {
|
||||
&self.swapchain
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<P> GpuFuture for PresentFuture<P> where P: GpuFuture {
|
||||
#[inline]
|
||||
fn cleanup_finished(&mut self) {
|
||||
@ -641,6 +683,10 @@ unsafe impl<P> GpuFuture for PresentFuture<P> where P: GpuFuture {
|
||||
|
||||
#[inline]
|
||||
unsafe fn build_submission(&self) -> Result<SubmitAnyBuilder, FlushError> {
|
||||
if self.flushed.load(Ordering::SeqCst) {
|
||||
return Ok(SubmitAnyBuilder::Empty);
|
||||
}
|
||||
|
||||
let queue = self.previous.queue().map(|q| q.clone());
|
||||
|
||||
// TODO: if the swapchain image layout is not PRESENT, should add a transition command
|
||||
@ -649,18 +695,18 @@ unsafe impl<P> GpuFuture for PresentFuture<P> where P: GpuFuture {
|
||||
Ok(match try!(self.previous.build_submission()) {
|
||||
SubmitAnyBuilder::Empty => {
|
||||
let mut builder = SubmitPresentBuilder::new();
|
||||
builder.add_swapchain(&self.swapchain, self.image_id);
|
||||
builder.add_swapchain(&self.swapchain, self.image_id as u32);
|
||||
SubmitAnyBuilder::QueuePresent(builder)
|
||||
},
|
||||
SubmitAnyBuilder::SemaphoresWait(sem) => {
|
||||
let mut builder: SubmitPresentBuilder = sem.into();
|
||||
builder.add_swapchain(&self.swapchain, self.image_id);
|
||||
builder.add_swapchain(&self.swapchain, self.image_id as u32);
|
||||
SubmitAnyBuilder::QueuePresent(builder)
|
||||
},
|
||||
SubmitAnyBuilder::CommandBuffer(cb) => {
|
||||
try!(cb.submit(&queue.unwrap())); // FIXME: wrong because build_submission can be called multiple times
|
||||
let mut builder = SubmitPresentBuilder::new();
|
||||
builder.add_swapchain(&self.swapchain, self.image_id);
|
||||
builder.add_swapchain(&self.swapchain, self.image_id as u32);
|
||||
SubmitAnyBuilder::QueuePresent(builder)
|
||||
},
|
||||
SubmitAnyBuilder::QueuePresent(present) => {
|
||||
@ -675,11 +721,25 @@ unsafe impl<P> GpuFuture for PresentFuture<P> where P: GpuFuture {
|
||||
|
||||
#[inline]
|
||||
fn flush(&self) -> Result<(), FlushError> {
|
||||
unimplemented!()
|
||||
unsafe {
|
||||
// If `flushed` already contains `true`, then `build_submission` will return `Empty`.
|
||||
|
||||
match self.build_submission()? {
|
||||
SubmitAnyBuilder::Empty => {}
|
||||
SubmitAnyBuilder::QueuePresent(present) => {
|
||||
present.submit(&self.queue)?;
|
||||
}
|
||||
_ => unreachable!()
|
||||
}
|
||||
|
||||
self.flushed.store(true, Ordering::SeqCst);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn signal_finished(&self) {
|
||||
self.flushed.store(true, Ordering::SeqCst);
|
||||
self.finished.store(true, Ordering::SeqCst);
|
||||
self.previous.signal_finished();
|
||||
}
|
||||
@ -703,14 +763,23 @@ unsafe impl<P> GpuFuture for PresentFuture<P> where P: GpuFuture {
|
||||
fn check_buffer_access(&self, buffer: &BufferAccess, exclusive: bool, queue: &Queue)
|
||||
-> Result<Option<(PipelineStages, AccessFlagBits)>, AccessCheckError>
|
||||
{
|
||||
unimplemented!() // TODO: VK specs don't say whether it is legal to do that
|
||||
self.previous.check_buffer_access(buffer, exclusive, queue)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn check_image_access(&self, image: &ImageAccess, layout: Layout, exclusive: bool, queue: &Queue)
|
||||
fn check_image_access(&self, image: &ImageAccess, layout: ImageLayout, exclusive: bool, queue: &Queue)
|
||||
-> Result<Option<(PipelineStages, AccessFlagBits)>, AccessCheckError>
|
||||
{
|
||||
unimplemented!() // TODO: VK specs don't say whether it is legal to do that
|
||||
let swapchain_image = self.swapchain.raw_image(self.image_id).unwrap();
|
||||
if swapchain_image.internal_object() == image.inner().internal_object() {
|
||||
// This future presents the swapchain image, which "unlocks" it. Therefore any attempt
|
||||
// to use this swapchain image afterwards shouldn't get granted automatic access.
|
||||
// Instead any attempt to access the image afterwards should get an authorization from
|
||||
// a later swapchain acquire future. Hence why we return `Unknown` here.
|
||||
Err(AccessCheckError::Unknown)
|
||||
} else {
|
||||
self.previous.check_image_access(image, layout, exclusive, queue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -37,9 +37,7 @@ pub struct Event {
|
||||
impl Event {
|
||||
/// See the docs of new().
|
||||
#[inline]
|
||||
pub fn raw(device: &Arc<Device>) -> Result<Event, OomError> {
|
||||
let vk = device.pointers();
|
||||
|
||||
pub fn raw(device: Arc<Device>) -> Result<Event, OomError> {
|
||||
let event = unsafe {
|
||||
// since the creation is constant, we use a `static` instead of a struct on the stack
|
||||
static mut INFOS: vk::EventCreateInfo = vk::EventCreateInfo {
|
||||
@ -49,13 +47,14 @@ impl Event {
|
||||
};
|
||||
|
||||
let mut output = mem::uninitialized();
|
||||
let vk = device.pointers();
|
||||
try!(check_errors(vk.CreateEvent(device.internal_object(), &INFOS,
|
||||
ptr::null(), &mut output)));
|
||||
output
|
||||
};
|
||||
|
||||
Ok(Event {
|
||||
device: device.clone(),
|
||||
device: device,
|
||||
event: event,
|
||||
})
|
||||
}
|
||||
@ -67,7 +66,7 @@ impl Event {
|
||||
/// - Panics if the device or host ran out of memory.
|
||||
///
|
||||
#[inline]
|
||||
pub fn new(device: &Arc<Device>) -> Arc<Event> {
|
||||
pub fn new(device: Arc<Device>) -> Arc<Event> {
|
||||
Arc::new(Event::raw(device).unwrap())
|
||||
}
|
||||
|
||||
@ -165,14 +164,14 @@ mod tests {
|
||||
#[test]
|
||||
fn event_create() {
|
||||
let (device, _) = gfx_dev_and_queue!();
|
||||
let event = Event::new(&device);
|
||||
let event = Event::new(device);
|
||||
assert!(!event.signaled().unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn event_set() {
|
||||
let (device, _) = gfx_dev_and_queue!();
|
||||
let mut event = Event::new(&device);
|
||||
let mut event = Event::new(device);
|
||||
assert!(!event.signaled().unwrap());
|
||||
|
||||
Arc::get_mut(&mut event).unwrap().set();
|
||||
@ -183,7 +182,7 @@ mod tests {
|
||||
fn event_reset() {
|
||||
let (device, _) = gfx_dev_and_queue!();
|
||||
|
||||
let mut event = Event::new(&device);
|
||||
let mut event = Event::new(device);
|
||||
Arc::get_mut(&mut event).unwrap().set();
|
||||
assert!(event.signaled().unwrap());
|
||||
|
||||
|
@ -20,7 +20,7 @@ use device::Device;
|
||||
use device::DeviceOwned;
|
||||
use device::Queue;
|
||||
use image::ImageAccess;
|
||||
use image::Layout;
|
||||
use image::ImageLayout;
|
||||
use sync::AccessCheckError;
|
||||
use sync::AccessFlagBits;
|
||||
use sync::FlushError;
|
||||
@ -58,6 +58,35 @@ pub enum FenceSignalFutureBehavior {
|
||||
}
|
||||
|
||||
/// Represents a fence being signaled after a previous event.
|
||||
///
|
||||
/// Contrary to most other future types, it is possible to block the current thread until the event
|
||||
/// happens. This is done by calling the `wait()` function.
|
||||
///
|
||||
/// Also note that the `GpuFuture` trait is implemented on `Arc<FenceSignalFuture<_>>`.
|
||||
/// This means that you can put this future in an `Arc` and keep a copy of it somewhere in order
|
||||
/// to know when the execution reached that point.
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::Arc;
|
||||
/// use vulkano::sync::GpuFuture;
|
||||
///
|
||||
/// # let future: Box<GpuFuture> = return;
|
||||
/// // Assuming you have a chain of operations, like this:
|
||||
/// // let future = ...
|
||||
/// // .then_execute(foo)
|
||||
/// // .then_execute(bar)
|
||||
///
|
||||
/// // You can signal a fence at this point of the chain, and put the future in an `Arc`.
|
||||
/// let fence_signal = Arc::new(future.then_signal_fence());
|
||||
///
|
||||
/// // And then continue the chain:
|
||||
/// // fence_signal.clone()
|
||||
/// // .then_execute(baz)
|
||||
/// // .then_execute(qux)
|
||||
///
|
||||
/// // Later you can wait until you reach the point of `fence_signal`:
|
||||
/// fence_signal.wait(None).unwrap();
|
||||
/// ```
|
||||
#[must_use = "Dropping this object will immediately block the thread until the GPU has finished processing the submission"]
|
||||
pub struct FenceSignalFuture<F> where F: GpuFuture {
|
||||
// Current state. See the docs of `FenceSignalFutureState`.
|
||||
@ -91,6 +120,32 @@ enum FenceSignalFutureState<F> {
|
||||
Poisonned,
|
||||
}
|
||||
|
||||
impl<F> FenceSignalFuture<F> where F: GpuFuture {
|
||||
/// Blocks the current thread until the fence is signaled by the GPU. Performs a flush if
|
||||
/// necessary.
|
||||
///
|
||||
/// If `timeout` is `None`, then the wait is infinite. Otherwise the thread will unblock after
|
||||
/// the specified timeout has elapsed and an error will be returned.
|
||||
///
|
||||
/// If the wait is successful, this function also cleans any resource locked by previous
|
||||
/// submissions.
|
||||
pub fn wait(&self, timeout: Option<Duration>) -> Result<(), FlushError> {
|
||||
let mut state = self.state.lock().unwrap();
|
||||
|
||||
self.flush_impl(&mut state)?;
|
||||
|
||||
match mem::replace(&mut *state, FenceSignalFutureState::Cleaned) {
|
||||
FenceSignalFutureState::Flushed(previous, fence) => {
|
||||
fence.wait(timeout)?;
|
||||
unsafe { previous.signal_finished(); }
|
||||
Ok(())
|
||||
},
|
||||
FenceSignalFutureState::Cleaned => Ok(()),
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F> FenceSignalFuture<F> where F: GpuFuture {
|
||||
// Implementation of `cleanup_finished`, but takes a `&self` instead of a `&mut self`.
|
||||
// This is an external function so that we can also call it from an `Arc<FenceSignalFuture>`.
|
||||
@ -99,12 +154,25 @@ impl<F> FenceSignalFuture<F> where F: GpuFuture {
|
||||
let mut state = self.state.lock().unwrap();
|
||||
|
||||
match *state {
|
||||
FenceSignalFutureState::Flushed(_, ref fence) => {
|
||||
FenceSignalFutureState::Flushed(ref mut prev, ref fence) => {
|
||||
match fence.wait(Some(Duration::from_secs(0))) {
|
||||
Ok(()) => (),
|
||||
Err(_) => return,
|
||||
Ok(()) => unsafe {
|
||||
prev.signal_finished()
|
||||
},
|
||||
Err(_) => {
|
||||
prev.cleanup_finished();
|
||||
return
|
||||
},
|
||||
}
|
||||
},
|
||||
FenceSignalFutureState::Pending(ref mut prev, _) => {
|
||||
prev.cleanup_finished();
|
||||
return;
|
||||
},
|
||||
FenceSignalFutureState::PartiallyFlushed(ref mut prev, _) => {
|
||||
prev.cleanup_finished();
|
||||
return;
|
||||
},
|
||||
_ => return,
|
||||
};
|
||||
|
||||
@ -305,7 +373,7 @@ unsafe impl<F> GpuFuture for FenceSignalFuture<F> where F: GpuFuture {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn check_image_access(&self, image: &ImageAccess, layout: Layout, exclusive: bool, queue: &Queue)
|
||||
fn check_image_access(&self, image: &ImageAccess, layout: ImageLayout, exclusive: bool, queue: &Queue)
|
||||
-> Result<Option<(PipelineStages, AccessFlagBits)>, AccessCheckError> {
|
||||
let state = self.state.lock().unwrap();
|
||||
if let Some(previous) = state.get_prev() {
|
||||
@ -393,7 +461,7 @@ unsafe impl<F> GpuFuture for Arc<FenceSignalFuture<F>> where F: GpuFuture {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn check_image_access(&self, image: &ImageAccess, layout: Layout, exclusive: bool, queue: &Queue)
|
||||
fn check_image_access(&self, image: &ImageAccess, layout: ImageLayout, exclusive: bool, queue: &Queue)
|
||||
-> Result<Option<(PipelineStages, AccessFlagBits)>, AccessCheckError>
|
||||
{
|
||||
(**self).check_image_access(image, layout, exclusive, queue)
|
||||
|
@ -15,7 +15,7 @@ use device::Device;
|
||||
use device::DeviceOwned;
|
||||
use device::Queue;
|
||||
use image::ImageAccess;
|
||||
use image::Layout;
|
||||
use image::ImageLayout;
|
||||
use sync::AccessCheckError;
|
||||
use sync::AccessFlagBits;
|
||||
use sync::FlushError;
|
||||
@ -173,7 +173,7 @@ unsafe impl<A, B> GpuFuture for JoinFuture<A, B> where A: GpuFuture, B: GpuFutur
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn check_image_access(&self, image: &ImageAccess, layout: Layout, exclusive: bool, queue: &Queue)
|
||||
fn check_image_access(&self, image: &ImageAccess, layout: ImageLayout, exclusive: bool, queue: &Queue)
|
||||
-> Result<Option<(PipelineStages, AccessFlagBits)>, AccessCheckError>
|
||||
{
|
||||
let first = self.first.check_image_access(image, layout, exclusive, queue);
|
||||
|
@ -21,7 +21,8 @@ use command_buffer::submit::SubmitCommandBufferError;
|
||||
use device::DeviceOwned;
|
||||
use device::Queue;
|
||||
use image::ImageAccess;
|
||||
use image::Layout;
|
||||
use image::ImageLayout;
|
||||
use swapchain;
|
||||
use swapchain::Swapchain;
|
||||
use swapchain::PresentFuture;
|
||||
use sync::AccessFlagBits;
|
||||
@ -59,6 +60,8 @@ pub unsafe trait GpuFuture: DeviceOwned {
|
||||
/// It is the responsibility of the caller to ensure that the submission is going to be
|
||||
/// submitted only once. However keep in mind that this function can perfectly be called
|
||||
/// multiple times (as long as the returned object is only submitted once).
|
||||
/// Also note that calling `flush()` on the future may change the value returned by
|
||||
/// `build_submission()`.
|
||||
///
|
||||
/// It is however the responsibility of the implementation to not return the same submission
|
||||
/// from multiple different future objects. For example if you implement `GpuFuture` on
|
||||
@ -124,7 +127,7 @@ pub unsafe trait GpuFuture: DeviceOwned {
|
||||
///
|
||||
/// > **Note**: Keep in mind that changing the layout of an image also requires exclusive
|
||||
/// > access.
|
||||
fn check_image_access(&self, image: &ImageAccess, layout: Layout, exclusive: bool,
|
||||
fn check_image_access(&self, image: &ImageAccess, layout: ImageLayout, exclusive: bool,
|
||||
queue: &Queue) -> Result<Option<(PipelineStages, AccessFlagBits)>, AccessCheckError>;
|
||||
|
||||
/// Joins this future with another one, representing the moment when both events have happened.
|
||||
@ -197,9 +200,7 @@ pub unsafe trait GpuFuture: DeviceOwned {
|
||||
/// > function. If so, consider using `then_signal_fence_and_flush`.
|
||||
#[inline]
|
||||
fn then_signal_fence(self) -> FenceSignalFuture<Self> where Self: Sized {
|
||||
fence_signal::then_signal_fence(self, FenceSignalFutureBehavior::Block {
|
||||
timeout: None
|
||||
})
|
||||
fence_signal::then_signal_fence(self, FenceSignalFutureBehavior::Continue)
|
||||
}
|
||||
|
||||
/// Signals a fence after this future. Returns another future that represents the signal.
|
||||
@ -225,7 +226,7 @@ pub unsafe trait GpuFuture: DeviceOwned {
|
||||
image_index: usize) -> PresentFuture<Self>
|
||||
where Self: Sized
|
||||
{
|
||||
Swapchain::present(swapchain, self, queue, image_index)
|
||||
swapchain::present(swapchain, self, queue, image_index)
|
||||
}
|
||||
}
|
||||
|
||||
@ -268,7 +269,7 @@ unsafe impl<F: ?Sized> GpuFuture for Box<F> where F: GpuFuture {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn check_image_access(&self, image: &ImageAccess, layout: Layout, exclusive: bool, queue: &Queue)
|
||||
fn check_image_access(&self, image: &ImageAccess, layout: ImageLayout, exclusive: bool, queue: &Queue)
|
||||
-> Result<Option<(PipelineStages, AccessFlagBits)>, AccessCheckError>
|
||||
{
|
||||
(**self).check_image_access(image, layout, exclusive, queue)
|
||||
@ -281,17 +282,26 @@ pub enum AccessError {
|
||||
/// Exclusive access is denied.
|
||||
ExclusiveDenied,
|
||||
|
||||
/// The resource is already in use, and there is no tracking of concurrent usages.
|
||||
AlreadyInUse,
|
||||
|
||||
UnexpectedImageLayout {
|
||||
allowed: Layout,
|
||||
requested: Layout,
|
||||
allowed: ImageLayout,
|
||||
requested: ImageLayout,
|
||||
},
|
||||
|
||||
/// Trying to use an image without transitionning it from the "undefined" or "preinitialized"
|
||||
/// layouts first.
|
||||
ImageNotInitialized {
|
||||
/// The layout that was requested for the image.
|
||||
requested: Layout,
|
||||
requested: ImageLayout,
|
||||
},
|
||||
|
||||
/// Trying to use a buffer that still contains garbage data.
|
||||
BufferNotInitialized,
|
||||
|
||||
/// Trying to use a swapchain image without depending on a corresponding acquire image future.
|
||||
SwapchainImageAcquireOnly,
|
||||
}
|
||||
|
||||
impl error::Error for AccessError {
|
||||
@ -301,6 +311,9 @@ impl error::Error for AccessError {
|
||||
AccessError::ExclusiveDenied => {
|
||||
"only shared access is allowed for this resource"
|
||||
},
|
||||
AccessError::AlreadyInUse => {
|
||||
"the resource is already in use, and there is no tracking of concurrent usages"
|
||||
},
|
||||
AccessError::UnexpectedImageLayout { .. } => {
|
||||
unimplemented!() // TODO: find a description
|
||||
},
|
||||
@ -308,6 +321,13 @@ impl error::Error for AccessError {
|
||||
"trying to use an image without transitionning it from the undefined or \
|
||||
preinitialized layouts first"
|
||||
},
|
||||
AccessError::BufferNotInitialized => {
|
||||
"trying to use a buffer that still contains garbage data"
|
||||
},
|
||||
AccessError::SwapchainImageAcquireOnly => {
|
||||
"trying to use a swapchain image without depending on a corresponding acquire \
|
||||
image future"
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ use device::Device;
|
||||
use device::DeviceOwned;
|
||||
use device::Queue;
|
||||
use image::ImageAccess;
|
||||
use image::Layout;
|
||||
use image::ImageLayout;
|
||||
use sync::AccessCheckError;
|
||||
use sync::AccessFlagBits;
|
||||
use sync::FlushError;
|
||||
@ -72,7 +72,7 @@ unsafe impl GpuFuture for NowFuture {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn check_image_access(&self, image: &ImageAccess, layout: Layout, exclusive: bool, queue: &Queue)
|
||||
fn check_image_access(&self, image: &ImageAccess, layout: ImageLayout, exclusive: bool, queue: &Queue)
|
||||
-> Result<Option<(PipelineStages, AccessFlagBits)>, AccessCheckError>
|
||||
{
|
||||
Err(AccessCheckError::Unknown)
|
||||
|
@ -20,7 +20,7 @@ use device::Device;
|
||||
use device::DeviceOwned;
|
||||
use device::Queue;
|
||||
use image::ImageAccess;
|
||||
use image::Layout;
|
||||
use image::ImageLayout;
|
||||
use sync::AccessCheckError;
|
||||
use sync::AccessFlagBits;
|
||||
use sync::FlushError;
|
||||
@ -136,7 +136,7 @@ unsafe impl<F> GpuFuture for SemaphoreSignalFuture<F> where F: GpuFuture {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn check_image_access(&self, image: &ImageAccess, layout: Layout, exclusive: bool, queue: &Queue)
|
||||
fn check_image_access(&self, image: &ImageAccess, layout: ImageLayout, exclusive: bool, queue: &Queue)
|
||||
-> Result<Option<(PipelineStages, AccessFlagBits)>, AccessCheckError>
|
||||
{
|
||||
self.previous.check_image_access(image, layout, exclusive, queue).map(|_| None)
|
||||
|
Loading…
Reference in New Issue
Block a user