diff --git a/private/raid/application.cpp b/private/raid/application.cpp index 78594da..abef77f 100644 --- a/private/raid/application.cpp +++ b/private/raid/application.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -62,6 +63,12 @@ int stbiEof(void* user) mijin::Stream& stream = *static_cast(user); return stream.isAtEnd(); } + +[[nodiscard]] +constexpr std::uint32_t vkMakeApiVersion(std::uint32_t variant, std::uint32_t major, std::uint32_t minor, std::uint32_t patch) noexcept +{ + return ((variant << 29U) | (major << 22U) | (minor << 12U) | patch); +} } int Application::run(int argc, char** argv) @@ -114,15 +121,26 @@ int Application::run(int argc, char** argv) ImGui::Render(); - glClearColor(0.3f, 0.3f, 0.3f, 1.f); - glClear(GL_COLOR_BUFFER_BIT); - ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + switch (mConfig.graphicsApi) + { + case GraphicsAPI::OPENGL: + gl.ClearColor(0.3f, 0.3f, 0.3f, 1.f); + gl.Clear(GL_COLOR_BUFFER_BIT); + ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + break; + case GraphicsAPI::VULKAN: + MIJIN_TRAP(); // TODO! + break; + } if (imguiIO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { ImGui::UpdatePlatformWindows(); ImGui::RenderPlatformWindowsDefault(); - SDL_GL_MakeCurrent(mWindow, mGLContext); + + if (mConfig.graphicsApi == GraphicsAPI::OPENGL) { + SDL_GL_MakeCurrent(mWindow, gl.context); + } } SDL_GL_SwapWindow(mWindow); @@ -223,23 +241,60 @@ ImTextureID Application::getOrLoadTexture(fs::path path) } // create texture - GLuint texture = 0; - glGenTextures(1, &texture); - glBindTexture(GL_TEXTURE_2D, texture); + ImTextureID texture = 0; + switch (mConfig.graphicsApi) + { + case GraphicsAPI::OPENGL: + { + GLuint glTexture = 0; + gl.GenTextures(1, &glTexture); + gl.BindTexture(GL_TEXTURE_2D, glTexture); - // setup texture - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + // setup texture + gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - // upload image - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData); + // upload image + gl.PixelStorei(GL_UNPACK_ROW_LENGTH, 0); + gl.TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData); - mTextures.emplace(std::move(path), texture); + texture = glTexture; + break; + } + case GraphicsAPI::VULKAN: + MIJIN_TRAP(); // TODO! + break; + } + + if (texture != 0) { + mTextures.emplace(std::move(path), texture); + } return texture; } +void Application::destroyTexture(ImTextureID texture) +{ + auto it = std::ranges::find_if(mTextures, [texture](const auto& entry) { + return entry.second == texture; + }); + MIJIN_ASSERT(it != mTextures.end(), "Invalid texture id."); + mTextures.erase(it); + + switch (mConfig.graphicsApi) + { + case GraphicsAPI::OPENGL: + { + const GLuint asUint = static_cast(texture); + gl.DeleteTextures(1, &asUint); + break; + } + case GraphicsAPI::VULKAN: + MIJIN_TRAP(); // TODO + break; + } +} + void Application::configureImgui() { ImGuiIO& imguiIO = ImGui::GetIO(); @@ -379,9 +434,19 @@ bool Application::init() { return false; } - if (!initGL()) + switch (mConfig.graphicsApi) { - return false; + case GraphicsAPI::OPENGL: + if (!initOpenGL()) + { + return false; + } + break; + case GraphicsAPI::VULKAN: + if (!initVulkan()) + { + return false; + } } if (!initImGui()) { @@ -407,14 +472,29 @@ void Application::cleanup() } ImGui::DestroyContext(); } - for (const auto& [path, texture] : mTextures) - { - const GLuint asUint = static_cast(texture); - glDeleteTextures(1, &asUint); + for (const auto& [_, texture] : mTextures) { + destroyTexture(texture); } - if (mGLContext != nullptr) + switch (mConfig.graphicsApi) { - SDL_GL_DestroyContext(mGLContext); + case GraphicsAPI::OPENGL: + if (gl.context != nullptr) + { + SDL_GL_DestroyContext(gl.context); + } + break; + case GraphicsAPI::VULKAN: + if (vk.device != nullptr) + { + vk.DeviceWaitIdle(vk.device); + vk.DestroyDevice(vk.device, nullptr); + } + if (vk.instance != nullptr) + { + vk.DestroyInstance(vk.instance, nullptr); + } + SDL_Vulkan_UnloadLibrary(); + break; } if (mWindow != nullptr) { @@ -438,48 +518,146 @@ bool Application::initSDL() msgInfo("SDL video driver: {}", SDL_GetCurrentVideoDriver()); - // GL attributes must be set before window creation - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); - SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + SDL_WindowFlags windowFlags = SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIGH_PIXEL_DENSITY; + switch (mConfig.graphicsApi) + { + case GraphicsAPI::OPENGL: + // GL attributes must be set before window creation + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); - // TODO: not sure if these really make sense, but they are in the example - SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); - SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); + // TODO: not sure if these really make sense, but they are in the example + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); + SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); + windowFlags |= SDL_WINDOW_OPENGL; + break; + case GraphicsAPI::VULKAN: + if (!SDL_Vulkan_LoadLibrary(nullptr)) + { + msgError("Error loading Vulkan library: {}.", SDL_GetError()); + return false; + } + windowFlags |= SDL_WINDOW_VULKAN; + break; + } - const SDL_WindowFlags WINDOW_FLAGS = 0 - | SDL_WINDOW_OPENGL - | SDL_WINDOW_RESIZABLE - | SDL_WINDOW_HIGH_PIXEL_DENSITY; mWindow = SDL_CreateWindow( /* title = */ getWindowTitle().c_str(), /* w = */ 1280, /* h = */ 720, - /* flags = */ WINDOW_FLAGS + /* flags = */ windowFlags ); return true; } -bool Application::initGL() +bool Application::initOpenGL() { - mGLContext = SDL_GL_CreateContext(mWindow); + gl.context = SDL_GL_CreateContext(mWindow); if (mWindow == nullptr) { - msgError("Error creating SDL window: {}.", SDL_GetError()); + msgError("Error creating OpenGL context: {}.", SDL_GetError()); return false; } - SDL_GL_MakeCurrent(mWindow, mGLContext); + SDL_GL_MakeCurrent(mWindow, gl.context); SDL_GL_SetSwapInterval(1); // enable vsync, at least for now - glClear = reinterpret_cast(SDL_GL_GetProcAddress("glClear")); - glClearColor = reinterpret_cast(SDL_GL_GetProcAddress("glClearColor")); - glGenTextures = reinterpret_cast(SDL_GL_GetProcAddress("glGenTextures")); - glBindTexture = reinterpret_cast(SDL_GL_GetProcAddress("glBindTexture")); - glTexParameteri = reinterpret_cast(SDL_GL_GetProcAddress("glTexParameteri")); - glPixelStorei = reinterpret_cast(SDL_GL_GetProcAddress("glPixelStorei")); - glTexImage2D = reinterpret_cast(SDL_GL_GetProcAddress("glTexImage2D")); - glDeleteTextures = reinterpret_cast(SDL_GL_GetProcAddress("glDeleteTextures")); + gl.Clear = reinterpret_cast(SDL_GL_GetProcAddress("glClear")); + gl.ClearColor = reinterpret_cast(SDL_GL_GetProcAddress("glClearColor")); + gl.GenTextures = reinterpret_cast(SDL_GL_GetProcAddress("glGenTextures")); + gl.BindTexture = reinterpret_cast(SDL_GL_GetProcAddress("glBindTexture")); + gl.TexParameteri = reinterpret_cast(SDL_GL_GetProcAddress("glTexParameteri")); + gl.PixelStorei = reinterpret_cast(SDL_GL_GetProcAddress("glPixelStorei")); + gl.TexImage2D = reinterpret_cast(SDL_GL_GetProcAddress("glTexImage2D")); + gl.DeleteTextures = reinterpret_cast(SDL_GL_GetProcAddress("glDeleteTextures")); + + return true; +} + +bool Application::initVulkan() +{ + vk.GetInstanceProc = reinterpret_cast(SDL_Vulkan_GetVkGetInstanceProcAddr()); + vk.CreateInstance = reinterpret_cast(vk.GetInstanceProc(nullptr, "vkCreateInstance")); + + const VkApplicationInfo applicationInfo = { + .apiVersion = vkMakeApiVersion(0, 1, 3, 0) // TODO: probably should let the user specify this? + }; + const VkInstanceCreateInfo instanceCreateInfo = { + .pApplicationInfo = &applicationInfo + }; + + if (const VkResult result = vk.CreateInstance(&instanceCreateInfo, nullptr, &vk.instance); result != VK_SUCCESS) + { + msgError("Error creating Vulkan instance: {:x}.", static_cast(result)); + return false; + } + + vk.DestroyInstance = reinterpret_cast(vk.GetInstanceProc(vk.instance, "vkDestroyInstance")); + vk.EnumeratePhysicalDevices = reinterpret_cast(vk.GetInstanceProc(vk.instance, "vkEnumeratePhysicalDevices")); + vk.GetPhysicalDeviceQueueFamilyProperties2 = reinterpret_cast(vk.GetInstanceProc(vk.instance, "vkGetPhysicalDeviceQueueFamilyProperties2")); + vk.CreateDevice = reinterpret_cast(vk.GetInstanceProc(vk.instance, "vkCreateDevice")); + vk.DestroyDevice = reinterpret_cast(vk.GetInstanceProc(vk.instance, "vkDestroyDevice")); + vk.DeviceWaitIdle = reinterpret_cast(vk.GetInstanceProc(vk.instance, "vkDeviceWaitIdle")); + vk.GetDeviceQueue = reinterpret_cast(vk.GetInstanceProc(vk.instance, "vkGetDeviceQueue")); + + // TODO: this is really cheap... (but should be sufficient in most cases) + std::uint32_t physicalDeviceCount = 1; + if (const VkResult result = vk.EnumeratePhysicalDevices(vk.instance, &physicalDeviceCount, &vk.physicalDevice); result != VK_SUCCESS) + { + msgError("Error enumerating Vulkan physical devices: {:x}.", static_cast(result)); + return false; + } + + std::uint32_t queueFamilyPropertyCount = 0; + vk.GetPhysicalDeviceQueueFamilyProperties2(vk.physicalDevice, &queueFamilyPropertyCount, nullptr); + + std::vector queueFamilyProperties; + queueFamilyProperties.resize(queueFamilyPropertyCount); + vk.GetPhysicalDeviceQueueFamilyProperties2(vk.physicalDevice, &queueFamilyPropertyCount, queueFamilyProperties.data()); + + // TODO: this should also check for surface support (but who cares?) + std::uint32_t queueFamilyIndex = 0; + for (; queueFamilyIndex < queueFamilyPropertyCount; ++queueFamilyIndex) + { + if (queueFamilyProperties[queueFamilyIndex].queueFamilyProperties.queueFlags & VK_QUEUE_GRAPHICS_BIT) { + break; + } + } + if (queueFamilyIndex == queueFamilyPropertyCount) + { + msgError("No suitable Vulkan queue family found."); + return false; + } + vk.queueFamilyIndex = queueFamilyIndex; + + static const float queuePriority = 1.0f; + const VkDeviceQueueCreateInfo queueCreateInfo = { + .queueFamilyIndex = vk.queueFamilyIndex, + .queueCount = 1, + .pQueuePriorities = &queuePriority + }; + static const std::array DEVICE_EXTENSIONS = { + "VK_KHR_surface" + }; + const VkDeviceCreateInfo deviceCreateInfo = { + .queueCreateInfoCount = 1, + .pQueueCreateInfos = &queueCreateInfo, + .enabledExtensionCount = static_cast(DEVICE_EXTENSIONS.size()), + .ppEnabledExtensionNames = DEVICE_EXTENSIONS.data() + }; + if (const VkResult result = vk.CreateDevice(vk.physicalDevice, &deviceCreateInfo, nullptr, &vk.device); result != VK_SUCCESS) + { + msgError("Error creating Vulkan device: {:x}.", static_cast(result)); + return false; + } + vk.GetDeviceQueue(vk.device, /* queueFamilyIndex = */ 0, /* queueIndex = */ 0, &vk.queue); + + if (!SDL_Vulkan_CreateSurface(mWindow, vk.instance, nullptr, &vk.surface)) + { + msgError("Error creating SDL Vulkan surface: {}.", SDL_GetError()); + return false; + } return true; } @@ -511,14 +689,22 @@ bool Application::initImGui() ImGui::StyleColorsDark(); // init the backends - if (!ImGui_ImplSDL3_InitForOpenGL(mWindow, mGLContext)) + switch (mConfig.graphicsApi) { - msgError("Error initializing ImGui SDL3 backend."); - return false; - } - if (!ImGui_ImplOpenGL3_Init(IMGUI_GLSL_VERSION)) - { - msgError("Error initializing ImGui OpenGL3 backend."); + case GraphicsAPI::OPENGL: + if (!ImGui_ImplSDL3_InitForOpenGL(mWindow, gl.context)) + { + msgError("Error initializing ImGui SDL3 backend."); + return false; + } + if (!ImGui_ImplOpenGL3_Init(IMGUI_GLSL_VERSION)) + { + msgError("Error initializing ImGui OpenGL3 backend."); + return false; + } + break; + case GraphicsAPI::VULKAN: + MIJIN_TRAP(); // TODO! return false; } diff --git a/public/raid/internal/opengl.hpp b/public/raid/internal/opengl.hpp new file mode 100644 index 0000000..88a93a2 --- /dev/null +++ b/public/raid/internal/opengl.hpp @@ -0,0 +1,45 @@ + +#pragma once + +#if !defined(RAID_INTERNAL_OPENGL_HPP_INCLUDED) +#define RAID_INTERNAL_OPENGL_HPP_INCLUDED 1 + +#include + +namespace raid +{ +struct MixinOpenGLApplication +{ + using GLbitfield = std::uint32_t; + using GLint = std::int32_t; + using GLuint = std::uint32_t; + using GLsizei = std::int32_t; + using GLenum = std::uint32_t; + using GLfloat = float; + + using glClear_fn_t = void (*)(GLbitfield); + using glClearColor_fn_t = void (*)(GLfloat, GLfloat, GLfloat, GLfloat); + using glGenTextures_fn_t = void (*)(GLsizei, GLuint*); + using glBindTexture_fn_t = void (*)(GLenum, GLuint); + using glTexParameteri_fn_t = void (*)(GLenum, GLenum, GLint); + using glPixelStorei_fn_t = void (*)(GLenum, GLint); + using glTexImage2D_fn_t = void (*)(GLenum, GLint, GLint, GLsizei, GLsizei, GLint, GLenum, GLenum, const void*); + using glDeleteTextures_fn_t = void (*)(GLsizei, const GLuint*); + + struct OpenGLData + { + SDL_GLContext context; + + glClear_fn_t Clear; + glClearColor_fn_t ClearColor; + glGenTextures_fn_t GenTextures; + glBindTexture_fn_t BindTexture; + glTexParameteri_fn_t TexParameteri; + glPixelStorei_fn_t PixelStorei; + glTexImage2D_fn_t TexImage2D; + glDeleteTextures_fn_t DeleteTextures; + }; +}; +} // namespace raid + +#endif // !defined(RAID_INTERNAL_OPENGL_HPP_INCLUDED) diff --git a/public/raid/internal/vulkan.hpp b/public/raid/internal/vulkan.hpp new file mode 100644 index 0000000..025ef90 --- /dev/null +++ b/public/raid/internal/vulkan.hpp @@ -0,0 +1,176 @@ + +#pragma once + +#if !defined(RAID_INTERNAL_VULKAN_HPP_INCLUDED) +#define RAID_INTERNAL_VULKAN_HPP_INCLUDED 1 + +#include + +#if !defined(RAID_USE_VULKAN_H) +#define RAID_USE_VULKAN_H 0 +#endif + +#if RAID_USE_VULKAN_H +#include +#endif + +namespace raid +{ +struct MixinVulkanApplication +{ +#if !RAID_USE_VULKAN_H + // flags + using VkFlags = std::uint32_t; + using VkInstanceCreateFlags = VkFlags; + using VkQueueFlags = VkFlags; + using VkDeviceCreateFlags = VkFlags; + using VkDeviceQueueCreateFlags = VkFlags; + + // enums + enum VkResult + { + VK_SUCCESS = 0 + }; + enum VkStructureType + { + VK_STRUCTURE_TYPE_APPLICATION_INFO = 0, + VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO = 1, + VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO = 2, + VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO = 3, + VK_STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2 = 1000059005 + }; + enum VkSystemAllocationScope {}; + enum VkInternalAllocationType {}; + enum VkQueueFlagBits : VkFlags + { + VK_QUEUE_GRAPHICS_BIT = 0x00000001 + }; + + // handles + using VkDevice = struct VkDevice_*; + using VkQueue = struct VkQueue_*; + + // structs + struct VkExtent3D + { + std::uint32_t width = 0; + std::uint32_t height = 0; + std::uint32_t depth = 0; + }; + + struct VkApplicationInfo + { + VkStructureType sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + const void* pNext = nullptr; + const char* pApplicationNane = nullptr; + std::uint32_t applicationVersion = 0; + const char* pEngineName = nullptr; + std::uint32_t engineVersion = 0; + std::uint32_t apiVersion = 0; + }; + struct VkInstanceCreateInfo + { + VkStructureType sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + const void* pNext = nullptr; + VkInstanceCreateFlags flags = 0; + const VkApplicationInfo* pApplicationInfo = nullptr; + std::uint32_t enabledLayerCount = 0; + const char* const* ppEnabledLayerNames = nullptr; + std::uint32_t enabledExtensionCount = 0; + const char* const* ppEnabledExtensionNames = nullptr; + }; + + struct VkQueueFamilyProperties + { + VkQueueFlags queueFlags = 0; + std::uint32_t queueCount = 0; + std::uint32_t timestampValidBits = 0; + VkExtent3D minImageTransferGranularity = {}; + }; + + struct VkQueueFamilyProperties2 + { + VkStructureType sType = VK_STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2; + void* pNext = nullptr; + VkQueueFamilyProperties queueFamilyProperties = {}; + }; + + struct VkDeviceQueueCreateInfo + { + VkStructureType sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + const void* pNext = nullptr; + VkDeviceQueueCreateFlags flags = 0; + std::uint32_t queueFamilyIndex = 0; + std::uint32_t queueCount = 0; + const float* pQueuePriorities = nullptr; + }; + + struct VkPhysicalDeviceFeatures; + struct VkDeviceCreateInfo + { + VkStructureType sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + const void* pNext = nullptr; + VkDeviceCreateFlags flags; + std::uint32_t queueCreateInfoCount = 0; + const VkDeviceQueueCreateInfo* pQueueCreateInfos = nullptr; + std::uint32_t enabledLayerCount = 0; + const char* const* ppEnabledLayerNames = nullptr; + uint32_t enabledExtensionCount = 0; + const char* const* ppEnabledExtensionNames = nullptr; + const VkPhysicalDeviceFeatures* pEnabledFeatures = nullptr; + }; + + // Vulkan function pointer types + using PFN_vkVoidFunction = void (*)(); + // TODO: VKAPI_PTR? + using PFN_vkAllocationFunction = void* (*)(void* pUserData, std::size_t size, std::size_t alignment, VkSystemAllocationScope allocationScope); + using PFN_vkReallocationFunction = void* (*)(void* pUserData, void* pOriginal, std::size_t size, std::size_t alignment, VkSystemAllocationScope allocationScope); + using PFN_vkFreeFunction = void (*)(void* pUserData, void* pMemory); + using PFN_vkInternalAllocationNotification = void (*)(void* pUserData, std::size_t size, VkInternalAllocationType allocationType, VkSystemAllocationScope allocationScope); + using PFN_vkInternalFreeNotification = void (*)(void* pUserData, std::size_t size, VkInternalAllocationType allocationType, VkSystemAllocationScope allocationScope); + struct VkAllocationCallbacks + { + void* pUserData = nullptr; + PFN_vkAllocationFunction pfnAllocation = nullptr; + PFN_vkReallocationFunction pfnReallocation = nullptr; + PFN_vkFreeFunction pfnFree = nullptr; + PFN_vkInternalAllocationNotification pfnInternalAllocation = nullptr; + PFN_vkInternalFreeNotification pfnInternalFree = nullptr; + }; + + // function pointers + using vkGetInstanceProcAddr_fn_t = PFN_vkVoidFunction (*)(VkInstance instance, const char* pName); + using vkCreateInstance_fn_t = VkResult (*)(const VkInstanceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkInstance* pInstance); + using vkDestroyInstance_fn_t = void (*)(VkInstance instance, const VkAllocationCallbacks* pAllocator); + using vkEnumeratePhysicalDevices_fn_t = VkResult (*)(VkInstance instance, std::uint32_t* pPhysicalDeviceCount, VkPhysicalDevice* pPhysicalDevices); + using vkGetPhysicalDeviceQueueFamilyProperties2_fn_t = void (*)(VkPhysicalDevice physicalDevice, std::uint32_t* pQueueFamilyPropertyCount, VkQueueFamilyProperties2* pQueueFamilyProperties); + using vkCreateDevice_fn_t = VkResult (*)(VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDevice* pDevice); + using vkDestroyDevice_fn_t = void (*)(VkDevice device, const VkAllocationCallbacks* pAllocator); + using vkDeviceWaitIdle_fn_t = VkResult (*)(VkDevice device); + using vkGetDeviceQueue_fn_t = void (*)(VkDevice device, std::uint32_t queueFamilyIndex, std::uint32_t queueIndex, VkQueue* pQueue); +#endif // !RAID_USE_VULKAN_H + + struct VulkanData + { + VkInstance instance; + VkPhysicalDevice physicalDevice; + VkDevice device; + VkQueue queue; + VkSurfaceKHR surface; + + vkGetInstanceProcAddr_fn_t GetInstanceProc; + vkCreateInstance_fn_t CreateInstance; + vkDestroyInstance_fn_t DestroyInstance; + vkEnumeratePhysicalDevices_fn_t EnumeratePhysicalDevices; + vkGetPhysicalDeviceQueueFamilyProperties2_fn_t GetPhysicalDeviceQueueFamilyProperties2; + vkCreateDevice_fn_t CreateDevice; + vkDestroyDevice_fn_t DestroyDevice; + vkDeviceWaitIdle_fn_t DeviceWaitIdle; + vkGetDeviceQueue_fn_t GetDeviceQueue; + + std::uint32_t queueFamilyIndex; + }; +}; +} // namespace raid + +#endif // !defined(RAID_INTERNAL_VULKAN_HPP_INCLUDED) diff --git a/public/raid/raid.hpp b/public/raid/raid.hpp index 6241bdb..76c88e1 100644 --- a/public/raid/raid.hpp +++ b/public/raid/raid.hpp @@ -14,6 +14,9 @@ #include #include #include +#include +#include "./internal/opengl.hpp" +#include "./internal/vulkan.hpp" namespace raid { @@ -61,16 +64,22 @@ struct ApplicationFlags : mijin::BitFlags bool x11OnWayland : 1 = false; }; +enum class GraphicsAPI : std::uint8_t +{ + OPENGL, + VULKAN +}; + struct ApplicationConfig { ApplicationFlags flags = {}; + GraphicsAPI graphicsApi = GraphicsAPI::OPENGL; }; -class Application +class Application : private MixinOpenGLApplication, MixinVulkanApplication { private: SDL_Window* mWindow = nullptr; - SDL_GLContext mGLContext = nullptr; mijin::StackedFileSystemAdapter mFS; mijin::MemoryFileSystemAdapter* mMemoryFS = nullptr; @@ -82,30 +91,11 @@ private: std::unordered_map> mMainWindowStyles; const ApplicationConfig mConfig; - using GLbitfield = std::uint32_t; - using GLint = std::int32_t; - using GLuint = std::uint32_t; - using GLsizei = std::int32_t; - using GLenum = std::uint32_t; - using GLfloat = float; - - using glClear_fn_t = void (*)(GLbitfield); - using glClearColor_fn_t = void (*)(GLfloat, GLfloat, GLfloat, GLfloat); - using glGenTextures_fn_t = void (*)(GLsizei, GLuint*); - using glBindTexture_fn_t = void (*)(GLenum, GLuint); - using glTexParameteri_fn_t = void (*)(GLenum, GLenum, GLint); - using glPixelStorei_fn_t = void (*)(GLenum, GLint); - using glTexImage2D_fn_t = void (*)(GLenum, GLint, GLint, GLsizei, GLsizei, GLint, GLenum, GLenum, const void*); - using glDeleteTextures_fn_t = void (*)(GLsizei, const GLuint*); - - glClear_fn_t glClear; - glClearColor_fn_t glClearColor; - glGenTextures_fn_t glGenTextures; - glBindTexture_fn_t glBindTexture; - glTexParameteri_fn_t glTexParameteri; - glPixelStorei_fn_t glPixelStorei; - glTexImage2D_fn_t glTexImage2D; - glDeleteTextures_fn_t glDeleteTextures; + union + { + OpenGLData gl; + VulkanData vk; + }; public: explicit Application(ApplicationConfig config = {}) noexcept : mConfig(config) {} virtual ~Application() = default; @@ -149,6 +139,8 @@ public: [[nodiscard]] ImTextureID getOrLoadTexture(fs::path path); + void destroyTexture(ImTextureID texture); + protected: virtual void render() = 0; virtual std::string getFolderName() = 0; @@ -215,7 +207,8 @@ protected: virtual void cleanup(); private: bool initSDL(); - bool initGL(); + bool initOpenGL(); + bool initVulkan(); bool initImGui(); void handleSDLEvents(); void loadImGuiConfig();