mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-05-05 01:20:42 +00:00
20a60efd98
* These captures were too broken and likely no-one has any in the wild, but just to be safe let's at least not crash on serialisation. * Prepare/application is not implemented yet so as of this commit sparse resources are broken.
1378 lines
45 KiB
C++
1378 lines
45 KiB
C++
/******************************************************************************
|
|
* The MIT License (MIT)
|
|
*
|
|
* Copyright (c) 2019-2021 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_common.h"
|
|
#include "vk_core.h"
|
|
#include "vk_manager.h"
|
|
#include "vk_resources.h"
|
|
|
|
WrappedVulkan *VkMarkerRegion::vk = NULL;
|
|
|
|
VkMarkerRegion::VkMarkerRegion(VkCommandBuffer cmd, const rdcstr &marker)
|
|
{
|
|
if(cmd == VK_NULL_HANDLE)
|
|
return;
|
|
|
|
cmdbuf = cmd;
|
|
Begin(marker, cmd);
|
|
}
|
|
|
|
VkMarkerRegion::VkMarkerRegion(VkQueue q, const rdcstr &marker)
|
|
{
|
|
if(q == VK_NULL_HANDLE)
|
|
{
|
|
if(vk)
|
|
q = vk->GetQ();
|
|
else
|
|
return;
|
|
}
|
|
|
|
queue = q;
|
|
Begin(marker, q);
|
|
}
|
|
|
|
VkMarkerRegion::~VkMarkerRegion()
|
|
{
|
|
if(queue)
|
|
End(queue);
|
|
else if(cmdbuf)
|
|
End(cmdbuf);
|
|
}
|
|
|
|
void VkMarkerRegion::Begin(const rdcstr &marker, VkCommandBuffer cmd)
|
|
{
|
|
if(cmd == VK_NULL_HANDLE)
|
|
return;
|
|
|
|
// check for presence of the marker extension
|
|
if(!ObjDisp(cmd)->CmdBeginDebugUtilsLabelEXT)
|
|
return;
|
|
|
|
VkDebugUtilsLabelEXT label = {};
|
|
label.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT;
|
|
label.pLabelName = marker.c_str();
|
|
ObjDisp(cmd)->CmdBeginDebugUtilsLabelEXT(Unwrap(cmd), &label);
|
|
}
|
|
|
|
void VkMarkerRegion::Set(const rdcstr &marker, VkCommandBuffer cmd)
|
|
{
|
|
if(cmd == VK_NULL_HANDLE)
|
|
return;
|
|
|
|
// check for presence of the marker extension
|
|
if(!ObjDisp(cmd)->CmdInsertDebugUtilsLabelEXT)
|
|
return;
|
|
|
|
VkDebugUtilsLabelEXT label = {};
|
|
label.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT;
|
|
label.pLabelName = marker.c_str();
|
|
ObjDisp(cmd)->CmdInsertDebugUtilsLabelEXT(Unwrap(cmd), &label);
|
|
}
|
|
|
|
void VkMarkerRegion::End(VkCommandBuffer cmd)
|
|
{
|
|
if(cmd == VK_NULL_HANDLE)
|
|
return;
|
|
|
|
// check for presence of the marker extension
|
|
if(!ObjDisp(cmd)->CmdEndDebugUtilsLabelEXT)
|
|
return;
|
|
|
|
ObjDisp(cmd)->CmdEndDebugUtilsLabelEXT(Unwrap(cmd));
|
|
}
|
|
|
|
void VkMarkerRegion::Begin(const rdcstr &marker, VkQueue q)
|
|
{
|
|
if(q == VK_NULL_HANDLE)
|
|
{
|
|
if(vk)
|
|
q = vk->GetQ();
|
|
else
|
|
return;
|
|
}
|
|
|
|
// check for presence of the marker extension
|
|
if(!ObjDisp(q)->QueueBeginDebugUtilsLabelEXT)
|
|
return;
|
|
|
|
VkDebugUtilsLabelEXT label = {};
|
|
label.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT;
|
|
label.pLabelName = marker.c_str();
|
|
ObjDisp(q)->QueueBeginDebugUtilsLabelEXT(Unwrap(q), &label);
|
|
}
|
|
|
|
void VkMarkerRegion::Set(const rdcstr &marker, VkQueue q)
|
|
{
|
|
if(q == VK_NULL_HANDLE)
|
|
{
|
|
if(vk)
|
|
q = vk->GetQ();
|
|
else
|
|
return;
|
|
}
|
|
|
|
// check for presence of the marker extension
|
|
if(!ObjDisp(q)->QueueInsertDebugUtilsLabelEXT)
|
|
return;
|
|
|
|
VkDebugUtilsLabelEXT label = {};
|
|
label.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT;
|
|
label.pLabelName = marker.c_str();
|
|
ObjDisp(q)->QueueInsertDebugUtilsLabelEXT(Unwrap(q), &label);
|
|
}
|
|
|
|
void VkMarkerRegion::End(VkQueue q)
|
|
{
|
|
if(q == VK_NULL_HANDLE)
|
|
{
|
|
if(vk)
|
|
q = vk->GetQ();
|
|
else
|
|
return;
|
|
}
|
|
|
|
// check for presence of the marker extension
|
|
if(!ObjDisp(q)->QueueEndDebugUtilsLabelEXT)
|
|
return;
|
|
|
|
ObjDisp(q)->QueueEndDebugUtilsLabelEXT(Unwrap(q));
|
|
}
|
|
|
|
template <>
|
|
void NameVulkanObject(VkImage obj, const rdcstr &name)
|
|
{
|
|
if(!VkMarkerRegion::vk)
|
|
return;
|
|
|
|
VkDevice dev = VkMarkerRegion::vk->GetDev();
|
|
|
|
if(!ObjDisp(dev)->SetDebugUtilsObjectNameEXT)
|
|
return;
|
|
|
|
VkDebugUtilsObjectNameInfoEXT info = {};
|
|
info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT;
|
|
info.objectType = VK_OBJECT_TYPE_IMAGE;
|
|
info.objectHandle = NON_DISP_TO_UINT64(Unwrap(obj));
|
|
info.pObjectName = name.c_str();
|
|
ObjDisp(dev)->SetDebugUtilsObjectNameEXT(Unwrap(dev), &info);
|
|
}
|
|
|
|
void GPUBuffer::Create(WrappedVulkan *driver, VkDevice dev, VkDeviceSize size, uint32_t ringSize,
|
|
uint32_t flags)
|
|
{
|
|
m_pDriver = driver;
|
|
device = dev;
|
|
createFlags = flags;
|
|
|
|
align = (VkDeviceSize)driver->GetDeviceProps().limits.minUniformBufferOffsetAlignment;
|
|
|
|
// for simplicity, consider the non-coherent atom size also an alignment requirement
|
|
align = AlignUp(align, driver->GetDeviceProps().limits.nonCoherentAtomSize);
|
|
|
|
sz = size;
|
|
// offset must be aligned, so ensure we have at least ringSize
|
|
// copies accounting for that
|
|
totalsize = AlignUp(size, align) * RDCMAX(1U, ringSize);
|
|
curoffset = 0;
|
|
|
|
ringCount = ringSize;
|
|
|
|
VkBufferCreateInfo bufInfo = {
|
|
VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, NULL, 0, totalsize, 0,
|
|
};
|
|
|
|
bufInfo.usage |= VK_BUFFER_USAGE_TRANSFER_DST_BIT;
|
|
|
|
if((flags & eGPUBufferReadback) == 0)
|
|
{
|
|
bufInfo.usage |= VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
|
|
bufInfo.usage |= VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
|
|
}
|
|
|
|
if(flags & eGPUBufferVBuffer)
|
|
bufInfo.usage |= VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
|
|
|
|
if(flags & eGPUBufferIBuffer)
|
|
bufInfo.usage |= VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
|
|
|
|
if(flags & eGPUBufferSSBO)
|
|
bufInfo.usage |= VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
|
|
|
|
if(flags & eGPUBufferIndirectBuffer)
|
|
bufInfo.usage |= VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT;
|
|
|
|
if(flags & eGPUBufferAddressable)
|
|
bufInfo.usage |= VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT;
|
|
|
|
VkResult vkr = driver->vkCreateBuffer(dev, &bufInfo, NULL, &buf);
|
|
RDCASSERTEQUAL(vkr, VK_SUCCESS);
|
|
|
|
VkMemoryRequirements mrq = {};
|
|
driver->vkGetBufferMemoryRequirements(dev, buf, &mrq);
|
|
|
|
VkMemoryAllocateInfo allocInfo = {VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, NULL, mrq.size, 0};
|
|
|
|
if(flags & eGPUBufferReadback)
|
|
allocInfo.memoryTypeIndex = driver->GetReadbackMemoryIndex(mrq.memoryTypeBits);
|
|
else if(flags & eGPUBufferGPULocal)
|
|
allocInfo.memoryTypeIndex = driver->GetGPULocalMemoryIndex(mrq.memoryTypeBits);
|
|
else
|
|
allocInfo.memoryTypeIndex = driver->GetUploadMemoryIndex(mrq.memoryTypeBits);
|
|
|
|
bool useBufferAddressKHR = driver->GetExtensions(NULL).ext_KHR_buffer_device_address;
|
|
|
|
VkMemoryAllocateFlagsInfo memFlags = {VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO};
|
|
if(useBufferAddressKHR && (flags & eGPUBufferAddressable))
|
|
{
|
|
allocInfo.pNext = &memFlags;
|
|
memFlags.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT;
|
|
}
|
|
|
|
vkr = driver->vkAllocateMemory(dev, &allocInfo, NULL, &mem);
|
|
RDCASSERTEQUAL(vkr, VK_SUCCESS);
|
|
|
|
vkr = driver->vkBindBufferMemory(dev, buf, mem, 0);
|
|
RDCASSERTEQUAL(vkr, VK_SUCCESS);
|
|
}
|
|
|
|
void GPUBuffer::FillDescriptor(VkDescriptorBufferInfo &desc)
|
|
{
|
|
desc.buffer = Unwrap(buf);
|
|
desc.offset = 0;
|
|
desc.range = sz;
|
|
}
|
|
|
|
void GPUBuffer::Destroy()
|
|
{
|
|
if(device != VK_NULL_HANDLE)
|
|
{
|
|
m_pDriver->vkDestroyBuffer(device, buf, NULL);
|
|
m_pDriver->vkFreeMemory(device, mem, NULL);
|
|
}
|
|
}
|
|
|
|
void *GPUBuffer::Map(uint32_t *bindoffset, VkDeviceSize usedsize)
|
|
{
|
|
VkDeviceSize offset = bindoffset ? curoffset : 0;
|
|
VkDeviceSize size = usedsize > 0 ? usedsize : sz;
|
|
|
|
// align the size so we always consume coherent atoms
|
|
size = AlignUp(size, align);
|
|
|
|
// wrap around the ring as soon as the 'sz' would overflow. This is because if we're using dynamic
|
|
// offsets in the descriptor the range is still set to that fixed size and the validation
|
|
// complains if we go off the end (even if it's unused). Rather than constantly update the
|
|
// descriptor, we just conservatively wrap and waste the last bit of space.
|
|
if(offset + sz > totalsize)
|
|
offset = 0;
|
|
RDCASSERT(offset + size <= totalsize);
|
|
|
|
// offset must be aligned
|
|
curoffset = AlignUp(offset + size, align);
|
|
|
|
if(bindoffset)
|
|
*bindoffset = (uint32_t)offset;
|
|
|
|
mapoffset = offset;
|
|
|
|
void *ptr = NULL;
|
|
VkResult vkr = m_pDriver->vkMapMemory(device, mem, offset, size, 0, (void **)&ptr);
|
|
RDCASSERTEQUAL(vkr, VK_SUCCESS);
|
|
|
|
if(createFlags & eGPUBufferReadback)
|
|
{
|
|
VkMappedMemoryRange range = {
|
|
VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, NULL, mem, offset, size,
|
|
};
|
|
|
|
vkr = m_pDriver->vkInvalidateMappedMemoryRanges(device, 1, &range);
|
|
RDCASSERTEQUAL(vkr, VK_SUCCESS);
|
|
}
|
|
|
|
return ptr;
|
|
}
|
|
|
|
void *GPUBuffer::Map(VkDeviceSize &bindoffset, VkDeviceSize usedsize)
|
|
{
|
|
uint32_t offs = 0;
|
|
|
|
void *ret = Map(&offs, usedsize);
|
|
|
|
bindoffset = offs;
|
|
|
|
return ret;
|
|
}
|
|
|
|
void GPUBuffer::Unmap()
|
|
{
|
|
if(!(createFlags & eGPUBufferReadback) && !(createFlags & eGPUBufferGPULocal))
|
|
{
|
|
VkMappedMemoryRange range = {
|
|
VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, NULL, mem, mapoffset, VK_WHOLE_SIZE,
|
|
};
|
|
|
|
VkResult vkr = m_pDriver->vkFlushMappedMemoryRanges(device, 1, &range);
|
|
RDCASSERTEQUAL(vkr, VK_SUCCESS);
|
|
}
|
|
|
|
m_pDriver->vkUnmapMemory(device, mem);
|
|
}
|
|
|
|
bool VkInitParams::IsSupportedVersion(uint64_t ver)
|
|
{
|
|
if(ver == CurrentVersion)
|
|
return true;
|
|
|
|
// 0x12 -> 0x13 - added full sparse resource support
|
|
if(ver == 0x12)
|
|
return true;
|
|
|
|
// 0x11 -> 0x12 - added inline uniform block support
|
|
if(ver == 0x11)
|
|
return true;
|
|
|
|
// 0x10 -> 0x11 - non-breaking changes to image state serialization
|
|
if(ver == 0x10)
|
|
return true;
|
|
|
|
// 0xF -> 0x10 - added serialisation of VkPhysicalDeviceDriverPropertiesKHR into enumerated
|
|
// physical devices
|
|
if(ver == 0xF)
|
|
return true;
|
|
|
|
// 0xE -> 0xF - serialisation of VkPhysicalDeviceVulkanMemoryModelFeaturesKHR changed in vulkan
|
|
// 1.1.99, adding a new field
|
|
if(ver == 0xE)
|
|
return true;
|
|
|
|
// 0xD -> 0xE - fixed serialisation directly of size_t members in VkDescriptorUpdateTemplateEntry
|
|
if(ver == 0xD)
|
|
return true;
|
|
|
|
// 0xC -> 0xD - supported multiple queues. This didn't cause a large change to the serialisation
|
|
// but there were some slight inconsistencies that required a version bump
|
|
if(ver == 0xC)
|
|
return true;
|
|
|
|
// 0xB -> 0xC - generally this is when we started serialising pNext chains that older RenderDoc
|
|
// couldn't support. But we don't need any special backwards compatibiltiy code as it's just added
|
|
// serialisation.
|
|
if(ver == 0xB)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
void SanitiseReplayImageLayout(VkImageLayout &layout)
|
|
{
|
|
// we don't replay with present layouts since we don't create actual swapchains. So change any
|
|
// present layouts to general layouts
|
|
if(layout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR || layout == VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR)
|
|
layout = VK_IMAGE_LAYOUT_GENERAL;
|
|
}
|
|
|
|
void SanitiseOldImageLayout(VkImageLayout &layout)
|
|
{
|
|
// we don't replay with present layouts since we don't create actual swapchains. So change any
|
|
// present layouts to general layouts
|
|
if(layout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR || layout == VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR)
|
|
layout = VK_IMAGE_LAYOUT_GENERAL;
|
|
|
|
// we can't transition to PREINITIALIZED, so instead use GENERAL. This allows host access so we
|
|
// can still replay maps of the image's memory. In theory we can still transition from
|
|
// PREINITIALIZED on replay, but consider that we need to be able to reset layouts and suddenly we
|
|
// have a problem transitioning from PREINITIALIZED more than once - so for that reason we
|
|
// instantly promote any images that are PREINITIALIZED to GENERAL at the start of the frame
|
|
// capture, and from then on treat it as the same
|
|
if(layout == VK_IMAGE_LAYOUT_PREINITIALIZED)
|
|
layout = VK_IMAGE_LAYOUT_GENERAL;
|
|
}
|
|
|
|
void SanitiseNewImageLayout(VkImageLayout &layout)
|
|
{
|
|
// apply any general image layout sanitisation
|
|
SanitiseOldImageLayout(layout);
|
|
|
|
// we also can't transition to UNDEFINED, so go to GENERAL instead. This is safe since if the
|
|
// layout was supposed to be undefined before then the only valid transition *from* the state is
|
|
// UNDEFINED, which will work silently.
|
|
if(layout == VK_IMAGE_LAYOUT_UNDEFINED)
|
|
layout = VK_IMAGE_LAYOUT_GENERAL;
|
|
}
|
|
|
|
void CombineDepthStencilLayouts(rdcarray<VkImageMemoryBarrier> &barriers)
|
|
{
|
|
for(size_t i = 0; i < barriers.size(); i++)
|
|
{
|
|
// only consider barriers on depth
|
|
// barriers not on D/S at all can be ignored
|
|
// barriers on both D/S already can be ignored
|
|
// barriers on stencil only can be ignored, because we expect to always find depth before
|
|
// stencil
|
|
if(barriers[i].subresourceRange.aspectMask != VK_IMAGE_ASPECT_DEPTH_BIT)
|
|
continue;
|
|
|
|
// search forward to see if we have an identical barrier on stencil for the same image. We
|
|
// expect a loose sort so all barriers for the same image are together.
|
|
// This means when we don't have separate depth-stencil layout support, the aspects should
|
|
// always be in the same layout so can be combined.
|
|
for(size_t j = i + 1; j < barriers.size(); j++)
|
|
{
|
|
// stop when we reach another image, no more possible matches expected after this
|
|
if(barriers[i].image != barriers[j].image)
|
|
break;
|
|
|
|
// only consider stencil aspect barriers
|
|
if(barriers[j].subresourceRange.aspectMask != VK_IMAGE_ASPECT_STENCIL_BIT)
|
|
continue;
|
|
|
|
// if the barriers are equal apart from the aspect mask, we can promote [i] to depth and
|
|
// stencil, and erase j
|
|
if(barriers[i].oldLayout == barriers[j].oldLayout &&
|
|
barriers[i].newLayout == barriers[j].newLayout &&
|
|
barriers[i].srcAccessMask == barriers[j].srcAccessMask &&
|
|
barriers[i].dstAccessMask == barriers[j].dstAccessMask &&
|
|
barriers[i].srcQueueFamilyIndex == barriers[j].srcQueueFamilyIndex &&
|
|
barriers[i].dstQueueFamilyIndex == barriers[j].dstQueueFamilyIndex &&
|
|
barriers[i].subresourceRange.baseArrayLayer == barriers[j].subresourceRange.baseArrayLayer &&
|
|
barriers[i].subresourceRange.baseMipLevel == barriers[j].subresourceRange.baseMipLevel &&
|
|
barriers[i].subresourceRange.layerCount == barriers[j].subresourceRange.layerCount &&
|
|
barriers[i].subresourceRange.levelCount == barriers[j].subresourceRange.levelCount)
|
|
{
|
|
barriers[i].subresourceRange.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
|
|
barriers.erase(j);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// either we merged the i'th element and we can skip it, or it's not mergeable and we can skip
|
|
// it. Either way we can continue the loop at i+1
|
|
}
|
|
}
|
|
|
|
int SampleCount(VkSampleCountFlagBits countFlag)
|
|
{
|
|
switch(countFlag)
|
|
{
|
|
case VK_SAMPLE_COUNT_1_BIT: return 1;
|
|
case VK_SAMPLE_COUNT_2_BIT: return 2;
|
|
case VK_SAMPLE_COUNT_4_BIT: return 4;
|
|
case VK_SAMPLE_COUNT_8_BIT: return 8;
|
|
case VK_SAMPLE_COUNT_16_BIT: return 16;
|
|
case VK_SAMPLE_COUNT_32_BIT: return 32;
|
|
case VK_SAMPLE_COUNT_64_BIT: return 64;
|
|
default: RDCERR("Unrecognised/not single flag %x", countFlag); break;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int SampleIndex(VkSampleCountFlagBits countFlag)
|
|
{
|
|
switch(countFlag)
|
|
{
|
|
case VK_SAMPLE_COUNT_1_BIT: return 0;
|
|
case VK_SAMPLE_COUNT_2_BIT: return 1;
|
|
case VK_SAMPLE_COUNT_4_BIT: return 2;
|
|
case VK_SAMPLE_COUNT_8_BIT: return 3;
|
|
case VK_SAMPLE_COUNT_16_BIT: return 4;
|
|
case VK_SAMPLE_COUNT_32_BIT: return 5;
|
|
case VK_SAMPLE_COUNT_64_BIT: return 6;
|
|
default: RDCERR("Unrecognised/not single flag %x", countFlag); break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int StageIndex(VkShaderStageFlagBits stageFlag)
|
|
{
|
|
switch(stageFlag)
|
|
{
|
|
case VK_SHADER_STAGE_VERTEX_BIT: return 0;
|
|
case VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT: return 1;
|
|
case VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT: return 2;
|
|
case VK_SHADER_STAGE_GEOMETRY_BIT: return 3;
|
|
case VK_SHADER_STAGE_FRAGMENT_BIT: return 4;
|
|
case VK_SHADER_STAGE_COMPUTE_BIT: return 5;
|
|
default: RDCERR("Unrecognised/not single flag %x", stageFlag); break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void DoPipelineBarrier(VkCommandBuffer cmd, size_t count, const VkImageMemoryBarrier *barriers)
|
|
{
|
|
RDCASSERT(cmd != VK_NULL_HANDLE);
|
|
ObjDisp(cmd)->CmdPipelineBarrier(Unwrap(cmd), VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
|
|
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0,
|
|
NULL, // global memory barriers
|
|
0, NULL, // buffer memory barriers
|
|
(uint32_t)count, barriers); // image memory barriers
|
|
}
|
|
|
|
void DoPipelineBarrier(VkCommandBuffer cmd, size_t count, const VkBufferMemoryBarrier *barriers)
|
|
{
|
|
RDCASSERT(cmd != VK_NULL_HANDLE);
|
|
ObjDisp(cmd)->CmdPipelineBarrier(Unwrap(cmd), VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
|
|
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0,
|
|
NULL, // global memory barriers
|
|
(uint32_t)count, barriers, // buffer memory barriers
|
|
0, NULL); // image memory barriers
|
|
}
|
|
|
|
void DoPipelineBarrier(VkCommandBuffer cmd, size_t count, const VkMemoryBarrier *barriers)
|
|
{
|
|
RDCASSERT(cmd != VK_NULL_HANDLE);
|
|
ObjDisp(cmd)->CmdPipelineBarrier(Unwrap(cmd), VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
|
|
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, (uint32_t)count,
|
|
barriers, // global memory barriers
|
|
0, NULL, // buffer memory barriers
|
|
0, NULL); // image memory barriers
|
|
}
|
|
|
|
Topology MakePrimitiveTopology(VkPrimitiveTopology Topo, uint32_t patchControlPoints)
|
|
{
|
|
switch(Topo)
|
|
{
|
|
default: break;
|
|
case VK_PRIMITIVE_TOPOLOGY_POINT_LIST: return Topology::PointList;
|
|
case VK_PRIMITIVE_TOPOLOGY_LINE_LIST: return Topology::LineList;
|
|
case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP: return Topology::LineStrip;
|
|
case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST: return Topology::TriangleList;
|
|
case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP: return Topology::TriangleStrip;
|
|
case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN: return Topology::TriangleFan;
|
|
case VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY: return Topology::LineList_Adj;
|
|
case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY: return Topology::LineStrip_Adj;
|
|
case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY: return Topology::TriangleList_Adj;
|
|
case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY: return Topology::TriangleStrip_Adj;
|
|
case VK_PRIMITIVE_TOPOLOGY_PATCH_LIST: return PatchList_Topology(patchControlPoints);
|
|
}
|
|
|
|
return Topology::Unknown;
|
|
}
|
|
|
|
VkPrimitiveTopology MakeVkPrimitiveTopology(Topology Topo)
|
|
{
|
|
switch(Topo)
|
|
{
|
|
case Topology::LineLoop: RDCWARN("Unsupported primitive topology on Vulkan: %x", Topo); break;
|
|
default: return VK_PRIMITIVE_TOPOLOGY_MAX_ENUM;
|
|
case Topology::PointList: return VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
|
|
case Topology::LineStrip: return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP;
|
|
case Topology::LineList: return VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
|
|
case Topology::LineStrip_Adj: return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY;
|
|
case Topology::LineList_Adj: return VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY;
|
|
case Topology::TriangleStrip: return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
|
|
case Topology::TriangleFan: return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN;
|
|
case Topology::TriangleList: return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
|
|
case Topology::TriangleStrip_Adj: return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY;
|
|
case Topology::TriangleList_Adj: return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY;
|
|
case Topology::PatchList_1CPs:
|
|
case Topology::PatchList_2CPs:
|
|
case Topology::PatchList_3CPs:
|
|
case Topology::PatchList_4CPs:
|
|
case Topology::PatchList_5CPs:
|
|
case Topology::PatchList_6CPs:
|
|
case Topology::PatchList_7CPs:
|
|
case Topology::PatchList_8CPs:
|
|
case Topology::PatchList_9CPs:
|
|
case Topology::PatchList_10CPs:
|
|
case Topology::PatchList_11CPs:
|
|
case Topology::PatchList_12CPs:
|
|
case Topology::PatchList_13CPs:
|
|
case Topology::PatchList_14CPs:
|
|
case Topology::PatchList_15CPs:
|
|
case Topology::PatchList_16CPs:
|
|
case Topology::PatchList_17CPs:
|
|
case Topology::PatchList_18CPs:
|
|
case Topology::PatchList_19CPs:
|
|
case Topology::PatchList_20CPs:
|
|
case Topology::PatchList_21CPs:
|
|
case Topology::PatchList_22CPs:
|
|
case Topology::PatchList_23CPs:
|
|
case Topology::PatchList_24CPs:
|
|
case Topology::PatchList_25CPs:
|
|
case Topology::PatchList_26CPs:
|
|
case Topology::PatchList_27CPs:
|
|
case Topology::PatchList_28CPs:
|
|
case Topology::PatchList_29CPs:
|
|
case Topology::PatchList_30CPs:
|
|
case Topology::PatchList_31CPs:
|
|
case Topology::PatchList_32CPs: return VK_PRIMITIVE_TOPOLOGY_PATCH_LIST;
|
|
}
|
|
|
|
return VK_PRIMITIVE_TOPOLOGY_MAX_ENUM;
|
|
}
|
|
|
|
AddressMode MakeAddressMode(VkSamplerAddressMode addr)
|
|
{
|
|
switch(addr)
|
|
{
|
|
case VK_SAMPLER_ADDRESS_MODE_REPEAT: return AddressMode::Wrap;
|
|
case VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT: return AddressMode::Mirror;
|
|
case VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE: return AddressMode::ClampEdge;
|
|
case VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER: return AddressMode::ClampBorder;
|
|
case VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE: return AddressMode::MirrorOnce;
|
|
default: break;
|
|
}
|
|
|
|
return AddressMode::Wrap;
|
|
}
|
|
|
|
void MakeBorderColor(VkBorderColor border, rdcfixedarray<float, 4> &BorderColor)
|
|
{
|
|
// we don't distinguish float/int, assume it matches
|
|
switch(border)
|
|
{
|
|
case VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK:
|
|
case VK_BORDER_COLOR_INT_TRANSPARENT_BLACK: BorderColor = {0.0f, 0.0f, 0.0f, 0.0f}; break;
|
|
case VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK:
|
|
case VK_BORDER_COLOR_INT_OPAQUE_BLACK: BorderColor = {0.0f, 0.0f, 0.0f, 1.0f}; break;
|
|
case VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE:
|
|
case VK_BORDER_COLOR_INT_OPAQUE_WHITE: BorderColor = {1.0f, 1.0f, 1.0f, 1.0f}; break;
|
|
default: BorderColor = {0.0f, 0.0f, 0.0f, 0.0f}; break;
|
|
}
|
|
}
|
|
|
|
CompareFunction MakeCompareFunc(VkCompareOp func)
|
|
{
|
|
switch(func)
|
|
{
|
|
case VK_COMPARE_OP_NEVER: return CompareFunction::Never;
|
|
case VK_COMPARE_OP_LESS: return CompareFunction::Less;
|
|
case VK_COMPARE_OP_EQUAL: return CompareFunction::Equal;
|
|
case VK_COMPARE_OP_LESS_OR_EQUAL: return CompareFunction::LessEqual;
|
|
case VK_COMPARE_OP_GREATER: return CompareFunction::Greater;
|
|
case VK_COMPARE_OP_NOT_EQUAL: return CompareFunction::NotEqual;
|
|
case VK_COMPARE_OP_GREATER_OR_EQUAL: return CompareFunction::GreaterEqual;
|
|
case VK_COMPARE_OP_ALWAYS: return CompareFunction::AlwaysTrue;
|
|
default: break;
|
|
}
|
|
|
|
return CompareFunction::AlwaysTrue;
|
|
}
|
|
|
|
FilterMode MakeFilterMode(VkFilter f)
|
|
{
|
|
switch(f)
|
|
{
|
|
case VK_FILTER_NEAREST: return FilterMode::Point;
|
|
case VK_FILTER_LINEAR: return FilterMode::Linear;
|
|
case VK_FILTER_CUBIC_EXT: return FilterMode::Cubic;
|
|
default: break;
|
|
}
|
|
|
|
return FilterMode::NoFilter;
|
|
}
|
|
|
|
static FilterMode MakeFilterMode(VkSamplerMipmapMode f)
|
|
{
|
|
switch(f)
|
|
{
|
|
case VK_SAMPLER_MIPMAP_MODE_NEAREST: return FilterMode::Point;
|
|
case VK_SAMPLER_MIPMAP_MODE_LINEAR: return FilterMode::Linear;
|
|
default: break;
|
|
}
|
|
|
|
return FilterMode::NoFilter;
|
|
}
|
|
|
|
TextureFilter MakeFilter(VkFilter minFilter, VkFilter magFilter, VkSamplerMipmapMode mipmapMode,
|
|
bool anisoEnable, bool compareEnable, VkSamplerReductionMode reduction)
|
|
{
|
|
TextureFilter ret;
|
|
|
|
if(anisoEnable)
|
|
{
|
|
ret.minify = ret.magnify = ret.mip = FilterMode::Anisotropic;
|
|
}
|
|
else
|
|
{
|
|
ret.minify = MakeFilterMode(minFilter);
|
|
ret.magnify = MakeFilterMode(magFilter);
|
|
ret.mip = MakeFilterMode(mipmapMode);
|
|
}
|
|
ret.filter = compareEnable ? FilterFunction::Comparison : FilterFunction::Normal;
|
|
|
|
if(compareEnable)
|
|
{
|
|
ret.filter = FilterFunction::Comparison;
|
|
}
|
|
else
|
|
{
|
|
switch(reduction)
|
|
{
|
|
default:
|
|
case VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE: ret.filter = FilterFunction::Normal; break;
|
|
case VK_SAMPLER_REDUCTION_MODE_MIN: ret.filter = FilterFunction::Minimum; break;
|
|
case VK_SAMPLER_REDUCTION_MODE_MAX: ret.filter = FilterFunction::Maximum; break;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
LogicOperation MakeLogicOp(VkLogicOp op)
|
|
{
|
|
switch(op)
|
|
{
|
|
case VK_LOGIC_OP_CLEAR: return LogicOperation::Clear;
|
|
case VK_LOGIC_OP_AND: return LogicOperation::And;
|
|
case VK_LOGIC_OP_AND_REVERSE: return LogicOperation::AndReverse;
|
|
case VK_LOGIC_OP_COPY: return LogicOperation::Copy;
|
|
case VK_LOGIC_OP_AND_INVERTED: return LogicOperation::AndInverted;
|
|
case VK_LOGIC_OP_NO_OP: return LogicOperation::NoOp;
|
|
case VK_LOGIC_OP_XOR: return LogicOperation::Xor;
|
|
case VK_LOGIC_OP_OR: return LogicOperation::Or;
|
|
case VK_LOGIC_OP_NOR: return LogicOperation::Nor;
|
|
case VK_LOGIC_OP_EQUIVALENT: return LogicOperation::Equivalent;
|
|
case VK_LOGIC_OP_INVERT: return LogicOperation::Invert;
|
|
case VK_LOGIC_OP_OR_REVERSE: return LogicOperation::OrReverse;
|
|
case VK_LOGIC_OP_COPY_INVERTED: return LogicOperation::CopyInverted;
|
|
case VK_LOGIC_OP_OR_INVERTED: return LogicOperation::OrInverted;
|
|
case VK_LOGIC_OP_NAND: return LogicOperation::Nand;
|
|
case VK_LOGIC_OP_SET: return LogicOperation::Set;
|
|
default: break;
|
|
}
|
|
|
|
return LogicOperation::NoOp;
|
|
}
|
|
|
|
BlendMultiplier MakeBlendMultiplier(VkBlendFactor blend)
|
|
{
|
|
switch(blend)
|
|
{
|
|
case VK_BLEND_FACTOR_ZERO: return BlendMultiplier::Zero;
|
|
case VK_BLEND_FACTOR_ONE: return BlendMultiplier::One;
|
|
case VK_BLEND_FACTOR_SRC_COLOR: return BlendMultiplier::SrcCol;
|
|
case VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR: return BlendMultiplier::InvSrcCol;
|
|
case VK_BLEND_FACTOR_DST_COLOR: return BlendMultiplier::DstCol;
|
|
case VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR: return BlendMultiplier::InvDstCol;
|
|
case VK_BLEND_FACTOR_SRC_ALPHA: return BlendMultiplier::SrcAlpha;
|
|
case VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA: return BlendMultiplier::InvSrcAlpha;
|
|
case VK_BLEND_FACTOR_DST_ALPHA: return BlendMultiplier::DstAlpha;
|
|
case VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA: return BlendMultiplier::InvDstAlpha;
|
|
case VK_BLEND_FACTOR_CONSTANT_COLOR: return BlendMultiplier::FactorRGB;
|
|
case VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR: return BlendMultiplier::InvFactorRGB;
|
|
case VK_BLEND_FACTOR_CONSTANT_ALPHA: return BlendMultiplier::FactorAlpha;
|
|
case VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA: return BlendMultiplier::InvFactorAlpha;
|
|
case VK_BLEND_FACTOR_SRC_ALPHA_SATURATE: return BlendMultiplier::SrcAlphaSat;
|
|
case VK_BLEND_FACTOR_SRC1_COLOR: return BlendMultiplier::Src1Col;
|
|
case VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR: return BlendMultiplier::InvSrc1Col;
|
|
case VK_BLEND_FACTOR_SRC1_ALPHA: return BlendMultiplier::Src1Alpha;
|
|
case VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA: return BlendMultiplier::InvSrc1Alpha;
|
|
default: break;
|
|
}
|
|
|
|
return BlendMultiplier::One;
|
|
}
|
|
|
|
BlendOperation MakeBlendOp(VkBlendOp op)
|
|
{
|
|
// Need to update this when we support VK_EXT_blend_operation_advanced
|
|
switch(op)
|
|
{
|
|
case VK_BLEND_OP_ADD: return BlendOperation::Add;
|
|
case VK_BLEND_OP_SUBTRACT: return BlendOperation::Subtract;
|
|
case VK_BLEND_OP_REVERSE_SUBTRACT: return BlendOperation::ReversedSubtract;
|
|
case VK_BLEND_OP_MIN: return BlendOperation::Minimum;
|
|
case VK_BLEND_OP_MAX: return BlendOperation::Maximum;
|
|
default: break;
|
|
}
|
|
|
|
return BlendOperation::Add;
|
|
}
|
|
|
|
StencilOperation MakeStencilOp(VkStencilOp op)
|
|
{
|
|
switch(op)
|
|
{
|
|
case VK_STENCIL_OP_KEEP: return StencilOperation::Keep;
|
|
case VK_STENCIL_OP_ZERO: return StencilOperation::Zero;
|
|
case VK_STENCIL_OP_REPLACE: return StencilOperation::Replace;
|
|
case VK_STENCIL_OP_INCREMENT_AND_CLAMP: return StencilOperation::IncSat;
|
|
case VK_STENCIL_OP_DECREMENT_AND_CLAMP: return StencilOperation::DecSat;
|
|
case VK_STENCIL_OP_INVERT: return StencilOperation::Invert;
|
|
case VK_STENCIL_OP_INCREMENT_AND_WRAP: return StencilOperation::IncWrap;
|
|
case VK_STENCIL_OP_DECREMENT_AND_WRAP: return StencilOperation::DecWrap;
|
|
default: break;
|
|
}
|
|
|
|
return StencilOperation::Keep;
|
|
}
|
|
|
|
rdcstr HumanDriverName(VkDriverId driverId)
|
|
{
|
|
switch(driverId)
|
|
{
|
|
case VK_DRIVER_ID_AMD_PROPRIETARY: return "AMD Propriertary";
|
|
case VK_DRIVER_ID_AMD_OPEN_SOURCE: return "AMD Open-source";
|
|
case VK_DRIVER_ID_MESA_RADV: return "AMD RADV";
|
|
case VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS: return "Intel Propriertary";
|
|
case VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA: return "Intel Open-source";
|
|
default: break;
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
BASIC_TYPE_SERIALISE_STRINGIFY(VkPackedVersion, (uint32_t &)el, SDBasic::UnsignedInteger, 4);
|
|
|
|
INSTANTIATE_SERIALISE_TYPE(VkPackedVersion);
|
|
|
|
template <typename SerialiserType>
|
|
void DoSerialise(SerialiserType &ser, VkInitParams &el)
|
|
{
|
|
SERIALISE_MEMBER(AppName);
|
|
SERIALISE_MEMBER(EngineName);
|
|
SERIALISE_MEMBER(AppVersion);
|
|
SERIALISE_MEMBER(EngineVersion);
|
|
SERIALISE_MEMBER(APIVersion).TypedAs("uint32_t"_lit);
|
|
SERIALISE_MEMBER(Layers);
|
|
SERIALISE_MEMBER(Extensions);
|
|
SERIALISE_MEMBER(InstanceID).TypedAs("VkInstance"_lit);
|
|
}
|
|
|
|
INSTANTIATE_SERIALISE_TYPE(VkInitParams);
|
|
|
|
VkDriverInfo::VkDriverInfo(const VkPhysicalDeviceProperties &physProps)
|
|
{
|
|
m_Vendor = GPUVendorFromPCIVendor(physProps.vendorID);
|
|
|
|
// add non-PCI vendor IDs
|
|
if(physProps.vendorID == VK_VENDOR_ID_VSI)
|
|
m_Vendor = GPUVendor::Verisilicon;
|
|
|
|
// Swiftshader
|
|
if(physProps.vendorID == 0x1AE0 && physProps.deviceID == 0xC0DE)
|
|
m_Vendor = GPUVendor::Software;
|
|
|
|
// mesa software
|
|
if(physProps.vendorID == VK_VENDOR_ID_MESA)
|
|
m_Vendor = GPUVendor::Software;
|
|
|
|
m_Major = VK_VERSION_MAJOR(physProps.driverVersion);
|
|
m_Minor = VK_VERSION_MINOR(physProps.driverVersion);
|
|
m_Patch = VK_VERSION_PATCH(physProps.driverVersion);
|
|
|
|
#if ENABLED(RDOC_APPLE)
|
|
metalBackend = true;
|
|
#endif
|
|
|
|
// nvidia uses its own version packing:
|
|
// 10 | 8 | 8 | 6
|
|
// major|minor|secondary_branch|tertiary_branch
|
|
if(m_Vendor == GPUVendor::nVidia)
|
|
{
|
|
m_Major = ((uint32_t)(physProps.driverVersion) >> (8 + 8 + 6)) & 0x3ff;
|
|
m_Minor = ((uint32_t)(physProps.driverVersion) >> (8 + 6)) & 0x0ff;
|
|
|
|
uint32_t secondary = ((uint32_t)(physProps.driverVersion) >> 6) & 0x0ff;
|
|
uint32_t tertiary = physProps.driverVersion & 0x03f;
|
|
|
|
m_Patch = (secondary << 8) | tertiary;
|
|
}
|
|
|
|
#if ENABLED(RDOC_WIN32)
|
|
// Ditto for Intel on windows
|
|
// 18 | 14
|
|
// major|minor
|
|
if(m_Vendor == GPUVendor::Intel)
|
|
{
|
|
m_Major = ((uint32_t)(physProps.driverVersion) >> 14) & 0xffffc;
|
|
m_Minor = (uint32_t)(physProps.driverVersion) & 0x3fff;
|
|
m_Patch = 0;
|
|
}
|
|
#endif
|
|
|
|
if(m_Vendor == GPUVendor::nVidia)
|
|
{
|
|
// drivers before 372.54 did not handle a glslang bugfix about separated samplers,
|
|
// and disabling texelFetch works as a workaround.
|
|
|
|
if(Major() < 372 || (Major() == 372 && Minor() < 54))
|
|
texelFetchBrokenDriver = true;
|
|
}
|
|
|
|
// only check this on windows. This is a bit of a hack, as really we want to check if we're
|
|
// using the AMD official driver, but there's not a great other way to distinguish it from
|
|
// the RADV open source driver.
|
|
#if ENABLED(RDOC_WIN32)
|
|
if(m_Vendor == GPUVendor::AMD)
|
|
{
|
|
// for AMD the bugfix version isn't clear as version numbering wasn't strong for a while, but
|
|
// any driver that reports a version of >= 1.0.0 is fine, as previous versions all reported
|
|
// 0.9.0 as the version.
|
|
|
|
if(Major() < 1)
|
|
texelFetchBrokenDriver = true;
|
|
|
|
// driver 18.5.2 which is vulkan version >= 2.0.33 contains the fix
|
|
if(physProps.driverVersion < VK_MAKE_VERSION(2, 0, 33))
|
|
unreliableImgMemReqs = true;
|
|
}
|
|
#endif
|
|
|
|
if(texelFetchBrokenDriver)
|
|
{
|
|
RDCWARN("Detected an older driver, enabling workaround. Try updating to the latest drivers.");
|
|
}
|
|
|
|
// same as above, only affects the AMD official driver
|
|
#if ENABLED(RDOC_WIN32)
|
|
if(m_Vendor == GPUVendor::AMD)
|
|
{
|
|
// driver 18.5.2 which is vulkan version >= 2.0.33 contains the fix
|
|
if(physProps.driverVersion < VK_MAKE_VERSION(2, 0, 33))
|
|
amdStorageMSAABrokenDriver = true;
|
|
}
|
|
|
|
if(m_Vendor == GPUVendor::AMD)
|
|
{
|
|
// not yet fully fixed
|
|
amdBDABrokenDriver = true;
|
|
}
|
|
#endif
|
|
|
|
// not fixed yet that I know of
|
|
qualcommLeakingUBOOffsets = (m_Vendor == GPUVendor::Qualcomm);
|
|
qualcommDrefNon2DCompileCrash = (m_Vendor == GPUVendor::Qualcomm);
|
|
qualcommLineWidthCrash = (m_Vendor == GPUVendor::Qualcomm);
|
|
}
|
|
|
|
FrameRefType GetRefType(VkDescriptorType descType)
|
|
{
|
|
switch(descType)
|
|
{
|
|
case VK_DESCRIPTOR_TYPE_SAMPLER:
|
|
case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
|
|
case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
|
|
case VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT:
|
|
case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
|
|
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
|
|
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
|
|
case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: return eFrameRef_Read;
|
|
case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
|
|
case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
|
|
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
|
|
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: return eFrameRef_ReadBeforeWrite;
|
|
default: RDCERR("Unexpected descriptor type");
|
|
}
|
|
|
|
return eFrameRef_Read;
|
|
}
|
|
|
|
bool IsValid(bool allowNULLDescriptors, const VkWriteDescriptorSet &write, uint32_t arrayElement)
|
|
{
|
|
if(write.descriptorType == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT)
|
|
return true;
|
|
|
|
// this makes assumptions that only hold within the context of Serialise_InitialState below,
|
|
// specifically that if pTexelBufferView/pBufferInfo is set then we are using them. In the general
|
|
// case they can be garbage and we must ignore them based on the descriptorType
|
|
|
|
if(write.pTexelBufferView)
|
|
return allowNULLDescriptors ? true : write.pTexelBufferView[arrayElement] != VK_NULL_HANDLE;
|
|
|
|
if(write.pBufferInfo)
|
|
return allowNULLDescriptors ? true : write.pBufferInfo[arrayElement].buffer != VK_NULL_HANDLE;
|
|
|
|
if(write.pImageInfo)
|
|
{
|
|
// only these two types need samplers
|
|
bool needSampler = (write.descriptorType == VK_DESCRIPTOR_TYPE_SAMPLER ||
|
|
write.descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
|
|
|
|
// but all types that aren't just a sampler need an image
|
|
bool needImage = (write.descriptorType != VK_DESCRIPTOR_TYPE_SAMPLER);
|
|
|
|
if(allowNULLDescriptors)
|
|
needImage = false;
|
|
|
|
if(needSampler && write.pImageInfo[arrayElement].sampler == VK_NULL_HANDLE)
|
|
return false;
|
|
|
|
if(needImage && write.pImageInfo[arrayElement].imageView == VK_NULL_HANDLE)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
RDCERR("Encountered VkWriteDescriptorSet with no data!");
|
|
|
|
return false;
|
|
}
|
|
|
|
void DescriptorSetSlotBufferInfo::SetFrom(const VkDescriptorBufferInfo &bufInfo)
|
|
{
|
|
buffer = GetResID(bufInfo.buffer);
|
|
offset = bufInfo.offset;
|
|
range = bufInfo.range;
|
|
}
|
|
|
|
void DescriptorSetSlotImageInfo::SetFrom(const VkDescriptorImageInfo &imInfo, bool setSampler,
|
|
bool setImageView)
|
|
{
|
|
if(setSampler)
|
|
sampler = GetResID(imInfo.sampler);
|
|
if(setImageView)
|
|
imageView = GetResID(imInfo.imageView);
|
|
imageLayout = imInfo.imageLayout;
|
|
}
|
|
|
|
void AddBindFrameRef(DescriptorBindRefs &refs, ResourceId id, FrameRefType ref)
|
|
{
|
|
if(id == ResourceId())
|
|
{
|
|
RDCERR("Unexpected NULL resource ID being added as a bind frame ref");
|
|
return;
|
|
}
|
|
FrameRefType &p = refs.bindFrameRefs[id];
|
|
// be conservative - mark refs as read before write if we see a write and a read ref on it
|
|
p = ComposeFrameRefsUnordered(p, ref);
|
|
}
|
|
|
|
void AddImgFrameRef(DescriptorBindRefs &refs, VkResourceRecord *view, FrameRefType refType)
|
|
{
|
|
AddBindFrameRef(refs, view->GetResourceID(), eFrameRef_Read);
|
|
if(view->resInfo && view->resInfo->IsSparse())
|
|
refs.sparseRefs.insert(view);
|
|
if(view->baseResourceMem != ResourceId())
|
|
AddBindFrameRef(refs, view->baseResourceMem, eFrameRef_Read);
|
|
|
|
FrameRefType &p = refs.bindFrameRefs[view->baseResource];
|
|
|
|
ImageRange imgRange = ImageRange((VkImageSubresourceRange)view->viewRange);
|
|
imgRange.viewType = view->viewRange.viewType();
|
|
|
|
FrameRefType maxRef =
|
|
MarkImageReferenced(refs.bindImageStates, view->baseResource, view->resInfo->imageInfo,
|
|
ImageSubresourceRange(imgRange), VK_QUEUE_FAMILY_IGNORED, refType);
|
|
|
|
p = ComposeFrameRefsDisjoint(p, maxRef);
|
|
}
|
|
|
|
void AddMemFrameRef(DescriptorBindRefs &refs, ResourceId mem, VkDeviceSize offset,
|
|
VkDeviceSize size, FrameRefType refType)
|
|
{
|
|
if(mem == ResourceId())
|
|
{
|
|
RDCERR("Unexpected NULL resource ID being added as a bind frame ref");
|
|
return;
|
|
}
|
|
FrameRefType &p = refs.bindFrameRefs[mem];
|
|
FrameRefType maxRef =
|
|
MarkMemoryReferenced(refs.bindMemRefs, mem, offset, size, refType, ComposeFrameRefsUnordered);
|
|
p = ComposeFrameRefsDisjoint(p, maxRef);
|
|
}
|
|
|
|
void DescriptorSetSlot::AccumulateBindRefs(DescriptorBindRefs &refs, VulkanResourceManager *rm,
|
|
FrameRefType ref) const
|
|
{
|
|
VkResourceRecord *bufView = NULL, *imgView = NULL, *buffer = NULL;
|
|
|
|
if(texelBufferView != ResourceId())
|
|
bufView = rm->GetResourceRecord(texelBufferView);
|
|
if(imageInfo.imageView != ResourceId())
|
|
imgView = rm->GetResourceRecord(imageInfo.imageView);
|
|
if(bufferInfo.buffer != ResourceId())
|
|
buffer = rm->GetResourceRecord(bufferInfo.buffer);
|
|
|
|
if(bufView)
|
|
{
|
|
AddBindFrameRef(refs, bufView->GetResourceID(), eFrameRef_Read);
|
|
if(bufView->resInfo && bufView->resInfo->IsSparse())
|
|
refs.sparseRefs.insert(bufView);
|
|
if(bufView->baseResource != ResourceId())
|
|
AddBindFrameRef(refs, bufView->baseResource, eFrameRef_Read);
|
|
if(bufView->baseResourceMem != ResourceId())
|
|
AddMemFrameRef(refs, bufView->baseResourceMem, bufView->memOffset, bufView->memSize, ref);
|
|
}
|
|
if(imgView)
|
|
{
|
|
AddImgFrameRef(refs, imgView, ref);
|
|
}
|
|
if(imageInfo.sampler != ResourceId())
|
|
{
|
|
AddBindFrameRef(refs, imageInfo.sampler, eFrameRef_Read);
|
|
}
|
|
if(buffer)
|
|
{
|
|
AddBindFrameRef(refs, bufferInfo.buffer, eFrameRef_Read);
|
|
if(buffer->resInfo && buffer->resInfo->IsSparse())
|
|
refs.sparseRefs.insert(buffer);
|
|
if(buffer->baseResource != ResourceId())
|
|
AddMemFrameRef(refs, buffer->baseResource, buffer->memOffset, buffer->memSize, ref);
|
|
}
|
|
}
|
|
|
|
#if ENABLED(ENABLE_UNIT_TESTS)
|
|
|
|
#undef None
|
|
#undef Always
|
|
|
|
#include "catch/catch.hpp"
|
|
|
|
bool operator==(const VkImageMemoryBarrier &a, const VkImageMemoryBarrier &b)
|
|
{
|
|
return memcmp(&a, &b, sizeof(VkImageMemoryBarrier)) == 0;
|
|
}
|
|
|
|
TEST_CASE("Validate CombineDepthStencilLayouts works", "[vulkan]")
|
|
{
|
|
VkImage imga, imgb;
|
|
|
|
// give the fake handles values
|
|
{
|
|
uint64_t a = 1;
|
|
uint64_t b = 2;
|
|
memcpy(&imga, &a, sizeof(a));
|
|
memcpy(&imgb, &b, sizeof(b));
|
|
}
|
|
|
|
rdcarray<VkImageMemoryBarrier> barriers, ref;
|
|
|
|
VkImageMemoryBarrier b = {VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER};
|
|
b.oldLayout = VK_IMAGE_LAYOUT_GENERAL;
|
|
b.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
|
|
|
SECTION("Ignored cases")
|
|
{
|
|
VkImageAspectFlags aspects[] = {VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_ASPECT_DEPTH_BIT,
|
|
VK_IMAGE_ASPECT_STENCIL_BIT,
|
|
VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT};
|
|
// only care about split depth and stencil for the same image
|
|
for(VkImageAspectFlags aspect : aspects)
|
|
{
|
|
barriers.clear();
|
|
|
|
// whole image
|
|
b.image = imga;
|
|
b.subresourceRange = {aspect, 0, 4, 0, 6};
|
|
barriers.push_back(b);
|
|
|
|
b.image = imgb;
|
|
b.subresourceRange = {aspect, 0, 4, 0, 6};
|
|
barriers.push_back(b);
|
|
|
|
ref = barriers;
|
|
CombineDepthStencilLayouts(barriers);
|
|
|
|
CHECK((ref == barriers));
|
|
|
|
barriers.clear();
|
|
|
|
// images split into different mips/arrays
|
|
b.image = imga;
|
|
b.subresourceRange = {aspect, 0, 1, 0, 3};
|
|
barriers.push_back(b);
|
|
b.subresourceRange = {aspect, 1, 2, 0, 3};
|
|
barriers.push_back(b);
|
|
b.subresourceRange = {aspect, 3, 1, 0, 3};
|
|
barriers.push_back(b);
|
|
b.subresourceRange = {aspect, 0, 4, 3, 3};
|
|
barriers.push_back(b);
|
|
|
|
b.image = imgb;
|
|
b.subresourceRange = {aspect, 0, 1, 0, 3};
|
|
barriers.push_back(b);
|
|
b.subresourceRange = {aspect, 1, 3, 0, 3};
|
|
barriers.push_back(b);
|
|
b.subresourceRange = {aspect, 0, 4, 3, 3};
|
|
barriers.push_back(b);
|
|
|
|
ref = barriers;
|
|
CombineDepthStencilLayouts(barriers);
|
|
|
|
CHECK((ref == barriers));
|
|
}
|
|
};
|
|
|
|
SECTION("Possible but unmergeable cases")
|
|
{
|
|
barriers.clear();
|
|
|
|
// could merge, but different images
|
|
b.image = imga;
|
|
b.subresourceRange = {VK_IMAGE_ASPECT_DEPTH_BIT, 0, 1, 0, 1};
|
|
barriers.push_back(b);
|
|
|
|
b.image = imgb;
|
|
b.subresourceRange = {VK_IMAGE_ASPECT_STENCIL_BIT, 0, 1, 0, 1};
|
|
barriers.push_back(b);
|
|
|
|
ref = barriers;
|
|
CombineDepthStencilLayouts(barriers);
|
|
|
|
CHECK((ref == barriers));
|
|
|
|
barriers.clear();
|
|
|
|
// could merge, but different subresource ranges
|
|
b.image = imga;
|
|
b.subresourceRange = {VK_IMAGE_ASPECT_DEPTH_BIT, 0, 1, 0, 1};
|
|
barriers.push_back(b);
|
|
|
|
b.image = imgb;
|
|
b.subresourceRange = {VK_IMAGE_ASPECT_STENCIL_BIT, 0, 2, 0, 1};
|
|
barriers.push_back(b);
|
|
|
|
ref = barriers;
|
|
CombineDepthStencilLayouts(barriers);
|
|
|
|
CHECK((ref == barriers));
|
|
|
|
barriers.clear();
|
|
|
|
b.image = imga;
|
|
b.subresourceRange = {VK_IMAGE_ASPECT_DEPTH_BIT, 0, 1, 0, 1};
|
|
barriers.push_back(b);
|
|
|
|
b.image = imgb;
|
|
b.subresourceRange = {VK_IMAGE_ASPECT_STENCIL_BIT, 0, 1, 1, 1};
|
|
barriers.push_back(b);
|
|
|
|
ref = barriers;
|
|
CombineDepthStencilLayouts(barriers);
|
|
|
|
CHECK((ref == barriers));
|
|
|
|
barriers.clear();
|
|
|
|
// could merge, but different layouts
|
|
b.image = imga;
|
|
b.oldLayout = VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL_KHR;
|
|
b.subresourceRange = {VK_IMAGE_ASPECT_DEPTH_BIT, 0, 2, 0, 1};
|
|
barriers.push_back(b);
|
|
|
|
b.image = imgb;
|
|
b.oldLayout = VK_IMAGE_LAYOUT_STENCIL_READ_ONLY_OPTIMAL_KHR;
|
|
b.subresourceRange = {VK_IMAGE_ASPECT_STENCIL_BIT, 0, 2, 0, 1};
|
|
barriers.push_back(b);
|
|
|
|
ref = barriers;
|
|
CombineDepthStencilLayouts(barriers);
|
|
|
|
CHECK((ref == barriers));
|
|
|
|
barriers.clear();
|
|
|
|
b.image = imga;
|
|
b.newLayout = VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL_KHR;
|
|
b.subresourceRange = {VK_IMAGE_ASPECT_DEPTH_BIT, 0, 2, 0, 1};
|
|
barriers.push_back(b);
|
|
|
|
b.image = imgb;
|
|
b.newLayout = VK_IMAGE_LAYOUT_STENCIL_READ_ONLY_OPTIMAL_KHR;
|
|
b.subresourceRange = {VK_IMAGE_ASPECT_STENCIL_BIT, 0, 2, 0, 1};
|
|
barriers.push_back(b);
|
|
|
|
ref = barriers;
|
|
CombineDepthStencilLayouts(barriers);
|
|
|
|
CHECK((ref == barriers));
|
|
};
|
|
|
|
SECTION("Whole-image depth and separate stencil barriers are merged when possible")
|
|
{
|
|
barriers.clear();
|
|
|
|
CHECK((ref == barriers));
|
|
b.image = imga;
|
|
b.subresourceRange = {VK_IMAGE_ASPECT_DEPTH_BIT, 0, 1, 0, 1};
|
|
barriers.push_back(b);
|
|
|
|
b.image = imga;
|
|
b.subresourceRange = {VK_IMAGE_ASPECT_STENCIL_BIT, 0, 1, 0, 1};
|
|
barriers.push_back(b);
|
|
|
|
b.image = imgb;
|
|
b.subresourceRange = {VK_IMAGE_ASPECT_DEPTH_BIT, 0, 1, 0, 1};
|
|
barriers.push_back(b);
|
|
|
|
ref = barriers;
|
|
CombineDepthStencilLayouts(barriers);
|
|
|
|
REQUIRE(barriers.size() == 2);
|
|
// aspect mask is combined now
|
|
CHECK(barriers[0].subresourceRange.aspectMask ==
|
|
(VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT));
|
|
// otherwise the barrier should be the same
|
|
CHECK(barriers[0].subresourceRange.baseMipLevel == 0);
|
|
CHECK(barriers[0].subresourceRange.levelCount == 1);
|
|
CHECK(barriers[0].subresourceRange.baseArrayLayer == 0);
|
|
CHECK(barriers[0].subresourceRange.layerCount == 1);
|
|
CHECK(barriers[0].oldLayout == ref[0].oldLayout);
|
|
CHECK(barriers[0].newLayout == ref[0].newLayout);
|
|
// the last barrier should be the same
|
|
CHECK((barriers[1] == ref[2]));
|
|
};
|
|
|
|
SECTION("Split depth and separate stencil barriers are merged when possible")
|
|
{
|
|
barriers.clear();
|
|
|
|
CHECK((ref == barriers));
|
|
b.image = imga;
|
|
b.subresourceRange = {VK_IMAGE_ASPECT_DEPTH_BIT, 0, 1, 0, 1};
|
|
barriers.push_back(b);
|
|
|
|
b.image = imga;
|
|
b.subresourceRange = {VK_IMAGE_ASPECT_DEPTH_BIT, 1, 1, 0, 1};
|
|
barriers.push_back(b);
|
|
|
|
b.image = imga;
|
|
b.subresourceRange = {VK_IMAGE_ASPECT_STENCIL_BIT, 0, 1, 0, 1};
|
|
barriers.push_back(b);
|
|
|
|
b.image = imga;
|
|
b.subresourceRange = {VK_IMAGE_ASPECT_STENCIL_BIT, 1, 1, 0, 1};
|
|
barriers.push_back(b);
|
|
|
|
CombineDepthStencilLayouts(barriers);
|
|
|
|
REQUIRE(barriers.size() == 2);
|
|
|
|
// aspect mask is combined now
|
|
CHECK(barriers[0].subresourceRange.aspectMask ==
|
|
(VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT));
|
|
// otherwise the barrier should be the same
|
|
CHECK(barriers[0].subresourceRange.baseMipLevel == 0);
|
|
CHECK(barriers[0].subresourceRange.levelCount == 1);
|
|
CHECK(barriers[0].subresourceRange.baseArrayLayer == 0);
|
|
CHECK(barriers[0].subresourceRange.layerCount == 1);
|
|
|
|
// aspect mask is combined now
|
|
CHECK(barriers[1].subresourceRange.aspectMask ==
|
|
(VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT));
|
|
// otherwise the barrier should be the same
|
|
CHECK(barriers[1].subresourceRange.baseMipLevel == 1);
|
|
CHECK(barriers[1].subresourceRange.levelCount == 1);
|
|
CHECK(barriers[1].subresourceRange.baseArrayLayer == 0);
|
|
CHECK(barriers[1].subresourceRange.layerCount == 1);
|
|
};
|
|
}
|
|
|
|
#endif
|