metaforce/aurora/lib/aurora.cpp

349 lines
11 KiB
C++
Raw Normal View History

2022-02-16 05:21:24 +00:00
#include <aurora/aurora.hpp>
2022-02-16 23:13:40 +00:00
#include <SDL.h>
2022-02-16 07:05:42 +00:00
#include <dawn/native/DawnNative.h>
2022-02-16 23:13:40 +00:00
#include <dawn/webgpu_cpp.h>
#include <logvisor/logvisor.hpp>
#include <magic_enum.hpp>
#ifdef DAWN_ENABLE_BACKEND_VULKAN
2022-02-16 07:05:42 +00:00
#include <SDL_vulkan.h>
#include <dawn/native/VulkanBackend.h>
#endif
2022-02-16 23:13:40 +00:00
#ifdef DAWN_ENABLE_BACKEND_OPENGL
2022-02-16 07:05:42 +00:00
#include <SDL_opengl.h>
#include <dawn/native/OpenGLBackend.h>
#endif
2022-02-16 23:13:40 +00:00
#ifdef DAWN_ENABLE_BACKEND_METAL
#include <SDL_metal.h>
#include <dawn/native/MetalBackend.h>
#endif
#include "dawn/BackendBinding.hpp"
2022-02-16 05:21:24 +00:00
namespace aurora {
2022-02-16 09:23:39 +00:00
// TODO: Move global state to a class/struct?
2022-02-16 07:05:42 +00:00
static logvisor::Module Log("aurora");
2022-02-16 23:13:40 +00:00
2022-02-16 09:23:39 +00:00
static std::unique_ptr<AppDelegate> g_AppDelegate;
2022-02-16 23:13:40 +00:00
static std::vector<std::string> g_Args;
2022-02-16 07:05:42 +00:00
// SDL
static SDL_Window* g_Window;
// Dawn / WebGPU
2022-02-16 23:13:40 +00:00
#ifdef DAWN_ENABLE_BACKEND_VULKAN
static wgpu::BackendType preferredBackendType = wgpu::BackendType::Vulkan;
#elif DAWN_ENABLE_BACKEND_METAL
static wgpu::BackendType preferredBackendType = wgpu::BackendType::Metal;
2022-02-16 07:05:42 +00:00
#else
2022-02-16 23:13:40 +00:00
static wgpu::BackendType preferredBackendType = wgpu::BackendType::OpenGL;
2022-02-16 07:05:42 +00:00
#endif
2022-02-16 23:13:40 +00:00
static std::unique_ptr<dawn::native::Instance> g_Instance;
static dawn::native::Adapter g_Adapter;
static wgpu::AdapterProperties g_AdapterProperties;
2022-02-16 07:05:42 +00:00
static wgpu::Device g_Device;
2022-02-16 23:13:40 +00:00
static wgpu::Queue g_Queue;
static wgpu::SwapChain g_SwapChain;
static std::unique_ptr<utils::BackendBinding> g_BackendBinding;
2022-02-16 07:05:42 +00:00
static void set_window_icon(Icon icon) noexcept {
SDL_Surface* iconSurface = SDL_CreateRGBSurfaceFrom(icon.data.get(), icon.width, icon.height, 32, 4 * icon.width,
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff
#else
0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000
#endif
);
if (iconSurface == nullptr) {
Log.report(logvisor::Fatal, FMT_STRING("Failed to create icon surface: {}"), SDL_GetError());
}
SDL_SetWindowIcon(g_Window, iconSurface);
SDL_FreeSurface(iconSurface);
}
static bool poll_events() noexcept {
SDL_Event event;
while (SDL_PollEvent(&event) != 0) {
switch (event.type) {
2022-02-16 09:23:39 +00:00
case SDL_WINDOWEVENT: {
switch (event.window.event) {
case SDL_WINDOWEVENT_SHOWN: {
break;
}
case SDL_WINDOWEVENT_HIDDEN: {
break;
}
case SDL_WINDOWEVENT_EXPOSED: {
/* TODO: ImGui code? */
/* TODO: Frame Time */
// g_AppDelegate->onAppIdle(1/60.f);
/* TODO: Render Texture */
// g_AppDelegate->onAppDraw();
/* TODO: ImGui present code? */
/* TODO: Present */
// g_AppDelegate->onAppPostDraw();
break;
}
case SDL_WINDOWEVENT_MOVED: {
g_AppDelegate->onAppWindowMoved(event.window.data1, event.window.data2);
break;
}
case SDL_WINDOWEVENT_RESIZED: {
g_AppDelegate->onAppWindowResized(
{static_cast<uint32_t>(event.window.data1), static_cast<uint32_t>(event.window.data2)});
break;
}
case SDL_WINDOWEVENT_SIZE_CHANGED: {
// TODO: handle size changed event
break;
}
case SDL_WINDOWEVENT_MINIMIZED: {
// TODO: handle minimized event
break;
}
case SDL_WINDOWEVENT_MAXIMIZED: {
// TODO: handle maximized event
break;
}
case SDL_WINDOWEVENT_RESTORED: {
// TODO: handle restored event
break;
}
case SDL_WINDOWEVENT_ENTER: {
// TODO: handle enter event (mouse focus)
break;
}
case SDL_WINDOWEVENT_LEAVE: {
// TODO: handle leave event (mouse focus)
break;
}
case SDL_WINDOWEVENT_FOCUS_GAINED: {
// TODO: handle focus gained event
break;
}
case SDL_WINDOWEVENT_FOCUS_LOST: {
// TODO: handle focus lost event
break;
}
case SDL_WINDOWEVENT_CLOSE: {
// TODO: handle window close event
break;
}
case SDL_WINDOWEVENT_TAKE_FOCUS: {
// TODO: handle take focus event
break;
}
case SDL_WINDOWEVENT_HIT_TEST: {
// TODO: handle hit test?
break;
}
case SDL_WINDOWEVENT_DISPLAY_CHANGED: {
// TODO: handle display chaaged event
break;
}
}
/* TODO: More window events */
break;
}
case SDL_CONTROLLERDEVICEADDED: {
break;
}
case SDL_CONTROLLERDEVICEREMOVED: {
break;
}
case SDL_CONTROLLERBUTTONDOWN: {
break;
}
case SDL_CONTROLLERBUTTONUP: {
break;
}
case SDL_CONTROLLERAXISMOTION: {
break;
}
case SDL_KEYDOWN: {
break;
}
case SDL_KEYUP: {
break;
}
case SDL_TEXTINPUT: {
break;
}
2022-02-16 07:05:42 +00:00
case SDL_QUIT:
2022-02-16 09:23:39 +00:00
g_AppDelegate->onAppExiting();
2022-02-16 07:05:42 +00:00
Log.report(logvisor::Info, FMT_STRING("Received quit request"));
return false;
}
// TODO why doesn't this work?
const auto typedEvent = magic_enum::enum_cast<SDL_EventType>(event.type);
if (typedEvent) {
Log.report(logvisor::Info, FMT_STRING("Received SDL event: {}"), magic_enum::enum_name(typedEvent.value()));
} else {
Log.report(logvisor::Info, FMT_STRING("Received SDL event: {}"), event.type);
}
}
return true;
}
2022-02-16 09:23:39 +00:00
void app_run(std::unique_ptr<AppDelegate> app, Icon icon, int argc, char** argv) noexcept {
g_AppDelegate = std::move(app);
/* Lets gather arguments skipping the program filename */
for (size_t i = 1; i < argc; ++i) {
g_Args.emplace_back(argv[i]);
}
2022-02-16 07:05:42 +00:00
if (SDL_Init(SDL_INIT_EVERYTHING) < 0) {
Log.report(logvisor::Fatal, FMT_STRING("Error initializing SDL: {}"), SDL_GetError());
}
2022-02-16 23:13:40 +00:00
Uint32 flags = SDL_WINDOW_SHOWN | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_RESIZABLE;
switch (preferredBackendType) {
#ifdef DAWN_ENABLE_BACKEND_VULKAN
2022-02-16 07:05:42 +00:00
case wgpu::BackendType::Vulkan:
flags |= SDL_WINDOW_VULKAN;
break;
#endif
2022-02-16 23:13:40 +00:00
#ifdef DAWN_ENABLE_BACKEND_METAL
2022-02-16 07:05:42 +00:00
case wgpu::BackendType::Metal:
flags |= SDL_WINDOW_METAL;
break;
#endif
2022-02-16 23:13:40 +00:00
#ifdef DAWN_ENABLE_BACKEND_OPENGL
2022-02-16 07:05:42 +00:00
case wgpu::BackendType::OpenGL:
flags |= SDL_WINDOW_OPENGL;
break;
#endif
default:
break;
}
g_Window = SDL_CreateWindow("Metaforce", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 1280, 720, flags);
if (g_Window == nullptr) {
Log.report(logvisor::Fatal, FMT_STRING("Error creating window: {}"), SDL_GetError());
}
set_window_icon(std::move(icon));
2022-02-16 23:13:40 +00:00
Log.report(logvisor::Info, FMT_STRING("Creating Dawn instance"));
g_Instance = std::make_unique<dawn::native::Instance>();
utils::DiscoverAdapter(g_Instance.get(), g_Window, preferredBackendType);
{
std::vector<dawn::native::Adapter> adapters = g_Instance->GetAdapters();
auto adapterIt = std::find_if(adapters.begin(), adapters.end(), [](const dawn::native::Adapter adapter) -> bool {
wgpu::AdapterProperties properties;
adapter.GetProperties(&properties);
return properties.backendType == preferredBackendType;
});
if (adapterIt == adapters.end()) {
Log.report(logvisor::Fatal, FMT_STRING("Failed to find usable graphics backend"));
2022-02-16 07:05:42 +00:00
}
2022-02-16 23:13:40 +00:00
g_Adapter = *adapterIt;
2022-02-16 07:05:42 +00:00
}
2022-02-16 23:13:40 +00:00
g_Adapter.GetProperties(&g_AdapterProperties);
const auto backendName = magic_enum::enum_name(g_AdapterProperties.backendType);
Log.report(logvisor::Info, FMT_STRING("Using {} graphics backend"), backendName);
g_Device = wgpu::Device::Acquire(g_Adapter.CreateDevice());
g_BackendBinding = std::unique_ptr<utils::BackendBinding>(
utils::CreateBinding(g_AdapterProperties.backendType, g_Window, g_Device.Get()));
if (!g_BackendBinding) {
2022-02-16 07:05:42 +00:00
Log.report(logvisor::Fatal, FMT_STRING("Unsupported backend {}"), backendName);
}
g_Queue = g_Device.GetQueue();
{
wgpu::SwapChainDescriptor descriptor{};
2022-02-16 23:13:40 +00:00
descriptor.implementation = g_BackendBinding->GetSwapChainImplementation();
2022-02-16 07:05:42 +00:00
g_SwapChain = g_Device.CreateSwapChain(nullptr, &descriptor);
}
{
2022-02-16 23:13:40 +00:00
auto size = get_window_size();
auto format = static_cast<wgpu::TextureFormat>(g_BackendBinding->GetPreferredSwapChainTextureFormat());
g_SwapChain.Configure(format, wgpu::TextureUsage::RenderAttachment, size.width, size.height);
2022-02-16 07:05:42 +00:00
}
2022-02-16 09:23:39 +00:00
g_AppDelegate->onAppLaunched();
g_AppDelegate->onAppWindowResized(get_window_size());
2022-02-16 23:13:40 +00:00
while (poll_events()) {
auto encoder = g_Device.CreateCommandEncoder();
{
std::array<wgpu::RenderPassColorAttachment, 1> attachments{wgpu::RenderPassColorAttachment{
.view = g_SwapChain.GetCurrentTextureView(),
.loadOp = wgpu::LoadOp::Clear,
.storeOp = wgpu::StoreOp::Store,
.clearColor = {0.5f, 0.5f, 0.5f, 1.f},
}};
auto renderPassDescriptor = wgpu::RenderPassDescriptor{
.label = "Render Pass",
.colorAttachmentCount = attachments.size(),
.colorAttachments = attachments.data(),
};
auto pass = encoder.BeginRenderPass(&renderPassDescriptor);
pass.End();
}
const auto buffer = encoder.Finish();
g_Queue.Submit(1, &buffer);
g_SwapChain.Present();
}
2022-02-16 07:05:42 +00:00
2022-02-16 23:13:40 +00:00
wgpuSwapChainRelease(g_SwapChain.Release());
wgpuQueueRelease(g_Queue.Release());
g_BackendBinding.reset();
wgpuDeviceRelease(g_Device.Release());
g_Instance.reset();
2022-02-16 07:05:42 +00:00
SDL_DestroyWindow(g_Window);
SDL_Quit();
}
2022-02-16 09:23:39 +00:00
std::vector<std::string> get_args() noexcept { return g_Args; }
2022-02-16 05:21:24 +00:00
WindowSize get_window_size() noexcept {
2022-02-16 09:23:39 +00:00
int width, height;
SDL_GetWindowSize(g_Window, &width, &height);
return {static_cast<uint32_t>(width), static_cast<uint32_t>(height)};
2022-02-16 05:21:24 +00:00
}
2022-02-16 07:05:42 +00:00
void set_window_title(zstring_view title) noexcept { SDL_SetWindowTitle(g_Window, title.c_str()); }
2022-02-16 05:21:24 +00:00
Backend get_backend() noexcept {
2022-02-16 23:13:40 +00:00
switch (g_AdapterProperties.backendType) {
2022-02-16 09:23:39 +00:00
case wgpu::BackendType::WebGPU:
return Backend::WebGPU;
case wgpu::BackendType::D3D11:
return Backend::D3D11;
case wgpu::BackendType::D3D12:
return Backend::D3D12;
case wgpu::BackendType::Metal:
return Backend::Metal;
case wgpu::BackendType::Vulkan:
return Backend::Vulkan;
case wgpu::BackendType::OpenGL:
return Backend::OpenGL;
2022-02-16 23:13:40 +00:00
case wgpu::BackendType::OpenGLES:
return Backend::OpenGLES;
2022-02-16 09:23:39 +00:00
default:
return Backend::Invalid;
}
2022-02-16 05:21:24 +00:00
}
2022-02-16 23:13:40 +00:00
std::string_view get_backend_string() noexcept { return magic_enum::enum_name(g_AdapterProperties.backendType); }
2022-02-16 05:21:24 +00:00
void set_fullscreen(bool fullscreen) noexcept {
2022-02-16 09:23:39 +00:00
auto flags = SDL_GetWindowFlags(g_Window);
if (fullscreen) {
flags |= SDL_WINDOW_FULLSCREEN;
} else {
flags &= ~SDL_WINDOW_FULLSCREEN;
}
SDL_SetWindowFullscreen(g_Window, flags);
2022-02-16 05:21:24 +00:00
}
2022-02-16 09:23:39 +00:00
2022-02-16 05:21:24 +00:00
int32_t get_controller_player_index(uint32_t which) noexcept {
return -1; // TODO
}
void set_controller_player_index(uint32_t which, int32_t index) noexcept {
// TODO
}
bool is_controller_gamecube(uint32_t which) noexcept {
return true; // TODO
}
std::string get_controller_name(uint32_t which) noexcept {
return ""; // TODO
}
} // namespace aurora