mirror of
https://gitea.windcorp.ru/Wind-Corporation/Progressia.git
synced 2025-04-22 00:40:46 +03:00
292 lines
7.7 KiB
C++
292 lines
7.7 KiB
C++
#pragma once
|
|
|
|
#define GLFW_INCLUDE_VULKAN
|
|
#include <GLFW/glfw3.h>
|
|
#include <cstring>
|
|
#include <functional>
|
|
#include <iostream>
|
|
#include <memory>
|
|
#include <optional>
|
|
#include <unordered_set>
|
|
#include <vector>
|
|
|
|
#define GLM_FORCE_RADIANS
|
|
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
|
|
#include <glm/mat4x4.hpp>
|
|
#include <glm/vec2.hpp>
|
|
#include <glm/vec3.hpp>
|
|
#include <glm/vec4.hpp>
|
|
|
|
#include "../../main/util.h"
|
|
|
|
#include "../../main/logging.h"
|
|
#include "../../main/rendering/graphics_interface.h"
|
|
|
|
namespace progressia::desktop {
|
|
|
|
namespace CstrUtils {
|
|
struct CstrHash {
|
|
std::size_t operator()(const char *s) const noexcept {
|
|
std::size_t acc = 0;
|
|
|
|
while (*s != 0) {
|
|
acc = acc * 31 + *s;
|
|
s++;
|
|
}
|
|
|
|
return acc;
|
|
}
|
|
};
|
|
|
|
struct CstrEqual {
|
|
bool operator()(const char *lhs, const char *rhs) const noexcept {
|
|
return strcmp(lhs, rhs) == 0;
|
|
}
|
|
};
|
|
|
|
struct CstrCompare {
|
|
bool operator()(const char *lhs, const char *rhs) const noexcept {
|
|
return strcmp(lhs, rhs) < 0;
|
|
}
|
|
};
|
|
|
|
using CstrHashSet = std::unordered_set<const char *, CstrHash, CstrEqual>;
|
|
} // namespace CstrUtils
|
|
|
|
class VkObjectWrapper : private progressia::main::NonCopyable {
|
|
// empty
|
|
};
|
|
|
|
constexpr std::size_t MAX_FRAMES_IN_FLIGHT = 2;
|
|
|
|
class VulkanErrorHandler;
|
|
class PhysicalDevice;
|
|
class Surface;
|
|
class Queue;
|
|
class Queues;
|
|
class CommandPool;
|
|
class RenderPass;
|
|
class Pipeline;
|
|
class SwapChain;
|
|
class TextureDescriptors;
|
|
class Adapter;
|
|
class Frame;
|
|
|
|
class Vulkan : public VkObjectWrapper {
|
|
private:
|
|
VkInstance instance = VK_NULL_HANDLE;
|
|
VkDevice device = VK_NULL_HANDLE;
|
|
|
|
std::unique_ptr<VulkanErrorHandler> errorHandler;
|
|
std::unique_ptr<PhysicalDevice> physicalDevice;
|
|
std::unique_ptr<Surface> surface;
|
|
std::unique_ptr<Queues> queues;
|
|
std::unique_ptr<CommandPool> commandPool;
|
|
std::unique_ptr<RenderPass> renderPass;
|
|
std::unique_ptr<Pipeline> pipeline;
|
|
std::unique_ptr<SwapChain> swapChain;
|
|
std::unique_ptr<TextureDescriptors> textureDescriptors;
|
|
std::unique_ptr<Adapter> adapter;
|
|
|
|
std::unique_ptr<progressia::main::GraphicsInterface> gint;
|
|
|
|
std::vector<std::optional<Frame>> frames;
|
|
std::size_t currentFrame;
|
|
bool isRenderingFrame;
|
|
uint64_t lastStartedFrame;
|
|
|
|
public:
|
|
Vulkan(std::vector<const char *> instanceExtensions,
|
|
std::vector<const char *> deviceExtensions,
|
|
std::vector<const char *> validationLayers);
|
|
|
|
~Vulkan();
|
|
|
|
VkInstance getInstance() const;
|
|
VkDevice getDevice() const;
|
|
|
|
const PhysicalDevice &getPhysicalDevice() const;
|
|
Surface &getSurface();
|
|
const Surface &getSurface() const;
|
|
Queues &getQueues();
|
|
const Queues &getQueues() const;
|
|
SwapChain &getSwapChain();
|
|
const SwapChain &getSwapChain() const;
|
|
CommandPool &getCommandPool();
|
|
const CommandPool &getCommandPool() const;
|
|
RenderPass &getRenderPass();
|
|
const RenderPass &getRenderPass() const;
|
|
Pipeline &getPipeline();
|
|
const Pipeline &getPipeline() const;
|
|
TextureDescriptors &getTextureDescriptors();
|
|
const TextureDescriptors &getTextureDescriptors() const;
|
|
Adapter &getAdapter();
|
|
const Adapter &getAdapter() const;
|
|
|
|
Frame *getCurrentFrame();
|
|
const Frame *getCurrentFrame() const;
|
|
|
|
progressia::main::GraphicsInterface &getGint();
|
|
const progressia::main::GraphicsInterface &getGint() const;
|
|
|
|
/*
|
|
* Returns false when the frame should be skipped
|
|
*/
|
|
bool startRender();
|
|
void endRender();
|
|
|
|
uint64_t getLastStartedFrame() const;
|
|
std::size_t getFrameInFlightIndex() const;
|
|
|
|
void waitIdle();
|
|
|
|
VkFormat findSupportedFormat(const std::vector<VkFormat> &, VkImageTiling,
|
|
VkFormatFeatureFlags);
|
|
uint32_t findMemoryType(uint32_t allowedByDevice,
|
|
VkMemoryPropertyFlags desiredProperties);
|
|
|
|
template <typename... Args>
|
|
VkResult call(const char *functionName, Args &&...args) {
|
|
|
|
using FunctionSignature = VkResult(VkInstance, Args...);
|
|
|
|
auto func = reinterpret_cast<FunctionSignature *>(
|
|
vkGetInstanceProcAddr(instance, functionName));
|
|
if (func != nullptr) {
|
|
return func(instance, std::forward<Args>(args)...);
|
|
} else {
|
|
progressia::main::logging::error()
|
|
<< "[Vulkan] [dynVkCall / VkResult]\tFunction not found for "
|
|
"name \""
|
|
<< functionName << "\"";
|
|
// REPORT_ERROR
|
|
return VK_ERROR_EXTENSION_NOT_PRESENT;
|
|
}
|
|
}
|
|
|
|
template <typename... Args>
|
|
VkResult callVoid(const char *functionName, Args &&...args) {
|
|
|
|
using FunctionSignature = void(VkInstance, Args...);
|
|
|
|
auto func = reinterpret_cast<FunctionSignature *>(
|
|
vkGetInstanceProcAddr(instance, functionName));
|
|
if (func != nullptr) {
|
|
func(instance, std::forward<Args>(args)...);
|
|
return VK_SUCCESS;
|
|
} else {
|
|
progressia::main::logging::error()
|
|
<< "[Vulkan] [dynVkCall / void]\tFunction not found for name \""
|
|
<< functionName << "\"";
|
|
// REPORT_ERROR
|
|
return VK_ERROR_EXTENSION_NOT_PRESENT;
|
|
}
|
|
}
|
|
|
|
void handleVkResult(const char *errorMessage, VkResult);
|
|
};
|
|
|
|
class VulkanErrorHandler : public VkObjectWrapper {
|
|
private:
|
|
VkDebugUtilsMessengerEXT debugMessenger;
|
|
Vulkan &vulkan;
|
|
|
|
public:
|
|
VulkanErrorHandler(Vulkan &vulkan);
|
|
|
|
std::unique_ptr<VkDebugUtilsMessengerCreateInfoEXT>
|
|
attachDebugProbe(VkInstanceCreateInfo &);
|
|
void onInstanceReady();
|
|
|
|
// NOLINTNEXTLINE(performance-trivially-destructible): fixing this makes code less readable due to use of macros in implementation
|
|
~VulkanErrorHandler();
|
|
|
|
void handleVkResult(const char *errorMessage, VkResult result);
|
|
};
|
|
|
|
class Surface : public VkObjectWrapper {
|
|
private:
|
|
VkSurfaceKHR vk;
|
|
Vulkan &vulkan;
|
|
|
|
public:
|
|
Surface(Vulkan &vulkan);
|
|
~Surface();
|
|
|
|
VkSurfaceKHR getVk();
|
|
};
|
|
|
|
class Queue {
|
|
private:
|
|
using Test = std::function<bool(VkPhysicalDevice, uint32_t, Vulkan &,
|
|
const VkQueueFamilyProperties &)>;
|
|
|
|
Test test;
|
|
std::optional<uint32_t> familyIndex;
|
|
VkQueue vk;
|
|
|
|
friend class Queues;
|
|
|
|
Queue(Test test);
|
|
|
|
public:
|
|
bool isSuitable(VkPhysicalDevice, uint32_t familyIndex, Vulkan &,
|
|
const VkQueueFamilyProperties &) const;
|
|
|
|
VkQueue getVk() const;
|
|
uint32_t getFamilyIndex() const;
|
|
|
|
void waitIdle() const;
|
|
};
|
|
|
|
class Queues {
|
|
private:
|
|
Queue graphicsQueue;
|
|
Queue presentQueue;
|
|
|
|
public:
|
|
Queues(VkPhysicalDevice physicalDevice, Vulkan &vulkan);
|
|
~Queues();
|
|
|
|
// cppcheck-suppress functionConst; this method modifies the Queue fields
|
|
void storeHandles(VkDevice device);
|
|
|
|
bool isComplete() const;
|
|
|
|
struct CreationRequest {
|
|
float priority;
|
|
std::vector<VkDeviceQueueCreateInfo> queueCreateInfos;
|
|
};
|
|
std::unique_ptr<CreationRequest>
|
|
requestCreation(VkDeviceCreateInfo &) const;
|
|
|
|
const Queue &getGraphicsQueue() const;
|
|
const Queue &getPresentQueue() const;
|
|
};
|
|
|
|
class CommandPool : public VkObjectWrapper {
|
|
|
|
private:
|
|
VkCommandPool pool;
|
|
const Queue &queue;
|
|
Vulkan &vulkan;
|
|
|
|
VkCommandBuffer allocateCommandBuffer();
|
|
void beginCommandBuffer(VkCommandBuffer commandBuffer,
|
|
VkCommandBufferUsageFlags usage);
|
|
|
|
public:
|
|
CommandPool(Vulkan &vulkan, const Queue &queue);
|
|
~CommandPool();
|
|
|
|
VkCommandBuffer beginSingleUse();
|
|
void runSingleUse(VkCommandBuffer, bool waitIdle = false);
|
|
|
|
VkCommandBuffer allocateMultiUse();
|
|
VkCommandBuffer beginMultiUse();
|
|
void submitMultiUse(VkCommandBuffer, bool waitIdle = false);
|
|
void freeMultiUse(VkCommandBuffer);
|
|
};
|
|
|
|
} // namespace progressia::desktop
|