Merge branch 'master' into incoming

This commit is contained in:
Pierre Krieger 2016-11-09 13:21:15 +01:00
commit 1ca3ef8231
19 changed files with 518 additions and 48 deletions

View File

@ -1,7 +1,9 @@
[package]
name = "vk-sys"
version = "0.2.0"
version = "0.2.1"
authors = ["Pierre Krieger <pierre.krieger1708@gmail.com>"]
repository = "https://github.com/tomaka/vulkano"
description = "Bindings for the Vulkan graphics API"
license = "MIT/Apache-2.0"
documentation = "https://docs.rs/vk-sys"
keywords = ["vulkan", "bindings", "graphics", "gpu"]

View File

@ -1,6 +1,6 @@
[package]
name = "vulkano-shaders"
version = "0.3.0"
version = "0.3.1"
authors = ["Pierre Krieger <pierre.krieger1708@gmail.com>"]
repository = "https://github.com/tomaka/vulkano"
description = "Shaders "

View File

@ -47,7 +47,10 @@ pub fn build_glsl_shaders<'a, I>(shaders: I)
let mut file_output = File::create(&dest.join("shaders").join(shader))
.expect("failed to open shader output");
let content = glsl_to_spirv::compile(&shader_content, ty).unwrap();
let content = match glsl_to_spirv::compile(&shader_content, ty) {
Ok(compiled) => compiled,
Err(message) => panic!("{}\nfailed to compile shader", message),
};
let output = reflect("Shader", content).unwrap();
write!(file_output, "{}", output).unwrap();
}

View File

@ -1,6 +1,6 @@
[package]
name = "vulkano-win"
version = "0.3.0"
version = "0.3.1"
authors = ["Pierre Krieger <pierre.krieger1708@gmail.com>"]
repository = "https://github.com/tomaka/vulkano"
description = "Link between vulkano and winit"

View File

@ -1,11 +1,11 @@
[package]
name = "vulkano"
version = "0.3.0"
version = "0.3.1"
authors = ["Pierre Krieger <pierre.krieger1708@gmail.com>"]
repository = "https://github.com/tomaka/vulkano"
description = "Safe wrapper for the Vulkan graphics API"
license = "MIT/Apache-2.0"
documentation = "http://tomaka.github.io/vulkano/vulkano/index.html"
documentation = "https://docs.rs/vulkano"
[dependencies]
crossbeam = "0.2.5"

View File

