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 device.rs
use std::ffi::{CStr, c_char, c_void};
use std::mem::ManuallyDrop;
use std::ptr::{null, null_mut};
use std::sync::Arc;
use volcanic::loader::DeviceCommands;
use volcanic::*;
use volcanic_mem::{Allocator, AllocatorConfig};
use super::{Error, Instance, MAX_PUSH_SIZE, Result, Surface, check, create, zeroed};
const OPTIONAL_EXTENSIONS: [&CStr; 2] = [
VK_EXT_DESCRIPTOR_HEAP_EXTENSION_NAME,
VK_EXT_MEMORY_BUDGET_EXTENSION_NAME,
];
/// `VK_EXT_present_timing` and the device extensions it depends on.
const PRESENT_TIMING_EXTENSIONS: [&CStr; 3] = [
VK_EXT_PRESENT_TIMING_EXTENSION_NAME,
VK_KHR_PRESENT_ID_2_EXTENSION_NAME,
VK_KHR_CALIBRATED_TIMESTAMPS_EXTENSION_NAME,
];
/// `VK_KHR_present_mode_fifo_latest_ready` and the EXT it was promoted from, preferred first.
const FIFO_LATEST_READY_EXTENSIONS: [&CStr; 2] = [
VK_KHR_PRESENT_MODE_FIFO_LATEST_READY_EXTENSION_NAME,
VK_EXT_PRESENT_MODE_FIFO_LATEST_READY_EXTENSION_NAME,
];
pub struct Device {
pub(crate) handle: VkDevice,
pub(crate) commands: DeviceCommands,
pub(crate) physical: VkPhysicalDevice,
pub(crate) queue: VkQueue,
pub(crate) queue_family: u32,
pub(crate) allocator: ManuallyDrop<Allocator>,
/// Whether `VK_EXT_descriptor_heap` is enabled. `heap_properties` is zeroed otherwise.
pub descriptor_heap: bool,
pub(crate) heap_properties: VkPhysicalDeviceDescriptorHeapPropertiesEXT,
/// Whether `VK_EXT_present_timing` is enabled with `presentTiming` and `presentId2`.
pub(crate) present_timing: bool,
pub(crate) present_at_absolute_time: bool,
/// Whether `VK_PRESENT_MODE_FIFO_LATEST_READY_KHR` may be used.
pub(crate) fifo_latest_ready: bool,
pub(crate) instance: Arc<Instance>,
pub(crate) name: String,
}
// SAFETY: Vulkan device handles may be used from any thread. The queue is externally
// synchronized, which holds because only the render thread submits.
unsafe impl Send for Device {}
unsafe impl Sync for Device {}
impl Device {
pub fn new(instance: &Arc<Instance>, surface: &Surface) -> Result<Arc<Self>> {
let commands = &instance.commands;
let physical_devices = super::enumerate(|count, items| unsafe {
commands.vkEnumeratePhysicalDevices(instance.handle, count, items)
})?;
let (physical, queue_family, properties, mut extensions) = physical_devices
.into_iter()
.filter_map(|physical| {
let properties = unsafe {
let mut properties = zeroed();
commands.vkGetPhysicalDeviceProperties(physical, &mut properties);
properties
};
let available = super::enumerate(|count, items| unsafe {
commands.vkEnumerateDeviceExtensionProperties(physical, null(), count, items)
})
.ok()?;
let supports = |wanted: &CStr| {
available
.iter()
.any(|e| unsafe { CStr::from_ptr(e.extensionName.as_ptr()) } == wanted)
};
let maintenance5 = (properties.apiVersion < VK_API_VERSION_1_4)
.then_some(VK_KHR_MAINTENANCE_5_EXTENSION_NAME);
let required = [VK_KHR_SWAPCHAIN_EXTENSION_NAME]
.into_iter()
.chain(maintenance5);
if properties.apiVersion < VK_API_VERSION_1_3
|| properties.limits.maxPushConstantsSize < MAX_PUSH_SIZE
|| !required.clone().all(supports)
|| !required_features(instance, physical)
{
return None;
}
let family = graphics_present_family(instance, physical, surface)?;
let optional = OPTIONAL_EXTENSIONS.into_iter().filter(|e| supports(e));
let timing = instance.surface_capabilities2
&& PRESENT_TIMING_EXTENSIONS.into_iter().all(supports);
let timing = PRESENT_TIMING_EXTENSIONS.into_iter().filter(|_| timing);
let latest_ready = FIFO_LATEST_READY_EXTENSIONS
.into_iter()
.find(|e| supports(e));
let extensions: Vec<&CStr> = (required.chain(optional).chain(timing))
.chain(latest_ready)
.collect();
Some((physical, family, properties, extensions))
})
.max_by_key(|(_, _, properties, extensions)| {
(
properties.deviceType == VkPhysicalDeviceType::DiscreteGpu,
extensions.contains(&VK_EXT_DESCRIPTOR_HEAP_EXTENSION_NAME),
)
})
.ok_or(Error::NoSuitableDevice)?;
let supported = optional_features(instance, physical, &extensions);
extensions.retain(|e| {
(supported.present_timing || !PRESENT_TIMING_EXTENSIONS.contains(e))
&& (supported.fifo_latest_ready || !FIFO_LATEST_READY_EXTENSIONS.contains(e))
});
let OptionalFeatures {
present_timing,
present_at_absolute_time,
fifo_latest_ready,
} = supported;
let core_1_4 = properties.apiVersion >= VK_API_VERSION_1_4;
let descriptor_heap = extensions.contains(&VK_EXT_DESCRIPTOR_HEAP_EXTENSION_NAME);
let memory_budget = extensions.contains(&VK_EXT_MEMORY_BUDGET_EXTENSION_NAME);
let name = unsafe { CStr::from_ptr(properties.deviceName.as_ptr()) }
.to_string_lossy()
.into_owned();
let mut heap_features = VkPhysicalDeviceDescriptorHeapFeaturesEXT {
sType: VkStructureType::PhysicalDeviceDescriptorHeapFeaturesExt,
descriptorHeap: VK_TRUE,
..zeroed()
};
let heap_next = if descriptor_heap {
(&raw mut heap_features).cast()
} else {
null_mut()
};
let mut features14 = VkPhysicalDeviceVulkan14Features {
sType: VkStructureType::PhysicalDeviceVulkan14Features,
pNext: heap_next,
maintenance5: VK_TRUE,
maintenance6: VK_TRUE,
..zeroed()
};
let mut maintenance5 = VkPhysicalDeviceMaintenance5Features {
sType: VkStructureType::PhysicalDeviceMaintenance5Features,
pNext: heap_next,
maintenance5: VK_TRUE,
};
let mut features13 = VkPhysicalDeviceVulkan13Features {
sType: VkStructureType::PhysicalDeviceVulkan13Features,
pNext: if core_1_4 {
(&raw mut features14).cast()
} else {
(&raw mut maintenance5).cast()
},
dynamicRendering: VK_TRUE,
synchronization2: VK_TRUE,
maintenance4: VK_TRUE,
shaderDemoteToHelperInvocation: VK_TRUE,
..zeroed()
};
let mut features12 = VkPhysicalDeviceVulkan12Features {
sType: VkStructureType::PhysicalDeviceVulkan12Features,
pNext: (&raw mut features13).cast(),
bufferDeviceAddress: VK_TRUE,
drawIndirectCount: VK_TRUE,
timelineSemaphore: VK_TRUE,
runtimeDescriptorArray: VK_TRUE,
shaderSampledImageArrayNonUniformIndexing: VK_TRUE,
scalarBlockLayout: VK_TRUE,
..zeroed()
};
let mut features11 = VkPhysicalDeviceVulkan11Features {
sType: VkStructureType::PhysicalDeviceVulkan11Features,
pNext: (&raw mut features12).cast(),
shaderDrawParameters: VK_TRUE,
..zeroed()
};
let mut next: *mut c_void = (&raw mut features11).cast();
let mut timing_features = VkPhysicalDevicePresentTimingFeaturesEXT {
sType: VkStructureType::PhysicalDevicePresentTimingFeaturesExt,
pNext: next,
presentTiming: VK_TRUE,
presentAtAbsoluteTime: present_at_absolute_time.into(),
..zeroed()
};
let mut id2_features = VkPhysicalDevicePresentId2FeaturesKHR {
sType: VkStructureType::PhysicalDevicePresentId2FeaturesKhr,
pNext: (&raw mut timing_features).cast(),
presentId2: VK_TRUE,
};
if present_timing {
next = (&raw mut id2_features).cast();
}
let mut latest_ready_features = VkPhysicalDevicePresentModeFifoLatestReadyFeaturesKHR {
sType: VkStructureType::PhysicalDevicePresentModeFifoLatestReadyFeaturesKhr,
pNext: next,
presentModeFifoLatestReady: VK_TRUE,
};
if fifo_latest_ready {
next = (&raw mut latest_ready_features).cast();
}
let features = VkPhysicalDeviceFeatures2 {
sType: VkStructureType::PhysicalDeviceFeatures2,
pNext: next,
features: VkPhysicalDeviceFeatures {
multiDrawIndirect: VK_TRUE,
drawIndirectFirstInstance: VK_TRUE,
fragmentStoresAndAtomics: VK_TRUE,
..zeroed()
},
};
let priority = 1.0_f32;
let queue_info = VkDeviceQueueCreateInfo {
sType: VkStructureType::DeviceQueueCreateInfo,
queueFamilyIndex: queue_family,
queueCount: 1,
pQueuePriorities: &priority,
..zeroed()
};
let extensions: Vec<*const c_char> = extensions.iter().map(|e| e.as_ptr()).collect();
let info = VkDeviceCreateInfo {
sType: VkStructureType::DeviceCreateInfo,
pNext: (&raw const features).cast(),
queueCreateInfoCount: 1,
pQueueCreateInfos: &queue_info,
enabledExtensionCount: extensions.len() as u32,
ppEnabledExtensionNames: extensions.as_ptr(),
..zeroed()
};
let handle =
create(|handle| unsafe { commands.vkCreateDevice(physical, &info, null(), handle) })?;
let device_commands = unsafe { instance.commands.load_device(handle)? };
let mut queue = null_mut();
unsafe { device_commands.vkGetDeviceQueue(handle, queue_family, 0, &mut queue) };
let config = AllocatorConfig {
memory_budget,
descriptor_heap,
..AllocatorConfig::default()
};
// SAFETY: the device enables Vulkan 1.3, `bufferDeviceAddress` and `maintenance5`, plus
// `VK_EXT_descriptor_heap` and `descriptorHeap` with `descriptor_heap`, and `Drop`
// destroys the allocator first.
let allocator =
unsafe { Allocator::new(commands, physical, &device_commands, handle, &config) }
.inspect_err(|_| unsafe { device_commands.vkDestroyDevice(handle, null()) })?;
let mut heap_properties = VkPhysicalDeviceDescriptorHeapPropertiesEXT {
sType: VkStructureType::PhysicalDeviceDescriptorHeapPropertiesExt,
..zeroed()
};
if descriptor_heap {
let mut properties2 = VkPhysicalDeviceProperties2 {
sType: VkStructureType::PhysicalDeviceProperties2,
pNext: (&raw mut heap_properties).cast::<c_void>(),
..zeroed()
};
unsafe { commands.vkGetPhysicalDeviceProperties2(physical, &mut properties2) };
heap_properties.pNext = null_mut();
}
tracing::info!(
"using {name}, descriptor heaps supported: {descriptor_heap}, present timing supported: {present_timing}"
);
Ok(Arc::new(Self {
handle,
commands: device_commands,
physical,
queue,
queue_family,
allocator: ManuallyDrop::new(allocator),
descriptor_heap,
heap_properties,
present_timing,
present_at_absolute_time,
fifo_latest_ready,
instance: instance.clone(),
name,
}))
}
pub fn wait_idle(&self) -> Result<()> {
check(unsafe { self.commands.vkDeviceWaitIdle(self.handle) })
}
pub fn create_semaphore(&self) -> Result<VkSemaphore> {
let info = VkSemaphoreCreateInfo {
sType: VkStructureType::SemaphoreCreateInfo,
..zeroed()
};
create(|semaphore| unsafe {
self.commands
.vkCreateSemaphore(self.handle, &info, null(), semaphore)
})
}
pub fn create_fence(&self, signaled: bool) -> Result<VkFence> {
let info = VkFenceCreateInfo {
sType: VkStructureType::FenceCreateInfo,
flags: if signaled {
VkFenceCreateFlags::SIGNALED
} else {
VkFenceCreateFlags::empty()
},
..zeroed()
};
create(|fence| unsafe {
self.commands
.vkCreateFence(self.handle, &info, null(), fence)
})
}
pub fn create_command_pool(&self, transient: bool) -> Result<VkCommandPool> {
let info = VkCommandPoolCreateInfo {
sType: VkStructureType::CommandPoolCreateInfo,
flags: if transient {
VkCommandPoolCreateFlags::TRANSIENT
} else {
VkCommandPoolCreateFlags::empty()
},
queueFamilyIndex: self.queue_family,
..zeroed()
};
create(|pool| unsafe {
self.commands
.vkCreateCommandPool(self.handle, &info, null(), pool)
})
}
pub fn allocate_command_buffer(&self, pool: VkCommandPool) -> Result<VkCommandBuffer> {
let info = VkCommandBufferAllocateInfo {
sType: VkStructureType::CommandBufferAllocateInfo,
commandPool: pool,
level: VkCommandBufferLevel::Primary,
commandBufferCount: 1,
..zeroed()
};
create(|buffer| unsafe {
self.commands
.vkAllocateCommandBuffers(self.handle, &info, buffer)
})
}
/// Records and synchronously executes one-off commands, such as initial uploads.
pub fn submit_now(&self, record: impl FnOnce(VkCommandBuffer)) -> Result<()> {
let pool = self.create_command_pool(true)?;
let result = (|| {
let command_buffer = self.allocate_command_buffer(pool)?;
let begin = VkCommandBufferBeginInfo {
sType: VkStructureType::CommandBufferBeginInfo,
flags: VkCommandBufferUsageFlags::ONE_TIME_SUBMIT,
..zeroed()
};
check(unsafe { self.commands.vkBeginCommandBuffer(command_buffer, &begin) })?;
record(command_buffer);
check(unsafe { self.commands.vkEndCommandBuffer(command_buffer) })?;
let command_info = VkCommandBufferSubmitInfo {
sType: VkStructureType::CommandBufferSubmitInfo,
commandBuffer: command_buffer,
..zeroed()
};
let submit = VkSubmitInfo2 {
sType: VkStructureType::SubmitInfo2,
commandBufferInfoCount: 1,
pCommandBufferInfos: &command_info,
..zeroed()
};
check(unsafe {
self.commands
.vkQueueSubmit2(self.queue, 1, &submit, VK_NULL_HANDLE)
})?;
check(unsafe { self.commands.vkQueueWaitIdle(self.queue) })
})();
unsafe {
self.commands
.vkDestroyCommandPool(self.handle, pool, null())
};
result
}
}
impl Drop for Device {
fn drop(&mut self) {
unsafe {
self.commands.vkDeviceWaitIdle(self.handle);
ManuallyDrop::drop(&mut self.allocator);
self.commands.vkDestroyDevice(self.handle, null());
}
}
}
/// Whether the device supports the features GPU-driven terrain culling needs.
fn required_features(instance: &Instance, physical: VkPhysicalDevice) -> bool {
let mut features12 = VkPhysicalDeviceVulkan12Features {
sType: VkStructureType::PhysicalDeviceVulkan12Features,
..zeroed()
};
let mut features = VkPhysicalDeviceFeatures2 {
sType: VkStructureType::PhysicalDeviceFeatures2,
pNext: (&raw mut features12).cast(),
..zeroed()
};
unsafe {
instance
.commands
.vkGetPhysicalDeviceFeatures2(physical, &mut features)
};
let core = &features.features;
[
core.multiDrawIndirect,
core.drawIndirectFirstInstance,
core.fragmentStoresAndAtomics,
features12.drawIndirectCount,
]
.iter()
.all(|&supported| supported == VK_TRUE)
}
struct OptionalFeatures {
/// `presentTiming` with `presentId2`.
present_timing: bool,
present_at_absolute_time: bool,
fifo_latest_ready: bool,
}
/// The optional features the device supports for the chosen `extensions`.
fn optional_features(
instance: &Instance,
physical: VkPhysicalDevice,
extensions: &[&CStr],
) -> OptionalFeatures {
let mut timing = VkPhysicalDevicePresentTimingFeaturesEXT {
sType: VkStructureType::PhysicalDevicePresentTimingFeaturesExt,
..zeroed()
};
let mut id2 = VkPhysicalDevicePresentId2FeaturesKHR {
sType: VkStructureType::PhysicalDevicePresentId2FeaturesKhr,
pNext: (&raw mut timing).cast(),
..zeroed()
};
let mut next: *mut c_void = null_mut();
if extensions.contains(&VK_EXT_PRESENT_TIMING_EXTENSION_NAME) {
next = (&raw mut id2).cast();
}
let mut latest_ready = VkPhysicalDevicePresentModeFifoLatestReadyFeaturesKHR {
sType: VkStructureType::PhysicalDevicePresentModeFifoLatestReadyFeaturesKhr,
pNext: next,
..zeroed()
};
if FIFO_LATEST_READY_EXTENSIONS
.iter()
.any(|e| extensions.contains(e))
{
next = (&raw mut latest_ready).cast();
}
let mut features = VkPhysicalDeviceFeatures2 {
sType: VkStructureType::PhysicalDeviceFeatures2,
pNext: next,
..zeroed()
};
unsafe {
instance
.commands
.vkGetPhysicalDeviceFeatures2(physical, &mut features)
};
let present_timing = timing.presentTiming == VK_TRUE && id2.presentId2 == VK_TRUE;
OptionalFeatures {
present_timing,
present_at_absolute_time: present_timing && timing.presentAtAbsoluteTime == VK_TRUE,
fifo_latest_ready: latest_ready.presentModeFifoLatestReady == VK_TRUE,
}
}
fn graphics_present_family(
instance: &Instance,
physical: VkPhysicalDevice,
surface: &Surface,
) -> Option<u32> {
let commands = &instance.commands;
let families = super::enumerate(|count, items: *mut VkQueueFamilyProperties| {
unsafe { commands.vkGetPhysicalDeviceQueueFamilyProperties(physical, count, items) };
VkResult::Success
})
.ok()?;
families.iter().enumerate().find_map(|(index, family)| {
let index = index as u32;
let mut present = VK_FALSE;
unsafe {
commands.vkGetPhysicalDeviceSurfaceSupportKHR(
physical,
index,
surface.handle,
&mut present,
);
}
(family.queueFlags.contains(VkQueueFlags::GRAPHICS) && present == VK_TRUE).then_some(index)
})
}