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 descriptors.rs
use std::ptr::null;
use std::sync::Arc;
use volcanic::*;
use super::heap::{DescriptorHeaps, HeapBinding};
use super::{Device, Image, MAX_PUSH_SIZE, Result, create, zeroed};
const STAGES: VkShaderStageFlags = VkShaderStageFlags::VERTEX
.union(VkShaderStageFlags::FRAGMENT)
.union(VkShaderStageFlags::COMPUTE);
const TYPES: [VkDescriptorType; 2] = [VkDescriptorType::SampledImage, VkDescriptorType::Sampler];
/// The sampled images at `[[vk::binding(0, 0)]]` and samplers at `[[vk::binding(1, 0)]]` that
/// all pipelines share, and the push data that goes with them.
pub enum Descriptors {
Heaps(DescriptorHeaps),
Set(DescriptorSet),
}
/// How pipelines reach their [`Descriptors`].
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Binding {
Heap(HeapBinding),
Layout(VkPipelineLayout),
}
impl Descriptors {
/// Descriptor heaps if `heaps` is set, which needs [`Device::descriptor_heap`], or else one
/// descriptor set.
pub fn new(device: &Arc<Device>, heaps: bool, images: u32, samplers: u32) -> Result<Self> {
Ok(if heaps {
Self::Heaps(DescriptorHeaps::new(device, images, samplers)?)
} else {
Self::Set(DescriptorSet::new(device, [images, samplers])?)
})
}
pub fn binding(&self) -> Binding {
match self {
Self::Heaps(heaps) => Binding::Heap(heaps.binding()),
Self::Set(set) => Binding::Layout(set.layout),
}
}
/// 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<()> {
match self {
Self::Heaps(heaps) => heaps.write_image(slot, image, layout),
Self::Set(set) => {
set.write_image(slot, image, layout);
Ok(())
}
}
}
pub fn write_sampler(&mut self, slot: u32, info: &VkSamplerCreateInfo) -> Result<()> {
match self {
Self::Heaps(heaps) => heaps.write_sampler(slot, info),
Self::Set(set) => set.write_sampler(slot, info),
}
}
/// # Safety
/// `command_buffer` must be in the recording state.
pub unsafe fn bind(&self, command_buffer: VkCommandBuffer) {
match self {
Self::Heaps(heaps) => unsafe { heaps.bind(command_buffer) },
Self::Set(set) => unsafe { set.bind(command_buffer) },
}
}
/// # Safety
/// `command_buffer` must be in the recording state, and `data` at most [`MAX_PUSH_SIZE`]
/// bytes in multiples of 4.
pub unsafe fn push(&self, command_buffer: VkCommandBuffer, data: &[u8]) {
match self {
Self::Heaps(heaps) => unsafe { heaps.push(command_buffer, data) },
Self::Set(set) => unsafe { set.push(command_buffer, data) },
}
}
}
/// One descriptor set and its pipeline layout with push constants, for devices without
/// descriptor heaps.
pub struct DescriptorSet {
handle: VkDescriptorSet,
pool: VkDescriptorPool,
set_layout: VkDescriptorSetLayout,
layout: VkPipelineLayout,
capacity: [u32; 2],
samplers: Vec<VkSampler>,
device: Arc<Device>,
}
impl DescriptorSet {
fn new(device: &Arc<Device>, capacity: [u32; 2]) -> Result<Self> {
let (commands, handle) = (&device.commands, device.handle);
let mut set = Self {
handle: VK_NULL_HANDLE,
pool: VK_NULL_HANDLE,
set_layout: VK_NULL_HANDLE,
layout: VK_NULL_HANDLE,
capacity,
samplers: Vec::new(),
device: device.clone(),
};
let bindings = [0, 1].map(|binding| VkDescriptorSetLayoutBinding {
binding,
descriptorType: TYPES[binding as usize],
descriptorCount: capacity[binding as usize],
stageFlags: STAGES,
pImmutableSamplers: null(),
});
let set_layout_info = VkDescriptorSetLayoutCreateInfo {
sType: VkStructureType::DescriptorSetLayoutCreateInfo,
bindingCount: bindings.len() as u32,
pBindings: bindings.as_ptr(),
..zeroed()
};
set.set_layout = create(|layout| unsafe {
commands.vkCreateDescriptorSetLayout(handle, &set_layout_info, null(), layout)
})?;
let push = VkPushConstantRange {
stageFlags: STAGES,
offset: 0,
size: MAX_PUSH_SIZE,
};
let layout_info = VkPipelineLayoutCreateInfo {
sType: VkStructureType::PipelineLayoutCreateInfo,
setLayoutCount: 1,
pSetLayouts: &set.set_layout,
pushConstantRangeCount: 1,
pPushConstantRanges: &push,
..zeroed()
};
set.layout = create(|layout| unsafe {
commands.vkCreatePipelineLayout(handle, &layout_info, null(), layout)
})?;
let sizes = bindings.map(|binding| VkDescriptorPoolSize {
r#type: binding.descriptorType,
descriptorCount: binding.descriptorCount,
});
let pool_info = VkDescriptorPoolCreateInfo {
sType: VkStructureType::DescriptorPoolCreateInfo,
maxSets: 1,
poolSizeCount: sizes.len() as u32,
pPoolSizes: sizes.as_ptr(),
..zeroed()
};
set.pool = create(|pool| unsafe {
commands.vkCreateDescriptorPool(handle, &pool_info, null(), pool)
})?;
let allocate_info = VkDescriptorSetAllocateInfo {
sType: VkStructureType::DescriptorSetAllocateInfo,
descriptorPool: set.pool,
descriptorSetCount: 1,
pSetLayouts: &set.set_layout,
..zeroed()
};
set.handle = create(|set| unsafe {
commands.vkAllocateDescriptorSets(handle, &allocate_info, set)
})?;
Ok(set)
}
fn write_image(&mut self, slot: u32, image: &Image, layout: VkImageLayout) {
let info = VkDescriptorImageInfo {
sampler: VK_NULL_HANDLE,
imageView: image.view(),
imageLayout: layout,
};
self.write(0, slot, &info);
}
fn write_sampler(&mut self, slot: u32, info: &VkSamplerCreateInfo) -> Result<()> {
let (commands, device) = (&self.device.commands, self.device.handle);
let sampler =
create(|sampler| unsafe { commands.vkCreateSampler(device, info, null(), sampler) })?;
self.samplers.push(sampler);
let info = VkDescriptorImageInfo {
sampler,
..zeroed()
};
self.write(1, slot, &info);
Ok(())
}
fn write(&mut self, binding: u32, slot: u32, info: &VkDescriptorImageInfo) {
assert!(
slot < self.capacity[binding as usize],
"descriptor slot {slot} out of range"
);
let write = VkWriteDescriptorSet {
sType: VkStructureType::WriteDescriptorSet,
dstSet: self.handle,
dstBinding: binding,
dstArrayElement: slot,
descriptorCount: 1,
descriptorType: TYPES[binding as usize],
pImageInfo: info,
..zeroed()
};
let commands = &self.device.commands;
unsafe { commands.vkUpdateDescriptorSets(self.device.handle, 1, &write, 0, null()) };
}
unsafe fn bind(&self, command_buffer: VkCommandBuffer) {
unsafe {
self.device.commands.vkCmdBindDescriptorSets(
command_buffer,
VkPipelineBindPoint::Graphics,
self.layout,
0,
1,
&self.handle,
0,
null(),
);
}
}
unsafe fn push(&self, command_buffer: VkCommandBuffer, data: &[u8]) {
unsafe {
self.device.commands.vkCmdPushConstants(
command_buffer,
self.layout,
STAGES,
0,
data.len() as u32,
data.as_ptr().cast(),
);
}
}
}
impl Drop for DescriptorSet {
fn drop(&mut self) {
let (commands, device) = (&self.device.commands, self.device.handle);
unsafe {
commands.vkDestroyDescriptorPool(device, self.pool, null());
commands.vkDestroyPipelineLayout(device, self.layout, null());
commands.vkDestroyDescriptorSetLayout(device, self.set_layout, null());
for &sampler in &self.samplers {
commands.vkDestroySampler(device, sampler, null());
}
}
}
}