Restructured file, removed friends, added public interface

This commit is contained in:
matthias@arch 2022-10-21 22:00:06 +02:00
parent 3a6ce0217a
commit 5d254bc513
2 changed files with 2219 additions and 2393 deletions

File diff suppressed because it is too large Load Diff

View File

@ -11,6 +11,7 @@
#include "vertex.hpp"
#include "shape.hpp"
#include "vulkan_util.hpp"
#include "vulkan_allocator.hpp"
#include <gz-util/log.hpp>
@ -23,11 +24,10 @@
#include <vector>
namespace gz::vk {
constexpr bool throwExceptionOnValidationError = true;
constexpr gz::Color VULKAN_MESSAGE_PREFIX_COLOR = gz::Color::BO_BLUE;
constexpr gz::Color VULKAN_MESSAGE_TIME_COLOR = gz::Color::BLUE;
const std::string CONFIG_FILE = "vulkan.conf";
const int MAX_FRAMES_IN_FLIGHT = 3;
#define SettingsTypes uint32_t, bool, float
/* const std::string MODEL_PATH = "models/gazebo-3d-model/gazebo.obj"; */
@ -40,6 +40,7 @@ namespace gz::vk {
{ "framerate", "60" },
{ "anisotropy_enable", "false" },
{ "max_anisotropy", "1" },
{ "max_frames_in_flight", "3" },
/* { "", "" } */
};
@ -51,14 +52,22 @@ namespace gz::vk {
constexpr VkAllocationCallbacks* NO_ALLOC = nullptr;
const size_t VERTEX_BUFFER_SIZE = 512;
const size_t INDEX_BUFFER_SIZE = 512;
enum InstanceCommandPool {
POOL_GRAPHICS, POOL_TRANSFER
};
/**
* @nosubgrouping
*/
class VulkanInstance {
friend class Renderer;
friend class Renderer2D;
friend class Renderer3D;
friend class TextureManager;
friend class TextureAtlas;
public:
VulkanInstance(gz::SettingsManagerCreateInfo<SettingsTypes>smCI) : settings(smCI) {};
/**
* @name Public Interface: For the "creator" of the instance
* @details
* These functions will probably called from your `main` function and loop.
*/
/// @{
VulkanInstance(gz::SettingsManagerCreateInfo<SettingsTypes>smCI) : settings(smCI), allocator(*this) {};
/**
* @brief Initializes the vulkan instance
* @details
@ -87,9 +96,19 @@ namespace gz::vk {
* -# Call endFrameDraw
*/
uint32_t beginFrameDraw();
void submitThisFrame(VkCommandBuffer& cmdBuffer);
void endFrameDraw(uint32_t imageIndex);
GLFWwindow* window;
void cleanup();
const GLFWwindow* getWindow() const { return window; }
/// @}
/**
* @name Public Interface: init and cleanup
* @details
* These functions can be used by other parts of your program that use this instance,
* like a renderer.
*/
public:
/// @{
/**
* @brief Destroys everything that was initalized in init
* @details
@ -99,182 +118,91 @@ namespace gz::vk {
* -# destroys the window
* -# calls glfwTerminate()
*/
void cleanup();
void registerCleanupCallback(std::function<void()> callbackF);
void registerSwapChainRecreateCallback(std::function<void()> callbackF);
/// The frame in the swap chain that is currently drawn to
uint32_t currentFrame = 0;
//
// SETTINGS
//
gz::SettingsManager<SettingsTypes> settings;
private:
std::vector<VkCommandBuffer> commandBuffersToSubmitThisFrame;
void createWindow();
std::vector<std::function<void()>> cleanupCallbacks;
std::vector<std::function<void()>> scRecreateCallbacks;
//
// INSTANCE
//
VkInstance instance;
/**
* @brief Create the vulkan instance
* @brief Register vulkan handles used by another object
* @details
* -# check if validationLayers are available (if enabled)
* -# create instance with info
* -# check if all extensions required by glfw are available
* Use this function to register vulkan handles used by another object.
* When a @ref debugLog "validation message" contains one of these handles, the owner can
* be determined and added to the message.
* This makes the debugging process easier when multiple objects use the same type of vulkan handle.
*/
void createInstance();
static void registerObjectUsingVulkan(const ObjectUsingVulkan& obj);
/// @}
//
// PHYSICAL DEVICE
//
VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
/// all the properties of the selected physcial device
VkPhysicalDeviceProperties phDevProperties;
/// all the features that the selected physical device supports
PhysicalDeviceFeatures phDevFeatures;
/**
* @brief Get rhe required physical device features
* @details
* The required features are:
* - synchronization2
*/
PhysicalDeviceFeatures getRequiredFeatures() const;
/**
* @brief Assign the physicalDevice handle to the @ref rateDevice "best rated" GPU
* @details
* After this method, physicalDevice, phDevProperties and phDevFeatures will be initialized
*/
void selectPhysicalDevice();
/**
* @brief Assigns a score to a physical device.
* @details
* score = GPU_TYPE_FACTOR + VRAM_SIZE (in MB) + SUPPORTED_QUEUES_FACTOR + FEATURES_FACTORS
* A GPU is unsuitable and gets score 0 if it does not:
* - have the needed queue families
* - support all necessary extensions
* - have swap chain support
*/
unsigned int rateDevice(VkPhysicalDevice device);
/**
* @brief Set valid values for the SettingsManager according to phDevFeatures and phDevProperties
* @details
* Must be called after selectPhysicalDevice and before createLogicalDevice
* Sets valid values for:
* - anisotropy_enable
* - max_anisotropy
*
*/
void setValidSettings();
/**
* @brief Find the best of the supported formats
* @param candidates Candidate format, from best to worst
* @returns The first format from candidates that is supported
*/
VkFormat findSupportedFormat(const std::vector<VkFormat>& candidates, VkImageTiling tiling, VkFormatFeatureFlags features);
/**
* @name Public Interface: Various utility
* @details
* These functions can be used by other parts of your program that use this instance,
* like a renderer.
*/
public:
/// @{
const VkDevice& getDevice() const { return device; }
const SettingsManager<SettingsTypes>& getSettings() const { return settings; }
const VkExtent2D& getScExtent() const { return scExtent; }
const std::vector<VkImage>& getScImages() const { return scImages; }
const VkFormat& getScImageFormat() const { return scImageFormat; }
//
// QUEUE
//
QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device);
QueueFamilyIndices qFamilyIndices;
VkQueue graphicsQ;
VkQueue presentQ;
// if possible, use dedicated transferQ. if not available, transferQ = graphicsQ
VkQueue transferQ;
//
// LOGICAL DEVICE
//
VkDevice device;
/**
* @details
* request anisotropic sampling feature
* @brief Get the maximum number of frames in flight
* @see MAX_FRAMES_IN_FLIGHT
*/
void createLogicalDevice();
const uint32_t getMaxFramesInFlight() const { return MAX_FRAMES_IN_FLIGHT; }
/**
* @brief Get the frame that is currently in flight
* @see currentFrame
*/
const uint32_t getCurrentFrame() const { return currentFrame; }
//
// SURFACE
//
VkSurfaceKHR surface;
void createSurface();
//
// SWAP CHAIN
//
VkSwapchainKHR swapChain;
std::vector<VkImage> scImages;
std::vector<VkImageView> scImageViews;
VkFormat scImageFormat;
VkExtent2D scExtent;
void submitThisFrame(VkCommandBuffer& cmdBuffer);
SwapChainSupport querySwapChainSupport(VkPhysicalDevice device);
/**
* @todo Rate formats if preferred is not available
*/
VkSurfaceFormatKHR selectSwapChainSurfaceFormat(const std::vector<VkSurfaceFormatKHR>& availableFormats);
/**
* @todo Check settings for preferred mode
*/
VkPresentModeKHR selectSwapChainPresentMode(const std::vector<VkPresentModeKHR>& availableModes);
VkExtent2D selectSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities);
void createSwapChain();
void createSwapChainImageViews();
void cleanupSwapChain();
/**
* @brief Recreate the swap chain for the current window size
* @brief Copy from srcBuffer to dstBuffer
* @details
* Does:
* -# get new window dimensions
* - blocks while dimensions == 0 (minimized)
* -# calls cleanupSwapChain
* -# calls createSwapChain
* -# other stuff
* -# calls all callbacks registered with registerSwapChainRecreateCallback
* Uses @ref beginSingleTimeCommands() "a single time command buffer" from commandPoolTransfer.
*/
void recreateSwapChain();
//
// IMAGE VIEW
//
void copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size);
/**
* @brief Create a 2D imageView with format for image.
* @brief Begin a command buffer that is going to be used once
* @param commandPool: The command pool from which the buffer should be allocated
*/
void createImageView(VkFormat format, VkImage& image, VkImageView& imageView, VkImageAspectFlags aspectFlags);
//
// RENDER PASS
//
/* VkRenderPass renderPassBegin; */
/* VkRenderPass renderPassEnd; */
VkCommandBuffer beginSingleTimeCommands(InstanceCommandPool commandPool);
/**
* @brief Create the render pass
* @details
* The subpass will contain the following attachments
* -# color attachment
* -# depth stencil attachment
* @brief Submit cmdBuffer on the queue and free it afterwards
* @param cmdBuffer: Command buffer returned by beginSingleTimeCommands()
* @param commandPool: The same pool as passed to beginSingleTimeCommands()
*/
/* void createRenderPassBegin(); */
/* void createRenderPassEnd(); */
//
// DESCRIPTORS
//
/* VkDescriptorSetLayout descriptorSetLayout; */
void endSingleTimeCommands(VkCommandBuffer cmdBuffer, InstanceCommandPool commandPool);
/// @}
/**
* @name Public Interface: Vulkan object creation/descruction
* @details
* These functions aim at making creation and desctruction of common vulkan objects easier
* and can be used by other parts of your program that use this instance,
* like a renderer.
*/
public:
/// @{
/**
* @brief Create MAX_FRAMES_IN_FLIGHT command buffers from the commandPoolGraphics
*/
void createCommandBuffers(std::vector<VkCommandBuffer>& commandBuffers);
/**
* @brief Destroy all command buffers (must be from commandPoolGraphics)
*/
void destroyCommandBuffers(std::vector<VkCommandBuffer>& commandBuffers);
/**
* @brief Create a descriptor layout with bindings
*/
void createDescriptorSetLayout(std::vector<VkDescriptorSetLayoutBinding> bindings, VkDescriptorSetLayout& layout);
/**
* @}
*/
//
// PIPELINE
//
/* void createGraphicsPipeline(); */
VkShaderModule createShaderModule(const std::vector<char>& code);
/**
* @brief Create a new graphics pipeline
* @param vertexShader Path to the SPIR-V vertex shader
@ -283,6 +211,8 @@ namespace gz::vk {
* @param renderPass The (already created) render pass
* @param pipelineLayout Pipeline layout handle to bind
* @param pipeline Pipeline handle to bind
* @param T the type of Vertex to use
*
* @details
* Create a pipeline with:
* - 2 shader stages: vertex and fragment shader
@ -292,95 +222,83 @@ namespace gz::vk {
* - rasterizer:
* - triangles are filled with the colors from the vertex (VK_POLYGON_FILL)
* - counter clockwise front face (VK_FRONT_FACE_COUNTER_CLOCKWISE)
*
*
*/
template<VertexType T>
void createGraphicsPipeline(const std::string& vertexShader, const std::string& fragmentShader, std::vector<VkDescriptorSetLayout>& descriptorSetLayouts, bool useDepthStencil, VkRenderPass& renderPass, Pipeline& pipeline);
//
// FONT
//
// TODO
/* VkPipeline fontPipeline; */
/* VkPipelineLayout fontPipelineLayout; */
/* void createFontPipeline(); */
//
// BUFFERS
//
void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory, VkSharingMode sharingMode=VK_SHARING_MODE_EXCLUSIVE, std::vector<uint32_t>* qFamiliesWithAccess=nullptr);
uint32_t findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties);
VkShaderModule createShaderModule(const std::vector<char>& code);
/**
* @brief Begin a command buffer that is going to be used once
* @param commandPool: The command pool from which the buffer should be allocated
* @brief Allocate memory, create a buffer and bind buffer to memory
* @param size The size of the buffer
* @param buffer The (null) handle to the buffer
* @param bufferMemory Reference to an (uninitialized) MemoryInfo struct. It will be valid when the function returns
* @param properties Properties that the buffers memory needs to satisfy
*/
VkCommandBuffer beginSingleTimeCommands(VkCommandPool commandPool);
void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, MemoryInfo& bufferMemory, VkSharingMode sharingMode=VK_SHARING_MODE_EXCLUSIVE, std::vector<uint32_t>* qFamiliesWithAccess=nullptr);
/**
* @brief Submit cmdBuffer on the queue and free it afterwards
* @param cmdBuffer: Command buffer returned by beginSingleTimeCommands()
* @param q: The queue belonging to the commandPool parameter from beginSingleTimeCommands()
* @brief Destroy buffer and free memory
*/
void endSingleTimeCommands(VkCommandBuffer cmdBuffer, VkCommandPool commandPool, VkQueue q);
/**
* @brief Copy from srcBuffer to dstBuffer
*/
void copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size);
//
// VERTEX BUFFER
//
void destroyBuffer(VkBuffer& buffer, MemoryInfo& bufferMemory);
/**
* @brief Create a vertex buffer
* @param vertexCount Number of vertices the buffer should contain
* @todo Basically the same as createIndexBuffer
* @param vertexBuffer The (null) handle to the buffer
* @param vertexBufferMemory Reference to an (uninitialized) MemoryInfo struct. It will be valid when the function returns
* @details
* The function is instantiated for Vertex2D and Vertex3D
*/
template<VertexType VertexT>
void createVertexBuffer(size_t vertexCount, VkBuffer& vertexBuffer, VkDeviceMemory& vertexBufferMemory, VkDeviceSize& vertexBufferSize);
//
// INDEX BUFFER
//
void createVertexBuffer(size_t vertexCount, VkBuffer& vertexBuffer, MemoryInfo& vertexBufferMemory, VkDeviceSize& vertexBufferSize);
/**
* @brief Create an index buffer
* @param indexCount Number of indices the buffer should contain
* @param indexBuffer The (null) handle to the buffer
* @param indexBufferMemory Reference to an (uninitialized) MemoryInfo struct. It will be valid when the function returns
* @details
* The function is instantiated for uint16_t and uint32_t
*/
template<SupportedIndexType T>
void createIndexBuffer(size_t indexCount, VkBuffer& indexBuffer, VkDeviceMemory& indexBufferMemory, VkDeviceSize& indexBufferSize);
//
// UNIFORM BUFFER
//
void createIndexBuffer(size_t indexCount, VkBuffer& indexBuffer, MemoryInfo& indexBufferMemory, VkDeviceSize& indexBufferSize);
/**
* @brief Create MAX_FRAMES_IN_FLIGHT uniform buffers
*/
template<typename T>
void createUniformBuffers(std::vector<VkBuffer>& uniformBuffers, std::vector<VkDeviceMemory>& uniformBuffersMemory);
/* void createUniformBuffers(); */
//
// FRAMEBUFFERS
//
void createUniformBuffers(std::vector<VkBuffer>& uniformBuffers, std::vector<MemoryInfo>& uniformBuffersMemory);
/**
* @brief Create as many framebuffers as the swap chain has images, with the extent of the swap chain
*/
void createFramebuffers(std::vector<VkFramebuffer>& framebuffers, std::vector<VkImageView>& imageViews, VkRenderPass& renderPass);
/**
* @brief Destroy the framebuffers
*/
void destroyFramebuffers(std::vector<VkFramebuffer>& framebuffers);
void createFramebuffers(std::vector<VkFramebuffer>& framebuffers, std::vector<VkImageView>& imageViews, VkRenderPass& renderPass);
bool frameBufferResized = false;
static void frameBufferResizedCallback(GLFWwindow* window, int width, int height);
//
// COMMAND POOL
//
VkCommandPool commandPoolGraphics;
VkCommandPool commandPoolTransfer;
void createCommandPools();
//
// COMMAND BUFFER
//
/**
* @brief Create MAX_FRAMES_IN_FLIGHT command buffers from the commandPoolGraphics
* @brief Create a texture sampler
* @todo add options and documentation
*/
void createCommandBuffers(std::vector<VkCommandBuffer>& commandBuffers);
/**
* @brief Destroy all command buffers (must be from commandPoolGraphics)
*/
void destroyCommandBuffers(std::vector<VkCommandBuffer>& commandBuffers);
std::vector<VkCommandBuffer> commandBuffersBegin;
std::vector<VkCommandBuffer> commandBuffersEnd;
//
// IMAGE UTILITY
//
void createTextureSampler(VkSampler& sampler);
/**
* @name Public Interface: Image utility
* @details
* These functions can be used by other parts of your program that use this instance,
* like a renderer.
*/
/// @{
/**
* @brief Create the image, allocate the imageMemory and bind the image to it
*/
void createImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags memoryProperties, VkImage& image, VkDeviceMemory& imageMemory);
/**
* @brief Create a 2D imageView with format for image.
*/
void createImageView(VkFormat format, VkImage& image, VkImageView& imageView, VkImageAspectFlags aspectFlags);
void copyBufferToImage(VkBuffer buffer, VkImage image, int32_t offsetX, int32_t offsetY, uint32_t width, uint32_t height);
/**
* @todo make a version using vkCmdResolveImage for multisampled images
@ -416,69 +334,15 @@ namespace gz::vk {
* @throws VkUserError if the transition is not supported.
*/
void transitionImageLayout(VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout, VkCommandBuffer* cmdBuffer=nullptr);
//
// DEPTH
//
/* VkImage depthImage; */
/* VkDeviceMemory depthImageMemory; */
/* VkImageView depthImageView; */
VkFormat findDepthFormat();
/* void createDepthImageAndView(); */
//
// TEXTURES
//
/* VkImage textureImage; */
/* VkDeviceMemory textureImageMemory; */
/* VkImageView textureImageView; */
/* VkSampler textureSampler; */
/// Load textures/image.png
/* void createTextureImageAndView(); */
void createTextureSampler(VkSampler& sampler);
//
// SYNCHRONIZATION
//
std::vector<VkSemaphore> imageAvailableSemaphores;
std::vector<VkSemaphore> renderFinishedSemaphores;
std::vector<VkFence> inFlightFences;
void createSyncObjects();
//
// SHADERS
//
static std::vector<char> readFile(const std::string& filename);
//
// MODELS
//
void loadModel();
VerticesAndIndices<uint32_t> model;
//
// DEBUG
//
void setupDebugMessenger();
void cleanupDebugMessenger();
VkDebugUtilsMessengerEXT debugMessenger;
static gz::Log vLog;
public:
//
// UTILITY
//
/**
* @name Debugging
*/
/// @{
/**
* @name Handle ownership
* @brief Retrieve the owner of a vulkan handle
* @see ObjectUsingVulkan
*/
/// @{
static std::vector<ObjectUsingVulkan> objectsUsingVulkan;
static void registerObjectUsingVulkan(const ObjectUsingVulkan& obj);
static void getHandleOwnerString(std::string_view message);
static std::string handleOwnerString;
static bool lastColorWhite;
/// @}
/**
* @name Debugging (validation messages)
*/
public:
/// @{
/**
* @brief Log messages from validation layers with the Apps logger
* @details
@ -486,7 +350,7 @@ namespace gz::vk {
* - Validation and performace messages usually have a bracket containing a VUID,
* these brackets are printed in yellow/red when its a warning/error.
* - Handles printed by the validation layer are checked with objectsUsingVulkan, if found
* the owner of the handle is added to the message @see registerObjectUsingVulkan, ObjectUsingVulkan
* the owner of the handle is added to the message @see registerObjectUsingVulkan(), ObjectUsingVulkan
* - Different message sources (layer, shader, general...) get a prefix with a different color
*/
static VKAPI_ATTR VkBool32 VKAPI_CALL debugLog(
@ -496,29 +360,293 @@ namespace gz::vk {
void* pUserData);
/// @}
private:
/**
* @name Settings
*/
/// @{
gz::SettingsManager<SettingsTypes> settings;
/**
* @brief Set valid values for the SettingsManager according to phDevFeatures and phDevProperties
* @details
* Must be called after selectPhysicalDevice() and before createLogicalDevice()
* Sets valid values for:
* - anisotropy_enable (bool) [features.samplerAnisotropy]
* - max_anisotropy (float) [1-limits.maxSamplerAnisotropy]
* - max_frames_in_flight (uint32_t) [1-4]
*
*/
void setValidSettings();
/**
* @brief The maximum number of frames that can be ready for presentation
* @details
* A higher number can lead to more stable frame rates, but also increases latency.
*
* This variable is assigned from setting "max_frames_in_flight".
* @warning
* Do not use the setting from the settings manager:
* A lot of resources depend on this variable and if it gets changed at runtime, the program would crash.
* Always use this variable (or the @ref getMaxFramesInFlight() "getter").
* Changes to max_frames_in_flight will take effect upon restart.
*/
uint32_t MAX_FRAMES_IN_FLIGHT;
/// @}
/**
* @name Window
*/
/// @{
GLFWwindow* window;
void createWindow();
/// @}
/**
* @name Framebuffers
*/
/// @{
bool frameBufferResized = false;
static void frameBufferResizedCallback(GLFWwindow* window, int width, int height);
/**
* @brief The frame that is currently in flight
* @details
* The value is between 0 and MAX_FRAMES_IN_FLIGHT - 1.
* It specifies which command buffer or uniform buffer to use for drawing the current frame,
* it is not the image index for the current @ref scImages "swap chain image".
*/
uint32_t currentFrame = 0;
/// @}
/**
* @name Cleanup
*/
/// @{
std::vector<std::function<void()>> cleanupCallbacks;
std::vector<std::function<void()>> scRecreateCallbacks;
/// @}
/**
* @name Instance
*/
/// @{
VkInstance instance;
/**
* @brief Create the vulkan instance
* @details
* -# check if validationLayers are available (if enabled)
* -# create instance with info
* -# check if all extensions required by glfw are available
*/
void createInstance();
/// @}
/**
* @name Physical device
*/
/// @{
VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
/// all the properties of the selected physcial device
VkPhysicalDeviceProperties phDevProperties;
/// all the features that the selected physical device supports
PhysicalDeviceFeatures phDevFeatures;
/**
* @brief Get the required physical device features
* @details
* The required features are:
* - synchronization2
*/
PhysicalDeviceFeatures getRequiredFeatures() const;
/**
* @brief Assign the physicalDevice handle to the @ref rateDevice "best rated" GPU
* @details
* After this method, physicalDevice, phDevProperties and phDevFeatures will be initialized
*/
void selectPhysicalDevice();
/**
* @brief Assigns a score to a physical device.
* @details
* score = GPU_TYPE_FACTOR + VRAM_SIZE (in MB) + SUPPORTED_QUEUES_FACTOR + FEATURES_FACTORS
* A GPU is unsuitable and gets score 0 if it does not:
* - have the needed queue families
* - support all necessary extensions
* - have swap chain support
*/
unsigned int rateDevice(VkPhysicalDevice device);
/**
* @brief Find the best of the supported formats
* @param candidates Candidate format, from best to worst
* @returns The first format from candidates that is supported
*/
VkFormat findSupportedFormat(const std::vector<VkFormat>& candidates, VkImageTiling tiling, VkFormatFeatureFlags features);
/// @}
/**
* @name Memory management
*/
/// @{
uint32_t findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties);
VulkanAllocator allocator;
/// @}
/**
* @name Queue
*/
/// @{
QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device);
QueueFamilyIndices qFamilyIndices;
VkQueue graphicsQ;
VkQueue presentQ;
// if possible, use dedicated transferQ. if not available, transferQ = graphicsQ
VkQueue transferQ;
/// @}
/**
* @name Logical device
*/
/// @{
VkDevice device;
/**
* @details
* request anisotropic sampling feature
*/
void createLogicalDevice();
/// @}
/**
* @name Surface
*/
/// @{
VkSurfaceKHR surface;
void createSurface();
/// @}
/**
* @name Swap chain
*/
/// @{
VkSwapchainKHR swapChain;
std::vector<VkImage> scImages;
std::vector<VkImageView> scImageViews;
VkFormat scImageFormat;
VkExtent2D scExtent;
SwapChainSupport querySwapChainSupport(VkPhysicalDevice device);
/**
* @todo Rate formats if preferred is not available
*/
VkSurfaceFormatKHR selectSwapChainSurfaceFormat(const std::vector<VkSurfaceFormatKHR>& availableFormats);
/**
* @todo Check settings for preferred mode
*/
VkPresentModeKHR selectSwapChainPresentMode(const std::vector<VkPresentModeKHR>& availableModes);
VkExtent2D selectSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities);
void createSwapChain();
void createSwapChainImageViews();
void cleanupSwapChain();
/**
* @brief Recreate the swap chain for the current window size
* @details
* Does:
* -# get new window dimensions
* - blocks while dimensions == 0 (minimized)
* -# calls cleanupSwapChain
* -# calls createSwapChain
* -# other stuff
* -# calls all callbacks registered with registerSwapChainRecreateCallback
*/
void recreateSwapChain();
/// @}
/**
* @name Command pools
*/
/// @{
VkCommandPool commandPoolGraphics;
VkCommandPool commandPoolTransfer;
void createCommandPools();
/// @}
/**
* @name Command buffers
*/
/// @{
std::vector<VkCommandBuffer> commandBuffersBegin;
std::vector<VkCommandBuffer> commandBuffersEnd;
/**
* @brief All command buffers to submit on graphics queue for the current frame
* @details
* Use submitThisFrame() to submit a command buffer
*/
std::vector<VkCommandBuffer> commandBuffersToSubmitThisFrame;
/// @}
/**
* @name Synchronization
*/
/// @{
std::vector<VkSemaphore> imageAvailableSemaphores;
std::vector<VkSemaphore> renderFinishedSemaphores;
std::vector<VkFence> inFlightFences;
void createSyncObjects();
/// @}
/**
* @name Shaders
*/
/// @{
/**
* @brief Load a binary file into vector of bytes
* @todo Move to more generic constant, does not need to be member function
*/
static std::vector<char> readFile(const std::string& filename);
/// @}
/**
* @name Models
*/
/// @{
void loadModel(VerticesAndIndices<uint32_t>& model);
/// @}
/**
* @name Utility
*/
/// @{
template<typename T, typename... Args>
VkResult runVkResultFunction(const char* name, Args&&... args) {
auto f = reinterpret_cast<T>(vkGetInstanceProcAddr(instance, name));
if (f == nullptr) {
vLog.error("getVkFunction: Could not get function:", name);
throw std::runtime_error("getVkFunction: Could not get function.");
}
else {
return f(std::forward<Args>(args)...);
}
};
VkResult runVkResultFunction(const char* name, Args&&... args);
template<typename T, typename... Args>
void runVkVoidFunction(const char* name, Args&&... args) {
auto f = reinterpret_cast<T>(vkGetInstanceProcAddr(instance, name));
if (f == nullptr) {
vLog.error("getVkFunction: Could not get function:", name);
throw std::runtime_error("getVkFunction: Could not get function.");
}
else {
f(std::forward<Args>(args)...);
}
}
void runVkVoidFunction(const char* name, Args&&... args);
/**
* @name Debugging: (log and validation debug messenger)
*/
/// @{
void setupDebugMessenger();
void cleanupDebugMessenger();
VkDebugUtilsMessengerEXT debugMessenger;
static gz::Log vLog;
/// @} /// @}
/**
* @name Debugging: (handle ownership)
*/
/// @{
static std::vector<ObjectUsingVulkan> objectsUsingVulkan;
/**
* @brief Search for handles in message and get their owners
* @details
* Searches for handles (hex numbers) in message and sets handleOwnerString.\n
* If message contains no handle, handleOwnerString will be empty.
* If message contains at least one handle, handleOwnerString will be:
* `Handle Ownerships: [ <handle> - <owner>, ...]`
*/
static void getHandleOwnerString(std::string_view message);
static std::string handleOwnerString;
/// Used for alternating the color of validation messages between grey and white
static bool lastColorWhite;
/// @}
}; // VulkanInstance
//
@ -528,7 +656,7 @@ namespace gz::vk {
// UNIFORM BUFFERS
//
template<typename T>
void VulkanInstance::createUniformBuffers(std::vector<VkBuffer>& uniformBuffers, std::vector<VkDeviceMemory>& uniformBuffersMemory) {
void VulkanInstance::createUniformBuffers(std::vector<VkBuffer>& uniformBuffers, std::vector<MemoryInfo>& uniformBuffersMemory) {
VkDeviceSize bufferSize = sizeof(T);
uniformBuffers.resize(MAX_FRAMES_IN_FLIGHT);
@ -537,11 +665,37 @@ namespace gz::vk {
for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
createBuffer(bufferSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, uniformBuffers[i], uniformBuffersMemory[i]);
}
}
}
//
// UTILITY
//
template<typename T, typename... Args>
VkResult VulkanInstance::runVkResultFunction(const char* name, Args&&... args) {
auto f = reinterpret_cast<T>(vkGetInstanceProcAddr(instance, name));
if (f == nullptr) {
vLog.error("getVkFunction: Could not get function:", name);
throw std::runtime_error("getVkFunction: Could not get function.");
}
else {
return f(std::forward<Args>(args)...);
}
};
template<typename T, typename... Args>
void VulkanInstance::runVkVoidFunction(const char* name, Args&&... args) {
auto f = reinterpret_cast<T>(vkGetInstanceProcAddr(instance, name));
if (f == nullptr) {
vLog.error("getVkFunction: Could not get function:", name);
throw std::runtime_error("getVkFunction: Could not get function.");
}
else {
f(std::forward<Args>(args)...);
}
}
}
/**
* @file Creating a vulkan instance
* @todo Write/get allocator for buffers
* @file Vulkan instance
*/