HelperVk.h#
↰ Parent directory (Vulkan
)
Contains helper functions for several common complicated Vulkan tasks, such as swapchain creation and texture uploading.
Includes#
ConvertToPVRVkTypes.h
(ConvertToPVRVkTypes.h)PVRAssets/Model.h
PVRAssets/PVRAssets.h
PVRCore/PVRCore.h
PVRCore/stream/FileStream.h
PVRCore/texture/TextureLoad.h
PVRUtils/MultiObject.h
PVRUtils/Vulkan/MemoryAllocator.h
PVRVk/CommandBufferVk.h
PVRVk/CommandPoolVk.h
PVRVk/DeviceVk.h
PVRVk/ExtensionsVk.h
PVRVk/FramebufferVk.h
PVRVk/ImageVk.h
PVRVk/InstanceVk.h
PVRVk/PhysicalDeviceVk.h
PVRVk/QueueVk.h
PVRVk/RenderPassVk.h
PVRVk/SurfaceVk.h
PVRVk/SwapchainVk.h
Included By#
Namespaces#
Classes#
Functions#
Variables#
Source Code#
#pragma once
#include "PVRCore/PVRCore.h"
#include "PVRCore/stream/FileStream.h"
#include "PVRAssets/Model.h"
#include "PVRAssets/PVRAssets.h"
#include "PVRCore/texture/TextureLoad.h"
#include "PVRVk/DeviceVk.h"
#include "PVRVk/PhysicalDeviceVk.h"
#include "PVRVk/InstanceVk.h"
#include "PVRVk/CommandBufferVk.h"
#include "PVRVk/ImageVk.h"
#include "PVRVk/SurfaceVk.h"
#include "ConvertToPVRVkTypes.h"
#include "PVRVk/CommandPoolVk.h"
#include "PVRVk/QueueVk.h"
#include "PVRVk/RenderPassVk.h"
#include "PVRVk/FramebufferVk.h"
#include "PVRVk/SwapchainVk.h"
#include "PVRUtils/Vulkan/MemoryAllocator.h"
#include "PVRUtils/MultiObject.h"
#include "PVRVk/ExtensionsVk.h"
namespace pvr {
namespace utils {
extern bool PVRUtils_Throw_On_Validation_Error;
#pragma region
inline bool isFormatDepthStencil(pvrvk::Format format) { return format >= pvrvk::Format::e_D16_UNORM && format <= pvrvk::Format::e_D32_SFLOAT_S8_UINT; }
void getMemoryTypeIndex(const pvrvk::PhysicalDevice& physicalDevice, const uint32_t allowedMemoryTypeBits, const pvrvk::MemoryPropertyFlags requiredMemoryProperties,
const pvrvk::MemoryPropertyFlags optimalMemoryProperties, uint32_t& outMemoryTypeIndex, pvrvk::MemoryPropertyFlags& outMemoryPropertyFlags);
inline void populateClearValues(const pvrvk::RenderPass& renderpass, const pvrvk::ClearValue& clearColor, const pvrvk::ClearValue& clearDepthStencilValue, pvrvk::ClearValue* outClearValues)
{
for (uint32_t i = 0; i < renderpass->getCreateInfo().getNumAttachmentDescription(); ++i)
{
const pvrvk::Format& format = renderpass->getCreateInfo().getAttachmentDescription(i).getFormat();
if (pvr::utils::isFormatDepthStencil(format)) { outClearValues[i] = clearDepthStencilValue; }
else
{
outClearValues[i] = clearColor;
}
}
}
inline uint8_t getNumSamplesFromSampleCountFlags(pvrvk::SampleCountFlags sampleCountFlags)
{
uint8_t numSamples = 0;
if (static_cast<uint32_t>(sampleCountFlags & pvrvk::SampleCountFlags::e_1_BIT) != 0) { numSamples += 1; }
if (static_cast<uint32_t>(sampleCountFlags & pvrvk::SampleCountFlags::e_2_BIT) != 0) { numSamples += 2; }
if (static_cast<uint32_t>(sampleCountFlags & pvrvk::SampleCountFlags::e_4_BIT) != 0) { numSamples += 4; }
if (static_cast<uint32_t>(sampleCountFlags & pvrvk::SampleCountFlags::e_8_BIT) != 0) { numSamples += 8; }
if (static_cast<uint32_t>(sampleCountFlags & pvrvk::SampleCountFlags::e_16_BIT) != 0) { numSamples += 16; }
if (static_cast<uint32_t>(sampleCountFlags & pvrvk::SampleCountFlags::e_32_BIT) != 0) { numSamples += 32; }
if (static_cast<uint32_t>(sampleCountFlags & pvrvk::SampleCountFlags::e_64_BIT) != 0) { numSamples += 64; }
return numSamples;
}
pvrvk::ImageAspectFlags inferAspectFromFormat(pvrvk::Format format, uint32_t planeIndex = 0);
void getColorBits(pvrvk::Format format, uint32_t& redBits, uint32_t& greenBits, uint32_t& blueBits, uint32_t& alphaBits);
void getDepthStencilBits(pvrvk::Format format, uint32_t& depthBits, uint32_t& stencilBits);
#pragma endregion
#pragma region
struct DebugUtilsCallbacks
{
public:
pvrvk::DebugUtilsMessenger debugUtilsMessengers[2];
pvrvk::DebugReportCallback debugCallbacks[2];
};
DebugUtilsCallbacks createDebugUtilsCallbacks(pvrvk::Instance& instance, void* pUserData = nullptr);
inline void beginQueueDebugLabel(pvrvk::Queue queue, const pvrvk::DebugUtilsLabel& labelInfo)
{
// if the VK_EXT_debug_utils extension is supported then start the queue label region
if (queue->getDevice()->getPhysicalDevice()->getInstance()->getEnabledExtensionTable().extDebugUtilsEnabled) { queue->beginDebugUtilsLabel(labelInfo); }
}
inline void endQueueDebugLabel(pvrvk::Queue queue)
{
// if the VK_EXT_debug_utils extension is supported then end the queue label region
if (queue->getDevice()->getPhysicalDevice()->getInstance()->getEnabledExtensionTable().extDebugUtilsEnabled) { queue->endDebugUtilsLabel(); }
}
inline void beginCommandBufferDebugLabel(pvrvk::CommandBufferBase commandBufferBase, const pvrvk::DebugUtilsLabel& labelInfo)
{
// if the VK_EXT_debug_utils extension is supported then start the queue label region
if (commandBufferBase->getDevice()->getPhysicalDevice()->getInstance()->getEnabledExtensionTable().extDebugUtilsEnabled) { commandBufferBase->beginDebugUtilsLabel(labelInfo); }
// else if the VK_EXT_debug_marker extension is supported then start the debug marker region
else if (commandBufferBase->getDevice()->getEnabledExtensionTable().extDebugMarkerEnabled)
{
pvrvk::DebugMarkerMarkerInfo markerInfo(labelInfo.getLabelName(), labelInfo.getR(), labelInfo.getG(), labelInfo.getB(), labelInfo.getA());
commandBufferBase->debugMarkerBeginEXT(markerInfo);
}
}
inline void endCommandBufferDebugLabel(pvrvk::CommandBufferBase commandBufferBase)
{
// if the VK_EXT_debug_utils extension is supported then end the command buffer label region
if (commandBufferBase->getDevice()->getPhysicalDevice()->getInstance()->getEnabledExtensionTable().extDebugUtilsEnabled) { commandBufferBase->endDebugUtilsLabel(); }
// else if the VK_EXT_debug_marker extension is supported then end the debug marker region
else if (commandBufferBase->getDevice()->getEnabledExtensionTable().extDebugMarkerEnabled)
{
commandBufferBase->debugMarkerEndEXT();
}
}
inline void insertDebugUtilsLabel(pvrvk::CommandBufferBase commandBufferBase, const pvrvk::DebugUtilsLabel& labelInfo)
{
// if the VK_EXT_debug_utils extension is supported then insert the queue label region
if (commandBufferBase->getDevice()->getPhysicalDevice()->getInstance()->getEnabledExtensionTable().extDebugUtilsEnabled)
{
commandBufferBase->insertDebugUtilsLabel(labelInfo);
} // else if the VK_EXT_debug_marker extension is supported then insert the debug marker
else if (commandBufferBase->getDevice()->getEnabledExtensionTable().extDebugMarkerEnabled)
{
pvrvk::DebugMarkerMarkerInfo markerInfo(labelInfo.getLabelName(), labelInfo.getR(), labelInfo.getG(), labelInfo.getB(), labelInfo.getA());
commandBufferBase->debugMarkerInsertEXT(markerInfo);
}
}
inline void beginCommandBufferDebugLabel(pvrvk::CommandBuffer& commandBuffer, const pvrvk::DebugUtilsLabel& labelInfo)
{
beginCommandBufferDebugLabel(static_cast<pvrvk::CommandBufferBase>(commandBuffer), labelInfo);
}
inline void beginCommandBufferDebugLabel(pvrvk::SecondaryCommandBuffer& secondaryCommandBuffer, const pvrvk::DebugUtilsLabel& labelInfo)
{
beginCommandBufferDebugLabel(static_cast<pvrvk::CommandBufferBase>(secondaryCommandBuffer), labelInfo);
}
inline void endCommandBufferDebugLabel(pvrvk::CommandBuffer& commandBuffer) { endCommandBufferDebugLabel(static_cast<pvrvk::CommandBufferBase>(commandBuffer)); }
inline void endCommandBufferDebugLabel(pvrvk::SecondaryCommandBuffer& secondaryCommandBuffer)
{
endCommandBufferDebugLabel(static_cast<pvrvk::CommandBufferBase>(secondaryCommandBuffer));
}
inline void insertDebugUtilsLabel(pvrvk::CommandBuffer& commandBuffer, const pvrvk::DebugUtilsLabel& labelInfo)
{
insertDebugUtilsLabel(static_cast<pvrvk::CommandBufferBase>(commandBuffer), labelInfo);
}
inline void insertDebugUtilsLabel(pvrvk::SecondaryCommandBuffer& secondaryCommandBuffer, const pvrvk::DebugUtilsLabel& labelInfo)
{
insertDebugUtilsLabel(static_cast<pvrvk::CommandBufferBase>(secondaryCommandBuffer), labelInfo);
}
VKAPI_ATTR VkBool32 VKAPI_CALL throwOnErrorDebugUtilsMessengerCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageTypes,
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData);
VKAPI_ATTR VkBool32 VKAPI_CALL logMessageDebugUtilsMessengerCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageTypes,
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData);
VKAPI_ATTR VkBool32 VKAPI_CALL throwOnErrorDebugReportCallback(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object, size_t location,
int32_t messageCode, const char* pLayerPrefix, const char* pMessage, void* pUserData);
VKAPI_ATTR VkBool32 VKAPI_CALL logMessageDebugReportCallback(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object, size_t location,
int32_t messageCode, const char* pLayerPrefix, const char* pMessage, void* pUserData);
inline LogLevel mapDebugUtilsMessageSeverityFlagsToLogLevel(pvrvk::DebugUtilsMessageSeverityFlagsEXT flags)
{
if ((flags & pvrvk::DebugUtilsMessageSeverityFlagsEXT::e_INFO_BIT_EXT) != 0) { return LogLevel::Information; }
if ((flags & pvrvk::DebugUtilsMessageSeverityFlagsEXT::e_WARNING_BIT_EXT) != 0) { return LogLevel::Warning; }
if ((flags & pvrvk::DebugUtilsMessageSeverityFlagsEXT::e_VERBOSE_BIT_EXT) != 0) { return LogLevel::Debug; }
if ((flags & pvrvk::DebugUtilsMessageSeverityFlagsEXT::e_ERROR_BIT_EXT) != 0) { return LogLevel::Error; }
return LogLevel::Information;
}
inline LogLevel mapDebugReportFlagsToLogLevel(pvrvk::DebugReportFlagsEXT flags)
{
if ((flags & pvrvk::DebugReportFlagsEXT::e_INFORMATION_BIT_EXT) != 0) { return LogLevel::Information; }
if ((flags & pvrvk::DebugReportFlagsEXT::e_WARNING_BIT_EXT) != 0) { return LogLevel::Warning; }
if ((flags & pvrvk::DebugReportFlagsEXT::e_PERFORMANCE_WARNING_BIT_EXT) != 0) { return LogLevel::Performance; }
if ((flags & pvrvk::DebugReportFlagsEXT::e_ERROR_BIT_EXT) != 0) { return LogLevel::Error; }
if ((flags & pvrvk::DebugReportFlagsEXT::e_DEBUG_BIT_EXT) != 0) { return LogLevel::Debug; }
return LogLevel::Information;
}
#pragma endregion
#pragma region
pvrvk::Buffer createBuffer(const pvrvk::Device& device, const pvrvk::BufferCreateInfo& createInfo, pvrvk::MemoryPropertyFlags requiredMemoryFlags,
pvrvk::MemoryPropertyFlags optimalMemoryFlags = pvrvk::MemoryPropertyFlags::e_NONE, const vma::Allocator& bufferAllocator = nullptr,
vma::AllocationCreateFlags vmaAllocationCreateFlags = vma::AllocationCreateFlags::e_MAPPED_BIT, pvrvk::MemoryAllocateFlags memoryAllocateFlags = pvrvk::MemoryAllocateFlags::e_NONE);
pvrvk::Image createImage(const pvrvk::Device& device, const pvrvk::ImageCreateInfo& createInfo,
pvrvk::MemoryPropertyFlags requiredMemoryFlags = pvrvk::MemoryPropertyFlags::e_DEVICE_LOCAL_BIT, pvrvk::MemoryPropertyFlags optimalMemoryFlags = pvrvk::MemoryPropertyFlags::e_NONE,
const vma::Allocator& imageAllocator = nullptr, vma::AllocationCreateFlags vmaAllocationCreateFlags = vma::AllocationCreateFlags::e_NONE);
#pragma endregion
#pragma region
bool isSupportedFormat(const pvrvk::PhysicalDevice& pdev, pvrvk::Format fmt);
void setImageLayoutAndQueueFamilyOwnership(pvrvk::CommandBufferBase srccmd, pvrvk::CommandBufferBase dstcmd, uint32_t srcQueueFamily, uint32_t dstQueueFamily,
pvrvk::ImageLayout oldLayout, pvrvk::ImageLayout newLayout, pvrvk::Image& image, uint32_t baseMipLevel, uint32_t numMipLevels, uint32_t baseArrayLayer, uint32_t numArrayLayers,
pvrvk::ImageAspectFlags aspect, bool isSafetyCritical = false);
inline void setImageLayout(pvrvk::Image& image, pvrvk::ImageLayout oldLayout, pvrvk::ImageLayout newLayout, pvrvk::CommandBufferBase transitionCmdBuffer)
{
setImageLayoutAndQueueFamilyOwnership(transitionCmdBuffer, pvrvk::CommandBufferBase(), static_cast<uint32_t>(-1), static_cast<uint32_t>(-1), oldLayout, newLayout, image, 0,
image->getNumMipLevels(), 0, static_cast<uint32_t>(image->getNumArrayLayers()), inferAspectFromFormat(image->getFormat()));
}
pvrvk::ImageView uploadImageAndViewSubmit(pvrvk::Device& device, const Texture& texture, bool allowDecompress, pvrvk::CommandPool& commandPool, pvrvk::Queue& queue,
pvrvk::ImageUsageFlags usageFlags = pvrvk::ImageUsageFlags::e_SAMPLED_BIT, pvrvk::ImageLayout finalLayout = pvrvk::ImageLayout::e_SHADER_READ_ONLY_OPTIMAL,
vma::Allocator stagingBufferAllocator = nullptr, vma::Allocator imageAllocator = nullptr,
vma::AllocationCreateFlags imageAllocationCreateFlags = vma::AllocationCreateFlags::e_NONE);
pvrvk::ImageView uploadImageAndView(pvrvk::Device& device, const Texture& texture, bool allowDecompress, pvrvk::SecondaryCommandBuffer& commandBuffer,
pvrvk::ImageUsageFlags usageFlags = pvrvk::ImageUsageFlags::e_SAMPLED_BIT, pvrvk::ImageLayout finalLayout = pvrvk::ImageLayout::e_SHADER_READ_ONLY_OPTIMAL,
vma::Allocator stagingBufferAllocator = nullptr, vma::Allocator imageAllocator = nullptr,
vma::AllocationCreateFlags imageAllocationCreateFlags = vma::AllocationCreateFlags::e_NONE);
pvrvk::ImageView uploadImageAndView(pvrvk::Device& device, const Texture& texture, bool allowDecompress, pvrvk::CommandBuffer& commandBuffer,
pvrvk::ImageUsageFlags usageFlags = pvrvk::ImageUsageFlags::e_SAMPLED_BIT, pvrvk::ImageLayout finalLayout = pvrvk::ImageLayout::e_SHADER_READ_ONLY_OPTIMAL,
vma::Allocator stagingBufferAllocator = nullptr, vma::Allocator imageAllocator = nullptr,
vma::AllocationCreateFlags imageAllocationCreateFlags = vma::AllocationCreateFlags::e_NONE);
pvrvk::Image uploadImage(pvrvk::Device& device, const Texture& texture, bool allowDecompress, pvrvk::CommandBuffer& commandBuffer,
pvrvk::ImageUsageFlags usageFlags = pvrvk::ImageUsageFlags::e_SAMPLED_BIT, pvrvk::ImageLayout finalLayout = pvrvk::ImageLayout::e_SHADER_READ_ONLY_OPTIMAL,
vma::Allocator stagingBufferAllocator = nullptr, vma::Allocator imageAllocator = nullptr,
vma::AllocationCreateFlags imageAllocationCreateFlags = vma::AllocationCreateFlags::e_NONE);
pvrvk::ImageView loadAndUploadImageAndView(pvrvk::Device& device, const char* fileName, bool allowDecompress, pvrvk::CommandBuffer& commandBuffer, IAssetProvider& assetProvider,
pvrvk::ImageUsageFlags usageFlags = pvrvk::ImageUsageFlags::e_SAMPLED_BIT, pvrvk::ImageLayout finalLayout = pvrvk::ImageLayout::e_SHADER_READ_ONLY_OPTIMAL,
Texture* outAssetTexture = nullptr, vma::Allocator stagingBufferAllocator = nullptr, vma::Allocator imageAllocator = nullptr,
vma::AllocationCreateFlags imageAllocationCreateFlags = vma::AllocationCreateFlags::e_NONE, const void* pNext = nullptr, bool isSafetyCritical = false);
pvrvk::Image loadAndUploadImage(pvrvk::Device& device, const char* fileName, bool allowDecompress, pvrvk::CommandBuffer& commandBuffer, IAssetProvider& assetProvider,
pvrvk::ImageUsageFlags usageFlags = pvrvk::ImageUsageFlags::e_SAMPLED_BIT, pvrvk::ImageLayout finalLayout = pvrvk::ImageLayout::e_SHADER_READ_ONLY_OPTIMAL,
Texture* outAssetTexture = nullptr, vma::Allocator stagingBufferAllocator = nullptr, vma::Allocator imageAllocator = nullptr,
vma::AllocationCreateFlags imageAllocationCreateFlags = vma::AllocationCreateFlags::e_NONE);
pvrvk::Image loadAndUploadImage(pvrvk::Device& device, const std::string& fileName, bool allowDecompress, pvrvk::CommandBuffer& commandBuffer, IAssetProvider& assetProvider,
pvrvk::ImageUsageFlags usageFlags = pvrvk::ImageUsageFlags::e_SAMPLED_BIT, pvrvk::ImageLayout finalLayout = pvrvk::ImageLayout::e_SHADER_READ_ONLY_OPTIMAL,
Texture* outAssetTexture = nullptr, vma::Allocator stagingBufferAllocator = nullptr, vma::Allocator imageAllocator = nullptr,
vma::AllocationCreateFlags imageAllocationCreateFlags = vma::AllocationCreateFlags::e_NONE);
pvrvk::ImageView loadAndUploadImageAndView(pvrvk::Device& device, const char* fileName, bool allowDecompress, pvrvk::SecondaryCommandBuffer& commandBuffer,
IAssetProvider& assetProvider, pvrvk::ImageUsageFlags usageFlags = pvrvk::ImageUsageFlags::e_SAMPLED_BIT,
pvrvk::ImageLayout finalLayout = pvrvk::ImageLayout::e_SHADER_READ_ONLY_OPTIMAL, Texture* outAssetTexture = nullptr, vma::Allocator stagingBufferAllocator = nullptr,
vma::Allocator imageAllocator = nullptr, vma::AllocationCreateFlags imageAllocationCreateFlags = vma::AllocationCreateFlags::e_NONE, const void* pNext = nullptr);
pvrvk::Image loadAndUploadImage(pvrvk::Device& device, const char* fileName, bool allowDecompress, pvrvk::SecondaryCommandBuffer& commandBuffer, IAssetProvider& assetProvider,
pvrvk::ImageUsageFlags usageFlags = pvrvk::ImageUsageFlags::e_SAMPLED_BIT, pvrvk::ImageLayout finalLayout = pvrvk::ImageLayout::e_SHADER_READ_ONLY_OPTIMAL,
Texture* outAssetTexture = nullptr, vma::Allocator stagingBufferAllocator = nullptr, vma::Allocator imageAllocator = nullptr,
vma::AllocationCreateFlags imageAllocationCreateFlags = vma::AllocationCreateFlags::e_NONE);
struct ImageUpdateInfo
{
// 1D/Array texture and common for rest
int32_t offsetX;
uint32_t imageWidth;
uint32_t dataWidth;
uint32_t arrayIndex;
uint32_t mipLevel;
const void* data;
uint32_t dataSize;
// 2D/ Array texture only
int32_t offsetY;
uint32_t imageHeight;
uint32_t dataHeight;
// cube/ Array Map only. Derive all states above
uint32_t cubeFace;
// 3D texture Only. Derive all states above Except arrayIndex
int32_t offsetZ;
uint32_t depth;
// YCbCr texture only
uint32_t numPlanes;
uint32_t planeIndex;
ImageUpdateInfo()
: offsetX(0), imageWidth(1), dataWidth(1), mipLevel(0), data(nullptr), dataSize(0), offsetY(0), imageHeight(1), dataHeight(1), cubeFace(0), offsetZ(0), depth(1), numPlanes(1)
{}
};
void updateImage(pvrvk::Device& device, pvrvk::CommandBufferBase transferCommandBuffer, ImageUpdateInfo* updateInfos, uint32_t numUpdateInfos, pvrvk::Format format,
pvrvk::ImageLayout layout, bool isCubeMap, pvrvk::Image& image, vma::Allocator bufferAllocator = nullptr, bool isSafetyCritical = false);
inline void updateHostVisibleBuffer(pvrvk::Buffer& buffer, const void* data, VkDeviceSize offset = 0, VkDeviceSize size = VK_WHOLE_SIZE, bool flushMemory = false)
{
void* mapData = nullptr;
bool unmap = false;
if (!buffer->getDeviceMemory()->isMapped())
{
mapData = buffer->getDeviceMemory()->map(offset, size);
unmap = true;
}
else
{
mapData = static_cast<char*>(buffer->getDeviceMemory()->getMappedData()) + offset;
}
memcpy(mapData, data, (size_t)size);
if (static_cast<uint32_t>(buffer->getDeviceMemory()->getMemoryFlags() & pvrvk::MemoryPropertyFlags::e_HOST_COHERENT_BIT) != 0) { flushMemory = false; }
if (flushMemory) { buffer->getDeviceMemory()->flushRange(offset, size); }
if (unmap) { buffer->getDeviceMemory()->unmap(); }
}
inline void updateBufferUsingStagingBuffer(pvrvk::Device& device, pvrvk::Buffer& buffer, pvrvk::CommandBufferBase uploadCmdBuffer, const void* data, VkDeviceSize offset = 0,
VkDeviceSize size = VK_WHOLE_SIZE, vma::Allocator stagingBufferAllocator = nullptr)
{
// Updating memory via the use of staging buffers is necessary when memory is not host visible. In this case the buffer memory will be updated indirectly as follows:
// 1. Create a staging buffer
// 2. map the staging buffer memory, update the memory, then unmap the buffer memory
// 3. Copy from the staging buffer to the target buffer
pvr::utils::beginCommandBufferDebugLabel(uploadCmdBuffer, pvrvk::DebugUtilsLabel("PVRUtilsVk::updateBufferUsingStagingBuffer"));
// 1. Create a staging buffer
pvrvk::MemoryPropertyFlags memoryFlags = pvrvk::MemoryPropertyFlags::e_HOST_VISIBLE_BIT;
pvrvk::Buffer stagingBuffer =
pvr::utils::createBuffer(device, pvrvk::BufferCreateInfo(size, pvrvk::BufferUsageFlags::e_TRANSFER_SRC_BIT), memoryFlags, memoryFlags, stagingBufferAllocator);
// 2. map (if required), then update the memory, then finally unmap (if required)
updateHostVisibleBuffer(stagingBuffer, data, 0, size, true);
// 3. Copy from the staging buffer to the target buffer
const pvrvk::BufferCopy bufferCopy(0, offset, size);
uploadCmdBuffer->copyBuffer(stagingBuffer, buffer, 1, &bufferCopy);
pvr::utils::endCommandBufferDebugLabel(uploadCmdBuffer);
}
void generateTextureAtlas(pvrvk::Device& device, const pvrvk::Image* inputImages, pvrvk::Rect2Df* outUVs, uint32_t numImages, pvrvk::ImageLayout inputImageLayout,
pvrvk::ImageView* outImageView, TextureHeader* outDescriptor, pvrvk::CommandBufferBase cmdBuffer, pvrvk::ImageLayout finalLayout = pvrvk::ImageLayout::e_SHADER_READ_ONLY_OPTIMAL,
vma::Allocator imageAllocator = nullptr, vma::AllocationCreateFlags imageAllocationCreateFlags = vma::AllocationCreateFlags::e_NONE);
#pragma endregion
#pragma region
struct VulkanVersion
{
uint32_t majorVersion;
uint32_t minorVersion;
uint32_t patchVersion;
bool isSafetyCritical;
VulkanVersion(uint32_t inMajorVersion = 1, uint32_t inMinorVersion = 0, uint32_t inPatchVersion = 0)
: majorVersion(inMajorVersion), minorVersion(inMinorVersion), patchVersion(inPatchVersion), isSafetyCritical(false)
{}
VulkanVersion(uint32_t inMajorVersion, uint32_t inMinorVersion, uint32_t inPatchVersion, bool inIsSafetyCritical)
: majorVersion(inMajorVersion), minorVersion(inMinorVersion), patchVersion(inPatchVersion), isSafetyCritical(inIsSafetyCritical)
{}
uint32_t toVulkanVersion() { return VK_MAKE_VERSION(majorVersion, minorVersion, patchVersion); }
};
struct InstanceLayers : public pvrvk::VulkanLayerList
{
InstanceLayers(bool forceLayers =
#ifdef DEBUG
true);
#else
false);
#endif
};
struct InstanceExtensions : public pvrvk::VulkanExtensionList
{
InstanceExtensions(VulkanVersion vkVersion = VulkanVersion());
};
struct DeviceExtensions : public pvrvk::VulkanExtensionList
{
DeviceExtensions(VulkanVersion vkVersion = VulkanVersion());
DeviceExtensions& addExtensionFeature(pvrvk::ExtensionFeatures& extensionFeature);
DeviceExtensions& addFragmentShadingRateExtensionAndFeature(pvrvk::PhysicalDevice& physicalDevice);
// DeviceExtensions& addRayTracingExtensionAndFeature(pvrvk::PhysicalDevice& physicalDevice);
private:
std::vector<std::shared_ptr<void>> _featureReferences;
};
pvrvk::Instance createInstance(const std::string& applicationName, VulkanVersion apiVersion = VulkanVersion(), const InstanceExtensions& instanceExtensions = InstanceExtensions(),
const InstanceLayers& instanceLayers = InstanceLayers(),
const pvrvk::DebugUtilsMessageSeverityFlagsEXT InstanceValidationFlags = pvrvk::DebugUtilsMessageSeverityFlagsEXT::e_WARNING_BIT_EXT |
pvrvk::DebugUtilsMessageSeverityFlagsEXT::e_ERROR_BIT_EXT);
struct QueuePopulateInfo
{
pvrvk::QueueFlags queueFlags;
pvrvk::Surface surface;
float priority;
QueuePopulateInfo(pvrvk::QueueFlags queueFlags, float priority = 1.0f) : queueFlags(queueFlags), priority(priority) {}
QueuePopulateInfo(pvrvk::QueueFlags queueFlags, pvrvk::Surface& surface, float priority = 1.0f) : queueFlags(queueFlags), surface(surface), priority(priority) {}
};
struct QueueAccessInfo
{
uint32_t familyId;
uint32_t queueId;
QueueAccessInfo() : familyId(static_cast<uint32_t>(-1)), queueId(static_cast<uint32_t>(-1)) {}
};
pvrvk::Device createDeviceAndQueues(pvrvk::PhysicalDevice physicalDevice, const QueuePopulateInfo* queueCreateInfos, uint32_t numQueueCreateInfos, QueueAccessInfo* outAccessInfo,
const DeviceExtensions& deviceExtensions = DeviceExtensions(), const VkDeviceObjectReservationCreateInfo* reservationCreateInfo = nullptr);
#pragma endregion
#pragma region
pvrvk::Surface createSurface(const pvrvk::Instance& instance, const pvrvk::PhysicalDevice& physicalDevice, void* window, void* display, void* connection);
inline bool isImageUsageSupportedBySurface(const pvrvk::SurfaceCapabilitiesKHR& surfaceCapabilities, pvrvk::ImageUsageFlags imageUsage)
{
return (static_cast<uint32_t>(surfaceCapabilities.getSupportedUsageFlags() & imageUsage) != 0);
}
pvrvk::Swapchain createSwapchain(const pvrvk::Device& device, const pvrvk::Surface& surface, pvr::DisplayAttributes& displayAttributes,
pvrvk::ImageUsageFlags swapchainImageUsageFlags = pvrvk::ImageUsageFlags::e_COLOR_ATTACHMENT_BIT,
const std::vector<pvrvk::Format>& preferredColorFormats = std::vector<pvrvk::Format>());
bool isSupportedDepthStencilFormat(const pvrvk::Device& device, pvrvk::Format format);
pvrvk::Format getSupportedDepthStencilFormat(const pvrvk::Device& device, std::vector<pvrvk::Format> preferredDepthFormats = {});
pvrvk::Format getSupportedDepthStencilFormat(const pvrvk::Device& device, pvr::DisplayAttributes& displayAttributes, std::vector<pvrvk::Format> preferredDepthFormats = {});
template<typename ImageContainer>
inline static void createAttachmentImages(ImageContainer& outImages, const pvrvk::Device& device, int32_t imageCount, pvrvk::Format format, const pvrvk::Extent2D& imageExtent,
const pvrvk::ImageUsageFlags& imageUsageFlags, pvrvk::SampleCountFlags sampleCount, const vma::Allocator& imageAllocator = nullptr,
vma::AllocationCreateFlags imageAllocationCreateFlags = vma::AllocationCreateFlags::e_DEDICATED_MEMORY_BIT, const std::string& objectName = std::string())
{
// the required memory property flags
const pvrvk::MemoryPropertyFlags requiredMemoryProperties = pvrvk::MemoryPropertyFlags::e_DEVICE_LOCAL_BIT;
// more optimal set of memory property flags
const pvrvk::MemoryPropertyFlags optimalMemoryProperties = (imageUsageFlags & pvrvk::ImageUsageFlags::e_TRANSIENT_ATTACHMENT_BIT) != 0
? pvrvk::MemoryPropertyFlags::e_LAZILY_ALLOCATED_BIT
: pvrvk::MemoryPropertyFlags::e_DEVICE_LOCAL_BIT;
for (int32_t i = 0; i < imageCount; ++i)
{
pvrvk::Image depthStencilImage = createImage(device,
pvrvk::ImageCreateInfo(pvrvk::ImageType::e_2D, format, pvrvk::Extent3D(imageExtent.getWidth(), imageExtent.getHeight(), 1u), imageUsageFlags, 1, 1, sampleCount),
requiredMemoryProperties, optimalMemoryProperties, imageAllocator, imageAllocationCreateFlags);
depthStencilImage->setObjectName(objectName + " Image [" + std::to_string(i) + std::string("]"));
outImages[i] = device->createImageView(pvrvk::ImageViewCreateInfo(depthStencilImage));
outImages[i]->setObjectName(std::string(objectName + " ImageView [" + std::to_string(i) + std::string("]")));
}
}
struct CreateSwapchainParameters
{
bool createDepthBuffer = true;
pvrvk::ImageUsageFlags colorImageUsageFlags = pvrvk::ImageUsageFlags::e_COLOR_ATTACHMENT_BIT;
pvrvk::ImageUsageFlags depthStencilImageUsageFlags = pvrvk::ImageUsageFlags::e_DEPTH_STENCIL_ATTACHMENT_BIT | pvrvk::ImageUsageFlags::e_TRANSIENT_ATTACHMENT_BIT;
pvrvk::ImageUsageFlags colorAttachmentFlagsIfMultisampled = pvrvk::ImageUsageFlags::e_COLOR_ATTACHMENT_BIT | pvrvk::ImageUsageFlags::e_TRANSIENT_ATTACHMENT_BIT;
pvrvk::ImageUsageFlags depthStencilAttachmentFlagsIfMultisampled = pvrvk::ImageUsageFlags::e_DEPTH_STENCIL_ATTACHMENT_BIT | pvrvk::ImageUsageFlags::e_TRANSIENT_ATTACHMENT_BIT;
vma::Allocator imageAllocator = nullptr;
vma::AllocationCreateFlags imageAllocatorFlags = vma::AllocationCreateFlags::e_DEDICATED_MEMORY_BIT;
pvrvk::ImageLayout initialSwapchainLayout = pvrvk::ImageLayout::e_UNDEFINED;
pvrvk::ImageLayout initialDepthStencilLayout = pvrvk::ImageLayout::e_UNDEFINED;
pvrvk::AttachmentLoadOp colorLoadOp = pvrvk::AttachmentLoadOp::e_CLEAR;
pvrvk::AttachmentStoreOp colorStoreOp = pvrvk::AttachmentStoreOp::e_STORE;
pvrvk::AttachmentLoadOp depthStencilLoadOp = pvrvk::AttachmentLoadOp::e_CLEAR;
pvrvk::AttachmentStoreOp depthStencilStoreOp = pvrvk::AttachmentStoreOp::e_DONT_CARE;
void addPreferredColorFormat(pvrvk::Format format) { preferredColorFormats.push_back(format); }
void addPreferredDepthStencilFormat(pvrvk::Format format) { preferredDepthStencilFormats.push_back(format); }
std::vector<pvrvk::Format> preferredColorFormats;
std::vector<pvrvk::Format> preferredDepthStencilFormats;
CreateSwapchainParameters(bool createDepthBuffer = true, pvrvk::ImageUsageFlags colorBufferImageUsageFlags = pvrvk::ImageUsageFlags::e_COLOR_ATTACHMENT_BIT,
pvrvk::ImageUsageFlags depthStencilBufferImageUsageFlags = pvrvk::ImageUsageFlags::e_DEPTH_STENCIL_ATTACHMENT_BIT | pvrvk::ImageUsageFlags::e_TRANSIENT_ATTACHMENT_BIT,
pvrvk::ImageUsageFlags intermediateColorImageUsageFlagsIfMultisampled = pvrvk::ImageUsageFlags::e_COLOR_ATTACHMENT_BIT,
pvrvk::ImageUsageFlags intermediateDepthStencilImageUsageFlags = pvrvk::ImageUsageFlags::e_DEPTH_STENCIL_ATTACHMENT_BIT | pvrvk::ImageUsageFlags::e_TRANSIENT_ATTACHMENT_BIT,
vma::Allocator imageAllocator = nullptr, vma::AllocationCreateFlags imageAllocatorFlags = vma::AllocationCreateFlags::e_DEDICATED_MEMORY_BIT)
: createDepthBuffer(createDepthBuffer), colorImageUsageFlags(colorBufferImageUsageFlags), depthStencilImageUsageFlags(depthStencilBufferImageUsageFlags),
colorAttachmentFlagsIfMultisampled(intermediateColorImageUsageFlagsIfMultisampled), depthStencilAttachmentFlagsIfMultisampled(intermediateDepthStencilImageUsageFlags),
imageAllocator(imageAllocator), imageAllocatorFlags(imageAllocatorFlags)
{ //
}
CreateSwapchainParameters& setAllocator(vma::Allocator allocator)
{
this->imageAllocator = allocator;
return *this;
}
CreateSwapchainParameters& setAllocatorFlags(vma::AllocationCreateFlags flags)
{
this->imageAllocatorFlags = flags;
return *this;
}
CreateSwapchainParameters& enableDepthBuffer(bool enableCreateDepthBuffer)
{
this->createDepthBuffer = enableCreateDepthBuffer;
return *this;
}
CreateSwapchainParameters& setColorImageUsageFlags(pvrvk::ImageUsageFlags flags)
{
this->colorImageUsageFlags = flags;
return *this;
}
CreateSwapchainParameters& setMultisampledColorResolveImageUsageFlags(pvrvk::ImageUsageFlags flags)
{
this->colorImageUsageFlags = flags;
return *this;
}
CreateSwapchainParameters& setDepthStencilImageUsageFlags(pvrvk::ImageUsageFlags flags)
{
this->depthStencilImageUsageFlags = flags;
return *this;
}
CreateSwapchainParameters& setMultisampledDepthStencilResolveImageUsageFlags(pvrvk::ImageUsageFlags flags)
{
this->depthStencilImageUsageFlags = flags;
return *this;
}
CreateSwapchainParameters& setMultisampledDepthStencilAttachmentFlags(pvrvk::ImageUsageFlags flags)
{
this->depthStencilAttachmentFlagsIfMultisampled = flags;
return *this;
}
CreateSwapchainParameters& setMultisampledColorAttachmentFlags(pvrvk::ImageUsageFlags flags)
{
this->colorAttachmentFlagsIfMultisampled = flags;
return *this;
}
CreateSwapchainParameters& setColorLoadOp(pvrvk::AttachmentLoadOp op)
{
this->colorLoadOp = op;
return *this;
}
CreateSwapchainParameters& setColorStoreOp(pvrvk::AttachmentStoreOp op)
{
this->colorStoreOp = op;
return *this;
}
CreateSwapchainParameters& setDepthStencilLoadOp(pvrvk::AttachmentLoadOp op)
{
this->depthStencilLoadOp = op;
return *this;
}
CreateSwapchainParameters& setDepthStencilStoreOp(pvrvk::AttachmentStoreOp op)
{
this->depthStencilStoreOp = op;
return *this;
}
CreateSwapchainParameters& setInitialSwapchainLayout(pvrvk::ImageLayout layout)
{
this->initialSwapchainLayout = layout;
return *this;
}
CreateSwapchainParameters& setInitialDepthStencilLayout(pvrvk::ImageLayout layout)
{
this->initialDepthStencilLayout = layout;
return *this;
}
};
struct OnScreenObjects
{
pvrvk::RenderPass renderPass;
std::vector<pvrvk::Framebuffer> framebuffer;
pvrvk::Swapchain swapchain;
std::vector<pvrvk::ImageView> depthStencilImages;
std::vector<pvrvk::ImageView> colorMultisampledAttachmentImages;
std::vector<pvrvk::ImageView> depthStencilMultisampledAttachmentImages;
bool isMultisampled() { return colorMultisampledAttachmentImages.size() != 0; }
bool hasDepthStencil() { return depthStencilImages.size() != 0; }
};
OnScreenObjects createSwapchainRenderpassFramebuffers(
const pvrvk::Device& device, const pvrvk::Surface& surface, pvr::DisplayAttributes& displayAttributes, const CreateSwapchainParameters& params = CreateSwapchainParameters());
[[deprecated("createDepthStencilImageAndViews is deprecated in v5.5 and will be removed. Superseded by createSwapchainRenderpassFramebuffers which supports multisampling "
"correctly and "
"provides an improved interface.")]] void
createSwapchainAndDepthStencilImageAndViews(const pvrvk::Device& device, const pvrvk::Surface& surface, pvr::DisplayAttributes& displayAttributes, pvrvk::Swapchain& outSwapchain,
Multi<pvrvk::ImageView>& outDepthStencilImages, const pvrvk::ImageUsageFlags& swapchainImageUsageFlags = pvrvk::ImageUsageFlags::e_COLOR_ATTACHMENT_BIT,
const pvrvk::ImageUsageFlags& dsImageUsageFlags = pvrvk::ImageUsageFlags::e_DEPTH_STENCIL_ATTACHMENT_BIT | pvrvk::ImageUsageFlags::e_TRANSIENT_ATTACHMENT_BIT,
const vma::Allocator& imageAllocator = nullptr, vma::AllocationCreateFlags dsImageAllocationCreateFlags = vma::AllocationCreateFlags::e_DEDICATED_MEMORY_BIT);
[[deprecated("createDepthStencilImageAndViews is deprecated. Superseded by createAttachmentImages, which has an improved interface and allows any kind of attachment and "
"container.")]] inline std::vector<pvrvk::ImageView>
createDepthStencilImageAndViews(const pvrvk::Device& device, int32_t imageCount, pvrvk::Format depthFormat, const pvrvk::Extent2D& imageExtent,
const pvrvk::ImageUsageFlags& imageUsageFlags = pvrvk::ImageUsageFlags::e_DEPTH_STENCIL_ATTACHMENT_BIT | pvrvk::ImageUsageFlags::e_TRANSIENT_ATTACHMENT_BIT,
pvrvk::SampleCountFlags sampleCount = pvrvk::SampleCountFlags::e_1_BIT, vma::Allocator dsImageAllocator = nullptr,
vma::AllocationCreateFlags dsImageAllocationCreateFlags = vma::AllocationCreateFlags::e_DEDICATED_MEMORY_BIT)
{
std::vector<pvrvk::ImageView> depthStencilImages(imageCount);
createAttachmentImages(depthStencilImages, device, imageCount, depthFormat, imageExtent, imageUsageFlags, sampleCount, dsImageAllocator, dsImageAllocationCreateFlags,
"PVRUtilsVk::DepthStencil ");
return depthStencilImages;
}
pvrvk::RenderPass createOnScreenRenderPass(const pvrvk::Swapchain& swapchain, bool hasDepthStencil, const pvrvk::Format depthStencilFormat = pvrvk::Format::e_UNDEFINED,
pvrvk::ImageLayout initialSwapchainLayout = pvrvk::ImageLayout::e_UNDEFINED, pvrvk::ImageLayout initialDepthStencilLayout = pvrvk::ImageLayout::e_UNDEFINED,
pvrvk::AttachmentLoadOp colorLoadOp = pvrvk::AttachmentLoadOp::e_CLEAR, pvrvk::AttachmentStoreOp colorStoreOp = pvrvk::AttachmentStoreOp::e_STORE,
pvrvk::AttachmentLoadOp depthStencilLoadOp = pvrvk::AttachmentLoadOp::e_CLEAR, pvrvk::AttachmentStoreOp depthStencilStoreOp = pvrvk::AttachmentStoreOp::e_DONT_CARE,
pvrvk::SampleCountFlags samples = pvrvk::SampleCountFlags::e_1_BIT);
namespace details {
inline void assignAttachmentIndexes(bool hasDepth, bool isMultisampled, int& outColorIdx, int& outDepthIdx, int& outColorResolveIdx, int& outDepthResolveIdx)
{
outColorIdx = 0;
outDepthIdx = hasDepth ? 1 : -1; // Either 1, or unused
outColorResolveIdx = isMultisampled ? hasDepth ? 2 : 1 : -1; // 1 or 2, or unused
outDepthResolveIdx = (isMultisampled && hasDepth) ? 3 : -1; // Either 3, or unused
}
} // namespace details
template<typename ContainerType>
inline ContainerType createOnscreenFramebuffers(const pvrvk::Swapchain& swapchain, const pvrvk::RenderPass& renderPass, const pvrvk::ImageView* depthStencilImages,
pvrvk::ImageView* colorMultisampledImages, pvrvk::ImageView* depthStencilMultisampledImages)
{
ContainerType framebuffers;
framebuffers.resize(swapchain->getSwapchainLength());
bool multisample = (renderPass->getCreateInfo().getSubpass(0).getNumResolveAttachmentReference() > 0);
if (multisample && !colorMultisampledImages)
{
throw InvalidArgumentError("colorResolveImages", "pvr::utils::createOnScreenFramebuffers: On a multisampled swapchain, color resolve images cannot be null be provided");
}
if (!multisample && colorMultisampledImages)
{
throw InvalidArgumentError("colorResolveImages", "pvr::utils::createOnScreenFramebuffers: On a non-multisampled swapchain, color resolve images must be null");
}
if (multisample && depthStencilImages && !depthStencilMultisampledImages)
{
throw InvalidArgumentError("depthStencilResolveImages",
"pvr::utils::createOnScreenFramebuffers: On a multisampled swapchain with a depth buffer, depth resolve images must be provided and cannot be null");
}
if (!multisample && depthStencilImages && depthStencilMultisampledImages)
{
throw InvalidArgumentError(
"depthStencilResolveImages", "pvr::utils::createOnScreenFramebuffers: On a non-multisampled swapchain with a depth buffer, depth resolve images must be null");
}
if (depthStencilMultisampledImages && !depthStencilImages)
{
throw InvalidArgumentError(
"depthStencilResolveImages", "pvr::utils::createOnScreenFramebuffers: Cannot provide depth resolve images without the corresponding depth images must be null");
}
int colorIdx, depthIdx, colorResolveIdx, depthResolveIdx;
details::assignAttachmentIndexes(depthStencilImages != 0, colorMultisampledImages != 0, colorIdx, depthIdx, colorResolveIdx, depthResolveIdx);
for (uint32_t i = 0; i < swapchain->getSwapchainLength(); ++i)
{
pvrvk::FramebufferCreateInfo framebufferInfo;
framebufferInfo.setDimensions(swapchain->getDimension());
if (multisample)
{
framebufferInfo.setAttachment(colorResolveIdx, swapchain->getImageView(i));
framebufferInfo.setAttachment(colorIdx, colorMultisampledImages[i]);
if (depthStencilImages)
{
framebufferInfo.setAttachment(depthIdx, depthStencilMultisampledImages[i]);
framebufferInfo.setAttachment(depthResolveIdx, depthStencilImages[i]);
}
}
else
{
framebufferInfo.setAttachment(colorIdx, swapchain->getImageView(i));
if (depthStencilImages) { framebufferInfo.setAttachment(depthIdx, depthStencilImages[i]); }
}
framebufferInfo.setRenderPass(renderPass);
framebuffers[i] = swapchain->getDevice()->createFramebuffer(framebufferInfo);
framebuffers[i]->setObjectName(std::string("PVRUtilsVk::OnScreenFrameBuffer [") + std::to_string(i) + std::string("]"));
}
return framebuffers;
}
[[deprecated("This function is deprecated in v5.5 and will be removed. It is superseded by "
"createSwapchainFramebufferRenderpass, which supports multisampling correctly and has an improved interface.")]] inline pvrvk::RenderPass
createOnscreenFramebufferAndRenderPass(const pvrvk::Swapchain& swapchain, pvrvk::ImageView* depthStencilImages, Multi<pvrvk::Framebuffer>& outFramebuffers,
pvrvk::ImageLayout initialSwapchainLayout = pvrvk::ImageLayout::e_UNDEFINED, pvrvk::ImageLayout initialDepthStencilLayout = pvrvk::ImageLayout::e_UNDEFINED,
pvrvk::AttachmentLoadOp colorLoadOp = pvrvk::AttachmentLoadOp::e_CLEAR, pvrvk::AttachmentStoreOp colorStoreOp = pvrvk::AttachmentStoreOp::e_STORE,
pvrvk::AttachmentLoadOp depthStencilLoadOp = pvrvk::AttachmentLoadOp::e_CLEAR, pvrvk::AttachmentStoreOp depthStencilStoreOp = pvrvk::AttachmentStoreOp::e_DONT_CARE)
{
pvrvk::RenderPass outRenderPass =
createOnScreenRenderPass(swapchain, depthStencilImages != nullptr, depthStencilImages ? depthStencilImages[0]->getFormat() : pvrvk::Format::e_UNDEFINED,
initialSwapchainLayout, initialDepthStencilLayout, colorLoadOp, colorStoreOp, depthStencilLoadOp, depthStencilStoreOp);
auto fbos = createOnscreenFramebuffers<Multi<pvrvk::Framebuffer>>(swapchain, outRenderPass, depthStencilImages, nullptr, nullptr);
std::swap(outFramebuffers, fbos);
return outRenderPass;
}
inline void populateViewportStateCreateInfo(const pvrvk::Framebuffer& framebuffer, pvrvk::PipelineViewportStateCreateInfo& outCreateInfo)
{
outCreateInfo.setViewportAndScissor(0,
pvrvk::Viewport(0.f, 0.f, static_cast<float>(framebuffer->getDimensions().getWidth()), static_cast<float>(framebuffer->getDimensions().getHeight())),
pvrvk::Rect2D(pvrvk::Offset2D(0, 0), pvrvk::Extent2D(framebuffer->getDimensions().getWidth(), framebuffer->getDimensions().getHeight())));
}
#pragma endregion
#pragma region
struct VertexBindings
{
std::string semanticName;
int16_t location;
};
struct VertexBindings_Name
{
StringHash semantic;
StringHash variableName;
};
inline void populateInputAssemblyFromMesh(const assets::Mesh& mesh, const VertexBindings* bindingMap, uint16_t numBindings,
pvrvk::PipelineVertexInputStateCreateInfo& vertexCreateInfo, pvrvk::PipelineInputAssemblerStateCreateInfo& inputAssemblerCreateInfo, uint16_t* numOutBuffers = nullptr)
{
vertexCreateInfo.clear();
if (numOutBuffers) { *numOutBuffers = 0; }
uint16_t current = 0;
while (current < numBindings)
{
auto attr = mesh.getVertexAttributeByName(bindingMap[current].semanticName.c_str());
if (attr)
{
VertexAttributeLayout layout = attr->getVertexLayout();
uint32_t stride = mesh.getStride(attr->getDataIndex());
if (numOutBuffers) { *numOutBuffers = std::max(static_cast<uint16_t>(attr->getDataIndex() + 1u), *numOutBuffers); }
const pvrvk::VertexInputAttributeDescription attribDesc(static_cast<uint32_t>(bindingMap[current].location), attr->getDataIndex(),
convertToPVRVkVertexInputFormat(layout.dataType, layout.width), static_cast<uint32_t>(layout.offset));
const pvrvk::VertexInputBindingDescription bindingDesc(attr->getDataIndex(), stride, pvrvk::VertexInputRate::e_VERTEX);
vertexCreateInfo.addInputAttribute(attribDesc).addInputBinding(bindingDesc);
}
else
{
Log("Could not find Attribute with Semantic %s in the supplied mesh. Will render without binding it, erroneously.", bindingMap[current].semanticName.c_str());
}
++current;
}
inputAssemblerCreateInfo.setPrimitiveTopology(convertToPVRVk(mesh.getMeshInfo().primitiveType));
}
inline void populateInputAssemblyFromMesh(const assets::Mesh& mesh, const VertexBindings_Name* bindingMap, uint32_t numBindings,
pvrvk::PipelineVertexInputStateCreateInfo& vertexCreateInfo, pvrvk::PipelineInputAssemblerStateCreateInfo& inputAssemblerCreateInfo, uint32_t* numOutBuffers = nullptr)
{
vertexCreateInfo.clear();
if (numOutBuffers) { *numOutBuffers = 0; }
uint32_t current = 0;
// In this scenario, we will be using our own indexes instead of user provided ones, correlating them by names.
vertexCreateInfo.clear();
while (current < numBindings)
{
auto attr = mesh.getVertexAttributeByName(bindingMap[current].semantic);
if (attr)
{
VertexAttributeLayout layout = attr->getVertexLayout();
uint32_t stride = mesh.getStride(attr->getDataIndex());
if (numOutBuffers) { *numOutBuffers = std::max<uint32_t>(attr->getDataIndex() + 1u, *numOutBuffers); }
const pvrvk::VertexInputAttributeDescription attribDesc(current, attr->getDataIndex(), convertToPVRVkVertexInputFormat(layout.dataType, layout.width), layout.offset);
const pvrvk::VertexInputBindingDescription bindingDesc(attr->getDataIndex(), stride, pvrvk::VertexInputRate::e_VERTEX);
vertexCreateInfo.addInputAttribute(attribDesc).addInputBinding(bindingDesc);
inputAssemblerCreateInfo.setPrimitiveTopology(convertToPVRVk(mesh.getMeshInfo().primitiveType));
}
else
{
Log("Could not find Attribute with Semantic %s in the supplied mesh. Will render without binding it, erroneously.", bindingMap[current].semantic.c_str());
}
++current;
}
}
inline void createSingleBuffersFromMesh(pvrvk::Device& device, const assets::Mesh& mesh, pvrvk::Buffer& outVbo, pvrvk::Buffer& outIbo, pvrvk::CommandBuffer& uploadCmdBuffer,
bool& requiresCommandBufferSubmission, vma::Allocator bufferAllocator = nullptr, vma::AllocationCreateFlags vmaAllocationCreateFlags = vma::AllocationCreateFlags::e_MAPPED_BIT)
{
requiresCommandBufferSubmission = false;
size_t total = 0;
for (uint32_t i = 0; i < mesh.getNumDataElements(); ++i) { total += mesh.getDataSize(i); }
pvrvk::MemoryPropertyFlags vboRequiredMemoryFlags = pvrvk::MemoryPropertyFlags::e_DEVICE_LOCAL_BIT;
pvrvk::MemoryPropertyFlags vboOptimalMemoryFlags = vboRequiredMemoryFlags;
outVbo = createBuffer(device, pvrvk::BufferCreateInfo(static_cast<uint32_t>(total), pvrvk::BufferUsageFlags::e_VERTEX_BUFFER_BIT | pvrvk::BufferUsageFlags::e_TRANSFER_DST_BIT),
vboRequiredMemoryFlags, vboOptimalMemoryFlags, bufferAllocator, vmaAllocationCreateFlags);
bool isVboHostVisible = (outVbo->getDeviceMemory()->getMemoryFlags() & pvrvk::MemoryPropertyFlags::e_HOST_VISIBLE_BIT) != 0;
if (!isVboHostVisible) { requiresCommandBufferSubmission = true; }
size_t current = 0;
for (uint32_t i = 0; i < mesh.getNumDataElements(); ++i)
{
if (isVboHostVisible)
{
updateHostVisibleBuffer(outVbo, static_cast<const void*>(mesh.getData(i)), static_cast<uint32_t>(current), static_cast<uint32_t>(mesh.getDataSize(i)), true);
}
else
{
updateBufferUsingStagingBuffer(device, outVbo, pvrvk::CommandBufferBase(uploadCmdBuffer), static_cast<const void*>(mesh.getData(i)), static_cast<uint32_t>(current),
static_cast<uint32_t>(mesh.getDataSize(i)), bufferAllocator);
}
current += mesh.getDataSize(i);
}
if (mesh.getNumFaces())
{
pvrvk::MemoryPropertyFlags iboRequiredMemoryFlags = pvrvk::MemoryPropertyFlags::e_DEVICE_LOCAL_BIT;
pvrvk::MemoryPropertyFlags iboOptimalMemoryFlags = iboRequiredMemoryFlags;
outIbo = createBuffer(device,
pvrvk::BufferCreateInfo(static_cast<uint32_t>(mesh.getFaces().getDataSize()), pvrvk::BufferUsageFlags::e_INDEX_BUFFER_BIT | pvrvk::BufferUsageFlags::e_TRANSFER_DST_BIT),
iboRequiredMemoryFlags, iboOptimalMemoryFlags, bufferAllocator, vmaAllocationCreateFlags);
bool isIboHostVisible = (outIbo->getDeviceMemory()->getMemoryFlags() & pvrvk::MemoryPropertyFlags::e_HOST_VISIBLE_BIT) != 0;
if (!isIboHostVisible) { requiresCommandBufferSubmission = true; }
if (isIboHostVisible)
{
updateHostVisibleBuffer(outIbo, static_cast<const void*>(mesh.getFaces().getData()), 0, static_cast<uint32_t>(mesh.getFaces().getDataSize()), true);
}
else
{
updateBufferUsingStagingBuffer(device, outIbo, pvrvk::CommandBufferBase(uploadCmdBuffer), static_cast<const void*>(mesh.getFaces().getData()), 0,
static_cast<uint32_t>(mesh.getFaces().getDataSize()), bufferAllocator);
}
}
else
{
outIbo.reset();
}
}
inline void createMultipleBuffersFromMesh(pvrvk::Device& device, const assets::Mesh& mesh, std::vector<pvrvk::Buffer>& outVbos, pvrvk::Buffer& outIbo,
pvrvk::CommandBuffer& uploadCmdBuffer, bool& requiresCommandBufferSubmission, vma::Allocator bufferAllocator,
vma::AllocationCreateFlags vmaAllocationCreateFlags = vma::AllocationCreateFlags::e_MAPPED_BIT)
{
requiresCommandBufferSubmission = false;
for (uint32_t i = 0; i < mesh.getNumDataElements(); ++i)
{
pvrvk::MemoryPropertyFlags requiredMemoryFlags = pvrvk::MemoryPropertyFlags::e_DEVICE_LOCAL_BIT;
pvrvk::MemoryPropertyFlags optimalMemoryFlags = requiredMemoryFlags;
outVbos.emplace_back(createBuffer(device,
pvrvk::BufferCreateInfo(static_cast<uint32_t>(mesh.getDataSize(i)), pvrvk::BufferUsageFlags::e_VERTEX_BUFFER_BIT | pvrvk::BufferUsageFlags::e_TRANSFER_DST_BIT),
requiredMemoryFlags, optimalMemoryFlags, bufferAllocator, vmaAllocationCreateFlags));
bool isBufferHostVisible = (outVbos.back()->getDeviceMemory()->getMemoryFlags() & pvrvk::MemoryPropertyFlags::e_HOST_VISIBLE_BIT) != 0;
if (!isBufferHostVisible) { requiresCommandBufferSubmission = true; }
if (isBufferHostVisible) { updateHostVisibleBuffer(outVbos.back(), static_cast<const void*>(mesh.getData(i)), 0, static_cast<uint32_t>(mesh.getDataSize(i)), true); }
else
{
updateBufferUsingStagingBuffer(device, outVbos.back(), pvrvk::CommandBufferBase(uploadCmdBuffer), static_cast<const void*>(mesh.getData(i)), 0,
static_cast<uint32_t>(mesh.getDataSize(i)), bufferAllocator);
}
}
if (mesh.getNumFaces())
{
pvrvk::MemoryPropertyFlags requiredMemoryFlags = pvrvk::MemoryPropertyFlags::e_DEVICE_LOCAL_BIT;
pvrvk::MemoryPropertyFlags optimalMemoryFlags = requiredMemoryFlags;
outIbo = createBuffer(device,
pvrvk::BufferCreateInfo(static_cast<uint32_t>(mesh.getFaces().getDataSize()), pvrvk::BufferUsageFlags::e_INDEX_BUFFER_BIT | pvrvk::BufferUsageFlags::e_TRANSFER_DST_BIT),
requiredMemoryFlags, optimalMemoryFlags, bufferAllocator, vmaAllocationCreateFlags);
bool isBufferHostVisible = (outIbo->getDeviceMemory()->getMemoryFlags() & pvrvk::MemoryPropertyFlags::e_HOST_VISIBLE_BIT) != 0;
if (!isBufferHostVisible) { requiresCommandBufferSubmission = true; }
if (isBufferHostVisible)
{
updateHostVisibleBuffer(outIbo, static_cast<const void*>(mesh.getFaces().getData()), 0, static_cast<uint32_t>(mesh.getFaces().getDataSize()), true);
}
else
{
updateBufferUsingStagingBuffer(device, outIbo, pvrvk::CommandBufferBase(uploadCmdBuffer), static_cast<const void*>(mesh.getFaces().getData()), 0,
static_cast<uint32_t>(mesh.getFaces().getDataSize()), bufferAllocator);
}
}
}
template<typename MeshIterator_, typename VboInsertIterator_, typename IboInsertIterator_>
inline void createSingleBuffersFromMeshes(pvrvk::Device& device, MeshIterator_ meshIter, MeshIterator_ meshIterEnd, VboInsertIterator_ outVbos, IboInsertIterator_ outIbos,
pvrvk::CommandBuffer& uploadCmdBuffer, bool& requiresCommandBufferSubmission, vma::Allocator bufferAllocator = nullptr,
vma::AllocationCreateFlags vmaAllocationCreateFlags = vma::AllocationCreateFlags::e_MAPPED_BIT)
{
pvr::utils::beginCommandBufferDebugLabel(uploadCmdBuffer, pvrvk::DebugUtilsLabel("PVRUtilsVk::createSingleBuffersFromMeshes"));
requiresCommandBufferSubmission = false;
while (meshIter != meshIterEnd)
{
size_t total = 0;
for (uint32_t ii = 0; ii < meshIter->getNumDataElements(); ++ii) { total += meshIter->getDataSize(ii); }
pvrvk::Buffer vbo;
pvrvk::MemoryPropertyFlags vboRequiredMemoryFlags = pvrvk::MemoryPropertyFlags::e_DEVICE_LOCAL_BIT;
pvrvk::MemoryPropertyFlags vboOptimalMemoryFlags = vboRequiredMemoryFlags;
vbo = createBuffer(device, pvrvk::BufferCreateInfo(static_cast<uint32_t>(total), pvrvk::BufferUsageFlags::e_VERTEX_BUFFER_BIT | pvrvk::BufferUsageFlags::e_TRANSFER_DST_BIT),
vboRequiredMemoryFlags, vboOptimalMemoryFlags, bufferAllocator, vmaAllocationCreateFlags);
bool isVboHostVisible = (vbo->getDeviceMemory()->getMemoryFlags() & pvrvk::MemoryPropertyFlags::e_HOST_VISIBLE_BIT) != 0;
if (!isVboHostVisible) { requiresCommandBufferSubmission = true; }
size_t current = 0;
for (size_t ii = 0; ii < meshIter->getNumDataElements(); ++ii)
{
if (isVboHostVisible)
{
updateHostVisibleBuffer(vbo, (const void*)meshIter->getData(static_cast<uint32_t>(ii)), static_cast<uint32_t>(current),
static_cast<uint32_t>(meshIter->getDataSize(static_cast<uint32_t>(ii))), true);
}
else
{
updateBufferUsingStagingBuffer(device, vbo, pvrvk::CommandBufferBase(uploadCmdBuffer), (const void*)meshIter->getData(static_cast<uint32_t>(ii)),
static_cast<uint32_t>(current), static_cast<uint32_t>(meshIter->getDataSize(static_cast<uint32_t>(ii))), bufferAllocator);
}
current += meshIter->getDataSize(static_cast<uint32_t>(ii));
}
outVbos = vbo;
if (meshIter->getNumFaces())
{
pvrvk::Buffer ibo;
pvrvk::MemoryPropertyFlags iboRequiredMemoryFlags = pvrvk::MemoryPropertyFlags::e_DEVICE_LOCAL_BIT;
pvrvk::MemoryPropertyFlags iboOptimalMemoryFlags = iboRequiredMemoryFlags;
ibo = createBuffer(device,
pvrvk::BufferCreateInfo(
static_cast<uint32_t>(meshIter->getFaces().getDataSize()), pvrvk::BufferUsageFlags::e_INDEX_BUFFER_BIT | pvrvk::BufferUsageFlags::e_TRANSFER_DST_BIT),
iboRequiredMemoryFlags, iboOptimalMemoryFlags, bufferAllocator, vmaAllocationCreateFlags);
bool isIboHostVisible = (ibo->getDeviceMemory()->getMemoryFlags() & pvrvk::MemoryPropertyFlags::e_HOST_VISIBLE_BIT) != 0;
if (!isIboHostVisible) { requiresCommandBufferSubmission = true; }
if (isIboHostVisible) { updateHostVisibleBuffer(ibo, static_cast<const void*>(meshIter->getFaces().getData()), 0, meshIter->getFaces().getDataSize(), true); }
else
{
updateBufferUsingStagingBuffer(device, ibo, pvrvk::CommandBufferBase(uploadCmdBuffer), static_cast<const void*>(meshIter->getFaces().getData()), 0,
meshIter->getFaces().getDataSize(), bufferAllocator);
}
outIbos = ibo;
}
else
{
outIbos = pvrvk::Buffer();
}
++outVbos;
++outIbos;
++meshIter;
}
pvr::utils::endCommandBufferDebugLabel(uploadCmdBuffer);
}
template<typename MeshIterator_, typename VboContainer_, typename IboContainer_>
inline void createSingleBuffersFromMeshes(pvrvk::Device& device, MeshIterator_ meshIter, MeshIterator_ meshIterEnd, VboContainer_& outVbos,
typename VboContainer_::iterator vbos_where, IboContainer_& outIbos, typename IboContainer_::iterator ibos_where, pvrvk::CommandBuffer& uploadCmdBuffer,
bool& requiresCommandBufferSubmission, vma::Allocator bufferAllocator = nullptr, vma::AllocationCreateFlags vmaAllocationCreateFlags = vma::AllocationCreateFlags::e_MAPPED_BIT)
{
createSingleBuffersFromMeshes(device, meshIter, meshIterEnd, std::inserter(outVbos, vbos_where), std::inserter(outIbos, ibos_where), uploadCmdBuffer,
requiresCommandBufferSubmission, bufferAllocator, vmaAllocationCreateFlags);
}
template<typename VboInsertIterator_, typename IboInsertIterator_>
inline void createSingleBuffersFromModel(pvrvk::Device& device, const assets::Model& model, VboInsertIterator_ vbos, IboInsertIterator_ ibos, pvrvk::CommandBuffer& uploadCmdBuffer,
bool& requiresCommandBufferSubmission, vma::Allocator bufferAllocator = nullptr, vma::AllocationCreateFlags vmaAllocationCreateFlags = vma::AllocationCreateFlags::e_MAPPED_BIT)
{
createSingleBuffersFromMeshes(device, model.beginMeshes(), model.endMeshes(), vbos, ibos, uploadCmdBuffer, requiresCommandBufferSubmission, bufferAllocator, vmaAllocationCreateFlags);
}
template<typename VboContainer_, typename IboContainer_>
inline void appendSingleBuffersFromModel(pvrvk::Device& device, const assets::Model& model, VboContainer_& vbos, IboContainer_& ibos, pvrvk::CommandBuffer& uploadCmdBuffer,
bool& requiresCommandBufferSubmission, vma::Allocator bufferAllocator = nullptr, vma::AllocationCreateFlags vmaAllocationCreateFlags = vma::AllocationCreateFlags::e_MAPPED_BIT)
{
createSingleBuffersFromMeshes(device, model.beginMeshes(), model.endMeshes(), std::inserter(vbos, vbos.end()), std::inserter(ibos, ibos.end()), uploadCmdBuffer,
requiresCommandBufferSubmission, bufferAllocator, vmaAllocationCreateFlags);
}
void create3dPlaneMesh(uint32_t width, uint32_t depth, bool generateTexCoords, bool generateNormalCoords, assets::Mesh& outMesh);
#pragma endregion
#pragma region
std::vector<unsigned char> captureImageRegion(pvrvk::Queue& queue, pvrvk::CommandPool& commandPool, pvrvk::Image& image, pvrvk::Offset3D srcOffset = pvrvk::Offset3D(0, 0, 0),
pvrvk::Extent3D srcExtent = pvrvk::Extent3D(static_cast<uint32_t>(-1), static_cast<uint32_t>(-1), static_cast<uint32_t>(-1)),
pvrvk::Format destinationImageFormat = pvrvk::Format::e_UNDEFINED, pvrvk::ImageLayout imageInitialLayout = pvrvk::ImageLayout::e_TRANSFER_SRC_OPTIMAL,
pvrvk::ImageLayout imageFinalLayout = pvrvk::ImageLayout::e_TRANSFER_DST_OPTIMAL, vma::Allocator bufferAllocator = nullptr, vma::Allocator imageAllocator = nullptr);
void saveImage(pvrvk::Queue& queue, pvrvk::CommandPool& commandPool, pvrvk::Image& image, const pvrvk::ImageLayout imageInitialLayout, const pvrvk::ImageLayout imageFinalLayout,
const std::string& filename, vma::Allocator bufferAllocator = nullptr, vma::Allocator imageAllocator = nullptr, const uint32_t screenshotScale = 1);
bool takeScreenshot(pvrvk::Queue& queue, pvrvk::CommandPool& commandPool, pvrvk::Swapchain& swapchain, const uint32_t swapIndex, const std::string& screenshotFileName,
vma::Allocator bufferAllocator = nullptr, vma::Allocator imageAllocator = nullptr, const uint32_t screenshotScale = 1);
std::vector<int> validatePhysicalDeviceExtensions(const pvrvk::Instance instance, const std::vector<std::string>& vectorExtensionNames);
bool formatWithTilingSupportsFeatureFlags(
pvrvk::Format imageFormat, pvrvk::ImageTiling imageTiling, pvrvk::FormatFeatureFlags formatFeatureFlags, const pvrvk::Instance instance, pvrvk::PhysicalDevice physicalDevice);
void readJsonUUID(std::string fileName, uint8_t* UUID);
#pragma endregion
} // namespace utils
} // namespace pvr