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 / resources.rs

use std::mem::ManuallyDrop;
use std::ptr::null;
use std::sync::Arc;

use volcanic::*;
use volcanic_mem::{BufferDesc, MemoryLocation};

use super::{Device, Result, create, zeroed};

/// A buffer sub-allocated from the device's allocator, persistently mapped when host-visible.
pub struct Buffer {
    inner: ManuallyDrop<volcanic_mem::Buffer>,
    device: Arc<Device>,
}

impl Buffer {
    /// Buffers the GPU reads through their address directly should include `SHADER_DEVICE_ADDRESS`
    /// in `usage`, so `CpuToGpu` places them in device-local host-visible memory when available.
    pub fn new(
        device: &Arc<Device>,
        size: u64,
        usage: VkBufferUsageFlags2,
        location: MemoryLocation,
    ) -> Result<Self> {
        let desc = BufferDesc {
            size,
            usage,
            location,
        };
        Ok(Self {
            inner: ManuallyDrop::new(device.allocator.create_buffer(&desc)?),
            device: device.clone(),
        })
    }

    pub fn handle(&self) -> VkBuffer {
        self.inner.handle()
    }

    pub fn size(&self) -> u64 {
        self.inner.size()
    }

    pub fn address(&self) -> VkDeviceAddress {
        self.inner.address()
    }

    /// Host view of a host-visible buffer. Panics for GPU-only buffers.
    pub fn mapped(&mut self) -> &mut [u8] {
        self.inner
            .mapped_slice_mut()
            .expect("buffer is not host-visible")
    }

    /// Makes host writes to `offset..offset + size` visible to the device. A no-op on coherent memory.
    pub fn flush(&self, offset: usize, size: usize) -> Result<()> {
        if size == 0 {
            return Ok(());
        }
        let allocation = self.inner.allocation();
        Ok(self
            .device
            .allocator
            .flush(allocation, offset as u64, size as u64)?)
    }

    /// Makes finished device writes visible to the host. A no-op on coherent memory.
    pub fn invalidate(&self) -> Result<()> {
        let allocation = self.inner.allocation();
        Ok(self
            .device
            .allocator
            .invalidate(allocation, 0, VK_WHOLE_SIZE)?)
    }

    pub fn write(&mut self, offset: usize, data: &[u8]) -> Result<()> {
        self.mapped()[offset..offset + data.len()].copy_from_slice(data);
        self.flush(offset, data.len())
    }
}

impl Drop for Buffer {
    fn drop(&mut self) {
        let inner = unsafe { ManuallyDrop::take(&mut self.inner) };
        self.device.allocator.destroy_buffer(inner);
    }
}

/// A 2D device-local image with a default view.
pub struct Image {
    inner: ManuallyDrop<volcanic_mem::Image>,
    view: VkImageView,
    format: VkFormat,
    aspect: VkImageAspectFlags,
    device: Arc<Device>,
}

impl Image {
    pub fn new(
        device: &Arc<Device>,
        extent: VkExtent2D,
        format: VkFormat,
        usage: VkImageUsageFlags,
        aspect: VkImageAspectFlags,
    ) -> Result<Self> {
        let info = VkImageCreateInfo {
            sType: VkStructureType::ImageCreateInfo,
            imageType: VkImageType::_2D,
            format,
            extent: VkExtent3D {
                width: extent.width,
                height: extent.height,
                depth: 1,
            },
            mipLevels: 1,
            arrayLayers: 1,
            samples: VkSampleCountFlags::_1,
            tiling: VkImageTiling::Optimal,
            usage,
            sharingMode: VkSharingMode::Exclusive,
            initialLayout: VkImageLayout::Undefined,
            ..zeroed()
        };
        let inner = device
            .allocator
            .create_image(&info, MemoryLocation::GpuOnly)?;
        let mut image = Self {
            inner: ManuallyDrop::new(inner),
            view: VK_NULL_HANDLE,
            format,
            aspect,
            device: device.clone(),
        };
        let view_info = image.view_create_info();
        image.view = create(|view| unsafe {
            device
                .commands
                .vkCreateImageView(device.handle, &view_info, null(), view)
        })?;
        Ok(image)
    }

    pub fn handle(&self) -> VkImage {
        self.inner.handle()
    }

    pub fn view(&self) -> VkImageView {
        self.view
    }

    /// The view description, which descriptor heaps use to build image descriptors.
    pub fn view_create_info(&self) -> VkImageViewCreateInfo {
        view_create_info(self.handle(), self.format, self.aspect)
    }
}

/// A 2D view of an image's first mip level and layer.
pub(crate) fn view_create_info(
    image: VkImage,
    format: VkFormat,
    aspect: VkImageAspectFlags,
) -> VkImageViewCreateInfo {
    VkImageViewCreateInfo {
        sType: VkStructureType::ImageViewCreateInfo,
        image,
        viewType: VkImageViewType::_2D,
        format,
        subresourceRange: subresource_range(aspect),
        ..zeroed()
    }
}

pub fn subresource_range(aspect: VkImageAspectFlags) -> VkImageSubresourceRange {
    VkImageSubresourceRange {
        aspectMask: aspect,
        levelCount: 1,
        layerCount: 1,
        ..zeroed()
    }
}

impl Drop for Image {
    fn drop(&mut self) {
        let commands = &self.device.commands;
        unsafe { commands.vkDestroyImageView(self.device.handle, self.view, null()) };
        let inner = unsafe { ManuallyDrop::take(&mut self.inner) };
        self.device.allocator.destroy_image(inner);
    }
}