Theme

alex/beta-minecraft-rust

Minecraft Beta 1.7.3 Rewrite

BRANCH main
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
Download file

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());
            }
        }
    }
}