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