@ -126,7 +126,7 @@ pub struct Device {
physical_device: usize,
device: vk::Device,
vk: vk::DevicePointers,
standard_pool: Mutex<Option<Weak<StdMemoryPool>>>, // TODO: use Weak::new() instead
standard_pool: Mutex<Weak<StdMemoryPool>>,
standard_command_pools: Mutex<HashMap<u32, Weak<StandardCommandPool>, BuildHasherDefault<FnvHasher>>>,
features: Features,
extensions: DeviceExtensions,
@ -154,10 +154,7 @@ impl Device {
///
/// # Panic
///
/// - Panics if one of the requested features is not supported by the physical device.
/// - Panics if one of the queue families doesn't belong to the given device.
/// - Panics if you request more queues from a family than available.
/// - Panics if one of the priorities is outside of the `[0.0 ; 1.0]` range.
///
// TODO: return Arc<Queue> and handle synchronization in the Queue
// TODO: should take the PhysicalDevice by value
@ -168,7 +165,9 @@ impl Device {
{
let queue_families = queue_families.into_iter();
assert!(phys.supported_features().superset_of(&requested_features));
if !phys.supported_features().superset_of(&requested_features) {
return Err(DeviceCreationError::UnsupportedFeatures);
}
let vk_i = phys.instance().pointers();
@ -203,13 +202,17 @@ impl Device {
// checking the parameters
assert_eq!(queue_family.physical_device().internal_object(),
phys.internal_object());
assert!(priority >= 0.0 && priority <= 1.0);
if priority < 0.0 || priority > 1.0 {
return Err(DeviceCreationError::PriorityOutOfRange);
}
// adding to `queues` and `output_queues`
if let Some(q) = queues.iter_mut().find(|q| q.0 == queue_family.id()) {
output_queues.push((queue_family.id(), q.1.len() as u32));
q.1.push(priority);
assert!(q.1.len() < queue_family.queues_count());
if q.1.len() >= queue_family.queues_count() {
return Err(DeviceCreationError::TooManyQueuesForFamily);
}
continue;
}
queues.push((queue_family.id(), vec![priority]));
@ -276,7 +279,7 @@ impl Device {
physical_device: phys.index(),
device: device,
vk: vk,
standard_pool: Mutex::new(None),
standard_pool: Mutex::new(Weak::new()),
standard_command_pools: Mutex::new(Default::default()),
features: requested_features.clone(),
extensions: extensions.clone(),
@ -345,13 +348,13 @@ impl Device {
pub fn standard_pool(me: &Arc<Self>) -> Arc<StdMemoryPool> {
let mut pool = me.standard_pool.lock().unwrap();
if let Some(p) = pool.as_ref().and_then(|w| w.upgrade()) {
if let Some(p) = pool.upgrade() {
return p;
}
// The weak pointer is empty, so we create the pool.
let new_pool = StdMemoryPool::new(me);
*pool = Some(Arc::downgrade(&new_pool));
*pool = Arc::downgrade(&new_pool);
new_pool
}
@ -466,7 +469,12 @@ pub enum DeviceCreationError {
OutOfHostMemory,
/// There is no memory available on the device (ie. video memory).
OutOfDeviceMemory,
// FIXME: other values
/// Tried to create too many queues for a given family.
TooManyQueuesForFamily,
/// Some of the requested features are unsupported by the physical device.
UnsupportedFeatures,
/// The priority of one of the queues is out of the [0.0; 1.0] range.
PriorityOutOfRange,
}
impl error::Error for DeviceCreationError {
@ -474,7 +482,18 @@ impl error::Error for DeviceCreationError {
fn description(&self) -> &str {
match *self {
DeviceCreationError::OutOfHostMemory => "no memory available on the host",
DeviceCreationError::OutOfDeviceMemory => "no memory available on the graphical device",
DeviceCreationError::OutOfDeviceMemory => {
"no memory available on the graphical device"
},
DeviceCreationError::TooManyQueuesForFamily => {
"tried to create too many queues for a given family"
},
DeviceCreationError::UnsupportedFeatures => {
"some of the requested features are unsupported by the physical device"
},
DeviceCreationError::PriorityOutOfRange => {
"the priority of one of the queues is out of the [0.0; 1.0] range"
},
}
}
}
@ -563,10 +582,79 @@ unsafe impl SynchronizedVulkanObject for Queue {
#[cfg(test)]
mod tests {
use std::sync::Arc;
use device::Device;
use device::DeviceCreationError;
use device::DeviceExtensions;
use features::Features;
use instance;
#[test]
fn one_ref() {
let (mut device, _) = gfx_dev_and_queue!();
assert!(Arc::get_mut(&mut device).is_some());
}
#[test]
fn too_many_queues() {
let instance = instance!();
let physical = match instance::PhysicalDevice::enumerate(&instance).next() {
Some(p) => p,
None => return
};
let family = physical.queue_families().next().unwrap();
let queues = (0 .. family.queues_count() + 1).map(|_| (family, 1.0));
match Device::new(&physical, &Features::none(), &DeviceExtensions::none(), queues) {
Err(DeviceCreationError::TooManyQueuesForFamily) => return, // Success
_ => panic!()
};
}
#[test]
fn unsupposed_features() {
let instance = instance!();
let physical = match instance::PhysicalDevice::enumerate(&instance).next() {
Some(p) => p,
None => return
};
let family = physical.queue_families().next().unwrap();
let features = Features::all();
// In the unlikely situation where the device supports everything, we ignore the test.
if physical.supported_features().superset_of(&features) {
return;
}
match Device::new(&physical, &features, &DeviceExtensions::none(), Some((family, 1.0))) {
Err(DeviceCreationError::UnsupportedFeatures) => return, // Success
_ => panic!()
};
}
#[test]
fn priority_out_of_range() {
let instance = instance!();
let physical = match instance::PhysicalDevice::enumerate(&instance).next() {
Some(p) => p,
None => return
};
let family = physical.queue_families().next().unwrap();
match Device::new(&physical, &Features::none(),
&DeviceExtensions::none(), Some((family, 1.4)))
{
Err(DeviceCreationError::PriorityOutOfRange) => (), // Success
_ => panic!()
};
match Device::new(&physical, &Features::none(),
&DeviceExtensions::none(), Some((family, -0.2)))
{
Err(DeviceCreationError::PriorityOutOfRange) => (), // Success
_ => panic!()
};
}
}

View File

@ -58,6 +58,18 @@ macro_rules! features {
}
}
/// Builds a `Features` object with all values to true.
///
/// > **Note**: This function is used for testing purposes, and is probably useless in
/// > a real code.
pub fn all() -> Features {
Features {
$(
$name: true,
)+
}
}
/// Returns true if `self` is a superset of the parameter.
///
/// That is, for each feature of the parameter that is true, the corresponding value

View File

@ -33,6 +33,53 @@ use instance::InstanceExtensions;
/// An instance of a Vulkan context. This is the main object that should be created by an
/// application before everything else.
///
/// See the documentation of [the `instance` module](index.html) for an introduction about
/// Vulkan instances.
///
/// # Extensions and application infos
///
/// Please check the documentation of [the `instance` module](index.html).
///
/// # Layers
///
/// When creating an `Instance`, you have the possibility to pass a list of **layers** that will
/// be activated on the newly-created instance. The list of available layers can be retrieved by
/// calling [the `layers_list` function](fn.layers_list.html).
///
/// A layer is a component that will hook and potentially modify the Vulkan function calls.
/// For example, activating a layer could add a frames-per-second counter on the screen, or it
/// could send informations to a debugger that will debug your application.
///
/// > **Note**: From an application's point of view, layers "just exist". In practice, on Windows
/// > and Linux layers can be installed by third party installers or by package managers and can
/// > also be activated by setting the value of the `VK_INSTANCE_LAYERS` environment variable
/// > before starting the program. See the documentation of the official Vulkan loader for these
/// > platforms.
///
/// > **Note**: In practice, the most common use of layers right now is for debugging purposes.
/// > To do so, you are encouraged to set the `VK_INSTANCE_LAYERS` environment variable on Windows
/// > or Linux instead of modifying the source code of your program. For example:
/// > `export VK_INSTANCE_LAYERS=VK_LAYER_LUNARG_api_dump` on Linux if you installed the Vulkan SDK
/// > will print the list of raw Vulkan function calls.
///
/// ## Example
///
/// ```ignore
/// // FIXME: this example doesn't run because of ownership problems ; Instance::new() needs a tweak
/// use vulkano::instance;
/// use vulkano::instance::Instance;
/// use vulkano::instance::InstanceExtensions;
///
/// // For the sake of the example, we activate all the layers that contain the word "foo" in their
/// // description.
/// let layers = instance::layers_list().unwrap()
/// .filter(|l| l.description().contains("foo"))
/// .map(|l| l.name());
///
/// let instance = Instance::new(None, &InstanceExtensions::none(), layers).unwrap();
/// ```
// TODO: mention that extensions must be supported by layers as well
pub struct Instance {
instance: vk::Instance,
//alloc: Option<Box<Alloc + Send + Sync>>,
@ -45,6 +92,21 @@ pub struct Instance {
impl Instance {
/// Initializes a new instance of Vulkan.
///
/// See the documentation of `Instance` or of [the `instance` module](index.html) for more
/// details.
///
/// # Example
///
/// ```no_run
/// use vulkano::instance::Instance;
/// use vulkano::instance::InstanceExtensions;
///
/// let instance = match Instance::new(None, &InstanceExtensions::none(), None) {
/// Ok(i) => i,
/// Err(err) => panic!("Couldn't build instance: {:?}", err)
/// };
/// ```
///
/// # Panic
///
/// - Panics if the version numbers passed in `ApplicationInfo` are too large can't be
@ -220,6 +282,19 @@ impl Instance {
}*/
/// Returns the list of extensions that have been loaded.
///
/// This list is equal to what was passed to `Instance::new()`.
///
/// # Example
///
/// ```no_run
/// use vulkano::instance::Instance;
/// use vulkano::instance::InstanceExtensions;
///
/// let extensions = InstanceExtensions::supported_by_core().unwrap();
/// let instance = Instance::new(None, &extensions, None).unwrap();
/// assert_eq!(instance.loaded_extensions(), &extensions);
/// ```
#[inline]
pub fn loaded_extensions(&self) -> &InstanceExtensions {
&self.extensions
@ -268,6 +343,7 @@ impl Drop for Instance {
}
/// Information that can be given to the Vulkan driver so that it can identify your application.
// TODO: better documentation for struct and methods
#[derive(Debug, Clone)]
pub struct ApplicationInfo<'a> {
/// Name of the application.
@ -401,6 +477,23 @@ struct PhysicalDeviceInfos {
///
/// This struct simply contains a pointer to an instance and a number representing the physical
/// device. You are therefore encouraged to pass this around by value instead of by reference.
///
/// # Example
///
/// ```no_run
/// # use vulkano::instance::Instance;
/// # use vulkano::instance::InstanceExtensions;
/// use vulkano::instance::PhysicalDevice;
///
/// # let instance = Instance::new(None, &InstanceExtensions::none(), None).unwrap();
/// for physical_device in PhysicalDevice::enumerate(&instance) {
/// print_infos(physical_device);
/// }
///
/// fn print_infos(dev: PhysicalDevice) {
/// println!("Name: {}", dev.name());
/// }
/// ```
#[derive(Debug, Copy, Clone)]
pub struct PhysicalDevice<'a> {
instance: &'a Arc<Instance>,
@ -409,6 +502,19 @@ pub struct PhysicalDevice<'a> {
impl<'a> PhysicalDevice<'a> {
/// Returns an iterator that enumerates the physical devices available.
///
/// # Example
///
/// ```no_run
/// # use vulkano::instance::Instance;
/// # use vulkano::instance::InstanceExtensions;
/// use vulkano::instance::PhysicalDevice;
///
/// # let instance = Instance::new(None, &InstanceExtensions::none(), None).unwrap();
/// for physical_device in PhysicalDevice::enumerate(&instance) {
/// println!("Available device: {}", physical_device.name());
/// }
/// ```
#[inline]
pub fn enumerate(instance: &'a Arc<Instance>) -> PhysicalDevicesIter<'a> {
PhysicalDevicesIter {
@ -420,6 +526,17 @@ impl<'a> PhysicalDevice<'a> {
/// Returns a physical device from its index. Returns `None` if out of range.
///
/// Indices range from 0 to the number of devices.
///
/// # Example
///
/// ```no_run
/// use vulkano::instance::Instance;
/// use vulkano::instance::InstanceExtensions;
/// use vulkano::instance::PhysicalDevice;
///
/// let instance = Instance::new(None, &InstanceExtensions::none(), None).unwrap();
/// let first_physical_device = PhysicalDevice::from_index(&instance, 0).unwrap();
/// ```
#[inline]
pub fn from_index(instance: &'a Arc<Instance>, index: usize) -> Option<PhysicalDevice<'a>> {
if instance.physical_devices.len() > index {
@ -433,6 +550,17 @@ impl<'a> PhysicalDevice<'a> {
}
/// Returns the instance corresponding to this physical device.
///
/// # Example
///
/// ```no_run
/// use vulkano::instance::PhysicalDevice;
///
/// fn do_something(physical_device: PhysicalDevice) {
/// let _loaded_extensions = physical_device.instance().loaded_extensions();
/// // ...
/// }
/// ```
#[inline]
pub fn instance(&self) -> &'a Arc<Instance> {
&self.instance
@ -458,6 +586,20 @@ impl<'a> PhysicalDevice<'a> {
}
/// Returns the type of the device.
///
/// # Example
///
/// ```no_run
/// # use vulkano::instance::Instance;
/// # use vulkano::instance::InstanceExtensions;
/// use vulkano::instance::PhysicalDevice;
///
/// # let instance = Instance::new(None, &InstanceExtensions::none(), None).unwrap();
/// for physical_device in PhysicalDevice::enumerate(&instance) {
/// println!("Available device: {} (type: {:?})",
/// physical_device.name(), physical_device.ty());
/// }
/// ```
#[inline]
pub fn ty(&self) -> PhysicalDeviceType {
match self.instance.physical_devices[self.device].properties.deviceType {

View File

@ -22,16 +22,41 @@ use instance::loader::LoadingError;
use version::Version;
/// Queries the list of layers that are available when creating an instance.
///
/// On success, this function returns an iterator that produces
/// [`LayerProperties`](struct.LayerProperties.html) objects. In order to enable a layer, you need
/// to pass its name (returned by `LayerProperties::name()`) when creating the
/// [`Instance`](struct.Instance.html).
///
/// This function returns an error if it failed to load the Vulkan library.
///
/// > **Note**: It is possible that one of the layers enumerated here is no longer available when
/// > you create the `Instance`. This will lead to an error when calling `Instance::new`. The
/// > author isn't aware of any situation where this would happen, but it is theoretically possible
/// > according to the specifications.
///
/// # Example
///
/// ```no_run
/// use vulkano::instance;
///
/// for layer in instance::layers_list().unwrap() {
/// println!("Available layer: {}", layer.name());
/// }
/// ```
pub fn layers_list() -> Result<LayersIterator, LayersListError> {
unsafe {
let entry_points = try!(loader::entry_points());
let mut num = 0;
try!(check_errors(entry_points.EnumerateInstanceLayerProperties(&mut num, ptr::null_mut())));
try!(check_errors({
entry_points.EnumerateInstanceLayerProperties(&mut num, ptr::null_mut())
}));
let mut layers: Vec<vk::LayerProperties> = Vec::with_capacity(num as usize);
try!(check_errors(entry_points.EnumerateInstanceLayerProperties(&mut num,
layers.as_mut_ptr())));
try!(check_errors({
entry_points.EnumerateInstanceLayerProperties(&mut num, layers.as_mut_ptr())
}));
layers.set_len(num as usize);
Ok(LayersIterator {
@ -40,31 +65,81 @@ pub fn layers_list() -> Result<LayersIterator, LayersListError> {
}
}
/// Properties of an available layer.
/// Properties of a layer.
pub struct LayerProperties {
props: vk::LayerProperties,
}
impl LayerProperties {
/// Returns the name of the layer.
///
/// If you want to enable this layer on an instance, you need to pass this value to
/// `Instance::new`.
///
/// # Example
///
/// ```no_run
/// use vulkano::instance;
///
/// for layer in instance::layers_list().unwrap() {
/// println!("Layer name: {}", layer.name());
/// }
/// ```
#[inline]
pub fn name(&self) -> &str {
unsafe { CStr::from_ptr(self.props.layerName.as_ptr()).to_str().unwrap() }
}
/// Returns a description of the layer.
///
/// This description is chosen by the layer itself.
///
/// # Example
///
/// ```no_run
/// use vulkano::instance;
///
/// for layer in instance::layers_list().unwrap() {
/// println!("Layer description: {}", layer.description());
/// }
/// ```
#[inline]
pub fn description(&self) -> &str {
unsafe { CStr::from_ptr(self.props.description.as_ptr()).to_str().unwrap() }
}
/// Returns the version of Vulkan supported by this layer.
///
/// # Example
///
/// ```no_run
/// use vulkano::instance;
/// use vulkano::instance::Version;
///
/// for layer in instance::layers_list().unwrap() {
/// if layer.vulkan_version() >= (Version { major: 2, minor: 0, patch: 0 }) {
/// println!("Layer {} requires Vulkan 2.0", layer.name());
/// }
/// }
/// ```
#[inline]
pub fn vulkan_version(&self) -> Version {
Version::from_vulkan_version(self.props.specVersion)
}
/// Returns an implementation-specific version number for this layer.
///
/// The number is chosen by the layer itself. It can be used for bug reports for example.
///
/// # Example
///
/// ```no_run
/// use vulkano::instance;
///
/// for layer in instance::layers_list().unwrap() {
/// println!("Layer {} - Version: {}", layer.name(), layer.implementation_version());
/// }
/// ```
#[inline]
pub fn implementation_version(&self) -> u32 {
self.props.implementationVersion
@ -159,7 +234,11 @@ mod tests {
#[test]
fn layers_list() {
let mut list = instance::layers_list().unwrap();
let mut list = match instance::layers_list() {
Ok(l) => l,
Err(_) => return
};
while let Some(_) = list.next() {}
}
}

View File

@ -17,7 +17,7 @@
//! use vulkano::instance::Instance;
//! use vulkano::instance::InstanceExtensions;
//!
//! let _instance = match Instance::new(None, &InstanceExtensions::none(), None) {
//! let instance = match Instance::new(None, &InstanceExtensions::none(), None) {
//! Ok(i) => i,
//! Err(err) => panic!("Couldn't build instance: {:?}", err)
//! };
@ -33,7 +33,7 @@
//!
//! # let instance = Instance::new(None, &InstanceExtensions::none(), None).unwrap();
//! for physical_device in PhysicalDevice::enumerate(&instance) {
//! println!("Available device: {}", physical_device.name())
//! println!("Available device: {}", physical_device.name());
//! }
//! ```
//!
@ -65,7 +65,7 @@
//! .. InstanceExtensions::none()
//! };
//!
//! let _instance = match Instance::new(None, &extensions, None) {
//! let instance = match Instance::new(None, &extensions, None) {
//! Ok(i) => i,
//! Err(err) => panic!("Couldn't build instance: {:?}", err)
//! };
@ -124,6 +124,7 @@ pub use self::layers::layers_list;
pub use self::layers::LayerProperties;
pub use self::layers::LayersIterator;
pub use self::loader::LoadingError;
pub use version::Version;
pub mod debug;

View File

@ -26,6 +26,20 @@ use check_errors;
use vk;
/// Represents memory that has been allocated.
///
/// The destructor of `DeviceMemory` automatically frees the memory.
///
/// # Example
///
/// ```no_run
/// use vulkano::memory::DeviceMemory;
///
/// # let device: std::sync::Arc<vulkano::device::Device> = unsafe { ::std::mem::uninitialized() };
/// let mem_ty = device.physical_device().memory_types().next().unwrap();
///
/// // Allocates 1kB of memory.
/// let memory = DeviceMemory::alloc(&device, mem_ty, 1024).unwrap();
/// ```
#[derive(Debug)]
pub struct DeviceMemory<D = Arc<Device>> where D: SafeDeref<Target = Device> {
memory: vk::DeviceMemory,
@ -46,8 +60,9 @@ impl<D> DeviceMemory<D> where D: SafeDeref<Target = Device> {
/// - Panics if `memory_type` doesn't belong to the same physical device as `device`.
///
// TODO: VK_ERROR_TOO_MANY_OBJECTS error
// TODO: remove that `D` generic and use `Arc<Device>`
#[inline]
pub fn alloc(device: &D, memory_type: &MemoryType, size: usize)
pub fn alloc(device: &D, memory_type: MemoryType, size: usize)
-> Result<DeviceMemory<D>, OomError>
where D: Clone
{
@ -90,7 +105,7 @@ impl<D> DeviceMemory<D> where D: SafeDeref<Target = Device> {
/// - Panics if `memory_type` doesn't belong to the same physical device as `device`.
/// - Panics if the memory type is not host-visible.
///
pub fn alloc_and_map(device: &D, memory_type: &MemoryType, size: usize)
pub fn alloc_and_map(device: &D, memory_type: MemoryType, size: usize)
-> Result<MappedDeviceMemory<D>, OomError>
where D: Clone
{
@ -156,6 +171,34 @@ impl<D> Drop for DeviceMemory<D> where D: SafeDeref<Target = Device> {
}
/// Represents memory that has been allocated and mapped in CPU accessible space.
///
/// Can be obtained with `DeviceMemory::alloc_and_map`. The function will panic if the memory type
/// is not host-accessible.
///
/// In order to access the content of the allocated memory, you can use the `read_write` method.
/// This method returns a guard object that derefs to the content.
///
/// # Example
///
/// ```no_run
/// use vulkano::memory::DeviceMemory;
///
/// # let device: std::sync::Arc<vulkano::device::Device> = unsafe { ::std::mem::uninitialized() };
/// // The memory type must be mappable.
/// let mem_ty = device.physical_device().memory_types()
/// .filter(|t| t.is_host_visible())
/// .next().unwrap(); // Vk specs guarantee that this can't fail
///
/// // Allocates 1kB of memory.
/// let memory = DeviceMemory::alloc_and_map(&device, mem_ty, 1024).unwrap();
///
/// // Get access to the content. Note that this is very unsafe for two reasons: 1) the content is
/// // uninitialized, and 2) the access is unsynchronized.
/// unsafe {
/// let mut content = memory.read_write::<[u8]>(0 .. 1024);
/// content[12] = 54; // `content` derefs to a `&[u8]` or a `&mut [u8]`
/// }
/// ```
#[derive(Debug)]
pub struct MappedDeviceMemory<D = Arc<Device>> where D: SafeDeref<Target = Device> {
memory: DeviceMemory<D>,
@ -165,7 +208,7 @@ pub struct MappedDeviceMemory<D = Arc<Device>> where D: SafeDeref<Target = Devic
impl<D> MappedDeviceMemory<D> where D: SafeDeref<Target = Device> {
/// Returns the underlying `DeviceMemory`.
// TODO: impl AsRef instead
// TODO: impl AsRef instead?
#[inline]
pub fn memory(&self) -> &DeviceMemory<D> {
&self.memory
@ -173,6 +216,11 @@ impl<D> MappedDeviceMemory<D> where D: SafeDeref<Target = Device> {
/// Gives access to the content of the memory.
///
/// This function takes care of calling `vkInvalidateMappedMemoryRanges` and
/// `vkFlushMappedMemoryRanges` on the given range. You are therefore encouraged to use the
/// smallest range as possible, and to not call this function multiple times in a row for
/// several small changes.
///
/// # Safety
///
/// - Type safety is not checked. You must ensure that `T` corresponds to the content of the
@ -225,6 +273,8 @@ impl<D> Drop for MappedDeviceMemory<D> where D: SafeDeref<Target = Device> {
}
/// Object that can be used to read or write the content of a `MappedDeviceMemory`.
///
/// This object derefs to the content, just like a `MutexGuard` for example.
pub struct CpuAccess<'a, T: ?Sized + 'a, D = Arc<Device>> where D: SafeDeref<Target = Device> + 'a {
pointer: *mut T,
mem: &'a MappedDeviceMemory<D>,
@ -233,7 +283,12 @@ pub struct CpuAccess<'a, T: ?Sized + 'a, D = Arc<Device>> where D: SafeDeref<Tar
}
impl<'a, T: ?Sized + 'a, D: 'a> CpuAccess<'a, T, D> where D: SafeDeref<Target = Device> {
/// Makes a new `CpuAccess` to access a sub-part of the current `CpuAccess`.
/// Builds a new `CpuAccess` to access a sub-part of the current `CpuAccess`.
///
/// This function is unstable. Don't use it directly.
// TODO: unsafe?
// TODO: decide what to do with this
#[doc(hidden)]
#[inline]
pub fn map<U: ?Sized + 'a, F>(self, f: F) -> CpuAccess<'a, U, D>
where F: FnOnce(*mut T) -> *mut U
@ -247,8 +302,10 @@ impl<'a, T: ?Sized + 'a, D: 'a> CpuAccess<'a, T, D> where D: SafeDeref<Target =
}
}
unsafe impl<'a, T: ?Sized + 'a, D: 'a> Send for CpuAccess<'a, T, D> where D: SafeDeref<Target = Device> {}
unsafe impl<'a, T: ?Sized + 'a, D: 'a> Sync for CpuAccess<'a, T, D> where D: SafeDeref<Target = Device> {}
unsafe impl<'a, T: ?Sized + 'a, D: 'a> Send for CpuAccess<'a, T, D>
where D: SafeDeref<Target = Device> {}
unsafe impl<'a, T: ?Sized + 'a, D: 'a> Sync for CpuAccess<'a, T, D>
where D: SafeDeref<Target = Device> {}
impl<'a, T: ?Sized + 'a, D: 'a> Deref for CpuAccess<'a, T, D> where D: SafeDeref<Target = Device> {
type Target = T;
@ -259,7 +316,9 @@ impl<'a, T: ?Sized + 'a, D: 'a> Deref for CpuAccess<'a, T, D> where D: SafeDeref
}
}
impl<'a, T: ?Sized + 'a, D: 'a> DerefMut for CpuAccess<'a, T, D> where D: SafeDeref<Target = Device> {
impl<'a, T: ?Sized + 'a, D: 'a> DerefMut for CpuAccess<'a, T, D>
where D: SafeDeref<Target = Device>
{
#[inline]
fn deref_mut(&mut self) -> &mut T {
unsafe { &mut *self.pointer }
@ -299,7 +358,7 @@ mod tests {
fn create() {
let (device, _) = gfx_dev_and_queue!();
let mem_ty = device.physical_device().memory_types().next().unwrap();
let _ = DeviceMemory::alloc(&device, &mem_ty, 256).unwrap();
let _ = DeviceMemory::alloc(&device, mem_ty, 256).unwrap();
}
#[test]
@ -307,7 +366,7 @@ mod tests {
fn zero_size() {
let (device, _) = gfx_dev_and_queue!();
let mem_ty = device.physical_device().memory_types().next().unwrap();
let _ = DeviceMemory::alloc(&device, &mem_ty, 0);
let _ = DeviceMemory::alloc(&device, mem_ty, 0);
}
#[test]
@ -317,7 +376,7 @@ mod tests {
let mem_ty = device.physical_device().memory_types().filter(|m| !m.is_lazily_allocated())
.next().unwrap();
match DeviceMemory::alloc(&device, &mem_ty, 0xffffffffffffffff) {
match DeviceMemory::alloc(&device, mem_ty, 0xffffffffffffffff) {
Err(OomError::OutOfDeviceMemory) => (),
_ => panic!()
}
@ -333,7 +392,7 @@ mod tests {
let mut allocs = Vec::new();
for _ in 0 .. 4 {
match DeviceMemory::alloc(&device, &mem_ty, heap_size / 3) {
match DeviceMemory::alloc(&device, mem_ty, heap_size / 3) {
Err(OomError::OutOfDeviceMemory) => return, // test succeeded
Ok(a) => allocs.push(a),
_ => ()

View File

@ -71,7 +71,7 @@
//! // Taking the first memory type for the sake of this example.
//! let ty = device.physical_device().memory_types().next().unwrap();
//!
//! let alloc = DeviceMemory::alloc(&device, &ty, 1024).expect("Failed to allocate memory");
//! let alloc = DeviceMemory::alloc(&device, ty, 1024).expect("Failed to allocate memory");
//!
//! // The memory is automatically free'd when `alloc` is destroyed.
//! ```

View File

@ -103,7 +103,7 @@ impl StdHostVisibleMemoryTypePool {
let new_block = {
const MIN_BLOCK_SIZE: usize = 8 * 1024 * 1024; // 8 MB
let to_alloc = cmp::max(MIN_BLOCK_SIZE, size.next_power_of_two());
let new_block = try!(DeviceMemory::alloc_and_map(&me.device, &me.memory_type(), to_alloc));
let new_block = try!(DeviceMemory::alloc_and_map(&me.device, me.memory_type(), to_alloc));
Arc::new(new_block)
};

View File

@ -24,6 +24,7 @@ mod non_host_visible;
mod pool;
/// Pool of GPU-visible memory that can be allocated from.
// TODO: remove 'static + Send + Sync
pub unsafe trait MemoryPool: 'static + Send + Sync {
/// Object that represents a single allocation. Its destructor should free the chunk.
type Alloc: MemoryPoolAlloc;
@ -48,6 +49,7 @@ pub unsafe trait MemoryPool: 'static + Send + Sync {
}
/// Object that represents a single allocation. Its destructor should free the chunk.
// TODO: remove 'static + Send + Sync
pub unsafe trait MemoryPoolAlloc: 'static + Send + Sync {
/// Returns the memory object from which this is allocated. Returns `None` if the memory is
/// not mapped.

View File

@ -102,7 +102,7 @@ impl StdNonHostVisibleMemoryTypePool {
let new_block = {
const MIN_BLOCK_SIZE: usize = 8 * 1024 * 1024; // 8 MB
let to_alloc = cmp::max(MIN_BLOCK_SIZE, size.next_power_of_two());
let new_block = try!(DeviceMemory::alloc(&me.device, &me.memory_type(), to_alloc));
let new_block = try!(DeviceMemory::alloc(&me.device, me.memory_type(), to_alloc));
Arc::new(new_block)
};

View File

@ -14,10 +14,13 @@
//!
//! You can create either an empty cache or a cache from some initial data. Whenever you create a
//! graphics or compute pipeline, you have the possibility to pass a reference to that cache.
//! TODO: ^ that's not the case yet
//! The Vulkan implementation will then look in the cache for an existing entry, or add one if it
//! doesn't exist.
//!
//! Once that is done, you can extract the data from the cache and store it.
//! Once that is done, you can extract the data from the cache and store it. See the documentation
//! of [`get_data`](struct.PipelineCache.html#method.get_data) for example of how to store the data
//! on the disk, and [`with_data`](struct.PipelineCache.html#method.with_data) for how to reload it.
//!
use std::mem;
use std::ptr;
@ -32,16 +35,53 @@ use check_errors;
use vk;
/// Opaque cache that contains pipeline objects.
///
/// See [the documentation of the module](index.html) for more info.
pub struct PipelineCache {
device: Arc<Device>,
cache: vk::PipelineCache,
}
impl PipelineCache {
/// Builds a new pipeline cache from existing data.
/// Builds a new pipeline cache from existing data. The data must have been previously obtained
/// with [`get_data`](#method.get_data).
///
/// The data must have been previously obtained with `get_data`.
// TODO: is that unsafe? is it safe to pass garbage data?
/// The data passed to this function will most likely be blindly trusted by the Vulkan
/// implementation. Therefore you can easily crash your application or the system by passing
/// wrong data. Hence why this function is unsafe.
///
/// # Example
///
/// This example loads a cache from a file, if it exists.
/// See [`get_data`](#method.get_data) for how to store the data in a file.
/// TODO: there's a header in the cached data that must be checked ; talk about this
///
/// ```
/// # use std::sync::Arc;
/// # use vulkano::device::Device;
/// use std::fs::File;
/// use std::io::Read;
/// use vulkano::pipeline::cache::PipelineCache;
/// # let device: Arc<Device> = return;
///
/// let data = {
/// let file = File::open("pipeline_cache.bin");
/// if let Ok(mut file) = file {
/// let mut data = Vec::new();
/// if let Ok(_) = file.read_to_end(&mut data) {
/// Some(data)
/// } else { None }
/// } else { None }
/// };
///
/// 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() }
/// } else {
/// PipelineCache::empty(&device).unwrap()
/// };
/// ```
#[inline]
pub unsafe fn with_data(device: &Arc<Device>, initial_data: &[u8])
-> Result<Arc<PipelineCache>, OomError>
{
@ -49,10 +89,22 @@ impl PipelineCache {
}
/// Builds a new empty pipeline cache.
///
/// # Example
///
/// ```
/// # use std::sync::Arc;
/// # use vulkano::device::Device;
/// use vulkano::pipeline::cache::PipelineCache;
/// # let device: Arc<Device> = return;
/// let cache = PipelineCache::empty(&device).unwrap();
/// ```
#[inline]
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]>)
-> Result<Arc<PipelineCache>, OomError>
{
@ -81,11 +133,14 @@ impl PipelineCache {
/// Merges other pipeline caches into this one.
///
/// It is `self` that is modified here. The pipeline caches passed as parameter are untouched.
///
/// # Panic
///
/// - Panics if `self` is included in the list of other pipelines.
///
// FIXME: vkMergePipelineCaches is not thread safe for the destination cache
// TODO: write example
pub fn merge<'a, I>(&self, pipelines: I) -> Result<(), OomError>
where I: IntoIterator<Item = &'a &'a Arc<PipelineCache>>
{
@ -106,7 +161,32 @@ impl PipelineCache {
/// Obtains the data from the cache.
///
/// This data can be stored and then reloaded and passed to `PipelineCache::new`.
/// This data can be stored and then reloaded and passed to `PipelineCache::with_data`.
///
/// # Example
///
/// This example stores the data of a pipeline cache on the disk.
/// See [`with_data`](#method.with_data) for how to reload it.
///
/// ```
/// use std::fs;
/// use std::fs::File;
/// use std::io::Write;
/// # use std::sync::Arc;
/// # use vulkano::pipeline::cache::PipelineCache;
///
/// # let cache: Arc<PipelineCache> = return;
/// // If an error happens (eg. no permission for the file) we simply skip storing the cache.
/// if let Ok(data) = cache.get_data() {
/// if let Ok(mut file) = File::create("pipeline_cache.bin.tmp") {
/// if let Ok(_) = file.write_all(&data) {
/// let _ = fs::rename("pipeline_cache.bin.tmp", "pipeline_cache.bin");
/// } else {
/// let _ = fs::remove_file("pipeline_cache.bin.tmp");
/// }
/// }
/// }
/// ```
pub fn get_data(&self) -> Result<Vec<u8>, OomError> {
unsafe {
let vk = self.device.pointers();

View File

@ -52,7 +52,7 @@ pub enum PrimitiveTopology {
TriangleFan,
LineListWithAdjacency,
LineStripWithAdjacency,
TriangleListWithAdjancecy,
TriangleListWithAdjacency,
TriangleStripWithAdjacency,
PatchList { vertices_per_patch: u32 },
}
@ -69,7 +69,7 @@ impl Into<vk::PrimitiveTopology> for PrimitiveTopology {
PrimitiveTopology::TriangleFan => vk::PRIMITIVE_TOPOLOGY_TRIANGLE_FAN,
PrimitiveTopology::LineListWithAdjacency => vk::PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY,
PrimitiveTopology::LineStripWithAdjacency => vk::PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY,
PrimitiveTopology::TriangleListWithAdjancecy => vk::PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY,
PrimitiveTopology::TriangleListWithAdjacency => vk::PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY,
PrimitiveTopology::TriangleStripWithAdjacency => vk::PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY,
PrimitiveTopology::PatchList { .. } => vk::PRIMITIVE_TOPOLOGY_PATCH_LIST,
}

View File

@ -491,7 +491,7 @@ impl GeometryShaderExecutionMode {
(GeometryShaderExecutionMode::Triangles, PrimitiveTopology::TriangleStrip) => true,
(GeometryShaderExecutionMode::Triangles, PrimitiveTopology::TriangleFan) => true,
(GeometryShaderExecutionMode::TrianglesWithAdjacency,
PrimitiveTopology::TriangleListWithAdjancecy) => true,
PrimitiveTopology::TriangleListWithAdjacency) => true,
(GeometryShaderExecutionMode::TrianglesWithAdjacency,
PrimitiveTopology::TriangleStripWithAdjacency) => true,
_ => false,

View File

@ -7,6 +7,8 @@
// notice may not be copied, modified, or distributed except
// according to those terms.
// The `Version` object is reexported from the `instance` module.
use std::cmp::Ordering;
/// Represents an API version of Vulkan.