Compare commits
3 Commits
917309e99c
...
ee4ea4ae0a
Author | SHA1 | Date | |
---|---|---|---|
ee4ea4ae0a | |||
b35edfcf20 | |||
180f2b70fa |
@ -5,6 +5,7 @@
|
|||||||
#include <thread>
|
#include <thread>
|
||||||
#include <fmt/base.h>
|
#include <fmt/base.h>
|
||||||
#include <SDL3/SDL.h>
|
#include <SDL3/SDL.h>
|
||||||
|
#include <SDL3/SDL_vulkan.h>
|
||||||
#include <mijin/detect.hpp>
|
#include <mijin/detect.hpp>
|
||||||
#include <mijin/debug/assert.hpp>
|
#include <mijin/debug/assert.hpp>
|
||||||
#include <mijin/platform/folders.hpp>
|
#include <mijin/platform/folders.hpp>
|
||||||
@ -62,6 +63,12 @@ int stbiEof(void* user)
|
|||||||
mijin::Stream& stream = *static_cast<mijin::Stream*>(user);
|
mijin::Stream& stream = *static_cast<mijin::Stream*>(user);
|
||||||
return stream.isAtEnd();
|
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)
|
int Application::run(int argc, char** argv)
|
||||||
@ -114,15 +121,26 @@ int Application::run(int argc, char** argv)
|
|||||||
|
|
||||||
ImGui::Render();
|
ImGui::Render();
|
||||||
|
|
||||||
glClearColor(0.3f, 0.3f, 0.3f, 1.f);
|
switch (mConfig.graphicsApi)
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
{
|
||||||
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
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)
|
if (imguiIO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
|
||||||
{
|
{
|
||||||
ImGui::UpdatePlatformWindows();
|
ImGui::UpdatePlatformWindows();
|
||||||
ImGui::RenderPlatformWindowsDefault();
|
ImGui::RenderPlatformWindowsDefault();
|
||||||
SDL_GL_MakeCurrent(mWindow, mGLContext);
|
|
||||||
|
if (mConfig.graphicsApi == GraphicsAPI::OPENGL) {
|
||||||
|
SDL_GL_MakeCurrent(mWindow, gl.context);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_GL_SwapWindow(mWindow);
|
SDL_GL_SwapWindow(mWindow);
|
||||||
@ -223,23 +241,60 @@ ImTextureID Application::getOrLoadTexture(fs::path path)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// create texture
|
// create texture
|
||||||
GLuint texture = 0;
|
ImTextureID texture = 0;
|
||||||
glGenTextures(1, &texture);
|
switch (mConfig.graphicsApi)
|
||||||
glBindTexture(GL_TEXTURE_2D, texture);
|
{
|
||||||
|
case GraphicsAPI::OPENGL:
|
||||||
|
{
|
||||||
|
GLuint glTexture = 0;
|
||||||
|
gl.GenTextures(1, &glTexture);
|
||||||
|
gl.BindTexture(GL_TEXTURE_2D, glTexture);
|
||||||
|
|
||||||
// setup texture
|
// setup texture
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
|
|
||||||
// upload image
|
// upload image
|
||||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
gl.PixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData);
|
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;
|
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<GLuint>(texture);
|
||||||
|
gl.DeleteTextures(1, &asUint);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case GraphicsAPI::VULKAN:
|
||||||
|
MIJIN_TRAP(); // TODO
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Application::configureImgui()
|
void Application::configureImgui()
|
||||||
{
|
{
|
||||||
ImGuiIO& imguiIO = ImGui::GetIO();
|
ImGuiIO& imguiIO = ImGui::GetIO();
|
||||||
@ -379,9 +434,19 @@ bool Application::init()
|
|||||||
{
|
{
|
||||||
return false;
|
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())
|
if (!initImGui())
|
||||||
{
|
{
|
||||||
@ -407,14 +472,29 @@ void Application::cleanup()
|
|||||||
}
|
}
|
||||||
ImGui::DestroyContext();
|
ImGui::DestroyContext();
|
||||||
}
|
}
|
||||||
for (const auto& [path, texture] : mTextures)
|
for (const auto& [_, texture] : mTextures) {
|
||||||
{
|
destroyTexture(texture);
|
||||||
const GLuint asUint = static_cast<GLuint>(texture);
|
|
||||||
glDeleteTextures(1, &asUint);
|
|
||||||
}
|
}
|
||||||
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)
|
if (mWindow != nullptr)
|
||||||
{
|
{
|
||||||
@ -426,9 +506,9 @@ void Application::cleanup()
|
|||||||
bool Application::initSDL()
|
bool Application::initSDL()
|
||||||
{
|
{
|
||||||
#if MIJIN_TARGET_OS == MIJIN_OS_LINUX
|
#if MIJIN_TARGET_OS == MIJIN_OS_LINUX
|
||||||
// prefer x11 over wayland, as ImGui viewports don't work with wayland
|
if (mConfig.flags.x11OnWayland) {
|
||||||
// TODO: this still doesn't work all the time, maybe there will be an update to ImGui in the future?
|
SDL_SetHint(SDL_HINT_VIDEO_DRIVER, "x11,wayland");
|
||||||
SDL_SetHint(SDL_HINT_VIDEO_DRIVER, "x11,wayland");
|
}
|
||||||
#endif
|
#endif
|
||||||
if (!SDL_Init(SDL_INIT_VIDEO))
|
if (!SDL_Init(SDL_INIT_VIDEO))
|
||||||
{
|
{
|
||||||
@ -438,48 +518,146 @@ bool Application::initSDL()
|
|||||||
|
|
||||||
msgInfo("SDL video driver: {}", SDL_GetCurrentVideoDriver());
|
msgInfo("SDL video driver: {}", SDL_GetCurrentVideoDriver());
|
||||||
|
|
||||||
// GL attributes must be set before window creation
|
SDL_WindowFlags windowFlags = SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIGH_PIXEL_DENSITY;
|
||||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
switch (mConfig.graphicsApi)
|
||||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
|
{
|
||||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
case GraphicsAPI::OPENGL:
|
||||||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
// 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
|
// 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_DEPTH_SIZE, 24);
|
||||||
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
|
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(
|
mWindow = SDL_CreateWindow(
|
||||||
/* title = */ getWindowTitle().c_str(),
|
/* title = */ getWindowTitle().c_str(),
|
||||||
/* w = */ 1280,
|
/* w = */ 1280,
|
||||||
/* h = */ 720,
|
/* h = */ 720,
|
||||||
/* flags = */ WINDOW_FLAGS
|
/* flags = */ windowFlags
|
||||||
);
|
);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Application::initGL()
|
bool Application::initOpenGL()
|
||||||
{
|
{
|
||||||
mGLContext = SDL_GL_CreateContext(mWindow);
|
gl.context = SDL_GL_CreateContext(mWindow);
|
||||||
if (mWindow == nullptr)
|
if (mWindow == nullptr)
|
||||||
{
|
{
|
||||||
msgError("Error creating SDL window: {}.", SDL_GetError());
|
msgError("Error creating OpenGL context: {}.", SDL_GetError());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
SDL_GL_MakeCurrent(mWindow, mGLContext);
|
SDL_GL_MakeCurrent(mWindow, gl.context);
|
||||||
SDL_GL_SetSwapInterval(1); // enable vsync, at least for now
|
SDL_GL_SetSwapInterval(1); // enable vsync, at least for now
|
||||||
|
|
||||||
glClear = reinterpret_cast<glClear_fn_t>(SDL_GL_GetProcAddress("glClear"));
|
gl.Clear = reinterpret_cast<glClear_fn_t>(SDL_GL_GetProcAddress("glClear"));
|
||||||
glClearColor = reinterpret_cast<glClearColor_fn_t>(SDL_GL_GetProcAddress("glClearColor"));
|
gl.ClearColor = reinterpret_cast<glClearColor_fn_t>(SDL_GL_GetProcAddress("glClearColor"));
|
||||||
glGenTextures = reinterpret_cast<glGenTextures_fn_t>(SDL_GL_GetProcAddress("glGenTextures"));
|
gl.GenTextures = reinterpret_cast<glGenTextures_fn_t>(SDL_GL_GetProcAddress("glGenTextures"));
|
||||||
glBindTexture = reinterpret_cast<glBindTexture_fn_t>(SDL_GL_GetProcAddress("glBindTexture"));
|
gl.BindTexture = reinterpret_cast<glBindTexture_fn_t>(SDL_GL_GetProcAddress("glBindTexture"));
|
||||||
glTexParameteri = reinterpret_cast<glTexParameteri_fn_t>(SDL_GL_GetProcAddress("glTexParameteri"));
|
gl.TexParameteri = reinterpret_cast<glTexParameteri_fn_t>(SDL_GL_GetProcAddress("glTexParameteri"));
|
||||||
glPixelStorei = reinterpret_cast<glPixelStorei_fn_t>(SDL_GL_GetProcAddress("glPixelStorei"));
|
gl.PixelStorei = reinterpret_cast<glPixelStorei_fn_t>(SDL_GL_GetProcAddress("glPixelStorei"));
|
||||||
glTexImage2D = reinterpret_cast<glTexImage2D_fn_t>(SDL_GL_GetProcAddress("glTexImage2D"));
|
gl.TexImage2D = reinterpret_cast<glTexImage2D_fn_t>(SDL_GL_GetProcAddress("glTexImage2D"));
|
||||||
glDeleteTextures = reinterpret_cast<glDeleteTextures_fn_t>(SDL_GL_GetProcAddress("glDeleteTextures"));
|
gl.DeleteTextures = reinterpret_cast<glDeleteTextures_fn_t>(SDL_GL_GetProcAddress("glDeleteTextures"));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Application::initVulkan()
|
||||||
|
{
|
||||||
|
vk.GetInstanceProc = reinterpret_cast<vkGetInstanceProcAddr_fn_t>(SDL_Vulkan_GetVkGetInstanceProcAddr());
|
||||||
|
vk.CreateInstance = reinterpret_cast<vkCreateInstance_fn_t>(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<unsigned>(result));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
vk.DestroyInstance = reinterpret_cast<vkDestroyInstance_fn_t>(vk.GetInstanceProc(vk.instance, "vkDestroyInstance"));
|
||||||
|
vk.EnumeratePhysicalDevices = reinterpret_cast<vkEnumeratePhysicalDevices_fn_t>(vk.GetInstanceProc(vk.instance, "vkEnumeratePhysicalDevices"));
|
||||||
|
vk.GetPhysicalDeviceQueueFamilyProperties2 = reinterpret_cast<vkGetPhysicalDeviceQueueFamilyProperties2_fn_t>(vk.GetInstanceProc(vk.instance, "vkGetPhysicalDeviceQueueFamilyProperties2"));
|
||||||
|
vk.CreateDevice = reinterpret_cast<vkCreateDevice_fn_t>(vk.GetInstanceProc(vk.instance, "vkCreateDevice"));
|
||||||
|
vk.DestroyDevice = reinterpret_cast<vkDestroyDevice_fn_t>(vk.GetInstanceProc(vk.instance, "vkDestroyDevice"));
|
||||||
|
vk.DeviceWaitIdle = reinterpret_cast<vkDeviceWaitIdle_fn_t>(vk.GetInstanceProc(vk.instance, "vkDeviceWaitIdle"));
|
||||||
|
vk.GetDeviceQueue = reinterpret_cast<vkGetDeviceQueue_fn_t>(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<unsigned>(result));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint32_t queueFamilyPropertyCount = 0;
|
||||||
|
vk.GetPhysicalDeviceQueueFamilyProperties2(vk.physicalDevice, &queueFamilyPropertyCount, nullptr);
|
||||||
|
|
||||||
|
std::vector<VkQueueFamilyProperties2> 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<std::uint32_t>(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<unsigned>(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;
|
return true;
|
||||||
}
|
}
|
||||||
@ -511,14 +689,22 @@ bool Application::initImGui()
|
|||||||
ImGui::StyleColorsDark();
|
ImGui::StyleColorsDark();
|
||||||
|
|
||||||
// init the backends
|
// init the backends
|
||||||
if (!ImGui_ImplSDL3_InitForOpenGL(mWindow, mGLContext))
|
switch (mConfig.graphicsApi)
|
||||||
{
|
{
|
||||||
msgError("Error initializing ImGui SDL3 backend.");
|
case GraphicsAPI::OPENGL:
|
||||||
return false;
|
if (!ImGui_ImplSDL3_InitForOpenGL(mWindow, gl.context))
|
||||||
}
|
{
|
||||||
if (!ImGui_ImplOpenGL3_Init(IMGUI_GLSL_VERSION))
|
msgError("Error initializing ImGui SDL3 backend.");
|
||||||
{
|
return false;
|
||||||
msgError("Error initializing ImGui OpenGL3 backend.");
|
}
|
||||||
|
if (!ImGui_ImplOpenGL3_Init(IMGUI_GLSL_VERSION))
|
||||||
|
{
|
||||||
|
msgError("Error initializing ImGui OpenGL3 backend.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case GraphicsAPI::VULKAN:
|
||||||
|
MIJIN_TRAP(); // TODO!
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
45
public/raid/internal/opengl.hpp
Normal file
45
public/raid/internal/opengl.hpp
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if !defined(RAID_INTERNAL_OPENGL_HPP_INCLUDED)
|
||||||
|
#define RAID_INTERNAL_OPENGL_HPP_INCLUDED 1
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
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)
|
176
public/raid/internal/vulkan.hpp
Normal file
176
public/raid/internal/vulkan.hpp
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if !defined(RAID_INTERNAL_VULKAN_HPP_INCLUDED)
|
||||||
|
#define RAID_INTERNAL_VULKAN_HPP_INCLUDED 1
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#if !defined(RAID_USE_VULKAN_H)
|
||||||
|
#define RAID_USE_VULKAN_H 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if RAID_USE_VULKAN_H
|
||||||
|
#include <vulkan/vulkan.h>
|
||||||
|
#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)
|
@ -14,6 +14,9 @@
|
|||||||
#include <mijin/virtual_filesystem/memory.hpp>
|
#include <mijin/virtual_filesystem/memory.hpp>
|
||||||
#include <mijin/virtual_filesystem/stacked.hpp>
|
#include <mijin/virtual_filesystem/stacked.hpp>
|
||||||
#include <SDL3/SDL.h>
|
#include <SDL3/SDL.h>
|
||||||
|
#include <SDL3/SDL_vulkan.h>
|
||||||
|
#include "./internal/opengl.hpp"
|
||||||
|
#include "./internal/vulkan.hpp"
|
||||||
|
|
||||||
namespace raid
|
namespace raid
|
||||||
{
|
{
|
||||||
@ -51,11 +54,32 @@ struct FontConfig
|
|||||||
FontFlags flags;
|
FontFlags flags;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Application
|
struct ApplicationFlags : mijin::BitFlags<ApplicationFlags>
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* (Linux only) prefer X11 even when on Wayland. Required for multi-viewport support, but currently really buggy.
|
||||||
|
* \see https://github.com/ocornut/imgui/issues/8609
|
||||||
|
* \see https://github.com/ocornut/imgui/issues/8587
|
||||||
|
*/
|
||||||
|
bool x11OnWayland : 1 = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class GraphicsAPI : std::uint8_t
|
||||||
|
{
|
||||||
|
OPENGL,
|
||||||
|
VULKAN
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ApplicationConfig
|
||||||
|
{
|
||||||
|
ApplicationFlags flags = {};
|
||||||
|
GraphicsAPI graphicsApi = GraphicsAPI::OPENGL;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Application : private MixinOpenGLApplication, MixinVulkanApplication
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
SDL_Window* mWindow = nullptr;
|
SDL_Window* mWindow = nullptr;
|
||||||
SDL_GLContext mGLContext = nullptr;
|
|
||||||
|
|
||||||
mijin::StackedFileSystemAdapter mFS;
|
mijin::StackedFileSystemAdapter mFS;
|
||||||
mijin::MemoryFileSystemAdapter* mMemoryFS = nullptr;
|
mijin::MemoryFileSystemAdapter* mMemoryFS = nullptr;
|
||||||
@ -65,34 +89,20 @@ private:
|
|||||||
bool mRunning = true;
|
bool mRunning = true;
|
||||||
ImGuiWindowFlags mMainWindowFlags = DEFAULT_MAIN_WINDOW_FLAGS;
|
ImGuiWindowFlags mMainWindowFlags = DEFAULT_MAIN_WINDOW_FLAGS;
|
||||||
std::unordered_map<ImGuiStyleVar, std::variant<float, ImVec2>> mMainWindowStyles;
|
std::unordered_map<ImGuiStyleVar, std::variant<float, ImVec2>> mMainWindowStyles;
|
||||||
|
const ApplicationConfig mConfig;
|
||||||
|
|
||||||
using GLbitfield = std::uint32_t;
|
union
|
||||||
using GLint = std::int32_t;
|
{
|
||||||
using GLuint = std::uint32_t;
|
OpenGLData gl;
|
||||||
using GLsizei = std::int32_t;
|
VulkanData vk;
|
||||||
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;
|
|
||||||
public:
|
public:
|
||||||
|
explicit Application(ApplicationConfig config = {}) noexcept : mConfig(config) {}
|
||||||
virtual ~Application() = default;
|
virtual ~Application() = default;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
const ApplicationConfig& getConfig() const noexcept { return mConfig; }
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
mijin::StackedFileSystemAdapter& getFS() { return mFS; }
|
mijin::StackedFileSystemAdapter& getFS() { return mFS; }
|
||||||
|
|
||||||
@ -129,6 +139,8 @@ public:
|
|||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
ImTextureID getOrLoadTexture(fs::path path);
|
ImTextureID getOrLoadTexture(fs::path path);
|
||||||
|
|
||||||
|
void destroyTexture(ImTextureID texture);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void render() = 0;
|
virtual void render() = 0;
|
||||||
virtual std::string getFolderName() = 0;
|
virtual std::string getFolderName() = 0;
|
||||||
@ -195,7 +207,8 @@ protected:
|
|||||||
virtual void cleanup();
|
virtual void cleanup();
|
||||||
private:
|
private:
|
||||||
bool initSDL();
|
bool initSDL();
|
||||||
bool initGL();
|
bool initOpenGL();
|
||||||
|
bool initVulkan();
|
||||||
bool initImGui();
|
bool initImGui();
|
||||||
void handleSDLEvents();
|
void handleSDLEvents();
|
||||||
void loadImGuiConfig();
|
void loadImGuiConfig();
|
||||||
@ -221,6 +234,8 @@ private:
|
|||||||
std::string mFolderName;
|
std::string mFolderName;
|
||||||
std::string mWindowTitle;
|
std::string mWindowTitle;
|
||||||
public:
|
public:
|
||||||
|
explicit QuickApp(ApplicationConfig config = {}) noexcept : Application(config) {}
|
||||||
|
|
||||||
void preInit(QuickAppOptions options);
|
void preInit(QuickAppOptions options);
|
||||||
void render() override;
|
void render() override;
|
||||||
std::string getFolderName() override;
|
std::string getFolderName() override;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user