vkd3d-proton/libs/vkd3d/device.c

4751 lines
193 KiB
C

/*
* Copyright 2016 Józef Kucia for CodeWeavers
*
* 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.
*
* 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.
*
* 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"
#ifdef VKD3D_ENABLE_RENDERDOC
#include "vkd3d_renderdoc.h"
#endif
struct vkd3d_struct
{
enum vkd3d_structure_type type;
const void *next;
};
#define vkd3d_find_struct(c, t) vkd3d_find_struct_(c, VKD3D_STRUCTURE_TYPE_##t)
static const void *vkd3d_find_struct_(const struct vkd3d_struct *chain,
enum vkd3d_structure_type type)
{
while (chain)
{
if (chain->type == type)
return chain;
chain = chain->next;
}
return NULL;
}
static uint32_t vkd3d_get_vk_version(void)
{
int major, minor;
vkd3d_parse_version(PACKAGE_VERSION, &major, &minor);
return VK_MAKE_VERSION(major, minor, 0);
}
struct vkd3d_optional_extension_info
{
const char *extension_name;
ptrdiff_t vulkan_info_offset;
};
#define VK_EXTENSION(name, member) \
{VK_ ## name ## _EXTENSION_NAME, offsetof(struct vkd3d_vulkan_info, member)}
static const struct vkd3d_optional_extension_info optional_instance_extensions[] =
{
/* EXT extensions */
VK_EXTENSION(EXT_DEBUG_UTILS, EXT_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),
/* EXT extensions */
VK_EXTENSION(EXT_CALIBRATED_TIMESTAMPS, EXT_calibrated_timestamps),
VK_EXTENSION(EXT_CONDITIONAL_RENDERING, EXT_conditional_rendering),
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_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_EXTERNAL_MEMORY_HOST, EXT_external_memory_host),
/* AMD extensions */
VK_EXTENSION(AMD_BUFFER_MARKER, AMD_buffer_marker),
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),
};
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)
{
const char *disabled_extensions;
if (!(disabled_extensions = getenv("VKD3D_DISABLE_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;
ptrdiff_t offset = optional_extensions[i].vulkan_info_offset;
bool *supported = (void *)((uintptr_t)vulkan_info + offset);
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 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;
const struct vkd3d_optional_instance_extensions_info *optional_extensions;
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)
{
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;
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);
}
optional_extensions = vkd3d_find_struct(create_info->next, OPTIONAL_INSTANCE_EXTENSIONS_INFO);
if (optional_extensions && optional_extensions->extension_count)
{
if (!(*user_extension_supported = vkd3d_calloc(optional_extensions->extension_count, sizeof(bool))))
{
vkd3d_free(vk_extensions);
return E_OUTOFMEMORY;
}
}
else
{
*user_extension_supported = NULL;
}
*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,
optional_extensions ? optional_extensions->extensions : NULL,
optional_extensions ? optional_extensions->extension_count : 0,
*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)
{
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;
}
static const struct vkd3d_debug_option vkd3d_config_options[] =
{
{"vk_debug", VKD3D_CONFIG_FLAG_VULKAN_DEBUG}, /* enable Vulkan debug extensions */
};
static uint64_t vkd3d_init_config_flags(void)
{
uint64_t config_flags;
const char *config;
config = getenv("VKD3D_CONFIG");
config_flags = vkd3d_parse_debug_options(config, vkd3d_config_options, ARRAY_SIZE(vkd3d_config_options));
if (config_flags)
TRACE("VKD3D_CONFIG='%s'.\n", config);
return config_flags;
}
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 struct vkd3d_optional_instance_extensions_info *optional_extensions;
const char *debug_layer_name = "VK_LAYER_KHRONOS_validation";
const struct vkd3d_application_info *vkd3d_application_info;
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;
}
if (create_info->wchar_size != 2 && create_info->wchar_size != 4)
{
ERR("Unexpected WCHAR size %zu.\n", create_info->wchar_size);
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;
instance->wchar_size = create_info->wchar_size;
instance->config_flags = vkd3d_init_config_flags();
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 (FAILED(hr = vkd3d_init_instance_caps(instance, create_info,
&extension_count, &user_extension_supported)))
{
if (instance->libvulkan)
vkd3d_dlclose(instance->libvulkan);
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));
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;
if ((vkd3d_application_info = vkd3d_find_struct(create_info->next, APPLICATION_INFO)))
{
application_info.pApplicationName = vkd3d_application_info->application_name;
application_info.applicationVersion = vkd3d_application_info->application_version;
if (vkd3d_application_info->engine_name)
{
application_info.pEngineName = vkd3d_application_info->engine_name;
application_info.engineVersion = vkd3d_application_info->engine_version;
}
}
else 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;
}
optional_extensions = vkd3d_find_struct(create_info->next, OPTIONAL_INSTANCE_EXTENSIONS_INFO);
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,
optional_extensions ? optional_extensions->extensions : NULL,
optional_extensions ? optional_extensions->extension_count : 0,
user_extension_supported, &instance->vk_info);
instance_info.ppEnabledExtensionNames = extensions;
vkd3d_free(user_extension_supported);
if (instance->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!\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 && (instance->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
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 (create_info->type != VKD3D_STRUCTURE_TYPE_INSTANCE_CREATE_INFO)
{
WARN("Invalid structure type %#x.\n", create_info->type);
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;
}
static void vkd3d_physical_device_info_init(struct vkd3d_physical_device_info *info, struct d3d12_device *device)
{
VkPhysicalDeviceShaderSubgroupExtendedTypesFeaturesKHR *shader_subgroup_extended_types_features;
const struct vkd3d_vk_instance_procs *vk_procs = &device->vkd3d_instance->vk_procs;
VkPhysicalDeviceSubgroupSizeControlPropertiesEXT *subgroup_size_control_properties;
VkPhysicalDeviceInlineUniformBlockPropertiesEXT *inline_uniform_block_properties;
VkPhysicalDeviceExtendedDynamicStateFeaturesEXT *extended_dynamic_state_features;
VkPhysicalDeviceExternalMemoryHostPropertiesEXT *external_memory_host_properties;
VkPhysicalDeviceBufferDeviceAddressFeaturesKHR *buffer_device_address_features;
VkPhysicalDeviceCustomBorderColorPropertiesEXT *custom_border_color_properties;
VkPhysicalDeviceConditionalRenderingFeaturesEXT *conditional_rendering_features;
VkPhysicalDeviceDescriptorIndexingPropertiesEXT *descriptor_indexing_properties;
VkPhysicalDeviceSamplerFilterMinmaxProperties *sampler_filter_minmax_properties;
VkPhysicalDeviceVertexAttributeDivisorPropertiesEXT *vertex_divisor_properties;
VkPhysicalDeviceTexelBufferAlignmentPropertiesEXT *buffer_alignment_properties;
VkPhysicalDeviceTimelineSemaphorePropertiesKHR *timeline_semaphore_properties;
VkPhysicalDeviceInlineUniformBlockFeaturesEXT *inline_uniform_block_features;
VkPhysicalDeviceDescriptorIndexingFeaturesEXT *descriptor_indexing_features;
VkPhysicalDeviceShaderSMBuiltinsPropertiesNV *shader_sm_builtins_properties;
VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT *vertex_divisor_features;
VkPhysicalDeviceTexelBufferAlignmentFeaturesEXT *buffer_alignment_features;
VkPhysicalDeviceShaderDemoteToHelperInvocationFeaturesEXT *demote_features;
VkPhysicalDeviceCustomBorderColorFeaturesEXT *custom_border_color_features;
VkPhysicalDeviceTimelineSemaphoreFeaturesKHR *timeline_semaphore_features;
VkPhysicalDevicePushDescriptorPropertiesKHR *push_descriptor_properties;
VkPhysicalDeviceShaderFloat16Int8FeaturesKHR *float16_int8_features;
VkPhysicalDeviceShaderCoreProperties2AMD *shader_core_properties2;
VkPhysicalDeviceDepthClipEnableFeaturesEXT *depth_clip_features;
VkPhysicalDeviceRobustness2PropertiesEXT *robustness2_properties;
VkPhysicalDeviceShaderCorePropertiesAMD *shader_core_properties;
VkPhysicalDeviceMaintenance3Properties *maintenance3_properties;
VkPhysicalDeviceTransformFeedbackPropertiesEXT *xfb_properties;
VkPhysicalDevice physical_device = device->vk_physical_device;
VkPhysicalDeviceRobustness2FeaturesEXT *robustness2_features;
VkPhysicalDeviceTransformFeedbackFeaturesEXT *xfb_features;
VkPhysicalDeviceSubgroupProperties *subgroup_properties;
struct vkd3d_vulkan_info *vulkan_info = &device->vk_info;
memset(info, 0, sizeof(*info));
buffer_device_address_features = &info->buffer_device_address_features;
conditional_rendering_features = &info->conditional_rendering_features;
depth_clip_features = &info->depth_clip_features;
descriptor_indexing_features = &info->descriptor_indexing_features;
descriptor_indexing_properties = &info->descriptor_indexing_properties;
inline_uniform_block_features = &info->inline_uniform_block_features;
inline_uniform_block_properties = &info->inline_uniform_block_properties;
push_descriptor_properties = &info->push_descriptor_properties;
maintenance3_properties = &info->maintenance3_properties;
demote_features = &info->demote_features;
buffer_alignment_features = &info->texel_buffer_alignment_features;
buffer_alignment_properties = &info->texel_buffer_alignment_properties;
vertex_divisor_features = &info->vertex_divisor_features;
vertex_divisor_properties = &info->vertex_divisor_properties;
xfb_features = &info->xfb_features;
xfb_properties = &info->xfb_properties;
subgroup_properties = &info->subgroup_properties;
timeline_semaphore_features = &info->timeline_semaphore_features;
timeline_semaphore_properties = &info->timeline_semaphore_properties;
custom_border_color_properties = &info->custom_border_color_properties;
custom_border_color_features = &info->custom_border_color_features;
subgroup_size_control_properties = &info->subgroup_size_control_properties;
shader_core_properties = &info->shader_core_properties;
shader_core_properties2 = &info->shader_core_properties2;
shader_sm_builtins_properties = &info->shader_sm_builtins_properties;
sampler_filter_minmax_properties = &info->sampler_filter_minmax_properties;
float16_int8_features = &info->float16_int8_features;
shader_subgroup_extended_types_features = &info->subgroup_extended_types_features;
robustness2_properties = &info->robustness2_properties;
robustness2_features = &info->robustness2_features;
extended_dynamic_state_features = &info->extended_dynamic_state_features;
external_memory_host_properties = &info->external_memory_host_properties;
info->features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
info->properties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
subgroup_properties->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES;
vk_prepend_struct(&info->properties2, subgroup_properties);
maintenance3_properties->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_3_PROPERTIES;
vk_prepend_struct(&info->properties2, maintenance3_properties);
if (vulkan_info->KHR_buffer_device_address)
{
buffer_device_address_features->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES_KHR;
vk_prepend_struct(&info->features2, buffer_device_address_features);
}
if (vulkan_info->KHR_timeline_semaphore)
{
timeline_semaphore_features->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES_KHR;
vk_prepend_struct(&info->features2, timeline_semaphore_features);
timeline_semaphore_properties->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_PROPERTIES_KHR;
vk_prepend_struct(&info->properties2, timeline_semaphore_properties);
}
if (vulkan_info->KHR_push_descriptor)
{
push_descriptor_properties->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR;
vk_prepend_struct(&info->properties2, push_descriptor_properties);
}
if (vulkan_info->KHR_shader_float16_int8)
{
float16_int8_features->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT16_INT8_FEATURES_KHR;
vk_prepend_struct(&info->features2, float16_int8_features);
}
if (vulkan_info->KHR_shader_subgroup_extended_types)
{
shader_subgroup_extended_types_features->sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SUBGROUP_EXTENDED_TYPES_FEATURES_KHR;
vk_prepend_struct(&info->features2, shader_subgroup_extended_types_features);
}
if (vulkan_info->EXT_calibrated_timestamps)
info->time_domains = vkd3d_physical_device_get_time_domains(device);
if (vulkan_info->EXT_conditional_rendering)
{
conditional_rendering_features->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CONDITIONAL_RENDERING_FEATURES_EXT;
vk_prepend_struct(&info->features2, conditional_rendering_features);
}
if (vulkan_info->EXT_custom_border_color)
{
custom_border_color_features->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_FEATURES_EXT;
vk_prepend_struct(&info->features2, custom_border_color_features);
custom_border_color_properties->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_PROPERTIES_EXT;
vk_prepend_struct(&info->properties2, custom_border_color_properties);
}
if (vulkan_info->EXT_depth_clip_enable)
{
depth_clip_features->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLIP_ENABLE_FEATURES_EXT;
vk_prepend_struct(&info->features2, depth_clip_features);
}
if (vulkan_info->EXT_descriptor_indexing)
{
descriptor_indexing_features->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES_EXT;
vk_prepend_struct(&info->features2, descriptor_indexing_features);
descriptor_indexing_properties->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_PROPERTIES_EXT;
vk_prepend_struct(&info->properties2, descriptor_indexing_properties);
}
if (vulkan_info->EXT_inline_uniform_block)
{
inline_uniform_block_features->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_FEATURES_EXT;
vk_prepend_struct(&info->features2, inline_uniform_block_features);
inline_uniform_block_properties->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_PROPERTIES_EXT;
vk_prepend_struct(&info->properties2, inline_uniform_block_properties);
}
if (vulkan_info->EXT_robustness2)
{
robustness2_features->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT;
vk_prepend_struct(&info->features2, robustness2_features);
robustness2_properties->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_PROPERTIES_EXT;
vk_prepend_struct(&info->properties2, robustness2_properties);
}
if (vulkan_info->EXT_sampler_filter_minmax)
{
sampler_filter_minmax_properties->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_FILTER_MINMAX_PROPERTIES_EXT;
vk_prepend_struct(&info->properties2, sampler_filter_minmax_properties);
}
if (vulkan_info->EXT_shader_demote_to_helper_invocation)
{
demote_features->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DEMOTE_TO_HELPER_INVOCATION_FEATURES_EXT;
vk_prepend_struct(&info->features2, demote_features);
}
if (vulkan_info->EXT_subgroup_size_control)
{
subgroup_size_control_properties->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_PROPERTIES_EXT;
vk_prepend_struct(&info->properties2, subgroup_size_control_properties);
}
if (vulkan_info->EXT_texel_buffer_alignment)
{
buffer_alignment_features->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXEL_BUFFER_ALIGNMENT_FEATURES_EXT;
vk_prepend_struct(&info->features2, buffer_alignment_features);
buffer_alignment_properties->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXEL_BUFFER_ALIGNMENT_PROPERTIES_EXT;
vk_prepend_struct(&info->properties2, buffer_alignment_properties);
}
if (vulkan_info->EXT_transform_feedback)
{
xfb_features->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_FEATURES_EXT;
vk_prepend_struct(&info->features2, xfb_features);
xfb_properties->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_PROPERTIES_EXT;
vk_prepend_struct(&info->properties2, xfb_properties);
}
if (vulkan_info->EXT_vertex_attribute_divisor)
{
vertex_divisor_features->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_FEATURES_EXT;
vk_prepend_struct(&info->features2, vertex_divisor_features);
vertex_divisor_properties->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_PROPERTIES_EXT;
vk_prepend_struct(&info->properties2, vertex_divisor_properties);
}
if (vulkan_info->EXT_extended_dynamic_state)
{
extended_dynamic_state_features->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT;
vk_prepend_struct(&info->features2, extended_dynamic_state_features);
}
if (vulkan_info->EXT_external_memory_host)
{
external_memory_host_properties->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_MEMORY_HOST_PROPERTIES_EXT;
vk_prepend_struct(&info->properties2, external_memory_host_properties);
}
if (vulkan_info->AMD_shader_core_properties)
{
shader_core_properties->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_CORE_PROPERTIES_AMD;
vk_prepend_struct(&info->properties2, shader_core_properties);
}
if (vulkan_info->AMD_shader_core_properties2)
{
shader_core_properties2->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_CORE_PROPERTIES_2_AMD;
vk_prepend_struct(&info->properties2, shader_core_properties2);
}
if (vulkan_info->NV_shader_sm_builtins)
{
shader_sm_builtins_properties->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SM_BUILTINS_PROPERTIES_NV;
vk_prepend_struct(&info->properties2, shader_sm_builtins_properties);
}
VK_CALL(vkGetPhysicalDeviceFeatures2(physical_device, &info->features2));
VK_CALL(vkGetPhysicalDeviceProperties2(physical_device, &info->properties2));
}
static void vkd3d_trace_physical_device_properties(const VkPhysicalDeviceProperties *properties)
{
const uint32_t driver_version = properties->driverVersion;
const uint32_t api_version = properties->apiVersion;
TRACE("Device name: %s.\n", properties->deviceName);
TRACE("Vendor ID: %#x, Device ID: %#x.\n", properties->vendorID, properties->deviceID);
TRACE("Driver version: %#x (%u.%u.%u, %u.%u.%u.%u).\n", driver_version,
VK_VERSION_MAJOR(driver_version), VK_VERSION_MINOR(driver_version), VK_VERSION_PATCH(driver_version),
driver_version >> 22, (driver_version >> 14) & 0xff, (driver_version >> 6) & 0xff, driver_version & 0x3f);
TRACE("API version: %u.%u.%u.\n",
VK_VERSION_MAJOR(api_version), VK_VERSION_MINOR(api_version), VK_VERSION_PATCH(api_version));
}
static void vkd3d_trace_physical_device(VkPhysicalDevice device,
const struct vkd3d_physical_device_info *info,
const struct vkd3d_vk_instance_procs *vk_procs)
{
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),
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)
{
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));
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));
}
}
}
static void vkd3d_trace_physical_device_limits(const struct vkd3d_physical_device_info *info)
{
const VkPhysicalDeviceVertexAttributeDivisorPropertiesEXT *divisor_properties;
const VkPhysicalDeviceLimits *limits = &info->properties2.properties.limits;
const VkPhysicalDeviceDescriptorIndexingPropertiesEXT *descriptor_indexing;
const VkPhysicalDeviceTexelBufferAlignmentPropertiesEXT *buffer_alignment;
const VkPhysicalDeviceMaintenance3Properties *maintenance3;
const VkPhysicalDeviceTransformFeedbackPropertiesEXT *xfb;
TRACE("Device limits:\n");
TRACE(" maxImageDimension1D: %u.\n", limits->maxImageDimension1D);
TRACE(" maxImageDimension2D: %u.\n", limits->maxImageDimension2D);
TRACE(" maxImageDimension3D: %u.\n", limits->maxImageDimension3D);
TRACE(" maxImageDimensionCube: %u.\n", limits->maxImageDimensionCube);
TRACE(" maxImageArrayLayers: %u.\n", limits->maxImageArrayLayers);
TRACE(" maxTexelBufferElements: %u.\n", limits->maxTexelBufferElements);
TRACE(" maxUniformBufferRange: %u.\n", limits->maxUniformBufferRange);
TRACE(" maxStorageBufferRange: %u.\n", limits->maxStorageBufferRange);
TRACE(" maxPushConstantsSize: %u.\n", limits->maxPushConstantsSize);
TRACE(" maxMemoryAllocationCount: %u.\n", limits->maxMemoryAllocationCount);
TRACE(" maxSamplerAllocationCount: %u.\n", limits->maxSamplerAllocationCount);
TRACE(" bufferImageGranularity: %#"PRIx64".\n", limits->bufferImageGranularity);
TRACE(" sparseAddressSpaceSize: %#"PRIx64".\n", limits->sparseAddressSpaceSize);
TRACE(" maxBoundDescriptorSets: %u.\n", limits->maxBoundDescriptorSets);
TRACE(" maxPerStageDescriptorSamplers: %u.\n", limits->maxPerStageDescriptorSamplers);
TRACE(" maxPerStageDescriptorUniformBuffers: %u.\n", limits->maxPerStageDescriptorUniformBuffers);
TRACE(" maxPerStageDescriptorStorageBuffers: %u.\n", limits->maxPerStageDescriptorStorageBuffers);
TRACE(" maxPerStageDescriptorSampledImages: %u.\n", limits->maxPerStageDescriptorSampledImages);
TRACE(" maxPerStageDescriptorStorageImages: %u.\n", limits->maxPerStageDescriptorStorageImages);
TRACE(" maxPerStageDescriptorInputAttachments: %u.\n", limits->maxPerStageDescriptorInputAttachments);
TRACE(" maxPerStageResources: %u.\n", limits->maxPerStageResources);
TRACE(" maxDescriptorSetSamplers: %u.\n", limits->maxDescriptorSetSamplers);
TRACE(" maxDescriptorSetUniformBuffers: %u.\n", limits->maxDescriptorSetUniformBuffers);
TRACE(" maxDescriptorSetUniformBuffersDynamic: %u.\n", limits->maxDescriptorSetUniformBuffersDynamic);
TRACE(" maxDescriptorSetStorageBuffers: %u.\n", limits->maxDescriptorSetStorageBuffers);
TRACE(" maxDescriptorSetStorageBuffersDynamic: %u.\n", limits->maxDescriptorSetStorageBuffersDynamic);
TRACE(" maxDescriptorSetSampledImages: %u.\n", limits->maxDescriptorSetSampledImages);
TRACE(" maxDescriptorSetStorageImages: %u.\n", limits->maxDescriptorSetStorageImages);
TRACE(" maxDescriptorSetInputAttachments: %u.\n", limits->maxDescriptorSetInputAttachments);
TRACE(" maxVertexInputAttributes: %u.\n", limits->maxVertexInputAttributes);
TRACE(" maxVertexInputBindings: %u.\n", limits->maxVertexInputBindings);
TRACE(" maxVertexInputAttributeOffset: %u.\n", limits->maxVertexInputAttributeOffset);
TRACE(" maxVertexInputBindingStride: %u.\n", limits->maxVertexInputBindingStride);
TRACE(" maxVertexOutputComponents: %u.\n", limits->maxVertexOutputComponents);
TRACE(" maxTessellationGenerationLevel: %u.\n", limits->maxTessellationGenerationLevel);
TRACE(" maxTessellationPatchSize: %u.\n", limits->maxTessellationPatchSize);
TRACE(" maxTessellationControlPerVertexInputComponents: %u.\n",
limits->maxTessellationControlPerVertexInputComponents);
TRACE(" maxTessellationControlPerVertexOutputComponents: %u.\n",
limits->maxTessellationControlPerVertexOutputComponents);
TRACE(" maxTessellationControlPerPatchOutputComponents: %u.\n",
limits->maxTessellationControlPerPatchOutputComponents);
TRACE(" maxTessellationControlTotalOutputComponents: %u.\n",
limits->maxTessellationControlTotalOutputComponents);
TRACE(" maxTessellationEvaluationInputComponents: %u.\n",
limits->maxTessellationEvaluationInputComponents);
TRACE(" maxTessellationEvaluationOutputComponents: %u.\n",
limits->maxTessellationEvaluationOutputComponents);
TRACE(" maxGeometryShaderInvocations: %u.\n", limits->maxGeometryShaderInvocations);
TRACE(" maxGeometryInputComponents: %u.\n", limits->maxGeometryInputComponents);
TRACE(" maxGeometryOutputComponents: %u.\n", limits->maxGeometryOutputComponents);
TRACE(" maxGeometryOutputVertices: %u.\n", limits->maxGeometryOutputVertices);
TRACE(" maxGeometryTotalOutputComponents: %u.\n", limits->maxGeometryTotalOutputComponents);
TRACE(" maxFragmentInputComponents: %u.\n", limits->maxFragmentInputComponents);
TRACE(" maxFragmentOutputAttachments: %u.\n", limits->maxFragmentOutputAttachments);
TRACE(" maxFragmentDualSrcAttachments: %u.\n", limits->maxFragmentDualSrcAttachments);
TRACE(" maxFragmentCombinedOutputResources: %u.\n", limits->maxFragmentCombinedOutputResources);
TRACE(" maxComputeSharedMemorySize: %u.\n", limits->maxComputeSharedMemorySize);
TRACE(" maxComputeWorkGroupCount: %u, %u, %u.\n", limits->maxComputeWorkGroupCount[0],
limits->maxComputeWorkGroupCount[1], limits->maxComputeWorkGroupCount[2]);
TRACE(" maxComputeWorkGroupInvocations: %u.\n", limits->maxComputeWorkGroupInvocations);
TRACE(" maxComputeWorkGroupSize: %u, %u, %u.\n", limits->maxComputeWorkGroupSize[0],
limits->maxComputeWorkGroupSize[1], limits->maxComputeWorkGroupSize[2]);
TRACE(" subPixelPrecisionBits: %u.\n", limits->subPixelPrecisionBits);
TRACE(" subTexelPrecisionBits: %u.\n", limits->subTexelPrecisionBits);
TRACE(" mipmapPrecisionBits: %u.\n", limits->mipmapPrecisionBits);
TRACE(" maxDrawIndexedIndexValue: %u.\n", limits->maxDrawIndexedIndexValue);
TRACE(" maxDrawIndirectCount: %u.\n", limits->maxDrawIndirectCount);
TRACE(" maxSamplerLodBias: %f.\n", limits->maxSamplerLodBias);
TRACE(" maxSamplerAnisotropy: %f.\n", limits->maxSamplerAnisotropy);
TRACE(" maxViewports: %u.\n", limits->maxViewports);
TRACE(" maxViewportDimensions: %u, %u.\n", limits->maxViewportDimensions[0],
limits->maxViewportDimensions[1]);
TRACE(" viewportBoundsRange: %f, %f.\n", limits->viewportBoundsRange[0], limits->viewportBoundsRange[1]);
TRACE(" viewportSubPixelBits: %u.\n", limits->viewportSubPixelBits);
TRACE(" minMemoryMapAlignment: %u.\n", (unsigned int)limits->minMemoryMapAlignment);
TRACE(" minTexelBufferOffsetAlignment: %#"PRIx64".\n", limits->minTexelBufferOffsetAlignment);
TRACE(" minUniformBufferOffsetAlignment: %#"PRIx64".\n", limits->minUniformBufferOffsetAlignment);
TRACE(" minStorageBufferOffsetAlignment: %#"PRIx64".\n", limits->minStorageBufferOffsetAlignment);
TRACE(" minTexelOffset: %d.\n", limits->minTexelOffset);
TRACE(" maxTexelOffset: %u.\n", limits->maxTexelOffset);
TRACE(" minTexelGatherOffset: %d.\n", limits->minTexelGatherOffset);
TRACE(" maxTexelGatherOffset: %u.\n", limits->maxTexelGatherOffset);
TRACE(" minInterpolationOffset: %f.\n", limits->minInterpolationOffset);
TRACE(" maxInterpolationOffset: %f.\n", limits->maxInterpolationOffset);
TRACE(" subPixelInterpolationOffsetBits: %u.\n", limits->subPixelInterpolationOffsetBits);
TRACE(" maxFramebufferWidth: %u.\n", limits->maxFramebufferWidth);
TRACE(" maxFramebufferHeight: %u.\n", limits->maxFramebufferHeight);
TRACE(" maxFramebufferLayers: %u.\n", limits->maxFramebufferLayers);
TRACE(" framebufferColorSampleCounts: %#x.\n", limits->framebufferColorSampleCounts);
TRACE(" framebufferDepthSampleCounts: %#x.\n", limits->framebufferDepthSampleCounts);
TRACE(" framebufferStencilSampleCounts: %#x.\n", limits->framebufferStencilSampleCounts);
TRACE(" framebufferNoAttachmentsSampleCounts: %#x.\n", limits->framebufferNoAttachmentsSampleCounts);
TRACE(" maxColorAttachments: %u.\n", limits->maxColorAttachments);
TRACE(" sampledImageColorSampleCounts: %#x.\n", limits->sampledImageColorSampleCounts);
TRACE(" sampledImageIntegerSampleCounts: %#x.\n", limits->sampledImageIntegerSampleCounts);
TRACE(" sampledImageDepthSampleCounts: %#x.\n", limits->sampledImageDepthSampleCounts);
TRACE(" sampledImageStencilSampleCounts: %#x.\n", limits->sampledImageStencilSampleCounts);
TRACE(" storageImageSampleCounts: %#x.\n", limits->storageImageSampleCounts);
TRACE(" maxSampleMaskWords: %u.\n", limits->maxSampleMaskWords);
TRACE(" timestampComputeAndGraphics: %#x.\n", limits->timestampComputeAndGraphics);
TRACE(" timestampPeriod: %f.\n", limits->timestampPeriod);
TRACE(" maxClipDistances: %u.\n", limits->maxClipDistances);
TRACE(" maxCullDistances: %u.\n", limits->maxCullDistances);
TRACE(" maxCombinedClipAndCullDistances: %u.\n", limits->maxCombinedClipAndCullDistances);
TRACE(" discreteQueuePriorities: %u.\n", limits->discreteQueuePriorities);
TRACE(" pointSizeRange: %f, %f.\n", limits->pointSizeRange[0], limits->pointSizeRange[1]);
TRACE(" lineWidthRange: %f, %f,\n", limits->lineWidthRange[0], limits->lineWidthRange[1]);
TRACE(" pointSizeGranularity: %f.\n", limits->pointSizeGranularity);
TRACE(" lineWidthGranularity: %f.\n", limits->lineWidthGranularity);
TRACE(" strictLines: %#x.\n", limits->strictLines);
TRACE(" standardSampleLocations: %#x.\n", limits->standardSampleLocations);
TRACE(" optimalBufferCopyOffsetAlignment: %#"PRIx64".\n", limits->optimalBufferCopyOffsetAlignment);
TRACE(" optimalBufferCopyRowPitchAlignment: %#"PRIx64".\n", limits->optimalBufferCopyRowPitchAlignment);
TRACE(" nonCoherentAtomSize: %#"PRIx64".\n", limits->nonCoherentAtomSize);
descriptor_indexing = &info->descriptor_indexing_properties;
TRACE(" VkPhysicalDeviceDescriptorIndexingPropertiesEXT:\n");
TRACE(" maxUpdateAfterBindDescriptorsInAllPools: %u.\n",
descriptor_indexing->maxUpdateAfterBindDescriptorsInAllPools);
TRACE(" shaderUniformBufferArrayNonUniformIndexingNative: %#x.\n",
descriptor_indexing->shaderUniformBufferArrayNonUniformIndexingNative);
TRACE(" shaderSampledImageArrayNonUniformIndexingNative: %#x.\n",
descriptor_indexing->shaderSampledImageArrayNonUniformIndexingNative);
TRACE(" shaderStorageBufferArrayNonUniformIndexingNative: %#x.\n",
descriptor_indexing->shaderStorageBufferArrayNonUniformIndexingNative);
TRACE(" shaderStorageImageArrayNonUniformIndexingNative: %#x.\n",
descriptor_indexing->shaderStorageImageArrayNonUniformIndexingNative);
TRACE(" shaderInputAttachmentArrayNonUniformIndexingNative: %#x.\n",
descriptor_indexing->shaderInputAttachmentArrayNonUniformIndexingNative);
TRACE(" robustBufferAccessUpdateAfterBind: %#x.\n",
descriptor_indexing->robustBufferAccessUpdateAfterBind);
TRACE(" quadDivergentImplicitLod: %#x.\n",
descriptor_indexing->quadDivergentImplicitLod);
TRACE(" maxPerStageDescriptorUpdateAfterBindSamplers: %u.\n",
descriptor_indexing->maxPerStageDescriptorUpdateAfterBindSamplers);
TRACE(" maxPerStageDescriptorUpdateAfterBindUniformBuffers: %u.\n",
descriptor_indexing->maxPerStageDescriptorUpdateAfterBindUniformBuffers);
TRACE(" maxPerStageDescriptorUpdateAfterBindStorageBuffers: %u.\n",
descriptor_indexing->maxPerStageDescriptorUpdateAfterBindStorageBuffers);
TRACE(" maxPerStageDescriptorUpdateAfterBindSampledImages: %u.\n",
descriptor_indexing->maxPerStageDescriptorUpdateAfterBindSampledImages);
TRACE(" maxPerStageDescriptorUpdateAfterBindStorageImages: %u.\n",
descriptor_indexing->maxPerStageDescriptorUpdateAfterBindStorageImages);
TRACE(" maxPerStageDescriptorUpdateAfterBindInputAttachments: %u.\n",
descriptor_indexing->maxPerStageDescriptorUpdateAfterBindInputAttachments);
TRACE(" maxPerStageUpdateAfterBindResources: %u.\n",
descriptor_indexing->maxPerStageUpdateAfterBindResources);
TRACE(" maxDescriptorSetUpdateAfterBindSamplers: %u.\n",
descriptor_indexing->maxDescriptorSetUpdateAfterBindSamplers);
TRACE(" maxDescriptorSetUpdateAfterBindUniformBuffers: %u.\n",
descriptor_indexing->maxDescriptorSetUpdateAfterBindUniformBuffers);
TRACE(" maxDescriptorSetUpdateAfterBindUniformBuffersDynamic: %u.\n",
descriptor_indexing->maxDescriptorSetUpdateAfterBindUniformBuffersDynamic);
TRACE(" maxDescriptorSetUpdateAfterBindStorageBuffers: %u.\n",
descriptor_indexing->maxDescriptorSetUpdateAfterBindStorageBuffers);
TRACE(" maxDescriptorSetUpdateAfterBindStorageBuffersDynamic: %u.\n",
descriptor_indexing->maxDescriptorSetUpdateAfterBindStorageBuffersDynamic);
TRACE(" maxDescriptorSetUpdateAfterBindSampledImages: %u.\n",
descriptor_indexing->maxDescriptorSetUpdateAfterBindSampledImages);
TRACE(" maxDescriptorSetUpdateAfterBindStorageImages: %u.\n",
descriptor_indexing->maxDescriptorSetUpdateAfterBindStorageImages);
TRACE(" maxDescriptorSetUpdateAfterBindInputAttachments: %u.\n",
descriptor_indexing->maxDescriptorSetUpdateAfterBindInputAttachments);
maintenance3 = &info->maintenance3_properties;
TRACE(" VkPhysicalDeviceMaintenance3Properties:\n");
TRACE(" maxPerSetDescriptors: %u.\n", maintenance3->maxPerSetDescriptors);
TRACE(" maxMemoryAllocationSize: %#"PRIx64".\n", maintenance3->maxMemoryAllocationSize);
buffer_alignment = &info->texel_buffer_alignment_properties;
TRACE(" VkPhysicalDeviceTexelBufferAlignmentPropertiesEXT:\n");
TRACE(" storageTexelBufferOffsetAlignmentBytes: %#"PRIx64".\n",
buffer_alignment->storageTexelBufferOffsetAlignmentBytes);
TRACE(" storageTexelBufferOffsetSingleTexelAlignment: %#x.\n",
buffer_alignment->storageTexelBufferOffsetSingleTexelAlignment);
TRACE(" uniformTexelBufferOffsetAlignmentBytes: %#"PRIx64".\n",
buffer_alignment->uniformTexelBufferOffsetAlignmentBytes);
TRACE(" uniformTexelBufferOffsetSingleTexelAlignment: %#x.\n",
buffer_alignment->uniformTexelBufferOffsetSingleTexelAlignment);
xfb = &info->xfb_properties;
TRACE(" VkPhysicalDeviceTransformFeedbackPropertiesEXT:\n");
TRACE(" maxTransformFeedbackStreams: %u.\n", xfb->maxTransformFeedbackStreams);
TRACE(" maxTransformFeedbackBuffers: %u.\n", xfb->maxTransformFeedbackBuffers);
TRACE(" maxTransformFeedbackBufferSize: %#"PRIx64".\n", xfb->maxTransformFeedbackBufferSize);
TRACE(" maxTransformFeedbackStreamDataSize: %u.\n", xfb->maxTransformFeedbackStreamDataSize);
TRACE(" maxTransformFeedbackBufferDataSize: %u.\n", xfb->maxTransformFeedbackBufferDataSize);
TRACE(" maxTransformFeedbackBufferDataStride: %u.\n", xfb->maxTransformFeedbackBufferDataStride);
TRACE(" transformFeedbackQueries: %#x.\n", xfb->transformFeedbackQueries);
TRACE(" transformFeedbackStreamsLinesTriangles: %#x.\n", xfb->transformFeedbackStreamsLinesTriangles);
TRACE(" transformFeedbackRasterizationStreamSelect: %#x.\n", xfb->transformFeedbackRasterizationStreamSelect);
TRACE(" transformFeedbackDraw: %x.\n", xfb->transformFeedbackDraw);
divisor_properties = &info->vertex_divisor_properties;
TRACE(" VkPhysicalDeviceVertexAttributeDivisorPropertiesEXT:\n");
TRACE(" maxVertexAttribDivisor: %u.\n", divisor_properties->maxVertexAttribDivisor);
}
static void vkd3d_trace_physical_device_features(const struct vkd3d_physical_device_info *info)
{
const VkPhysicalDeviceConditionalRenderingFeaturesEXT *conditional_rendering_features;
const VkPhysicalDeviceShaderDemoteToHelperInvocationFeaturesEXT *demote_features;
const VkPhysicalDeviceTexelBufferAlignmentFeaturesEXT *buffer_alignment_features;
const VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT *divisor_features;
const VkPhysicalDeviceCustomBorderColorFeaturesEXT *border_color_features;
const VkPhysicalDeviceDescriptorIndexingFeaturesEXT *descriptor_indexing;
const VkPhysicalDeviceDepthClipEnableFeaturesEXT *depth_clip_features;
const VkPhysicalDeviceFeatures *features = &info->features2.features;
const VkPhysicalDeviceTransformFeedbackFeaturesEXT *xfb;
TRACE("Device features:\n");
TRACE(" robustBufferAccess: %#x.\n", features->robustBufferAccess);
TRACE(" fullDrawIndexUint32: %#x.\n", features->fullDrawIndexUint32);
TRACE(" imageCubeArray: %#x.\n", features->imageCubeArray);
TRACE(" independentBlend: %#x.\n", features->independentBlend);
TRACE(" geometryShader: %#x.\n", features->geometryShader);
TRACE(" tessellationShader: %#x.\n", features->tessellationShader);
TRACE(" sampleRateShading: %#x.\n", features->sampleRateShading);
TRACE(" dualSrcBlend: %#x.\n", features->dualSrcBlend);
TRACE(" logicOp: %#x.\n", features->logicOp);
TRACE(" multiDrawIndirect: %#x.\n", features->multiDrawIndirect);
TRACE(" drawIndirectFirstInstance: %#x.\n", features->drawIndirectFirstInstance);
TRACE(" depthClamp: %#x.\n", features->depthClamp);
TRACE(" depthBiasClamp: %#x.\n", features->depthBiasClamp);
TRACE(" fillModeNonSolid: %#x.\n", features->fillModeNonSolid);
TRACE(" depthBounds: %#x.\n", features->depthBounds);
TRACE(" wideLines: %#x.\n", features->wideLines);
TRACE(" largePoints: %#x.\n", features->largePoints);
TRACE(" alphaToOne: %#x.\n", features->alphaToOne);
TRACE(" multiViewport: %#x.\n", features->multiViewport);
TRACE(" samplerAnisotropy: %#x.\n", features->samplerAnisotropy);
TRACE(" textureCompressionETC2: %#x.\n", features->textureCompressionETC2);
TRACE(" textureCompressionASTC_LDR: %#x.\n", features->textureCompressionASTC_LDR);
TRACE(" textureCompressionBC: %#x.\n", features->textureCompressionBC);
TRACE(" occlusionQueryPrecise: %#x.\n", features->occlusionQueryPrecise);
TRACE(" pipelineStatisticsQuery: %#x.\n", features->pipelineStatisticsQuery);
TRACE(" vertexOipelineStoresAndAtomics: %#x.\n", features->vertexPipelineStoresAndAtomics);
TRACE(" fragmentStoresAndAtomics: %#x.\n", features->fragmentStoresAndAtomics);
TRACE(" shaderTessellationAndGeometryPointSize: %#x.\n", features->shaderTessellationAndGeometryPointSize);
TRACE(" shaderImageGatherExtended: %#x.\n", features->shaderImageGatherExtended);
TRACE(" shaderStorageImageExtendedFormats: %#x.\n", features->shaderStorageImageExtendedFormats);
TRACE(" shaderStorageImageMultisample: %#x.\n", features->shaderStorageImageMultisample);
TRACE(" shaderStorageImageReadWithoutFormat: %#x.\n", features->shaderStorageImageReadWithoutFormat);
TRACE(" shaderStorageImageWriteWithoutFormat: %#x.\n", features->shaderStorageImageWriteWithoutFormat);
TRACE(" shaderUniformBufferArrayDynamicIndexing: %#x.\n", features->shaderUniformBufferArrayDynamicIndexing);
TRACE(" shaderSampledImageArrayDynamicIndexing: %#x.\n", features->shaderSampledImageArrayDynamicIndexing);
TRACE(" shaderStorageBufferArrayDynamicIndexing: %#x.\n", features->shaderStorageBufferArrayDynamicIndexing);
TRACE(" shaderStorageImageArrayDynamicIndexing: %#x.\n", features->shaderStorageImageArrayDynamicIndexing);
TRACE(" shaderClipDistance: %#x.\n", features->shaderClipDistance);
TRACE(" shaderCullDistance: %#x.\n", features->shaderCullDistance);
TRACE(" shaderFloat64: %#x.\n", features->shaderFloat64);
TRACE(" shaderInt64: %#x.\n", features->shaderInt64);
TRACE(" shaderInt16: %#x.\n", features->shaderInt16);
TRACE(" shaderResourceResidency: %#x.\n", features->shaderResourceResidency);
TRACE(" shaderResourceMinLod: %#x.\n", features->shaderResourceMinLod);
TRACE(" sparseBinding: %#x.\n", features->sparseBinding);
TRACE(" sparseResidencyBuffer: %#x.\n", features->sparseResidencyBuffer);
TRACE(" sparseResidencyImage2D: %#x.\n", features->sparseResidencyImage2D);
TRACE(" sparseResidencyImage3D: %#x.\n", features->sparseResidencyImage3D);
TRACE(" sparseResidency2Samples: %#x.\n", features->sparseResidency2Samples);
TRACE(" sparseResidency4Samples: %#x.\n", features->sparseResidency4Samples);
TRACE(" sparseResidency8Samples: %#x.\n", features->sparseResidency8Samples);
TRACE(" sparseResidency16Samples: %#x.\n", features->sparseResidency16Samples);
TRACE(" sparseResidencyAliased: %#x.\n", features->sparseResidencyAliased);
TRACE(" variableMultisampleRate: %#x.\n", features->variableMultisampleRate);
TRACE(" inheritedQueries: %#x.\n", features->inheritedQueries);
descriptor_indexing = &info->descriptor_indexing_features;
TRACE(" VkPhysicalDeviceDescriptorIndexingFeaturesEXT:\n");
TRACE(" shaderInputAttachmentArrayDynamicIndexing: %#x.\n",
descriptor_indexing->shaderInputAttachmentArrayDynamicIndexing);
TRACE(" shaderUniformTexelBufferArrayDynamicIndexing: %#x.\n",
descriptor_indexing->shaderUniformTexelBufferArrayDynamicIndexing);
TRACE(" shaderStorageTexelBufferArrayDynamicIndexing: %#x.\n",
descriptor_indexing->shaderStorageTexelBufferArrayDynamicIndexing);
TRACE(" shaderUniformBufferArrayNonUniformIndexing: %#x.\n",
descriptor_indexing->shaderUniformBufferArrayNonUniformIndexing);
TRACE(" shaderSampledImageArrayNonUniformIndexing: %#x.\n",
descriptor_indexing->shaderSampledImageArrayNonUniformIndexing);
TRACE(" shaderStorageBufferArrayNonUniformIndexing: %#x.\n",
descriptor_indexing->shaderStorageBufferArrayNonUniformIndexing);
TRACE(" shaderStorageImageArrayNonUniformIndexing: %#x.\n",
descriptor_indexing->shaderStorageImageArrayNonUniformIndexing);
TRACE(" shaderInputAttachmentArrayNonUniformIndexing: %#x.\n",
descriptor_indexing->shaderInputAttachmentArrayNonUniformIndexing);
TRACE(" shaderUniformTexelBufferArrayNonUniformIndexing: %#x.\n",
descriptor_indexing->shaderUniformTexelBufferArrayNonUniformIndexing);
TRACE(" shaderStorageTexelBufferArrayNonUniformIndexing: %#x.\n",
descriptor_indexing->shaderStorageTexelBufferArrayNonUniformIndexing);
TRACE(" descriptorBindingUniformBufferUpdateAfterBind: %#x.\n",
descriptor_indexing->descriptorBindingUniformBufferUpdateAfterBind);
TRACE(" descriptorBindingSampledImageUpdateAfterBind: %#x.\n",
descriptor_indexing->descriptorBindingSampledImageUpdateAfterBind);
TRACE(" descriptorBindingStorageImageUpdateAfterBind: %#x.\n",
descriptor_indexing->descriptorBindingStorageImageUpdateAfterBind);
TRACE(" descriptorBindingStorageBufferUpdateAfterBind: %#x.\n",
descriptor_indexing->descriptorBindingStorageBufferUpdateAfterBind);
TRACE(" descriptorBindingUniformTexelBufferUpdateAfterBind: %#x.\n",
descriptor_indexing->descriptorBindingUniformTexelBufferUpdateAfterBind);
TRACE(" descriptorBindingStorageTexelBufferUpdateAfterBind: %#x.\n",
descriptor_indexing->descriptorBindingStorageTexelBufferUpdateAfterBind);
TRACE(" descriptorBindingUpdateUnusedWhilePending: %#x.\n",
descriptor_indexing->descriptorBindingUpdateUnusedWhilePending);
TRACE(" descriptorBindingPartiallyBound: %#x.\n",
descriptor_indexing->descriptorBindingPartiallyBound);
TRACE(" descriptorBindingVariableDescriptorCount: %#x.\n",
descriptor_indexing->descriptorBindingVariableDescriptorCount);
TRACE(" runtimeDescriptorArray: %#x.\n",
descriptor_indexing->runtimeDescriptorArray);
conditional_rendering_features = &info->conditional_rendering_features;
TRACE(" VkPhysicalDeviceConditionalRenderingFeaturesEXT:\n");
TRACE(" conditionalRendering: %#x.\n", conditional_rendering_features->conditionalRendering);
depth_clip_features = &info->depth_clip_features;
TRACE(" VkPhysicalDeviceDepthClipEnableFeaturesEXT:\n");
TRACE(" depthClipEnable: %#x.\n", depth_clip_features->depthClipEnable);
demote_features = &info->demote_features;
TRACE(" VkPhysicalDeviceShaderDemoteToHelperInvocationFeaturesEXT:\n");
TRACE(" shaderDemoteToHelperInvocation: %#x.\n", demote_features->shaderDemoteToHelperInvocation);
buffer_alignment_features = &info->texel_buffer_alignment_features;
TRACE(" VkPhysicalDeviceTexelBufferAlignmentFeaturesEXT:\n");
TRACE(" texelBufferAlignment: %#x.\n", buffer_alignment_features->texelBufferAlignment);
xfb = &info->xfb_features;
TRACE(" VkPhysicalDeviceTransformFeedbackFeaturesEXT:\n");
TRACE(" transformFeedback: %#x.\n", xfb->transformFeedback);
TRACE(" geometryStreams: %#x.\n", xfb->geometryStreams);
divisor_features = &info->vertex_divisor_features;
TRACE(" VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT:\n");
TRACE(" vertexAttributeInstanceRateDivisor: %#x.\n",
divisor_features->vertexAttributeInstanceRateDivisor);
TRACE(" vertexAttributeInstanceRateZeroDivisor: %#x.\n",
divisor_features->vertexAttributeInstanceRateZeroDivisor);
border_color_features = &info->custom_border_color_features;
TRACE(" VkPhysicalDeviceCustomBorderColorFeaturesEXT:\n");
TRACE(" customBorderColors: %#x\n", border_color_features->customBorderColors);
TRACE(" customBorderColorWithoutFormat: %#x\n", 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;
const struct vkd3d_optional_device_extensions_info *optional_extensions;
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);
}
optional_extensions = vkd3d_find_struct(create_info->next, OPTIONAL_DEVICE_EXTENSIONS_INFO);
if (optional_extensions && optional_extensions->extension_count)
{
if (!(*user_extension_supported = vkd3d_calloc(optional_extensions->extension_count, sizeof(bool))))
{
vkd3d_free(vk_extensions);
return E_OUTOFMEMORY;
}
}
else
{
*user_extension_supported = NULL;
}
*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,
optional_extensions ? optional_extensions->extensions : NULL,
optional_extensions ? optional_extensions->extension_count : 0,
*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;
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;
/* Shader extensions. */
vulkan_info->shader_extension_count = 0;
if (vulkan_info->EXT_shader_demote_to_helper_invocation)
{
vulkan_info->shader_extensions[vulkan_info->shader_extension_count++] =
VKD3D_SHADER_TARGET_EXTENSION_SPV_EXT_DEMOTE_TO_HELPER_INVOCATION;
}
if (physical_device_info->features2.features.shaderStorageImageReadWithoutFormat)
{
vulkan_info->shader_extensions[vulkan_info->shader_extension_count++] =
VKD3D_SHADER_TARGET_EXTENSION_READ_STORAGE_IMAGE_WITHOUT_FORMAT;
}
/* 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;
if (vulkan_info->EXT_descriptor_indexing && descriptor_indexing
&& (descriptor_indexing->descriptorBindingUniformBufferUpdateAfterBind
|| descriptor_indexing->descriptorBindingStorageBufferUpdateAfterBind
|| descriptor_indexing->descriptorBindingUniformTexelBufferUpdateAfterBind
|| descriptor_indexing->descriptorBindingStorageTexelBufferUpdateAfterBind)
&& !physical_device_info->descriptor_indexing_properties.robustBufferAccessUpdateAfterBind)
{
WARN("Disabling robust buffer access for the update after bind feature.\n");
features->robustBufferAccess = VK_FALSE;
physical_device_info->robustness2_features.robustBufferAccess2 = VK_FALSE;
}
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;
}
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;
uint32_t count;
unsigned int i;
VkResult vr;
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 (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 *queue = device->queues[i];
if (!queue)
continue;
/* Don't destroy the same queue twice */
for (j = i; j < VKD3D_QUEUE_FAMILY_COUNT; j++)
{
if (device->queues[j] == queue)
device->queues[j] = NULL;
}
vkd3d_queue_destroy(queue, device);
}
}
static HRESULT d3d12_device_create_vkd3d_queues(struct d3d12_device *device,
const struct vkd3d_device_queue_info *queue_info)
{
unsigned int i, j;
HRESULT hr;
device->queue_family_count = 0;
memset(device->queues, 0, sizeof(device->queues));
memset(device->queue_family_indices, 0, sizeof(device->queue_family_indices));
for (i = 0; i < VKD3D_QUEUE_FAMILY_COUNT; i++)
{
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->queues[i] = device->queues[j];
}
if (device->queues[i])
continue;
if (FAILED((hr = vkd3d_queue_create(device, queue_info->family_index[i],
&queue_info->vk_properties[i], &device->queues[i]))))
goto out_destroy_queues;
device->queue_family_indices[device->queue_family_count++] = queue_info->family_index[i];
}
return S_OK;
out_destroy_queues:
d3d12_device_destroy_vkd3d_queues(device);
return hr;
}
static float queue_priorities[] = {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;
unsigned int i, j;
uint32_t count;
bool duplicate;
memset(info, 0, sizeof(*info));
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_TRANSFER] = vkd3d_find_queue(count, queue_properties,
VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT, VK_QUEUE_TRANSFER_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);
/* Works around https://gitlab.freedesktop.org/mesa/mesa/issues/2529.
* The other viable workaround was to disable VK_EXT_descriptor_indexing for the time being,
* but that did not work out as we relied on global_bo_list to deal with games like RE2 which appear
* to potentially access descriptors which reference freed memory. This is fine in D3D12, but we need
* PARTIALLY_BOUND_BIT semantics to make that work well.
* Just disabling async compute works around the issue as well. */
#define VKD3D_FORCE_SINGLE_QUEUE 1
if (info->family_index[VKD3D_QUEUE_FAMILY_COMPUTE] == VK_QUEUE_FAMILY_IGNORED || VKD3D_FORCE_SINGLE_QUEUE)
info->family_index[VKD3D_QUEUE_FAMILY_COMPUTE] = info->family_index[VKD3D_QUEUE_FAMILY_GRAPHICS];
if (info->family_index[VKD3D_QUEUE_FAMILY_TRANSFER] == VK_QUEUE_FAMILY_IGNORED || VKD3D_FORCE_SINGLE_QUEUE)
info->family_index[VKD3D_QUEUE_FAMILY_TRANSFER] = info->family_index[VKD3D_QUEUE_FAMILY_COMPUTE];
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 = 1;
queue_info->pQueuePriorities = queue_priorities;
}
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;
const struct vkd3d_optional_device_extensions_info *optional_extensions;
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 (FAILED(hr = vkd3d_init_device_extensions(device, create_info,
&extension_count, &user_extension_supported)))
return hr;
vkd3d_physical_device_info_init(&device->device_info, device);
if (FAILED(hr = vkd3d_init_device_caps(device, create_info, &device->device_info)))
return hr;
if (!(extensions = vkd3d_calloc(extension_count, sizeof(*extensions))))
{
vkd3d_free(user_extension_supported);
return E_OUTOFMEMORY;
}
optional_extensions = vkd3d_find_struct(create_info->next, OPTIONAL_DEVICE_EXTENSIONS_INFO);
/* 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,
optional_extensions ? optional_extensions->extensions : NULL,
optional_extensions ? optional_extensions->extension_count : 0,
user_extension_supported, &device->vk_info);
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));
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;
}
#define VKD3D_VA_FALLBACK_BASE 0x8000000000000000ull
#define VKD3D_VA_SLAB_BASE 0x0000001000000000ull
#define VKD3D_VA_SLAB_SIZE_SHIFT 32
#define VKD3D_VA_SLAB_SIZE (1ull << VKD3D_VA_SLAB_SIZE_SHIFT)
#define VKD3D_VA_SLAB_COUNT (64 * 1024)
static D3D12_GPU_VIRTUAL_ADDRESS vkd3d_gpu_va_allocator_allocate_slab(struct vkd3d_gpu_va_allocator *allocator,
size_t aligned_size, void *ptr)
{
struct vkd3d_gpu_va_slab *slab;
D3D12_GPU_VIRTUAL_ADDRESS address;
unsigned slab_idx;
slab = allocator->free_slab;
allocator->free_slab = slab->ptr;
slab->size = aligned_size;
slab->ptr = ptr;
/* It is critical that the multiplication happens in 64-bit to not
* overflow. */
slab_idx = slab - allocator->slabs;
address = VKD3D_VA_SLAB_BASE + slab_idx * VKD3D_VA_SLAB_SIZE;
TRACE("Allocated address %#"PRIx64", slab %u, size %zu.\n", address, slab_idx, aligned_size);
return address;
}
static D3D12_GPU_VIRTUAL_ADDRESS vkd3d_gpu_va_allocator_allocate_fallback(struct vkd3d_gpu_va_allocator *allocator,
size_t alignment, size_t aligned_size, void *ptr)
{
struct vkd3d_gpu_va_allocation *allocation;
D3D12_GPU_VIRTUAL_ADDRESS base, ceiling;
base = allocator->fallback_floor;
ceiling = ~(D3D12_GPU_VIRTUAL_ADDRESS)0;
ceiling -= alignment - 1;
if (aligned_size > ceiling || ceiling - aligned_size < base)
return 0;
base = (base + (alignment - 1)) & ~((D3D12_GPU_VIRTUAL_ADDRESS)alignment - 1);
if (!vkd3d_array_reserve((void **)&allocator->fallback_allocations, &allocator->fallback_allocations_size,
allocator->fallback_allocation_count + 1, sizeof(*allocator->fallback_allocations)))
return 0;
allocation = &allocator->fallback_allocations[allocator->fallback_allocation_count++];
allocation->base = base;
allocation->size = aligned_size;
allocation->ptr = ptr;
/* This pointer is bumped and never lowered on a free. However, this will
* only fail once we have exhausted 63 bits of address space. */
allocator->fallback_floor = base + aligned_size;
TRACE("Allocated address %#"PRIx64", size %zu.\n", base, aligned_size);
return base;
}
D3D12_GPU_VIRTUAL_ADDRESS vkd3d_gpu_va_allocator_allocate(struct vkd3d_gpu_va_allocator *allocator,
size_t alignment, size_t size, void *ptr)
{
D3D12_GPU_VIRTUAL_ADDRESS address;
int rc;
if (size > ~(size_t)0 - (alignment - 1))
return 0;
size = align(size, alignment);
if ((rc = pthread_mutex_lock(&allocator->mutex)))
{
ERR("Failed to lock mutex, error %d.\n", rc);
return 0;
}
if (size <= VKD3D_VA_SLAB_SIZE && allocator->free_slab)
address = vkd3d_gpu_va_allocator_allocate_slab(allocator, size, ptr);
else
address = vkd3d_gpu_va_allocator_allocate_fallback(allocator, alignment, size, ptr);
pthread_mutex_unlock(&allocator->mutex);
return address;
}
static void *vkd3d_gpu_va_allocator_dereference_slab(struct vkd3d_gpu_va_allocator *allocator,
D3D12_GPU_VIRTUAL_ADDRESS address)
{
const struct vkd3d_gpu_va_slab *slab;
D3D12_GPU_VIRTUAL_ADDRESS base_offset;
unsigned int slab_idx;
base_offset = address - VKD3D_VA_SLAB_BASE;
slab_idx = base_offset >> VKD3D_VA_SLAB_SIZE_SHIFT;
if (slab_idx >= VKD3D_VA_SLAB_COUNT)
{
ERR("Invalid slab index %u for address %#"PRIx64".\n", slab_idx, address);
return NULL;
}
slab = &allocator->slabs[slab_idx];
base_offset -= slab_idx * VKD3D_VA_SLAB_SIZE;
if (base_offset >= slab->size)
{
ERR("Address %#"PRIx64" is %#"PRIx64" bytes into slab %u of size %zu.\n",
address, base_offset, slab_idx, slab->size);
return NULL;
}
return slab->ptr;
}
static int vkd3d_gpu_va_allocation_compare(const void *k, const void *e)
{
const struct vkd3d_gpu_va_allocation *allocation = e;
const D3D12_GPU_VIRTUAL_ADDRESS *address = k;
if (*address < allocation->base)
return -1;
if (*address - allocation->base >= allocation->size)
return 1;
return 0;
}
static void *vkd3d_gpu_va_allocator_dereference_fallback(struct vkd3d_gpu_va_allocator *allocator,
D3D12_GPU_VIRTUAL_ADDRESS address)
{
struct vkd3d_gpu_va_allocation *allocation;
allocation = bsearch(&address, allocator->fallback_allocations, allocator->fallback_allocation_count,
sizeof(*allocation), vkd3d_gpu_va_allocation_compare);
return allocation ? allocation->ptr : NULL;
}
void *vkd3d_gpu_va_allocator_dereference(struct vkd3d_gpu_va_allocator *allocator,
D3D12_GPU_VIRTUAL_ADDRESS address)
{
void *ret;
int rc;
/* If we land in the non-fallback region, dereferencing VA is lock-less.
* The base pointer is immutable, and the only way we can have a data race
* is if some other thread is poking into the
* slab_mem_allocation[base_index] block. This can only happen if someone
* is trying to free the entry while we're dereferencing it, which would
* be a serious application bug. */
if (address < VKD3D_VA_FALLBACK_BASE)
return vkd3d_gpu_va_allocator_dereference_slab(allocator, address);
/* Slow fallback. */
if ((rc = pthread_mutex_lock(&allocator->mutex)))
{
ERR("Failed to lock mutex, error %d.\n", rc);
return NULL;
}
ret = vkd3d_gpu_va_allocator_dereference_fallback(allocator, address);
pthread_mutex_unlock(&allocator->mutex);
return ret;
}
static void vkd3d_gpu_va_allocator_free_slab(struct vkd3d_gpu_va_allocator *allocator,
D3D12_GPU_VIRTUAL_ADDRESS address)
{
D3D12_GPU_VIRTUAL_ADDRESS base_offset;
struct vkd3d_gpu_va_slab *slab;
unsigned int slab_idx;
base_offset = address - VKD3D_VA_SLAB_BASE;
slab_idx = base_offset >> VKD3D_VA_SLAB_SIZE_SHIFT;
if (slab_idx >= VKD3D_VA_SLAB_COUNT)
{
ERR("Invalid slab index %u for address %#"PRIx64".\n", slab_idx, address);
return;
}
TRACE("Freeing address %#"PRIx64", slab %u.\n", address, slab_idx);
slab = &allocator->slabs[slab_idx];
slab->size = 0;
slab->ptr = allocator->free_slab;
allocator->free_slab = slab;
}
static void vkd3d_gpu_va_allocator_free_fallback(struct vkd3d_gpu_va_allocator *allocator,
D3D12_GPU_VIRTUAL_ADDRESS address)
{
struct vkd3d_gpu_va_allocation *allocation;
unsigned int index;
allocation = bsearch(&address, allocator->fallback_allocations, allocator->fallback_allocation_count,
sizeof(*allocation), vkd3d_gpu_va_allocation_compare);
if (!allocation || allocation->base != address)
{
ERR("Address %#"PRIx64" does not match any allocation.\n", address);
return;
}
index = allocation - allocator->fallback_allocations;
--allocator->fallback_allocation_count;
if (index != allocator->fallback_allocation_count)
memmove(&allocator->fallback_allocations[index], &allocator->fallback_allocations[index + 1],
(allocator->fallback_allocation_count - index) * sizeof(*allocation));
}
void vkd3d_gpu_va_allocator_free(struct vkd3d_gpu_va_allocator *allocator, D3D12_GPU_VIRTUAL_ADDRESS address)
{
int rc;
if ((rc = pthread_mutex_lock(&allocator->mutex)))
{
ERR("Failed to lock mutex, error %d.\n", rc);
return;
}
if (address < VKD3D_VA_FALLBACK_BASE)
{
vkd3d_gpu_va_allocator_free_slab(allocator, address);
pthread_mutex_unlock(&allocator->mutex);
return;
}
vkd3d_gpu_va_allocator_free_fallback(allocator, address);
pthread_mutex_unlock(&allocator->mutex);
}
static bool vkd3d_gpu_va_allocator_init(struct vkd3d_gpu_va_allocator *allocator)
{
unsigned int i;
int rc;
memset(allocator, 0, sizeof(*allocator));
allocator->fallback_floor = VKD3D_VA_FALLBACK_BASE;
/* To remain lock-less, we cannot grow the slabs array after the fact. If
* we commit to a maximum number of allocations here, we can dereference
* without taking a lock as the base pointer never changes. We would be
* able to grow more seamlessly using an array of pointers, but that would
* make dereferencing slightly less efficient. */
if (!(allocator->slabs = vkd3d_calloc(VKD3D_VA_SLAB_COUNT, sizeof(*allocator->slabs))))
return false;
/* Mark all slabs as free. */
allocator->free_slab = &allocator->slabs[0];
for (i = 0; i < VKD3D_VA_SLAB_COUNT - 1; ++i)
{
allocator->slabs[i].ptr = &allocator->slabs[i + 1];
}
if ((rc = pthread_mutex_init(&allocator->mutex, NULL)))
{
ERR("Failed to initialize mutex, error %d.\n", rc);
vkd3d_free(allocator->slabs);
return false;
}
return true;
}
static void vkd3d_gpu_va_allocator_cleanup(struct vkd3d_gpu_va_allocator *allocator)
{
int rc;
if ((rc = pthread_mutex_lock(&allocator->mutex)))
{
ERR("Failed to lock mutex, error %d.\n", rc);
return;
}
vkd3d_free(allocator->slabs);
vkd3d_free(allocator->fallback_allocations);
pthread_mutex_unlock(&allocator->mutex);
pthread_mutex_destroy(&allocator->mutex);
}
/* ID3D12Device */
static inline struct d3d12_device *impl_from_ID3D12Device(d3d12_device_iface *iface)
{
return CONTAINING_RECORD(iface, struct d3d12_device, ID3D12Device_iface);
}
static 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_ID3D12Object)
|| IsEqualGUID(riid, &IID_IUnknown))
{
ID3D12Device_AddRef(iface);
*object = 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;
vkd3d_private_store_destroy(&device->private_store);
vkd3d_cleanup_format_info(device);
vkd3d_shader_debug_ring_cleanup(&device->debug_ring, device);
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);
vkd3d_destroy_null_resources(&device->null_resources, device);
vkd3d_gpu_va_allocator_cleanup(&device->gpu_va_allocator);
vkd3d_render_pass_cache_cleanup(&device->render_pass_cache, device);
vkd3d_fence_worker_stop(&device->fence_worker, device);
d3d12_device_destroy_vkd3d_queues(device);
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 ULONG STDMETHODCALLTYPE d3d12_device_Release(d3d12_device_iface *iface)
{
struct d3d12_device *device = impl_from_ID3D12Device(iface);
ULONG refcount = InterlockedDecrement(&device->refcount);
TRACE("%p decreasing refcount to %u.\n", device, refcount);
if (!refcount)
{
d3d12_device_destroy(device);
vkd3d_free(device);
}
return refcount;
}
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);
}
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);
}
static HRESULT STDMETHODCALLTYPE d3d12_device_SetName(d3d12_device_iface *iface, const WCHAR *name)
{
struct d3d12_device *device = impl_from_ID3D12Device(iface);
TRACE("iface %p, name %s.\n", iface, debugstr_w(name, device->wchar_size));
return vkd3d_set_vk_object_name(device, (uint64_t)(uintptr_t)device->vk_device,
VK_OBJECT_TYPE_DEVICE, name);
}
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);
struct d3d12_command_allocator *object;
HRESULT hr;
TRACE("iface %p, type %#x, riid %s, command_allocator %p.\n",
iface, type, debugstr_guid(riid), command_allocator);
if (FAILED(hr = d3d12_command_allocator_create(device, type, &object)))
return hr;
return return_interface(&object->ID3D12CommandAllocator_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_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)
{
struct d3d12_device *device = impl_from_ID3D12Device(iface);
struct d3d12_command_allocator *allocator;
struct d3d12_command_list *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 (!(allocator = unsafe_impl_from_ID3D12CommandAllocator(command_allocator)))
{
WARN("Command allocator is NULL.\n");
return E_INVALIDARG;
}
if (allocator->type != type)
{
WARN("Command list types do not match (allocator %#x, list %#x).\n",
allocator->type, type);
return E_INVALIDARG;
}
if (FAILED(hr = d3d12_command_list_create(device, node_mask, type, command_allocator,
initial_pipeline_state, &object)))
return hr;
return return_interface(&object->ID3D12GraphicsCommandList_iface,
&IID_ID3D12GraphicsCommandList, riid, command_list);
}
/* Direct3D feature levels restrict which formats can be optionally supported. */
static void vkd3d_restrict_format_support_for_feature_level(D3D12_FEATURE_DATA_FORMAT_SUPPORT *format_support)
{
static const D3D12_FEATURE_DATA_FORMAT_SUPPORT blacklisted_format_features[] =
{
{DXGI_FORMAT_B8G8R8A8_TYPELESS, D3D12_FORMAT_SUPPORT1_TYPED_UNORDERED_ACCESS_VIEW,
D3D12_FORMAT_SUPPORT2_UAV_TYPED_LOAD | D3D12_FORMAT_SUPPORT2_UAV_TYPED_STORE},
{DXGI_FORMAT_B8G8R8A8_UNORM, D3D12_FORMAT_SUPPORT1_TYPED_UNORDERED_ACCESS_VIEW,
D3D12_FORMAT_SUPPORT2_UAV_TYPED_LOAD | D3D12_FORMAT_SUPPORT2_UAV_TYPED_STORE},
};
unsigned int i;
for (i = 0; i < ARRAY_SIZE(blacklisted_format_features); ++i)
{
if (blacklisted_format_features[i].Format == format_support->Format)
{
format_support->Support1 &= ~blacklisted_format_features[i].Support1;
format_support->Support2 &= ~blacklisted_format_features[i].Support2;
break;
}
}
}
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 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:
{
const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
D3D12_FEATURE_DATA_FORMAT_SUPPORT *data = feature_data;
VkFormatFeatureFlagBits image_features;
const struct vkd3d_format *format;
VkFormatProperties properties;
if (feature_data_size != sizeof(*data))
{
WARN("Invalid size %u.\n", feature_data_size);
return E_INVALIDARG;
}
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;
}
VK_CALL(vkGetPhysicalDeviceFormatProperties(device->vk_physical_device, format->vk_format, &properties));
image_features = properties.linearTilingFeatures | properties.optimalTilingFeatures;
if (properties.bufferFeatures)
data->Support1 |= D3D12_FORMAT_SUPPORT1_BUFFER;
if (properties.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_SAMPLED_IMAGE_BIT)
{
data->Support1 |= D3D12_FORMAT_SUPPORT1_SHADER_LOAD | D3D12_FORMAT_SUPPORT1_MULTISAMPLE_LOAD
| D3D12_FORMAT_SUPPORT1_SHADER_GATHER;
if (image_features & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT)
{
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_COLOR_ATTACHMENT_BIT)
data->Support1 |= D3D12_FORMAT_SUPPORT1_RENDER_TARGET | D3D12_FORMAT_SUPPORT1_MULTISAMPLE_RENDERTARGET;
if (image_features & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT)
data->Support1 |= D3D12_FORMAT_SUPPORT1_BLENDABLE;
if (image_features & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT)
data->Support1 |= D3D12_FORMAT_SUPPORT1_DEPTH_STENCIL;
if (image_features & VK_FORMAT_FEATURE_BLIT_SRC_BIT)
data->Support1 |= D3D12_FORMAT_SUPPORT1_MULTISAMPLE_RESOLVE;
if (image_features & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT)
data->Support1 |= D3D12_FORMAT_SUPPORT1_TYPED_UNORDERED_ACCESS_VIEW;
if (image_features & VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT)
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;
vkd3d_restrict_format_support_for_feature_level(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_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);
switch (descriptor_heap_type)
{
case D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV:
case D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER:
return sizeof(struct d3d12_desc);
case D3D12_DESCRIPTOR_HEAP_TYPE_RTV:
return sizeof(struct d3d12_rtv_desc);
case D3D12_DESCRIPTOR_HEAP_TYPE_DSV:
return sizeof(struct d3d12_dsv_desc);
default:
FIXME("Unhandled type %#x.\n", descriptor_heap_type);
return 0;
}
}
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(d3d12_desc_from_cpu_handle(descriptor), 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(d3d12_desc_from_cpu_handle(descriptor),
device, unsafe_impl_from_ID3D12Resource(resource), desc);
}
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)
{
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(d3d12_desc_from_cpu_handle(descriptor),
device, unsafe_impl_from_ID3D12Resource(resource),
unsafe_impl_from_ID3D12Resource(counter_resource), desc);
}
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), unsafe_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_dsv_desc_create_dsv(d3d12_dsv_desc_from_cpu_handle(descriptor),
impl_from_ID3D12Device(iface), unsafe_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(d3d12_desc_from_cpu_handle(descriptor), device, desc);
}
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;
unsigned int dst_range_size, src_range_size;
struct d3d12_desc *dst, *src;
if (descriptor_heap_type != D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV
&& descriptor_heap_type != D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER)
{
FIXME("Unhandled descriptor heap type %#x.\n", descriptor_heap_type);
return;
}
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 = d3d12_desc_from_cpu_handle(dst_descriptor_range_offsets[dst_range_idx]);
src = d3d12_desc_from_cpu_handle(src_descriptor_range_offsets[src_range_idx]);
while (dst_idx < dst_range_size && src_idx < src_range_size)
d3d12_desc_copy(&dst[dst_idx++], &src[src_idx++], device);
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)
{
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);
d3d12_device_copy_descriptors(impl_from_ID3D12Device(iface),
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_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)
{
struct d3d12_device *device = impl_from_ID3D12Device(iface);
struct d3d12_heap *heap_object;
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, desc, initial_state,
optimized_clear_value, debugstr_guid(iid), resource);
heap_object = unsafe_impl_from_ID3D12Heap(heap);
if (FAILED(hr = d3d12_placed_resource_create(device, heap_object, heap_offset,
desc, initial_state, optimized_clear_value, &object)))
return hr;
return return_interface(&object->ID3D12Resource_iface, &IID_ID3D12Resource, 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)
{
struct d3d12_device *device = impl_from_ID3D12Device(iface);
FIXME("iface %p, object %p, attributes %p, access %#x, name %s, handle %p stub!\n",
iface, object, attributes, access, debugstr_w(name, device->wchar_size), handle);
return E_NOTIMPL;
}
static HRESULT STDMETHODCALLTYPE d3d12_device_OpenSharedHandle(d3d12_device_iface *iface,
HANDLE handle, REFIID riid, void **object)
{
FIXME("iface %p, handle %p, riid %s, object %p stub!\n",
iface, handle, debugstr_guid(riid), object);
return E_NOTIMPL;
}
static HRESULT STDMETHODCALLTYPE d3d12_device_OpenSharedHandleByName(d3d12_device_iface *iface,
const WCHAR *name, DWORD access, HANDLE *handle)
{
struct d3d12_device *device = impl_from_ID3D12Device(iface);
FIXME("iface %p, name %s, access %#x, handle %p stub!\n",
iface, debugstr_w(name, device->wchar_size), 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_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 (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_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)
{
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};
unsigned int i, sub_resource_idx, miplevel_idx, row_count, row_size, row_pitch;
unsigned int width, height, depth, array_size;
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);
if (total_bytes)
*total_bytes = ~(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);
return;
}
if (FAILED(d3d12_resource_validate_desc(desc, device)))
{
WARN("Invalid resource desc.\n");
return;
}
array_size = d3d12_resource_desc_get_layer_count(desc);
if (first_sub_resource >= desc->MipLevels * array_size
|| sub_resource_count > desc->MipLevels * array_size - first_sub_resource)
{
WARN("Invalid sub-resource range %u-%u for resource.\n", first_sub_resource, sub_resource_count);
return;
}
offset = 0;
total = 0;
for (i = 0; i < sub_resource_count; ++i)
{
sub_resource_idx = first_sub_resource + i;
miplevel_idx = sub_resource_idx % desc->MipLevels;
width = align(d3d12_resource_desc_get_width(desc, miplevel_idx), format->block_width);
height = align(d3d12_resource_desc_get_height(desc, miplevel_idx), format->block_height);
depth = d3d12_resource_desc_get_depth(desc, miplevel_idx);
row_count = height / format->block_height;
row_size = (width / format->block_width) * format->byte_count * format->block_byte_count;
row_pitch = align(row_size, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
if (layouts)
{
layouts[i].Offset = base_offset + offset;
layouts[i].Footprint.Format = desc->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) + size;
total = offset + size;
offset = align(total, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT);
}
if (total_bytes)
*total_bytes = total;
}
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,
REFIID iid, void **command_signature)
{
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, 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 = &unsafe_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;
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;
HRESULT hr;
TRACE("iface %p, blob %p, blob_size %lu, iid %s, lib %p.\n",
iface, blob, blob_size, debugstr_guid(iid), lib);
if (FAILED(hr = d3d12_pipeline_library_create(device, blob, blob_size, &pipeline_library)))
return hr;
return return_interface(&pipeline_library->ID3D12PipelineLibrary_iface,
&IID_ID3D12PipelineLibrary, iid, lib);
}
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;
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);
if (FAILED(hr = d3d12_heap_create_from_host_pointer(device, address, allocation_size, &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);
struct d3d12_command_list *object;
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 (FAILED(hr = d3d12_command_list_create(device, node_mask, type, NULL, NULL, &object)))
return hr;
return return_interface(&object->ID3D12GraphicsCommandList_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_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)
{
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_committed_resource_create(device, heap_properties, heap_flags,
desc, initial_state, optimized_clear_value, &object)))
{
*resource = NULL;
return hr;
}
return return_interface(&object->ID3D12Resource_iface, &IID_ID3D12Resource, 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;
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);
if (FAILED(hr = d3d12_reserved_resource_create(device,
desc, 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_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)
{
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_DESC *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_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)
{
FIXME("iface %p, desc %p, iid %s, state_object %p stub!\n",
iface, desc, debugstr_guid(iid), state_object);
return E_NOTIMPL;
}
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)
{
FIXME("iface %p, desc %p, info %p stub!\n", iface, desc, info);
}
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 CONST_VTBL struct ID3D12Device6Vtbl 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,
d3d12_device_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,
};
#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->queues[VKD3D_QUEUE_FAMILY_SPARSE_BINDING])
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;
return D3D12_TILED_RESOURCES_TIER_2;
}
static D3D12_RESOURCE_HEAP_TIER d3d12_device_determine_heap_tier(struct d3d12_device *device)
{
const VkPhysicalDeviceLimits *limits = &device->device_info.properties2.properties.limits;
const VkPhysicalDeviceMemoryProperties *mem_properties = &device->memory_properties;
const struct vkd3d_memory_info *mem_info = &device->memory_info;
uint32_t i, host_visible_types = 0;
for (i = 0; i < mem_properties->memoryTypeCount; i++)
{
if (mem_properties->memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)
host_visible_types |= 1u << i;
}
// 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.
if (limits->bufferImageGranularity > D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT ||
!(mem_info->buffer_type_mask & mem_info->sampled_type_mask & mem_info->rt_ds_type_mask) ||
!(mem_info->buffer_type_mask & mem_info->sampled_type_mask & host_visible_types))
return D3D12_RESOURCE_HEAP_TIER_1;
return D3D12_RESOURCE_HEAP_TIER_2;
}
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 =
features->shaderStorageImageExtendedFormats &&
features->shaderStorageImageReadWithoutFormat;
/* Requires VK_EXT_fragment_shader_interlock */
options->ROVsSupported = FALSE;
/* Requires VK_EXT_conservative_rasterization */
options->ConservativeRasterizationTier = D3D12_CONSERVATIVE_RASTERIZATION_TIER_NOT_SUPPORTED;
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;
/* Does spirv-dxil support this? */
options1->Int64ShaderOps = FALSE;
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->queues[VKD3D_QUEUE_FAMILY_TRANSFER]->timestamp_bits;
options3->CastingFullyTypedFormatSupported = TRUE;
/* Currently not supported */
options3->WriteBufferImmediateSupportFlags = 0;
/* Currently not supported */
options3->ViewInstancingTier = D3D12_VIEW_INSTANCING_TIER_NOT_SUPPORTED;
/* Currently not supported */
options3->BarycentricsSupported = FALSE;
}
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;
}
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;
/* Currently not supported */
options5->RaytracingTier = D3D12_RAYTRACING_TIER_NOT_SUPPORTED;
}
static void d3d12_device_caps_init_feature_options6(struct d3d12_device *device)
{
D3D12_FEATURE_DATA_D3D12_OPTIONS6 *options6 = &device->d3d12_caps.options6;
/* Currently not supported */
options6->AdditionalShadingRatesSupported = FALSE;
options6->PerPrimitiveShadingRateSupportedWithViewportIndexing = FALSE;
options6->VariableShadingRateTier = D3D12_VARIABLE_SHADING_RATE_TIER_NOT_SUPPORTED;
options6->ShadingRateImageTileSize = 0;
/* Not implemented */
options6->BackgroundProcessingSupported = FALSE;
}
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;
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;
/* 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;
if (device->api_version >= VK_API_VERSION_1_1 &&
vkd3d_shader_supports_dxil() &&
physical_device_info->subgroup_properties.subgroupSize >= 4 &&
(physical_device_info->subgroup_properties.supportedOperations & required) == required &&
(physical_device_info->subgroup_properties.supportedStages & required_stages) == required_stages)
{
/* 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)
*/
if (device->device_info.float16_int8_features.shaderFloat16 &&
device->device_info.subgroup_extended_types_features.shaderSubgroupExtendedTypes)
{
/* These features are required by FidelityFX SSSR demo. */
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
*/
/* 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.
*/
}
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(struct d3d12_device *device)
{
D3D_FEATURE_LEVEL fl_override = (D3D_FEATURE_LEVEL)0;
struct d3d12_caps *caps = &device->d3d12_caps;
const char* fl_string;
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 },
};
if (!(fl_string = getenv("VKD3D_FEATURE_LEVEL")))
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->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);
}
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_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_level(device);
d3d12_device_caps_init_shader_model(device);
d3d12_device_caps_override(device);
}
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;
}
struct d3d12_device *unsafe_impl_from_ID3D12Device(d3d12_device_iface *iface)
{
if (!iface)
return NULL;
#ifdef VKD3D_ENABLE_PROFILING
assert(iface->lpVtbl == &d3d12_device_vtbl ||
iface->lpVtbl == &d3d12_device_vtbl_profiled);
#else
assert(iface->lpVtbl == &d3d12_device_vtbl);
#endif
return impl_from_ID3D12Device(iface);
}
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;
memset(device, 0, sizeof(*device));
#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->wchar_size = instance->wchar_size;
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;
}
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_fence_worker_start(&device->fence_worker, device)))
goto out_free_private_store;
if (FAILED(hr = vkd3d_init_format_info(device)))
goto out_stop_fence_worker;
if (FAILED(hr = vkd3d_memory_info_init(&device->memory_info, device)))
goto out_cleanup_format_info;
if (FAILED(hr = vkd3d_init_null_resources(&device->null_resources, device)))
goto out_cleanup_format_info;
if (FAILED(hr = vkd3d_bindless_state_init(&device->bindless_state, device)))
goto out_destroy_null_resources;
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;
vkd3d_render_pass_cache_init(&device->render_pass_cache);
vkd3d_gpu_va_allocator_init(&device->gpu_va_allocator);
if ((device->parent = create_info->parent))
IUnknown_AddRef(device->parent);
d3d12_device_caps_init(device);
return S_OK;
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_destroy_null_resources:
vkd3d_destroy_null_resources(&device->null_resources, device);
out_cleanup_format_info:
vkd3d_cleanup_format_info(device);
out_stop_fence_worker:
vkd3d_fence_worker_stop(&device->fence_worker, 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;
}
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;
if (!(object = vkd3d_malloc(sizeof(*object))))
return E_OUTOFMEMORY;
if (FAILED(hr = d3d12_device_init(object, instance, create_info)))
{
vkd3d_free(object);
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);
return E_INVALIDARG;
}
TRACE("Created device %p.\n", object);
*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;
}