mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-08-04 05:41:06 +00:00
1158 lines
51 KiB
C++
1158 lines
51 KiB
C++
/******************************************************************************
|
|
* The MIT License (MIT)
|
|
*
|
|
* Copyright (c) 2019-2023 Baldur Karlsson
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
******************************************************************************/
|
|
|
|
#include "../vk_core.h"
|
|
#include "../vk_debug.h"
|
|
#include "api/replay/version.h"
|
|
|
|
static char fakeRenderDocUUID[VK_UUID_SIZE] = {};
|
|
|
|
void MakeFakeUUID()
|
|
{
|
|
// assign a fake UUID, so that we get SPIR-V instead of cached pipeline data.
|
|
// the start is "rdoc", and the end is the time that this call was first made
|
|
if(fakeRenderDocUUID[0] == 0)
|
|
{
|
|
// 0123456789ABCDEF
|
|
// rdocyymmddHHMMSS
|
|
// we pass size+1 so that there's room for a null terminator (the UUID doesn't
|
|
// need a null terminator as it's a fixed size non-string array)
|
|
rdcstr uuid = StringFormat::sntimef(Timing::GetUTCTime(), "rdoc%y%m%d%H%M%S");
|
|
RDCASSERT(uuid.size() == sizeof(fakeRenderDocUUID));
|
|
memcpy(fakeRenderDocUUID, uuid.c_str(), RDCMIN((size_t)VK_UUID_SIZE, uuid.size()));
|
|
}
|
|
}
|
|
|
|
void ClampPhysDevAPIVersion(VkPhysicalDeviceProperties *pProperties, VkPhysicalDevice physicalDevice)
|
|
{
|
|
// for Vulkan 1.3 bufferDeviceAddress is core. If the bufferDeviceAddressCaptureReplay feature is
|
|
// not available, we can't support it so we must clamp to version 1.2 for that physical device.
|
|
if(pProperties->apiVersion >= VK_API_VERSION_1_3)
|
|
{
|
|
// for 1.1 this is core so we should definitely have this function.
|
|
if(ObjDisp(physicalDevice)->GetPhysicalDeviceFeatures2 != NULL)
|
|
{
|
|
VkPhysicalDeviceFeatures2 features = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2};
|
|
|
|
// similarly this struct must be valid if the device is 1.3
|
|
VkPhysicalDeviceVulkan12Features vk12 = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES};
|
|
|
|
features.pNext = &vk12;
|
|
|
|
ObjDisp(physicalDevice)->GetPhysicalDeviceFeatures2(Unwrap(physicalDevice), &features);
|
|
|
|
if(vk12.bufferDeviceAddressCaptureReplay == VK_FALSE)
|
|
{
|
|
RDCWARN(
|
|
"Vulkan feature bufferDeviceAddressCaptureReplay is not available. Clamping physical "
|
|
"device %s from reported version %d.%d to 1.2",
|
|
pProperties->deviceName, VK_VERSION_MAJOR(pProperties->apiVersion),
|
|
VK_VERSION_MINOR(pProperties->apiVersion));
|
|
|
|
pProperties->apiVersion = VK_API_VERSION_1_2;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// if we don't have GPDP2 the application has not initialised the instance at 1.3+
|
|
// let's clamp the version just to be safe since we can't check, and this will help protect
|
|
// against buggy applications
|
|
pProperties->apiVersion = VK_API_VERSION_1_2;
|
|
}
|
|
}
|
|
}
|
|
|
|
void WrappedVulkan::vkGetPhysicalDeviceFeatures(VkPhysicalDevice physicalDevice,
|
|
VkPhysicalDeviceFeatures *pFeatures)
|
|
{
|
|
ObjDisp(physicalDevice)->GetPhysicalDeviceFeatures(Unwrap(physicalDevice), pFeatures);
|
|
}
|
|
|
|
void WrappedVulkan::vkGetPhysicalDeviceFormatProperties(VkPhysicalDevice physicalDevice,
|
|
VkFormat format,
|
|
VkFormatProperties *pFormatProperties)
|
|
{
|
|
ObjDisp(physicalDevice)
|
|
->GetPhysicalDeviceFormatProperties(Unwrap(physicalDevice), format, pFormatProperties);
|
|
|
|
// we require all these properties at minimum for an image to be created, since we add these to
|
|
// any usage. Fortunately, in the formats the spec requires an implementation to support,
|
|
// optimalTiledFeatures must contain all these and more, so we can safely remove support for any
|
|
// format that only includes a subset.
|
|
uint32_t minRequiredMask = VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT;
|
|
|
|
const InstanceDeviceInfo &exts = GetExtensions(GetRecord(physicalDevice));
|
|
|
|
// transfer src/dst bits were added in KHR_maintenance1. Before then we assume that if
|
|
// SAMPLED_IMAGE_BIT was present it's safe to add the transfer bits too.
|
|
if(exts.ext_KHR_maintenance1)
|
|
minRequiredMask |= VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT;
|
|
|
|
if((pFormatProperties->linearTilingFeatures & minRequiredMask) != minRequiredMask)
|
|
pFormatProperties->linearTilingFeatures = 0;
|
|
if((pFormatProperties->optimalTilingFeatures & minRequiredMask) != minRequiredMask)
|
|
pFormatProperties->optimalTilingFeatures = 0;
|
|
|
|
// don't report support for DISJOINT_BIT_KHR binding
|
|
pFormatProperties->linearTilingFeatures &= ~VK_FORMAT_FEATURE_DISJOINT_BIT;
|
|
pFormatProperties->optimalTilingFeatures &= ~VK_FORMAT_FEATURE_DISJOINT_BIT;
|
|
}
|
|
|
|
void WrappedVulkan::vkGetPhysicalDeviceFormatProperties2(VkPhysicalDevice physicalDevice,
|
|
VkFormat format,
|
|
VkFormatProperties2 *pFormatProperties)
|
|
{
|
|
ObjDisp(physicalDevice)
|
|
->GetPhysicalDeviceFormatProperties2(Unwrap(physicalDevice), format, pFormatProperties);
|
|
|
|
// we require transfer source and dest these properties at minimum for an image to be created,
|
|
// since we add these to
|
|
// any usage. Fortunately, in the formats the spec requires an implementation to support,
|
|
// optimalTiledFeatures must contain all these and more, so we can safely remove support for any
|
|
// format that only includes a subset.
|
|
uint32_t minRequiredMask = VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT;
|
|
|
|
const InstanceDeviceInfo &exts = GetExtensions(GetRecord(physicalDevice));
|
|
|
|
// transfer src/dst bits were added in KHR_maintenance1. Before then we assume that if
|
|
// SAMPLED_IMAGE_BIT was present it's safe to add the transfer bits too.
|
|
if(exts.ext_KHR_maintenance1)
|
|
minRequiredMask |= VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT;
|
|
|
|
if((pFormatProperties->formatProperties.linearTilingFeatures & minRequiredMask) != minRequiredMask)
|
|
pFormatProperties->formatProperties.linearTilingFeatures = 0;
|
|
if((pFormatProperties->formatProperties.optimalTilingFeatures & minRequiredMask) != minRequiredMask)
|
|
pFormatProperties->formatProperties.optimalTilingFeatures = 0;
|
|
|
|
// don't report support for DISJOINT_BIT_KHR binding
|
|
pFormatProperties->formatProperties.linearTilingFeatures &= ~VK_FORMAT_FEATURE_DISJOINT_BIT;
|
|
pFormatProperties->formatProperties.optimalTilingFeatures &= ~VK_FORMAT_FEATURE_DISJOINT_BIT;
|
|
}
|
|
|
|
VkResult WrappedVulkan::vkGetPhysicalDeviceImageFormatProperties(
|
|
VkPhysicalDevice physicalDevice, VkFormat format, VkImageType type, VkImageTiling tiling,
|
|
VkImageUsageFlags usage, VkImageCreateFlags flags,
|
|
VkImageFormatProperties *pImageFormatProperties)
|
|
{
|
|
// we're going to add these usage bits implicitly on image create, so ensure we get an accurate
|
|
// response by adding them here. It's OK to add these, since these can't make a required format
|
|
// suddenly report as unsupported (all required formats must support these usages), so it can only
|
|
// make an optional format unsupported which is what we want.
|
|
usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
|
|
VK_IMAGE_USAGE_SAMPLED_BIT;
|
|
|
|
VkResult vkr =
|
|
ObjDisp(physicalDevice)
|
|
->GetPhysicalDeviceImageFormatProperties(Unwrap(physicalDevice), format, type, tiling,
|
|
usage, flags, pImageFormatProperties);
|
|
|
|
if(vkr == VK_SUCCESS)
|
|
{
|
|
// check that the format is one we allow to be supported - if not we return an error to be
|
|
// consistent.
|
|
VkFormatProperties props;
|
|
vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &props);
|
|
if(props.linearTilingFeatures == 0 && props.optimalTilingFeatures == 0)
|
|
{
|
|
RDCEraseEl(*pImageFormatProperties);
|
|
return VK_ERROR_FORMAT_NOT_SUPPORTED;
|
|
}
|
|
}
|
|
|
|
return vkr;
|
|
}
|
|
|
|
VkResult WrappedVulkan::vkGetPhysicalDeviceImageFormatProperties2(
|
|
VkPhysicalDevice physicalDevice, const VkPhysicalDeviceImageFormatInfo2 *pImageFormatInfo,
|
|
VkImageFormatProperties2 *pImageFormatProperties)
|
|
{
|
|
// we're going to add these usage bits implicitly on image create, so ensure we get an accurate
|
|
// response by adding them here. It's OK to add these, since these can't make a required format
|
|
// suddenly report as unsupported (all required formats must support these usages), so it can only
|
|
// make an optional format unsupported which is what we want.
|
|
VkPhysicalDeviceImageFormatInfo2 info = *pImageFormatInfo;
|
|
info.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
|
|
VK_IMAGE_USAGE_SAMPLED_BIT;
|
|
|
|
VkResult vkr = ObjDisp(physicalDevice)
|
|
->GetPhysicalDeviceImageFormatProperties2(Unwrap(physicalDevice), &info,
|
|
pImageFormatProperties);
|
|
|
|
if(vkr == VK_SUCCESS)
|
|
{
|
|
// check that the format is one we allow to be supported - if not we return an error to be
|
|
// consistent.
|
|
VkFormatProperties props;
|
|
vkGetPhysicalDeviceFormatProperties(physicalDevice, pImageFormatInfo->format, &props);
|
|
if(props.linearTilingFeatures == 0 && props.optimalTilingFeatures == 0)
|
|
{
|
|
RDCEraseEl(pImageFormatProperties->imageFormatProperties);
|
|
return VK_ERROR_FORMAT_NOT_SUPPORTED;
|
|
}
|
|
}
|
|
|
|
return vkr;
|
|
}
|
|
|
|
void WrappedVulkan::vkGetPhysicalDeviceSparseImageFormatProperties(
|
|
VkPhysicalDevice physicalDevice, VkFormat format, VkImageType type,
|
|
VkSampleCountFlagBits samples, VkImageUsageFlags usage, VkImageTiling tiling,
|
|
uint32_t *pPropertyCount, VkSparseImageFormatProperties *pProperties)
|
|
{
|
|
ObjDisp(physicalDevice)
|
|
->GetPhysicalDeviceSparseImageFormatProperties(Unwrap(physicalDevice), format, type, samples,
|
|
usage, tiling, pPropertyCount, pProperties);
|
|
}
|
|
|
|
void WrappedVulkan::vkGetPhysicalDeviceProperties(VkPhysicalDevice physicalDevice,
|
|
VkPhysicalDeviceProperties *pProperties)
|
|
{
|
|
ObjDisp(physicalDevice)->GetPhysicalDeviceProperties(Unwrap(physicalDevice), pProperties);
|
|
|
|
MakeFakeUUID();
|
|
|
|
memcpy(pProperties->pipelineCacheUUID, fakeRenderDocUUID, VK_UUID_SIZE);
|
|
|
|
ClampPhysDevAPIVersion(pProperties, physicalDevice);
|
|
}
|
|
|
|
void WrappedVulkan::vkGetPhysicalDeviceQueueFamilyProperties(
|
|
VkPhysicalDevice physicalDevice, uint32_t *pCount, VkQueueFamilyProperties *pQueueFamilyProperties)
|
|
{
|
|
// report the actual physical device properties - this will be remapped on replay if necessary
|
|
ObjDisp(physicalDevice)
|
|
->GetPhysicalDeviceQueueFamilyProperties(Unwrap(physicalDevice), pCount,
|
|
pQueueFamilyProperties);
|
|
|
|
// remove any protected bits that might be set
|
|
if(pCount && pQueueFamilyProperties)
|
|
{
|
|
for(uint32_t i = 0; i < *pCount; i++)
|
|
pQueueFamilyProperties[i].queueFlags &= ~VK_QUEUE_PROTECTED_BIT;
|
|
}
|
|
}
|
|
|
|
void WrappedVulkan::vkGetPhysicalDeviceMemoryProperties(
|
|
VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties *pMemoryProperties)
|
|
{
|
|
ObjDisp(physicalDevice)->GetPhysicalDeviceMemoryProperties(Unwrap(physicalDevice), pMemoryProperties);
|
|
}
|
|
|
|
void WrappedVulkan::vkGetImageSubresourceLayout(VkDevice device, VkImage image,
|
|
const VkImageSubresource *pSubresource,
|
|
VkSubresourceLayout *pLayout)
|
|
{
|
|
ObjDisp(device)->GetImageSubresourceLayout(Unwrap(device), Unwrap(image), pSubresource, pLayout);
|
|
}
|
|
|
|
void WrappedVulkan::vkGetBufferMemoryRequirements(VkDevice device, VkBuffer buffer,
|
|
VkMemoryRequirements *pMemoryRequirements)
|
|
{
|
|
// if we have cached memory requirements, use them. These were fetched at create time (which is
|
|
// still valid, they don't change over the lifetime of the resource) and may be slightly more
|
|
// pessimistic for the case of external memory bound resources. See vkCreateBuffer/vkCreateImage
|
|
if(IsCaptureMode(m_State) && GetRecord(buffer)->resInfo)
|
|
*pMemoryRequirements = GetRecord(buffer)->resInfo->memreqs;
|
|
else
|
|
ObjDisp(device)->GetBufferMemoryRequirements(Unwrap(device), Unwrap(buffer), pMemoryRequirements);
|
|
}
|
|
|
|
void WrappedVulkan::vkGetImageMemoryRequirements(VkDevice device, VkImage image,
|
|
VkMemoryRequirements *pMemoryRequirements)
|
|
{
|
|
// if we have cached memory requirements, use them. These were fetched at create time (which is
|
|
// still valid, they don't change over the lifetime of the resource) and may be slightly more
|
|
// pessimistic for the case of external memory bound resources. See vkCreateBuffer/vkCreateImage
|
|
if(IsCaptureMode(m_State) && GetRecord(image)->resInfo)
|
|
*pMemoryRequirements = GetRecord(image)->resInfo->memreqs;
|
|
else
|
|
ObjDisp(device)->GetImageMemoryRequirements(Unwrap(device), Unwrap(image), pMemoryRequirements);
|
|
|
|
// AMD can have some variability in the returned size, so we need to pad the reported size to
|
|
// allow for this. The variability isn't quite clear, but for now we assume aligning size to
|
|
// alignment * 4 should be sufficient (adding on a fixed padding won't help the problem as it
|
|
// won't remove the variability, nor will adding then aligning for the same reason).
|
|
if(GetDriverInfo().AMDUnreliableImageMemoryRequirements() && pMemoryRequirements->size > 0)
|
|
{
|
|
VkMemoryRequirements &memreq = *pMemoryRequirements;
|
|
|
|
VkDeviceSize oldsize = memreq.size;
|
|
memreq.size = AlignUp(memreq.size, memreq.alignment * 4);
|
|
|
|
// if it's already 'super aligned', then bump it up a little. We assume that this case
|
|
// represents the low-end of the variation range, and other variations will be a little higher.
|
|
// The other alternative is the variations are all lower and this one happened to be super
|
|
// aligned, which I think (arbitrarily really) is less likely.
|
|
if(oldsize == memreq.size)
|
|
memreq.size = AlignUp(memreq.size + 1, memreq.alignment * 4);
|
|
|
|
RDCDEBUG(
|
|
"Padded image memory requirements from %llu to %llu (base alignment %llu) (%f%% increase)",
|
|
oldsize, memreq.size, memreq.alignment,
|
|
(100.0 * double(memreq.size - oldsize)) / double(oldsize));
|
|
}
|
|
}
|
|
|
|
void WrappedVulkan::vkGetImageSparseMemoryRequirements(
|
|
VkDevice device, VkImage image, uint32_t *pNumRequirements,
|
|
VkSparseImageMemoryRequirements *pSparseMemoryRequirements)
|
|
{
|
|
ObjDisp(device)->GetImageSparseMemoryRequirements(Unwrap(device), Unwrap(image), pNumRequirements,
|
|
pSparseMemoryRequirements);
|
|
}
|
|
|
|
void WrappedVulkan::vkGetDeviceBufferMemoryRequirements(VkDevice device,
|
|
const VkDeviceBufferMemoryRequirements *pInfo,
|
|
VkMemoryRequirements2 *pMemoryRequirements)
|
|
{
|
|
byte *tempMem = GetTempMemory(GetNextPatchSize(pInfo));
|
|
VkDeviceBufferMemoryRequirements *unwrappedInfo = UnwrapStructAndChain(m_State, tempMem, pInfo);
|
|
|
|
VkBufferCreateInfo *info = (VkBufferCreateInfo *)unwrappedInfo->pCreateInfo;
|
|
|
|
// patch the create info the same as we would for vkCreateBuffer
|
|
info->usage |= VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
|
|
info->usage |= VK_BUFFER_USAGE_TRANSFER_DST_BIT;
|
|
|
|
if(IsCaptureMode(m_State) && (info->usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT))
|
|
info->flags |= VK_BUFFER_CREATE_DEVICE_ADDRESS_CAPTURE_REPLAY_BIT;
|
|
|
|
ObjDisp(device)->GetDeviceBufferMemoryRequirements(Unwrap(device), unwrappedInfo,
|
|
pMemoryRequirements);
|
|
|
|
// if the buffer is external, create a non-external and return the worst case memory requirements
|
|
// so that the memory allocated is sufficient for us on replay when the buffer is non-external
|
|
bool isExternal = FindNextStruct(unwrappedInfo->pCreateInfo,
|
|
VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO) != NULL;
|
|
|
|
if(isExternal)
|
|
{
|
|
bool removed =
|
|
RemoveNextStruct(unwrappedInfo, VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO);
|
|
|
|
RDCASSERTMSG("Couldn't find next struct indicating external memory", removed);
|
|
|
|
VkMemoryRequirements2 nonExternalReq = {};
|
|
nonExternalReq.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2;
|
|
|
|
ObjDisp(device)->GetDeviceBufferMemoryRequirements(Unwrap(device), unwrappedInfo,
|
|
&nonExternalReq);
|
|
|
|
pMemoryRequirements->memoryRequirements.size =
|
|
RDCMAX(pMemoryRequirements->memoryRequirements.size, nonExternalReq.memoryRequirements.size);
|
|
pMemoryRequirements->memoryRequirements.alignment =
|
|
RDCMAX(pMemoryRequirements->memoryRequirements.alignment,
|
|
nonExternalReq.memoryRequirements.alignment);
|
|
|
|
if((pMemoryRequirements->memoryRequirements.memoryTypeBits &
|
|
nonExternalReq.memoryRequirements.memoryTypeBits) == 0)
|
|
{
|
|
RDCWARN(
|
|
"External buffer shares no memory types with non-external buffer. This buffer "
|
|
"will not be replayable.");
|
|
}
|
|
else
|
|
{
|
|
pMemoryRequirements->memoryRequirements.memoryTypeBits &=
|
|
nonExternalReq.memoryRequirements.memoryTypeBits;
|
|
}
|
|
}
|
|
}
|
|
|
|
void WrappedVulkan::vkGetDeviceImageMemoryRequirements(VkDevice device,
|
|
const VkDeviceImageMemoryRequirements *pInfo,
|
|
VkMemoryRequirements2 *pMemoryRequirements)
|
|
{
|
|
size_t tempMemSize = GetNextPatchSize(pInfo);
|
|
|
|
// reserve space for a patched view format list if necessary
|
|
if(pInfo->pCreateInfo->samples != VK_SAMPLE_COUNT_1_BIT)
|
|
{
|
|
const VkImageFormatListCreateInfo *formatListInfo =
|
|
(const VkImageFormatListCreateInfo *)FindNextStruct(
|
|
pInfo->pCreateInfo, VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO);
|
|
|
|
if(formatListInfo)
|
|
tempMemSize += sizeof(VkFormat) * (formatListInfo->viewFormatCount + 1);
|
|
}
|
|
|
|
byte *tempMem = GetTempMemory(tempMemSize);
|
|
VkDeviceImageMemoryRequirements *unwrappedInfo = UnwrapStructAndChain(m_State, tempMem, pInfo);
|
|
|
|
VkImageCreateInfo *info = (VkImageCreateInfo *)unwrappedInfo->pCreateInfo;
|
|
|
|
info->usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
|
|
|
if(IsCaptureMode(m_State))
|
|
{
|
|
info->usage |= VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
|
info->usage &= ~VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT;
|
|
}
|
|
|
|
if(IsYUVFormat(info->format))
|
|
info->flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
|
|
|
|
if(info->samples != VK_SAMPLE_COUNT_1_BIT)
|
|
{
|
|
info->usage |= VK_IMAGE_USAGE_SAMPLED_BIT;
|
|
info->flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
|
|
|
|
if(IsCaptureMode(m_State))
|
|
{
|
|
if(!IsDepthOrStencilFormat(info->format))
|
|
{
|
|
if(GetDebugManager() && GetShaderCache()->IsBuffer2MSSupported())
|
|
info->usage |= VK_IMAGE_USAGE_STORAGE_BIT;
|
|
}
|
|
else
|
|
{
|
|
info->usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
|
|
}
|
|
}
|
|
}
|
|
|
|
info->flags &= ~VK_IMAGE_CREATE_SUBSAMPLED_BIT_EXT;
|
|
|
|
VkImageStencilUsageCreateInfo *separateStencilUsage =
|
|
(VkImageStencilUsageCreateInfo *)FindNextStruct(
|
|
info, VK_STRUCTURE_TYPE_IMAGE_STENCIL_USAGE_CREATE_INFO);
|
|
if(separateStencilUsage)
|
|
{
|
|
separateStencilUsage->stencilUsage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
|
|
|
if(IsCaptureMode(m_State))
|
|
{
|
|
info->usage |= VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
|
info->usage &= ~VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT;
|
|
}
|
|
|
|
if(info->samples != VK_SAMPLE_COUNT_1_BIT)
|
|
{
|
|
separateStencilUsage->stencilUsage |=
|
|
VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
|
|
}
|
|
}
|
|
|
|
// similarly for the image format list for MSAA textures, add the UINT cast format we will need
|
|
if(info->samples != VK_SAMPLE_COUNT_1_BIT)
|
|
{
|
|
VkImageFormatListCreateInfo *formatListInfo = (VkImageFormatListCreateInfo *)FindNextStruct(
|
|
info, VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO);
|
|
|
|
if(formatListInfo)
|
|
{
|
|
uint32_t bs = GetByteSize(1, 1, 1, info->format, 0);
|
|
|
|
VkFormat msaaCopyFormat = VK_FORMAT_UNDEFINED;
|
|
if(bs == 1)
|
|
msaaCopyFormat = VK_FORMAT_R8_UINT;
|
|
else if(bs == 2)
|
|
msaaCopyFormat = VK_FORMAT_R16_UINT;
|
|
else if(bs == 4)
|
|
msaaCopyFormat = VK_FORMAT_R32_UINT;
|
|
else if(bs == 8)
|
|
msaaCopyFormat = VK_FORMAT_R32G32_UINT;
|
|
else if(bs == 16)
|
|
msaaCopyFormat = VK_FORMAT_R32G32B32A32_UINT;
|
|
|
|
const VkFormat *oldFmts = formatListInfo->pViewFormats;
|
|
VkFormat *newFmts = (VkFormat *)tempMem;
|
|
formatListInfo->pViewFormats = newFmts;
|
|
|
|
bool needAdded = true;
|
|
uint32_t i = 0;
|
|
for(; i < formatListInfo->viewFormatCount; i++)
|
|
{
|
|
newFmts[i] = oldFmts[i];
|
|
if(newFmts[i] == msaaCopyFormat)
|
|
needAdded = false;
|
|
}
|
|
|
|
if(needAdded)
|
|
{
|
|
newFmts[i] = msaaCopyFormat;
|
|
formatListInfo->viewFormatCount++;
|
|
}
|
|
}
|
|
}
|
|
|
|
ObjDisp(device)->GetDeviceImageMemoryRequirements(Unwrap(device), unwrappedInfo,
|
|
pMemoryRequirements);
|
|
|
|
// if the image is external, create a non-external and return the worst case memory requirements
|
|
// so that the memory allocated is sufficient for us on replay when the image is non-external
|
|
bool isExternal = FindNextStruct(unwrappedInfo->pCreateInfo,
|
|
VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO) != NULL;
|
|
|
|
if(isExternal)
|
|
{
|
|
bool removed =
|
|
RemoveNextStruct(unwrappedInfo, VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO);
|
|
|
|
RDCASSERTMSG("Couldn't find next struct indicating external memory", removed);
|
|
|
|
VkMemoryRequirements2 nonExternalReq = {};
|
|
nonExternalReq.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2;
|
|
|
|
ObjDisp(device)->GetDeviceImageMemoryRequirements(Unwrap(device), unwrappedInfo, &nonExternalReq);
|
|
|
|
pMemoryRequirements->memoryRequirements.size =
|
|
RDCMAX(pMemoryRequirements->memoryRequirements.size, nonExternalReq.memoryRequirements.size);
|
|
pMemoryRequirements->memoryRequirements.alignment =
|
|
RDCMAX(pMemoryRequirements->memoryRequirements.alignment,
|
|
nonExternalReq.memoryRequirements.alignment);
|
|
|
|
if((pMemoryRequirements->memoryRequirements.memoryTypeBits &
|
|
nonExternalReq.memoryRequirements.memoryTypeBits) == 0)
|
|
{
|
|
RDCWARN(
|
|
"External image shares no memory types with non-external image. This image "
|
|
"will not be replayable.");
|
|
}
|
|
else
|
|
{
|
|
pMemoryRequirements->memoryRequirements.memoryTypeBits &=
|
|
nonExternalReq.memoryRequirements.memoryTypeBits;
|
|
}
|
|
}
|
|
}
|
|
|
|
void WrappedVulkan::vkGetDeviceImageSparseMemoryRequirements(
|
|
VkDevice device, const VkDeviceImageMemoryRequirements *pInfo,
|
|
uint32_t *pSparseMemoryRequirementCount,
|
|
VkSparseImageMemoryRequirements2 *pSparseMemoryRequirements)
|
|
{
|
|
byte *tempMem = GetTempMemory(GetNextPatchSize(pInfo));
|
|
VkDeviceImageMemoryRequirements *unwrappedInfo = UnwrapStructAndChain(m_State, tempMem, pInfo);
|
|
|
|
ObjDisp(device)->GetDeviceImageSparseMemoryRequirements(
|
|
Unwrap(device), unwrappedInfo, pSparseMemoryRequirementCount, pSparseMemoryRequirements);
|
|
}
|
|
|
|
void WrappedVulkan::vkGetBufferMemoryRequirements2(VkDevice device,
|
|
const VkBufferMemoryRequirementsInfo2 *pInfo,
|
|
VkMemoryRequirements2 *pMemoryRequirements)
|
|
{
|
|
VkBufferMemoryRequirementsInfo2 unwrappedInfo = *pInfo;
|
|
unwrappedInfo.buffer = Unwrap(unwrappedInfo.buffer);
|
|
ObjDisp(device)->GetBufferMemoryRequirements2(Unwrap(device), &unwrappedInfo, pMemoryRequirements);
|
|
|
|
// if we have cached memory requirements, use them. These were fetched at create time (which is
|
|
// still valid, they don't change over the lifetime of the resource) and may be slightly more
|
|
// pessimistic for the case of external memory bound resources. See vkCreateBuffer/vkCreateImage
|
|
if(IsCaptureMode(m_State) && GetRecord(pInfo->buffer)->resInfo)
|
|
pMemoryRequirements->memoryRequirements = GetRecord(pInfo->buffer)->resInfo->memreqs;
|
|
}
|
|
|
|
void WrappedVulkan::vkGetImageMemoryRequirements2(VkDevice device,
|
|
const VkImageMemoryRequirementsInfo2 *pInfo,
|
|
VkMemoryRequirements2 *pMemoryRequirements)
|
|
{
|
|
VkImageMemoryRequirementsInfo2 unwrappedInfo = *pInfo;
|
|
unwrappedInfo.image = Unwrap(unwrappedInfo.image);
|
|
ObjDisp(device)->GetImageMemoryRequirements2(Unwrap(device), &unwrappedInfo, pMemoryRequirements);
|
|
|
|
// if we have cached memory requirements, use them. These were fetched at create time (which is
|
|
// still valid, they don't change over the lifetime of the resource) and may be slightly more
|
|
// pessimistic for the case of external memory bound resources. See vkCreateBuffer/vkCreateImage
|
|
if(IsCaptureMode(m_State) && GetRecord(pInfo->image)->resInfo)
|
|
pMemoryRequirements->memoryRequirements = GetRecord(pInfo->image)->resInfo->memreqs;
|
|
|
|
// don't do remapping here on replay.
|
|
if(IsReplayMode(m_State))
|
|
return;
|
|
|
|
// AMD can have some variability in the returned size, so we need to pad the reported size to
|
|
// allow for this. The variability isn't quite clear, but for now we assume aligning size to
|
|
// alignment * 4 should be sufficient (adding on a fixed padding won't help the problem as it
|
|
// won't remove the variability, nor will adding then aligning for the same reason).
|
|
if(GetDriverInfo().AMDUnreliableImageMemoryRequirements() &&
|
|
pMemoryRequirements->memoryRequirements.size > 0)
|
|
{
|
|
VkMemoryRequirements &memreq = pMemoryRequirements->memoryRequirements;
|
|
|
|
VkDeviceSize oldsize = memreq.size;
|
|
memreq.size = AlignUp(memreq.size, memreq.alignment * 4);
|
|
|
|
// if it's already 'super aligned', then bump it up a little. We assume that this case
|
|
// represents the low-end of the variation range, and other variations will be a little higher.
|
|
// The other alternative is the variations are all lower and this one happened to be super
|
|
// aligned, which I think (arbitrarily really) is less likely.
|
|
if(oldsize == memreq.size)
|
|
memreq.size = AlignUp(memreq.size + 1, memreq.alignment * 4);
|
|
|
|
RDCDEBUG(
|
|
"Padded image memory requirements from %llu to %llu (base alignment %llu) (%f%% increase)",
|
|
oldsize, memreq.size, memreq.alignment,
|
|
(100.0 * double(memreq.size - oldsize)) / double(oldsize));
|
|
}
|
|
}
|
|
|
|
void WrappedVulkan::vkGetImageSparseMemoryRequirements2(
|
|
VkDevice device, const VkImageSparseMemoryRequirementsInfo2 *pInfo,
|
|
uint32_t *pSparseMemoryRequirementCount,
|
|
VkSparseImageMemoryRequirements2 *pSparseMemoryRequirements)
|
|
{
|
|
VkImageSparseMemoryRequirementsInfo2 unwrappedInfo = *pInfo;
|
|
unwrappedInfo.image = Unwrap(unwrappedInfo.image);
|
|
ObjDisp(device)->GetImageSparseMemoryRequirements2(
|
|
Unwrap(device), &unwrappedInfo, pSparseMemoryRequirementCount, pSparseMemoryRequirements);
|
|
}
|
|
|
|
void WrappedVulkan::vkGetDeviceMemoryCommitment(VkDevice device, VkDeviceMemory memory,
|
|
VkDeviceSize *pCommittedMemoryInBytes)
|
|
{
|
|
ObjDisp(device)->GetDeviceMemoryCommitment(Unwrap(device), Unwrap(memory), pCommittedMemoryInBytes);
|
|
}
|
|
|
|
void WrappedVulkan::vkGetRenderAreaGranularity(VkDevice device, VkRenderPass renderPass,
|
|
VkExtent2D *pGranularity)
|
|
{
|
|
return ObjDisp(device)->GetRenderAreaGranularity(Unwrap(device), Unwrap(renderPass), pGranularity);
|
|
}
|
|
|
|
VkResult WrappedVulkan::vkGetPipelineCacheData(VkDevice device, VkPipelineCache pipelineCache,
|
|
size_t *pDataSize, void *pData)
|
|
{
|
|
// required header and 4 NULL bytes
|
|
size_t totalSize = sizeof(VkPipelineCacheHeaderVersionOne) + 4;
|
|
|
|
if(pDataSize && !pData)
|
|
*pDataSize = totalSize;
|
|
|
|
if(pDataSize && pData)
|
|
{
|
|
if(*pDataSize < totalSize)
|
|
{
|
|
memset(pData, 0, *pDataSize);
|
|
return VK_INCOMPLETE;
|
|
}
|
|
|
|
VkPipelineCacheHeaderVersionOne *header = (VkPipelineCacheHeaderVersionOne *)pData;
|
|
|
|
RDCCOMPILE_ASSERT(sizeof(VkPipelineCacheHeaderVersionOne) == 16 + VK_UUID_SIZE,
|
|
"Pipeline cache header size is wrong");
|
|
|
|
header->headerSize = sizeof(VkPipelineCacheHeaderVersionOne);
|
|
header->headerVersion = VK_PIPELINE_CACHE_HEADER_VERSION_ONE;
|
|
// just in case the user expects a valid vendorID/deviceID, write the real one
|
|
// MULTIDEVICE need to get the right physical device for this device
|
|
header->vendorID = m_PhysicalDeviceData.props.vendorID;
|
|
header->deviceID = m_PhysicalDeviceData.props.deviceID;
|
|
|
|
MakeFakeUUID();
|
|
|
|
memcpy(header->pipelineCacheUUID, fakeRenderDocUUID, VK_UUID_SIZE);
|
|
|
|
RDCCOMPILE_ASSERT(VK_UUID_SIZE == 16, "VK_UUID_SIZE has changed");
|
|
|
|
// empty bytes
|
|
uint32_t *ptr = (uint32_t *)(header + 1);
|
|
*ptr = 0;
|
|
}
|
|
|
|
// we don't want the application to use pipeline caches at all, and especially
|
|
// don't want to return any data for future use. We thus return a technically
|
|
// valid but empty pipeline cache. Our UUID changes every run so in theory the
|
|
// application should never provide an old cache, but just in case we will nop
|
|
// it out in create pipeline cache
|
|
return VK_SUCCESS;
|
|
}
|
|
|
|
VkResult WrappedVulkan::vkMergePipelineCaches(VkDevice device, VkPipelineCache destCache,
|
|
uint32_t srcCacheCount,
|
|
const VkPipelineCache *pSrcCaches)
|
|
{
|
|
// do nothing, our pipeline caches are always dummies
|
|
return VK_SUCCESS;
|
|
}
|
|
|
|
VkResult WrappedVulkan::vkGetPhysicalDeviceExternalImageFormatPropertiesNV(
|
|
VkPhysicalDevice physicalDevice, VkFormat format, VkImageType type, VkImageTiling tiling,
|
|
VkImageUsageFlags usage, VkImageCreateFlags flags,
|
|
VkExternalMemoryHandleTypeFlagsNV externalHandleType,
|
|
VkExternalImageFormatPropertiesNV *pExternalImageFormatProperties)
|
|
{
|
|
return ObjDisp(physicalDevice)
|
|
->GetPhysicalDeviceExternalImageFormatPropertiesNV(Unwrap(physicalDevice), format, type,
|
|
tiling, usage, flags, externalHandleType,
|
|
pExternalImageFormatProperties);
|
|
}
|
|
|
|
#if defined(VK_USE_PLATFORM_WIN32_KHR)
|
|
VkResult WrappedVulkan::vkGetMemoryWin32HandleNV(VkDevice device, VkDeviceMemory memory,
|
|
VkExternalMemoryHandleTypeFlagsNV handleType,
|
|
HANDLE *pHandle)
|
|
{
|
|
return ObjDisp(device)->GetMemoryWin32HandleNV(Unwrap(device), Unwrap(memory), handleType, pHandle);
|
|
}
|
|
|
|
VkResult WrappedVulkan::vkGetMemoryWin32HandleKHR(
|
|
VkDevice device, const VkMemoryGetWin32HandleInfoKHR *pGetWin32HandleInfo, HANDLE *pHandle)
|
|
{
|
|
VkMemoryGetWin32HandleInfoKHR unwrappedInfo = *pGetWin32HandleInfo;
|
|
unwrappedInfo.memory = Unwrap(unwrappedInfo.memory);
|
|
return ObjDisp(device)->GetMemoryWin32HandleKHR(Unwrap(device), &unwrappedInfo, pHandle);
|
|
}
|
|
|
|
VkResult WrappedVulkan::vkGetMemoryWin32HandlePropertiesKHR(
|
|
VkDevice device, VkExternalMemoryHandleTypeFlagBits handleType, HANDLE handle,
|
|
VkMemoryWin32HandlePropertiesKHR *pMemoryWin32HandleProperties)
|
|
{
|
|
return ObjDisp(device)->GetMemoryWin32HandlePropertiesKHR(Unwrap(device), handleType, handle,
|
|
pMemoryWin32HandleProperties);
|
|
}
|
|
#endif
|
|
|
|
VkResult WrappedVulkan::vkGetMemoryFdKHR(VkDevice device, const VkMemoryGetFdInfoKHR *pGetFdInfo,
|
|
int *pFd)
|
|
{
|
|
VkMemoryGetFdInfoKHR unwrappedInfo = *pGetFdInfo;
|
|
unwrappedInfo.memory = Unwrap(unwrappedInfo.memory);
|
|
return ObjDisp(device)->GetMemoryFdKHR(Unwrap(device), &unwrappedInfo, pFd);
|
|
}
|
|
|
|
VkResult WrappedVulkan::vkGetMemoryFdPropertiesKHR(VkDevice device,
|
|
VkExternalMemoryHandleTypeFlagBits handleType,
|
|
int fd,
|
|
VkMemoryFdPropertiesKHR *pMemoryFdProperties)
|
|
{
|
|
return ObjDisp(device)->GetMemoryFdPropertiesKHR(Unwrap(device), handleType, fd,
|
|
pMemoryFdProperties);
|
|
}
|
|
|
|
void WrappedVulkan::vkGetPhysicalDeviceExternalBufferProperties(
|
|
VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalBufferInfo *pExternalBufferInfo,
|
|
VkExternalBufferProperties *pExternalBufferProperties)
|
|
{
|
|
return ObjDisp(physicalDevice)
|
|
->GetPhysicalDeviceExternalBufferProperties(Unwrap(physicalDevice), pExternalBufferInfo,
|
|
pExternalBufferProperties);
|
|
}
|
|
|
|
void WrappedVulkan::vkGetPhysicalDeviceExternalSemaphoreProperties(
|
|
VkPhysicalDevice physicalDevice,
|
|
const VkPhysicalDeviceExternalSemaphoreInfo *pExternalSemaphoreInfo,
|
|
VkExternalSemaphoreProperties *pExternalSemaphoreProperties)
|
|
{
|
|
return ObjDisp(physicalDevice)
|
|
->GetPhysicalDeviceExternalSemaphoreProperties(Unwrap(physicalDevice), pExternalSemaphoreInfo,
|
|
pExternalSemaphoreProperties);
|
|
}
|
|
|
|
void WrappedVulkan::vkGetPhysicalDeviceExternalFenceProperties(
|
|
VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalFenceInfo *pExternalFenceInfo,
|
|
VkExternalFenceProperties *pExternalFenceProperties)
|
|
{
|
|
return ObjDisp(physicalDevice)
|
|
->GetPhysicalDeviceExternalFenceProperties(Unwrap(physicalDevice), pExternalFenceInfo,
|
|
pExternalFenceProperties);
|
|
}
|
|
|
|
void WrappedVulkan::vkGetPhysicalDeviceFeatures2(VkPhysicalDevice physicalDevice,
|
|
VkPhysicalDeviceFeatures2 *pFeatures)
|
|
{
|
|
ObjDisp(physicalDevice)->GetPhysicalDeviceFeatures2(Unwrap(physicalDevice), pFeatures);
|
|
|
|
// if the user is requesting protected memory, make sure it's reported as NOT supported
|
|
VkPhysicalDeviceProtectedMemoryFeatures *protectedMem =
|
|
(VkPhysicalDeviceProtectedMemoryFeatures *)FindNextStruct(
|
|
pFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES);
|
|
|
|
if(protectedMem)
|
|
{
|
|
RDCWARN("Forcibly disabling support for protected memory");
|
|
protectedMem->protectedMemory = VK_FALSE;
|
|
}
|
|
|
|
// in Vulkan 1.2 buffer_device_address can be used without an extension, so we can't hide the
|
|
// extension when capture/replay is not supported. Instead we hide the feature bit here.
|
|
VkPhysicalDeviceVulkan12Features *vulkan12 = (VkPhysicalDeviceVulkan12Features *)FindNextStruct(
|
|
pFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES);
|
|
|
|
if(vulkan12)
|
|
{
|
|
if(vulkan12->bufferDeviceAddressCaptureReplay == VK_FALSE)
|
|
{
|
|
RDCWARN(
|
|
"VkPhysicalDeviceVulkan12Features::bufferDeviceAddressCaptureReplay is false, "
|
|
"can't support capture of bufferDeviceAddress");
|
|
vulkan12->bufferDeviceAddress = vulkan12->bufferDeviceAddressMultiDevice = VK_FALSE;
|
|
}
|
|
}
|
|
|
|
// we don't want to report support for mesh shaders + multiview
|
|
VkPhysicalDeviceMeshShaderFeaturesEXT *mesh =
|
|
(VkPhysicalDeviceMeshShaderFeaturesEXT *)FindNextStruct(
|
|
pFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MESH_SHADER_FEATURES_EXT);
|
|
|
|
if(mesh)
|
|
{
|
|
if(mesh->multiviewMeshShader)
|
|
{
|
|
RDCWARN("Disabling support for multiview + mesh shaders");
|
|
mesh->multiviewMeshShader = VK_FALSE;
|
|
}
|
|
}
|
|
|
|
// report features depending on extensions not supported in RenderDoc as not supported
|
|
VkPhysicalDeviceExtendedDynamicState3FeaturesEXT *dynState3 =
|
|
(VkPhysicalDeviceExtendedDynamicState3FeaturesEXT *)FindNextStruct(
|
|
pFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_3_FEATURES_EXT);
|
|
|
|
#define DISABLE_EDS3_FEATURE(feature) \
|
|
if(dynState3->feature == VK_TRUE) \
|
|
{ \
|
|
RDCWARN("Forcibly disabling support for physical device feature '" #feature "'"); \
|
|
dynState3->feature = VK_FALSE; \
|
|
}
|
|
|
|
if(dynState3)
|
|
{
|
|
// need VK_EXT_blend_operation_advanced
|
|
DISABLE_EDS3_FEATURE(extendedDynamicState3ColorBlendAdvanced);
|
|
// need VK_NV_clip_space_w_scaling
|
|
DISABLE_EDS3_FEATURE(extendedDynamicState3ViewportWScalingEnable);
|
|
// need VK_NV_viewport_swizzle
|
|
DISABLE_EDS3_FEATURE(extendedDynamicState3ViewportSwizzle);
|
|
// need VK_NV_fragment_coverage_to_color
|
|
DISABLE_EDS3_FEATURE(extendedDynamicState3CoverageToColorEnable);
|
|
DISABLE_EDS3_FEATURE(extendedDynamicState3CoverageToColorLocation);
|
|
// need VK_NV_framebuffer_mixed_samples
|
|
DISABLE_EDS3_FEATURE(extendedDynamicState3CoverageModulationMode);
|
|
DISABLE_EDS3_FEATURE(extendedDynamicState3CoverageModulationTableEnable);
|
|
DISABLE_EDS3_FEATURE(extendedDynamicState3CoverageModulationTable);
|
|
// need VK_NV_coverage_reduction_mode
|
|
DISABLE_EDS3_FEATURE(extendedDynamicState3CoverageReductionMode);
|
|
// need VK_NV_representative_fragment_test
|
|
DISABLE_EDS3_FEATURE(extendedDynamicState3RepresentativeFragmentTestEnable);
|
|
// VK_NV_shading_rate_image
|
|
DISABLE_EDS3_FEATURE(extendedDynamicState3ShadingRateImageEnable);
|
|
}
|
|
|
|
#undef DISABLE_EDS3_FEATURE
|
|
}
|
|
|
|
void WrappedVulkan::vkGetPhysicalDeviceProperties2(VkPhysicalDevice physicalDevice,
|
|
VkPhysicalDeviceProperties2 *pProperties)
|
|
{
|
|
ObjDisp(physicalDevice)->GetPhysicalDeviceProperties2(Unwrap(physicalDevice), pProperties);
|
|
|
|
MakeFakeUUID();
|
|
|
|
memcpy(pProperties->properties.pipelineCacheUUID, fakeRenderDocUUID, VK_UUID_SIZE);
|
|
|
|
ClampPhysDevAPIVersion(&pProperties->properties, physicalDevice);
|
|
}
|
|
|
|
void WrappedVulkan::vkGetPhysicalDeviceQueueFamilyProperties2(
|
|
VkPhysicalDevice physicalDevice, uint32_t *pCount,
|
|
VkQueueFamilyProperties2 *pQueueFamilyProperties)
|
|
{
|
|
return ObjDisp(physicalDevice)
|
|
->GetPhysicalDeviceQueueFamilyProperties2(Unwrap(physicalDevice), pCount,
|
|
pQueueFamilyProperties);
|
|
}
|
|
|
|
void WrappedVulkan::vkGetPhysicalDeviceMemoryProperties2(
|
|
VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties2 *pMemoryProperties)
|
|
{
|
|
return ObjDisp(physicalDevice)
|
|
->GetPhysicalDeviceMemoryProperties2(Unwrap(physicalDevice), pMemoryProperties);
|
|
}
|
|
|
|
void WrappedVulkan::vkGetPhysicalDeviceSparseImageFormatProperties2(
|
|
VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSparseImageFormatInfo2 *pFormatInfo,
|
|
uint32_t *pPropertyCount, VkSparseImageFormatProperties2 *pProperties)
|
|
{
|
|
return ObjDisp(physicalDevice)
|
|
->GetPhysicalDeviceSparseImageFormatProperties2(Unwrap(physicalDevice), pFormatInfo,
|
|
pPropertyCount, pProperties);
|
|
}
|
|
|
|
VkResult WrappedVulkan::vkGetShaderInfoAMD(VkDevice device, VkPipeline pipeline,
|
|
VkShaderStageFlagBits shaderStage,
|
|
VkShaderInfoTypeAMD infoType, size_t *pInfoSize,
|
|
void *pInfo)
|
|
{
|
|
return ObjDisp(device)->GetShaderInfoAMD(Unwrap(device), Unwrap(pipeline), shaderStage, infoType,
|
|
pInfoSize, pInfo);
|
|
}
|
|
|
|
void WrappedVulkan::vkGetDescriptorSetLayoutSupport(VkDevice device,
|
|
const VkDescriptorSetLayoutCreateInfo *pCreateInfo,
|
|
VkDescriptorSetLayoutSupport *pSupport)
|
|
{
|
|
VkDescriptorSetLayoutCreateInfo unwrapped = UnwrapInfo(pCreateInfo);
|
|
return ObjDisp(device)->GetDescriptorSetLayoutSupport(Unwrap(device), &unwrapped, pSupport);
|
|
}
|
|
|
|
VkResult WrappedVulkan::vkEnumeratePhysicalDeviceGroups(
|
|
VkInstance instance, uint32_t *pPhysicalDeviceGroupCount,
|
|
VkPhysicalDeviceGroupProperties *pPhysicalDeviceGroupProperties)
|
|
{
|
|
// we ignore the 'real' physical device groups, and report one group per physical device. We use
|
|
// our internal enumerate function to make sure we handle wrapping the objects.
|
|
uint32_t numPhys = 0;
|
|
vkEnumeratePhysicalDevices(instance, &numPhys, NULL);
|
|
|
|
VkPhysicalDevice *phys = new VkPhysicalDevice[numPhys];
|
|
vkEnumeratePhysicalDevices(instance, &numPhys, phys);
|
|
|
|
uint32_t outputSpace = pPhysicalDeviceGroupCount ? *pPhysicalDeviceGroupCount : 0;
|
|
|
|
if(pPhysicalDeviceGroupCount)
|
|
*pPhysicalDeviceGroupCount = numPhys;
|
|
|
|
if(pPhysicalDeviceGroupProperties)
|
|
{
|
|
// list one group per device
|
|
for(uint32_t i = 0; i < outputSpace; i++)
|
|
{
|
|
RDCEraseEl(pPhysicalDeviceGroupProperties[i]);
|
|
pPhysicalDeviceGroupProperties[i].sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GROUP_PROPERTIES;
|
|
pPhysicalDeviceGroupProperties[i].physicalDeviceCount = 1;
|
|
pPhysicalDeviceGroupProperties[i].physicalDevices[0] = phys[i];
|
|
pPhysicalDeviceGroupProperties[i].subsetAllocation = VK_FALSE;
|
|
}
|
|
}
|
|
|
|
delete[] phys;
|
|
|
|
if(pPhysicalDeviceGroupProperties && outputSpace < numPhys)
|
|
return VK_INCOMPLETE;
|
|
|
|
return VK_SUCCESS;
|
|
}
|
|
|
|
void WrappedVulkan::vkGetDeviceGroupPeerMemoryFeatures(VkDevice device, uint32_t heapIndex,
|
|
uint32_t localDeviceIndex,
|
|
uint32_t remoteDeviceIndex,
|
|
VkPeerMemoryFeatureFlags *pPeerMemoryFeatures)
|
|
{
|
|
return ObjDisp(device)->GetDeviceGroupPeerMemoryFeatures(
|
|
Unwrap(device), heapIndex, localDeviceIndex, remoteDeviceIndex, pPeerMemoryFeatures);
|
|
}
|
|
|
|
VkResult WrappedVulkan::vkCreateValidationCacheEXT(VkDevice device,
|
|
const VkValidationCacheCreateInfoEXT *pCreateInfo,
|
|
const VkAllocationCallbacks *pAllocator,
|
|
VkValidationCacheEXT *pValidationCache)
|
|
{
|
|
return ObjDisp(device)->CreateValidationCacheEXT(Unwrap(device), pCreateInfo, pAllocator,
|
|
pValidationCache);
|
|
}
|
|
|
|
void WrappedVulkan::vkDestroyValidationCacheEXT(VkDevice device, VkValidationCacheEXT validationCache,
|
|
const VkAllocationCallbacks *pAllocator)
|
|
{
|
|
return ObjDisp(device)->DestroyValidationCacheEXT(Unwrap(device), validationCache, pAllocator);
|
|
}
|
|
|
|
VkResult WrappedVulkan::vkMergeValidationCachesEXT(VkDevice device, VkValidationCacheEXT dstCache,
|
|
uint32_t srcCacheCount,
|
|
const VkValidationCacheEXT *pSrcCaches)
|
|
{
|
|
return ObjDisp(device)->MergeValidationCachesEXT(Unwrap(device), dstCache, srcCacheCount,
|
|
pSrcCaches);
|
|
}
|
|
|
|
VkResult WrappedVulkan::vkGetValidationCacheDataEXT(VkDevice device,
|
|
VkValidationCacheEXT validationCache,
|
|
size_t *pDataSize, void *pData)
|
|
{
|
|
return ObjDisp(device)->GetValidationCacheDataEXT(Unwrap(device), validationCache, pDataSize,
|
|
pData);
|
|
}
|
|
void WrappedVulkan::vkGetPhysicalDeviceMultisamplePropertiesEXT(
|
|
VkPhysicalDevice physicalDevice, VkSampleCountFlagBits samples,
|
|
VkMultisamplePropertiesEXT *pMultisampleProperties)
|
|
{
|
|
return ObjDisp(physicalDevice)
|
|
->GetPhysicalDeviceMultisamplePropertiesEXT(Unwrap(physicalDevice), samples,
|
|
pMultisampleProperties);
|
|
}
|
|
|
|
VkResult WrappedVulkan::vkGetPhysicalDeviceCalibrateableTimeDomainsEXT(
|
|
VkPhysicalDevice physicalDevice, uint32_t *pTimeDomainCount, VkTimeDomainEXT *pTimeDomains)
|
|
{
|
|
return ObjDisp(physicalDevice)
|
|
->GetPhysicalDeviceCalibrateableTimeDomainsEXT(Unwrap(physicalDevice), pTimeDomainCount,
|
|
pTimeDomains);
|
|
}
|
|
|
|
VkResult WrappedVulkan::vkGetCalibratedTimestampsEXT(VkDevice device, uint32_t timestampCount,
|
|
const VkCalibratedTimestampInfoEXT *pTimestampInfos,
|
|
uint64_t *pTimestamps, uint64_t *pMaxDeviation)
|
|
{
|
|
return ObjDisp(device)->GetCalibratedTimestampsEXT(Unwrap(device), timestampCount,
|
|
pTimestampInfos, pTimestamps, pMaxDeviation);
|
|
}
|
|
|
|
VkDeviceAddress WrappedVulkan::vkGetBufferDeviceAddressEXT(VkDevice device,
|
|
const VkBufferDeviceAddressInfoEXT *pInfo)
|
|
{
|
|
VkBufferDeviceAddressInfoEXT unwrappedInfo = *pInfo;
|
|
unwrappedInfo.buffer = Unwrap(unwrappedInfo.buffer);
|
|
return ObjDisp(device)->GetBufferDeviceAddressEXT(Unwrap(device), &unwrappedInfo);
|
|
}
|
|
|
|
VkResult WrappedVulkan::vkGetPipelineExecutablePropertiesKHR(
|
|
VkDevice device, const VkPipelineInfoKHR *pPipelineInfo, uint32_t *pExecutableCount,
|
|
VkPipelineExecutablePropertiesKHR *pProperties)
|
|
{
|
|
VkPipelineInfoKHR unwrappedInfo = *pPipelineInfo;
|
|
unwrappedInfo.pipeline = Unwrap(unwrappedInfo.pipeline);
|
|
return ObjDisp(device)->GetPipelineExecutablePropertiesKHR(Unwrap(device), &unwrappedInfo,
|
|
pExecutableCount, pProperties);
|
|
}
|
|
|
|
VkResult WrappedVulkan::vkGetPipelineExecutableStatisticsKHR(
|
|
VkDevice device, const VkPipelineExecutableInfoKHR *pExecutableInfo, uint32_t *pStatisticCount,
|
|
VkPipelineExecutableStatisticKHR *pStatistics)
|
|
{
|
|
VkPipelineExecutableInfoKHR unwrappedInfo = *pExecutableInfo;
|
|
unwrappedInfo.pipeline = Unwrap(unwrappedInfo.pipeline);
|
|
return ObjDisp(device)->GetPipelineExecutableStatisticsKHR(Unwrap(device), &unwrappedInfo,
|
|
pStatisticCount, pStatistics);
|
|
}
|
|
|
|
VkResult WrappedVulkan::vkGetPipelineExecutableInternalRepresentationsKHR(
|
|
VkDevice device, const VkPipelineExecutableInfoKHR *pExecutableInfo,
|
|
uint32_t *pInternalRepresentationCount,
|
|
VkPipelineExecutableInternalRepresentationKHR *pInternalRepresentations)
|
|
{
|
|
VkPipelineExecutableInfoKHR unwrappedInfo = *pExecutableInfo;
|
|
unwrappedInfo.pipeline = Unwrap(unwrappedInfo.pipeline);
|
|
return ObjDisp(device)->GetPipelineExecutableInternalRepresentationsKHR(
|
|
Unwrap(device), &unwrappedInfo, pInternalRepresentationCount, pInternalRepresentations);
|
|
}
|
|
|
|
VkDeviceAddress WrappedVulkan::vkGetBufferDeviceAddress(VkDevice device,
|
|
VkBufferDeviceAddressInfo *pInfo)
|
|
{
|
|
VkBufferDeviceAddressInfo unwrappedInfo = *pInfo;
|
|
unwrappedInfo.buffer = Unwrap(unwrappedInfo.buffer);
|
|
return ObjDisp(device)->GetBufferDeviceAddress(Unwrap(device), &unwrappedInfo);
|
|
}
|
|
|
|
uint64_t WrappedVulkan::vkGetBufferOpaqueCaptureAddress(VkDevice device,
|
|
VkBufferDeviceAddressInfo *pInfo)
|
|
{
|
|
VkBufferDeviceAddressInfo unwrappedInfo = *pInfo;
|
|
unwrappedInfo.buffer = Unwrap(unwrappedInfo.buffer);
|
|
return ObjDisp(device)->GetBufferOpaqueCaptureAddress(Unwrap(device), &unwrappedInfo);
|
|
}
|
|
|
|
uint64_t WrappedVulkan::vkGetDeviceMemoryOpaqueCaptureAddress(
|
|
VkDevice device, VkDeviceMemoryOpaqueCaptureAddressInfo *pInfo)
|
|
{
|
|
VkDeviceMemoryOpaqueCaptureAddressInfo unwrappedInfo = *pInfo;
|
|
unwrappedInfo.memory = Unwrap(unwrappedInfo.memory);
|
|
return ObjDisp(device)->GetDeviceMemoryOpaqueCaptureAddress(Unwrap(device), &unwrappedInfo);
|
|
}
|
|
|
|
VkResult WrappedVulkan::vkGetPhysicalDeviceToolProperties(VkPhysicalDevice physicalDevice,
|
|
uint32_t *pToolCount,
|
|
VkPhysicalDeviceToolProperties *pToolProperties)
|
|
{
|
|
// check how many tools are downstream. The function pointer will be NULL if no-one else supports
|
|
// this extension except us.
|
|
uint32_t downstreamCount = 0;
|
|
if(ObjDisp(physicalDevice)->GetPhysicalDeviceToolProperties != NULL)
|
|
ObjDisp(physicalDevice)
|
|
->GetPhysicalDeviceToolProperties(Unwrap(physicalDevice), &downstreamCount, NULL);
|
|
|
|
// if we're just enumerating, pToolProperties is NULL, so set the tool count and return
|
|
if(pToolCount && pToolProperties == NULL)
|
|
{
|
|
*pToolCount = downstreamCount + 1;
|
|
return VK_SUCCESS;
|
|
}
|
|
|
|
// otherwise we expect both to be non-NULL
|
|
if(pToolCount == NULL || pToolProperties == NULL)
|
|
return VK_INCOMPLETE;
|
|
|
|
// this is how much space is in the array, don't forget it
|
|
uint32_t availableCount = *pToolCount;
|
|
|
|
VkResult vkr = VK_SUCCESS;
|
|
|
|
if(ObjDisp(physicalDevice)->GetPhysicalDeviceToolProperties != NULL)
|
|
{
|
|
// call downstream to populate the array (up to what's available)
|
|
// this writes up to availableCount properties into pToolProperties, and sets the number written
|
|
// in pToolCount
|
|
vkr = ObjDisp(physicalDevice)
|
|
->GetPhysicalDeviceToolProperties(Unwrap(physicalDevice), pToolCount, pToolProperties);
|
|
}
|
|
else
|
|
{
|
|
// nothing written downstream
|
|
*pToolCount = 0;
|
|
}
|
|
|
|
// if available isn't enough, return VK_INCOMPLETE now
|
|
if(vkr == VK_INCOMPLETE || availableCount < downstreamCount + 1)
|
|
return VK_INCOMPLETE;
|
|
|
|
// otherwise we write our own properties in after any downstream properties, then increment
|
|
// pToolCount
|
|
|
|
VkPhysicalDeviceToolProperties &props = *(pToolProperties + *pToolCount);
|
|
|
|
const rdcstr name = "RenderDoc"_lit;
|
|
const rdcstr version = StringFormat::Fmt(
|
|
"%s (%s)", FULL_VERSION_STRING, GitVersionHash[0] == 'N' ? "Unknown revision" : GitVersionHash);
|
|
const rdcstr description = "Debugging capture layer for RenderDoc"_lit;
|
|
|
|
RDCASSERTMSG("Name is too long for VkPhysicalDeviceToolProperties",
|
|
name.length() < sizeof(props.name));
|
|
RDCASSERTMSG("Version is too long for VkPhysicalDeviceToolProperties",
|
|
version.length() < sizeof(props.version));
|
|
RDCASSERTMSG("Description is too long for VkPhysicalDeviceToolProperties",
|
|
description.length() < sizeof(props.description));
|
|
|
|
memcpy(props.name, name.c_str(), name.length() + 1);
|
|
memcpy(props.version, version.c_str(), version.length() + 1);
|
|
props.purposes = VK_TOOL_PURPOSE_TRACING_BIT | VK_TOOL_PURPOSE_DEBUG_MARKERS_BIT_EXT |
|
|
VK_TOOL_PURPOSE_MODIFYING_FEATURES_BIT;
|
|
memcpy(props.description, description.c_str(), description.length() + 1);
|
|
// do not tell people about the layer
|
|
RDCEraseEl(props.layer);
|
|
|
|
(*pToolCount)++;
|
|
return VK_SUCCESS;
|
|
}
|
|
|
|
VkResult WrappedVulkan::vkGetPhysicalDeviceFragmentShadingRatesKHR(
|
|
VkPhysicalDevice physicalDevice, uint32_t *pFragmentShadingRateCount,
|
|
VkPhysicalDeviceFragmentShadingRateKHR *pFragmentShadingRates)
|
|
{
|
|
return ObjDisp(physicalDevice)
|
|
->GetPhysicalDeviceFragmentShadingRatesKHR(Unwrap(physicalDevice), pFragmentShadingRateCount,
|
|
pFragmentShadingRates);
|
|
}
|