Add ktx
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,172 @@
|
||||
/* -*- tab-width: 4; -*- */
|
||||
/* vi: set sw=2 ts=4 expandtab: */
|
||||
|
||||
#ifndef VULKAN_APP_SDL_H_1456211188
|
||||
#define VULKAN_APP_SDL_H_1456211188
|
||||
|
||||
/*
|
||||
* Copyright 2017-2020 Mark Callow.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <new>
|
||||
#include <vector>
|
||||
#define VK_ENABLE_BETA_EXTENSIONS 1
|
||||
#include <vulkan/vulkan.hpp>
|
||||
#include <SDL3/SDL_vulkan.h> // Must be after vulkan.hpp.
|
||||
|
||||
#include "AppBaseSDL.h"
|
||||
#include "VulkanContext.h"
|
||||
#include "VulkanSwapchain.h"
|
||||
#include "vulkantextoverlay.hpp"
|
||||
#include "unused.h"
|
||||
|
||||
class VulkanAppSDL : public AppBaseSDL {
|
||||
public:
|
||||
VulkanAppSDL(const char* const name,
|
||||
int width, int height,
|
||||
const uint32_t version,
|
||||
bool enableTextOverlay)
|
||||
: AppBaseSDL(name), w_width(width), w_height(height),
|
||||
subOptimalPresentWarned(false), validate(false),
|
||||
vkVersion(version),
|
||||
enableTextOverlay(enableTextOverlay),
|
||||
textOverlay(nullptr)
|
||||
{
|
||||
// The overridden new below will zero the storage. Thus
|
||||
// we avoid a long list of initializers.
|
||||
appTitle = name;
|
||||
};
|
||||
virtual ~VulkanAppSDL();
|
||||
virtual bool doEvent(SDL_Event* event);
|
||||
virtual void drawFrame(uint32_t msTicks);
|
||||
virtual void finalize();
|
||||
virtual bool initialize(Args& args);
|
||||
virtual void onFPSUpdate();
|
||||
virtual void resizeWindow(int width, int height);
|
||||
virtual void windowResized();
|
||||
|
||||
static void* operator new(size_t size) {
|
||||
void* storage = ::operator new(size);
|
||||
memset(storage, 0, size);
|
||||
return storage;
|
||||
}
|
||||
|
||||
static void operator delete(void* storage, size_t) {
|
||||
::operator delete(storage);
|
||||
}
|
||||
|
||||
void updateTextOverlay();
|
||||
|
||||
// Called when the text overlay is updating
|
||||
// Can be overridden in derived class to add custom text to the overlay
|
||||
virtual void getOverlayText(float yOffset);
|
||||
|
||||
protected:
|
||||
bool createDevice();
|
||||
bool createInstance();
|
||||
bool createPipelineCache();
|
||||
bool createSemaphores();
|
||||
bool createSurface();
|
||||
bool createSwapchain();
|
||||
bool findGpu();
|
||||
void flushInitialCommands();
|
||||
bool initializeVulkan();
|
||||
bool prepareColorBuffers();
|
||||
bool prepareCommandBuffers();
|
||||
bool prepareDepthBuffer();
|
||||
bool prepareDescriptorLayout();
|
||||
void prepareFrame();
|
||||
bool preparePresentCommandBuffers();
|
||||
bool prepareRenderPass();
|
||||
bool preparePipeline();
|
||||
bool prepareDescriptorSet();
|
||||
bool prepareFramebuffers();
|
||||
void prepareTextOverlay();
|
||||
void submitFrame();
|
||||
|
||||
bool setupDebugReporting();
|
||||
|
||||
enum stencilRequirement { eNoStencil = 0, eStencil = 1 };
|
||||
enum depthRequirement { e16bits = 0, e24bits = 1, e32bits = 2 };
|
||||
bool getSupportedDepthFormat(vk::PhysicalDevice gpu,
|
||||
stencilRequirement requiredStencil,
|
||||
depthRequirement requiredDepth,
|
||||
vk::ImageTiling tiling,
|
||||
vk::Format& pFormat,
|
||||
vk::ImageAspectFlags& pAspectMask);
|
||||
|
||||
// Sets text on window title bar.
|
||||
void setWindowTitle();
|
||||
|
||||
void setImageLayout(VkImage image, VkImageAspectFlags aspectMask,
|
||||
VkImageLayout old_image_layout,
|
||||
VkImageLayout new_image_layout,
|
||||
VkAccessFlags srcAccessMask);
|
||||
|
||||
VKAPI_ATTR VkBool32 VKAPI_CALL
|
||||
debugFunc(VkFlags msgFlags, VkDebugReportObjectTypeEXT objType,
|
||||
uint64_t srcObject, size_t location, int32_t msgCode,
|
||||
const char *pLayerPrefix, const char *pMsg);
|
||||
std::string& wrapText(std::string& source, size_t width = 70,
|
||||
const std::string& whitespace = " \t\r");
|
||||
uint32_t showDebugReport(uint32_t mbFlags, const std::string title,
|
||||
std::string message, bool enableAbort);
|
||||
|
||||
static bool checkLayers(uint32_t nameCount, const char **names,
|
||||
uint32_t layerCount, VkLayerProperties *layers);
|
||||
static VKAPI_ATTR VkBool32 VKAPI_CALL
|
||||
debugFunc(VkFlags msgFlags, VkDebugReportObjectTypeEXT objType,
|
||||
uint64_t srcObject, size_t location, int32_t msgCode,
|
||||
const char *pLayerPrefix, const char *pMsg, void *pUserData);
|
||||
|
||||
bool prepared = false;
|
||||
// Set true if want presents v-sync'ed.
|
||||
bool enableVSync = false;
|
||||
|
||||
uint32_t w_width;
|
||||
uint32_t w_height;
|
||||
|
||||
bool subOptimalPresentWarned;
|
||||
bool validate;
|
||||
|
||||
std::vector<const char*> extensionNames;
|
||||
std::vector<const char*> deviceValidationLayers;
|
||||
|
||||
VkCommandBuffer setupCmdBuffer;
|
||||
VkSurfaceKHR vsSurface;
|
||||
|
||||
VulkanContext vkctx;
|
||||
|
||||
// Index of active framebuffer.
|
||||
uint32_t currentBuffer;
|
||||
|
||||
// Synchronization semaphores
|
||||
struct {
|
||||
// Swap chain image presentation
|
||||
VkSemaphore presentComplete;
|
||||
// Command buffer submission and execution
|
||||
VkSemaphore renderComplete;
|
||||
// Text overlay submission and execution
|
||||
VkSemaphore textOverlayComplete;
|
||||
} semaphores;
|
||||
|
||||
const uint32_t vkVersion;
|
||||
|
||||
// Saved for clean-up
|
||||
std::vector<vk::ShaderModule> shaderModules;
|
||||
|
||||
bool enableTextOverlay = false;
|
||||
VulkanTextOverlay *textOverlay;
|
||||
// List of shader modules created (stored for cleanup)
|
||||
|
||||
VkDebugReportCallbackEXT msgCallback;
|
||||
|
||||
PFN_vkCreateDebugReportCallbackEXT pfnCreateDebugReportCallbackEXT;
|
||||
PFN_vkDestroyDebugReportCallbackEXT pfnDestroyDebugReportCallbackEXT;
|
||||
PFN_vkDebugReportMessageEXT pfnDebugReportMessageEXT;
|
||||
};
|
||||
|
||||
#endif /* VULKAN_APP_SDL_H_1456211188 */
|
||||
@@ -0,0 +1,363 @@
|
||||
/* -*- tab-width: 4; -*- */
|
||||
/* vi: set sw=2 ts=4 expandtab: */
|
||||
|
||||
/*
|
||||
* Copyright 2017-2020 Mark Callow.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @class VulkanContext
|
||||
* @~English
|
||||
*
|
||||
* @brief Class for holding and passing Vulkan context info to applications.
|
||||
*
|
||||
* @author Mark Callow, github.com/MarkCallow.
|
||||
*/
|
||||
|
||||
#include <exception>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <SDL3/SDL_iostream.h>
|
||||
#include "VulkanContext.h"
|
||||
// Until exceptions are used everywhere...
|
||||
#include "vulkancheckres.h"
|
||||
#include "ltexceptions.h"
|
||||
#include "unused.h"
|
||||
|
||||
vk::CommandBuffer
|
||||
VulkanContext::createCommandBuffer(vk::CommandBufferLevel level, bool begin)
|
||||
{
|
||||
vk::CommandBuffer cmdBuffer;
|
||||
|
||||
vk::CommandBufferAllocateInfo cmdBufAllocateInfo(
|
||||
commandPool,
|
||||
level,
|
||||
1);
|
||||
|
||||
vk::Result res
|
||||
= device.allocateCommandBuffers(&cmdBufAllocateInfo, &cmdBuffer);
|
||||
|
||||
if (res != vk::Result::eSuccess) {
|
||||
throw bad_vulkan_alloc((int)res, "device.allocateCommandBuffers");
|
||||
}
|
||||
|
||||
// If requested, also start the new command buffer
|
||||
if (begin)
|
||||
{
|
||||
vk::CommandBufferBeginInfo cmdBufInfo;
|
||||
res = cmdBuffer.begin(&cmdBufInfo);
|
||||
if (res != vk::Result::eSuccess) {
|
||||
throw bad_vulkan_alloc((int)res, "cmdBuffer.begin");
|
||||
}
|
||||
}
|
||||
|
||||
return cmdBuffer;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VulkanContext::flushCommandBuffer(vk::CommandBuffer& cmdBuffer,
|
||||
bool free)
|
||||
{
|
||||
if (!cmdBuffer) {
|
||||
return;
|
||||
}
|
||||
|
||||
cmdBuffer.end();
|
||||
|
||||
vk::SubmitInfo submitInfo;
|
||||
submitInfo.commandBufferCount = 1;
|
||||
submitInfo.pCommandBuffers = &cmdBuffer;
|
||||
|
||||
vk::Result res = queue.submit(1, &submitInfo, nullptr);
|
||||
if (res != vk::Result::eSuccess) {
|
||||
if (res == vk::Result::eErrorDeviceLost) {
|
||||
throw std::runtime_error("Vulkan device lost.");
|
||||
} else {
|
||||
throw bad_vulkan_alloc((int)res, "queue.submit");
|
||||
}
|
||||
}
|
||||
|
||||
queue.waitIdle();
|
||||
|
||||
if (free) {
|
||||
device.freeCommandBuffers(commandPool, 1, &cmdBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
VulkanContext::createDrawCommandBuffers()
|
||||
{
|
||||
VkCommandBufferAllocateInfo aInfo;
|
||||
aInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
||||
aInfo.pNext = NULL;
|
||||
aInfo.commandPool = commandPool;
|
||||
aInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
||||
aInfo.commandBufferCount = 1;
|
||||
|
||||
drawCmdBuffers.resize(swapchain.imageCount);
|
||||
for (uint32_t i = 0; i < swapchain.imageCount; i++) {
|
||||
VK_CHECK_RESULT(vkAllocateCommandBuffers(device, &aInfo,
|
||||
&drawCmdBuffers[i]));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
VulkanContext::createPresentCommandBuffers()
|
||||
{
|
||||
VkCommandBufferAllocateInfo aInfo;
|
||||
aInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
||||
aInfo.pNext = NULL;
|
||||
aInfo.commandPool = commandPool;
|
||||
aInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
||||
aInfo.commandBufferCount = 1;
|
||||
|
||||
prePresentCmdBuffers.resize(swapchain.imageCount);
|
||||
postPresentCmdBuffers.resize(swapchain.imageCount);
|
||||
for (uint32_t i = 0; i < swapchain.imageCount; i++) {
|
||||
VK_CHECK_RESULT(vkAllocateCommandBuffers(device, &aInfo,
|
||||
&prePresentCmdBuffers[i]));
|
||||
VK_CHECK_RESULT(vkAllocateCommandBuffers(device, &aInfo,
|
||||
&postPresentCmdBuffers[i]));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
VulkanContext::destroyDrawCommandBuffers()
|
||||
{
|
||||
if (drawCmdBuffers.size() > 0) {
|
||||
vkFreeCommandBuffers(device, commandPool,
|
||||
static_cast<uint32_t>(drawCmdBuffers.size()),
|
||||
drawCmdBuffers.data());
|
||||
}
|
||||
for (uint32_t i = 0; i < drawCmdBuffers.size(); ++i) {
|
||||
drawCmdBuffers[i] = nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
VulkanContext::destroyPresentCommandBuffers()
|
||||
{
|
||||
vkFreeCommandBuffers(device, commandPool,
|
||||
static_cast<uint32_t>(drawCmdBuffers.size()),
|
||||
prePresentCmdBuffers.data());
|
||||
vkFreeCommandBuffers(device, commandPool,
|
||||
static_cast<uint32_t>(drawCmdBuffers.size()),
|
||||
postPresentCmdBuffers.data());
|
||||
}
|
||||
|
||||
bool
|
||||
VulkanContext::checkDrawCommandBuffers()
|
||||
{
|
||||
for (auto& cmdBuffer : drawCmdBuffers)
|
||||
{
|
||||
if (cmdBuffer == VK_NULL_HANDLE)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
VulkanContext::createBuffer(vk::BufferUsageFlags usageFlags,
|
||||
vk::MemoryPropertyFlags memoryPropertyFlags,
|
||||
vk::DeviceSize size,
|
||||
void * data,
|
||||
vk::Buffer* buffer,
|
||||
vk::DeviceMemory* memory)
|
||||
{
|
||||
vk::MemoryRequirements memReqs;
|
||||
vk::MemoryAllocateInfo memAlloc(0, 0);
|
||||
vk::BufferCreateInfo bufferCreateInfo({}, size, usageFlags);
|
||||
|
||||
vk::Result res = device.createBuffer(&bufferCreateInfo, nullptr, buffer);
|
||||
if (res != vk::Result::eSuccess) {
|
||||
return false;
|
||||
}
|
||||
|
||||
device.getBufferMemoryRequirements(*buffer, &memReqs);
|
||||
memAlloc.allocationSize = memReqs.size;
|
||||
memAlloc.memoryTypeIndex = getMemoryType(memReqs.memoryTypeBits,
|
||||
memoryPropertyFlags);
|
||||
res = device.allocateMemory(&memAlloc, nullptr, memory);
|
||||
if (res == vk::Result::eSuccess) {
|
||||
if (data != nullptr)
|
||||
{
|
||||
void *mapped;
|
||||
mapped = device.mapMemory(*memory, 0, size, {});
|
||||
memcpy(mapped, data, (size_t)size);
|
||||
device.unmapMemory(*memory);
|
||||
}
|
||||
device.bindBufferMemory(*buffer, *memory, 0);
|
||||
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
VulkanContext::createBuffer(vk::BufferUsageFlags usage,
|
||||
vk::DeviceSize size,
|
||||
void* data,
|
||||
vk::Buffer* buffer,
|
||||
vk::DeviceMemory* memory)
|
||||
{
|
||||
return createBuffer(usage, vk::MemoryPropertyFlagBits::eHostVisible,
|
||||
size, data, buffer, memory);
|
||||
}
|
||||
|
||||
bool
|
||||
VulkanContext::createBuffer(vk::BufferUsageFlags usage,
|
||||
vk::DeviceSize size,
|
||||
void* data,
|
||||
vk::Buffer* buffer,
|
||||
vk::DeviceMemory* memory,
|
||||
vk::DescriptorBufferInfo* descriptor)
|
||||
{
|
||||
bool res = createBuffer(usage, size, data, buffer, memory);
|
||||
if (res)
|
||||
{
|
||||
descriptor->offset = 0;
|
||||
descriptor->buffer = *buffer;
|
||||
descriptor->range = size;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
VulkanContext::createBuffer(vk::BufferUsageFlags usage,
|
||||
vk::MemoryPropertyFlags memoryPropertyFlags,
|
||||
vk::DeviceSize size,
|
||||
void* data,
|
||||
vk::Buffer* buffer,
|
||||
vk::DeviceMemory* memory,
|
||||
vk::DescriptorBufferInfo* descriptor)
|
||||
{
|
||||
bool res = createBuffer(usage, memoryPropertyFlags, size, data, buffer, memory);
|
||||
if (res)
|
||||
{
|
||||
descriptor->offset = 0;
|
||||
descriptor->buffer = *buffer;
|
||||
descriptor->range = size;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
VulkanContext::getMemoryType(uint32_t typeBits,
|
||||
vk::MemoryPropertyFlags requirementsMask,
|
||||
uint32_t *typeIndex) const
|
||||
{
|
||||
// Search memtypes to find first index with desired properties
|
||||
for (uint32_t i = 0; i < VK_MAX_MEMORY_TYPES; i++) {
|
||||
if ((typeBits & 1) == 1) {
|
||||
// Type is available, does it match user properties?
|
||||
if ((memoryProperties.memoryTypes[i].propertyFlags &
|
||||
requirementsMask) == requirementsMask) {
|
||||
*typeIndex = i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
typeBits >>= 1;
|
||||
}
|
||||
// No memory types matched, return failure
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
VulkanContext::getMemoryType(uint32_t typeBits,
|
||||
vk::MemoryPropertyFlags requirementsMask) const
|
||||
{
|
||||
// Search memtypes to find first index with desired properties
|
||||
for (uint32_t i = 0; i < VK_MAX_MEMORY_TYPES; i++) {
|
||||
if ((typeBits & 1) == 1) {
|
||||
// Type is available, does it match user properties?
|
||||
if ((memoryProperties.memoryTypes[i].propertyFlags &
|
||||
requirementsMask) == requirementsMask) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
typeBits >>= 1;
|
||||
}
|
||||
// No memory types matched, return failure
|
||||
// TODO throw error
|
||||
return 0;
|
||||
}
|
||||
|
||||
vk::PipelineShaderStageCreateInfo
|
||||
VulkanContext::loadShader(std::string filename,
|
||||
vk::ShaderStageFlagBits stage,
|
||||
const char* const modname)
|
||||
{
|
||||
vk::PipelineShaderStageCreateInfo shaderStage({}, stage);
|
||||
shaderStage.module = loadShader(filename);
|
||||
shaderStage.pName = modname;
|
||||
return shaderStage;
|
||||
}
|
||||
|
||||
vk::ShaderModule
|
||||
VulkanContext::loadShader(std::string filename)
|
||||
{
|
||||
size_t codeSize;
|
||||
uint32_t* shaderCode;
|
||||
|
||||
shaderCode = readSpv(filename.c_str(), &codeSize);
|
||||
|
||||
vk::ShaderModule shaderModule;
|
||||
vk::ShaderModuleCreateInfo moduleCreateInfo({}, codeSize, shaderCode);
|
||||
|
||||
vk::Result res
|
||||
= device.createShaderModule(&moduleCreateInfo, NULL, &shaderModule);
|
||||
if (res != vk::Result::eSuccess) {
|
||||
throw bad_vulkan_alloc((int)res, "device.createShaderModule");
|
||||
}
|
||||
|
||||
delete[] shaderCode;
|
||||
|
||||
assert(shaderModule);
|
||||
return shaderModule;
|
||||
}
|
||||
|
||||
uint32_t*
|
||||
VulkanContext::readSpv(const char *filename, size_t *pSize) {
|
||||
size_t size;
|
||||
U_ASSERT_ONLY size_t retval;
|
||||
uint32_t* shader_code;
|
||||
|
||||
SDL_IOStream* io = SDL_IOFromFile(filename, "rb");
|
||||
if (!io) {
|
||||
std::stringstream message;
|
||||
|
||||
// String returned by SDL_GetError() includes file name.
|
||||
message << "Open of shader failed: " << SDL_GetError();
|
||||
throw std::runtime_error(message.str());
|
||||
}
|
||||
|
||||
size = (size_t)SDL_GetIOSize(io);
|
||||
|
||||
// Round-up to next 4-byte size.
|
||||
shader_code = new uint32_t[(size + 3)/4];
|
||||
retval = SDL_ReadIO(io, shader_code, size);
|
||||
assert(retval == size);
|
||||
|
||||
*pSize = size;
|
||||
|
||||
SDL_CloseIO(io);
|
||||
return shader_code;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
/* -*- tab-width: 4; -*- */
|
||||
/* vi: set sw=2 ts=4 expandtab: */
|
||||
|
||||
#ifndef VULKAN_TEXTURE_H_229895365400979164311947449304284143508
|
||||
#define VULKAN_TEXTURE_H_229895365400979164311947449304284143508
|
||||
|
||||
/*
|
||||
* Copyright 2017-2020 Mark Callow.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <vector>
|
||||
#define VK_ENABLE_BETA_EXTENSIONS 1
|
||||
#include <vulkan/vulkan.hpp>
|
||||
#include "VulkanSwapchain.h"
|
||||
|
||||
struct VulkanDepthBuffer {
|
||||
VkFormat format;
|
||||
VkImage image;
|
||||
VkMemoryAllocateInfo memAlloc;
|
||||
VkDeviceMemory mem;
|
||||
VkImageView view;
|
||||
};
|
||||
|
||||
struct VulkanContext {
|
||||
vk::Instance instance;
|
||||
vk::PhysicalDevice gpu;
|
||||
vk::PhysicalDeviceFeatures gpuFeatures;
|
||||
#if VK_KHR_portability_subset
|
||||
vk::PhysicalDevicePortabilitySubsetFeaturesKHR gpuPortabilityFeatures;
|
||||
#endif
|
||||
vk::PhysicalDeviceProperties gpuProperties;
|
||||
vk::PhysicalDeviceMemoryProperties memoryProperties;
|
||||
vk::Device device;
|
||||
vk::CommandPool commandPool;
|
||||
vk::Queue queue;
|
||||
|
||||
bool gpuIsPortabilitySubsetDevice = false;
|
||||
|
||||
struct {
|
||||
bool pvrtc = false;
|
||||
bool astc_hdr = false;
|
||||
bool astc_3d = false;
|
||||
} enabledDeviceExtensions;
|
||||
std::vector<VkCommandBuffer> drawCmdBuffers;
|
||||
std::vector<VkCommandBuffer> postPresentCmdBuffers;
|
||||
std::vector<VkCommandBuffer> prePresentCmdBuffers;
|
||||
VkRenderPass renderPass;
|
||||
// Pipeline stage flags for the submit info structure
|
||||
const VkPipelineStageFlags submitPipelineStages =
|
||||
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
|
||||
// Contains command buffers and semaphores to be presented to the queue
|
||||
VkSubmitInfo drawCmdSubmitInfo;
|
||||
|
||||
vk::DescriptorPool descriptorPool;
|
||||
VkPipelineCache pipelineCache;
|
||||
|
||||
VulkanSwapchain swapchain;
|
||||
// List of frame buffers (same as number of swap chain images)
|
||||
std::vector<VkFramebuffer>framebuffers;
|
||||
VulkanDepthBuffer depthBuffer;
|
||||
|
||||
// Create a new command buffer, opening it for command entry,
|
||||
// if requested.
|
||||
vk::CommandBuffer createCommandBuffer(vk::CommandBufferLevel level,
|
||||
bool begin);
|
||||
// End a command buffer, submit it to the queue and free, if requested.
|
||||
// Note : Waits for the queue to become idle
|
||||
void flushCommandBuffer(vk::CommandBuffer& commandBuffer, bool free);
|
||||
|
||||
// Create a command buffer for each image in the swap chain.
|
||||
bool createDrawCommandBuffers();
|
||||
bool createPresentCommandBuffers();
|
||||
bool checkDrawCommandBuffers();
|
||||
void destroyDrawCommandBuffers();
|
||||
void destroyPresentCommandBuffers();
|
||||
|
||||
|
||||
bool getMemoryType(uint32_t typeBits,
|
||||
vk::MemoryPropertyFlags requirementsMask,
|
||||
uint32_t *typeIndex) const;
|
||||
uint32_t getMemoryType(uint32_t typeBits,
|
||||
vk::MemoryPropertyFlags requirementsMask) const;
|
||||
|
||||
// Create a buffer, fill it with data (if != NULL) and bind buffer memory
|
||||
bool createBuffer(
|
||||
vk::BufferUsageFlags usageFlags,
|
||||
vk::MemoryPropertyFlags memoryPropertyFlags,
|
||||
vk::DeviceSize size,
|
||||
void *data,
|
||||
vk::Buffer *buffer,
|
||||
vk::DeviceMemory *memory);
|
||||
// This version always uses HOST_VISIBLE memory
|
||||
bool createBuffer(
|
||||
vk::BufferUsageFlags usage,
|
||||
vk::DeviceSize size,
|
||||
void *data,
|
||||
vk::Buffer *buffer,
|
||||
vk::DeviceMemory *memory);
|
||||
// Overload that assigns buffer info to descriptor
|
||||
bool createBuffer(
|
||||
vk::BufferUsageFlags usage,
|
||||
vk::DeviceSize size,
|
||||
void* data,
|
||||
vk::Buffer* buffer,
|
||||
vk::DeviceMemory* memory,
|
||||
vk::DescriptorBufferInfo* descriptor);
|
||||
// Overload to pass memory property flags
|
||||
bool createBuffer(
|
||||
vk::BufferUsageFlags usage,
|
||||
vk::MemoryPropertyFlags memoryPropertyFlags,
|
||||
vk::DeviceSize size,
|
||||
void* data,
|
||||
vk::Buffer* buffer,
|
||||
vk::DeviceMemory* memory,
|
||||
vk::DescriptorBufferInfo* descriptor);
|
||||
|
||||
vk::PipelineShaderStageCreateInfo loadShader(std::string filename,
|
||||
vk::ShaderStageFlagBits stage,
|
||||
const char* const modname = "main");
|
||||
vk::ShaderModule loadShader(std::string filename);
|
||||
uint32_t* readSpv(const char *filename, size_t *pSize);
|
||||
|
||||
bool gpuSupportsSwizzle() {
|
||||
#if VK_KHR_portability_subset
|
||||
return !gpuIsPortabilitySubsetDevice
|
||||
|| gpuPortabilityFeatures.imageViewFormatSwizzle;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* VULKAN_TEXTURE_H_229895365400979164311947449304284143508 */
|
||||
@@ -0,0 +1,434 @@
|
||||
/* -*- tab-width: 4; -*- */
|
||||
/* vi: set sw=2 ts=4 expandtab: */
|
||||
|
||||
/*
|
||||
* Copyright 2017-2020 Mark Callow.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @class VulkanSwapchain
|
||||
* @~English
|
||||
*
|
||||
* @brief Manage the swapchain for a Vulkan app.
|
||||
*
|
||||
* A swap chain is a collection of image buffers used for rendering
|
||||
* The images can then be presented to the windowing system for display.
|
||||
*
|
||||
* @author Mark Callow, github.com/MarkCallow.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <vector>
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <fcntl.h>
|
||||
#include <io.h>
|
||||
#else
|
||||
#endif
|
||||
|
||||
#include "VulkanSwapchain.h"
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
#include <SDL3/SDL_vulkan.h>
|
||||
#include "AppBaseSDL.h"
|
||||
#include "unused.h"
|
||||
|
||||
#define ERROR_RETURN(msg) \
|
||||
(void)SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, theApp->name(), \
|
||||
msg, NULL); \
|
||||
return false;
|
||||
|
||||
#define GET_INSTANCE_PROC_ADDR(inst, entrypoint) \
|
||||
{ \
|
||||
pfn##entrypoint = \
|
||||
(PFN_vk##entrypoint)vkGetInstanceProcAddr(inst, "vk"#entrypoint); \
|
||||
if (pfn##entrypoint == NULL) { \
|
||||
ERROR_RETURN("vkGetInstanceProcAddr: unable to find vk"#entrypoint); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define GET_DEVICE_PROC_ADDR(device, entrypoint) \
|
||||
{ \
|
||||
pfn##entrypoint = \
|
||||
(PFN_vk##entrypoint)vkGetDeviceProcAddr(device, "vk"#entrypoint); \
|
||||
if (pfn##entrypoint == NULL) { \
|
||||
ERROR_RETURN("vkGetDeviceProcAddr: unable to find vk"#entrypoint); \
|
||||
} \
|
||||
}
|
||||
|
||||
// Creates an os specific surface
|
||||
// Tries to find a graphics and a present queue
|
||||
bool
|
||||
VulkanSwapchain::initSurface(SDL_Window* window)
|
||||
{
|
||||
U_ASSERT_ONLY VkResult err;
|
||||
|
||||
if (!SDL_Vulkan_CreateSurface(window, instance, nullptr, &surface)) {
|
||||
std::string msg = "SDL_CreateVulkanSurface failed: ";
|
||||
msg += SDL_GetError();
|
||||
ERROR_RETURN(msg.c_str());
|
||||
}
|
||||
|
||||
// Get available queue family properties
|
||||
uint32_t queueCount;
|
||||
vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueCount, NULL);
|
||||
assert(queueCount >= 1);
|
||||
|
||||
std::vector<VkQueueFamilyProperties> queueProps(queueCount);
|
||||
vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueCount,
|
||||
queueProps.data());
|
||||
|
||||
// Iterate over the queues looking for ones which support presenting.
|
||||
std::vector<VkBool32> supportsPresent(queueCount);
|
||||
for (uint32_t i = 0; i < queueCount; i++) {
|
||||
vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, i, surface,
|
||||
&supportsPresent[i]);
|
||||
}
|
||||
|
||||
// Search for a graphics- and present-capable queue.
|
||||
uint32_t graphicsQueueIndex = UINT32_MAX;
|
||||
uint32_t presentQueueIndex = UINT32_MAX;
|
||||
for (uint32_t i = 0; i < queueCount; i++) {
|
||||
if ((queueProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0)
|
||||
{
|
||||
if (graphicsQueueIndex == UINT32_MAX)
|
||||
{
|
||||
graphicsQueueIndex = i;
|
||||
}
|
||||
|
||||
if (supportsPresent[i] == VK_TRUE)
|
||||
{
|
||||
graphicsQueueIndex = i;
|
||||
presentQueueIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (presentQueueIndex == UINT32_MAX) {
|
||||
// If there's no queue that supports both present and graphics
|
||||
// try to find a separate present queue
|
||||
for (uint32_t i = 0; i < queueCount; ++i)
|
||||
{
|
||||
if (supportsPresent[i] == VK_TRUE)
|
||||
{
|
||||
presentQueueIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Exit if either a graphics or a presenting queue hasn't been found
|
||||
if (graphicsQueueIndex == UINT32_MAX
|
||||
|| presentQueueIndex == UINT32_MAX)
|
||||
{
|
||||
ERROR_RETURN("Could not find a graphics or presenting queue!");
|
||||
}
|
||||
|
||||
// TODO: Add support for separate graphics and presenting queue
|
||||
if (graphicsQueueIndex != presentQueueIndex)
|
||||
{
|
||||
ERROR_RETURN("Separate graphics and present queues not yet supported!");
|
||||
}
|
||||
|
||||
queueIndex = graphicsQueueIndex;
|
||||
|
||||
// Get list of supported surface formats
|
||||
uint32_t formatCount;
|
||||
err = vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface,
|
||||
&formatCount, NULL);
|
||||
assert(err == VK_SUCCESS);
|
||||
assert(formatCount > 0);
|
||||
|
||||
std::vector<VkSurfaceFormatKHR> surfaceFormats(formatCount);
|
||||
err = vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface,
|
||||
&formatCount,
|
||||
surfaceFormats.data());
|
||||
assert(err == VK_SUCCESS);
|
||||
|
||||
// If the surface format list only includes one entry with
|
||||
// VK_FORMAT_UNDEFINED, there is no preferred format.
|
||||
// Assume VK_FORMAT_B8G8R8A8_RGB.
|
||||
// TODO: Consider passing in desired format from app.
|
||||
if ((formatCount == 1) && (surfaceFormats[0].format == VK_FORMAT_UNDEFINED))
|
||||
{
|
||||
colorFormat = VK_FORMAT_B8G8R8A8_SRGB;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(formatCount >= 1);
|
||||
uint32_t i;
|
||||
for (i = 0; i < formatCount; i++) {
|
||||
if (surfaceFormats[i].format == VK_FORMAT_B8G8R8A8_SRGB) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == formatCount) {
|
||||
// Pick the first available, if no SRGB.
|
||||
// FIXME probably should raise an error...
|
||||
i = 0;
|
||||
}
|
||||
colorFormat = surfaceFormats[i].format;
|
||||
}
|
||||
colorSpace = surfaceFormats[0].colorSpace;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Connect to the instance and device and get all required function pointers
|
||||
bool
|
||||
VulkanSwapchain::connectDevice(VkDevice targetDevice)
|
||||
{
|
||||
this->device = targetDevice;
|
||||
#if USE_FUNCPTRS_FOR_KHR_EXTS
|
||||
GET_DEVICE_PROC_ADDR(device, CreateSwapchainKHR);
|
||||
GET_DEVICE_PROC_ADDR(device, DestroySwapchainKHR);
|
||||
GET_DEVICE_PROC_ADDR(device, GetSwapchainImagesKHR);
|
||||
GET_DEVICE_PROC_ADDR(device, AcquireNextImageKHR);
|
||||
GET_DEVICE_PROC_ADDR(device, QueuePresentKHR);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Connect to the instance and device and get all required function pointers
|
||||
bool
|
||||
VulkanSwapchain::connectInstance(VkInstance targetInstance,
|
||||
VkPhysicalDevice targetPhysicalDevice)
|
||||
{
|
||||
this->instance = targetInstance;
|
||||
this->physicalDevice = targetPhysicalDevice;
|
||||
#if USE_FUNCPTRS_FOR_KHR_EXTS
|
||||
GET_INSTANCE_PROC_ADDR(instance, GetPhysicalDeviceSurfaceSupportKHR);
|
||||
GET_INSTANCE_PROC_ADDR(instance, GetPhysicalDeviceSurfaceCapabilitiesKHR);
|
||||
GET_INSTANCE_PROC_ADDR(instance, GetPhysicalDeviceSurfaceFormatsKHR);
|
||||
GET_INSTANCE_PROC_ADDR(instance, GetPhysicalDeviceSurfacePresentModesKHR);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Create the swap chain and get images with given width and height
|
||||
void
|
||||
VulkanSwapchain::create(uint32_t *width, uint32_t *height,
|
||||
bool vsync)
|
||||
{
|
||||
U_ASSERT_ONLY VkResult err;
|
||||
VkSwapchainKHR oldSwapchain = swapchain;
|
||||
|
||||
// Get physical device surface properties and formats
|
||||
VkSurfaceCapabilitiesKHR surfCaps;
|
||||
err = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface,
|
||||
&surfCaps);
|
||||
assert(err == VK_SUCCESS);
|
||||
|
||||
// Get available present modes
|
||||
uint32_t presentModeCount;
|
||||
err = vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface,
|
||||
&presentModeCount, NULL);
|
||||
assert(err == VK_SUCCESS);
|
||||
assert(presentModeCount > 0);
|
||||
|
||||
std::vector<VkPresentModeKHR> presentModes(presentModeCount);
|
||||
|
||||
err = vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface,
|
||||
&presentModeCount,
|
||||
presentModes.data());
|
||||
assert(err == VK_SUCCESS);
|
||||
|
||||
VkExtent2D swapchainExtent = {};
|
||||
// width and height are either both 0xFFFFFFFF, or both not 0xFFFFFFFF.
|
||||
if (surfCaps.currentExtent.width == UINT32_MAX)
|
||||
{
|
||||
// If the surface size is undefined, the size is set to
|
||||
// the size of the images requested.
|
||||
swapchainExtent.width = *width;
|
||||
swapchainExtent.height = *height;
|
||||
}
|
||||
else
|
||||
{
|
||||
swapchainExtent = surfCaps.currentExtent;
|
||||
*width = surfCaps.currentExtent.width;
|
||||
*height = surfCaps.currentExtent.height;
|
||||
}
|
||||
|
||||
|
||||
// Select a present mode for the swapchain
|
||||
|
||||
// The VK_PRESENT_MODE_FIFO_KHR mode must always be present as per spec
|
||||
// This mode waits for the vertical blank ("v-sync")
|
||||
VkPresentModeKHR swapchainPresentMode = VK_PRESENT_MODE_FIFO_KHR;
|
||||
|
||||
// If v-sync is not requested, try to find a mailbox mode if present
|
||||
// It's the lowest latency non-tearing present mode available
|
||||
if (!vsync)
|
||||
{
|
||||
for (size_t i = 0; i < presentModeCount; i++)
|
||||
{
|
||||
if (presentModes[i] == VK_PRESENT_MODE_MAILBOX_KHR)
|
||||
{
|
||||
swapchainPresentMode = VK_PRESENT_MODE_MAILBOX_KHR;
|
||||
break;
|
||||
}
|
||||
if ((swapchainPresentMode != VK_PRESENT_MODE_MAILBOX_KHR)
|
||||
&& (presentModes[i] == VK_PRESENT_MODE_IMMEDIATE_KHR))
|
||||
{
|
||||
swapchainPresentMode = VK_PRESENT_MODE_IMMEDIATE_KHR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Determine the number of images
|
||||
uint32_t desiredNumberOfSwapchainImages = surfCaps.minImageCount + 1;
|
||||
if ((surfCaps.maxImageCount > 0)
|
||||
&& (desiredNumberOfSwapchainImages > surfCaps.maxImageCount))
|
||||
{
|
||||
desiredNumberOfSwapchainImages = surfCaps.maxImageCount;
|
||||
}
|
||||
|
||||
VkSurfaceTransformFlagsKHR preTransform;
|
||||
if (surfCaps.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) {
|
||||
preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
|
||||
} else {
|
||||
preTransform = surfCaps.currentTransform;
|
||||
}
|
||||
|
||||
VkSwapchainCreateInfoKHR swapchainCI = {};
|
||||
swapchainCI.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
|
||||
swapchainCI.pNext = NULL;
|
||||
swapchainCI.surface = surface;
|
||||
swapchainCI.minImageCount = desiredNumberOfSwapchainImages;
|
||||
swapchainCI.imageFormat = colorFormat;
|
||||
swapchainCI.imageColorSpace = colorSpace;
|
||||
swapchainCI.imageExtent = { swapchainExtent.width, swapchainExtent.height };
|
||||
swapchainCI.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
||||
swapchainCI.preTransform = (VkSurfaceTransformFlagBitsKHR)preTransform;
|
||||
swapchainCI.imageArrayLayers = 1;
|
||||
swapchainCI.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
swapchainCI.queueFamilyIndexCount = 0;
|
||||
swapchainCI.pQueueFamilyIndices = NULL;
|
||||
swapchainCI.presentMode = swapchainPresentMode;
|
||||
swapchainCI.oldSwapchain = oldSwapchain;
|
||||
swapchainCI.clipped = true;
|
||||
swapchainCI.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
|
||||
|
||||
err = vkCreateSwapchainKHR(device, &swapchainCI, nullptr, &swapchain);
|
||||
assert(err == VK_SUCCESS);
|
||||
|
||||
// If an existing swap chain is re-created, destroy the old swap chain
|
||||
// This also cleans up all the presentable images
|
||||
if (oldSwapchain != VK_NULL_HANDLE)
|
||||
{
|
||||
for (uint32_t i = 0; i < imageCount; i++)
|
||||
{
|
||||
vkDestroyImageView(device, buffers[i].view, nullptr);
|
||||
}
|
||||
vkDestroySwapchainKHR(device, oldSwapchain, nullptr);
|
||||
}
|
||||
|
||||
err = vkGetSwapchainImagesKHR(device, swapchain, &imageCount, NULL);
|
||||
assert(err == VK_SUCCESS);
|
||||
|
||||
// Get the swap chain images
|
||||
images.resize(imageCount);
|
||||
err = vkGetSwapchainImagesKHR(device, swapchain,
|
||||
&imageCount, images.data());
|
||||
assert(err == VK_SUCCESS);
|
||||
|
||||
// Get the swap chain buffers containing the image and imageview
|
||||
buffers.resize(imageCount);
|
||||
for (uint32_t i = 0; i < imageCount; i++)
|
||||
{
|
||||
VkImageViewCreateInfo colorAttachmentView = {};
|
||||
colorAttachmentView.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||
colorAttachmentView.pNext = NULL;
|
||||
colorAttachmentView.format = colorFormat;
|
||||
colorAttachmentView.components = {
|
||||
VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
VK_COMPONENT_SWIZZLE_IDENTITY
|
||||
};
|
||||
colorAttachmentView.subresourceRange.aspectMask
|
||||
= VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
colorAttachmentView.subresourceRange.baseMipLevel = 0;
|
||||
colorAttachmentView.subresourceRange.levelCount = 1;
|
||||
colorAttachmentView.subresourceRange.baseArrayLayer = 0;
|
||||
colorAttachmentView.subresourceRange.layerCount = 1;
|
||||
colorAttachmentView.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||
colorAttachmentView.flags = 0;
|
||||
|
||||
buffers[i].image = images[i];
|
||||
|
||||
colorAttachmentView.image = buffers[i].image;
|
||||
|
||||
err = vkCreateImageView(device, &colorAttachmentView, nullptr,
|
||||
&buffers[i].view);
|
||||
assert(err == VK_SUCCESS);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Acquires the next image in the swap chain
|
||||
VkResult
|
||||
VulkanSwapchain::acquireNextImage(VkSemaphore presentCompleteSemaphore,
|
||||
uint32_t *currentBuffer)
|
||||
{
|
||||
return vkAcquireNextImageKHR(device, swapchain, UINT64_MAX,
|
||||
presentCompleteSemaphore,
|
||||
(VkFence)nullptr,
|
||||
currentBuffer);
|
||||
}
|
||||
|
||||
|
||||
// Present the current image to the queue
|
||||
VkResult
|
||||
VulkanSwapchain::queuePresent(VkQueue queue, uint32_t currentBuffer)
|
||||
{
|
||||
VkPresentInfoKHR presentInfo = {};
|
||||
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
|
||||
presentInfo.pNext = NULL;
|
||||
presentInfo.swapchainCount = 1;
|
||||
presentInfo.pSwapchains = &swapchain;
|
||||
presentInfo.pImageIndices = ¤tBuffer;
|
||||
return vkQueuePresentKHR(queue, &presentInfo);
|
||||
}
|
||||
|
||||
|
||||
// Present the current image to the queue when semaphore signaled.
|
||||
VkResult
|
||||
VulkanSwapchain::queuePresent(VkQueue queue, uint32_t currentBuffer,
|
||||
VkSemaphore waitSemaphore)
|
||||
{
|
||||
VkPresentInfoKHR presentInfo = {};
|
||||
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
|
||||
presentInfo.pNext = NULL;
|
||||
presentInfo.swapchainCount = 1;
|
||||
presentInfo.pSwapchains = &swapchain;
|
||||
presentInfo.pImageIndices = ¤tBuffer;
|
||||
if (waitSemaphore != VK_NULL_HANDLE)
|
||||
{
|
||||
presentInfo.pWaitSemaphores = &waitSemaphore;
|
||||
presentInfo.waitSemaphoreCount = 1;
|
||||
}
|
||||
return vkQueuePresentKHR(queue, &presentInfo);
|
||||
}
|
||||
|
||||
|
||||
// Free all Vulkan resources used by the swap chain
|
||||
void
|
||||
VulkanSwapchain::cleanup()
|
||||
{
|
||||
for (uint32_t i = 0; i < imageCount; i++)
|
||||
{
|
||||
vkDestroyImageView(device, buffers[i].view, nullptr);
|
||||
}
|
||||
vkDestroySwapchainKHR(device, swapchain, nullptr);
|
||||
vkDestroySurfaceKHR(instance, surface, nullptr);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
/* -*- tab-width: 4; -*- */
|
||||
/* vi: set sw=2 ts=4 expandtab: */
|
||||
|
||||
/*
|
||||
* Copyright 2017-2020 Mark Callow.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
|
||||
#if !defined(USE_FUNCPTRS_FOR_KHR_EXTS)
|
||||
#define USE_FUNCPTRS_FOR_KHR_EXTS 0
|
||||
#endif
|
||||
|
||||
typedef struct _SwapchainBuffers {
|
||||
VkImage image;
|
||||
VkImageView view;
|
||||
} SwapchainBuffer;
|
||||
|
||||
class VulkanSwapchain
|
||||
{
|
||||
public:
|
||||
VkFormat colorFormat;
|
||||
VkColorSpaceKHR colorSpace;
|
||||
|
||||
VkSwapchainKHR swapchain = VK_NULL_HANDLE;
|
||||
|
||||
uint32_t imageCount;
|
||||
std::vector<VkImage> images;
|
||||
std::vector<SwapchainBuffer> buffers;
|
||||
|
||||
// Index of the detected graphics- and present-capable device queue.
|
||||
uint32_t queueIndex = UINT32_MAX;
|
||||
|
||||
// Creates an OS specific surface.
|
||||
// Looks for a graphics and a present queue
|
||||
bool initSurface(struct SDL_Window* window);
|
||||
|
||||
// Connect to device and get required device function pointers.
|
||||
bool connectDevice(VkDevice device);
|
||||
|
||||
// Connect to instance and get required instance function pointers.
|
||||
bool connectInstance(VkInstance instance,
|
||||
VkPhysicalDevice physicalDevice);
|
||||
|
||||
// Create the swap chain and get images with given width and height
|
||||
void create(uint32_t *width, uint32_t *height,
|
||||
bool vsync = false);
|
||||
|
||||
// Acquires the next image in the swap chain
|
||||
VkResult acquireNextImage(VkSemaphore presentCompleteSemaphore,
|
||||
uint32_t *currentBuffer);
|
||||
|
||||
// Present the current image to the queue
|
||||
VkResult queuePresent(VkQueue queue, uint32_t currentBuffer);
|
||||
|
||||
// Present the current image to the queue
|
||||
VkResult queuePresent(VkQueue queue, uint32_t currentBuffer,
|
||||
VkSemaphore waitSemaphore);
|
||||
|
||||
|
||||
// Free all Vulkan resources used by the swap chain
|
||||
void cleanup();
|
||||
|
||||
private:
|
||||
VkInstance instance;
|
||||
VkDevice device;
|
||||
VkPhysicalDevice physicalDevice;
|
||||
VkSurfaceKHR surface;
|
||||
#if USE_FUNCPTRS_FOR_KHR_EXTS
|
||||
PFN_vkGetPhysicalDeviceSurfaceSupportKHR
|
||||
pfnGetPhysicalDeviceSurfaceSupportKHR;
|
||||
PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR
|
||||
pfnGetPhysicalDeviceSurfaceCapabilitiesKHR;
|
||||
PFN_vkGetPhysicalDeviceSurfaceFormatsKHR
|
||||
pfnGetPhysicalDeviceSurfaceFormatsKHR;
|
||||
PFN_vkGetPhysicalDeviceSurfacePresentModesKHR
|
||||
pfnGetPhysicalDeviceSurfacePresentModesKHR;
|
||||
|
||||
PFN_vkCreateSwapchainKHR pfnCreateSwapchainKHR;
|
||||
PFN_vkDestroySwapchainKHR pfnDestroySwapchainKHR;
|
||||
PFN_vkGetSwapchainImagesKHR pfnGetSwapchainImagesKHR;
|
||||
PFN_vkAcquireNextImageKHR pfnAcquireNextImageKHR;
|
||||
PFN_vkQueuePresentKHR pfnQueuePresentKHR;
|
||||
|
||||
#define vkGetPhysicalDeviceSurfaceSupportKHR \
|
||||
pfnGetPhysicalDeviceSurfaceSupportKHR
|
||||
#define vkGetPhysicalDeviceSurfaceCapabilitiesKHR \
|
||||
pfnGetPhysicalDeviceSurfaceCapabilitiesKHR
|
||||
#define vkGetPhysicalDeviceSurfaceFormatsKHR \
|
||||
pfnGetPhysicalDeviceSurfaceFormatsKHR
|
||||
#define vkGetPhysicalDeviceSurfacePresentModesKHR \
|
||||
pfnGetPhysicalDeviceSurfacePresentModesKHR
|
||||
|
||||
#define vkCreateSwapchainKHR pfnCreateSwapchainKHR
|
||||
#define vkDestroySwapchainKHR pfnDestroySwapchainKHR
|
||||
#define vkGetSwapchainImagesKHR pfnGetSwapchainImagesKHR
|
||||
#define vkAcquireNextImageKHR pfnAcquireNextImageKHR
|
||||
#define vkQueuePresentKHR pfnQueuePresentKHR
|
||||
#endif
|
||||
};
|
||||
@@ -0,0 +1,8 @@
|
||||
<!-- Copyright 2024 Mark Callow -->
|
||||
<!-- SPDX-License-Identifier: Apache-2.0 -->
|
||||
|
||||
# Vulkan ICD and Explicit Layer Manifest Files
|
||||
|
||||
These were copied from `macOS/share/vulkan` in the Vulkan SDK and have been
|
||||
modified for inclusion in a macOS application bundle to cause the Vulkan loader
|
||||
to find the libraries within the bundle.
|
||||
+1709
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"file_format_version": "1.0.0",
|
||||
"ICD": {
|
||||
"library_path": "../../../Frameworks/libMoltenVK.dylib",
|
||||
"api_version": "1.2.0",
|
||||
"is_portability_driver": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright 2017-2020 Mark Callow
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#version 450 core
|
||||
|
||||
layout (location = 0) in vec2 inUV;
|
||||
|
||||
layout (binding = 0) uniform sampler2D samplerFont;
|
||||
|
||||
layout (location = 0) out vec4 outFragColor;
|
||||
|
||||
void main(void)
|
||||
{
|
||||
float color = texture(samplerFont, inUV).r;
|
||||
outFragColor = vec4(vec3(color), 1.0);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright 2017-2020 Mark Callow
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#version 450 core
|
||||
|
||||
layout (location = 0) in vec2 inPos;
|
||||
layout (location = 1) in vec2 inUV;
|
||||
|
||||
layout (location = 0) out vec2 outUV;
|
||||
|
||||
out gl_PerVertex
|
||||
{
|
||||
vec4 gl_Position;
|
||||
};
|
||||
|
||||
void main(void)
|
||||
{
|
||||
gl_Position = vec4(inPos, 0.0, 1.0);
|
||||
outUV = inUV;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/* -*- tab-width: 4; -*- */
|
||||
/* vi: set sw=2 ts=4 expandtab: */
|
||||
|
||||
#ifndef VULKAN_CHECK_RES_H_1456211188
|
||||
#define VULKAN_CHECK_RES_H_1456211188
|
||||
|
||||
/*
|
||||
* Copyright 2017-2020 Mark Callow.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
|
||||
#include <SDL3/SDL_messagebox.h>
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @file
|
||||
* @~English
|
||||
*
|
||||
* @brief Check result of a Vulkan command.
|
||||
*
|
||||
* Use for commands that will always succeed unless usage is invalid.
|
||||
*/
|
||||
#if defined(DEBUG)
|
||||
#define VK_CHECK_RESULT(f) \
|
||||
{ \
|
||||
VkResult res = (f); \
|
||||
if (res != VK_SUCCESS) \
|
||||
{ \
|
||||
std::stringstream msg; \
|
||||
msg << "Fatal : VkResult is \"" << res << "\" in " << __FILE__ \
|
||||
<< " at line " << __LINE__ << std::endl; \
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, \
|
||||
"VkSample_02_cube_textured", \
|
||||
msg.str().c_str(), \
|
||||
NULL); \
|
||||
assert(res == VK_SUCCESS); \
|
||||
} \
|
||||
}
|
||||
#else
|
||||
#define VK_CHECK_RESULT(f) (void)f
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,288 @@
|
||||
/*
|
||||
* Vulkan examples debug wrapper
|
||||
*
|
||||
* Appendix for VK_EXT_Debug_Report can be found at https://github.com/KhronosGroup/Vulkan-Docs/blob/1.0-VK_EXT_debug_report/doc/specs/vulkan/appendices/debug_report.txt
|
||||
*
|
||||
* Copyright 2016 Sascha Willems - www.saschawillems.de
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "vulkandebug.h"
|
||||
#include <iostream>
|
||||
#include "unused.h"
|
||||
|
||||
namespace vkDebug
|
||||
{
|
||||
int validationLayerCount = 1;
|
||||
const char *validationLayerNames[] =
|
||||
{
|
||||
// This is a meta layer that enables all of the standard
|
||||
// validation layers in the correct order :
|
||||
// threading, parameter_validation, device_limits, object_tracker, image, core_validation, swapchain, and unique_objects
|
||||
"VK_LAYER_LUNARG_standard_validation"
|
||||
};
|
||||
|
||||
PFN_vkCreateDebugReportCallbackEXT CreateDebugReportCallback = VK_NULL_HANDLE;
|
||||
PFN_vkDestroyDebugReportCallbackEXT DestroyDebugReportCallback = VK_NULL_HANDLE;
|
||||
PFN_vkDebugReportMessageEXT dbgBreakCallback = VK_NULL_HANDLE;
|
||||
|
||||
VkDebugReportCallbackEXT msgCallback;
|
||||
|
||||
VkBool32 messageCallback(
|
||||
VkDebugReportFlagsEXT flags,
|
||||
VkDebugReportObjectTypeEXT /*objType*/,
|
||||
uint64_t /*srcObject*/,
|
||||
size_t /*location*/,
|
||||
int32_t msgCode,
|
||||
const char* pLayerPrefix,
|
||||
const char* pMsg,
|
||||
void* /*pUserData*/)
|
||||
{
|
||||
// Message text passed in by validation layer
|
||||
std::string text(pMsg);
|
||||
|
||||
// Select prefix depending on flags passed to the callback
|
||||
// Note that multiple flags may be set for a single validation message
|
||||
std::string prefix("");
|
||||
|
||||
// Error that may result in undefined behaviour
|
||||
if (flags & VK_DEBUG_REPORT_ERROR_BIT_EXT)
|
||||
{
|
||||
prefix += "ERROR:";
|
||||
};
|
||||
// Warnings may hint at unexpected / non-spec API usage
|
||||
if (flags & VK_DEBUG_REPORT_WARNING_BIT_EXT)
|
||||
{
|
||||
prefix += "WARNING:";
|
||||
};
|
||||
// May indicate sub-optimal usage of the API
|
||||
if (flags & VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT)
|
||||
{
|
||||
prefix += "PERFORMANCE:";
|
||||
};
|
||||
// Informal messages that may become handy during debugging
|
||||
if (flags & VK_DEBUG_REPORT_INFORMATION_BIT_EXT)
|
||||
{
|
||||
prefix += "INFO:";
|
||||
}
|
||||
// Diagnostic info from the Vulkan loader and layers
|
||||
// Usually not helpful in terms of API usage, but may help to debug layer and loader problems
|
||||
if (flags & VK_DEBUG_REPORT_DEBUG_BIT_EXT)
|
||||
{
|
||||
prefix += "DEBUG:";
|
||||
}
|
||||
|
||||
// Display message to default output (console if activated)
|
||||
std::cout << prefix << " [" << pLayerPrefix << "] Code " << msgCode << " : " << pMsg << "\n";
|
||||
|
||||
fflush(stdout);
|
||||
|
||||
// The return value of this callback controls wether the Vulkan call that caused
|
||||
// the validation message will be aborted or not
|
||||
// We return VK_FALSE as we DON'T want Vulkan calls that cause a validation message
|
||||
// (and return a VkResult) to abort
|
||||
// If you instead want to have calls abort, pass in VK_TRUE and the function will
|
||||
// return VK_ERROR_VALIDATION_FAILED_EXT
|
||||
return VK_FALSE;
|
||||
}
|
||||
|
||||
void setupDebugging(VkInstance instance, VkDebugReportFlagsEXT flags, VkDebugReportCallbackEXT callBack)
|
||||
{
|
||||
CreateDebugReportCallback = (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr(instance, "vkCreateDebugReportCallbackEXT");
|
||||
DestroyDebugReportCallback = (PFN_vkDestroyDebugReportCallbackEXT)vkGetInstanceProcAddr(instance, "vkDestroyDebugReportCallbackEXT");
|
||||
dbgBreakCallback = (PFN_vkDebugReportMessageEXT)vkGetInstanceProcAddr(instance, "vkDebugReportMessageEXT");
|
||||
|
||||
VkDebugReportCallbackCreateInfoEXT dbgCreateInfo = {};
|
||||
dbgCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT;
|
||||
dbgCreateInfo.pfnCallback = (PFN_vkDebugReportCallbackEXT)messageCallback;
|
||||
dbgCreateInfo.flags = flags;
|
||||
|
||||
U_ASSERT_ONLY VkResult err = CreateDebugReportCallback(
|
||||
instance,
|
||||
&dbgCreateInfo,
|
||||
nullptr,
|
||||
(callBack != VK_NULL_HANDLE) ? &callBack : &msgCallback);
|
||||
assert(!err);
|
||||
(void)err; // Supress VC++ unused variable warning for Release config.
|
||||
}
|
||||
|
||||
void freeDebugCallback(VkInstance instance)
|
||||
{
|
||||
if (msgCallback != VK_NULL_HANDLE)
|
||||
{
|
||||
DestroyDebugReportCallback(instance, msgCallback, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
namespace DebugMarker
|
||||
{
|
||||
bool active = false;
|
||||
|
||||
PFN_vkDebugMarkerSetObjectTagEXT pfnDebugMarkerSetObjectTag = VK_NULL_HANDLE;
|
||||
PFN_vkDebugMarkerSetObjectNameEXT pfnDebugMarkerSetObjectName = VK_NULL_HANDLE;
|
||||
PFN_vkCmdDebugMarkerBeginEXT pfnCmdDebugMarkerBegin = VK_NULL_HANDLE;
|
||||
PFN_vkCmdDebugMarkerEndEXT pfnCmdDebugMarkerEnd = VK_NULL_HANDLE;
|
||||
PFN_vkCmdDebugMarkerInsertEXT pfnCmdDebugMarkerInsert = VK_NULL_HANDLE;
|
||||
|
||||
void setup(VkDevice device)
|
||||
{
|
||||
pfnDebugMarkerSetObjectTag = (PFN_vkDebugMarkerSetObjectTagEXT)vkGetDeviceProcAddr(device, "vkDebugMarkerSetObjectTagEXT");
|
||||
pfnDebugMarkerSetObjectName = (PFN_vkDebugMarkerSetObjectNameEXT)vkGetDeviceProcAddr(device, "vkDebugMarkerSetObjectNameEXT");
|
||||
pfnCmdDebugMarkerBegin = (PFN_vkCmdDebugMarkerBeginEXT)vkGetDeviceProcAddr(device, "vkCmdDebugMarkerBeginEXT");
|
||||
pfnCmdDebugMarkerEnd = (PFN_vkCmdDebugMarkerEndEXT)vkGetDeviceProcAddr(device, "vkCmdDebugMarkerEndEXT");
|
||||
pfnCmdDebugMarkerInsert = (PFN_vkCmdDebugMarkerInsertEXT)vkGetDeviceProcAddr(device, "vkCmdDebugMarkerInsertEXT");
|
||||
|
||||
// Set flag if at least one function pointer is present
|
||||
active = (pfnDebugMarkerSetObjectName != VK_NULL_HANDLE);
|
||||
}
|
||||
|
||||
void setObjectName(VkDevice device, uint64_t object, VkDebugReportObjectTypeEXT objectType, const char *name)
|
||||
{
|
||||
// Check for valid function pointer (may not be present if not running in a debugging application)
|
||||
if (pfnDebugMarkerSetObjectName)
|
||||
{
|
||||
VkDebugMarkerObjectNameInfoEXT nameInfo = {};
|
||||
nameInfo.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_OBJECT_NAME_INFO_EXT;
|
||||
nameInfo.objectType = objectType;
|
||||
nameInfo.object = object;
|
||||
nameInfo.pObjectName = name;
|
||||
pfnDebugMarkerSetObjectName(device, &nameInfo);
|
||||
}
|
||||
}
|
||||
|
||||
void setObjectTag(VkDevice device, uint64_t object, VkDebugReportObjectTypeEXT objectType, uint64_t name, size_t tagSize, const void* tag)
|
||||
{
|
||||
// Check for valid function pointer (may not be present if not running in a debugging application)
|
||||
if (pfnDebugMarkerSetObjectTag)
|
||||
{
|
||||
VkDebugMarkerObjectTagInfoEXT tagInfo = {};
|
||||
tagInfo.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_OBJECT_TAG_INFO_EXT;
|
||||
tagInfo.objectType = objectType;
|
||||
tagInfo.object = object;
|
||||
tagInfo.tagName = name;
|
||||
tagInfo.tagSize = tagSize;
|
||||
tagInfo.pTag = tag;
|
||||
pfnDebugMarkerSetObjectTag(device, &tagInfo);
|
||||
}
|
||||
}
|
||||
|
||||
void beginRegion(VkCommandBuffer cmdbuffer, const char* pMarkerName, glm::vec4 color)
|
||||
{
|
||||
// Check for valid function pointer (may not be present if not running in a debugging application)
|
||||
if (pfnCmdDebugMarkerBegin)
|
||||
{
|
||||
VkDebugMarkerMarkerInfoEXT markerInfo = {};
|
||||
markerInfo.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT;
|
||||
memcpy(markerInfo.color, &color[0], sizeof(float) * 4);
|
||||
markerInfo.pMarkerName = pMarkerName;
|
||||
pfnCmdDebugMarkerBegin(cmdbuffer, &markerInfo);
|
||||
}
|
||||
}
|
||||
|
||||
void insert(VkCommandBuffer cmdbuffer, std::string markerName, glm::vec4 color)
|
||||
{
|
||||
// Check for valid function pointer (may not be present if not running in a debugging application)
|
||||
if (pfnCmdDebugMarkerInsert)
|
||||
{
|
||||
VkDebugMarkerMarkerInfoEXT markerInfo = {};
|
||||
markerInfo.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT;
|
||||
memcpy(markerInfo.color, &color[0], sizeof(float) * 4);
|
||||
markerInfo.pMarkerName = markerName.c_str();
|
||||
pfnCmdDebugMarkerInsert(cmdbuffer, &markerInfo);
|
||||
}
|
||||
}
|
||||
|
||||
void endRegion(VkCommandBuffer cmdBuffer)
|
||||
{
|
||||
// Check for valid function (may not be present if not runnin in a debugging application)
|
||||
if (pfnCmdDebugMarkerEnd)
|
||||
{
|
||||
pfnCmdDebugMarkerEnd(cmdBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
void setCommandBufferName(VkDevice device, VkCommandBuffer cmdBuffer, const char * name)
|
||||
{
|
||||
setObjectName(device, (uint64_t)cmdBuffer, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, name);
|
||||
}
|
||||
|
||||
void setQueueName(VkDevice device, VkQueue queue, const char * name)
|
||||
{
|
||||
setObjectName(device, (uint64_t)queue, VK_DEBUG_REPORT_OBJECT_TYPE_QUEUE_EXT, name);
|
||||
}
|
||||
|
||||
void setImageName(VkDevice device, VkImage image, const char * name)
|
||||
{
|
||||
setObjectName(device, (uint64_t)image, VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT, name);
|
||||
}
|
||||
|
||||
void setSamplerName(VkDevice device, VkSampler sampler, const char * name)
|
||||
{
|
||||
setObjectName(device, (uint64_t)sampler, VK_DEBUG_REPORT_OBJECT_TYPE_SAMPLER_EXT, name);
|
||||
}
|
||||
|
||||
void setBufferName(VkDevice device, VkBuffer buffer, const char * name)
|
||||
{
|
||||
setObjectName(device, (uint64_t)buffer, VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_EXT, name);
|
||||
}
|
||||
|
||||
void setDeviceMemoryName(VkDevice device, VkDeviceMemory memory, const char * name)
|
||||
{
|
||||
setObjectName(device, (uint64_t)memory, VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_MEMORY_EXT, name);
|
||||
}
|
||||
|
||||
void setShaderModuleName(VkDevice device, VkShaderModule shaderModule, const char * name)
|
||||
{
|
||||
setObjectName(device, (uint64_t)shaderModule, VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT, name);
|
||||
}
|
||||
|
||||
void setPipelineName(VkDevice device, VkPipeline pipeline, const char * name)
|
||||
{
|
||||
setObjectName(device, (uint64_t)pipeline, VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT, name);
|
||||
}
|
||||
|
||||
void setPipelineLayoutName(VkDevice device, VkPipelineLayout pipelineLayout, const char * name)
|
||||
{
|
||||
setObjectName(device, (uint64_t)pipelineLayout, VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_LAYOUT_EXT, name);
|
||||
}
|
||||
|
||||
void setRenderPassName(VkDevice device, VkRenderPass renderPass, const char * name)
|
||||
{
|
||||
setObjectName(device, (uint64_t)renderPass, VK_DEBUG_REPORT_OBJECT_TYPE_RENDER_PASS_EXT, name);
|
||||
}
|
||||
|
||||
void setFramebufferName(VkDevice device, VkFramebuffer framebuffer, const char * name)
|
||||
{
|
||||
setObjectName(device, (uint64_t)framebuffer, VK_DEBUG_REPORT_OBJECT_TYPE_FRAMEBUFFER_EXT, name);
|
||||
}
|
||||
|
||||
void setDescriptorSetLayoutName(VkDevice device, VkDescriptorSetLayout descriptorSetLayout, const char * name)
|
||||
{
|
||||
setObjectName(device, (uint64_t)descriptorSetLayout, VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT_EXT, name);
|
||||
}
|
||||
|
||||
void setDescriptorSetName(VkDevice device, VkDescriptorSet descriptorSet, const char * name)
|
||||
{
|
||||
setObjectName(device, (uint64_t)descriptorSet, VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_EXT, name);
|
||||
}
|
||||
|
||||
void setSemaphoreName(VkDevice device, VkSemaphore semaphore, const char * name)
|
||||
{
|
||||
setObjectName(device, (uint64_t)semaphore, VK_DEBUG_REPORT_OBJECT_TYPE_SEMAPHORE_EXT, name);
|
||||
}
|
||||
|
||||
void setFenceName(VkDevice device, VkFence fence, const char * name)
|
||||
{
|
||||
setObjectName(device, (uint64_t)fence, VK_DEBUG_REPORT_OBJECT_TYPE_FENCE_EXT, name);
|
||||
}
|
||||
|
||||
void setEventName(VkDevice device, VkEvent _event, const char * name)
|
||||
{
|
||||
setObjectName(device, (uint64_t)_event, VK_DEBUG_REPORT_OBJECT_TYPE_EVENT_EXT, name);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// vi: set sw=2 ts=4 expandtab:
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright 2017-2020 Mark Callow
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "vulkan/vulkan.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <vector>
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <fcntl.h>
|
||||
#include <io.h>
|
||||
#endif
|
||||
|
||||
#define GLM_FORCE_RADIANS
|
||||
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
|
||||
#include "disable_glm_warnings.h"
|
||||
#include <glm/glm.hpp>
|
||||
#include "reenable_warnings.h"
|
||||
|
||||
namespace vkDebug
|
||||
{
|
||||
// Default validation layers
|
||||
extern int validationLayerCount;
|
||||
extern const char *validationLayerNames[];
|
||||
|
||||
// Default debug callback
|
||||
VkBool32 messageCallback(
|
||||
VkDebugReportFlagsEXT flags,
|
||||
VkDebugReportObjectTypeEXT objType,
|
||||
uint64_t srcObject,
|
||||
size_t location,
|
||||
int32_t msgCode,
|
||||
const char* pLayerPrefix,
|
||||
const char* pMsg,
|
||||
void* pUserData);
|
||||
|
||||
// Load debug function pointers and set debug callback
|
||||
// if callBack is NULL, default message callback will be used
|
||||
void setupDebugging(
|
||||
VkInstance instance,
|
||||
VkDebugReportFlagsEXT flags,
|
||||
VkDebugReportCallbackEXT callBack);
|
||||
// Clear debug callback
|
||||
void freeDebugCallback(VkInstance instance);
|
||||
|
||||
// Setup and functions for the VK_EXT_debug_marker_extension
|
||||
// Extension spec can be found at https://github.com/KhronosGroup/Vulkan-Docs/blob/1.0-VK_EXT_debug_marker/doc/specs/vulkan/appendices/VK_EXT_debug_marker.txt
|
||||
// Note that the extension will only be present if run from an offline debugging application
|
||||
// The actual check for extension presence and enabling it on the device is done in the example base class
|
||||
// See VulkanExampleBase::createInstance and VulkanExampleBase::createDevice (base/vulkanexamplebase.cpp)
|
||||
namespace DebugMarker
|
||||
{
|
||||
// Set to true if function pointer for the debug marker are available
|
||||
extern bool active;
|
||||
|
||||
// Get function pointers for the debug report extensions from the device
|
||||
void setup(VkDevice device);
|
||||
|
||||
// Sets the debug name of an object
|
||||
// All Objects in Vulkan are represented by their 64-bit handles which are passed into this function
|
||||
// along with the object type
|
||||
void setObjectName(VkDevice device, uint64_t object, VkDebugReportObjectTypeEXT objectType, const char *name);
|
||||
|
||||
// Set the tag for an object
|
||||
void setObjectTag(VkDevice device, uint64_t object, VkDebugReportObjectTypeEXT objectType, uint64_t name, size_t tagSize, const void* tag);
|
||||
|
||||
// Start a new debug marker region
|
||||
void beginRegion(VkCommandBuffer cmdbuffer, const char* pMarkerName, glm::vec4 color);
|
||||
|
||||
// Insert a new debug marker into the command buffer
|
||||
void insert(VkCommandBuffer cmdbuffer, std::string markerName, glm::vec4 color);
|
||||
|
||||
// End the current debug marker region
|
||||
void endRegion(VkCommandBuffer cmdBuffer);
|
||||
|
||||
// Object specific naming functions
|
||||
void setCommandBufferName(VkDevice device, VkCommandBuffer cmdBuffer, const char * name);
|
||||
void setQueueName(VkDevice device, VkQueue queue, const char * name);
|
||||
void setImageName(VkDevice device, VkImage image, const char * name);
|
||||
void setSamplerName(VkDevice device, VkSampler sampler, const char * name);
|
||||
void setBufferName(VkDevice device, VkBuffer buffer, const char * name);
|
||||
void setDeviceMemoryName(VkDevice device, VkDeviceMemory memory, const char * name);
|
||||
void setShaderModuleName(VkDevice device, VkShaderModule shaderModule, const char * name);
|
||||
void setPipelineName(VkDevice device, VkPipeline pipeline, const char * name);
|
||||
void setPipelineLayoutName(VkDevice device, VkPipelineLayout pipelineLayout, const char * name);
|
||||
void setRenderPassName(VkDevice device, VkRenderPass renderPass, const char * name);
|
||||
void setFramebufferName(VkDevice device, VkFramebuffer framebuffer, const char * name);
|
||||
void setDescriptorSetLayoutName(VkDevice device, VkDescriptorSetLayout descriptorSetLayout, const char * name);
|
||||
void setDescriptorSetName(VkDevice device, VkDescriptorSet descriptorSet, const char * name);
|
||||
void setSemaphoreName(VkDevice device, VkSemaphore semaphore, const char * name);
|
||||
void setFenceName(VkDevice device, VkFence fence, const char * name);
|
||||
void setEventName(VkDevice device, VkEvent _event, const char * name);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// vi: set sw=2 ts=4 expandtab:
|
||||
@@ -0,0 +1,839 @@
|
||||
/*
|
||||
* Copyright 2016 Sascha Willems - www.saschawillems.de
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Text overlay class for displaying debug information
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
#include "vulkantools.h"
|
||||
#include "vulkandebug.h"
|
||||
|
||||
#include "stb/stb_font_consolas_24_latin1.inl"
|
||||
|
||||
// Defines for the STB font used
|
||||
// STB font files can be found at http://nothings.org/stb/font/
|
||||
#define STB_FONT_NAME stb_font_consolas_24_latin1
|
||||
#define STB_FONT_WIDTH STB_FONT_consolas_24_latin1_BITMAP_WIDTH
|
||||
#define STB_FONT_HEIGHT STB_FONT_consolas_24_latin1_BITMAP_HEIGHT
|
||||
#define STB_FIRST_CHAR STB_FONT_consolas_24_latin1_FIRST_CHAR
|
||||
#define STB_NUM_CHARS STB_FONT_consolas_24_latin1_NUM_CHARS
|
||||
#define STB_MISSING_GLPYH 0x80 // Actually a control character.
|
||||
|
||||
// Max. number of chars the text overlay buffer can hold
|
||||
#define MAX_CHAR_COUNT 1024
|
||||
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @brief Given the lead byte of a UTF-8 sequence returns the expected length of the codepoint
|
||||
* @param[in] leadByte The lead byte of a UTF-8 sequence
|
||||
* @return The expected length of the codepoint */
|
||||
[[nodiscard]] constexpr inline int sequenceLength(uint8_t leadByte) noexcept {
|
||||
if ((leadByte & 0b1000'0000u) == 0b0000'0000u)
|
||||
return 1;
|
||||
if ((leadByte & 0b1110'0000u) == 0b1100'0000u)
|
||||
return 2;
|
||||
if ((leadByte & 0b1111'0000u) == 0b1110'0000u)
|
||||
return 3;
|
||||
if ((leadByte & 0b1111'1000u) == 0b1111'0000u)
|
||||
return 4;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @brief Checks if the codepoint was coded as a longer than required sequence
|
||||
* @param[in] codepoint The unicode codepoint
|
||||
* @param[in] length The UTF-8 sequence length
|
||||
* @return True if the sequence length was inappropriate for the given codepoint */
|
||||
[[nodiscard]] constexpr inline bool isOverlongSequence(uint32_t codepoint, int length) noexcept {
|
||||
if (codepoint < 0x80)
|
||||
return length != 1;
|
||||
else if (codepoint < 0x800)
|
||||
return length != 2;
|
||||
else if (codepoint < 0x10000)
|
||||
return length != 3;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @brief Checks if the codepoint is valid
|
||||
* @param[in] codepoint The unicode codepoint
|
||||
* @return True if the codepoint is a valid unicode codepoint */
|
||||
[[nodiscard]] constexpr inline bool isCodepointValid(uint32_t codepoint) noexcept {
|
||||
return codepoint <= 0x0010FFFFu
|
||||
&& !(0xD800u <= codepoint && codepoint <= 0xDBFFu);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @brief Safely checks and advances a UTF-8 sequence iterator to the start of the next unicode codepoint
|
||||
* @param[in] it iterator to be advanced
|
||||
* @param[in] end iterator pointing to the end of the range
|
||||
* @return True if the advance operation was successful and the advanced codepoint was a valid UTF-8 sequence */
|
||||
template <typename Iterator>
|
||||
[[nodiscard]] constexpr bool advanceUTF8(Iterator& it, Iterator end,
|
||||
uint32_t& codepoint) noexcept {
|
||||
if (it == end)
|
||||
return false;
|
||||
|
||||
const auto length = sequenceLength(*it);
|
||||
if (length == 0)
|
||||
return false;
|
||||
|
||||
if (std::distance(it, end) < length)
|
||||
return false;
|
||||
|
||||
for (int i = 1; i < length; ++i) {
|
||||
const auto trailByte = *(it + i);
|
||||
if ((static_cast<uint8_t>(trailByte) & 0b1100'0000u) != 0b1000'0000u)
|
||||
return false;
|
||||
}
|
||||
|
||||
codepoint = 0;
|
||||
switch (length) {
|
||||
case 1:
|
||||
codepoint |= *it++;
|
||||
break;
|
||||
case 2:
|
||||
codepoint |= (*it++ & 0b0001'1111u) << 6u;
|
||||
codepoint |= (*it++ & 0b0011'1111u);
|
||||
break;
|
||||
case 3:
|
||||
codepoint |= (*it++ & 0b0000'1111u) << 12u;
|
||||
codepoint |= (*it++ & 0b0011'1111u) << 6u;
|
||||
codepoint |= (*it++ & 0b0011'1111u);
|
||||
break;
|
||||
case 4:
|
||||
codepoint |= (*it++ & 0b0000'0111u) << 18u;
|
||||
codepoint |= (*it++ & 0b0011'1111u) << 12u;
|
||||
codepoint |= (*it++ & 0b0011'1111u) << 6u;
|
||||
codepoint |= (*it++ & 0b0011'1111u);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!isCodepointValid(codepoint))
|
||||
return false;
|
||||
|
||||
if (isOverlongSequence(codepoint, length))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Mostly self-contained text overlay class
|
||||
// todo : comment
|
||||
class VulkanTextOverlay
|
||||
{
|
||||
private:
|
||||
VkPhysicalDevice physicalDevice;
|
||||
VkDevice device;
|
||||
VkPhysicalDeviceMemoryProperties deviceMemoryProperties;
|
||||
VkQueue queue;
|
||||
VkFormat colorFormat;
|
||||
VkFormat depthFormat;
|
||||
|
||||
uint32_t *frameBufferWidth;
|
||||
uint32_t *frameBufferHeight;
|
||||
|
||||
VkSampler sampler;
|
||||
VkImage image;
|
||||
VkImageView view;
|
||||
VkBuffer buffer;
|
||||
VkDeviceMemory memory;
|
||||
VkDeviceMemory imageMemory;
|
||||
VkDescriptorPool descriptorPool;
|
||||
VkDescriptorSetLayout descriptorSetLayout;
|
||||
VkDescriptorSet descriptorSet;
|
||||
VkPipelineLayout pipelineLayout;
|
||||
VkPipelineCache pipelineCache;
|
||||
VkPipeline pipeline;
|
||||
VkRenderPass renderPass;
|
||||
VkCommandPool commandPool;
|
||||
std::vector<VkFramebuffer*> frameBuffers;
|
||||
std::vector<VkPipelineShaderStageCreateInfo> shaderStages;
|
||||
|
||||
// Pointer to mapped vertex buffer
|
||||
glm::vec4 *mapped = nullptr;
|
||||
// Used during text updates
|
||||
glm::vec4 *mappedLocal = nullptr;
|
||||
|
||||
stb_fontchar stbFontData[STB_NUM_CHARS];
|
||||
uint32_t numLetters;
|
||||
|
||||
// Try to find appropriate memory type for a memory allocation
|
||||
uint32_t getMemoryType(uint32_t typeBits, VkFlags properties)
|
||||
{
|
||||
for (uint32_t i = 0; i < 32; i++)
|
||||
{
|
||||
if ((typeBits & 1) == 1)
|
||||
{
|
||||
if ((deviceMemoryProperties.memoryTypes[i].propertyFlags & properties) == properties)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
typeBits >>= 1;
|
||||
}
|
||||
|
||||
// todo : throw error
|
||||
return 0;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
enum TextAlign { alignLeft, alignCenter, alignRight };
|
||||
|
||||
bool visible = true;
|
||||
bool invalidated = false;
|
||||
|
||||
std::vector<VkCommandBuffer> cmdBuffers;
|
||||
|
||||
VulkanTextOverlay(
|
||||
VkPhysicalDevice physicalDevice,
|
||||
VkDevice device,
|
||||
VkQueue queue,
|
||||
std::vector<VkFramebuffer> &framebuffers,
|
||||
VkFormat colorformat,
|
||||
VkFormat depthformat,
|
||||
uint32_t *framebufferwidth,
|
||||
uint32_t *framebufferheight,
|
||||
std::vector<VkPipelineShaderStageCreateInfo> shaderstages)
|
||||
{
|
||||
this->physicalDevice = physicalDevice;
|
||||
this->device = device;
|
||||
this->queue = queue;
|
||||
this->colorFormat = colorformat;
|
||||
this->depthFormat = depthformat;
|
||||
|
||||
this->frameBuffers.resize(framebuffers.size());
|
||||
for (uint32_t i = 0; i < framebuffers.size(); i++)
|
||||
{
|
||||
this->frameBuffers[i] = &framebuffers[i];
|
||||
}
|
||||
|
||||
this->shaderStages = shaderstages;
|
||||
|
||||
this->frameBufferWidth = framebufferwidth;
|
||||
this->frameBufferHeight = framebufferheight;
|
||||
|
||||
vkGetPhysicalDeviceMemoryProperties(physicalDevice, &deviceMemoryProperties);
|
||||
cmdBuffers.resize(framebuffers.size());
|
||||
prepareResources();
|
||||
prepareRenderPass();
|
||||
preparePipeline();
|
||||
}
|
||||
|
||||
~VulkanTextOverlay()
|
||||
{
|
||||
// Free up all Vulkan resources requested by the text overlay
|
||||
vkDestroySampler(device, sampler, nullptr);
|
||||
vkDestroyImage(device, image, nullptr);
|
||||
vkDestroyImageView(device, view, nullptr);
|
||||
vkDestroyBuffer(device, buffer, nullptr);
|
||||
vkFreeMemory(device, memory, nullptr);
|
||||
vkFreeMemory(device, imageMemory, nullptr);
|
||||
vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr);
|
||||
vkDestroyDescriptorPool(device, descriptorPool, nullptr);
|
||||
vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
|
||||
vkDestroyPipelineCache(device, pipelineCache, nullptr);
|
||||
vkDestroyPipeline(device, pipeline, nullptr);
|
||||
vkDestroyRenderPass(device, renderPass, nullptr);
|
||||
vkFreeCommandBuffers(device, commandPool, static_cast<uint32_t>(cmdBuffers.size()), cmdBuffers.data());
|
||||
vkDestroyCommandPool(device, commandPool, nullptr);
|
||||
}
|
||||
|
||||
// Prepare all vulkan resources required to render the font
|
||||
// The text overlay uses separate resources for descriptors (pool, sets, layouts), pipelines and command buffers
|
||||
void prepareResources()
|
||||
{
|
||||
static unsigned char font24pixels[STB_FONT_HEIGHT][STB_FONT_WIDTH];
|
||||
STB_FONT_NAME(stbFontData, font24pixels, STB_FONT_HEIGHT);
|
||||
|
||||
// Command buffer
|
||||
|
||||
// Pool
|
||||
VkCommandPoolCreateInfo cmdPoolInfo = {};
|
||||
cmdPoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
|
||||
cmdPoolInfo.queueFamilyIndex = 0; // todo : pass from example base / swap chain
|
||||
cmdPoolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
|
||||
VK_CHECK_RESULT(vkCreateCommandPool(device, &cmdPoolInfo, nullptr, &commandPool));
|
||||
|
||||
VkCommandBufferAllocateInfo cmdBufAllocateInfo =
|
||||
vkTools::initializers::commandBufferAllocateInfo(
|
||||
commandPool,
|
||||
VK_COMMAND_BUFFER_LEVEL_PRIMARY,
|
||||
(uint32_t)cmdBuffers.size());
|
||||
|
||||
VK_CHECK_RESULT(vkAllocateCommandBuffers(device, &cmdBufAllocateInfo, cmdBuffers.data()));
|
||||
|
||||
// Vertex buffer, 4 per character.
|
||||
VkDeviceSize bufferSize = MAX_CHAR_COUNT * sizeof(glm::vec4) * 4;
|
||||
|
||||
VkBufferCreateInfo bufferInfo = vkTools::initializers::bufferCreateInfo(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, bufferSize);
|
||||
VK_CHECK_RESULT(vkCreateBuffer(device, &bufferInfo, nullptr, &buffer));
|
||||
|
||||
VkMemoryRequirements memReqs;
|
||||
VkMemoryAllocateInfo allocInfo = vkTools::initializers::memoryAllocateInfo();
|
||||
|
||||
vkGetBufferMemoryRequirements(device, buffer, &memReqs);
|
||||
allocInfo.allocationSize = memReqs.size;
|
||||
allocInfo.memoryTypeIndex = getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
|
||||
VK_CHECK_RESULT(vkAllocateMemory(device, &allocInfo, nullptr, &memory));
|
||||
VK_CHECK_RESULT(vkBindBufferMemory(device, buffer, memory, 0));
|
||||
|
||||
// Map persistent
|
||||
VK_CHECK_RESULT(vkMapMemory(device, memory, 0, VK_WHOLE_SIZE, 0, (void **)&mapped));
|
||||
|
||||
|
||||
// Font texture
|
||||
VkImageCreateInfo imageInfo = vkTools::initializers::imageCreateInfo();
|
||||
imageInfo.imageType = VK_IMAGE_TYPE_2D;
|
||||
imageInfo.format = VK_FORMAT_R8_UNORM;
|
||||
imageInfo.extent.width = STB_FONT_WIDTH;
|
||||
imageInfo.extent.height = STB_FONT_HEIGHT;
|
||||
imageInfo.extent.depth = 1;
|
||||
imageInfo.mipLevels = 1;
|
||||
imageInfo.arrayLayers = 1;
|
||||
imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
|
||||
imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
|
||||
VK_CHECK_RESULT(vkCreateImage(device, &imageInfo, nullptr, &image));
|
||||
|
||||
vkGetImageMemoryRequirements(device, image, &memReqs);
|
||||
allocInfo.allocationSize = memReqs.size;
|
||||
allocInfo.memoryTypeIndex = getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
||||
VK_CHECK_RESULT(vkAllocateMemory(device, &allocInfo, nullptr, &imageMemory));
|
||||
VK_CHECK_RESULT(vkBindImageMemory(device, image, imageMemory, 0));
|
||||
|
||||
// Staging
|
||||
|
||||
struct {
|
||||
VkDeviceMemory memory;
|
||||
VkBuffer buffer;
|
||||
} stagingBuffer;
|
||||
|
||||
VkBufferCreateInfo bufferCreateInfo = vkTools::initializers::bufferCreateInfo();
|
||||
bufferCreateInfo.size = allocInfo.allocationSize;
|
||||
bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
|
||||
bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
|
||||
VK_CHECK_RESULT(vkCreateBuffer(device, &bufferCreateInfo, nullptr, &stagingBuffer.buffer));
|
||||
|
||||
// Get memory requirements for the staging buffer (alignment, memory type bits)
|
||||
vkGetBufferMemoryRequirements(device, stagingBuffer.buffer, &memReqs);
|
||||
|
||||
allocInfo.allocationSize = memReqs.size;
|
||||
// Get memory type index for a host visible buffer
|
||||
allocInfo.memoryTypeIndex = getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
|
||||
|
||||
VK_CHECK_RESULT(vkAllocateMemory(device, &allocInfo, nullptr, &stagingBuffer.memory));
|
||||
VK_CHECK_RESULT(vkBindBufferMemory(device, stagingBuffer.buffer, stagingBuffer.memory, 0));
|
||||
|
||||
uint8_t *data;
|
||||
VK_CHECK_RESULT(vkMapMemory(device, stagingBuffer.memory, 0, allocInfo.allocationSize, 0, (void **)&data));
|
||||
memcpy(data, &font24pixels[0][0], STB_FONT_WIDTH * STB_FONT_HEIGHT);
|
||||
vkUnmapMemory(device, stagingBuffer.memory);
|
||||
|
||||
// Copy to image
|
||||
|
||||
VkCommandBuffer copyCmd;
|
||||
cmdBufAllocateInfo.commandBufferCount = 1;
|
||||
VK_CHECK_RESULT(vkAllocateCommandBuffers(device, &cmdBufAllocateInfo, ©Cmd));
|
||||
|
||||
VkCommandBufferBeginInfo cmdBufInfo = vkTools::initializers::commandBufferBeginInfo();
|
||||
VK_CHECK_RESULT(vkBeginCommandBuffer(copyCmd, &cmdBufInfo));
|
||||
|
||||
// Prepare for transfer
|
||||
vkTools::setImageLayout(
|
||||
copyCmd,
|
||||
image,
|
||||
VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
VK_IMAGE_LAYOUT_PREINITIALIZED,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
||||
|
||||
VkBufferImageCopy bufferCopyRegion = {};
|
||||
bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
bufferCopyRegion.imageSubresource.mipLevel = 0;
|
||||
bufferCopyRegion.imageSubresource.layerCount = 1;
|
||||
bufferCopyRegion.imageExtent.width = STB_FONT_WIDTH;
|
||||
bufferCopyRegion.imageExtent.height = STB_FONT_HEIGHT;
|
||||
bufferCopyRegion.imageExtent.depth = 1;
|
||||
|
||||
vkCmdCopyBufferToImage(
|
||||
copyCmd,
|
||||
stagingBuffer.buffer,
|
||||
image,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
1,
|
||||
&bufferCopyRegion
|
||||
);
|
||||
|
||||
// Prepare for shader read
|
||||
vkTools::setImageLayout(
|
||||
copyCmd,
|
||||
image,
|
||||
VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
|
||||
VK_CHECK_RESULT(vkEndCommandBuffer(copyCmd));
|
||||
|
||||
VkSubmitInfo submitInfo = vkTools::initializers::submitInfo();
|
||||
submitInfo.commandBufferCount = 1;
|
||||
submitInfo.pCommandBuffers = ©Cmd;
|
||||
|
||||
VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE));
|
||||
VK_CHECK_RESULT(vkQueueWaitIdle(queue));
|
||||
|
||||
vkFreeCommandBuffers(device, commandPool, 1, ©Cmd);
|
||||
vkFreeMemory(device, stagingBuffer.memory, nullptr);
|
||||
vkDestroyBuffer(device, stagingBuffer.buffer, nullptr);
|
||||
|
||||
|
||||
VkImageViewCreateInfo imageViewInfo = vkTools::initializers::imageViewCreateInfo();
|
||||
imageViewInfo.image = image;
|
||||
imageViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||
imageViewInfo.format = imageInfo.format;
|
||||
imageViewInfo.components = {
|
||||
VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
VK_COMPONENT_SWIZZLE_IDENTITY
|
||||
};
|
||||
imageViewInfo.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
|
||||
|
||||
VK_CHECK_RESULT(vkCreateImageView(device, &imageViewInfo, nullptr, &view));
|
||||
|
||||
// Sampler
|
||||
VkSamplerCreateInfo samplerInfo = vkTools::initializers::samplerCreateInfo();
|
||||
samplerInfo.magFilter = VK_FILTER_LINEAR;
|
||||
samplerInfo.minFilter = VK_FILTER_LINEAR;
|
||||
samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
|
||||
samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
|
||||
samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
|
||||
samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
|
||||
samplerInfo.mipLodBias = 0.0f;
|
||||
samplerInfo.compareOp = VK_COMPARE_OP_NEVER;
|
||||
samplerInfo.minLod = 0.0f;
|
||||
samplerInfo.maxLod = 1.0f;
|
||||
samplerInfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;
|
||||
samplerInfo.anisotropyEnable = VK_FALSE;
|
||||
samplerInfo.maxAnisotropy = 1.0;
|
||||
VK_CHECK_RESULT(vkCreateSampler(device, &samplerInfo, nullptr, &sampler));
|
||||
|
||||
// Descriptor
|
||||
// Font uses a separate descriptor pool
|
||||
std::array<VkDescriptorPoolSize, 1> poolSizes;
|
||||
poolSizes[0] = vkTools::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1);
|
||||
|
||||
VkDescriptorPoolCreateInfo descriptorPoolInfo =
|
||||
vkTools::initializers::descriptorPoolCreateInfo(
|
||||
static_cast<uint32_t>(poolSizes.size()),
|
||||
poolSizes.data(),
|
||||
1);
|
||||
|
||||
VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &descriptorPool));
|
||||
|
||||
// Descriptor set layout
|
||||
std::array<VkDescriptorSetLayoutBinding, 1> setLayoutBindings;
|
||||
setLayoutBindings[0] = vkTools::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 0);
|
||||
|
||||
VkDescriptorSetLayoutCreateInfo descriptorSetLayoutInfo =
|
||||
vkTools::initializers::descriptorSetLayoutCreateInfo(
|
||||
setLayoutBindings.data(),
|
||||
static_cast<uint32_t>(setLayoutBindings.size()));
|
||||
|
||||
VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorSetLayoutInfo, nullptr, &descriptorSetLayout));
|
||||
|
||||
// Pipeline layout
|
||||
VkPipelineLayoutCreateInfo pipelineLayoutInfo =
|
||||
vkTools::initializers::pipelineLayoutCreateInfo(
|
||||
&descriptorSetLayout,
|
||||
1);
|
||||
|
||||
VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout));
|
||||
|
||||
// Descriptor set
|
||||
VkDescriptorSetAllocateInfo descriptorSetAllocInfo =
|
||||
vkTools::initializers::descriptorSetAllocateInfo(
|
||||
descriptorPool,
|
||||
&descriptorSetLayout,
|
||||
1);
|
||||
|
||||
VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &descriptorSetAllocInfo, &descriptorSet));
|
||||
|
||||
VkDescriptorImageInfo texDescriptor =
|
||||
vkTools::initializers::descriptorImageInfo(
|
||||
sampler,
|
||||
view,
|
||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
|
||||
std::array<VkWriteDescriptorSet, 1> writeDescriptorSets;
|
||||
writeDescriptorSets[0] = vkTools::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 0, &texDescriptor);
|
||||
vkUpdateDescriptorSets(device, static_cast<uint32_t>(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, NULL);
|
||||
|
||||
// Pipeline cache
|
||||
VkPipelineCacheCreateInfo pipelineCacheCreateInfo = {};
|
||||
pipelineCacheCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
|
||||
VK_CHECK_RESULT(vkCreatePipelineCache(device, &pipelineCacheCreateInfo, nullptr, &pipelineCache));
|
||||
}
|
||||
|
||||
// Prepare a separate pipeline for the font rendering decoupled from the main application
|
||||
void preparePipeline()
|
||||
{
|
||||
VkPipelineInputAssemblyStateCreateInfo inputAssemblyState =
|
||||
vkTools::initializers::pipelineInputAssemblyStateCreateInfo(
|
||||
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP,
|
||||
0,
|
||||
// primmitiveRestartEnable not needed but disabling it results in a MoltenVK
|
||||
// feature not present warning.
|
||||
VK_TRUE);
|
||||
|
||||
VkPipelineRasterizationStateCreateInfo rasterizationState =
|
||||
vkTools::initializers::pipelineRasterizationStateCreateInfo(
|
||||
VK_POLYGON_MODE_FILL,
|
||||
VK_CULL_MODE_BACK_BIT,
|
||||
VK_FRONT_FACE_CLOCKWISE,
|
||||
0);
|
||||
// Because we haven't enabled the depthClamp device feature.
|
||||
rasterizationState.depthClampEnable = VK_FALSE;
|
||||
|
||||
// Enable blending
|
||||
VkPipelineColorBlendAttachmentState blendAttachmentState =
|
||||
vkTools::initializers::pipelineColorBlendAttachmentState(0xf, VK_TRUE);
|
||||
|
||||
blendAttachmentState.srcColorBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
blendAttachmentState.dstColorBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
blendAttachmentState.colorBlendOp = VK_BLEND_OP_ADD;
|
||||
blendAttachmentState.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
blendAttachmentState.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
blendAttachmentState.alphaBlendOp = VK_BLEND_OP_ADD;
|
||||
blendAttachmentState.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
|
||||
|
||||
VkPipelineColorBlendStateCreateInfo colorBlendState =
|
||||
vkTools::initializers::pipelineColorBlendStateCreateInfo(
|
||||
1,
|
||||
&blendAttachmentState);
|
||||
|
||||
VkPipelineDepthStencilStateCreateInfo depthStencilState =
|
||||
vkTools::initializers::pipelineDepthStencilStateCreateInfo(
|
||||
VK_FALSE,
|
||||
VK_FALSE,
|
||||
VK_COMPARE_OP_LESS_OR_EQUAL);
|
||||
|
||||
VkPipelineViewportStateCreateInfo viewportState =
|
||||
vkTools::initializers::pipelineViewportStateCreateInfo(1, 1, 0);
|
||||
|
||||
VkPipelineMultisampleStateCreateInfo multisampleState =
|
||||
vkTools::initializers::pipelineMultisampleStateCreateInfo(
|
||||
VK_SAMPLE_COUNT_1_BIT,
|
||||
0);
|
||||
|
||||
std::vector<VkDynamicState> dynamicStateEnables = {
|
||||
VK_DYNAMIC_STATE_VIEWPORT,
|
||||
VK_DYNAMIC_STATE_SCISSOR
|
||||
};
|
||||
|
||||
VkPipelineDynamicStateCreateInfo dynamicState =
|
||||
vkTools::initializers::pipelineDynamicStateCreateInfo(
|
||||
dynamicStateEnables.data(),
|
||||
static_cast<uint32_t>(dynamicStateEnables.size()),
|
||||
0);
|
||||
|
||||
std::array<VkVertexInputBindingDescription, 2> vertexBindings = {};
|
||||
vertexBindings[0] = vkTools::initializers::vertexInputBindingDescription(0, sizeof(glm::vec4), VK_VERTEX_INPUT_RATE_VERTEX);
|
||||
vertexBindings[1] = vkTools::initializers::vertexInputBindingDescription(1, sizeof(glm::vec4), VK_VERTEX_INPUT_RATE_VERTEX);
|
||||
|
||||
std::array<VkVertexInputAttributeDescription, 2> vertexAttribs = {};
|
||||
// Position
|
||||
vertexAttribs[0] = vkTools::initializers::vertexInputAttributeDescription(0, 0, VK_FORMAT_R32G32_SFLOAT, 0);
|
||||
// UV
|
||||
vertexAttribs[1] = vkTools::initializers::vertexInputAttributeDescription(1, 1, VK_FORMAT_R32G32_SFLOAT, sizeof(glm::vec2));
|
||||
|
||||
VkPipelineVertexInputStateCreateInfo inputState = vkTools::initializers::pipelineVertexInputStateCreateInfo();
|
||||
inputState.vertexBindingDescriptionCount = static_cast<uint32_t>(vertexBindings.size());
|
||||
inputState.pVertexBindingDescriptions = vertexBindings.data();
|
||||
inputState.vertexAttributeDescriptionCount = static_cast<uint32_t>(vertexAttribs.size());
|
||||
inputState.pVertexAttributeDescriptions = vertexAttribs.data();
|
||||
|
||||
VkGraphicsPipelineCreateInfo pipelineCreateInfo =
|
||||
vkTools::initializers::pipelineCreateInfo(
|
||||
pipelineLayout,
|
||||
renderPass,
|
||||
0);
|
||||
|
||||
pipelineCreateInfo.pVertexInputState = &inputState;
|
||||
pipelineCreateInfo.pInputAssemblyState = &inputAssemblyState;
|
||||
pipelineCreateInfo.pRasterizationState = &rasterizationState;
|
||||
pipelineCreateInfo.pColorBlendState = &colorBlendState;
|
||||
pipelineCreateInfo.pMultisampleState = &multisampleState;
|
||||
pipelineCreateInfo.pViewportState = &viewportState;
|
||||
pipelineCreateInfo.pDepthStencilState = &depthStencilState;
|
||||
pipelineCreateInfo.pDynamicState = &dynamicState;
|
||||
pipelineCreateInfo.stageCount = static_cast<uint32_t>(shaderStages.size());
|
||||
pipelineCreateInfo.pStages = shaderStages.data();
|
||||
|
||||
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipeline));
|
||||
}
|
||||
|
||||
// Prepare a separate render pass for rendering the text as an overlay
|
||||
void prepareRenderPass()
|
||||
{
|
||||
VkAttachmentDescription attachments[2] = {};
|
||||
|
||||
// Color attachment
|
||||
attachments[0].format = colorFormat;
|
||||
attachments[0].samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
// Don't clear the framebuffer (like the renderpass from the example does)
|
||||
attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
|
||||
attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
||||
attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
||||
attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||||
attachments[0].initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
attachments[0].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
|
||||
// Depth attachment
|
||||
attachments[1].format = depthFormat;
|
||||
attachments[1].samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
||||
attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||||
attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
||||
attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||||
attachments[1].initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
||||
attachments[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
||||
|
||||
VkAttachmentReference colorReference = {};
|
||||
colorReference.attachment = 0;
|
||||
colorReference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
|
||||
VkAttachmentReference depthReference = {};
|
||||
depthReference.attachment = 1;
|
||||
depthReference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
||||
|
||||
VkSubpassDescription subpass = {};
|
||||
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
|
||||
subpass.flags = 0;
|
||||
subpass.inputAttachmentCount = 0;
|
||||
subpass.pInputAttachments = NULL;
|
||||
subpass.colorAttachmentCount = 1;
|
||||
subpass.pColorAttachments = &colorReference;
|
||||
subpass.pResolveAttachments = NULL;
|
||||
subpass.pDepthStencilAttachment = &depthReference;
|
||||
subpass.preserveAttachmentCount = 0;
|
||||
subpass.pPreserveAttachments = NULL;
|
||||
|
||||
VkRenderPassCreateInfo renderPassInfo = {};
|
||||
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
|
||||
renderPassInfo.pNext = NULL;
|
||||
renderPassInfo.attachmentCount = 2;
|
||||
renderPassInfo.pAttachments = attachments;
|
||||
renderPassInfo.subpassCount = 1;
|
||||
renderPassInfo.pSubpasses = &subpass;
|
||||
renderPassInfo.dependencyCount = 0;
|
||||
renderPassInfo.pDependencies = NULL;
|
||||
|
||||
VK_CHECK_RESULT(vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass));
|
||||
}
|
||||
|
||||
// Map buffer
|
||||
void beginTextUpdate()
|
||||
{
|
||||
mappedLocal = mapped;
|
||||
numLetters = 0;
|
||||
}
|
||||
|
||||
// Add text to the current buffer
|
||||
// todo : drop shadow? color attribute?
|
||||
void addText(std::string text, float x, float y, TextAlign align)
|
||||
{
|
||||
if (numLetters == MAX_CHAR_COUNT)
|
||||
return;
|
||||
|
||||
assert(mapped != nullptr);
|
||||
|
||||
const float charW = 1.5f / *frameBufferWidth;
|
||||
const float charH = 1.5f / *frameBufferHeight;
|
||||
|
||||
float fbW = (float)*frameBufferWidth;
|
||||
float fbH = (float)*frameBufferHeight;
|
||||
x = (x / fbW * 2.0f) - 1.0f;
|
||||
y = (y / fbH * 2.0f) - 1.0f;
|
||||
|
||||
// Calculate text width
|
||||
float textWidth = 0;
|
||||
uint32_t codepoint;
|
||||
auto it = text.begin();
|
||||
while (it != text.end())
|
||||
{
|
||||
if (!advanceUTF8(it, text.end(), codepoint))
|
||||
break;
|
||||
// TODO: Get a UTF8 font. Consider changing to Dear ImGUI
|
||||
// https://github.com/ocornut/imgui.
|
||||
// Placeholder to avoid crashing.
|
||||
if (codepoint > STB_NUM_CHARS + STB_FIRST_CHAR)
|
||||
codepoint = STB_MISSING_GLPYH;
|
||||
stb_fontchar *charData = &stbFontData[(uint32_t)codepoint - STB_FIRST_CHAR];
|
||||
textWidth += charData->advance * charW;
|
||||
}
|
||||
switch (align)
|
||||
{
|
||||
case alignRight:
|
||||
x -= textWidth;
|
||||
break;
|
||||
case alignCenter:
|
||||
x -= textWidth / 2.0f;
|
||||
break;
|
||||
case alignLeft:
|
||||
break;
|
||||
}
|
||||
|
||||
// Generate a uv mapped quad per char in the new text
|
||||
it = text.begin();
|
||||
while (it != text.end())
|
||||
{
|
||||
if (!advanceUTF8(it, text.end(), codepoint))
|
||||
break;
|
||||
if (codepoint > STB_NUM_CHARS + STB_FIRST_CHAR)
|
||||
codepoint = STB_MISSING_GLPYH;
|
||||
|
||||
stb_fontchar *charData = &stbFontData[(uint32_t)codepoint - STB_FIRST_CHAR];
|
||||
|
||||
mappedLocal->x = (x + (float)charData->x0 * charW);
|
||||
mappedLocal->y = (y + (float)charData->y0 * charH);
|
||||
mappedLocal->z = charData->s0;
|
||||
mappedLocal->w = charData->t0;
|
||||
mappedLocal++;
|
||||
|
||||
mappedLocal->x = (x + (float)charData->x1 * charW);
|
||||
mappedLocal->y = (y + (float)charData->y0 * charH);
|
||||
mappedLocal->z = charData->s1;
|
||||
mappedLocal->w = charData->t0;
|
||||
mappedLocal++;
|
||||
|
||||
mappedLocal->x = (x + (float)charData->x0 * charW);
|
||||
mappedLocal->y = (y + (float)charData->y1 * charH);
|
||||
mappedLocal->z = charData->s0;
|
||||
mappedLocal->w = charData->t1;
|
||||
mappedLocal++;
|
||||
|
||||
mappedLocal->x = (x + (float)charData->x1 * charW);
|
||||
mappedLocal->y = (y + (float)charData->y1 * charH);
|
||||
mappedLocal->z = charData->s1;
|
||||
mappedLocal->w = charData->t1;
|
||||
mappedLocal++;
|
||||
|
||||
x += charData->advance * charW;
|
||||
|
||||
numLetters++;
|
||||
|
||||
if (numLetters == MAX_CHAR_COUNT)
|
||||
break; // Truncate the text.
|
||||
}
|
||||
}
|
||||
// Unmap buffer and update command buffers
|
||||
void endTextUpdate()
|
||||
{
|
||||
updateCommandBuffers();
|
||||
}
|
||||
|
||||
// Needs to be called by the application
|
||||
void updateCommandBuffers()
|
||||
{
|
||||
VkCommandBufferBeginInfo cmdBufInfo = vkTools::initializers::commandBufferBeginInfo();
|
||||
|
||||
VkRenderPassBeginInfo renderPassBeginInfo = vkTools::initializers::renderPassBeginInfo();
|
||||
renderPassBeginInfo.renderPass = renderPass;
|
||||
renderPassBeginInfo.renderArea.extent.width = *frameBufferWidth;
|
||||
renderPassBeginInfo.renderArea.extent.height = *frameBufferHeight;
|
||||
renderPassBeginInfo.clearValueCount = 0;
|
||||
renderPassBeginInfo.pClearValues = nullptr;
|
||||
|
||||
for (uint32_t i = 0; i < cmdBuffers.size(); ++i)
|
||||
{
|
||||
renderPassBeginInfo.framebuffer = *frameBuffers[i];
|
||||
|
||||
VK_CHECK_RESULT(vkBeginCommandBuffer(cmdBuffers[i], &cmdBufInfo));
|
||||
|
||||
if (vkDebug::DebugMarker::active)
|
||||
{
|
||||
vkDebug::DebugMarker::beginRegion(cmdBuffers[i], "Text overlay", glm::vec4(1.0f, 0.94f, 0.3f, 1.0f));
|
||||
}
|
||||
|
||||
vkCmdBeginRenderPass(cmdBuffers[i], &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
|
||||
|
||||
VkViewport viewport = vkTools::initializers::viewport((float)*frameBufferWidth, (float)*frameBufferHeight, 0.0f, 1.0f);
|
||||
vkCmdSetViewport(cmdBuffers[i], 0, 1, &viewport);
|
||||
|
||||
VkRect2D scissor = vkTools::initializers::rect2D(*frameBufferWidth, *frameBufferHeight, 0, 0);
|
||||
vkCmdSetScissor(cmdBuffers[i], 0, 1, &scissor);
|
||||
|
||||
vkCmdBindPipeline(cmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
|
||||
vkCmdBindDescriptorSets(cmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, NULL);
|
||||
|
||||
VkDeviceSize offsets = 0;
|
||||
vkCmdBindVertexBuffers(cmdBuffers[i], 0, 1, &buffer, &offsets);
|
||||
vkCmdBindVertexBuffers(cmdBuffers[i], 1, 1, &buffer, &offsets);
|
||||
for (uint32_t j = 0; j < numLetters; j++)
|
||||
{
|
||||
vkCmdDraw(cmdBuffers[i], 4, 1, j * 4, 0);
|
||||
}
|
||||
|
||||
vkCmdEndRenderPass(cmdBuffers[i]);
|
||||
|
||||
if (vkDebug::DebugMarker::active)
|
||||
{
|
||||
vkDebug::DebugMarker::endRegion(cmdBuffers[i]);
|
||||
}
|
||||
|
||||
VK_CHECK_RESULT(vkEndCommandBuffer(cmdBuffers[i]));
|
||||
}
|
||||
}
|
||||
|
||||
// Submit the text command buffers to a queue
|
||||
void submit(VkQueue targetQueue, uint32_t bufferindex, VkSubmitInfo submitInfo)
|
||||
{
|
||||
if (!visible)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
submitInfo.pCommandBuffers = &cmdBuffers[bufferindex];
|
||||
submitInfo.commandBufferCount = 1;
|
||||
|
||||
VK_CHECK_RESULT(vkQueueSubmit(targetQueue, 1, &submitInfo, VK_NULL_HANDLE));
|
||||
}
|
||||
|
||||
void reallocateCommandBuffers()
|
||||
{
|
||||
vkFreeCommandBuffers(device, commandPool, static_cast<uint32_t>(cmdBuffers.size()), cmdBuffers.data());
|
||||
|
||||
VkCommandBufferAllocateInfo cmdBufAllocateInfo =
|
||||
vkTools::initializers::commandBufferAllocateInfo(
|
||||
commandPool,
|
||||
VK_COMMAND_BUFFER_LEVEL_PRIMARY,
|
||||
static_cast<uint32_t>(cmdBuffers.size()));
|
||||
|
||||
VK_CHECK_RESULT(vkAllocateCommandBuffers(device, &cmdBufAllocateInfo, cmdBuffers.data()));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// vi: set sw=2 ts=4 expandtab:
|
||||
@@ -0,0 +1,918 @@
|
||||
/*
|
||||
* Copyright 2016 Sascha Willems - www.saschawillems.de
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Assorted commonly used Vulkan helper functions
|
||||
*
|
||||
* asserts on unhandled cases in setImageLayout added by Mark Callow, 2017.3.3.
|
||||
*/
|
||||
|
||||
#if defined(_WIN32)
|
||||
#define _CRT_SECURE_NO_WARNINGS // For fopen
|
||||
#endif
|
||||
|
||||
#include "vulkantools.h"
|
||||
#include "unused.h"
|
||||
|
||||
namespace vkTools
|
||||
{
|
||||
|
||||
VkBool32 checkGlobalExtensionPresent(const char* extensionName)
|
||||
{
|
||||
uint32_t extensionCount = 0;
|
||||
std::vector<VkExtensionProperties> extensions;
|
||||
vkEnumerateInstanceExtensionProperties(NULL, &extensionCount, NULL);
|
||||
extensions.resize(extensionCount);
|
||||
vkEnumerateInstanceExtensionProperties(NULL, &extensionCount, extensions.data());
|
||||
for (auto& ext : extensions)
|
||||
{
|
||||
if (!strcmp(extensionName, ext.extensionName))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
VkBool32 checkDeviceExtensionPresent(VkPhysicalDevice physicalDevice, const char* extensionName)
|
||||
{
|
||||
uint32_t extensionCount = 0;
|
||||
std::vector<VkExtensionProperties> extensions;
|
||||
vkEnumerateDeviceExtensionProperties(physicalDevice, NULL, &extensionCount, NULL);
|
||||
extensions.resize(extensionCount);
|
||||
vkEnumerateDeviceExtensionProperties(physicalDevice, NULL, &extensionCount, extensions.data());
|
||||
for (auto& ext : extensions)
|
||||
{
|
||||
if (!strcmp(extensionName, ext.extensionName))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string errorString(VkResult errorCode)
|
||||
{
|
||||
switch (errorCode)
|
||||
{
|
||||
#define STR(r) case VK_ ##r: return #r
|
||||
STR(NOT_READY);
|
||||
STR(TIMEOUT);
|
||||
STR(EVENT_SET);
|
||||
STR(EVENT_RESET);
|
||||
STR(INCOMPLETE);
|
||||
STR(ERROR_OUT_OF_HOST_MEMORY);
|
||||
STR(ERROR_OUT_OF_DEVICE_MEMORY);
|
||||
STR(ERROR_INITIALIZATION_FAILED);
|
||||
STR(ERROR_DEVICE_LOST);
|
||||
STR(ERROR_MEMORY_MAP_FAILED);
|
||||
STR(ERROR_LAYER_NOT_PRESENT);
|
||||
STR(ERROR_EXTENSION_NOT_PRESENT);
|
||||
STR(ERROR_FEATURE_NOT_PRESENT);
|
||||
STR(ERROR_INCOMPATIBLE_DRIVER);
|
||||
STR(ERROR_TOO_MANY_OBJECTS);
|
||||
STR(ERROR_FORMAT_NOT_SUPPORTED);
|
||||
STR(ERROR_SURFACE_LOST_KHR);
|
||||
STR(ERROR_NATIVE_WINDOW_IN_USE_KHR);
|
||||
STR(SUBOPTIMAL_KHR);
|
||||
STR(ERROR_OUT_OF_DATE_KHR);
|
||||
STR(ERROR_INCOMPATIBLE_DISPLAY_KHR);
|
||||
STR(ERROR_VALIDATION_FAILED_EXT);
|
||||
STR(ERROR_INVALID_SHADER_NV);
|
||||
#undef STR
|
||||
default:
|
||||
return "UNKNOWN_ERROR";
|
||||
}
|
||||
}
|
||||
|
||||
VkBool32 getSupportedDepthFormat(VkPhysicalDevice physicalDevice, VkFormat *depthFormat)
|
||||
{
|
||||
// Since all depth formats may be optional, we need to find a suitable depth format to use
|
||||
// Start with the highest precision packed format
|
||||
std::vector<VkFormat> depthFormats = {
|
||||
VK_FORMAT_D32_SFLOAT_S8_UINT,
|
||||
VK_FORMAT_D32_SFLOAT,
|
||||
VK_FORMAT_D24_UNORM_S8_UINT,
|
||||
VK_FORMAT_D16_UNORM_S8_UINT,
|
||||
VK_FORMAT_D16_UNORM
|
||||
};
|
||||
|
||||
for (auto& format : depthFormats)
|
||||
{
|
||||
VkFormatProperties formatProps;
|
||||
vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &formatProps);
|
||||
// Format must support depth stencil attachment for optimal tiling
|
||||
if (formatProps.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT)
|
||||
{
|
||||
*depthFormat = format;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create an image memory barrier for changing the layout of
|
||||
// an image and put it into an active command buffer
|
||||
// See chapter 11.4 "Image Layout" for details
|
||||
|
||||
void setImageLayout(
|
||||
VkCommandBuffer cmdbuffer,
|
||||
VkImage image,
|
||||
VkImageAspectFlags /*aspectMask*/,
|
||||
VkImageLayout oldImageLayout,
|
||||
VkImageLayout newImageLayout,
|
||||
VkImageSubresourceRange subresourceRange)
|
||||
{
|
||||
// Create an image barrier object
|
||||
VkImageMemoryBarrier imageMemoryBarrier = vkTools::initializers::imageMemoryBarrier();
|
||||
imageMemoryBarrier.oldLayout = oldImageLayout;
|
||||
imageMemoryBarrier.newLayout = newImageLayout;
|
||||
imageMemoryBarrier.image = image;
|
||||
imageMemoryBarrier.subresourceRange = subresourceRange;
|
||||
|
||||
// Source layouts (old)
|
||||
// Source access mask controls actions that have to be finished on the old layout
|
||||
// before it will be transitioned to the new layout
|
||||
switch (oldImageLayout)
|
||||
{
|
||||
case VK_IMAGE_LAYOUT_UNDEFINED:
|
||||
// Image layout is undefined (or does not matter)
|
||||
// Only valid as initial layout
|
||||
// No flags required, listed only for completeness
|
||||
imageMemoryBarrier.srcAccessMask = 0;
|
||||
break;
|
||||
|
||||
case VK_IMAGE_LAYOUT_PREINITIALIZED:
|
||||
// Image is preinitialized
|
||||
// Only valid as initial layout for linear images, preserves memory contents
|
||||
// Make sure host writes have been finished
|
||||
imageMemoryBarrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
|
||||
break;
|
||||
|
||||
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
|
||||
// Image is a color attachment
|
||||
// Make sure any writes to the color buffer have been finished
|
||||
imageMemoryBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
break;
|
||||
|
||||
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
|
||||
// Image is a depth/stencil attachment
|
||||
// Make sure any writes to the depth/stencil buffer have been finished
|
||||
imageMemoryBarrier.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
|
||||
break;
|
||||
|
||||
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
|
||||
// Image is a transfer source
|
||||
// Make sure any reads from the image have been finished
|
||||
imageMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
|
||||
break;
|
||||
|
||||
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
|
||||
// Image is a transfer destination
|
||||
// Make sure any writes to the image have been finished
|
||||
imageMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
break;
|
||||
|
||||
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
|
||||
// Image is read by a shader
|
||||
// Make sure any shader reads from the image have been finished
|
||||
imageMemoryBarrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(0); // Attempt to use unhandled case.
|
||||
}
|
||||
|
||||
// Target layouts (new)
|
||||
// Destination access mask controls the dependency for the new image layout
|
||||
switch (newImageLayout)
|
||||
{
|
||||
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
|
||||
// Image will be used as a transfer destination
|
||||
// Make sure any writes to the image have been finished
|
||||
imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
break;
|
||||
|
||||
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
|
||||
// Image will be used as a transfer source
|
||||
// Make sure any reads from and writes to the image have been finished
|
||||
imageMemoryBarrier.srcAccessMask = imageMemoryBarrier.srcAccessMask | VK_ACCESS_TRANSFER_READ_BIT;
|
||||
imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
|
||||
break;
|
||||
|
||||
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
|
||||
// Image will be used as a color attachment
|
||||
// Make sure any writes to the color buffer have been finished
|
||||
imageMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
|
||||
imageMemoryBarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
break;
|
||||
|
||||
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
|
||||
// Image layout will be used as a depth/stencil attachment
|
||||
// Make sure any writes to depth/stencil buffer have been finished
|
||||
imageMemoryBarrier.dstAccessMask = imageMemoryBarrier.dstAccessMask | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
|
||||
break;
|
||||
|
||||
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
|
||||
// Image will be read in a shader (sampler, input attachment)
|
||||
// Make sure any writes to the image have been finished
|
||||
if (imageMemoryBarrier.srcAccessMask == 0)
|
||||
{
|
||||
imageMemoryBarrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
}
|
||||
imageMemoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(0); // Attempt to use unhandled case.
|
||||
}
|
||||
|
||||
// Put barrier on top
|
||||
VkPipelineStageFlags srcStageFlags = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
|
||||
VkPipelineStageFlags destStageFlags = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
|
||||
|
||||
// Put barrier inside setup command buffer
|
||||
vkCmdPipelineBarrier(
|
||||
cmdbuffer,
|
||||
srcStageFlags,
|
||||
destStageFlags,
|
||||
0,
|
||||
0, nullptr,
|
||||
0, nullptr,
|
||||
1, &imageMemoryBarrier);
|
||||
}
|
||||
|
||||
// Fixed sub resource on first mip level and layer
|
||||
void setImageLayout(
|
||||
VkCommandBuffer cmdbuffer,
|
||||
VkImage image,
|
||||
VkImageAspectFlags aspectMask,
|
||||
VkImageLayout oldImageLayout,
|
||||
VkImageLayout newImageLayout)
|
||||
{
|
||||
VkImageSubresourceRange subresourceRange = {};
|
||||
subresourceRange.aspectMask = aspectMask;
|
||||
subresourceRange.baseMipLevel = 0;
|
||||
subresourceRange.levelCount = 1;
|
||||
subresourceRange.layerCount = 1;
|
||||
setImageLayout(cmdbuffer, image, aspectMask, oldImageLayout, newImageLayout, subresourceRange);
|
||||
}
|
||||
|
||||
void exitFatal(std::string message, std::string caption)
|
||||
{
|
||||
(void)SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, caption.c_str(),
|
||||
message.c_str(), NULL);
|
||||
std::cerr << message << "\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
std::string readTextFile(const char *fileName)
|
||||
{
|
||||
std::string fileContent;
|
||||
std::ifstream fileStream(fileName, std::ios::in);
|
||||
if (!fileStream.is_open()) {
|
||||
printf("File %s not found\n", fileName);
|
||||
return "";
|
||||
}
|
||||
std::string line = "";
|
||||
while (!fileStream.eof()) {
|
||||
getline(fileStream, line);
|
||||
fileContent.append(line + "\n");
|
||||
}
|
||||
fileStream.close();
|
||||
return fileContent;
|
||||
}
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
// Android shaders are stored as assets in the apk
|
||||
// So they need to be loaded via the asset manager
|
||||
VkShaderModule loadShader(AAssetManager* assetManager, const char *fileName, VkDevice device, VkShaderStageFlagBits stage)
|
||||
{
|
||||
// Load shader from compressed asset
|
||||
AAsset* asset = AAssetManager_open(assetManager, fileName, AASSET_MODE_STREAMING);
|
||||
assert(asset);
|
||||
size_t size = AAsset_getLength(asset);
|
||||
assert(size > 0);
|
||||
|
||||
char *shaderCode = new char[size];
|
||||
AAsset_read(asset, shaderCode, size);
|
||||
AAsset_close(asset);
|
||||
|
||||
VkShaderModule shaderModule;
|
||||
VkShaderModuleCreateInfo moduleCreateInfo;
|
||||
moduleCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
|
||||
moduleCreateInfo.pNext = NULL;
|
||||
moduleCreateInfo.codeSize = size;
|
||||
moduleCreateInfo.pCode = (uint32_t*)shaderCode;
|
||||
moduleCreateInfo.flags = 0;
|
||||
|
||||
VK_CHECK_RESULT(vkCreateShaderModule(device, &moduleCreateInfo, NULL, &shaderModule));
|
||||
|
||||
delete[] shaderCode;
|
||||
|
||||
return shaderModule;
|
||||
}
|
||||
#else
|
||||
VkShaderModule loadShader(const char *fileName, VkDevice device, VkShaderStageFlagBits /*stage*/)
|
||||
{
|
||||
size_t size;
|
||||
|
||||
FILE *fp = fopen(fileName, "rb");
|
||||
assert(fp);
|
||||
|
||||
fseek(fp, 0L, SEEK_END);
|
||||
size = ftell(fp);
|
||||
|
||||
fseek(fp, 0L, SEEK_SET);
|
||||
|
||||
//shaderCode = malloc(size);
|
||||
char *shaderCode = new char[size];
|
||||
U_ASSERT_ONLY size_t retval = fread(shaderCode, size, 1, fp);
|
||||
assert(retval == 1);
|
||||
(void)retval; // Supress VC++ unused variable warning for Release config.
|
||||
assert(size > 0);
|
||||
|
||||
fclose(fp);
|
||||
|
||||
VkShaderModule shaderModule;
|
||||
VkShaderModuleCreateInfo moduleCreateInfo;
|
||||
moduleCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
|
||||
moduleCreateInfo.pNext = NULL;
|
||||
moduleCreateInfo.codeSize = size;
|
||||
moduleCreateInfo.pCode = (uint32_t*)shaderCode;
|
||||
moduleCreateInfo.flags = 0;
|
||||
|
||||
VK_CHECK_RESULT(vkCreateShaderModule(device, &moduleCreateInfo, NULL, &shaderModule));
|
||||
|
||||
delete[] shaderCode;
|
||||
|
||||
return shaderModule;
|
||||
}
|
||||
#endif
|
||||
|
||||
VkShaderModule loadShaderGLSL(const char *fileName, VkDevice device, VkShaderStageFlagBits stage)
|
||||
{
|
||||
std::string shaderSrc = readTextFile(fileName);
|
||||
const char *shaderCode = shaderSrc.c_str();
|
||||
size_t size = strlen(shaderCode);
|
||||
assert(size > 0);
|
||||
|
||||
VkShaderModule shaderModule;
|
||||
VkShaderModuleCreateInfo moduleCreateInfo;
|
||||
moduleCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
|
||||
moduleCreateInfo.pNext = NULL;
|
||||
moduleCreateInfo.codeSize = 3 * sizeof(uint32_t) + size + 1;
|
||||
moduleCreateInfo.pCode = (uint32_t*)malloc(moduleCreateInfo.codeSize);
|
||||
moduleCreateInfo.flags = 0;
|
||||
|
||||
// Magic SPV number
|
||||
((uint32_t *)moduleCreateInfo.pCode)[0] = 0x07230203;
|
||||
((uint32_t *)moduleCreateInfo.pCode)[1] = 0;
|
||||
((uint32_t *)moduleCreateInfo.pCode)[2] = stage;
|
||||
memcpy(((uint32_t *)moduleCreateInfo.pCode + 3), shaderCode, size + 1);
|
||||
|
||||
VK_CHECK_RESULT(vkCreateShaderModule(device, &moduleCreateInfo, NULL, &shaderModule));
|
||||
|
||||
return shaderModule;
|
||||
}
|
||||
|
||||
VkImageMemoryBarrier prePresentBarrier(VkImage presentImage)
|
||||
{
|
||||
VkImageMemoryBarrier imageMemoryBarrier = vkTools::initializers::imageMemoryBarrier();
|
||||
imageMemoryBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
imageMemoryBarrier.dstAccessMask = 0;
|
||||
imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
||||
imageMemoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
imageMemoryBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
imageMemoryBarrier.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
|
||||
imageMemoryBarrier.image = presentImage;
|
||||
return imageMemoryBarrier;
|
||||
}
|
||||
|
||||
VkImageMemoryBarrier postPresentBarrier(VkImage presentImage)
|
||||
{
|
||||
VkImageMemoryBarrier imageMemoryBarrier = vkTools::initializers::imageMemoryBarrier();
|
||||
imageMemoryBarrier.srcAccessMask = 0;
|
||||
imageMemoryBarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
||||
imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
imageMemoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
imageMemoryBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
imageMemoryBarrier.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
|
||||
imageMemoryBarrier.image = presentImage;
|
||||
return imageMemoryBarrier;
|
||||
}
|
||||
|
||||
void destroyUniformData(VkDevice device, vkTools::UniformData *uniformData)
|
||||
{
|
||||
if (uniformData->mapped != nullptr)
|
||||
{
|
||||
vkUnmapMemory(device, uniformData->memory);
|
||||
}
|
||||
vkDestroyBuffer(device, uniformData->buffer, nullptr);
|
||||
vkFreeMemory(device, uniformData->memory, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
VkMemoryAllocateInfo vkTools::initializers::memoryAllocateInfo()
|
||||
{
|
||||
VkMemoryAllocateInfo memAllocInfo = {};
|
||||
memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
||||
memAllocInfo.pNext = NULL;
|
||||
memAllocInfo.allocationSize = 0;
|
||||
memAllocInfo.memoryTypeIndex = 0;
|
||||
return memAllocInfo;
|
||||
}
|
||||
|
||||
VkCommandBufferAllocateInfo vkTools::initializers::commandBufferAllocateInfo(VkCommandPool commandPool, VkCommandBufferLevel level, uint32_t bufferCount)
|
||||
{
|
||||
VkCommandBufferAllocateInfo commandBufferAllocateInfo = {};
|
||||
commandBufferAllocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
||||
commandBufferAllocateInfo.commandPool = commandPool;
|
||||
commandBufferAllocateInfo.level = level;
|
||||
commandBufferAllocateInfo.commandBufferCount = bufferCount;
|
||||
return commandBufferAllocateInfo;
|
||||
}
|
||||
|
||||
VkCommandPoolCreateInfo vkTools::initializers::commandPoolCreateInfo()
|
||||
{
|
||||
VkCommandPoolCreateInfo cmdPoolCreateInfo = {};
|
||||
cmdPoolCreateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
|
||||
return cmdPoolCreateInfo;
|
||||
}
|
||||
|
||||
VkCommandBufferBeginInfo vkTools::initializers::commandBufferBeginInfo()
|
||||
{
|
||||
VkCommandBufferBeginInfo cmdBufferBeginInfo = {};
|
||||
cmdBufferBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
||||
cmdBufferBeginInfo.pNext = NULL;
|
||||
return cmdBufferBeginInfo;
|
||||
}
|
||||
|
||||
VkCommandBufferInheritanceInfo vkTools::initializers::commandBufferInheritanceInfo()
|
||||
{
|
||||
VkCommandBufferInheritanceInfo cmdBufferInheritanceInfo = {};
|
||||
cmdBufferInheritanceInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO;
|
||||
return cmdBufferInheritanceInfo;
|
||||
}
|
||||
|
||||
VkRenderPassBeginInfo vkTools::initializers::renderPassBeginInfo()
|
||||
{
|
||||
VkRenderPassBeginInfo renderPassBeginInfo = {};
|
||||
renderPassBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
|
||||
renderPassBeginInfo.pNext = NULL;
|
||||
return renderPassBeginInfo;
|
||||
}
|
||||
|
||||
VkRenderPassCreateInfo vkTools::initializers::renderPassCreateInfo()
|
||||
{
|
||||
VkRenderPassCreateInfo renderPassCreateInfo = {};
|
||||
renderPassCreateInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
|
||||
renderPassCreateInfo.pNext = NULL;
|
||||
return renderPassCreateInfo;
|
||||
}
|
||||
|
||||
VkImageMemoryBarrier vkTools::initializers::imageMemoryBarrier()
|
||||
{
|
||||
VkImageMemoryBarrier imageMemoryBarrier = {};
|
||||
imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
||||
imageMemoryBarrier.pNext = NULL;
|
||||
// Some default values
|
||||
imageMemoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
imageMemoryBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
return imageMemoryBarrier;
|
||||
}
|
||||
|
||||
VkBufferMemoryBarrier vkTools::initializers::bufferMemoryBarrier()
|
||||
{
|
||||
VkBufferMemoryBarrier bufferMemoryBarrier = {};
|
||||
bufferMemoryBarrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
|
||||
bufferMemoryBarrier.pNext = NULL;
|
||||
return bufferMemoryBarrier;
|
||||
}
|
||||
|
||||
VkMemoryBarrier vkTools::initializers::memoryBarrier()
|
||||
{
|
||||
VkMemoryBarrier memoryBarrier = {};
|
||||
memoryBarrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER;
|
||||
memoryBarrier.pNext = NULL;
|
||||
return memoryBarrier;
|
||||
}
|
||||
|
||||
VkImageCreateInfo vkTools::initializers::imageCreateInfo()
|
||||
{
|
||||
VkImageCreateInfo imageCreateInfo = {};
|
||||
imageCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
||||
imageCreateInfo.pNext = NULL;
|
||||
return imageCreateInfo;
|
||||
}
|
||||
|
||||
VkSamplerCreateInfo vkTools::initializers::samplerCreateInfo()
|
||||
{
|
||||
VkSamplerCreateInfo samplerCreateInfo = {};
|
||||
samplerCreateInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
|
||||
samplerCreateInfo.pNext = NULL;
|
||||
return samplerCreateInfo;
|
||||
}
|
||||
|
||||
VkImageViewCreateInfo vkTools::initializers::imageViewCreateInfo()
|
||||
{
|
||||
VkImageViewCreateInfo imageViewCreateInfo = {};
|
||||
imageViewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||
imageViewCreateInfo.pNext = NULL;
|
||||
return imageViewCreateInfo;
|
||||
}
|
||||
|
||||
VkFramebufferCreateInfo vkTools::initializers::framebufferCreateInfo()
|
||||
{
|
||||
VkFramebufferCreateInfo framebufferCreateInfo = {};
|
||||
framebufferCreateInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
|
||||
framebufferCreateInfo.pNext = NULL;
|
||||
return framebufferCreateInfo;
|
||||
}
|
||||
|
||||
VkSemaphoreCreateInfo vkTools::initializers::semaphoreCreateInfo()
|
||||
{
|
||||
VkSemaphoreCreateInfo semaphoreCreateInfo = {};
|
||||
semaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
|
||||
semaphoreCreateInfo.pNext = NULL;
|
||||
semaphoreCreateInfo.flags = 0;
|
||||
return semaphoreCreateInfo;
|
||||
}
|
||||
|
||||
VkFenceCreateInfo vkTools::initializers::fenceCreateInfo(VkFenceCreateFlags flags)
|
||||
{
|
||||
VkFenceCreateInfo fenceCreateInfo = {};
|
||||
fenceCreateInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
|
||||
fenceCreateInfo.flags = flags;
|
||||
return fenceCreateInfo;
|
||||
}
|
||||
|
||||
VkEventCreateInfo vkTools::initializers::eventCreateInfo()
|
||||
{
|
||||
VkEventCreateInfo eventCreateInfo = {};
|
||||
eventCreateInfo.sType = VK_STRUCTURE_TYPE_EVENT_CREATE_INFO;
|
||||
return eventCreateInfo;
|
||||
}
|
||||
|
||||
VkSubmitInfo vkTools::initializers::submitInfo()
|
||||
{
|
||||
VkSubmitInfo submitInfo = {};
|
||||
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
||||
submitInfo.pNext = NULL;
|
||||
return submitInfo;
|
||||
}
|
||||
|
||||
VkViewport vkTools::initializers::viewport(
|
||||
float width,
|
||||
float height,
|
||||
float minDepth,
|
||||
float maxDepth)
|
||||
{
|
||||
VkViewport viewport = {};
|
||||
viewport.width = width;
|
||||
viewport.height = height;
|
||||
viewport.minDepth = minDepth;
|
||||
viewport.maxDepth = maxDepth;
|
||||
return viewport;
|
||||
}
|
||||
|
||||
VkRect2D vkTools::initializers::rect2D(
|
||||
int32_t width,
|
||||
int32_t height,
|
||||
int32_t offsetX,
|
||||
int32_t offsetY)
|
||||
{
|
||||
VkRect2D rect2D = {};
|
||||
rect2D.extent.width = width;
|
||||
rect2D.extent.height = height;
|
||||
rect2D.offset.x = offsetX;
|
||||
rect2D.offset.y = offsetY;
|
||||
return rect2D;
|
||||
}
|
||||
|
||||
VkBufferCreateInfo vkTools::initializers::bufferCreateInfo()
|
||||
{
|
||||
VkBufferCreateInfo bufCreateInfo = {};
|
||||
bufCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
||||
return bufCreateInfo;
|
||||
}
|
||||
|
||||
VkBufferCreateInfo vkTools::initializers::bufferCreateInfo(
|
||||
VkBufferUsageFlags usage,
|
||||
VkDeviceSize size)
|
||||
{
|
||||
VkBufferCreateInfo bufCreateInfo = {};
|
||||
bufCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
||||
bufCreateInfo.pNext = NULL;
|
||||
bufCreateInfo.usage = usage;
|
||||
bufCreateInfo.size = size;
|
||||
bufCreateInfo.flags = 0;
|
||||
return bufCreateInfo;
|
||||
}
|
||||
|
||||
VkDescriptorPoolCreateInfo vkTools::initializers::descriptorPoolCreateInfo(
|
||||
uint32_t poolSizeCount,
|
||||
VkDescriptorPoolSize* pPoolSizes,
|
||||
uint32_t maxSets)
|
||||
{
|
||||
VkDescriptorPoolCreateInfo descriptorPoolInfo = {};
|
||||
descriptorPoolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
|
||||
descriptorPoolInfo.pNext = NULL;
|
||||
descriptorPoolInfo.poolSizeCount = poolSizeCount;
|
||||
descriptorPoolInfo.pPoolSizes = pPoolSizes;
|
||||
descriptorPoolInfo.maxSets = maxSets;
|
||||
return descriptorPoolInfo;
|
||||
}
|
||||
|
||||
VkDescriptorPoolSize vkTools::initializers::descriptorPoolSize(
|
||||
VkDescriptorType type,
|
||||
uint32_t descriptorCount)
|
||||
{
|
||||
VkDescriptorPoolSize descriptorPoolSize = {};
|
||||
descriptorPoolSize.type = type;
|
||||
descriptorPoolSize.descriptorCount = descriptorCount;
|
||||
return descriptorPoolSize;
|
||||
}
|
||||
|
||||
VkDescriptorSetLayoutBinding vkTools::initializers::descriptorSetLayoutBinding(
|
||||
VkDescriptorType type,
|
||||
VkShaderStageFlags stageFlags,
|
||||
uint32_t binding)
|
||||
{
|
||||
VkDescriptorSetLayoutBinding setLayoutBinding = {};
|
||||
setLayoutBinding.descriptorType = type;
|
||||
setLayoutBinding.stageFlags = stageFlags;
|
||||
setLayoutBinding.binding = binding;
|
||||
// Default value in all examples
|
||||
setLayoutBinding.descriptorCount = 1;
|
||||
return setLayoutBinding;
|
||||
}
|
||||
|
||||
VkDescriptorSetLayoutCreateInfo vkTools::initializers::descriptorSetLayoutCreateInfo(
|
||||
const VkDescriptorSetLayoutBinding* pBindings,
|
||||
uint32_t bindingCount)
|
||||
{
|
||||
VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo = {};
|
||||
descriptorSetLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
|
||||
descriptorSetLayoutCreateInfo.pNext = NULL;
|
||||
descriptorSetLayoutCreateInfo.pBindings = pBindings;
|
||||
descriptorSetLayoutCreateInfo.bindingCount = bindingCount;
|
||||
return descriptorSetLayoutCreateInfo;
|
||||
}
|
||||
|
||||
VkPipelineLayoutCreateInfo vkTools::initializers::pipelineLayoutCreateInfo(
|
||||
const VkDescriptorSetLayout* pSetLayouts,
|
||||
uint32_t setLayoutCount)
|
||||
{
|
||||
VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = {};
|
||||
pipelineLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
|
||||
pipelineLayoutCreateInfo.pNext = NULL;
|
||||
pipelineLayoutCreateInfo.setLayoutCount = setLayoutCount;
|
||||
pipelineLayoutCreateInfo.pSetLayouts = pSetLayouts;
|
||||
return pipelineLayoutCreateInfo;
|
||||
}
|
||||
|
||||
VkDescriptorSetAllocateInfo vkTools::initializers::descriptorSetAllocateInfo(
|
||||
VkDescriptorPool descriptorPool,
|
||||
const VkDescriptorSetLayout* pSetLayouts,
|
||||
uint32_t descriptorSetCount)
|
||||
{
|
||||
VkDescriptorSetAllocateInfo descriptorSetAllocateInfo = {};
|
||||
descriptorSetAllocateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
|
||||
descriptorSetAllocateInfo.pNext = NULL;
|
||||
descriptorSetAllocateInfo.descriptorPool = descriptorPool;
|
||||
descriptorSetAllocateInfo.pSetLayouts = pSetLayouts;
|
||||
descriptorSetAllocateInfo.descriptorSetCount = descriptorSetCount;
|
||||
return descriptorSetAllocateInfo;
|
||||
}
|
||||
|
||||
VkDescriptorImageInfo vkTools::initializers::descriptorImageInfo(VkSampler sampler, VkImageView imageView, VkImageLayout imageLayout)
|
||||
{
|
||||
VkDescriptorImageInfo descriptorImageInfo = {};
|
||||
descriptorImageInfo.sampler = sampler;
|
||||
descriptorImageInfo.imageView = imageView;
|
||||
descriptorImageInfo.imageLayout = imageLayout;
|
||||
return descriptorImageInfo;
|
||||
}
|
||||
|
||||
VkWriteDescriptorSet vkTools::initializers::writeDescriptorSet(
|
||||
VkDescriptorSet dstSet,
|
||||
VkDescriptorType type,
|
||||
uint32_t binding,
|
||||
VkDescriptorBufferInfo* bufferInfo)
|
||||
{
|
||||
VkWriteDescriptorSet writeDescriptorSet = {};
|
||||
writeDescriptorSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
writeDescriptorSet.pNext = NULL;
|
||||
writeDescriptorSet.dstSet = dstSet;
|
||||
writeDescriptorSet.descriptorType = type;
|
||||
writeDescriptorSet.dstBinding = binding;
|
||||
writeDescriptorSet.pBufferInfo = bufferInfo;
|
||||
// Default value in all examples
|
||||
writeDescriptorSet.descriptorCount = 1;
|
||||
return writeDescriptorSet;
|
||||
}
|
||||
|
||||
VkWriteDescriptorSet vkTools::initializers::writeDescriptorSet(
|
||||
VkDescriptorSet dstSet,
|
||||
VkDescriptorType type,
|
||||
uint32_t binding,
|
||||
VkDescriptorImageInfo * imageInfo)
|
||||
{
|
||||
VkWriteDescriptorSet writeDescriptorSet = {};
|
||||
writeDescriptorSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
writeDescriptorSet.pNext = NULL;
|
||||
writeDescriptorSet.dstSet = dstSet;
|
||||
writeDescriptorSet.descriptorType = type;
|
||||
writeDescriptorSet.dstBinding = binding;
|
||||
writeDescriptorSet.pImageInfo = imageInfo;
|
||||
// Default value in all examples
|
||||
writeDescriptorSet.descriptorCount = 1;
|
||||
return writeDescriptorSet;
|
||||
}
|
||||
|
||||
VkVertexInputBindingDescription vkTools::initializers::vertexInputBindingDescription(
|
||||
uint32_t binding,
|
||||
uint32_t stride,
|
||||
VkVertexInputRate inputRate)
|
||||
{
|
||||
VkVertexInputBindingDescription vInputBindDescription = {};
|
||||
vInputBindDescription.binding = binding;
|
||||
vInputBindDescription.stride = stride;
|
||||
vInputBindDescription.inputRate = inputRate;
|
||||
return vInputBindDescription;
|
||||
}
|
||||
|
||||
VkVertexInputAttributeDescription vkTools::initializers::vertexInputAttributeDescription(
|
||||
uint32_t binding,
|
||||
uint32_t location,
|
||||
VkFormat format,
|
||||
uint32_t offset)
|
||||
{
|
||||
VkVertexInputAttributeDescription vInputAttribDescription = {};
|
||||
vInputAttribDescription.location = location;
|
||||
vInputAttribDescription.binding = binding;
|
||||
vInputAttribDescription.format = format;
|
||||
vInputAttribDescription.offset = offset;
|
||||
return vInputAttribDescription;
|
||||
}
|
||||
|
||||
VkPipelineVertexInputStateCreateInfo vkTools::initializers::pipelineVertexInputStateCreateInfo()
|
||||
{
|
||||
VkPipelineVertexInputStateCreateInfo pipelineVertexInputStateCreateInfo = {};
|
||||
pipelineVertexInputStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
|
||||
pipelineVertexInputStateCreateInfo.pNext = NULL;
|
||||
return pipelineVertexInputStateCreateInfo;
|
||||
}
|
||||
|
||||
VkPipelineInputAssemblyStateCreateInfo vkTools::initializers::pipelineInputAssemblyStateCreateInfo(
|
||||
VkPrimitiveTopology topology,
|
||||
VkPipelineInputAssemblyStateCreateFlags flags,
|
||||
VkBool32 primitiveRestartEnable)
|
||||
{
|
||||
VkPipelineInputAssemblyStateCreateInfo pipelineInputAssemblyStateCreateInfo = {};
|
||||
pipelineInputAssemblyStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
|
||||
pipelineInputAssemblyStateCreateInfo.topology = topology;
|
||||
pipelineInputAssemblyStateCreateInfo.flags = flags;
|
||||
pipelineInputAssemblyStateCreateInfo.primitiveRestartEnable = primitiveRestartEnable;
|
||||
return pipelineInputAssemblyStateCreateInfo;
|
||||
}
|
||||
|
||||
VkPipelineRasterizationStateCreateInfo vkTools::initializers::pipelineRasterizationStateCreateInfo(
|
||||
VkPolygonMode polygonMode,
|
||||
VkCullModeFlags cullMode,
|
||||
VkFrontFace frontFace,
|
||||
VkPipelineRasterizationStateCreateFlags flags)
|
||||
{
|
||||
VkPipelineRasterizationStateCreateInfo pipelineRasterizationStateCreateInfo = {};
|
||||
pipelineRasterizationStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
|
||||
pipelineRasterizationStateCreateInfo.polygonMode = polygonMode;
|
||||
pipelineRasterizationStateCreateInfo.cullMode = cullMode;
|
||||
pipelineRasterizationStateCreateInfo.frontFace = frontFace;
|
||||
pipelineRasterizationStateCreateInfo.flags = flags;
|
||||
pipelineRasterizationStateCreateInfo.depthClampEnable = VK_TRUE;
|
||||
pipelineRasterizationStateCreateInfo.lineWidth = 1.0f;
|
||||
return pipelineRasterizationStateCreateInfo;
|
||||
}
|
||||
|
||||
VkPipelineColorBlendAttachmentState vkTools::initializers::pipelineColorBlendAttachmentState(
|
||||
VkColorComponentFlags colorWriteMask,
|
||||
VkBool32 blendEnable)
|
||||
{
|
||||
VkPipelineColorBlendAttachmentState pipelineColorBlendAttachmentState = {};
|
||||
pipelineColorBlendAttachmentState.colorWriteMask = colorWriteMask;
|
||||
pipelineColorBlendAttachmentState.blendEnable = blendEnable;
|
||||
return pipelineColorBlendAttachmentState;
|
||||
}
|
||||
|
||||
VkPipelineColorBlendStateCreateInfo vkTools::initializers::pipelineColorBlendStateCreateInfo(
|
||||
uint32_t attachmentCount,
|
||||
const VkPipelineColorBlendAttachmentState * pAttachments)
|
||||
{
|
||||
VkPipelineColorBlendStateCreateInfo pipelineColorBlendStateCreateInfo = {};
|
||||
pipelineColorBlendStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
|
||||
pipelineColorBlendStateCreateInfo.pNext = NULL;
|
||||
pipelineColorBlendStateCreateInfo.attachmentCount = attachmentCount;
|
||||
pipelineColorBlendStateCreateInfo.pAttachments = pAttachments;
|
||||
return pipelineColorBlendStateCreateInfo;
|
||||
}
|
||||
|
||||
VkPipelineDepthStencilStateCreateInfo vkTools::initializers::pipelineDepthStencilStateCreateInfo(
|
||||
VkBool32 depthTestEnable,
|
||||
VkBool32 depthWriteEnable,
|
||||
VkCompareOp depthCompareOp)
|
||||
{
|
||||
VkPipelineDepthStencilStateCreateInfo pipelineDepthStencilStateCreateInfo = {};
|
||||
pipelineDepthStencilStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
|
||||
pipelineDepthStencilStateCreateInfo.depthTestEnable = depthTestEnable;
|
||||
pipelineDepthStencilStateCreateInfo.depthWriteEnable = depthWriteEnable;
|
||||
pipelineDepthStencilStateCreateInfo.depthCompareOp = depthCompareOp;
|
||||
pipelineDepthStencilStateCreateInfo.front = pipelineDepthStencilStateCreateInfo.back;
|
||||
pipelineDepthStencilStateCreateInfo.back.compareOp = VK_COMPARE_OP_ALWAYS;
|
||||
return pipelineDepthStencilStateCreateInfo;
|
||||
}
|
||||
|
||||
VkPipelineViewportStateCreateInfo vkTools::initializers::pipelineViewportStateCreateInfo(
|
||||
uint32_t viewportCount,
|
||||
uint32_t scissorCount,
|
||||
VkPipelineViewportStateCreateFlags flags)
|
||||
{
|
||||
VkPipelineViewportStateCreateInfo pipelineViewportStateCreateInfo = {};
|
||||
pipelineViewportStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
|
||||
pipelineViewportStateCreateInfo.viewportCount = viewportCount;
|
||||
pipelineViewportStateCreateInfo.scissorCount = scissorCount;
|
||||
pipelineViewportStateCreateInfo.flags = flags;
|
||||
return pipelineViewportStateCreateInfo;
|
||||
}
|
||||
|
||||
VkPipelineMultisampleStateCreateInfo vkTools::initializers::pipelineMultisampleStateCreateInfo(
|
||||
VkSampleCountFlagBits rasterizationSamples,
|
||||
VkPipelineMultisampleStateCreateFlags flags)
|
||||
{
|
||||
VkPipelineMultisampleStateCreateInfo pipelineMultisampleStateCreateInfo = {};
|
||||
pipelineMultisampleStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
|
||||
pipelineMultisampleStateCreateInfo.rasterizationSamples = rasterizationSamples;
|
||||
pipelineMultisampleStateCreateInfo.flags = flags;
|
||||
return pipelineMultisampleStateCreateInfo;
|
||||
}
|
||||
|
||||
VkPipelineDynamicStateCreateInfo vkTools::initializers::pipelineDynamicStateCreateInfo(
|
||||
const VkDynamicState * pDynamicStates,
|
||||
uint32_t dynamicStateCount,
|
||||
VkPipelineDynamicStateCreateFlags flags)
|
||||
{
|
||||
VkPipelineDynamicStateCreateInfo pipelineDynamicStateCreateInfo = {};
|
||||
pipelineDynamicStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
|
||||
pipelineDynamicStateCreateInfo.pDynamicStates = pDynamicStates;
|
||||
pipelineDynamicStateCreateInfo.dynamicStateCount = dynamicStateCount;
|
||||
pipelineDynamicStateCreateInfo.flags = flags;
|
||||
return pipelineDynamicStateCreateInfo;
|
||||
}
|
||||
|
||||
VkPipelineTessellationStateCreateInfo vkTools::initializers::pipelineTessellationStateCreateInfo(uint32_t patchControlPoints)
|
||||
{
|
||||
VkPipelineTessellationStateCreateInfo pipelineTessellationStateCreateInfo = {};
|
||||
pipelineTessellationStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO;
|
||||
pipelineTessellationStateCreateInfo.patchControlPoints = patchControlPoints;
|
||||
return pipelineTessellationStateCreateInfo;
|
||||
}
|
||||
|
||||
VkGraphicsPipelineCreateInfo vkTools::initializers::pipelineCreateInfo(
|
||||
VkPipelineLayout layout,
|
||||
VkRenderPass renderPass,
|
||||
VkPipelineCreateFlags flags)
|
||||
{
|
||||
VkGraphicsPipelineCreateInfo pipelineCreateInfo = {};
|
||||
pipelineCreateInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
|
||||
pipelineCreateInfo.pNext = NULL;
|
||||
pipelineCreateInfo.layout = layout;
|
||||
pipelineCreateInfo.renderPass = renderPass;
|
||||
pipelineCreateInfo.flags = flags;
|
||||
return pipelineCreateInfo;
|
||||
}
|
||||
|
||||
VkComputePipelineCreateInfo vkTools::initializers::computePipelineCreateInfo(VkPipelineLayout layout, VkPipelineCreateFlags flags)
|
||||
{
|
||||
VkComputePipelineCreateInfo computePipelineCreateInfo = {};
|
||||
computePipelineCreateInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
|
||||
computePipelineCreateInfo.layout = layout;
|
||||
computePipelineCreateInfo.flags = flags;
|
||||
return computePipelineCreateInfo;
|
||||
}
|
||||
|
||||
VkPushConstantRange vkTools::initializers::pushConstantRange(
|
||||
VkShaderStageFlags stageFlags,
|
||||
uint32_t size,
|
||||
uint32_t offset)
|
||||
{
|
||||
VkPushConstantRange pushConstantRange = {};
|
||||
pushConstantRange.stageFlags = stageFlags;
|
||||
pushConstantRange.offset = offset;
|
||||
pushConstantRange.size = size;
|
||||
return pushConstantRange;
|
||||
}
|
||||
|
||||
// vi: set sw=2 ts=4 expandtab:
|
||||
@@ -0,0 +1,312 @@
|
||||
/*
|
||||
* Copyright 2016 Sascha Willems - www.saschawillems.de
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Assorted commonly used Vulkan helper functions
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "vulkan/vulkan.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#if defined(_WIN32)
|
||||
#include <windows.h>
|
||||
#include <fcntl.h>
|
||||
#include <io.h>
|
||||
#elif defined(__ANDROID__)
|
||||
#include "vulkanandroid.h"
|
||||
#include <android/asset_manager.h>
|
||||
#endif
|
||||
|
||||
#include <SDL3/SDL_messagebox.h>
|
||||
|
||||
// Custom define for better code readability
|
||||
#define VK_FLAGS_NONE 0
|
||||
// Default fence timeout in nanoseconds
|
||||
#define DEFAULT_FENCE_TIMEOUT 100000000000
|
||||
|
||||
#if 0
|
||||
// Macro to check and display Vulkan return results
|
||||
#define VK_CHECK_RESULT(f) \
|
||||
{ \
|
||||
VkResult res = (f); \
|
||||
if (res != VK_SUCCESS) \
|
||||
{ \
|
||||
std::cout << "Fatal : VkResult is \"" << vkTools::errorString(res) << "\" in " << __FILE__ << " at line " << __LINE__ << std::endl; \
|
||||
assert(res == VK_SUCCESS); \
|
||||
} \
|
||||
}
|
||||
#endif
|
||||
#if defined(DEBUG)
|
||||
#include <sstream>
|
||||
extern const char* appName();
|
||||
#define VK_CHECK_RESULT(f) \
|
||||
{ \
|
||||
VkResult res = (f); \
|
||||
if (res != VK_SUCCESS) \
|
||||
{ \
|
||||
std::stringstream msg; \
|
||||
msg << "Fatal error. VkResult is \"" \
|
||||
<< vkTools::errorString(res) << "\" in " << __FILE__ \
|
||||
<< " at line " << __LINE__ << std::endl; \
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, \
|
||||
appName(), \
|
||||
msg.str().c_str(), \
|
||||
NULL); \
|
||||
assert(res == VK_SUCCESS); \
|
||||
} \
|
||||
}
|
||||
#else
|
||||
#define VK_CHECK_RESULT(f) (void)f
|
||||
#endif
|
||||
|
||||
|
||||
namespace vkTools
|
||||
{
|
||||
// Check if extension is globally available
|
||||
VkBool32 checkGlobalExtensionPresent(const char* extensionName);
|
||||
// Check if extension is present on the given device
|
||||
VkBool32 checkDeviceExtensionPresent(VkPhysicalDevice physicalDevice, const char* extensionName);
|
||||
// Return string representation of a vulkan error string
|
||||
std::string errorString(VkResult errorCode);
|
||||
|
||||
// Selected a suitable supported depth format starting with 32 bit down to 16 bit
|
||||
// Returns false if none of the depth formats in the list is supported by the device
|
||||
VkBool32 getSupportedDepthFormat(VkPhysicalDevice physicalDevice, VkFormat *depthFormat);
|
||||
|
||||
// Put an image memory barrier for setting an image layout on the sub resource into the given command buffer
|
||||
void setImageLayout(
|
||||
VkCommandBuffer cmdbuffer,
|
||||
VkImage image,
|
||||
VkImageAspectFlags aspectMask,
|
||||
VkImageLayout oldImageLayout,
|
||||
VkImageLayout newImageLayout,
|
||||
VkImageSubresourceRange subresourceRange);
|
||||
// Uses a fixed sub resource layout with first mip level and layer
|
||||
void setImageLayout(
|
||||
VkCommandBuffer cmdbuffer,
|
||||
VkImage image,
|
||||
VkImageAspectFlags aspectMask,
|
||||
VkImageLayout oldImageLayout,
|
||||
VkImageLayout newImageLayout);
|
||||
|
||||
// Display error message and exit on fatal error
|
||||
void exitFatal(std::string message, std::string caption);
|
||||
// Load a text file (e.g. GLGL shader) into a std::string
|
||||
std::string readTextFile(const char *fileName);
|
||||
// Load a binary file into a buffer (e.g. SPIR-V)
|
||||
char *readBinaryFile(const char *filename, size_t *psize);
|
||||
|
||||
// Load a SPIR-V shader
|
||||
#if defined(__ANDROID__)
|
||||
VkShaderModule loadShader(AAssetManager* assetManager, const char *fileName, VkDevice device, VkShaderStageFlagBits stage);
|
||||
#else
|
||||
VkShaderModule loadShader(const char *fileName, VkDevice device, VkShaderStageFlagBits stage);
|
||||
#endif
|
||||
|
||||
// Load a GLSL shader
|
||||
// Note : Only for testing purposes, support for directly feeding GLSL shaders into Vulkan
|
||||
// may be dropped at some point
|
||||
VkShaderModule loadShaderGLSL(const char *fileName, VkDevice device, VkShaderStageFlagBits stage);
|
||||
|
||||
// Returns a pre-present image memory barrier
|
||||
// Transforms the image's layout from color attachment to present khr
|
||||
VkImageMemoryBarrier prePresentBarrier(VkImage presentImage);
|
||||
|
||||
// Returns a post-present image memory barrier
|
||||
// Transforms the image's layout back from present khr to color attachment
|
||||
VkImageMemoryBarrier postPresentBarrier(VkImage presentImage);
|
||||
|
||||
// Contains all vulkan objects
|
||||
// required for a uniform data object
|
||||
struct UniformData
|
||||
{
|
||||
VkBuffer buffer;
|
||||
VkDeviceMemory memory;
|
||||
VkDescriptorBufferInfo descriptor;
|
||||
uint32_t allocSize;
|
||||
void* mapped = nullptr;
|
||||
};
|
||||
|
||||
// Destroy (and free) Vulkan resources used by a uniform data structure
|
||||
void destroyUniformData(VkDevice device, vkTools::UniformData *uniformData);
|
||||
|
||||
// Contains often used vulkan object initializers
|
||||
// Save lot of VK_STRUCTURE_TYPE assignments
|
||||
// Some initializers are parameterized for convenience
|
||||
namespace initializers
|
||||
{
|
||||
VkMemoryAllocateInfo memoryAllocateInfo();
|
||||
|
||||
VkCommandBufferAllocateInfo commandBufferAllocateInfo(
|
||||
VkCommandPool commandPool,
|
||||
VkCommandBufferLevel level,
|
||||
uint32_t bufferCount);
|
||||
|
||||
VkCommandPoolCreateInfo commandPoolCreateInfo();
|
||||
VkCommandBufferBeginInfo commandBufferBeginInfo();
|
||||
VkCommandBufferInheritanceInfo commandBufferInheritanceInfo();
|
||||
|
||||
VkRenderPassBeginInfo renderPassBeginInfo();
|
||||
VkRenderPassCreateInfo renderPassCreateInfo();
|
||||
|
||||
VkImageMemoryBarrier imageMemoryBarrier();
|
||||
VkBufferMemoryBarrier bufferMemoryBarrier();
|
||||
VkMemoryBarrier memoryBarrier();
|
||||
|
||||
VkImageCreateInfo imageCreateInfo();
|
||||
VkSamplerCreateInfo samplerCreateInfo();
|
||||
VkImageViewCreateInfo imageViewCreateInfo();
|
||||
|
||||
VkFramebufferCreateInfo framebufferCreateInfo();
|
||||
|
||||
VkSemaphoreCreateInfo semaphoreCreateInfo();
|
||||
VkFenceCreateInfo fenceCreateInfo(VkFenceCreateFlags flags);
|
||||
VkEventCreateInfo eventCreateInfo();
|
||||
|
||||
VkSubmitInfo submitInfo();
|
||||
|
||||
VkViewport viewport(
|
||||
float width,
|
||||
float height,
|
||||
float minDepth,
|
||||
float maxDepth);
|
||||
|
||||
VkRect2D rect2D(
|
||||
int32_t width,
|
||||
int32_t height,
|
||||
int32_t offsetX,
|
||||
int32_t offsetY);
|
||||
|
||||
VkBufferCreateInfo bufferCreateInfo();
|
||||
|
||||
VkBufferCreateInfo bufferCreateInfo(
|
||||
VkBufferUsageFlags usage,
|
||||
VkDeviceSize size);
|
||||
|
||||
VkDescriptorPoolCreateInfo descriptorPoolCreateInfo(
|
||||
uint32_t poolSizeCount,
|
||||
VkDescriptorPoolSize* pPoolSizes,
|
||||
uint32_t maxSets);
|
||||
|
||||
VkDescriptorPoolSize descriptorPoolSize(
|
||||
VkDescriptorType type,
|
||||
uint32_t descriptorCount);
|
||||
|
||||
VkDescriptorSetLayoutBinding descriptorSetLayoutBinding(
|
||||
VkDescriptorType type,
|
||||
VkShaderStageFlags stageFlags,
|
||||
uint32_t binding);
|
||||
|
||||
VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo(
|
||||
const VkDescriptorSetLayoutBinding* pBindings,
|
||||
uint32_t bindingCount);
|
||||
|
||||
VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo(
|
||||
const VkDescriptorSetLayout* pSetLayouts,
|
||||
uint32_t setLayoutCount );
|
||||
|
||||
VkDescriptorSetAllocateInfo descriptorSetAllocateInfo(
|
||||
VkDescriptorPool descriptorPool,
|
||||
const VkDescriptorSetLayout* pSetLayouts,
|
||||
uint32_t descriptorSetCount);
|
||||
|
||||
VkDescriptorImageInfo descriptorImageInfo(
|
||||
VkSampler sampler,
|
||||
VkImageView imageView,
|
||||
VkImageLayout imageLayout);
|
||||
|
||||
VkWriteDescriptorSet writeDescriptorSet(
|
||||
VkDescriptorSet dstSet,
|
||||
VkDescriptorType type,
|
||||
uint32_t binding,
|
||||
VkDescriptorBufferInfo* bufferInfo);
|
||||
|
||||
VkWriteDescriptorSet writeDescriptorSet(
|
||||
VkDescriptorSet dstSet,
|
||||
VkDescriptorType type,
|
||||
uint32_t binding,
|
||||
VkDescriptorImageInfo* imageInfo);
|
||||
|
||||
VkVertexInputBindingDescription vertexInputBindingDescription(
|
||||
uint32_t binding,
|
||||
uint32_t stride,
|
||||
VkVertexInputRate inputRate);
|
||||
|
||||
VkVertexInputAttributeDescription vertexInputAttributeDescription(
|
||||
uint32_t binding,
|
||||
uint32_t location,
|
||||
VkFormat format,
|
||||
uint32_t offset);
|
||||
|
||||
VkPipelineVertexInputStateCreateInfo pipelineVertexInputStateCreateInfo();
|
||||
|
||||
VkPipelineInputAssemblyStateCreateInfo pipelineInputAssemblyStateCreateInfo(
|
||||
VkPrimitiveTopology topology,
|
||||
VkPipelineInputAssemblyStateCreateFlags flags,
|
||||
VkBool32 primitiveRestartEnable);
|
||||
|
||||
VkPipelineRasterizationStateCreateInfo pipelineRasterizationStateCreateInfo(
|
||||
VkPolygonMode polygonMode,
|
||||
VkCullModeFlags cullMode,
|
||||
VkFrontFace frontFace,
|
||||
VkPipelineRasterizationStateCreateFlags flags);
|
||||
|
||||
VkPipelineColorBlendAttachmentState pipelineColorBlendAttachmentState(
|
||||
VkColorComponentFlags colorWriteMask,
|
||||
VkBool32 blendEnable);
|
||||
|
||||
VkPipelineColorBlendStateCreateInfo pipelineColorBlendStateCreateInfo(
|
||||
uint32_t attachmentCount,
|
||||
const VkPipelineColorBlendAttachmentState* pAttachments);
|
||||
|
||||
VkPipelineDepthStencilStateCreateInfo pipelineDepthStencilStateCreateInfo(
|
||||
VkBool32 depthTestEnable,
|
||||
VkBool32 depthWriteEnable,
|
||||
VkCompareOp depthCompareOp);
|
||||
|
||||
VkPipelineViewportStateCreateInfo pipelineViewportStateCreateInfo(
|
||||
uint32_t viewportCount,
|
||||
uint32_t scissorCount,
|
||||
VkPipelineViewportStateCreateFlags flags);
|
||||
|
||||
VkPipelineMultisampleStateCreateInfo pipelineMultisampleStateCreateInfo(
|
||||
VkSampleCountFlagBits rasterizationSamples,
|
||||
VkPipelineMultisampleStateCreateFlags flags);
|
||||
|
||||
VkPipelineDynamicStateCreateInfo pipelineDynamicStateCreateInfo(
|
||||
const VkDynamicState *pDynamicStates,
|
||||
uint32_t dynamicStateCount,
|
||||
VkPipelineDynamicStateCreateFlags flags);
|
||||
|
||||
VkPipelineTessellationStateCreateInfo pipelineTessellationStateCreateInfo(
|
||||
uint32_t patchControlPoints);
|
||||
|
||||
VkGraphicsPipelineCreateInfo pipelineCreateInfo(
|
||||
VkPipelineLayout layout,
|
||||
VkRenderPass renderPass,
|
||||
VkPipelineCreateFlags flags);
|
||||
|
||||
VkComputePipelineCreateInfo computePipelineCreateInfo(
|
||||
VkPipelineLayout layout,
|
||||
VkPipelineCreateFlags flags);
|
||||
|
||||
VkPushConstantRange pushConstantRange(
|
||||
VkShaderStageFlags stageFlags,
|
||||
uint32_t size,
|
||||
uint32_t offset);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// vi: set sw=2 ts=4 expandtab:
|
||||
Reference in New Issue
Block a user