diff --git a/include/aurora/aurora.h b/include/aurora/aurora.h index fd688f9..c66f622 100644 --- a/include/aurora/aurora.h +++ b/include/aurora/aurora.h @@ -80,6 +80,7 @@ typedef struct { AuroraBackend desiredBackend; uint32_t msaa; uint16_t maxTextureAnisotropy; + bool vsync; bool startFullscreen; bool allowJoystickBackgroundEvents; bool allowTextureReplacements; diff --git a/include/aurora/gfx.h b/include/aurora/gfx.h index e1df519..0319345 100644 --- a/include/aurora/gfx.h +++ b/include/aurora/gfx.h @@ -30,6 +30,8 @@ typedef struct { const AuroraStats* aurora_get_stats(); +void aurora_enable_vsync(bool enabled); + #ifdef __cplusplus } #endif diff --git a/lib/aurora.cpp b/lib/aurora.cpp index ffaf490..fc7ea0a 100644 --- a/lib/aurora.cpp +++ b/lib/aurora.cpp @@ -17,6 +17,7 @@ namespace aurora { AuroraConfig g_config; +uint32_t g_sdlCustomEventsStart; namespace { Module Log("aurora"); @@ -78,6 +79,9 @@ AuroraInfo initialize(int argc, char* argv[], const AuroraConfig& config) noexce } ASSERT(window::initialize(), "Error initializing window"); + g_sdlCustomEventsStart = SDL_RegisterEvents(2); + ASSERT(g_sdlCustomEventsStart, "Failed to allocate user events: {}", SDL_GetError()); + #ifdef AURORA_ENABLE_GX /* Attempt to create a window using the calling application's desired backend */ AuroraBackend selectedBackend = config.desiredBackend; diff --git a/lib/internal.hpp b/lib/internal.hpp index e352404..da2a683 100644 --- a/lib/internal.hpp +++ b/lib/internal.hpp @@ -124,6 +124,7 @@ auto underlying(T value) -> std::underlying_type_t { namespace aurora { extern AuroraConfig g_config; +extern uint32_t g_sdlCustomEventsStart; template class ArrayRef { diff --git a/lib/webgpu/gpu.cpp b/lib/webgpu/gpu.cpp index 21e6fe1..e955beb 100644 --- a/lib/webgpu/gpu.cpp +++ b/lib/webgpu/gpu.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -46,6 +47,31 @@ wgpu::BindGroup g_CopyBindGroup; static wgpu::Adapter g_adapter; wgpu::Instance g_instance; static wgpu::AdapterInfo g_adapterInfo; +static wgpu::SurfaceCapabilities g_surfaceCapabilities; + +static wgpu::PresentMode best_present_mode(bool vsync) { + const auto supports = [](const wgpu::PresentMode candidate) { + for (size_t i = 0; i < g_surfaceCapabilities.presentModeCount; ++i) { + if (g_surfaceCapabilities.presentModes[i] == candidate) { + return true; + } + } + return false; + }; + if (vsync) { + if (supports(wgpu::PresentMode::FifoRelaxed)) { + return wgpu::PresentMode::FifoRelaxed; + } + } else { + if (supports(wgpu::PresentMode::Mailbox)) { + return wgpu::PresentMode::Mailbox; + } + if (supports(wgpu::PresentMode::Immediate)) { + return wgpu::PresentMode::Immediate; + } + } + return wgpu::PresentMode::Fifo; +} TextureWithSampler create_render_texture(uint32_t width, uint32_t height, bool multisampled) { const wgpu::Extent3D size{ @@ -70,13 +96,13 @@ TextureWithSampler create_render_texture(uint32_t width, uint32_t height, bool m }; auto texture = g_device.CreateTexture(&textureDescriptor); - const wgpu::TextureViewDescriptor viewDescriptor{ + constexpr wgpu::TextureViewDescriptor viewDescriptor{ .label = "Render texture view", .dimension = wgpu::TextureViewDimension::e2D, }; auto view = texture.CreateView(&viewDescriptor); - const wgpu::SamplerDescriptor samplerDescriptor{ + constexpr wgpu::SamplerDescriptor samplerDescriptor{ .label = "Render sampler", .addressModeU = wgpu::AddressMode::ClampToEdge, .addressModeV = wgpu::AddressMode::ClampToEdge, @@ -441,7 +467,7 @@ bool initialize(AuroraBackend auroraBackend) { requiredLimits.minUniformBufferOffsetAlignment, requiredLimits.minStorageBufferOffsetAlignment); std::vector requiredFeatures{wgpu::FeatureName::TextureComponentSwizzle}; #ifdef WEBGPU_DAWN - const std::array enableToggles{ + constexpr std::array enableToggles{ /* clang-format off */ #if _WIN32 "use_dxc", @@ -520,22 +546,21 @@ bool initialize(AuroraBackend auroraBackend) { } g_queue = g_device.GetQueue(); - wgpu::SurfaceCapabilities surfaceCapabilities; - const wgpu::Status status = g_surface.GetCapabilities(g_adapter, &surfaceCapabilities); + const wgpu::Status status = g_surface.GetCapabilities(g_adapter, &g_surfaceCapabilities); if (status != wgpu::Status::Success) { Log.error("Failed to get surface capabilities: {}", magic_enum::enum_name(status)); return false; } - if (surfaceCapabilities.formatCount == 0) { + if (g_surfaceCapabilities.formatCount == 0) { Log.error("Surface has no formats"); return false; } - if (surfaceCapabilities.presentModeCount == 0) { + if (g_surfaceCapabilities.presentModeCount == 0) { Log.error("Surface has no present modes"); return false; } - auto surfaceFormat = surfaceCapabilities.formats[0]; - auto presentMode = surfaceCapabilities.presentModes[0]; + auto surfaceFormat = g_surfaceCapabilities.formats[0]; + auto presentMode = best_present_mode(g_config.vsync); if (surfaceFormat == wgpu::TextureFormat::RGBA8UnormSrgb) { surfaceFormat = wgpu::TextureFormat::RGBA8Unorm; } else if (surfaceFormat == wgpu::TextureFormat::BGRA8UnormSrgb) { @@ -625,3 +650,9 @@ void resize_swapchain(uint32_t width, uint32_t height, uint32_t native_width, ui create_copy_bind_group(); } } // namespace aurora::webgpu + +void aurora_enable_vsync(const bool enabled) { + aurora::webgpu::g_graphicsConfig.surfaceConfiguration.presentMode = aurora::webgpu::best_present_mode(enabled); + SDL_Event event{.type = aurora::g_sdlCustomEventsStart + 1}; + SDL_PushEvent(&event); +} diff --git a/lib/window.cpp b/lib/window.cpp index 552989a..6c92fd2 100644 --- a/lib/window.cpp +++ b/lib/window.cpp @@ -27,7 +27,6 @@ namespace aurora::window { namespace { Module Log("aurora::window"); -uint32_t g_sdlResizeAspectRatioEvent; SDL_Window* g_window; SDL_Renderer* g_renderer; bool g_aspectRatioLocked; @@ -160,12 +159,18 @@ const AuroraEvent* poll_events() { .type = AURORA_EXIT, }); default: - if (event.type == g_sdlResizeAspectRatioEvent) { + if (event.type == g_sdlCustomEventsStart) { + // Future resize event resize_swapchain(); g_events.push_back(AuroraEvent{ .type = AURORA_WINDOW_RESIZED, .windowSize = get_window_size(), }); + } else if (event.type == g_sdlCustomEventsStart + 1) { + // Refresh surface (vsync changed) +#ifdef AURORA_ENABLE_GX + webgpu::refresh_surface(false); +#endif } break; } @@ -314,10 +319,6 @@ bool initialize() { SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, SDL_GetError()); } - TRY( - (g_sdlResizeAspectRatioEvent = SDL_RegisterEvents(1)), - "Failed to allocate user events: {}", SDL_GetError()); - return true; } @@ -388,8 +389,7 @@ void center_window() { } static void push_future_resize_event() { - SDL_Event event = {}; - event.type = g_sdlResizeAspectRatioEvent; + SDL_Event event{.type = g_sdlCustomEventsStart}; TRY_WARN(SDL_PushEvent(&event), "Failed to push SDL event for future resize: {}", SDL_GetError()); }