alex/beta-minecraft-rust
Minecraft Beta 1.7.3 Rewrite
BRANCH main
SNAPSHOT Draw terrain GPU-driven from packed per-section buffers with GPU occlusion culling, add a render distance slider (2-128) with a quad/disk shape, cut chunk bookkeeping at high render distances, and fix a light underflow at y=0
DOWNLOAD
root crates client src gpu heap.rs
use std::mem::ManuallyDrop;
use std::sync::Arc;
use volcanic::*;
use volcanic_mem::{DescriptorHeap, DescriptorHeapKind};
use super::{Device, Image, Result, check, zeroed};
/// One host-written descriptor heap of equally sized slots.
struct Heap {
inner: ManuallyDrop<DescriptorHeap>,
stride: u64,
capacity: u32,
device: Arc<Device>,
}
impl Heap {
fn new(
device: &Arc<Device>,
kind: DescriptorHeapKind,
capacity: u32,
descriptor_size: u64,
descriptor_alignment: u64,
) -> Result<Self> {
let stride = descriptor_size.next_multiple_of(descriptor_alignment);
let bytes = stride * u64::from(capacity);
let inner = device.allocator.create_descriptor_heap(kind, bytes)?;
Ok(Self {
inner: ManuallyDrop::new(inner),
stride,
capacity,
device: device.clone(),
})
}
/// Writes one descriptor into `index` through `write` and flushes it.
fn write(
&mut self,
index: u32,
write: impl FnOnce(&VkHostAddressRangeEXT) -> VkResult,
) -> Result<()> {
assert!(
index < self.capacity,
"descriptor slot {index} out of range"
);
let offset = self.stride * u64::from(index);
check(write(&self.inner.host_range(offset, self.stride)))?;
let allocation = self.inner.buffer().allocation();
Ok(self
.device
.allocator
.flush(allocation, offset, self.stride)?)
}
}
impl Drop for Heap {
fn drop(&mut self) {
let inner = unsafe { ManuallyDrop::take(&mut self.inner) };
self.device.allocator.destroy_descriptor_heap(inner);
}
}
/// Byte strides of heap slots, needed to map shader bindings onto the heaps.
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct HeapBinding {
pub image_stride: u32,
pub sampler_stride: u32,
}
/// The resource (image) heap and sampler heap shared by all pipelines.
pub struct DescriptorHeaps {
resources: Heap,
samplers: Heap,
device: Arc<Device>,
}
impl DescriptorHeaps {
pub fn new(device: &Arc<Device>, images: u32, samplers: u32) -> Result<Self> {
let p = &device.heap_properties;
let resources = Heap::new(
device,
DescriptorHeapKind::Resource,
images,
p.imageDescriptorSize,
p.imageDescriptorAlignment,
)?;
let samplers = Heap::new(
device,
DescriptorHeapKind::Sampler,
samplers,
p.samplerDescriptorSize,
p.samplerDescriptorAlignment,
)?;
Ok(Self {
resources,
samplers,
device: device.clone(),
})
}
pub fn binding(&self) -> HeapBinding {
HeapBinding {
image_stride: self.resources.stride as u32,
sampler_stride: self.samplers.stride as u32,
}
}
/// Writes a sampled-image descriptor. The caller must not overwrite a slot the GPU may still read.
pub fn write_image(&mut self, slot: u32, image: &Image, layout: VkImageLayout) -> Result<()> {
let view = image.view_create_info();
let image_info = VkImageDescriptorInfoEXT {
sType: VkStructureType::ImageDescriptorInfoExt,
pView: &view,
layout,
..zeroed()
};
let info = VkResourceDescriptorInfoEXT {
sType: VkStructureType::ResourceDescriptorInfoExt,
r#type: VkDescriptorType::SampledImage,
data: VkResourceDescriptorDataEXT {
pImage: &image_info,
},
..zeroed()
};
let (commands, device) = (&self.device.commands, self.device.handle);
self.resources.write(slot, |target| unsafe {
commands.vkWriteResourceDescriptorsEXT(device, 1, &info, target)
})
}
pub fn write_sampler(&mut self, slot: u32, info: &VkSamplerCreateInfo) -> Result<()> {
let (commands, device) = (&self.device.commands, self.device.handle);
self.samplers.write(slot, |target| unsafe {
commands.vkWriteSamplerDescriptorsEXT(device, 1, info, target)
})
}
/// # Safety
/// `command_buffer` must be in the recording state.
pub unsafe fn bind(&self, command_buffer: VkCommandBuffer) {
let commands = &self.device.commands;
unsafe {
commands.vkCmdBindResourceHeapEXT(command_buffer, &self.resources.inner.bind_info());
commands.vkCmdBindSamplerHeapEXT(command_buffer, &self.samplers.inner.bind_info());
}
}
/// # Safety
/// `command_buffer` must be in the recording state.
pub unsafe fn push(&self, command_buffer: VkCommandBuffer, data: &[u8]) {
let info = VkPushDataInfoEXT {
sType: VkStructureType::PushDataInfoExt,
data: VkHostAddressRangeConstEXT {
address: data.as_ptr().cast(),
size: data.len(),
},
..zeroed()
};
unsafe { self.device.commands.vkCmdPushDataEXT(command_buffer, &info) };
}
}