vkd3d-proton/libs/vkd3d/device.c

6792 lines
286 KiB
C
Raw Normal View History

/*
* Copyright 2016 Józef Kucia for CodeWeavers
*
2017-06-16 20:05:54 +01:00
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
2017-06-16 20:05:54 +01:00
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
2017-06-16 20:05:54 +01:00
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#define VKD3D_DBG_CHANNEL VKD3D_DBG_CHANNEL_API
#include "vkd3d_private.h"
#include "vkd3d_sonames.h"
#include "vkd3d_descriptor_debug.h"
#include "vkd3d_platform.h"
#ifdef VKD3D_ENABLE_RENDERDOC
#include "vkd3d_renderdoc.h"
#endif
static uint32_t vkd3d_get_vk_version(void)
{
int major, minor, patch;
vkd3d_parse_version(PACKAGE_VERSION, &major, &minor, &patch);
INFO("vkd3d-proton - applicationVersion: %d.%d.%d.\n", major, minor, patch);
return VK_MAKE_VERSION(major, minor, patch);
}
struct vkd3d_optional_extension_info
{
const char *extension_name;
ptrdiff_t vulkan_info_offset;
uint64_t required_config_flags;
};
#define VK_EXTENSION(name, member) \
{VK_ ## name ## _EXTENSION_NAME, offsetof(struct vkd3d_vulkan_info, member), 0}
#define VK_EXTENSION_COND(name, member, required_flags) \
{VK_ ## name ## _EXTENSION_NAME, offsetof(struct vkd3d_vulkan_info, member), required_flags}
static const struct vkd3d_optional_extension_info optional_instance_extensions[] =
{
/* EXT extensions */
VK_EXTENSION_COND(EXT_DEBUG_UTILS, EXT_debug_utils, VKD3D_CONFIG_FLAG_DEBUG_UTILS),
};
static const struct vkd3d_optional_extension_info optional_device_extensions[] =
{
/* KHR extensions */
VK_EXTENSION(KHR_BUFFER_DEVICE_ADDRESS, KHR_buffer_device_address),
VK_EXTENSION(KHR_DRAW_INDIRECT_COUNT, KHR_draw_indirect_count),
VK_EXTENSION(KHR_IMAGE_FORMAT_LIST, KHR_image_format_list),
VK_EXTENSION(KHR_PUSH_DESCRIPTOR, KHR_push_descriptor),
VK_EXTENSION(KHR_TIMELINE_SEMAPHORE, KHR_timeline_semaphore),
VK_EXTENSION(KHR_SHADER_FLOAT16_INT8, KHR_shader_float16_int8),
VK_EXTENSION(KHR_SHADER_SUBGROUP_EXTENDED_TYPES, KHR_shader_subgroup_extended_types),
VK_EXTENSION_COND(KHR_RAY_TRACING_PIPELINE, KHR_ray_tracing_pipeline, VKD3D_CONFIG_FLAG_DXR),
VK_EXTENSION_COND(KHR_ACCELERATION_STRUCTURE, KHR_acceleration_structure, VKD3D_CONFIG_FLAG_DXR),
VK_EXTENSION_COND(KHR_DEFERRED_HOST_OPERATIONS, KHR_deferred_host_operations, VKD3D_CONFIG_FLAG_DXR),
VK_EXTENSION_COND(KHR_PIPELINE_LIBRARY, KHR_pipeline_library, VKD3D_CONFIG_FLAG_DXR),
VK_EXTENSION_COND(KHR_RAY_QUERY, KHR_ray_query, VKD3D_CONFIG_FLAG_DXR11),
VK_EXTENSION_COND(KHR_RAY_TRACING_MAINTENANCE_1, KHR_ray_tracing_maintenance1, VKD3D_CONFIG_FLAG_DXR11),
VK_EXTENSION(KHR_SPIRV_1_4, KHR_spirv_1_4),
VK_EXTENSION(KHR_SHADER_FLOAT_CONTROLS, KHR_shader_float_controls),
VK_EXTENSION(KHR_FRAGMENT_SHADING_RATE, KHR_fragment_shading_rate),
/* Only required to silence validation errors. */
VK_EXTENSION(KHR_CREATE_RENDERPASS_2, KHR_create_renderpass2),
VK_EXTENSION(KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE, KHR_sampler_mirror_clamp_to_edge),
VK_EXTENSION(KHR_SEPARATE_DEPTH_STENCIL_LAYOUTS, KHR_separate_depth_stencil_layouts),
VK_EXTENSION(KHR_SHADER_INTEGER_DOT_PRODUCT, KHR_shader_integer_dot_product),
VK_EXTENSION(KHR_FORMAT_FEATURE_FLAGS_2, KHR_format_feature_flags2),
VK_EXTENSION(KHR_SHADER_ATOMIC_INT64, KHR_shader_atomic_int64),
VK_EXTENSION(KHR_BIND_MEMORY_2, KHR_bind_memory2),
VK_EXTENSION(KHR_COPY_COMMANDS_2, KHR_copy_commands2),
VK_EXTENSION(KHR_DYNAMIC_RENDERING, KHR_dynamic_rendering),
/* Only required to silence validation errors. */
VK_EXTENSION(KHR_DEPTH_STENCIL_RESOLVE, KHR_depth_stencil_resolve),
VK_EXTENSION(KHR_DRIVER_PROPERTIES, KHR_driver_properties),
VK_EXTENSION(KHR_UNIFORM_BUFFER_STANDARD_LAYOUT, KHR_uniform_buffer_standard_layout),
VK_EXTENSION(KHR_MAINTENANCE_4, KHR_maintenance4),
VK_EXTENSION(KHR_FRAGMENT_SHADER_BARYCENTRIC, KHR_fragment_shader_barycentric),
#ifdef _WIN32
VK_EXTENSION(KHR_EXTERNAL_MEMORY_WIN32, KHR_external_memory_win32),
VK_EXTENSION(KHR_EXTERNAL_SEMAPHORE_WIN32, KHR_external_semaphore_win32),
#endif
/* EXT extensions */
VK_EXTENSION(EXT_CALIBRATED_TIMESTAMPS, EXT_calibrated_timestamps),
VK_EXTENSION(EXT_CONDITIONAL_RENDERING, EXT_conditional_rendering),
VK_EXTENSION(EXT_CONSERVATIVE_RASTERIZATION, EXT_conservative_rasterization),
VK_EXTENSION(EXT_CUSTOM_BORDER_COLOR, EXT_custom_border_color),
VK_EXTENSION(EXT_DEPTH_CLIP_ENABLE, EXT_depth_clip_enable),
VK_EXTENSION(EXT_DESCRIPTOR_INDEXING, EXT_descriptor_indexing),
VK_EXTENSION(EXT_IMAGE_VIEW_MIN_LOD, EXT_image_view_min_lod),
VK_EXTENSION(EXT_INLINE_UNIFORM_BLOCK, EXT_inline_uniform_block),
VK_EXTENSION(EXT_ROBUSTNESS_2, EXT_robustness2),
VK_EXTENSION(EXT_SAMPLER_FILTER_MINMAX, EXT_sampler_filter_minmax),
VK_EXTENSION(EXT_SHADER_DEMOTE_TO_HELPER_INVOCATION, EXT_shader_demote_to_helper_invocation),
VK_EXTENSION(EXT_SHADER_STENCIL_EXPORT, EXT_shader_stencil_export),
VK_EXTENSION(EXT_SHADER_VIEWPORT_INDEX_LAYER, EXT_shader_viewport_index_layer),
VK_EXTENSION(EXT_SUBGROUP_SIZE_CONTROL, EXT_subgroup_size_control),
VK_EXTENSION(EXT_TEXEL_BUFFER_ALIGNMENT, EXT_texel_buffer_alignment),
VK_EXTENSION(EXT_TRANSFORM_FEEDBACK, EXT_transform_feedback),
VK_EXTENSION(EXT_VERTEX_ATTRIBUTE_DIVISOR, EXT_vertex_attribute_divisor),
VK_EXTENSION(EXT_EXTENDED_DYNAMIC_STATE, EXT_extended_dynamic_state),
VK_EXTENSION(EXT_EXTENDED_DYNAMIC_STATE_2, EXT_extended_dynamic_state2),
VK_EXTENSION(EXT_EXTERNAL_MEMORY_HOST, EXT_external_memory_host),
VK_EXTENSION(EXT_4444_FORMATS, EXT_4444_formats),
VK_EXTENSION(EXT_SHADER_IMAGE_ATOMIC_INT64, EXT_shader_image_atomic_int64),
VK_EXTENSION(EXT_SCALAR_BLOCK_LAYOUT, EXT_scalar_block_layout),
VK_EXTENSION(EXT_PIPELINE_CREATION_FEEDBACK, EXT_pipeline_creation_feedback),
/* AMD extensions */
VK_EXTENSION(AMD_BUFFER_MARKER, AMD_buffer_marker),
VK_EXTENSION(AMD_DEVICE_COHERENT_MEMORY, AMD_device_coherent_memory),
VK_EXTENSION(AMD_SHADER_CORE_PROPERTIES, AMD_shader_core_properties),
VK_EXTENSION(AMD_SHADER_CORE_PROPERTIES_2, AMD_shader_core_properties2),
/* NV extensions */
VK_EXTENSION(NV_SHADER_SM_BUILTINS, NV_shader_sm_builtins),
VK_EXTENSION(NVX_BINARY_IMPORT, NVX_binary_import),
VK_EXTENSION(NVX_IMAGE_VIEW_HANDLE, NVX_image_view_handle),
VK_EXTENSION(NV_FRAGMENT_SHADER_BARYCENTRIC, NV_fragment_shader_barycentric),
VK_EXTENSION(NV_COMPUTE_SHADER_DERIVATIVES, NV_compute_shader_derivatives),
VK_EXTENSION_COND(NV_DEVICE_DIAGNOSTIC_CHECKPOINTS, NV_device_diagnostic_checkpoints, VKD3D_CONFIG_FLAG_BREADCRUMBS),
VK_EXTENSION(NV_DEVICE_GENERATED_COMMANDS, NV_device_generated_commands),
/* VALVE extensions */
VK_EXTENSION(VALVE_MUTABLE_DESCRIPTOR_TYPE, VALVE_mutable_descriptor_type),
VK_EXTENSION(VALVE_DESCRIPTOR_SET_HOST_MAPPING, VALVE_descriptor_set_host_mapping),
};
static unsigned int get_spec_version(const VkExtensionProperties *extensions,
unsigned int count, const char *extension_name)
{
unsigned int i;
for (i = 0; i < count; ++i)
{
if (!strcmp(extensions[i].extensionName, extension_name))
return extensions[i].specVersion;
}
return 0;
}
static bool is_extension_disabled(const char *extension_name)
{
char disabled_extensions[VKD3D_PATH_MAX];
if (!vkd3d_get_env_var("VKD3D_DISABLE_EXTENSIONS", disabled_extensions, sizeof(disabled_extensions)))
return false;
return vkd3d_debug_list_has_member(disabled_extensions, extension_name);
}
static bool has_extension(const VkExtensionProperties *extensions,
unsigned int count, const char *extension_name)
{
unsigned int i;
for (i = 0; i < count; ++i)
{
if (is_extension_disabled(extension_name))
{
WARN("Extension %s is disabled.\n", debugstr_a(extension_name));
continue;
}
if (!strcmp(extensions[i].extensionName, extension_name))
return true;
}
return false;
}
static unsigned int vkd3d_check_extensions(const VkExtensionProperties *extensions, unsigned int count,
const char * const *required_extensions, unsigned int required_extension_count,
const struct vkd3d_optional_extension_info *optional_extensions, unsigned int optional_extension_count,
const char * const *user_extensions, unsigned int user_extension_count,
const char * const *optional_user_extensions, unsigned int optional_user_extension_count,
bool *user_extension_supported, struct vkd3d_vulkan_info *vulkan_info, const char *extension_type)
{
unsigned int extension_count = 0;
unsigned int i;
for (i = 0; i < required_extension_count; ++i)
{
if (!has_extension(extensions, count, required_extensions[i]))
ERR("Required %s extension %s is not supported.\n",
extension_type, debugstr_a(required_extensions[i]));
++extension_count;
}
for (i = 0; i < optional_extension_count; ++i)
{
const char *extension_name = optional_extensions[i].extension_name;
uint64_t required_flags = optional_extensions[i].required_config_flags;
bool has_required_flags = (vkd3d_config_flags & required_flags) == required_flags;
ptrdiff_t offset = optional_extensions[i].vulkan_info_offset;
bool *supported = (void *)((uintptr_t)vulkan_info + offset);
if (!has_required_flags)
continue;
if ((*supported = has_extension(extensions, count, extension_name)))
{
TRACE("Found %s extension.\n", debugstr_a(extension_name));
++extension_count;
}
}
for (i = 0; i < user_extension_count; ++i)
{
if (!has_extension(extensions, count, user_extensions[i]))
ERR("Required user %s extension %s is not supported.\n",
extension_type, debugstr_a(user_extensions[i]));
++extension_count;
}
assert(!optional_user_extension_count || user_extension_supported);
for (i = 0; i < optional_user_extension_count; ++i)
{
if (has_extension(extensions, count, optional_user_extensions[i]))
{
user_extension_supported[i] = true;
++extension_count;
}
else
{
user_extension_supported[i] = false;
WARN("Optional user %s extension %s is not supported.\n",
extension_type, debugstr_a(optional_user_extensions[i]));
}
}
return extension_count;
}
static unsigned int vkd3d_append_extension(const char *extensions[],
unsigned int extension_count, const char *extension_name)
{
unsigned int i;
/* avoid duplicates */
for (i = 0; i < extension_count; ++i)
{
if (!strcmp(extensions[i], extension_name))
return extension_count;
}
extensions[extension_count++] = extension_name;
return extension_count;
}
static unsigned int vkd3d_enable_extensions(const char *extensions[],
const char * const *required_extensions, unsigned int required_extension_count,
const struct vkd3d_optional_extension_info *optional_extensions, unsigned int optional_extension_count,
const char * const *user_extensions, unsigned int user_extension_count,
const char * const *optional_user_extensions, unsigned int optional_user_extension_count,
bool *user_extension_supported, const struct vkd3d_vulkan_info *vulkan_info)
{
unsigned int extension_count = 0;
unsigned int i;
for (i = 0; i < required_extension_count; ++i)
{
extensions[extension_count++] = required_extensions[i];
}
for (i = 0; i < optional_extension_count; ++i)
{
ptrdiff_t offset = optional_extensions[i].vulkan_info_offset;
const bool *supported = (void *)((uintptr_t)vulkan_info + offset);
if (*supported)
extensions[extension_count++] = optional_extensions[i].extension_name;
}
for (i = 0; i < user_extension_count; ++i)
{
extension_count = vkd3d_append_extension(extensions, extension_count, user_extensions[i]);
}
assert(!optional_user_extension_count || user_extension_supported);
for (i = 0; i < optional_user_extension_count; ++i)
{
if (!user_extension_supported[i])
continue;
extension_count = vkd3d_append_extension(extensions, extension_count, optional_user_extensions[i]);
}
return extension_count;
}
static bool vkd3d_remove_extension(const char *to_remove, const char *extensions[], uint32_t *extension_count)
{
uint32_t i;
for (i = 0; i < *extension_count; i++)
{
if (strcmp(to_remove, extensions[i]) != 0)
continue;
WARN("Removing %s extension from the array.\n", to_remove);
if (i < (*extension_count - 1))
extensions[i] = extensions[*extension_count - 1];
(*extension_count)--;
return true;
}
return false;
}
static bool vkd3d_disable_nvx_extensions(struct d3d12_device *device, const char *extensions[], uint32_t *enabled_extension_count)
{
struct vkd3d_vulkan_info *vk_info = &device->vk_info;
bool disabled = false;
unsigned int i;
static const struct vkd3d_optional_extension_info nvx_extensions[] =
{
VK_EXTENSION(NVX_BINARY_IMPORT, NVX_binary_import),
VK_EXTENSION(NVX_IMAGE_VIEW_HANDLE, NVX_image_view_handle),
};
for (i = 0; i < ARRAY_SIZE(nvx_extensions); ++i)
{
const char *extension_name = nvx_extensions[i].extension_name;
bool *supported = (void *)((uintptr_t)vk_info + nvx_extensions[i].vulkan_info_offset);
if (vkd3d_remove_extension(extension_name, extensions, enabled_extension_count))
{
*supported = false;
disabled = true;
}
}
return disabled;
}
static HRESULT vkd3d_init_instance_caps(struct vkd3d_instance *instance,
const struct vkd3d_instance_create_info *create_info,
uint32_t *instance_extension_count, bool *user_extension_supported)
{
const struct vkd3d_vk_global_procs *vk_procs = &instance->vk_global_procs;
struct vkd3d_vulkan_info *vulkan_info = &instance->vk_info;
VkExtensionProperties *vk_extensions;
uint32_t count;
VkResult vr;
memset(vulkan_info, 0, sizeof(*vulkan_info));
*instance_extension_count = 0;
if ((vr = vk_procs->vkEnumerateInstanceExtensionProperties(NULL, &count, NULL)) < 0)
2016-10-13 12:50:36 +01:00
{
ERR("Failed to enumerate instance extensions, vr %d.\n", vr);
return hresult_from_vk_result(vr);
}
if (!count)
return S_OK;
if (!(vk_extensions = vkd3d_calloc(count, sizeof(*vk_extensions))))
return E_OUTOFMEMORY;
2016-10-13 12:50:36 +01:00
TRACE("Enumerating %u instance extensions.\n", count);
if ((vr = vk_procs->vkEnumerateInstanceExtensionProperties(NULL, &count, vk_extensions)) < 0)
{
ERR("Failed to enumerate instance extensions, vr %d.\n", vr);
vkd3d_free(vk_extensions);
return hresult_from_vk_result(vr);
}
*instance_extension_count = vkd3d_check_extensions(vk_extensions, count, NULL, 0,
optional_instance_extensions, ARRAY_SIZE(optional_instance_extensions),
create_info->instance_extensions,
create_info->instance_extension_count,
create_info->optional_instance_extensions,
create_info->optional_instance_extension_count,
user_extension_supported, vulkan_info, "instance");
vkd3d_free(vk_extensions);
return S_OK;
}
static HRESULT vkd3d_init_vk_global_procs(struct vkd3d_instance *instance,
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr)
{
HRESULT hr;
if (!vkGetInstanceProcAddr)
{
if (!(instance->libvulkan = vkd3d_dlopen(SONAME_LIBVULKAN)))
{
ERR("Failed to load libvulkan: %s.\n", vkd3d_dlerror());
return E_FAIL;
}
if (!(vkGetInstanceProcAddr = vkd3d_dlsym(instance->libvulkan, "vkGetInstanceProcAddr")))
{
ERR("Could not load function pointer for vkGetInstanceProcAddr().\n");
vkd3d_dlclose(instance->libvulkan);
instance->libvulkan = NULL;
return E_FAIL;
}
}
else
{
instance->libvulkan = NULL;
}
if (FAILED(hr = vkd3d_load_vk_global_procs(&instance->vk_global_procs, vkGetInstanceProcAddr)))
{
if (instance->libvulkan)
vkd3d_dlclose(instance->libvulkan);
instance->libvulkan = NULL;
return hr;
}
return S_OK;
}
static VkBool32 VKAPI_PTR vkd3d_debug_messenger_callback(
VkDebugUtilsMessageSeverityFlagBitsEXT message_severity,
VkDebugUtilsMessageTypeFlagsEXT message_types,
const VkDebugUtilsMessengerCallbackDataEXT *callback_data,
void *userdata)
{
/* Avoid some useless validation warnings which don't contribute much.
* - Map memory, likely validation layer bug due to memory alloc flags.
* - Pipeline layout limits on NV which are not relevant here.
* - SPV_EXT_buffer_device_address shenanigans (need to fix glslang).
* - Sample count mismatch in fallback copy shaders.
*/
unsigned int i;
static const uint32_t ignored_ids[] = {
0xc05b3a9du,
0x2864340eu,
0xbfcfaec2u,
0x96f03c1cu,
0x8189c842u,
0x3d492883u,
0x1608dec0u,
};
for (i = 0; i < ARRAY_SIZE(ignored_ids); i++)
if ((uint32_t)callback_data->messageIdNumber == ignored_ids[i])
return VK_FALSE;
if (message_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT)
ERR("%s\n", debugstr_a(callback_data->pMessage));
else if (message_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT)
WARN("%s\n", debugstr_a(callback_data->pMessage));
(void)userdata;
(void)message_types;
return VK_FALSE;
}
static void vkd3d_init_debug_messenger_callback(struct vkd3d_instance *instance)
{
const struct vkd3d_vk_instance_procs *vk_procs = &instance->vk_procs;
VkDebugUtilsMessengerCreateInfoEXT callback_info;
VkInstance vk_instance = instance->vk_instance;
VkDebugUtilsMessengerEXT callback;
VkResult vr;
callback_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
callback_info.pNext = NULL;
callback_info.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT;
callback_info.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT;
callback_info.pfnUserCallback = vkd3d_debug_messenger_callback;
callback_info.pUserData = NULL;
callback_info.flags = 0;
if ((vr = VK_CALL(vkCreateDebugUtilsMessengerEXT(vk_instance, &callback_info, NULL, &callback)) < 0))
{
WARN("Failed to create debug report callback, vr %d.\n", vr);
return;
}
instance->vk_debug_callback = callback;
}
/* Could be a flag style enum if needed. */
enum vkd3d_application_feature_override
{
VKD3D_APPLICATION_FEATURE_OVERRIDE_NONE = 0,
VKD3D_APPLICATION_FEATURE_OVERRIDE_PROMOTE_DXR_TO_ULTIMATE,
};
static enum vkd3d_application_feature_override vkd3d_application_feature_override;
uint64_t vkd3d_config_flags;
struct vkd3d_shader_quirk_info vkd3d_shader_quirk_info;
struct vkd3d_instance_application_meta
{
enum vkd3d_string_compare_mode mode;
const char *name;
uint64_t global_flags_add;
uint64_t global_flags_remove;
enum vkd3d_application_feature_override override;
};
static const struct vkd3d_instance_application_meta application_override[] = {
/* MSVC fails to compile empty array. */
{ VKD3D_STRING_COMPARE_EXACT, "GravityMark.exe", VKD3D_CONFIG_FLAG_FORCE_MINIMUM_SUBGROUP_SIZE, 0 },
{ VKD3D_STRING_COMPARE_EXACT, "Deathloop.exe", VKD3D_CONFIG_FLAG_IGNORE_RTV_HOST_VISIBLE, 0 },
/* Halo Infinite (1240440).
* Game relies on NON_ZEROED committed UAVs to be cleared to zero on allocation.
* This works okay with zerovram on first game boot, but not later, since this memory is guaranteed to be recycled.
* Game also relies on indirectly modifying CBV root descriptors, which means we are forced to rely on RAW_VA_CBV. */
{ VKD3D_STRING_COMPARE_EXACT, "HaloInfinite.exe",
VKD3D_CONFIG_FLAG_ZERO_MEMORY_WORKAROUNDS_COMMITTED_BUFFER_UAV | VKD3D_CONFIG_FLAG_FORCE_RAW_VA_CBV |
VKD3D_CONFIG_FLAG_USE_HOST_IMPORT_FALLBACK, 0 },
/* Shadow of the Tomb Raider (750920).
* Invariant workarounds actually cause more issues than they resolve on NV.
* RADV already has workarounds by default.
* FIXME: The proper workaround will be a workaround which force-emits mul + add + precise. The vertex shaders
* are broken enough that normal invariance is not enough. */
{ VKD3D_STRING_COMPARE_EXACT, "SOTTR.exe", VKD3D_CONFIG_FLAG_FORCE_NO_INVARIANT_POSITION, 0 },
/* Elden Ring (1245620).
* Game is really churny on committed memory allocations, and does not use NOT_ZEROED. Clearing works causes bubbles.
* It seems to work just fine however to skip the clears. */
{ VKD3D_STRING_COMPARE_EXACT, "eldenring.exe",
VKD3D_CONFIG_FLAG_MEMORY_ALLOCATOR_SKIP_CLEAR | VKD3D_CONFIG_FLAG_PIPELINE_LIBRARY_IGNORE_MISMATCH_DRIVER |
VKD3D_CONFIG_FLAG_RECYCLE_COMMAND_POOLS, 0 },
/* Serious Sam 4 (257420).
* Invariant workarounds cause graphical glitches when rendering foliage on NV. */
{ VKD3D_STRING_COMPARE_EXACT, "Sam4.exe", VKD3D_CONFIG_FLAG_FORCE_NO_INVARIANT_POSITION, 0 },
/* Cyberpunk 2077 (1091500). */
{ VKD3D_STRING_COMPARE_EXACT, "Cyberpunk2077.exe", VKD3D_CONFIG_FLAG_ALLOW_SBT_COLLECTION, 0 },
/* Resident Evil: Village (1196590).
* Game relies on mesh + sampler feedback to be exposed to use DXR.
* Likely used as a proxy for Turing+ to avoid potential software fallbacks on Pascal. */
{ VKD3D_STRING_COMPARE_EXACT, "re8.exe",
VKD3D_CONFIG_FLAG_FORCE_NATIVE_FP16, 0, VKD3D_APPLICATION_FEATURE_OVERRIDE_PROMOTE_DXR_TO_ULTIMATE },
/* Resident Evil 2 remake (883710). Same as RE: Village. */
{ VKD3D_STRING_COMPARE_EXACT, "re2.exe",
VKD3D_CONFIG_FLAG_FORCE_NATIVE_FP16, 0, VKD3D_APPLICATION_FEATURE_OVERRIDE_PROMOTE_DXR_TO_ULTIMATE },
{ VKD3D_STRING_COMPARE_EXACT, "re3.exe",
VKD3D_CONFIG_FLAG_FORCE_NATIVE_FP16, 0, VKD3D_APPLICATION_FEATURE_OVERRIDE_PROMOTE_DXR_TO_ULTIMATE },
{ VKD3D_STRING_COMPARE_EXACT, "re7.exe",
VKD3D_CONFIG_FLAG_FORCE_NATIVE_FP16, 0, VKD3D_APPLICATION_FEATURE_OVERRIDE_PROMOTE_DXR_TO_ULTIMATE },
{ VKD3D_STRING_COMPARE_NEVER, NULL, 0, 0 }
};
struct vkd3d_shader_quirk_meta
{
enum vkd3d_string_compare_mode mode;
const char *name;
const struct vkd3d_shader_quirk_info *info;
};
static const struct vkd3d_shader_quirk_hash ue4_hashes[] = {
{ 0x08a323ee81c1e393ull, VKD3D_SHADER_QUIRK_FORCE_EXPLICIT_LOD_IN_CONTROL_FLOW },
{ 0x75dcbd76ee898815ull, VKD3D_SHADER_QUIRK_FORCE_EXPLICIT_LOD_IN_CONTROL_FLOW },
{ 0x6c37b5a66059b751ull, VKD3D_SHADER_QUIRK_FORCE_EXPLICIT_LOD_IN_CONTROL_FLOW },
};
static const struct vkd3d_shader_quirk_info ue4_quirks = {
ue4_hashes, ARRAY_SIZE(ue4_hashes), 0,
};
static const struct vkd3d_shader_quirk_info f1_2019_2020_quirks = {
NULL, 0, VKD3D_SHADER_QUIRK_FORCE_TGSM_BARRIERS,
};
static const struct vkd3d_shader_quirk_meta application_shader_quirks[] = {
/* Unreal Engine 4 */
{ VKD3D_STRING_COMPARE_ENDS_WITH, "-Shipping.exe", &ue4_quirks },
/* F1 2020 (1080110) */
{ VKD3D_STRING_COMPARE_EXACT, "F1_2020_dx12.exe", &f1_2019_2020_quirks },
/* F1 2019 (928600) */
{ VKD3D_STRING_COMPARE_EXACT, "F1_2019_dx12.exe", &f1_2019_2020_quirks },
/* MSVC fails to compile empty array. */
{ VKD3D_STRING_COMPARE_NEVER, NULL, NULL },
};
static void vkd3d_instance_apply_application_workarounds(void)
{
char app[VKD3D_PATH_MAX];
size_t i;
if (!vkd3d_get_program_name(app))
return;
for (i = 0; i < ARRAY_SIZE(application_override); i++)
{
if (vkd3d_string_compare(application_override[i].mode, app, application_override[i].name))
{
vkd3d_config_flags |= application_override[i].global_flags_add;
vkd3d_config_flags &= ~application_override[i].global_flags_remove;
INFO("Detected game %s, adding config 0x%"PRIx64", removing masks 0x%"PRIx64".\n",
app, application_override[i].global_flags_add, application_override[i].global_flags_remove);
vkd3d_application_feature_override = application_override[i].override;
break;
}
}
for (i = 0; i < ARRAY_SIZE(application_shader_quirks); i++)
{
if (vkd3d_string_compare(application_shader_quirks[i].mode, app, application_shader_quirks[i].name))
{
vkd3d_shader_quirk_info = *application_shader_quirks[i].info;
INFO("Detected game %s, adding shader quirks for specific shaders.\n", app);
break;
}
}
}
static void vkd3d_instance_deduce_config_flags_from_environment(void)
{
char env[VKD3D_PATH_MAX];
cache: Implement an on-disk pipeline library. With VKD3D_SHADER_CACHE_PATH, we can add automatic serialization of pipeline blobs to disk, even for games which do not make any use of GetCachedBlob of ID3D12PipelineLibrary interfaces. Most applications expect drivers to have some kind of internal caching. This is implemented as a system where a disk thread will manage a private ID3D12PipelineLibrary, and new PSOs are automatically committed to this library. PSO creation will also consult this internal pipeline library if applications do not provide their own blob. The strategy for updating the cache is based on a read-only cache which is mmaped from disk, with an exclusive write-only portion for new blobs, which ensures some degree of safety if there are multiple concurrent processes using the same cache. The memory layout of the disk cache is optimized to be very efficient for appending new blobs, just simple fwrites + fflush. The format is also robust against sliced files, which solves the problem where applications tear down without destroying the D3D12 device properly. This structure is very similar to Fossilize, and in fact the idea is to move towards actually using the Fossilize format directly later. This implementation prepares us for this scenario where e.g. Steam could potentially manage the vkd3d-proton cache. The main complication in this implementation is that we have to merge the read-only and write caches. Signed-off-by: Hans-Kristian Arntzen <post@arntzen-software.no>
2022-02-02 13:12:20 +00:00
if (vkd3d_get_env_var("VKD3D_SHADER_OVERRIDE", env, sizeof(env)) ||
vkd3d_get_env_var("VKD3D_SHADER_DUMP_PATH", env, sizeof(env)))
{
INFO("VKD3D_SHADER_OVERRIDE or VKD3D_SHADER_DUMP_PATH is used, pipeline_library_ignore_spirv option is enforced.\n");
vkd3d_config_flags |= VKD3D_CONFIG_FLAG_PIPELINE_LIBRARY_IGNORE_SPIRV;
}
cache: Implement an on-disk pipeline library. With VKD3D_SHADER_CACHE_PATH, we can add automatic serialization of pipeline blobs to disk, even for games which do not make any use of GetCachedBlob of ID3D12PipelineLibrary interfaces. Most applications expect drivers to have some kind of internal caching. This is implemented as a system where a disk thread will manage a private ID3D12PipelineLibrary, and new PSOs are automatically committed to this library. PSO creation will also consult this internal pipeline library if applications do not provide their own blob. The strategy for updating the cache is based on a read-only cache which is mmaped from disk, with an exclusive write-only portion for new blobs, which ensures some degree of safety if there are multiple concurrent processes using the same cache. The memory layout of the disk cache is optimized to be very efficient for appending new blobs, just simple fwrites + fflush. The format is also robust against sliced files, which solves the problem where applications tear down without destroying the D3D12 device properly. This structure is very similar to Fossilize, and in fact the idea is to move towards actually using the Fossilize format directly later. This implementation prepares us for this scenario where e.g. Steam could potentially manage the vkd3d-proton cache. The main complication in this implementation is that we have to merge the read-only and write caches. Signed-off-by: Hans-Kristian Arntzen <post@arntzen-software.no>
2022-02-02 13:12:20 +00:00
vkd3d_get_env_var("VKD3D_SHADER_CACHE_PATH", env, sizeof(env));
if (strcmp(env, "0") == 0)
cache: Implement an on-disk pipeline library. With VKD3D_SHADER_CACHE_PATH, we can add automatic serialization of pipeline blobs to disk, even for games which do not make any use of GetCachedBlob of ID3D12PipelineLibrary interfaces. Most applications expect drivers to have some kind of internal caching. This is implemented as a system where a disk thread will manage a private ID3D12PipelineLibrary, and new PSOs are automatically committed to this library. PSO creation will also consult this internal pipeline library if applications do not provide their own blob. The strategy for updating the cache is based on a read-only cache which is mmaped from disk, with an exclusive write-only portion for new blobs, which ensures some degree of safety if there are multiple concurrent processes using the same cache. The memory layout of the disk cache is optimized to be very efficient for appending new blobs, just simple fwrites + fflush. The format is also robust against sliced files, which solves the problem where applications tear down without destroying the D3D12 device properly. This structure is very similar to Fossilize, and in fact the idea is to move towards actually using the Fossilize format directly later. This implementation prepares us for this scenario where e.g. Steam could potentially manage the vkd3d-proton cache. The main complication in this implementation is that we have to merge the read-only and write caches. Signed-off-by: Hans-Kristian Arntzen <post@arntzen-software.no>
2022-02-02 13:12:20 +00:00
{
INFO("Shader cache is explicitly disabled, relying solely on application caches.\n");
vkd3d_config_flags |= VKD3D_CONFIG_FLAG_PIPELINE_LIBRARY_APP_CACHE_ONLY;
}
/* If we're using a global shader cache, it's meaningless to use PSO caches. */
if (!(vkd3d_config_flags & VKD3D_CONFIG_FLAG_PIPELINE_LIBRARY_APP_CACHE_ONLY))
{
INFO("shader_cache is used, global_pipeline_cache is enforced.\n");
vkd3d_config_flags |= VKD3D_CONFIG_FLAG_GLOBAL_PIPELINE_CACHE;
}
}
static void vkd3d_instance_apply_global_shader_quirks(void)
{
struct override
{
uint64_t config;
uint32_t quirk;
bool negative;
};
static const struct override overrides[] =
{
{ VKD3D_CONFIG_FLAG_FORCE_NO_INVARIANT_POSITION, VKD3D_SHADER_QUIRK_INVARIANT_POSITION, true },
};
uint64_t eq_test;
unsigned int i;
for (i = 0; i < ARRAY_SIZE(overrides); i++)
{
eq_test = overrides[i].negative ? 0 : overrides[i].config;
if ((vkd3d_config_flags & overrides[i].config) == eq_test)
vkd3d_shader_quirk_info.global_quirks |= overrides[i].quirk;
}
}
static const struct vkd3d_debug_option vkd3d_config_options[] =
{
/* Enable Vulkan debug extensions. */
{"vk_debug", VKD3D_CONFIG_FLAG_VULKAN_DEBUG},
{"skip_application_workarounds", VKD3D_CONFIG_FLAG_SKIP_APPLICATION_WORKAROUNDS},
{"debug_utils", VKD3D_CONFIG_FLAG_DEBUG_UTILS},
{"force_static_cbv", VKD3D_CONFIG_FLAG_FORCE_STATIC_CBV},
{"dxr", VKD3D_CONFIG_FLAG_DXR},
{"dxr11", VKD3D_CONFIG_FLAG_DXR | VKD3D_CONFIG_FLAG_DXR11},
{"single_queue", VKD3D_CONFIG_FLAG_SINGLE_QUEUE},
{"descriptor_qa_checks", VKD3D_CONFIG_FLAG_DESCRIPTOR_QA_CHECKS},
{"force_rtv_exclusive_queue", VKD3D_CONFIG_FLAG_FORCE_RTV_EXCLUSIVE_QUEUE},
{"force_dsv_exclusive_queue", VKD3D_CONFIG_FLAG_FORCE_DSV_EXCLUSIVE_QUEUE},
{"force_exclusive_queue", VKD3D_CONFIG_FLAG_FORCE_RTV_EXCLUSIVE_QUEUE | VKD3D_CONFIG_FLAG_FORCE_DSV_EXCLUSIVE_QUEUE},
{"no_upload_hvv", VKD3D_CONFIG_FLAG_NO_UPLOAD_HVV},
{"log_memory_budget", VKD3D_CONFIG_FLAG_LOG_MEMORY_BUDGET},
{"force_host_cached", VKD3D_CONFIG_FLAG_FORCE_HOST_CACHED},
{"no_invariant_position", VKD3D_CONFIG_FLAG_FORCE_NO_INVARIANT_POSITION},
{"global_pipeline_cache", VKD3D_CONFIG_FLAG_GLOBAL_PIPELINE_CACHE},
{"pipeline_library_no_serialize_spirv", VKD3D_CONFIG_FLAG_PIPELINE_LIBRARY_NO_SERIALIZE_SPIRV},
{"pipeline_library_sanitize_spirv", VKD3D_CONFIG_FLAG_PIPELINE_LIBRARY_SANITIZE_SPIRV},
{"pipeline_library_log", VKD3D_CONFIG_FLAG_PIPELINE_LIBRARY_LOG},
{"pipeline_library_ignore_spirv", VKD3D_CONFIG_FLAG_PIPELINE_LIBRARY_IGNORE_SPIRV},
{"mutable_single_set", VKD3D_CONFIG_FLAG_MUTABLE_SINGLE_SET},
{"memory_allocator_skip_clear", VKD3D_CONFIG_FLAG_MEMORY_ALLOCATOR_SKIP_CLEAR},
{"recycle_command_pools", VKD3D_CONFIG_FLAG_RECYCLE_COMMAND_POOLS},
{"pipeline_library_ignore_mismatch_driver", VKD3D_CONFIG_FLAG_PIPELINE_LIBRARY_IGNORE_MISMATCH_DRIVER},
{"breadcrumbs", VKD3D_CONFIG_FLAG_BREADCRUMBS},
cache: Implement an on-disk pipeline library. With VKD3D_SHADER_CACHE_PATH, we can add automatic serialization of pipeline blobs to disk, even for games which do not make any use of GetCachedBlob of ID3D12PipelineLibrary interfaces. Most applications expect drivers to have some kind of internal caching. This is implemented as a system where a disk thread will manage a private ID3D12PipelineLibrary, and new PSOs are automatically committed to this library. PSO creation will also consult this internal pipeline library if applications do not provide their own blob. The strategy for updating the cache is based on a read-only cache which is mmaped from disk, with an exclusive write-only portion for new blobs, which ensures some degree of safety if there are multiple concurrent processes using the same cache. The memory layout of the disk cache is optimized to be very efficient for appending new blobs, just simple fwrites + fflush. The format is also robust against sliced files, which solves the problem where applications tear down without destroying the D3D12 device properly. This structure is very similar to Fossilize, and in fact the idea is to move towards actually using the Fossilize format directly later. This implementation prepares us for this scenario where e.g. Steam could potentially manage the vkd3d-proton cache. The main complication in this implementation is that we have to merge the read-only and write caches. Signed-off-by: Hans-Kristian Arntzen <post@arntzen-software.no>
2022-02-02 13:12:20 +00:00
{"pipeline_library_app_cache", VKD3D_CONFIG_FLAG_PIPELINE_LIBRARY_APP_CACHE_ONLY},
{"shader_cache_sync", VKD3D_CONFIG_FLAG_SHADER_CACHE_SYNC},
{"force_raw_va_cbv", VKD3D_CONFIG_FLAG_FORCE_RAW_VA_CBV},
{"allow_sbt_collection", VKD3D_CONFIG_FLAG_ALLOW_SBT_COLLECTION},
};
static void vkd3d_config_flags_init_once(void)
{
char config[VKD3D_PATH_MAX];
vkd3d_get_env_var("VKD3D_CONFIG", config, sizeof(config));
vkd3d_config_flags = vkd3d_parse_debug_options(config, vkd3d_config_options, ARRAY_SIZE(vkd3d_config_options));
if (!(vkd3d_config_flags & VKD3D_CONFIG_FLAG_SKIP_APPLICATION_WORKAROUNDS))
vkd3d_instance_apply_application_workarounds();
vkd3d_instance_deduce_config_flags_from_environment();
vkd3d_instance_apply_global_shader_quirks();
#ifdef VKD3D_ENABLE_RENDERDOC
/* Enable debug utils by default for Renderdoc builds */
TRACE("Renderdoc build implies VKD3D_CONFIG_FLAG_DEBUG_UTILS.\n");
vkd3d_config_flags |= VKD3D_CONFIG_FLAG_DEBUG_UTILS;
#endif
/* If we're going to use vk_debug, make sure we can get debug callbacks. */
if (vkd3d_config_flags & VKD3D_CONFIG_FLAG_VULKAN_DEBUG)
vkd3d_config_flags |= VKD3D_CONFIG_FLAG_DEBUG_UTILS;
if (vkd3d_config_flags)
INFO("VKD3D_CONFIG='%s'.\n", config);
}
static pthread_once_t vkd3d_config_flags_once = PTHREAD_ONCE_INIT;
static void vkd3d_config_flags_init(void)
{
pthread_once(&vkd3d_config_flags_once, vkd3d_config_flags_init_once);
}
static HRESULT vkd3d_instance_init(struct vkd3d_instance *instance,
const struct vkd3d_instance_create_info *create_info)
{
const struct vkd3d_vk_global_procs *vk_global_procs = &instance->vk_global_procs;
const char *debug_layer_name = "VK_LAYER_KHRONOS_validation";
bool *user_extension_supported = NULL;
VkApplicationInfo application_info;
VkInstanceCreateInfo instance_info;
char application_name[VKD3D_PATH_MAX];
VkLayerProperties *layers;
uint32_t extension_count;
const char **extensions;
uint32_t layer_count, i;
VkInstance vk_instance;
VkResult vr;
HRESULT hr;
uint32_t loader_version = VK_API_VERSION_1_0;
TRACE("Build: %s.\n", vkd3d_version);
memset(instance, 0, sizeof(*instance));
if (!create_info->pfn_signal_event)
{
ERR("Invalid signal event function pointer.\n");
return E_INVALIDARG;
}
if (!create_info->pfn_create_thread != !create_info->pfn_join_thread)
{
ERR("Invalid create/join thread function pointers.\n");
return E_INVALIDARG;
}
instance->signal_event = create_info->pfn_signal_event;
instance->create_thread = create_info->pfn_create_thread;
instance->join_thread = create_info->pfn_join_thread;
vkd3d_config_flags_init();
if (FAILED(hr = vkd3d_init_vk_global_procs(instance, create_info->pfn_vkGetInstanceProcAddr)))
{
ERR("Failed to initialize Vulkan global procs, hr %#x.\n", hr);
return hr;
}
if (create_info->optional_instance_extension_count)
{
if (!(user_extension_supported = vkd3d_calloc(create_info->optional_instance_extension_count, sizeof(bool))))
return E_OUTOFMEMORY;
}
if (FAILED(hr = vkd3d_init_instance_caps(instance, create_info,
&extension_count, user_extension_supported)))
{
if (instance->libvulkan)
vkd3d_dlclose(instance->libvulkan);
vkd3d_free(user_extension_supported);
return hr;
}
if (vk_global_procs->vkEnumerateInstanceVersion)
vk_global_procs->vkEnumerateInstanceVersion(&loader_version);
if (loader_version < VKD3D_MIN_API_VERSION)
{
ERR("Vulkan %u.%u not supported by loader.\n",
VK_VERSION_MAJOR(VKD3D_MIN_API_VERSION),
VK_VERSION_MINOR(VKD3D_MIN_API_VERSION));
if (instance->libvulkan)
vkd3d_dlclose(instance->libvulkan);
vkd3d_free(user_extension_supported);
return E_INVALIDARG;
}
/* Do not opt-in to versions we don't need yet. */
if (loader_version > VKD3D_MAX_API_VERSION)
loader_version = VKD3D_MAX_API_VERSION;
application_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
application_info.pNext = NULL;
application_info.pApplicationName = NULL;
application_info.applicationVersion = 0;
application_info.pEngineName = "vkd3d";
application_info.engineVersion = vkd3d_get_vk_version();
application_info.apiVersion = loader_version;
INFO("vkd3d-proton - build: %"PRIx64".\n", vkd3d_build);
if (vkd3d_get_program_name(application_name))
application_info.pApplicationName = application_name;
TRACE("Application: %s.\n", debugstr_a(application_info.pApplicationName));
if (!(extensions = vkd3d_calloc(extension_count, sizeof(*extensions))))
{
if (instance->libvulkan)
vkd3d_dlclose(instance->libvulkan);
vkd3d_free(user_extension_supported);
return E_OUTOFMEMORY;
}
instance_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
instance_info.pNext = NULL;
instance_info.flags = 0;
instance_info.pApplicationInfo = &application_info;
instance_info.enabledLayerCount = 0;
instance_info.ppEnabledLayerNames = NULL;
instance_info.enabledExtensionCount = vkd3d_enable_extensions(extensions, NULL, 0,
optional_instance_extensions, ARRAY_SIZE(optional_instance_extensions),
create_info->instance_extensions,
create_info->instance_extension_count,
create_info->optional_instance_extensions,
create_info->optional_instance_extension_count,
user_extension_supported, &instance->vk_info);
2016-10-13 12:50:36 +01:00
instance_info.ppEnabledExtensionNames = extensions;
vkd3d_free(user_extension_supported);
if (vkd3d_config_flags & VKD3D_CONFIG_FLAG_VULKAN_DEBUG)
{
layers = NULL;
if (vk_global_procs->vkEnumerateInstanceLayerProperties(&layer_count, NULL) == VK_SUCCESS &&
layer_count &&
(layers = vkd3d_malloc(layer_count * sizeof(*layers))) &&
vk_global_procs->vkEnumerateInstanceLayerProperties(&layer_count, layers) == VK_SUCCESS)
{
for (i = 0; i < layer_count; i++)
{
if (strcmp(layers[i].layerName, debug_layer_name) == 0)
{
instance_info.enabledLayerCount = 1;
instance_info.ppEnabledLayerNames = &debug_layer_name;
break;
}
}
}
if (instance_info.enabledLayerCount == 0)
{
ERR("Failed to enumerate instance layers, will not use VK_LAYER_KHRONOS_validation directly.\n"
"Use VKD3D_CONFIG=vk_debug VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation instead!\n");
}
vkd3d_free(layers);
}
vr = vk_global_procs->vkCreateInstance(&instance_info, NULL, &vk_instance);
vkd3d_free((void *)extensions);
if (vr < 0)
{
ERR("Failed to create Vulkan instance, vr %d.\n", vr);
if (instance->libvulkan)
vkd3d_dlclose(instance->libvulkan);
return hresult_from_vk_result(vr);
}
if (FAILED(hr = vkd3d_load_vk_instance_procs(&instance->vk_procs, vk_global_procs, vk_instance)))
{
ERR("Failed to load instance procs, hr %#x.\n", hr);
if (instance->vk_procs.vkDestroyInstance)
instance->vk_procs.vkDestroyInstance(vk_instance, NULL);
if (instance->libvulkan)
vkd3d_dlclose(instance->libvulkan);
return hr;
}
instance->vk_instance = vk_instance;
instance->instance_version = loader_version;
TRACE("Created Vulkan instance %p, version %u.%u.\n", vk_instance,
VK_VERSION_MAJOR(loader_version),
VK_VERSION_MINOR(loader_version));
instance->refcount = 1;
instance->vk_debug_callback = VK_NULL_HANDLE;
if (instance->vk_info.EXT_debug_utils && (vkd3d_config_flags & VKD3D_CONFIG_FLAG_VULKAN_DEBUG))
vkd3d_init_debug_messenger_callback(instance);
#ifdef VKD3D_ENABLE_RENDERDOC
/* Need to init this sometime after creating the instance so that the layer has loaded. */
vkd3d_renderdoc_init();
#endif
#ifdef VKD3D_ENABLE_DESCRIPTOR_QA
vkd3d_descriptor_debug_init();
#endif
return S_OK;
}
VKD3D_EXPORT HRESULT vkd3d_create_instance(const struct vkd3d_instance_create_info *create_info,
struct vkd3d_instance **instance)
{
struct vkd3d_instance *object;
HRESULT hr;
TRACE("create_info %p, instance %p.\n", create_info, instance);
vkd3d_init_profiling();
if (!create_info || !instance)
return E_INVALIDARG;
if (!(object = vkd3d_malloc(sizeof(*object))))
return E_OUTOFMEMORY;
if (FAILED(hr = vkd3d_instance_init(object, create_info)))
{
vkd3d_free(object);
return hr;
}
TRACE("Created instance %p.\n", object);
*instance = object;
return S_OK;
}
static void vkd3d_destroy_instance(struct vkd3d_instance *instance)
{
const struct vkd3d_vk_instance_procs *vk_procs = &instance->vk_procs;
VkInstance vk_instance = instance->vk_instance;
if (instance->vk_debug_callback)
VK_CALL(vkDestroyDebugUtilsMessengerEXT(vk_instance, instance->vk_debug_callback, NULL));
VK_CALL(vkDestroyInstance(vk_instance, NULL));
if (instance->libvulkan)
vkd3d_dlclose(instance->libvulkan);
vkd3d_free(instance);
}
VKD3D_EXPORT ULONG vkd3d_instance_incref(struct vkd3d_instance *instance)
{
ULONG refcount = InterlockedIncrement(&instance->refcount);
TRACE("%p increasing refcount to %u.\n", instance, refcount);
return refcount;
}
VKD3D_EXPORT ULONG vkd3d_instance_decref(struct vkd3d_instance *instance)
{
ULONG refcount = InterlockedDecrement(&instance->refcount);
TRACE("%p decreasing refcount to %u.\n", instance, refcount);
if (!refcount)
vkd3d_destroy_instance(instance);
return refcount;
}
VKD3D_EXPORT VkInstance vkd3d_instance_get_vk_instance(struct vkd3d_instance *instance)
{
return instance->vk_instance;
}
static uint32_t vkd3d_physical_device_get_time_domains(struct d3d12_device *device)
{
const struct vkd3d_vk_instance_procs *vk_procs = &device->vkd3d_instance->vk_procs;
VkPhysicalDevice physical_device = device->vk_physical_device;
uint32_t i, domain_count = 0;
VkTimeDomainEXT *domains;
uint32_t result = 0;
VkResult vr;
if ((vr = VK_CALL(vkGetPhysicalDeviceCalibrateableTimeDomainsEXT(physical_device, &domain_count, NULL))) < 0)
{
ERR("Failed to enumerate time domains, vr %d.\n", vr);
return 0;
}
if (!(domains = vkd3d_calloc(domain_count, sizeof(*domains))))
return 0;
if ((vr = VK_CALL(vkGetPhysicalDeviceCalibrateableTimeDomainsEXT(physical_device, &domain_count, domains))) < 0)
{
ERR("Failed to enumerate time domains, vr %d.\n", vr);
vkd3d_free(domains);
return 0;
}
for (i = 0; i < domain_count; i++)
{
switch (domains[i])
{
case VK_TIME_DOMAIN_DEVICE_EXT:
result |= VKD3D_TIME_DOMAIN_DEVICE;
break;
case VK_TIME_DOMAIN_QUERY_PERFORMANCE_COUNTER_EXT:
result |= VKD3D_TIME_DOMAIN_QPC;
break;
default:
break;
}
}
vkd3d_free(domains);
return result;
}
bool d3d12_device_supports_ray_tracing_tier_1_0(const struct d3d12_device *device)
{
return device->device_info.acceleration_structure_features.accelerationStructure &&
device->device_info.ray_tracing_pipeline_features.rayTracingPipeline &&
device->d3d12_caps.options5.RaytracingTier >= D3D12_RAYTRACING_TIER_1_0;
}
bool d3d12_device_supports_variable_shading_rate_tier_1(struct d3d12_device *device)
{
const struct vkd3d_physical_device_info *info = &device->device_info;
return info->fragment_shading_rate_features.pipelineFragmentShadingRate &&
(device->vk_info.device_limits.framebufferColorSampleCounts & VK_SAMPLE_COUNT_2_BIT);
}
UINT d3d12_determine_shading_rate_image_tile_size(struct d3d12_device *device)
{
VkExtent2D min_texel_size = device->device_info.fragment_shading_rate_properties.minFragmentShadingRateAttachmentTexelSize;
VkExtent2D max_texel_size = device->device_info.fragment_shading_rate_properties.maxFragmentShadingRateAttachmentTexelSize;
const UINT valid_shading_rate_image_tile_sizes[] =
{
8, 16, 32
};
for (uint32_t i = 0; i < ARRAY_SIZE(valid_shading_rate_image_tile_sizes); i++)
{
UINT tile_size = valid_shading_rate_image_tile_sizes[i];
if (tile_size >= min_texel_size.width && tile_size >= min_texel_size.height &&
tile_size <= max_texel_size.height && tile_size <= max_texel_size.height)
return tile_size;
}
/* No valid D3D12 tile size. */
return 0;
}
bool d3d12_device_supports_variable_shading_rate_tier_2(struct d3d12_device *device)
{
const struct vkd3d_physical_device_info *info = &device->device_info;
return info->fragment_shading_rate_properties.fragmentShadingRateNonTrivialCombinerOps &&
info->fragment_shading_rate_features.attachmentFragmentShadingRate &&
info->fragment_shading_rate_features.primitiveFragmentShadingRate &&
d3d12_determine_shading_rate_image_tile_size(device) != 0;
}
static D3D12_VARIABLE_SHADING_RATE_TIER d3d12_device_determine_variable_shading_rate_tier(struct d3d12_device *device)
{
if (!d3d12_device_supports_variable_shading_rate_tier_1(device))
return D3D12_VARIABLE_SHADING_RATE_TIER_NOT_SUPPORTED;
if (!d3d12_device_supports_variable_shading_rate_tier_2(device))
return D3D12_VARIABLE_SHADING_RATE_TIER_1;
return D3D12_VARIABLE_SHADING_RATE_TIER_2;
}
static const struct
{
VkExtent2D fragment_size;
VkSampleCountFlags min_sample_counts;
} additional_shading_rates[] =
{
{ { 2, 4 }, VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_2_BIT },
{ { 4, 2 }, VK_SAMPLE_COUNT_1_BIT },
{ { 4, 4 }, VK_SAMPLE_COUNT_1_BIT },
};
static bool d3d12_device_determine_additional_shading_rates_supported(struct d3d12_device *device)
{
const struct vkd3d_vk_instance_procs *vk_procs = &device->vkd3d_instance->vk_procs;
VkPhysicalDeviceFragmentShadingRateKHR *fragment_shading_rates;
VkPhysicalDevice physical_device = device->vk_physical_device;
uint32_t additional_shading_rates_supported = 0;
uint32_t fragment_shading_rate_count;
uint32_t i, j;
VkResult vr;
/* Early out if we don't support at least variable shading rate TIER1 */
if (d3d12_device_determine_variable_shading_rate_tier(device) == D3D12_VARIABLE_SHADING_RATE_TIER_NOT_SUPPORTED)
return false;
if ((vr = VK_CALL(vkGetPhysicalDeviceFragmentShadingRatesKHR(physical_device, &fragment_shading_rate_count, NULL))) < 0)
{
ERR("Failed to enumerate additional shading rates, vr %d.\n", vr);
return false;
}
if (!(fragment_shading_rates = vkd3d_calloc(fragment_shading_rate_count, sizeof(*fragment_shading_rates))))
return false;
for (i = 0; i < fragment_shading_rate_count; i++)
fragment_shading_rates[i].sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_KHR;
if ((vr = VK_CALL(vkGetPhysicalDeviceFragmentShadingRatesKHR(physical_device, &fragment_shading_rate_count, fragment_shading_rates))) < 0)
{
ERR("Failed to enumerate additional shading rates, vr %d.\n", vr);
vkd3d_free(fragment_shading_rates);
return false;
}
for (i = 0; i < fragment_shading_rate_count; i++)
{
for (j = 0; j < ARRAY_SIZE(additional_shading_rates); j++)
{
if (fragment_shading_rates[i].fragmentSize.width == additional_shading_rates[j].fragment_size.width &&
fragment_shading_rates[i].fragmentSize.height == additional_shading_rates[j].fragment_size.height &&
(fragment_shading_rates[i].sampleCounts & additional_shading_rates[j].min_sample_counts) == additional_shading_rates[j].min_sample_counts)
{
additional_shading_rates_supported++;
break;
}
}
}
vkd3d_free(fragment_shading_rates);
return additional_shading_rates_supported == ARRAY_SIZE(additional_shading_rates);
}
static void vkd3d_physical_device_info_apply_workarounds(struct vkd3d_physical_device_info *info)
{
/* A performance workaround for NV.
* The 16 byte offset is a lie, as that is only actually required when we
* use vectorized load-stores. When we emit vectorized load-store ops,
* the storage buffer must be aligned properly, so this is fine in practice
* and is a nice speed boost. */
if (info->properties2.properties.vendorID == VKD3D_VENDOR_ID_NVIDIA)
info->properties2.properties.limits.minStorageBufferOffsetAlignment = 4;
}
static void vkd3d_physical_device_info_init(struct vkd3d_physical_device_info *info, struct d3d12_device *device)
{
const struct vkd3d_vk_instance_procs *vk_procs = &device->vkd3d_instance->vk_procs;
struct vkd3d_vulkan_info *vulkan_info = &device->vk_info;
memset(info, 0, sizeof(*info));
info->features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
info->properties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
info->subgroup_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES;
vk_prepend_struct(&info->properties2, &info->subgroup_properties);
info->maintenance3_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_3_PROPERTIES;
vk_prepend_struct(&info->properties2, &info->maintenance3_properties);
if (vulkan_info->KHR_buffer_device_address)
{
info->buffer_device_address_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES_KHR;
vk_prepend_struct(&info->features2, &info->buffer_device_address_features);
}
if (vulkan_info->KHR_timeline_semaphore)
{
info->timeline_semaphore_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES_KHR;
vk_prepend_struct(&info->features2, &info->timeline_semaphore_features);
info->timeline_semaphore_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_PROPERTIES_KHR;
vk_prepend_struct(&info->properties2, &info->timeline_semaphore_properties);
}
if (vulkan_info->KHR_push_descriptor)
{
info->push_descriptor_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR;
vk_prepend_struct(&info->properties2, &info->push_descriptor_properties);
}
if (vulkan_info->KHR_shader_float16_int8)
{
info->float16_int8_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT16_INT8_FEATURES_KHR;
vk_prepend_struct(&info->features2, &info->float16_int8_features);
}
/* Core in Vulkan 1.1. */
info->storage_16bit_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES;
vk_prepend_struct(&info->features2, &info->storage_16bit_features);
if (vulkan_info->KHR_shader_subgroup_extended_types)
{
info->subgroup_extended_types_features.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SUBGROUP_EXTENDED_TYPES_FEATURES_KHR;
vk_prepend_struct(&info->features2, &info->subgroup_extended_types_features);
}
if (vulkan_info->KHR_maintenance4)
{
info->maintenance4_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_4_PROPERTIES;
vk_prepend_struct(&info->properties2, &info->maintenance4_properties);
info->maintenance4_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_4_FEATURES;
vk_prepend_struct(&info->features2, &info->maintenance4_features);
}
if (vulkan_info->EXT_calibrated_timestamps)
info->time_domains = vkd3d_physical_device_get_time_domains(device);
if (vulkan_info->EXT_conditional_rendering)
{
info->conditional_rendering_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CONDITIONAL_RENDERING_FEATURES_EXT;
vk_prepend_struct(&info->features2, &info->conditional_rendering_features);
}
if (vulkan_info->EXT_conservative_rasterization)
{
info->conservative_rasterization_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CONSERVATIVE_RASTERIZATION_PROPERTIES_EXT;
vk_prepend_struct(&info->properties2, &info->conservative_rasterization_properties);
}
if (vulkan_info->EXT_custom_border_color)
{
info->custom_border_color_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_FEATURES_EXT;
vk_prepend_struct(&info->features2, &info->custom_border_color_features);
info->custom_border_color_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_PROPERTIES_EXT;
vk_prepend_struct(&info->properties2, &info->custom_border_color_properties);
}
if (vulkan_info->EXT_depth_clip_enable)
{
info->depth_clip_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLIP_ENABLE_FEATURES_EXT;
vk_prepend_struct(&info->features2, &info->depth_clip_features);
}
if (vulkan_info->EXT_descriptor_indexing)
{
info->descriptor_indexing_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES_EXT;
vk_prepend_struct(&info->features2, &info->descriptor_indexing_features);
info->descriptor_indexing_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_PROPERTIES_EXT;
vk_prepend_struct(&info->properties2, &info->descriptor_indexing_properties);
}
if (vulkan_info->EXT_inline_uniform_block)
{
info->inline_uniform_block_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_FEATURES_EXT;
vk_prepend_struct(&info->features2, &info->inline_uniform_block_features);
info->inline_uniform_block_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_PROPERTIES_EXT;
vk_prepend_struct(&info->properties2, &info->inline_uniform_block_properties);
}
if (vulkan_info->EXT_robustness2)
{
info->robustness2_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT;
vk_prepend_struct(&info->features2, &info->robustness2_features);
info->robustness2_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_PROPERTIES_EXT;
vk_prepend_struct(&info->properties2, &info->robustness2_properties);
}
if (vulkan_info->EXT_sampler_filter_minmax)
{
info->sampler_filter_minmax_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_FILTER_MINMAX_PROPERTIES_EXT;
vk_prepend_struct(&info->properties2, &info->sampler_filter_minmax_properties);
}
if (vulkan_info->EXT_shader_demote_to_helper_invocation)
{
info->demote_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DEMOTE_TO_HELPER_INVOCATION_FEATURES_EXT;
vk_prepend_struct(&info->features2, &info->demote_features);
}
if (vulkan_info->EXT_subgroup_size_control)
{
info->subgroup_size_control_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_PROPERTIES_EXT;
info->subgroup_size_control_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_FEATURES_EXT;
vk_prepend_struct(&info->properties2, &info->subgroup_size_control_properties);
vk_prepend_struct(&info->features2, &info->subgroup_size_control_features);
}
if (vulkan_info->EXT_texel_buffer_alignment)
{
info->texel_buffer_alignment_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXEL_BUFFER_ALIGNMENT_FEATURES_EXT;
vk_prepend_struct(&info->features2, &info->texel_buffer_alignment_features);
info->texel_buffer_alignment_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXEL_BUFFER_ALIGNMENT_PROPERTIES_EXT;
vk_prepend_struct(&info->properties2, &info->texel_buffer_alignment_properties);
}
if (vulkan_info->EXT_transform_feedback)
{
info->xfb_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_FEATURES_EXT;
vk_prepend_struct(&info->features2, &info->xfb_features);
info->xfb_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_PROPERTIES_EXT;
vk_prepend_struct(&info->properties2, &info->xfb_properties);
}
if (vulkan_info->EXT_vertex_attribute_divisor)
{
info->vertex_divisor_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_FEATURES_EXT;
vk_prepend_struct(&info->features2, &info->vertex_divisor_features);
info->vertex_divisor_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_PROPERTIES_EXT;
vk_prepend_struct(&info->properties2, &info->vertex_divisor_properties);
}
if (vulkan_info->EXT_extended_dynamic_state)
{
info->extended_dynamic_state_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT;
vk_prepend_struct(&info->features2, &info->extended_dynamic_state_features);
}
if (vulkan_info->EXT_extended_dynamic_state2)
{
info->extended_dynamic_state2_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_2_FEATURES_EXT;
vk_prepend_struct(&info->features2, &info->extended_dynamic_state2_features);
}
if (vulkan_info->EXT_external_memory_host)
{
info->external_memory_host_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_MEMORY_HOST_PROPERTIES_EXT;
vk_prepend_struct(&info->properties2, &info->external_memory_host_properties);
}
if (vulkan_info->EXT_4444_formats)
{
info->ext_4444_formats_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_4444_FORMATS_FEATURES_EXT;
vk_prepend_struct(&info->features2, &info->ext_4444_formats_features);
}
if (vulkan_info->AMD_shader_core_properties)
{
info->shader_core_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_CORE_PROPERTIES_AMD;
vk_prepend_struct(&info->properties2, &info->shader_core_properties);
}
if (vulkan_info->AMD_shader_core_properties2)
{
info->shader_core_properties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_CORE_PROPERTIES_2_AMD;
vk_prepend_struct(&info->properties2, &info->shader_core_properties2);
}
if (vulkan_info->NV_shader_sm_builtins)
{
info->shader_sm_builtins_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SM_BUILTINS_PROPERTIES_NV;
vk_prepend_struct(&info->properties2, &info->shader_sm_builtins_properties);
}
if (vulkan_info->VALVE_mutable_descriptor_type)
{
info->mutable_descriptor_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MUTABLE_DESCRIPTOR_TYPE_FEATURES_VALVE;
vk_prepend_struct(&info->features2, &info->mutable_descriptor_features);
}
if (vulkan_info->EXT_image_view_min_lod)
{
info->image_view_min_lod_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_VIEW_MIN_LOD_FEATURES_EXT;
vk_prepend_struct(&info->features2, &info->image_view_min_lod_features);
}
if (vulkan_info->KHR_acceleration_structure && vulkan_info->KHR_ray_tracing_pipeline &&
vulkan_info->KHR_deferred_host_operations && vulkan_info->KHR_spirv_1_4)
{
info->acceleration_structure_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_FEATURES_KHR;
info->acceleration_structure_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_PROPERTIES_KHR;
info->ray_tracing_pipeline_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_FEATURES_KHR;
info->ray_tracing_pipeline_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_PROPERTIES_KHR;
vk_prepend_struct(&info->features2, &info->acceleration_structure_features);
vk_prepend_struct(&info->features2, &info->ray_tracing_pipeline_features);
vk_prepend_struct(&info->properties2, &info->acceleration_structure_properties);
vk_prepend_struct(&info->properties2, &info->ray_tracing_pipeline_properties);
}
if (vulkan_info->KHR_ray_query)
{
info->ray_query_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_QUERY_FEATURES_KHR;
vk_prepend_struct(&info->features2, &info->ray_query_features);
}
if (vulkan_info->KHR_ray_tracing_maintenance1)
{
info->ray_tracing_maintenance1_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_MAINTENANCE_1_FEATURES_KHR;
vk_prepend_struct(&info->features2, &info->ray_tracing_maintenance1_features);
}
if (vulkan_info->KHR_shader_float_controls)
{
info->float_control_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT_CONTROLS_PROPERTIES_KHR;
vk_prepend_struct(&info->properties2, &info->float_control_properties);
}
if (vulkan_info->KHR_fragment_shading_rate)
{
info->fragment_shading_rate_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_PROPERTIES_KHR;
info->fragment_shading_rate_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_FEATURES_KHR;
vk_prepend_struct(&info->properties2, &info->fragment_shading_rate_properties);
vk_prepend_struct(&info->features2, &info->fragment_shading_rate_features);
}
if (vulkan_info->KHR_separate_depth_stencil_layouts)
{
info->separate_depth_stencil_layout_features.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SEPARATE_DEPTH_STENCIL_LAYOUTS_FEATURES;
vk_prepend_struct(&info->features2, &info->separate_depth_stencil_layout_features);
}
if (vulkan_info->KHR_shader_integer_dot_product)
{
info->shader_integer_dot_product_features.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_INTEGER_DOT_PRODUCT_FEATURES_KHR;
info->shader_integer_dot_product_properties.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_INTEGER_DOT_PRODUCT_PROPERTIES_KHR;
vk_prepend_struct(&info->features2, &info->shader_integer_dot_product_features);
vk_prepend_struct(&info->properties2, &info->shader_integer_dot_product_properties);
}
if (vulkan_info->NV_fragment_shader_barycentric && !vulkan_info->KHR_fragment_shader_barycentric)
{
info->barycentric_features_nv.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADER_BARYCENTRIC_FEATURES_NV;
vk_prepend_struct(&info->features2, &info->barycentric_features_nv);
}
if (vulkan_info->KHR_fragment_shader_barycentric)
{
info->barycentric_features_khr.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADER_BARYCENTRIC_FEATURES_KHR;
vk_prepend_struct(&info->features2, &info->barycentric_features_khr);
}
if (vulkan_info->NV_compute_shader_derivatives)
{
info->compute_shader_derivatives_features_nv.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COMPUTE_SHADER_DERIVATIVES_FEATURES_NV;
vk_prepend_struct(&info->features2, &info->compute_shader_derivatives_features_nv);
}
if (vulkan_info->NV_device_generated_commands)
{
info->device_generated_commands_features_nv.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEVICE_GENERATED_COMMANDS_FEATURES_NV;
info->device_generated_commands_properties_nv.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEVICE_GENERATED_COMMANDS_PROPERTIES_NV;
vk_prepend_struct(&info->features2, &info->device_generated_commands_features_nv);
vk_prepend_struct(&info->properties2, &info->device_generated_commands_properties_nv);
}
if (vulkan_info->KHR_shader_atomic_int64)
{
info->shader_atomic_int64_features.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_ATOMIC_INT64_FEATURES_KHR;
vk_prepend_struct(&info->features2, &info->shader_atomic_int64_features);
}
if (vulkan_info->EXT_shader_image_atomic_int64)
{
info->shader_image_atomic_int64_features.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_IMAGE_ATOMIC_INT64_FEATURES_EXT;
vk_prepend_struct(&info->features2, &info->shader_image_atomic_int64_features);
}
if (vulkan_info->EXT_scalar_block_layout)
{
info->scalar_block_layout_features.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SCALAR_BLOCK_LAYOUT_FEATURES_EXT;
vk_prepend_struct(&info->features2, &info->scalar_block_layout_features);
}
if (vulkan_info->KHR_uniform_buffer_standard_layout)
{
info->uniform_buffer_standard_layout_features.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_UNIFORM_BUFFER_STANDARD_LAYOUT_FEATURES;
vk_prepend_struct(&info->features2, &info->uniform_buffer_standard_layout_features);
}
if (vulkan_info->KHR_dynamic_rendering)
{
info->dynamic_rendering_features.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES_KHR;
vk_prepend_struct(&info->features2, &info->dynamic_rendering_features);
}
if (vulkan_info->VALVE_descriptor_set_host_mapping)
{
info->descriptor_set_host_mapping_features.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_SET_HOST_MAPPING_FEATURES_VALVE;
vk_prepend_struct(&info->features2, &info->descriptor_set_host_mapping_features);
}
if (vulkan_info->KHR_driver_properties)
{
info->driver_properties.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES_KHR;
vk_prepend_struct(&info->properties2, &info->driver_properties);
}
if (vulkan_info->AMD_device_coherent_memory)
{
info->device_coherent_memory_features_amd.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COHERENT_MEMORY_FEATURES_AMD;
vk_prepend_struct(&info->features2, &info->device_coherent_memory_features_amd);
}
/* Core in Vulkan 1.1. */
info->shader_draw_parameters_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETERS_FEATURES;
vk_prepend_struct(&info->features2, &info->shader_draw_parameters_features);
VK_CALL(vkGetPhysicalDeviceFeatures2(device->vk_physical_device, &info->features2));
VK_CALL(vkGetPhysicalDeviceProperties2(device->vk_physical_device, &info->properties2));
}
static void vkd3d_trace_physical_device_properties(const VkPhysicalDeviceProperties *properties)
{
TRACE("Device name: %s.\n", properties->deviceName);
TRACE("Vendor ID: %#x, Device ID: %#x.\n", properties->vendorID, properties->deviceID);
if (properties->vendorID == VKD3D_VENDOR_ID_NVIDIA)
{
TRACE("Driver version: %#x (%u.%u.%u).\n", properties->driverVersion,
VKD3D_DRIVER_VERSION_MAJOR_NV(properties->driverVersion),
VKD3D_DRIVER_VERSION_MINOR_NV(properties->driverVersion),
VKD3D_DRIVER_VERSION_PATCH_NV(properties->driverVersion));
}
else
{
TRACE("Driver version: %#x (%u.%u.%u).\n", properties->driverVersion,
VK_VERSION_MAJOR(properties->driverVersion),
VK_VERSION_MINOR(properties->driverVersion),
VK_VERSION_PATCH(properties->driverVersion));
}
TRACE("API version: %u.%u.%u.\n",
VK_VERSION_MAJOR(properties->apiVersion),
VK_VERSION_MINOR(properties->apiVersion),
VK_VERSION_PATCH(properties->apiVersion));
}
static void vkd3d_trace_physical_device(VkPhysicalDevice device,
const struct vkd3d_physical_device_info *info,
const struct vkd3d_vk_instance_procs *vk_procs)
{
VKD3D_UNUSED char debug_buffer[VKD3D_DEBUG_FLAGS_BUFFER_SIZE];
VkPhysicalDeviceMemoryProperties memory_properties;
VkQueueFamilyProperties *queue_properties;
unsigned int i, j;
uint32_t count;
vkd3d_trace_physical_device_properties(&info->properties2.properties);
VK_CALL(vkGetPhysicalDeviceQueueFamilyProperties(device, &count, NULL));
TRACE("Queue families [%u]:\n", count);
if (!(queue_properties = vkd3d_calloc(count, sizeof(VkQueueFamilyProperties))))
return;
VK_CALL(vkGetPhysicalDeviceQueueFamilyProperties(device, &count, queue_properties));
for (i = 0; i < count; ++i)
{
TRACE(" Queue family [%u]: flags %s, count %u, timestamp bits %u, image transfer granularity %s.\n",
i, debug_vk_queue_flags(queue_properties[i].queueFlags, debug_buffer),
queue_properties[i].queueCount, queue_properties[i].timestampValidBits,
debug_vk_extent_3d(queue_properties[i].minImageTransferGranularity));
}
vkd3d_free(queue_properties);
VK_CALL(vkGetPhysicalDeviceMemoryProperties(device, &memory_properties));
for (i = 0; i < memory_properties.memoryHeapCount; ++i)
{
VKD3D_UNUSED const VkMemoryHeap *heap = &memory_properties.memoryHeaps[i];
TRACE("Memory heap [%u]: size %#"PRIx64" (%"PRIu64" MiB), flags %s, memory types:\n",
i, heap->size, heap->size / 1024 / 1024, debug_vk_memory_heap_flags(heap->flags, debug_buffer));
for (j = 0; j < memory_properties.memoryTypeCount; ++j)
{
const VkMemoryType *type = &memory_properties.memoryTypes[j];
if (type->heapIndex != i)
continue;
TRACE(" Memory type [%u]: flags %s.\n", j, debug_vk_memory_property_flags(type->propertyFlags, debug_buffer));
}
}
}
static void vkd3d_trace_physical_device_limits(const struct vkd3d_physical_device_info *info)
{
TRACE("Device limits:\n");
TRACE(" maxImageDimension1D: %u.\n", info->properties2.properties.limits.maxImageDimension1D);
TRACE(" maxImageDimension2D: %u.\n", info->properties2.properties.limits.maxImageDimension2D);
TRACE(" maxImageDimension3D: %u.\n", info->properties2.properties.limits.maxImageDimension3D);
TRACE(" maxImageDimensionCube: %u.\n", info->properties2.properties.limits.maxImageDimensionCube);
TRACE(" maxImageArrayLayers: %u.\n", info->properties2.properties.limits.maxImageArrayLayers);
TRACE(" maxTexelBufferElements: %u.\n", info->properties2.properties.limits.maxTexelBufferElements);
TRACE(" maxUniformBufferRange: %u.\n", info->properties2.properties.limits.maxUniformBufferRange);
TRACE(" maxStorageBufferRange: %u.\n", info->properties2.properties.limits.maxStorageBufferRange);
TRACE(" maxPushConstantsSize: %u.\n", info->properties2.properties.limits.maxPushConstantsSize);
TRACE(" maxMemoryAllocationCount: %u.\n", info->properties2.properties.limits.maxMemoryAllocationCount);
TRACE(" maxSamplerAllocationCount: %u.\n", info->properties2.properties.limits.maxSamplerAllocationCount);
TRACE(" bufferImageGranularity: %#"PRIx64".\n", info->properties2.properties.limits.bufferImageGranularity);
TRACE(" sparseAddressSpaceSize: %#"PRIx64".\n", info->properties2.properties.limits.sparseAddressSpaceSize);
TRACE(" maxBoundDescriptorSets: %u.\n", info->properties2.properties.limits.maxBoundDescriptorSets);
TRACE(" maxPerStageDescriptorSamplers: %u.\n", info->properties2.properties.limits.maxPerStageDescriptorSamplers);
TRACE(" maxPerStageDescriptorUniformBuffers: %u.\n", info->properties2.properties.limits.maxPerStageDescriptorUniformBuffers);
TRACE(" maxPerStageDescriptorStorageBuffers: %u.\n", info->properties2.properties.limits.maxPerStageDescriptorStorageBuffers);
TRACE(" maxPerStageDescriptorSampledImages: %u.\n", info->properties2.properties.limits.maxPerStageDescriptorSampledImages);
TRACE(" maxPerStageDescriptorStorageImages: %u.\n", info->properties2.properties.limits.maxPerStageDescriptorStorageImages);
TRACE(" maxPerStageDescriptorInputAttachments: %u.\n", info->properties2.properties.limits.maxPerStageDescriptorInputAttachments);
TRACE(" maxPerStageResources: %u.\n", info->properties2.properties.limits.maxPerStageResources);
TRACE(" maxDescriptorSetSamplers: %u.\n", info->properties2.properties.limits.maxDescriptorSetSamplers);
TRACE(" maxDescriptorSetUniformBuffers: %u.\n", info->properties2.properties.limits.maxDescriptorSetUniformBuffers);
TRACE(" maxDescriptorSetUniformBuffersDynamic: %u.\n", info->properties2.properties.limits.maxDescriptorSetUniformBuffersDynamic);
TRACE(" maxDescriptorSetStorageBuffers: %u.\n", info->properties2.properties.limits.maxDescriptorSetStorageBuffers);
TRACE(" maxDescriptorSetStorageBuffersDynamic: %u.\n", info->properties2.properties.limits.maxDescriptorSetStorageBuffersDynamic);
TRACE(" maxDescriptorSetSampledImages: %u.\n", info->properties2.properties.limits.maxDescriptorSetSampledImages);
TRACE(" maxDescriptorSetStorageImages: %u.\n", info->properties2.properties.limits.maxDescriptorSetStorageImages);
TRACE(" maxDescriptorSetInputAttachments: %u.\n", info->properties2.properties.limits.maxDescriptorSetInputAttachments);
TRACE(" maxVertexInputAttributes: %u.\n", info->properties2.properties.limits.maxVertexInputAttributes);
TRACE(" maxVertexInputBindings: %u.\n", info->properties2.properties.limits.maxVertexInputBindings);
TRACE(" maxVertexInputAttributeOffset: %u.\n", info->properties2.properties.limits.maxVertexInputAttributeOffset);
TRACE(" maxVertexInputBindingStride: %u.\n", info->properties2.properties.limits.maxVertexInputBindingStride);
TRACE(" maxVertexOutputComponents: %u.\n", info->properties2.properties.limits.maxVertexOutputComponents);
TRACE(" maxTessellationGenerationLevel: %u.\n", info->properties2.properties.limits.maxTessellationGenerationLevel);
TRACE(" maxTessellationPatchSize: %u.\n", info->properties2.properties.limits.maxTessellationPatchSize);
TRACE(" maxTessellationControlPerVertexInputComponents: %u.\n",
info->properties2.properties.limits.maxTessellationControlPerVertexInputComponents);
TRACE(" maxTessellationControlPerVertexOutputComponents: %u.\n",
info->properties2.properties.limits.maxTessellationControlPerVertexOutputComponents);
TRACE(" maxTessellationControlPerPatchOutputComponents: %u.\n",
info->properties2.properties.limits.maxTessellationControlPerPatchOutputComponents);
TRACE(" maxTessellationControlTotalOutputComponents: %u.\n",
info->properties2.properties.limits.maxTessellationControlTotalOutputComponents);
TRACE(" maxTessellationEvaluationInputComponents: %u.\n",
info->properties2.properties.limits.maxTessellationEvaluationInputComponents);
TRACE(" maxTessellationEvaluationOutputComponents: %u.\n",
info->properties2.properties.limits.maxTessellationEvaluationOutputComponents);
TRACE(" maxGeometryShaderInvocations: %u.\n", info->properties2.properties.limits.maxGeometryShaderInvocations);
TRACE(" maxGeometryInputComponents: %u.\n", info->properties2.properties.limits.maxGeometryInputComponents);
TRACE(" maxGeometryOutputComponents: %u.\n", info->properties2.properties.limits.maxGeometryOutputComponents);
TRACE(" maxGeometryOutputVertices: %u.\n", info->properties2.properties.limits.maxGeometryOutputVertices);
TRACE(" maxGeometryTotalOutputComponents: %u.\n", info->properties2.properties.limits.maxGeometryTotalOutputComponents);
TRACE(" maxFragmentInputComponents: %u.\n", info->properties2.properties.limits.maxFragmentInputComponents);
TRACE(" maxFragmentOutputAttachments: %u.\n", info->properties2.properties.limits.maxFragmentOutputAttachments);
TRACE(" maxFragmentDualSrcAttachments: %u.\n", info->properties2.properties.limits.maxFragmentDualSrcAttachments);
TRACE(" maxFragmentCombinedOutputResources: %u.\n", info->properties2.properties.limits.maxFragmentCombinedOutputResources);
TRACE(" maxComputeSharedMemorySize: %u.\n", info->properties2.properties.limits.maxComputeSharedMemorySize);
TRACE(" maxComputeWorkGroupCount: %u, %u, %u.\n", info->properties2.properties.limits.maxComputeWorkGroupCount[0],
info->properties2.properties.limits.maxComputeWorkGroupCount[1], info->properties2.properties.limits.maxComputeWorkGroupCount[2]);
TRACE(" maxComputeWorkGroupInvocations: %u.\n", info->properties2.properties.limits.maxComputeWorkGroupInvocations);
TRACE(" maxComputeWorkGroupSize: %u, %u, %u.\n", info->properties2.properties.limits.maxComputeWorkGroupSize[0],
info->properties2.properties.limits.maxComputeWorkGroupSize[1], info->properties2.properties.limits.maxComputeWorkGroupSize[2]);
TRACE(" subPixelPrecisionBits: %u.\n", info->properties2.properties.limits.subPixelPrecisionBits);
TRACE(" subTexelPrecisionBits: %u.\n", info->properties2.properties.limits.subTexelPrecisionBits);
TRACE(" mipmapPrecisionBits: %u.\n", info->properties2.properties.limits.mipmapPrecisionBits);
TRACE(" maxDrawIndexedIndexValue: %u.\n", info->properties2.properties.limits.maxDrawIndexedIndexValue);
TRACE(" maxDrawIndirectCount: %u.\n", info->properties2.properties.limits.maxDrawIndirectCount);
TRACE(" maxSamplerLodBias: %f.\n", info->properties2.properties.limits.maxSamplerLodBias);
TRACE(" maxSamplerAnisotropy: %f.\n", info->properties2.properties.limits.maxSamplerAnisotropy);
TRACE(" maxViewports: %u.\n", info->properties2.properties.limits.maxViewports);
TRACE(" maxViewportDimensions: %u, %u.\n", info->properties2.properties.limits.maxViewportDimensions[0],
info->properties2.properties.limits.maxViewportDimensions[1]);
TRACE(" viewportBoundsRange: %f, %f.\n", info->properties2.properties.limits.viewportBoundsRange[0], info->properties2.properties.limits.viewportBoundsRange[1]);
TRACE(" viewportSubPixelBits: %u.\n", info->properties2.properties.limits.viewportSubPixelBits);
TRACE(" minMemoryMapAlignment: %u.\n", (unsigned int)info->properties2.properties.limits.minMemoryMapAlignment);
TRACE(" minTexelBufferOffsetAlignment: %#"PRIx64".\n", info->properties2.properties.limits.minTexelBufferOffsetAlignment);
TRACE(" minUniformBufferOffsetAlignment: %#"PRIx64".\n", info->properties2.properties.limits.minUniformBufferOffsetAlignment);
TRACE(" minStorageBufferOffsetAlignment: %#"PRIx64".\n", info->properties2.properties.limits.minStorageBufferOffsetAlignment);
TRACE(" minTexelOffset: %d.\n", info->properties2.properties.limits.minTexelOffset);
TRACE(" maxTexelOffset: %u.\n", info->properties2.properties.limits.maxTexelOffset);
TRACE(" minTexelGatherOffset: %d.\n", info->properties2.properties.limits.minTexelGatherOffset);
TRACE(" maxTexelGatherOffset: %u.\n", info->properties2.properties.limits.maxTexelGatherOffset);
TRACE(" minInterpolationOffset: %f.\n", info->properties2.properties.limits.minInterpolationOffset);
TRACE(" maxInterpolationOffset: %f.\n", info->properties2.properties.limits.maxInterpolationOffset);
TRACE(" subPixelInterpolationOffsetBits: %u.\n", info->properties2.properties.limits.subPixelInterpolationOffsetBits);
TRACE(" maxFramebufferWidth: %u.\n", info->properties2.properties.limits.maxFramebufferWidth);
TRACE(" maxFramebufferHeight: %u.\n", info->properties2.properties.limits.maxFramebufferHeight);
TRACE(" maxFramebufferLayers: %u.\n", info->properties2.properties.limits.maxFramebufferLayers);
TRACE(" framebufferColorSampleCounts: %#x.\n", info->properties2.properties.limits.framebufferColorSampleCounts);
TRACE(" framebufferDepthSampleCounts: %#x.\n", info->properties2.properties.limits.framebufferDepthSampleCounts);
TRACE(" framebufferStencilSampleCounts: %#x.\n", info->properties2.properties.limits.framebufferStencilSampleCounts);
TRACE(" framebufferNoAttachmentsSampleCounts: %#x.\n", info->properties2.properties.limits.framebufferNoAttachmentsSampleCounts);
TRACE(" maxColorAttachments: %u.\n", info->properties2.properties.limits.maxColorAttachments);
TRACE(" sampledImageColorSampleCounts: %#x.\n", info->properties2.properties.limits.sampledImageColorSampleCounts);
TRACE(" sampledImageIntegerSampleCounts: %#x.\n", info->properties2.properties.limits.sampledImageIntegerSampleCounts);
TRACE(" sampledImageDepthSampleCounts: %#x.\n", info->properties2.properties.limits.sampledImageDepthSampleCounts);
TRACE(" sampledImageStencilSampleCounts: %#x.\n", info->properties2.properties.limits.sampledImageStencilSampleCounts);
TRACE(" storageImageSampleCounts: %#x.\n", info->properties2.properties.limits.storageImageSampleCounts);
TRACE(" maxSampleMaskWords: %u.\n", info->properties2.properties.limits.maxSampleMaskWords);
TRACE(" timestampComputeAndGraphics: %#x.\n", info->properties2.properties.limits.timestampComputeAndGraphics);
TRACE(" timestampPeriod: %f.\n", info->properties2.properties.limits.timestampPeriod);
TRACE(" maxClipDistances: %u.\n", info->properties2.properties.limits.maxClipDistances);
TRACE(" maxCullDistances: %u.\n", info->properties2.properties.limits.maxCullDistances);
TRACE(" maxCombinedClipAndCullDistances: %u.\n", info->properties2.properties.limits.maxCombinedClipAndCullDistances);
TRACE(" discreteQueuePriorities: %u.\n", info->properties2.properties.limits.discreteQueuePriorities);
TRACE(" pointSizeRange: %f, %f.\n", info->properties2.properties.limits.pointSizeRange[0], info->properties2.properties.limits.pointSizeRange[1]);
TRACE(" lineWidthRange: %f, %f,\n", info->properties2.properties.limits.lineWidthRange[0], info->properties2.properties.limits.lineWidthRange[1]);
TRACE(" pointSizeGranularity: %f.\n", info->properties2.properties.limits.pointSizeGranularity);
TRACE(" lineWidthGranularity: %f.\n", info->properties2.properties.limits.lineWidthGranularity);
TRACE(" strictLines: %#x.\n", info->properties2.properties.limits.strictLines);
TRACE(" standardSampleLocations: %#x.\n", info->properties2.properties.limits.standardSampleLocations);
TRACE(" optimalBufferCopyOffsetAlignment: %#"PRIx64".\n", info->properties2.properties.limits.optimalBufferCopyOffsetAlignment);
TRACE(" optimalBufferCopyRowPitchAlignment: %#"PRIx64".\n", info->properties2.properties.limits.optimalBufferCopyRowPitchAlignment);
TRACE(" nonCoherentAtomSize: %#"PRIx64".\n", info->properties2.properties.limits.nonCoherentAtomSize);
TRACE(" VkPhysicalDeviceDescriptorIndexingPropertiesEXT:\n");
TRACE(" maxUpdateAfterBindDescriptorsInAllPools: %u.\n",
info->descriptor_indexing_properties.maxUpdateAfterBindDescriptorsInAllPools);
TRACE(" shaderUniformBufferArrayNonUniformIndexingNative: %#x.\n",
info->descriptor_indexing_properties.shaderUniformBufferArrayNonUniformIndexingNative);
TRACE(" shaderSampledImageArrayNonUniformIndexingNative: %#x.\n",
info->descriptor_indexing_properties.shaderSampledImageArrayNonUniformIndexingNative);
TRACE(" shaderStorageBufferArrayNonUniformIndexingNative: %#x.\n",
info->descriptor_indexing_properties.shaderStorageBufferArrayNonUniformIndexingNative);
TRACE(" shaderStorageImageArrayNonUniformIndexingNative: %#x.\n",
info->descriptor_indexing_properties.shaderStorageImageArrayNonUniformIndexingNative);
TRACE(" shaderInputAttachmentArrayNonUniformIndexingNative: %#x.\n",
info->descriptor_indexing_properties.shaderInputAttachmentArrayNonUniformIndexingNative);
TRACE(" robustBufferAccessUpdateAfterBind: %#x.\n",
info->descriptor_indexing_properties.robustBufferAccessUpdateAfterBind);
TRACE(" quadDivergentImplicitLod: %#x.\n",
info->descriptor_indexing_properties.quadDivergentImplicitLod);
TRACE(" maxPerStageDescriptorUpdateAfterBindSamplers: %u.\n",
info->descriptor_indexing_properties.maxPerStageDescriptorUpdateAfterBindSamplers);
TRACE(" maxPerStageDescriptorUpdateAfterBindUniformBuffers: %u.\n",
info->descriptor_indexing_properties.maxPerStageDescriptorUpdateAfterBindUniformBuffers);
TRACE(" maxPerStageDescriptorUpdateAfterBindStorageBuffers: %u.\n",
info->descriptor_indexing_properties.maxPerStageDescriptorUpdateAfterBindStorageBuffers);
TRACE(" maxPerStageDescriptorUpdateAfterBindSampledImages: %u.\n",
info->descriptor_indexing_properties.maxPerStageDescriptorUpdateAfterBindSampledImages);
TRACE(" maxPerStageDescriptorUpdateAfterBindStorageImages: %u.\n",
info->descriptor_indexing_properties.maxPerStageDescriptorUpdateAfterBindStorageImages);
TRACE(" maxPerStageDescriptorUpdateAfterBindInputAttachments: %u.\n",
info->descriptor_indexing_properties.maxPerStageDescriptorUpdateAfterBindInputAttachments);
TRACE(" maxPerStageUpdateAfterBindResources: %u.\n",
info->descriptor_indexing_properties.maxPerStageUpdateAfterBindResources);
TRACE(" maxDescriptorSetUpdateAfterBindSamplers: %u.\n",
info->descriptor_indexing_properties.maxDescriptorSetUpdateAfterBindSamplers);
TRACE(" maxDescriptorSetUpdateAfterBindUniformBuffers: %u.\n",
info->descriptor_indexing_properties.maxDescriptorSetUpdateAfterBindUniformBuffers);
TRACE(" maxDescriptorSetUpdateAfterBindUniformBuffersDynamic: %u.\n",
info->descriptor_indexing_properties.maxDescriptorSetUpdateAfterBindUniformBuffersDynamic);
TRACE(" maxDescriptorSetUpdateAfterBindStorageBuffers: %u.\n",
info->descriptor_indexing_properties.maxDescriptorSetUpdateAfterBindStorageBuffers);
TRACE(" maxDescriptorSetUpdateAfterBindStorageBuffersDynamic: %u.\n",
info->descriptor_indexing_properties.maxDescriptorSetUpdateAfterBindStorageBuffersDynamic);
TRACE(" maxDescriptorSetUpdateAfterBindSampledImages: %u.\n",
info->descriptor_indexing_properties.maxDescriptorSetUpdateAfterBindSampledImages);
TRACE(" maxDescriptorSetUpdateAfterBindStorageImages: %u.\n",
info->descriptor_indexing_properties.maxDescriptorSetUpdateAfterBindStorageImages);
TRACE(" maxDescriptorSetUpdateAfterBindInputAttachments: %u.\n",
info->descriptor_indexing_properties.maxDescriptorSetUpdateAfterBindInputAttachments);
TRACE(" maxPerSetDescriptors: %u.\n", info->maintenance3_properties.maxPerSetDescriptors);
TRACE(" maxMemoryAllocationSize: %#"PRIx64".\n", info->maintenance3_properties.maxMemoryAllocationSize);
TRACE(" VkPhysicalDeviceTexelBufferAlignmentPropertiesEXT:\n");
TRACE(" storageTexelBufferOffsetAlignmentBytes: %#"PRIx64".\n",
info->texel_buffer_alignment_properties.storageTexelBufferOffsetAlignmentBytes);
TRACE(" storageTexelBufferOffsetSingleTexelAlignment: %#x.\n",
info->texel_buffer_alignment_properties.storageTexelBufferOffsetSingleTexelAlignment);
TRACE(" uniformTexelBufferOffsetAlignmentBytes: %#"PRIx64".\n",
info->texel_buffer_alignment_properties.uniformTexelBufferOffsetAlignmentBytes);
TRACE(" uniformTexelBufferOffsetSingleTexelAlignment: %#x.\n",
info->texel_buffer_alignment_properties.uniformTexelBufferOffsetSingleTexelAlignment);
TRACE(" VkPhysicalDeviceTransformFeedbackPropertiesEXT:\n");
TRACE(" maxTransformFeedbackStreams: %u.\n", info->xfb_properties.maxTransformFeedbackStreams);
TRACE(" maxTransformFeedbackBuffers: %u.\n", info->xfb_properties.maxTransformFeedbackBuffers);
TRACE(" maxTransformFeedbackBufferSize: %#"PRIx64".\n", info->xfb_properties.maxTransformFeedbackBufferSize);
TRACE(" maxTransformFeedbackStreamDataSize: %u.\n", info->xfb_properties.maxTransformFeedbackStreamDataSize);
TRACE(" maxTransformFeedbackBufferDataSize: %u.\n", info->xfb_properties.maxTransformFeedbackBufferDataSize);
TRACE(" maxTransformFeedbackBufferDataStride: %u.\n", info->xfb_properties.maxTransformFeedbackBufferDataStride);
TRACE(" transformFeedbackQueries: %#x.\n", info->xfb_properties.transformFeedbackQueries);
TRACE(" transformFeedbackStreamsLinesTriangles: %#x.\n", info->xfb_properties.transformFeedbackStreamsLinesTriangles);
TRACE(" transformFeedbackRasterizationStreamSelect: %#x.\n", info->xfb_properties.transformFeedbackRasterizationStreamSelect);
TRACE(" transformFeedbackDraw: %x.\n", info->xfb_properties.transformFeedbackDraw);
TRACE(" VkPhysicalDeviceVertexAttributeDivisorPropertiesEXT:\n");
TRACE(" maxVertexAttribDivisor: %u.\n", info->vertex_divisor_properties.maxVertexAttribDivisor);
}
static void vkd3d_trace_physical_device_features(const struct vkd3d_physical_device_info *info)
{
TRACE("Device features:\n");
TRACE(" robustBufferAccess: %#x.\n", info->features2.features.robustBufferAccess);
TRACE(" fullDrawIndexUint32: %#x.\n", info->features2.features.fullDrawIndexUint32);
TRACE(" imageCubeArray: %#x.\n", info->features2.features.imageCubeArray);
TRACE(" independentBlend: %#x.\n", info->features2.features.independentBlend);
TRACE(" geometryShader: %#x.\n", info->features2.features.geometryShader);
TRACE(" tessellationShader: %#x.\n", info->features2.features.tessellationShader);
TRACE(" sampleRateShading: %#x.\n", info->features2.features.sampleRateShading);
TRACE(" dualSrcBlend: %#x.\n", info->features2.features.dualSrcBlend);
TRACE(" logicOp: %#x.\n", info->features2.features.logicOp);
TRACE(" multiDrawIndirect: %#x.\n", info->features2.features.multiDrawIndirect);
TRACE(" drawIndirectFirstInstance: %#x.\n", info->features2.features.drawIndirectFirstInstance);
TRACE(" depthClamp: %#x.\n", info->features2.features.depthClamp);
TRACE(" depthBiasClamp: %#x.\n", info->features2.features.depthBiasClamp);
TRACE(" fillModeNonSolid: %#x.\n", info->features2.features.fillModeNonSolid);
TRACE(" depthBounds: %#x.\n", info->features2.features.depthBounds);
TRACE(" wideLines: %#x.\n", info->features2.features.wideLines);
TRACE(" largePoints: %#x.\n", info->features2.features.largePoints);
TRACE(" alphaToOne: %#x.\n", info->features2.features.alphaToOne);
TRACE(" multiViewport: %#x.\n", info->features2.features.multiViewport);
TRACE(" samplerAnisotropy: %#x.\n", info->features2.features.samplerAnisotropy);
TRACE(" textureCompressionETC2: %#x.\n", info->features2.features.textureCompressionETC2);
TRACE(" textureCompressionASTC_LDR: %#x.\n", info->features2.features.textureCompressionASTC_LDR);
TRACE(" textureCompressionBC: %#x.\n", info->features2.features.textureCompressionBC);
TRACE(" occlusionQueryPrecise: %#x.\n", info->features2.features.occlusionQueryPrecise);
TRACE(" pipelineStatisticsQuery: %#x.\n", info->features2.features.pipelineStatisticsQuery);
TRACE(" vertexOipelineStoresAndAtomics: %#x.\n", info->features2.features.vertexPipelineStoresAndAtomics);
TRACE(" fragmentStoresAndAtomics: %#x.\n", info->features2.features.fragmentStoresAndAtomics);
TRACE(" shaderTessellationAndGeometryPointSize: %#x.\n", info->features2.features.shaderTessellationAndGeometryPointSize);
TRACE(" shaderImageGatherExtended: %#x.\n", info->features2.features.shaderImageGatherExtended);
TRACE(" shaderStorageImageExtendedFormats: %#x.\n", info->features2.features.shaderStorageImageExtendedFormats);
TRACE(" shaderStorageImageMultisample: %#x.\n", info->features2.features.shaderStorageImageMultisample);
TRACE(" shaderStorageImageReadWithoutFormat: %#x.\n", info->features2.features.shaderStorageImageReadWithoutFormat);
TRACE(" shaderStorageImageWriteWithoutFormat: %#x.\n", info->features2.features.shaderStorageImageWriteWithoutFormat);
TRACE(" shaderUniformBufferArrayDynamicIndexing: %#x.\n", info->features2.features.shaderUniformBufferArrayDynamicIndexing);
TRACE(" shaderSampledImageArrayDynamicIndexing: %#x.\n", info->features2.features.shaderSampledImageArrayDynamicIndexing);
TRACE(" shaderStorageBufferArrayDynamicIndexing: %#x.\n", info->features2.features.shaderStorageBufferArrayDynamicIndexing);
TRACE(" shaderStorageImageArrayDynamicIndexing: %#x.\n", info->features2.features.shaderStorageImageArrayDynamicIndexing);
TRACE(" shaderClipDistance: %#x.\n", info->features2.features.shaderClipDistance);
TRACE(" shaderCullDistance: %#x.\n", info->features2.features.shaderCullDistance);
TRACE(" shaderFloat64: %#x.\n", info->features2.features.shaderFloat64);
TRACE(" shaderInt64: %#x.\n", info->features2.features.shaderInt64);
TRACE(" shaderInt16: %#x.\n", info->features2.features.shaderInt16);
TRACE(" shaderResourceResidency: %#x.\n", info->features2.features.shaderResourceResidency);
TRACE(" shaderResourceMinLod: %#x.\n", info->features2.features.shaderResourceMinLod);
TRACE(" sparseBinding: %#x.\n", info->features2.features.sparseBinding);
TRACE(" sparseResidencyBuffer: %#x.\n", info->features2.features.sparseResidencyBuffer);
TRACE(" sparseResidencyImage2D: %#x.\n", info->features2.features.sparseResidencyImage2D);
TRACE(" sparseResidencyImage3D: %#x.\n", info->features2.features.sparseResidencyImage3D);
TRACE(" sparseResidency2Samples: %#x.\n", info->features2.features.sparseResidency2Samples);
TRACE(" sparseResidency4Samples: %#x.\n", info->features2.features.sparseResidency4Samples);
TRACE(" sparseResidency8Samples: %#x.\n", info->features2.features.sparseResidency8Samples);
TRACE(" sparseResidency16Samples: %#x.\n", info->features2.features.sparseResidency16Samples);
TRACE(" sparseResidencyAliased: %#x.\n", info->features2.features.sparseResidencyAliased);
TRACE(" variableMultisampleRate: %#x.\n", info->features2.features.variableMultisampleRate);
TRACE(" inheritedQueries: %#x.\n", info->features2.features.inheritedQueries);
TRACE(" VkPhysicalDeviceDescriptorIndexingFeaturesEXT:\n");
TRACE(" shaderInputAttachmentArrayDynamicIndexing: %#x.\n",
info->descriptor_indexing_features.shaderInputAttachmentArrayDynamicIndexing);
TRACE(" shaderUniformTexelBufferArrayDynamicIndexing: %#x.\n",
info->descriptor_indexing_features.shaderUniformTexelBufferArrayDynamicIndexing);
TRACE(" shaderStorageTexelBufferArrayDynamicIndexing: %#x.\n",
info->descriptor_indexing_features.shaderStorageTexelBufferArrayDynamicIndexing);
TRACE(" shaderUniformBufferArrayNonUniformIndexing: %#x.\n",
info->descriptor_indexing_features.shaderUniformBufferArrayNonUniformIndexing);
TRACE(" shaderSampledImageArrayNonUniformIndexing: %#x.\n",
info->descriptor_indexing_features.shaderSampledImageArrayNonUniformIndexing);
TRACE(" shaderStorageBufferArrayNonUniformIndexing: %#x.\n",
info->descriptor_indexing_features.shaderStorageBufferArrayNonUniformIndexing);
TRACE(" shaderStorageImageArrayNonUniformIndexing: %#x.\n",
info->descriptor_indexing_features.shaderStorageImageArrayNonUniformIndexing);
TRACE(" shaderInputAttachmentArrayNonUniformIndexing: %#x.\n",
info->descriptor_indexing_features.shaderInputAttachmentArrayNonUniformIndexing);
TRACE(" shaderUniformTexelBufferArrayNonUniformIndexing: %#x.\n",
info->descriptor_indexing_features.shaderUniformTexelBufferArrayNonUniformIndexing);
TRACE(" shaderStorageTexelBufferArrayNonUniformIndexing: %#x.\n",
info->descriptor_indexing_features.shaderStorageTexelBufferArrayNonUniformIndexing);
TRACE(" descriptorBindingUniformBufferUpdateAfterBind: %#x.\n",
info->descriptor_indexing_features.descriptorBindingUniformBufferUpdateAfterBind);
TRACE(" descriptorBindingSampledImageUpdateAfterBind: %#x.\n",
info->descriptor_indexing_features.descriptorBindingSampledImageUpdateAfterBind);
TRACE(" descriptorBindingStorageImageUpdateAfterBind: %#x.\n",
info->descriptor_indexing_features.descriptorBindingStorageImageUpdateAfterBind);
TRACE(" descriptorBindingStorageBufferUpdateAfterBind: %#x.\n",
info->descriptor_indexing_features.descriptorBindingStorageBufferUpdateAfterBind);
TRACE(" descriptorBindingUniformTexelBufferUpdateAfterBind: %#x.\n",
info->descriptor_indexing_features.descriptorBindingUniformTexelBufferUpdateAfterBind);
TRACE(" descriptorBindingStorageTexelBufferUpdateAfterBind: %#x.\n",
info->descriptor_indexing_features.descriptorBindingStorageTexelBufferUpdateAfterBind);
TRACE(" descriptorBindingUpdateUnusedWhilePending: %#x.\n",
info->descriptor_indexing_features.descriptorBindingUpdateUnusedWhilePending);
TRACE(" descriptorBindingPartiallyBound: %#x.\n",
info->descriptor_indexing_features.descriptorBindingPartiallyBound);
TRACE(" descriptorBindingVariableDescriptorCount: %#x.\n",
info->descriptor_indexing_features.descriptorBindingVariableDescriptorCount);
TRACE(" runtimeDescriptorArray: %#x.\n",
info->descriptor_indexing_features.runtimeDescriptorArray);
TRACE(" VkPhysicalDeviceConditionalRenderingFeaturesEXT:\n");
TRACE(" conditionalRendering: %#x.\n", info->conditional_rendering_features.conditionalRendering);
TRACE(" VkPhysicalDeviceDepthClipEnableFeaturesEXT:\n");
TRACE(" depthClipEnable: %#x.\n", info->depth_clip_features.depthClipEnable);
TRACE(" VkPhysicalDeviceShaderDemoteToHelperInvocationFeaturesEXT:\n");
TRACE(" shaderDemoteToHelperInvocation: %#x.\n", info->demote_features.shaderDemoteToHelperInvocation);
TRACE(" VkPhysicalDeviceTexelBufferAlignmentFeaturesEXT:\n");
TRACE(" texelBufferAlignment: %#x.\n", info->texel_buffer_alignment_features.texelBufferAlignment);
TRACE(" VkPhysicalDeviceTransformFeedbackFeaturesEXT:\n");
TRACE(" transformFeedback: %#x.\n", info->xfb_features.transformFeedback);
TRACE(" geometryStreams: %#x.\n", info->xfb_features.geometryStreams);
TRACE(" VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT:\n");
TRACE(" vertexAttributeInstanceRateDivisor: %#x.\n",
info->vertex_divisor_features.vertexAttributeInstanceRateDivisor);
TRACE(" vertexAttributeInstanceRateZeroDivisor: %#x.\n",
info->vertex_divisor_features.vertexAttributeInstanceRateZeroDivisor);
TRACE(" VkPhysicalDeviceCustomBorderColorFeaturesEXT:\n");
TRACE(" customBorderColors: %#x\n", info->custom_border_color_features.customBorderColors);
TRACE(" customBorderColorWithoutFormat: %#x\n", info->custom_border_color_features.customBorderColorWithoutFormat);
}
static HRESULT vkd3d_init_device_extensions(struct d3d12_device *device,
const struct vkd3d_device_create_info *create_info,
uint32_t *device_extension_count, bool *user_extension_supported)
{
const struct vkd3d_vk_instance_procs *vk_procs = &device->vkd3d_instance->vk_procs;
VkPhysicalDevice physical_device = device->vk_physical_device;
struct vkd3d_vulkan_info *vulkan_info = &device->vk_info;
VkExtensionProperties *vk_extensions;
uint32_t count;
VkResult vr;
*device_extension_count = 0;
if ((vr = VK_CALL(vkEnumerateDeviceExtensionProperties(physical_device, NULL, &count, NULL))) < 0)
{
ERR("Failed to enumerate device extensions, vr %d.\n", vr);
return hresult_from_vk_result(vr);
}
if (!count)
return S_OK;
if (!(vk_extensions = vkd3d_calloc(count, sizeof(*vk_extensions))))
return E_OUTOFMEMORY;
TRACE("Enumerating %u device extensions.\n", count);
if ((vr = VK_CALL(vkEnumerateDeviceExtensionProperties(physical_device, NULL, &count, vk_extensions))) < 0)
{
ERR("Failed to enumerate device extensions, vr %d.\n", vr);
vkd3d_free(vk_extensions);
return hresult_from_vk_result(vr);
}
*device_extension_count = vkd3d_check_extensions(vk_extensions, count, NULL, 0,
optional_device_extensions, ARRAY_SIZE(optional_device_extensions),
create_info->device_extensions,
create_info->device_extension_count,
create_info->optional_device_extensions,
create_info->optional_device_extension_count,
user_extension_supported, vulkan_info, "device");
if (get_spec_version(vk_extensions, count, VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME) < 3)
vulkan_info->EXT_vertex_attribute_divisor = false;
vkd3d_free(vk_extensions);
return S_OK;
}
static HRESULT vkd3d_init_device_caps(struct d3d12_device *device,
const struct vkd3d_device_create_info *create_info,
struct vkd3d_physical_device_info *physical_device_info)
{
const struct vkd3d_vk_instance_procs *vk_procs = &device->vkd3d_instance->vk_procs;
VkPhysicalDeviceAccelerationStructureFeaturesKHR *acceleration_structure;
VkPhysicalDeviceBufferDeviceAddressFeaturesKHR *buffer_device_address;
VkPhysicalDeviceDescriptorIndexingFeaturesEXT *descriptor_indexing;
VkPhysicalDevice physical_device = device->vk_physical_device;
struct vkd3d_vulkan_info *vulkan_info = &device->vk_info;
VkPhysicalDeviceFeatures *features;
vkd3d_trace_physical_device(physical_device, physical_device_info, vk_procs);
vkd3d_trace_physical_device_features(physical_device_info);
vkd3d_trace_physical_device_limits(physical_device_info);
features = &physical_device_info->features2.features;
if (!features->sparseResidencyBuffer || !features->sparseResidencyImage2D)
{
features->sparseResidencyBuffer = VK_FALSE;
features->sparseResidencyImage2D = VK_FALSE;
physical_device_info->properties2.properties.sparseProperties.residencyNonResidentStrict = VK_FALSE;
}
vulkan_info->device_limits = physical_device_info->properties2.properties.limits;
vulkan_info->sparse_properties = physical_device_info->properties2.properties.sparseProperties;
vulkan_info->rasterization_stream = physical_device_info->xfb_properties.transformFeedbackRasterizationStreamSelect;
vulkan_info->transform_feedback_queries = physical_device_info->xfb_properties.transformFeedbackQueries;
vulkan_info->max_vertex_attrib_divisor = max(physical_device_info->vertex_divisor_properties.maxVertexAttribDivisor, 1);
if (!physical_device_info->conditional_rendering_features.conditionalRendering)
vulkan_info->EXT_conditional_rendering = false;
if (!physical_device_info->depth_clip_features.depthClipEnable)
vulkan_info->EXT_depth_clip_enable = false;
if (!physical_device_info->demote_features.shaderDemoteToHelperInvocation)
vulkan_info->EXT_shader_demote_to_helper_invocation = false;
if (!physical_device_info->texel_buffer_alignment_features.texelBufferAlignment)
vulkan_info->EXT_texel_buffer_alignment = false;
vulkan_info->texel_buffer_alignment_properties = physical_device_info->texel_buffer_alignment_properties;
vulkan_info->vertex_attrib_zero_divisor = physical_device_info->vertex_divisor_features.vertexAttributeInstanceRateZeroDivisor;
/* Disable unused Vulkan features. */
features->shaderTessellationAndGeometryPointSize = VK_FALSE;
buffer_device_address = &physical_device_info->buffer_device_address_features;
buffer_device_address->bufferDeviceAddressCaptureReplay = VK_FALSE;
buffer_device_address->bufferDeviceAddressMultiDevice = VK_FALSE;
descriptor_indexing = &physical_device_info->descriptor_indexing_features;
descriptor_indexing->shaderInputAttachmentArrayDynamicIndexing = VK_FALSE;
descriptor_indexing->shaderInputAttachmentArrayNonUniformIndexing = VK_FALSE;
acceleration_structure = &physical_device_info->acceleration_structure_features;
acceleration_structure->accelerationStructureCaptureReplay = VK_FALSE;
/* Don't need or require these. */
physical_device_info->extended_dynamic_state2_features.extendedDynamicState2LogicOp = VK_FALSE;
physical_device_info->extended_dynamic_state2_features.extendedDynamicState2PatchControlPoints = VK_FALSE;
if (!physical_device_info->descriptor_indexing_properties.robustBufferAccessUpdateAfterBind)
{
/* Generally, we cannot enable robustness if this is not supported,
* but this means we cannot support D3D12 at all, so just disabling robustBufferAccess is not a viable option.
* This can be observed on RADV, where this feature for some reason is not supported at all,
* but this apparently was just a missed feature bit.
* https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/9281.
*
* Another reason to not disable it, is that we will end up with two
* split application infos in Fossilize, which is annoying for pragmatic reasons.
* Validation does not appear to complain, so we just go ahead and enable robustness anyways. */
WARN("Device does not expose robust buffer access for the update after bind feature, enabling it anyways.\n");
}
if (!vulkan_info->KHR_timeline_semaphore)
{
ERR("Timeline semaphores are not supported by this implementation. This is required for correct operation.\n");
return E_INVALIDARG;
}
if (!physical_device_info->separate_depth_stencil_layout_features.separateDepthStencilLayouts)
{
ERR("separateDepthStencilLayouts is not supported by this implementation. This is required for correct operation.\n");
return E_INVALIDARG;
}
if (!vulkan_info->KHR_sampler_mirror_clamp_to_edge)
{
ERR("KHR_sampler_mirror_clamp_to_edge is not supported by this implementation. This is required for correct operation.\n");
return E_INVALIDARG;
}
if (!physical_device_info->robustness2_features.nullDescriptor)
{
ERR("Null descriptor in VK_EXT_robustness2 is not supported by this implementation. This is required for correct operation.\n");
return E_INVALIDARG;
}
if (vulkan_info->KHR_fragment_shading_rate)
physical_device_info->additional_shading_rates_supported = d3d12_device_determine_additional_shading_rates_supported(device);
if (!physical_device_info->shader_draw_parameters_features.shaderDrawParameters)
{
ERR("shaderDrawParameters is not supported by this implementation. This is required for correct operation.\n");
return E_INVALIDARG;
}
if (!vulkan_info->KHR_bind_memory2)
{
ERR("KHR_bind_memory2 is not supported by this implementation. This is required for correct operation.\n");
return E_INVALIDARG;
}
if (!vulkan_info->KHR_copy_commands2)
{
ERR("KHR_copy_commands2 is not supported by this implementation. This is required for correct operation.\n");
return E_INVALIDARG;
}
if (!physical_device_info->dynamic_rendering_features.dynamicRendering)
{
ERR("KHR_dynamic_rendering is not supported by this implementation. This is required for correct operation.\n");
return E_INVALIDARG;
}
if (!physical_device_info->extended_dynamic_state_features.extendedDynamicState)
{
ERR("EXT_extended_dynamic_state is not supported by this implementation. This is required for correct operation.\n");
return E_INVALIDARG;
}
if (!physical_device_info->extended_dynamic_state2_features.extendedDynamicState2)
{
ERR("EXT_extended_dynamic_state2 is not supported by this implementation. This is required for correct operation.\n");
return E_INVALIDARG;
}
if (!physical_device_info->maintenance4_features.maintenance4)
{
ERR("KHR_maintenance4 is not supported by this implementation. This is required for correct operation.\n");
return E_INVALIDARG;
}
return S_OK;
}
static HRESULT vkd3d_select_physical_device(struct vkd3d_instance *instance,
unsigned int device_index, VkPhysicalDevice *selected_device)
{
VkPhysicalDevice dgpu_device = VK_NULL_HANDLE, igpu_device = VK_NULL_HANDLE;
const struct vkd3d_vk_instance_procs *vk_procs = &instance->vk_procs;
VkInstance vk_instance = instance->vk_instance;
VkPhysicalDeviceProperties device_properties;
VkPhysicalDevice device = VK_NULL_HANDLE;
VkPhysicalDevice *physical_devices;
char filter[VKD3D_PATH_MAX];
bool has_filter;
uint32_t count;
unsigned int i;
VkResult vr;
has_filter = vkd3d_get_env_var("VKD3D_FILTER_DEVICE_NAME", filter, sizeof(filter));
count = 0;
if ((vr = VK_CALL(vkEnumeratePhysicalDevices(vk_instance, &count, NULL))) < 0)
{
ERR("Failed to enumerate physical devices, vr %d.\n", vr);
return hresult_from_vk_result(vr);
}
if (!count)
{
ERR("No physical device available.\n");
return E_FAIL;
}
if (!(physical_devices = vkd3d_calloc(count, sizeof(*physical_devices))))
return E_OUTOFMEMORY;
TRACE("Enumerating %u physical device(s).\n", count);
if ((vr = VK_CALL(vkEnumeratePhysicalDevices(vk_instance, &count, physical_devices))) < 0)
{
ERR("Failed to enumerate physical devices, vr %d.\n", vr);
vkd3d_free(physical_devices);
return hresult_from_vk_result(vr);
}
if (device_index != ~0u && device_index >= count)
WARN("Device index %u is out of range.\n", device_index);
for (i = 0; i < count; ++i)
{
VK_CALL(vkGetPhysicalDeviceProperties(physical_devices[i], &device_properties));
vkd3d_trace_physical_device_properties(&device_properties);
if (device_properties.apiVersion < VKD3D_MIN_API_VERSION)
{
WARN("Physical device %u does not support required Vulkan version, ignoring.\n", i);
continue;
}
if (i == device_index)
device = physical_devices[i];
if (has_filter && !strstr(device_properties.deviceName, filter))
{
INFO("Device %s doesn't match filter %s, skipping.\n", device_properties.deviceName, filter);
continue;
}
if (device_properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU && !dgpu_device)
dgpu_device = physical_devices[i];
else if (device_properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU && !igpu_device)
igpu_device = physical_devices[i];
}
if (!device)
device = dgpu_device ? dgpu_device : igpu_device;
if (!device)
device = physical_devices[0];
vkd3d_free(physical_devices);
VK_CALL(vkGetPhysicalDeviceProperties(device, &device_properties));
TRACE("Using device: %s, %#x:%#x.\n", device_properties.deviceName,
device_properties.vendorID, device_properties.deviceID);
*selected_device = device;
return S_OK;
}
/* Vulkan queues */
struct vkd3d_device_queue_info
{
unsigned int family_index[VKD3D_QUEUE_FAMILY_COUNT];
VkQueueFamilyProperties vk_properties[VKD3D_QUEUE_FAMILY_COUNT];
unsigned int vk_family_count;
VkDeviceQueueCreateInfo vk_queue_create_info[VKD3D_QUEUE_FAMILY_COUNT];
};
static void d3d12_device_destroy_vkd3d_queues(struct d3d12_device *device)
{
unsigned int i, j;
for (i = 0; i < VKD3D_QUEUE_FAMILY_COUNT; i++)
{
struct vkd3d_queue_family_info *queue_family = device->queue_families[i];
if (!queue_family)
continue;
/* Don't destroy the same queue family twice */
for (j = i; j < VKD3D_QUEUE_FAMILY_COUNT; j++)
{
if (device->queue_families[j] == queue_family)
device->queue_families[j] = NULL;
}
for (j = 0; j < queue_family->queue_count; j++)
{
if (queue_family->queues[j])
vkd3d_queue_destroy(queue_family->queues[j], device);
}
vkd3d_free(queue_family->queues);
vkd3d_free(queue_family);
}
}
static HRESULT d3d12_device_create_vkd3d_queues(struct d3d12_device *device,
const struct vkd3d_device_queue_info *queue_info)
{
unsigned int i, j, k;
HRESULT hr;
device->unique_queue_mask = 0;
device->queue_family_count = 0;
memset(device->queue_families, 0, sizeof(device->queue_families));
memset(device->queue_family_indices, 0, sizeof(device->queue_family_indices));
for (i = 0, k = 0; i < VKD3D_QUEUE_FAMILY_COUNT; i++)
{
struct vkd3d_queue_family_info *info;
if (queue_info->family_index[i] == VK_QUEUE_FAMILY_IGNORED)
continue;
for (j = 0; j < i; j++)
{
if (queue_info->family_index[i] == queue_info->family_index[j])
device->queue_families[i] = device->queue_families[j];
}
if (device->queue_families[i])
continue;
if (!(info = vkd3d_calloc(1, sizeof(*info))))
{
hr = E_OUTOFMEMORY;
goto out_destroy_queues;
}
info->queue_count = queue_info->vk_queue_create_info[k++].queueCount;
if (!(info->queues = vkd3d_calloc(info->queue_count, sizeof(*info->queues))))
{
hr = E_OUTOFMEMORY;
goto out_destroy_queues;
}
for (j = 0; j < info->queue_count; j++)
{
if (FAILED((hr = vkd3d_queue_create(device, queue_info->family_index[i],
j, &queue_info->vk_properties[i], &info->queues[j]))))
goto out_destroy_queues;
}
info->vk_family_index = queue_info->family_index[i];
info->vk_queue_flags = queue_info->vk_properties[i].queueFlags;
info->timestamp_bits = queue_info->vk_properties[i].timestampValidBits;
device->queue_families[i] = info;
device->queue_family_indices[device->queue_family_count++] = info->vk_family_index;
if (info->queue_count && i != VKD3D_QUEUE_FAMILY_INTERNAL_COMPUTE)
device->unique_queue_mask |= 1u << i;
}
return S_OK;
out_destroy_queues:
d3d12_device_destroy_vkd3d_queues(device);
return hr;
}
#define VKD3D_MAX_QUEUE_COUNT_PER_FAMILY (4u)
static float queue_priorities[] = {1.0f, 1.0f, 1.0f, 1.0f};
static uint32_t vkd3d_find_queue(unsigned int count, const VkQueueFamilyProperties *properties,
VkQueueFlags mask, VkQueueFlags flags)
{
unsigned int i;
for (i = 0; i < count; i++)
{
if ((properties[i].queueFlags & mask) == flags)
return i;
}
return VK_QUEUE_FAMILY_IGNORED;
}
static HRESULT vkd3d_select_queues(const struct vkd3d_instance *vkd3d_instance,
VkPhysicalDevice physical_device, struct vkd3d_device_queue_info *info)
{
const struct vkd3d_vk_instance_procs *vk_procs = &vkd3d_instance->vk_procs;
VkQueueFamilyProperties *queue_properties = NULL;
VkDeviceQueueCreateInfo *queue_info = NULL;
bool duplicate, single_queue;
unsigned int i, j;
uint32_t count;
memset(info, 0, sizeof(*info));
single_queue = !!(vkd3d_config_flags & VKD3D_CONFIG_FLAG_SINGLE_QUEUE);
VK_CALL(vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &count, NULL));
if (!(queue_properties = vkd3d_calloc(count, sizeof(*queue_properties))))
return E_OUTOFMEMORY;
VK_CALL(vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &count, queue_properties));
info->family_index[VKD3D_QUEUE_FAMILY_GRAPHICS] = vkd3d_find_queue(count, queue_properties,
VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT, VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT);
info->family_index[VKD3D_QUEUE_FAMILY_COMPUTE] = vkd3d_find_queue(count, queue_properties,
VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT, VK_QUEUE_COMPUTE_BIT);
info->family_index[VKD3D_QUEUE_FAMILY_SPARSE_BINDING] = vkd3d_find_queue(count, queue_properties,
VK_QUEUE_SPARSE_BINDING_BIT, VK_QUEUE_SPARSE_BINDING_BIT);
if (info->family_index[VKD3D_QUEUE_FAMILY_COMPUTE] == VK_QUEUE_FAMILY_IGNORED)
info->family_index[VKD3D_QUEUE_FAMILY_COMPUTE] = info->family_index[VKD3D_QUEUE_FAMILY_GRAPHICS];
/* Prefer compute queues for transfer. When using concurrent sharing, DMA queue tends to force compression off. */
info->family_index[VKD3D_QUEUE_FAMILY_TRANSFER] = info->family_index[VKD3D_QUEUE_FAMILY_COMPUTE];
info->family_index[VKD3D_QUEUE_FAMILY_INTERNAL_COMPUTE] = info->family_index[VKD3D_QUEUE_FAMILY_COMPUTE];
if (single_queue)
{
info->family_index[VKD3D_QUEUE_FAMILY_COMPUTE] = info->family_index[VKD3D_QUEUE_FAMILY_GRAPHICS];
info->family_index[VKD3D_QUEUE_FAMILY_TRANSFER] = info->family_index[VKD3D_QUEUE_FAMILY_GRAPHICS];
}
for (i = 0; i < VKD3D_QUEUE_FAMILY_COUNT; ++i)
{
if (info->family_index[i] == VK_QUEUE_FAMILY_IGNORED)
continue;
info->vk_properties[i] = queue_properties[info->family_index[i]];
/* Ensure that we don't create the same queue multiple times */
duplicate = false;
for (j = 0; j < i && !duplicate; j++)
duplicate = info->family_index[i] == info->family_index[j];
if (duplicate)
continue;
queue_info = &info->vk_queue_create_info[info->vk_family_count++];
queue_info->sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queue_info->pNext = NULL;
queue_info->flags = 0;
queue_info->queueFamilyIndex = info->family_index[i];
queue_info->queueCount = min(info->vk_properties[i].queueCount, VKD3D_MAX_QUEUE_COUNT_PER_FAMILY);
queue_info->pQueuePriorities = queue_priorities;
if (single_queue)
queue_info->queueCount = 1;
}
vkd3d_free(queue_properties);
if (info->family_index[VKD3D_QUEUE_FAMILY_GRAPHICS] == VK_QUEUE_FAMILY_IGNORED)
{
ERR("Could not find a suitable queue family for a direct command queue.\n");
return E_FAIL;
}
return S_OK;
}
static HRESULT vkd3d_create_vk_device(struct d3d12_device *device,
const struct vkd3d_device_create_info *create_info)
{
const struct vkd3d_vk_instance_procs *vk_procs = &device->vkd3d_instance->vk_procs;
struct vkd3d_device_queue_info device_queue_info;
VkPhysicalDeviceProperties device_properties;
bool *user_extension_supported = NULL;
VkPhysicalDevice physical_device;
VkDeviceCreateInfo device_info;
unsigned int device_index;
uint32_t extension_count;
const char **extensions;
VkDevice vk_device;
VkResult vr;
HRESULT hr;
TRACE("device %p, create_info %p.\n", device, create_info);
physical_device = create_info->vk_physical_device;
device_index = vkd3d_env_var_as_uint("VKD3D_VULKAN_DEVICE", ~0u);
if ((!physical_device || device_index != ~0u)
&& FAILED(hr = vkd3d_select_physical_device(device->vkd3d_instance, device_index, &physical_device)))
return hr;
device->vk_physical_device = physical_device;
VK_CALL(vkGetPhysicalDeviceProperties(device->vk_physical_device, &device_properties));
device->api_version = min(device_properties.apiVersion, VKD3D_MAX_API_VERSION);
if (FAILED(hr = vkd3d_select_queues(device->vkd3d_instance, physical_device, &device_queue_info)))
return hr;
TRACE("Using queue family %u for direct command queues.\n",
device_queue_info.family_index[VKD3D_QUEUE_FAMILY_GRAPHICS]);
TRACE("Using queue family %u for compute command queues.\n",
device_queue_info.family_index[VKD3D_QUEUE_FAMILY_COMPUTE]);
TRACE("Using queue family %u for copy command queues.\n",
device_queue_info.family_index[VKD3D_QUEUE_FAMILY_TRANSFER]);
TRACE("Using queue family %u for sparse binding.\n",
device_queue_info.family_index[VKD3D_QUEUE_FAMILY_SPARSE_BINDING]);
VK_CALL(vkGetPhysicalDeviceMemoryProperties(physical_device, &device->memory_properties));
if (create_info->optional_device_extension_count)
{
if (!(user_extension_supported = vkd3d_calloc(create_info->optional_device_extension_count, sizeof(bool))))
return E_OUTOFMEMORY;
}
if (FAILED(hr = vkd3d_init_device_extensions(device, create_info,
&extension_count, user_extension_supported)))
{
vkd3d_free(user_extension_supported);
return hr;
}
vkd3d_physical_device_info_init(&device->device_info, device);
vkd3d_physical_device_info_apply_workarounds(&device->device_info);
if (FAILED(hr = vkd3d_init_device_caps(device, create_info, &device->device_info)))
{
vkd3d_free(user_extension_supported);
return hr;
}
if (!(extensions = vkd3d_calloc(extension_count, sizeof(*extensions))))
{
vkd3d_free(user_extension_supported);
return E_OUTOFMEMORY;
}
/* Create device */
device_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
device_info.pNext = device->device_info.features2.pNext;
device_info.flags = 0;
device_info.queueCreateInfoCount = device_queue_info.vk_family_count;
device_info.pQueueCreateInfos = device_queue_info.vk_queue_create_info;
device_info.enabledLayerCount = 0;
device_info.ppEnabledLayerNames = NULL;
device_info.enabledExtensionCount = vkd3d_enable_extensions(extensions, NULL, 0,
optional_device_extensions, ARRAY_SIZE(optional_device_extensions),
create_info->device_extensions,
create_info->device_extension_count,
create_info->optional_device_extensions,
create_info->optional_device_extension_count,
user_extension_supported, &device->vk_info);
2016-10-13 12:50:36 +01:00
device_info.ppEnabledExtensionNames = extensions;
device_info.pEnabledFeatures = &device->device_info.features2.features;
vkd3d_free(user_extension_supported);
vr = VK_CALL(vkCreateDevice(physical_device, &device_info, NULL, &vk_device));
if (vr == VK_ERROR_INITIALIZATION_FAILED &&
vkd3d_disable_nvx_extensions(device, extensions, &device_info.enabledExtensionCount))
{
WARN("Disabled extensions that can cause Vulkan device creation to fail, retrying.\n");
vr = VK_CALL(vkCreateDevice(physical_device, &device_info, NULL, &vk_device));
}
vkd3d_free((void *)extensions);
if (vr < 0)
{
ERR("Failed to create Vulkan device, vr %d.\n", vr);
return hresult_from_vk_result(vr);
}
if (FAILED(hr = vkd3d_load_vk_device_procs(&device->vk_procs, vk_procs, vk_device)))
{
ERR("Failed to load device procs, hr %#x.\n", hr);
if (device->vk_procs.vkDestroyDevice)
device->vk_procs.vkDestroyDevice(vk_device, NULL);
return hr;
}
device->vk_device = vk_device;
if (FAILED(hr = d3d12_device_create_vkd3d_queues(device, &device_queue_info)))
{
ERR("Failed to create queues, hr %#x.\n", hr);
device->vk_procs.vkDestroyDevice(vk_device, NULL);
return hr;
}
TRACE("Created Vulkan device %p.\n", vk_device);
return hr;
}
struct d3d12_device_singleton
{
struct list entry;
LUID adapter_luid;
struct d3d12_device *device;
};
static pthread_mutex_t d3d12_device_map_mutex = PTHREAD_MUTEX_INITIALIZER;
static struct list d3d12_device_map = LIST_INIT(d3d12_device_map);
static struct d3d12_device *d3d12_find_device_singleton(LUID luid)
{
struct d3d12_device_singleton* current;
LIST_FOR_EACH_ENTRY(current, &d3d12_device_map, struct d3d12_device_singleton, entry)
{
if (!memcmp(&current->adapter_luid, &luid, sizeof(LUID)))
return current->device;
}
return NULL;
}
static void d3d12_add_device_singleton(struct d3d12_device *device, LUID luid)
{
struct d3d12_device_singleton *e;
if (!(e = vkd3d_malloc(sizeof(*e))))
{
ERR("Failed to register device singleton for adapter.");
return;
}
e->adapter_luid = luid;
e->device = device;
list_add_tail(&d3d12_device_map, &e->entry);
}
static void d3d12_remove_device_singleton(LUID luid)
{
struct d3d12_device_singleton *current;
LIST_FOR_EACH_ENTRY(current, &d3d12_device_map, struct d3d12_device_singleton, entry)
{
if (!memcmp(&current->adapter_luid, &luid, sizeof(LUID)))
{
list_remove(&current->entry);
vkd3d_free(current);
return;
}
}
}
static HRESULT d3d12_device_create_scratch_buffer(struct d3d12_device *device, enum vkd3d_scratch_pool_kind kind,
VkDeviceSize size, uint32_t memory_types, struct vkd3d_scratch_buffer *scratch)
{
HRESULT hr;
TRACE("device %p, size %llu, scratch %p.\n", device, size, scratch);
if (kind == VKD3D_SCRATCH_POOL_KIND_DEVICE_STORAGE)
{
struct vkd3d_allocate_heap_memory_info alloc_info;
/* We only care about memory types for INDIRECT_PREPROCESS. */
assert(memory_types == ~0u);
memset(&alloc_info, 0, sizeof(alloc_info));
alloc_info.heap_desc.Properties.Type = D3D12_HEAP_TYPE_DEFAULT;
alloc_info.heap_desc.SizeInBytes = size;
alloc_info.heap_desc.Alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;
alloc_info.heap_desc.Flags = D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS | D3D12_HEAP_FLAG_CREATE_NOT_ZEROED;
alloc_info.extra_allocation_flags = VKD3D_ALLOCATION_FLAG_INTERNAL_SCRATCH;
if (FAILED(hr = vkd3d_allocate_heap_memory(device, &device->memory_allocator,
&alloc_info, &scratch->allocation)))
return hr;
}
else if (kind == VKD3D_SCRATCH_POOL_KIND_INDIRECT_PREPROCESS)
{
struct vkd3d_allocate_memory_info alloc_info;
memset(&alloc_info, 0, sizeof(alloc_info));
alloc_info.heap_properties.Type = D3D12_HEAP_TYPE_DEFAULT;
alloc_info.memory_requirements.size = size;
alloc_info.memory_requirements.memoryTypeBits = memory_types;
alloc_info.memory_requirements.alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;
alloc_info.heap_flags = D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS | D3D12_HEAP_FLAG_CREATE_NOT_ZEROED;
alloc_info.optional_memory_properties = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
alloc_info.flags = VKD3D_ALLOCATION_FLAG_GLOBAL_BUFFER | VKD3D_ALLOCATION_FLAG_INTERNAL_SCRATCH;
if (FAILED(hr = vkd3d_allocate_memory(device, &device->memory_allocator,
&alloc_info, &scratch->allocation)))
return hr;
}
else
{
return E_INVALIDARG;
}
scratch->offset = 0;
return S_OK;
}
static void d3d12_device_destroy_scratch_buffer(struct d3d12_device *device, const struct vkd3d_scratch_buffer *scratch)
{
TRACE("device %p, scratch %p.\n", device, scratch);
vkd3d_free_memory(device, &device->memory_allocator, &scratch->allocation);
}
HRESULT d3d12_device_get_scratch_buffer(struct d3d12_device *device, enum vkd3d_scratch_pool_kind kind,
VkDeviceSize min_size, uint32_t memory_types, struct vkd3d_scratch_buffer *scratch)
{
struct d3d12_device_scratch_pool *pool = &device->scratch_pools[kind];
struct vkd3d_scratch_buffer *candidate;
size_t i;
if (min_size > VKD3D_SCRATCH_BUFFER_SIZE)
return d3d12_device_create_scratch_buffer(device, kind, min_size, memory_types, scratch);
pthread_mutex_lock(&device->mutex);
for (i = pool->scratch_buffer_count; i; i--)
{
candidate = &pool->scratch_buffers[i - 1];
/* Extremely unlikely to fail since we have separate lists per pool kind, but to be 100% correct ... */
if (memory_types & (1u << candidate->allocation.device_allocation.vk_memory_type))
{
*scratch = *candidate;
scratch->offset = 0;
pool->scratch_buffers[i - 1] = pool->scratch_buffers[--pool->scratch_buffer_count];
pthread_mutex_unlock(&device->mutex);
return S_OK;
}
}
pthread_mutex_unlock(&device->mutex);
return d3d12_device_create_scratch_buffer(device, kind, VKD3D_SCRATCH_BUFFER_SIZE, memory_types, scratch);
}
void d3d12_device_return_scratch_buffer(struct d3d12_device *device, enum vkd3d_scratch_pool_kind kind,
const struct vkd3d_scratch_buffer *scratch)
{
struct d3d12_device_scratch_pool *pool = &device->scratch_pools[kind];
pthread_mutex_lock(&device->mutex);
if (scratch->allocation.resource.size == VKD3D_SCRATCH_BUFFER_SIZE &&
pool->scratch_buffer_count < VKD3D_SCRATCH_BUFFER_COUNT)
{
pool->scratch_buffers[pool->scratch_buffer_count++] = *scratch;
pthread_mutex_unlock(&device->mutex);
}
else
{
pthread_mutex_unlock(&device->mutex);
d3d12_device_destroy_scratch_buffer(device, scratch);
}
}
uint64_t d3d12_device_get_descriptor_heap_gpu_va(struct d3d12_device *device)
{
uint64_t va;
/* The virtual GPU descriptor VAs are of form (unique << 32) | (desc index * sizeof(d3d12_desc)),
* which simplifies local root signature tables.
* Also simplifies SetRootDescriptorTable since we can deduce offset without memory lookups. */
pthread_mutex_lock(&device->mutex);
if (device->descriptor_heap_gpu_va_count)
va = device->descriptor_heap_gpu_vas[--device->descriptor_heap_gpu_va_count];
else
va = ++device->descriptor_heap_gpu_next;
pthread_mutex_unlock(&device->mutex);
va <<= 32;
return va;
}
void d3d12_device_return_descriptor_heap_gpu_va(struct d3d12_device *device, uint64_t va)
{
pthread_mutex_lock(&device->mutex);
vkd3d_array_reserve((void **)&device->descriptor_heap_gpu_vas, &device->descriptor_heap_gpu_va_size,
device->descriptor_heap_gpu_va_count + 1, sizeof(*device->descriptor_heap_gpu_vas));
device->descriptor_heap_gpu_vas[device->descriptor_heap_gpu_va_count++] = (uint32_t)(va >> 32);
pthread_mutex_unlock(&device->mutex);
}
static HRESULT d3d12_device_create_query_pool(struct d3d12_device *device, uint32_t type_index, struct vkd3d_query_pool *pool)
{
const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
VkQueryPoolCreateInfo pool_info;
VkResult vr;
TRACE("device %p, type_index %u, pool %p.\n", device, type_index, pool);
pool_info.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO;
pool_info.pNext = NULL;
pool_info.flags = 0;
pool_info.pipelineStatistics = 0;
switch (type_index)
{
case VKD3D_QUERY_TYPE_INDEX_OCCLUSION:
/* Expect a large number of occlusion queries
* to be used within a single command list */
pool_info.queryType = VK_QUERY_TYPE_OCCLUSION;
pool_info.queryCount = 4096;
break;
case VKD3D_QUERY_TYPE_INDEX_PIPELINE_STATISTICS:
pool_info.queryType = VK_QUERY_TYPE_PIPELINE_STATISTICS;
pool_info.queryCount = 128;
pool_info.pipelineStatistics =
VK_QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_VERTICES_BIT |
VK_QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_PRIMITIVES_BIT |
VK_QUERY_PIPELINE_STATISTIC_VERTEX_SHADER_INVOCATIONS_BIT |
VK_QUERY_PIPELINE_STATISTIC_GEOMETRY_SHADER_INVOCATIONS_BIT |
VK_QUERY_PIPELINE_STATISTIC_GEOMETRY_SHADER_PRIMITIVES_BIT |
VK_QUERY_PIPELINE_STATISTIC_CLIPPING_INVOCATIONS_BIT |
VK_QUERY_PIPELINE_STATISTIC_CLIPPING_PRIMITIVES_BIT |
VK_QUERY_PIPELINE_STATISTIC_FRAGMENT_SHADER_INVOCATIONS_BIT |
VK_QUERY_PIPELINE_STATISTIC_TESSELLATION_CONTROL_SHADER_PATCHES_BIT |
VK_QUERY_PIPELINE_STATISTIC_TESSELLATION_EVALUATION_SHADER_INVOCATIONS_BIT |
VK_QUERY_PIPELINE_STATISTIC_COMPUTE_SHADER_INVOCATIONS_BIT;
break;
case VKD3D_QUERY_TYPE_INDEX_TRANSFORM_FEEDBACK:
pool_info.queryType = VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT;
pool_info.queryCount = 128;
break;
case VKD3D_QUERY_TYPE_INDEX_RT_COMPACTED_SIZE:
pool_info.queryType = VK_QUERY_TYPE_ACCELERATION_STRUCTURE_COMPACTED_SIZE_KHR;
pool_info.queryCount = 128;
break;
case VKD3D_QUERY_TYPE_INDEX_RT_CURRENT_SIZE:
pool_info.queryType = VK_QUERY_TYPE_ACCELERATION_STRUCTURE_SIZE_KHR;
pool_info.queryCount = 128;
break;
case VKD3D_QUERY_TYPE_INDEX_RT_SERIALIZE_SIZE:
pool_info.queryType = VK_QUERY_TYPE_ACCELERATION_STRUCTURE_SERIALIZATION_SIZE_KHR;
pool_info.queryCount = 128;
break;
case VKD3D_QUERY_TYPE_INDEX_RT_SERIALIZE_SIZE_BOTTOM_LEVEL_POINTERS:
pool_info.queryType = VK_QUERY_TYPE_ACCELERATION_STRUCTURE_SERIALIZATION_BOTTOM_LEVEL_POINTERS_KHR;
pool_info.queryCount = 128;
break;
default:
ERR("Unhandled query type %u.\n", type_index);
return E_INVALIDARG;
}
if ((vr = VK_CALL(vkCreateQueryPool(device->vk_device, &pool_info, NULL, &pool->vk_query_pool))) < 0)
{
ERR("Failed to create query pool, vr %u.\n", vr);
return hresult_from_vk_result(vr);
}
pool->type_index = type_index;
pool->query_count = pool_info.queryCount;
pool->next_index = 0;
return S_OK;
}
static void d3d12_device_destroy_query_pool(struct d3d12_device *device, const struct vkd3d_query_pool *pool)
{
const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
TRACE("device %p, pool %p.\n", device, pool);
VK_CALL(vkDestroyQueryPool(device->vk_device, pool->vk_query_pool, NULL));
}
HRESULT d3d12_device_get_query_pool(struct d3d12_device *device, uint32_t type_index, struct vkd3d_query_pool *pool)
{
size_t i;
pthread_mutex_lock(&device->mutex);
for (i = 0; i < device->query_pool_count; i++)
{
if (device->query_pools[i].type_index == type_index)
{
*pool = device->query_pools[i];
pool->next_index = 0;
if (--device->query_pool_count != i)
device->query_pools[i] = device->query_pools[device->query_pool_count];
pthread_mutex_unlock(&device->mutex);
return S_OK;
}
}
pthread_mutex_unlock(&device->mutex);
return d3d12_device_create_query_pool(device, type_index, pool);
}
void d3d12_device_return_query_pool(struct d3d12_device *device, const struct vkd3d_query_pool *pool)
{
pthread_mutex_lock(&device->mutex);
if (device->query_pool_count < VKD3D_VIRTUAL_QUERY_POOL_COUNT)
{
device->query_pools[device->query_pool_count++] = *pool;
pthread_mutex_unlock(&device->mutex);
}
else
{
pthread_mutex_unlock(&device->mutex);
d3d12_device_destroy_query_pool(device, pool);
}
}
/* ID3D12Device */
extern ULONG STDMETHODCALLTYPE d3d12_device_vkd3d_ext_AddRef(ID3D12DeviceExt *iface);
HRESULT STDMETHODCALLTYPE d3d12_device_QueryInterface(d3d12_device_iface *iface,
REFIID riid, void **object)
{
TRACE("iface %p, riid %s, object %p.\n", iface, debugstr_guid(riid), object);
if (IsEqualGUID(riid, &IID_ID3D12Device)
|| IsEqualGUID(riid, &IID_ID3D12Device1)
|| IsEqualGUID(riid, &IID_ID3D12Device2)
|| IsEqualGUID(riid, &IID_ID3D12Device3)
|| IsEqualGUID(riid, &IID_ID3D12Device4)
|| IsEqualGUID(riid, &IID_ID3D12Device5)
|| IsEqualGUID(riid, &IID_ID3D12Device6)
|| IsEqualGUID(riid, &IID_ID3D12Device7)
|| IsEqualGUID(riid, &IID_ID3D12Device8)
|| IsEqualGUID(riid, &IID_ID3D12Device9)
|| IsEqualGUID(riid, &IID_ID3D12Object)
|| IsEqualGUID(riid, &IID_IUnknown))
{
ID3D12Device_AddRef(iface);
*object = iface;
return S_OK;
}
if (IsEqualGUID(riid, &IID_ID3D12DeviceExt))
{
struct d3d12_device *device = impl_from_ID3D12Device(iface);
d3d12_device_vkd3d_ext_AddRef(&device->ID3D12DeviceExt_iface);
*object = &device->ID3D12DeviceExt_iface;
return S_OK;
}
WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(riid));
*object = NULL;
return E_NOINTERFACE;
}
static ULONG STDMETHODCALLTYPE d3d12_device_AddRef(d3d12_device_iface *iface)
{
struct d3d12_device *device = impl_from_ID3D12Device(iface);
ULONG refcount = InterlockedIncrement(&device->refcount);
TRACE("%p increasing refcount to %u.\n", device, refcount);
return refcount;
}
static void d3d12_device_destroy(struct d3d12_device *device)
{
const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
size_t i, j;
for (i = 0; i < VKD3D_SCRATCH_POOL_KIND_COUNT; i++)
for (j = 0; j < device->scratch_pools[i].scratch_buffer_count; j++)
d3d12_device_destroy_scratch_buffer(device, &device->scratch_pools[i].scratch_buffers[j]);
for (i = 0; i < device->query_pool_count; i++)
d3d12_device_destroy_query_pool(device, &device->query_pools[i]);
for (i = 0; i < device->cached_command_allocator_count; i++)
VK_CALL(vkDestroyCommandPool(device->vk_device, device->cached_command_allocators[i].vk_command_pool, NULL));
vkd3d_free(device->descriptor_heap_gpu_vas);
vkd3d_private_store_destroy(&device->private_store);
vkd3d_cleanup_format_info(device);
vkd3d_memory_info_cleanup(&device->memory_info, device);
vkd3d_shader_debug_ring_cleanup(&device->debug_ring, device);
#ifdef VKD3D_ENABLE_BREADCRUMBS
if (vkd3d_config_flags & VKD3D_CONFIG_FLAG_BREADCRUMBS)
vkd3d_breadcrumb_tracer_cleanup(&device->breadcrumb_tracer, device);
#endif
vkd3d_pipeline_library_flush_disk_cache(&device->disk_cache);
vkd3d_sampler_state_cleanup(&device->sampler_state, device);
vkd3d_view_map_destroy(&device->sampler_map, device);
vkd3d_meta_ops_cleanup(&device->meta_ops, device);
vkd3d_bindless_state_cleanup(&device->bindless_state, device);
d3d12_device_destroy_vkd3d_queues(device);
vkd3d_memory_allocator_cleanup(&device->memory_allocator, device);
/* Tear down descriptor global info late, so we catch last minute faults after we drain the queues. */
vkd3d_descriptor_debug_free_global_info(device->descriptor_qa_global_info, device);
#ifdef VKD3D_ENABLE_RENDERDOC
if (vkd3d_renderdoc_active() && vkd3d_renderdoc_global_capture_enabled())
vkd3d_renderdoc_end_capture(device->vkd3d_instance->vk_instance);
#endif
VK_CALL(vkDestroyDevice(device->vk_device, NULL));
pthread_mutex_destroy(&device->mutex);
if (device->parent)
IUnknown_Release(device->parent);
vkd3d_instance_decref(device->vkd3d_instance);
}
static void d3d12_device_set_name(struct d3d12_device *device, const char *name)
{
vkd3d_set_vk_object_name(device, (uint64_t)(uintptr_t)device->vk_device,
VK_OBJECT_TYPE_DEVICE, name);
}
static ULONG STDMETHODCALLTYPE d3d12_device_Release(d3d12_device_iface *iface)
{
struct d3d12_device *device = impl_from_ID3D12Device(iface);
ULONG cur_refcount, cas_refcount;
bool is_locked = false;
cur_refcount = 0;
cas_refcount = vkd3d_atomic_uint32_load_explicit(&device->refcount, vkd3d_memory_order_relaxed);
/* In order to prevent another thread from resurrecting a destroyed device,
* we need to lock the container mutex before decrementing the ref count to
* zero, so we'll have to use a CAS loop rather than basic atomic decrement */
while (cas_refcount != cur_refcount)
{
cur_refcount = cas_refcount;
if (cur_refcount == 1 && !is_locked)
{
pthread_mutex_lock(&d3d12_device_map_mutex);
is_locked = true;
}
cas_refcount = vkd3d_atomic_uint32_compare_exchange((uint32_t*)&device->refcount, cur_refcount,
cur_refcount - 1, vkd3d_memory_order_acq_rel, vkd3d_memory_order_relaxed);
}
if (cur_refcount == 1)
{
d3d12_remove_device_singleton(device->adapter_luid);
d3d12_device_destroy(device);
vkd3d_free(device);
}
if (is_locked)
pthread_mutex_unlock(&d3d12_device_map_mutex);
TRACE("%p decreasing refcount to %u.\n", device, cur_refcount - 1);
return cur_refcount - 1;
}
static HRESULT STDMETHODCALLTYPE d3d12_device_GetPrivateData(d3d12_device_iface *iface,
REFGUID guid, UINT *data_size, void *data)
{
struct d3d12_device *device = impl_from_ID3D12Device(iface);
TRACE("iface %p, guid %s, data_size %p, data %p.\n",
iface, debugstr_guid(guid), data_size, data);
return vkd3d_get_private_data(&device->private_store, guid, data_size, data);
}
static HRESULT STDMETHODCALLTYPE d3d12_device_SetPrivateData(d3d12_device_iface *iface,
REFGUID guid, UINT data_size, const void *data)
{
struct d3d12_device *device = impl_from_ID3D12Device(iface);
TRACE("iface %p, guid %s, data_size %u, data %p.\n",
iface, debugstr_guid(guid), data_size, data);
return vkd3d_set_private_data(&device->private_store, guid, data_size, data,
(vkd3d_set_name_callback) d3d12_device_set_name, device);
}
static HRESULT STDMETHODCALLTYPE d3d12_device_SetPrivateDataInterface(d3d12_device_iface *iface,
REFGUID guid, const IUnknown *data)
{
struct d3d12_device *device = impl_from_ID3D12Device(iface);
TRACE("iface %p, guid %s, data %p.\n", iface, debugstr_guid(guid), data);
return vkd3d_set_private_data_interface(&device->private_store, guid, data,
(vkd3d_set_name_callback) d3d12_device_set_name, device);
}
static UINT STDMETHODCALLTYPE d3d12_device_GetNodeCount(d3d12_device_iface *iface)
{
TRACE("iface %p.\n", iface);
return 1;
}
static HRESULT STDMETHODCALLTYPE d3d12_device_CreateCommandQueue(d3d12_device_iface *iface,
const D3D12_COMMAND_QUEUE_DESC *desc, REFIID riid, void **command_queue)
{
struct d3d12_device *device = impl_from_ID3D12Device(iface);
struct d3d12_command_queue *object;
HRESULT hr;
TRACE("iface %p, desc %p, riid %s, command_queue %p.\n",
iface, desc, debugstr_guid(riid), command_queue);
if (FAILED(hr = d3d12_command_queue_create(device, desc, &object)))
return hr;
return return_interface(&object->ID3D12CommandQueue_iface, &IID_ID3D12CommandQueue,
riid, command_queue);
}
static HRESULT STDMETHODCALLTYPE d3d12_device_CreateCommandAllocator(d3d12_device_iface *iface,
D3D12_COMMAND_LIST_TYPE type, REFIID riid, void **command_allocator)
{
struct d3d12_device *device = impl_from_ID3D12Device(iface);
ID3D12CommandAllocator *allocator_iface;
HRESULT hr;
TRACE("iface %p, type %#x, riid %s, command_allocator %p.\n",
iface, type, debugstr_guid(riid), command_allocator);
if (type == D3D12_COMMAND_LIST_TYPE_BUNDLE)
{
struct d3d12_bundle_allocator *object;
if (FAILED(hr = d3d12_bundle_allocator_create(device, &object)))
return hr;
allocator_iface = &object->ID3D12CommandAllocator_iface;
}
else
{
struct d3d12_command_allocator *object;
if (FAILED(hr = d3d12_command_allocator_create(device, type, &object)))
return hr;
allocator_iface = &object->ID3D12CommandAllocator_iface;
}
return return_interface(allocator_iface, &IID_ID3D12CommandAllocator, riid, command_allocator);
}
static HRESULT STDMETHODCALLTYPE d3d12_device_CreateGraphicsPipelineState(d3d12_device_iface *iface,
const D3D12_GRAPHICS_PIPELINE_STATE_DESC *desc, REFIID riid, void **pipeline_state)
{
struct d3d12_device *device = impl_from_ID3D12Device(iface);
struct d3d12_pipeline_state_desc pipeline_desc;
struct d3d12_pipeline_state *object;
HRESULT hr;
TRACE("iface %p, desc %p, riid %s, pipeline_state %p.\n",
iface, desc, debugstr_guid(riid), pipeline_state);
if (FAILED(hr = vkd3d_pipeline_state_desc_from_d3d12_graphics_desc(&pipeline_desc, desc)))
return hr;
if (FAILED(hr = d3d12_pipeline_state_create(device,
VK_PIPELINE_BIND_POINT_GRAPHICS, &pipeline_desc, &object)))
return hr;
return return_interface(&object->ID3D12PipelineState_iface,
&IID_ID3D12PipelineState, riid, pipeline_state);
}
static HRESULT STDMETHODCALLTYPE d3d12_device_CreateComputePipelineState(d3d12_device_iface *iface,
const D3D12_COMPUTE_PIPELINE_STATE_DESC *desc, REFIID riid, void **pipeline_state)
{
struct d3d12_device *device = impl_from_ID3D12Device(iface);
struct d3d12_pipeline_state_desc pipeline_desc;
struct d3d12_pipeline_state *object;
HRESULT hr;
TRACE("iface %p, desc %p, riid %s, pipeline_state %p.\n",
iface, desc, debugstr_guid(riid), pipeline_state);
if (FAILED(hr = vkd3d_pipeline_state_desc_from_d3d12_compute_desc(&pipeline_desc, desc)))
return hr;
if (FAILED(hr = d3d12_pipeline_state_create(device,
VK_PIPELINE_BIND_POINT_COMPUTE, &pipeline_desc, &object)))
return hr;
return return_interface(&object->ID3D12PipelineState_iface,
&IID_ID3D12PipelineState, riid, pipeline_state);
}
static HRESULT STDMETHODCALLTYPE d3d12_device_CreateCommandList1(d3d12_device_iface *iface,
UINT node_mask, D3D12_COMMAND_LIST_TYPE type, D3D12_COMMAND_LIST_FLAGS flags,
REFIID riid, void **command_list);
static HRESULT STDMETHODCALLTYPE d3d12_device_CreateCommandList(d3d12_device_iface *iface,
UINT node_mask, D3D12_COMMAND_LIST_TYPE type, ID3D12CommandAllocator *command_allocator,
ID3D12PipelineState *initial_pipeline_state, REFIID riid, void **command_list)
{
ID3D12GraphicsCommandList *object;
HRESULT hr;
TRACE("iface %p, node_mask 0x%08x, type %#x, command_allocator %p, "
"initial_pipeline_state %p, riid %s, command_list %p.\n",
iface, node_mask, type, command_allocator,
initial_pipeline_state, debugstr_guid(riid), command_list);
if (FAILED(hr = d3d12_device_CreateCommandList1(iface, node_mask, type,
D3D12_COMMAND_LIST_FLAG_NONE, &IID_ID3D12GraphicsCommandList, (void **)&object)))
return hr;
if (FAILED(hr = ID3D12GraphicsCommandList_Reset(object, command_allocator, initial_pipeline_state)))
{
ID3D12GraphicsCommandList_Release(object);
return hr;
}
return return_interface(object, &IID_ID3D12GraphicsCommandList, riid, command_list);
}
static void vkd3d_determine_format_support_for_feature_level(const struct d3d12_device *device,
D3D12_FEATURE_DATA_FORMAT_SUPPORT *format_support)
{
/* TypedUAVLoadAdditionalFormats is an all or nothing set */
if (!device->d3d12_caps.options.TypedUAVLoadAdditionalFormats)
format_support->Support2 &= ~D3D12_FORMAT_SUPPORT2_UAV_TYPED_LOAD;
/* R32_{UINT, SINT, FLOAT} always have to support typed reads */
if (format_support->Format == DXGI_FORMAT_R32_UINT ||
format_support->Format == DXGI_FORMAT_R32_SINT ||
format_support->Format == DXGI_FORMAT_R32_FLOAT)
{
format_support->Support2 |= D3D12_FORMAT_SUPPORT2_UAV_TYPED_LOAD;
}
}
static HRESULT d3d12_device_check_multisample_quality_levels(struct d3d12_device *device,
D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS *data)
{
const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
VkImageFormatProperties vk_properties;
const struct vkd3d_format *format;
VkSampleCountFlagBits vk_samples;
VkImageUsageFlags vk_usage = 0;
VkResult vr;
TRACE("Format %#x, sample count %u, flags %#x.\n", data->Format, data->SampleCount, data->Flags);
data->NumQualityLevels = 0;
if (!(vk_samples = vk_samples_from_sample_count(data->SampleCount)))
WARN("Invalid sample count %u.\n", data->SampleCount);
if (!data->SampleCount)
return E_FAIL;
if (data->SampleCount == 1)
{
data->NumQualityLevels = 1;
goto done;
}
if (data->Format == DXGI_FORMAT_UNKNOWN)
goto done;
if (!(format = vkd3d_get_format(device, data->Format, false)))
format = vkd3d_get_format(device, data->Format, true);
if (!format)
{
FIXME("Unhandled format %#x.\n", data->Format);
return E_INVALIDARG;
}
if (data->Flags)
FIXME("Ignoring flags %#x.\n", data->Flags);
if (format->vk_aspect_mask & VK_IMAGE_ASPECT_COLOR_BIT)
vk_usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
else
vk_usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
vr = VK_CALL(vkGetPhysicalDeviceImageFormatProperties(device->vk_physical_device,
format->vk_format, VK_IMAGE_TYPE_2D, VK_IMAGE_TILING_OPTIMAL, vk_usage, 0, &vk_properties));
if (vr == VK_ERROR_FORMAT_NOT_SUPPORTED)
{
WARN("Format %#x is not supported.\n", format->dxgi_format);
goto done;
}
if (vr < 0)
{
ERR("Failed to get image format properties, vr %d.\n", vr);
return hresult_from_vk_result(vr);
}
if (vk_properties.sampleCounts & vk_samples)
data->NumQualityLevels = 1;
done:
TRACE("Returning %u quality levels.\n", data->NumQualityLevels);
return S_OK;
}
bool d3d12_device_is_uma(struct d3d12_device *device, bool *coherent)
{
unsigned int i;
if (coherent)
*coherent = true;
for (i = 0; i < device->memory_properties.memoryTypeCount; ++i)
{
if (!(device->memory_properties.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT))
return false;
if (coherent && !(device->memory_properties.memoryTypes[i].propertyFlags
& VK_MEMORY_PROPERTY_HOST_COHERENT_BIT))
*coherent = false;
}
return true;
}
static bool vk_format_is_supported_by_global_read_write_without_format(VkFormat format)
{
size_t i;
/* from https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/vkspec.html#formats-without-shader-storage-format */
static const VkFormat supported_formats[] =
{
VK_FORMAT_R8G8B8A8_UNORM,
VK_FORMAT_R8G8B8A8_SNORM,
VK_FORMAT_R8G8B8A8_UINT,
VK_FORMAT_R8G8B8A8_SINT,
VK_FORMAT_R32_UINT,
VK_FORMAT_R32_SINT,
VK_FORMAT_R32_SFLOAT,
VK_FORMAT_R32G32_UINT,
VK_FORMAT_R32G32_SINT,
VK_FORMAT_R32G32_SFLOAT,
VK_FORMAT_R32G32B32A32_UINT,
VK_FORMAT_R32G32B32A32_SINT,
VK_FORMAT_R32G32B32A32_SFLOAT,
VK_FORMAT_R16G16B16A16_UINT,
VK_FORMAT_R16G16B16A16_SINT,
VK_FORMAT_R16G16B16A16_SFLOAT,
VK_FORMAT_R16G16_SFLOAT,
VK_FORMAT_B10G11R11_UFLOAT_PACK32,
VK_FORMAT_R16_SFLOAT,
VK_FORMAT_R16G16B16A16_UNORM,
VK_FORMAT_A2B10G10R10_UNORM_PACK32,
VK_FORMAT_R16G16_UNORM,
VK_FORMAT_R8G8_UNORM,
VK_FORMAT_R16_UNORM,
VK_FORMAT_R8_UNORM,
VK_FORMAT_R16G16B16A16_SNORM,
VK_FORMAT_R16G16_SNORM,
VK_FORMAT_R8G8_SNORM,
VK_FORMAT_R16_SNORM,
VK_FORMAT_R8_SNORM,
VK_FORMAT_R16G16_SINT,
VK_FORMAT_R8G8_SINT,
VK_FORMAT_R16_SINT,
VK_FORMAT_R8_SINT,
VK_FORMAT_A2B10G10R10_UINT_PACK32,
VK_FORMAT_R16G16_UINT,
VK_FORMAT_R8G8_UINT,
VK_FORMAT_R16_UINT,
VK_FORMAT_R8_UINT,
};
for (i = 0; i < ARRAY_SIZE(supported_formats); i++)
{
if (format == supported_formats[i])
return true;
}
return false;
}
static HRESULT d3d12_device_get_format_support(struct d3d12_device *device, D3D12_FEATURE_DATA_FORMAT_SUPPORT *data)
{
const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
VkFormatFeatureFlags2KHR image_features;
VkFormatProperties3KHR properties3;
const struct vkd3d_format *format;
VkFormatProperties2 properties;
data->Support1 = D3D12_FORMAT_SUPPORT1_NONE;
data->Support2 = D3D12_FORMAT_SUPPORT2_NONE;
if (!(format = vkd3d_get_format(device, data->Format, false)))
format = vkd3d_get_format(device, data->Format, true);
if (!format)
{
FIXME("Unhandled format %#x.\n", data->Format);
return E_INVALIDARG;
}
properties.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2;
properties.pNext = NULL;
if (device->vk_info.KHR_format_feature_flags2)
{
properties3.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_3_KHR;
properties3.pNext = NULL;
vk_prepend_struct(&properties, &properties3);
}
VK_CALL(vkGetPhysicalDeviceFormatProperties2(device->vk_physical_device, format->vk_format, &properties));
if (device->vk_info.KHR_format_feature_flags2)
image_features = properties3.linearTilingFeatures | properties3.optimalTilingFeatures;
else
image_features = properties.formatProperties.linearTilingFeatures | properties.formatProperties.optimalTilingFeatures;
if (properties.formatProperties.bufferFeatures)
data->Support1 |= D3D12_FORMAT_SUPPORT1_BUFFER;
if (properties.formatProperties.bufferFeatures & VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)
data->Support1 |= D3D12_FORMAT_SUPPORT1_IA_VERTEX_BUFFER;
if (data->Format == DXGI_FORMAT_R16_UINT || data->Format == DXGI_FORMAT_R32_UINT)
data->Support1 |= D3D12_FORMAT_SUPPORT1_IA_INDEX_BUFFER;
if (image_features)
{
data->Support1 |= D3D12_FORMAT_SUPPORT1_TEXTURE1D | D3D12_FORMAT_SUPPORT1_TEXTURE2D
| D3D12_FORMAT_SUPPORT1_TEXTURE3D | D3D12_FORMAT_SUPPORT1_TEXTURECUBE;
}
if (image_features & VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_BIT_KHR)
{
data->Support1 |= D3D12_FORMAT_SUPPORT1_SHADER_LOAD | D3D12_FORMAT_SUPPORT1_MULTISAMPLE_LOAD
| D3D12_FORMAT_SUPPORT1_SHADER_GATHER;
if (image_features & VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_FILTER_LINEAR_BIT_KHR)
{
data->Support1 |= D3D12_FORMAT_SUPPORT1_SHADER_SAMPLE
| D3D12_FORMAT_SUPPORT1_MIP;
}
if (format->vk_aspect_mask & VK_IMAGE_ASPECT_DEPTH_BIT)
{
data->Support1 |= D3D12_FORMAT_SUPPORT1_SHADER_SAMPLE_COMPARISON
| D3D12_FORMAT_SUPPORT1_SHADER_GATHER_COMPARISON;
}
}
if (image_features & VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BIT_KHR)
data->Support1 |= D3D12_FORMAT_SUPPORT1_RENDER_TARGET | D3D12_FORMAT_SUPPORT1_MULTISAMPLE_RENDERTARGET;
if (image_features & VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BLEND_BIT_KHR)
data->Support1 |= D3D12_FORMAT_SUPPORT1_BLENDABLE;
if (image_features & VK_FORMAT_FEATURE_2_DEPTH_STENCIL_ATTACHMENT_BIT_KHR)
data->Support1 |= D3D12_FORMAT_SUPPORT1_DEPTH_STENCIL;
if (image_features & VK_FORMAT_FEATURE_2_BLIT_SRC_BIT_KHR)
data->Support1 |= D3D12_FORMAT_SUPPORT1_MULTISAMPLE_RESOLVE;
if (image_features & VK_FORMAT_FEATURE_2_STORAGE_IMAGE_BIT_KHR)
{
data->Support1 |= D3D12_FORMAT_SUPPORT1_TYPED_UNORDERED_ACCESS_VIEW;
if (image_features & VK_FORMAT_FEATURE_2_STORAGE_READ_WITHOUT_FORMAT_BIT_KHR)
data->Support2 |= D3D12_FORMAT_SUPPORT2_UAV_TYPED_LOAD;
if (image_features & VK_FORMAT_FEATURE_2_STORAGE_WRITE_WITHOUT_FORMAT_BIT_KHR)
data->Support2 |= D3D12_FORMAT_SUPPORT2_UAV_TYPED_STORE;
if (!device->vk_info.KHR_format_feature_flags2 &&
vk_format_is_supported_by_global_read_write_without_format(format->vk_format))
{
if (device->device_info.features2.features.shaderStorageImageReadWithoutFormat)
data->Support2 |= D3D12_FORMAT_SUPPORT2_UAV_TYPED_LOAD;
if (device->device_info.features2.features.shaderStorageImageWriteWithoutFormat)
data->Support2 |= D3D12_FORMAT_SUPPORT2_UAV_TYPED_STORE;
}
}
if (image_features & VK_FORMAT_FEATURE_2_STORAGE_IMAGE_ATOMIC_BIT_KHR)
{
data->Support2 |= 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;
}
return S_OK;
}
static HRESULT STDMETHODCALLTYPE d3d12_device_CheckFeatureSupport(d3d12_device_iface *iface,
D3D12_FEATURE feature, void *feature_data, UINT feature_data_size)
{
struct d3d12_device *device = impl_from_ID3D12Device(iface);
TRACE("iface %p, feature %#x, feature_data %p, feature_data_size %u.\n",
iface, feature, feature_data, feature_data_size);
switch (feature)
{
case D3D12_FEATURE_D3D12_OPTIONS:
{
D3D12_FEATURE_DATA_D3D12_OPTIONS *data = feature_data;
if (feature_data_size != sizeof(*data))
{
WARN("Invalid size %u.\n", feature_data_size);
return E_INVALIDARG;
}
*data = device->d3d12_caps.options;
TRACE("Double precision shader ops %#x.\n", data->DoublePrecisionFloatShaderOps);
TRACE("Output merger logic op %#x.\n", data->OutputMergerLogicOp);
TRACE("Shader min precision support %#x.\n", data->MinPrecisionSupport);
TRACE("Tiled resources tier %#x.\n", data->TiledResourcesTier);
TRACE("Resource binding tier %#x.\n", data->ResourceBindingTier);
TRACE("PS specified stencil ref %#x.\n", data->PSSpecifiedStencilRefSupported);
TRACE("Typed UAV load and additional formats %#x.\n", data->TypedUAVLoadAdditionalFormats);
TRACE("ROV %#x.\n", data->ROVsSupported);
TRACE("Conservative rasterization tier %#x.\n", data->ConservativeRasterizationTier);
TRACE("Max GPU virtual address bits per resource %u.\n", data->MaxGPUVirtualAddressBitsPerResource);
TRACE("Standard swizzle 64KB %#x.\n", data->StandardSwizzle64KBSupported);
TRACE("Cross-node sharing tier %#x.\n", data->CrossNodeSharingTier);
TRACE("Cross-adapter row-major texture %#x.\n", data->CrossAdapterRowMajorTextureSupported);
TRACE("VP and RT array index from any shader without GS emulation %#x.\n",
data->VPAndRTArrayIndexFromAnyShaderFeedingRasterizerSupportedWithoutGSEmulation);
TRACE("Resource heap tier %#x.\n", data->ResourceHeapTier);
return S_OK;
}
case D3D12_FEATURE_ARCHITECTURE:
{
D3D12_FEATURE_DATA_ARCHITECTURE *data = feature_data;
bool coherent;
if (feature_data_size != sizeof(*data))
{
WARN("Invalid size %u.\n", feature_data_size);
return E_INVALIDARG;
}
if (data->NodeIndex)
{
FIXME("Multi-adapter not supported.\n");
return E_INVALIDARG;
}
WARN("Assuming device does not support tile based rendering.\n");
data->TileBasedRenderer = FALSE;
data->UMA = d3d12_device_is_uma(device, &coherent);
data->CacheCoherentUMA = data->UMA ? coherent : FALSE;
TRACE("Tile based renderer %#x, UMA %#x, cache coherent UMA %#x.\n",
data->TileBasedRenderer, data->UMA, data->CacheCoherentUMA);
return S_OK;
}
case D3D12_FEATURE_FEATURE_LEVELS:
{
struct d3d12_caps *caps = &device->d3d12_caps;
D3D12_FEATURE_DATA_FEATURE_LEVELS *data = feature_data;
unsigned int i;
if (feature_data_size != sizeof(*data))
{
WARN("Invalid size %u.\n", feature_data_size);
return E_INVALIDARG;
}
if (!data->NumFeatureLevels)
return E_INVALIDARG;
data->MaxSupportedFeatureLevel = 0;
for (i = 0; i < data->NumFeatureLevels; ++i)
{
D3D_FEATURE_LEVEL fl = data->pFeatureLevelsRequested[i];
if (data->MaxSupportedFeatureLevel < fl && fl <= caps->max_feature_level)
data->MaxSupportedFeatureLevel = fl;
}
TRACE("Max supported feature level %#x.\n", data->MaxSupportedFeatureLevel);
return S_OK;
}
case D3D12_FEATURE_FORMAT_SUPPORT:
{
D3D12_FEATURE_DATA_FORMAT_SUPPORT *data = feature_data;
HRESULT hr;
if (feature_data_size != sizeof(*data))
{
WARN("Invalid size %u.\n", feature_data_size);
return E_INVALIDARG;
}
if (FAILED(hr = d3d12_device_get_format_support(device, data)))
return hr;
vkd3d_determine_format_support_for_feature_level(device, data);
TRACE("Format %#x, support1 %#x, support2 %#x.\n", data->Format, data->Support1, data->Support2);
return S_OK;
}
case D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS:
{
D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS *data = feature_data;
if (feature_data_size != sizeof(*data))
{
WARN("Invalid size %u.\n", feature_data_size);
return E_INVALIDARG;
}
return d3d12_device_check_multisample_quality_levels(device, data);
}
case D3D12_FEATURE_FORMAT_INFO:
{
D3D12_FEATURE_DATA_FORMAT_INFO *data = feature_data;
const struct vkd3d_format *format;
if (feature_data_size != sizeof(*data))
{
WARN("Invalid size %u.\n", feature_data_size);
return E_INVALIDARG;
}
if (data->Format == DXGI_FORMAT_UNKNOWN)
{
data->PlaneCount = 1;
return S_OK;
}
if (!(format = vkd3d_get_format(device, data->Format, false)))
format = vkd3d_get_format(device, data->Format, true);
if (!format)
{
FIXME("Unhandled format %#x.\n", data->Format);
return E_INVALIDARG;
}
data->PlaneCount = format->plane_count;
TRACE("Format %#x, plane count %"PRIu8".\n", data->Format, data->PlaneCount);
return S_OK;
}
case D3D12_FEATURE_GPU_VIRTUAL_ADDRESS_SUPPORT:
{
const D3D12_FEATURE_DATA_D3D12_OPTIONS *options = &device->d3d12_caps.options;
D3D12_FEATURE_DATA_GPU_VIRTUAL_ADDRESS_SUPPORT *data = feature_data;
if (feature_data_size != sizeof(*data))
{
WARN("Invalid size %u.\n", feature_data_size);
return E_INVALIDARG;
}
data->MaxGPUVirtualAddressBitsPerResource = options->MaxGPUVirtualAddressBitsPerResource;
data->MaxGPUVirtualAddressBitsPerProcess = options->MaxGPUVirtualAddressBitsPerResource;
TRACE("Max GPU virtual address bits per resource %u, Max GPU virtual address bits per process %u.\n",
data->MaxGPUVirtualAddressBitsPerResource, data->MaxGPUVirtualAddressBitsPerProcess);
return S_OK;
}
case D3D12_FEATURE_SHADER_MODEL:
{
D3D12_FEATURE_DATA_SHADER_MODEL *data = feature_data;
if (feature_data_size != sizeof(*data))
{
WARN("Invalid size %u.\n", feature_data_size);
return E_INVALIDARG;
}
TRACE("Request shader model %#x.\n", data->HighestShaderModel);
data->HighestShaderModel = min(data->HighestShaderModel, device->d3d12_caps.max_shader_model);
TRACE("Shader model %#x.\n", data->HighestShaderModel);
return S_OK;
}
case D3D12_FEATURE_D3D12_OPTIONS1:
{
D3D12_FEATURE_DATA_D3D12_OPTIONS1 *data = feature_data;
if (feature_data_size != sizeof(*data))
{
WARN("Invalid size %u.\n", feature_data_size);
return E_INVALIDARG;
}
*data = device->d3d12_caps.options1;
TRACE("Wave ops %#x.\n", data->WaveOps);
TRACE("Min wave lane count %u.\n", data->WaveLaneCountMin);
TRACE("Max wave lane count %u.\n", data->WaveLaneCountMax);
TRACE("Total lane count %u.\n", data->TotalLaneCount);
TRACE("Expanded compute resource states %#x.\n", data->ExpandedComputeResourceStates);
TRACE("Int64 shader ops %#x.\n", data->Int64ShaderOps);
return S_OK;
}
case D3D12_FEATURE_PROTECTED_RESOURCE_SESSION_SUPPORT:
{
D3D12_FEATURE_DATA_PROTECTED_RESOURCE_SESSION_SUPPORT *data = feature_data;
if (feature_data_size != sizeof(*data))
{
WARN("Invalid size %u.\n", feature_data_size);
return E_INVALIDARG;
}
if (data->NodeIndex)
{
FIXME("Multi-adapter not supported.\n");
return E_INVALIDARG;
}
data->Support = D3D12_PROTECTED_RESOURCE_SESSION_SUPPORT_FLAG_NONE;
TRACE("Protected resource session support %#x.", data->Support);
return S_OK;
}
case D3D12_FEATURE_ROOT_SIGNATURE:
{
D3D12_FEATURE_DATA_ROOT_SIGNATURE *data = feature_data;
if (feature_data_size != sizeof(*data))
{
WARN("Invalid size %u.\n", feature_data_size);
return E_INVALIDARG;
}
TRACE("Root signature requested %#x.\n", data->HighestVersion);
data->HighestVersion = min(data->HighestVersion, D3D_ROOT_SIGNATURE_VERSION_1_1);
TRACE("Root signature version %#x.\n", data->HighestVersion);
return S_OK;
}
case D3D12_FEATURE_ARCHITECTURE1:
{
D3D12_FEATURE_DATA_ARCHITECTURE1 *data = feature_data;
bool coherent;
if (feature_data_size != sizeof(*data))
{
WARN("Invalid size %u.\n", feature_data_size);
return E_INVALIDARG;
}
if (data->NodeIndex)
{
FIXME("Multi-adapter not supported.\n");
return E_INVALIDARG;
}
WARN("Assuming device does not support tile based rendering.\n");
data->TileBasedRenderer = FALSE;
data->UMA = d3d12_device_is_uma(device, &coherent);
data->CacheCoherentUMA = data->UMA ? coherent : FALSE;
data->IsolatedMMU = TRUE; /* Appears to be true Windows drivers */
TRACE("Tile based renderer %#x, UMA %#x, cache coherent UMA %#x, isolated MMU %#x.\n",
data->TileBasedRenderer, data->UMA, data->CacheCoherentUMA, data->IsolatedMMU);
return S_OK;
}
case D3D12_FEATURE_D3D12_OPTIONS2:
{
D3D12_FEATURE_DATA_D3D12_OPTIONS2 *data = feature_data;
if (feature_data_size != sizeof(*data))
{
WARN("Invalid size %u.\n", feature_data_size);
return E_INVALIDARG;
}
*data = device->d3d12_caps.options2;
TRACE("Depth bounds test %#x.\n", data->DepthBoundsTestSupported);
TRACE("Programmable sample positions tier %u.\n", data->ProgrammableSamplePositionsTier);
return S_OK;
}
case D3D12_FEATURE_SHADER_CACHE:
{
D3D12_FEATURE_DATA_SHADER_CACHE *data = feature_data;
if (feature_data_size != sizeof(*data))
{
WARN("Invalid size %u.\n", feature_data_size);
return E_INVALIDARG;
}
/* We cannot query shader cache features from the Vulkan driver,
* but all relevant drivers have their own disk cache, so we'll
* advertize support for the AUTOMATIC_*_CACHE feature bits. */
data->SupportFlags = D3D12_SHADER_CACHE_SUPPORT_SINGLE_PSO |
D3D12_SHADER_CACHE_SUPPORT_LIBRARY |
D3D12_SHADER_CACHE_SUPPORT_AUTOMATIC_INPROC_CACHE |
D3D12_SHADER_CACHE_SUPPORT_AUTOMATIC_DISK_CACHE;
TRACE("Shader cache support flags %#x.", data->SupportFlags);
return S_OK;
}
case D3D12_FEATURE_COMMAND_QUEUE_PRIORITY:
{
D3D12_FEATURE_DATA_COMMAND_QUEUE_PRIORITY *data = feature_data;
if (feature_data_size != sizeof(*data))
{
WARN("Invalid size %u.\n", feature_data_size);
return E_INVALIDARG;
}
/* FIXME We ignore priorities since Vulkan queues are created up-front */
data->PriorityForTypeIsSupported = data->Priority <= D3D12_COMMAND_QUEUE_PRIORITY_HIGH;
TRACE("Command list type %u supports priority %u: %#x.",
data->CommandListType, data->Priority, data->PriorityForTypeIsSupported);
return S_OK;
}
case D3D12_FEATURE_D3D12_OPTIONS3:
{
D3D12_FEATURE_DATA_D3D12_OPTIONS3 *data = feature_data;
if (feature_data_size != sizeof(*data))
{
WARN("Invalid size %u.\n", feature_data_size);
return E_INVALIDARG;
}
*data = device->d3d12_caps.options3;
TRACE("Copy queue timestamp queries %#x.", data->CopyQueueTimestampQueriesSupported);
TRACE("Casting fully typed formats %#x.", data->CastingFullyTypedFormatSupported);
TRACE("Write buffer immediate support flags %#x.", data->WriteBufferImmediateSupportFlags);
TRACE("View instancing tier %u.", data->ViewInstancingTier);
TRACE("Barycentrics %#x.", data->BarycentricsSupported);
return S_OK;
}
case D3D12_FEATURE_EXISTING_HEAPS:
{
D3D12_FEATURE_DATA_EXISTING_HEAPS *data = feature_data;
if (feature_data_size != sizeof(*data))
{
WARN("Invalid size %u.\n", feature_data_size);
return E_INVALIDARG;
}
/* Would require some sort of wine
* interop to support file handles */
data->Supported = FALSE;
TRACE("Existing heaps %#x.", data->Supported);
return S_OK;
}
case D3D12_FEATURE_D3D12_OPTIONS4:
{
D3D12_FEATURE_DATA_D3D12_OPTIONS4 *data = feature_data;
if (feature_data_size != sizeof(*data))
{
WARN("Invalid size %u.\n", feature_data_size);
return E_INVALIDARG;
}
*data = device->d3d12_caps.options4;
TRACE("64kB alignment for MSAA textures %#x.\n", data->MSAA64KBAlignedTextureSupported);
TRACE("Shared resource compatibility tier %u.\n", data->SharedResourceCompatibilityTier);
TRACE("Native 16-bit shader ops %#x.\n", data->Native16BitShaderOpsSupported);
return S_OK;
}
case D3D12_FEATURE_SERIALIZATION:
{
D3D12_FEATURE_DATA_SERIALIZATION *data = feature_data;
if (feature_data_size != sizeof(*data))
{
WARN("Invalid size %u.\n", feature_data_size);
return E_INVALIDARG;
}
if (data->NodeIndex)
{
FIXME("Multi-adapter not supported.\n");
return E_INVALIDARG;
}
data->HeapSerializationTier = D3D12_HEAP_SERIALIZATION_TIER_0;
TRACE("Heap serialization tier %u.\n", data->HeapSerializationTier);
return S_OK;
}
case D3D12_FEATURE_CROSS_NODE:
{
D3D12_FEATURE_DATA_CROSS_NODE *data = feature_data;
if (feature_data_size != sizeof(*data))
{
WARN("Invalid size %u.\n", feature_data_size);
return E_INVALIDARG;
}
data->SharingTier = D3D12_CROSS_NODE_SHARING_TIER_NOT_SUPPORTED;
data->AtomicShaderInstructions = FALSE;
TRACE("Cross-node sharing tier %u.\n", data->SharingTier);
TRACE("Cross-node atomic shader instructions %#x.\n", data->AtomicShaderInstructions);
return S_OK;
}
case D3D12_FEATURE_D3D12_OPTIONS5:
{
D3D12_FEATURE_DATA_D3D12_OPTIONS5 *data = feature_data;
if (feature_data_size != sizeof(*data))
{
WARN("Invalid size %u.\n", feature_data_size);
return E_INVALIDARG;
}
*data = device->d3d12_caps.options5;
TRACE("SRV-only Tiled Resources Tier 3 %#x.\n", data->SRVOnlyTiledResourceTier3);
TRACE("Render pass tier %u.\n", data->RenderPassesTier);
TRACE("Raytracing tier %u.\n", data->RaytracingTier);
return S_OK;
}
case D3D12_FEATURE_D3D12_OPTIONS6:
{
D3D12_FEATURE_DATA_D3D12_OPTIONS6 *data = feature_data;
if (feature_data_size != sizeof(*data))
{
WARN("Invalid size %u.\n", feature_data_size);
return E_INVALIDARG;
}
*data = device->d3d12_caps.options6;
TRACE("Additional shading rates %#x.\n", data->AdditionalShadingRatesSupported);
TRACE("Per-primtive shading rate with viewport indexing %#x.\n", data->PerPrimitiveShadingRateSupportedWithViewportIndexing);
TRACE("Variable shading rate tier %u.\n", data->VariableShadingRateTier);
TRACE("Shading rate image tile size %u.\n", data->ShadingRateImageTileSize);
TRACE("Background processing %#x.\n", data->BackgroundProcessingSupported);
return S_OK;
}
case D3D12_FEATURE_D3D12_OPTIONS7:
{
D3D12_FEATURE_DATA_D3D12_OPTIONS7 *data = feature_data;
if (feature_data_size != sizeof(*data))
{
WARN("Invalid size %u.\n", feature_data_size);
return E_INVALIDARG;
}
*data = device->d3d12_caps.options7;
TRACE("Mesh shading tier %#x.\n", data->MeshShaderTier);
TRACE("Sampler feedback tier %#x.\n", data->SamplerFeedbackTier);
return S_OK;
}
case D3D12_FEATURE_D3D12_OPTIONS8:
{
D3D12_FEATURE_DATA_D3D12_OPTIONS8 *data = feature_data;
if (feature_data_size != sizeof(*data))
{
WARN("Invalid size %u.\n", feature_data_size);
return E_INVALIDARG;
}
*data = device->d3d12_caps.options8;
TRACE("Unaligned block textures supported %u.\n", data->UnalignedBlockTexturesSupported);
return S_OK;
}
case D3D12_FEATURE_D3D12_OPTIONS9:
{
D3D12_FEATURE_DATA_D3D12_OPTIONS9 *data = feature_data;
if (feature_data_size != sizeof(*data))
{
WARN("Invalid size %u.\n", feature_data_size);
return E_INVALIDARG;
}
*data = device->d3d12_caps.options9;
TRACE("AtomicInt64 on typed resource supported %u.\n", data->AtomicInt64OnTypedResourceSupported);
TRACE("AtomicInt64 on group shared supported %u.\n", data->AtomicInt64OnGroupSharedSupported);
TRACE("Mesh shader pipeline stats supported %u.\n", data->MeshShaderPipelineStatsSupported);
TRACE("Mesh shader supports full range render target array index %u.\n", data->MeshShaderSupportsFullRangeRenderTargetArrayIndex);
TRACE("Derivatives in mesh and amplification shaders supported %u.\n", data->DerivativesInMeshAndAmplificationShadersSupported);
TRACE("Wave MMA tier #%x.\n", data->WaveMMATier);
return S_OK;
}
case D3D12_FEATURE_D3D12_OPTIONS10:
{
D3D12_FEATURE_DATA_D3D12_OPTIONS10 *data = feature_data;
if (feature_data_size != sizeof(*data))
{
WARN("Invalid size %u.\n", feature_data_size);
return E_INVALIDARG;
}
*data = device->d3d12_caps.options10;
TRACE("Variable rate shading sum combiner supported %u.\n", data->VariableRateShadingSumCombinerSupported);
TRACE("Mesh shader per primitive shading rate supported %u.\n", data->MeshShaderPerPrimitiveShadingRateSupported);
return S_OK;
}
case D3D12_FEATURE_D3D12_OPTIONS11:
{
D3D12_FEATURE_DATA_D3D12_OPTIONS11 *data = feature_data;
if (feature_data_size != sizeof(*data))
{
WARN("Invalid size %u.\n", feature_data_size);
return E_INVALIDARG;
}
*data = device->d3d12_caps.options11;
TRACE("AtomicInt64 on descriptor heap resource supported %u.\n", data->AtomicInt64OnDescriptorHeapResourceSupported);
return S_OK;
}
case D3D12_FEATURE_QUERY_META_COMMAND:
{
D3D12_FEATURE_DATA_QUERY_META_COMMAND *data = feature_data;
if (feature_data_size != sizeof(*data))
{
WARN("Invalid size %u.\n", feature_data_size);
return E_INVALIDARG;
}
FIXME("Unsupported meta command %s.\n", debugstr_guid(&data->CommandId));
return E_INVALIDARG;
}
default:
FIXME("Unhandled feature %#x.\n", feature);
return E_NOTIMPL;
}
}
static HRESULT STDMETHODCALLTYPE d3d12_device_CreateDescriptorHeap(d3d12_device_iface *iface,
const D3D12_DESCRIPTOR_HEAP_DESC *desc, REFIID riid, void **descriptor_heap)
{
struct d3d12_device *device = impl_from_ID3D12Device(iface);
struct d3d12_descriptor_heap *object;
HRESULT hr;
TRACE("iface %p, desc %p, riid %s, descriptor_heap %p.\n",
iface, desc, debugstr_guid(riid), descriptor_heap);
if (FAILED(hr = d3d12_descriptor_heap_create(device, desc, &object)))
return hr;
return return_interface(&object->ID3D12DescriptorHeap_iface,
&IID_ID3D12DescriptorHeap, riid, descriptor_heap);
}
static UINT STDMETHODCALLTYPE d3d12_device_GetDescriptorHandleIncrementSize(d3d12_device_iface *iface,
D3D12_DESCRIPTOR_HEAP_TYPE descriptor_heap_type)
{
TRACE("iface %p, descriptor_heap_type %#x.\n", iface, descriptor_heap_type);
return d3d12_device_get_descriptor_handle_increment_size(descriptor_heap_type);
}
static HRESULT STDMETHODCALLTYPE d3d12_device_CreateRootSignature(d3d12_device_iface *iface,
UINT node_mask, const void *bytecode, SIZE_T bytecode_length,
REFIID riid, void **root_signature)
{
struct d3d12_device *device = impl_from_ID3D12Device(iface);
struct d3d12_root_signature *object;
HRESULT hr;
TRACE("iface %p, node_mask 0x%08x, bytecode %p, bytecode_length %lu, riid %s, root_signature %p.\n",
iface, node_mask, bytecode, bytecode_length, debugstr_guid(riid), root_signature);
debug_ignored_node_mask(node_mask);
if (FAILED(hr = d3d12_root_signature_create(device, bytecode, bytecode_length, &object)))
return hr;
return return_interface(&object->ID3D12RootSignature_iface,
&IID_ID3D12RootSignature, riid, root_signature);
}
static void STDMETHODCALLTYPE d3d12_device_CreateConstantBufferView(d3d12_device_iface *iface,
const D3D12_CONSTANT_BUFFER_VIEW_DESC *desc, D3D12_CPU_DESCRIPTOR_HANDLE descriptor)
{
struct d3d12_device *device = impl_from_ID3D12Device(iface);
TRACE("iface %p, desc %p, descriptor %#lx.\n", iface, desc, descriptor.ptr);
d3d12_desc_create_cbv(descriptor.ptr, device, desc);
}
static void STDMETHODCALLTYPE d3d12_device_CreateShaderResourceView(d3d12_device_iface *iface,
ID3D12Resource *resource, const D3D12_SHADER_RESOURCE_VIEW_DESC *desc,
D3D12_CPU_DESCRIPTOR_HANDLE descriptor)
{
struct d3d12_device *device = impl_from_ID3D12Device(iface);
TRACE("iface %p, resource %p, desc %p, descriptor %#lx.\n",
iface, resource, desc, descriptor.ptr);
d3d12_desc_create_srv(descriptor.ptr, device, impl_from_ID3D12Resource(resource), desc);
}
VKD3D_THREAD_LOCAL struct D3D12_UAV_INFO *d3d12_uav_info = NULL;
static void STDMETHODCALLTYPE d3d12_device_CreateUnorderedAccessView(d3d12_device_iface *iface,
ID3D12Resource *resource, ID3D12Resource *counter_resource,
const D3D12_UNORDERED_ACCESS_VIEW_DESC *desc, D3D12_CPU_DESCRIPTOR_HANDLE descriptor)
{
VkImageViewAddressPropertiesNVX out_info = { VK_STRUCTURE_TYPE_IMAGE_VIEW_ADDRESS_PROPERTIES_NVX };
VkImageViewHandleInfoNVX imageViewHandleInfo = { VK_STRUCTURE_TYPE_IMAGE_VIEW_HANDLE_INFO_NVX };
const struct vkd3d_vk_device_procs *vk_procs;
VkResult vr;
struct d3d12_resource *d3d12_resource_ = impl_from_ID3D12Resource(resource);
struct d3d12_device *device = impl_from_ID3D12Device(iface);
TRACE("iface %p, resource %p, counter_resource %p, desc %p, descriptor %#lx.\n",
iface, resource, counter_resource, desc, descriptor.ptr);
d3d12_desc_create_uav(descriptor.ptr,
device, d3d12_resource_,
impl_from_ID3D12Resource(counter_resource), desc);
/* d3d12_uav_info stores the pointer to data from previous call to d3d12_device_vkd3d_ext_CaptureUAVInfo(). Below code will update the data. */
if (d3d12_uav_info)
{
struct d3d12_desc_split d = d3d12_desc_decode_va(descriptor.ptr);
imageViewHandleInfo.imageView = d.view->info.view->vk_image_view;
imageViewHandleInfo.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
vk_procs = &device->vk_procs;
d3d12_uav_info->surfaceHandle = VK_CALL(vkGetImageViewHandleNVX(device->vk_device, &imageViewHandleInfo));
if ((vr = VK_CALL(vkGetImageViewAddressNVX(device->vk_device, imageViewHandleInfo.imageView, &out_info))) < 0)
{
ERR("Failed to get imageview address, vr %d.\n", vr);
return;
}
d3d12_uav_info->gpuVAStart = out_info.deviceAddress;
d3d12_uav_info->gpuVASize = out_info.size;
/* Set this to null so that subsequent calls to this API wont update the previous pointer. */
d3d12_uav_info = NULL;
}
}
static void STDMETHODCALLTYPE d3d12_device_CreateRenderTargetView(d3d12_device_iface *iface,
ID3D12Resource *resource, const D3D12_RENDER_TARGET_VIEW_DESC *desc,
D3D12_CPU_DESCRIPTOR_HANDLE descriptor)
{
TRACE("iface %p, resource %p, desc %p, descriptor %#lx.\n",
iface, resource, desc, descriptor.ptr);
d3d12_rtv_desc_create_rtv(d3d12_rtv_desc_from_cpu_handle(descriptor),
impl_from_ID3D12Device(iface), impl_from_ID3D12Resource(resource), desc);
}
static void STDMETHODCALLTYPE d3d12_device_CreateDepthStencilView(d3d12_device_iface *iface,
ID3D12Resource *resource, const D3D12_DEPTH_STENCIL_VIEW_DESC *desc,
D3D12_CPU_DESCRIPTOR_HANDLE descriptor)
{
TRACE("iface %p, resource %p, desc %p, descriptor %#lx.\n",
iface, resource, desc, descriptor.ptr);
d3d12_rtv_desc_create_dsv(d3d12_rtv_desc_from_cpu_handle(descriptor),
impl_from_ID3D12Device(iface), impl_from_ID3D12Resource(resource), desc);
}
static void STDMETHODCALLTYPE d3d12_device_CreateSampler(d3d12_device_iface *iface,
const D3D12_SAMPLER_DESC *desc, D3D12_CPU_DESCRIPTOR_HANDLE descriptor)
{
struct d3d12_device *device = impl_from_ID3D12Device(iface);
TRACE("iface %p, desc %p, descriptor %#lx.\n", iface, desc, descriptor.ptr);
d3d12_desc_create_sampler(descriptor.ptr, device, desc);
}
static inline D3D12_CPU_DESCRIPTOR_HANDLE d3d12_advance_cpu_descriptor_handle(D3D12_CPU_DESCRIPTOR_HANDLE handle,
unsigned int increment, unsigned int units)
{
handle.ptr += increment * units;
return handle;
}
static inline void d3d12_device_copy_descriptors_cbv_srv_uav_sampler(struct d3d12_device *device,
D3D12_CPU_DESCRIPTOR_HANDLE dst, D3D12_CPU_DESCRIPTOR_HANDLE src,
D3D12_DESCRIPTOR_HEAP_TYPE heap_type,
UINT descriptor_count)
{
#ifndef VKD3D_ENABLE_DESCRIPTOR_QA
if (descriptor_count == 1)
{
/* Most common path. This path is faster for 1 descriptor. */
d3d12_desc_copy_single(dst.ptr, src.ptr, device);
}
else
#endif
{
d3d12_desc_copy(dst.ptr, src.ptr, descriptor_count, heap_type, device);
}
}
static inline void d3d12_device_copy_descriptors(struct d3d12_device *device,
UINT dst_descriptor_range_count, const D3D12_CPU_DESCRIPTOR_HANDLE *dst_descriptor_range_offsets,
const UINT *dst_descriptor_range_sizes,
UINT src_descriptor_range_count, const D3D12_CPU_DESCRIPTOR_HANDLE *src_descriptor_range_offsets,
const UINT *src_descriptor_range_sizes,
D3D12_DESCRIPTOR_HEAP_TYPE descriptor_heap_type)
{
unsigned int dst_range_idx, dst_idx, src_range_idx, src_idx;
D3D12_CPU_DESCRIPTOR_HANDLE dst, src, dst_start, src_start;
unsigned int dst_range_size, src_range_size, copy_count;
unsigned int increment;
increment = d3d12_device_get_descriptor_handle_increment_size(descriptor_heap_type);
dst_range_idx = dst_idx = 0;
src_range_idx = src_idx = 0;
while (dst_range_idx < dst_descriptor_range_count && src_range_idx < src_descriptor_range_count)
{
dst_range_size = dst_descriptor_range_sizes ? dst_descriptor_range_sizes[dst_range_idx] : 1;
src_range_size = src_descriptor_range_sizes ? src_descriptor_range_sizes[src_range_idx] : 1;
dst_start = dst_descriptor_range_offsets[dst_range_idx];
src_start = src_descriptor_range_offsets[src_range_idx];
copy_count = min(dst_range_size - dst_idx, src_range_size - src_idx);
dst = d3d12_advance_cpu_descriptor_handle(dst_start, increment, dst_idx);
src = d3d12_advance_cpu_descriptor_handle(src_start, increment, src_idx);
switch (descriptor_heap_type)
{
case D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER:
case D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV:
d3d12_desc_copy(dst.ptr, src.ptr, copy_count,
descriptor_heap_type, device);
break;
case D3D12_DESCRIPTOR_HEAP_TYPE_RTV:
case D3D12_DESCRIPTOR_HEAP_TYPE_DSV:
d3d12_rtv_desc_copy(d3d12_rtv_desc_from_cpu_handle(dst),
d3d12_rtv_desc_from_cpu_handle(src), copy_count);
break;
default:
ERR("Unhandled descriptor heap type %u.\n", descriptor_heap_type);
return;
}
dst_idx += copy_count;
src_idx += copy_count;
if (dst_idx >= dst_range_size)
{
++dst_range_idx;
dst_idx = 0;
}
if (src_idx >= src_range_size)
{
++src_range_idx;
src_idx = 0;
}
}
}
static void STDMETHODCALLTYPE d3d12_device_CopyDescriptors(d3d12_device_iface *iface,
UINT dst_descriptor_range_count, const D3D12_CPU_DESCRIPTOR_HANDLE *dst_descriptor_range_offsets,
const UINT *dst_descriptor_range_sizes,
UINT src_descriptor_range_count, const D3D12_CPU_DESCRIPTOR_HANDLE *src_descriptor_range_offsets,
const UINT *src_descriptor_range_sizes,
D3D12_DESCRIPTOR_HEAP_TYPE descriptor_heap_type)
{
TRACE("iface %p, dst_descriptor_range_count %u, dst_descriptor_range_offsets %p, "
"dst_descriptor_range_sizes %p, src_descriptor_range_count %u, "
"src_descriptor_range_offsets %p, src_descriptor_range_sizes %p, "
"descriptor_heap_type %#x.\n",
iface, dst_descriptor_range_count, dst_descriptor_range_offsets,
dst_descriptor_range_sizes, src_descriptor_range_count, src_descriptor_range_offsets,
src_descriptor_range_sizes, descriptor_heap_type);
d3d12_device_copy_descriptors(impl_from_ID3D12Device(iface),
dst_descriptor_range_count, dst_descriptor_range_offsets,
dst_descriptor_range_sizes,
src_descriptor_range_count, src_descriptor_range_offsets,
src_descriptor_range_sizes,
descriptor_heap_type);
}
static void STDMETHODCALLTYPE d3d12_device_CopyDescriptorsSimple(d3d12_device_iface *iface,
UINT descriptor_count, const D3D12_CPU_DESCRIPTOR_HANDLE dst_descriptor_range_offset,
const D3D12_CPU_DESCRIPTOR_HANDLE src_descriptor_range_offset,
D3D12_DESCRIPTOR_HEAP_TYPE descriptor_heap_type)
{
struct d3d12_device *device;
TRACE("iface %p, descriptor_count %u, dst_descriptor_range_offset %#lx, "
"src_descriptor_range_offset %#lx, descriptor_heap_type %#x.\n",
iface, descriptor_count, dst_descriptor_range_offset.ptr, src_descriptor_range_offset.ptr,
descriptor_heap_type);
device = unsafe_impl_from_ID3D12Device(iface);
if (descriptor_heap_type == D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV ||
descriptor_heap_type == D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER)
{
/* Fast and hot path. */
d3d12_device_copy_descriptors_cbv_srv_uav_sampler(device,
dst_descriptor_range_offset, src_descriptor_range_offset,
descriptor_heap_type,
descriptor_count);
}
else
{
d3d12_device_copy_descriptors(device,
1, &dst_descriptor_range_offset, &descriptor_count,
1, &src_descriptor_range_offset, &descriptor_count,
descriptor_heap_type);
}
}
static D3D12_RESOURCE_ALLOCATION_INFO* STDMETHODCALLTYPE d3d12_device_GetResourceAllocationInfo1(d3d12_device_iface *iface,
D3D12_RESOURCE_ALLOCATION_INFO *info, UINT visible_mask, UINT count, const D3D12_RESOURCE_DESC *resource_descs,
D3D12_RESOURCE_ALLOCATION_INFO1 *resource_infos);
static D3D12_RESOURCE_ALLOCATION_INFO * STDMETHODCALLTYPE d3d12_device_GetResourceAllocationInfo(
d3d12_device_iface *iface, D3D12_RESOURCE_ALLOCATION_INFO *info, UINT visible_mask,
UINT count, const D3D12_RESOURCE_DESC *resource_descs)
{
TRACE("iface %p, info %p, visible_mask 0x%08x, count %u, resource_descs %p.\n",
iface, info, visible_mask, count, resource_descs);
return d3d12_device_GetResourceAllocationInfo1(iface, info, visible_mask, count, resource_descs, NULL);
}
static D3D12_HEAP_PROPERTIES * STDMETHODCALLTYPE d3d12_device_GetCustomHeapProperties(d3d12_device_iface *iface,
D3D12_HEAP_PROPERTIES *heap_properties, UINT node_mask, D3D12_HEAP_TYPE heap_type)
{
struct d3d12_device *device = impl_from_ID3D12Device(iface);
bool coherent;
TRACE("iface %p, heap_properties %p, node_mask 0x%08x, heap_type %#x.\n",
iface, heap_properties, node_mask, heap_type);
debug_ignored_node_mask(node_mask);
heap_properties->Type = D3D12_HEAP_TYPE_CUSTOM;
switch (heap_type)
{
case D3D12_HEAP_TYPE_DEFAULT:
heap_properties->CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_NOT_AVAILABLE;
heap_properties->MemoryPoolPreference = d3d12_device_is_uma(device, NULL)
? D3D12_MEMORY_POOL_L0 : D3D12_MEMORY_POOL_L1;
break;
case D3D12_HEAP_TYPE_UPLOAD:
heap_properties->CPUPageProperty = d3d12_device_is_uma(device, &coherent) && coherent
? D3D12_CPU_PAGE_PROPERTY_WRITE_BACK : D3D12_CPU_PAGE_PROPERTY_WRITE_COMBINE;
heap_properties->MemoryPoolPreference = D3D12_MEMORY_POOL_L0;
break;
case D3D12_HEAP_TYPE_READBACK:
heap_properties->CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_WRITE_BACK;
heap_properties->MemoryPoolPreference = D3D12_MEMORY_POOL_L0;
break;
default:
FIXME("Unhandled heap type %#x.\n", heap_type);
break;
};
heap_properties->CreationNodeMask = 1;
heap_properties->VisibleNodeMask = 1;
return heap_properties;
}
static HRESULT STDMETHODCALLTYPE d3d12_device_CreateCommittedResource1(d3d12_device_iface *iface,
const D3D12_HEAP_PROPERTIES *heap_properties, D3D12_HEAP_FLAGS heap_flags,
const D3D12_RESOURCE_DESC *desc, D3D12_RESOURCE_STATES initial_state,
const D3D12_CLEAR_VALUE *optimized_clear_value,
ID3D12ProtectedResourceSession *protected_session,
REFIID iid, void **resource);
static HRESULT STDMETHODCALLTYPE d3d12_device_CreateCommittedResource(d3d12_device_iface *iface,
const D3D12_HEAP_PROPERTIES *heap_properties, D3D12_HEAP_FLAGS heap_flags,
const D3D12_RESOURCE_DESC *desc, D3D12_RESOURCE_STATES initial_state,
const D3D12_CLEAR_VALUE *optimized_clear_value, REFIID iid, void **resource)
{
TRACE("iface %p, heap_properties %p, heap_flags %#x, desc %p, initial_state %#x, "
"optimized_clear_value %p, iid %s, resource %p.\n",
iface, heap_properties, heap_flags, desc, initial_state,
optimized_clear_value, debugstr_guid(iid), resource);
return d3d12_device_CreateCommittedResource1(iface, heap_properties, heap_flags,
desc, initial_state, optimized_clear_value, NULL, iid, resource);
}
static HRESULT STDMETHODCALLTYPE d3d12_device_CreateHeap1(d3d12_device_iface *iface,
const D3D12_HEAP_DESC *desc, ID3D12ProtectedResourceSession *protected_session,
REFIID iid, void **heap);
static HRESULT STDMETHODCALLTYPE d3d12_device_CreateHeap(d3d12_device_iface *iface,
const D3D12_HEAP_DESC *desc, REFIID iid, void **heap)
{
TRACE("iface %p, desc %p, iid %s, heap %p.\n",
iface, desc, debugstr_guid(iid), heap);
return d3d12_device_CreateHeap1(iface, desc, NULL, iid, heap);
}
static HRESULT STDMETHODCALLTYPE d3d12_device_CreatePlacedResource1(d3d12_device_iface *iface,
ID3D12Heap *heap, UINT64 heap_offset, const D3D12_RESOURCE_DESC1 *resource_desc,
D3D12_RESOURCE_STATES initial_state, const D3D12_CLEAR_VALUE *optimized_clear_value,
REFIID riid, void **resource);
static HRESULT STDMETHODCALLTYPE d3d12_device_CreatePlacedResource(d3d12_device_iface *iface,
ID3D12Heap *heap, UINT64 heap_offset,
const D3D12_RESOURCE_DESC *desc, D3D12_RESOURCE_STATES initial_state,
const D3D12_CLEAR_VALUE *optimized_clear_value, REFIID iid, void **resource)
{
D3D12_RESOURCE_DESC1 desc1;
TRACE("iface %p, heap %p, heap_offset %#"PRIx64", desc %p, initial_state %#x, "
"optimized_clear_value %p, riid %s, resource %p.\n",
iface, heap, heap_offset, desc, initial_state,
optimized_clear_value, debugstr_guid(iid), resource);
d3d12_resource_promote_desc(desc, &desc1);
return d3d12_device_CreatePlacedResource1(iface, heap, heap_offset, &desc1,
initial_state, optimized_clear_value, iid, resource);
}
static HRESULT STDMETHODCALLTYPE d3d12_device_CreateReservedResource1(d3d12_device_iface *iface,
const D3D12_RESOURCE_DESC *desc, D3D12_RESOURCE_STATES initial_state, const D3D12_CLEAR_VALUE *optimized_clear_value,
ID3D12ProtectedResourceSession *protected_session, REFIID iid, void **resource);
static HRESULT STDMETHODCALLTYPE d3d12_device_CreateReservedResource(d3d12_device_iface *iface,
const D3D12_RESOURCE_DESC *desc, D3D12_RESOURCE_STATES initial_state,
const D3D12_CLEAR_VALUE *optimized_clear_value, REFIID iid, void **resource)
{
TRACE("iface %p, desc %p, initial_state %#x, optimized_clear_value %p, iid %s, resource %p.\n",
iface, desc, initial_state, optimized_clear_value, debugstr_guid(iid), resource);
return d3d12_device_CreateReservedResource1(iface, desc, initial_state,
optimized_clear_value, NULL, iid, resource);
}
static HRESULT STDMETHODCALLTYPE d3d12_device_CreateSharedHandle(d3d12_device_iface *iface,
ID3D12DeviceChild *object, const SECURITY_ATTRIBUTES *attributes, DWORD access,
const WCHAR *name, HANDLE *handle)
{
#ifdef _WIN32
struct d3d12_device *device = impl_from_ID3D12Device(iface);
const struct vkd3d_vk_device_procs *vk_procs;
struct DxvkSharedTextureMetadata metadata;
ID3D12Resource *resource_iface;
ID3D12Fence *fence_iface;
vk_procs = &device->vk_procs;
TRACE("iface %p, object %p, attributes %p, access %#x, name %s, handle %p\n",
iface, object, attributes, access, debugstr_w(name), handle);
if (SUCCEEDED(ID3D12DeviceChild_QueryInterface(object, &IID_ID3D12Resource, (void**)&resource_iface)))
{
struct d3d12_resource *resource = impl_from_ID3D12Resource(resource_iface);
VkMemoryGetWin32HandleInfoKHR win32_handle_info;
VkResult vr;
if (!(resource->heap_flags & D3D12_HEAP_FLAG_SHARED))
{
ID3D12Resource_Release(resource_iface);
return DXGI_ERROR_INVALID_CALL;
}
if (attributes)
FIXME("attributes %p not handled.\n", attributes);
if (access)
FIXME("access %#x not handled.\n", access);
if (name)
FIXME("name %s not handled.\n", debugstr_w(name));
win32_handle_info.sType = VK_STRUCTURE_TYPE_MEMORY_GET_WIN32_HANDLE_INFO_KHR;
win32_handle_info.pNext = NULL;
win32_handle_info.memory = resource->mem.device_allocation.vk_memory;
win32_handle_info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT;
vr = VK_CALL(vkGetMemoryWin32HandleKHR(device->vk_device, &win32_handle_info, handle));
if (vr == VK_SUCCESS)
{
if (resource->desc.Dimension != D3D12_RESOURCE_DIMENSION_TEXTURE2D)
{
FIXME("Shared texture metadata structure only supports 2D textures.");
}
else
{
metadata.Width = resource->desc.Width;
metadata.Height = resource->desc.Height;
metadata.MipLevels = resource->desc.MipLevels;
metadata.ArraySize = resource->desc.DepthOrArraySize;
metadata.Format = resource->desc.Format;
metadata.SampleDesc = resource->desc.SampleDesc;
metadata.Usage = D3D11_USAGE_DEFAULT;
metadata.BindFlags = D3D11_BIND_SHADER_RESOURCE;
metadata.CPUAccessFlags = 0;
metadata.MiscFlags = D3D11_RESOURCE_MISC_SHARED_NTHANDLE;
if (resource->desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET)
metadata.BindFlags |= D3D11_BIND_RENDER_TARGET;
if (resource->desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL)
metadata.BindFlags |= D3D11_BIND_DEPTH_STENCIL;
if (resource->desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS)
metadata.BindFlags |= D3D11_BIND_UNORDERED_ACCESS;
if (resource->desc.Flags & D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE)
metadata.BindFlags &= ~D3D11_BIND_SHADER_RESOURCE;
if (!vkd3d_set_shared_metadata(*handle, &metadata, sizeof(metadata)))
ERR("Failed to set metadata for shared resource, importing created handle will fail.\n");
}
}
ID3D12Resource_Release(resource_iface);
return vr ? E_FAIL : S_OK;
}
if (SUCCEEDED(ID3D12DeviceChild_QueryInterface(object, &IID_ID3D12Fence, (void**)&fence_iface)))
{
VkSemaphoreGetWin32HandleInfoKHR win32_handle_info;
struct d3d12_shared_fence *fence;
VkResult vr;
if (!is_shared_ID3D12Fence(fence_iface))
{
ID3D12Fence_Release(fence_iface);
return DXGI_ERROR_INVALID_CALL;
}
fence = shared_impl_from_ID3D12Fence(fence_iface);
if (attributes)
FIXME("attributes %p not handled\n", attributes);
if (access)
FIXME("access %#x not handled\n", access);
if (name)
FIXME("name %s not handled\n", debugstr_w(name));
win32_handle_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_WIN32_HANDLE_INFO_KHR;
win32_handle_info.pNext = NULL;
win32_handle_info.semaphore = fence->timeline_semaphore;
win32_handle_info.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT;
vr = VK_CALL(vkGetSemaphoreWin32HandleKHR(device->vk_device, &win32_handle_info, handle));
ID3D12Fence_Release(fence_iface);
return vr ? E_FAIL : S_OK;
}
FIXME("Creating shared handle for type of object %p unsupported.\n", object);
return E_NOTIMPL;
#else
FIXME("CreateSharedHandle can only be implemented in native Win32.\n");
return E_NOTIMPL;
#endif
}
#ifdef _WIN32
static inline bool handle_is_kmt_style(HANDLE handle)
{
return ((ULONG_PTR)handle & 0x40000000) && ((ULONG_PTR)handle - 2) % 4 == 0;
}
#endif
static HRESULT STDMETHODCALLTYPE d3d12_device_OpenSharedHandle(d3d12_device_iface *iface,
HANDLE handle, REFIID riid, void **object)
{
#ifdef _WIN32
struct d3d12_device *device = impl_from_ID3D12Device(iface);
const struct vkd3d_vk_device_procs *vk_procs;
HRESULT hr;
vk_procs = &device->vk_procs;
TRACE("iface %p, handle %p, riid %s, object %p\n",
iface, handle, debugstr_guid(riid), object);
if (IsEqualGUID(riid, &IID_ID3D12Resource))
{
struct DxvkSharedTextureMetadata metadata;
D3D12_HEAP_PROPERTIES heap_props;
struct d3d12_resource *resource;
D3D12_RESOURCE_DESC1 desc;
bool kmt_handle = false;
if (handle_is_kmt_style(handle))
{
handle = vkd3d_open_kmt_handle(handle);
kmt_handle = true;
if (handle == INVALID_HANDLE_VALUE)
{
WARN("Failed to open KMT-style ID3D12Resource shared handle.\n");
*object = NULL;
return E_INVALIDARG;
}
}
if (!vkd3d_get_shared_metadata(handle, &metadata, sizeof(metadata), NULL))
{
WARN("Failed to get ID3D12Resource shared handle metadata.\n");
if (kmt_handle)
CloseHandle(handle);
*object = NULL;
return E_INVALIDARG;
}
desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
desc.Alignment = 0;
desc.Width = metadata.Width;
desc.Height = metadata.Height;
desc.DepthOrArraySize = metadata.ArraySize;
desc.MipLevels = metadata.MipLevels;
desc.Format = metadata.Format;
desc.SampleDesc = metadata.SampleDesc;
switch (metadata.TextureLayout)
{
case D3D11_TEXTURE_LAYOUT_UNDEFINED: desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; break;
case D3D11_TEXTURE_LAYOUT_ROW_MAJOR: desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; break;
case D3D11_TEXTURE_LAYOUT_64K_STANDARD_SWIZZLE: desc.Layout = D3D12_TEXTURE_LAYOUT_64KB_STANDARD_SWIZZLE; break;
default: desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
}
desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS;
if (metadata.BindFlags & D3D11_BIND_RENDER_TARGET)
desc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
if (metadata.BindFlags & D3D11_BIND_DEPTH_STENCIL)
desc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
if (metadata.BindFlags & D3D11_BIND_UNORDERED_ACCESS)
desc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
if ((metadata.BindFlags & D3D11_BIND_DEPTH_STENCIL) && !(metadata.BindFlags & D3D11_BIND_SHADER_RESOURCE))
desc.Flags |= D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE;
desc.SamplerFeedbackMipRegion.Width = 0;
desc.SamplerFeedbackMipRegion.Height = 0;
desc.SamplerFeedbackMipRegion.Depth = 0;
heap_props.Type = D3D12_HEAP_TYPE_DEFAULT;
heap_props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
heap_props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
heap_props.CreationNodeMask = 0;
heap_props.VisibleNodeMask = 0;
hr = d3d12_resource_create_committed(device, &desc, &heap_props,
D3D12_HEAP_FLAG_SHARED, D3D12_RESOURCE_STATE_COMMON, NULL, handle, &resource);
if (kmt_handle)
CloseHandle(handle);
if (FAILED(hr))
{
WARN("Failed to open shared ID3D12Resource, hr %#x.\n", hr);
*object = NULL;
return hr;
}
return return_interface(&resource->ID3D12Resource_iface, &IID_ID3D12Resource, riid, object);
}
if (IsEqualGUID(riid, &IID_ID3D12Fence))
{
VkImportSemaphoreWin32HandleInfoKHR import_info;
struct d3d12_shared_fence *fence;
VkResult vr;
hr = d3d12_shared_fence_create(device, 0, D3D12_FENCE_FLAG_SHARED, &fence);
if (FAILED(hr))
{
WARN("Failed to create object for imported ID3D12Fence, hr %#x.\n", hr);
*object = NULL;
return hr;
}
import_info.sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_WIN32_HANDLE_INFO_KHR;
import_info.pNext = NULL;
import_info.semaphore = fence->timeline_semaphore;
import_info.flags = 0;
import_info.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT;
import_info.handle = handle;
import_info.name = NULL;
vr = VK_CALL(vkImportSemaphoreWin32HandleKHR(device->vk_device, &import_info));
if (vr != VK_SUCCESS)
{
WARN("Failed to open shared ID3D12Fence, vr %d.\n", vr);
ID3D12Fence1_Release(&fence->ID3D12Fence_iface);
*object = NULL;
return E_FAIL;
}
return return_interface(&fence->ID3D12Fence_iface, &IID_ID3D12Fence, riid, object);
}
FIXME("Opening shared handle type %s unsupported\n", debugstr_guid(riid));
return E_NOTIMPL;
#else
FIXME("OpenSharedhandle can only be implemented in native Win32.\n");
return E_NOTIMPL;
#endif
}
static HRESULT STDMETHODCALLTYPE d3d12_device_OpenSharedHandleByName(d3d12_device_iface *iface,
const WCHAR *name, DWORD access, HANDLE *handle)
{
FIXME("iface %p, name %s, access %#x, handle %p stub!\n",
iface, debugstr_w(name), access, handle);
return E_NOTIMPL;
}
static HRESULT STDMETHODCALLTYPE d3d12_device_MakeResident(d3d12_device_iface *iface,
UINT object_count, ID3D12Pageable * const *objects)
{
FIXME_ONCE("iface %p, object_count %u, objects %p stub!\n",
iface, object_count, objects);
return S_OK;
}
static HRESULT STDMETHODCALLTYPE d3d12_device_Evict(d3d12_device_iface *iface,
UINT object_count, ID3D12Pageable * const *objects)
{
FIXME_ONCE("iface %p, object_count %u, objects %p stub!\n",
iface, object_count, objects);
return S_OK;
}
static HRESULT STDMETHODCALLTYPE d3d12_device_CreateFence(d3d12_device_iface *iface,
UINT64 initial_value, D3D12_FENCE_FLAGS flags, REFIID riid, void **fence)
{
struct d3d12_device *device = impl_from_ID3D12Device(iface);
struct d3d12_shared_fence *shared_object;
struct d3d12_fence *object;
HRESULT hr;
TRACE("iface %p, intial_value %#"PRIx64", flags %#x, riid %s, fence %p.\n",
iface, initial_value, flags, debugstr_guid(riid), fence);
if (flags & D3D12_FENCE_FLAG_SHARED)
{
if (SUCCEEDED(hr = d3d12_shared_fence_create(device, initial_value, flags, &shared_object)))
return return_interface(&shared_object->ID3D12Fence_iface, &IID_ID3D12Fence, riid, fence);
if (hr != E_NOTIMPL)
return hr;
FIXME("Shared fences not supported by Vulkan host, returning regular fence.\n");
}
if (FAILED(hr = d3d12_fence_create(device, initial_value, flags, &object)))
return hr;
return return_interface(&object->ID3D12Fence_iface, &IID_ID3D12Fence, riid, fence);
}
static HRESULT STDMETHODCALLTYPE d3d12_device_GetDeviceRemovedReason(d3d12_device_iface *iface)
{
struct d3d12_device *device = impl_from_ID3D12Device(iface);
TRACE("iface %p.\n", iface);
return device->removed_reason;
}
static void STDMETHODCALLTYPE d3d12_device_GetCopyableFootprints1(d3d12_device_iface *iface,
const D3D12_RESOURCE_DESC1 *desc, UINT first_sub_resource, UINT sub_resource_count,
UINT64 base_offset, D3D12_PLACED_SUBRESOURCE_FOOTPRINT *layouts, UINT *row_counts,
UINT64 *row_sizes, UINT64 *total_bytes);
static void STDMETHODCALLTYPE d3d12_device_GetCopyableFootprints(d3d12_device_iface *iface,
const D3D12_RESOURCE_DESC *desc, UINT first_sub_resource, UINT sub_resource_count,
UINT64 base_offset, D3D12_PLACED_SUBRESOURCE_FOOTPRINT *layouts,
UINT *row_counts, UINT64 *row_sizes, UINT64 *total_bytes)
{
D3D12_RESOURCE_DESC1 desc1;
TRACE("iface %p, desc %p, first_sub_resource %u, sub_resource_count %u, base_offset %#"PRIx64", "
"layouts %p, row_counts %p, row_sizes %p, total_bytes %p.\n",
iface, desc, first_sub_resource, sub_resource_count, base_offset,
layouts, row_counts, row_sizes, total_bytes);
d3d12_resource_promote_desc(desc, &desc1);
d3d12_device_GetCopyableFootprints1(iface, &desc1, first_sub_resource,
sub_resource_count, base_offset, layouts, row_counts, row_sizes, total_bytes);
}
static HRESULT STDMETHODCALLTYPE d3d12_device_CreateQueryHeap(d3d12_device_iface *iface,
const D3D12_QUERY_HEAP_DESC *desc, REFIID iid, void **heap)
{
struct d3d12_device *device = impl_from_ID3D12Device(iface);
struct d3d12_query_heap *object;
HRESULT hr;
TRACE("iface %p, desc %p, iid %s, heap %p.\n",
iface, desc, debugstr_guid(iid), heap);
if (FAILED(hr = d3d12_query_heap_create(device, desc, &object)))
return hr;
return return_interface(&object->ID3D12QueryHeap_iface, &IID_ID3D12QueryHeap, iid, heap);
}
static HRESULT STDMETHODCALLTYPE d3d12_device_SetStablePowerState(d3d12_device_iface *iface, BOOL enable)
{
FIXME("iface %p, enable %#x stub!\n", iface, enable);
return E_NOTIMPL;
}
static HRESULT STDMETHODCALLTYPE d3d12_device_CreateCommandSignature(d3d12_device_iface *iface,
const D3D12_COMMAND_SIGNATURE_DESC *desc, ID3D12RootSignature *root_signature_iface,
REFIID iid, void **command_signature)
{
struct d3d12_root_signature *root_signature = impl_from_ID3D12RootSignature(root_signature_iface);
struct d3d12_device *device = impl_from_ID3D12Device(iface);
struct d3d12_command_signature *object;
HRESULT hr;
TRACE("iface %p, desc %p, root_signature %p, iid %s, command_signature %p.\n",
iface, desc, root_signature, debugstr_guid(iid), command_signature);
if (FAILED(hr = d3d12_command_signature_create(device, root_signature, desc, &object)))
return hr;
return return_interface(&object->ID3D12CommandSignature_iface,
&IID_ID3D12CommandSignature, iid, command_signature);
}
static void STDMETHODCALLTYPE d3d12_device_GetResourceTiling(d3d12_device_iface *iface,
ID3D12Resource *resource, UINT *tile_count, D3D12_PACKED_MIP_INFO *packed_mip_info,
D3D12_TILE_SHAPE *tile_shape, UINT *tiling_count, UINT first_tiling,
D3D12_SUBRESOURCE_TILING *tilings)
{
struct d3d12_sparse_info *sparse = &impl_from_ID3D12Resource(resource)->sparse;
unsigned int max_tiling_count, i;
TRACE("iface %p, resource %p, tile_count %p, packed_mip_info %p, "
"tile_shape %p, tiling_count %p, first_tiling %u, tilings %p.\n",
iface, resource, tile_count, packed_mip_info, tile_shape, tiling_count,
first_tiling, tilings);
if (tile_count)
*tile_count = sparse->tile_count;
if (packed_mip_info)
*packed_mip_info = sparse->packed_mips;
if (tile_shape)
*tile_shape = sparse->tile_shape;
if (tiling_count)
{
max_tiling_count = sparse->tiling_count - min(first_tiling, sparse->tiling_count);
max_tiling_count = min(max_tiling_count, *tiling_count);
for (i = 0; i < max_tiling_count; i++)
tilings[i] = sparse->tilings[first_tiling + i];
*tiling_count = max_tiling_count;
}
}
static LUID * STDMETHODCALLTYPE d3d12_device_GetAdapterLuid(d3d12_device_iface *iface, LUID *luid)
{
struct d3d12_device *device = impl_from_ID3D12Device(iface);
TRACE("iface %p, luid %p.\n", iface, luid);
*luid = device->adapter_luid;
return luid;
}
static HRESULT STDMETHODCALLTYPE d3d12_device_CreatePipelineLibrary(d3d12_device_iface *iface,
const void *blob, SIZE_T blob_size, REFIID iid, void **lib)
{
struct d3d12_device *device = impl_from_ID3D12Device(iface);
struct d3d12_pipeline_library *pipeline_library;
uint32_t flags;
HRESULT hr;
TRACE("iface %p, blob %p, blob_size %lu, iid %s, lib %p.\n",
iface, blob, blob_size, debugstr_guid(iid), lib);
flags = 0;
/* If we use a disk cache, it is somewhat meaningless to use application pipeline libraries
* to store SPIR-V / blob.
* We only need to store metadata so we can implement the API correctly w.r.t. return values, and
* PSO reload. */
if (!(vkd3d_config_flags & VKD3D_CONFIG_FLAG_PIPELINE_LIBRARY_NO_SERIALIZE_SPIRV) &&
!device->disk_cache.library)
flags |= VKD3D_PIPELINE_LIBRARY_FLAG_SAVE_FULL_SPIRV;
/* If we're using global pipeline caches, these are irrelevant.
* Do not use pipeline library blobs at all. */
if (!(vkd3d_config_flags & VKD3D_CONFIG_FLAG_GLOBAL_PIPELINE_CACHE))
{
flags |= VKD3D_PIPELINE_LIBRARY_FLAG_SAVE_PSO_BLOB |
VKD3D_PIPELINE_LIBRARY_FLAG_USE_PIPELINE_CACHE_UUID;
}
if (FAILED(hr = d3d12_pipeline_library_create(device, blob, blob_size,
flags, &pipeline_library)))
return hr;
if (lib)
{
return return_interface(&pipeline_library->ID3D12PipelineLibrary_iface,
&IID_ID3D12PipelineLibrary, iid, lib);
}
else
{
ID3D12PipelineLibrary_Release(&pipeline_library->ID3D12PipelineLibrary_iface);
return S_FALSE;
}
}
static HRESULT STDMETHODCALLTYPE d3d12_device_SetEventOnMultipleFenceCompletion(d3d12_device_iface *iface,
ID3D12Fence *const *fences, const UINT64 *values, UINT fence_count,
D3D12_MULTIPLE_FENCE_WAIT_FLAGS flags, HANDLE event)
{
FIXME("iface %p, fences %p, values %p, fence_count %u, flags %#x, event %p stub!\n",
iface, fences, values, fence_count, flags, event);
return E_NOTIMPL;
}
static HRESULT STDMETHODCALLTYPE d3d12_device_SetResidencyPriority(d3d12_device_iface *iface,
UINT object_count, ID3D12Pageable *const *objects, const D3D12_RESIDENCY_PRIORITY *priorities)
{
FIXME_ONCE("iface %p, object_count %u, objects %p, priorities %p stub!\n",
iface, object_count, objects, priorities);
return S_OK;
}
static HRESULT STDMETHODCALLTYPE d3d12_device_CreatePipelineState(d3d12_device_iface *iface,
const D3D12_PIPELINE_STATE_STREAM_DESC *desc, REFIID riid, void **pipeline_state)
{
struct d3d12_device *device = impl_from_ID3D12Device(iface);
struct d3d12_pipeline_state_desc pipeline_desc;
struct d3d12_pipeline_state *object;
VkPipelineBindPoint pipeline_type;
HRESULT hr;
TRACE("iface %p, desc %p, riid %s, pipeline_state %p.\n",
iface, desc, debugstr_guid(riid), pipeline_state);
if (FAILED(hr = vkd3d_pipeline_state_desc_from_d3d12_stream_desc(&pipeline_desc, desc, &pipeline_type)))
return hr;
if (FAILED(hr = d3d12_pipeline_state_create(device, pipeline_type, &pipeline_desc, &object)))
return hr;
return return_interface(&object->ID3D12PipelineState_iface,
&IID_ID3D12PipelineState, riid, pipeline_state);
}
static HRESULT STDMETHODCALLTYPE d3d12_device_OpenExistingHeapFromAddress(d3d12_device_iface *iface,
void *address, REFIID riid, void **heap)
{
#ifdef _WIN32
MEMORY_BASIC_INFORMATION info;
struct d3d12_device *device;
struct d3d12_heap *object;
D3D12_HEAP_DESC heap_desc;
size_t allocation_size;
HRESULT hr;
TRACE("iface %p, address %p, riid %s, heap %p\n",
iface, address, debugstr_guid(riid), heap);
if (!VirtualQuery(address, &info, sizeof(info)))
{
ERR("Failed to VirtualQuery host pointer.\n");
return E_INVALIDARG;
}
/* Allocation base must equal address. */
if (info.AllocationBase != address)
return E_INVALIDARG;
if (info.BaseAddress != info.AllocationBase)
return E_INVALIDARG;
/* All pages must be committed. */
if (info.State != MEM_COMMIT)
return E_INVALIDARG;
/* We can only have one region of page protection types.
* Verify this by querying the end of the range. */
allocation_size = info.RegionSize;
if (VirtualQuery((uint8_t *)address + allocation_size, &info, sizeof(info)) &&
info.AllocationBase == address)
{
/* All pages must have same protections, so there cannot be multiple regions for VirtualQuery. */
return E_INVALIDARG;
}
device = impl_from_ID3D12Device(iface);
memset(&heap_desc, 0, sizeof(heap_desc));
heap_desc.Alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;
heap_desc.Flags = D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS |
(address ? (D3D12_HEAP_FLAG_SHARED | D3D12_HEAP_FLAG_SHARED_CROSS_ADAPTER) : 0);
heap_desc.Properties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_WRITE_BACK;
heap_desc.Properties.MemoryPoolPreference = D3D12_MEMORY_POOL_L0;
heap_desc.Properties.Type = D3D12_HEAP_TYPE_CUSTOM;
heap_desc.Properties.CreationNodeMask = 1;
heap_desc.Properties.VisibleNodeMask = 1;
heap_desc.SizeInBytes = allocation_size;
if (FAILED(hr = d3d12_heap_create(device, &heap_desc, address, &object)))
{
*heap = NULL;
return hr;
}
return return_interface(&object->ID3D12Heap_iface, &IID_ID3D12Heap, riid, heap);
#else
FIXME("OpenExistingHeapFromAddress can only be implemented in native Win32.\n");
return E_NOTIMPL;
#endif
}
static HRESULT STDMETHODCALLTYPE d3d12_device_OpenExistingHeapFromFileMapping(d3d12_device_iface *iface,
HANDLE file_mapping, REFIID riid, void **heap)
{
#ifdef _WIN32
void *addr;
HRESULT hr;
TRACE("iface %p, file_mapping %p, riid %s, heap %p\n",
iface, file_mapping, debugstr_guid(riid), heap);
/* 0 size maps everything. */
addr = MapViewOfFile(file_mapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);
if (!addr)
return E_INVALIDARG;
hr = d3d12_device_OpenExistingHeapFromAddress(iface, addr, riid, heap);
UnmapViewOfFile(addr);
return hr;
#else
FIXME("OpenExistingHeapFromFileMapping can only be implemented in native Win32.\n");
return E_NOTIMPL;
#endif
}
static HRESULT STDMETHODCALLTYPE d3d12_device_EnqueueMakeResident(d3d12_device_iface *iface,
D3D12_RESIDENCY_FLAGS flags, UINT num_objects, ID3D12Pageable *const *objects,
ID3D12Fence *fence_to_signal, UINT64 fence_value_to_signal)
{
FIXME_ONCE("iface %p, flags %#x, num_objects %u, objects %p, fence_to_signal %p, fence_value_to_signal %"PRIu64" stub!\n",
iface, flags, num_objects, objects, fence_to_signal, fence_value_to_signal);
return ID3D12Fence_Signal(fence_to_signal, fence_value_to_signal);
}
static HRESULT STDMETHODCALLTYPE d3d12_device_CreateCommandList1(d3d12_device_iface *iface,
UINT node_mask, D3D12_COMMAND_LIST_TYPE type, D3D12_COMMAND_LIST_FLAGS flags,
REFIID riid, void **command_list)
{
struct d3d12_device *device = impl_from_ID3D12Device(iface);
d3d12_command_list_iface *command_list_iface;
HRESULT hr;
TRACE("iface %p, node_mask 0x%08x, type %#x, flags %#x, riid %s, command_list %p.\n",
iface, node_mask, type, flags, debugstr_guid(riid), command_list);
if (type == D3D12_COMMAND_LIST_TYPE_BUNDLE)
{
struct d3d12_bundle *object;
if (FAILED(hr = d3d12_bundle_create(device, node_mask, type, &object)))
return hr;
command_list_iface = &object->ID3D12GraphicsCommandList_iface;
}
else
{
struct d3d12_command_list *object;
if (FAILED(hr = d3d12_command_list_create(device, node_mask, type, &object)))
return hr;
command_list_iface = &object->ID3D12GraphicsCommandList_iface;
}
return return_interface(command_list_iface, &IID_ID3D12GraphicsCommandList, riid, command_list);
}
static HRESULT STDMETHODCALLTYPE d3d12_device_CreateProtectedResourceSession(d3d12_device_iface *iface,
const D3D12_PROTECTED_RESOURCE_SESSION_DESC *desc, REFIID iid, void **session)
{
FIXME("iface %p, desc %p, iid %s, session %p stub!\n",
iface, desc, debugstr_guid(iid), session);
return E_NOTIMPL;
}
static HRESULT STDMETHODCALLTYPE d3d12_device_CreateCommittedResource2(d3d12_device_iface *iface,
const D3D12_HEAP_PROPERTIES *heap_properties, D3D12_HEAP_FLAGS heap_flags, const D3D12_RESOURCE_DESC1 *desc,
D3D12_RESOURCE_STATES initial_state, const D3D12_CLEAR_VALUE *optimized_clear_value,
ID3D12ProtectedResourceSession *protected_session, REFIID iid, void **resource);
static HRESULT STDMETHODCALLTYPE d3d12_device_CreateCommittedResource1(d3d12_device_iface *iface,
const D3D12_HEAP_PROPERTIES *heap_properties, D3D12_HEAP_FLAGS heap_flags,
const D3D12_RESOURCE_DESC *desc, D3D12_RESOURCE_STATES initial_state,
const D3D12_CLEAR_VALUE *optimized_clear_value,
ID3D12ProtectedResourceSession *protected_session,
REFIID iid, void **resource)
{
D3D12_RESOURCE_DESC1 desc1;
TRACE("iface %p, heap_properties %p, heap_flags %#x, desc %p, initial_state %#x, "
"optimized_clear_value %p, protected_session %p, iid %s, resource %p.\n",
iface, heap_properties, heap_flags, desc, initial_state,
optimized_clear_value, protected_session, debugstr_guid(iid), resource);
d3d12_resource_promote_desc(desc, &desc1);
return d3d12_device_CreateCommittedResource2(iface, heap_properties, heap_flags, &desc1,
initial_state, optimized_clear_value, protected_session, iid, resource);
}
static HRESULT STDMETHODCALLTYPE d3d12_device_CreateHeap1(d3d12_device_iface *iface,
const D3D12_HEAP_DESC *desc, ID3D12ProtectedResourceSession *protected_session,
REFIID iid, void **heap)
{
struct d3d12_device *device = impl_from_ID3D12Device(iface);
struct d3d12_heap *object;
HRESULT hr;
TRACE("iface %p, desc %p, protected_session %p, iid %s, heap %p.\n",
iface, desc, protected_session, debugstr_guid(iid), heap);
if (protected_session)
FIXME("Ignoring protected session %p.\n", protected_session);
if (FAILED(hr = d3d12_heap_create(device, desc, NULL, &object)))
{
*heap = NULL;
return hr;
}
return return_interface(&object->ID3D12Heap_iface, &IID_ID3D12Heap, iid, heap);
}
static HRESULT STDMETHODCALLTYPE d3d12_device_CreateReservedResource1(d3d12_device_iface *iface,
const D3D12_RESOURCE_DESC *desc, D3D12_RESOURCE_STATES initial_state, const D3D12_CLEAR_VALUE *optimized_clear_value,
ID3D12ProtectedResourceSession *protected_session, REFIID iid, void **resource)
{
struct d3d12_device *device = impl_from_ID3D12Device(iface);
struct d3d12_resource *object;
D3D12_RESOURCE_DESC1 desc1;
HRESULT hr;
TRACE("iface %p, desc %p, initial_state %#x, optimized_clear_value %p, protected_session %p, iid %s, resource %p.\n",
iface, desc, initial_state, optimized_clear_value, protected_session, debugstr_guid(iid), resource);
if (protected_session)
FIXME("Ignoring protected session %p.\n", protected_session);
d3d12_resource_promote_desc(desc, &desc1);
if (FAILED(hr = d3d12_resource_create_reserved(device, &desc1,
initial_state, optimized_clear_value, &object)))
return hr;
return return_interface(&object->ID3D12Resource_iface, &IID_ID3D12Resource, iid, resource);
}
static D3D12_RESOURCE_ALLOCATION_INFO* STDMETHODCALLTYPE d3d12_device_GetResourceAllocationInfo2(d3d12_device_iface *iface,
D3D12_RESOURCE_ALLOCATION_INFO *info, UINT visible_mask, UINT count, const D3D12_RESOURCE_DESC1 *resource_descs,
D3D12_RESOURCE_ALLOCATION_INFO1 *resource_infos);
static D3D12_RESOURCE_ALLOCATION_INFO* STDMETHODCALLTYPE d3d12_device_GetResourceAllocationInfo1(d3d12_device_iface *iface,
D3D12_RESOURCE_ALLOCATION_INFO *info, UINT visible_mask, UINT count, const D3D12_RESOURCE_DESC *resource_descs,
D3D12_RESOURCE_ALLOCATION_INFO1 *resource_infos)
{
D3D12_RESOURCE_DESC1 local_descs[16];
D3D12_RESOURCE_DESC1 *desc1;
unsigned int i;
TRACE("iface %p, info %p, visible_mask 0x%08x, count %u, resource_descs %p, resource_infos %p.\n",
iface, info, visible_mask, count, resource_descs, resource_infos);
if (count > ARRAY_SIZE(local_descs))
desc1 = vkd3d_malloc(sizeof(*desc1) * count);
else
{
/* Avoid a compiler warning */
memset(local_descs, 0, sizeof(local_descs));
desc1 = local_descs;
}
for (i = 0; i < count; i++)
d3d12_resource_promote_desc(&resource_descs[i], &desc1[i]);
d3d12_device_GetResourceAllocationInfo2(iface, info, visible_mask, count, desc1, resource_infos);
if (desc1 != local_descs)
vkd3d_free(desc1);
return info;
}
static HRESULT STDMETHODCALLTYPE d3d12_device_CreateLifetimeTracker(d3d12_device_iface *iface,
ID3D12LifetimeOwner *owner, REFIID iid, void **tracker)
{
FIXME("iface %p, owner %p, iid %s, tracker %p stub!\n",
iface, owner, debugstr_guid(iid), tracker);
return E_NOTIMPL;
}
static void STDMETHODCALLTYPE d3d12_device_RemoveDevice(d3d12_device_iface *iface)
{
FIXME("iface %p stub!\n", iface);
}
static HRESULT STDMETHODCALLTYPE d3d12_device_EnumerateMetaCommands(d3d12_device_iface *iface,
UINT *count, D3D12_META_COMMAND_DESC *descs)
{
FIXME("iface %p, count %p, descs %p stub!\n", iface, count, descs);
return E_NOTIMPL;
}
static HRESULT STDMETHODCALLTYPE d3d12_device_EnumerateMetaCommandParameters(d3d12_device_iface *iface,
REFGUID command_id, D3D12_META_COMMAND_PARAMETER_STAGE stage, UINT *total_size,
UINT *param_count, D3D12_META_COMMAND_PARAMETER_DESC *param_descs)
{
FIXME("iface %p, command_id %s, stage %u, total_size %p, param_count %p, param_descs %p stub!\n",
iface, debugstr_guid(command_id), stage, total_size, param_count, param_descs);
return E_NOTIMPL;
}
static HRESULT STDMETHODCALLTYPE d3d12_device_CreateMetaCommand(d3d12_device_iface *iface,
REFGUID command_id, UINT node_mask, const void *param_data, SIZE_T param_size,
REFIID iid, void **meta_command)
{
FIXME("iface %p, command_id %s, node_mask %#x, param_data %p, param_size %lu, iid %s, meta_command %p stub!\n",
iface, debugstr_guid(command_id), node_mask, param_data, param_size, debugstr_guid(iid), meta_command);
return E_NOTIMPL;
}
static HRESULT STDMETHODCALLTYPE d3d12_device_CreateStateObject(d3d12_device_iface *iface,
const D3D12_STATE_OBJECT_DESC *desc, REFIID iid, void **state_object)
{
struct d3d12_device *device = impl_from_ID3D12Device(iface);
struct d3d12_state_object *state;
HRESULT hr;
TRACE("iface %p, desc %p, iid %s, state_object %p!\n",
iface, desc, debugstr_guid(iid), state_object);
if (FAILED(hr = d3d12_state_object_create(device, desc, NULL, &state)))
return hr;
return return_interface(&state->ID3D12StateObject_iface, &IID_ID3D12StateObject, iid, state_object);
}
static void STDMETHODCALLTYPE d3d12_device_GetRaytracingAccelerationStructurePrebuildInfo(d3d12_device_iface *iface,
const D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_INPUTS *desc,
D3D12_RAYTRACING_ACCELERATION_STRUCTURE_PREBUILD_INFO *info)
{
struct d3d12_device *device = impl_from_ID3D12Device(iface);
const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
struct vkd3d_acceleration_structure_build_info build_info;
VkAccelerationStructureBuildSizesInfoKHR size_info;
TRACE("iface %p, desc %p, info %p!\n", iface, desc, info);
if (!d3d12_device_supports_ray_tracing_tier_1_0(device))
{
ERR("Acceleration structure is not supported. Calling this is invalid.\n");
memset(info, 0, sizeof(*info));
return;
}
if (!vkd3d_acceleration_structure_convert_inputs(device, &build_info, desc))
{
ERR("Failed to convert inputs.\n");
memset(info, 0, sizeof(*info));
return;
}
memset(&size_info, 0, sizeof(size_info));
size_info.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_SIZES_INFO_KHR;
VK_CALL(vkGetAccelerationStructureBuildSizesKHR(device->vk_device,
VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR, &build_info.build_info,
build_info.primitive_counts, &size_info));
vkd3d_acceleration_structure_build_info_cleanup(&build_info);
info->ResultDataMaxSizeInBytes = size_info.accelerationStructureSize;
info->ScratchDataSizeInBytes = size_info.buildScratchSize;
info->UpdateScratchDataSizeInBytes = size_info.updateScratchSize;
TRACE("ResultDataMaxSizeInBytes: %"PRIu64".\n", info->ResultDataMaxSizeInBytes);
TRACE("ScratchDatSizeInBytes: %"PRIu64".\n", info->ScratchDataSizeInBytes);
TRACE("UpdateScratchDataSizeInBytes: %"PRIu64".\n", info->UpdateScratchDataSizeInBytes);
}
static D3D12_DRIVER_MATCHING_IDENTIFIER_STATUS STDMETHODCALLTYPE d3d12_device_CheckDriverMatchingIdentifier(d3d12_device_iface *iface,
D3D12_SERIALIZED_DATA_TYPE serialized_data_type, const D3D12_SERIALIZED_DATA_DRIVER_MATCHING_IDENTIFIER *identifier)
{
FIXME("iface %p, serialized_data_type %u, identifier %p stub!\n",
iface, serialized_data_type, identifier);
if (serialized_data_type != D3D12_SERIALIZED_DATA_RAYTRACING_ACCELERATION_STRUCTURE)
return D3D12_DRIVER_MATCHING_IDENTIFIER_UNSUPPORTED_TYPE;
return D3D12_DRIVER_MATCHING_IDENTIFIER_UNRECOGNIZED;
}
static HRESULT STDMETHODCALLTYPE d3d12_device_SetBackgroundProcessingMode(d3d12_device_iface *iface,
D3D12_BACKGROUND_PROCESSING_MODE mode, D3D12_MEASUREMENTS_ACTION action, HANDLE event,
BOOL further_measurements)
{
FIXME("iface %p, mode %u, action %u, event %p, further_measurements %#x stub!\n",
iface, mode, action, event, further_measurements);
return E_NOTIMPL;
}
static HRESULT STDMETHODCALLTYPE d3d12_device_AddToStateObject(d3d12_device_iface *iface,
const D3D12_STATE_OBJECT_DESC *addition,
ID3D12StateObject *parent_state, REFIID riid, void **new_state_object)
{
struct d3d12_device *device = impl_from_ID3D12Device(iface);
struct d3d12_state_object *parent;
struct d3d12_state_object *state;
HRESULT hr;
TRACE("iface %p, addition %p, state_object %p, riid %s, new_state_object %p stub!\n",
iface, addition, parent_state, debugstr_guid(riid), new_state_object);
parent = impl_from_ID3D12StateObject(parent_state);
if (FAILED(hr = d3d12_state_object_add(device, addition, parent, &state)))
return hr;
return return_interface(&state->ID3D12StateObject_iface, &IID_ID3D12StateObject, riid, new_state_object);
}
static HRESULT STDMETHODCALLTYPE d3d12_device_CreateProtectedResourceSession1(d3d12_device_iface *iface,
const D3D12_PROTECTED_RESOURCE_SESSION_DESC1 *desc, REFIID riid, void **session)
{
FIXME("iface %p, desc %p, riid %s, session %p stub!\n",
iface, desc, debugstr_guid(riid), session);
return E_NOTIMPL;
}
static D3D12_RESOURCE_ALLOCATION_INFO* STDMETHODCALLTYPE d3d12_device_GetResourceAllocationInfo2(d3d12_device_iface *iface,
D3D12_RESOURCE_ALLOCATION_INFO *info, UINT visible_mask, UINT count, const D3D12_RESOURCE_DESC1 *resource_descs,
D3D12_RESOURCE_ALLOCATION_INFO1 *resource_infos)
{
struct d3d12_device *device = impl_from_ID3D12Device(iface);
uint64_t requested_alignment, resource_offset;
D3D12_RESOURCE_ALLOCATION_INFO resource_info;
bool hasMsaaResource = false;
unsigned int i;
TRACE("iface %p, info %p, visible_mask 0x%08x, count %u, resource_descs %p.\n",
iface, info, visible_mask, count, resource_descs);
debug_ignored_node_mask(visible_mask);
info->SizeInBytes = 0;
info->Alignment = 0;
for (i = 0; i < count; i++)
{
const D3D12_RESOURCE_DESC1 *desc = &resource_descs[i];
hasMsaaResource |= desc->SampleDesc.Count > 1;
if (FAILED(d3d12_resource_validate_desc(desc, device)))
{
WARN("Invalid resource desc.\n");
goto invalid;
}
if (desc->Dimension == D3D12_RESOURCE_DIMENSION_BUFFER)
{
resource_info.SizeInBytes = desc->Width;
resource_info.Alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;
}
else
{
if (FAILED(vkd3d_get_image_allocation_info(device, desc, &resource_info)))
{
WARN("Failed to get allocation info for texture.\n");
goto invalid;
}
requested_alignment = desc->Alignment
? desc->Alignment : D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;
resource_info.Alignment = max(resource_info.Alignment, requested_alignment);
}
resource_info.SizeInBytes = align(resource_info.SizeInBytes, resource_info.Alignment);
resource_offset = align(info->SizeInBytes, resource_info.Alignment);
if (resource_infos)
{
resource_infos[i].Offset = resource_offset;
resource_infos[i].SizeInBytes = resource_info.SizeInBytes;
resource_infos[i].Alignment = resource_info.Alignment;
}
info->SizeInBytes = resource_offset + resource_info.SizeInBytes;
info->Alignment = max(info->Alignment, resource_info.Alignment);
}
return info;
invalid:
info->SizeInBytes = ~(uint64_t)0;
/* FIXME: Should we support D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT for small MSSA resources? */
if (hasMsaaResource)
info->Alignment = D3D12_DEFAULT_MSAA_RESOURCE_PLACEMENT_ALIGNMENT;
else
info->Alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;
return info;
}
static HRESULT STDMETHODCALLTYPE d3d12_device_CreateCommittedResource2(d3d12_device_iface *iface,
const D3D12_HEAP_PROPERTIES *heap_properties, D3D12_HEAP_FLAGS heap_flags, const D3D12_RESOURCE_DESC1 *desc,
D3D12_RESOURCE_STATES initial_state, const D3D12_CLEAR_VALUE *optimized_clear_value,
ID3D12ProtectedResourceSession *protected_session, REFIID iid, void **resource)
{
struct d3d12_device *device = impl_from_ID3D12Device(iface);
struct d3d12_resource *object;
HRESULT hr;
TRACE("iface %p, heap_properties %p, heap_flags %#x, desc %p, initial_state %#x, "
"optimized_clear_value %p, protected_session %p, iid %s, resource %p.\n",
iface, heap_properties, heap_flags, desc, initial_state,
optimized_clear_value, protected_session, debugstr_guid(iid), resource);
if (protected_session)
FIXME("Ignoring protected session %p.\n", protected_session);
if (FAILED(hr = d3d12_resource_create_committed(device, desc, heap_properties,
heap_flags, initial_state, optimized_clear_value, NULL, &object)))
{
*resource = NULL;
return hr;
}
return return_interface(&object->ID3D12Resource_iface, &IID_ID3D12Resource, iid, resource);
}
static HRESULT STDMETHODCALLTYPE d3d12_device_CreatePlacedResource1(d3d12_device_iface *iface,
ID3D12Heap *heap, UINT64 heap_offset, const D3D12_RESOURCE_DESC1 *resource_desc,
D3D12_RESOURCE_STATES initial_state, const D3D12_CLEAR_VALUE *optimized_clear_value,
REFIID iid, void **resource)
{
struct d3d12_heap *heap_object = impl_from_ID3D12Heap(heap);
struct d3d12_device *device = impl_from_ID3D12Device(iface);
struct d3d12_resource *object;
HRESULT hr;
TRACE("iface %p, heap %p, heap_offset %#"PRIx64", desc %p, initial_state %#x, "
"optimized_clear_value %p, iid %s, resource %p.\n",
iface, heap, heap_offset, resource_desc, initial_state,
optimized_clear_value, debugstr_guid(iid), resource);
if (FAILED(hr = d3d12_resource_create_placed(device, resource_desc, heap_object,
heap_offset, initial_state, optimized_clear_value, &object)))
return hr;
return return_interface(&object->ID3D12Resource_iface, &IID_ID3D12Resource, iid, resource);
}
static void STDMETHODCALLTYPE d3d12_device_CreateSamplerFeedbackUnorderedAccessView(d3d12_device_iface *iface,
ID3D12Resource *target_resource, ID3D12Resource *feedback_resource, D3D12_CPU_DESCRIPTOR_HANDLE descriptor)
{
FIXME("iface %p, target_resource %p, feedback_resource %p, descriptor %#lx stub!\n",
iface, target_resource, feedback_resource, descriptor);
}
static void STDMETHODCALLTYPE d3d12_device_GetCopyableFootprints1(d3d12_device_iface *iface,
const D3D12_RESOURCE_DESC1 *desc, UINT first_sub_resource, UINT sub_resource_count,
UINT64 base_offset, D3D12_PLACED_SUBRESOURCE_FOOTPRINT *layouts, UINT *row_counts,
UINT64 *row_sizes, UINT64 *total_bytes)
{
struct d3d12_device *device = impl_from_ID3D12Device(iface);
static const struct vkd3d_format vkd3d_format_unknown
= {DXGI_FORMAT_UNKNOWN, VK_FORMAT_UNDEFINED, 1, 1, 1, 1, 0, 1};
unsigned int i, sub_resource_idx, miplevel_idx, row_count, row_size, row_pitch;
unsigned int width, height, depth, num_planes, num_subresources;
unsigned int num_subresources_per_plane, plane_idx;
struct vkd3d_format_footprint plane_footprint;
const struct vkd3d_format *format;
uint64_t offset, size, total;
TRACE("iface %p, desc %p, first_sub_resource %u, sub_resource_count %u, base_offset %#"PRIx64", "
"layouts %p, row_counts %p, row_sizes %p, total_bytes %p.\n",
iface, desc, first_sub_resource, sub_resource_count, base_offset,
layouts, row_counts, row_sizes, total_bytes);
if (layouts)
memset(layouts, 0xff, sizeof(*layouts) * sub_resource_count);
if (row_counts)
memset(row_counts, 0xff, sizeof(*row_counts) * sub_resource_count);
if (row_sizes)
memset(row_sizes, 0xff, sizeof(*row_sizes) * sub_resource_count);
total = ~(uint64_t)0;
if (desc->Dimension == D3D12_RESOURCE_DIMENSION_BUFFER)
{
format = &vkd3d_format_unknown;
}
else if (!(format = vkd3d_format_from_d3d12_resource_desc(device, desc, 0)))
{
WARN("Invalid format %#x.\n", desc->Format);
goto end;
}
if (FAILED(d3d12_resource_validate_desc(desc, device)))
{
WARN("Invalid resource desc.\n");
goto end;
}
num_planes = format->plane_count;
num_subresources_per_plane = d3d12_resource_desc_get_sub_resource_count_per_plane(desc);
num_subresources = d3d12_resource_desc_get_sub_resource_count(device, desc);
if (first_sub_resource >= num_subresources
|| sub_resource_count > num_subresources - first_sub_resource)
{
WARN("Invalid sub-resource range %u-%u for resource.\n", first_sub_resource, sub_resource_count);
goto end;
}
offset = 0;
total = 0;
for (i = 0; i < sub_resource_count; ++i)
{
sub_resource_idx = first_sub_resource + i;
plane_idx = sub_resource_idx / num_subresources_per_plane;
plane_footprint = vkd3d_format_footprint_for_plane(format, plane_idx);
miplevel_idx = sub_resource_idx % desc->MipLevels;
width = align(d3d12_resource_desc_get_width(desc, miplevel_idx), plane_footprint.block_width);
height = align(d3d12_resource_desc_get_height(desc, miplevel_idx), plane_footprint.block_height);
depth = d3d12_resource_desc_get_depth(desc, miplevel_idx);
row_count = height / plane_footprint.block_height;
row_size = (width / plane_footprint.block_width) * plane_footprint.block_byte_count;
/* For whatever reason, we need to use 512 bytes of alignment for depth-stencil formats.
* This is not documented, but it is observed behavior on both NV and WARP drivers.
* See test_get_copyable_footprints_planar(). */
row_pitch = align(row_size, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT * num_planes);
if (layouts)
{
layouts[i].Offset = base_offset + offset;
layouts[i].Footprint.Format = plane_footprint.dxgi_format;
layouts[i].Footprint.Width = width;
layouts[i].Footprint.Height = height;
layouts[i].Footprint.Depth = depth;
layouts[i].Footprint.RowPitch = row_pitch;
}
if (row_counts)
row_counts[i] = row_count;
if (row_sizes)
row_sizes[i] = row_size;
size = max(0, row_count - 1) * row_pitch + row_size;
size = max(0, depth - 1) * align(size, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT * num_planes) + size;
total = offset + size;
offset = align(total, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT);
}
end:
if (total_bytes)
*total_bytes = total;
}
static HRESULT STDMETHODCALLTYPE d3d12_device_CreateShaderCacheSession(d3d12_device_iface *iface,
const D3D12_SHADER_CACHE_SESSION_DESC *desc, REFIID iid, void **session)
{
FIXME("iface %p, desc %p, iid %s, session %p stub!\n",
iface, desc, debugstr_guid(iid), session);
return E_NOTIMPL;
}
static HRESULT STDMETHODCALLTYPE d3d12_device_ShaderCacheControl(d3d12_device_iface *iface,
D3D12_SHADER_CACHE_KIND_FLAGS kinds, D3D12_SHADER_CACHE_CONTROL_FLAGS control)
{
FIXME("iface %p, kinds %#x, control %#x stub!\n", iface, kinds, control);
return E_NOTIMPL;
}
static HRESULT STDMETHODCALLTYPE d3d12_device_CreateCommandQueue1(d3d12_device_iface *iface,
const D3D12_COMMAND_QUEUE_DESC *desc, REFIID creator_id, REFIID iid, void **command_queue)
{
TRACE("iface %p, desc %p, creator_id %s, iid %s, command_queue %p.\n",
iface, desc, debugstr_guid(creator_id), debugstr_guid(iid), command_queue);
WARN("Ignoring creator id %s.\n", debugstr_guid(creator_id));
return d3d12_device_CreateCommandQueue(iface, desc, iid, command_queue);
}
CONST_VTBL struct ID3D12Device9Vtbl d3d12_device_vtbl =
{
/* IUnknown methods */
d3d12_device_QueryInterface,
d3d12_device_AddRef,
d3d12_device_Release,
/* ID3D12Object methods */
d3d12_device_GetPrivateData,
d3d12_device_SetPrivateData,
d3d12_device_SetPrivateDataInterface,
(void *)d3d12_object_SetName,
/* ID3D12Device methods */
d3d12_device_GetNodeCount,
d3d12_device_CreateCommandQueue,
d3d12_device_CreateCommandAllocator,
d3d12_device_CreateGraphicsPipelineState,
d3d12_device_CreateComputePipelineState,
d3d12_device_CreateCommandList,
d3d12_device_CheckFeatureSupport,
d3d12_device_CreateDescriptorHeap,
d3d12_device_GetDescriptorHandleIncrementSize,
d3d12_device_CreateRootSignature,
d3d12_device_CreateConstantBufferView,
d3d12_device_CreateShaderResourceView,
d3d12_device_CreateUnorderedAccessView,
d3d12_device_CreateRenderTargetView,
d3d12_device_CreateDepthStencilView,
d3d12_device_CreateSampler,
d3d12_device_CopyDescriptors,
d3d12_device_CopyDescriptorsSimple,
d3d12_device_GetResourceAllocationInfo,
d3d12_device_GetCustomHeapProperties,
d3d12_device_CreateCommittedResource,
d3d12_device_CreateHeap,
d3d12_device_CreatePlacedResource,
d3d12_device_CreateReservedResource,
d3d12_device_CreateSharedHandle,
d3d12_device_OpenSharedHandle,
d3d12_device_OpenSharedHandleByName,
d3d12_device_MakeResident,
d3d12_device_Evict,
d3d12_device_CreateFence,
d3d12_device_GetDeviceRemovedReason,
d3d12_device_GetCopyableFootprints,
d3d12_device_CreateQueryHeap,
d3d12_device_SetStablePowerState,
d3d12_device_CreateCommandSignature,
d3d12_device_GetResourceTiling,
d3d12_device_GetAdapterLuid,
/* ID3D12Device1 methods */
d3d12_device_CreatePipelineLibrary,
d3d12_device_SetEventOnMultipleFenceCompletion,
d3d12_device_SetResidencyPriority,
/* ID3D12Device2 methods */
d3d12_device_CreatePipelineState,
/* ID3D12Device3 methods */
d3d12_device_OpenExistingHeapFromAddress,
d3d12_device_OpenExistingHeapFromFileMapping,
d3d12_device_EnqueueMakeResident,
/* ID3D12Device4 methods */
d3d12_device_CreateCommandList1,
d3d12_device_CreateProtectedResourceSession,
d3d12_device_CreateCommittedResource1,
d3d12_device_CreateHeap1,
d3d12_device_CreateReservedResource1,
d3d12_device_GetResourceAllocationInfo1,
/* ID3D12Device5 methods */
d3d12_device_CreateLifetimeTracker,
d3d12_device_RemoveDevice,
d3d12_device_EnumerateMetaCommands,
d3d12_device_EnumerateMetaCommandParameters,
d3d12_device_CreateMetaCommand,
d3d12_device_CreateStateObject,
d3d12_device_GetRaytracingAccelerationStructurePrebuildInfo,
d3d12_device_CheckDriverMatchingIdentifier,
/* ID3D12Device6 methods */
d3d12_device_SetBackgroundProcessingMode,
/* ID3D12Device7 methods */
d3d12_device_AddToStateObject,
d3d12_device_CreateProtectedResourceSession1,
/* ID3D12Device8 methods */
d3d12_device_GetResourceAllocationInfo2,
d3d12_device_CreateCommittedResource2,
d3d12_device_CreatePlacedResource1,
d3d12_device_CreateSamplerFeedbackUnorderedAccessView,
d3d12_device_GetCopyableFootprints1,
/* ID3D12Device9 methods */
d3d12_device_CreateShaderCacheSession,
d3d12_device_ShaderCacheControl,
d3d12_device_CreateCommandQueue1,
};
#ifdef VKD3D_ENABLE_PROFILING
#include "device_profiled.h"
#endif
static D3D12_RESOURCE_BINDING_TIER d3d12_device_determine_resource_binding_tier(struct d3d12_device *device)
{
const uint32_t tier_2_required_flags = VKD3D_BINDLESS_SRV | VKD3D_BINDLESS_SAMPLER;
const uint32_t tier_3_required_flags = VKD3D_BINDLESS_CBV | VKD3D_BINDLESS_UAV;
uint32_t bindless_flags = device->bindless_state.flags;
if ((bindless_flags & tier_2_required_flags) != tier_2_required_flags)
return D3D12_RESOURCE_BINDING_TIER_1;
if ((bindless_flags & tier_3_required_flags) != tier_3_required_flags)
return D3D12_RESOURCE_BINDING_TIER_2;
return D3D12_RESOURCE_BINDING_TIER_3;
}
static D3D12_TILED_RESOURCES_TIER d3d12_device_determine_tiled_resources_tier(struct d3d12_device *device)
{
const VkPhysicalDeviceSamplerFilterMinmaxProperties *minmax_properties = &device->device_info.sampler_filter_minmax_properties;
const VkPhysicalDeviceSparseProperties *sparse_properties = &device->device_info.properties2.properties.sparseProperties;
const VkPhysicalDeviceFeatures *features = &device->device_info.features2.features;
if (!features->sparseBinding || !features->sparseResidencyAliased ||
!features->sparseResidencyBuffer || !features->sparseResidencyImage2D ||
!sparse_properties->residencyStandard2DBlockShape ||
!device->queue_families[VKD3D_QUEUE_FAMILY_SPARSE_BINDING] ||
!device->queue_families[VKD3D_QUEUE_FAMILY_SPARSE_BINDING]->queue_count)
return D3D12_TILED_RESOURCES_TIER_NOT_SUPPORTED;
if (!features->shaderResourceResidency || !features->shaderResourceMinLod ||
sparse_properties->residencyAlignedMipSize ||
!sparse_properties->residencyNonResidentStrict ||
!minmax_properties->filterMinmaxSingleComponentFormats)
return D3D12_TILED_RESOURCES_TIER_1;
if (!features->sparseResidencyImage3D ||
!sparse_properties->residencyStandard3DBlockShape)
return D3D12_TILED_RESOURCES_TIER_2;
return D3D12_TILED_RESOURCES_TIER_3;
}
static D3D12_CONSERVATIVE_RASTERIZATION_TIER d3d12_device_determine_conservative_rasterization_tier(struct d3d12_device *device)
{
const VkPhysicalDeviceConservativeRasterizationPropertiesEXT *conservative_properties = &device->device_info.conservative_rasterization_properties;
if (!device->vk_info.EXT_conservative_rasterization)
return D3D12_CONSERVATIVE_RASTERIZATION_TIER_NOT_SUPPORTED;
if (!conservative_properties->degenerateTrianglesRasterized)
return D3D12_CONSERVATIVE_RASTERIZATION_TIER_1;
if (!conservative_properties->fullyCoveredFragmentShaderInputVariable)
return D3D12_CONSERVATIVE_RASTERIZATION_TIER_2;
return D3D12_CONSERVATIVE_RASTERIZATION_TIER_3;
}
static bool d3d12_device_supports_rtas_formats(struct d3d12_device *device, const VkFormat *format, size_t count)
{
const struct vkd3d_vk_instance_procs *vk_procs = &device->vkd3d_instance->vk_procs;
VkFormatProperties properties;
size_t i;
for (i = 0; i < count; i++)
{
VK_CALL(vkGetPhysicalDeviceFormatProperties(device->vk_physical_device, format[i], &properties));
if (!(properties.bufferFeatures & VK_FORMAT_FEATURE_ACCELERATION_STRUCTURE_VERTEX_BUFFER_BIT_KHR))
{
INFO("Vulkan format %u is not supported for RTAS VBO.\n", format[i]);
return false;
}
}
return true;
}
static D3D12_RAYTRACING_TIER d3d12_device_determine_ray_tracing_tier(struct d3d12_device *device)
{
const struct vkd3d_physical_device_info *info = &device->device_info;
D3D12_RAYTRACING_TIER tier = D3D12_RAYTRACING_TIER_NOT_SUPPORTED;
bool supports_vbo_formats;
/* Tier 1.0 formats. 1.1 adds:
* - RGBA8_{U,S}NORM
* - RG16_UNORM
* - RGBA16_UNORM
*/
static const VkFormat required_vbo_formats[] = {
VK_FORMAT_R32G32_SFLOAT,
VK_FORMAT_R32G32B32_SFLOAT,
VK_FORMAT_R16G16_SFLOAT,
VK_FORMAT_R16G16_SNORM,
VK_FORMAT_R16G16B16A16_SFLOAT,
VK_FORMAT_R16G16B16A16_SNORM,
};
static const VkFormat required_vbo_formats_tier_11[] = {
VK_FORMAT_R16G16B16A16_UNORM,
VK_FORMAT_R16G16_UNORM,
VK_FORMAT_A2B10G10R10_UNORM_PACK32,
VK_FORMAT_R8G8B8A8_UNORM,
VK_FORMAT_R8G8_UNORM,
VK_FORMAT_R8G8B8A8_SNORM,
VK_FORMAT_R8G8_SNORM,
};
if (info->ray_tracing_pipeline_features.rayTracingPipeline &&
info->acceleration_structure_features.accelerationStructure &&
info->ray_tracing_pipeline_properties.maxRayHitAttributeSize >= D3D12_RAYTRACING_MAX_ATTRIBUTE_SIZE_IN_BYTES &&
/* Group handle size must match exactly or ShaderRecord layout will not match.
* Can potentially fixup local root signature if HandleSize < 32,
* but Vulkan is essentially specced to match DXR directly. */
info->ray_tracing_pipeline_properties.shaderGroupHandleSize == D3D12_SHADER_IDENTIFIER_SIZE_IN_BYTES &&
info->ray_tracing_pipeline_properties.shaderGroupBaseAlignment <= D3D12_RAYTRACING_SHADER_TABLE_BYTE_ALIGNMENT &&
info->ray_tracing_pipeline_properties.shaderGroupHandleAlignment <= D3D12_RAYTRACING_SHADER_RECORD_BYTE_ALIGNMENT)
{
/* DXR has 31 ray depth min-spec, but no content uses that. We will instead pass through implementations
* which only expose 1 level of recursion and fail PSO compiles if they actually exceed device limits. */
supports_vbo_formats = d3d12_device_supports_rtas_formats(device,
required_vbo_formats, ARRAY_SIZE(required_vbo_formats));
if (supports_vbo_formats)
{
if (vkd3d_config_flags & VKD3D_CONFIG_FLAG_DXR)
{
INFO("DXR support enabled.\n");
tier = D3D12_RAYTRACING_TIER_1_0;
}
else
INFO("Could enable DXR, but VKD3D_CONFIG=dxr is not used.\n");
}
}
if (tier == D3D12_RAYTRACING_TIER_1_0 && info->ray_query_features.rayQuery &&
info->ray_tracing_pipeline_features.rayTraversalPrimitiveCulling)
{
/* Try to enable DXR 1.1.
* Hide this support behind a CONFIG flag for time being.
* TODO: require VK_KHR_ray_tracing_maintenance1. */
supports_vbo_formats = d3d12_device_supports_rtas_formats(device,
required_vbo_formats_tier_11, ARRAY_SIZE(required_vbo_formats_tier_11));
if (supports_vbo_formats)
{
INFO("DXR 1.1 support enabled.\n");
tier = D3D12_RAYTRACING_TIER_1_1;
}
}
return tier;
}
static D3D12_RESOURCE_HEAP_TIER d3d12_device_determine_heap_tier(struct d3d12_device *device)
{
const VkPhysicalDeviceLimits *limits = &device->device_info.properties2.properties.limits;
const struct vkd3d_memory_info *mem_info = &device->memory_info;
const struct vkd3d_memory_info_domain *non_cpu_domain;
non_cpu_domain = &mem_info->non_cpu_accessible_domain;
/* Heap Tier 2 requires us to be able to create a heap that supports all resource
* categories at the same time, except RT/DS textures on UPLOAD/READBACK heaps.
* Ignore CPU visible heaps since we only place buffers there. Textures are promoted to committed always. */
if (limits->bufferImageGranularity > D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT ||
!(non_cpu_domain->buffer_type_mask & non_cpu_domain->sampled_type_mask & non_cpu_domain->rt_ds_type_mask))
return D3D12_RESOURCE_HEAP_TIER_1;
return D3D12_RESOURCE_HEAP_TIER_2;
}
static bool d3d12_device_determine_additional_typed_uav_support(struct d3d12_device *device)
{
D3D12_FEATURE_DATA_FORMAT_SUPPORT format_support;
size_t i;
/* from https://docs.microsoft.com/en-us/windows/win32/direct3d12/typed-unordered-access-view-loads#supported-formats-and-api-calls */
static const DXGI_FORMAT required_formats[] =
{
/* required */
DXGI_FORMAT_R32_FLOAT,
DXGI_FORMAT_R32_UINT,
DXGI_FORMAT_R32_SINT,
/* supported as a set */
DXGI_FORMAT_R32G32B32A32_FLOAT,
DXGI_FORMAT_R32G32B32A32_UINT,
DXGI_FORMAT_R32G32B32A32_SINT,
DXGI_FORMAT_R16G16B16A16_FLOAT,
DXGI_FORMAT_R16G16B16A16_UINT,
DXGI_FORMAT_R16G16B16A16_SINT,
DXGI_FORMAT_R8G8B8A8_UNORM,
DXGI_FORMAT_R8G8B8A8_UINT,
DXGI_FORMAT_R8G8B8A8_SINT,
DXGI_FORMAT_R16_FLOAT,
DXGI_FORMAT_R16_UINT,
DXGI_FORMAT_R16_SINT,
DXGI_FORMAT_R8_UNORM,
DXGI_FORMAT_R8_UINT,
DXGI_FORMAT_R8_SINT,
};
for (i = 0; i < ARRAY_SIZE(required_formats); i++)
{
format_support.Format = required_formats[i];
d3d12_device_get_format_support(device, &format_support);
if (!(format_support.Support2 & D3D12_FORMAT_SUPPORT2_UAV_TYPED_LOAD))
return false;
}
return true;
}
static void d3d12_device_caps_init_feature_options(struct d3d12_device *device)
{
const VkPhysicalDeviceFeatures *features = &device->device_info.features2.features;
D3D12_FEATURE_DATA_D3D12_OPTIONS *options = &device->d3d12_caps.options;
const struct vkd3d_vulkan_info *vk_info = &device->vk_info;
options->DoublePrecisionFloatShaderOps = features->shaderFloat64;
options->OutputMergerLogicOp = features->logicOp;
/* Currently not supported */
options->MinPrecisionSupport = D3D12_SHADER_MIN_PRECISION_SUPPORT_NONE;
options->TiledResourcesTier = d3d12_device_determine_tiled_resources_tier(device);
options->ResourceBindingTier = d3d12_device_determine_resource_binding_tier(device);
options->PSSpecifiedStencilRefSupported = vk_info->EXT_shader_stencil_export;
options->TypedUAVLoadAdditionalFormats = d3d12_device_determine_additional_typed_uav_support(device);
/* Requires VK_EXT_fragment_shader_interlock */
options->ROVsSupported = FALSE;
options->ConservativeRasterizationTier = d3d12_device_determine_conservative_rasterization_tier(device);
options->MaxGPUVirtualAddressBitsPerResource = 40; /* XXX */
options->StandardSwizzle64KBSupported = FALSE;
options->CrossNodeSharingTier = D3D12_CROSS_NODE_SHARING_TIER_NOT_SUPPORTED;
options->CrossAdapterRowMajorTextureSupported = FALSE;
options->VPAndRTArrayIndexFromAnyShaderFeedingRasterizerSupportedWithoutGSEmulation = vk_info->EXT_shader_viewport_index_layer;
options->ResourceHeapTier = d3d12_device_determine_heap_tier(device);
}
static void d3d12_device_caps_init_feature_options1(struct d3d12_device *device)
{
D3D12_FEATURE_DATA_D3D12_OPTIONS1 *options1 = &device->d3d12_caps.options1;
options1->WaveOps = device->d3d12_caps.max_shader_model >= D3D_SHADER_MODEL_6_0;
if (device->vk_info.EXT_subgroup_size_control)
{
options1->WaveLaneCountMin = device->device_info.subgroup_size_control_properties.minSubgroupSize;
options1->WaveLaneCountMax = device->device_info.subgroup_size_control_properties.maxSubgroupSize;
}
else
{
WARN("Device info for WaveLaneCountMin and WaveLaneCountMax may be inaccurate.\n");
options1->WaveLaneCountMin = device->device_info.subgroup_properties.subgroupSize;
options1->WaveLaneCountMax = device->device_info.subgroup_properties.subgroupSize;
}
if (device->vk_info.AMD_shader_core_properties)
{
const VkPhysicalDeviceShaderCorePropertiesAMD *amd = &device->device_info.shader_core_properties;
const VkPhysicalDeviceShaderCoreProperties2AMD *amd2 = &device->device_info.shader_core_properties2;
uint32_t compute_units;
if (device->vk_info.AMD_shader_core_properties2)
compute_units = amd2->activeComputeUnitCount;
else
compute_units = amd->shaderEngineCount *
amd->computeUnitsPerShaderArray;
/* There doesn't seem to be a way to get the SIMD width,
* so just assume 64 lanes per CU. True on GCN and RDNA. */
options1->TotalLaneCount = compute_units * 64;
}
else if (device->vk_info.NV_shader_sm_builtins)
{
/* Approximation, since we cannot query the number of lanes
* per SM. Appears to be correct on Pascal and Turing. */
const VkPhysicalDeviceShaderSMBuiltinsPropertiesNV *nv = &device->device_info.shader_sm_builtins_properties;
options1->TotalLaneCount = nv->shaderSMCount * nv->shaderWarpsPerSM * 2;
}
else
{
options1->TotalLaneCount = 32 * device->device_info.subgroup_properties.subgroupSize;
WARN("No device info available for TotalLaneCount = .\n");
}
options1->ExpandedComputeResourceStates = TRUE;
options1->Int64ShaderOps = device->device_info.features2.features.shaderInt64;
FIXME("TotalLaneCount = %u, may be inaccurate.\n", options1->TotalLaneCount);
}
static void d3d12_device_caps_init_feature_options2(struct d3d12_device *device)
{
const VkPhysicalDeviceFeatures *features = &device->device_info.features2.features;
D3D12_FEATURE_DATA_D3D12_OPTIONS2 *options2 = &device->d3d12_caps.options2;
options2->DepthBoundsTestSupported = features->depthBounds;
/* Requires VK_EXT_sample_locations */
options2->ProgrammableSamplePositionsTier = D3D12_PROGRAMMABLE_SAMPLE_POSITIONS_TIER_NOT_SUPPORTED;
}
static void d3d12_device_caps_init_feature_options3(struct d3d12_device *device)
{
D3D12_FEATURE_DATA_D3D12_OPTIONS3 *options3 = &device->d3d12_caps.options3;
options3->CopyQueueTimestampQueriesSupported = !!device->queue_families[VKD3D_QUEUE_FAMILY_TRANSFER]->timestamp_bits;
options3->CastingFullyTypedFormatSupported = TRUE;
options3->WriteBufferImmediateSupportFlags = D3D12_COMMAND_LIST_SUPPORT_FLAG_DIRECT |
D3D12_COMMAND_LIST_SUPPORT_FLAG_COMPUTE | D3D12_COMMAND_LIST_SUPPORT_FLAG_COPY |
D3D12_COMMAND_LIST_SUPPORT_FLAG_BUNDLE;
/* Currently not supported */
options3->ViewInstancingTier = D3D12_VIEW_INSTANCING_TIER_NOT_SUPPORTED;
options3->BarycentricsSupported =
device->device_info.barycentric_features_nv.fragmentShaderBarycentric ||
device->device_info.barycentric_features_khr.fragmentShaderBarycentric;
}
static void d3d12_device_caps_init_feature_options4(struct d3d12_device *device)
{
D3D12_FEATURE_DATA_D3D12_OPTIONS4 *options4 = &device->d3d12_caps.options4;
/* Requires changes to format compatibility */
options4->MSAA64KBAlignedTextureSupported = FALSE;
/* Shared resources not supported */
options4->SharedResourceCompatibilityTier = D3D12_SHARED_RESOURCE_COMPATIBILITY_TIER_0;
options4->Native16BitShaderOpsSupported = device->device_info.float16_int8_features.shaderFloat16 &&
device->device_info.features2.features.shaderInt16 &&
device->device_info.storage_16bit_features.uniformAndStorageBuffer16BitAccess &&
device->device_info.subgroup_extended_types_features.shaderSubgroupExtendedTypes;
}
static void d3d12_device_caps_init_feature_options5(struct d3d12_device *device)
{
const D3D12_FEATURE_DATA_D3D12_OPTIONS *options = &device->d3d12_caps.options;
D3D12_FEATURE_DATA_D3D12_OPTIONS5 *options5 = &device->d3d12_caps.options5;
options5->SRVOnlyTiledResourceTier3 = options->TiledResourcesTier >= D3D12_TILED_RESOURCES_TIER_3;
/* Currently not supported */
options5->RenderPassesTier = D3D12_RENDER_PASS_TIER_0;
options5->RaytracingTier = d3d12_device_determine_ray_tracing_tier(device);
}
static void d3d12_device_caps_init_feature_options6(struct d3d12_device *device)
{
D3D12_FEATURE_DATA_D3D12_OPTIONS6 *options6 = &device->d3d12_caps.options6;
options6->AdditionalShadingRatesSupported = device->device_info.additional_shading_rates_supported;
options6->VariableShadingRateTier = d3d12_device_determine_variable_shading_rate_tier(device);
if (options6->VariableShadingRateTier >= D3D12_VARIABLE_SHADING_RATE_TIER_2)
{
options6->ShadingRateImageTileSize = d3d12_determine_shading_rate_image_tile_size(device);
options6->PerPrimitiveShadingRateSupportedWithViewportIndexing =
device->device_info.fragment_shading_rate_properties.primitiveFragmentShadingRateWithMultipleViewports;
}
else
{
options6->ShadingRateImageTileSize = 0;
options6->PerPrimitiveShadingRateSupportedWithViewportIndexing = FALSE;
}
/* Not implemented */
options6->BackgroundProcessingSupported = FALSE;
}
static void d3d12_device_caps_init_feature_options7(struct d3d12_device *device)
{
D3D12_FEATURE_DATA_D3D12_OPTIONS7 *options7 = &device->d3d12_caps.options7;
/* Not supported */
options7->MeshShaderTier = D3D12_MESH_SHADER_TIER_NOT_SUPPORTED;
options7->SamplerFeedbackTier = D3D12_SAMPLER_FEEDBACK_TIER_NOT_SUPPORTED;
}
static void d3d12_device_caps_init_feature_options8(struct d3d12_device *device)
{
D3D12_FEATURE_DATA_D3D12_OPTIONS8 *options8 = &device->d3d12_caps.options8;
/* Advertise that it is possible to use a top mip level which is not aligned to 4.
* Weird legacy D3D11 requirement that is not relevant anymore, and does not exist in Vulkan. */
options8->UnalignedBlockTexturesSupported = TRUE;
}
static void d3d12_device_caps_init_feature_options9(struct d3d12_device *device)
{
D3D12_FEATURE_DATA_D3D12_OPTIONS9 *options9 = &device->d3d12_caps.options9;
options9->AtomicInt64OnGroupSharedSupported =
device->device_info.shader_atomic_int64_features.shaderSharedInt64Atomics;
/* Unsure if sparse 64-bit image atomics is also required. */
options9->AtomicInt64OnTypedResourceSupported =
device->device_info.shader_image_atomic_int64_features.shaderImageInt64Atomics;
options9->DerivativesInMeshAndAmplificationShadersSupported = FALSE;
options9->MeshShaderSupportsFullRangeRenderTargetArrayIndex = FALSE;
options9->MeshShaderPipelineStatsSupported = FALSE;
options9->WaveMMATier = D3D12_WAVE_MMA_TIER_NOT_SUPPORTED;
}
static void d3d12_device_caps_init_feature_options10(struct d3d12_device *device)
{
D3D12_FEATURE_DATA_D3D12_OPTIONS10 *options10 = &device->d3d12_caps.options10;
options10->VariableRateShadingSumCombinerSupported =
d3d12_device_determine_variable_shading_rate_tier(device) >= D3D12_VARIABLE_SHADING_RATE_TIER_1;
options10->MeshShaderPerPrimitiveShadingRateSupported = FALSE;
}
static void d3d12_device_caps_init_feature_options11(struct d3d12_device *device)
{
D3D12_FEATURE_DATA_D3D12_OPTIONS11 *options11 = &device->d3d12_caps.options11;
options11->AtomicInt64OnDescriptorHeapResourceSupported =
device->device_info.shader_atomic_int64_features.shaderBufferInt64Atomics;
}
static void d3d12_device_caps_init_feature_level(struct d3d12_device *device)
{
const VkPhysicalDeviceFeatures *features = &device->device_info.features2.features;
const struct vkd3d_vulkan_info *vk_info = &device->vk_info;
struct d3d12_caps *caps = &device->d3d12_caps;
caps->max_feature_level = D3D_FEATURE_LEVEL_11_0;
if (caps->options.OutputMergerLogicOp && features->vertexPipelineStoresAndAtomics &&
vk_info->device_limits.maxPerStageDescriptorStorageBuffers >= D3D12_UAV_SLOT_COUNT &&
vk_info->device_limits.maxPerStageDescriptorStorageImages >= D3D12_UAV_SLOT_COUNT)
caps->max_feature_level = D3D_FEATURE_LEVEL_11_1;
if (caps->max_feature_level >= D3D_FEATURE_LEVEL_11_1 &&
caps->options.TiledResourcesTier >= D3D12_TILED_RESOURCES_TIER_2 &&
caps->options.ResourceBindingTier >= D3D12_RESOURCE_BINDING_TIER_2 &&
caps->options.TypedUAVLoadAdditionalFormats)
caps->max_feature_level = D3D_FEATURE_LEVEL_12_0;
if (caps->max_feature_level >= D3D_FEATURE_LEVEL_12_0 && caps->options.ROVsSupported &&
caps->options.ConservativeRasterizationTier >= D3D12_CONSERVATIVE_RASTERIZATION_TIER_1)
caps->max_feature_level = D3D_FEATURE_LEVEL_12_1;
if (caps->max_feature_level >= D3D_FEATURE_LEVEL_12_1 && caps->max_shader_model >= D3D_SHADER_MODEL_6_5 &&
caps->options.VPAndRTArrayIndexFromAnyShaderFeedingRasterizerSupportedWithoutGSEmulation &&
caps->options1.WaveOps && caps->options1.Int64ShaderOps && caps->options2.DepthBoundsTestSupported &&
caps->options3.CopyQueueTimestampQueriesSupported && caps->options3.CastingFullyTypedFormatSupported &&
caps->options.ResourceBindingTier >= D3D12_RESOURCE_BINDING_TIER_3 &&
caps->options.ConservativeRasterizationTier >= D3D12_CONSERVATIVE_RASTERIZATION_TIER_3 &&
caps->options.TiledResourcesTier >= D3D12_TILED_RESOURCES_TIER_3 &&
caps->options5.RaytracingTier >= D3D12_RAYTRACING_TIER_1_1 &&
caps->options6.VariableShadingRateTier >= D3D12_VARIABLE_SHADING_RATE_TIER_2 &&
caps->options7.MeshShaderTier >= D3D12_MESH_SHADER_TIER_1 &&
caps->options7.SamplerFeedbackTier >= D3D12_SAMPLER_FEEDBACK_TIER_0_9)
caps->max_feature_level = D3D_FEATURE_LEVEL_12_2;
TRACE("Max feature level: %#x.\n", caps->max_feature_level);
}
static void d3d12_device_caps_init_shader_model(struct d3d12_device *device)
{
const struct vkd3d_physical_device_info *physical_device_info = &device->device_info;
bool denorm_behavior;
/* SHUFFLE is required to implement WaveReadLaneAt with dynamically uniform index before SPIR-V 1.5 / Vulkan 1.2. */
static const VkSubgroupFeatureFlags required =
VK_SUBGROUP_FEATURE_ARITHMETIC_BIT |
VK_SUBGROUP_FEATURE_BASIC_BIT |
VK_SUBGROUP_FEATURE_BALLOT_BIT |
VK_SUBGROUP_FEATURE_SHUFFLE_BIT |
VK_SUBGROUP_FEATURE_QUAD_BIT |
VK_SUBGROUP_FEATURE_VOTE_BIT;
static const VkSubgroupFeatureFlags required_stages =
VK_SHADER_STAGE_COMPUTE_BIT |
VK_SHADER_STAGE_FRAGMENT_BIT;
/* We need to support modern cbuffer layout in SM 6.0, which is equivalent to array of scalars with
* tight packing. Either scalar block layout or the more relaxed UBO standard layout feature exposes this. */
if (device->api_version >= VK_API_VERSION_1_1 &&
physical_device_info->subgroup_properties.subgroupSize >= 4 &&
(physical_device_info->uniform_buffer_standard_layout_features.uniformBufferStandardLayout ||
physical_device_info->scalar_block_layout_features.scalarBlockLayout) &&
(physical_device_info->subgroup_properties.supportedOperations & required) == required &&
(physical_device_info->subgroup_properties.supportedStages & required_stages) == required_stages)
{
/* From testing on native Polaris drivers, AMD expose SM 6.5, even if lots of features are not supported.
* This is a good hint that shader model versions are not tied to features which have caps bits.
* Only consider required features here. */
/* SM 6.0 adds:
* https://github.com/microsoft/DirectXShaderCompiler/wiki/Shader-Model-6.0
* WaveOps, int64 (optional)
*/
device->d3d12_caps.max_shader_model = D3D_SHADER_MODEL_6_0;
TRACE("Enabling support for SM 6.0.\n");
/* SM 6.1 adds:
* https://github.com/microsoft/DirectXShaderCompiler/wiki/Shader-Model-6.1
* SV_ViewID (VK_KHR_multiview?), SV_Barycentrics
*/
/* SM 6.2 adds:
* https://github.com/microsoft/DirectXShaderCompiler/wiki/Shader-Model-6.2
* FP16, Denorm modes (float controls extension)
*/
/* DXIL allows control over denorm behavior for FP32 only.
* shaderDenorm handling appears to work just fine on NV, despite the properties struct saying otherwise.
* Assume that this is just a driver oversight, since otherwise we cannot expose SM 6.2 there ... */
denorm_behavior = ((device->device_info.float_control_properties.denormBehaviorIndependence ==
VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_32_BIT_ONLY) ||
(device->device_info.float_control_properties.denormBehaviorIndependence ==
VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL)) &&
(device->device_info.properties2.properties.vendorID == VKD3D_VENDOR_ID_NVIDIA ||
(device->device_info.float_control_properties.shaderDenormFlushToZeroFloat32 &&
device->device_info.float_control_properties.shaderDenormPreserveFloat32));
if (denorm_behavior)
{
device->d3d12_caps.max_shader_model = D3D_SHADER_MODEL_6_2;
TRACE("Enabling support for SM 6.2.\n");
}
/* SM 6.3 adds:
* https://github.com/microsoft/DirectXShaderCompiler/wiki/Shader-Model-6.3
* Ray tracing (lib_6_3 multi entry point targets).
*/
if (device->d3d12_caps.max_shader_model == D3D_SHADER_MODEL_6_2 && device->vk_info.KHR_spirv_1_4)
{
/* SPIR-V 1.4 is required for lib_6_3 since that is required for RT. */
device->d3d12_caps.max_shader_model = D3D_SHADER_MODEL_6_3;
TRACE("Enabling support for SM 6.3.\n");
}
/* SM 6.4 adds:
* https://github.com/microsoft/DirectXShaderCompiler/wiki/Shader-Model-6.4
* Integer dot products (8-bit)
* Mixed FP16 dot products
* Variable rate shading
* Library subobjects
*/
/* SM 6.5 adds:
* https://github.com/microsoft/DirectXShaderCompiler/wiki/Shader-Model-6.5
* DXR 1.1, Sampler feedback, Mesh shaders, Amplification shaders, more wave ops.
*/
if (device->d3d12_caps.max_shader_model == D3D_SHADER_MODEL_6_3)
{
/* Nothing in SM 6.4 requires special support (except for VRS which is optional).
* The rest is just shader arithmetic intrinsics and reflection. */
/* The only required features in SM 6.5 are WaveMulti and WaveMultiPrefix wave ops
* which map directly to normal wave operations. */
device->d3d12_caps.max_shader_model = D3D_SHADER_MODEL_6_5;
TRACE("Enabling support for SM 6.5.\n");
}
/* Required features:
* - ComputeShader derivatives (linear only, dxil-spirv can synthesize Quad).
* - 64-bit atomics. Only buffer atomics are required for SM 6.6.
* - Strict IsHelperInvocation(). The emulated path might have some edge cases here,
* no reason not to require it.
* - 8-bit integers. Widely supported, even on older targets. Can be emulated if need be.
* - WaveSize attribute, requiredSubgroupSizeStages + FullSubgroups feature is required.
* - RayPayload attribute (purely metadata in DXIL land, irrelevant for us).
*/
if (device->d3d12_caps.max_shader_model == D3D_SHADER_MODEL_6_5 &&
device->device_info.compute_shader_derivatives_features_nv.computeDerivativeGroupLinear &&
device->device_info.shader_atomic_int64_features.shaderBufferInt64Atomics &&
device->device_info.demote_features.shaderDemoteToHelperInvocation &&
device->device_info.float16_int8_features.shaderInt8 &&
device->device_info.subgroup_size_control_features.computeFullSubgroups &&
device->device_info.subgroup_size_control_features.subgroupSizeControl &&
(device->device_info.subgroup_size_control_properties.requiredSubgroupSizeStages & VK_SHADER_STAGE_COMPUTE_BIT))
{
INFO("Enabling support for SM 6.6.\n");
device->d3d12_caps.max_shader_model = D3D_SHADER_MODEL_6_6;
}
}
else
{
device->d3d12_caps.max_shader_model = D3D_SHADER_MODEL_5_1;
TRACE("Enabling support for SM 5.1.\n");
}
}
static void d3d12_device_caps_override_application(struct d3d12_device *device)
{
/* Some games rely on certain features to be exposed before they let the primary feature
* be exposed. */
switch (vkd3d_application_feature_override)
{
case VKD3D_APPLICATION_FEATURE_OVERRIDE_PROMOTE_DXR_TO_ULTIMATE:
if (device->d3d12_caps.options5.RaytracingTier >= D3D12_RAYTRACING_TIER_1_0)
{
device->d3d12_caps.options7.MeshShaderTier = D3D12_MESH_SHADER_TIER_1;
device->d3d12_caps.options7.SamplerFeedbackTier = D3D12_SAMPLER_FEEDBACK_TIER_1_0;
INFO("DXR enabled. Application also requires Mesh/Sampler feedback to be exposed (but unused). "
"Enabling these features automatically.\n");
}
break;
default:
break;
}
}
static void d3d12_device_caps_override(struct d3d12_device *device)
{
D3D_FEATURE_LEVEL fl_override = (D3D_FEATURE_LEVEL)0;
struct d3d12_caps *caps = &device->d3d12_caps;
char fl_string[VKD3D_PATH_MAX];
unsigned int i;
static const struct
{
const char* string;
D3D_FEATURE_LEVEL feature_level;
}
feature_levels[] =
{
{ "11_0", D3D_FEATURE_LEVEL_11_0 },
{ "11_1", D3D_FEATURE_LEVEL_11_1 },
{ "12_0", D3D_FEATURE_LEVEL_12_0 },
{ "12_1", D3D_FEATURE_LEVEL_12_1 },
{ "12_2", D3D_FEATURE_LEVEL_12_2 },
};
if (!vkd3d_get_env_var("VKD3D_FEATURE_LEVEL", fl_string, sizeof(fl_string)))
return;
for (i = 0; i < ARRAY_SIZE(feature_levels); i++)
{
if (!strcmp(fl_string, feature_levels[i].string))
{
fl_override = feature_levels[i].feature_level;
break;
}
}
if (!fl_override)
{
WARN("Unrecognized feature level %s.\n", fl_string);
return;
}
if (fl_override >= D3D_FEATURE_LEVEL_11_1)
caps->options.OutputMergerLogicOp = TRUE;
if (fl_override >= D3D_FEATURE_LEVEL_12_0)
{
caps->options.TiledResourcesTier = max(caps->options.TiledResourcesTier, D3D12_TILED_RESOURCES_TIER_2);
caps->options.ResourceBindingTier = max(caps->options.ResourceBindingTier, D3D12_RESOURCE_BINDING_TIER_2);
caps->max_shader_model = max(caps->max_shader_model, D3D_SHADER_MODEL_6_0);
caps->options.TypedUAVLoadAdditionalFormats = TRUE;
}
if (fl_override >= D3D_FEATURE_LEVEL_12_1)
{
caps->options.ROVsSupported = TRUE;
caps->options.ConservativeRasterizationTier = max(caps->options.ConservativeRasterizationTier, D3D12_CONSERVATIVE_RASTERIZATION_TIER_1);
}
if (fl_override >= D3D_FEATURE_LEVEL_12_2)
{
caps->options5.RaytracingTier = max(caps->options5.RaytracingTier, D3D12_RAYTRACING_TIER_1_1);
caps->options6.VariableShadingRateTier = max(caps->options6.VariableShadingRateTier, D3D12_VARIABLE_SHADING_RATE_TIER_1);
caps->options.ResourceBindingTier = max(caps->options.ResourceBindingTier, D3D12_RESOURCE_BINDING_TIER_3);
caps->options.TiledResourcesTier = max(caps->options.TiledResourcesTier, D3D12_TILED_RESOURCES_TIER_3);
caps->options.ConservativeRasterizationTier = max(caps->options.ConservativeRasterizationTier, D3D12_CONSERVATIVE_RASTERIZATION_TIER_3);
caps->max_shader_model = max(caps->max_shader_model, D3D_SHADER_MODEL_6_5);
caps->options7.MeshShaderTier = max(caps->options7.MeshShaderTier, D3D12_MESH_SHADER_TIER_1);
caps->options7.SamplerFeedbackTier = max(caps->options7.SamplerFeedbackTier, D3D12_SAMPLER_FEEDBACK_TIER_1_0);
}
caps->max_feature_level = fl_override;
WARN("Overriding feature level: %#x.\n", fl_override);
}
static void d3d12_device_caps_init(struct d3d12_device *device)
{
d3d12_device_caps_init_shader_model(device);
d3d12_device_caps_init_feature_options(device);
d3d12_device_caps_init_feature_options1(device);
d3d12_device_caps_init_feature_options2(device);
d3d12_device_caps_init_feature_options3(device);
d3d12_device_caps_init_feature_options4(device);
d3d12_device_caps_init_feature_options5(device);
d3d12_device_caps_init_feature_options6(device);
d3d12_device_caps_init_feature_options7(device);
d3d12_device_caps_init_feature_options8(device);
d3d12_device_caps_init_feature_options9(device);
d3d12_device_caps_init_feature_options10(device);
d3d12_device_caps_init_feature_options11(device);
d3d12_device_caps_init_feature_level(device);
d3d12_device_caps_override(device);
d3d12_device_caps_override_application(device);
}
static void vkd3d_init_shader_extensions(struct d3d12_device *device)
{
device->vk_info.shader_extension_count = 0;
if (device->vk_info.EXT_shader_demote_to_helper_invocation)
{
device->vk_info.shader_extensions[device->vk_info.shader_extension_count++] =
VKD3D_SHADER_TARGET_EXTENSION_SPV_EXT_DEMOTE_TO_HELPER_INVOCATION;
}
if (device->device_info.shader_integer_dot_product_features.shaderIntegerDotProduct)
{
device->vk_info.shader_extensions[device->vk_info.shader_extension_count++] =
VKD3D_SHADER_TARGET_EXTENSION_SPV_KHR_INTEGER_DOT_PRODUCT;
}
if (d3d12_device_determine_additional_typed_uav_support(device))
{
device->vk_info.shader_extensions[device->vk_info.shader_extension_count++] =
VKD3D_SHADER_TARGET_EXTENSION_READ_STORAGE_IMAGE_WITHOUT_FORMAT;
}
if (device->device_info.ray_tracing_pipeline_features.rayTraversalPrimitiveCulling)
{
device->vk_info.shader_extensions[device->vk_info.shader_extension_count++] =
VKD3D_SHADER_TARGET_EXTENSION_RAY_TRACING_PRIMITIVE_CULLING;
}
if (device->device_info.scalar_block_layout_features.scalarBlockLayout)
{
device->vk_info.shader_extensions[device->vk_info.shader_extension_count++] =
VKD3D_SHADER_TARGET_EXTENSION_SCALAR_BLOCK_LAYOUT;
if (device->device_info.properties2.properties.vendorID == VKD3D_VENDOR_ID_AMD)
{
/* Raw load-store instructions on AMD are bounds checked correctly per component.
* In other cases, we must be careful when emitting byte address buffers and block
* any attempt to vectorize vec3.
* We can still vectorize vec3 structured buffers however as long as SCALAR_BLOCK_LAYOUT
* is supported. */
device->vk_info.shader_extensions[device->vk_info.shader_extension_count++] =
VKD3D_SHADER_TARGET_EXTENSION_ASSUME_PER_COMPONENT_SSBO_ROBUSTNESS;
}
}
if (device->device_info.barycentric_features_khr.fragmentShaderBarycentric)
{
device->vk_info.shader_extensions[device->vk_info.shader_extension_count++] =
VKD3D_SHADER_TARGET_EXTENSION_BARYCENTRIC_KHR;
}
if (device->d3d12_caps.options4.Native16BitShaderOpsSupported &&
(device->device_info.driver_properties.driverID == VK_DRIVER_ID_MESA_RADV ||
(vkd3d_config_flags & VKD3D_CONFIG_FLAG_FORCE_NATIVE_FP16)))
{
/* Native FP16 is buggy on NV for now. */
device->vk_info.shader_extensions[device->vk_info.shader_extension_count++] =
VKD3D_SHADER_TARGET_EXTENSION_MIN_PRECISION_IS_NATIVE_16BIT;
}
}
static void vkd3d_compute_shader_interface_key(struct d3d12_device *device)
{
/* This key needs to hold all state which could potentially affect shader compilation.
* We may generate different SPIR-V based on the bindless state flags.
* The bindless states are affected by various flags. */
unsigned int i;
uint64_t key;
key = hash_fnv1_init();
/* Technically, any changes in vkd3d-shader will be reflected in the vkd3d-proton Git hash,
* but it is useful to be able to modify the internal revision while developing since
* we have no mechanism for emitting dirty Git revisions. */
key = hash_fnv1_iterate_u64(key, vkd3d_shader_get_revision());
key = hash_fnv1_iterate_u32(key, device->bindless_state.flags);
key = hash_fnv1_iterate_u32(key, device->bindless_state.cbv_srv_uav_count);
key = hash_fnv1_iterate_u32(key, device->bindless_state.set_count);
for (i = 0; i < device->bindless_state.set_count; i++)
{
key = hash_fnv1_iterate_u32(key, device->bindless_state.set_info[i].flags);
key = hash_fnv1_iterate_u32(key, device->bindless_state.set_info[i].binding_index);
key = hash_fnv1_iterate_u32(key, device->bindless_state.set_info[i].set_index);
key = hash_fnv1_iterate_u32(key, device->bindless_state.set_info[i].heap_type);
key = hash_fnv1_iterate_u32(key, device->bindless_state.set_info[i].vk_descriptor_type);
}
key = hash_fnv1_iterate_u32(key, vkd3d_shader_quirk_info.global_quirks);
key = hash_fnv1_iterate_u32(key, vkd3d_shader_quirk_info.default_quirks);
key = hash_fnv1_iterate_u32(key, vkd3d_shader_quirk_info.num_hashes);
/* If apps attempt to use the same shader cache with different executables, we might end up with different
* quirk tables due to app workarounds, so hash that too. */
for (i = 0; i < vkd3d_shader_quirk_info.num_hashes; i++)
{
key = hash_fnv1_iterate_u64(key, vkd3d_shader_quirk_info.hashes[i].shader_hash);
key = hash_fnv1_iterate_u32(key, vkd3d_shader_quirk_info.hashes[i].quirks);
}
for (i = 0; i < device->vk_info.shader_extension_count; i++)
key = hash_fnv1_iterate_u32(key, device->vk_info.shader_extensions[i]);
device->shader_interface_key = key;
}
static bool d3d12_device_supports_feature_level(struct d3d12_device *device, D3D_FEATURE_LEVEL feature_level)
{
return feature_level <= device->d3d12_caps.max_feature_level;
}
extern CONST_VTBL struct ID3D12DeviceExtVtbl d3d12_device_vkd3d_ext_vtbl;
static HRESULT d3d12_device_init(struct d3d12_device *device,
struct vkd3d_instance *instance, const struct vkd3d_device_create_info *create_info)
{
const struct vkd3d_vk_device_procs *vk_procs;
HRESULT hr;
int rc;
#ifdef VKD3D_ENABLE_PROFILING
if (vkd3d_uses_profiling())
device->ID3D12Device_iface.lpVtbl = &d3d12_device_vtbl_profiled;
else
device->ID3D12Device_iface.lpVtbl = &d3d12_device_vtbl;
#else
device->ID3D12Device_iface.lpVtbl = &d3d12_device_vtbl;
#endif
device->refcount = 1;
vkd3d_instance_incref(device->vkd3d_instance = instance);
device->vk_info = instance->vk_info;
device->signal_event = instance->signal_event;
device->adapter_luid = create_info->adapter_luid;
device->removed_reason = S_OK;
device->vk_device = VK_NULL_HANDLE;
if ((rc = pthread_mutex_init(&device->mutex, NULL)))
{
ERR("Failed to initialize mutex, error %d.\n", rc);
hr = hresult_from_errno(rc);
goto out_free_instance;
}
device->ID3D12DeviceExt_iface.lpVtbl = &d3d12_device_vkd3d_ext_vtbl;
if (FAILED(hr = vkd3d_create_vk_device(device, create_info)))
goto out_free_mutex;
if (FAILED(hr = vkd3d_private_store_init(&device->private_store)))
goto out_free_vk_resources;
if (FAILED(hr = vkd3d_memory_allocator_init(&device->memory_allocator, device)))
goto out_free_private_store;
if (FAILED(hr = vkd3d_init_format_info(device)))
goto out_free_memory_allocator;
if (FAILED(hr = vkd3d_memory_info_init(&device->memory_info, device)))
goto out_cleanup_format_info;
if (FAILED(hr = vkd3d_bindless_state_init(&device->bindless_state, device)))
goto out_cleanup_memory_info;
if (FAILED(hr = vkd3d_view_map_init(&device->sampler_map)))
goto out_cleanup_bindless_state;
if (FAILED(hr = vkd3d_sampler_state_init(&device->sampler_state, device)))
goto out_cleanup_view_map;
if (FAILED(hr = vkd3d_meta_ops_init(&device->meta_ops, device)))
goto out_cleanup_sampler_state;
if (FAILED(hr = vkd3d_shader_debug_ring_init(&device->debug_ring, device)))
goto out_cleanup_meta_ops;
#ifdef VKD3D_ENABLE_BREADCRUMBS
if (vkd3d_config_flags & VKD3D_CONFIG_FLAG_BREADCRUMBS)
if (FAILED(hr = vkd3d_breadcrumb_tracer_init(&device->breadcrumb_tracer, device)))
goto out_cleanup_debug_ring;
#endif
if (vkd3d_descriptor_debug_active_qa_checks())
{
if (FAILED(hr = vkd3d_descriptor_debug_alloc_global_info(&device->descriptor_qa_global_info,
VKD3D_DESCRIPTOR_DEBUG_DEFAULT_NUM_COOKIES, device)))
goto out_cleanup_breadcrumb_tracer;
}
if ((device->parent = create_info->parent))
IUnknown_AddRef(device->parent);
d3d12_device_caps_init(device);
vkd3d_init_shader_extensions(device);
vkd3d_compute_shader_interface_key(device);
/* Make sure all extensions and shader interface keys are computed. */
if (FAILED(hr = vkd3d_pipeline_library_init_disk_cache(&device->disk_cache, device)))
goto out_cleanup_descriptor_qa_global_info;
#ifdef VKD3D_ENABLE_RENDERDOC
if (vkd3d_renderdoc_active() && vkd3d_renderdoc_global_capture_enabled())
vkd3d_renderdoc_begin_capture(device->vkd3d_instance->vk_instance);
#endif
return S_OK;
out_cleanup_descriptor_qa_global_info:
vkd3d_descriptor_debug_free_global_info(device->descriptor_qa_global_info, device);
out_cleanup_breadcrumb_tracer:
#ifdef VKD3D_ENABLE_BREADCRUMBS
if (vkd3d_config_flags & VKD3D_CONFIG_FLAG_BREADCRUMBS)
vkd3d_breadcrumb_tracer_cleanup(&device->breadcrumb_tracer, device);
out_cleanup_debug_ring:
#endif
vkd3d_shader_debug_ring_cleanup(&device->debug_ring, device);
out_cleanup_meta_ops:
vkd3d_meta_ops_cleanup(&device->meta_ops, device);
out_cleanup_sampler_state:
vkd3d_sampler_state_cleanup(&device->sampler_state, device);
out_cleanup_view_map:
vkd3d_view_map_destroy(&device->sampler_map, device);
out_cleanup_bindless_state:
vkd3d_bindless_state_cleanup(&device->bindless_state, device);
out_cleanup_memory_info:
vkd3d_memory_info_cleanup(&device->memory_info, device);
out_cleanup_format_info:
vkd3d_cleanup_format_info(device);
out_free_memory_allocator:
vkd3d_memory_allocator_cleanup(&device->memory_allocator, device);
out_free_private_store:
vkd3d_private_store_destroy(&device->private_store);
out_free_vk_resources:
vk_procs = &device->vk_procs;
VK_CALL(vkDestroyDevice(device->vk_device, NULL));
out_free_instance:
vkd3d_instance_decref(device->vkd3d_instance);
out_free_mutex:
pthread_mutex_destroy(&device->mutex);
return hr;
}
bool d3d12_device_validate_shader_meta(struct d3d12_device *device, const struct vkd3d_shader_meta *meta)
{
/* TODO: Add more as required. */
if ((meta->flags & VKD3D_SHADER_META_FLAG_USES_NATIVE_16BIT_OPERATIONS) &&
!device->d3d12_caps.options4.Native16BitShaderOpsSupported)
{
WARN("Attempting to use 16-bit operations in shader %016"PRIx64", but this is not supported.", meta->hash);
return false;
}
if (meta->cs_required_wave_size)
{
const struct vkd3d_physical_device_info *info = &device->device_info;
if (!info->subgroup_size_control_features.subgroupSizeControl ||
!info->subgroup_size_control_features.computeFullSubgroups ||
!(info->subgroup_size_control_properties.requiredSubgroupSizeStages & VK_SHADER_STAGE_COMPUTE_BIT))
{
ERR("Required subgroup size control features are not supported for SM 6.6 WaveSize.\n");
return E_INVALIDARG;
}
if (meta->cs_required_wave_size < info->subgroup_size_control_properties.minSubgroupSize ||
meta->cs_required_wave_size > info->subgroup_size_control_properties.maxSubgroupSize)
{
ERR("Requested WaveSize %u, but supported range is [%u, %u].\n",
meta->cs_required_wave_size,
info->subgroup_size_control_properties.minSubgroupSize,
info->subgroup_size_control_properties.maxSubgroupSize);
return E_INVALIDARG;
}
}
return true;
}
HRESULT d3d12_device_create(struct vkd3d_instance *instance,
const struct vkd3d_device_create_info *create_info, struct d3d12_device **device)
{
struct d3d12_device *object;
HRESULT hr;
pthread_mutex_lock(&d3d12_device_map_mutex);
if ((object = d3d12_find_device_singleton(create_info->adapter_luid)))
{
TRACE("Returned existing singleton device %p.\n", object);
d3d12_device_add_ref(*device = object);
pthread_mutex_unlock(&d3d12_device_map_mutex);
return S_OK;
}
if (!(object = vkd3d_calloc(1, sizeof(*object))))
{
pthread_mutex_unlock(&d3d12_device_map_mutex);
return E_OUTOFMEMORY;
}
if (FAILED(hr = d3d12_device_init(object, instance, create_info)))
{
vkd3d_free(object);
pthread_mutex_unlock(&d3d12_device_map_mutex);
return hr;
}
if (!d3d12_device_supports_feature_level(object, create_info->minimum_feature_level))
{
WARN("Feature level %#x is not supported.\n", create_info->minimum_feature_level);
d3d12_device_destroy(object);
vkd3d_free(object);
pthread_mutex_unlock(&d3d12_device_map_mutex);
return E_INVALIDARG;
}
TRACE("Created device %p.\n", object);
d3d12_add_device_singleton(object, create_info->adapter_luid);
pthread_mutex_unlock(&d3d12_device_map_mutex);
*device = object;
return S_OK;
}
void d3d12_device_mark_as_removed(struct d3d12_device *device, HRESULT reason,
const char *message, ...)
{
va_list args;
va_start(args, message);
WARN("Device %p is lost (reason %#x, \"%s\").\n",
device, reason, vkd3d_dbg_vsprintf(message, args));
va_end(args);
device->removed_reason = reason;
}
HRESULT vkd3d_create_thread(struct vkd3d_instance *instance,
PFN_vkd3d_thread thread_main, void *data, union vkd3d_thread_handle *thread)
{
HRESULT hr = S_OK;
int rc;
if (instance->create_thread)
{
if (!(thread->handle = instance->create_thread(thread_main, data)))
{
ERR("Failed to create thread.\n");
hr = E_FAIL;
}
}
else
{
if ((rc = pthread_create(&thread->pthread, NULL, thread_main, data)))
{
ERR("Failed to create thread, error %d.\n", rc);
hr = hresult_from_errno(rc);
}
}
return hr;
}
HRESULT vkd3d_join_thread(struct vkd3d_instance *instance, union vkd3d_thread_handle *thread)
{
HRESULT hr = S_OK;
int rc;
if (instance->join_thread)
{
if (FAILED(hr = instance->join_thread(thread->handle)))
ERR("Failed to join thread, hr %#x.\n", hr);
}
else
{
if ((rc = pthread_join(thread->pthread, NULL)))
{
ERR("Failed to join thread, error %d.\n", rc);
hr = hresult_from_errno(rc);
}
}
return hr;
}
VKD3D_EXPORT IUnknown *vkd3d_get_device_parent(ID3D12Device *device)
{
struct d3d12_device *d3d12_device = impl_from_ID3D12Device((d3d12_device_iface *)device);
return d3d12_device->parent;
}
VKD3D_EXPORT VkDevice vkd3d_get_vk_device(ID3D12Device *device)
{
struct d3d12_device *d3d12_device = impl_from_ID3D12Device((d3d12_device_iface *)device);
return d3d12_device->vk_device;
}
VKD3D_EXPORT VkPhysicalDevice vkd3d_get_vk_physical_device(ID3D12Device *device)
{
struct d3d12_device *d3d12_device = impl_from_ID3D12Device((d3d12_device_iface *)device);
return d3d12_device->vk_physical_device;
}
VKD3D_EXPORT struct vkd3d_instance *vkd3d_instance_from_device(ID3D12Device *device)
{
struct d3d12_device *d3d12_device = impl_from_ID3D12Device((d3d12_device_iface *)device);
return d3d12_device->vkd3d_instance;
}