aurora/lib/aurora.cpp
Phillip Stephens ca822a7679 Input: Fix bad assumption about init
If we try and initialize anything controller related before the event
subsystem is initialized we lose all controllers detected on startup.
To solve this we simply wait until the first frame is about to be rendered
*then* initialize the controller subsystems
2023-01-21 19:48:26 -08:00

226 lines
5.8 KiB
C++

#include <aurora/aurora.h>
#include "gfx/common.hpp"
#include "imgui.hpp"
#include "input.hpp"
#include "internal.hpp"
#include "webgpu/gpu.hpp"
#include "window.hpp"
#include <SDL.h>
#include <SDL_filesystem.h>
#include <imgui.h>
namespace aurora {
static Module Log("aurora");
AuroraConfig g_config;
// GPU
using webgpu::g_device;
using webgpu::g_queue;
using webgpu::g_swapChain;
constexpr std::array PreferredBackendOrder{
#ifdef ENABLE_BACKEND_WEBGPU
BACKEND_WEBGPU,
#endif
#ifdef DAWN_ENABLE_BACKEND_D3D12
// BACKEND_D3D12,
#endif
#ifdef DAWN_ENABLE_BACKEND_METAL
BACKEND_METAL,
#endif
#ifdef DAWN_ENABLE_BACKEND_VULKAN
BACKEND_VULKAN,
#endif
#ifdef DAWN_ENABLE_BACKEND_DESKTOP_GL
BACKEND_OPENGL,
#endif
#ifdef DAWN_ENABLE_BACKEND_OPENGLES
BACKEND_OPENGLES,
#endif
#ifdef DAWN_ENABLE_BACKEND_NULL
BACKEND_NULL,
#endif
};
static bool g_initialFrame = false;
static AuroraInfo initialize(int argc, char* argv[], const AuroraConfig& config) noexcept {
g_config = config;
if (g_config.appName == nullptr) {
g_config.appName = "Aurora";
}
if (g_config.configPath == nullptr) {
g_config.configPath = SDL_GetPrefPath(nullptr, g_config.appName);
}
if (g_config.msaa == 0) {
g_config.msaa = 1;
}
if (g_config.maxTextureAnisotropy == 0) {
g_config.maxTextureAnisotropy = 16;
}
window::initialize();
/* Attempt to create a window using the calling application's desired backend */
AuroraBackend selectedBackend = config.desiredBackend;
bool windowCreated = false;
if (selectedBackend != BACKEND_AUTO && window::create_window(selectedBackend)) {
if (webgpu::initialize(selectedBackend)) {
windowCreated = true;
} else {
window::destroy_window();
}
}
if (!windowCreated) {
for (const auto backendType : PreferredBackendOrder) {
selectedBackend = backendType;
if (!window::create_window(selectedBackend)) {
continue;
}
if (webgpu::initialize(selectedBackend)) {
windowCreated = true;
break;
} else {
window::destroy_window();
}
}
}
ASSERT(windowCreated, "Error creating window: {}", SDL_GetError());
// Initialize SDL_Renderer for ImGui when we can't use a Dawn backend
if (webgpu::g_backendType == wgpu::BackendType::Null) {
ASSERT(window::create_renderer(), "Failed to initialize SDL renderer: {}", SDL_GetError());
}
window::show_window();
gfx::initialize();
imgui::create_context();
const auto size = window::get_window_size();
Log.report(LOG_INFO, FMT_STRING("Using framebuffer size {}x{} scale {}"), size.fb_width, size.fb_height, size.scale);
if (g_config.imGuiInitCallback != nullptr) {
g_config.imGuiInitCallback(&size);
}
imgui::initialize();
if (aurora_begin_frame()) {
g_initialFrame = true;
}
g_config.desiredBackend = selectedBackend;
return {
.backend = selectedBackend,
.configPath = g_config.configPath,
.window = window::get_sdl_window(),
.windowSize = size,
};
}
#ifndef EMSCRIPTEN
static wgpu::TextureView g_currentView;
#endif
static void shutdown() noexcept {
#ifndef EMSCRIPTEN
g_currentView = {};
#endif
imgui::shutdown();
gfx::shutdown();
webgpu::shutdown();
window::shutdown();
}
static const AuroraEvent* update() noexcept {
if (g_initialFrame) {
aurora_end_frame();
g_initialFrame = false;
input::initialize();
}
const auto* events = window::poll_events();
imgui::new_frame(window::get_window_size());
return events;
}
static bool begin_frame() noexcept {
#ifndef EMSCRIPTEN
g_currentView = g_swapChain.GetCurrentTextureView();
if (!g_currentView) {
ImGui::EndFrame();
// Force swapchain recreation
const auto size = window::get_window_size();
webgpu::resize_swapchain(size.fb_width, size.fb_height, true);
return false;
}
#endif
gfx::begin_frame();
return true;
}
static void end_frame() noexcept {
const auto encoderDescriptor = wgpu::CommandEncoderDescriptor{
.label = "Redraw encoder",
};
auto encoder = g_device.CreateCommandEncoder(&encoderDescriptor);
gfx::end_frame(encoder);
gfx::render(encoder);
{
const std::array attachments{
wgpu::RenderPassColorAttachment{
#ifdef EMSCRIPTEN
.view = g_swapChain.GetCurrentTextureView(),
#else
.view = g_currentView,
#endif
.loadOp = wgpu::LoadOp::Clear,
.storeOp = wgpu::StoreOp::Store,
},
};
const wgpu::RenderPassDescriptor renderPassDescriptor{
.label = "Post render pass",
.colorAttachmentCount = attachments.size(),
.colorAttachments = attachments.data(),
};
auto pass = encoder.BeginRenderPass(&renderPassDescriptor);
// Copy EFB -> XFB (swapchain)
pass.SetPipeline(webgpu::g_CopyPipeline);
pass.SetBindGroup(0, webgpu::g_CopyBindGroup, 0, nullptr);
pass.Draw(3);
if (!g_initialFrame) {
// Render ImGui
imgui::render(pass);
}
pass.End();
}
const wgpu::CommandBufferDescriptor cmdBufDescriptor{.label = "Redraw command buffer"};
const auto buffer = encoder.Finish(&cmdBufDescriptor);
g_queue.Submit(1, &buffer);
#ifdef WEBGPU_DAWN
g_swapChain.Present();
g_currentView = {};
#else
emscripten_sleep(0);
#endif
if (!g_initialFrame) {
ImGui::EndFrame();
}
}
} // namespace aurora
// C API bindings
AuroraInfo aurora_initialize(int argc, char* argv[], const AuroraConfig* config) {
return aurora::initialize(argc, argv, *config);
}
void aurora_shutdown() { aurora::shutdown(); }
const AuroraEvent* aurora_update() { return aurora::update(); }
bool aurora_begin_frame() { return aurora::begin_frame(); }
void aurora_end_frame() { aurora::end_frame(); }
AuroraBackend aurora_get_backend() { return aurora::g_config.desiredBackend; }
const AuroraBackend* aurora_get_available_backends(size_t* count) {
*count = aurora::PreferredBackendOrder.size();
return aurora::PreferredBackendOrder.data();
}