vkd3d-proton/libs/vkd3d/resource.c

6637 lines
257 KiB
C

/*
* Copyright 2016 Józef Kucia for CodeWeavers
* Copyright 2019 Conor McCarthy 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 <float.h>
#include "vkd3d_private.h"
#include "vkd3d_rw_spinlock.h"
#include "vkd3d_descriptor_debug.h"
#include "hashmap.h"
#define VKD3D_NULL_SRV_FORMAT DXGI_FORMAT_R8G8B8A8_UNORM
#define VKD3D_NULL_UAV_FORMAT DXGI_FORMAT_R32_UINT
static LONG64 global_cookie_counter;
LONG64 vkd3d_allocate_cookie()
{
return InterlockedIncrement64(&global_cookie_counter);
}
static VkImageType vk_image_type_from_d3d12_resource_dimension(D3D12_RESOURCE_DIMENSION dimension)
{
switch (dimension)
{
case D3D12_RESOURCE_DIMENSION_TEXTURE1D:
return VK_IMAGE_TYPE_1D;
case D3D12_RESOURCE_DIMENSION_TEXTURE2D:
return VK_IMAGE_TYPE_2D;
case D3D12_RESOURCE_DIMENSION_TEXTURE3D:
return VK_IMAGE_TYPE_3D;
default:
ERR("Invalid resource dimension %#x.\n", dimension);
return VK_IMAGE_TYPE_2D;
}
}
VkSampleCountFlagBits vk_samples_from_sample_count(unsigned int sample_count)
{
switch (sample_count)
{
case 1:
return VK_SAMPLE_COUNT_1_BIT;
case 2:
return VK_SAMPLE_COUNT_2_BIT;
case 4:
return VK_SAMPLE_COUNT_4_BIT;
case 8:
return VK_SAMPLE_COUNT_8_BIT;
case 16:
return VK_SAMPLE_COUNT_16_BIT;
case 32:
return VK_SAMPLE_COUNT_32_BIT;
case 64:
return VK_SAMPLE_COUNT_64_BIT;
default:
return 0;
}
}
VkSampleCountFlagBits vk_samples_from_dxgi_sample_desc(const DXGI_SAMPLE_DESC *desc)
{
VkSampleCountFlagBits vk_samples;
if ((vk_samples = vk_samples_from_sample_count(desc->Count)))
return vk_samples;
FIXME("Unhandled sample count %u.\n", desc->Count);
return VK_SAMPLE_COUNT_1_BIT;
}
HRESULT vkd3d_create_buffer(struct d3d12_device *device,
const D3D12_HEAP_PROPERTIES *heap_properties, D3D12_HEAP_FLAGS heap_flags,
const D3D12_RESOURCE_DESC1 *desc, VkBuffer *vk_buffer)
{
const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
VkExternalMemoryBufferCreateInfo external_info;
const bool sparse_resource = !heap_properties;
VkBufferCreateInfo buffer_info;
D3D12_HEAP_TYPE heap_type;
VkResult vr;
heap_type = heap_properties ? heap_properties->Type : D3D12_HEAP_TYPE_DEFAULT;
buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
buffer_info.pNext = NULL;
buffer_info.flags = 0;
buffer_info.size = desc->Width;
/* This is only used by OpenExistingHeapFrom*,
* and external host memory is the only way for us to do CROSS_ADAPTER. */
if (desc->Flags & D3D12_RESOURCE_FLAG_ALLOW_CROSS_ADAPTER)
{
external_info.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO;
external_info.pNext = NULL;
external_info.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_ALLOCATION_BIT_EXT;
buffer_info.pNext = &external_info;
}
if (sparse_resource)
{
buffer_info.flags |= VK_BUFFER_CREATE_SPARSE_BINDING_BIT |
VK_BUFFER_CREATE_SPARSE_RESIDENCY_BIT |
VK_BUFFER_CREATE_SPARSE_ALIASED_BIT;
/* If we attempt to bind sparse buffer with non-64k pages, we crash drivers.
* Specs seems a bit unclear how non-aligned VkBuffer sizes are supposed to work,
* so be safe. Pad out sparse buffers to their natural page size. */
buffer_info.size = align64(buffer_info.size, VKD3D_TILE_SIZE);
}
buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT
| VK_BUFFER_USAGE_TRANSFER_DST_BIT
| VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT
| VK_BUFFER_USAGE_STORAGE_BUFFER_BIT
| VK_BUFFER_USAGE_INDEX_BUFFER_BIT
| VK_BUFFER_USAGE_VERTEX_BUFFER_BIT
| VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT;
if (device->vk_info.EXT_conditional_rendering)
buffer_info.usage |= VK_BUFFER_USAGE_CONDITIONAL_RENDERING_BIT_EXT;
if (heap_type == D3D12_HEAP_TYPE_DEFAULT && device->vk_info.EXT_transform_feedback)
{
buffer_info.usage |= VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT
| VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_COUNTER_BUFFER_BIT_EXT;
}
if (d3d12_device_supports_ray_tracing_tier_1_0(device))
{
/* Allows us to place GENERIC acceleration structures on top of VkBuffers.
* This should only be allowed on non-host visible heaps. UPLOAD / READBACK is banned
* because of resource state rules, but CUSTOM might be allowed, needs to be verified. */
if (heap_type == D3D12_HEAP_TYPE_DEFAULT || !is_cpu_accessible_heap(heap_properties))
buffer_info.usage |= VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR;
/* This is always allowed. Used for vertex/index buffer inputs to RTAS build. */
buffer_info.usage |= VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR;
}
if (heap_type == D3D12_HEAP_TYPE_UPLOAD)
buffer_info.usage &= ~VK_BUFFER_USAGE_TRANSFER_DST_BIT;
else if (heap_type == D3D12_HEAP_TYPE_READBACK)
{
buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
}
if (device->device_info.buffer_device_address_features.bufferDeviceAddress)
buffer_info.usage |= VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_KHR;
if (desc->Flags & D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS)
buffer_info.usage |= VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT;
if (!(desc->Flags & D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE))
buffer_info.usage |= VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT;
/* Buffers always have properties of D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS. */
if (desc->Flags & D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS)
{
WARN("D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS cannot be set for buffers.\n");
return E_INVALIDARG;
}
if (device->queue_family_count > 1)
{
buffer_info.sharingMode = VK_SHARING_MODE_CONCURRENT;
buffer_info.queueFamilyIndexCount = device->queue_family_count;
buffer_info.pQueueFamilyIndices = device->queue_family_indices;
}
else
{
buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
buffer_info.queueFamilyIndexCount = 0;
buffer_info.pQueueFamilyIndices = NULL;
}
if (desc->Flags & (D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL))
FIXME("Unsupported resource flags %#x.\n", desc->Flags);
if ((vr = VK_CALL(vkCreateBuffer(device->vk_device, &buffer_info, NULL, vk_buffer))) < 0)
{
WARN("Failed to create Vulkan buffer, vr %d.\n", vr);
*vk_buffer = VK_NULL_HANDLE;
}
return hresult_from_vk_result(vr);
}
static unsigned int max_miplevel_count(const D3D12_RESOURCE_DESC1 *desc)
{
unsigned int size = max(desc->Width, desc->Height);
size = max(size, d3d12_resource_desc_get_depth(desc, 0));
return vkd3d_log2i(size) + 1;
}
static bool vkd3d_get_format_compatibility_list(const struct d3d12_device *device,
const D3D12_RESOURCE_DESC1 *desc, struct vkd3d_format_compatibility_list *out_list)
{
static const VkFormat r32_uav_formats[] = { VK_FORMAT_R32_UINT, VK_FORMAT_R32_SINT, VK_FORMAT_R32_SFLOAT };
const struct vkd3d_format *format = vkd3d_get_format(device, desc->Format, false);
struct vkd3d_format_compatibility_list list;
unsigned int i;
memset(&list, 0, sizeof(list));
if (desc->Format < device->format_compatibility_list_count)
list = device->format_compatibility_lists[desc->Format];
if (desc->Flags & D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS)
{
/* Legacy D3D11 compatibility rule that allows typed UAV loads on FL11.0 hardware.
* 5.3.9.5 from D3D11 functional spec. 32-bit typeless formats can be viewed as R32{U,I,F}.*/
if (format->byte_count == 4 && format->type == VKD3D_FORMAT_TYPE_TYPELESS)
{
for (i = 0; i < ARRAY_SIZE(r32_uav_formats); i++)
vkd3d_format_compatibility_list_add_format(&list, r32_uav_formats[i]);
}
/* 64-bit image atomics in D3D12 are done through RG32_UINT instead.
* We don't actually create 64-bit image views correctly at the moment,
* but adding the alias gives a clear signal to driver that we might use atomics on the image,
* which should disable compression or similar.
* If we can create R32G32_UINT views on this resource, we need to add R64_UINT as well as a potential
* mutable format. */
if (device->device_info.shader_image_atomic_int64_features.shaderImageInt64Atomics)
{
for (i = 0; i < list.format_count; i++)
{
if (list.vk_formats[i] == VK_FORMAT_R32G32_UINT)
{
vkd3d_format_compatibility_list_add_format(&list, VK_FORMAT_R64_UINT);
break;
}
}
}
}
if (list.format_count < 2)
return false;
*out_list = list;
return true;
}
static bool vkd3d_is_linear_tiling_supported(const struct d3d12_device *device, VkImageCreateInfo *image_info)
{
const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
VkImageFormatProperties properties;
bool supported;
VkResult vr;
if ((vr = VK_CALL(vkGetPhysicalDeviceImageFormatProperties(device->vk_physical_device, image_info->format,
image_info->imageType, VK_IMAGE_TILING_LINEAR, image_info->usage, image_info->flags, &properties))) < 0)
{
if (vr != VK_ERROR_FORMAT_NOT_SUPPORTED)
WARN("Failed to get device image format properties, vr %d.\n", vr);
else
{
WARN("Attempting to create linear image, but not supported.\n"
"usage: %#x, flags: %#x, fmt: %u, image_type: %u\n",
image_info->usage, image_info->flags, image_info->format, image_info->imageType);
}
return false;
}
supported = image_info->extent.depth <= properties.maxExtent.depth
&& image_info->mipLevels <= properties.maxMipLevels
&& image_info->arrayLayers <= properties.maxArrayLayers
&& (image_info->samples & properties.sampleCounts);
if (!supported)
{
WARN("Linear tiling not supported for mipLevels = %u, arrayLayers = %u, sampes = %u, depth = %u.\n",
image_info->mipLevels, image_info->arrayLayers, image_info->samples, image_info->extent.depth);
}
return supported;
}
static bool d3d12_device_prefers_general_depth_stencil(const struct d3d12_device *device)
{
if (device->vk_info.KHR_driver_properties)
{
if (device->device_info.driver_properties.driverID == VK_DRIVER_ID_NVIDIA_PROPRIETARY)
{
/* NVIDIA doesn't really care about layouts for the most part. */
return true;
}
else if (device->device_info.driver_properties.driverID == VK_DRIVER_ID_MESA_RADV)
{
/* RADV can use TC-compat HTILE without too much issues on Polaris and later.
* Use GENERAL for these GPUs.
* Pre-Polaris we run into issues where even read-only depth requires decompress
* so using GENERAL shouldn't really make things worse, it's going to run pretty bad
* either way. */
return true;
}
}
return false;
}
static VkImageLayout vk_common_image_layout_from_d3d12_desc(const struct d3d12_device *device,
const D3D12_RESOURCE_DESC1 *desc)
{
/* We need aggressive decay and promotion into anything. */
if (desc->Flags & D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS)
return VK_IMAGE_LAYOUT_GENERAL;
if (desc->Layout == D3D12_TEXTURE_LAYOUT_ROW_MAJOR)
return VK_IMAGE_LAYOUT_GENERAL;
/* This is counter-intuitive, but using GENERAL layout for depth-stencils works around
* having to perform DSV plane tracking all the time, since we don't necessarily know at recording time
* if a DSV image is OPTIMAL or READ_ONLY.
* This saves us many redundant barriers while rendering, especially since games tend
* to split their rendering across many command lists in parallel.
* On several implementations, GENERAL is a perfectly fine layout to use,
* on others it is a disaster since compression is disabled :') */
if (((desc->Flags & (D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL | D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE)) ==
D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL) &&
d3d12_device_prefers_general_depth_stencil(device))
{
return VK_IMAGE_LAYOUT_GENERAL;
}
/* DENY_SHADER_RESOURCE only allowed with ALLOW_DEPTH_STENCIL */
if (desc->Flags & D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE)
return VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
if (desc->Flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL)
return VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL;
return VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
}
static bool vkd3d_sparse_image_may_have_mip_tail(const D3D12_RESOURCE_DESC1 *desc,
const VkSparseImageFormatProperties *sparse_info)
{
VkExtent3D mip_extent, block_extent = sparse_info->imageGranularity;
unsigned int mip_level;
/* probe smallest mip level in the image */
mip_level = desc->MipLevels - 1;
mip_extent.width = d3d12_resource_desc_get_width(desc, mip_level);
mip_extent.height = d3d12_resource_desc_get_height(desc, mip_level);
mip_extent.depth = d3d12_resource_desc_get_depth(desc, mip_level);
if (sparse_info->flags & VK_SPARSE_IMAGE_FORMAT_ALIGNED_MIP_SIZE_BIT)
{
return mip_extent.width % block_extent.width ||
mip_extent.height % block_extent.height ||
mip_extent.depth % block_extent.depth;
}
return mip_extent.width < block_extent.width ||
mip_extent.height < block_extent.height ||
mip_extent.depth < block_extent.depth;
}
static bool vkd3d_resource_can_be_vrs(struct d3d12_device *device,
const D3D12_HEAP_PROPERTIES *heap_properties, const D3D12_RESOURCE_DESC1 *desc)
{
return device->device_info.fragment_shading_rate_features.attachmentFragmentShadingRate &&
desc->Format == DXGI_FORMAT_R8_UINT &&
desc->Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE2D &&
desc->MipLevels == 1 &&
desc->SampleDesc.Count == 1 &&
desc->SampleDesc.Quality == 0 &&
desc->Layout == D3D12_TEXTURE_LAYOUT_UNKNOWN &&
heap_properties &&
!is_cpu_accessible_heap(heap_properties) &&
!(desc->Flags & (D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET |
D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL |
D3D12_RESOURCE_FLAG_ALLOW_CROSS_ADAPTER |
D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS |
D3D12_RESOURCE_FLAG_VIDEO_DECODE_REFERENCE_ONLY));
}
static HRESULT vkd3d_resource_make_vrs_view(struct d3d12_device *device,
VkImage image, VkImageView* view)
{
const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
VkImageViewCreateInfo view_info;
VkResult vr;
view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
view_info.pNext = NULL;
view_info.flags = 0;
view_info.image = image;
view_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
view_info.format = VK_FORMAT_R8_UINT;
view_info.components = (VkComponentMapping) {
VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY,
VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY
};
view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
view_info.subresourceRange.baseMipLevel = 0;
view_info.subresourceRange.levelCount = 1;
view_info.subresourceRange.baseArrayLayer = 0;
view_info.subresourceRange.layerCount = 1;
if ((vr = VK_CALL(vkCreateImageView(device->vk_device, &view_info, NULL, view))) < 0)
ERR("Failed to create implicit VRS view, vr %d.\n", vr);
return hresult_from_vk_result(vr);
}
static bool vkd3d_format_allows_shader_copies(DXGI_FORMAT dxgi_format)
{
unsigned int i;
static const DXGI_FORMAT shader_copy_formats[] = {
DXGI_FORMAT_D32_FLOAT,
DXGI_FORMAT_D32_FLOAT_S8X24_UINT,
DXGI_FORMAT_D16_UNORM,
DXGI_FORMAT_R32_TYPELESS,
DXGI_FORMAT_R32_FLOAT,
DXGI_FORMAT_R32_UINT,
DXGI_FORMAT_R32_SINT,
DXGI_FORMAT_R16_TYPELESS,
DXGI_FORMAT_R16_FLOAT,
DXGI_FORMAT_R16_UINT,
DXGI_FORMAT_R16_SINT,
DXGI_FORMAT_R16_UNORM,
DXGI_FORMAT_R16_SNORM,
DXGI_FORMAT_R8_TYPELESS,
DXGI_FORMAT_R8_UINT,
DXGI_FORMAT_R8_SINT,
DXGI_FORMAT_R8_UNORM,
DXGI_FORMAT_R8_SNORM,
DXGI_FORMAT_A8_UNORM,
};
for (i = 0; i < ARRAY_SIZE(shader_copy_formats); i++)
{
if (dxgi_format == shader_copy_formats[i])
return true;
}
return false;
}
static bool vkd3d_format_check_usage_support(struct d3d12_device *device, VkFormat format, VkImageUsageFlags usage, VkImageTiling tiling)
{
const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
VkFormatFeatureFlags required_flags, supported_flags;
VkFormatProperties format_properties;
VK_CALL(vkGetPhysicalDeviceFormatProperties(device->vk_physical_device, format, &format_properties));
supported_flags = tiling == VK_IMAGE_TILING_LINEAR
? format_properties.linearTilingFeatures
: format_properties.optimalTilingFeatures;
required_flags = 0;
if (usage & VK_IMAGE_USAGE_SAMPLED_BIT)
required_flags |= VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT;
if (usage & VK_IMAGE_USAGE_STORAGE_BIT)
required_flags |= VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT;
if (usage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)
required_flags |= VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT;
if (usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)
required_flags |= VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT;
if (usage & VK_IMAGE_USAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR)
required_flags |= VK_FORMAT_FEATURE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR;
return (supported_flags & required_flags) == required_flags;
}
struct vkd3d_image_create_info
{
struct vkd3d_format_compatibility_list format_compat_list;
VkImageFormatListCreateInfoKHR format_list;
VkImageCreateInfo image_info;
};
static HRESULT vkd3d_get_image_create_info(struct d3d12_device *device,
const D3D12_HEAP_PROPERTIES *heap_properties, D3D12_HEAP_FLAGS heap_flags,
const D3D12_RESOURCE_DESC1 *desc, struct d3d12_resource *resource,
struct vkd3d_image_create_info *create_info)
{
struct vkd3d_format_compatibility_list *compat_list = &create_info->format_compat_list;
VkImageFormatListCreateInfoKHR *format_list = &create_info->format_list;
const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
VkImageCreateInfo *image_info = &create_info->image_info;
const bool sparse_resource = !heap_properties;
const struct vkd3d_format *format;
bool use_concurrent;
unsigned int i;
if (!resource)
{
if (!(format = vkd3d_format_from_d3d12_resource_desc(device, desc, 0)))
{
WARN("Invalid DXGI format %#x.\n", desc->Format);
return E_INVALIDARG;
}
}
else
{
format = resource->format;
}
image_info->sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
image_info->pNext = NULL;
image_info->flags = 0;
if (!(desc->Flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL))
{
if (vkd3d_get_format_compatibility_list(device, desc, compat_list))
{
format_list->sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO_KHR;
format_list->pNext = NULL;
format_list->viewFormatCount = compat_list->format_count;
format_list->pViewFormats = compat_list->vk_formats;
image_info->pNext = format_list;
image_info->flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
}
}
if (desc->Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE2D
&& desc->Width == desc->Height && desc->DepthOrArraySize >= 6
&& desc->SampleDesc.Count == 1)
image_info->flags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
if (sparse_resource)
{
image_info->flags |= VK_IMAGE_CREATE_SPARSE_BINDING_BIT |
VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT |
VK_IMAGE_CREATE_SPARSE_ALIASED_BIT;
if (desc->Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE1D)
{
WARN("Tiled 1D textures not supported.\n");
return E_INVALIDARG;
}
if (desc->Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE3D &&
device->d3d12_caps.options.TiledResourcesTier < D3D12_TILED_RESOURCES_TIER_3)
{
WARN("Tiled 3D textures not supported by device.\n");
return E_INVALIDARG;
}
if (!is_power_of_two(format->vk_aspect_mask))
{
WARN("Multi-planar format %u not supported for tiled resources.\n", desc->Format);
return E_INVALIDARG;
}
}
image_info->imageType = vk_image_type_from_d3d12_resource_dimension(desc->Dimension);
image_info->format = format->vk_format;
image_info->extent.width = desc->Width;
image_info->extent.height = desc->Height;
if (desc->Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE3D)
{
image_info->extent.depth = desc->DepthOrArraySize;
image_info->arrayLayers = 1;
}
else
{
image_info->extent.depth = 1;
image_info->arrayLayers = desc->DepthOrArraySize;
}
image_info->mipLevels = min(desc->MipLevels, max_miplevel_count(desc));
image_info->samples = vk_samples_from_dxgi_sample_desc(&desc->SampleDesc);
if (sparse_resource)
{
if (desc->Layout != D3D12_TEXTURE_LAYOUT_64KB_UNDEFINED_SWIZZLE)
{
WARN("D3D12_TEXTURE_LAYOUT_64KB_UNDEFINED_SWIZZLE must be used for reserved texture.\n");
return E_INVALIDARG;
}
image_info->tiling = VK_IMAGE_TILING_OPTIMAL;
}
else if (desc->Layout == D3D12_TEXTURE_LAYOUT_UNKNOWN || desc->Layout == D3D12_TEXTURE_LAYOUT_64KB_UNDEFINED_SWIZZLE)
{
image_info->tiling = VK_IMAGE_TILING_OPTIMAL;
}
else if (desc->Layout == D3D12_TEXTURE_LAYOUT_ROW_MAJOR)
{
image_info->tiling = VK_IMAGE_TILING_LINEAR;
}
else
{
FIXME("Unsupported layout %#x.\n", desc->Layout);
return E_NOTIMPL;
}
image_info->usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
if (desc->Flags & D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET)
image_info->usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
if (desc->Flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL)
image_info->usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
if (desc->Flags & D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS)
image_info->usage |= VK_IMAGE_USAGE_STORAGE_BIT;
if (!(desc->Flags & D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE))
image_info->usage |= VK_IMAGE_USAGE_SAMPLED_BIT;
/* Additional usage flags for shader-based copies */
if (vkd3d_format_allows_shader_copies(format->dxgi_format))
{
image_info->usage |= (format->vk_aspect_mask & VK_IMAGE_ASPECT_DEPTH_BIT)
? VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT
: VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
}
if (vkd3d_resource_can_be_vrs(device, heap_properties, desc))
image_info->usage |= VK_IMAGE_USAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR;
/* Additional image flags as necessary */
if (image_info->imageType == VK_IMAGE_TYPE_3D &&
(image_info->usage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT))
image_info->flags |= VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT;
use_concurrent = !!(device->unique_queue_mask & (device->unique_queue_mask - 1));
if (!(desc->Flags & D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS))
{
/* Ignore config flags for actual simultaneous access cases. */
if (((desc->Flags & D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET) &&
(vkd3d_config_flags & VKD3D_CONFIG_FLAG_FORCE_RTV_EXCLUSIVE_QUEUE)) ||
((desc->Flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL) &&
(vkd3d_config_flags & VKD3D_CONFIG_FLAG_FORCE_DSV_EXCLUSIVE_QUEUE)))
{
use_concurrent = false;
}
}
if (use_concurrent)
{
/* For multi-queue, we have to use CONCURRENT since D3D does
* not give us enough information to do ownership transfers. */
image_info->sharingMode = VK_SHARING_MODE_CONCURRENT;
image_info->queueFamilyIndexCount = device->queue_family_count;
image_info->pQueueFamilyIndices = device->queue_family_indices;
}
else
{
image_info->sharingMode = VK_SHARING_MODE_EXCLUSIVE;
image_info->queueFamilyIndexCount = 0;
image_info->pQueueFamilyIndices = NULL;
}
if (heap_properties && is_cpu_accessible_heap(heap_properties))
{
image_info->initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
/* Required for ReadFromSubresource(). */
image_info->tiling = VK_IMAGE_TILING_LINEAR;
if ((vkd3d_config_flags & VKD3D_CONFIG_FLAG_IGNORE_RTV_HOST_VISIBLE) &&
(image_info->usage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT))
{
WARN("Workaround applied. Ignoring RTV on linear resources.\n");
image_info->usage &= ~VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
if (resource)
resource->desc.Flags &= ~D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
}
}
else
{
image_info->initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
}
if ((image_info->flags & VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT) &&
!vkd3d_format_check_usage_support(device, format->vk_format, image_info->usage, image_info->tiling))
image_info->flags |= VK_IMAGE_CREATE_EXTENDED_USAGE_BIT;
if (image_info->tiling == VK_IMAGE_TILING_LINEAR)
{
bool supported = vkd3d_is_linear_tiling_supported(device, image_info);
/* Apparently NV drivers do not support EXTENDED_USAGE_BIT on linear images? */
if (!supported && (image_info->flags & VK_IMAGE_CREATE_EXTENDED_USAGE_BIT))
{
WARN("Linear image not supported, attempting without EXTENDED_USAGE as a workaround ...\n");
image_info->flags &= ~VK_IMAGE_CREATE_EXTENDED_USAGE_BIT;
supported = vkd3d_is_linear_tiling_supported(device, image_info);
}
if (!supported)
{
WARN("Linear image not supported, forcing OPTIMAL tiling ...\n");
image_info->tiling = VK_IMAGE_TILING_OPTIMAL;
if ((image_info->flags & VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT) &&
!vkd3d_format_check_usage_support(device, format->vk_format, image_info->usage, image_info->tiling))
image_info->flags |= VK_IMAGE_CREATE_EXTENDED_USAGE_BIT;
}
}
if (sparse_resource)
{
VkSparseImageFormatProperties sparse_infos[2];
uint32_t sparse_info_count = ARRAY_SIZE(sparse_infos);
// D3D12 only allows sparse images with one aspect, so we can only
// get one struct for metadata aspect and one for the data aspect
VK_CALL(vkGetPhysicalDeviceSparseImageFormatProperties(
device->vk_physical_device, image_info->format,
image_info->imageType, image_info->samples, image_info->usage,
image_info->tiling, &sparse_info_count, sparse_infos));
if (!sparse_info_count)
{
ERR("Sparse images not supported with format %u, type %u, samples %u, usage %#x, tiling %u.\n",
image_info->format, image_info->imageType, image_info->samples, image_info->usage, image_info->tiling);
return E_INVALIDARG;
}
for (i = 0; i < sparse_info_count; i++)
{
if (sparse_infos[i].aspectMask & VK_IMAGE_ASPECT_METADATA_BIT)
continue;
if (vkd3d_sparse_image_may_have_mip_tail(desc, &sparse_infos[i]) && desc->DepthOrArraySize > 1 && desc->MipLevels > 1)
{
WARN("Multiple array layers not supported for sparse images with mip tail.\n");
return E_INVALIDARG;
}
}
}
if (resource)
{
if (image_info->tiling == VK_IMAGE_TILING_LINEAR)
{
resource->flags |= VKD3D_RESOURCE_LINEAR_TILING;
resource->common_layout = VK_IMAGE_LAYOUT_GENERAL;
}
else
resource->common_layout = vk_common_image_layout_from_d3d12_desc(device, desc);
if (desc->Flags & D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS)
resource->flags |= VKD3D_RESOURCE_SIMULTANEOUS_ACCESS;
}
return S_OK;
}
static HRESULT vkd3d_create_image(struct d3d12_device *device,
const D3D12_HEAP_PROPERTIES *heap_properties, D3D12_HEAP_FLAGS heap_flags,
const D3D12_RESOURCE_DESC1 *desc, struct d3d12_resource *resource, VkImage *vk_image)
{
const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
struct vkd3d_image_create_info create_info;
VkResult vr;
HRESULT hr;
if (FAILED(hr = vkd3d_get_image_create_info(device, heap_properties,
heap_flags, desc, resource, &create_info)))
return hr;
if ((vr = VK_CALL(vkCreateImage(device->vk_device, &create_info.image_info, NULL, vk_image))) < 0)
WARN("Failed to create Vulkan image, vr %d.\n", vr);
return hresult_from_vk_result(vr);
}
HRESULT vkd3d_get_image_allocation_info(struct d3d12_device *device,
const D3D12_RESOURCE_DESC1 *desc, D3D12_RESOURCE_ALLOCATION_INFO *allocation_info)
{
static const D3D12_HEAP_PROPERTIES heap_properties = {D3D12_HEAP_TYPE_DEFAULT};
const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
VkDeviceImageMemoryRequirementsKHR requirement_info;
struct vkd3d_image_create_info create_info;
D3D12_RESOURCE_DESC1 validated_desc;
VkMemoryRequirements2 requirements;
VkDeviceSize target_alignment;
HRESULT hr;
assert(desc->Dimension != D3D12_RESOURCE_DIMENSION_BUFFER);
assert(d3d12_resource_validate_desc(desc, device) == S_OK);
if (!desc->MipLevels)
{
validated_desc = *desc;
validated_desc.MipLevels = max_miplevel_count(desc);
desc = &validated_desc;
}
if (FAILED(hr = vkd3d_get_image_create_info(device, &heap_properties, 0, desc, NULL, &create_info)))
return hr;
requirement_info.sType = VK_STRUCTURE_TYPE_DEVICE_IMAGE_MEMORY_REQUIREMENTS_KHR;
requirement_info.pNext = NULL;
requirement_info.pCreateInfo = &create_info.image_info;
requirement_info.planeAspect = 0; /* irrelevant for us */
requirements.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2;
requirements.pNext = NULL;
VK_CALL(vkGetDeviceImageMemoryRequirementsKHR(device->vk_device, &requirement_info, &requirements));
allocation_info->SizeInBytes = requirements.memoryRequirements.size;
allocation_info->Alignment = requirements.memoryRequirements.alignment;
/* Do not report alignments greater than DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT
* since that might confuse apps. Instead, pad the allocation so that we can
* align the image ourselves. */
target_alignment = desc->Alignment ? desc->Alignment : D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;
if (allocation_info->Alignment > target_alignment)
{
allocation_info->SizeInBytes += allocation_info->Alignment - target_alignment;
allocation_info->Alignment = target_alignment;
}
return hr;
}
struct vkd3d_view_entry
{
struct hash_map_entry entry;
struct vkd3d_view_key key;
struct vkd3d_view *view;
};
static bool d3d12_sampler_needs_border_color(D3D12_TEXTURE_ADDRESS_MODE u,
D3D12_TEXTURE_ADDRESS_MODE v, D3D12_TEXTURE_ADDRESS_MODE w);
static uint32_t vkd3d_view_entry_hash(const void *key)
{
const struct vkd3d_view_key *k = key;
uint32_t hash;
switch (k->view_type)
{
case VKD3D_VIEW_TYPE_BUFFER:
case VKD3D_VIEW_TYPE_ACCELERATION_STRUCTURE:
hash = hash_uint64((uint64_t)k->u.buffer.buffer);
hash = hash_combine(hash, hash_uint64(k->u.buffer.offset));
hash = hash_combine(hash, hash_uint64(k->u.buffer.size));
hash = hash_combine(hash, (uintptr_t)k->u.buffer.format);
break;
case VKD3D_VIEW_TYPE_IMAGE:
hash = hash_uint64((uint64_t)k->u.texture.image);
hash = hash_combine(hash, k->u.texture.view_type);
hash = hash_combine(hash, (uintptr_t)k->u.texture.format);
hash = hash_combine(hash, k->u.texture.miplevel_idx);
hash = hash_combine(hash, k->u.texture.miplevel_count);
hash = hash_combine(hash, float_bits_to_uint32(k->u.texture.miplevel_clamp));
hash = hash_combine(hash, k->u.texture.layer_idx);
hash = hash_combine(hash, k->u.texture.layer_count);
hash = hash_combine(hash, k->u.texture.components.r);
hash = hash_combine(hash, k->u.texture.components.g);
hash = hash_combine(hash, k->u.texture.components.b);
hash = hash_combine(hash, k->u.texture.components.a);
hash = hash_combine(hash, k->u.texture.image_usage);
hash = hash_combine(hash, k->u.texture.allowed_swizzle);
break;
case VKD3D_VIEW_TYPE_SAMPLER:
hash = (uint32_t)k->u.sampler.Filter;
hash = hash_combine(hash, (uint32_t)k->u.sampler.AddressU);
hash = hash_combine(hash, (uint32_t)k->u.sampler.AddressV);
hash = hash_combine(hash, (uint32_t)k->u.sampler.AddressW);
hash = hash_combine(hash, float_bits_to_uint32(k->u.sampler.MipLODBias));
hash = hash_combine(hash, (uint32_t)k->u.sampler.MaxAnisotropy);
hash = hash_combine(hash, (uint32_t)k->u.sampler.ComparisonFunc);
if (d3d12_sampler_needs_border_color(k->u.sampler.AddressU, k->u.sampler.AddressV, k->u.sampler.AddressW))
{
hash = hash_combine(hash, float_bits_to_uint32(k->u.sampler.BorderColor[0]));
hash = hash_combine(hash, float_bits_to_uint32(k->u.sampler.BorderColor[1]));
hash = hash_combine(hash, float_bits_to_uint32(k->u.sampler.BorderColor[2]));
hash = hash_combine(hash, float_bits_to_uint32(k->u.sampler.BorderColor[3]));
}
hash = hash_combine(hash, float_bits_to_uint32(k->u.sampler.MinLOD));
hash = hash_combine(hash, float_bits_to_uint32(k->u.sampler.MaxLOD));
break;
default:
ERR("Unexpected view type %d.\n", k->view_type);
return 0;
}
return hash;
}
static bool vkd3d_view_entry_compare(const void *key, const struct hash_map_entry *entry)
{
const struct vkd3d_view_entry *e = (const struct vkd3d_view_entry*) entry;
const struct vkd3d_view_key *k = key;
if (k->view_type != e->key.view_type)
return false;
switch (k->view_type)
{
case VKD3D_VIEW_TYPE_BUFFER:
case VKD3D_VIEW_TYPE_ACCELERATION_STRUCTURE:
return k->u.buffer.buffer == e->key.u.buffer.buffer &&
k->u.buffer.format == e->key.u.buffer.format &&
k->u.buffer.offset == e->key.u.buffer.offset &&
k->u.buffer.size == e->key.u.buffer.size;
case VKD3D_VIEW_TYPE_IMAGE:
return k->u.texture.image == e->key.u.texture.image &&
k->u.texture.view_type == e->key.u.texture.view_type &&
k->u.texture.format == e->key.u.texture.format &&
k->u.texture.miplevel_idx == e->key.u.texture.miplevel_idx &&
k->u.texture.miplevel_count == e->key.u.texture.miplevel_count &&
k->u.texture.miplevel_clamp == e->key.u.texture.miplevel_clamp &&
k->u.texture.layer_idx == e->key.u.texture.layer_idx &&
k->u.texture.layer_count == e->key.u.texture.layer_count &&
k->u.texture.components.r == e->key.u.texture.components.r &&
k->u.texture.components.g == e->key.u.texture.components.g &&
k->u.texture.components.b == e->key.u.texture.components.b &&
k->u.texture.components.a == e->key.u.texture.components.a &&
k->u.texture.image_usage == e->key.u.texture.image_usage &&
k->u.texture.allowed_swizzle == e->key.u.texture.allowed_swizzle;
case VKD3D_VIEW_TYPE_SAMPLER:
return k->u.sampler.Filter == e->key.u.sampler.Filter &&
k->u.sampler.AddressU == e->key.u.sampler.AddressU &&
k->u.sampler.AddressV == e->key.u.sampler.AddressV &&
k->u.sampler.AddressW == e->key.u.sampler.AddressW &&
k->u.sampler.MipLODBias == e->key.u.sampler.MipLODBias &&
k->u.sampler.MaxAnisotropy == e->key.u.sampler.MaxAnisotropy &&
k->u.sampler.ComparisonFunc == e->key.u.sampler.ComparisonFunc &&
(!d3d12_sampler_needs_border_color(k->u.sampler.AddressU, k->u.sampler.AddressV, k->u.sampler.AddressW) ||
(k->u.sampler.BorderColor[0] == e->key.u.sampler.BorderColor[0] &&
k->u.sampler.BorderColor[1] == e->key.u.sampler.BorderColor[1] &&
k->u.sampler.BorderColor[2] == e->key.u.sampler.BorderColor[2] &&
k->u.sampler.BorderColor[3] == e->key.u.sampler.BorderColor[3])) &&
k->u.sampler.MinLOD == e->key.u.sampler.MinLOD &&
k->u.sampler.MaxLOD == e->key.u.sampler.MaxLOD;
break;
default:
ERR("Unexpected view type %d.\n", k->view_type);
return false;
}
}
HRESULT vkd3d_view_map_init(struct vkd3d_view_map *view_map)
{
view_map->spinlock = 0;
hash_map_init(&view_map->map, &vkd3d_view_entry_hash, &vkd3d_view_entry_compare, sizeof(struct vkd3d_view_entry));
return S_OK;
}
static void vkd3d_view_destroy(struct vkd3d_view *view, struct d3d12_device *device);
void vkd3d_view_map_destroy(struct vkd3d_view_map *view_map, struct d3d12_device *device)
{
uint32_t i;
for (i = 0; i < view_map->map.entry_count; i++)
{
struct vkd3d_view_entry *e = (struct vkd3d_view_entry *)hash_map_get_entry(&view_map->map, i);
if (e->entry.flags & HASH_MAP_ENTRY_OCCUPIED)
vkd3d_view_destroy(e->view, device);
}
hash_map_clear(&view_map->map);
}
static struct vkd3d_view *vkd3d_view_create(enum vkd3d_view_type type);
static HRESULT d3d12_create_sampler(struct d3d12_device *device,
const D3D12_SAMPLER_DESC *desc, VkSampler *vk_sampler);
struct vkd3d_view *vkd3d_view_map_create_view(struct vkd3d_view_map *view_map,
struct d3d12_device *device, const struct vkd3d_view_key *key)
{
struct vkd3d_view_entry entry, *e;
struct vkd3d_view *redundant_view;
struct vkd3d_view *view;
bool success;
/* In the steady state, we will be reading existing entries from a view map.
* Prefer read-write spinlocks here to reduce contention as much as possible. */
rw_spinlock_acquire_read(&view_map->spinlock);
if ((e = (struct vkd3d_view_entry *)hash_map_find(&view_map->map, key)))
{
view = e->view;
rw_spinlock_release_read(&view_map->spinlock);
return view;
}
rw_spinlock_release_read(&view_map->spinlock);
switch (key->view_type)
{
case VKD3D_VIEW_TYPE_BUFFER:
success = vkd3d_create_buffer_view(device, &key->u.buffer, &view);
break;
case VKD3D_VIEW_TYPE_IMAGE:
success = vkd3d_create_texture_view(device, &key->u.texture, &view);
break;
case VKD3D_VIEW_TYPE_SAMPLER:
success = (view = vkd3d_view_create(VKD3D_VIEW_TYPE_SAMPLER)) &&
SUCCEEDED(d3d12_create_sampler(device, &key->u.sampler, &view->vk_sampler));
break;
case VKD3D_VIEW_TYPE_ACCELERATION_STRUCTURE:
success = vkd3d_create_acceleration_structure_view(device, &key->u.buffer, &view);
break;
default:
ERR("Unsupported view type %u.\n", key->view_type);
success = false;
break;
}
if (!success)
return NULL;
vkd3d_descriptor_debug_register_view_cookie(device->descriptor_qa_global_info,
view->cookie, view_map->resource_cookie);
entry.key = *key;
entry.view = view;
rw_spinlock_acquire_write(&view_map->spinlock);
if (!(e = (struct vkd3d_view_entry *)hash_map_insert(&view_map->map, key, &entry.entry)))
ERR("Failed to insert view into hash map.\n");
if (e->view != view)
{
/* We yielded on the insert because another thread came in-between, and allocated a new hash map entry.
* This can happen between releasing reader lock, and acquiring writer lock. */
redundant_view = view;
view = e->view;
rw_spinlock_release_write(&view_map->spinlock);
vkd3d_view_decref(redundant_view, device);
}
else
{
/* If we start emitting too many typed SRVs, we will eventually crash on NV, since
* VkBufferView objects appear to consume GPU resources. */
if ((view_map->map.used_count % 1024) == 0)
ERR("Intense view map pressure! Got %u views in hash map %p.\n", view_map->map.used_count, &view_map->map);
view = e->view;
rw_spinlock_release_write(&view_map->spinlock);
}
return view;
}
struct vkd3d_sampler_key
{
D3D12_STATIC_SAMPLER_DESC desc;
};
struct vkd3d_sampler_entry
{
struct hash_map_entry entry;
D3D12_STATIC_SAMPLER_DESC desc;
VkSampler vk_sampler;
};
static uint32_t vkd3d_sampler_entry_hash(const void *key)
{
const struct vkd3d_sampler_key *k = key;
uint32_t hash;
hash = (uint32_t)k->desc.Filter;
hash = hash_combine(hash, (uint32_t)k->desc.AddressU);
hash = hash_combine(hash, (uint32_t)k->desc.AddressV);
hash = hash_combine(hash, (uint32_t)k->desc.AddressW);
hash = hash_combine(hash, float_bits_to_uint32(k->desc.MipLODBias));
hash = hash_combine(hash, k->desc.MaxAnisotropy);
hash = hash_combine(hash, (uint32_t)k->desc.ComparisonFunc);
hash = hash_combine(hash, (uint32_t)k->desc.BorderColor);
hash = hash_combine(hash, float_bits_to_uint32(k->desc.MinLOD));
hash = hash_combine(hash, float_bits_to_uint32(k->desc.MaxLOD));
return hash;
}
static bool vkd3d_sampler_entry_compare(const void *key, const struct hash_map_entry *entry)
{
const struct vkd3d_sampler_entry *e = (const struct vkd3d_sampler_entry*) entry;
const struct vkd3d_sampler_key *k = key;
return k->desc.Filter == e->desc.Filter &&
k->desc.AddressU == e->desc.AddressU &&
k->desc.AddressV == e->desc.AddressV &&
k->desc.AddressW == e->desc.AddressW &&
k->desc.MipLODBias == e->desc.MipLODBias &&
k->desc.MaxAnisotropy == e->desc.MaxAnisotropy &&
k->desc.ComparisonFunc == e->desc.ComparisonFunc &&
k->desc.BorderColor == e->desc.BorderColor &&
k->desc.MinLOD == e->desc.MinLOD &&
k->desc.MaxLOD == e->desc.MaxLOD;
}
HRESULT vkd3d_sampler_state_init(struct vkd3d_sampler_state *state,
struct d3d12_device *device)
{
int rc;
memset(state, 0, sizeof(*state));
if ((rc = pthread_mutex_init(&state->mutex, NULL)))
return hresult_from_errno(rc);
hash_map_init(&state->map, &vkd3d_sampler_entry_hash, &vkd3d_sampler_entry_compare, sizeof(struct vkd3d_sampler_entry));
return S_OK;
}
void vkd3d_sampler_state_cleanup(struct vkd3d_sampler_state *state,
struct d3d12_device *device)
{
const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
uint32_t i;
for (i = 0; i < state->vk_descriptor_pool_count; i++)
VK_CALL(vkDestroyDescriptorPool(device->vk_device, state->vk_descriptor_pools[i], NULL));
vkd3d_free(state->vk_descriptor_pools);
for (i = 0; i < state->map.entry_count; i++)
{
struct vkd3d_sampler_entry *e = (struct vkd3d_sampler_entry *)hash_map_get_entry(&state->map, i);
if (e->entry.flags & HASH_MAP_ENTRY_OCCUPIED)
VK_CALL(vkDestroySampler(device->vk_device, e->vk_sampler, NULL));
}
hash_map_clear(&state->map);
pthread_mutex_destroy(&state->mutex);
}
HRESULT d3d12_create_static_sampler(struct d3d12_device *device,
const D3D12_STATIC_SAMPLER_DESC *desc, VkSampler *vk_sampler);
HRESULT vkd3d_sampler_state_create_static_sampler(struct vkd3d_sampler_state *state,
struct d3d12_device *device, const D3D12_STATIC_SAMPLER_DESC *desc, VkSampler *vk_sampler)
{
struct vkd3d_sampler_entry entry, *e;
HRESULT hr;
int rc;
if ((rc = pthread_mutex_lock(&state->mutex)))
{
ERR("Failed to lock mutex, rc %d.\n", rc);
return hresult_from_errno(rc);
}
if ((e = (struct vkd3d_sampler_entry*)hash_map_find(&state->map, desc)))
{
*vk_sampler = e->vk_sampler;
pthread_mutex_unlock(&state->mutex);
return S_OK;
}
if (FAILED(hr = d3d12_create_static_sampler(device, desc, vk_sampler)))
{
pthread_mutex_unlock(&state->mutex);
return hr;
}
entry.desc = *desc;
entry.vk_sampler = *vk_sampler;
if (!hash_map_insert(&state->map, desc, &entry.entry))
ERR("Failed to insert sampler into hash map.\n");
pthread_mutex_unlock(&state->mutex);
return S_OK;
}
static VkResult vkd3d_sampler_state_create_descriptor_pool(struct d3d12_device *device, VkDescriptorPool *vk_pool)
{
const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
VkDescriptorPoolCreateInfo pool_info;
VkDescriptorPoolSize pool_size;
pool_size.type = VK_DESCRIPTOR_TYPE_SAMPLER;
pool_size.descriptorCount = 16384;
pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
pool_info.pNext = NULL;
pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
pool_info.maxSets = 4096;
pool_info.poolSizeCount = 1;
pool_info.pPoolSizes = &pool_size;
return VK_CALL(vkCreateDescriptorPool(device->vk_device, &pool_info, NULL, vk_pool));
}
HRESULT vkd3d_sampler_state_allocate_descriptor_set(struct vkd3d_sampler_state *state,
struct d3d12_device *device, VkDescriptorSetLayout vk_layout, VkDescriptorSet *vk_set,
VkDescriptorPool *vk_pool)
{
const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
VkResult vr = VK_ERROR_OUT_OF_POOL_MEMORY;
VkDescriptorSetAllocateInfo alloc_info;
size_t i;
int rc;
if ((rc = pthread_mutex_lock(&state->mutex)))
{
ERR("Failed to lock mutex, rc %d.\n", rc);
return hresult_from_errno(rc);
}
alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
alloc_info.pNext = NULL;
alloc_info.descriptorSetCount = 1;
alloc_info.pSetLayouts = &vk_layout;
for (i = 0; i < state->vk_descriptor_pool_count; i++)
{
alloc_info.descriptorPool = state->vk_descriptor_pools[i];
vr = VK_CALL(vkAllocateDescriptorSets(device->vk_device, &alloc_info, vk_set));
if (vr == VK_SUCCESS)
{
*vk_pool = alloc_info.descriptorPool;
break;
}
}
if (vr == VK_ERROR_OUT_OF_POOL_MEMORY || vr == VK_ERROR_FRAGMENTED_POOL)
{
vr = vkd3d_sampler_state_create_descriptor_pool(device, &alloc_info.descriptorPool);
if (vr != VK_SUCCESS)
{
pthread_mutex_unlock(&state->mutex);
return hresult_from_vk_result(vr);
}
if (!vkd3d_array_reserve((void **)&state->vk_descriptor_pools, &state->vk_descriptor_pools_size,
state->vk_descriptor_pool_count + 1, sizeof(*state->vk_descriptor_pools)))
{
VK_CALL(vkDestroyDescriptorPool(device->vk_device, alloc_info.descriptorPool, NULL));
pthread_mutex_unlock(&state->mutex);
return E_OUTOFMEMORY;
}
state->vk_descriptor_pools[state->vk_descriptor_pool_count++] = alloc_info.descriptorPool;
vr = VK_CALL(vkAllocateDescriptorSets(device->vk_device, &alloc_info, vk_set));
*vk_pool = alloc_info.descriptorPool;
}
pthread_mutex_unlock(&state->mutex);
return hresult_from_vk_result(vr);
}
void vkd3d_sampler_state_free_descriptor_set(struct vkd3d_sampler_state *state,
struct d3d12_device *device, VkDescriptorSet vk_set, VkDescriptorPool vk_pool)
{
const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
int rc;
if ((rc = pthread_mutex_lock(&state->mutex)))
ERR("Failed to lock mutex, rc %d.\n", rc);
if (vk_pool && vk_set)
VK_CALL(vkFreeDescriptorSets(device->vk_device, vk_pool, 1, &vk_set));
pthread_mutex_unlock(&state->mutex);
}
static void d3d12_resource_get_tiling(struct d3d12_device *device, struct d3d12_resource *resource,
UINT *total_tile_count, D3D12_PACKED_MIP_INFO *packed_mip_info, D3D12_TILE_SHAPE *tile_shape,
D3D12_SUBRESOURCE_TILING *tilings, VkSparseImageMemoryRequirements *vk_info)
{
const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
VkSparseImageMemoryRequirements *memory_requirements = NULL;
unsigned int i, tile_count, packed_tiles, standard_mips;
const D3D12_RESOURCE_DESC1 *desc = &resource->desc;
uint32_t memory_requirement_count = 0;
VkExtent3D block_extent;
memset(vk_info, 0, sizeof(*vk_info));
if (desc->Dimension == D3D12_RESOURCE_DIMENSION_BUFFER)
{
tile_count = align(desc->Width, VKD3D_TILE_SIZE) / VKD3D_TILE_SIZE;
packed_mip_info->NumStandardMips = 0;
packed_mip_info->NumPackedMips = 0;
packed_mip_info->NumTilesForPackedMips = 0;
packed_mip_info->StartTileIndexInOverallResource = 0;
tile_shape->WidthInTexels = VKD3D_TILE_SIZE;
tile_shape->HeightInTexels = 1;
tile_shape->DepthInTexels = 1;
tilings[0].WidthInTiles = tile_count;
tilings[0].HeightInTiles = 1;
tilings[0].DepthInTiles = 1;
tilings[0].StartTileIndexInOverallResource = 0;
*total_tile_count = tile_count;
}
else
{
VK_CALL(vkGetImageSparseMemoryRequirements(device->vk_device,
resource->res.vk_image, &memory_requirement_count, NULL));
if (!memory_requirement_count)
{
ERR("Failed to query sparse memory requirements.\n");
return;
}
memory_requirements = vkd3d_malloc(memory_requirement_count * sizeof(*memory_requirements));
VK_CALL(vkGetImageSparseMemoryRequirements(device->vk_device,
resource->res.vk_image, &memory_requirement_count, memory_requirements));
for (i = 0; i < memory_requirement_count; i++)
{
if (!(memory_requirements[i].formatProperties.aspectMask & VK_IMAGE_ASPECT_METADATA_BIT))
*vk_info = memory_requirements[i];
}
vkd3d_free(memory_requirements);
/* Assume that there is no mip tail if either the size is zero or
* if the first LOD is out of range. It's not clear what drivers
* are supposed to report here if the image has no mip tail. */
standard_mips = vk_info->imageMipTailSize
? min(desc->MipLevels, vk_info->imageMipTailFirstLod)
: desc->MipLevels;
packed_tiles = standard_mips < desc->MipLevels
? align(vk_info->imageMipTailSize, VKD3D_TILE_SIZE) / VKD3D_TILE_SIZE
: 0;
if (!(vk_info->formatProperties.flags & VK_SPARSE_IMAGE_FORMAT_SINGLE_MIPTAIL_BIT))
packed_tiles *= d3d12_resource_desc_get_layer_count(desc);
block_extent = vk_info->formatProperties.imageGranularity;
tile_count = 0;
for (i = 0; i < d3d12_resource_desc_get_sub_resource_count_per_plane(desc); i++)
{
unsigned int mip_level = i % desc->MipLevels;
unsigned int tile_count_w = align(d3d12_resource_desc_get_width(desc, mip_level), block_extent.width) / block_extent.width;
unsigned int tile_count_h = align(d3d12_resource_desc_get_height(desc, mip_level), block_extent.height) / block_extent.height;
unsigned int tile_count_d = align(d3d12_resource_desc_get_depth(desc, mip_level), block_extent.depth) / block_extent.depth;
if (mip_level < standard_mips)
{
tilings[i].WidthInTiles = tile_count_w;
tilings[i].HeightInTiles = tile_count_h;
tilings[i].DepthInTiles = tile_count_d;
tilings[i].StartTileIndexInOverallResource = tile_count;
tile_count += tile_count_w * tile_count_h * tile_count_d;
}
else
{
tilings[i].WidthInTiles = 0;
tilings[i].HeightInTiles = 0;
tilings[i].DepthInTiles = 0;
tilings[i].StartTileIndexInOverallResource = ~0u;
}
}
packed_mip_info->NumStandardMips = standard_mips;
packed_mip_info->NumTilesForPackedMips = packed_tiles;
packed_mip_info->NumPackedMips = desc->MipLevels - standard_mips;
packed_mip_info->StartTileIndexInOverallResource = packed_tiles ? tile_count : 0;
tile_count += packed_tiles;
/* Docs say that we should clear tile_shape to zero if there are no standard mips,
* but this conflicts with all native drivers, so the docs are likely lying here.
* See test_get_resource_tiling() for info. */
tile_shape->WidthInTexels = block_extent.width;
tile_shape->HeightInTexels = block_extent.height;
tile_shape->DepthInTexels = block_extent.depth;
*total_tile_count = tile_count;
}
}
static void d3d12_resource_destroy(struct d3d12_resource *resource, struct d3d12_device *device);
static ULONG d3d12_resource_incref(struct d3d12_resource *resource)
{
ULONG refcount = InterlockedIncrement(&resource->internal_refcount);
TRACE("%p increasing refcount to %u.\n", resource, refcount);
return refcount;
}
static ULONG d3d12_resource_decref(struct d3d12_resource *resource)
{
ULONG refcount = InterlockedDecrement(&resource->internal_refcount);
TRACE("%p decreasing refcount to %u.\n", resource, refcount);
if (!refcount)
d3d12_resource_destroy(resource, resource->device);
return refcount;
}
bool d3d12_resource_is_cpu_accessible(const struct d3d12_resource *resource)
{
return !(resource->flags & VKD3D_RESOURCE_RESERVED) &&
is_cpu_accessible_heap(&resource->heap_properties);
}
static bool d3d12_resource_validate_box(const struct d3d12_resource *resource,
unsigned int sub_resource_idx, const D3D12_BOX *box)
{
unsigned int mip_level = sub_resource_idx % resource->desc.MipLevels;
uint32_t width_mask, height_mask;
uint64_t width, height, depth;
width = d3d12_resource_desc_get_width(&resource->desc, mip_level);
height = d3d12_resource_desc_get_height(&resource->desc, mip_level);
depth = d3d12_resource_desc_get_depth(&resource->desc, mip_level);
width_mask = resource->format->block_width - 1;
height_mask = resource->format->block_height - 1;
return box->left <= width && box->right <= width
&& box->top <= height && box->bottom <= height
&& box->front <= depth && box->back <= depth
&& !(box->left & width_mask)
&& !(box->right & width_mask)
&& !(box->top & height_mask)
&& !(box->bottom & height_mask);
}
static void d3d12_resource_get_level_box(const struct d3d12_resource *resource,
unsigned int level, D3D12_BOX *box)
{
box->left = 0;
box->top = 0;
box->front = 0;
box->right = d3d12_resource_desc_get_width(&resource->desc, level);
box->bottom = d3d12_resource_desc_get_height(&resource->desc, level);
box->back = d3d12_resource_desc_get_depth(&resource->desc, level);
}
static void d3d12_resource_set_name(struct d3d12_resource *resource, const char *name)
{
/* Multiple committed and placed buffers may refer to the same VkBuffer,
* which may cause race conditions if the app calls this concurrently */
if (d3d12_resource_is_buffer(resource) && (resource->flags & VKD3D_RESOURCE_RESERVED))
vkd3d_set_vk_object_name(resource->device, (uint64_t)resource->res.vk_buffer,
VK_OBJECT_TYPE_BUFFER, name);
else if (d3d12_resource_is_texture(resource))
vkd3d_set_vk_object_name(resource->device, (uint64_t)resource->res.vk_image,
VK_OBJECT_TYPE_IMAGE, name);
}
/* ID3D12Resource */
static HRESULT STDMETHODCALLTYPE d3d12_resource_QueryInterface(d3d12_resource_iface *iface,
REFIID riid, void **object)
{
TRACE("iface %p, riid %s, object %p.\n", iface, debugstr_guid(riid), object);
if (IsEqualGUID(riid, &IID_ID3D12Resource)
|| IsEqualGUID(riid, &IID_ID3D12Resource1)
|| IsEqualGUID(riid, &IID_ID3D12Resource2)
|| IsEqualGUID(riid, &IID_ID3D12Pageable)
|| IsEqualGUID(riid, &IID_ID3D12DeviceChild)
|| IsEqualGUID(riid, &IID_ID3D12Object)
|| IsEqualGUID(riid, &IID_IUnknown))
{
ID3D12Resource_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_resource_AddRef(d3d12_resource_iface *iface)
{
struct d3d12_resource *resource = impl_from_ID3D12Resource2(iface);
ULONG refcount = InterlockedIncrement(&resource->refcount);
TRACE("%p increasing refcount to %u.\n", resource, refcount);
if (refcount == 1)
{
struct d3d12_device *device = resource->device;
d3d12_device_add_ref(device);
d3d12_resource_incref(resource);
}
return refcount;
}
static ULONG STDMETHODCALLTYPE d3d12_resource_Release(d3d12_resource_iface *iface)
{
struct d3d12_resource *resource = impl_from_ID3D12Resource2(iface);
ULONG refcount = InterlockedDecrement(&resource->refcount);
TRACE("%p decreasing refcount to %u.\n", resource, refcount);
if (!refcount)
d3d12_resource_decref(resource);
return refcount;
}
static HRESULT STDMETHODCALLTYPE d3d12_resource_GetPrivateData(d3d12_resource_iface *iface,
REFGUID guid, UINT *data_size, void *data)
{
struct d3d12_resource *resource = impl_from_ID3D12Resource2(iface);
TRACE("iface %p, guid %s, data_size %p, data %p.\n", iface, debugstr_guid(guid), data_size, data);
return vkd3d_get_private_data(&resource->private_store, guid, data_size, data);
}
static HRESULT STDMETHODCALLTYPE d3d12_resource_SetPrivateData(d3d12_resource_iface *iface,
REFGUID guid, UINT data_size, const void *data)
{
struct d3d12_resource *resource = impl_from_ID3D12Resource2(iface);
TRACE("iface %p, guid %s, data_size %u, data %p.\n", iface, debugstr_guid(guid), data_size, data);
return vkd3d_set_private_data(&resource->private_store, guid, data_size, data,
(vkd3d_set_name_callback) d3d12_resource_set_name, resource);
}
static HRESULT STDMETHODCALLTYPE d3d12_resource_SetPrivateDataInterface(d3d12_resource_iface *iface,
REFGUID guid, const IUnknown *data)
{
struct d3d12_resource *resource = impl_from_ID3D12Resource2(iface);
TRACE("iface %p, guid %s, data %p.\n", iface, debugstr_guid(guid), data);
return vkd3d_set_private_data_interface(&resource->private_store, guid, data,
(vkd3d_set_name_callback) d3d12_resource_set_name, resource);
}
static HRESULT STDMETHODCALLTYPE d3d12_resource_GetDevice(d3d12_resource_iface *iface, REFIID iid, void **device)
{
struct d3d12_resource *resource = impl_from_ID3D12Resource2(iface);
TRACE("iface %p, iid %s, device %p.\n", iface, debugstr_guid(iid), device);
return d3d12_device_query_interface(resource->device, iid, device);
}
static bool d3d12_resource_get_mapped_memory_range(struct d3d12_resource *resource,
UINT subresource, const D3D12_RANGE *range, VkMappedMemoryRange *vk_mapped_range)
{
const struct d3d12_device *device = resource->device;
if (range && range->End <= range->Begin)
return false;
if (device->memory_properties.memoryTypes[resource->mem.device_allocation.vk_memory_type].propertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)
return false;
vk_mapped_range->sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
vk_mapped_range->pNext = NULL;
vk_mapped_range->memory = resource->mem.device_allocation.vk_memory;
if (resource->desc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER)
{
vk_mapped_range->offset = resource->mem.offset;
vk_mapped_range->size = resource->desc.Width;
}
else
{
FIXME("Not implemented for textures.\n");
return false;
}
if (range)
{
vk_mapped_range->offset += range->Begin;
vk_mapped_range->size = range->End - range->Begin;
}
return true;
}
static void d3d12_resource_invalidate_range(struct d3d12_resource *resource,
UINT subresource, const D3D12_RANGE *read_range)
{
const struct vkd3d_vk_device_procs *vk_procs = &resource->device->vk_procs;
VkMappedMemoryRange mapped_range;
if (!d3d12_resource_get_mapped_memory_range(resource, subresource, read_range, &mapped_range))
return;
VK_CALL(vkInvalidateMappedMemoryRanges(resource->device->vk_device, 1, &mapped_range));
}
static void d3d12_resource_flush_range(struct d3d12_resource *resource,
UINT subresource, const D3D12_RANGE *written_range)
{
const struct vkd3d_vk_device_procs *vk_procs = &resource->device->vk_procs;
VkMappedMemoryRange mapped_range;
if (!d3d12_resource_get_mapped_memory_range(resource, subresource, written_range, &mapped_range))
return;
VK_CALL(vkFlushMappedMemoryRanges(resource->device->vk_device, 1, &mapped_range));
}
static void d3d12_resource_get_map_ptr(struct d3d12_resource *resource, void **data)
{
assert(resource->mem.cpu_address);
*data = resource->mem.cpu_address;
}
static bool d3d12_resource_texture_validate_map(struct d3d12_resource *resource)
{
bool invalid_map;
/* Very special case that is explicitly called out in the D3D12 validation layers. */
invalid_map = resource->desc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE3D &&
resource->desc.MipLevels > 1;
return !invalid_map;
}
static HRESULT STDMETHODCALLTYPE d3d12_resource_Map(d3d12_resource_iface *iface, UINT sub_resource,
const D3D12_RANGE *read_range, void **data)
{
struct d3d12_resource *resource = impl_from_ID3D12Resource2(iface);
unsigned int sub_resource_count;
TRACE("iface %p, sub_resource %u, read_range %p, data %p.\n",
iface, sub_resource, read_range, data);
if (!d3d12_resource_is_cpu_accessible(resource))
{
WARN("Resource is not CPU accessible.\n");
return E_INVALIDARG;
}
sub_resource_count = d3d12_resource_get_sub_resource_count(resource);
if (sub_resource >= sub_resource_count)
{
WARN("Sub-resource index %u is out of range (%u sub-resources).\n", sub_resource, sub_resource_count);
return E_INVALIDARG;
}
if (d3d12_resource_is_texture(resource) && (data || !d3d12_resource_texture_validate_map(resource)))
{
/* Cannot get pointer to mapped texture.
* It is only possible to make UNKNOWN textures host visible,
* and only NULL map + Write/ReadSubresource is allowed in this scenario. */
return E_INVALIDARG;
}
if (resource->flags & VKD3D_RESOURCE_RESERVED)
{
FIXME("Not implemented for this resource type.\n");
return E_NOTIMPL;
}
if (data)
{
d3d12_resource_get_map_ptr(resource, data);
TRACE("Returning pointer %p.\n", *data);
}
d3d12_resource_invalidate_range(resource, sub_resource, read_range);
return S_OK;
}
static void STDMETHODCALLTYPE d3d12_resource_Unmap(d3d12_resource_iface *iface, UINT sub_resource,
const D3D12_RANGE *written_range)
{
struct d3d12_resource *resource = impl_from_ID3D12Resource2(iface);
unsigned int sub_resource_count;
TRACE("iface %p, sub_resource %u, written_range %p.\n",
iface, sub_resource, written_range);
sub_resource_count = d3d12_resource_get_sub_resource_count(resource);
if (sub_resource >= sub_resource_count)
{
WARN("Sub-resource index %u is out of range (%u sub-resources).\n", sub_resource, sub_resource_count);
return;
}
d3d12_resource_flush_range(resource, sub_resource, written_range);
}
static D3D12_RESOURCE_DESC * STDMETHODCALLTYPE d3d12_resource_GetDesc(d3d12_resource_iface *iface,
D3D12_RESOURCE_DESC *resource_desc)
{
struct d3d12_resource *resource = impl_from_ID3D12Resource2(iface);
TRACE("iface %p, resource_desc %p.\n", iface, resource_desc);
resource_desc->Dimension = resource->desc.Dimension;
resource_desc->Alignment = resource->desc.Alignment;
resource_desc->Width = resource->desc.Width;
resource_desc->Height = resource->desc.Height;
resource_desc->DepthOrArraySize = resource->desc.DepthOrArraySize;
resource_desc->MipLevels = resource->desc.MipLevels;
resource_desc->Format = resource->desc.Format;
resource_desc->SampleDesc = resource->desc.SampleDesc;
resource_desc->Layout = resource->desc.Layout;
resource_desc->Flags = resource->desc.Flags;
return resource_desc;
}
static D3D12_GPU_VIRTUAL_ADDRESS STDMETHODCALLTYPE d3d12_resource_GetGPUVirtualAddress(d3d12_resource_iface *iface)
{
struct d3d12_resource *resource = impl_from_ID3D12Resource2(iface);
TRACE("iface %p.\n", iface);
return resource->res.va;
}
static HRESULT STDMETHODCALLTYPE d3d12_resource_WriteToSubresource(d3d12_resource_iface *iface,
UINT dst_sub_resource, const D3D12_BOX *dst_box, const void *src_data,
UINT src_row_pitch, UINT src_slice_pitch)
{
struct d3d12_resource *resource = impl_from_ID3D12Resource2(iface);
const struct vkd3d_vk_device_procs *vk_procs;
VkImageSubresource vk_sub_resource;
VkSubresourceLayout vk_layout;
struct d3d12_device *device;
uint8_t *dst_data;
D3D12_BOX box;
TRACE("iface %p, src_data %p, src_row_pitch %u, src_slice_pitch %u, "
"dst_sub_resource %u, dst_box %s.\n",
iface, src_data, src_row_pitch, src_slice_pitch, dst_sub_resource, debug_d3d12_box(dst_box));
if (d3d12_resource_is_buffer(resource))
{
WARN("Buffers are not supported.\n");
return E_INVALIDARG;
}
device = resource->device;
vk_procs = &device->vk_procs;
if (resource->format->vk_aspect_mask != VK_IMAGE_ASPECT_COLOR_BIT)
{
FIXME("Not supported for format %#x.\n", resource->format->dxgi_format);
return E_NOTIMPL;
}
vk_sub_resource.arrayLayer = dst_sub_resource / resource->desc.MipLevels;
vk_sub_resource.mipLevel = dst_sub_resource % resource->desc.MipLevels;
vk_sub_resource.aspectMask = resource->format->vk_aspect_mask;
if (!dst_box)
{
d3d12_resource_get_level_box(resource, vk_sub_resource.mipLevel, &box);
dst_box = &box;
}
else if (!d3d12_resource_validate_box(resource, dst_sub_resource, dst_box))
{
WARN("Invalid box %s.\n", debug_d3d12_box(dst_box));
return E_INVALIDARG;
}
if (d3d12_box_is_empty(dst_box))
{
WARN("Empty box %s.\n", debug_d3d12_box(dst_box));
return S_OK;
}
if (!d3d12_resource_is_cpu_accessible(resource))
{
FIXME_ONCE("Not implemented for this resource type.\n");
return E_NOTIMPL;
}
if (!(resource->flags & VKD3D_RESOURCE_LINEAR_TILING))
{
FIXME_ONCE("Not implemented for image tiling other than VK_IMAGE_TILING_LINEAR.\n");
return E_NOTIMPL;
}
VK_CALL(vkGetImageSubresourceLayout(device->vk_device, resource->res.vk_image, &vk_sub_resource, &vk_layout));
TRACE("Offset %#"PRIx64", size %#"PRIx64", row pitch %#"PRIx64", depth pitch %#"PRIx64".\n",
vk_layout.offset, vk_layout.size, vk_layout.rowPitch, vk_layout.depthPitch);
d3d12_resource_get_map_ptr(resource, (void **)&dst_data);
dst_data += vk_layout.offset + vkd3d_format_get_data_offset(resource->format, vk_layout.rowPitch,
vk_layout.depthPitch, dst_box->left, dst_box->top, dst_box->front);
vkd3d_format_copy_data(resource->format, src_data, src_row_pitch, src_slice_pitch,
dst_data, vk_layout.rowPitch, vk_layout.depthPitch, dst_box->right - dst_box->left,
dst_box->bottom - dst_box->top, dst_box->back - dst_box->front);
return S_OK;
}
static HRESULT STDMETHODCALLTYPE d3d12_resource_ReadFromSubresource(d3d12_resource_iface *iface,
void *dst_data, UINT dst_row_pitch, UINT dst_slice_pitch,
UINT src_sub_resource, const D3D12_BOX *src_box)
{
struct d3d12_resource *resource = impl_from_ID3D12Resource2(iface);
const struct vkd3d_vk_device_procs *vk_procs;
VkImageSubresource vk_sub_resource;
VkSubresourceLayout vk_layout;
struct d3d12_device *device;
uint8_t *src_data;
D3D12_BOX box;
TRACE("iface %p, dst_data %p, dst_row_pitch %u, dst_slice_pitch %u, "
"src_sub_resource %u, src_box %s.\n",
iface, dst_data, dst_row_pitch, dst_slice_pitch, src_sub_resource, debug_d3d12_box(src_box));
if (d3d12_resource_is_buffer(resource))
{
WARN("Buffers are not supported.\n");
return E_INVALIDARG;
}
device = resource->device;
vk_procs = &device->vk_procs;
if (resource->format->vk_aspect_mask != VK_IMAGE_ASPECT_COLOR_BIT)
{
FIXME("Not supported for format %#x.\n", resource->format->dxgi_format);
return E_NOTIMPL;
}
vk_sub_resource.arrayLayer = src_sub_resource / resource->desc.MipLevels;
vk_sub_resource.mipLevel = src_sub_resource % resource->desc.MipLevels;
vk_sub_resource.aspectMask = resource->format->vk_aspect_mask;
if (!src_box)
{
d3d12_resource_get_level_box(resource, vk_sub_resource.mipLevel, &box);
src_box = &box;
}
else if (!d3d12_resource_validate_box(resource, src_sub_resource, src_box))
{
WARN("Invalid box %s.\n", debug_d3d12_box(src_box));
return E_INVALIDARG;
}
if (d3d12_box_is_empty(src_box))
{
WARN("Empty box %s.\n", debug_d3d12_box(src_box));
return S_OK;
}
if (!d3d12_resource_is_cpu_accessible(resource))
{
FIXME_ONCE("Not implemented for this resource type.\n");
return E_NOTIMPL;
}
if (!(resource->flags & VKD3D_RESOURCE_LINEAR_TILING))
{
FIXME_ONCE("Not implemented for image tiling other than VK_IMAGE_TILING_LINEAR.\n");
return E_NOTIMPL;
}
VK_CALL(vkGetImageSubresourceLayout(device->vk_device, resource->res.vk_image, &vk_sub_resource, &vk_layout));
TRACE("Offset %#"PRIx64", size %#"PRIx64", row pitch %#"PRIx64", depth pitch %#"PRIx64".\n",
vk_layout.offset, vk_layout.size, vk_layout.rowPitch, vk_layout.depthPitch);
d3d12_resource_get_map_ptr(resource, (void **)&src_data);
src_data += vk_layout.offset + vkd3d_format_get_data_offset(resource->format, vk_layout.rowPitch,
vk_layout.depthPitch, src_box->left, src_box->top, src_box->front);
vkd3d_format_copy_data(resource->format, src_data, vk_layout.rowPitch, vk_layout.depthPitch,
dst_data, dst_row_pitch, dst_slice_pitch, src_box->right - src_box->left,
src_box->bottom - src_box->top, src_box->back - src_box->front);
return S_OK;
}
static HRESULT STDMETHODCALLTYPE d3d12_resource_GetHeapProperties(d3d12_resource_iface *iface,
D3D12_HEAP_PROPERTIES *heap_properties, D3D12_HEAP_FLAGS *flags)
{
struct d3d12_resource *resource = impl_from_ID3D12Resource2(iface);
TRACE("iface %p, heap_properties %p, flags %p.\n",
iface, heap_properties, flags);
if (resource->flags & VKD3D_RESOURCE_EXTERNAL)
{
if (heap_properties)
{
memset(heap_properties, 0, sizeof(*heap_properties));
heap_properties->Type = D3D12_HEAP_TYPE_DEFAULT;
heap_properties->CreationNodeMask = 1;
heap_properties->VisibleNodeMask = 1;
}
if (flags)
*flags = D3D12_HEAP_FLAG_NONE;
return S_OK;
}
if (resource->flags & VKD3D_RESOURCE_RESERVED)
{
WARN("Cannot get heap properties for reserved resources.\n");
return E_INVALIDARG;
}
if (heap_properties)
*heap_properties = resource->heap_properties;
if (flags)
*flags = resource->heap_flags;
return S_OK;
}
static HRESULT STDMETHODCALLTYPE d3d12_resource_GetProtectedResourceSession(d3d12_resource_iface *iface,
REFIID iid, void **protected_session)
{
FIXME("iface %p, iid %s, protected_session %p stub!", iface, debugstr_guid(iid), protected_session);
return E_NOTIMPL;
}
static D3D12_RESOURCE_DESC1 * STDMETHODCALLTYPE d3d12_resource_GetDesc1(d3d12_resource_iface *iface,
D3D12_RESOURCE_DESC1 *resource_desc)
{
struct d3d12_resource *resource = impl_from_ID3D12Resource2(iface);
TRACE("iface %p, resource_desc %p.\n", iface, resource_desc);
*resource_desc = resource->desc;
return resource_desc;
}
CONST_VTBL struct ID3D12Resource2Vtbl d3d12_resource_vtbl =
{
/* IUnknown methods */
d3d12_resource_QueryInterface,
d3d12_resource_AddRef,
d3d12_resource_Release,
/* ID3D12Object methods */
d3d12_resource_GetPrivateData,
d3d12_resource_SetPrivateData,
d3d12_resource_SetPrivateDataInterface,
(void *)d3d12_object_SetName,
/* ID3D12DeviceChild methods */
d3d12_resource_GetDevice,
/* ID3D12Resource methods */
d3d12_resource_Map,
d3d12_resource_Unmap,
d3d12_resource_GetDesc,
d3d12_resource_GetGPUVirtualAddress,
d3d12_resource_WriteToSubresource,
d3d12_resource_ReadFromSubresource,
d3d12_resource_GetHeapProperties,
/* ID3D12Resource1 methods */
d3d12_resource_GetProtectedResourceSession,
/* ID3D12Resource2 methods */
d3d12_resource_GetDesc1,
};
VkImageAspectFlags vk_image_aspect_flags_from_d3d12(
const struct vkd3d_format *format, uint32_t plane_idx)
{
VkImageAspectFlags aspect_mask = format->vk_aspect_mask;
uint32_t i;
/* For all formats we currently handle, the n-th aspect bit in Vulkan
* corresponds to the n-th plane in D3D12, so isolate the respective
* bit in the aspect mask. */
for (i = 0; i < plane_idx; i++)
aspect_mask &= aspect_mask - 1;
if (!aspect_mask)
{
WARN("Invalid plane index %u for format %u.\n", plane_idx, format->vk_format);
aspect_mask = format->vk_aspect_mask;
}
return aspect_mask & -aspect_mask;
}
VkImageSubresource vk_image_subresource_from_d3d12(
const struct vkd3d_format *format, uint32_t subresource_idx,
unsigned int miplevel_count, unsigned int layer_count,
bool all_aspects)
{
VkImageSubresource subresource;
subresource.aspectMask = format->vk_aspect_mask;
subresource.mipLevel = subresource_idx % miplevel_count;
subresource.arrayLayer = (subresource_idx / miplevel_count) % layer_count;
if (!all_aspects)
{
subresource.aspectMask = vk_image_aspect_flags_from_d3d12(
format, subresource_idx / (miplevel_count * layer_count));
}
return subresource;
}
VkImageSubresource d3d12_resource_get_vk_subresource(const struct d3d12_resource *resource,
uint32_t subresource_idx, bool all_aspects)
{
return vk_image_subresource_from_d3d12(
resource->format, subresource_idx,
resource->desc.MipLevels, d3d12_resource_desc_get_layer_count(&resource->desc),
all_aspects);
}
static HRESULT d3d12_validate_resource_flags(D3D12_RESOURCE_FLAGS flags)
{
unsigned int unknown_flags = flags & ~(D3D12_RESOURCE_FLAG_NONE
| D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET
| D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL
| D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS
| D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE
| D3D12_RESOURCE_FLAG_ALLOW_CROSS_ADAPTER
| D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS);
if (unknown_flags)
FIXME("Unknown resource flags %#x.\n", unknown_flags);
if ((flags & D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS) && (flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL))
{
ERR("ALLOW_SIMULTANEOUS_ACCESS and ALLOW_DEPTH_STENCIL is not allowed.\n");
return E_INVALIDARG;
}
if ((flags & D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS) && (flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL))
{
ERR("ALLOW_UNORDERED_ACCESS and ALLOW_DEPTH_STENCIL is not allowed.\n");
return E_INVALIDARG;
}
return S_OK;
}
static bool d3d12_resource_validate_texture_format(const D3D12_RESOURCE_DESC1 *desc,
const struct vkd3d_format *format)
{
if (!vkd3d_format_is_compressed(format))
return true;
if (desc->Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE1D && format->block_height > 1)
{
WARN("1D texture with a format block height > 1.\n");
return false;
}
if (align(desc->Width, format->block_width) != desc->Width
|| align(desc->Height, format->block_height) != desc->Height)
{
WARN("Invalid size %"PRIu64"x%u for block compressed format %#x.\n",
desc->Width, desc->Height, desc->Format);
return false;
}
return true;
}
static bool d3d12_resource_validate_texture_alignment(const D3D12_RESOURCE_DESC1 *desc,
const struct vkd3d_format *format)
{
uint64_t estimated_size;
if (!desc->Alignment)
return true;
if (desc->Alignment != D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT
&& desc->Alignment != D3D12_SMALL_RESOURCE_PLACEMENT_ALIGNMENT
&& (desc->SampleDesc.Count == 1 || desc->Alignment != D3D12_DEFAULT_MSAA_RESOURCE_PLACEMENT_ALIGNMENT))
{
WARN("Invalid resource alignment %#"PRIx64".\n", desc->Alignment);
return false;
}
if (desc->Alignment < D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT)
{
/* Windows uses the slice size to determine small alignment eligibility. DepthOrArraySize is ignored. */
estimated_size = desc->Width * desc->Height * format->byte_count * format->block_byte_count
/ (format->block_width * format->block_height);
if (estimated_size > D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT)
{
WARN("Invalid resource alignment %#"PRIx64" (required %#x).\n",
desc->Alignment, D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT);
return false;
}
}
/* The size check for MSAA textures with D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT is probably
* not important. The 4MB requirement is no longer universal and Vulkan has no such requirement. */
return true;
}
void d3d12_resource_promote_desc(const D3D12_RESOURCE_DESC *desc, D3D12_RESOURCE_DESC1 *desc1)
{
desc1->Dimension = desc->Dimension;
desc1->Alignment = desc->Alignment;
desc1->Width = desc->Width;
desc1->Height = desc->Height;
desc1->DepthOrArraySize = desc->DepthOrArraySize;
desc1->MipLevels = desc->MipLevels;
desc1->Format = desc->Format;
desc1->SampleDesc = desc->SampleDesc;
desc1->Layout = desc->Layout;
desc1->Flags = desc->Flags;
desc1->SamplerFeedbackMipRegion.Width = 0;
desc1->SamplerFeedbackMipRegion.Height = 0;
desc1->SamplerFeedbackMipRegion.Depth = 0;
}
HRESULT d3d12_resource_validate_desc(const D3D12_RESOURCE_DESC1 *desc, struct d3d12_device *device)
{
const struct vkd3d_format *format;
switch (desc->Dimension)
{
case D3D12_RESOURCE_DIMENSION_BUFFER:
if (desc->MipLevels != 1)
{
WARN("Invalid miplevel count %u for buffer.\n", desc->MipLevels);
return E_INVALIDARG;
}
if (desc->Alignment != 0 && desc->Alignment != D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT)
{
WARN("Invalid alignment %"PRIu64" for buffer resource. Must be 0 or D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT.\n",
desc->Alignment);
return E_INVALIDARG;
}
if (desc->Format != DXGI_FORMAT_UNKNOWN || desc->Layout != D3D12_TEXTURE_LAYOUT_ROW_MAJOR
|| desc->Height != 1 || desc->DepthOrArraySize != 1
|| desc->SampleDesc.Count != 1 || desc->SampleDesc.Quality != 0)
{
WARN("Invalid parameters for a buffer resource.\n");
return E_INVALIDARG;
}
if (desc->Flags & D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS)
{
WARN("D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS cannot be set for buffers.\n");
return E_INVALIDARG;
}
break;
case D3D12_RESOURCE_DIMENSION_TEXTURE1D:
if (desc->Height != 1)
{
WARN("1D texture with a height of %u.\n", desc->Height);
return E_INVALIDARG;
}
/* Fall through. */
case D3D12_RESOURCE_DIMENSION_TEXTURE2D:
case D3D12_RESOURCE_DIMENSION_TEXTURE3D:
if (desc->SampleDesc.Count == 0)
{
WARN("Invalid sample count 0.\n");
return E_INVALIDARG;
}
if (!(format = vkd3d_format_from_d3d12_resource_desc(device, desc, 0)))
{
WARN("Invalid format %#x.\n", desc->Format);
return E_INVALIDARG;
}
if (!d3d12_resource_validate_texture_format(desc, format)
|| !d3d12_resource_validate_texture_alignment(desc, format))
return E_INVALIDARG;
break;
default:
WARN("Invalid resource dimension %#x.\n", desc->Dimension);
return E_INVALIDARG;
}
return d3d12_validate_resource_flags(desc->Flags);
}
static HRESULT d3d12_resource_validate_heap_properties(const D3D12_RESOURCE_DESC1 *desc,
const D3D12_HEAP_PROPERTIES *heap_properties, D3D12_RESOURCE_STATES initial_state)
{
if (heap_properties->Type == D3D12_HEAP_TYPE_UPLOAD
|| heap_properties->Type == D3D12_HEAP_TYPE_READBACK)
{
if (desc->Dimension != D3D12_RESOURCE_DIMENSION_BUFFER)
{
WARN("Textures cannot be created on upload/readback heaps.\n");
return E_INVALIDARG;
}
if (desc->Flags & (D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS))
{
WARN("Render target and unordered access buffers cannot be created on upload/readback heaps.\n");
return E_INVALIDARG;
}
}
if (heap_properties->Type == D3D12_HEAP_TYPE_UPLOAD && initial_state != D3D12_RESOURCE_STATE_GENERIC_READ)
{
WARN("For D3D12_HEAP_TYPE_UPLOAD the state must be D3D12_RESOURCE_STATE_GENERIC_READ.\n");
return E_INVALIDARG;
}
if (heap_properties->Type == D3D12_HEAP_TYPE_READBACK && initial_state != D3D12_RESOURCE_STATE_COPY_DEST)
{
WARN("For D3D12_HEAP_TYPE_READBACK the state must be D3D12_RESOURCE_STATE_COPY_DEST.\n");
return E_INVALIDARG;
}
if (desc->Layout == D3D12_TEXTURE_LAYOUT_ROW_MAJOR)
{
/* ROW_MAJOR textures are severely restricted in D3D12.
* See test_map_texture_validation() for details. */
if (desc->Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE2D)
{
if (!(desc->Flags & D3D12_RESOURCE_FLAG_ALLOW_CROSS_ADAPTER))
{
WARN("ALLOW_CROSS_ADAPTER flag must be set to use ROW_MAJOR layout on textures.\n");
return E_INVALIDARG;
}
if (desc->MipLevels > 1 || desc->DepthOrArraySize > 1)
{
WARN("For ROW_MAJOR textures, MipLevels and DepthOrArraySize must be 1.\n");
return E_INVALIDARG;
}
if (heap_properties->Type == D3D12_HEAP_TYPE_CUSTOM &&
heap_properties->CPUPageProperty != D3D12_CPU_PAGE_PROPERTY_NOT_AVAILABLE)
{
WARN("ROW_MAJOR textures cannot be CPU visible with CUSTOM heaps.\n");
return E_INVALIDARG;
}
}
else if (desc->Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE1D ||
desc->Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE3D)
{
WARN("1D and 3D textures cannot be ROW_MAJOR layout.\n");
return E_INVALIDARG;
}
}
return S_OK;
}
static HRESULT d3d12_resource_validate_create_info(const D3D12_RESOURCE_DESC1 *desc,
const D3D12_HEAP_PROPERTIES *heap_properties, D3D12_RESOURCE_STATES initial_state,
const D3D12_CLEAR_VALUE *optimized_clear_value, struct d3d12_device *device)
{
HRESULT hr;
if (FAILED(hr = d3d12_resource_validate_desc(desc, device)))
return hr;
if (heap_properties)
{
if (FAILED(hr = d3d12_resource_validate_heap_properties(desc, heap_properties, initial_state)))
return hr;
}
if (optimized_clear_value)
{
if (desc->Dimension == D3D12_RESOURCE_DIMENSION_BUFFER)
{
WARN("Optimized clear value must be NULL for buffers.\n");
return E_INVALIDARG;
}
WARN("Ignoring optimized clear value.\n");
}
if (!is_valid_resource_state(initial_state))
{
WARN("Invalid initial resource state %#x.\n", initial_state);
return E_INVALIDARG;
}
return S_OK;
}
static HRESULT d3d12_resource_bind_sparse_metadata(struct d3d12_resource *resource,
struct d3d12_device *device, struct d3d12_sparse_info *sparse)
{
const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
VkSparseImageMemoryRequirements *sparse_requirements = NULL;
VkSparseImageOpaqueMemoryBindInfo opaque_bind;
VkMemoryRequirements memory_requirements;
VkSparseMemoryBind *memory_binds = NULL;
struct vkd3d_queue *vkd3d_queue = NULL;
uint32_t sparse_requirement_count;
VkQueue vk_queue = VK_NULL_HANDLE;
unsigned int i, j, k, bind_count;
VkBindSparseInfo bind_info;
VkDeviceSize metadata_size;
HRESULT hr = S_OK;
VkResult vr;
if (d3d12_resource_is_buffer(resource))
return S_OK;
/* We expect the metadata aspect for image resources to be uncommon on most
* drivers, so most of the time we'll just return early. The implementation
* is therefore aimed at simplicity, and not very well tested in practice. */
VK_CALL(vkGetImageSparseMemoryRequirements(device->vk_device,
resource->res.vk_image, &sparse_requirement_count, NULL));
if (!(sparse_requirements = vkd3d_malloc(sparse_requirement_count * sizeof(*sparse_requirements))))
{
ERR("Failed to allocate sparse memory requirement array.\n");
hr = E_OUTOFMEMORY;
goto cleanup;
}
VK_CALL(vkGetImageSparseMemoryRequirements(device->vk_device,
resource->res.vk_image, &sparse_requirement_count, sparse_requirements));
/* Find out how much memory and how many bind infos we need */
metadata_size = 0;
bind_count = 0;
for (i = 0; i < sparse_requirement_count; i++)
{
const VkSparseImageMemoryRequirements *req = &sparse_requirements[i];
if (req->formatProperties.aspectMask & VK_IMAGE_ASPECT_METADATA_BIT)
{
uint32_t layer_count = 1;
if (!(req->formatProperties.flags & VK_SPARSE_IMAGE_FORMAT_SINGLE_MIPTAIL_BIT))
layer_count = d3d12_resource_desc_get_layer_count(&resource->desc);
metadata_size *= layer_count * req->imageMipTailSize;
bind_count += layer_count;
}
}
if (!metadata_size)
goto cleanup;
/* Allocate memory for metadata mip tail */
TRACE("Allocating sparse metadata for resource %p.\n", resource);
VK_CALL(vkGetImageMemoryRequirements(device->vk_device, resource->res.vk_image, &memory_requirements));
if ((vr = vkd3d_allocate_device_memory(device, metadata_size, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
memory_requirements.memoryTypeBits, NULL, &sparse->vk_metadata_memory)))
{
ERR("Failed to allocate device memory for sparse metadata, vr %d.\n", vr);
hr = hresult_from_vk_result(vr);
goto cleanup;
}
/* Fill in opaque memory bind info */
if (!(memory_binds = vkd3d_malloc(bind_count * sizeof(*memory_binds))))
{
ERR("Failed to allocate sparse memory bind info array.\n");
hr = E_OUTOFMEMORY;
goto cleanup;
}
metadata_size = 0;
for (i = 0, j = 0; i < sparse_requirement_count; i++)
{
const VkSparseImageMemoryRequirements *req = &sparse_requirements[i];
if (req->formatProperties.aspectMask & VK_IMAGE_ASPECT_METADATA_BIT)
{
uint32_t layer_count = 1;
if (!(req->formatProperties.flags & VK_SPARSE_IMAGE_FORMAT_SINGLE_MIPTAIL_BIT))
layer_count = d3d12_resource_desc_get_layer_count(&resource->desc);
for (k = 0; k < layer_count; k++)
{
VkSparseMemoryBind *bind = &memory_binds[j++];
bind->resourceOffset = req->imageMipTailOffset + req->imageMipTailStride * k;
bind->size = req->imageMipTailSize;
bind->memory = sparse->vk_metadata_memory.vk_memory;
bind->memoryOffset = metadata_size;
bind->flags = VK_SPARSE_MEMORY_BIND_METADATA_BIT;
metadata_size += req->imageMipTailSize;
}
}
}
/* Bind metadata memory to the image */
opaque_bind.image = resource->res.vk_image;
opaque_bind.bindCount = bind_count;
opaque_bind.pBinds = memory_binds;
bind_info.sType = VK_STRUCTURE_TYPE_BIND_SPARSE_INFO;
bind_info.pNext = NULL;
bind_info.waitSemaphoreCount = 0;
bind_info.pWaitSemaphores = NULL;
bind_info.bufferBindCount = 0;
bind_info.pBufferBinds = NULL;
bind_info.imageOpaqueBindCount = 1;
bind_info.pImageOpaqueBinds = &opaque_bind;
bind_info.imageBindCount = 0;
bind_info.pImageBinds = NULL;
bind_info.signalSemaphoreCount = 0;
bind_info.pSignalSemaphores = NULL;
vkd3d_queue = device->queue_families[VKD3D_QUEUE_FAMILY_SPARSE_BINDING]->queues[0];
if (!(vk_queue = vkd3d_queue_acquire(vkd3d_queue)))
{
ERR("Failed to acquire queue %p.\n", vkd3d_queue);
goto cleanup;
}
if ((vr = VK_CALL(vkQueueBindSparse(vk_queue, 1, &bind_info, VK_NULL_HANDLE))) < 0)
{
ERR("Failed to bind sparse metadata to image, vr %d.\n", vr);
hr = hresult_from_vk_result(vr);
goto cleanup;
}
/* The application is free to use or destroy the resource
* immediately after creation, so we need to wait for the
* sparse binding operation to finish on the GPU. */
if ((vr = VK_CALL(vkQueueWaitIdle(vk_queue))))
{
ERR("Failed to wait for sparse binding to complete.\n");
hr = hresult_from_vk_result(vr);
}
cleanup:
if (vkd3d_queue && vk_queue)
vkd3d_queue_release(vkd3d_queue);
vkd3d_free(sparse_requirements);
vkd3d_free(memory_binds);
return hr;
}
static HRESULT d3d12_resource_init_sparse_info(struct d3d12_resource *resource,
struct d3d12_device *device, struct d3d12_sparse_info *sparse)
{
VkSparseImageMemoryRequirements vk_memory_requirements;
unsigned int i, subresource;
VkOffset3D tile_offset;
HRESULT hr;
memset(sparse, 0, sizeof(*sparse));
if (!(resource->flags & VKD3D_RESOURCE_RESERVED))
return S_OK;
sparse->tiling_count = d3d12_resource_desc_get_sub_resource_count_per_plane(&resource->desc);
sparse->tile_count = 0;
if (!(sparse->tilings = vkd3d_malloc(sparse->tiling_count * sizeof(*sparse->tilings))))
{
ERR("Failed to allocate subresource tiling info array.\n");
return E_OUTOFMEMORY;
}
d3d12_resource_get_tiling(device, resource, &sparse->tile_count, &sparse->packed_mips,
&sparse->tile_shape, sparse->tilings, &vk_memory_requirements);
if (!(sparse->tiles = vkd3d_malloc(sparse->tile_count * sizeof(*sparse->tiles))))
{
ERR("Failed to allocate tile mapping array.\n");
return E_OUTOFMEMORY;
}
tile_offset.x = 0;
tile_offset.y = 0;
tile_offset.z = 0;
subresource = 0;
for (i = 0; i < sparse->tile_count; i++)
{
if (d3d12_resource_is_buffer(resource))
{
VkDeviceSize offset = VKD3D_TILE_SIZE * i;
sparse->tiles[i].buffer.offset = offset;
sparse->tiles[i].buffer.length = align(min(VKD3D_TILE_SIZE, resource->desc.Width - offset),
VKD3D_TILE_SIZE);
}
else if (sparse->packed_mips.NumPackedMips && i >= sparse->packed_mips.StartTileIndexInOverallResource)
{
VkDeviceSize offset = VKD3D_TILE_SIZE * (i - sparse->packed_mips.StartTileIndexInOverallResource);
sparse->tiles[i].buffer.offset = vk_memory_requirements.imageMipTailOffset + offset;
sparse->tiles[i].buffer.length = align(min(VKD3D_TILE_SIZE, vk_memory_requirements.imageMipTailSize - offset),
VKD3D_TILE_SIZE);
}
else
{
struct d3d12_sparse_image_region *region = &sparse->tiles[i].image;
VkExtent3D block_extent = vk_memory_requirements.formatProperties.imageGranularity;
VkExtent3D mip_extent;
assert(subresource < sparse->tiling_count && sparse->tilings[subresource].WidthInTiles &&
sparse->tilings[subresource].HeightInTiles && sparse->tilings[subresource].DepthInTiles);
region->subresource.aspectMask = vk_memory_requirements.formatProperties.aspectMask;
region->subresource.mipLevel = subresource % resource->desc.MipLevels;
region->subresource.arrayLayer = subresource / resource->desc.MipLevels;
region->subresource_index = subresource;
region->offset.x = tile_offset.x * block_extent.width;
region->offset.y = tile_offset.y * block_extent.height;
region->offset.z = tile_offset.z * block_extent.depth;
mip_extent.width = d3d12_resource_desc_get_width(&resource->desc, region->subresource.mipLevel);
mip_extent.height = d3d12_resource_desc_get_height(&resource->desc, region->subresource.mipLevel);
mip_extent.depth = d3d12_resource_desc_get_depth(&resource->desc, region->subresource.mipLevel);
region->extent.width = min(block_extent.width, mip_extent.width - region->offset.x);
region->extent.height = min(block_extent.height, mip_extent.height - region->offset.y);
region->extent.depth = min(block_extent.depth, mip_extent.depth - region->offset.z);
if (++tile_offset.x == (int32_t)sparse->tilings[subresource].WidthInTiles)
{
tile_offset.x = 0;
if (++tile_offset.y == (int32_t)sparse->tilings[subresource].HeightInTiles)
{
tile_offset.y = 0;
if (++tile_offset.z == (int32_t)sparse->tilings[subresource].DepthInTiles)
{
tile_offset.z = 0;
/* Find next subresource that is not part of the packed mip tail */
while ((++subresource % resource->desc.MipLevels) >= sparse->packed_mips.NumStandardMips)
continue;
}
}
}
}
sparse->tiles[i].vk_memory = VK_NULL_HANDLE;
sparse->tiles[i].vk_offset = 0;
}
if (FAILED(hr = d3d12_resource_bind_sparse_metadata(resource, device, sparse)))
return hr;
return S_OK;
}
static void d3d12_resource_destroy(struct d3d12_resource *resource, struct d3d12_device *device)
{
const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
vkd3d_view_map_destroy(&resource->view_map, resource->device);
vkd3d_descriptor_debug_unregister_cookie(device->descriptor_qa_global_info, resource->res.cookie);
if (resource->flags & VKD3D_RESOURCE_EXTERNAL)
return;
if (resource->flags & VKD3D_RESOURCE_RESERVED)
{
vkd3d_free_device_memory(device, &resource->sparse.vk_metadata_memory);
vkd3d_free(resource->sparse.tiles);
vkd3d_free(resource->sparse.tilings);
if (resource->res.va)
{
vkd3d_va_map_remove(&device->memory_allocator.va_map, &resource->res);
if (!device->device_info.buffer_device_address_features.bufferDeviceAddress)
vkd3d_va_map_free_fake_va(&device->memory_allocator.va_map, resource->res.va, resource->res.size);
}
}
if (d3d12_resource_is_texture(resource))
VK_CALL(vkDestroyImage(device->vk_device, resource->res.vk_image, NULL));
else if (resource->flags & VKD3D_RESOURCE_RESERVED)
VK_CALL(vkDestroyBuffer(device->vk_device, resource->res.vk_buffer, NULL));
if ((resource->flags & VKD3D_RESOURCE_ALLOCATION) && resource->mem.device_allocation.vk_memory)
vkd3d_free_memory(device, &device->memory_allocator, &resource->mem);
if (resource->vrs_view)
VK_CALL(vkDestroyImageView(device->vk_device, resource->vrs_view, NULL));
vkd3d_private_store_destroy(&resource->private_store);
d3d12_device_release(resource->device);
vkd3d_free(resource);
}
static HRESULT d3d12_resource_create_vk_resource(struct d3d12_resource *resource, struct d3d12_device *device)
{
const D3D12_HEAP_PROPERTIES *heap_properties;
HRESULT hr;
heap_properties = resource->flags & VKD3D_RESOURCE_RESERVED
? NULL : &resource->heap_properties;
if (resource->desc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER)
{
if (FAILED(hr = vkd3d_create_buffer(device, heap_properties,
D3D12_HEAP_FLAG_NONE, &resource->desc, &resource->res.vk_buffer)))
return hr;
}
else
{
resource->initial_layout_transition = 1;
if (!resource->desc.MipLevels)
resource->desc.MipLevels = max_miplevel_count(&resource->desc);
if (FAILED(hr = vkd3d_create_image(device, heap_properties,
D3D12_HEAP_FLAG_NONE, &resource->desc, resource, &resource->res.vk_image)))
return hr;
}
return S_OK;
}
static HRESULT d3d12_resource_create(struct d3d12_device *device, uint32_t flags,
const D3D12_RESOURCE_DESC1 *desc, const D3D12_HEAP_PROPERTIES *heap_properties,
D3D12_HEAP_FLAGS heap_flags, D3D12_RESOURCE_STATES initial_state,
const D3D12_CLEAR_VALUE *optimized_clear_value, struct d3d12_resource **resource)
{
struct d3d12_resource *object;
HRESULT hr;
if (FAILED(hr = d3d12_resource_validate_create_info(desc,
heap_properties, initial_state, optimized_clear_value, device)))
return hr;
if (!(object = vkd3d_malloc(sizeof(*object))))
return E_OUTOFMEMORY;
memset(object, 0, sizeof(*object));
object->ID3D12Resource_iface.lpVtbl = &d3d12_resource_vtbl;
if (FAILED(hr = vkd3d_view_map_init(&object->view_map)))
{
vkd3d_free(object);
return hr;
}
if (FAILED(hr = vkd3d_private_store_init(&object->private_store)))
{
vkd3d_view_map_destroy(&object->view_map, device);
vkd3d_free(object);
return hr;
}
object->refcount = 1;
object->internal_refcount = 1;
object->desc = *desc;
object->device = device;
object->flags = flags;
object->format = vkd3d_format_from_d3d12_resource_desc(device, desc, 0);
object->res.cookie = vkd3d_allocate_cookie();
#ifdef VKD3D_ENABLE_DESCRIPTOR_QA
object->view_map.resource_cookie = object->res.cookie;
#endif
/* RTAS are "special" buffers. They can never transition out of this state. */
if (initial_state == D3D12_RESOURCE_STATE_RAYTRACING_ACCELERATION_STRUCTURE)
object->flags |= VKD3D_RESOURCE_ACCELERATION_STRUCTURE;
object->initial_state = initial_state;
if (heap_properties)
object->heap_properties = *heap_properties;
object->heap_flags = heap_flags;
d3d12_device_add_ref(device);
vkd3d_descriptor_debug_register_resource_cookie(device->descriptor_qa_global_info,
object->res.cookie, desc);
*resource = object;
return S_OK;
}
HRESULT d3d12_resource_create_committed(struct d3d12_device *device, const D3D12_RESOURCE_DESC1 *desc,
const D3D12_HEAP_PROPERTIES *heap_properties, D3D12_HEAP_FLAGS heap_flags, D3D12_RESOURCE_STATES initial_state,
const D3D12_CLEAR_VALUE *optimized_clear_value, struct d3d12_resource **resource)
{
const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
struct d3d12_resource *object;
HRESULT hr;
if (FAILED(hr = d3d12_resource_create(device, VKD3D_RESOURCE_COMMITTED | VKD3D_RESOURCE_ALLOCATION,
desc, heap_properties, heap_flags, initial_state, optimized_clear_value, &object)))
return hr;
if (d3d12_resource_is_texture(object))
{
VkMemoryDedicatedRequirements dedicated_requirements;
struct vkd3d_allocate_memory_info allocate_info;
VkMemoryDedicatedAllocateInfo dedicated_info;
VkImageMemoryRequirementsInfo2 image_info;
VkMemoryRequirements2 memory_requirements;
VkBindImageMemoryInfo bind_info;
bool use_dedicated_allocation;
VkResult vr;
if (FAILED(hr = d3d12_resource_create_vk_resource(object, device)))
goto fail;
image_info.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2;
image_info.pNext = NULL;
image_info.image = object->res.vk_image;
dedicated_requirements.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS;
dedicated_requirements.pNext = NULL;
memory_requirements.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2;
memory_requirements.pNext = &dedicated_requirements;
VK_CALL(vkGetImageMemoryRequirements2(device->vk_device, &image_info, &memory_requirements));
if (!(use_dedicated_allocation = dedicated_requirements.prefersDedicatedAllocation))
{
const uint32_t type_mask = memory_requirements.memoryRequirements.memoryTypeBits & device->memory_info.global_mask;
const struct vkd3d_memory_info_domain *domain = d3d12_device_get_memory_info_domain(device, heap_properties);
use_dedicated_allocation = (type_mask & domain->buffer_type_mask) != type_mask;
}
memset(&allocate_info, 0, sizeof(allocate_info));
allocate_info.memory_requirements = memory_requirements.memoryRequirements;
allocate_info.heap_properties = *heap_properties;
allocate_info.heap_flags = heap_flags;
if (desc->Flags & (D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL))
allocate_info.heap_flags |= D3D12_HEAP_FLAG_ALLOW_ONLY_RT_DS_TEXTURES;
else
allocate_info.heap_flags |= D3D12_HEAP_FLAG_ALLOW_ONLY_NON_RT_DS_TEXTURES;
if (use_dedicated_allocation)
{
dedicated_info.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO;
dedicated_info.pNext = NULL;
dedicated_info.image = object->res.vk_image;
dedicated_info.buffer = VK_NULL_HANDLE;
allocate_info.pNext = &dedicated_info;
allocate_info.flags = VKD3D_ALLOCATION_FLAG_DEDICATED;
}
else
{
/* We want to allow suballocations and we need the allocation to
* be cleared to zero, which only works if we allow buffers */
allocate_info.heap_flags &= ~D3D12_HEAP_FLAG_DENY_BUFFERS;
allocate_info.flags = VKD3D_ALLOCATION_FLAG_GLOBAL_BUFFER;
}
if (FAILED(hr = vkd3d_allocate_memory(device, &device->memory_allocator, &allocate_info, &object->mem)))
goto fail;
bind_info.sType = VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO;
bind_info.pNext = NULL;
bind_info.image = object->res.vk_image;
bind_info.memory = object->mem.device_allocation.vk_memory;
bind_info.memoryOffset = object->mem.offset;
if ((vr = VK_CALL(vkBindImageMemory2KHR(device->vk_device, 1, &bind_info))))
{
ERR("Failed to bind image memory, vr %d.\n", vr);
hr = hresult_from_vk_result(vr);
goto fail;
}
if (vkd3d_resource_can_be_vrs(device, heap_properties, desc))
{
/* Make the implicit VRS view here... */
if (FAILED(hr = vkd3d_resource_make_vrs_view(device, object->res.vk_image, &object->vrs_view)))
goto fail;
}
}
else
{
struct vkd3d_allocate_heap_memory_info allocate_info;
memset(&allocate_info, 0, sizeof(allocate_info));
allocate_info.heap_desc.Properties = *heap_properties;
allocate_info.heap_desc.Alignment = desc->Alignment ? desc->Alignment : D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;
allocate_info.heap_desc.SizeInBytes = align(desc->Width, allocate_info.heap_desc.Alignment);
allocate_info.heap_desc.Flags = heap_flags | D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS;
/* Be very careful with suballocated buffers. */
if ((vkd3d_config_flags & VKD3D_CONFIG_FLAG_ZERO_MEMORY_WORKAROUNDS_COMMITTED_BUFFER_UAV) &&
(desc->Flags & D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS) &&
desc->Width < VKD3D_VA_BLOCK_SIZE)
{
allocate_info.heap_desc.Flags &= ~D3D12_HEAP_FLAG_CREATE_NOT_ZEROED;
}
if (FAILED(hr = vkd3d_allocate_heap_memory(device,
&device->memory_allocator, &allocate_info, &object->mem)))
goto fail;
object->res.vk_buffer = object->mem.resource.vk_buffer;
object->res.va = object->mem.resource.va;
}
*resource = object;
return S_OK;
fail:
d3d12_resource_destroy(object, device);
return hr;
}
static HRESULT d3d12_resource_validate_heap(const D3D12_RESOURCE_DESC1 *resource_desc, struct d3d12_heap *heap)
{
D3D12_HEAP_FLAGS deny_flag;
if (resource_desc->Dimension == D3D12_RESOURCE_DIMENSION_BUFFER)
deny_flag = D3D12_HEAP_FLAG_DENY_BUFFERS;
else if (resource_desc->Flags & (D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL))
deny_flag = D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES;
else
deny_flag = D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES;
if (heap->desc.Flags & deny_flag)
{
WARN("Cannot create placed resource on heap that denies resource category %#x.\n", deny_flag);
return E_INVALIDARG;
}
if ((heap->desc.Flags & D3D12_HEAP_FLAG_SHARED_CROSS_ADAPTER) &&
!(resource_desc->Flags & D3D12_RESOURCE_FLAG_ALLOW_CROSS_ADAPTER))
{
ERR("Must declare ALLOW_CROSS_ADAPTER resource flag when heap is cross adapter.\n");
return E_INVALIDARG;
}
return S_OK;
}
HRESULT d3d12_resource_create_placed(struct d3d12_device *device, const D3D12_RESOURCE_DESC1 *desc,
struct d3d12_heap *heap, uint64_t heap_offset, D3D12_RESOURCE_STATES initial_state,
const D3D12_CLEAR_VALUE *optimized_clear_value, struct d3d12_resource **resource)
{
const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
VkMemoryRequirements memory_requirements;
VkBindImageMemoryInfo bind_info;
struct d3d12_resource *object;
bool force_committed;
VkResult vr;
HRESULT hr;
if (FAILED(hr = d3d12_resource_validate_heap(desc, heap)))
return hr;
/* Placed linear textures are ... problematic
* since we have no way of signalling that they have different alignment and size requirements
* than optimal textures. GetResourceAllocationInfo() does not take heap property information
* and assumes that we are not modifying the tiling mode. */
force_committed = desc->Dimension != D3D12_RESOURCE_DIMENSION_BUFFER &&
is_cpu_accessible_heap(&heap->desc.Properties);
if (force_committed || heap->allocation.device_allocation.vk_memory == VK_NULL_HANDLE)
{
if (!force_committed)
WARN("Placing resource on heap with no memory backing it. Falling back to committed resource.\n");
if (FAILED(hr = d3d12_resource_create_committed(device, desc, &heap->desc.Properties,
heap->desc.Flags & ~(D3D12_HEAP_FLAG_DENY_BUFFERS |
D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES |
D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES),
initial_state, optimized_clear_value, resource)))
{
ERR("Failed to create fallback committed resource.\n");
}
return hr;
}
if (FAILED(hr = d3d12_resource_create(device, VKD3D_RESOURCE_PLACED, desc,
&heap->desc.Properties, heap->desc.Flags, initial_state, optimized_clear_value, &object)))
return hr;
object->heap = heap;
if (d3d12_resource_is_texture(object))
{
if (FAILED(hr = d3d12_resource_create_vk_resource(object, device)))
goto fail;
/* Align manually. This works because we padded the required allocation size reported to the app. */
VK_CALL(vkGetImageMemoryRequirements(device->vk_device, object->res.vk_image, &memory_requirements));
heap_offset = align(heap_offset, memory_requirements.alignment);
if (heap_offset + memory_requirements.size > heap->allocation.resource.size)
{
ERR("Heap too small for the texture (heap=%"PRIu64", res=%"PRIu64".\n",
heap->allocation.resource.size, heap_offset + memory_requirements.size);
hr = E_INVALIDARG;
goto fail;
}
}
else
{
if (heap_offset + desc->Width > heap->allocation.resource.size)
{
ERR("Heap too small for the buffer (heap=%"PRIu64", res=%"PRIu64".\n",
heap->allocation.resource.size, heap_offset + desc->Width);
hr = E_INVALIDARG;
goto fail;
}
}
vkd3d_memory_allocation_slice(&object->mem, &heap->allocation, heap_offset, 0);
if (d3d12_resource_is_texture(object))
{
bind_info.sType = VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO;
bind_info.pNext = NULL;
bind_info.image = object->res.vk_image;
bind_info.memory = object->mem.device_allocation.vk_memory;
bind_info.memoryOffset = object->mem.offset;
if ((vr = VK_CALL(vkBindImageMemory2KHR(device->vk_device, 1, &bind_info))) < 0)
{
ERR("Failed to bind image memory, vr %d.\n", vr);
hr = hresult_from_vk_result(vr);
goto fail;
}
}
else
{
object->res.vk_buffer = object->mem.resource.vk_buffer;
object->res.va = object->mem.resource.va;
}
if (vkd3d_resource_can_be_vrs(device, &heap->desc.Properties, desc))
{
/* Make the implicit VRS view here... */
if (FAILED(hr = vkd3d_resource_make_vrs_view(device, object->res.vk_image, &object->vrs_view)))
goto fail;
}
/* Placed RTV and DSV *must* be explicitly initialized after alias barriers and first use,
* so there is no need to do initial layout transition ourselves.
* It is extremely dangerous to do so since the initialization will clobber other
* aliased buffers when clearing DCC/HTILE state.
* For details, see:
* https://docs.microsoft.com/en-us/windows/win32/api/d3d12/nf-d3d12-id3d12device-createplacedresource#notes-on-the-required-resource-initialization. */
if (desc->Flags & (D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL))
object->initial_layout_transition = 0;
*resource = object;
return S_OK;
fail:
d3d12_resource_destroy(object, device);
return hr;
}
HRESULT d3d12_resource_create_reserved(struct d3d12_device *device,
const D3D12_RESOURCE_DESC1 *desc, D3D12_RESOURCE_STATES initial_state,
const D3D12_CLEAR_VALUE *optimized_clear_value, struct d3d12_resource **resource)
{
struct d3d12_resource *object;
HRESULT hr;
if (FAILED(hr = d3d12_resource_create(device, VKD3D_RESOURCE_RESERVED, desc,
NULL, D3D12_HEAP_FLAG_NONE, initial_state, optimized_clear_value, &object)))
return hr;
if (FAILED(hr = d3d12_resource_create_vk_resource(object, device)))
goto fail;
if (FAILED(hr = d3d12_resource_init_sparse_info(object, device, &object->sparse)))
goto fail;
if (d3d12_resource_is_buffer(object))
{
object->res.size = object->desc.Width;
if (device->device_info.buffer_device_address_features.bufferDeviceAddress)
object->res.va = vkd3d_get_buffer_device_address(device, object->res.vk_buffer);
else
object->res.va = vkd3d_va_map_alloc_fake_va(&device->memory_allocator.va_map, object->res.size);
if (!object->res.va)
{
ERR("Failed to get VA for sparse resource.\n");
return E_FAIL;
}
vkd3d_va_map_insert(&device->memory_allocator.va_map, &object->res);
}
*resource = object;
return S_OK;
fail:
d3d12_resource_destroy(object, device);
return hr;
}
VKD3D_EXPORT HRESULT vkd3d_create_image_resource(ID3D12Device *device,
const struct vkd3d_image_resource_create_info *create_info, ID3D12Resource **resource)
{
struct d3d12_device *d3d12_device = impl_from_ID3D12Device((d3d12_device_iface *)device);
struct d3d12_resource *object;
HRESULT hr;
TRACE("device %p, create_info %p, resource %p.\n", device, create_info, resource);
if (!create_info || !resource)
return E_INVALIDARG;
if (!(object = vkd3d_malloc(sizeof(*object))))
return E_OUTOFMEMORY;
memset(object, 0, sizeof(*object));
object->ID3D12Resource_iface.lpVtbl = &d3d12_resource_vtbl;
object->refcount = 1;
object->internal_refcount = 1;
object->res.vk_image = create_info->vk_image;
object->flags = create_info->flags;
object->flags |= VKD3D_RESOURCE_EXTERNAL;
object->initial_layout_transition = 1;
object->common_layout = vk_common_image_layout_from_d3d12_desc(d3d12_device, &object->desc);
memset(&object->sparse, 0, sizeof(object->sparse));
d3d12_resource_promote_desc(&create_info->desc, &object->desc);
object->format = vkd3d_format_from_d3d12_resource_desc(d3d12_device, &object->desc, 0);
if (FAILED(hr = vkd3d_view_map_init(&object->view_map)))
{
vkd3d_free(object);
return hr;
}
if (FAILED(hr = vkd3d_private_store_init(&object->private_store)))
{
vkd3d_free(object);
return hr;
}
d3d12_device_add_ref(object->device = d3d12_device);
TRACE("Created resource %p.\n", object);
*resource = (ID3D12Resource *)&object->ID3D12Resource_iface;
return S_OK;
}
VKD3D_EXPORT ULONG vkd3d_resource_incref(ID3D12Resource *resource)
{
TRACE("resource %p.\n", resource);
return d3d12_resource_incref(impl_from_ID3D12Resource(resource));
}
VKD3D_EXPORT ULONG vkd3d_resource_decref(ID3D12Resource *resource)
{
TRACE("resource %p.\n", resource);
return d3d12_resource_decref(impl_from_ID3D12Resource(resource));
}
/* CBVs, SRVs, UAVs */
static struct vkd3d_view *vkd3d_view_create(enum vkd3d_view_type type)
{
struct vkd3d_view *view;
if ((view = vkd3d_malloc(sizeof(*view))))
{
view->refcount = 1;
view->type = type;
view->cookie = vkd3d_allocate_cookie();
}
return view;
}
void vkd3d_view_incref(struct vkd3d_view *view)
{
InterlockedIncrement(&view->refcount);
}
static void vkd3d_view_destroy(struct vkd3d_view *view, struct d3d12_device *device)
{
const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
TRACE("Destroying view %p.\n", view);
vkd3d_descriptor_debug_unregister_cookie(device->descriptor_qa_global_info, view->cookie);
switch (view->type)
{
case VKD3D_VIEW_TYPE_BUFFER:
VK_CALL(vkDestroyBufferView(device->vk_device, view->vk_buffer_view, NULL));
break;
case VKD3D_VIEW_TYPE_IMAGE:
VK_CALL(vkDestroyImageView(device->vk_device, view->vk_image_view, NULL));
break;
case VKD3D_VIEW_TYPE_SAMPLER:
VK_CALL(vkDestroySampler(device->vk_device, view->vk_sampler, NULL));
break;
case VKD3D_VIEW_TYPE_ACCELERATION_STRUCTURE:
VK_CALL(vkDestroyAccelerationStructureKHR(device->vk_device, view->vk_acceleration_structure, NULL));
break;
default:
WARN("Unhandled view type %d.\n", view->type);
}
vkd3d_free(view);
}
void vkd3d_view_decref(struct vkd3d_view *view, struct d3d12_device *device)
{
if (!InterlockedDecrement(&view->refcount))
vkd3d_view_destroy(view, device);
}
void d3d12_desc_copy_single(vkd3d_cpu_descriptor_va_t dst_va, vkd3d_cpu_descriptor_va_t src_va,
struct d3d12_device *device)
{
VkCopyDescriptorSet vk_copies[VKD3D_MAX_BINDLESS_DESCRIPTOR_SETS];
const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
const struct vkd3d_bindless_set_info *set_info;
struct vkd3d_descriptor_binding binding;
uint32_t set_mask, set_info_index;
struct d3d12_desc_split src, dst;
VkCopyDescriptorSet *vk_copy;
uint32_t copy_count = 0;
uint32_t flags;
src = d3d12_desc_decode_va(src_va);
dst = d3d12_desc_decode_va(dst_va);
flags = src.types->flags;
set_mask = src.types->set_info_mask;
if (flags & VKD3D_DESCRIPTOR_FLAG_SINGLE_DESCRIPTOR)
{
/* Faster path, there is no need to fetch set info deep into the guts of d3d12_device,
* and also, we don't get a dependency chain on the CTZ loop -> index, which causes OoO bubbles
* it seems. */
binding = src.types->single_binding;
if (src.heap->sets[binding.set].copy_template_single)
{
src.heap->sets[binding.set].copy_template_single(
dst.heap->sets[binding.set].mapped_set,
src.heap->sets[binding.set].mapped_set,
dst.offset, src.offset);
}
else
{
vk_copy = &vk_copies[copy_count++];
vk_copy->sType = VK_STRUCTURE_TYPE_COPY_DESCRIPTOR_SET;
vk_copy->pNext = NULL;
vk_copy->srcSet = src.heap->sets[binding.set].vk_descriptor_set;
vk_copy->srcBinding = binding.binding;
vk_copy->srcArrayElement = src.offset;
vk_copy->dstSet = dst.heap->sets[binding.set].vk_descriptor_set;
vk_copy->dstBinding = binding.binding;
vk_copy->dstArrayElement = dst.offset;
vk_copy->descriptorCount = 1;
}
}
else
{
/* Need to copy multiple descriptors. CTZ loop. */
while (set_mask)
{
set_info_index = vkd3d_bitmask_iter32(&set_mask);
set_info = &device->bindless_state.set_info[set_info_index];
if (set_info->host_copy_template_single)
{
set_info->host_copy_template_single(
dst.heap->sets[set_info->set_index].mapped_set,
src.heap->sets[set_info->set_index].mapped_set,
dst.offset, src.offset);
}
else
{
binding = vkd3d_bindless_state_binding_from_info_index(&device->bindless_state, set_info_index);
vk_copy = &vk_copies[copy_count++];
vk_copy->sType = VK_STRUCTURE_TYPE_COPY_DESCRIPTOR_SET;
vk_copy->pNext = NULL;
vk_copy->srcSet = src.heap->sets[binding.set].vk_descriptor_set;
vk_copy->srcBinding = binding.binding;
vk_copy->srcArrayElement = src.offset;
vk_copy->dstSet = dst.heap->sets[binding.set].vk_descriptor_set;
vk_copy->dstBinding = binding.binding;
vk_copy->dstArrayElement = dst.offset;
vk_copy->descriptorCount = 1;
}
}
}
if (flags & VKD3D_DESCRIPTOR_FLAG_RAW_VA_AUX_BUFFER)
{
if (dst.heap->raw_va_aux_buffer.host_ptr)
{
const VkDeviceAddress *src_vas = src.heap->raw_va_aux_buffer.host_ptr;
VkDeviceAddress *dst_vas = dst.heap->raw_va_aux_buffer.host_ptr;
dst_vas[dst.offset] = src_vas[src.offset];
}
else
{
binding = vkd3d_bindless_state_find_set(
&device->bindless_state, VKD3D_BINDLESS_SET_UAV | VKD3D_BINDLESS_SET_AUX_BUFFER);
vk_copy = &vk_copies[copy_count++];
vk_copy->sType = VK_STRUCTURE_TYPE_COPY_DESCRIPTOR_SET;
vk_copy->pNext = NULL;
vk_copy->srcSet = src.heap->sets[binding.set].vk_descriptor_set;
vk_copy->srcBinding = binding.binding;
vk_copy->srcArrayElement = src.offset;
vk_copy->dstSet = dst.heap->sets[binding.set].vk_descriptor_set;
vk_copy->dstBinding = binding.binding;
vk_copy->dstArrayElement = dst.offset;
vk_copy->descriptorCount = 1;
}
}
if (copy_count)
VK_CALL(vkUpdateDescriptorSets(device->vk_device, 0, NULL, copy_count, vk_copies));
if (flags & VKD3D_DESCRIPTOR_FLAG_BUFFER_OFFSET)
{
const struct vkd3d_bound_buffer_range *src_buffer_ranges = src.heap->buffer_ranges.host_ptr;
struct vkd3d_bound_buffer_range *dst_buffer_ranges = dst.heap->buffer_ranges.host_ptr;
dst_buffer_ranges[dst.offset] = src_buffer_ranges[src.offset];
}
*dst.types = *src.types;
*dst.view = *src.view;
}
void d3d12_desc_copy_range(vkd3d_cpu_descriptor_va_t dst_va, vkd3d_cpu_descriptor_va_t src_va,
unsigned int count, D3D12_DESCRIPTOR_HEAP_TYPE heap_type, struct d3d12_device *device)
{
VkCopyDescriptorSet vk_copies[VKD3D_MAX_BINDLESS_DESCRIPTOR_SETS];
const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
const struct vkd3d_bindless_set_info *set_info;
struct vkd3d_descriptor_binding binding;
struct d3d12_desc_split src, dst;
VkCopyDescriptorSet *vk_copy;
uint32_t set_info_mask = 0;
uint32_t copy_count = 0;
uint32_t set_info_index;
unsigned int i;
src = d3d12_desc_decode_va(src_va);
dst = d3d12_desc_decode_va(dst_va);
for (i = 0; i < count; i++)
set_info_mask |= src.types[i].set_info_mask;
memcpy(dst.view, src.view, sizeof(*dst.view) * count);
memcpy(dst.types, src.types, sizeof(*dst.types) * count);
while (set_info_mask)
{
set_info_index = vkd3d_bitmask_iter32(&set_info_mask);
set_info = &device->bindless_state.set_info[set_info_index];
if (set_info->host_copy_template)
{
set_info->host_copy_template(
dst.heap->sets[set_info->set_index].mapped_set,
src.heap->sets[set_info->set_index].mapped_set,
dst.offset, src.offset, count);
}
else
{
binding = vkd3d_bindless_state_binding_from_info_index(&device->bindless_state, set_info_index);
vk_copy = &vk_copies[copy_count++];
vk_copy->sType = VK_STRUCTURE_TYPE_COPY_DESCRIPTOR_SET;
vk_copy->pNext = NULL;
vk_copy->srcSet = src.heap->sets[binding.set].vk_descriptor_set;
vk_copy->srcBinding = binding.binding;
vk_copy->srcArrayElement = src.offset;
vk_copy->dstSet = dst.heap->sets[binding.set].vk_descriptor_set;
vk_copy->dstBinding = binding.binding;
vk_copy->dstArrayElement = dst.offset;
vk_copy->descriptorCount = count;
}
}
if (heap_type == D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV)
{
if (device->bindless_state.flags & VKD3D_RAW_VA_AUX_BUFFER)
{
const VkDeviceAddress *src_vas = src.heap->raw_va_aux_buffer.host_ptr;
VkDeviceAddress *dst_vas = dst.heap->raw_va_aux_buffer.host_ptr;
memcpy(dst_vas + dst.offset, src_vas + src.offset, sizeof(*dst_vas) * count);
}
else
{
binding = vkd3d_bindless_state_find_set(&device->bindless_state, VKD3D_BINDLESS_SET_UAV | VKD3D_BINDLESS_SET_AUX_BUFFER);
vk_copy = &vk_copies[copy_count++];
vk_copy->sType = VK_STRUCTURE_TYPE_COPY_DESCRIPTOR_SET;
vk_copy->pNext = NULL;
vk_copy->srcSet = src.heap->sets[binding.set].vk_descriptor_set;
vk_copy->srcBinding = binding.binding;
vk_copy->srcArrayElement = src.offset;
vk_copy->dstSet = dst.heap->sets[binding.set].vk_descriptor_set;
vk_copy->dstBinding = binding.binding;
vk_copy->dstArrayElement = dst.offset;
vk_copy->descriptorCount = count;
}
if (device->bindless_state.flags & (VKD3D_TYPED_OFFSET_BUFFER | VKD3D_SSBO_OFFSET_BUFFER))
{
const struct vkd3d_bound_buffer_range *src_ranges = src.heap->buffer_ranges.host_ptr;
struct vkd3d_bound_buffer_range *dst_ranges = dst.heap->buffer_ranges.host_ptr;
memcpy(dst_ranges + dst.offset, src_ranges + src.offset, sizeof(*dst_ranges) * count);
}
}
if (copy_count)
VK_CALL(vkUpdateDescriptorSets(device->vk_device, 0, NULL, copy_count, vk_copies));
}
void d3d12_desc_copy(vkd3d_cpu_descriptor_va_t dst_va, vkd3d_cpu_descriptor_va_t src_va,
unsigned int count, D3D12_DESCRIPTOR_HEAP_TYPE heap_type, struct d3d12_device *device)
{
unsigned int i;
#ifdef VKD3D_ENABLE_DESCRIPTOR_QA
{
struct d3d12_desc_split dst, src;
dst = d3d12_desc_decode_va(dst_va);
src = d3d12_desc_decode_va(src_va);
for (i = 0; i < count; i++)
{
vkd3d_descriptor_debug_copy_descriptor(
dst.heap->descriptor_heap_info.host_ptr, dst.heap->cookie, dst.offset,
src.heap->descriptor_heap_info.host_ptr, src.heap->cookie, src.offset,
src.view[i].cookie);
}
}
#endif
if (device->bindless_state.flags & VKD3D_BINDLESS_MUTABLE_TYPE)
d3d12_desc_copy_range(dst_va, src_va, count, heap_type, device);
else
{
/* This path is quite rare. Could potentially amortize d3d12_desc_decode_va(). */
for (i = 0; i < count; i++)
{
d3d12_desc_copy_single(dst_va, src_va, device);
dst_va += VKD3D_RESOURCE_DESC_INCREMENT;
src_va += VKD3D_RESOURCE_DESC_INCREMENT;
}
}
}
bool vkd3d_create_raw_r32ui_vk_buffer_view(struct d3d12_device *device,
VkBuffer vk_buffer, VkDeviceSize offset, VkDeviceSize range, VkBufferView *vk_view)
{
const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
struct VkBufferViewCreateInfo view_desc;
VkResult vr;
if (offset % 4)
FIXME("Offset %#"PRIx64" violates the required alignment 4.\n", offset);
view_desc.sType = VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO;
view_desc.pNext = NULL;
view_desc.flags = 0;
view_desc.buffer = vk_buffer;
view_desc.format = VK_FORMAT_R32_UINT;
view_desc.offset = offset;
view_desc.range = range;
if ((vr = VK_CALL(vkCreateBufferView(device->vk_device, &view_desc, NULL, vk_view))) < 0)
WARN("Failed to create Vulkan buffer view, vr %d.\n", vr);
return vr == VK_SUCCESS;
}
bool vkd3d_create_vk_buffer_view(struct d3d12_device *device,
VkBuffer vk_buffer, const struct vkd3d_format *format,
VkDeviceSize offset, VkDeviceSize range, VkBufferView *vk_view)
{
const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
struct VkBufferViewCreateInfo view_desc;
VkResult vr;
if (vkd3d_format_is_compressed(format))
{
WARN("Invalid format for buffer view %#x.\n", format->dxgi_format);
return false;
}
view_desc.sType = VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO;
view_desc.pNext = NULL;
view_desc.flags = 0;
view_desc.buffer = vk_buffer;
view_desc.format = format->vk_format;
view_desc.offset = offset;
view_desc.range = range;
if ((vr = VK_CALL(vkCreateBufferView(device->vk_device, &view_desc, NULL, vk_view))) < 0)
WARN("Failed to create Vulkan buffer view, vr %d.\n", vr);
return vr == VK_SUCCESS;
}
bool vkd3d_create_buffer_view(struct d3d12_device *device, const struct vkd3d_buffer_view_desc *desc, struct vkd3d_view **view)
{
const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
struct vkd3d_view *object;
VkBufferView vk_view;
if (!vkd3d_create_vk_buffer_view(device, desc->buffer, desc->format, desc->offset, desc->size, &vk_view))
return false;
if (!(object = vkd3d_view_create(VKD3D_VIEW_TYPE_BUFFER)))
{
VK_CALL(vkDestroyBufferView(device->vk_device, vk_view, NULL));
return false;
}
object->vk_buffer_view = vk_view;
object->format = desc->format;
object->info.buffer.offset = desc->offset;
object->info.buffer.size = desc->size;
*view = object;
return true;
}
bool vkd3d_create_acceleration_structure_view(struct d3d12_device *device, const struct vkd3d_buffer_view_desc *desc,
struct vkd3d_view **view)
{
const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
VkAccelerationStructureKHR vk_acceleration_structure;
VkAccelerationStructureCreateInfoKHR create_info;
VkDeviceAddress buffer_address;
VkDeviceAddress rtas_address;
struct vkd3d_view *object;
VkResult vr;
create_info.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_CREATE_INFO_KHR;
create_info.pNext = NULL;
create_info.type = VK_ACCELERATION_STRUCTURE_TYPE_GENERIC_KHR;
create_info.createFlags = 0;
create_info.deviceAddress = 0;
create_info.buffer = desc->buffer;
create_info.offset = desc->offset;
create_info.size = desc->size;
vr = VK_CALL(vkCreateAccelerationStructureKHR(device->vk_device, &create_info, NULL, &vk_acceleration_structure));
if (vr != VK_SUCCESS)
return false;
if (!(object = vkd3d_view_create(VKD3D_VIEW_TYPE_ACCELERATION_STRUCTURE)))
{
VK_CALL(vkDestroyAccelerationStructureKHR(device->vk_device, vk_acceleration_structure, NULL));
return false;
}
/* Sanity check. Spec should guarantee this.
* There is a note in the spec for vkGetAccelerationStructureDeviceAddressKHR:
* The acceleration structure device address may be different from the
* buffer device address corresponding to the acceleration structure's
* start offset in its storage buffer for acceleration structure types
* other than VK_ACCELERATION_STRUCTURE_TYPE_GENERIC_KHR. */
buffer_address = vkd3d_get_buffer_device_address(device, desc->buffer) + desc->offset;
rtas_address = vkd3d_get_acceleration_structure_device_address(device, vk_acceleration_structure);
if (buffer_address != rtas_address)
{
FIXME("buffer_address = 0x%"PRIx64", rtas_address = 0x%"PRIx64".\n", buffer_address, rtas_address);
}
object->vk_acceleration_structure = vk_acceleration_structure;
object->format = desc->format;
object->info.buffer.offset = desc->offset;
object->info.buffer.size = desc->size;
*view = object;
return true;
}
#define VKD3D_VIEW_RAW_BUFFER 0x1
static bool vkd3d_create_buffer_view_for_resource(struct d3d12_device *device,
struct d3d12_resource *resource, DXGI_FORMAT view_format,
unsigned int offset, unsigned int size, unsigned int structure_stride,
unsigned int flags, struct vkd3d_view **view)
{
const struct vkd3d_format *format;
struct vkd3d_view_key key;
VkDeviceSize element_size;
if (view_format == DXGI_FORMAT_R32_TYPELESS && (flags & VKD3D_VIEW_RAW_BUFFER))
{
format = vkd3d_get_format(device, DXGI_FORMAT_R32_UINT, false);
element_size = format->byte_count;
}
else if (view_format == DXGI_FORMAT_UNKNOWN && structure_stride)
{
format = vkd3d_get_format(device, DXGI_FORMAT_R32_UINT, false);
element_size = structure_stride;
}
else if ((format = vkd3d_format_from_d3d12_resource_desc(device, &resource->desc, view_format)))
{
element_size = format->byte_count;
}
else
{
WARN("Failed to find format for %#x.\n", resource->desc.Format);
return false;
}
assert(d3d12_resource_is_buffer(resource));
key.view_type = VKD3D_VIEW_TYPE_BUFFER;
key.u.buffer.buffer = resource->res.vk_buffer;
key.u.buffer.format = format;
key.u.buffer.offset = resource->mem.offset + offset * element_size;
key.u.buffer.size = size * element_size;
return !!(*view = vkd3d_view_map_create_view(&resource->view_map, device, &key));
}
static void vkd3d_set_view_swizzle_for_format(VkComponentMapping *components,
const struct vkd3d_format *format, bool allowed_swizzle)
{
components->r = VK_COMPONENT_SWIZZLE_R;
components->g = VK_COMPONENT_SWIZZLE_G;
components->b = VK_COMPONENT_SWIZZLE_B;
components->a = VK_COMPONENT_SWIZZLE_A;
if (format->vk_aspect_mask == VK_IMAGE_ASPECT_STENCIL_BIT)
{
if (allowed_swizzle)
{
components->r = VK_COMPONENT_SWIZZLE_ZERO;
components->g = VK_COMPONENT_SWIZZLE_R;
components->b = VK_COMPONENT_SWIZZLE_ZERO;
components->a = VK_COMPONENT_SWIZZLE_ZERO;
}
else
{
FIXME("Stencil swizzle is not supported for format %#x.\n",
format->dxgi_format);
}
}
if (format->dxgi_format == DXGI_FORMAT_A8_UNORM)
{
if (allowed_swizzle)
{
components->r = VK_COMPONENT_SWIZZLE_ZERO;
components->g = VK_COMPONENT_SWIZZLE_ZERO;
components->b = VK_COMPONENT_SWIZZLE_ZERO;
components->a = VK_COMPONENT_SWIZZLE_R;
}
else
{
FIXME("Alpha swizzle is not supported.\n");
}
}
if (format->dxgi_format == DXGI_FORMAT_B8G8R8X8_UNORM
|| format->dxgi_format == DXGI_FORMAT_B8G8R8X8_UNORM_SRGB)
{
if (allowed_swizzle)
{
components->r = VK_COMPONENT_SWIZZLE_R;
components->g = VK_COMPONENT_SWIZZLE_G;
components->b = VK_COMPONENT_SWIZZLE_B;
components->a = VK_COMPONENT_SWIZZLE_ONE;
}
else
{
FIXME("B8G8R8X8 swizzle is not supported.\n");
}
}
}
static VkComponentSwizzle vk_component_swizzle_from_d3d12(unsigned int component_mapping,
unsigned int component_index)
{
D3D12_SHADER_COMPONENT_MAPPING mapping
= D3D12_DECODE_SHADER_4_COMPONENT_MAPPING(component_index, component_mapping);
switch (mapping)
{
case D3D12_SHADER_COMPONENT_MAPPING_FROM_MEMORY_COMPONENT_0:
return VK_COMPONENT_SWIZZLE_R;
case D3D12_SHADER_COMPONENT_MAPPING_FROM_MEMORY_COMPONENT_1:
return VK_COMPONENT_SWIZZLE_G;
case D3D12_SHADER_COMPONENT_MAPPING_FROM_MEMORY_COMPONENT_2:
return VK_COMPONENT_SWIZZLE_B;
case D3D12_SHADER_COMPONENT_MAPPING_FROM_MEMORY_COMPONENT_3:
return VK_COMPONENT_SWIZZLE_A;
case D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_0:
return VK_COMPONENT_SWIZZLE_ZERO;
case D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_1:
return VK_COMPONENT_SWIZZLE_ONE;
}
FIXME("Invalid component mapping %#x.\n", mapping);
return VK_COMPONENT_SWIZZLE_IDENTITY;
}
static void vk_component_mapping_from_d3d12(VkComponentMapping *components,
unsigned int component_mapping)
{
components->r = vk_component_swizzle_from_d3d12(component_mapping, 0);
components->g = vk_component_swizzle_from_d3d12(component_mapping, 1);
components->b = vk_component_swizzle_from_d3d12(component_mapping, 2);
components->a = vk_component_swizzle_from_d3d12(component_mapping, 3);
}
static VkComponentSwizzle swizzle_vk_component(const VkComponentMapping *components,
VkComponentSwizzle component, VkComponentSwizzle swizzle)
{
switch (swizzle)
{
case VK_COMPONENT_SWIZZLE_IDENTITY:
break;
case VK_COMPONENT_SWIZZLE_R:
component = components->r;
break;
case VK_COMPONENT_SWIZZLE_G:
component = components->g;
break;
case VK_COMPONENT_SWIZZLE_B:
component = components->b;
break;
case VK_COMPONENT_SWIZZLE_A:
component = components->a;
break;
case VK_COMPONENT_SWIZZLE_ONE:
case VK_COMPONENT_SWIZZLE_ZERO:
component = swizzle;
break;
default:
FIXME("Invalid component swizzle %#x.\n", swizzle);
break;
}
assert(component != VK_COMPONENT_SWIZZLE_IDENTITY);
return component;
}
static void vk_component_mapping_compose(VkComponentMapping *dst, const VkComponentMapping *b)
{
const VkComponentMapping a = *dst;
dst->r = swizzle_vk_component(&a, a.r, b->r);
dst->g = swizzle_vk_component(&a, a.g, b->g);
dst->b = swizzle_vk_component(&a, a.b, b->b);
dst->a = swizzle_vk_component(&a, a.a, b->a);
}
static bool init_default_texture_view_desc(struct vkd3d_texture_view_desc *desc,
struct d3d12_resource *resource, DXGI_FORMAT view_format)
{
const struct d3d12_device *device = resource->device;
if (!(desc->format = vkd3d_format_from_d3d12_resource_desc(device, &resource->desc, view_format)))
{
FIXME("Failed to find format (resource format %#x, view format %#x).\n",
resource->desc.Format, view_format);
return false;
}
desc->aspect_mask = desc->format->vk_aspect_mask;
desc->image = resource->res.vk_image;
desc->miplevel_idx = 0;
desc->miplevel_count = 1;
desc->miplevel_clamp = 0.0f;
desc->layer_idx = 0;
desc->layer_count = d3d12_resource_desc_get_layer_count(&resource->desc);
desc->image_usage = 0;
switch (resource->desc.Dimension)
{
case D3D12_RESOURCE_DIMENSION_TEXTURE1D:
desc->view_type = resource->desc.DepthOrArraySize > 1
? VK_IMAGE_VIEW_TYPE_1D_ARRAY : VK_IMAGE_VIEW_TYPE_1D;
break;
case D3D12_RESOURCE_DIMENSION_TEXTURE2D:
desc->view_type = resource->desc.DepthOrArraySize > 1
? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D;
break;
case D3D12_RESOURCE_DIMENSION_TEXTURE3D:
desc->view_type = VK_IMAGE_VIEW_TYPE_3D;
desc->layer_count = 1;
break;
default:
FIXME("Resource dimension %#x not implemented.\n", resource->desc.Dimension);
return false;
}
desc->components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
desc->components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
desc->components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
desc->components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
desc->allowed_swizzle = false;
return true;
}
bool vkd3d_create_texture_view(struct d3d12_device *device, const struct vkd3d_texture_view_desc *desc, struct vkd3d_view **view)
{
const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
VkImageViewUsageCreateInfo image_usage_create_info;
const struct vkd3d_format *format = desc->format;
VkImageViewMinLodCreateInfoEXT min_lod_desc;
VkImageView vk_view = VK_NULL_HANDLE;
VkImageViewCreateInfo view_desc;
struct vkd3d_view *object;
uint32_t clamp_base_level;
uint32_t end_level;
VkResult vr;
view_desc.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
view_desc.pNext = NULL;
view_desc.flags = 0;
view_desc.image = desc->image;
view_desc.viewType = desc->view_type;
view_desc.format = format->vk_format;
vkd3d_set_view_swizzle_for_format(&view_desc.components, format, desc->allowed_swizzle);
if (desc->allowed_swizzle)
vk_component_mapping_compose(&view_desc.components, &desc->components);
view_desc.subresourceRange.aspectMask = desc->aspect_mask;
view_desc.subresourceRange.baseMipLevel = desc->miplevel_idx;
view_desc.subresourceRange.levelCount = desc->miplevel_count;
view_desc.subresourceRange.baseArrayLayer = desc->layer_idx;
view_desc.subresourceRange.layerCount = desc->layer_count;
/* If we clamp out of bounds, then don't make a view
* and use a NULL descriptor to stay in-spec.
* The clamp is absolute, and not affected by the baseMipLevel. */
if (desc->miplevel_clamp <= (float)(desc->miplevel_idx + desc->miplevel_count - 1))
{
if (desc->miplevel_clamp > (float)desc->miplevel_idx)
{
if (device->device_info.image_view_min_lod_features.minLod)
{
min_lod_desc.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_MIN_LOD_CREATE_INFO_EXT;
min_lod_desc.pNext = NULL;
min_lod_desc.minLod = desc->miplevel_clamp;
vk_prepend_struct(&view_desc, &min_lod_desc);
}
else
{
FIXME_ONCE("Cannot handle MinResourceLOD clamp of %f correctly.\n", desc->miplevel_clamp);
/* This is not correct, but it's the best we can do without VK_EXT_image_view_min_lod.
* It should at least avoid a scenario where implicit LOD fetches from invalid levels. */
clamp_base_level = (uint32_t)desc->miplevel_clamp;
end_level = view_desc.subresourceRange.baseMipLevel + view_desc.subresourceRange.levelCount;
view_desc.subresourceRange.levelCount = end_level - clamp_base_level;
view_desc.subresourceRange.baseMipLevel = clamp_base_level;
}
}
image_usage_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO;
image_usage_create_info.pNext = NULL;
image_usage_create_info.usage = desc->image_usage;
vk_prepend_struct(&view_desc, &image_usage_create_info);
if ((vr = VK_CALL(vkCreateImageView(device->vk_device, &view_desc, NULL, &vk_view))) < 0)
{
WARN("Failed to create Vulkan image view, vr %d.\n", vr);
return false;
}
}
if (!(object = vkd3d_view_create(VKD3D_VIEW_TYPE_IMAGE)))
{
VK_CALL(vkDestroyImageView(device->vk_device, vk_view, NULL));
return false;
}
object->vk_image_view = vk_view;
object->format = format;
object->info.texture.vk_view_type = desc->view_type;
object->info.texture.miplevel_idx = desc->miplevel_idx;
object->info.texture.layer_idx = desc->layer_idx;
object->info.texture.layer_count = desc->layer_count;
*view = object;
return true;
}
static inline void vkd3d_init_write_descriptor_set(VkWriteDescriptorSet *vk_write, const struct d3d12_desc_split *split,
struct vkd3d_descriptor_binding binding,
VkDescriptorType vk_descriptor_type, const union vkd3d_descriptor_info *info)
{
vk_write->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
vk_write->pNext = NULL;
vk_write->dstSet = split->heap->sets[binding.set].vk_descriptor_set;
vk_write->dstBinding = binding.binding;
vk_write->dstArrayElement = split->offset;
vk_write->descriptorCount = 1;
vk_write->descriptorType = vk_descriptor_type;
vk_write->pImageInfo = &info->image;
vk_write->pBufferInfo = &info->buffer;
vk_write->pTexelBufferView = &info->buffer_view;
}
static void d3d12_descriptor_heap_write_null_descriptor_template(vkd3d_cpu_descriptor_va_t desc_va,
VkDescriptorType vk_mutable_descriptor_type)
{
/* For null descriptors, some games don't write the correct type (usually an image SRV),
* so we will need to splat null descriptors over all descriptor sets.
* For MUTABLE, this would normally just be one descriptor set, but
* we need MUTABLE + STORAGE_BUFFER, or 6 sets for non-mutable :\ */
VkWriteDescriptorSet writes[VKD3D_MAX_BINDLESS_DESCRIPTOR_SETS];
const struct vkd3d_vk_device_procs *vk_procs;
struct d3d12_desc_split desc;
unsigned int num_writes, i;
unsigned int offset;
VkDeviceAddress *va;
desc = d3d12_desc_decode_va(desc_va);
/* When mutable descriptors are not supported, set a dummy type.
This will make those drivers not care about the null type being different between
null writes. */
if (!desc.heap->null_descriptor_template.has_mutable_descriptors)
vk_mutable_descriptor_type = 0;
/* Skip writes with the same null type that are already null. */
if (!(desc.types->flags & VKD3D_DESCRIPTOR_FLAG_NON_NULL)
&& desc.types->current_null_type == vk_mutable_descriptor_type)
return;
num_writes = desc.heap->null_descriptor_template.num_writes;
vk_procs = &desc.heap->device->vk_procs;
offset = desc.offset;
for (i = 0; i < num_writes; i++)
{
writes[i] = desc.heap->null_descriptor_template.writes[i];
if (writes[i].descriptorType == VK_DESCRIPTOR_TYPE_MUTABLE_VALVE)
writes[i].descriptorType = vk_mutable_descriptor_type;
writes[i].dstArrayElement = offset;
}
if (num_writes)
VK_CALL(vkUpdateDescriptorSets(desc.heap->device->vk_device, num_writes, writes, 0, NULL));
desc.view->cookie = 0;
desc.types->flags = 0;
desc.types->set_info_mask = desc.heap->null_descriptor_template.set_info_mask;
desc.types->current_null_type = vk_mutable_descriptor_type;
memset(desc.view, 0, sizeof(*desc.view));
if (num_writes == 1)
{
desc.types->flags |= VKD3D_DESCRIPTOR_FLAG_SINGLE_DESCRIPTOR;
/* If the template has one descriptor write, this is a single set descriptor heap. */
desc.types->single_binding.set = 0;
desc.types->single_binding.binding = desc.heap->null_descriptor_template.writes[0].dstBinding;
}
va = desc.heap->raw_va_aux_buffer.host_ptr;
if (va)
va[offset] = 0;
/* Notify descriptor QA that we have a universal null descriptor. */
vkd3d_descriptor_debug_write_descriptor(desc.heap->descriptor_heap_info.host_ptr,
desc.heap->cookie, offset,
VKD3D_DESCRIPTOR_QA_TYPE_UNIFORM_BUFFER_BIT |
VKD3D_DESCRIPTOR_QA_TYPE_STORAGE_BUFFER_BIT |
VKD3D_DESCRIPTOR_QA_TYPE_SAMPLED_IMAGE_BIT |
VKD3D_DESCRIPTOR_QA_TYPE_STORAGE_IMAGE_BIT |
VKD3D_DESCRIPTOR_QA_TYPE_UNIFORM_TEXEL_BUFFER_BIT |
VKD3D_DESCRIPTOR_QA_TYPE_STORAGE_TEXEL_BUFFER_BIT |
VKD3D_DESCRIPTOR_QA_TYPE_RAW_VA_BIT |
VKD3D_DESCRIPTOR_QA_TYPE_RT_ACCELERATION_STRUCTURE_BIT, 0);
}
void d3d12_desc_create_cbv(vkd3d_cpu_descriptor_va_t desc_va,
struct d3d12_device *device, const D3D12_CONSTANT_BUFFER_VIEW_DESC *desc)
{
const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
const struct vkd3d_unique_resource *resource = NULL;
union vkd3d_descriptor_info descriptor_info;
struct vkd3d_descriptor_binding binding;
VkDescriptorType vk_descriptor_type;
VkWriteDescriptorSet vk_write;
struct d3d12_desc_split d;
uint32_t info_index;
if (!desc)
{
WARN("Constant buffer desc is NULL.\n");
return;
}
if (desc->SizeInBytes & (D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT - 1))
{
WARN("Size is not %u bytes aligned.\n", D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT);
return;
}
vk_descriptor_type = vkd3d_bindless_state_get_cbv_descriptor_type(&device->bindless_state);
if (!desc->BufferLocation)
{
d3d12_descriptor_heap_write_null_descriptor_template(desc_va, vk_descriptor_type);
return;
}
d = d3d12_desc_decode_va(desc_va);
resource = vkd3d_va_map_deref(&device->memory_allocator.va_map, desc->BufferLocation);
descriptor_info.buffer.buffer = resource->vk_buffer;
descriptor_info.buffer.offset = desc->BufferLocation - resource->va;
descriptor_info.buffer.range = min(desc->SizeInBytes, resource->size - descriptor_info.buffer.offset);
info_index = vkd3d_bindless_state_find_set_info_index(&device->bindless_state, VKD3D_BINDLESS_SET_CBV);
binding = vkd3d_bindless_state_binding_from_info_index(&device->bindless_state, info_index);
d.view->cookie = resource ? resource->cookie : 0;
d.types->set_info_mask = 1u << info_index;
d.types->flags = VKD3D_DESCRIPTOR_FLAG_OFFSET_RANGE | VKD3D_DESCRIPTOR_FLAG_NON_NULL |
VKD3D_DESCRIPTOR_FLAG_SINGLE_DESCRIPTOR;
d.types->single_binding = binding;
d.view->info.buffer = descriptor_info.buffer;
vkd3d_init_write_descriptor_set(&vk_write, &d, binding, vk_descriptor_type, &descriptor_info);
vkd3d_descriptor_debug_write_descriptor(d.heap->descriptor_heap_info.host_ptr,
d.heap->cookie,
d.offset,
vk_descriptor_type == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER ?
VKD3D_DESCRIPTOR_QA_TYPE_UNIFORM_BUFFER_BIT :
VKD3D_DESCRIPTOR_QA_TYPE_STORAGE_BUFFER_BIT,
d.view->cookie);
VK_CALL(vkUpdateDescriptorSets(device->vk_device, 1, &vk_write, 0, NULL));
}
static unsigned int vkd3d_view_flags_from_d3d12_buffer_srv_flags(D3D12_BUFFER_SRV_FLAGS flags)
{
if (flags == D3D12_BUFFER_SRV_FLAG_RAW)
return VKD3D_VIEW_RAW_BUFFER;
if (flags)
FIXME("Unhandled buffer SRV flags %#x.\n", flags);
return 0;
}
static void vkd3d_buffer_view_get_bound_range_ssbo(
struct d3d12_device *device, struct d3d12_resource *resource,
VkDeviceSize offset, VkDeviceSize range, VkDescriptorBufferInfo *vk_buffer,
struct vkd3d_bound_buffer_range *bound_range)
{
if (resource)
{
VkDeviceSize alignment = d3d12_device_get_ssbo_alignment(device);
VkDeviceSize aligned_begin = offset & ~(alignment - 1);
VkDeviceSize aligned_end = min((offset + range + alignment - 1) & ~(alignment - 1), resource->desc.Width);
/* heap_offset is guaranteed to have 64KiB alignment */
vk_buffer->buffer = resource->res.vk_buffer;
vk_buffer->offset = resource->mem.offset + aligned_begin;
vk_buffer->range = aligned_end - aligned_begin;
bound_range->byte_offset = offset - aligned_begin;
bound_range->byte_count = range;
}
else
{
vk_buffer->buffer = VK_NULL_HANDLE;
vk_buffer->offset = 0;
vk_buffer->range = VK_WHOLE_SIZE;
bound_range->byte_offset = 0;
bound_range->byte_count = 0;
}
}
static bool vkd3d_buffer_view_get_aligned_view(
struct d3d12_device *device, struct d3d12_resource *resource,
DXGI_FORMAT format, unsigned int vk_flags,
VkDeviceSize first_element, VkDeviceSize num_elements,
VkDeviceSize structured_stride, struct vkd3d_bound_buffer_range *bound_range,
struct vkd3d_view **view)
{
const struct vkd3d_format *vkd3d_format;
VkDeviceSize max_resource_elements;
VkDeviceSize max_element_headroom;
VkDeviceSize element_align;
VkDeviceSize max_elements;
VkDeviceSize begin_range;
VkDeviceSize end_range;
if (device->bindless_state.flags & VKD3D_TYPED_OFFSET_BUFFER)
{
/* For typed buffers, we will try to remove two cases of extreme hashmap contention, i.e.
* first_element and num_elements. By quantizing these two and relying on offset buffers,
* we should achieve a bounded value for number of possible views we can create for a given resource. */
max_elements = device->device_info.properties2.properties.limits.maxTexelBufferElements;
if (format)
{
vkd3d_format = vkd3d_get_format(device, format, false);
max_resource_elements = resource->desc.Width / vkd3d_format->byte_count;
}
else
{
/* For structured buffers, we need to rescale input parameters to
* be in terms of u32 since the offset buffer must be in terms of words.
* When using typed buffers, the offset buffer is in format of u32
* (element offset, element size). */
first_element = (first_element * structured_stride) / sizeof(uint32_t);
num_elements = (num_elements * structured_stride) / sizeof(uint32_t);
structured_stride = sizeof(uint32_t);
max_resource_elements = resource->desc.Width / sizeof(uint32_t);
}
/* Requantizing the typed offset is shaky business if we overflow max_elements when doing so.
* We can always fall back to 0 offset for the difficult and rare cases. */
if (num_elements > max_elements)
{
FIXME("Application is attempting to use more elements in a typed buffer (%llu) than supported by device (%llu).\n",
(unsigned long long)num_elements, (unsigned long long)max_elements);
bound_range->element_offset = 0;
bound_range->element_count = num_elements;
}
else if (num_elements >= max_resource_elements)
{
bound_range->element_offset = 0;
bound_range->element_count = num_elements;
}
else
{
/* Quantizing to alignment of N will at most increment number of elements in the view by N - 1. */
max_element_headroom = max_elements - num_elements + 1;
/* Based on headroom, align offset to the largest POT factor of N. */
element_align = 1u << vkd3d_log2i(max_element_headroom);
begin_range = first_element & ~(element_align - 1);
end_range = (first_element + num_elements + element_align - 1) & ~(element_align - 1);
end_range = min(end_range, max_resource_elements);
bound_range->element_offset = first_element - begin_range;
bound_range->element_count = num_elements;
first_element = begin_range;
num_elements = end_range - begin_range;
}
}
if (!vkd3d_create_buffer_view_for_resource(device, resource, format,
first_element, num_elements,
structured_stride, vk_flags, view))
return false;
return true;
}
static void vkd3d_create_buffer_srv(vkd3d_cpu_descriptor_va_t desc_va,
struct d3d12_device *device, struct d3d12_resource *resource,
const D3D12_SHADER_RESOURCE_VIEW_DESC *desc)
{
const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
VKD3D_UNUSED vkd3d_descriptor_qa_flags descriptor_qa_flags = 0;
struct vkd3d_bound_buffer_range bound_range = { 0, 0, 0, 0 };
union vkd3d_descriptor_info descriptor_info[2];
struct vkd3d_descriptor_binding binding;
VkDescriptorType vk_descriptor_type;
bool mutable_uses_single_descriptor;
VkWriteDescriptorSet vk_write[2];
struct vkd3d_view *view = NULL;
uint32_t vk_write_count = 0;
struct d3d12_desc_split d;
unsigned int vk_flags;
uint32_t info_index;
bool desc_is_raw;
bool emit_typed;
bool emit_ssbo;
if (!desc)
{
FIXME("Default buffer SRV not supported.\n");
return;
}
d = d3d12_desc_decode_va(desc_va);
if (desc->ViewDimension == D3D12_SRV_DIMENSION_RAYTRACING_ACCELERATION_STRUCTURE)
{
if (!desc->RaytracingAccelerationStructure.Location)
{
/* There is no concrete descriptor to use here,
* so just write a SAMPLED_IMAGE to clear out mutable descriptor.
* What we really want to clear here is the raw VA. */
d3d12_descriptor_heap_write_null_descriptor_template(desc_va, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE);
return;
}
if (d3d12_device_supports_ray_tracing_tier_1_0(device))
{
/* We implement this as a raw VA in the aux buffer. */
VkDeviceAddress *raw_addresses = d.heap->raw_va_aux_buffer.host_ptr;
uint32_t descriptor_index = d.offset;
raw_addresses[descriptor_index] = desc->RaytracingAccelerationStructure.Location;
d.types->flags = VKD3D_DESCRIPTOR_FLAG_RAW_VA_AUX_BUFFER |
VKD3D_DESCRIPTOR_FLAG_NON_NULL;
d.types->set_info_mask = 0;
/* There is no resource tied to this descriptor, just a naked pointer. */
d.view->cookie = 0;
}
else
WARN("Using CreateSRV for RTAS without RT support?\n");
vkd3d_descriptor_debug_write_descriptor(d.heap->descriptor_heap_info.host_ptr,
d.heap->cookie, d.offset,
VKD3D_DESCRIPTOR_QA_TYPE_RT_ACCELERATION_STRUCTURE_BIT | VKD3D_DESCRIPTOR_QA_TYPE_RAW_VA_BIT,
d.view->cookie);
return;
}
if (desc->ViewDimension != D3D12_SRV_DIMENSION_BUFFER)
{
WARN("Unexpected view dimension %#x.\n", desc->ViewDimension);
return;
}
mutable_uses_single_descriptor = !!(device->bindless_state.flags & VKD3D_BINDLESS_MUTABLE_TYPE_RAW_SSBO);
desc_is_raw = (desc->Format == DXGI_FORMAT_UNKNOWN && desc->Buffer.StructureByteStride) ||
(desc->Buffer.Flags & D3D12_BUFFER_SRV_FLAG_RAW);
emit_ssbo = !mutable_uses_single_descriptor || desc_is_raw;
emit_typed = !mutable_uses_single_descriptor || !desc_is_raw;
if (!resource)
{
if (mutable_uses_single_descriptor)
{
d3d12_descriptor_heap_write_null_descriptor_template(desc_va,
desc_is_raw ? VK_DESCRIPTOR_TYPE_STORAGE_BUFFER : VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER);
}
else
{
/* In the mutable set, always write texel buffer. The STORAGE_BUFFER set is also written to. */
d3d12_descriptor_heap_write_null_descriptor_template(desc_va,
VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER);
}
return;
}
d.types->set_info_mask = 0;
d.types->flags = 0;
if (d3d12_device_use_ssbo_raw_buffer(device) && emit_ssbo)
{
VkDeviceSize stride = desc->Format == DXGI_FORMAT_UNKNOWN
? desc->Buffer.StructureByteStride :
vkd3d_get_format(device, desc->Format, false)->byte_count;
vkd3d_buffer_view_get_bound_range_ssbo(device, resource,
desc->Buffer.FirstElement * stride, desc->Buffer.NumElements * stride,
&descriptor_info[vk_write_count].buffer, &bound_range);
info_index = vkd3d_bindless_state_find_set_info_index(&device->bindless_state,
VKD3D_BINDLESS_SET_SRV | VKD3D_BINDLESS_SET_RAW_SSBO);
binding = vkd3d_bindless_state_binding_from_info_index(&device->bindless_state, info_index);
d.view->info.buffer = descriptor_info[vk_write_count].buffer;
d.view->cookie = resource ? resource->res.cookie : 0;
d.types->set_info_mask |= 1u << info_index;
d.types->flags |= VKD3D_DESCRIPTOR_FLAG_OFFSET_RANGE |
VKD3D_DESCRIPTOR_FLAG_NON_NULL;
if (device->bindless_state.flags & VKD3D_SSBO_OFFSET_BUFFER)
d.types->flags |= VKD3D_DESCRIPTOR_FLAG_BUFFER_OFFSET;
d.types->single_binding = binding;
vk_descriptor_type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
descriptor_qa_flags |= VKD3D_DESCRIPTOR_QA_TYPE_STORAGE_BUFFER_BIT;
vkd3d_init_write_descriptor_set(&vk_write[vk_write_count], &d, binding,
vk_descriptor_type, &descriptor_info[vk_write_count]);
vk_write_count++;
}
if (emit_typed)
{
vk_flags = vkd3d_view_flags_from_d3d12_buffer_srv_flags(desc->Buffer.Flags);
if (!vkd3d_buffer_view_get_aligned_view(device, resource, desc->Format, vk_flags,
desc->Buffer.FirstElement, desc->Buffer.NumElements, desc->Buffer.StructureByteStride,
&bound_range, &view))
return;
descriptor_info[vk_write_count].buffer_view = view ? view->vk_buffer_view : VK_NULL_HANDLE;
info_index = vkd3d_bindless_state_find_set_info_index(&device->bindless_state,
VKD3D_BINDLESS_SET_SRV | VKD3D_BINDLESS_SET_BUFFER);
binding = vkd3d_bindless_state_binding_from_info_index(&device->bindless_state, info_index);
d.view->info.view = view;
/* Typed cookie takes precedence over raw cookie.
* The typed cookie is more unique than raw cookie,
* since raw cookie is just the ID3D12Resource. */
d.view->cookie = view ? view->cookie : 0;
d.types->set_info_mask |= 1u << info_index;
d.types->flags |= VKD3D_DESCRIPTOR_FLAG_VIEW | VKD3D_DESCRIPTOR_FLAG_NON_NULL;
if (device->bindless_state.flags & VKD3D_TYPED_OFFSET_BUFFER)
d.types->flags |= VKD3D_DESCRIPTOR_FLAG_BUFFER_OFFSET;
d.types->single_binding = binding;
vk_descriptor_type = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;
descriptor_qa_flags |= VKD3D_DESCRIPTOR_QA_TYPE_UNIFORM_TEXEL_BUFFER_BIT;
vkd3d_init_write_descriptor_set(&vk_write[vk_write_count], &d, binding,
vk_descriptor_type, &descriptor_info[vk_write_count]);
vk_write_count++;
}
if (d.types->flags & VKD3D_DESCRIPTOR_FLAG_BUFFER_OFFSET)
{
struct vkd3d_bound_buffer_range *buffer_ranges = d.heap->buffer_ranges.host_ptr;
buffer_ranges[d.offset] = bound_range;
}
if (mutable_uses_single_descriptor)
d.types->flags |= VKD3D_DESCRIPTOR_FLAG_SINGLE_DESCRIPTOR;
vkd3d_descriptor_debug_write_descriptor(d.heap->descriptor_heap_info.host_ptr,
d.heap->cookie, d.offset, descriptor_qa_flags, d.view->cookie);
if (vk_write_count)
VK_CALL(vkUpdateDescriptorSets(device->vk_device, vk_write_count, vk_write, 0, NULL));
}
static void vkd3d_create_texture_srv(vkd3d_cpu_descriptor_va_t desc_va,
struct d3d12_device *device, struct d3d12_resource *resource,
const D3D12_SHADER_RESOURCE_VIEW_DESC *desc)
{
const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
union vkd3d_descriptor_info descriptor_info;
struct vkd3d_descriptor_binding binding;
struct vkd3d_view *view = NULL;
VkWriteDescriptorSet vk_write;
struct vkd3d_view_key key;
struct d3d12_desc_split d;
uint32_t info_index;
if (!resource)
{
d3d12_descriptor_heap_write_null_descriptor_template(desc_va, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE);
return;
}
d = d3d12_desc_decode_va(desc_va);
if (!init_default_texture_view_desc(&key.u.texture, resource, desc ? desc->Format : 0))
return;
key.view_type = VKD3D_VIEW_TYPE_IMAGE;
key.u.texture.miplevel_count = VK_REMAINING_MIP_LEVELS;
key.u.texture.allowed_swizzle = true;
key.u.texture.image_usage = VK_IMAGE_USAGE_SAMPLED_BIT;
if (desc)
{
if (desc->Shader4ComponentMapping != D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING)
{
TRACE("Component mapping %s for format %#x.\n",
debug_d3d12_shader_component_mapping(desc->Shader4ComponentMapping), desc->Format);
vk_component_mapping_from_d3d12(&key.u.texture.components, desc->Shader4ComponentMapping);
}
switch (desc->ViewDimension)
{
case D3D12_SRV_DIMENSION_TEXTURE1D:
key.u.texture.view_type = VK_IMAGE_VIEW_TYPE_1D;
key.u.texture.miplevel_idx = desc->Texture1D.MostDetailedMip;
key.u.texture.miplevel_count = desc->Texture1D.MipLevels;
key.u.texture.miplevel_clamp = desc->Texture1D.ResourceMinLODClamp;
key.u.texture.layer_count = 1;
break;
case D3D12_SRV_DIMENSION_TEXTURE1DARRAY:
key.u.texture.view_type = VK_IMAGE_VIEW_TYPE_1D_ARRAY;
key.u.texture.miplevel_idx = desc->Texture1DArray.MostDetailedMip;
key.u.texture.miplevel_count = desc->Texture1DArray.MipLevels;
key.u.texture.miplevel_clamp = desc->Texture1DArray.ResourceMinLODClamp;
key.u.texture.layer_idx = desc->Texture1DArray.FirstArraySlice;
key.u.texture.layer_count = desc->Texture1DArray.ArraySize;
break;
case D3D12_SRV_DIMENSION_TEXTURE2D:
key.u.texture.view_type = VK_IMAGE_VIEW_TYPE_2D;
key.u.texture.miplevel_idx = desc->Texture2D.MostDetailedMip;
key.u.texture.miplevel_count = desc->Texture2D.MipLevels;
key.u.texture.miplevel_clamp = desc->Texture2D.ResourceMinLODClamp;
key.u.texture.layer_count = 1;
key.u.texture.aspect_mask = vk_image_aspect_flags_from_d3d12(resource->format, desc->Texture2D.PlaneSlice);
break;
case D3D12_SRV_DIMENSION_TEXTURE2DARRAY:
key.u.texture.view_type = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
key.u.texture.miplevel_idx = desc->Texture2DArray.MostDetailedMip;
key.u.texture.miplevel_count = desc->Texture2DArray.MipLevels;
key.u.texture.miplevel_clamp = desc->Texture2DArray.ResourceMinLODClamp;
key.u.texture.layer_idx = desc->Texture2DArray.FirstArraySlice;
key.u.texture.layer_count = desc->Texture2DArray.ArraySize;
key.u.texture.aspect_mask = vk_image_aspect_flags_from_d3d12(resource->format, desc->Texture2DArray.PlaneSlice);
break;
case D3D12_SRV_DIMENSION_TEXTURE2DMS:
key.u.texture.view_type = VK_IMAGE_VIEW_TYPE_2D;
key.u.texture.layer_count = 1;
break;
case D3D12_SRV_DIMENSION_TEXTURE2DMSARRAY:
key.u.texture.view_type = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
key.u.texture.layer_idx = desc->Texture2DMSArray.FirstArraySlice;
key.u.texture.layer_count = desc->Texture2DMSArray.ArraySize;
break;
case D3D12_SRV_DIMENSION_TEXTURE3D:
key.u.texture.view_type = VK_IMAGE_VIEW_TYPE_3D;
key.u.texture.miplevel_idx = desc->Texture3D.MostDetailedMip;
key.u.texture.miplevel_count = desc->Texture3D.MipLevels;
key.u.texture.miplevel_clamp = desc->Texture3D.ResourceMinLODClamp;
break;
case D3D12_SRV_DIMENSION_TEXTURECUBE:
key.u.texture.view_type = VK_IMAGE_VIEW_TYPE_CUBE;
key.u.texture.miplevel_idx = desc->TextureCube.MostDetailedMip;
key.u.texture.miplevel_count = desc->TextureCube.MipLevels;
key.u.texture.miplevel_clamp = desc->TextureCube.ResourceMinLODClamp;
key.u.texture.layer_count = 6;
break;
case D3D12_SRV_DIMENSION_TEXTURECUBEARRAY:
key.u.texture.view_type = VK_IMAGE_VIEW_TYPE_CUBE_ARRAY;
key.u.texture.miplevel_idx = desc->TextureCubeArray.MostDetailedMip;
key.u.texture.miplevel_count = desc->TextureCubeArray.MipLevels;
key.u.texture.miplevel_clamp = desc->TextureCubeArray.ResourceMinLODClamp;
key.u.texture.layer_idx = desc->TextureCubeArray.First2DArrayFace;
key.u.texture.layer_count = desc->TextureCubeArray.NumCubes;
if (key.u.texture.layer_count != VK_REMAINING_ARRAY_LAYERS)
key.u.texture.layer_count *= 6;
break;
default:
FIXME("Unhandled view dimension %#x.\n", desc->ViewDimension);
}
}
if (key.u.texture.miplevel_count == VK_REMAINING_MIP_LEVELS)
key.u.texture.miplevel_count = resource->desc.MipLevels - key.u.texture.miplevel_idx;
if (!(view = vkd3d_view_map_create_view(&resource->view_map, device, &key)))
return;
descriptor_info.image.sampler = VK_NULL_HANDLE;
descriptor_info.image.imageView = view ? view->vk_image_view : VK_NULL_HANDLE;
descriptor_info.image.imageLayout = view ? resource->common_layout : VK_IMAGE_LAYOUT_UNDEFINED;
info_index = vkd3d_bindless_state_find_set_info_index(&device->bindless_state,
VKD3D_BINDLESS_SET_SRV | VKD3D_BINDLESS_SET_IMAGE);
binding = vkd3d_bindless_state_binding_from_info_index(&device->bindless_state, info_index);
d.view->info.view = view;
d.view->cookie = view ? view->cookie : 0;
d.types->set_info_mask = 1u << info_index;
d.types->flags = VKD3D_DESCRIPTOR_FLAG_VIEW | VKD3D_DESCRIPTOR_FLAG_NON_NULL |
VKD3D_DESCRIPTOR_FLAG_SINGLE_DESCRIPTOR;
d.types->single_binding = binding;
vkd3d_init_write_descriptor_set(&vk_write, &d, binding,
VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, &descriptor_info);
vkd3d_descriptor_debug_write_descriptor(d.heap->descriptor_heap_info.host_ptr,
d.heap->cookie, d.offset,
VKD3D_DESCRIPTOR_QA_TYPE_SAMPLED_IMAGE_BIT, d.view->cookie);
VK_CALL(vkUpdateDescriptorSets(device->vk_device, 1, &vk_write, 0, NULL));
}
void d3d12_desc_create_srv(vkd3d_cpu_descriptor_va_t desc_va,
struct d3d12_device *device, struct d3d12_resource *resource,
const D3D12_SHADER_RESOURCE_VIEW_DESC *desc)
{
bool is_buffer;
if (resource)
{
is_buffer = d3d12_resource_is_buffer(resource);
}
else if (desc)
{
is_buffer = desc->ViewDimension == D3D12_SRV_DIMENSION_BUFFER ||
desc->ViewDimension == D3D12_SRV_DIMENSION_RAYTRACING_ACCELERATION_STRUCTURE;
}
else
{
WARN("Description required for NULL SRV.");
return;
}
if (is_buffer)
vkd3d_create_buffer_srv(desc_va, device, resource, desc);
else
vkd3d_create_texture_srv(desc_va, device, resource, desc);
}
static unsigned int vkd3d_view_flags_from_d3d12_buffer_uav_flags(D3D12_BUFFER_UAV_FLAGS flags)
{
if (flags == D3D12_BUFFER_UAV_FLAG_RAW)
return VKD3D_VIEW_RAW_BUFFER;
if (flags)
FIXME("Unhandled buffer UAV flags %#x.\n", flags);
return 0;
}
VkDeviceAddress vkd3d_get_buffer_device_address(struct d3d12_device *device, VkBuffer vk_buffer)
{
const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
VkBufferDeviceAddressInfoKHR address_info;
address_info.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO_KHR;
address_info.pNext = NULL;
address_info.buffer = vk_buffer;
return VK_CALL(vkGetBufferDeviceAddressKHR(device->vk_device, &address_info));
}
VkDeviceAddress vkd3d_get_acceleration_structure_device_address(struct d3d12_device *device,
VkAccelerationStructureKHR vk_acceleration_structure)
{
const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
VkAccelerationStructureDeviceAddressInfoKHR address_info;
address_info.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_DEVICE_ADDRESS_INFO_KHR;
address_info.pNext = NULL;
address_info.accelerationStructure = vk_acceleration_structure;
return VK_CALL(vkGetAccelerationStructureDeviceAddressKHR(device->vk_device, &address_info));
}
static void vkd3d_create_buffer_uav(vkd3d_cpu_descriptor_va_t desc_va, struct d3d12_device *device,
struct d3d12_resource *resource, struct d3d12_resource *counter_resource,
const D3D12_UNORDERED_ACCESS_VIEW_DESC *desc)
{
const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
VKD3D_UNUSED vkd3d_descriptor_qa_flags descriptor_qa_flags = 0;
struct vkd3d_bound_buffer_range bound_range = { 0, 0, 0, 0 };
union vkd3d_descriptor_info descriptor_info[3];
struct vkd3d_descriptor_binding binding;
unsigned int flags, vk_write_count = 0;
bool mutable_uses_single_descriptor;
VkDescriptorType vk_descriptor_type;
VkDeviceAddress uav_counter_address;
VkWriteDescriptorSet vk_write[3];
struct vkd3d_view *view = NULL;
VkBufferView uav_counter_view;
struct d3d12_desc_split d;
uint32_t info_index;
bool desc_is_raw;
bool emit_typed;
bool emit_ssbo;
if (!desc)
{
FIXME("Default buffer UAV not supported.\n");
return;
}
if (desc->ViewDimension != D3D12_UAV_DIMENSION_BUFFER)
{
WARN("Unexpected view dimension %#x.\n", desc->ViewDimension);
return;
}
mutable_uses_single_descriptor = !!(device->bindless_state.flags & VKD3D_BINDLESS_MUTABLE_TYPE_RAW_SSBO);
desc_is_raw = (desc->Format == DXGI_FORMAT_UNKNOWN && desc->Buffer.StructureByteStride) ||
(desc->Buffer.Flags & D3D12_BUFFER_UAV_FLAG_RAW);
emit_ssbo = !mutable_uses_single_descriptor || desc_is_raw;
emit_typed = !mutable_uses_single_descriptor || !desc_is_raw;
if (!resource)
{
if (mutable_uses_single_descriptor)
{
d3d12_descriptor_heap_write_null_descriptor_template(desc_va,
desc_is_raw ? VK_DESCRIPTOR_TYPE_STORAGE_BUFFER : VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER);
}
else
{
/* In the mutable set, always write texel buffer. The STORAGE_BUFFER set is also written to. */
d3d12_descriptor_heap_write_null_descriptor_template(desc_va,
VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER);
}
return;
}
d = d3d12_desc_decode_va(desc_va);
/* Handle UAV itself */
d.types->set_info_mask = 0;
d.types->flags = VKD3D_DESCRIPTOR_FLAG_RAW_VA_AUX_BUFFER |
VKD3D_DESCRIPTOR_FLAG_NON_NULL;
if (d3d12_device_use_ssbo_raw_buffer(device) && emit_ssbo)
{
VkDescriptorBufferInfo *buffer_info = &descriptor_info[vk_write_count].buffer;
VkDeviceSize stride = desc->Format == DXGI_FORMAT_UNKNOWN
? desc->Buffer.StructureByteStride :
vkd3d_get_format(device, desc->Format, false)->byte_count;
vkd3d_buffer_view_get_bound_range_ssbo(device, resource,
desc->Buffer.FirstElement * stride, desc->Buffer.NumElements * stride,
buffer_info, &bound_range);
info_index = vkd3d_bindless_state_find_set_info_index(&device->bindless_state,
VKD3D_BINDLESS_SET_UAV | VKD3D_BINDLESS_SET_RAW_SSBO);
binding = vkd3d_bindless_state_binding_from_info_index(&device->bindless_state, info_index);
d.view->info.buffer = *buffer_info;
d.view->cookie = resource ? resource->res.cookie : 0;
d.types->set_info_mask |= 1u << info_index;
d.types->flags |= VKD3D_DESCRIPTOR_FLAG_OFFSET_RANGE;
if (device->bindless_state.flags & VKD3D_SSBO_OFFSET_BUFFER)
d.types->flags |= VKD3D_DESCRIPTOR_FLAG_BUFFER_OFFSET;
d.types->single_binding = binding;
vk_descriptor_type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
descriptor_qa_flags |= VKD3D_DESCRIPTOR_QA_TYPE_STORAGE_BUFFER_BIT;
vkd3d_init_write_descriptor_set(&vk_write[vk_write_count], &d, binding,
vk_descriptor_type, &descriptor_info[vk_write_count]);
vk_write_count++;
}
if (emit_typed)
{
flags = vkd3d_view_flags_from_d3d12_buffer_uav_flags(desc->Buffer.Flags);
if (!vkd3d_buffer_view_get_aligned_view(device, resource, desc->Format, flags,
desc->Buffer.FirstElement, desc->Buffer.NumElements,
desc->Buffer.StructureByteStride, &bound_range, &view))
return;
info_index = vkd3d_bindless_state_find_set_info_index(&device->bindless_state,
VKD3D_BINDLESS_SET_UAV | VKD3D_BINDLESS_SET_BUFFER);
binding = vkd3d_bindless_state_binding_from_info_index(&device->bindless_state, info_index);
d.view->info.view = view;
/* Typed cookie takes precedence over raw cookie.
* The typed cookie is more unique than raw cookie,
* since raw cookie is just the ID3D12Resource. */
d.view->cookie = view ? view->cookie : 0;
d.types->set_info_mask |= 1u << info_index;
d.types->flags |= VKD3D_DESCRIPTOR_FLAG_VIEW;
if (device->bindless_state.flags & VKD3D_TYPED_OFFSET_BUFFER)
d.types->flags |= VKD3D_DESCRIPTOR_FLAG_BUFFER_OFFSET;
d.types->single_binding = binding;
descriptor_info[vk_write_count].buffer_view = view ? view->vk_buffer_view : VK_NULL_HANDLE;
vk_descriptor_type = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER;
descriptor_qa_flags |= VKD3D_DESCRIPTOR_QA_TYPE_STORAGE_TEXEL_BUFFER_BIT;
vkd3d_init_write_descriptor_set(&vk_write[vk_write_count], &d, binding,
vk_descriptor_type, &descriptor_info[vk_write_count]);
vk_write_count++;
}
if (d.types->flags & VKD3D_DESCRIPTOR_FLAG_BUFFER_OFFSET)
{
struct vkd3d_bound_buffer_range *buffer_ranges = d.heap->buffer_ranges.host_ptr;
buffer_ranges[d.offset] = bound_range;
}
if (mutable_uses_single_descriptor)
d.types->flags |= VKD3D_DESCRIPTOR_FLAG_SINGLE_DESCRIPTOR;
/* Handle UAV counter */
uav_counter_view = VK_NULL_HANDLE;
uav_counter_address = 0;
if (resource && counter_resource)
{
assert(d3d12_resource_is_buffer(counter_resource));
assert(desc->Buffer.StructureByteStride);
if (device->bindless_state.flags & VKD3D_RAW_VA_AUX_BUFFER)
{
VkDeviceAddress address = vkd3d_get_buffer_device_address(device, counter_resource->res.vk_buffer);
uav_counter_address = address + counter_resource->mem.offset + desc->Buffer.CounterOffsetInBytes;
}
else
{
struct vkd3d_view *view;
if (!vkd3d_create_buffer_view_for_resource(device, counter_resource, DXGI_FORMAT_R32_UINT,
desc->Buffer.CounterOffsetInBytes / sizeof(uint32_t), 1, 0, 0, &view))
return;
uav_counter_view = view->vk_buffer_view;
}
/* This is used to denote that a counter descriptor is present, irrespective of underlying descriptor type. */
descriptor_qa_flags |= VKD3D_DESCRIPTOR_QA_TYPE_RAW_VA_BIT;
}
if (device->bindless_state.flags & VKD3D_RAW_VA_AUX_BUFFER)
{
VkDeviceAddress *counter_addresses = d.heap->raw_va_aux_buffer.host_ptr;
uint32_t descriptor_index = d.offset;
counter_addresses[descriptor_index] = uav_counter_address;
}
else
{
struct vkd3d_descriptor_binding binding = vkd3d_bindless_state_find_set(
&device->bindless_state, VKD3D_BINDLESS_SET_UAV | VKD3D_BINDLESS_SET_AUX_BUFFER);
descriptor_info[vk_write_count].buffer_view = uav_counter_view;
vkd3d_init_write_descriptor_set(&vk_write[vk_write_count], &d,
binding,
VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, &descriptor_info[vk_write_count]);
vk_write_count++;
}
vkd3d_descriptor_debug_write_descriptor(d.heap->descriptor_heap_info.host_ptr,
d.heap->cookie, d.offset,
descriptor_qa_flags, d.view->cookie);
VK_CALL(vkUpdateDescriptorSets(device->vk_device, vk_write_count, vk_write, 0, NULL));
}
static void vkd3d_create_texture_uav(vkd3d_cpu_descriptor_va_t desc_va,
struct d3d12_device *device, struct d3d12_resource *resource,
const D3D12_UNORDERED_ACCESS_VIEW_DESC *desc)
{
const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
union vkd3d_descriptor_info descriptor_info;
struct vkd3d_descriptor_binding binding;
struct vkd3d_view *view = NULL;
VkWriteDescriptorSet vk_write;
struct d3d12_desc_split d;
struct vkd3d_view_key key;
uint32_t info_index;
if (!resource)
{
d3d12_descriptor_heap_write_null_descriptor_template(desc_va, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE);
return;
}
d = d3d12_desc_decode_va(desc_va);
key.view_type = VKD3D_VIEW_TYPE_IMAGE;
if (!init_default_texture_view_desc(&key.u.texture, resource, desc ? desc->Format : 0))
return;
key.u.texture.image_usage = VK_IMAGE_USAGE_STORAGE_BIT;
if (vkd3d_format_is_compressed(key.u.texture.format))
{
WARN("UAVs cannot be created for compressed formats.\n");
return;
}
if (desc)
{
switch (desc->ViewDimension)
{
case D3D12_UAV_DIMENSION_TEXTURE1D:
key.u.texture.view_type = VK_IMAGE_VIEW_TYPE_1D;
key.u.texture.miplevel_idx = desc->Texture1D.MipSlice;
key.u.texture.layer_count = 1;
break;
case D3D12_UAV_DIMENSION_TEXTURE1DARRAY:
key.u.texture.view_type = VK_IMAGE_VIEW_TYPE_1D_ARRAY;
key.u.texture.miplevel_idx = desc->Texture1DArray.MipSlice;
key.u.texture.layer_idx = desc->Texture1DArray.FirstArraySlice;
key.u.texture.layer_count = desc->Texture1DArray.ArraySize;
break;
case D3D12_UAV_DIMENSION_TEXTURE2D:
key.u.texture.view_type = VK_IMAGE_VIEW_TYPE_2D;
key.u.texture.miplevel_idx = desc->Texture2D.MipSlice;
key.u.texture.layer_count = 1;
key.u.texture.aspect_mask = vk_image_aspect_flags_from_d3d12(resource->format, desc->Texture2D.PlaneSlice);
break;
case D3D12_UAV_DIMENSION_TEXTURE2DARRAY:
key.u.texture.view_type = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
key.u.texture.miplevel_idx = desc->Texture2DArray.MipSlice;
key.u.texture.layer_idx = desc->Texture2DArray.FirstArraySlice;
key.u.texture.layer_count = desc->Texture2DArray.ArraySize;
key.u.texture.aspect_mask = vk_image_aspect_flags_from_d3d12(resource->format, desc->Texture2DArray.PlaneSlice);
break;
case D3D12_UAV_DIMENSION_TEXTURE3D:
key.u.texture.view_type = VK_IMAGE_VIEW_TYPE_3D;
key.u.texture.miplevel_idx = desc->Texture3D.MipSlice;
if (desc->Texture3D.FirstWSlice ||
((desc->Texture3D.WSize != max(1u, (UINT)resource->desc.DepthOrArraySize >> desc->Texture3D.MipSlice)) &&
(desc->Texture3D.WSize != UINT_MAX)))
{
FIXME("Unhandled depth view %u-%u.\n",
desc->Texture3D.FirstWSlice, desc->Texture3D.WSize);
}
break;
default:
FIXME("Unhandled view dimension %#x.\n", desc->ViewDimension);
}
}
if (!(view = vkd3d_view_map_create_view(&resource->view_map, device, &key)))
return;
descriptor_info.image.sampler = VK_NULL_HANDLE;
descriptor_info.image.imageView = view ? view->vk_image_view : VK_NULL_HANDLE;
descriptor_info.image.imageLayout = view ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_UNDEFINED;
info_index = vkd3d_bindless_state_find_set_info_index(&device->bindless_state,
VKD3D_BINDLESS_SET_UAV | VKD3D_BINDLESS_SET_IMAGE);
binding = vkd3d_bindless_state_binding_from_info_index(&device->bindless_state, info_index);
d.view->info.view = view;
d.view->cookie = view ? view->cookie : 0;
d.types->set_info_mask = 1u << info_index;
d.types->flags = VKD3D_DESCRIPTOR_FLAG_VIEW | VKD3D_DESCRIPTOR_FLAG_NON_NULL |
VKD3D_DESCRIPTOR_FLAG_SINGLE_DESCRIPTOR;
d.types->single_binding = binding;
vkd3d_init_write_descriptor_set(&vk_write, &d, binding,
VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, &descriptor_info);
vkd3d_descriptor_debug_write_descriptor(d.heap->descriptor_heap_info.host_ptr,
d.heap->cookie, d.offset,
VKD3D_DESCRIPTOR_QA_TYPE_STORAGE_IMAGE_BIT, d.view->cookie);
VK_CALL(vkUpdateDescriptorSets(device->vk_device, 1, &vk_write, 0, NULL));
}
void d3d12_desc_create_uav(vkd3d_cpu_descriptor_va_t desc_va, struct d3d12_device *device,
struct d3d12_resource *resource, struct d3d12_resource *counter_resource,
const D3D12_UNORDERED_ACCESS_VIEW_DESC *desc)
{
bool is_buffer;
if (resource)
{
is_buffer = d3d12_resource_is_buffer(resource);
}
else if (desc)
{
is_buffer = desc->ViewDimension == D3D12_UAV_DIMENSION_BUFFER;
}
else
{
WARN("Description required for NULL UAV.");
return;
}
if (counter_resource && (!resource || !is_buffer))
FIXME("Ignoring counter resource %p.\n", counter_resource);
if (is_buffer)
vkd3d_create_buffer_uav(desc_va, device, resource, counter_resource, desc);
else
vkd3d_create_texture_uav(desc_va, device, resource, desc);
}
bool vkd3d_create_raw_buffer_view(struct d3d12_device *device,
D3D12_GPU_VIRTUAL_ADDRESS gpu_address, VkBufferView *vk_buffer_view)
{
const struct vkd3d_unique_resource *resource;
uint64_t range;
uint64_t offset;
resource = vkd3d_va_map_deref(&device->memory_allocator.va_map, gpu_address);
assert(resource && resource->va && resource->size);
offset = gpu_address - resource->va;
range = min(resource->size - offset, device->vk_info.device_limits.maxStorageBufferRange);
return vkd3d_create_raw_r32ui_vk_buffer_view(device, resource->vk_buffer,
offset, range, vk_buffer_view);
}
/* samplers */
static VkFilter vk_filter_from_d3d12(D3D12_FILTER_TYPE type)
{
switch (type)
{
case D3D12_FILTER_TYPE_POINT:
return VK_FILTER_NEAREST;
case D3D12_FILTER_TYPE_LINEAR:
return VK_FILTER_LINEAR;
default:
FIXME("Unhandled filter type %#x.\n", type);
return VK_FILTER_NEAREST;
}
}
static VkSamplerMipmapMode vk_mipmap_mode_from_d3d12(D3D12_FILTER_TYPE type)
{
switch (type)
{
case D3D12_FILTER_TYPE_POINT:
return VK_SAMPLER_MIPMAP_MODE_NEAREST;
case D3D12_FILTER_TYPE_LINEAR:
return VK_SAMPLER_MIPMAP_MODE_LINEAR;
default:
FIXME("Unhandled filter type %#x.\n", type);
return VK_SAMPLER_MIPMAP_MODE_NEAREST;
}
}
static VkSamplerAddressMode vk_address_mode_from_d3d12(D3D12_TEXTURE_ADDRESS_MODE mode)
{
switch (mode)
{
case D3D12_TEXTURE_ADDRESS_MODE_WRAP:
return VK_SAMPLER_ADDRESS_MODE_REPEAT;
case D3D12_TEXTURE_ADDRESS_MODE_MIRROR:
return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;
case D3D12_TEXTURE_ADDRESS_MODE_CLAMP:
return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
case D3D12_TEXTURE_ADDRESS_MODE_BORDER:
return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
case D3D12_TEXTURE_ADDRESS_MODE_MIRROR_ONCE:
return VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE;
default:
FIXME("Unhandled address mode %#x.\n", mode);
return VK_SAMPLER_ADDRESS_MODE_REPEAT;
}
}
static VkSamplerReductionModeEXT vk_reduction_mode_from_d3d12(D3D12_FILTER_REDUCTION_TYPE mode)
{
switch (mode)
{
case D3D12_FILTER_REDUCTION_TYPE_STANDARD:
case D3D12_FILTER_REDUCTION_TYPE_COMPARISON:
return VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE_EXT;
case D3D12_FILTER_REDUCTION_TYPE_MINIMUM:
return VK_SAMPLER_REDUCTION_MODE_MIN_EXT;
case D3D12_FILTER_REDUCTION_TYPE_MAXIMUM:
return VK_SAMPLER_REDUCTION_MODE_MAX_EXT;
default:
FIXME("Unhandled reduction mode %#x.\n", mode);
return VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE_EXT;
}
}
static bool d3d12_sampler_needs_border_color(D3D12_TEXTURE_ADDRESS_MODE u,
D3D12_TEXTURE_ADDRESS_MODE v, D3D12_TEXTURE_ADDRESS_MODE w)
{
return u == D3D12_TEXTURE_ADDRESS_MODE_BORDER ||
v == D3D12_TEXTURE_ADDRESS_MODE_BORDER ||
w == D3D12_TEXTURE_ADDRESS_MODE_BORDER;
}
static VkBorderColor vk_static_border_color_from_d3d12(D3D12_STATIC_BORDER_COLOR border_color)
{
switch (border_color)
{
case D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK:
return VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;
case D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK:
return VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK;
case D3D12_STATIC_BORDER_COLOR_OPAQUE_WHITE:
return VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;
default:
WARN("Unhandled static border color %u.\n", border_color);
return VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;
}
}
static VkBorderColor vk_border_color_from_d3d12(struct d3d12_device *device, const float *border_color)
{
unsigned int i;
static const struct
{
float color[4];
VkBorderColor vk_border_color;
}
border_colors[] = {
{ {0.0f, 0.0f, 0.0f, 0.0f}, VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK },
{ {0.0f, 0.0f, 0.0f, 1.0f}, VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK },
{ {1.0f, 1.0f, 1.0f, 1.0f}, VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE },
};
for (i = 0; i < ARRAY_SIZE(border_colors); i++)
{
if (!memcmp(border_color, border_colors[i].color, sizeof(border_colors[i].color)))
return border_colors[i].vk_border_color;
}
if (!device->device_info.custom_border_color_features.customBorderColorWithoutFormat)
{
FIXME("Unsupported border color (%f, %f, %f, %f).\n",
border_color[0], border_color[1], border_color[2], border_color[3]);
return VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;
}
return VK_BORDER_COLOR_FLOAT_CUSTOM_EXT;
}
HRESULT d3d12_create_static_sampler(struct d3d12_device *device,
const D3D12_STATIC_SAMPLER_DESC *desc, VkSampler *vk_sampler)
{
const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
VkSamplerReductionModeCreateInfoEXT reduction_desc;
VkSamplerCreateInfo sampler_desc;
VkResult vr;
reduction_desc.sType = VK_STRUCTURE_TYPE_SAMPLER_REDUCTION_MODE_CREATE_INFO_EXT;
reduction_desc.pNext = NULL;
reduction_desc.reductionMode = vk_reduction_mode_from_d3d12(D3D12_DECODE_FILTER_REDUCTION(desc->Filter));
sampler_desc.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
sampler_desc.pNext = NULL;
sampler_desc.flags = 0;
sampler_desc.magFilter = vk_filter_from_d3d12(D3D12_DECODE_MAG_FILTER(desc->Filter));
sampler_desc.minFilter = vk_filter_from_d3d12(D3D12_DECODE_MIN_FILTER(desc->Filter));
sampler_desc.mipmapMode = vk_mipmap_mode_from_d3d12(D3D12_DECODE_MIP_FILTER(desc->Filter));
sampler_desc.addressModeU = vk_address_mode_from_d3d12(desc->AddressU);
sampler_desc.addressModeV = vk_address_mode_from_d3d12(desc->AddressV);
sampler_desc.addressModeW = vk_address_mode_from_d3d12(desc->AddressW);
sampler_desc.mipLodBias = desc->MipLODBias;
sampler_desc.anisotropyEnable = D3D12_DECODE_IS_ANISOTROPIC_FILTER(desc->Filter);
sampler_desc.maxAnisotropy = desc->MaxAnisotropy;
sampler_desc.compareEnable = D3D12_DECODE_IS_COMPARISON_FILTER(desc->Filter);
sampler_desc.compareOp = sampler_desc.compareEnable ? vk_compare_op_from_d3d12(desc->ComparisonFunc) : 0;
sampler_desc.minLod = desc->MinLOD;
sampler_desc.maxLod = desc->MaxLOD;
sampler_desc.borderColor = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;
sampler_desc.unnormalizedCoordinates = VK_FALSE;
if (d3d12_sampler_needs_border_color(desc->AddressU, desc->AddressV, desc->AddressW))
sampler_desc.borderColor = vk_static_border_color_from_d3d12(desc->BorderColor);
if (reduction_desc.reductionMode != VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE_EXT && device->vk_info.EXT_sampler_filter_minmax)
vk_prepend_struct(&sampler_desc, &reduction_desc);
if ((vr = VK_CALL(vkCreateSampler(device->vk_device, &sampler_desc, NULL, vk_sampler))) < 0)
WARN("Failed to create Vulkan sampler, vr %d.\n", vr);
return hresult_from_vk_result(vr);
}
static HRESULT d3d12_create_sampler(struct d3d12_device *device,
const D3D12_SAMPLER_DESC *desc, VkSampler *vk_sampler)
{
const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
VkSamplerCustomBorderColorCreateInfoEXT border_color_info;
VkSamplerReductionModeCreateInfoEXT reduction_desc;
VkSamplerCreateInfo sampler_desc;
VkResult vr;
border_color_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CUSTOM_BORDER_COLOR_CREATE_INFO_EXT;
border_color_info.pNext = NULL;
memcpy(border_color_info.customBorderColor.float32, desc->BorderColor,
sizeof(border_color_info.customBorderColor.float32));
border_color_info.format = VK_FORMAT_UNDEFINED;
reduction_desc.sType = VK_STRUCTURE_TYPE_SAMPLER_REDUCTION_MODE_CREATE_INFO_EXT;
reduction_desc.pNext = NULL;
reduction_desc.reductionMode = vk_reduction_mode_from_d3d12(D3D12_DECODE_FILTER_REDUCTION(desc->Filter));
sampler_desc.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
sampler_desc.pNext = NULL;
sampler_desc.flags = 0;
sampler_desc.magFilter = vk_filter_from_d3d12(D3D12_DECODE_MAG_FILTER(desc->Filter));
sampler_desc.minFilter = vk_filter_from_d3d12(D3D12_DECODE_MIN_FILTER(desc->Filter));
sampler_desc.mipmapMode = vk_mipmap_mode_from_d3d12(D3D12_DECODE_MIP_FILTER(desc->Filter));
sampler_desc.addressModeU = vk_address_mode_from_d3d12(desc->AddressU);
sampler_desc.addressModeV = vk_address_mode_from_d3d12(desc->AddressV);
sampler_desc.addressModeW = vk_address_mode_from_d3d12(desc->AddressW);
sampler_desc.mipLodBias = desc->MipLODBias;
sampler_desc.anisotropyEnable = D3D12_DECODE_IS_ANISOTROPIC_FILTER(desc->Filter);
sampler_desc.maxAnisotropy = desc->MaxAnisotropy;
sampler_desc.compareEnable = D3D12_DECODE_IS_COMPARISON_FILTER(desc->Filter);
sampler_desc.compareOp = sampler_desc.compareEnable ? vk_compare_op_from_d3d12(desc->ComparisonFunc) : 0;
sampler_desc.minLod = desc->MinLOD;
sampler_desc.maxLod = desc->MaxLOD;
sampler_desc.borderColor = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;
sampler_desc.unnormalizedCoordinates = VK_FALSE;
if (d3d12_sampler_needs_border_color(desc->AddressU, desc->AddressV, desc->AddressW))
sampler_desc.borderColor = vk_border_color_from_d3d12(device, desc->BorderColor);
if (sampler_desc.borderColor == VK_BORDER_COLOR_FLOAT_CUSTOM_EXT)
vk_prepend_struct(&sampler_desc, &border_color_info);
if (reduction_desc.reductionMode != VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE_EXT && device->vk_info.EXT_sampler_filter_minmax)
vk_prepend_struct(&sampler_desc, &reduction_desc);
if ((vr = VK_CALL(vkCreateSampler(device->vk_device, &sampler_desc, NULL, vk_sampler))) < 0)
WARN("Failed to create Vulkan sampler, vr %d.\n", vr);
return hresult_from_vk_result(vr);
}
void d3d12_desc_create_sampler(vkd3d_cpu_descriptor_va_t desc_va,
struct d3d12_device *device, const D3D12_SAMPLER_DESC *desc)
{
const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
union vkd3d_descriptor_info descriptor_info;
struct vkd3d_descriptor_binding binding;
VkWriteDescriptorSet vk_write;
struct d3d12_desc_split d;
struct vkd3d_view_key key;
struct vkd3d_view *view;
uint32_t info_index;
if (!desc)
{
WARN("NULL sampler desc.\n");
return;
}
d = d3d12_desc_decode_va(desc_va);
key.view_type = VKD3D_VIEW_TYPE_SAMPLER;
key.u.sampler = *desc;
if (!(view = vkd3d_view_map_create_view(&device->sampler_map, device, &key)))
return;
vkd3d_descriptor_debug_register_view_cookie(device->descriptor_qa_global_info, view->cookie, 0);
info_index = vkd3d_bindless_state_find_set_info_index(&device->bindless_state, VKD3D_BINDLESS_SET_SAMPLER);
binding = vkd3d_bindless_state_binding_from_info_index(&device->bindless_state, info_index);
d.view->info.view = view;
d.view->cookie = view->cookie;
d.types->set_info_mask = 1u << info_index;
d.types->flags = VKD3D_DESCRIPTOR_FLAG_VIEW | VKD3D_DESCRIPTOR_FLAG_NON_NULL | VKD3D_DESCRIPTOR_FLAG_SINGLE_DESCRIPTOR;
d.types->single_binding = binding;
descriptor_info.image.sampler = view->vk_sampler;
descriptor_info.image.imageView = VK_NULL_HANDLE;
descriptor_info.image.imageLayout = VK_IMAGE_LAYOUT_UNDEFINED;
vkd3d_init_write_descriptor_set(&vk_write, &d, binding, VK_DESCRIPTOR_TYPE_SAMPLER, &descriptor_info);
vkd3d_descriptor_debug_write_descriptor(d.heap->descriptor_heap_info.host_ptr,
d.heap->cookie, d.offset,
VKD3D_DESCRIPTOR_QA_TYPE_SAMPLER_BIT, d.view->cookie);
VK_CALL(vkUpdateDescriptorSets(device->vk_device, 1, &vk_write, 0, NULL));
}
/* RTVs */
void d3d12_rtv_desc_copy(struct d3d12_rtv_desc *dst, struct d3d12_rtv_desc *src, unsigned int count)
{
memcpy(dst, src, sizeof(*dst) * count);
}
void d3d12_rtv_desc_create_rtv(struct d3d12_rtv_desc *rtv_desc, struct d3d12_device *device,
struct d3d12_resource *resource, const D3D12_RENDER_TARGET_VIEW_DESC *desc)
{
struct vkd3d_view_key key;
struct vkd3d_view *view;
if (!resource)
{
memset(rtv_desc, 0, sizeof(*rtv_desc));
return;
}
if (!(resource->desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET))
FIXME("Resource %p does not set D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET.\n", resource);
if (!init_default_texture_view_desc(&key.u.texture, resource, desc ? desc->Format : 0))
return;
if (key.u.texture.format->vk_aspect_mask != VK_IMAGE_ASPECT_COLOR_BIT)
{
WARN("Trying to create RTV for depth/stencil format %#x.\n", key.u.texture.format->dxgi_format);
return;
}
key.view_type = VKD3D_VIEW_TYPE_IMAGE;
key.u.texture.image_usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
if (desc)
{
switch (desc->ViewDimension)
{
case D3D12_RTV_DIMENSION_TEXTURE1D:
key.u.texture.view_type = VK_IMAGE_VIEW_TYPE_1D;
key.u.texture.miplevel_idx = desc->Texture1D.MipSlice;
key.u.texture.layer_count = 1;
break;
case D3D12_RTV_DIMENSION_TEXTURE1DARRAY:
key.u.texture.view_type = VK_IMAGE_VIEW_TYPE_1D_ARRAY;
key.u.texture.miplevel_idx = desc->Texture1DArray.MipSlice;
key.u.texture.layer_idx = desc->Texture1DArray.FirstArraySlice;
key.u.texture.layer_count = desc->Texture1DArray.ArraySize;
break;
case D3D12_RTV_DIMENSION_TEXTURE2D:
key.u.texture.view_type = VK_IMAGE_VIEW_TYPE_2D;
key.u.texture.miplevel_idx = desc->Texture2D.MipSlice;
key.u.texture.layer_count = 1;
key.u.texture.aspect_mask = vk_image_aspect_flags_from_d3d12(resource->format, desc->Texture2D.PlaneSlice);
break;
case D3D12_RTV_DIMENSION_TEXTURE2DARRAY:
key.u.texture.view_type = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
key.u.texture.miplevel_idx = desc->Texture2DArray.MipSlice;
key.u.texture.layer_idx = desc->Texture2DArray.FirstArraySlice;
key.u.texture.layer_count = desc->Texture2DArray.ArraySize;
key.u.texture.aspect_mask = vk_image_aspect_flags_from_d3d12(resource->format, desc->Texture2DArray.PlaneSlice);
break;
case D3D12_RTV_DIMENSION_TEXTURE2DMS:
key.u.texture.view_type = VK_IMAGE_VIEW_TYPE_2D;
key.u.texture.layer_count = 1;
break;
case D3D12_RTV_DIMENSION_TEXTURE2DMSARRAY:
key.u.texture.view_type = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
key.u.texture.layer_idx = desc->Texture2DMSArray.FirstArraySlice;
key.u.texture.layer_count = desc->Texture2DMSArray.ArraySize;
break;
case D3D12_RTV_DIMENSION_TEXTURE3D:
key.u.texture.view_type = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
key.u.texture.miplevel_idx = desc->Texture3D.MipSlice;
key.u.texture.layer_idx = desc->Texture3D.FirstWSlice;
key.u.texture.layer_count = desc->Texture3D.WSize;
break;
default:
FIXME("Unhandled view dimension %#x.\n", desc->ViewDimension);
}
/* Avoid passing down UINT32_MAX here since that makes framebuffer logic later rather awkward. */
key.u.texture.layer_count = min(key.u.texture.layer_count, resource->desc.DepthOrArraySize - key.u.texture.layer_idx);
}
else if (resource->desc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE3D)
{
key.u.texture.view_type = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
key.u.texture.layer_idx = 0;
key.u.texture.layer_count = resource->desc.DepthOrArraySize;
}
assert(d3d12_resource_is_texture(resource));
if (!(view = vkd3d_view_map_create_view(&resource->view_map, device, &key)))
return;
vkd3d_descriptor_debug_register_view_cookie(device->descriptor_qa_global_info, view->cookie, resource->res.cookie);
rtv_desc->sample_count = vk_samples_from_dxgi_sample_desc(&resource->desc.SampleDesc);
rtv_desc->format = key.u.texture.format;
rtv_desc->width = d3d12_resource_desc_get_width(&resource->desc, key.u.texture.miplevel_idx);
rtv_desc->height = d3d12_resource_desc_get_height(&resource->desc, key.u.texture.miplevel_idx);
rtv_desc->layer_count = key.u.texture.layer_count;
rtv_desc->view = view;
rtv_desc->resource = resource;
}
void d3d12_rtv_desc_create_dsv(struct d3d12_rtv_desc *dsv_desc, struct d3d12_device *device,
struct d3d12_resource *resource, const D3D12_DEPTH_STENCIL_VIEW_DESC *desc)
{
struct vkd3d_view_key key;
struct vkd3d_view *view;
if (!resource)
{
memset(dsv_desc, 0, sizeof(*dsv_desc));
return;
}
if (!(resource->desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL))
FIXME("Resource %p does not set D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL.\n", resource);
if (resource->desc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE3D)
{
WARN("Cannot create DSV for 3D texture.\n");
return;
}
if (!init_default_texture_view_desc(&key.u.texture, resource, desc ? desc->Format : 0))
return;
if (!(key.u.texture.format->vk_aspect_mask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)))
{
WARN("Trying to create DSV for format %#x.\n", key.u.texture.format->dxgi_format);
return;
}
key.view_type = VKD3D_VIEW_TYPE_IMAGE;
key.u.texture.image_usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
if (desc)
{
switch (desc->ViewDimension)
{
case D3D12_DSV_DIMENSION_TEXTURE1D:
key.u.texture.miplevel_idx = desc->Texture1D.MipSlice;
key.u.texture.layer_count = 1;
break;
case D3D12_DSV_DIMENSION_TEXTURE1DARRAY:
key.u.texture.view_type = VK_IMAGE_VIEW_TYPE_1D_ARRAY;
key.u.texture.miplevel_idx = desc->Texture1DArray.MipSlice;
key.u.texture.layer_idx = desc->Texture1DArray.FirstArraySlice;
key.u.texture.layer_count = desc->Texture1DArray.ArraySize;
break;
case D3D12_DSV_DIMENSION_TEXTURE2D:
key.u.texture.miplevel_idx = desc->Texture2D.MipSlice;
key.u.texture.layer_count = 1;
break;
case D3D12_DSV_DIMENSION_TEXTURE2DARRAY:
key.u.texture.view_type = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
key.u.texture.miplevel_idx = desc->Texture2DArray.MipSlice;
key.u.texture.layer_idx = desc->Texture2DArray.FirstArraySlice;
key.u.texture.layer_count = desc->Texture2DArray.ArraySize;
break;
case D3D12_DSV_DIMENSION_TEXTURE2DMS:
key.u.texture.view_type = VK_IMAGE_VIEW_TYPE_2D;
key.u.texture.layer_count = 1;
break;
case D3D12_DSV_DIMENSION_TEXTURE2DMSARRAY:
key.u.texture.view_type = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
key.u.texture.layer_idx = desc->Texture2DMSArray.FirstArraySlice;
key.u.texture.layer_count = desc->Texture2DMSArray.ArraySize;
break;
default:
FIXME("Unhandled view dimension %#x.\n", desc->ViewDimension);
}
/* Avoid passing down UINT32_MAX here since that makes framebuffer logic later rather awkward. */
key.u.texture.layer_count = min(key.u.texture.layer_count, resource->desc.DepthOrArraySize - key.u.texture.layer_idx);
}
assert(d3d12_resource_is_texture(resource));
if (!(view = vkd3d_view_map_create_view(&resource->view_map, device, &key)))
return;
vkd3d_descriptor_debug_register_view_cookie(device->descriptor_qa_global_info, view->cookie, resource->res.cookie);
dsv_desc->sample_count = vk_samples_from_dxgi_sample_desc(&resource->desc.SampleDesc);
dsv_desc->format = key.u.texture.format;
dsv_desc->width = d3d12_resource_desc_get_width(&resource->desc, key.u.texture.miplevel_idx);
dsv_desc->height = d3d12_resource_desc_get_height(&resource->desc, key.u.texture.miplevel_idx);
dsv_desc->layer_count = key.u.texture.layer_count;
dsv_desc->view = view;
dsv_desc->resource = resource;
}
/* ID3D12DescriptorHeap */
static HRESULT STDMETHODCALLTYPE d3d12_descriptor_heap_QueryInterface(ID3D12DescriptorHeap *iface,
REFIID riid, void **object)
{
TRACE("iface %p, riid %s, object %p.\n", iface, debugstr_guid(riid), object);
if (IsEqualGUID(riid, &IID_ID3D12DescriptorHeap)
|| IsEqualGUID(riid, &IID_ID3D12Pageable)
|| IsEqualGUID(riid, &IID_ID3D12DeviceChild)
|| IsEqualGUID(riid, &IID_ID3D12Object)
|| IsEqualGUID(riid, &IID_IUnknown))
{
ID3D12DescriptorHeap_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_descriptor_heap_AddRef(ID3D12DescriptorHeap *iface)
{
struct d3d12_descriptor_heap *heap = impl_from_ID3D12DescriptorHeap(iface);
ULONG refcount = InterlockedIncrement(&heap->refcount);
TRACE("%p increasing refcount to %u.\n", heap, refcount);
return refcount;
}
static ULONG STDMETHODCALLTYPE d3d12_descriptor_heap_Release(ID3D12DescriptorHeap *iface)
{
struct d3d12_descriptor_heap *heap = impl_from_ID3D12DescriptorHeap(iface);
ULONG refcount = InterlockedDecrement(&heap->refcount);
TRACE("%p decreasing refcount to %u.\n", heap, refcount);
if (!refcount)
{
struct d3d12_device *device = heap->device;
d3d12_descriptor_heap_cleanup(heap);
vkd3d_private_store_destroy(&heap->private_store);
vkd3d_free_aligned(heap);
d3d12_device_release(device);
}
return refcount;
}
static HRESULT STDMETHODCALLTYPE d3d12_descriptor_heap_GetPrivateData(ID3D12DescriptorHeap *iface,
REFGUID guid, UINT *data_size, void *data)
{
struct d3d12_descriptor_heap *heap = impl_from_ID3D12DescriptorHeap(iface);
TRACE("iface %p, guid %s, data_size %p, data %p.\n", iface, debugstr_guid(guid), data_size, data);
return vkd3d_get_private_data(&heap->private_store, guid, data_size, data);
}
static HRESULT STDMETHODCALLTYPE d3d12_descriptor_heap_SetPrivateData(ID3D12DescriptorHeap *iface,
REFGUID guid, UINT data_size, const void *data)
{
struct d3d12_descriptor_heap *heap = impl_from_ID3D12DescriptorHeap(iface);
TRACE("iface %p, guid %s, data_size %u, data %p.\n", iface, debugstr_guid(guid), data_size, data);
return vkd3d_set_private_data(&heap->private_store, guid, data_size, data,
NULL, NULL);
}
static HRESULT STDMETHODCALLTYPE d3d12_descriptor_heap_SetPrivateDataInterface(ID3D12DescriptorHeap *iface,
REFGUID guid, const IUnknown *data)
{
struct d3d12_descriptor_heap *heap = impl_from_ID3D12DescriptorHeap(iface);
TRACE("iface %p, guid %s, data %p.\n", iface, debugstr_guid(guid), data);
return vkd3d_set_private_data_interface(&heap->private_store, guid, data,
NULL, NULL);
}
static HRESULT STDMETHODCALLTYPE d3d12_descriptor_heap_GetDevice(ID3D12DescriptorHeap *iface, REFIID iid, void **device)
{
struct d3d12_descriptor_heap *heap = impl_from_ID3D12DescriptorHeap(iface);
TRACE("iface %p, iid %s, device %p.\n", iface, debugstr_guid(iid), device);
return d3d12_device_query_interface(heap->device, iid, device);
}
static D3D12_DESCRIPTOR_HEAP_DESC * STDMETHODCALLTYPE d3d12_descriptor_heap_GetDesc(ID3D12DescriptorHeap *iface,
D3D12_DESCRIPTOR_HEAP_DESC *desc)
{
struct d3d12_descriptor_heap *heap = impl_from_ID3D12DescriptorHeap(iface);
TRACE("iface %p, desc %p.\n", iface, desc);
*desc = heap->desc;
return desc;
}
static D3D12_CPU_DESCRIPTOR_HANDLE * STDMETHODCALLTYPE d3d12_descriptor_heap_GetCPUDescriptorHandleForHeapStart(
ID3D12DescriptorHeap *iface, D3D12_CPU_DESCRIPTOR_HANDLE *descriptor)
{
struct d3d12_descriptor_heap *heap = impl_from_ID3D12DescriptorHeap(iface);
TRACE("iface %p, descriptor %p.\n", iface, descriptor);
*descriptor = heap->cpu_va;
return descriptor;
}
static D3D12_GPU_DESCRIPTOR_HANDLE * STDMETHODCALLTYPE d3d12_descriptor_heap_GetGPUDescriptorHandleForHeapStart(
ID3D12DescriptorHeap *iface, D3D12_GPU_DESCRIPTOR_HANDLE *descriptor)
{
struct d3d12_descriptor_heap *heap = impl_from_ID3D12DescriptorHeap(iface);
TRACE("iface %p, descriptor %p.\n", iface, descriptor);
descriptor->ptr = heap->gpu_va;
return descriptor;
}
CONST_VTBL struct ID3D12DescriptorHeapVtbl d3d12_descriptor_heap_vtbl =
{
/* IUnknown methods */
d3d12_descriptor_heap_QueryInterface,
d3d12_descriptor_heap_AddRef,
d3d12_descriptor_heap_Release,
/* ID3D12Object methods */
d3d12_descriptor_heap_GetPrivateData,
d3d12_descriptor_heap_SetPrivateData,
d3d12_descriptor_heap_SetPrivateDataInterface,
(void *)d3d12_object_SetName,
/* ID3D12DeviceChild methods */
d3d12_descriptor_heap_GetDevice,
/* ID3D12DescriptorHeap methods */
d3d12_descriptor_heap_GetDesc,
d3d12_descriptor_heap_GetCPUDescriptorHandleForHeapStart,
d3d12_descriptor_heap_GetGPUDescriptorHandleForHeapStart,
};
static HRESULT d3d12_descriptor_heap_create_descriptor_pool(struct d3d12_descriptor_heap *descriptor_heap,
VkDescriptorPool *vk_descriptor_pool)
{
const struct vkd3d_vk_device_procs *vk_procs = &descriptor_heap->device->vk_procs;
VkDescriptorPoolSize vk_pool_sizes[VKD3D_MAX_BINDLESS_DESCRIPTOR_SETS];
const struct d3d12_device *device = descriptor_heap->device;
unsigned int i, pool_count = 0, ssbo_count = 0;
VkDescriptorPoolCreateInfo vk_pool_info;
VkDescriptorPoolSize *ssbo_pool = NULL;
VkResult vr;
for (i = 0; i < device->bindless_state.set_count; i++)
{
const struct vkd3d_bindless_set_info *set_info = &device->bindless_state.set_info[i];
if (set_info->heap_type == descriptor_heap->desc.Type)
{
VkDescriptorPoolSize *vk_pool_size = &vk_pool_sizes[pool_count++];
vk_pool_size->type = set_info->vk_descriptor_type;
vk_pool_size->descriptorCount = descriptor_heap->desc.NumDescriptors;
if (vkd3d_descriptor_debug_active_qa_checks() &&
descriptor_heap->desc.Type == D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV)
{
vk_pool_size->descriptorCount += VKD3D_DESCRIPTOR_DEBUG_NUM_PAD_DESCRIPTORS;
}
if (set_info->vk_descriptor_type == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)
ssbo_pool = vk_pool_size;
}
ssbo_count += vkd3d_popcount(set_info->flags & VKD3D_BINDLESS_SET_EXTRA_MASK);
}
if (ssbo_count && !ssbo_pool)
{
ssbo_pool = &vk_pool_sizes[pool_count++];
ssbo_pool->type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
ssbo_pool->descriptorCount = 0; /* see below */
}
if (ssbo_pool)
ssbo_pool->descriptorCount += ssbo_count;
if (!pool_count)
return S_OK;
/* If using mutable type, we will allocate the most conservative size.
* This is fine since we're attempting to allocate a completely generic descriptor set. */
vk_pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
vk_pool_info.pNext = NULL;
vk_pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT_EXT;
if (!(descriptor_heap->desc.Flags & D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE) &&
(descriptor_heap->device->bindless_state.flags & VKD3D_BINDLESS_MUTABLE_TYPE))
vk_pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_HOST_ONLY_BIT_VALVE;
vk_pool_info.maxSets = pool_count;
vk_pool_info.poolSizeCount = pool_count;
vk_pool_info.pPoolSizes = vk_pool_sizes;
if ((vr = VK_CALL(vkCreateDescriptorPool(device->vk_device,
&vk_pool_info, NULL, vk_descriptor_pool))) < 0)
{
ERR("Failed to create descriptor pool, vr %d.\n", vr);
return hresult_from_vk_result(vr);
}
return S_OK;
}
static void d3d12_descriptor_heap_zero_initialize(struct d3d12_descriptor_heap *descriptor_heap,
VkDescriptorType vk_descriptor_type, VkDescriptorSet vk_descriptor_set,
uint32_t binding_index, uint32_t descriptor_count)
{
const struct vkd3d_vk_device_procs *vk_procs = &descriptor_heap->device->vk_procs;
const struct d3d12_device *device = descriptor_heap->device;
VkDescriptorBufferInfo *buffer_infos = NULL;
VkDescriptorImageInfo *image_infos = NULL;
VkBufferView *buffer_view_infos = NULL;
VkWriteDescriptorSet write;
uint32_t i;
/* Clear out descriptor heap with the largest possible descriptor type we know of when using mutable descriptor type.
* Purely for defensive purposes. */
if (vk_descriptor_type == VK_DESCRIPTOR_TYPE_MUTABLE_VALVE)
vk_descriptor_type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
write.pNext = NULL;
write.descriptorType = vk_descriptor_type;
write.dstSet = vk_descriptor_set;
write.dstBinding = binding_index;
write.dstArrayElement = 0;
write.descriptorCount = descriptor_count;
write.pTexelBufferView = NULL;
write.pImageInfo = NULL;
write.pBufferInfo = NULL;
switch (vk_descriptor_type)
{
case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
image_infos = vkd3d_calloc(descriptor_count, sizeof(*image_infos));
write.pImageInfo = image_infos;
break;
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
buffer_infos = vkd3d_calloc(descriptor_count, sizeof(*buffer_infos));
write.pBufferInfo = buffer_infos;
for (i = 0; i < descriptor_count; i++)
buffer_infos[i].range = VK_WHOLE_SIZE;
break;
case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
buffer_view_infos = vkd3d_calloc(descriptor_count, sizeof(*buffer_view_infos));
write.pTexelBufferView = buffer_view_infos;
break;
default:
break;
}
VK_CALL(vkUpdateDescriptorSets(device->vk_device, 1, &write, 0, NULL));
vkd3d_free(image_infos);
vkd3d_free(buffer_view_infos);
vkd3d_free(buffer_infos);
}
static void d3d12_descriptor_heap_get_host_mapping(struct d3d12_descriptor_heap *descriptor_heap,
const struct vkd3d_bindless_set_info *binding, uint32_t set_index)
{
const struct vkd3d_vk_device_procs *vk_procs = &descriptor_heap->device->vk_procs;
uint8_t *mapped_set = NULL;
descriptor_heap->sets[set_index].mapped_set = NULL;
descriptor_heap->sets[set_index].copy_template = NULL;
descriptor_heap->sets[set_index].copy_template_single = NULL;
if (binding->host_copy_template && binding->host_copy_template_single)
{
VK_CALL(vkGetDescriptorSetHostMappingVALVE(descriptor_heap->device->vk_device,
descriptor_heap->sets[set_index].vk_descriptor_set, (void**)&mapped_set));
if (mapped_set)
{
mapped_set += binding->host_mapping_offset;
descriptor_heap->sets[set_index].mapped_set = mapped_set;
/* Keep a local copy close so we can fetch stuff from same cache line easily. */
descriptor_heap->sets[set_index].copy_template = binding->host_copy_template;
descriptor_heap->sets[set_index].copy_template_single = binding->host_copy_template_single;
}
}
}
static HRESULT d3d12_descriptor_heap_create_descriptor_set(struct d3d12_descriptor_heap *descriptor_heap,
const struct vkd3d_bindless_set_info *binding, VkDescriptorSet *vk_descriptor_set)
{
const struct vkd3d_vk_device_procs *vk_procs = &descriptor_heap->device->vk_procs;
VkDescriptorSetVariableDescriptorCountAllocateInfoEXT vk_variable_count_info;
uint32_t descriptor_count = descriptor_heap->desc.NumDescriptors;
const struct d3d12_device *device = descriptor_heap->device;
VkDescriptorSetAllocateInfo vk_set_info;
VkResult vr;
if (vkd3d_descriptor_debug_active_qa_checks() && descriptor_heap->desc.Type == D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV)
descriptor_count += VKD3D_DESCRIPTOR_DEBUG_NUM_PAD_DESCRIPTORS;
vk_variable_count_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_ALLOCATE_INFO_EXT;
vk_variable_count_info.pNext = NULL;
vk_variable_count_info.descriptorSetCount = 1;
vk_variable_count_info.pDescriptorCounts = &descriptor_count;
vk_set_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
vk_set_info.pNext = &vk_variable_count_info;
vk_set_info.descriptorPool = descriptor_heap->vk_descriptor_pool;
vk_set_info.descriptorSetCount = 1;
vk_set_info.pSetLayouts = &binding->vk_host_set_layout;
if (descriptor_heap->desc.Flags & D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE)
vk_set_info.pSetLayouts = &binding->vk_set_layout;
if ((vr = VK_CALL(vkAllocateDescriptorSets(device->vk_device, &vk_set_info, vk_descriptor_set))) < 0)
{
ERR("Failed to allocate descriptor set, vr %d.\n", vr);
return hresult_from_vk_result(vr);
}
if (binding->vk_descriptor_type != VK_DESCRIPTOR_TYPE_SAMPLER)
{
d3d12_descriptor_heap_zero_initialize(descriptor_heap,
binding->vk_descriptor_type, *vk_descriptor_set,
binding->binding_index, descriptor_count);
}
return S_OK;
}
static void d3d12_descriptor_heap_get_buffer_range(struct d3d12_descriptor_heap *descriptor_heap,
VkDeviceSize *offset, VkDeviceSize size, struct vkd3d_host_visible_buffer_range *range)
{
if (size)
{
range->descriptor.buffer = descriptor_heap->vk_buffer;
range->descriptor.offset = *offset;
range->descriptor.range = size;
range->host_ptr = void_ptr_offset(descriptor_heap->host_memory, *offset);
*offset += size;
}
else
{
range->descriptor.buffer = VK_NULL_HANDLE;
range->descriptor.offset = 0;
range->descriptor.range = VK_WHOLE_SIZE;
range->host_ptr = NULL;
}
}
static HRESULT d3d12_descriptor_heap_init_data_buffer(struct d3d12_descriptor_heap *descriptor_heap,
struct d3d12_device *device, const D3D12_DESCRIPTOR_HEAP_DESC *desc)
{
const struct vkd3d_vk_device_procs *vk_procs = &descriptor_heap->device->vk_procs;
VkDeviceSize alignment = max(device->device_info.properties2.properties.limits.minStorageBufferOffsetAlignment,
device->device_info.properties2.properties.limits.nonCoherentAtomSize);
VkDeviceSize raw_va_buffer_size = 0, offset_buffer_size = 0;
VKD3D_UNUSED VkDeviceSize descriptor_heap_info_size = 0;
VkMemoryPropertyFlags property_flags;
D3D12_RESOURCE_DESC1 buffer_desc;
VkDeviceSize buffer_size, offset;
D3D12_HEAP_PROPERTIES heap_info;
D3D12_HEAP_FLAGS heap_flags;
VkResult vr;
HRESULT hr;
if (desc->Type == D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV)
{
if (device->bindless_state.flags & VKD3D_RAW_VA_AUX_BUFFER)
{
raw_va_buffer_size = align(desc->NumDescriptors * sizeof(VkDeviceAddress), alignment);
if (vkd3d_descriptor_debug_active_qa_checks())
raw_va_buffer_size += align(VKD3D_DESCRIPTOR_DEBUG_NUM_PAD_DESCRIPTORS * sizeof(VkDeviceAddress), alignment);
}
if (device->bindless_state.flags & (VKD3D_SSBO_OFFSET_BUFFER | VKD3D_TYPED_OFFSET_BUFFER))
offset_buffer_size = align(desc->NumDescriptors * sizeof(struct vkd3d_bound_buffer_range), alignment);
if (vkd3d_descriptor_debug_active_qa_checks())
descriptor_heap_info_size = align(vkd3d_descriptor_debug_heap_info_size(desc->NumDescriptors), alignment);
}
buffer_size = raw_va_buffer_size + offset_buffer_size;
buffer_size += descriptor_heap_info_size;
if (!buffer_size)
return S_OK;
if (desc->Flags & D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE)
{
memset(&buffer_desc, 0, sizeof(buffer_desc));
buffer_desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
buffer_desc.Width = buffer_size;
buffer_desc.Height = 1;
buffer_desc.DepthOrArraySize = 1;
buffer_desc.MipLevels = 1;
buffer_desc.SampleDesc.Count = 1;
buffer_desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
buffer_desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
/* host-visible device memory */
memset(&heap_info, 0, sizeof(heap_info));
heap_info.Type = D3D12_HEAP_TYPE_UPLOAD;
heap_flags = D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS;
if (FAILED(hr = vkd3d_create_buffer(device, &heap_info, heap_flags, &buffer_desc, &descriptor_heap->vk_buffer)))
return hr;
property_flags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
if (vkd3d_config_flags & VKD3D_CONFIG_FLAG_FORCE_HOST_CACHED)
property_flags |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
else if (!(vkd3d_config_flags & VKD3D_CONFIG_FLAG_NO_UPLOAD_HVV))
property_flags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
if (FAILED(hr = vkd3d_allocate_buffer_memory(device, descriptor_heap->vk_buffer,
property_flags, &descriptor_heap->device_allocation)))
return hr;
if ((vr = VK_CALL(vkMapMemory(device->vk_device, descriptor_heap->device_allocation.vk_memory,
0, VK_WHOLE_SIZE, 0, &descriptor_heap->host_memory))))
{
ERR("Failed to map buffer, vr %d.\n", vr);
return hresult_from_vk_result(vr);
}
}
else
{
memset(&descriptor_heap->device_allocation, 0, sizeof(descriptor_heap->device_allocation));
descriptor_heap->vk_buffer = VK_NULL_HANDLE;
descriptor_heap->host_memory = vkd3d_calloc(1, buffer_size);
}
offset = 0;
d3d12_descriptor_heap_get_buffer_range(descriptor_heap, &offset, raw_va_buffer_size, &descriptor_heap->raw_va_aux_buffer);
d3d12_descriptor_heap_get_buffer_range(descriptor_heap, &offset, offset_buffer_size, &descriptor_heap->buffer_ranges);
#ifdef VKD3D_ENABLE_DESCRIPTOR_QA
d3d12_descriptor_heap_get_buffer_range(descriptor_heap, &offset,
descriptor_heap_info_size,
&descriptor_heap->descriptor_heap_info);
#endif
return S_OK;
}
static void d3d12_descriptor_heap_update_extra_bindings(struct d3d12_descriptor_heap *descriptor_heap,
struct d3d12_device *device)
{
VkDescriptorBufferInfo vk_buffer_info[VKD3D_BINDLESS_SET_MAX_EXTRA_BINDINGS];
VkWriteDescriptorSet vk_writes[VKD3D_BINDLESS_SET_MAX_EXTRA_BINDINGS];
const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
unsigned int i, binding_index, set_index = 0, write_count = 0;
uint32_t flags;
for (i = 0; i < device->bindless_state.set_count; i++)
{
const struct vkd3d_bindless_set_info *set_info = &device->bindless_state.set_info[i];
if (set_info->heap_type != descriptor_heap->desc.Type)
continue;
flags = set_info->flags & VKD3D_BINDLESS_SET_EXTRA_MASK;
binding_index = 0;
while (flags)
{
enum vkd3d_bindless_set_flag flag = (enum vkd3d_bindless_set_flag)(flags & -flags);
VkDescriptorBufferInfo *vk_buffer = &vk_buffer_info[write_count];
VkWriteDescriptorSet *vk_write = &vk_writes[write_count];
vk_write->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
vk_write->pNext = NULL;
vk_write->dstSet = descriptor_heap->sets[set_index].vk_descriptor_set;
vk_write->dstBinding = binding_index++;
vk_write->dstArrayElement = 0;
vk_write->descriptorCount = 1;
vk_write->descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
vk_write->pImageInfo = NULL;
vk_write->pBufferInfo = vk_buffer;
vk_write->pTexelBufferView = NULL;
switch (flag)
{
case VKD3D_BINDLESS_SET_EXTRA_RAW_VA_AUX_BUFFER:
*vk_buffer = descriptor_heap->raw_va_aux_buffer.descriptor;
break;
case VKD3D_BINDLESS_SET_EXTRA_OFFSET_BUFFER:
*vk_buffer = descriptor_heap->buffer_ranges.descriptor;
break;
#ifdef VKD3D_ENABLE_DESCRIPTOR_QA
case VKD3D_BINDLESS_SET_EXTRA_GLOBAL_HEAP_INFO_BUFFER:
*vk_buffer = *vkd3d_descriptor_debug_get_global_info_descriptor(device->descriptor_qa_global_info);
break;
case VKD3D_BINDLESS_SET_EXTRA_DESCRIPTOR_HEAP_INFO_BUFFER:
*vk_buffer = descriptor_heap->descriptor_heap_info.descriptor;
break;
#endif
default:
ERR("Unsupported extra flags %#x.\n", flag);
continue;
}
write_count += 1;
flags -= flag;
}
set_index += 1;
}
if (write_count)
VK_CALL(vkUpdateDescriptorSets(device->vk_device, write_count, vk_writes, 0, NULL));
}
static void d3d12_descriptor_heap_add_null_descriptor_template(
struct d3d12_descriptor_heap *descriptor_heap,
const struct vkd3d_bindless_set_info *set_info,
unsigned int set_info_index)
{
struct VkWriteDescriptorSet *write;
unsigned int index;
index = descriptor_heap->null_descriptor_template.num_writes;
write = &descriptor_heap->null_descriptor_template.writes[index];
write->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
write->pNext = NULL;
write->descriptorCount = 1;
write->dstSet = descriptor_heap->sets[set_info->set_index].vk_descriptor_set;
write->dstBinding = set_info->binding_index;
/* Replaced when instantiating template. */
write->dstArrayElement = 0;
/* For mutable, will be replaced when instantiating template. */
write->descriptorType = set_info->vk_descriptor_type;
write->pBufferInfo = &descriptor_heap->null_descriptor_template.buffer;
write->pImageInfo = &descriptor_heap->null_descriptor_template.image;
write->pTexelBufferView = &descriptor_heap->null_descriptor_template.buffer_view;
if (index == 0)
{
descriptor_heap->null_descriptor_template.buffer.offset = 0;
descriptor_heap->null_descriptor_template.buffer.range = VK_WHOLE_SIZE;
descriptor_heap->null_descriptor_template.buffer.buffer = VK_NULL_HANDLE;
descriptor_heap->null_descriptor_template.image.sampler = VK_NULL_HANDLE;
descriptor_heap->null_descriptor_template.image.imageLayout = VK_IMAGE_LAYOUT_UNDEFINED;
descriptor_heap->null_descriptor_template.image.imageView = VK_NULL_HANDLE;
descriptor_heap->null_descriptor_template.buffer_view = VK_NULL_HANDLE;
descriptor_heap->null_descriptor_template.has_mutable_descriptors =
descriptor_heap->device->vk_info.VALVE_mutable_descriptor_type;
}
descriptor_heap->null_descriptor_template.num_writes++;
descriptor_heap->null_descriptor_template.set_info_mask |= 1u << set_info_index;
}
static HRESULT d3d12_descriptor_heap_init(struct d3d12_descriptor_heap *descriptor_heap,
struct d3d12_device *device, const D3D12_DESCRIPTOR_HEAP_DESC *desc)
{
unsigned int i;
HRESULT hr;
memset(descriptor_heap, 0, sizeof(*descriptor_heap));
descriptor_heap->ID3D12DescriptorHeap_iface.lpVtbl = &d3d12_descriptor_heap_vtbl;
descriptor_heap->refcount = 1;
descriptor_heap->device = device;
descriptor_heap->desc = *desc;
if (desc->Flags & D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE)
descriptor_heap->gpu_va = d3d12_device_get_descriptor_heap_gpu_va(device);
if (FAILED(hr = d3d12_descriptor_heap_create_descriptor_pool(descriptor_heap,
&descriptor_heap->vk_descriptor_pool)))
goto fail;
if (desc->Type == D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV ||
desc->Type == D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER)
{
for (i = 0; i < device->bindless_state.set_count; i++)
{
const struct vkd3d_bindless_set_info *set_info = &device->bindless_state.set_info[i];
if (set_info->heap_type == desc->Type)
{
if (FAILED(hr = d3d12_descriptor_heap_create_descriptor_set(descriptor_heap,
set_info, &descriptor_heap->sets[set_info->set_index].vk_descriptor_set)))
goto fail;
d3d12_descriptor_heap_get_host_mapping(descriptor_heap, set_info, set_info->set_index);
if (descriptor_heap->desc.Type == D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV)
d3d12_descriptor_heap_add_null_descriptor_template(descriptor_heap, set_info, i);
}
}
}
if (FAILED(hr = d3d12_descriptor_heap_init_data_buffer(descriptor_heap, device, desc)))
goto fail;
if (desc->Flags & D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE)
d3d12_descriptor_heap_update_extra_bindings(descriptor_heap, device);
if (FAILED(hr = vkd3d_private_store_init(&descriptor_heap->private_store)))
goto fail;
d3d12_device_add_ref(descriptor_heap->device);
return S_OK;
fail:
d3d12_descriptor_heap_cleanup(descriptor_heap);
return hr;
}
static void d3d12_descriptor_heap_init_descriptors(struct d3d12_descriptor_heap *descriptor_heap)
{
struct vkd3d_descriptor_metadata_types *meta;
unsigned int i;
switch (descriptor_heap->desc.Type)
{
case D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV:
case D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER:
meta = (struct vkd3d_descriptor_metadata_types *)descriptor_heap->descriptors;
for (i = 0; i < descriptor_heap->desc.NumDescriptors; i++)
meta[i].set_info_mask = descriptor_heap->null_descriptor_template.set_info_mask;
break;
case D3D12_DESCRIPTOR_HEAP_TYPE_RTV:
case D3D12_DESCRIPTOR_HEAP_TYPE_DSV:
break;
default:
WARN("Unhandled descriptor heap type: %d.\n", descriptor_heap->desc.Type);
}
}
HRESULT d3d12_descriptor_heap_create(struct d3d12_device *device,
const D3D12_DESCRIPTOR_HEAP_DESC *desc, struct d3d12_descriptor_heap **descriptor_heap)
{
size_t max_descriptor_count, descriptor_size;
struct d3d12_descriptor_heap *object;
unsigned int num_descriptor_bits = 0;
unsigned int num_descriptors_pot = 0;
size_t required_size;
size_t alignment;
HRESULT hr;
if (!(descriptor_size = d3d12_device_get_descriptor_handle_increment_size(desc->Type)))
{
WARN("No descriptor size for descriptor type %#x.\n", desc->Type);
return E_INVALIDARG;
}
if ((desc->Flags & D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE)
&& (desc->Type == D3D12_DESCRIPTOR_HEAP_TYPE_RTV || desc->Type == D3D12_DESCRIPTOR_HEAP_TYPE_DSV))
{
WARN("RTV/DSV descriptor heaps cannot be shader visible.\n");
return E_INVALIDARG;
}
max_descriptor_count = (UINT32_MAX - sizeof(*object)) / descriptor_size;
if (desc->NumDescriptors > max_descriptor_count)
{
WARN("Invalid descriptor count %u (max %zu).\n", desc->NumDescriptors, max_descriptor_count);
return E_OUTOFMEMORY;
}
if (desc->Type == D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV || desc->Type == D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER)
{
/* Always allocate a POT number of descriptors. We want the CPU VA layout to be very specific.
* This allows us to synthesize pointers to the heap and metadata without
* performing any de-references, which is great, because it avoids cache misses,
* and reduces our dependency chain of memory accesses required to perform a descriptor copy.
* Missing caches in descriptor copies is a large chunk of our CPU overhead, so we should make sure to avoid it
* when we can.
* We encode descriptors as:
* <--- MSBs ------------------------------------------------------- LSBs --->
* | descriptor_heap VA | heap_offset | log2i_ceil(num_descriptors) (5 bits) |
* The base VA of the heap therefore becomes descriptor_heap CPU VA + log2i_ceil(num_descriptors).
* The increment is set to 1 << 5. */
num_descriptor_bits = vkd3d_log2i_ceil(max(1, desc->NumDescriptors));
num_descriptors_pot = 1u << num_descriptor_bits;
required_size = sizeof(struct d3d12_descriptor_heap);
/* From base descriptor heap, we can offset directly to get metadata_bindings.
* Metadata view data is placed at an offset we can deduce from num descriptors. */
required_size += num_descriptors_pot * sizeof(struct vkd3d_descriptor_metadata_types);
required_size += desc->NumDescriptors * sizeof(struct vkd3d_descriptor_metadata_view);
/* The alignment should scale roughly with size of the heap,
* so any wasted space shouldn't really be that bad. */
alignment = max(D3D12_DESC_ALIGNMENT, num_descriptors_pot * VKD3D_RESOURCE_DESC_INCREMENT);
}
else
{
/* For RTV/DSV just store the descriptors inline after the data structure.
* Performance isn't that critical. */
required_size = sizeof(struct d3d12_descriptor_heap);
required_size += desc->NumDescriptors * sizeof(struct d3d12_rtv_desc);
alignment = D3D12_DESC_ALIGNMENT;
}
if (!(object = vkd3d_malloc_aligned(required_size, alignment)))
return E_OUTOFMEMORY;
memset(object, 0, required_size);
if (FAILED(hr = d3d12_descriptor_heap_init(object, device, desc)))
{
vkd3d_free_aligned(object);
return hr;
}
if (desc->Type == D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV || desc->Type == D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER)
{
/* See comments above on how this is supposed to work */
object->cpu_va.ptr = (SIZE_T)object + num_descriptor_bits;
}
else
{
object->cpu_va.ptr = (SIZE_T)object->descriptors;
}
d3d12_descriptor_heap_init_descriptors(object);
TRACE("Created descriptor heap %p.\n", object);
#ifdef VKD3D_ENABLE_DESCRIPTOR_QA
object->cookie = vkd3d_allocate_cookie();
vkd3d_descriptor_debug_register_heap(object->descriptor_heap_info.host_ptr, object->cookie, desc);
#endif
*descriptor_heap = object;
return S_OK;
}
void d3d12_descriptor_heap_cleanup(struct d3d12_descriptor_heap *descriptor_heap)
{
const struct vkd3d_vk_device_procs *vk_procs = &descriptor_heap->device->vk_procs;
struct d3d12_device *device = descriptor_heap->device;
if (!descriptor_heap->device_allocation.vk_memory)
vkd3d_free(descriptor_heap->host_memory);
if (descriptor_heap->gpu_va != 0)
d3d12_device_return_descriptor_heap_gpu_va(device, descriptor_heap->gpu_va);
VK_CALL(vkDestroyBuffer(device->vk_device, descriptor_heap->vk_buffer, NULL));
vkd3d_free_device_memory(device, &descriptor_heap->device_allocation);
VK_CALL(vkDestroyDescriptorPool(device->vk_device, descriptor_heap->vk_descriptor_pool, NULL));
vkd3d_descriptor_debug_unregister_heap(descriptor_heap->cookie);
}
static void d3d12_query_heap_set_name(struct d3d12_query_heap *heap, const char *name)
{
if (heap->vk_query_pool)
{
vkd3d_set_vk_object_name(heap->device, (uint64_t)heap->vk_query_pool,
VK_OBJECT_TYPE_QUERY_POOL, name);
}
else /*if (heap->vk_buffer)*/
{
vkd3d_set_vk_object_name(heap->device, (uint64_t)heap->vk_buffer,
VK_OBJECT_TYPE_BUFFER, name);
}
}
/* ID3D12QueryHeap */
static HRESULT STDMETHODCALLTYPE d3d12_query_heap_QueryInterface(ID3D12QueryHeap *iface,
REFIID iid, void **out)
{
TRACE("iface %p, iid %s, out %p.\n", iface, debugstr_guid(iid), out);
if (IsEqualGUID(iid, &IID_ID3D12QueryHeap)
|| IsEqualGUID(iid, &IID_ID3D12Pageable)
|| IsEqualGUID(iid, &IID_ID3D12DeviceChild)
|| IsEqualGUID(iid, &IID_ID3D12Object)
|| IsEqualGUID(iid, &IID_IUnknown))
{
ID3D12QueryHeap_AddRef(iface);
*out = iface;
return S_OK;
}
WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(iid));
*out = NULL;
return E_NOINTERFACE;
}
static ULONG STDMETHODCALLTYPE d3d12_query_heap_AddRef(ID3D12QueryHeap *iface)
{
struct d3d12_query_heap *heap = impl_from_ID3D12QueryHeap(iface);
ULONG refcount = InterlockedIncrement(&heap->refcount);
TRACE("%p increasing refcount to %u.\n", heap, refcount);
return refcount;
}
static ULONG STDMETHODCALLTYPE d3d12_query_heap_Release(ID3D12QueryHeap *iface)
{
struct d3d12_query_heap *heap = impl_from_ID3D12QueryHeap(iface);
ULONG refcount = InterlockedDecrement(&heap->refcount);
TRACE("%p decreasing refcount to %u.\n", heap, refcount);
if (!refcount)
{
struct d3d12_device *device = heap->device;
const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
vkd3d_private_store_destroy(&heap->private_store);
VK_CALL(vkDestroyQueryPool(device->vk_device, heap->vk_query_pool, NULL));
VK_CALL(vkDestroyBuffer(device->vk_device, heap->vk_buffer, NULL));
vkd3d_free_device_memory(device, &heap->device_allocation);
vkd3d_free(heap);
d3d12_device_release(device);
}
return refcount;
}
static HRESULT STDMETHODCALLTYPE d3d12_query_heap_GetPrivateData(ID3D12QueryHeap *iface,
REFGUID guid, UINT *data_size, void *data)
{
struct d3d12_query_heap *heap = impl_from_ID3D12QueryHeap(iface);
TRACE("iface %p, guid %s, data_size %p, data %p.\n", iface, debugstr_guid(guid), data_size, data);
return vkd3d_get_private_data(&heap->private_store, guid, data_size, data);
}
static HRESULT STDMETHODCALLTYPE d3d12_query_heap_SetPrivateData(ID3D12QueryHeap *iface,
REFGUID guid, UINT data_size, const void *data)
{
struct d3d12_query_heap *heap = impl_from_ID3D12QueryHeap(iface);
TRACE("iface %p, guid %s, data_size %u, data %p.\n", iface, debugstr_guid(guid), data_size, data);
return vkd3d_set_private_data(&heap->private_store, guid, data_size, data,
(vkd3d_set_name_callback) d3d12_query_heap_set_name, heap);
}
static HRESULT STDMETHODCALLTYPE d3d12_query_heap_SetPrivateDataInterface(ID3D12QueryHeap *iface,
REFGUID guid, const IUnknown *data)
{
struct d3d12_query_heap *heap = impl_from_ID3D12QueryHeap(iface);
TRACE("iface %p, guid %s, data %p.\n", iface, debugstr_guid(guid), data);
return vkd3d_set_private_data_interface(&heap->private_store, guid, data,
(vkd3d_set_name_callback) d3d12_query_heap_set_name, heap);
}
static HRESULT STDMETHODCALLTYPE d3d12_query_heap_GetDevice(ID3D12QueryHeap *iface, REFIID iid, void **device)
{
struct d3d12_query_heap *heap = impl_from_ID3D12QueryHeap(iface);
TRACE("iface %p, iid %s, device %p.\n", iface, debugstr_guid(iid), device);
return d3d12_device_query_interface(heap->device, iid, device);
}
CONST_VTBL struct ID3D12QueryHeapVtbl d3d12_query_heap_vtbl =
{
/* IUnknown methods */
d3d12_query_heap_QueryInterface,
d3d12_query_heap_AddRef,
d3d12_query_heap_Release,
/* ID3D12Object methods */
d3d12_query_heap_GetPrivateData,
d3d12_query_heap_SetPrivateData,
d3d12_query_heap_SetPrivateDataInterface,
(void *)d3d12_object_SetName,
/* ID3D12DeviceChild methods */
d3d12_query_heap_GetDevice,
};
HRESULT d3d12_query_heap_create(struct d3d12_device *device, const D3D12_QUERY_HEAP_DESC *desc,
struct d3d12_query_heap **heap)
{
const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
D3D12_HEAP_PROPERTIES heap_properties;
D3D12_RESOURCE_DESC1 buffer_desc;
struct d3d12_query_heap *object;
VkQueryPoolCreateInfo pool_info;
VkResult vr;
HRESULT hr;
if (!(object = vkd3d_malloc(sizeof(*object))))
return E_OUTOFMEMORY;
memset(object, 0, sizeof(*object));
object->ID3D12QueryHeap_iface.lpVtbl = &d3d12_query_heap_vtbl;
object->refcount = 1;
object->device = device;
object->desc = *desc;
if (!d3d12_query_heap_type_is_inline(desc->Type))
{
pool_info.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO;
pool_info.pNext = NULL;
pool_info.flags = 0;
pool_info.queryCount = desc->Count;
switch (desc->Type)
{
case D3D12_QUERY_HEAP_TYPE_TIMESTAMP:
pool_info.queryType = VK_QUERY_TYPE_TIMESTAMP;
pool_info.pipelineStatistics = 0;
break;
case D3D12_QUERY_HEAP_TYPE_PIPELINE_STATISTICS:
pool_info.queryType = VK_QUERY_TYPE_PIPELINE_STATISTICS;
pool_info.pipelineStatistics = VK_QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_VERTICES_BIT
| VK_QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_PRIMITIVES_BIT
| VK_QUERY_PIPELINE_STATISTIC_VERTEX_SHADER_INVOCATIONS_BIT
| VK_QUERY_PIPELINE_STATISTIC_GEOMETRY_SHADER_INVOCATIONS_BIT
| VK_QUERY_PIPELINE_STATISTIC_GEOMETRY_SHADER_PRIMITIVES_BIT
| VK_QUERY_PIPELINE_STATISTIC_CLIPPING_INVOCATIONS_BIT
| VK_QUERY_PIPELINE_STATISTIC_CLIPPING_PRIMITIVES_BIT
| VK_QUERY_PIPELINE_STATISTIC_FRAGMENT_SHADER_INVOCATIONS_BIT
| VK_QUERY_PIPELINE_STATISTIC_TESSELLATION_CONTROL_SHADER_PATCHES_BIT
| VK_QUERY_PIPELINE_STATISTIC_TESSELLATION_EVALUATION_SHADER_INVOCATIONS_BIT
| VK_QUERY_PIPELINE_STATISTIC_COMPUTE_SHADER_INVOCATIONS_BIT;
break;
default:
WARN("Invalid query heap type %u.\n", desc->Type);
vkd3d_free(object);
return E_INVALIDARG;
}
if ((vr = VK_CALL(vkCreateQueryPool(device->vk_device, &pool_info, NULL, &object->vk_query_pool))) < 0)
{
WARN("Failed to create Vulkan query pool, vr %d.\n", vr);
vkd3d_free(object);
return hresult_from_vk_result(vr);
}
}
else
{
memset(&heap_properties, 0, sizeof(heap_properties));
heap_properties.Type = D3D12_HEAP_TYPE_DEFAULT;
memset(&buffer_desc, 0, sizeof(buffer_desc));
buffer_desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
buffer_desc.Width = d3d12_query_heap_type_get_data_size(desc->Type) * desc->Count;
buffer_desc.Height = 1;
buffer_desc.DepthOrArraySize = 1;
buffer_desc.MipLevels = 1;
buffer_desc.Format = DXGI_FORMAT_UNKNOWN;
buffer_desc.SampleDesc.Count = 1;
buffer_desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
buffer_desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
if (FAILED(hr = vkd3d_create_buffer(device, &heap_properties,
D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS, &buffer_desc, &object->vk_buffer)))
{
vkd3d_free(object);
return hr;
}
if (FAILED(hr = vkd3d_allocate_buffer_memory(device, object->vk_buffer,
VK_MEMORY_HEAP_DEVICE_LOCAL_BIT, &object->device_allocation)))
{
VK_CALL(vkDestroyBuffer(device->vk_device, object->vk_buffer, NULL));
vkd3d_free(object);
return hr;
}
/* Explicit initialization is not required for these since
* we can expect the buffer to be zero-initialized. */
object->initialized = 1;
}
if (FAILED(hr = vkd3d_private_store_init(&object->private_store)))
{
vkd3d_free(object);
return hr;
}
d3d12_device_add_ref(device);
TRACE("Created query heap %p.\n", object);
*heap = object;
return S_OK;
}
struct vkd3d_memory_topology
{
VkDeviceSize largest_device_local_heap_size;
VkDeviceSize largest_host_only_heap_size;
uint32_t largest_device_local_heap_index;
uint32_t largest_host_only_heap_index;
uint32_t device_local_heap_count;
uint32_t host_only_heap_count;
bool exists_device_only_type;
bool exists_host_only_type;
};
static void vkd3d_memory_info_get_topology(struct vkd3d_memory_topology *topology,
struct d3d12_device *device)
{
VkMemoryPropertyFlags flags;
VkDeviceSize heap_size;
uint32_t heap_index;
unsigned int i;
memset(topology, 0, sizeof(*topology));
for (i = 0; i < device->memory_properties.memoryHeapCount; i++)
{
heap_size = device->memory_properties.memoryHeaps[i].size;
if (device->memory_properties.memoryHeaps[i].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT)
{
if (heap_size > topology->largest_device_local_heap_size)
{
topology->largest_device_local_heap_index = i;
topology->largest_device_local_heap_size = heap_size;
}
topology->device_local_heap_count++;
}
else
{
if (heap_size > topology->largest_host_only_heap_size)
{
topology->largest_host_only_heap_index = i;
topology->largest_host_only_heap_size = heap_size;
}
topology->host_only_heap_count++;
}
}
for (i = 0; i < device->memory_properties.memoryTypeCount; i++)
{
flags = device->memory_properties.memoryTypes[i].propertyFlags;
heap_index = device->memory_properties.memoryTypes[i].heapIndex;
if (heap_index == topology->largest_device_local_heap_index &&
(flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0 &&
(flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
{
topology->exists_device_only_type = true;
}
else if (heap_index == topology->largest_host_only_heap_index &&
(flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) == 0 &&
(flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
{
topology->exists_host_only_type = true;
}
}
}
static uint32_t vkd3d_memory_info_find_global_mask(const struct vkd3d_memory_topology *topology, struct d3d12_device *device)
{
/* Never allow memory types from any PCI-pinned heap.
* If we allow it, it might end up being used as a fallback memory type, which will cause severe instabilities.
* These types should only be used in a controlled fashion. */
VkMemoryPropertyFlags flags;
uint32_t heap_index;
uint32_t i, mask;
if (!(vkd3d_config_flags & VKD3D_CONFIG_FLAG_NO_UPLOAD_HVV))
return UINT32_MAX;
/* If we only have one device local heap, or no host-only heaps, there is nothing to do. */
if (topology->device_local_heap_count <= 1 || topology->host_only_heap_count == 0)
return UINT32_MAX;
/* Verify that there exists a DEVICE_LOCAL type that is not HOST_VISIBLE on this device
* which maps to the largest device local heap. That way, it is safe to mask out all memory types which are
* DEVICE_LOCAL | HOST_VISIBLE.
* Similarly, there must exist a host-only type. */
if (!topology->exists_device_only_type || !topology->exists_host_only_type)
return UINT32_MAX;
/* Mask out any memory types which are deemed problematic. */
for (i = 0, mask = 0; i < device->memory_properties.memoryTypeCount; i++)
{
const VkMemoryPropertyFlags pinned_mask = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
flags = device->memory_properties.memoryTypes[i].propertyFlags;
heap_index = device->memory_properties.memoryTypes[i].heapIndex;
if (heap_index != topology->largest_device_local_heap_index &&
heap_index != topology->largest_host_only_heap_index &&
(flags & pinned_mask) == pinned_mask)
{
mask |= 1u << i;
WARN("Blocking memory type %u for use (PCI-pinned memory).\n", i);
}
}
return ~mask;
}
static void vkd3d_memory_info_init_budgets(struct vkd3d_memory_info *info,
const struct vkd3d_memory_topology *topology,
struct d3d12_device *device)
{
bool heap_index_needs_budget;
VkMemoryPropertyFlags flags;
uint32_t heap_index;
uint32_t i;
info->budget_sensitive_mask = 0;
/* Nothing to do if we don't have separate heaps. */
if (topology->device_local_heap_count == 0 || topology->host_only_heap_count == 0)
return;
if (!topology->exists_device_only_type || !topology->exists_host_only_type)
return;
for (i = 0; i < device->memory_properties.memoryTypeCount; i++)
{
const VkMemoryPropertyFlags pinned_mask = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
flags = device->memory_properties.memoryTypes[i].propertyFlags;
heap_index = device->memory_properties.memoryTypes[i].heapIndex;
/* Work around a driver workaround on NV drivers which targets certain
* older DXVK versions (use of DXVK DXGI is likely what impacts us here),
* since we don't see this behavior in native builds.
* Even with resizable BAR, we might observe two different heaps,
* with very slightly different heap sizes.
* It's straight forward to be universally robust against these kinds of scenarios,
* so just go for that.
* If we're within 75% of the actual VRAM size, assume we've hit this scenario.
* This should exclude small BAR from explicit budget, since that's just 256 MB. */
heap_index_needs_budget =
(device->memory_properties.memoryHeaps[heap_index].size >
3 * device->memory_properties.memoryHeaps[topology->largest_device_local_heap_index].size / 4) &&
(device->memory_properties.memoryHeaps[heap_index].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT);
if ((flags & pinned_mask) == pinned_mask && heap_index_needs_budget)
{
/* Limit this type. This limit is a pure heuristic and we might need further tuning here.
* If there's a separate heap type for PCI-e BAR,
* don't bother limiting it since the size is already going to be tiny.
* The driver will limit us naturally. */
info->budget_sensitive_mask |= 1u << i;
info->type_budget[i] = device->memory_properties.memoryHeaps[heap_index].size / 16;
info->type_current[i] = 0;
}
}
INFO("Applying resizable BAR budget to memory types: 0x%x.\n", info->budget_sensitive_mask);
}
void vkd3d_memory_info_cleanup(struct vkd3d_memory_info *info,
struct d3d12_device *device)
{
pthread_mutex_destroy(&info->budget_lock);
}
HRESULT vkd3d_memory_info_init(struct vkd3d_memory_info *info,
struct d3d12_device *device)
{
const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
VkDeviceBufferMemoryRequirementsKHR buffer_requirement_info;
VkDeviceImageMemoryRequirementsKHR image_requirement_info;
VkMemoryRequirements2 memory_requirements;
struct vkd3d_memory_topology topology;
VkBufferCreateInfo buffer_info;
uint32_t sampled_type_mask_cpu;
VkImageCreateInfo image_info;
uint32_t rt_ds_type_mask_cpu;
uint32_t sampled_type_mask;
uint32_t host_visible_mask;
uint32_t buffer_type_mask;
uint32_t rt_ds_type_mask;
uint32_t i;
vkd3d_memory_info_get_topology(&topology, device);
info->global_mask = vkd3d_memory_info_find_global_mask(&topology, device);
vkd3d_memory_info_init_budgets(info, &topology, device);
if (pthread_mutex_init(&info->budget_lock, NULL) != 0)
return E_OUTOFMEMORY;
buffer_requirement_info.sType = VK_STRUCTURE_TYPE_DEVICE_BUFFER_MEMORY_REQUIREMENTS_KHR;
buffer_requirement_info.pNext = NULL;
buffer_requirement_info.pCreateInfo = &buffer_info;
image_requirement_info.sType = VK_STRUCTURE_TYPE_DEVICE_IMAGE_MEMORY_REQUIREMENTS_KHR;
image_requirement_info.pNext = NULL;
image_requirement_info.pCreateInfo = &image_info;
image_requirement_info.planeAspect = 0;
memory_requirements.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2;
memory_requirements.pNext = NULL;
memset(&buffer_info, 0, sizeof(buffer_info));
buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
buffer_info.size = 65536;
buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT |
VK_BUFFER_USAGE_TRANSFER_DST_BIT |
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT |
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT |
VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT |
VK_BUFFER_USAGE_INDEX_BUFFER_BIT |
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT |
VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT;
if (device->device_info.acceleration_structure_features.accelerationStructure)
{
/* Caps are not necessarily overridden yet.
* Enabling RTAS should not change acceptable memory mask, but to be safe ... */
buffer_info.usage |=
VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR |
VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR;
}
VK_CALL(vkGetDeviceBufferMemoryRequirementsKHR(device->vk_device, &buffer_requirement_info, &memory_requirements));
buffer_type_mask = memory_requirements.memoryRequirements.memoryTypeBits;
memset(&image_info, 0, sizeof(image_info));
image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
image_info.imageType = VK_IMAGE_TYPE_2D;
image_info.format = VK_FORMAT_R8G8B8A8_UNORM;
image_info.extent.width = 16;
image_info.extent.height = 16;
image_info.extent.depth = 1;
image_info.mipLevels = 1;
image_info.arrayLayers = 1;
image_info.samples = VK_SAMPLE_COUNT_1_BIT;
image_info.tiling = VK_IMAGE_TILING_OPTIMAL;
image_info.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT |
VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
VK_IMAGE_USAGE_SAMPLED_BIT |
VK_IMAGE_USAGE_STORAGE_BIT;
image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
VK_CALL(vkGetDeviceImageMemoryRequirementsKHR(device->vk_device, &image_requirement_info, &memory_requirements));
sampled_type_mask = memory_requirements.memoryRequirements.memoryTypeBits;
/* CPU accessible images are always LINEAR.
* If we ever get a way to write to OPTIMAL-ly tiled images, we can drop this and just
* do sampled_type_mask_cpu & host_visible_set. */
image_info.tiling = VK_IMAGE_TILING_LINEAR;
image_info.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
image_info.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT |
VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
VK_IMAGE_USAGE_SAMPLED_BIT;
/* Deliberately omit STORAGE_BIT here, since it's not supported at all on NV with HOST_VISIBLE.
* Probably not 100% correct, but we can fix this if we get host visible OPTIMAL at some point. */
sampled_type_mask_cpu = 0;
if (vkd3d_is_linear_tiling_supported(device, &image_info))
{
VK_CALL(vkGetDeviceImageMemoryRequirementsKHR(device->vk_device, &image_requirement_info, &memory_requirements));
sampled_type_mask_cpu = memory_requirements.memoryRequirements.memoryTypeBits;
}
image_info.tiling = VK_IMAGE_TILING_OPTIMAL;
image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
image_info.format = VK_FORMAT_R8G8B8A8_UNORM;
image_info.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT |
VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
VK_IMAGE_USAGE_SAMPLED_BIT |
VK_IMAGE_USAGE_STORAGE_BIT;
VK_CALL(vkGetDeviceImageMemoryRequirementsKHR(device->vk_device, &image_requirement_info, &memory_requirements));
rt_ds_type_mask = memory_requirements.memoryRequirements.memoryTypeBits;
image_info.tiling = VK_IMAGE_TILING_LINEAR;
image_info.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
rt_ds_type_mask_cpu = 0;
if (vkd3d_is_linear_tiling_supported(device, &image_info))
{
VK_CALL(vkGetDeviceImageMemoryRequirementsKHR(device->vk_device, &image_requirement_info, &memory_requirements));
rt_ds_type_mask_cpu = memory_requirements.memoryRequirements.memoryTypeBits;
}
image_info.tiling = VK_IMAGE_TILING_OPTIMAL;
image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
image_info.format = VK_FORMAT_D32_SFLOAT_S8_UINT;
image_info.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT |
VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT |
VK_IMAGE_USAGE_SAMPLED_BIT;
VK_CALL(vkGetDeviceImageMemoryRequirementsKHR(device->vk_device, &image_requirement_info, &memory_requirements));
rt_ds_type_mask &= memory_requirements.memoryRequirements.memoryTypeBits;
/* Unsure if we can have host visible depth-stencil.
* On AMD, we can get linear RT, but not linear DS, so for now, just don't check for that.
* We will fail in resource creation instead. */
buffer_type_mask &= info->global_mask;
sampled_type_mask &= info->global_mask;
rt_ds_type_mask &= info->global_mask;
sampled_type_mask_cpu &= info->global_mask;
rt_ds_type_mask_cpu &= info->global_mask;
info->non_cpu_accessible_domain.buffer_type_mask = buffer_type_mask;
info->non_cpu_accessible_domain.sampled_type_mask = sampled_type_mask;
info->non_cpu_accessible_domain.rt_ds_type_mask = rt_ds_type_mask;
host_visible_mask = 0;
for (i = 0; i < device->memory_properties.memoryTypeCount; i++)
if (device->memory_properties.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)
host_visible_mask |= 1u << i;
info->cpu_accessible_domain.buffer_type_mask = buffer_type_mask & host_visible_mask;
info->cpu_accessible_domain.sampled_type_mask = sampled_type_mask_cpu & host_visible_mask;
info->cpu_accessible_domain.rt_ds_type_mask = rt_ds_type_mask_cpu & host_visible_mask;
/* If we cannot support linear render targets, this is fine.
* If we don't fix this up here, we will fail to create a host visible TIER_2 heap.
* Ignore any requirements for color attachments since we're never going to use it anyways. */
if (info->cpu_accessible_domain.rt_ds_type_mask == 0 ||
(vkd3d_config_flags & VKD3D_CONFIG_FLAG_IGNORE_RTV_HOST_VISIBLE))
info->cpu_accessible_domain.rt_ds_type_mask = info->cpu_accessible_domain.sampled_type_mask;
TRACE("Device supports buffers on memory types 0x%#x.\n", buffer_type_mask);
TRACE("Device supports textures on memory types 0x%#x.\n", sampled_type_mask);
TRACE("Device supports render targets on memory types 0x%#x.\n", rt_ds_type_mask);
TRACE("Device supports CPU visible textures on memory types 0x%#x.\n",
info->cpu_accessible_domain.sampled_type_mask);
TRACE("Device supports CPU visible render targets on memory types 0x%#x.\n",
info->cpu_accessible_domain.rt_ds_type_mask);
return S_OK;
}