use std::fmt;
use std::mem;
use std::ptr;
use std::ops::Deref;
use std::ops::DerefMut;
use std::ops::Range;
use std::os::raw::c_void;
use std::sync::Arc;
use instance::MemoryType;
use device::Device;
use device::DeviceOwned;
use memory::Content;
use OomError;
use VulkanObject;
use VulkanPointers;
use check_errors;
use vk;
pub struct DeviceMemory {
memory: vk::DeviceMemory,
device: Arc<Device>,
size: usize,
memory_type_index: u32,
}
impl DeviceMemory {
#[inline]
pub fn alloc(device: Arc<Device>, memory_type: MemoryType, size: usize)
-> Result<DeviceMemory, OomError>
{
assert!(size >= 1);
assert_eq!(device.physical_device().internal_object(),
memory_type.physical_device().internal_object());
let memory = unsafe {
let vk = device.pointers();
let infos = vk::MemoryAllocateInfo {
sType: vk::STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
pNext: ptr::null(),
allocationSize: size as u64,
memoryTypeIndex: memory_type.id(),
};
let mut output = mem::uninitialized();
try!(check_errors(vk.AllocateMemory(device.internal_object(), &infos,
ptr::null(), &mut output)));
output
};
Ok(DeviceMemory {
memory: memory,
device: device,
size: size,
memory_type_index: memory_type.id(),
})
}
pub fn alloc_and_map(device: Arc<Device>, memory_type: MemoryType, size: usize)
-> Result<MappedDeviceMemory, OomError>
{
let vk = device.pointers();
assert!(memory_type.is_host_visible());
let mem = try!(DeviceMemory::alloc(device.clone(), memory_type, size));
let coherent = memory_type.is_host_coherent();
let ptr = unsafe {
let mut output = mem::uninitialized();
try!(check_errors(vk.MapMemory(device.internal_object(), mem.memory, 0,
mem.size as vk::DeviceSize, 0 ,
&mut output)));
output
};
Ok(MappedDeviceMemory {
memory: mem,
pointer: ptr,
coherent: coherent,
})
}
#[inline]
pub fn memory_type(&self) -> MemoryType {
self.device.physical_device().memory_type_by_id(self.memory_type_index).unwrap()
}
#[inline]
pub fn size(&self) -> usize {
self.size
}
}
unsafe impl DeviceOwned for DeviceMemory {
#[inline]
fn device(&self) -> &Arc<Device> {
&self.device
}
}
impl fmt::Debug for DeviceMemory {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("DeviceMemory")
.field("device", &*self.device)
.field("memory_type", &self.memory_type())
.field("size", &self.size)
.finish()
}
}
unsafe impl VulkanObject for DeviceMemory {
type Object = vk::DeviceMemory;
#[inline]
fn internal_object(&self) -> vk::DeviceMemory {
self.memory
}
}
impl Drop for DeviceMemory {
#[inline]
fn drop(&mut self) {
unsafe {
let vk = self.device.pointers();
vk.FreeMemory(self.device.internal_object(), self.memory, ptr::null());
}
}
}
pub struct MappedDeviceMemory {
memory: DeviceMemory,
pointer: *mut c_void,
coherent: bool,
}
impl MappedDeviceMemory {
pub fn unmap(self) -> DeviceMemory {
unsafe {
let device = self.memory.device();
let vk = device.pointers();
vk.UnmapMemory(device.internal_object(), self.memory.memory);
}
self.memory
}
#[inline]
pub unsafe fn read_write<T: ?Sized>(&self, range: Range<usize>) -> CpuAccess<T>
where T: Content
{
let vk = self.memory.device().pointers();
let pointer = T::ref_from_ptr((self.pointer as usize + range.start) as *mut _,
range.end - range.start).unwrap();
if !self.coherent {
let range = vk::MappedMemoryRange {
sType: vk::STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,
pNext: ptr::null(),
memory: self.memory.internal_object(),
offset: range.start as u64,
size: (range.end - range.start) as u64,
};
vk.InvalidateMappedMemoryRanges(self.memory.device().internal_object(), 1, &range);
}
CpuAccess {
pointer: pointer,
mem: self,
coherent: self.coherent,
range: range,
}
}
}
impl AsRef<DeviceMemory> for MappedDeviceMemory {
#[inline]
fn as_ref(&self) -> &DeviceMemory {
&self.memory
}
}
impl AsMut<DeviceMemory> for MappedDeviceMemory {
#[inline]
fn as_mut(&mut self) -> &mut DeviceMemory {
&mut self.memory
}
}
unsafe impl DeviceOwned for MappedDeviceMemory {
#[inline]
fn device(&self) -> &Arc<Device> {
self.memory.device()
}
}
unsafe impl Send for MappedDeviceMemory {}
unsafe impl Sync for MappedDeviceMemory {}
impl fmt::Debug for MappedDeviceMemory {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_tuple("MappedDeviceMemory")
.field(&self.memory)
.finish()
}
}
pub struct CpuAccess<'a, T: ?Sized + 'a> {
pointer: *mut T,
mem: &'a MappedDeviceMemory,
coherent: bool,
range: Range<usize>,
}
impl<'a, T: ?Sized + 'a> CpuAccess<'a, T> {
#[doc(hidden)]
#[inline]
pub fn map<U: ?Sized + 'a, F>(self, f: F) -> CpuAccess<'a, U>
where F: FnOnce(*mut T) -> *mut U
{
CpuAccess {
pointer: f(self.pointer),
mem: self.mem,
coherent: self.coherent,
range: self.range.clone(), }
}
}
unsafe impl<'a, T: ?Sized + 'a> Send for CpuAccess<'a, T> {}
unsafe impl<'a, T: ?Sized + 'a> Sync for CpuAccess<'a, T> {}
impl<'a, T: ?Sized + 'a> Deref for CpuAccess<'a, T> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
unsafe { &*self.pointer }
}
}
impl<'a, T: ?Sized + 'a> DerefMut for CpuAccess<'a, T> {
#[inline]
fn deref_mut(&mut self) -> &mut T {
unsafe { &mut *self.pointer }
}
}
impl<'a, T: ?Sized + 'a> Drop for CpuAccess<'a, T> {
#[inline]
fn drop(&mut self) {
if !self.coherent {
let vk = self.mem.as_ref().device().pointers();
let range = vk::MappedMemoryRange {
sType: vk::STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,
pNext: ptr::null(),
memory: self.mem.as_ref().internal_object(),
offset: self.range.start as u64,
size: (self.range.end - self.range.start) as u64,
};
unsafe {
vk.FlushMappedMemoryRanges(self.mem.as_ref().device().internal_object(),
1, &range);
}
}
}
}
#[cfg(test)]
mod tests {
use OomError;
use memory::DeviceMemory;
#[test]
fn create() {
let (device, _) = gfx_dev_and_queue!();
let mem_ty = device.physical_device().memory_types().next().unwrap();
let _ = DeviceMemory::alloc(device.clone(), mem_ty, 256).unwrap();
}
#[test]
#[should_panic]
fn zero_size() {
let (device, _) = gfx_dev_and_queue!();
let mem_ty = device.physical_device().memory_types().next().unwrap();
let _ = DeviceMemory::alloc(device.clone(), mem_ty, 0);
}
#[test]
#[cfg(target_pointer_width = "64")]
fn oom_single() {
let (device, _) = gfx_dev_and_queue!();
let mem_ty = device.physical_device().memory_types().filter(|m| !m.is_lazily_allocated())
.next().unwrap();
match DeviceMemory::alloc(device.clone(), mem_ty, 0xffffffffffffffff) {
Err(OomError::OutOfDeviceMemory) => (),
_ => panic!()
}
}
#[test]
#[ignore] fn oom_multi() {
let (device, _) = gfx_dev_and_queue!();
let mem_ty = device.physical_device().memory_types().filter(|m| !m.is_lazily_allocated())
.next().unwrap();
let heap_size = mem_ty.heap().size();
let mut allocs = Vec::new();
for _ in 0 .. 4 {
match DeviceMemory::alloc(device.clone(), mem_ty, heap_size / 3) {
Err(OomError::OutOfDeviceMemory) => return, Ok(a) => allocs.push(a),
_ => ()
}
}
panic!()
}
}