/* * Copyright © Microsoft Corporation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "dzn_private.h" #include "vk_alloc.h" #include "vk_common_entrypoints.h" #include "vk_cmd_enqueue_entrypoints.h" #include "vk_debug_report.h" #include "vk_format.h" #include "vk_sync_dummy.h" #include "vk_util.h" #include "util/debug.h" #include "util/macros.h" #include "glsl_types.h" #include #include #include #include #include #if defined(VK_USE_PLATFORM_WIN32_KHR) || \ defined(VK_USE_PLATFORM_DISPLAY_KHR) #define DZN_USE_WSI_PLATFORM #endif #define DZN_API_VERSION VK_MAKE_VERSION(1, 0, VK_HEADER_VERSION) static const vk_instance_extension_table instance_extensions = { .KHR_get_physical_device_properties2 = true, #ifdef DZN_USE_WSI_PLATFORM .KHR_surface = true, #endif #ifdef VK_USE_PLATFORM_WIN32_KHR .KHR_win32_surface = true, #endif #ifdef VK_USE_PLATFORM_DISPLAY_KHR .KHR_display = true, .KHR_get_display_properties2 = true, .EXT_direct_mode_display = true, .EXT_display_surface_counter = true, #endif .EXT_debug_report = true, .EXT_debug_utils = true, }; static void dzn_physical_device_get_extensions(dzn_physical_device *pdev) { pdev->vk.supported_extensions = vk_device_extension_table { #ifdef DZN_USE_WSI_PLATFORM .KHR_swapchain = true, #endif }; } VKAPI_ATTR VkResult VKAPI_CALL dzn_EnumerateInstanceExtensionProperties(const char *pLayerName, uint32_t *pPropertyCount, VkExtensionProperties *pProperties) { /* We don't support any layers */ if (pLayerName) return vk_error(NULL, VK_ERROR_LAYER_NOT_PRESENT); return vk_enumerate_instance_extension_properties( &instance_extensions, pPropertyCount, pProperties); } static const struct debug_control dzn_debug_options[] = { { "sync", DZN_DEBUG_SYNC }, { "nir", DZN_DEBUG_NIR }, { "dxil", DZN_DEBUG_DXIL }, { "warp", DZN_DEBUG_WARP }, { "internal", DZN_DEBUG_INTERNAL }, { "signature", DZN_DEBUG_SIG }, { "gbv", DZN_DEBUG_GBV }, { NULL, 0 } }; static void dzn_physical_device_destroy(dzn_physical_device *pdev) { dzn_instance *instance = container_of(pdev->vk.instance, dzn_instance, vk); list_del(&pdev->link); if (pdev->dev) pdev->dev->Release(); if (pdev->adapter) pdev->adapter->Release(); dzn_wsi_finish(pdev); vk_physical_device_finish(&pdev->vk); vk_free(&instance->vk.alloc, pdev); } static void dzn_instance_destroy(dzn_instance *instance, const VkAllocationCallbacks *alloc) { if (!instance) return; if (instance->dxc.validator) instance->dxc.validator->Release(); if (instance->dxc.library) instance->dxc.library->Release(); if (instance->dxc.compiler) instance->dxc.compiler->Release(); list_for_each_entry_safe(dzn_physical_device, pdev, &instance->physical_devices, link) { dzn_physical_device_destroy(pdev); } vk_instance_finish(&instance->vk); vk_free2(vk_default_allocator(), alloc, instance); } static VkResult dzn_instance_create(const VkInstanceCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkInstance *out) { dzn_instance *instance = (dzn_instance *) vk_zalloc2(vk_default_allocator(), pAllocator, sizeof(*instance), 8, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); if (!instance) return vk_error(NULL, VK_ERROR_OUT_OF_HOST_MEMORY); vk_instance_dispatch_table dispatch_table; vk_instance_dispatch_table_from_entrypoints(&dispatch_table, &dzn_instance_entrypoints, true); VkResult result = vk_instance_init(&instance->vk, &instance_extensions, &dispatch_table, pCreateInfo, pAllocator ? pAllocator : vk_default_allocator()); if (result != VK_SUCCESS) { vk_free2(vk_default_allocator(), pAllocator, instance); return result; } list_inithead(&instance->physical_devices); instance->physical_devices_enumerated = false; instance->debug_flags = parse_debug_string(getenv("DZN_DEBUG"), dzn_debug_options); instance->dxc.validator = dxil_get_validator(); instance->dxc.library = dxc_get_library(); instance->dxc.compiler = dxc_get_compiler(); instance->d3d12.serialize_root_sig = d3d12_get_serialize_root_sig(); if (!instance->dxc.validator || !instance->dxc.library || !instance->dxc.compiler || !instance->d3d12.serialize_root_sig) { dzn_instance_destroy(instance, pAllocator); return vk_error(NULL, VK_ERROR_INITIALIZATION_FAILED); } d3d12_enable_debug_layer(); if (instance->debug_flags & DZN_DEBUG_GBV) d3d12_enable_gpu_validation(); *out = dzn_instance_to_handle(instance); return VK_SUCCESS; } VKAPI_ATTR VkResult VKAPI_CALL dzn_CreateInstance(const VkInstanceCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkInstance *pInstance) { return dzn_instance_create(pCreateInfo, pAllocator, pInstance); } VKAPI_ATTR void VKAPI_CALL dzn_DestroyInstance(VkInstance instance, const VkAllocationCallbacks *pAllocator) { dzn_instance_destroy(dzn_instance_from_handle(instance), pAllocator); } static VkResult dzn_physical_device_create(dzn_instance *instance, IDXGIAdapter1 *adapter, const DXGI_ADAPTER_DESC1 *adapter_desc) { dzn_physical_device *pdev = (dzn_physical_device *) vk_zalloc(&instance->vk.alloc, sizeof(*pdev), 8, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); if (!pdev) return vk_error(instance, VK_ERROR_OUT_OF_HOST_MEMORY); vk_physical_device_dispatch_table dispatch_table; vk_physical_device_dispatch_table_from_entrypoints(&dispatch_table, &dzn_physical_device_entrypoints, true); vk_physical_device_dispatch_table_from_entrypoints(&dispatch_table, &wsi_physical_device_entrypoints, false); VkResult result = vk_physical_device_init(&pdev->vk, &instance->vk, NULL, /* We set up extensions later */ &dispatch_table); if (result != VK_SUCCESS) { vk_free(&instance->vk.alloc, pdev); return result; } mtx_init(&pdev->dev_lock, mtx_plain); pdev->adapter_desc = *adapter_desc; pdev->adapter = adapter; adapter->AddRef(); list_addtail(&pdev->link, &instance->physical_devices); vk_warn_non_conformant_implementation("dzn"); /* TODO: correct UUIDs */ memset(pdev->pipeline_cache_uuid, 0, VK_UUID_SIZE); memset(pdev->driver_uuid, 0, VK_UUID_SIZE); memset(pdev->device_uuid, 0, VK_UUID_SIZE); /* TODO: something something queue families */ result = dzn_wsi_init(pdev); if (result != VK_SUCCESS) { dzn_physical_device_destroy(pdev); return result; } dzn_physical_device_get_extensions(pdev); uint32_t num_sync_types = 0; pdev->sync_types[num_sync_types++] = &dzn_sync_type; pdev->sync_types[num_sync_types++] = &vk_sync_dummy_type; pdev->sync_types[num_sync_types] = NULL; assert(num_sync_types <= MAX_SYNC_TYPES); pdev->vk.supported_sync_types = pdev->sync_types; return VK_SUCCESS; } static void dzn_physical_device_cache_caps(dzn_physical_device *pdev) { D3D_FEATURE_LEVEL checklist[] = { D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_12_0, D3D_FEATURE_LEVEL_12_1, D3D_FEATURE_LEVEL_12_2, }; D3D12_FEATURE_DATA_FEATURE_LEVELS levels = { .NumFeatureLevels = ARRAY_SIZE(checklist), .pFeatureLevelsRequested = checklist, }; pdev->dev->CheckFeatureSupport(D3D12_FEATURE_FEATURE_LEVELS, &levels, sizeof(levels)); pdev->feature_level = levels.MaxSupportedFeatureLevel; pdev->dev->CheckFeatureSupport(D3D12_FEATURE_ARCHITECTURE1, &pdev->architecture, sizeof(pdev->architecture)); pdev->dev->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS, &pdev->options, sizeof(pdev->options)); pdev->queue_families[pdev->queue_family_count++] = { .props = { .queueFlags = VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT, .queueCount = 1, .timestampValidBits = 64, .minImageTransferGranularity = { 0, 0, 0 }, }, .desc = { .Type = D3D12_COMMAND_LIST_TYPE_DIRECT, }, }; pdev->queue_families[pdev->queue_family_count++] = { .props = { .queueFlags = VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT, .queueCount = 8, .timestampValidBits = 64, .minImageTransferGranularity = { 0, 0, 0 }, }, .desc = { .Type = D3D12_COMMAND_LIST_TYPE_COMPUTE, }, }; pdev->queue_families[pdev->queue_family_count++] = { .props = { .queueFlags = VK_QUEUE_TRANSFER_BIT, .queueCount = 1, .timestampValidBits = 0, .minImageTransferGranularity = { 0, 0, 0 }, }, .desc = { .Type = D3D12_COMMAND_LIST_TYPE_COPY, }, }; assert(pdev->queue_family_count <= ARRAY_SIZE(pdev->queue_families)); D3D12_COMMAND_QUEUE_DESC queue_desc = { .Type = D3D12_COMMAND_LIST_TYPE_DIRECT, .Priority = D3D12_COMMAND_QUEUE_PRIORITY_NORMAL, .Flags = D3D12_COMMAND_QUEUE_FLAG_NONE, .NodeMask = 0, }; ComPtr cmdqueue; pdev->dev->CreateCommandQueue(&queue_desc, IID_PPV_ARGS(&cmdqueue)); uint64_t ts_freq; cmdqueue->GetTimestampFrequency(&ts_freq); pdev->timestamp_period = 1000000000.0f / ts_freq; } static void dzn_physical_device_init_memory(dzn_physical_device *pdev) { VkPhysicalDeviceMemoryProperties *mem = &pdev->memory; const DXGI_ADAPTER_DESC1 *desc = &pdev->adapter_desc; mem->memoryHeapCount = 1; mem->memoryHeaps[0] = VkMemoryHeap { .size = desc->SharedSystemMemory, .flags = 0, }; mem->memoryTypes[mem->memoryTypeCount++] = VkMemoryType { .propertyFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, .heapIndex = 0, }; mem->memoryTypes[mem->memoryTypeCount++] = VkMemoryType { .propertyFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, .heapIndex = 0, }; if (!pdev->architecture.UMA) { mem->memoryHeaps[mem->memoryHeapCount++] = VkMemoryHeap { .size = desc->DedicatedVideoMemory, .flags = VK_MEMORY_HEAP_DEVICE_LOCAL_BIT, }; mem->memoryTypes[mem->memoryTypeCount++] = VkMemoryType { .propertyFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, .heapIndex = mem->memoryHeapCount - 1, }; } else { mem->memoryHeaps[0].flags |= VK_MEMORY_HEAP_DEVICE_LOCAL_BIT; mem->memoryTypes[0].propertyFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; mem->memoryTypes[1].propertyFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; } constexpr unsigned MaxTier2MemoryTypes = 3; assert(mem->memoryTypeCount <= MaxTier2MemoryTypes); if (pdev->options.ResourceHeapTier == D3D12_RESOURCE_HEAP_TIER_1) { unsigned oldMemoryTypeCount = mem->memoryTypeCount; VkMemoryType oldMemoryTypes[MaxTier2MemoryTypes]; memcpy(oldMemoryTypes, mem->memoryTypes, oldMemoryTypeCount * sizeof(VkMemoryType)); mem->memoryTypeCount = 0; for (unsigned oldMemoryTypeIdx = 0; oldMemoryTypeIdx < oldMemoryTypeCount; ++oldMemoryTypeIdx) { D3D12_HEAP_FLAGS flags[] = { D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS, D3D12_HEAP_FLAG_ALLOW_ONLY_RT_DS_TEXTURES, /* Note: Vulkan requires *all* images to come from the same memory type as long as * the tiling property (and a few other misc properties) are the same. So, this * non-RT/DS texture flag will only be used for TILING_LINEAR textures, which * can't be render targets. */ D3D12_HEAP_FLAG_ALLOW_ONLY_NON_RT_DS_TEXTURES }; for (D3D12_HEAP_FLAGS flag : flags) { pdev->heap_flags_for_mem_type[mem->memoryTypeCount] = flag; mem->memoryTypes[mem->memoryTypeCount] = oldMemoryTypes[oldMemoryTypeIdx]; mem->memoryTypeCount++; } } } } static D3D12_HEAP_FLAGS dzn_physical_device_get_heap_flags_for_mem_type(const dzn_physical_device *pdev, uint32_t mem_type) { return pdev->heap_flags_for_mem_type[mem_type]; } uint32_t dzn_physical_device_get_mem_type_mask_for_resource(const dzn_physical_device *pdev, const D3D12_RESOURCE_DESC *desc) { if (pdev->options.ResourceHeapTier > D3D12_RESOURCE_HEAP_TIER_1) return (1u << pdev->memory.memoryTypeCount) - 1; D3D12_HEAP_FLAGS deny_flag; if (desc->Dimension == D3D12_RESOURCE_DIMENSION_BUFFER) deny_flag = D3D12_HEAP_FLAG_DENY_BUFFERS; else if (desc->Flags & (D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL)) deny_flag = D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES; else deny_flag = D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES; uint32_t mask = 0; for (unsigned i = 0; i < pdev->memory.memoryTypeCount; ++i) { if ((pdev->heap_flags_for_mem_type[i] & deny_flag) == D3D12_HEAP_FLAG_NONE) mask |= (1 << i); } return mask; } static uint32_t dzn_physical_device_get_max_mip_levels(bool is_3d) { return is_3d ? 11 : 14; } static uint32_t dzn_physical_device_get_max_extent(bool is_3d) { uint32_t max_mip = dzn_physical_device_get_max_mip_levels(is_3d); return 1 << max_mip; } static uint32_t dzn_physical_device_get_max_array_layers() { return dzn_physical_device_get_max_extent(false); } static ID3D12Device1 * dzn_physical_device_get_d3d12_dev(dzn_physical_device *pdev) { dzn_instance *instance = container_of(pdev->vk.instance, dzn_instance, vk); mtx_lock(&pdev->dev_lock); if (!pdev->dev) { pdev->dev = d3d12_create_device(pdev->adapter, instance->dxc.validator == nullptr); dzn_physical_device_cache_caps(pdev); dzn_physical_device_init_memory(pdev); } mtx_unlock(&pdev->dev_lock); return pdev->dev; } D3D12_FEATURE_DATA_FORMAT_SUPPORT dzn_physical_device_get_format_support(dzn_physical_device *pdev, VkFormat format) { VkImageUsageFlags usage = vk_format_is_depth_or_stencil(format) ? VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT : 0; VkImageAspectFlags aspects = 0; if (vk_format_has_depth(format)) aspects = VK_IMAGE_ASPECT_DEPTH_BIT; if (vk_format_has_stencil(format)) aspects = VK_IMAGE_ASPECT_STENCIL_BIT; D3D12_FEATURE_DATA_FORMAT_SUPPORT dfmt_info = { .Format = dzn_image_get_dxgi_format(format, usage, aspects), }; ID3D12Device *dev = dzn_physical_device_get_d3d12_dev(pdev); HRESULT hres = dev->CheckFeatureSupport(D3D12_FEATURE_FORMAT_SUPPORT, &dfmt_info, sizeof(dfmt_info)); assert(!FAILED(hres)); if (usage != VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) return dfmt_info; /* Depth/stencil resources have different format when they're accessed * as textures, query the capabilities for this format too. */ dzn_foreach_aspect(aspect, aspects) { D3D12_FEATURE_DATA_FORMAT_SUPPORT dfmt_info2 = { .Format = dzn_image_get_dxgi_format(format, 0, aspect), }; hres = dev->CheckFeatureSupport(D3D12_FEATURE_FORMAT_SUPPORT, &dfmt_info2, sizeof(dfmt_info2)); assert(!FAILED(hres)); #define DS_SRV_FORMAT_SUPPORT1_MASK \ (D3D12_FORMAT_SUPPORT1_SHADER_LOAD | \ D3D12_FORMAT_SUPPORT1_SHADER_SAMPLE | \ D3D12_FORMAT_SUPPORT1_SHADER_SAMPLE_COMPARISON | \ D3D12_FORMAT_SUPPORT1_SHADER_SAMPLE_MONO_TEXT | \ D3D12_FORMAT_SUPPORT1_MULTISAMPLE_RESOLVE | \ D3D12_FORMAT_SUPPORT1_MULTISAMPLE_LOAD | \ D3D12_FORMAT_SUPPORT1_SHADER_GATHER | \ D3D12_FORMAT_SUPPORT1_TYPED_UNORDERED_ACCESS_VIEW | \ D3D12_FORMAT_SUPPORT1_SHADER_GATHER_COMPARISON) dfmt_info.Support1 |= dfmt_info2.Support1 & DS_SRV_FORMAT_SUPPORT1_MASK; dfmt_info.Support2 |= dfmt_info2.Support2; } return dfmt_info; } void dzn_physical_device_get_format_properties(dzn_physical_device *pdev, VkFormat format, VkFormatProperties2 *properties) { D3D12_FEATURE_DATA_FORMAT_SUPPORT dfmt_info = dzn_physical_device_get_format_support(pdev, format); VkFormatProperties *base_props = &properties->formatProperties; vk_foreach_struct(ext, properties->pNext) { dzn_debug_ignored_stype(ext->sType); } if (dfmt_info.Format == DXGI_FORMAT_UNKNOWN) { *base_props = VkFormatProperties { }; return; } ID3D12Device *dev = dzn_physical_device_get_d3d12_dev(pdev); *base_props = VkFormatProperties { .linearTilingFeatures = VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT, .optimalTilingFeatures = VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT, .bufferFeatures = VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT, }; if (dfmt_info.Support1 & D3D12_FORMAT_SUPPORT1_IA_VERTEX_BUFFER) base_props->bufferFeatures |= VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT; #define TEX_FLAGS (D3D12_FORMAT_SUPPORT1_TEXTURE1D | \ D3D12_FORMAT_SUPPORT1_TEXTURE2D | \ D3D12_FORMAT_SUPPORT1_TEXTURE3D | \ D3D12_FORMAT_SUPPORT1_TEXTURECUBE) if (dfmt_info.Support1 & TEX_FLAGS) { base_props->optimalTilingFeatures |= VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT; } if (dfmt_info.Support1 & D3D12_FORMAT_SUPPORT1_SHADER_SAMPLE) { base_props->optimalTilingFeatures |= VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT; } if ((dfmt_info.Support1 & D3D12_FORMAT_SUPPORT1_SHADER_LOAD) && (dfmt_info.Support1 & D3D12_FORMAT_SUPPORT1_TYPED_UNORDERED_ACCESS_VIEW)) { base_props->optimalTilingFeatures |= VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT; base_props->bufferFeatures |= VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT; } #define ATOMIC_FLAGS (D3D12_FORMAT_SUPPORT2_UAV_ATOMIC_ADD | \ D3D12_FORMAT_SUPPORT2_UAV_ATOMIC_BITWISE_OPS | \ D3D12_FORMAT_SUPPORT2_UAV_ATOMIC_COMPARE_STORE_OR_COMPARE_EXCHANGE | \ D3D12_FORMAT_SUPPORT2_UAV_ATOMIC_EXCHANGE | \ D3D12_FORMAT_SUPPORT2_UAV_ATOMIC_SIGNED_MIN_OR_MAX | \ D3D12_FORMAT_SUPPORT2_UAV_ATOMIC_UNSIGNED_MIN_OR_MAX) if ((dfmt_info.Support2 & ATOMIC_FLAGS) == ATOMIC_FLAGS) { base_props->optimalTilingFeatures |= VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT; base_props->bufferFeatures |= VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_ATOMIC_BIT; } if (dfmt_info.Support1 & D3D12_FORMAT_SUPPORT1_SHADER_LOAD) base_props->bufferFeatures |= VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT; /* Color/depth/stencil attachment cap implies input attachement cap, and input * attachment loads are lowered to texture loads in dozen, hence the requirement * to have shader-load support. */ if (dfmt_info.Support1 & D3D12_FORMAT_SUPPORT1_SHADER_LOAD) { if (dfmt_info.Support1 & D3D12_FORMAT_SUPPORT1_RENDER_TARGET) { base_props->optimalTilingFeatures |= VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT; } if (dfmt_info.Support1 & D3D12_FORMAT_SUPPORT1_BLENDABLE) base_props->optimalTilingFeatures |= VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT; if (dfmt_info.Support1 & D3D12_FORMAT_SUPPORT1_DEPTH_STENCIL) { base_props->optimalTilingFeatures |= VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT; } } /* B4G4R4A4 support is required, but d3d12 doesn't support it. We map this * format to R4G4B4A4 and adjust the SRV component-mapping to fake * B4G4R4A4, but that forces us to limit the usage to sampling, which, * luckily, is exactly what we need to support the required features. */ if (format == VK_FORMAT_B4G4R4A4_UNORM_PACK16) { VkFormatFeatureFlags bgra4_req_features = VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT; base_props->optimalTilingFeatures &= bgra4_req_features; base_props->bufferFeatures = VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT; } } VkResult dzn_physical_device_get_image_format_properties(dzn_physical_device *pdev, const VkPhysicalDeviceImageFormatInfo2 *info, VkImageFormatProperties2 *properties) { const VkPhysicalDeviceExternalImageFormatInfo *external_info = NULL; VkExternalImageFormatProperties *external_props = NULL; *properties = VkImageFormatProperties2 { .sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2, }; /* Extract input structs */ vk_foreach_struct_const(s, info->pNext) { switch (s->sType) { case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO: external_info = (const VkPhysicalDeviceExternalImageFormatInfo *)s; break; default: dzn_debug_ignored_stype(s->sType); break; } } assert(info->tiling == VK_IMAGE_TILING_OPTIMAL || info->tiling == VK_IMAGE_TILING_LINEAR); /* Extract output structs */ vk_foreach_struct(s, properties->pNext) { switch (s->sType) { case VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES: external_props = (VkExternalImageFormatProperties *)s; break; default: dzn_debug_ignored_stype(s->sType); break; } } assert((external_props != NULL) == (external_info != NULL)); /* TODO: support image import */ if (external_info && external_info->handleType != 0) return VK_ERROR_FORMAT_NOT_SUPPORTED; if (info->tiling != VK_IMAGE_TILING_OPTIMAL && (info->usage & ~(VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT))) return VK_ERROR_FORMAT_NOT_SUPPORTED; if (info->tiling != VK_IMAGE_TILING_OPTIMAL && vk_format_is_depth_or_stencil(info->format)) return VK_ERROR_FORMAT_NOT_SUPPORTED; D3D12_FEATURE_DATA_FORMAT_SUPPORT dfmt_info = dzn_physical_device_get_format_support(pdev, info->format); if (dfmt_info.Format == DXGI_FORMAT_UNKNOWN) return VK_ERROR_FORMAT_NOT_SUPPORTED; bool is_bgra4 = info->format == VK_FORMAT_B4G4R4A4_UNORM_PACK16; ID3D12Device *dev = dzn_physical_device_get_d3d12_dev(pdev); if ((info->type == VK_IMAGE_TYPE_1D && !(dfmt_info.Support1 & D3D12_FORMAT_SUPPORT1_TEXTURE1D)) || (info->type == VK_IMAGE_TYPE_2D && !(dfmt_info.Support1 & D3D12_FORMAT_SUPPORT1_TEXTURE2D)) || (info->type == VK_IMAGE_TYPE_3D && !(dfmt_info.Support1 & D3D12_FORMAT_SUPPORT1_TEXTURE3D)) || ((info->flags & VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT) && !(dfmt_info.Support1 & D3D12_FORMAT_SUPPORT1_TEXTURECUBE))) return VK_ERROR_FORMAT_NOT_SUPPORTED; if ((info->usage & VK_IMAGE_USAGE_SAMPLED_BIT) && !(dfmt_info.Support1 & D3D12_FORMAT_SUPPORT1_SHADER_SAMPLE)) return VK_ERROR_FORMAT_NOT_SUPPORTED; if ((info->usage & VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT) && (!(dfmt_info.Support1 & D3D12_FORMAT_SUPPORT1_SHADER_LOAD) || is_bgra4)) return VK_ERROR_FORMAT_NOT_SUPPORTED; if ((info->usage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) && (!(dfmt_info.Support1 & D3D12_FORMAT_SUPPORT1_RENDER_TARGET) || is_bgra4)) return VK_ERROR_FORMAT_NOT_SUPPORTED; if ((info->usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) && (!(dfmt_info.Support1 & D3D12_FORMAT_SUPPORT1_DEPTH_STENCIL) || is_bgra4)) return VK_ERROR_FORMAT_NOT_SUPPORTED; if ((info->usage & VK_IMAGE_USAGE_STORAGE_BIT) && (!(dfmt_info.Support1 & D3D12_FORMAT_SUPPORT1_TYPED_UNORDERED_ACCESS_VIEW) || is_bgra4)) return VK_ERROR_FORMAT_NOT_SUPPORTED; bool is_3d = info->type == VK_IMAGE_TYPE_3D; uint32_t max_extent = dzn_physical_device_get_max_extent(is_3d); if (info->tiling == VK_IMAGE_TILING_OPTIMAL && dfmt_info.Support1 & D3D12_FORMAT_SUPPORT1_MIP) properties->imageFormatProperties.maxMipLevels = dzn_physical_device_get_max_mip_levels(is_3d); else properties->imageFormatProperties.maxMipLevels = 1; if (info->tiling == VK_IMAGE_TILING_OPTIMAL) properties->imageFormatProperties.maxArrayLayers = dzn_physical_device_get_max_array_layers(); else properties->imageFormatProperties.maxArrayLayers = 1; switch (info->type) { case VK_IMAGE_TYPE_1D: properties->imageFormatProperties.maxExtent.width = max_extent; properties->imageFormatProperties.maxExtent.height = 1; properties->imageFormatProperties.maxExtent.depth = 1; break; case VK_IMAGE_TYPE_2D: properties->imageFormatProperties.maxExtent.width = max_extent; properties->imageFormatProperties.maxExtent.height = max_extent; properties->imageFormatProperties.maxExtent.depth = 1; break; case VK_IMAGE_TYPE_3D: if (info->tiling != VK_IMAGE_TILING_OPTIMAL) return VK_ERROR_FORMAT_NOT_SUPPORTED; properties->imageFormatProperties.maxExtent.width = max_extent; properties->imageFormatProperties.maxExtent.height = max_extent; properties->imageFormatProperties.maxExtent.depth = max_extent; break; default: unreachable("bad VkImageType"); } /* From the Vulkan 1.0 spec, section 34.1.1. Supported Sample Counts: * * sampleCounts will be set to VK_SAMPLE_COUNT_1_BIT if at least one of the * following conditions is true: * * - tiling is VK_IMAGE_TILING_LINEAR * - type is not VK_IMAGE_TYPE_2D * - flags contains VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT * - neither the VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT flag nor the * VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT flag in * VkFormatProperties::optimalTilingFeatures returned by * vkGetPhysicalDeviceFormatProperties is set. * * D3D12 has a few more constraints: * - no UAVs on multisample resources */ bool rt_or_ds_cap = dfmt_info.Support1 & (D3D12_FORMAT_SUPPORT1_RENDER_TARGET | D3D12_FORMAT_SUPPORT1_DEPTH_STENCIL); properties->imageFormatProperties.sampleCounts = VK_SAMPLE_COUNT_1_BIT; if (info->tiling != VK_IMAGE_TILING_LINEAR && info->type == VK_IMAGE_TYPE_2D && !(info->flags & VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT) && rt_or_ds_cap && !(info->usage & VK_IMAGE_USAGE_STORAGE_BIT)) { for (uint32_t s = VK_SAMPLE_COUNT_2_BIT; s < VK_SAMPLE_COUNT_64_BIT; s <<= 1) { D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS ms_info = { .Format = dfmt_info.Format, .SampleCount = s, }; HRESULT hres = dev->CheckFeatureSupport(D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS, &ms_info, sizeof(ms_info)); if (!FAILED(hres) && ms_info.NumQualityLevels > 0) properties->imageFormatProperties.sampleCounts |= s; } } /* TODO: set correct value here */ properties->imageFormatProperties.maxResourceSize = UINT32_MAX; return VK_SUCCESS; } VKAPI_ATTR void VKAPI_CALL dzn_GetPhysicalDeviceFormatProperties2(VkPhysicalDevice physicalDevice, VkFormat format, VkFormatProperties2 *pFormatProperties) { VK_FROM_HANDLE(dzn_physical_device, pdev, physicalDevice); dzn_physical_device_get_format_properties(pdev, format, pFormatProperties); } VKAPI_ATTR VkResult VKAPI_CALL dzn_GetPhysicalDeviceImageFormatProperties2(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceImageFormatInfo2 *info, VkImageFormatProperties2 *props) { VK_FROM_HANDLE(dzn_physical_device, pdev, physicalDevice); return dzn_physical_device_get_image_format_properties(pdev, info, props); } VKAPI_ATTR VkResult VKAPI_CALL dzn_GetPhysicalDeviceImageFormatProperties(VkPhysicalDevice physicalDevice, VkFormat format, VkImageType type, VkImageTiling tiling, VkImageUsageFlags usage, VkImageCreateFlags createFlags, VkImageFormatProperties *pImageFormatProperties) { const VkPhysicalDeviceImageFormatInfo2 info = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2, .format = format, .type = type, .tiling = tiling, .usage = usage, .flags = createFlags, }; VkImageFormatProperties2 props = {}; VkResult result = dzn_GetPhysicalDeviceImageFormatProperties2(physicalDevice, &info, &props); *pImageFormatProperties = props.imageFormatProperties; return result; } VKAPI_ATTR void VKAPI_CALL dzn_GetPhysicalDeviceSparseImageFormatProperties(VkPhysicalDevice physicalDevice, VkFormat format, VkImageType type, VkSampleCountFlagBits samples, VkImageUsageFlags usage, VkImageTiling tiling, uint32_t *pPropertyCount, VkSparseImageFormatProperties *pProperties) { *pPropertyCount = 0; } VKAPI_ATTR void VKAPI_CALL dzn_GetPhysicalDeviceSparseImageFormatProperties2(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSparseImageFormatInfo2 *pFormatInfo, uint32_t *pPropertyCount, VkSparseImageFormatProperties2 *pProperties) { *pPropertyCount = 0; } VKAPI_ATTR void VKAPI_CALL dzn_GetPhysicalDeviceExternalBufferProperties(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalBufferInfo *pExternalBufferInfo, VkExternalBufferProperties *pExternalBufferProperties) { pExternalBufferProperties->externalMemoryProperties = VkExternalMemoryProperties { .compatibleHandleTypes = (VkExternalMemoryHandleTypeFlags)pExternalBufferInfo->handleType, }; } VKAPI_ATTR VkResult VKAPI_CALL dzn_EnumeratePhysicalDevices(VkInstance inst, uint32_t *pPhysicalDeviceCount, VkPhysicalDevice *pPhysicalDevices) { VK_FROM_HANDLE(dzn_instance, instance, inst); if (!instance->physical_devices_enumerated) { ComPtr factory = dxgi_get_factory(false); ComPtr adapter(NULL); for (UINT i = 0; SUCCEEDED(factory->EnumAdapters1(i, &adapter)); ++i) { DXGI_ADAPTER_DESC1 desc; adapter->GetDesc1(&desc); if (instance->debug_flags & DZN_DEBUG_WARP) { if ((desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) == 0) continue; } VkResult result = dzn_physical_device_create(instance, adapter.Get(), &desc); if (result != VK_SUCCESS) return result; } } VK_OUTARRAY_MAKE_TYPED(VkPhysicalDevice, out, pPhysicalDevices, pPhysicalDeviceCount); list_for_each_entry(dzn_physical_device, pdev, &instance->physical_devices, link) { vk_outarray_append_typed(VkPhysicalDevice, &out, i) *i = dzn_physical_device_to_handle(pdev); } instance->physical_devices_enumerated = true; return vk_outarray_status(&out); } VKAPI_ATTR VkResult VKAPI_CALL dzn_EnumerateInstanceVersion(uint32_t *pApiVersion) { *pApiVersion = DZN_API_VERSION; return VK_SUCCESS; } bool dzn_physical_device_supports_compressed_format(dzn_physical_device *pdev, const VkFormat *formats, uint32_t format_count) { #define REQUIRED_COMPRESSED_CAPS \ (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | \ VK_FORMAT_FEATURE_BLIT_SRC_BIT | \ VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT) for (uint32_t i = 0; i < format_count; i++) { VkFormatProperties2 props = {}; dzn_physical_device_get_format_properties(pdev, formats[i], &props); if ((props.formatProperties.optimalTilingFeatures & REQUIRED_COMPRESSED_CAPS) != REQUIRED_COMPRESSED_CAPS) return false; } return true; } bool dzn_physical_device_supports_bc(dzn_physical_device *pdev) { static const VkFormat formats[] = { VK_FORMAT_BC1_RGB_UNORM_BLOCK, VK_FORMAT_BC1_RGB_SRGB_BLOCK, VK_FORMAT_BC1_RGBA_UNORM_BLOCK, VK_FORMAT_BC1_RGBA_SRGB_BLOCK, VK_FORMAT_BC2_UNORM_BLOCK, VK_FORMAT_BC2_SRGB_BLOCK, VK_FORMAT_BC3_UNORM_BLOCK, VK_FORMAT_BC3_SRGB_BLOCK, VK_FORMAT_BC4_UNORM_BLOCK, VK_FORMAT_BC4_SNORM_BLOCK, VK_FORMAT_BC5_UNORM_BLOCK, VK_FORMAT_BC5_SNORM_BLOCK, VK_FORMAT_BC6H_UFLOAT_BLOCK, VK_FORMAT_BC6H_SFLOAT_BLOCK, VK_FORMAT_BC7_UNORM_BLOCK, VK_FORMAT_BC7_SRGB_BLOCK, }; return dzn_physical_device_supports_compressed_format(pdev, formats, ARRAY_SIZE(formats)); } VKAPI_ATTR void VKAPI_CALL dzn_GetPhysicalDeviceFeatures(VkPhysicalDevice physicalDevice, VkPhysicalDeviceFeatures *pFeatures) { VK_FROM_HANDLE(dzn_physical_device, pdev, physicalDevice); *pFeatures = VkPhysicalDeviceFeatures { .robustBufferAccess = true, /* This feature is mandatory */ .fullDrawIndexUint32 = false, .imageCubeArray = true, .independentBlend = false, .geometryShader = false, .tessellationShader = false, .sampleRateShading = false, .dualSrcBlend = false, .logicOp = false, .multiDrawIndirect = false, .drawIndirectFirstInstance = false, .depthClamp = false, .depthBiasClamp = false, .fillModeNonSolid = false, .depthBounds = false, .wideLines = false, .largePoints = false, .alphaToOne = false, .multiViewport = false, .samplerAnisotropy = false, .textureCompressionETC2 = false, .textureCompressionASTC_LDR = false, .textureCompressionBC = dzn_physical_device_supports_bc(pdev), .occlusionQueryPrecise = true, .pipelineStatisticsQuery = true, .vertexPipelineStoresAndAtomics = true, .fragmentStoresAndAtomics = true, .shaderTessellationAndGeometryPointSize = false, .shaderImageGatherExtended = false, .shaderStorageImageExtendedFormats = false, .shaderStorageImageMultisample = false, .shaderStorageImageReadWithoutFormat = false, .shaderStorageImageWriteWithoutFormat = false, .shaderUniformBufferArrayDynamicIndexing = false, .shaderSampledImageArrayDynamicIndexing = false, .shaderStorageBufferArrayDynamicIndexing = false, .shaderStorageImageArrayDynamicIndexing = false, .shaderClipDistance = false, .shaderCullDistance = false, .shaderFloat64 = false, .shaderInt64 = false, .shaderInt16 = false, .shaderResourceResidency = false, .shaderResourceMinLod = false, .sparseBinding = false, .sparseResidencyBuffer = false, .sparseResidencyImage2D = false, .sparseResidencyImage3D = false, .sparseResidency2Samples = false, .sparseResidency4Samples = false, .sparseResidency8Samples = false, .sparseResidency16Samples = false, .sparseResidencyAliased = false, .variableMultisampleRate = false, .inheritedQueries = false, }; } VKAPI_ATTR void VKAPI_CALL dzn_GetPhysicalDeviceFeatures2(VkPhysicalDevice physicalDevice, VkPhysicalDeviceFeatures2 *pFeatures) { dzn_GetPhysicalDeviceFeatures(physicalDevice, &pFeatures->features); vk_foreach_struct(ext, pFeatures->pNext) { dzn_debug_ignored_stype(ext->sType); } } VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL dzn_GetInstanceProcAddr(VkInstance _instance, const char *pName) { VK_FROM_HANDLE(dzn_instance, instance, _instance); return vk_instance_get_proc_addr(&instance->vk, &dzn_instance_entrypoints, pName); } /* Windows will use a dll definition file to avoid build errors. */ #ifdef _WIN32 #undef PUBLIC #define PUBLIC #endif /* With version 1+ of the loader interface the ICD should expose * vk_icdGetInstanceProcAddr to work around certain LD_PRELOAD issues seen in apps. */ PUBLIC VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vk_icdGetInstanceProcAddr(VkInstance instance, const char *pName); PUBLIC VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vk_icdGetInstanceProcAddr(VkInstance instance, const char *pName) { return dzn_GetInstanceProcAddr(instance, pName); } /* With version 4+ of the loader interface the ICD should expose * vk_icdGetPhysicalDeviceProcAddr() */ PUBLIC VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vk_icdGetPhysicalDeviceProcAddr(VkInstance _instance, const char* pName); VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vk_icdGetPhysicalDeviceProcAddr(VkInstance _instance, const char* pName) { VK_FROM_HANDLE(dzn_instance, instance, _instance); return vk_instance_get_physical_device_proc_addr(&instance->vk, pName); } /* vk_icd.h does not declare this function, so we declare it here to * suppress Wmissing-prototypes. */ PUBLIC VKAPI_ATTR VkResult VKAPI_CALL vk_icdNegotiateLoaderICDInterfaceVersion(uint32_t* pSupportedVersion); PUBLIC VKAPI_ATTR VkResult VKAPI_CALL vk_icdNegotiateLoaderICDInterfaceVersion(uint32_t* pSupportedVersion) { /* For the full details on loader interface versioning, see * . * What follows is a condensed summary, to help you navigate the large and * confusing official doc. * * - Loader interface v0 is incompatible with later versions. We don't * support it. * * - In loader interface v1: * - The first ICD entrypoint called by the loader is * vk_icdGetInstanceProcAddr(). The ICD must statically expose this * entrypoint. * - The ICD must statically expose no other Vulkan symbol unless it is * linked with -Bsymbolic. * - Each dispatchable Vulkan handle created by the ICD must be * a pointer to a struct whose first member is VK_LOADER_DATA. The * ICD must initialize VK_LOADER_DATA.loadMagic to ICD_LOADER_MAGIC. * - The loader implements vkCreate{PLATFORM}SurfaceKHR() and * vkDestroySurfaceKHR(). The ICD must be capable of working with * such loader-managed surfaces. * * - Loader interface v2 differs from v1 in: * - The first ICD entrypoint called by the loader is * vk_icdNegotiateLoaderICDInterfaceVersion(). The ICD must * statically expose this entrypoint. * * - Loader interface v3 differs from v2 in: * - The ICD must implement vkCreate{PLATFORM}SurfaceKHR(), * vkDestroySurfaceKHR(), and other API which uses VKSurfaceKHR, * because the loader no longer does so. * * - Loader interface v4 differs from v3 in: * - The ICD must implement vk_icdGetPhysicalDeviceProcAddr(). */ *pSupportedVersion = MIN2(*pSupportedVersion, 4u); return VK_SUCCESS; } VKAPI_ATTR void VKAPI_CALL dzn_GetPhysicalDeviceProperties(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties *pProperties) { VK_FROM_HANDLE(dzn_physical_device, pdevice, physicalDevice); /* minimum from the spec */ const VkSampleCountFlags supported_sample_counts = VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT; /* FIXME: this is mostly bunk for now */ VkPhysicalDeviceLimits limits = { /* TODO: support older feature levels */ .maxImageDimension1D = (1 << 14), .maxImageDimension2D = (1 << 14), .maxImageDimension3D = (1 << 11), .maxImageDimensionCube = (1 << 14), .maxImageArrayLayers = (1 << 11), /* from here on, we simply use the minimum values from the spec for now */ .maxTexelBufferElements = 65536, .maxUniformBufferRange = 16384, .maxStorageBufferRange = (1ul << 27), .maxPushConstantsSize = 128, .maxMemoryAllocationCount = 4096, .maxSamplerAllocationCount = 4000, .bufferImageGranularity = 131072, .sparseAddressSpaceSize = 0, .maxBoundDescriptorSets = MAX_SETS, .maxPerStageDescriptorSamplers = 16, .maxPerStageDescriptorUniformBuffers = 12, .maxPerStageDescriptorStorageBuffers = 4, .maxPerStageDescriptorSampledImages = 16, .maxPerStageDescriptorStorageImages = 4, .maxPerStageDescriptorInputAttachments = 4, .maxPerStageResources = 128, .maxDescriptorSetSamplers = 96, .maxDescriptorSetUniformBuffers = 72, .maxDescriptorSetUniformBuffersDynamic = MAX_DYNAMIC_UNIFORM_BUFFERS, .maxDescriptorSetStorageBuffers = 24, .maxDescriptorSetStorageBuffersDynamic = MAX_DYNAMIC_STORAGE_BUFFERS, .maxDescriptorSetSampledImages = 96, .maxDescriptorSetStorageImages = 24, .maxDescriptorSetInputAttachments = 4, .maxVertexInputAttributes = 16, .maxVertexInputBindings = 16, .maxVertexInputAttributeOffset = 2047, .maxVertexInputBindingStride = 2048, .maxVertexOutputComponents = 64, .maxTessellationGenerationLevel = 0, .maxTessellationPatchSize = 0, .maxTessellationControlPerVertexInputComponents = 0, .maxTessellationControlPerVertexOutputComponents = 0, .maxTessellationControlPerPatchOutputComponents = 0, .maxTessellationControlTotalOutputComponents = 0, .maxTessellationEvaluationInputComponents = 0, .maxTessellationEvaluationOutputComponents = 0, .maxGeometryShaderInvocations = 0, .maxGeometryInputComponents = 0, .maxGeometryOutputComponents = 0, .maxGeometryOutputVertices = 0, .maxGeometryTotalOutputComponents = 0, .maxFragmentInputComponents = 64, .maxFragmentOutputAttachments = 4, .maxFragmentDualSrcAttachments = 0, .maxFragmentCombinedOutputResources = 4, .maxComputeSharedMemorySize = 16384, .maxComputeWorkGroupCount = { 65535, 65535, 65535 }, .maxComputeWorkGroupInvocations = 128, .maxComputeWorkGroupSize = { 128, 128, 64 }, .subPixelPrecisionBits = 4, .subTexelPrecisionBits = 4, .mipmapPrecisionBits = 4, .maxDrawIndexedIndexValue = 0x00ffffff, .maxDrawIndirectCount = 1, .maxSamplerLodBias = 2.0f, .maxSamplerAnisotropy = 1.0f, .maxViewports = 1, .maxViewportDimensions = { 4096, 4096 }, .viewportBoundsRange = { -8192, 8191 }, .viewportSubPixelBits = 0, .minMemoryMapAlignment = 64, .minTexelBufferOffsetAlignment = 256, .minUniformBufferOffsetAlignment = 256, .minStorageBufferOffsetAlignment = 256, .minTexelOffset = -8, .maxTexelOffset = 7, .minTexelGatherOffset = 0, .maxTexelGatherOffset = 0, .minInterpolationOffset = 0.0f, .maxInterpolationOffset = 0.0f, .subPixelInterpolationOffsetBits = 0, .maxFramebufferWidth = 4096, .maxFramebufferHeight = 4096, .maxFramebufferLayers = 256, .framebufferColorSampleCounts = supported_sample_counts, .framebufferDepthSampleCounts = supported_sample_counts, .framebufferStencilSampleCounts = supported_sample_counts, .framebufferNoAttachmentsSampleCounts = supported_sample_counts, .maxColorAttachments = 4, .sampledImageColorSampleCounts = supported_sample_counts, .sampledImageIntegerSampleCounts = VK_SAMPLE_COUNT_1_BIT, .sampledImageDepthSampleCounts = supported_sample_counts, .sampledImageStencilSampleCounts = supported_sample_counts, .storageImageSampleCounts = VK_SAMPLE_COUNT_1_BIT, .maxSampleMaskWords = 1, .timestampComputeAndGraphics = true, .timestampPeriod = pdevice->timestamp_period, .maxClipDistances = 8, .maxCullDistances = 8, .maxCombinedClipAndCullDistances = 8, .discreteQueuePriorities = 2, .pointSizeRange = { 1.0f, 1.0f }, .lineWidthRange = { 1.0f, 1.0f }, .pointSizeGranularity = 0.0f, .lineWidthGranularity = 0.0f, .strictLines = 0, .standardSampleLocations = false, .optimalBufferCopyOffsetAlignment = 1, .optimalBufferCopyRowPitchAlignment = 1, .nonCoherentAtomSize = 256, }; const DXGI_ADAPTER_DESC1& desc = pdevice->adapter_desc; VkPhysicalDeviceType devtype = VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU; if (desc.Flags == DXGI_ADAPTER_FLAG_SOFTWARE) devtype = VK_PHYSICAL_DEVICE_TYPE_CPU; else if (false) { // TODO: detect discreete GPUs /* This is a tad tricky to get right, because we need to have the * actual ID3D12Device before we can query the * D3D12_FEATURE_DATA_ARCHITECTURE structure... So for now, let's * just pretend everything is integrated, because... well, that's * what I have at hand right now ;) */ devtype = VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU; } *pProperties = VkPhysicalDeviceProperties { .apiVersion = DZN_API_VERSION, .driverVersion = vk_get_driver_version(), .vendorID = desc.VendorId, .deviceID = desc.DeviceId, .deviceType = devtype, .limits = limits, .sparseProperties = { 0 }, }; snprintf(pProperties->deviceName, sizeof(pProperties->deviceName), "Microsoft Direct3D12 (%S)", desc.Description); memcpy(pProperties->pipelineCacheUUID, pdevice->pipeline_cache_uuid, VK_UUID_SIZE); } VKAPI_ATTR void VKAPI_CALL dzn_GetPhysicalDeviceProperties2(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties2 *pProperties) { VK_FROM_HANDLE(dzn_physical_device, pdevice, physicalDevice); dzn_GetPhysicalDeviceProperties(physicalDevice, &pProperties->properties); vk_foreach_struct(ext, pProperties->pNext) { switch (ext->sType) { case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES: { VkPhysicalDeviceIDProperties *id_props = (VkPhysicalDeviceIDProperties *)ext; memcpy(id_props->deviceUUID, pdevice->device_uuid, VK_UUID_SIZE); memcpy(id_props->driverUUID, pdevice->driver_uuid, VK_UUID_SIZE); /* The LUID is for Windows. */ id_props->deviceLUIDValid = false; break; } default: dzn_debug_ignored_stype(ext->sType); break; } } } VKAPI_ATTR void VKAPI_CALL dzn_GetPhysicalDeviceQueueFamilyProperties2(VkPhysicalDevice physicalDevice, uint32_t *pQueueFamilyPropertyCount, VkQueueFamilyProperties2 *pQueueFamilyProperties) { VK_FROM_HANDLE(dzn_physical_device, pdev, physicalDevice); VK_OUTARRAY_MAKE_TYPED(VkQueueFamilyProperties2, out, pQueueFamilyProperties, pQueueFamilyPropertyCount); (void)dzn_physical_device_get_d3d12_dev(pdev); for (uint32_t i = 0; i < pdev->queue_family_count; i++) { vk_outarray_append_typed(VkQueueFamilyProperties2, &out, p) { p->queueFamilyProperties = pdev->queue_families[i].props; vk_foreach_struct(ext, pQueueFamilyProperties->pNext) { dzn_debug_ignored_stype(ext->sType); } } } } VKAPI_ATTR void VKAPI_CALL dzn_GetPhysicalDeviceMemoryProperties(VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties *pMemoryProperties) { VK_FROM_HANDLE(dzn_physical_device, pdev, physicalDevice); // Ensure memory caps are up-to-date (void)dzn_physical_device_get_d3d12_dev(pdev); *pMemoryProperties = pdev->memory; } VKAPI_ATTR void VKAPI_CALL dzn_GetPhysicalDeviceMemoryProperties2(VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties2 *pMemoryProperties) { dzn_GetPhysicalDeviceMemoryProperties(physicalDevice, &pMemoryProperties->memoryProperties); vk_foreach_struct(ext, pMemoryProperties->pNext) { dzn_debug_ignored_stype(ext->sType); } } VKAPI_ATTR VkResult VKAPI_CALL dzn_EnumerateInstanceLayerProperties(uint32_t *pPropertyCount, VkLayerProperties *pProperties) { if (pProperties == NULL) { *pPropertyCount = 0; return VK_SUCCESS; } return vk_error(NULL, VK_ERROR_LAYER_NOT_PRESENT); } static VkResult dzn_queue_sync_wait(dzn_queue *queue, const struct vk_sync_wait *wait) { if (wait->sync->type == &vk_sync_dummy_type) return VK_SUCCESS; dzn_device *device = container_of(queue->vk.base.device, dzn_device, vk); assert(wait->sync->type == &dzn_sync_type); dzn_sync *sync = container_of(wait->sync, dzn_sync, vk); uint64_t value = (sync->vk.flags & VK_SYNC_IS_TIMELINE) ? wait->wait_value : 1; assert(sync->fence != NULL); if (value > 0 && FAILED(queue->cmdqueue->Wait(sync->fence, value))) return vk_error(device, VK_ERROR_UNKNOWN); return VK_SUCCESS; } static VkResult dzn_queue_sync_signal(dzn_queue *queue, const struct vk_sync_signal *signal) { if (signal->sync->type == &vk_sync_dummy_type) return VK_SUCCESS; dzn_device *device = container_of(queue->vk.base.device, dzn_device, vk); assert(signal->sync->type == &dzn_sync_type); dzn_sync *sync = container_of(signal->sync, dzn_sync, vk); uint64_t value = (sync->vk.flags & VK_SYNC_IS_TIMELINE) ? signal->signal_value : 1; assert(value > 0); assert(sync->fence != NULL); if (FAILED(queue->cmdqueue->Signal(sync->fence, value))) return vk_error(device, VK_ERROR_UNKNOWN); return VK_SUCCESS; } static VkResult dzn_queue_submit(struct vk_queue *q, struct vk_queue_submit *info) { dzn_queue *queue = container_of(q, dzn_queue, vk); dzn_device *device = container_of(q->base.device, dzn_device, vk); VkResult result = VK_SUCCESS; for (uint32_t i = 0; i < info->wait_count; i++) { result = dzn_queue_sync_wait(queue, &info->waits[i]); if (result != VK_SUCCESS) return result; } for (uint32_t i = 0; i < info->command_buffer_count; i++) { dzn_cmd_buffer *cmd_buffer = container_of(info->command_buffers[i], dzn_cmd_buffer, vk); ID3D12CommandList *cmdlists[] = { cmd_buffer->cmdlist }; util_dynarray_foreach(&cmd_buffer->events.wait, dzn_event *, evt) { if (FAILED(queue->cmdqueue->Wait((*evt)->fence, 1))) return vk_error(device, VK_ERROR_UNKNOWN); } util_dynarray_foreach(&cmd_buffer->queries.wait, dzn_cmd_buffer_query_range, range) { mtx_lock(&range->qpool->queries_lock); for (uint32_t q = range->start; q < range->start + range->count; q++) { struct dzn_query *query = &range->qpool->queries[q]; if (query->fence && FAILED(queue->cmdqueue->Wait(query->fence, query->fence_value))) return vk_error(device, VK_ERROR_UNKNOWN); } mtx_unlock(&range->qpool->queries_lock); } util_dynarray_foreach(&cmd_buffer->queries.reset, dzn_cmd_buffer_query_range, range) { mtx_lock(&range->qpool->queries_lock); for (uint32_t q = range->start; q < range->start + range->count; q++) { struct dzn_query *query = &range->qpool->queries[q]; if (query->fence) { query->fence->Release(); query->fence = NULL; } query->fence_value = 0; } mtx_unlock(&range->qpool->queries_lock); } queue->cmdqueue->ExecuteCommandLists(1, cmdlists); util_dynarray_foreach(&cmd_buffer->events.signal, dzn_cmd_event_signal, evt) { if (FAILED(queue->cmdqueue->Signal(evt->event->fence, evt->value ? 1 : 0))) return vk_error(device, VK_ERROR_UNKNOWN); } util_dynarray_foreach(&cmd_buffer->queries.signal, dzn_cmd_buffer_query_range, range) { mtx_lock(&range->qpool->queries_lock); for (uint32_t q = range->start; q < range->start + range->count; q++) { struct dzn_query *query = &range->qpool->queries[q]; query->fence_value = queue->fence_point + 1; query->fence = queue->fence; query->fence->AddRef(); } mtx_unlock(&range->qpool->queries_lock); } } for (uint32_t i = 0; i < info->signal_count; i++) { result = dzn_queue_sync_signal(queue, &info->signals[i]); if (result != VK_SUCCESS) return vk_error(device, VK_ERROR_UNKNOWN); } if (FAILED(queue->cmdqueue->Signal(queue->fence, ++queue->fence_point))) return vk_error(device, VK_ERROR_UNKNOWN); return VK_SUCCESS; } static void dzn_queue_finish(dzn_queue *queue) { if (queue->cmdqueue) queue->cmdqueue->Release(); if (queue->fence) queue->fence->Release(); vk_queue_finish(&queue->vk); } static VkResult dzn_queue_init(dzn_queue *queue, dzn_device *device, const VkDeviceQueueCreateInfo *pCreateInfo, uint32_t index_in_family) { dzn_physical_device *pdev = container_of(device->vk.physical, dzn_physical_device, vk); VkResult result = vk_queue_init(&queue->vk, &device->vk, pCreateInfo, index_in_family); if (result != VK_SUCCESS) return result; queue->vk.driver_submit = dzn_queue_submit; assert(pCreateInfo->queueFamilyIndex < pdev->queue_family_count); D3D12_COMMAND_QUEUE_DESC queue_desc = pdev->queue_families[pCreateInfo->queueFamilyIndex].desc; queue_desc.Priority = (INT)(pCreateInfo->pQueuePriorities[index_in_family] * (float)D3D12_COMMAND_QUEUE_PRIORITY_HIGH); queue_desc.NodeMask = 0; if (FAILED(device->dev->CreateCommandQueue(&queue_desc, IID_PPV_ARGS(&queue->cmdqueue)))) { dzn_queue_finish(queue); return vk_error(device->vk.physical->instance, VK_ERROR_INITIALIZATION_FAILED); } if (FAILED(device->dev->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&queue->fence)))) { dzn_queue_finish(queue); return vk_error(device->vk.physical->instance, VK_ERROR_INITIALIZATION_FAILED); } return VK_SUCCESS; } static VkResult check_physical_device_features(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceFeatures *features) { VkPhysicalDeviceFeatures supported_features; dzn_GetPhysicalDeviceFeatures(physicalDevice, &supported_features); VkBool32 *supported_feature = (VkBool32 *)&supported_features; VkBool32 *enabled_feature = (VkBool32 *)features; unsigned num_features = sizeof(VkPhysicalDeviceFeatures) / sizeof(VkBool32); for (uint32_t i = 0; i < num_features; i++) { if (enabled_feature[i] && !supported_feature[i]) return VK_ERROR_FEATURE_NOT_PRESENT; } return VK_SUCCESS; } static VkResult dzn_device_create_sync_for_memory(struct vk_device *device, VkDeviceMemory memory, bool signal_memory, struct vk_sync **sync_out) { return vk_sync_create(device, &vk_sync_dummy_type, (enum vk_sync_flags)0, 1, sync_out); } static void dzn_device_ref_pipeline_layout(struct vk_device *dev, VkPipelineLayout layout) { VK_FROM_HANDLE(dzn_pipeline_layout, playout, layout); dzn_pipeline_layout_ref(playout); } static void dzn_device_unref_pipeline_layout(struct vk_device *dev, VkPipelineLayout layout) { VK_FROM_HANDLE(dzn_pipeline_layout, playout, layout); dzn_pipeline_layout_unref(playout); } static VkResult dzn_device_query_init(dzn_device *device) { /* FIXME: create the resource in the default heap */ D3D12_HEAP_PROPERTIES hprops = device->dev->GetCustomHeapProperties(0, D3D12_HEAP_TYPE_UPLOAD); D3D12_RESOURCE_DESC rdesc = { .Dimension = D3D12_RESOURCE_DIMENSION_BUFFER, .Alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT, .Width = DZN_QUERY_REFS_RES_SIZE, .Height = 1, .DepthOrArraySize = 1, .MipLevels = 1, .Format = DXGI_FORMAT_UNKNOWN, .SampleDesc = { .Count = 1, .Quality = 0 }, .Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR, .Flags = D3D12_RESOURCE_FLAG_NONE, }; if (FAILED(device->dev->CreateCommittedResource(&hprops, D3D12_HEAP_FLAG_NONE, &rdesc, D3D12_RESOURCE_STATE_GENERIC_READ, NULL, IID_PPV_ARGS(&device->queries.refs)))) return vk_error(device->vk.physical, VK_ERROR_OUT_OF_DEVICE_MEMORY); uint8_t *queries_ref; if (FAILED(device->queries.refs->Map(0, NULL, (void **)&queries_ref))) return vk_error(device->vk.physical, VK_ERROR_OUT_OF_HOST_MEMORY); memset(queries_ref + DZN_QUERY_REFS_ALL_ONES_OFFSET, 0xff, DZN_QUERY_REFS_SECTION_SIZE); memset(queries_ref + DZN_QUERY_REFS_ALL_ZEROS_OFFSET, 0x0, DZN_QUERY_REFS_SECTION_SIZE); device->queries.refs->Unmap(0, NULL); return VK_SUCCESS; } static void dzn_device_query_finish(dzn_device *device) { if (device->queries.refs) device->queries.refs->Release(); } static void dzn_device_destroy(dzn_device *device, const VkAllocationCallbacks *pAllocator) { if (!device) return; dzn_instance *instance = container_of(device->vk.physical->instance, dzn_instance, vk); vk_foreach_queue_safe(q, &device->vk) { dzn_queue *queue = container_of(q, dzn_queue, vk); dzn_queue_finish(queue); } dzn_device_query_finish(device); dzn_meta_finish(device); if (device->dev) device->dev->Release(); vk_device_finish(&device->vk); vk_free2(&instance->vk.alloc, pAllocator, device); } static VkResult dzn_device_create(dzn_physical_device *pdev, const VkDeviceCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkDevice *out) { dzn_instance *instance = container_of(pdev->vk.instance, dzn_instance, vk); uint32_t queue_count = 0; for (uint32_t qf = 0; qf < pCreateInfo->queueCreateInfoCount; qf++) { const VkDeviceQueueCreateInfo *qinfo = &pCreateInfo->pQueueCreateInfos[qf]; queue_count += qinfo->queueCount; } VK_MULTIALLOC(ma); VK_MULTIALLOC_DECL(&ma, dzn_device, device, 1); VK_MULTIALLOC_DECL(&ma, dzn_queue, queues, queue_count); if (!vk_multialloc_zalloc2(&ma, &instance->vk.alloc, pAllocator, VK_SYSTEM_ALLOCATION_SCOPE_DEVICE)) return vk_error(pdev, VK_ERROR_OUT_OF_HOST_MEMORY); vk_device_dispatch_table dispatch_table; /* For secondary command buffer support, overwrite any command entrypoints * in the main device-level dispatch table with * vk_cmd_enqueue_unless_primary_Cmd*. */ vk_device_dispatch_table_from_entrypoints(&dispatch_table, &vk_cmd_enqueue_unless_primary_device_entrypoints, true); vk_device_dispatch_table_from_entrypoints(&dispatch_table, &dzn_device_entrypoints, false); vk_device_dispatch_table_from_entrypoints(&dispatch_table, &wsi_device_entrypoints, false); /* Populate our primary cmd_dispatch table. */ vk_device_dispatch_table_from_entrypoints(&device->cmd_dispatch, &dzn_device_entrypoints, true); vk_device_dispatch_table_from_entrypoints(&device->cmd_dispatch, &vk_common_device_entrypoints, false); VkResult result = vk_device_init(&device->vk, &pdev->vk, &dispatch_table, pCreateInfo, pAllocator); if (result != VK_SUCCESS) { vk_free2(&device->vk.alloc, pAllocator, device); return result; } /* Must be done after vk_device_init() because this function memset(0) the * whole struct. */ device->vk.command_dispatch_table = &device->cmd_dispatch; device->vk.ref_pipeline_layout = dzn_device_ref_pipeline_layout; device->vk.unref_pipeline_layout = dzn_device_unref_pipeline_layout; device->vk.create_sync_for_memory = dzn_device_create_sync_for_memory; device->dev = dzn_physical_device_get_d3d12_dev(pdev); if (!device->dev) { dzn_device_destroy(device, pAllocator); return vk_error(pdev, VK_ERROR_INITIALIZATION_FAILED); } device->dev->AddRef(); ID3D12InfoQueue *info_queue; if (SUCCEEDED(device->dev->QueryInterface(IID_PPV_ARGS(&info_queue)))) { D3D12_MESSAGE_SEVERITY severities[] = { D3D12_MESSAGE_SEVERITY_INFO, D3D12_MESSAGE_SEVERITY_WARNING, }; D3D12_MESSAGE_ID msg_ids[] = { D3D12_MESSAGE_ID_CLEARRENDERTARGETVIEW_MISMATCHINGCLEARVALUE, }; D3D12_INFO_QUEUE_FILTER NewFilter = {}; NewFilter.DenyList.NumSeverities = ARRAY_SIZE(severities); NewFilter.DenyList.pSeverityList = severities; NewFilter.DenyList.NumIDs = ARRAY_SIZE(msg_ids); NewFilter.DenyList.pIDList = msg_ids; info_queue->PushStorageFilter(&NewFilter); } result = dzn_meta_init(device); if (result != VK_SUCCESS) { dzn_device_destroy(device, pAllocator); return result; } result = dzn_device_query_init(device); if (result != VK_SUCCESS) { dzn_device_destroy(device, pAllocator); return result; } uint32_t qindex = 0; for (uint32_t qf = 0; qf < pCreateInfo->queueCreateInfoCount; qf++) { const VkDeviceQueueCreateInfo *qinfo = &pCreateInfo->pQueueCreateInfos[qf]; for (uint32_t q = 0; q < qinfo->queueCount; q++) { result = dzn_queue_init(&queues[qindex++], device, qinfo, q); if (result != VK_SUCCESS) { dzn_device_destroy(device, pAllocator); return result; } } } assert(queue_count == qindex); *out = dzn_device_to_handle(device); return VK_SUCCESS; } ID3D12RootSignature * dzn_device_create_root_sig(dzn_device *device, const D3D12_VERSIONED_ROOT_SIGNATURE_DESC *desc) { dzn_instance *instance = container_of(device->vk.physical->instance, dzn_instance, vk); ComPtr sig, error; if (FAILED(instance->d3d12.serialize_root_sig(desc, &sig, &error))) { if (instance->debug_flags & DZN_DEBUG_SIG) { const char* error_msg = (const char*)error->GetBufferPointer(); fprintf(stderr, "== SERIALIZE ROOT SIG ERROR =============================================\n" "%s\n" "== END ==========================================================\n", error_msg); } return NULL; } ID3D12RootSignature *root_sig; if (FAILED(device->dev->CreateRootSignature(0, sig->GetBufferPointer(), sig->GetBufferSize(), IID_PPV_ARGS(&root_sig)))) return NULL; return root_sig; } VKAPI_ATTR VkResult VKAPI_CALL dzn_CreateDevice(VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkDevice *pDevice) { VK_FROM_HANDLE(dzn_physical_device, physical_device, physicalDevice); dzn_instance *instance = container_of(physical_device->vk.instance, dzn_instance, vk); VkResult result; assert(pCreateInfo->sType == VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO); /* Check enabled features */ if (pCreateInfo->pEnabledFeatures) { result = check_physical_device_features(physicalDevice, pCreateInfo->pEnabledFeatures); if (result != VK_SUCCESS) return vk_error(physical_device, result); } /* Check requested queues and fail if we are requested to create any * queues with flags we don't support. */ assert(pCreateInfo->queueCreateInfoCount > 0); for (uint32_t i = 0; i < pCreateInfo->queueCreateInfoCount; i++) { if (pCreateInfo->pQueueCreateInfos[i].flags != 0) return vk_error(physical_device, VK_ERROR_INITIALIZATION_FAILED); } return dzn_device_create(physical_device, pCreateInfo, pAllocator, pDevice); } VKAPI_ATTR void VKAPI_CALL dzn_DestroyDevice(VkDevice dev, const VkAllocationCallbacks *pAllocator) { VK_FROM_HANDLE(dzn_device, device, dev); device->vk.dispatch_table.DeviceWaitIdle(dev); dzn_device_destroy(device, pAllocator); } static void dzn_device_memory_destroy(dzn_device_memory *mem, const VkAllocationCallbacks *pAllocator) { if (!mem) return; dzn_device *device = container_of(mem->base.device, dzn_device, vk); if (mem->map) mem->map_res->Unmap(0, NULL); if (mem->map_res) mem->map_res->Release(); if (mem->heap) mem->heap->Release(); vk_object_base_finish(&mem->base); vk_free2(&device->vk.alloc, pAllocator, mem); } static VkResult dzn_device_memory_create(dzn_device *device, const VkMemoryAllocateInfo *pAllocateInfo, const VkAllocationCallbacks *pAllocator, VkDeviceMemory *out) { dzn_physical_device *pdevice = container_of(device->vk.physical, dzn_physical_device, vk); dzn_device_memory *mem = (dzn_device_memory *) vk_zalloc2(&device->vk.alloc, pAllocator, sizeof(*mem), 8, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); if (!mem) return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY); vk_object_base_init(&device->vk, &mem->base, VK_OBJECT_TYPE_DEVICE_MEMORY); /* The Vulkan 1.0.33 spec says "allocationSize must be greater than 0". */ assert(pAllocateInfo->allocationSize > 0); mem->size = pAllocateInfo->allocationSize; #if 0 const VkExportMemoryAllocateInfo *export_info = NULL; VkMemoryAllocateFlags vk_flags = 0; #endif vk_foreach_struct_const(ext, pAllocateInfo->pNext) { dzn_debug_ignored_stype(ext->sType); } const VkMemoryType *mem_type = &pdevice->memory.memoryTypes[pAllocateInfo->memoryTypeIndex]; D3D12_HEAP_DESC heap_desc = {}; // TODO: fix all of these: heap_desc.SizeInBytes = pAllocateInfo->allocationSize; heap_desc.Alignment = heap_desc.SizeInBytes >= D3D12_DEFAULT_MSAA_RESOURCE_PLACEMENT_ALIGNMENT ? D3D12_DEFAULT_MSAA_RESOURCE_PLACEMENT_ALIGNMENT : D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT; heap_desc.Flags = dzn_physical_device_get_heap_flags_for_mem_type(pdevice, pAllocateInfo->memoryTypeIndex); /* TODO: Unsure about this logic??? */ mem->initial_state = D3D12_RESOURCE_STATE_COMMON; heap_desc.Properties.Type = D3D12_HEAP_TYPE_CUSTOM; heap_desc.Properties.MemoryPoolPreference = ((mem_type->propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) && !pdevice->architecture.UMA) ? D3D12_MEMORY_POOL_L1 : D3D12_MEMORY_POOL_L0; if (mem_type->propertyFlags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) { heap_desc.Properties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_WRITE_BACK; } else if (mem_type->propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) { heap_desc.Properties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_WRITE_COMBINE; } else { heap_desc.Properties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_NOT_AVAILABLE; } if (FAILED(device->dev->CreateHeap(&heap_desc, IID_PPV_ARGS(&mem->heap)))) { dzn_device_memory_destroy(mem, pAllocator); return vk_error(device, VK_ERROR_OUT_OF_DEVICE_MEMORY); } if ((mem_type->propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) && !(heap_desc.Flags & D3D12_HEAP_FLAG_DENY_BUFFERS)){ D3D12_RESOURCE_DESC res_desc = {}; res_desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; res_desc.Format = DXGI_FORMAT_UNKNOWN; res_desc.Alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT; res_desc.Width = heap_desc.SizeInBytes; res_desc.Height = 1; res_desc.DepthOrArraySize = 1; res_desc.MipLevels = 1; res_desc.SampleDesc.Count = 1; res_desc.SampleDesc.Quality = 0; res_desc.Flags = D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE; res_desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; HRESULT hr = device->dev->CreatePlacedResource(mem->heap, 0, &res_desc, mem->initial_state, NULL, IID_PPV_ARGS(&mem->map_res)); if (FAILED(hr)) { dzn_device_memory_destroy(mem, pAllocator); return vk_error(device, VK_ERROR_OUT_OF_DEVICE_MEMORY); } } *out = dzn_device_memory_to_handle(mem); return VK_SUCCESS; } VKAPI_ATTR VkResult VKAPI_CALL dzn_AllocateMemory(VkDevice device, const VkMemoryAllocateInfo *pAllocateInfo, const VkAllocationCallbacks *pAllocator, VkDeviceMemory *pMem) { return dzn_device_memory_create(dzn_device_from_handle(device), pAllocateInfo, pAllocator, pMem); } VKAPI_ATTR void VKAPI_CALL dzn_FreeMemory(VkDevice device, VkDeviceMemory mem, const VkAllocationCallbacks *pAllocator) { dzn_device_memory_destroy(dzn_device_memory_from_handle(mem), pAllocator); } VKAPI_ATTR VkResult VKAPI_CALL dzn_MapMemory(VkDevice _device, VkDeviceMemory _memory, VkDeviceSize offset, VkDeviceSize size, VkMemoryMapFlags flags, void **ppData) { VK_FROM_HANDLE(dzn_device, device, _device); VK_FROM_HANDLE(dzn_device_memory, mem, _memory); if (mem == NULL) { *ppData = NULL; return VK_SUCCESS; } if (size == VK_WHOLE_SIZE) size = mem->size - offset; /* From the Vulkan spec version 1.0.32 docs for MapMemory: * * * If size is not equal to VK_WHOLE_SIZE, size must be greater than 0 * assert(size != 0); * * If size is not equal to VK_WHOLE_SIZE, size must be less than or * equal to the size of the memory minus offset */ assert(size > 0); assert(offset + size <= mem->size); assert(mem->map_res); D3D12_RANGE range = {}; range.Begin = offset; range.End = offset + size; void *map = NULL; if (FAILED(mem->map_res->Map(0, &range, &map))) return vk_error(device, VK_ERROR_MEMORY_MAP_FAILED); mem->map = map; mem->map_size = size; *ppData = ((uint8_t*) map) + offset; return VK_SUCCESS; } VKAPI_ATTR void VKAPI_CALL dzn_UnmapMemory(VkDevice _device, VkDeviceMemory _memory) { VK_FROM_HANDLE(dzn_device, device, _device); VK_FROM_HANDLE(dzn_device_memory, mem, _memory); if (mem == NULL) return; assert(mem->map_res); mem->map_res->Unmap(0, NULL); mem->map = NULL; mem->map_size = 0; } VKAPI_ATTR VkResult VKAPI_CALL dzn_FlushMappedMemoryRanges(VkDevice _device, uint32_t memoryRangeCount, const VkMappedMemoryRange *pMemoryRanges) { return VK_SUCCESS; } VKAPI_ATTR VkResult VKAPI_CALL dzn_InvalidateMappedMemoryRanges(VkDevice _device, uint32_t memoryRangeCount, const VkMappedMemoryRange *pMemoryRanges) { return VK_SUCCESS; } static void dzn_buffer_destroy(dzn_buffer *buf, const VkAllocationCallbacks *pAllocator) { if (!buf) return; dzn_device *device = container_of(buf->base.device, dzn_device, vk); if (buf->res) buf->res->Release(); vk_object_base_finish(&buf->base); vk_free2(&device->vk.alloc, pAllocator, buf); } static VkResult dzn_buffer_create(dzn_device *device, const VkBufferCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkBuffer *out) { dzn_buffer *buf = (dzn_buffer *) vk_zalloc2(&device->vk.alloc, pAllocator, sizeof(*buf), 8, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); if (!buf) return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY); vk_object_base_init(&device->vk, &buf->base, VK_OBJECT_TYPE_BUFFER); buf->create_flags = pCreateInfo->flags; buf->size = pCreateInfo->size; buf->usage = pCreateInfo->usage; if (buf->usage & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT) buf->size = ALIGN_POT(buf->size, 256); buf->desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; buf->desc.Format = DXGI_FORMAT_UNKNOWN; buf->desc.Alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT; buf->desc.Width = buf->size; buf->desc.Height = 1; buf->desc.DepthOrArraySize = 1; buf->desc.MipLevels = 1; buf->desc.SampleDesc.Count = 1; buf->desc.SampleDesc.Quality = 0; buf->desc.Flags = D3D12_RESOURCE_FLAG_NONE; buf->desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; if (buf->usage & (VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT)) buf->desc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; *out = dzn_buffer_to_handle(buf); return VK_SUCCESS; } DXGI_FORMAT dzn_buffer_get_dxgi_format(VkFormat format) { enum pipe_format pfmt = vk_format_to_pipe_format(format); return dzn_pipe_to_dxgi_format(pfmt); } D3D12_TEXTURE_COPY_LOCATION dzn_buffer_get_copy_loc(const dzn_buffer *buf, VkFormat format, const VkBufferImageCopy2KHR *region, VkImageAspectFlagBits aspect, uint32_t layer) { const uint32_t buffer_row_length = region->bufferRowLength ? region->bufferRowLength : region->imageExtent.width; const uint32_t buffer_image_height = region->bufferImageHeight ? region->bufferImageHeight : region->imageExtent.height; VkFormat plane_format = dzn_image_get_plane_format(format, aspect); enum pipe_format pfmt = vk_format_to_pipe_format(plane_format); uint32_t blksz = util_format_get_blocksize(pfmt); uint32_t blkw = util_format_get_blockwidth(pfmt); uint32_t blkh = util_format_get_blockheight(pfmt); D3D12_TEXTURE_COPY_LOCATION loc = { .pResource = buf->res, .Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT, .PlacedFootprint = { .Footprint = { .Format = dzn_image_get_placed_footprint_format(format, aspect), .Width = region->imageExtent.width, .Height = region->imageExtent.height, .Depth = region->imageExtent.depth, .RowPitch = blksz * DIV_ROUND_UP(buffer_row_length, blkw), }, }, }; uint32_t buffer_layer_stride = loc.PlacedFootprint.Footprint.RowPitch * DIV_ROUND_UP(loc.PlacedFootprint.Footprint.Height, blkh); loc.PlacedFootprint.Offset = region->bufferOffset + (layer * buffer_layer_stride); return loc; } D3D12_TEXTURE_COPY_LOCATION dzn_buffer_get_line_copy_loc(const dzn_buffer *buf, VkFormat format, const VkBufferImageCopy2KHR *region, const D3D12_TEXTURE_COPY_LOCATION *loc, uint32_t y, uint32_t z, uint32_t *start_x) { uint32_t buffer_row_length = region->bufferRowLength ? region->bufferRowLength : region->imageExtent.width; uint32_t buffer_image_height = region->bufferImageHeight ? region->bufferImageHeight : region->imageExtent.height; format = dzn_image_get_plane_format(format, region->imageSubresource.aspectMask); enum pipe_format pfmt = vk_format_to_pipe_format(format); uint32_t blksz = util_format_get_blocksize(pfmt); uint32_t blkw = util_format_get_blockwidth(pfmt); uint32_t blkh = util_format_get_blockheight(pfmt); uint32_t blkd = util_format_get_blockdepth(pfmt); D3D12_TEXTURE_COPY_LOCATION new_loc = *loc; uint32_t buffer_row_stride = DIV_ROUND_UP(buffer_row_length, blkw) * blksz; uint32_t buffer_layer_stride = buffer_row_stride * DIV_ROUND_UP(buffer_image_height, blkh); uint64_t tex_offset = ((y / blkh) * buffer_row_stride) + ((z / blkd) * buffer_layer_stride); uint64_t offset = loc->PlacedFootprint.Offset + tex_offset; uint32_t offset_alignment = D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT; while (offset_alignment % blksz) offset_alignment += D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT; new_loc.PlacedFootprint.Footprint.Height = blkh; new_loc.PlacedFootprint.Footprint.Depth = 1; new_loc.PlacedFootprint.Offset = (offset / offset_alignment) * offset_alignment; *start_x = ((offset % offset_alignment) / blksz) * blkw; new_loc.PlacedFootprint.Footprint.Width = *start_x + region->imageExtent.width; new_loc.PlacedFootprint.Footprint.RowPitch = ALIGN_POT(DIV_ROUND_UP(new_loc.PlacedFootprint.Footprint.Width, blkw) * blksz, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT); return new_loc; } bool dzn_buffer_supports_region_copy(const D3D12_TEXTURE_COPY_LOCATION *loc) { return !(loc->PlacedFootprint.Offset & (D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT - 1)) && !(loc->PlacedFootprint.Footprint.RowPitch & (D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1)); } VKAPI_ATTR VkResult VKAPI_CALL dzn_CreateBuffer(VkDevice device, const VkBufferCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkBuffer *pBuffer) { return dzn_buffer_create(dzn_device_from_handle(device), pCreateInfo, pAllocator, pBuffer); } VKAPI_ATTR void VKAPI_CALL dzn_DestroyBuffer(VkDevice device, VkBuffer buffer, const VkAllocationCallbacks *pAllocator) { dzn_buffer_destroy(dzn_buffer_from_handle(buffer), pAllocator); } VKAPI_ATTR void VKAPI_CALL dzn_GetBufferMemoryRequirements2(VkDevice dev, const VkBufferMemoryRequirementsInfo2 *pInfo, VkMemoryRequirements2 *pMemoryRequirements) { VK_FROM_HANDLE(dzn_device, device, dev); VK_FROM_HANDLE(dzn_buffer, buffer, pInfo->buffer); dzn_physical_device *pdev = container_of(device->vk.physical, dzn_physical_device, vk); /* uh, this is grossly over-estimating things */ uint32_t alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT; VkDeviceSize size = buffer->size; if (buffer->usage & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT) { alignment = MAX2(alignment, D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT); size = ALIGN_POT(size, D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT); } pMemoryRequirements->memoryRequirements.size = size; pMemoryRequirements->memoryRequirements.alignment = alignment; pMemoryRequirements->memoryRequirements.memoryTypeBits = dzn_physical_device_get_mem_type_mask_for_resource(pdev, &buffer->desc); vk_foreach_struct(ext, pMemoryRequirements->pNext) { switch (ext->sType) { case VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS: { VkMemoryDedicatedRequirements *requirements = (VkMemoryDedicatedRequirements *)ext; /* TODO: figure out dedicated allocations */ requirements->prefersDedicatedAllocation = false; requirements->requiresDedicatedAllocation = false; break; } default: dzn_debug_ignored_stype(ext->sType); break; } } #if 0 D3D12_RESOURCE_ALLOCATION_INFO GetResourceAllocationInfo( UINT visibleMask, UINT numResourceDescs, const D3D12_RESOURCE_DESC *pResourceDescs); #endif } VKAPI_ATTR VkResult VKAPI_CALL dzn_BindBufferMemory2(VkDevice _device, uint32_t bindInfoCount, const VkBindBufferMemoryInfo *pBindInfos) { VK_FROM_HANDLE(dzn_device, device, _device); for (uint32_t i = 0; i < bindInfoCount; i++) { assert(pBindInfos[i].sType == VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO); VK_FROM_HANDLE(dzn_device_memory, mem, pBindInfos[i].memory); VK_FROM_HANDLE(dzn_buffer, buffer, pBindInfos[i].buffer); if (FAILED(device->dev->CreatePlacedResource(mem->heap, pBindInfos[i].memoryOffset, &buffer->desc, mem->initial_state, NULL, IID_PPV_ARGS(&buffer->res)))) return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY); } return VK_SUCCESS; } static VkResult dzn_framebuffer_create(dzn_device *device, const VkFramebufferCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkFramebuffer *out) { VK_MULTIALLOC(ma); VK_MULTIALLOC_DECL(&ma, dzn_framebuffer, framebuffer, 1); VK_MULTIALLOC_DECL(&ma, dzn_image_view *, attachments, pCreateInfo->attachmentCount); if (!vk_multialloc_zalloc2(&ma, &device->vk.alloc, pAllocator, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT)) return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY); framebuffer->width = pCreateInfo->width; framebuffer->height = pCreateInfo->height; framebuffer->layers = pCreateInfo->layers; framebuffer->attachments = attachments; framebuffer->attachment_count = pCreateInfo->attachmentCount; for (uint32_t i = 0; i < framebuffer->attachment_count; i++) { VK_FROM_HANDLE(dzn_image_view, iview, pCreateInfo->pAttachments[i]); framebuffer->attachments[i] = iview; } vk_object_base_init(&device->vk, &framebuffer->base, VK_OBJECT_TYPE_FRAMEBUFFER); *out = dzn_framebuffer_to_handle(framebuffer); return VK_SUCCESS; } static void dzn_framebuffer_destroy(dzn_framebuffer *framebuffer, const VkAllocationCallbacks *pAllocator) { if (!framebuffer) return; dzn_device *device = container_of(framebuffer->base.device, dzn_device, vk); vk_object_base_finish(&framebuffer->base); vk_free2(&device->vk.alloc, pAllocator, framebuffer); } VKAPI_ATTR VkResult VKAPI_CALL dzn_CreateFramebuffer(VkDevice device, const VkFramebufferCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkFramebuffer *pFramebuffer) { return dzn_framebuffer_create(dzn_device_from_handle(device), pCreateInfo, pAllocator, pFramebuffer); } VKAPI_ATTR void VKAPI_CALL dzn_DestroyFramebuffer(VkDevice device, VkFramebuffer fb, const VkAllocationCallbacks *pAllocator) { dzn_framebuffer_destroy(dzn_framebuffer_from_handle(fb), pAllocator); } static void dzn_event_destroy(dzn_event *event, const VkAllocationCallbacks *pAllocator) { if (!event) return; dzn_device *device = container_of(event->base.device, dzn_device, vk); if (event->fence) event->fence->Release(); vk_object_base_finish(&event->base); vk_free2(&device->vk.alloc, pAllocator, event); } static VkResult dzn_event_create(dzn_device *device, const VkEventCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkEvent *out) { dzn_event *event = (dzn_event *) vk_zalloc2(&device->vk.alloc, pAllocator, sizeof(*event), 8, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); if (!event) return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY); vk_object_base_init(&device->vk, &event->base, VK_OBJECT_TYPE_EVENT); if (FAILED(device->dev->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&event->fence)))) { dzn_event_destroy(event, pAllocator); return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY); } *out = dzn_event_to_handle(event); return VK_SUCCESS; } VKAPI_ATTR VkResult VKAPI_CALL dzn_CreateEvent(VkDevice device, const VkEventCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkEvent *pEvent) { return dzn_event_create(dzn_device_from_handle(device), pCreateInfo, pAllocator, pEvent); } VKAPI_ATTR void VKAPI_CALL dzn_DestroyEvent(VkDevice device, VkEvent event, const VkAllocationCallbacks *pAllocator) { return dzn_event_destroy(dzn_event_from_handle(event), pAllocator); } VKAPI_ATTR VkResult VKAPI_CALL dzn_ResetEvent(VkDevice dev, VkEvent evt) { VK_FROM_HANDLE(dzn_device, device, dev); VK_FROM_HANDLE(dzn_event, event, evt); if (FAILED(event->fence->Signal(0))) return vk_error(device, VK_ERROR_OUT_OF_DEVICE_MEMORY); return VK_SUCCESS; } VKAPI_ATTR VkResult VKAPI_CALL dzn_SetEvent(VkDevice dev, VkEvent evt) { VK_FROM_HANDLE(dzn_device, device, dev); VK_FROM_HANDLE(dzn_event, event, evt); if (FAILED(event->fence->Signal(1))) return vk_error(device, VK_ERROR_OUT_OF_DEVICE_MEMORY); return VK_SUCCESS; } VKAPI_ATTR VkResult VKAPI_CALL dzn_GetEventStatus(VkDevice device, VkEvent evt) { VK_FROM_HANDLE(dzn_event, event, evt); return event->fence->GetCompletedValue() == 0 ? VK_EVENT_RESET : VK_EVENT_SET; } VKAPI_ATTR void VKAPI_CALL dzn_GetDeviceMemoryCommitment(VkDevice device, VkDeviceMemory memory, VkDeviceSize *pCommittedMemoryInBytes) { VK_FROM_HANDLE(dzn_device_memory, mem, memory); // TODO: find if there's a way to query/track actual heap residency *pCommittedMemoryInBytes = mem->size; } VKAPI_ATTR VkResult VKAPI_CALL dzn_QueueBindSparse(VkQueue queue, uint32_t bindInfoCount, const VkBindSparseInfo *pBindInfo, VkFence fence) { // FIXME: add proper implem dzn_stub(); return VK_SUCCESS; } static D3D12_TEXTURE_ADDRESS_MODE dzn_sampler_translate_addr_mode(VkSamplerAddressMode in) { switch (in) { case VK_SAMPLER_ADDRESS_MODE_REPEAT: return D3D12_TEXTURE_ADDRESS_MODE_WRAP; case VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT: return D3D12_TEXTURE_ADDRESS_MODE_MIRROR; case VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE: return D3D12_TEXTURE_ADDRESS_MODE_CLAMP; case VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER: return D3D12_TEXTURE_ADDRESS_MODE_BORDER; default: unreachable("Invalid address mode"); } } static void dzn_sampler_destroy(dzn_sampler *sampler, const VkAllocationCallbacks *pAllocator) { if (!sampler) return; dzn_device *device = container_of(sampler->base.device, dzn_device, vk); vk_object_base_finish(&sampler->base); vk_free2(&device->vk.alloc, pAllocator, sampler); } static VkResult dzn_sampler_create(dzn_device *device, const VkSamplerCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkSampler *out) { dzn_sampler *sampler = (dzn_sampler *) vk_zalloc2(&device->vk.alloc, pAllocator, sizeof(*sampler), 8, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); if (!sampler) return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY); vk_object_base_init(&device->vk, &sampler->base, VK_OBJECT_TYPE_SAMPLER); const VkSamplerCustomBorderColorCreateInfoEXT *pBorderColor = (const VkSamplerCustomBorderColorCreateInfoEXT *) vk_find_struct_const(pCreateInfo->pNext, SAMPLER_CUSTOM_BORDER_COLOR_CREATE_INFO_EXT); /* TODO: have a sampler pool to allocate shader-invisible descs which we * can copy to the desc_set when UpdateDescriptorSets() is called. */ sampler->desc.Filter = dzn_translate_sampler_filter(pCreateInfo); sampler->desc.AddressU = dzn_sampler_translate_addr_mode(pCreateInfo->addressModeU); sampler->desc.AddressV = dzn_sampler_translate_addr_mode(pCreateInfo->addressModeV); sampler->desc.AddressW = dzn_sampler_translate_addr_mode(pCreateInfo->addressModeW); sampler->desc.MipLODBias = pCreateInfo->mipLodBias; sampler->desc.MaxAnisotropy = pCreateInfo->maxAnisotropy; sampler->desc.MinLOD = pCreateInfo->minLod; sampler->desc.MaxLOD = pCreateInfo->maxLod; if (pCreateInfo->compareEnable) sampler->desc.ComparisonFunc = dzn_translate_compare_op(pCreateInfo->compareOp); bool reads_border_color = pCreateInfo->addressModeU == VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER || pCreateInfo->addressModeV == VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER || pCreateInfo->addressModeW == VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER; if (reads_border_color) { switch (pCreateInfo->borderColor) { case VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK: case VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK: sampler->desc.BorderColor[0] = 0.0f; sampler->desc.BorderColor[1] = 0.0f; sampler->desc.BorderColor[2] = 0.0f; sampler->desc.BorderColor[3] = pCreateInfo->borderColor == VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK ? 0.0f : 1.0f; sampler->static_border_color = pCreateInfo->borderColor == VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK ? D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK : D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK; break; case VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE: sampler->desc.BorderColor[0] = sampler->desc.BorderColor[1] = 1.0f; sampler->desc.BorderColor[2] = sampler->desc.BorderColor[3] = 1.0f; sampler->static_border_color = D3D12_STATIC_BORDER_COLOR_OPAQUE_WHITE; break; case VK_BORDER_COLOR_FLOAT_CUSTOM_EXT: sampler->static_border_color = (D3D12_STATIC_BORDER_COLOR)-1; for (unsigned i = 0; i < ARRAY_SIZE(sampler->desc.BorderColor); i++) sampler->desc.BorderColor[i] = pBorderColor->customBorderColor.float32[i]; break; case VK_BORDER_COLOR_INT_TRANSPARENT_BLACK: case VK_BORDER_COLOR_INT_OPAQUE_BLACK: case VK_BORDER_COLOR_INT_OPAQUE_WHITE: case VK_BORDER_COLOR_INT_CUSTOM_EXT: /* FIXME: sampling from integer textures is not supported yet. */ sampler->static_border_color = (D3D12_STATIC_BORDER_COLOR)-1; break; default: unreachable("Unsupported border color"); } } *out = dzn_sampler_to_handle(sampler); return VK_SUCCESS; } VKAPI_ATTR VkResult VKAPI_CALL dzn_CreateSampler(VkDevice device, const VkSamplerCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkSampler *pSampler) { return dzn_sampler_create(dzn_device_from_handle(device), pCreateInfo, pAllocator, pSampler); } VKAPI_ATTR void VKAPI_CALL dzn_DestroySampler(VkDevice device, VkSampler sampler, const VkAllocationCallbacks *pAllocator) { return dzn_sampler_destroy(dzn_sampler_from_handle(sampler), pAllocator); }