Files
aurora/lib/aurora.cpp
T

376 lines
12 KiB
C++
Raw Normal View History

2022-07-27 11:25:25 -04:00
#include <aurora/aurora.h>
2026-02-23 22:59:05 +03:00
#ifdef AURORA_ENABLE_GX
2022-07-27 11:25:25 -04:00
#include "gfx/common.hpp"
2026-03-12 23:35:17 -07:00
#include "gx/fifo.hpp"
2022-07-27 11:25:25 -04:00
#include "imgui.hpp"
2026-02-23 22:59:05 +03:00
#include "webgpu/gpu.hpp"
#include <webgpu/webgpu_cpp.h>
#endif
2026-04-27 22:43:30 -07:00
#ifdef AURORA_ENABLE_RMLUI
#include "rmlui.hpp"
#endif
2023-01-21 19:48:26 -08:00
#include "input.hpp"
2022-07-27 11:25:25 -04:00
#include "internal.hpp"
#include "window.hpp"
2025-04-02 19:57:16 -06:00
#include <SDL3/SDL_filesystem.h>
2025-04-03 00:12:22 -06:00
#include <magic_enum.hpp>
2022-07-27 11:25:25 -04:00
2026-04-09 03:19:58 +02:00
#include "tracy/Tracy.hpp"
2022-07-27 11:25:25 -04:00
namespace aurora {
AuroraConfig g_config;
2026-04-06 23:04:43 -06:00
uint32_t g_sdlCustomEventsStart;
char g_gameName[4];
2022-07-27 11:25:25 -04:00
2025-04-03 21:03:08 -06:00
namespace {
Module Log("aurora");
2026-02-23 22:59:05 +03:00
#ifdef AURORA_ENABLE_GX
2022-07-27 11:25:25 -04:00
// GPU
using webgpu::g_device;
using webgpu::g_queue;
2025-04-03 00:12:22 -06:00
using webgpu::g_surface;
2026-02-23 22:59:05 +03:00
#endif
2022-07-27 11:25:25 -04:00
2026-02-23 22:59:05 +03:00
#ifdef AURORA_ENABLE_GX
2022-07-27 11:25:25 -04:00
constexpr std::array PreferredBackendOrder{
#ifdef ENABLE_BACKEND_WEBGPU
BACKEND_WEBGPU,
#endif
2022-07-27 11:25:25 -04:00
#ifdef DAWN_ENABLE_BACKEND_D3D12
BACKEND_D3D12,
2022-07-27 11:25:25 -04:00
#endif
#ifdef DAWN_ENABLE_BACKEND_METAL
BACKEND_METAL,
#endif
#ifdef DAWN_ENABLE_BACKEND_VULKAN
BACKEND_VULKAN,
#endif
#ifdef DAWN_ENABLE_BACKEND_D3D11
BACKEND_D3D11,
2022-07-27 11:25:25 -04:00
#endif
2025-04-03 00:12:22 -06:00
// #ifdef DAWN_ENABLE_BACKEND_DESKTOP_GL
// BACKEND_OPENGL,
// #endif
// #ifdef DAWN_ENABLE_BACKEND_OPENGLES
// BACKEND_OPENGLES,
// #endif
2022-07-27 11:25:25 -04:00
#ifdef DAWN_ENABLE_BACKEND_NULL
BACKEND_NULL,
#endif
};
2026-02-23 22:59:05 +03:00
#else
constexpr std::array<AuroraBackend, 0> PreferredBackendOrder{};
#endif
2022-07-27 11:25:25 -04:00
2025-04-03 21:03:08 -06:00
bool g_initialFrame = false;
2022-07-27 11:25:25 -04:00
2025-04-03 21:03:08 -06:00
AuroraInfo initialize(int argc, char* argv[], const AuroraConfig& config) noexcept {
2022-07-27 11:25:25 -04:00
g_config = config;
2025-04-06 16:37:05 -06:00
Log.info("Aurora initializing");
2022-07-27 11:25:25 -04:00
if (g_config.appName == nullptr) {
g_config.appName = "Aurora";
} else {
g_config.appName = strdup(g_config.appName);
2022-07-27 11:25:25 -04:00
}
if (g_config.configPath == nullptr) {
g_config.configPath = SDL_GetPrefPath(nullptr, g_config.appName);
} else {
g_config.configPath = strdup(g_config.configPath);
2022-07-27 11:25:25 -04:00
}
if (g_config.msaa == 0) {
g_config.msaa = 1;
}
if (g_config.maxTextureAnisotropy == 0) {
g_config.maxTextureAnisotropy = 16;
}
2025-04-02 19:57:16 -06:00
ASSERT(window::initialize(), "Error initializing window");
2022-07-27 11:25:25 -04:00
2026-04-06 23:04:43 -06:00
g_sdlCustomEventsStart = SDL_RegisterEvents(2);
ASSERT(g_sdlCustomEventsStart, "Failed to allocate user events: {}", SDL_GetError());
ASSERT(window::initialize_event_watch(), "Error initializing SDL event watch");
2026-04-06 23:04:43 -06:00
2026-02-23 22:59:05 +03:00
#ifdef AURORA_ENABLE_GX
2022-07-27 11:25:25 -04:00
/* 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();
}
}
}
2022-08-09 02:05:33 -04:00
ASSERT(windowCreated, "Error creating window: {}", SDL_GetError());
2022-07-27 11:25:25 -04:00
// Initialize SDL_Renderer for ImGui when we can't use a Dawn backend
if (webgpu::g_backendType == wgpu::BackendType::Null) {
2022-08-09 02:05:33 -04:00
ASSERT(window::create_renderer(), "Failed to initialize SDL renderer: {}", SDL_GetError());
2022-07-27 11:25:25 -04:00
}
2026-02-23 22:59:05 +03:00
#else
AuroraBackend selectedBackend = BACKEND_NULL;
ASSERT(window::create_window(BACKEND_NULL), "Error creating window: {}", SDL_GetError());
ASSERT(window::create_renderer(), "Failed to initialize SDL renderer: {}", SDL_GetError());
#endif
2022-07-27 11:25:25 -04:00
window::show_window();
2023-01-21 19:48:26 -08:00
2026-02-23 22:59:05 +03:00
#ifdef AURORA_ENABLE_GX
2022-07-27 11:25:25 -04:00
gfx::initialize();
imgui::create_context();
2026-02-23 22:59:05 +03:00
#endif
2022-07-27 11:25:25 -04:00
const auto size = window::get_window_size();
2025-04-06 16:37:05 -06:00
Log.info("Using framebuffer size {}x{} scale {}", size.fb_width, size.fb_height, size.scale);
2026-02-23 22:59:05 +03:00
#ifdef AURORA_ENABLE_GX
2022-07-27 11:25:25 -04:00
if (g_config.imGuiInitCallback != nullptr) {
g_config.imGuiInitCallback(&size);
}
imgui::initialize();
2026-02-23 22:59:05 +03:00
#endif
2022-07-27 11:25:25 -04:00
2026-04-27 22:43:30 -07:00
#ifdef AURORA_ENABLE_RMLUI
rmlui::initialize(size);
#endif
2025-04-03 00:12:22 -06:00
g_initialFrame = true;
g_config.desiredBackend = selectedBackend;
2022-07-27 11:25:25 -04:00
return {
.backend = selectedBackend,
.configPath = g_config.configPath,
2022-08-09 02:05:33 -04:00
.window = window::get_sdl_window(),
2022-07-27 11:25:25 -04:00
.windowSize = size,
};
}
2026-02-23 22:59:05 +03:00
#ifdef AURORA_ENABLE_GX
2025-04-03 21:03:08 -06:00
wgpu::TextureView g_currentView;
2026-02-23 22:59:05 +03:00
#endif
2022-07-27 11:25:25 -04:00
2025-04-03 21:03:08 -06:00
void shutdown() noexcept {
2026-04-27 22:43:30 -07:00
#ifdef AURORA_ENABLE_RMLUI
rmlui::shutdown();
#endif
2026-02-23 22:59:05 +03:00
#ifdef AURORA_ENABLE_GX
g_currentView = {};
2022-07-27 11:25:25 -04:00
imgui::shutdown();
gfx::shutdown();
webgpu::shutdown();
2026-02-23 22:59:05 +03:00
#endif
input::shutdown();
2022-07-27 11:25:25 -04:00
window::shutdown();
}
2025-04-03 21:03:08 -06:00
const AuroraEvent* update() noexcept {
2026-04-09 03:19:58 +02:00
ZoneScoped;
2022-07-27 11:25:25 -04:00
if (g_initialFrame) {
g_initialFrame = false;
2023-01-21 19:48:26 -08:00
input::initialize();
2022-07-27 11:25:25 -04:00
}
2025-04-03 00:12:22 -06:00
return window::poll_events();
2022-07-27 11:25:25 -04:00
}
2025-04-03 21:03:08 -06:00
bool begin_frame() noexcept {
2026-04-09 03:19:58 +02:00
ZoneScoped;
2026-02-23 22:59:05 +03:00
#ifdef AURORA_ENABLE_GX
{
window::SurfaceLock surfaceLock;
if (!window::is_presentable()) {
webgpu::release_surface();
return false;
}
if (window::is_paused()) {
return false;
}
2026-02-19 23:53:38 -07:00
if (!g_surface) {
webgpu::refresh_surface(true);
if (!g_surface) {
return false;
}
}
wgpu::SurfaceTexture surfaceTexture;
g_surface.GetCurrentTexture(&surfaceTexture);
switch (surfaceTexture.status) {
case wgpu::SurfaceGetCurrentTextureStatus::SuccessOptimal:
g_currentView = surfaceTexture.texture.CreateView();
break;
case wgpu::SurfaceGetCurrentTextureStatus::Timeout:
Log.warn("Surface texture acquisition timed out");
return false;
case wgpu::SurfaceGetCurrentTextureStatus::SuccessSuboptimal:
case wgpu::SurfaceGetCurrentTextureStatus::Outdated:
Log.info("Surface texture is {}, reconfiguring swapchain", magic_enum::enum_name(surfaceTexture.status));
webgpu::refresh_surface(false);
return false;
case wgpu::SurfaceGetCurrentTextureStatus::Lost:
Log.warn("Surface texture is {}, releasing surface", magic_enum::enum_name(surfaceTexture.status));
webgpu::release_surface();
case wgpu::SurfaceGetCurrentTextureStatus::Error:
Log.warn("Surface texture is {}, dropping surface", magic_enum::enum_name(surfaceTexture.status));
g_surface = {};
return false;
default:
Log.error("Failed to get surface texture: {}", magic_enum::enum_name(surfaceTexture.status));
2026-02-19 23:53:38 -07:00
return false;
}
2025-04-03 00:12:22 -06:00
}
2026-04-09 03:19:58 +02:00
2025-04-03 00:12:22 -06:00
imgui::new_frame(window::get_window_size());
if (!gfx::begin_frame()) {
g_currentView = {};
return false;
}
2026-02-23 22:59:05 +03:00
#endif
2022-07-27 11:25:25 -04:00
return true;
}
2025-04-03 21:03:08 -06:00
void end_frame() noexcept {
2026-04-09 03:19:58 +02:00
ZoneScoped;
2026-02-23 22:59:05 +03:00
#ifdef AURORA_ENABLE_GX
2026-03-12 23:35:17 -07:00
gx::fifo::drain();
const auto encoderDescriptor = wgpu::CommandEncoderDescriptor{
2022-07-27 11:25:25 -04:00
.label = "Redraw encoder",
};
auto encoder = g_device.CreateCommandEncoder(&encoderDescriptor);
2022-07-27 11:25:25 -04:00
gfx::end_frame(encoder);
gfx::render(encoder);
{
window::SurfaceLock surfaceLock;
if (window::is_presentable() && g_surface && g_currentView) {
const auto& presentSource = webgpu::present_source();
auto viewport = webgpu::calculate_present_viewport(webgpu::g_graphicsConfig.surfaceConfiguration.width,
webgpu::g_graphicsConfig.surfaceConfiguration.height,
presentSource.size.width, presentSource.size.height);
wgpu::BindGroup presentBindGroup = webgpu::g_CopyBindGroup;
#if AURORA_ENABLE_RMLUI
if (rmlui::is_initialized()) {
const auto rmlOutput = rmlui::render(encoder, viewport);
if (rmlOutput.texture != nullptr) {
presentBindGroup = rmlOutput.copyBindGroup;
}
}
#endif
{
const std::array attachments{
wgpu::RenderPassColorAttachment{
.view = g_currentView,
.loadOp = wgpu::LoadOp::Clear,
.storeOp = wgpu::StoreOp::Store,
},
};
const wgpu::RenderPassDescriptor renderPassDescriptor{
.label = "EFB copy render pass",
.colorAttachmentCount = attachments.size(),
.colorAttachments = attachments.data(),
};
const auto pass = encoder.BeginRenderPass(&renderPassDescriptor);
// Copy EFB -> XFB (swapchain)
pass.SetPipeline(webgpu::g_CopyPipeline);
pass.SetBindGroup(0, presentBindGroup, 0, nullptr);
pass.SetViewport(viewport.left, viewport.top, viewport.width, viewport.height, viewport.znear, viewport.zfar);
pass.Draw(3);
pass.End();
}
{
const std::array attachments{
wgpu::RenderPassColorAttachment{
.view = g_currentView,
.loadOp = wgpu::LoadOp::Load,
.storeOp = wgpu::StoreOp::Store,
},
};
const wgpu::RenderPassDescriptor renderPassDescriptor{
.label = "ImGui render pass",
.colorAttachmentCount = attachments.size(),
.colorAttachments = attachments.data(),
};
const auto pass = encoder.BeginRenderPass(&renderPassDescriptor);
pass.SetViewport(0.f, 0.f, static_cast<float>(webgpu::g_graphicsConfig.surfaceConfiguration.width),
static_cast<float>(webgpu::g_graphicsConfig.surfaceConfiguration.height), 0.f, 1.f);
imgui::render(pass);
pass.End();
}
} else {
Log.info("Skipping present; window not presentable");
webgpu::release_surface();
}
const wgpu::CommandBufferDescriptor cmdBufDescriptor{.label = "Redraw command buffer"};
const auto buffer = encoder.Finish(&cmdBufDescriptor);
g_queue.Submit(1, &buffer);
gfx::after_submit();
if (window::is_presentable() && g_surface) {
auto presentStatus = g_surface.Present();
if (presentStatus != wgpu::Status::Success) {
Log.warn("Surface present failed: {}", static_cast<int>(presentStatus));
webgpu::release_surface();
}
} else if (g_surface) {
webgpu::release_surface();
}
g_currentView = {};
2026-03-30 21:33:32 -06:00
}
2026-04-09 03:19:58 +02:00
TracyPlotConfig("aurora: lastVertSize", tracy::PlotFormatType::Memory, false, true, 0);
TracyPlotConfig("aurora: lastUniformSize", tracy::PlotFormatType::Memory, false, true, 0);
TracyPlotConfig("aurora: lastIndexSize", tracy::PlotFormatType::Memory, false, true, 0);
TracyPlotConfig("aurora: lastStorageSize", tracy::PlotFormatType::Memory, false, true, 0);
TracyPlotConfig("aurora: lastTextureUploadSize", tracy::PlotFormatType::Memory, false, true, 0);
TracyPlot("aurora: queuedPipelines", static_cast<int64_t>(gfx::g_stats.queuedPipelines));
TracyPlot("aurora: createdPipelines", static_cast<int64_t>(gfx::g_stats.createdPipelines));
TracyPlot("aurora: drawCallCount", static_cast<int64_t>(gfx::g_stats.drawCallCount));
TracyPlot("aurora: mergedDrawCallCount", static_cast<int64_t>(gfx::g_stats.mergedDrawCallCount));
TracyPlot("aurora: lastVertSize", static_cast<int64_t>(gfx::g_stats.lastVertSize));
TracyPlot("aurora: lastUniformSize", static_cast<int64_t>(gfx::g_stats.lastUniformSize));
TracyPlot("aurora: lastIndexSize", static_cast<int64_t>(gfx::g_stats.lastIndexSize));
TracyPlot("aurora: lastStorageSize", static_cast<int64_t>(gfx::g_stats.lastStorageSize));
TracyPlot("aurora: lastTextureUploadSize", static_cast<int64_t>(gfx::g_stats.lastTextureUploadSize));
2026-02-23 22:59:05 +03:00
#endif
2022-07-27 11:25:25 -04:00
}
2025-04-03 21:03:08 -06:00
} // namespace
2022-07-27 11:25:25 -04:00
} // 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) {
if (count != nullptr) {
*count = aurora::PreferredBackendOrder.size();
}
return aurora::PreferredBackendOrder.data();
}
void aurora_set_log_level(AuroraLogLevel level) { aurora::g_config.logLevel = level; }
void aurora_set_pause_on_focus_lost(bool value) { aurora::g_config.pauseOnFocusLost = value; }
void aurora_set_background_input(bool value) {
aurora::g_config.allowJoystickBackgroundEvents = value;
aurora::window::set_background_input(value);
}