blob: cbb79ab877dde0c05346608b743945ef624e8f7f [file] [log] [blame]
/*------------------------------------------------------------------------
* Vulkan Conformance Tests
* ------------------------
*
* Copyright (c) 2022 The Khronos Group Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*//*!
* \file vktGlobalPriorityQueueUtils.cpp
* \brief Global Priority Queue Utils
*//*--------------------------------------------------------------------*/
#include "vktGlobalPriorityQueueUtils.hpp"
#include "vkImageUtil.hpp"
#include "vkQueryUtil.hpp"
#include "vkTypeUtil.hpp"
#include "vktTestCase.hpp"
#include "deStringUtil.hpp"
#include <vector>
using namespace vk;
namespace vkt
{
namespace synchronization
{
deUint32 findQueueFamilyIndex (const InstanceInterface& vki,
VkPhysicalDevice dev,
VkQueueFlags includeFlags,
VkQueueFlags excludeFlags,
bool priorityQueryEnabled,
QueueGlobalPriorities priorities,
const bool eitherAnyOrAll)
{
deUint32 queueFamilyPropertyCount = 0;
vki.getPhysicalDeviceQueueFamilyProperties2(dev, &queueFamilyPropertyCount, nullptr);
std::vector<VkQueueFamilyGlobalPriorityPropertiesKHR> familyPriorityProperties
(priorityQueryEnabled ? queueFamilyPropertyCount : 0);
std::vector<VkQueueFamilyProperties2> familyProperties2(queueFamilyPropertyCount);
for (deUint32 i = 0; i < queueFamilyPropertyCount; ++i)
{
VkQueueFamilyGlobalPriorityPropertiesKHR* item = nullptr;
if (priorityQueryEnabled)
{
item = &familyPriorityProperties[i];
*item = {};
item->sType = VK_STRUCTURE_TYPE_QUEUE_FAMILY_GLOBAL_PRIORITY_PROPERTIES_KHR;
}
VkQueueFamilyProperties2& item2 = familyProperties2[i];
item2 = {};
item2.sType = VK_STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2;
item2.pNext = item;
}
vki.getPhysicalDeviceQueueFamilyProperties2(dev, &queueFamilyPropertyCount, familyProperties2.data());
for (deUint32 familyIndex = 0; familyIndex < queueFamilyPropertyCount; ++familyIndex)
{
const VkQueueFamilyProperties& props = familyProperties2[familyIndex].queueFamilyProperties;
const VkQueueFlags queueFlags = props.queueFlags;
if ( ((queueFlags & excludeFlags) == 0) && ((queueFlags & includeFlags) == includeFlags) )
{
bool matches = true;
if (priorityQueryEnabled)
{
const QueueGlobalPriorities prios(familyPriorityProperties[familyIndex]);
if (eitherAnyOrAll)
matches = priorities.any(prios);
else matches = priorities.all(prios);
}
if (matches)
{
return familyIndex;
}
}
}
return INVALID_UINT32;
}
QueueGlobalPriorities::QueueGlobalPriorities ()
: m_priorities {}
{
}
QueueGlobalPriorities::QueueGlobalPriorities (const QueueGlobalPriorities& other)
: m_priorities (other.m_priorities)
{
}
QueueGlobalPriorities::QueueGlobalPriorities (const VkQueueFamilyGlobalPriorityPropertiesKHR& source)
: m_priorities ()
{
for (deUint32 i = 0; i < source.priorityCount; ++i)
m_priorities.insert(source.priorities[i]);
}
QueueGlobalPriorities::QueueGlobalPriorities (std::initializer_list<Priority> priorities)
{
for (auto i = priorities.begin(); i != priorities.end(); ++i)
m_priorities.insert(*i);
}
QueueGlobalPriorities QueueGlobalPriorities::full ()
{
return QueueGlobalPriorities(
{
VK_QUEUE_GLOBAL_PRIORITY_LOW_KHR,
VK_QUEUE_GLOBAL_PRIORITY_MEDIUM_KHR,
VK_QUEUE_GLOBAL_PRIORITY_HIGH_KHR,
VK_QUEUE_GLOBAL_PRIORITY_REALTIME_KHR
});
}
bool QueueGlobalPriorities::insert (const Priority& prio)
{
auto item = m_priorities.find(prio);
if (m_priorities.end() == item) {
m_priorities.insert(prio);
return true;
}
return false;
}
bool QueueGlobalPriorities::remove (const Priority& prio)
{
auto item = m_priorities.find(prio);
if (m_priorities.end() != item) {
m_priorities.erase(item);
return true;
}
return false;
}
bool QueueGlobalPriorities::any (const Priority& prio) const
{
return m_priorities.find(prio) != m_priorities.end();
}
auto QueueGlobalPriorities::make (void* pNext) const -> VkQueueFamilyGlobalPriorityPropertiesKHR
{
auto start = m_priorities.begin();
VkQueueFamilyGlobalPriorityPropertiesKHR res{};
res.sType = VK_STRUCTURE_TYPE_QUEUE_FAMILY_GLOBAL_PRIORITY_PROPERTIES_KHR;
res.pNext = pNext;
res.priorityCount = static_cast<deUint32>(m_priorities.size());
for (auto i = start; i != m_priorities.end(); ++i)
res.priorities[std::distance(start, i)] = *i;
return res;
}
bool QueueGlobalPriorities::any (const QueueGlobalPriorities& other) const
{
for (auto i = other.m_priorities.begin(); i != other.m_priorities.end(); ++i)
if (any(*i)) return true;
return false;
}
bool QueueGlobalPriorities::all (const QueueGlobalPriorities& other) const
{
return m_priorities == other.m_priorities;
}
SpecialDevice::SpecialDevice (Context& ctx,
VkQueueFlagBits transitionFrom,
VkQueueFlagBits transitionTo,
VkQueueGlobalPriorityKHR priorityFrom,
VkQueueGlobalPriorityKHR priorityTo,
bool enableProtected,
bool enableSparseBinding)
: queueFamilyIndexFrom (m_queueFamilyIndexFrom)
, queueFamilyIndexTo (m_queueFamilyIndexTo)
, device (m_device)
, queueFrom (m_queueFrom)
, queueTo (m_queueTo)
, m_vkd (ctx.getDeviceInterface())
, m_transitionFrom (transitionFrom)
, m_transitionTo (transitionTo)
, m_queueFamilyIndexFrom (INVALID_UINT32)
, m_queueFamilyIndexTo (INVALID_UINT32)
, m_device (0)
, m_queueFrom (0)
, m_queueTo (0)
, m_allocator ()
, m_creationResult (VK_RESULT_MAX_ENUM)
{
const InstanceInterface& vki = ctx.getInstanceInterface();
const DeviceInterface& vkd = ctx.getDeviceInterface();
const VkPhysicalDevice dev = ctx.getPhysicalDevice();
const VkPhysicalDeviceMemoryProperties memoryProperties = getPhysicalDeviceMemoryProperties(vki, dev);
VkQueueFlags flagFrom = transitionFrom;
VkQueueFlags flagTo = transitionTo;
if (enableProtected)
{
flagFrom |= VK_QUEUE_PROTECTED_BIT;
flagTo |= VK_QUEUE_PROTECTED_BIT;
}
if (enableSparseBinding)
{
flagFrom |= VK_QUEUE_SPARSE_BINDING_BIT;
flagTo |= VK_QUEUE_SPARSE_BINDING_BIT;
}
m_queueFamilyIndexFrom = findQueueFamilyIndex(vki, dev, flagFrom, getColissionFlags(transitionFrom), true, { priorityFrom });
m_queueFamilyIndexTo = findQueueFamilyIndex(vki, dev, flagTo, getColissionFlags(transitionTo), true, { priorityTo });
DE_ASSERT(m_queueFamilyIndexFrom != INVALID_UINT32);
DE_ASSERT(m_queueFamilyIndexTo != INVALID_UINT32);
const float queuePriority = 1.0f;
VkDeviceQueueCreateInfo queueCreateInfos[2];
VkDeviceQueueGlobalPriorityCreateInfoKHR priorityCreateInfos[2];
{
priorityCreateInfos[0].sType = priorityCreateInfos[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_GLOBAL_PRIORITY_CREATE_INFO_KHR;
priorityCreateInfos[0].pNext = priorityCreateInfos[1].pNext = nullptr;
queueCreateInfos[0].sType = queueCreateInfos[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queueCreateInfos[0].flags = queueCreateInfos[1].flags = enableProtected ? VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT : 0;
queueCreateInfos[0].queueCount = queueCreateInfos[1].queueCount = 1;
priorityCreateInfos[0].globalPriority = priorityFrom;
queueCreateInfos[0].pNext = &priorityCreateInfos[0];
queueCreateInfos[0].queueFamilyIndex = m_queueFamilyIndexFrom;
priorityCreateInfos[1].globalPriority = priorityTo;
queueCreateInfos[1].pNext = &priorityCreateInfos[1];
queueCreateInfos[1].queueFamilyIndex = m_queueFamilyIndexTo;
queueCreateInfos[0].pQueuePriorities = &queuePriority;
queueCreateInfos[1].pQueuePriorities = &queuePriority;
}
const VkPhysicalDeviceFeatures& deviceFeatures = ctx.getDeviceFeatures();
std::vector<const char*> extensions;
std::vector<std::string> filteredExtensions;
{
const deUint32 majorApi = VK_API_VERSION_MAJOR(ctx.getUsedApiVersion());
const deUint32 minorApi = VK_API_VERSION_MINOR(ctx.getUsedApiVersion());
std::vector<VkExtensionProperties> availableExtensions = enumerateDeviceExtensionProperties(vki, dev, nullptr);
const bool khrBufferAddress = availableExtensions.end() !=
std::find_if(availableExtensions.begin(), availableExtensions.end(),
[](const VkExtensionProperties& p) { return deStringEqual(p.extensionName, "VK_KHR_buffer_device_address"); });
for (const auto& ext : availableExtensions)
{
if (khrBufferAddress && deStringEqual(ext.extensionName, "VK_EXT_buffer_device_address"))
continue;
if (VK_API_VERSION_MAJOR(ext.specVersion) <= majorApi && VK_API_VERSION_MINOR(ext.specVersion) <= minorApi)
filteredExtensions.emplace_back(ext.extensionName);
}
extensions.resize(filteredExtensions.size());
std::transform(filteredExtensions.begin(), filteredExtensions.end(), extensions.begin(), [](const std::string& s) { return s.c_str(); });
}
VkDeviceCreateInfo deviceCreateInfo {};
deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
deviceCreateInfo.queueCreateInfoCount = 2;
deviceCreateInfo.pQueueCreateInfos = queueCreateInfos;
deviceCreateInfo.pEnabledFeatures = &deviceFeatures;
deviceCreateInfo.enabledExtensionCount = static_cast<deUint32>(extensions.size());
deviceCreateInfo.ppEnabledExtensionNames = de::dataOrNull(extensions);
deviceCreateInfo.ppEnabledLayerNames = nullptr;
deviceCreateInfo.enabledLayerCount = 0;
m_creationResult = vki.createDevice(dev, &deviceCreateInfo, nullptr, &m_device);
if (VK_SUCCESS == m_creationResult && VkDevice(0) != m_device)
{
m_allocator = de::MovePtr<vk::Allocator>(new SimpleAllocator(vkd, m_device, memoryProperties));
if (enableProtected)
{
VkDeviceQueueInfo2 queueInfo{};
queueInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_INFO_2;
queueInfo.flags = VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT;
queueInfo.queueIndex = 0;
queueInfo.queueFamilyIndex = m_queueFamilyIndexFrom;
m_vkd.getDeviceQueue2(m_device, &queueInfo, &m_queueFrom);
queueInfo.queueFamilyIndex = m_queueFamilyIndexTo;
m_vkd.getDeviceQueue2(m_device, &queueInfo, &m_queueTo);
}
else
{
m_vkd.getDeviceQueue(m_device, m_queueFamilyIndexFrom, 0, &m_queueFrom);
m_vkd.getDeviceQueue(m_device, m_queueFamilyIndexTo, 0, &m_queueTo);
}
}
}
VkQueueFlags SpecialDevice::getColissionFlags (VkQueueFlagBits bits)
{
switch (bits)
{
case VK_QUEUE_TRANSFER_BIT:
return (VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT);
case VK_QUEUE_COMPUTE_BIT:
return VK_QUEUE_GRAPHICS_BIT;
case VK_QUEUE_GRAPHICS_BIT:
return 0;
default: break;
}
DE_ASSERT(false);
return 0;
}
Allocator& SpecialDevice::getAllocator() const
{
return *m_allocator;
}
SpecialDevice::SpecialDevice (SpecialDevice&& src)
: queueFamilyIndexFrom (m_queueFamilyIndexFrom)
, queueFamilyIndexTo (m_queueFamilyIndexTo)
, device (m_device)
, queueFrom (m_queueFrom)
, queueTo (m_queueTo)
, m_vkd (src.m_vkd)
, m_transitionFrom (src.m_transitionFrom)
, m_transitionTo (src.m_transitionTo)
, m_queueFamilyIndexFrom (src.m_queueFamilyIndexFrom)
, m_queueFamilyIndexTo (src.m_queueFamilyIndexTo)
, m_device (src.m_device)
, m_queueFrom (src.m_queueFrom)
, m_queueTo (src.m_queueTo)
, m_allocator (src.m_allocator.release())
, m_creationResult (src.m_creationResult)
{
src.m_queueFamilyIndexFrom = INVALID_UINT32;
src.m_queueFamilyIndexTo = INVALID_UINT32;
src.m_device = VkDevice(0);
src.m_queueFrom = VkQueue(0);
src.m_queueTo = VkQueue(0);
}
SpecialDevice::~SpecialDevice ()
{
if (VkDevice(0) != m_device)
{
m_vkd.destroyDevice (m_device, nullptr);
m_device = VkDevice(0);
}
}
bool SpecialDevice::isValid (VkResult& creationResult) const
{
creationResult = m_creationResult;
return (VkDevice(0) != m_device);
}
BufferWithMemory::BufferWithMemory (const vk::InstanceInterface& vki,
const DeviceInterface& vkd,
const vk::VkPhysicalDevice phys,
const VkDevice device,
Allocator& allocator,
const VkBufferCreateInfo& bufferCreateInfo,
const MemoryRequirement memoryRequirement,
const VkQueue sparseQueue)
: m_amISparse ((bufferCreateInfo.flags & VK_BUFFER_CREATE_SPARSE_BINDING_BIT) != 0)
, m_buffer (createBuffer(vkd, device, &bufferCreateInfo))
, m_requirements (getBufferMemoryRequirements(vkd, device, *m_buffer))
{
if (m_amISparse)
{
DE_ASSERT(sparseQueue != VkQueue(0));
const VkPhysicalDeviceMemoryProperties memoryProperties = getPhysicalDeviceMemoryProperties(vki, phys);
const deUint32 memoryTypeIndex = selectMatchingMemoryType(memoryProperties, m_requirements.memoryTypeBits, memoryRequirement);
const VkDeviceSize alignment = 0;
const VkDeviceSize lastChunkSize = m_requirements.size % m_requirements.alignment;
const uint32_t chunkCount = static_cast<uint32_t>(m_requirements.size / m_requirements.alignment + (lastChunkSize ? 1 : 0));
Move<VkFence> fence = createFence(vkd, device);
std::vector<VkSparseMemoryBind> bindings(chunkCount);
for (uint32_t i = 0; i < chunkCount; ++i)
{
VkMemoryAllocateInfo allocInfo{};
allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
allocInfo.pNext = nullptr;
allocInfo.allocationSize = m_requirements.alignment;
allocInfo.memoryTypeIndex = memoryTypeIndex;
de::MovePtr<Allocation> allocation = allocator.allocate(allocInfo, alignment /*unreferenced parameter*/);
VkSparseMemoryBind& binding = bindings[i];
binding.resourceOffset = m_requirements.alignment * i;
binding.size = m_requirements.alignment;
binding.memory = allocation->getMemory();
binding.memoryOffset = allocation->getOffset();
binding.flags = 0;
m_allocations.emplace_back(allocation.release());
}
VkSparseBufferMemoryBindInfo bindInfo{};
bindInfo.buffer = *m_buffer;
bindInfo.bindCount = chunkCount;
bindInfo.pBinds = de::dataOrNull(bindings);
VkBindSparseInfo sparseInfo{};
sparseInfo.sType = VK_STRUCTURE_TYPE_BIND_SPARSE_INFO;
sparseInfo.pNext = nullptr;
sparseInfo.waitSemaphoreCount = 0;
sparseInfo.pWaitSemaphores = nullptr;
sparseInfo.bufferBindCount = 1;
sparseInfo.pBufferBinds = &bindInfo;
sparseInfo.imageOpaqueBindCount = 0;
sparseInfo.pImageOpaqueBinds = nullptr;
sparseInfo.imageBindCount = 0;
sparseInfo.pImageBinds = nullptr;
sparseInfo.signalSemaphoreCount = 0;
sparseInfo.pSignalSemaphores = nullptr;
VK_CHECK(vkd.queueBindSparse(sparseQueue, 1, &sparseInfo, *fence));
VK_CHECK(vkd.waitForFences(device, 1u, &fence.get(), DE_TRUE, ~0ull));
}
else
{
de::MovePtr<Allocation> allocation = allocator.allocate(m_requirements, memoryRequirement);
VK_CHECK(vkd.bindBufferMemory(device, *m_buffer, allocation->getMemory(), allocation->getOffset()));
m_allocations.emplace_back(allocation.release());
}
}
void BufferWithMemory::assertIAmSparse () const
{
if (m_amISparse) TCU_THROW(NotSupportedError, "Host access pointer not implemented for sparse buffers");
}
void* BufferWithMemory::getHostPtr (void) const
{
assertIAmSparse();
return m_allocations[0]->getHostPtr();
}
void BufferWithMemory::invalidateAlloc (const DeviceInterface& vk, const VkDevice device) const
{
assertIAmSparse();
::vk::invalidateAlloc(vk, device, *m_allocations[0]);
}
void BufferWithMemory::flushAlloc (const DeviceInterface& vk, const VkDevice device) const
{
assertIAmSparse();
::vk::flushAlloc(vk, device, *m_allocations[0]);
}
ImageWithMemory::ImageWithMemory (const InstanceInterface& vki,
const DeviceInterface& vkd,
const VkPhysicalDevice phys,
const VkDevice device,
Allocator& allocator,
const VkImageCreateInfo& imageCreateInfo,
const VkQueue sparseQueue,
const MemoryRequirement memoryRequirement)
: m_image(((imageCreateInfo.flags & VK_IMAGE_CREATE_SPARSE_BINDING_BIT) != 0)
? (new image::SparseImage(vkd, device, phys, vki, imageCreateInfo, sparseQueue, allocator, mapVkFormat(imageCreateInfo.format)))
: (new image::Image(vkd, device, allocator, imageCreateInfo, memoryRequirement)))
{
}
} // synchronization
} // vkt