feat(render3d): Vulkan textures, samplers and buffers
gpu_vk_res.ludic gains what gpu.ludic's texture and buffer handles stand for on Vulkan: - gvk_tex_storage / _upload / _mips / _read / _release: an image and view per handle, a full mip chain when pixels come with it (the renderer asks for mipmaps after the upload) and one level for a target, every level in SHADER_READ_ONLY between uses. Uploads are converted to what the image stores: a missing alpha filled opaque (three-channel formats are stored with four), 16-bit PNG samples byte-swapped when the unpack state says so, 32-bit float HDR halved into half floats. Mips are blitted down level by level; a read-back brings level 0 home. - gvk_sampler: one VkSampler per filter / wrap / compare / anisotropy combination, made when first asked for, with GL's defaults where the renderer set nothing. - gvk_buf_*: vertex, index and instance buffers behind one handle, kept when an upload fits. Host-visible while the backend comes up. gpu_vk.ludic switches on anisotropic sampling where the device has it and reads its limit. examples/rendering/vk_resources.ludic checks it all: VKRES OK, validation-clean, every allocation freed, on the RTX 3070 Ti (16x anisotropy) and on MoltenVK. One run on the Mac crashed while a headless game run was using the GPU and did not come back in two reruns. OpenGL frames byte-identical at the five viewpoints; 59 self-tests pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
eb428ba5f3
commit
c660ac881c
3 changed files with 572 additions and 2 deletions
|
|
@ -16,6 +16,7 @@ var gvk_family: int = -1
|
|||
var gvk_mp: bytes = null # VkPhysicalDeviceMemoryProperties
|
||||
var gvk_device_name: string = ""
|
||||
var gvk_why: string = "" # why the device did not come up, for the fallback notice
|
||||
var gvk_max_aniso: int = 0x3F800000 # the device's anisotropy limit as float bits; 1.0 when it has none
|
||||
|
||||
function gvk_fail(what: string, r: int) -> bool {
|
||||
gvk_why = `{what} failed (VkResult {r})`
|
||||
|
|
@ -113,6 +114,9 @@ function gvk_init() -> bool {
|
|||
if Vk.get_i32(f12, VkPhysicalDeviceVulkan12Features_descriptorIndexing) != 1 { missing = missing + " descriptorIndexing" }
|
||||
if Vk.get_i32(f12, VkPhysicalDeviceVulkan12Features_timelineSemaphore) != 1 { missing = missing + " timelineSemaphore" }
|
||||
if len(missing) > 0 { gvk_why = `{gvk_device_name} lacks{missing}`; return false }
|
||||
# anisotropic filtering is a 1.0 feature every desktop GPU has; ask for it when it is there
|
||||
let aniso = Vk.get_i32(f2, VkPhysicalDeviceFeatures2_features + VkPhysicalDeviceFeatures_samplerAnisotropy) == 1
|
||||
if aniso { gvk_max_aniso = Vk.get_i32(props, VkPhysicalDeviceProperties_limits + VkPhysicalDeviceLimits_maxSamplerAnisotropy) }
|
||||
let want13 = bytes(VkPhysicalDeviceVulkan13Features_sizeof)
|
||||
Vk.zero(want13, VkPhysicalDeviceVulkan13Features_sizeof)
|
||||
Vk.put_i32(want13, VkPhysicalDeviceVulkan13Features_sType, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES)
|
||||
|
|
@ -124,6 +128,11 @@ function gvk_init() -> bool {
|
|||
Vk.put_ptr(want12, VkPhysicalDeviceVulkan12Features_pNext, want13)
|
||||
Vk.put_i32(want12, VkPhysicalDeviceVulkan12Features_descriptorIndexing, 1)
|
||||
Vk.put_i32(want12, VkPhysicalDeviceVulkan12Features_timelineSemaphore, 1)
|
||||
let want2 = bytes(VkPhysicalDeviceFeatures2_sizeof)
|
||||
Vk.zero(want2, VkPhysicalDeviceFeatures2_sizeof)
|
||||
Vk.put_i32(want2, VkPhysicalDeviceFeatures2_sType, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2)
|
||||
Vk.put_ptr(want2, VkPhysicalDeviceFeatures2_pNext, want12)
|
||||
if aniso { Vk.put_i32(want2, VkPhysicalDeviceFeatures2_features + VkPhysicalDeviceFeatures_samplerAnisotropy, 1) }
|
||||
|
||||
Vk.put_i32(cnt, 0, 0)
|
||||
Vk.enumerate_device_extension_properties(gvk_pd, null, cnt, null)
|
||||
|
|
@ -142,7 +151,7 @@ function gvk_init() -> bool {
|
|||
let dci = bytes(VkDeviceCreateInfo_sizeof)
|
||||
Vk.zero(dci, VkDeviceCreateInfo_sizeof)
|
||||
Vk.put_i32(dci, VkDeviceCreateInfo_sType, VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO)
|
||||
Vk.put_ptr(dci, VkDeviceCreateInfo_pNext, want12)
|
||||
Vk.put_ptr(dci, VkDeviceCreateInfo_pNext, want2)
|
||||
Vk.put_i32(dci, VkDeviceCreateInfo_queueCreateInfoCount, 1)
|
||||
Vk.put_ptr(dci, VkDeviceCreateInfo_pQueueCreateInfos, qci)
|
||||
if gvk_ext_in(dexts, nde, "VK_KHR_portability_subset") {
|
||||
|
|
@ -160,10 +169,17 @@ function gvk_init() -> bool {
|
|||
|
||||
if not gvk_cmd_init() { return false }
|
||||
gvk_ready = true
|
||||
print(`r3d: vulkan on {gvk_device_name}`)
|
||||
print(`r3d: vulkan on {gvk_device_name}, anisotropy up to {gvk_aniso_x(gvk_max_aniso)}x`)
|
||||
return true
|
||||
}
|
||||
|
||||
# a positive float, as its bits, to a whole number (16.0 -> 16) for a message
|
||||
function gvk_aniso_x(bits: int) -> int {
|
||||
let e = ((bits >> 23) & 255) - 127
|
||||
if e < 0 { return 0 }
|
||||
return ((bits & 0x7FFFFF) | 0x800000) >> (23 - e)
|
||||
}
|
||||
|
||||
# ---- memory -------------------------------------------------------------------------------
|
||||
# The first memory type a resource allows with every property wanted; -1 if there is none.
|
||||
function gvk_mem_type(allowed: int, want: int) -> int {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue