mirror of
https://github.com/encounter/aurora.git
synced 2026-07-09 18:19:33 -07:00
Refactor viewport fit handling; use game viewport for RmlUi
This commit is contained in:
@@ -83,19 +83,19 @@ void GXPopDebugGroup();
|
||||
void GXInsertDebugMarker(const char* label);
|
||||
|
||||
typedef enum _AuroraViewportPolicy {
|
||||
AURORA_VIEWPORT_FIT = 0, // Scale logical viewport to fit
|
||||
AURORA_VIEWPORT_STRETCH = 1, // Stretch logical viewport to fit (widescreen enabled)
|
||||
AURORA_VIEWPORT_NATIVE = 2, // Use native framebuffer resolution
|
||||
AURORA_VIEWPORT_FIT = 0, // Preserve logical aspect in the content framebuffer
|
||||
AURORA_VIEWPORT_STRETCH = 1, // Match content framebuffer aspect to the native surface
|
||||
AURORA_VIEWPORT_NATIVE = 2, // Use active framebuffer pixels directly
|
||||
} AuroraViewportPolicy;
|
||||
|
||||
/**
|
||||
* Configures how GXSetViewport/GXSetScissor parameters are applied to the actual render viewport.
|
||||
* Configures content framebuffer sizing and how GXSetViewport/GXSetScissor parameters are applied to rendering.
|
||||
* When AURORA_VIEWPORT_NATIVE is used, GXSetTexCopySrc/GXSetTexCopyDst will use native framebuffer resolution.
|
||||
*/
|
||||
void AuroraSetViewportPolicy(AuroraViewportPolicy policy);
|
||||
|
||||
/**
|
||||
* Retrieves the current render size based on the framebuffer size and viewport policy.
|
||||
* Retrieves the current content framebuffer size.
|
||||
*/
|
||||
void AuroraGetRenderSize(u32* width, u32* height);
|
||||
|
||||
|
||||
+13
-43
@@ -69,36 +69,6 @@ constexpr std::array<AuroraBackend, 0> PreferredBackendOrder{};
|
||||
|
||||
bool g_initialFrame = false;
|
||||
|
||||
#ifdef AURORA_ENABLE_GX
|
||||
gfx::Viewport calculate_present_viewport(uint32_t surface_width, uint32_t surface_height, uint32_t content_width,
|
||||
uint32_t content_height) {
|
||||
if (surface_width == 0 || surface_height == 0 || content_width == 0 || content_height == 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
uint32_t viewport_width = surface_width;
|
||||
uint32_t viewport_height = std::min<uint32_t>(
|
||||
surface_height, std::max<uint32_t>(1u, static_cast<uint32_t>(std::lround(static_cast<double>(viewport_width) *
|
||||
static_cast<double>(content_height) /
|
||||
static_cast<double>(content_width)))));
|
||||
if (viewport_height == surface_height) {
|
||||
viewport_width = std::min<uint32_t>(
|
||||
surface_width, std::max<uint32_t>(1u, static_cast<uint32_t>(std::lround(static_cast<double>(viewport_height) *
|
||||
static_cast<double>(content_width) /
|
||||
static_cast<double>(content_height)))));
|
||||
}
|
||||
|
||||
return {
|
||||
.left = static_cast<float>((surface_width - viewport_width) / 2),
|
||||
.top = static_cast<float>((surface_height - viewport_height) / 2),
|
||||
.width = static_cast<float>(viewport_width),
|
||||
.height = static_cast<float>(viewport_height),
|
||||
.znear = 0.f,
|
||||
.zfar = 1.f,
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
AuroraInfo initialize(int argc, char* argv[], const AuroraConfig& config) noexcept {
|
||||
g_config = config;
|
||||
Log.info("Aurora initializing");
|
||||
@@ -270,19 +240,18 @@ void end_frame() noexcept {
|
||||
auto encoder = g_device.CreateCommandEncoder(&encoderDescriptor);
|
||||
gfx::end_frame(encoder);
|
||||
gfx::render(encoder);
|
||||
const auto viewport = calculate_present_viewport(webgpu::g_graphicsConfig.surfaceConfiguration.width,
|
||||
webgpu::g_graphicsConfig.surfaceConfiguration.height,
|
||||
webgpu::g_frameBuffer.size.width, webgpu::g_frameBuffer.size.height);
|
||||
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()) {
|
||||
rmlui::render(encoder, g_currentView,
|
||||
{
|
||||
.width = webgpu::g_graphicsConfig.surfaceConfiguration.width,
|
||||
.height = webgpu::g_graphicsConfig.surfaceConfiguration.height,
|
||||
.depthOrArrayLayers = 1,
|
||||
},
|
||||
viewport);
|
||||
} else
|
||||
const auto rmlOutput = rmlui::render(encoder, viewport);
|
||||
if (rmlOutput.texture != nullptr) {
|
||||
presentBindGroup = rmlOutput.copyBindGroup;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
{
|
||||
const std::array attachments{
|
||||
@@ -300,7 +269,7 @@ void end_frame() noexcept {
|
||||
const auto pass = encoder.BeginRenderPass(&renderPassDescriptor);
|
||||
// Copy EFB -> XFB (swapchain)
|
||||
pass.SetPipeline(webgpu::g_CopyPipeline);
|
||||
pass.SetBindGroup(0, webgpu::g_CopyBindGroup, 0, nullptr);
|
||||
pass.SetBindGroup(0, presentBindGroup, 0, nullptr);
|
||||
pass.SetViewport(viewport.left, viewport.top, viewport.width, viewport.height, viewport.znear, viewport.zfar);
|
||||
|
||||
pass.Draw(3);
|
||||
@@ -320,7 +289,8 @@ void end_frame() noexcept {
|
||||
.colorAttachments = attachments.data(),
|
||||
};
|
||||
const auto pass = encoder.BeginRenderPass(&renderPassDescriptor);
|
||||
pass.SetViewport(viewport.left, viewport.top, viewport.width, viewport.height, viewport.znear, viewport.zfar);
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
#include "dolphin/gx/GXAurora.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
|
||||
#include "__gx.h"
|
||||
@@ -10,7 +8,6 @@
|
||||
|
||||
#include "../../gfx/common.hpp"
|
||||
#include "../../gx/fifo.hpp"
|
||||
#include "../vi/vi_internal.hpp"
|
||||
|
||||
static void GXWriteString(const char* label) {
|
||||
auto length = strlen(label);
|
||||
@@ -36,33 +33,18 @@ void GXInsertDebugMarker(const char* label) {
|
||||
GXWriteString(label);
|
||||
}
|
||||
|
||||
void AuroraSetViewportPolicy(AuroraViewportPolicy policy) { g_gxState.viewportPolicy = policy; }
|
||||
void AuroraSetViewportPolicy(AuroraViewportPolicy policy) {
|
||||
g_gxState.viewportPolicy = policy;
|
||||
aurora::window::set_frame_buffer_aspect_fit(policy == AURORA_VIEWPORT_FIT);
|
||||
}
|
||||
|
||||
void AuroraGetRenderSize(u32* width, u32* height) {
|
||||
const auto windowSize = aurora::window::get_window_size();
|
||||
u32 renderWidth = windowSize.fb_width;
|
||||
u32 renderHeight = windowSize.fb_height;
|
||||
|
||||
if (g_gxState.viewportPolicy == AURORA_VIEWPORT_FIT) {
|
||||
const auto efbSize = aurora::vi::configured_fb_size();
|
||||
if (efbSize.x != 0 && efbSize.y != 0 && renderWidth != 0 && renderHeight != 0) {
|
||||
const double targetAspect = static_cast<double>(renderWidth) / static_cast<double>(renderHeight);
|
||||
const double contentAspect = static_cast<double>(efbSize.x) / static_cast<double>(efbSize.y);
|
||||
if (targetAspect > contentAspect) {
|
||||
renderWidth =
|
||||
std::max<u32>(1u, static_cast<u32>(std::lround(static_cast<double>(renderHeight) * contentAspect)));
|
||||
} else {
|
||||
renderHeight =
|
||||
std::max<u32>(1u, static_cast<u32>(std::lround(static_cast<double>(renderWidth) / contentAspect)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (width != nullptr) {
|
||||
*width = renderWidth;
|
||||
*width = windowSize.fb_width;
|
||||
}
|
||||
if (height != nullptr) {
|
||||
*height = renderHeight;
|
||||
*height = windowSize.fb_height;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+17
-9
@@ -9,20 +9,28 @@
|
||||
namespace aurora::vi {
|
||||
std::optional<GXRenderModeObj> g_renderMode;
|
||||
|
||||
void configure(const GXRenderModeObj* rm) noexcept {
|
||||
if (rm == nullptr) {
|
||||
g_renderMode.reset();
|
||||
return;
|
||||
}
|
||||
g_renderMode = *rm;
|
||||
}
|
||||
|
||||
Vec2<uint32_t> configured_fb_size() noexcept {
|
||||
Vec2<uint32_t> render_mode_size() noexcept {
|
||||
if (!g_renderMode) {
|
||||
return {640, 480};
|
||||
}
|
||||
return {g_renderMode->fbWidth, g_renderMode->efbHeight};
|
||||
}
|
||||
|
||||
void configure(const GXRenderModeObj* rm) noexcept {
|
||||
const auto oldSize = render_mode_size();
|
||||
if (rm == nullptr) {
|
||||
g_renderMode.reset();
|
||||
} else {
|
||||
g_renderMode = *rm;
|
||||
}
|
||||
if (render_mode_size() != oldSize) {
|
||||
window::request_frame_buffer_resize();
|
||||
}
|
||||
}
|
||||
|
||||
Vec2<uint32_t> configured_fb_size() noexcept {
|
||||
return render_mode_size();
|
||||
}
|
||||
} // namespace aurora::vi
|
||||
|
||||
extern "C" {
|
||||
|
||||
+2
-14
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "../internal.hpp"
|
||||
#include "../webgpu/gpu.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
@@ -208,20 +209,7 @@ struct ClipRect {
|
||||
bool operator!=(const ClipRect& rhs) const { return !(*this == rhs); }
|
||||
};
|
||||
|
||||
struct Viewport {
|
||||
float left;
|
||||
float top;
|
||||
float width;
|
||||
float height;
|
||||
float znear;
|
||||
float zfar;
|
||||
|
||||
bool operator==(const Viewport& rhs) const {
|
||||
return left == rhs.left && top == rhs.top && width == rhs.width && height == rhs.height && znear == rhs.znear &&
|
||||
zfar == rhs.zfar;
|
||||
}
|
||||
bool operator!=(const Viewport& rhs) const { return !(*this == rhs); }
|
||||
};
|
||||
using webgpu::Viewport;
|
||||
|
||||
struct TextureRef;
|
||||
using TextureHandle = std::shared_ptr<TextureRef>;
|
||||
|
||||
+12
-38
@@ -246,31 +246,6 @@ Vec2<uint32_t> logical_fb_size() noexcept {
|
||||
return gfx::is_offscreen() ? gfx::get_render_target_size() : vi::configured_fb_size();
|
||||
}
|
||||
|
||||
std::tuple<float, float, float, float> calculate_inner_box(const int targetWidth, const int targetHeight,
|
||||
const int logicalFbWidth, const int logicalFbHeight) noexcept {
|
||||
if (g_gxState.viewportPolicy == AURORA_VIEWPORT_STRETCH) {
|
||||
return {
|
||||
0.0f,
|
||||
0.0f,
|
||||
static_cast<float>(targetWidth),
|
||||
static_cast<float>(targetHeight),
|
||||
};
|
||||
} else {
|
||||
const auto scale = std::min(static_cast<float>(targetWidth) / static_cast<float>(logicalFbWidth),
|
||||
static_cast<float>(targetHeight) / static_cast<float>(logicalFbHeight));
|
||||
const auto offsetX =
|
||||
std::trunc((static_cast<float>(targetWidth) - static_cast<float>(logicalFbWidth) * scale) * 0.5f);
|
||||
const auto offsetY =
|
||||
std::trunc((static_cast<float>(targetHeight) - static_cast<float>(logicalFbHeight) * scale) * 0.5f);
|
||||
return {
|
||||
offsetX,
|
||||
offsetY,
|
||||
static_cast<float>(targetWidth) - 2.0f * offsetX,
|
||||
static_cast<float>(targetHeight) - 2.0f * offsetY,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
gfx::Viewport map_logical_viewport(const gfx::Viewport& logicalViewport) noexcept {
|
||||
if (g_gxState.viewportPolicy == AURORA_VIEWPORT_NATIVE) {
|
||||
return logicalViewport;
|
||||
@@ -282,14 +257,13 @@ gfx::Viewport map_logical_viewport(const gfx::Viewport& logicalViewport) noexcep
|
||||
return logicalViewport;
|
||||
}
|
||||
|
||||
const auto [offsetX, offsetY, innerWidth, innerHeight] =
|
||||
calculate_inner_box(targetWidth, targetHeight, logicalFbWidth, logicalFbHeight);
|
||||
|
||||
const float scaleX = static_cast<float>(targetWidth) / static_cast<float>(logicalFbWidth);
|
||||
const float scaleY = static_cast<float>(targetHeight) / static_cast<float>(logicalFbHeight);
|
||||
return {
|
||||
.left = offsetX + (logicalViewport.left / static_cast<float>(logicalFbWidth)) * innerWidth,
|
||||
.top = offsetY + (logicalViewport.top / static_cast<float>(logicalFbHeight)) * innerHeight,
|
||||
.width = (logicalViewport.width / static_cast<float>(logicalFbWidth)) * innerWidth,
|
||||
.height = (logicalViewport.height / static_cast<float>(logicalFbHeight)) * innerHeight,
|
||||
.left = logicalViewport.left * scaleX,
|
||||
.top = logicalViewport.top * scaleY,
|
||||
.width = logicalViewport.width * scaleX,
|
||||
.height = logicalViewport.height * scaleY,
|
||||
.znear = logicalViewport.znear,
|
||||
.zfar = logicalViewport.zfar,
|
||||
};
|
||||
@@ -306,13 +280,13 @@ gfx::ClipRect map_logical_scissor(const gfx::ClipRect& logicalScissor) noexcept
|
||||
return logicalScissor;
|
||||
}
|
||||
|
||||
const auto [offsetX, offsetY, innerWidth, innerHeight] =
|
||||
calculate_inner_box(targetWidth, targetHeight, logicalFbWidth, logicalFbHeight);
|
||||
const float scaleX = static_cast<float>(targetWidth) / static_cast<float>(logicalFbWidth);
|
||||
const float scaleY = static_cast<float>(targetHeight) / static_cast<float>(logicalFbHeight);
|
||||
|
||||
const float left = offsetX + (static_cast<float>(logicalScissor.x) / static_cast<float>(logicalFbWidth)) * innerWidth;
|
||||
const float top = offsetY + (static_cast<float>(logicalScissor.y) / static_cast<float>(logicalFbHeight)) * innerHeight;
|
||||
const float right = offsetX + (static_cast<float>(logicalScissor.x + logicalScissor.width) / static_cast<float>(logicalFbWidth)) * innerWidth;
|
||||
const float bottom = offsetY + (static_cast<float>(logicalScissor.y + logicalScissor.height) / static_cast<float>(logicalFbHeight)) * innerHeight;
|
||||
const float left = static_cast<float>(logicalScissor.x) * scaleX;
|
||||
const float top = static_cast<float>(logicalScissor.y) * scaleY;
|
||||
const float right = static_cast<float>(logicalScissor.x + logicalScissor.width) * scaleX;
|
||||
const float bottom = static_cast<float>(logicalScissor.y + logicalScissor.height) * scaleY;
|
||||
|
||||
const auto mappedLeft = std::clamp(static_cast<int32_t>(std::floor(left)), 0, static_cast<int32_t>(targetWidth));
|
||||
const auto mappedTop = std::clamp(static_cast<int32_t>(std::floor(top)), 0, static_cast<int32_t>(targetHeight));
|
||||
|
||||
+332
-17
@@ -13,21 +13,292 @@
|
||||
#include "webgpu/gpu.hpp"
|
||||
|
||||
namespace aurora::rmlui {
|
||||
Rml::Context* g_context = nullptr;
|
||||
FileInterface_SDL* g_fileInterface = nullptr;
|
||||
|
||||
namespace {
|
||||
Module Log("aurora::rmlui");
|
||||
constexpr size_t MaxTrackedTouches = 16;
|
||||
|
||||
struct TrackedTouch {
|
||||
SDL_FingerID id = 0;
|
||||
Rml::Vector2f position;
|
||||
bool active = false;
|
||||
};
|
||||
|
||||
uint32_t s_pressedMouseButtons = 0;
|
||||
std::array<TrackedTouch, MaxTrackedTouches> s_trackedTouches{};
|
||||
webgpu::TextureWithSampler s_renderTarget;
|
||||
wgpu::BindGroup s_renderTargetCopyBindGroup;
|
||||
|
||||
WebGPURenderInterface* get_render_interface() noexcept {
|
||||
return static_cast<WebGPURenderInterface*>(Backend::GetRenderInterface()); // NOLINT(*-pro-type-static-cast-downcast)
|
||||
}
|
||||
|
||||
Rml::Vector2i dimensions_from_viewport(const gfx::Viewport& viewport) noexcept {
|
||||
return {
|
||||
std::max(1, static_cast<int>(std::lround(viewport.width))),
|
||||
std::max(1, static_cast<int>(std::lround(viewport.height))),
|
||||
};
|
||||
}
|
||||
|
||||
Rml::Vector2i presentation_dimensions_from_window_size(const AuroraWindowSize& size) noexcept {
|
||||
const auto viewport =
|
||||
webgpu::calculate_present_viewport(size.native_fb_width, size.native_fb_height, size.fb_width, size.fb_height);
|
||||
return dimensions_from_viewport(viewport);
|
||||
}
|
||||
|
||||
float density_ratio_for_window(const AuroraWindowSize& size) noexcept {
|
||||
if (size.width == 0 || size.height == 0 || size.native_fb_width == 0 || size.native_fb_height == 0) {
|
||||
return 1.f;
|
||||
}
|
||||
|
||||
return std::min(static_cast<float>(size.native_fb_width) / static_cast<float>(size.width),
|
||||
static_cast<float>(size.native_fb_height) / static_cast<float>(size.height));
|
||||
}
|
||||
|
||||
void sync_context_metrics(Rml::Vector2i dimensions) noexcept {
|
||||
if (g_context == nullptr || dimensions.x <= 0 || dimensions.y <= 0) {
|
||||
return;
|
||||
}
|
||||
if (g_context->GetDimensions() != dimensions) {
|
||||
g_context->SetDimensions(dimensions);
|
||||
}
|
||||
const float ratio = density_ratio_for_window(window::get_window_size());
|
||||
if (g_context->GetDensityIndependentPixelRatio() != ratio) {
|
||||
g_context->SetDensityIndependentPixelRatio(ratio);
|
||||
}
|
||||
}
|
||||
|
||||
void ensure_render_target(Rml::Vector2i dimensions) noexcept {
|
||||
if (dimensions.x <= 0 || dimensions.y <= 0) {
|
||||
return;
|
||||
}
|
||||
const auto width = static_cast<uint32_t>(dimensions.x);
|
||||
const auto height = static_cast<uint32_t>(dimensions.y);
|
||||
if (s_renderTarget.view && s_renderTarget.size.width == width && s_renderTarget.size.height == height) {
|
||||
return;
|
||||
}
|
||||
s_renderTarget = webgpu::create_render_texture(width, height, false);
|
||||
s_renderTargetCopyBindGroup = webgpu::create_copy_bind_group(s_renderTarget);
|
||||
}
|
||||
|
||||
void copy_game_to_render_target(const wgpu::CommandEncoder& encoder) noexcept {
|
||||
if (!s_renderTarget.view) {
|
||||
return;
|
||||
}
|
||||
const std::array attachments{
|
||||
wgpu::RenderPassColorAttachment{
|
||||
.view = s_renderTarget.view,
|
||||
.loadOp = wgpu::LoadOp::Clear,
|
||||
.storeOp = wgpu::StoreOp::Store,
|
||||
},
|
||||
};
|
||||
const wgpu::RenderPassDescriptor renderPassDescriptor{
|
||||
.label = "RmlUi game frame copy pass",
|
||||
.colorAttachmentCount = attachments.size(),
|
||||
.colorAttachments = attachments.data(),
|
||||
};
|
||||
const auto pass = encoder.BeginRenderPass(&renderPassDescriptor);
|
||||
pass.SetPipeline(webgpu::g_CopyPipeline);
|
||||
pass.SetBindGroup(0, webgpu::g_CopyBindGroup, 0, nullptr);
|
||||
pass.SetViewport(0.f, 0.f, static_cast<float>(s_renderTarget.size.width),
|
||||
static_cast<float>(s_renderTarget.size.height), 0.f, 1.f);
|
||||
pass.Draw(3);
|
||||
pass.End();
|
||||
}
|
||||
|
||||
struct MappedPoint {
|
||||
Rml::Vector2f position;
|
||||
bool inside = false;
|
||||
};
|
||||
|
||||
MappedPoint map_native_point_to_content(float nativeX, float nativeY) noexcept {
|
||||
if (g_context == nullptr) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto size = window::get_window_size();
|
||||
const Rml::Vector2i contentSize = g_context->GetDimensions();
|
||||
if (size.native_fb_width == 0 || size.native_fb_height == 0 || contentSize.x <= 0 || contentSize.y <= 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto viewport =
|
||||
webgpu::calculate_present_viewport(size.native_fb_width, size.native_fb_height,
|
||||
static_cast<uint32_t>(contentSize.x), static_cast<uint32_t>(contentSize.y));
|
||||
const float right = viewport.left + viewport.width;
|
||||
const float bottom = viewport.top + viewport.height;
|
||||
if (viewport.width <= 0.f || viewport.height <= 0.f || nativeX < viewport.left || nativeY < viewport.top ||
|
||||
nativeX >= right || nativeY >= bottom) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return {
|
||||
.position =
|
||||
{
|
||||
(nativeX - viewport.left) * static_cast<float>(contentSize.x) / viewport.width,
|
||||
(nativeY - viewport.top) * static_cast<float>(contentSize.y) / viewport.height,
|
||||
},
|
||||
.inside = true,
|
||||
};
|
||||
}
|
||||
|
||||
MappedPoint map_window_point_to_content(float windowX, float windowY) noexcept {
|
||||
const auto size = window::get_window_size();
|
||||
if (size.width == 0 || size.height == 0) {
|
||||
return {};
|
||||
}
|
||||
return map_native_point_to_content(
|
||||
windowX * static_cast<float>(size.native_fb_width) / static_cast<float>(size.width),
|
||||
windowY * static_cast<float>(size.native_fb_height) / static_cast<float>(size.height));
|
||||
}
|
||||
|
||||
MappedPoint map_touch_point_to_content(const SDL_TouchFingerEvent& event) noexcept {
|
||||
const auto size = window::get_window_size();
|
||||
return map_native_point_to_content(event.x * static_cast<float>(size.native_fb_width),
|
||||
event.y * static_cast<float>(size.native_fb_height));
|
||||
}
|
||||
|
||||
int rounded_content_coord(float value) noexcept { return static_cast<int>(std::floor(value)); }
|
||||
|
||||
bool mouse_button_tracked(uint8_t button) noexcept {
|
||||
return button < 32 && (s_pressedMouseButtons & (1u << button)) != 0;
|
||||
}
|
||||
|
||||
void set_mouse_button_tracked(uint8_t button, bool tracked) noexcept {
|
||||
if (button >= 32) {
|
||||
return;
|
||||
}
|
||||
const uint32_t mask = 1u << button;
|
||||
if (tracked) {
|
||||
s_pressedMouseButtons |= mask;
|
||||
} else {
|
||||
s_pressedMouseButtons &= ~mask;
|
||||
}
|
||||
}
|
||||
|
||||
TrackedTouch* find_tracked_touch(SDL_FingerID id) noexcept {
|
||||
for (auto& touch : s_trackedTouches) {
|
||||
if (touch.active && touch.id == id) {
|
||||
return &touch;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
TrackedTouch* find_free_touch() noexcept {
|
||||
for (auto& touch : s_trackedTouches) {
|
||||
if (!touch.active) {
|
||||
return &touch;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Rml::TouchList touch_list(SDL_FingerID id, Rml::Vector2f position) {
|
||||
return {Rml::Touch{static_cast<Rml::TouchId>(id), position}};
|
||||
}
|
||||
|
||||
void handle_mouse_motion(const SDL_MouseMotionEvent& motion) noexcept {
|
||||
const MappedPoint mapped = map_window_point_to_content(motion.x, motion.y);
|
||||
if (!mapped.inside) {
|
||||
g_context->ProcessMouseLeave();
|
||||
return;
|
||||
}
|
||||
g_context->ProcessMouseMove(rounded_content_coord(mapped.position.x), rounded_content_coord(mapped.position.y),
|
||||
RmlSDL::GetKeyModifierState());
|
||||
}
|
||||
|
||||
void handle_mouse_button_down(const SDL_MouseButtonEvent& button) noexcept {
|
||||
const MappedPoint mapped = map_window_point_to_content(button.x, button.y);
|
||||
if (!mapped.inside) {
|
||||
g_context->ProcessMouseLeave();
|
||||
return;
|
||||
}
|
||||
g_context->ProcessMouseMove(rounded_content_coord(mapped.position.x), rounded_content_coord(mapped.position.y),
|
||||
RmlSDL::GetKeyModifierState());
|
||||
g_context->ProcessMouseButtonDown(RmlSDL::ConvertMouseButton(button.button), RmlSDL::GetKeyModifierState());
|
||||
set_mouse_button_tracked(button.button, true);
|
||||
SDL_CaptureMouse(true);
|
||||
}
|
||||
|
||||
void handle_mouse_button_up(const SDL_MouseButtonEvent& button) noexcept {
|
||||
if (!mouse_button_tracked(button.button)) {
|
||||
return;
|
||||
}
|
||||
const auto mapped = map_window_point_to_content(button.x, button.y);
|
||||
if (mapped.inside) {
|
||||
g_context->ProcessMouseMove(rounded_content_coord(mapped.position.x), rounded_content_coord(mapped.position.y),
|
||||
RmlSDL::GetKeyModifierState());
|
||||
} else {
|
||||
g_context->ProcessMouseLeave();
|
||||
}
|
||||
g_context->ProcessMouseButtonUp(RmlSDL::ConvertMouseButton(button.button), RmlSDL::GetKeyModifierState());
|
||||
set_mouse_button_tracked(button.button, false);
|
||||
if (s_pressedMouseButtons == 0) {
|
||||
SDL_CaptureMouse(false);
|
||||
}
|
||||
}
|
||||
|
||||
void handle_mouse_wheel(const SDL_MouseWheelEvent& wheel) noexcept {
|
||||
float mouseX = 0.f;
|
||||
float mouseY = 0.f;
|
||||
SDL_GetMouseState(&mouseX, &mouseY);
|
||||
if (!map_window_point_to_content(mouseX, mouseY).inside) {
|
||||
g_context->ProcessMouseLeave();
|
||||
return;
|
||||
}
|
||||
g_context->ProcessMouseWheel(Rml::Vector2f{wheel.x, -wheel.y}, RmlSDL::GetKeyModifierState());
|
||||
}
|
||||
|
||||
void handle_touch_down(const SDL_TouchFingerEvent& finger) noexcept {
|
||||
if (find_tracked_touch(finger.fingerID) != nullptr) {
|
||||
return;
|
||||
}
|
||||
const auto mapped = map_touch_point_to_content(finger);
|
||||
if (!mapped.inside) {
|
||||
return;
|
||||
}
|
||||
auto* tracked = find_free_touch();
|
||||
if (tracked == nullptr) {
|
||||
return;
|
||||
}
|
||||
*tracked = {
|
||||
.id = finger.fingerID,
|
||||
.position = mapped.position,
|
||||
.active = true,
|
||||
};
|
||||
g_context->ProcessTouchStart(touch_list(finger.fingerID, mapped.position), RmlSDL::GetKeyModifierState());
|
||||
}
|
||||
|
||||
void handle_touch_motion(const SDL_TouchFingerEvent& finger) noexcept {
|
||||
auto* tracked = find_tracked_touch(finger.fingerID);
|
||||
if (tracked == nullptr) {
|
||||
return;
|
||||
}
|
||||
const auto mapped = map_touch_point_to_content(finger);
|
||||
if (!mapped.inside) {
|
||||
return;
|
||||
}
|
||||
tracked->position = mapped.position;
|
||||
g_context->ProcessTouchMove(touch_list(finger.fingerID, mapped.position), RmlSDL::GetKeyModifierState());
|
||||
}
|
||||
|
||||
void handle_touch_up(const SDL_TouchFingerEvent& finger) noexcept {
|
||||
auto* tracked = find_tracked_touch(finger.fingerID);
|
||||
if (tracked == nullptr) {
|
||||
return;
|
||||
}
|
||||
const auto position = tracked->position;
|
||||
*tracked = {};
|
||||
g_context->ProcessTouchEnd(touch_list(finger.fingerID, position), RmlSDL::GetKeyModifierState());
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Rml::Context* g_context = nullptr;
|
||||
FileInterface_SDL* g_fileInterface = nullptr;
|
||||
|
||||
void initialize(const AuroraWindowSize& size) noexcept {
|
||||
int width = static_cast<int>(size.native_fb_width);
|
||||
int height = static_cast<int>(size.native_fb_height);
|
||||
if (!Backend::Initialize("Aurora RmlUi Backend", width, height, false)) {
|
||||
const Rml::Vector2i dim = presentation_dimensions_from_window_size(size);
|
||||
if (!Backend::Initialize("Aurora RmlUi Backend", dim.x, dim.y, false)) {
|
||||
Log.error("Failed to initialize RmlUI Backend!");
|
||||
return;
|
||||
}
|
||||
@@ -39,16 +310,17 @@ void initialize(const AuroraWindowSize& size) noexcept {
|
||||
Rml::SetRenderInterface(renderInterface);
|
||||
Rml::SetFileInterface(g_fileInterface);
|
||||
|
||||
renderInterface->SetWindowSize({width, height});
|
||||
renderInterface->SetWindowSize(dim);
|
||||
renderInterface->SetRenderTargetFormat(webgpu::g_graphicsConfig.surfaceConfiguration.format);
|
||||
renderInterface->CreateDeviceObjects();
|
||||
|
||||
Rml::Initialise();
|
||||
g_context = Rml::CreateContext("main", Rml::Vector2i(width, height));
|
||||
g_context = Rml::CreateContext("main", dim);
|
||||
|
||||
if (!g_context) {
|
||||
if (g_context) {
|
||||
sync_context_metrics(dim);
|
||||
} else {
|
||||
Log.error("Failed to initialize RmlUI Context!");
|
||||
|
||||
Rml::Shutdown();
|
||||
Backend::Shutdown();
|
||||
}
|
||||
@@ -70,32 +342,73 @@ void handle_event(SDL_Event& event) noexcept {
|
||||
return;
|
||||
}
|
||||
|
||||
// RmlSDL::InputEventHandler only passes through -wheel.y, but we want horizontal scrolling too
|
||||
if (event.type == SDL_EVENT_MOUSE_WHEEL) {
|
||||
g_context->ProcessMouseWheel(Rml::Vector2f{event.wheel.x, -event.wheel.y}, RmlSDL::GetKeyModifierState());
|
||||
switch (event.type) {
|
||||
case SDL_EVENT_MOUSE_MOTION:
|
||||
handle_mouse_motion(event.motion);
|
||||
return;
|
||||
case SDL_EVENT_MOUSE_BUTTON_DOWN:
|
||||
handle_mouse_button_down(event.button);
|
||||
return;
|
||||
case SDL_EVENT_MOUSE_BUTTON_UP:
|
||||
handle_mouse_button_up(event.button);
|
||||
return;
|
||||
case SDL_EVENT_MOUSE_WHEEL:
|
||||
handle_mouse_wheel(event.wheel);
|
||||
return;
|
||||
case SDL_EVENT_FINGER_DOWN:
|
||||
handle_touch_down(event.tfinger);
|
||||
return;
|
||||
case SDL_EVENT_FINGER_MOTION:
|
||||
handle_touch_motion(event.tfinger);
|
||||
return;
|
||||
case SDL_EVENT_FINGER_UP:
|
||||
case SDL_EVENT_FINGER_CANCELED:
|
||||
handle_touch_up(event.tfinger);
|
||||
return;
|
||||
case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
|
||||
sync_context_metrics(presentation_dimensions_from_window_size(window::get_window_size()));
|
||||
return;
|
||||
case SDL_EVENT_WINDOW_MOUSE_LEAVE:
|
||||
g_context->ProcessMouseLeave();
|
||||
return;
|
||||
case SDL_EVENT_WINDOW_DISPLAY_SCALE_CHANGED:
|
||||
sync_context_metrics(presentation_dimensions_from_window_size(window::get_window_size()));
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
RmlSDL::InputEventHandler(g_context, window::get_sdl_window(), event);
|
||||
}
|
||||
|
||||
void render(const wgpu::CommandEncoder& encoder, const wgpu::TextureView& outputView, const wgpu::Extent3D& size,
|
||||
const gfx::Viewport& viewport) noexcept {
|
||||
RenderOutput render(const wgpu::CommandEncoder& encoder, const webgpu::Viewport& presentViewport) noexcept {
|
||||
if (g_context == nullptr) {
|
||||
return;
|
||||
return {};
|
||||
}
|
||||
|
||||
const Rml::Vector2i dim = dimensions_from_viewport(presentViewport);
|
||||
ensure_render_target(dim);
|
||||
if (!s_renderTarget.view) {
|
||||
return {};
|
||||
}
|
||||
|
||||
copy_game_to_render_target(encoder);
|
||||
sync_context_metrics(dim);
|
||||
g_context->Update();
|
||||
|
||||
auto* renderInterface = get_render_interface();
|
||||
renderInterface->SetWindowSize(g_context->GetDimensions());
|
||||
renderInterface->BeginFrame(encoder, outputView, size, viewport);
|
||||
renderInterface->BeginFrame(encoder, s_renderTarget);
|
||||
|
||||
Backend::BeginFrame();
|
||||
g_context->Render();
|
||||
Backend::PresentFrame();
|
||||
|
||||
renderInterface->EndFrame();
|
||||
return {
|
||||
.texture = &s_renderTarget,
|
||||
.copyBindGroup = s_renderTargetCopyBindGroup,
|
||||
};
|
||||
}
|
||||
|
||||
void shutdown() noexcept {
|
||||
@@ -106,5 +419,7 @@ void shutdown() noexcept {
|
||||
Rml::Shutdown();
|
||||
Backend::Shutdown();
|
||||
g_context = nullptr;
|
||||
s_renderTarget = {};
|
||||
s_renderTargetCopyBindGroup = {};
|
||||
}
|
||||
} // namespace aurora::rmlui
|
||||
|
||||
+7
-3
@@ -1,16 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include "aurora/aurora.h"
|
||||
#include "gfx/common.hpp"
|
||||
#include "webgpu/gpu.hpp"
|
||||
|
||||
#include <SDL3/SDL_events.h>
|
||||
#include <aurora/rmlui.hpp>
|
||||
#include <dawn/webgpu_cpp.h>
|
||||
|
||||
namespace aurora::rmlui {
|
||||
struct RenderOutput {
|
||||
const webgpu::TextureWithSampler* texture = nullptr;
|
||||
wgpu::BindGroup copyBindGroup;
|
||||
};
|
||||
|
||||
void initialize(const AuroraWindowSize& size) noexcept;
|
||||
void handle_event(SDL_Event& event) noexcept;
|
||||
void render(const wgpu::CommandEncoder& encoder, const wgpu::TextureView& outputView, const wgpu::Extent3D& size,
|
||||
const gfx::Viewport& viewport) noexcept;
|
||||
RenderOutput render(const wgpu::CommandEncoder& encoder, const webgpu::Viewport& presentViewport) noexcept;
|
||||
void shutdown() noexcept;
|
||||
} // namespace aurora::rmlui
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include "../logging.hpp"
|
||||
#include "../window.hpp"
|
||||
#include "../webgpu/gpu.hpp"
|
||||
|
||||
namespace aurora::rmlui {
|
||||
namespace {
|
||||
@@ -51,16 +52,36 @@ void SystemInterface_Aurora::ActivateKeyboard(Rml::Vector2f caret_position, floa
|
||||
}
|
||||
|
||||
const AuroraWindowSize size = window::get_window_size();
|
||||
if (size.native_fb_width == 0 || size.native_fb_height == 0 || size.width == 0 || size.height == 0) {
|
||||
if (size.native_fb_width == 0 || size.native_fb_height == 0 || size.width == 0 || size.height == 0 ||
|
||||
size.fb_width == 0 || size.fb_height == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const float scaleX = static_cast<float>(size.width) / static_cast<float>(size.native_fb_width);
|
||||
const float scaleY = static_cast<float>(size.height) / static_cast<float>(size.native_fb_height);
|
||||
const float inputLineHeight = std::max(line_height * scaleY, 1.0f);
|
||||
Rml::Vector2i contentSize{static_cast<int>(size.fb_width), static_cast<int>(size.fb_height)};
|
||||
if (const Rml::Context* context = get_context()) {
|
||||
contentSize = context->GetDimensions();
|
||||
}
|
||||
if (contentSize.x <= 0 || contentSize.y <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto viewport = webgpu::calculate_present_viewport(size.native_fb_width, size.native_fb_height,
|
||||
static_cast<uint32_t>(contentSize.x),
|
||||
static_cast<uint32_t>(contentSize.y));
|
||||
if (viewport.width <= 0.f || viewport.height <= 0.f) {
|
||||
return;
|
||||
}
|
||||
|
||||
const float nativeToWindowX = static_cast<float>(size.width) / static_cast<float>(size.native_fb_width);
|
||||
const float nativeToWindowY = static_cast<float>(size.height) / static_cast<float>(size.native_fb_height);
|
||||
const float contentToNativeX = viewport.width / static_cast<float>(contentSize.x);
|
||||
const float contentToNativeY = viewport.height / static_cast<float>(contentSize.y);
|
||||
const float inputLineHeight = std::max(line_height * contentToNativeY * nativeToWindowY, 1.0f);
|
||||
const float nativeCaretX = viewport.left + caret_position.x * contentToNativeX;
|
||||
const float nativeCaretY = viewport.top + caret_position.y * contentToNativeY;
|
||||
const SDL_Rect rect = {
|
||||
.x = static_cast<int>(std::lround(caret_position.x * scaleX)),
|
||||
.y = static_cast<int>(std::lround((caret_position.y * scaleY) + inputLineHeight)),
|
||||
.x = static_cast<int>(std::lround(nativeCaretX * nativeToWindowX)),
|
||||
.y = static_cast<int>(std::lround(nativeCaretY * nativeToWindowY + inputLineHeight)),
|
||||
.w = 1,
|
||||
.h = static_cast<int>(std::lround(inputLineHeight)),
|
||||
};
|
||||
|
||||
@@ -721,11 +721,14 @@ void WebGPURenderInterface::EnsureRenderTarget(RenderTarget& target, const char*
|
||||
};
|
||||
target.texture = webgpu::g_device.CreateTexture(&textureDesc);
|
||||
target.view = target.texture.CreateView(nullptr);
|
||||
target.bindGroup = CreateImageBindGroup(target.view);
|
||||
}
|
||||
|
||||
wgpu::BindGroup WebGPURenderInterface::CreateImageBindGroup(const wgpu::TextureView& view) const {
|
||||
const std::array bindGroupEntries{
|
||||
wgpu::BindGroupEntry{
|
||||
.binding = 0,
|
||||
.textureView = target.view,
|
||||
.textureView = view,
|
||||
},
|
||||
};
|
||||
const wgpu::BindGroupDescriptor bindGroupDesc{
|
||||
@@ -733,7 +736,7 @@ void WebGPURenderInterface::EnsureRenderTarget(RenderTarget& target, const char*
|
||||
.entryCount = bindGroupEntries.size(),
|
||||
.entries = bindGroupEntries.data(),
|
||||
};
|
||||
target.bindGroup = webgpu::g_device.CreateBindGroup(&bindGroupDesc);
|
||||
return webgpu::g_device.CreateBindGroup(&bindGroupDesc);
|
||||
}
|
||||
|
||||
void WebGPURenderInterface::EnsureFrameTargets(const wgpu::Extent3D& size) {
|
||||
@@ -741,7 +744,6 @@ void WebGPURenderInterface::EnsureFrameTargets(const wgpu::Extent3D& size) {
|
||||
m_layers.resize(1);
|
||||
}
|
||||
|
||||
EnsureRenderTarget(m_layers[0], "RmlUi Base Layer", size);
|
||||
EnsureRenderTarget(m_postprocessTargets[0], "RmlUi Postprocess A", size);
|
||||
EnsureRenderTarget(m_postprocessTargets[1], "RmlUi Postprocess B", size);
|
||||
EnsureRenderTarget(m_postprocessTargets[2], "RmlUi Postprocess C", size);
|
||||
@@ -1041,38 +1043,30 @@ void WebGPURenderInterface::RenderFilters(Rml::Span<const Rml::CompiledFilterHan
|
||||
EndActivePass();
|
||||
}
|
||||
|
||||
void WebGPURenderInterface::BeginFrame(const wgpu::CommandEncoder& encoder, const wgpu::TextureView& outputView,
|
||||
const wgpu::Extent3D& size, const gfx::Viewport& viewport) {
|
||||
void WebGPURenderInterface::BeginFrame(const wgpu::CommandEncoder& encoder,
|
||||
const webgpu::TextureWithSampler& target) {
|
||||
m_encoder = encoder;
|
||||
m_outputView = outputView;
|
||||
m_frameSize = size;
|
||||
m_viewport = viewport;
|
||||
m_frameSize = target.size;
|
||||
m_viewport = {
|
||||
.left = 0.f,
|
||||
.top = 0.f,
|
||||
.width = static_cast<float>(target.size.width),
|
||||
.height = static_cast<float>(target.size.height),
|
||||
.znear = 0.f,
|
||||
.zfar = 1.f,
|
||||
};
|
||||
m_nextLayer = 1;
|
||||
m_layerStack = {0};
|
||||
m_activeLayer = 0;
|
||||
|
||||
NewFrame();
|
||||
EnsureFrameTargets(size);
|
||||
|
||||
const std::array copyAttachments{
|
||||
wgpu::RenderPassColorAttachment{
|
||||
.view = m_layers[0].view,
|
||||
.loadOp = wgpu::LoadOp::Clear,
|
||||
.storeOp = wgpu::StoreOp::Store,
|
||||
.clearValue = {0.f, 0.f, 0.f, 0.f},
|
||||
},
|
||||
EnsureFrameTargets(target.size);
|
||||
m_layers[0] = {
|
||||
.texture = target.texture,
|
||||
.view = target.view,
|
||||
.bindGroup = CreateImageBindGroup(target.view),
|
||||
.size = target.size,
|
||||
};
|
||||
const wgpu::RenderPassDescriptor copyPassDesc{
|
||||
.label = "RmlUi game frame copy pass",
|
||||
.colorAttachmentCount = copyAttachments.size(),
|
||||
.colorAttachments = copyAttachments.data(),
|
||||
};
|
||||
m_pass = m_encoder.BeginRenderPass(©PassDesc);
|
||||
ApplyViewport();
|
||||
m_pass.SetPipeline(webgpu::g_CopyPipeline);
|
||||
m_pass.SetBindGroup(0, webgpu::g_CopyBindGroup, 0, nullptr);
|
||||
m_pass.Draw(3);
|
||||
EndActivePass();
|
||||
|
||||
BeginRenderTargetPass(m_layers[0].view, wgpu::LoadOp::Load, "RmlUi base layer pass", true);
|
||||
}
|
||||
@@ -1080,35 +1074,11 @@ void WebGPURenderInterface::BeginFrame(const wgpu::CommandEncoder& encoder, cons
|
||||
void WebGPURenderInterface::EndFrame() {
|
||||
EndActivePass();
|
||||
|
||||
const wgpu::RenderPassDepthStencilAttachment depthStencilAttachment{
|
||||
.view = GetClipMaskStencilView(m_frameSize),
|
||||
.stencilLoadOp = wgpu::LoadOp::Load,
|
||||
.stencilStoreOp = wgpu::StoreOp::Store,
|
||||
};
|
||||
const std::array attachments{
|
||||
wgpu::RenderPassColorAttachment{
|
||||
.view = m_outputView,
|
||||
.loadOp = wgpu::LoadOp::Clear,
|
||||
.storeOp = wgpu::StoreOp::Store,
|
||||
.clearValue = {0.f, 0.f, 0.f, 0.f},
|
||||
},
|
||||
};
|
||||
const wgpu::RenderPassDescriptor renderPassDesc{
|
||||
.label = "RmlUi output copy pass",
|
||||
.colorAttachmentCount = attachments.size(),
|
||||
.colorAttachments = attachments.data(),
|
||||
.depthStencilAttachment = &depthStencilAttachment,
|
||||
};
|
||||
m_pass = m_encoder.BeginRenderPass(&renderPassDesc);
|
||||
m_pass.SetViewport(0.f, 0.f, static_cast<float>(m_frameSize.width), static_cast<float>(m_frameSize.height), 0.f, 1.f);
|
||||
m_pass.SetScissorRect(0, 0, m_frameSize.width, m_frameSize.height);
|
||||
m_pass.SetStencilReference(0);
|
||||
DrawFullscreenTexture(m_layers[0].bindGroup, m_blitPipelines[static_cast<size_t>(BlitPipelineType::Replace)]);
|
||||
EndActivePass();
|
||||
|
||||
m_layerStack.clear();
|
||||
if (!m_layers.empty()) {
|
||||
m_layers[0] = {};
|
||||
}
|
||||
m_encoder = nullptr;
|
||||
m_outputView = nullptr;
|
||||
}
|
||||
|
||||
Rml::LayerHandle WebGPURenderInterface::PushLayer() {
|
||||
|
||||
@@ -7,6 +7,10 @@
|
||||
#include "../gfx/clear.hpp"
|
||||
#include "RmlUi/Core/RenderInterface.h"
|
||||
|
||||
namespace aurora::webgpu {
|
||||
struct TextureWithSampler;
|
||||
} // namespace aurora::webgpu
|
||||
|
||||
namespace aurora::rmlui {
|
||||
|
||||
inline constexpr uint32_t MaxHighQualityBlurRadius = 64;
|
||||
@@ -92,7 +96,6 @@ class WebGPURenderInterface : public Rml::RenderInterface {
|
||||
};
|
||||
|
||||
wgpu::CommandEncoder m_encoder;
|
||||
wgpu::TextureView m_outputView;
|
||||
wgpu::RenderPassEncoder m_pass;
|
||||
|
||||
std::array<wgpu::RenderPipeline, static_cast<size_t>(PipelineType::Count)> m_pipelines;
|
||||
@@ -156,6 +159,7 @@ class WebGPURenderInterface : public Rml::RenderInterface {
|
||||
|
||||
void EnsureRenderTarget(RenderTarget& target, const char* label, const wgpu::Extent3D& size);
|
||||
void EnsureFrameTargets(const wgpu::Extent3D& size);
|
||||
wgpu::BindGroup CreateImageBindGroup(const wgpu::TextureView& view) const;
|
||||
Rml::Rectanglei GetActiveScissorRegion() const;
|
||||
TexCoordLimits GetPostprocessTexCoordLimits() const;
|
||||
TexCoordLimits GetPostprocessTexCoordLimits(Rml::Rectanglei region) const;
|
||||
@@ -205,8 +209,7 @@ public:
|
||||
Rml::TextureHandle texture) override;
|
||||
void ReleaseShader(Rml::CompiledShaderHandle shader) override;
|
||||
|
||||
void BeginFrame(const wgpu::CommandEncoder& encoder, const wgpu::TextureView& outputView, const wgpu::Extent3D& size,
|
||||
const gfx::Viewport& viewport);
|
||||
void BeginFrame(const wgpu::CommandEncoder& encoder, const webgpu::TextureWithSampler& target);
|
||||
void EndFrame();
|
||||
void SetWindowSize(const Rml::Vector2i& window_size) { m_windowSize = window_size; }
|
||||
void SetRenderTargetFormat(wgpu::TextureFormat render_target_format) { m_renderTargetFormat = render_target_format; }
|
||||
|
||||
+38
-7
@@ -1,7 +1,7 @@
|
||||
#include "gpu.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
@@ -9,7 +9,6 @@
|
||||
#include <aurora/aurora.h>
|
||||
#include <aurora/gfx.h>
|
||||
#include <magic_enum.hpp>
|
||||
#include <webgpu/webgpu.h>
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
|
||||
#include "../gfx/common.hpp"
|
||||
@@ -127,6 +126,38 @@ TextureWithSampler create_render_texture(uint32_t width, uint32_t height, bool m
|
||||
};
|
||||
}
|
||||
|
||||
const TextureWithSampler& present_source() noexcept {
|
||||
return g_graphicsConfig.msaaSamples > 1 ? g_frameBufferResolved : g_frameBuffer;
|
||||
}
|
||||
|
||||
Viewport calculate_present_viewport(uint32_t surface_width, uint32_t surface_height, uint32_t content_width,
|
||||
uint32_t content_height) noexcept {
|
||||
if (surface_width == 0 || surface_height == 0 || content_width == 0 || content_height == 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
uint32_t viewport_width = surface_width;
|
||||
uint32_t viewport_height = std::min<uint32_t>(
|
||||
surface_height, std::max<uint32_t>(1u, static_cast<uint32_t>(std::lround(static_cast<double>(viewport_width) *
|
||||
static_cast<double>(content_height) /
|
||||
static_cast<double>(content_width)))));
|
||||
if (viewport_height == surface_height) {
|
||||
viewport_width = std::min<uint32_t>(
|
||||
surface_width, std::max<uint32_t>(1u, static_cast<uint32_t>(std::lround(static_cast<double>(viewport_height) *
|
||||
static_cast<double>(content_width) /
|
||||
static_cast<double>(content_height)))));
|
||||
}
|
||||
|
||||
return {
|
||||
.left = static_cast<float>((surface_width - viewport_width) / 2),
|
||||
.top = static_cast<float>((surface_height - viewport_height) / 2),
|
||||
.width = static_cast<float>(viewport_width),
|
||||
.height = static_cast<float>(viewport_height),
|
||||
.znear = 0.f,
|
||||
.zfar = 1.f,
|
||||
};
|
||||
}
|
||||
|
||||
static TextureWithSampler create_depth_texture(uint32_t width, uint32_t height) {
|
||||
const wgpu::Extent3D size{
|
||||
.width = width,
|
||||
@@ -277,15 +308,15 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||
g_CopyPipeline = g_device.CreateRenderPipeline(&pipelineDescriptor);
|
||||
}
|
||||
|
||||
void create_copy_bind_group() {
|
||||
wgpu::BindGroup create_copy_bind_group(const TextureWithSampler& source) {
|
||||
const std::array bindGroupEntries{
|
||||
wgpu::BindGroupEntry{
|
||||
.binding = 0,
|
||||
.sampler = g_graphicsConfig.msaaSamples > 1 ? g_frameBufferResolved.sampler : g_frameBuffer.sampler,
|
||||
.sampler = source.sampler,
|
||||
},
|
||||
wgpu::BindGroupEntry{
|
||||
.binding = 1,
|
||||
.textureView = g_graphicsConfig.msaaSamples > 1 ? g_frameBufferResolved.view : g_frameBuffer.view,
|
||||
.textureView = source.view,
|
||||
},
|
||||
};
|
||||
const wgpu::BindGroupDescriptor bindGroupDescriptor{
|
||||
@@ -293,7 +324,7 @@ void create_copy_bind_group() {
|
||||
.entryCount = bindGroupEntries.size(),
|
||||
.entries = bindGroupEntries.data(),
|
||||
};
|
||||
g_CopyBindGroup = g_device.CreateBindGroup(&bindGroupDescriptor);
|
||||
return g_device.CreateBindGroup(&bindGroupDescriptor);
|
||||
}
|
||||
|
||||
static wgpu::BackendType to_wgpu_backend(AuroraBackend backend) {
|
||||
@@ -668,7 +699,7 @@ void resize_swapchain(uint32_t width, uint32_t height, uint32_t native_width, ui
|
||||
g_frameBuffer = create_render_texture(width, height, true);
|
||||
g_frameBufferResolved = create_render_texture(width, height, false);
|
||||
g_depthBuffer = create_depth_texture(width, height);
|
||||
create_copy_bind_group();
|
||||
g_CopyBindGroup = create_copy_bind_group(present_source());
|
||||
}
|
||||
} // namespace aurora::webgpu
|
||||
|
||||
|
||||
+22
-3
@@ -24,6 +24,20 @@ struct TextureWithSampler {
|
||||
wgpu::TextureFormat format;
|
||||
wgpu::Sampler sampler;
|
||||
};
|
||||
struct Viewport {
|
||||
float left;
|
||||
float top;
|
||||
float width;
|
||||
float height;
|
||||
float znear;
|
||||
float zfar;
|
||||
|
||||
bool operator==(const Viewport& rhs) const {
|
||||
return left == rhs.left && top == rhs.top && width == rhs.width && height == rhs.height && znear == rhs.znear &&
|
||||
zfar == rhs.zfar;
|
||||
}
|
||||
bool operator!=(const Viewport& rhs) const { return !(*this == rhs); }
|
||||
};
|
||||
|
||||
extern wgpu::Device g_device;
|
||||
extern wgpu::Queue g_queue;
|
||||
@@ -40,13 +54,18 @@ extern wgpu::Instance g_instance;
|
||||
bool initialize(AuroraBackend backend);
|
||||
void shutdown();
|
||||
bool refresh_surface(bool recreate = true);
|
||||
void resize_swapchain(uint32_t width, uint32_t height, uint32_t native_width, uint32_t native_height, bool force = false);
|
||||
void resize_swapchain(uint32_t width, uint32_t height, uint32_t native_width, uint32_t native_height,
|
||||
bool force = false);
|
||||
TextureWithSampler create_render_texture(uint32_t width, uint32_t height, bool multisampled);
|
||||
const TextureWithSampler& present_source() noexcept;
|
||||
wgpu::BindGroup create_copy_bind_group(const TextureWithSampler& source);
|
||||
Viewport calculate_present_viewport(uint32_t surface_width, uint32_t surface_height, uint32_t content_width,
|
||||
uint32_t content_height) noexcept;
|
||||
void draw_clear(const wgpu::RenderPassEncoder& pass, bool clearColor, bool clearAlpha, bool clearDepth,
|
||||
const Vec4<float>& clearColorValue, float clearDepthValue);
|
||||
|
||||
size_t load_from_cache(void const * key, size_t keySize, void * value, size_t valueSize, void * userdata);
|
||||
void store_to_cache(void const * key, size_t keySize, void const * value, size_t valueSize, void * userdata);
|
||||
size_t load_from_cache(void const* key, size_t keySize, void* value, size_t valueSize, void* userdata);
|
||||
void store_to_cache(void const* key, size_t keySize, void const* value, size_t valueSize, void* userdata);
|
||||
void cache_shutdown();
|
||||
|
||||
} // namespace aurora::webgpu
|
||||
|
||||
+45
-3
@@ -34,6 +34,7 @@ Module Log("aurora::window");
|
||||
SDL_Window* g_window;
|
||||
SDL_Renderer* g_renderer;
|
||||
float g_frameBufferScale = 0.f;
|
||||
bool g_frameBufferAspectFit = false;
|
||||
AuroraWindowSize g_windowSize;
|
||||
std::vector<AuroraEvent> g_events;
|
||||
|
||||
@@ -64,6 +65,25 @@ Vec2<int> scale_frame_buffer_to_aspect(int base_width, int base_height, float sc
|
||||
};
|
||||
}
|
||||
|
||||
Vec2<int> fit_frame_buffer_to_aspect(int width, int height, float aspect) {
|
||||
if (width <= 0 || height <= 0 || aspect <= 0.f) {
|
||||
return {std::max(width, 1), std::max(height, 1)};
|
||||
}
|
||||
|
||||
const float targetAspect = static_cast<float>(width) / static_cast<float>(height);
|
||||
if (targetAspect > aspect) {
|
||||
return {
|
||||
std::max(1, static_cast<int>(std::lround(static_cast<float>(height) * aspect))),
|
||||
height,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
width,
|
||||
std::max(1, static_cast<int>(std::lround(static_cast<float>(width) / aspect))),
|
||||
};
|
||||
}
|
||||
|
||||
void resize_swapchain() noexcept {
|
||||
const auto size = get_window_size();
|
||||
if (size == g_windowSize) {
|
||||
@@ -380,6 +400,15 @@ AuroraWindowSize get_window_size() {
|
||||
fb_w = scaledW;
|
||||
fb_h = scaledH;
|
||||
}
|
||||
if (g_frameBufferAspectFit) {
|
||||
const auto [baseW, baseH] = vi::configured_fb_size();
|
||||
if (baseW > 0 && baseH > 0) {
|
||||
const auto [fitW, fitH] =
|
||||
fit_frame_buffer_to_aspect(fb_w, fb_h, static_cast<float>(baseW) / static_cast<float>(baseH));
|
||||
fb_w = fitW;
|
||||
fb_h = fitH;
|
||||
}
|
||||
}
|
||||
|
||||
const float scale = SDL_GetWindowDisplayScale(g_window);
|
||||
return {
|
||||
@@ -426,6 +455,13 @@ static void push_future_resize_event() {
|
||||
TRY_WARN(SDL_PushEvent(&event), "Failed to push SDL event for future resize: {}", SDL_GetError());
|
||||
}
|
||||
|
||||
void request_frame_buffer_resize() {
|
||||
if (g_window != nullptr) {
|
||||
// Defer so that we don't try to reconfigure the surface in the middle of game logic.
|
||||
push_future_resize_event();
|
||||
}
|
||||
}
|
||||
|
||||
void set_frame_buffer_scale(float scale) {
|
||||
if (scale < 0.f) {
|
||||
scale = 0.f;
|
||||
@@ -434,10 +470,16 @@ void set_frame_buffer_scale(float scale) {
|
||||
return;
|
||||
}
|
||||
g_frameBufferScale = scale;
|
||||
if (g_window != nullptr) {
|
||||
// Defer so that we don't try to reconfigure the surface in the middle of game logic.
|
||||
push_future_resize_event();
|
||||
request_frame_buffer_resize();
|
||||
}
|
||||
|
||||
void set_frame_buffer_aspect_fit(bool fit) {
|
||||
if (g_frameBufferAspectFit == fit) {
|
||||
return;
|
||||
}
|
||||
|
||||
g_frameBufferAspectFit = fit;
|
||||
request_frame_buffer_resize();
|
||||
}
|
||||
|
||||
} // namespace aurora::window
|
||||
|
||||
@@ -23,5 +23,7 @@ bool get_fullscreen();
|
||||
void set_window_size(uint32_t width, uint32_t height);
|
||||
void set_window_position(uint32_t x, uint32_t y);
|
||||
void center_window();
|
||||
void request_frame_buffer_resize();
|
||||
void set_frame_buffer_scale(float scale);
|
||||
void set_frame_buffer_aspect_fit(bool fit);
|
||||
}; // namespace aurora::window
|
||||
|
||||
@@ -248,6 +248,7 @@ bool try_bind_replacement(GXTexObj_&, GXTexMapID) noexcept { return false; }
|
||||
#include "../lib/window.hpp"
|
||||
namespace aurora::window {
|
||||
AuroraWindowSize get_window_size() { return {640, 480, 640, 480, 640, 480, 1.0f}; }
|
||||
void set_frame_buffer_aspect_fit(bool) {}
|
||||
} // namespace aurora::window
|
||||
|
||||
// --- WebGPU C API stubs (prevent linker errors from wgpu:: destructors) ---
|
||||
|
||||
Reference in New Issue
Block a user