mirror of
https://github.com/encounter/aurora.git
synced 2026-07-09 18:19:33 -07:00
Viewport scaling rework & IR configuration (#127)
This is a breaking change: `GXSetViewport`, `GXSetScissor`, `GXSetTexCopySrc`, `GXSetTexCopyDst` now accept logical coords relative to the `VIConfigure` EFB size. `GXSetViewportRender` and `GXSetScissorRender` are used to override the scaled viewport/scissor from the logical coords.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
if (AURORA_ENABLE_GX)
|
||||
add_executable(simple simple.c)
|
||||
target_link_libraries(simple PRIVATE aurora::core aurora::gx aurora::main)
|
||||
target_link_libraries(simple PRIVATE aurora::core aurora::gx aurora::main aurora::vi)
|
||||
endif ()
|
||||
|
||||
@@ -11,6 +11,18 @@ extern "C" {
|
||||
// Subcommands for GX_LOAD_AURORA.
|
||||
//
|
||||
|
||||
/**
|
||||
* Sets the actual render viewport in native framebuffer coordinates.
|
||||
* Must be followed by six f32 values: left, top, width, height, nearz, farz.
|
||||
*/
|
||||
#define GX_LOAD_AURORA_VIEWPORT_RENDER 0x0001
|
||||
|
||||
/**
|
||||
* Sets the actual render scissor in native framebuffer coordinates.
|
||||
* Must be followed by four u32 values: left, top, width, height.
|
||||
*/
|
||||
#define GX_LOAD_AURORA_SCISSOR_RENDER 0x0002
|
||||
|
||||
/**
|
||||
* Aurora equivalent of CP_REG_ARRAYBASE_ID: sets the base address and size of a vertex array.
|
||||
* This command must be followed by a 64-bit memory address, 32-bit size, and 1-byte little-endian flag.
|
||||
@@ -68,6 +80,35 @@ 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
|
||||
} AuroraViewportPolicy;
|
||||
|
||||
/**
|
||||
* Configures how GXSetViewport/GXSetScissor parameters are applied to the actual render viewport.
|
||||
* 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.
|
||||
*/
|
||||
void AuroraGetRenderSize(u32* width, u32* height);
|
||||
|
||||
/**
|
||||
* Sets the actual render viewport in native framebuffer coordinates.
|
||||
* Overrides the automatically scaled values set by the logical GXSetViewport.
|
||||
*/
|
||||
void GXSetViewportRender(f32 left, f32 top, f32 wd, f32 ht, f32 nearz, f32 farz);
|
||||
|
||||
/**
|
||||
* Sets the actual render scissor in native framebuffer coordinates.
|
||||
* Overrides the automatically scaled values set by the logical GXSetScissor.
|
||||
*/
|
||||
void GXSetScissorRender(u32 left, u32 top, u32 wd, u32 ht);
|
||||
|
||||
/**
|
||||
* Create an offscreen framebuffer and switch rendering to it.
|
||||
* All subsequent GX rendering will target this framebuffer until GXRestoreFrameBuffer() is called.
|
||||
|
||||
@@ -39,6 +39,12 @@ void VISetWindowSize(uint32_t width, uint32_t height);
|
||||
void VISetWindowPosition(uint32_t x, uint32_t y);
|
||||
void VICenterWindow();
|
||||
|
||||
/**
|
||||
* Sets the internal framebuffer resolution to a specific scale factor of the configured EFB size.
|
||||
* A value of 0.0f means "Auto", which will use the underlying swapchain size (usually the window size).
|
||||
*/
|
||||
void VISetFrameBufferScale(float scale);
|
||||
|
||||
/**
|
||||
* \brief Lock the GX framebuffer to a specific aspect ratio, without changing the native framebuffer.
|
||||
*
|
||||
|
||||
+40
-11
@@ -64,6 +64,41 @@ constexpr std::array<AuroraBackend, 0> PreferredBackendOrder{};
|
||||
|
||||
bool g_initialFrame = false;
|
||||
|
||||
#ifdef AURORA_ENABLE_GX
|
||||
struct PresentViewport {
|
||||
float x;
|
||||
float y;
|
||||
float width;
|
||||
float height;
|
||||
};
|
||||
|
||||
PresentViewport 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 {
|
||||
.x = static_cast<float>((surface_width - viewport_width) / 2),
|
||||
.y = static_cast<float>((surface_height - viewport_height) / 2),
|
||||
.width = static_cast<float>(viewport_width),
|
||||
.height = static_cast<float>(viewport_height),
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
AuroraInfo initialize(int argc, char* argv[], const AuroraConfig& config) noexcept {
|
||||
g_config = config;
|
||||
Log.info("Aurora initializing");
|
||||
@@ -149,10 +184,7 @@ AuroraInfo initialize(int argc, char* argv[], const AuroraConfig& config) noexce
|
||||
};
|
||||
}
|
||||
|
||||
void aurora_set_log_level(AuroraLogLevel level) noexcept {
|
||||
g_config.logLevel = level;
|
||||
}
|
||||
|
||||
void aurora_set_log_level(AuroraLogLevel level) noexcept { g_config.logLevel = level; }
|
||||
|
||||
#ifdef AURORA_ENABLE_GX
|
||||
wgpu::TextureView g_currentView;
|
||||
@@ -244,13 +276,10 @@ void end_frame() noexcept {
|
||||
pass.SetPipeline(webgpu::g_CopyPipeline);
|
||||
pass.SetBindGroup(0, webgpu::g_CopyBindGroup, 0, nullptr);
|
||||
|
||||
// Center viewport to framebuffer size in case we're at an aspect ratio lock.
|
||||
{
|
||||
uint32_t pos_x = (webgpu::g_graphicsConfig.surfaceConfiguration.width - webgpu::g_frameBuffer.size.width) / 2;
|
||||
uint32_t pos_y = (webgpu::g_graphicsConfig.surfaceConfiguration.height - webgpu::g_frameBuffer.size.height) / 2;
|
||||
|
||||
pass.SetViewport(pos_x, pos_y, webgpu::g_frameBuffer.size.width, webgpu::g_frameBuffer.size.height, 0, 1);
|
||||
}
|
||||
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);
|
||||
pass.SetViewport(viewport.x, viewport.y, viewport.width, viewport.height, 0, 1);
|
||||
|
||||
pass.Draw(3);
|
||||
imgui::render(pass);
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
#include "dolphin/gx/GXAurora.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
|
||||
#include "__gx.h"
|
||||
#include "gx.hpp"
|
||||
#include "../../window.hpp"
|
||||
|
||||
#include "../../gfx/common.hpp"
|
||||
#include "../../gx/fifo.hpp"
|
||||
#include "../vi/vi_internal.hpp"
|
||||
|
||||
static void GXWriteString(const char* label) {
|
||||
auto length = strlen(label);
|
||||
@@ -25,15 +29,61 @@ void GXPushDebugGroup(const char* label) {
|
||||
GXWriteString(label);
|
||||
}
|
||||
|
||||
void GXPopDebugGroup() {
|
||||
GX_WRITE_AURORA(GX_LOAD_AURORA_DEBUG_GROUP_POP);
|
||||
}
|
||||
void GXPopDebugGroup() { GX_WRITE_AURORA(GX_LOAD_AURORA_DEBUG_GROUP_POP); }
|
||||
|
||||
void GXInsertDebugMarker(const char* label) {
|
||||
GX_WRITE_AURORA(GX_LOAD_AURORA_DEBUG_MARKER_INSERT);
|
||||
GXWriteString(label);
|
||||
}
|
||||
|
||||
void AuroraSetViewportPolicy(AuroraViewportPolicy policy) { g_gxState.viewportPolicy = policy; }
|
||||
|
||||
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;
|
||||
}
|
||||
if (height != nullptr) {
|
||||
*height = renderHeight;
|
||||
}
|
||||
}
|
||||
|
||||
void GXSetViewportRender(f32 left, f32 top, f32 wd, f32 ht, f32 nearz, f32 farz) {
|
||||
GX_WRITE_AURORA(GX_LOAD_AURORA_VIEWPORT_RENDER);
|
||||
GX_WRITE_F32(left);
|
||||
GX_WRITE_F32(top);
|
||||
GX_WRITE_F32(wd);
|
||||
GX_WRITE_F32(ht);
|
||||
GX_WRITE_F32(nearz);
|
||||
GX_WRITE_F32(farz);
|
||||
}
|
||||
|
||||
void GXSetScissorRender(u32 left, u32 top, u32 wd, u32 ht) {
|
||||
GX_WRITE_AURORA(GX_LOAD_AURORA_SCISSOR_RENDER);
|
||||
GX_WRITE_U32(left);
|
||||
GX_WRITE_U32(top);
|
||||
GX_WRITE_U32(wd);
|
||||
GX_WRITE_U32(ht);
|
||||
}
|
||||
|
||||
void GXCreateFrameBuffer(u32 width, u32 height) {
|
||||
aurora::gx::fifo::drain();
|
||||
aurora::gfx::begin_offscreen(width, height);
|
||||
|
||||
@@ -4,22 +4,19 @@
|
||||
extern "C" {
|
||||
|
||||
void GXSetScissor(u32 left, u32 top, u32 width, u32 height) {
|
||||
const u32 tp = top + 342;
|
||||
const u32 lf = left + 342;
|
||||
const u32 tp = top + 340;
|
||||
const u32 lf = left + 340;
|
||||
const u32 bm = tp + height - 1;
|
||||
const u32 rt = lf + width - 1;
|
||||
|
||||
// NOTE: changed bit size from 11 to 16 to accommodate for higher resolutions
|
||||
SET_REG_FIELD(0, __gx->suScis0, 16, 0, tp);
|
||||
SET_REG_FIELD(0, __gx->suScis0, 16, 16, lf);
|
||||
SET_REG_FIELD(0, __gx->suScis1, 16, 0, bm);
|
||||
SET_REG_FIELD(0, __gx->suScis1, 16, 16, rt);
|
||||
SET_REG_FIELD(0, __gx->suScis0, 11, 0, tp);
|
||||
SET_REG_FIELD(0, __gx->suScis0, 11, 12, lf);
|
||||
SET_REG_FIELD(0, __gx->suScis1, 11, 0, bm);
|
||||
SET_REG_FIELD(0, __gx->suScis1, 11, 12, rt);
|
||||
|
||||
// GX_WRITE_RAS_REG(__gx->suScis0);
|
||||
// GX_WRITE_RAS_REG(__gx->suScis1);
|
||||
// __gx->bpSent = 1;
|
||||
|
||||
aurora::gfx::set_scissor(left, top, width, height);
|
||||
GX_WRITE_RAS_REG(__gx->suScis0);
|
||||
GX_WRITE_RAS_REG(__gx->suScis1);
|
||||
__gx->bpSent = 1;
|
||||
}
|
||||
|
||||
// TODO GXSetScissorBoxOffset
|
||||
|
||||
@@ -5,8 +5,31 @@
|
||||
#include "../../gfx/texture.hpp"
|
||||
#include "../../window.hpp"
|
||||
#include "../../gfx/clear.hpp"
|
||||
#include "../../webgpu/wgpu.hpp"
|
||||
#include "../../webgpu/gpu.hpp"
|
||||
#include "../vi/vi_internal.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
namespace {
|
||||
aurora::Vec2<uint32_t> scale_copy_dst(u32 logicalWidth, u32 logicalHeight) {
|
||||
if (g_gxState.viewportPolicy == AURORA_VIEWPORT_NATIVE) {
|
||||
return {logicalWidth, logicalHeight};
|
||||
}
|
||||
|
||||
const auto [logicalFbWidth, logicalFbHeight] = aurora::gx::logical_fb_size();
|
||||
const auto [targetWidth, targetHeight] = aurora::gfx::get_render_target_size();
|
||||
if (logicalFbWidth == 0 || logicalFbHeight == 0 || targetWidth == 0 || targetHeight == 0) {
|
||||
return {logicalWidth, logicalHeight};
|
||||
}
|
||||
|
||||
const float scaleX = static_cast<float>(targetWidth) / static_cast<float>(logicalFbWidth);
|
||||
const float scaleY = static_cast<float>(targetHeight) / static_cast<float>(logicalFbHeight);
|
||||
const auto scaledWidth = std::max<u32>(static_cast<u32>(std::lround(static_cast<float>(logicalWidth) * scaleX)), 1);
|
||||
const auto scaledHeight = std::max<u32>(static_cast<u32>(std::lround(static_cast<float>(logicalHeight) * scaleY)), 1);
|
||||
return {scaledWidth, scaledHeight};
|
||||
}
|
||||
} // namespace
|
||||
|
||||
extern "C" {
|
||||
GXRenderModeObj GXNtsc480IntDf = {
|
||||
@@ -125,9 +148,8 @@ void GXSetDispCopyGamma(GXGamma gamma) {}
|
||||
void GXCopyDisp(void* dest, GXBool clear) {}
|
||||
|
||||
void GXCopyTex(void* dest, GXBool clear) {
|
||||
const auto& rect = g_gxState.texCopySrc;
|
||||
const u32 dstWidth = g_gxState.texCopyDstWidth;
|
||||
const u32 dstHeight = g_gxState.texCopyDstHeight;
|
||||
const auto rect = aurora::gx::map_logical_scissor(g_gxState.texCopySrc);
|
||||
const auto [dstWidth, dstHeight] = scale_copy_dst(g_gxState.texCopyDstWidth, g_gxState.texCopyDstHeight);
|
||||
const auto texCopyFmt = g_gxState.texCopyFmt;
|
||||
|
||||
const aurora::gx::GXState::CopyTextureKey key{
|
||||
|
||||
@@ -238,14 +238,13 @@ void GXGetProjectionv(f32* p) {
|
||||
void GXGetScissor(u32* left, u32* top, u32* wd, u32* ht) {
|
||||
CHECK(left != nullptr && top != nullptr && wd != nullptr && ht != nullptr, "null scissor output");
|
||||
|
||||
// NOTE: changed bit size from 11 to 16 to accommodate for higher resolutions
|
||||
const u32 tp = GET_REG_FIELD(__gx->suScis0, 16, 0);
|
||||
const u32 lf = GET_REG_FIELD(__gx->suScis0, 16, 16);
|
||||
const u32 bm = GET_REG_FIELD(__gx->suScis1, 16, 0);
|
||||
const u32 rt = GET_REG_FIELD(__gx->suScis1, 16, 16);
|
||||
const u32 tp = GET_REG_FIELD(__gx->suScis0, 11, 0);
|
||||
const u32 lf = GET_REG_FIELD(__gx->suScis0, 11, 12);
|
||||
const u32 bm = GET_REG_FIELD(__gx->suScis1, 11, 0);
|
||||
const u32 rt = GET_REG_FIELD(__gx->suScis1, 11, 12);
|
||||
|
||||
*left = lf - 342;
|
||||
*top = tp - 342;
|
||||
*left = lf - 340;
|
||||
*top = tp - 340;
|
||||
*wd = rt - lf + 1;
|
||||
*ht = bm - tp + 1;
|
||||
}
|
||||
|
||||
@@ -117,10 +117,28 @@ void GXSetViewport(float left, float top, float width, float height, float nearZ
|
||||
}
|
||||
|
||||
void GXSetViewportJitter(float left, float top, float width, float height, float nearZ, float farZ, u32 field) {
|
||||
float sx;
|
||||
float sy;
|
||||
float sz;
|
||||
float ox;
|
||||
float oy;
|
||||
float oz;
|
||||
float zmin;
|
||||
float zmax;
|
||||
|
||||
if (field == 0) {
|
||||
top -= 0.5f;
|
||||
}
|
||||
|
||||
sx = width / 2.0f;
|
||||
sy = -height / 2.0f;
|
||||
ox = 340.0f + (left + width / 2.0f);
|
||||
oy = 340.0f + (top + height / 2.0f);
|
||||
zmin = 1.6777215e7f * nearZ;
|
||||
zmax = 1.6777215e7f * farZ;
|
||||
sz = zmax - zmin;
|
||||
oz = zmax;
|
||||
|
||||
__gx->vpLeft = left;
|
||||
__gx->vpTop = top;
|
||||
__gx->vpWd = width;
|
||||
@@ -128,8 +146,15 @@ void GXSetViewportJitter(float left, float top, float width, float height, float
|
||||
__gx->vpNearz = nearZ;
|
||||
__gx->vpFarz = farZ;
|
||||
|
||||
// TODO: custom aurora FIFO command
|
||||
aurora::gfx::set_viewport(left, top, width, height, nearZ, farZ);
|
||||
GX_WRITE_U8(0x10);
|
||||
GX_WRITE_U32(0x0005101A);
|
||||
GX_WRITE_XF_REG_F(26, sx);
|
||||
GX_WRITE_XF_REG_F(27, sy);
|
||||
GX_WRITE_XF_REG_F(28, sz);
|
||||
GX_WRITE_XF_REG_F(29, ox);
|
||||
GX_WRITE_XF_REG_F(30, oy);
|
||||
GX_WRITE_XF_REG_F(31, oz);
|
||||
__gx->bpSent = 0;
|
||||
}
|
||||
|
||||
void GXProject(f32 x, f32 y, f32 z, const f32 mtx[3][4], const f32* pm, const f32* vp, f32* sx,
|
||||
|
||||
+31
-2
@@ -1,9 +1,39 @@
|
||||
#include <dolphin/vi.h>
|
||||
|
||||
#include "../../window.hpp"
|
||||
#include "aurora/math.hpp"
|
||||
#include "vi_internal.hpp"
|
||||
|
||||
#include <optional>
|
||||
|
||||
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 {
|
||||
if (!g_renderMode) {
|
||||
return {640, 480};
|
||||
}
|
||||
return {g_renderMode->fbWidth, g_renderMode->efbHeight};
|
||||
}
|
||||
} // namespace aurora::vi
|
||||
|
||||
extern "C" {
|
||||
void VIInit() {}
|
||||
void VIConfigure(const GXRenderModeObj* rm) { aurora::vi::configure(rm); }
|
||||
void VIConfigurePan(u16 xOrg, u16 yOrg, u16 width, u16 height) {
|
||||
(void)xOrg;
|
||||
(void)yOrg;
|
||||
(void)width;
|
||||
(void)height;
|
||||
}
|
||||
u32 VIGetTvFormat() { return 0; }
|
||||
void VIFlush() {}
|
||||
|
||||
@@ -13,6 +43,5 @@ bool VIGetWindowFullscreen() { return aurora::window::get_fullscreen(); }
|
||||
void VISetWindowSize(uint32_t width, uint32_t height) { aurora::window::set_window_size(width, height); }
|
||||
void VISetWindowPosition(uint32_t x, uint32_t y) { aurora::window::set_window_position(x, y); }
|
||||
void VICenterWindow() { aurora::window::center_window(); }
|
||||
void VILockAspectRatio(int width, int height) { return aurora::window::lock_aspect_ratio(width, height); }
|
||||
void VIUnlockAspectRatio() { return aurora::window::unlock_aspect_ratio(); }
|
||||
void VISetFrameBufferScale(float scale) { aurora::window::set_frame_buffer_scale(scale); }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <dolphin/gx/GXStruct.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include <aurora/math.hpp>
|
||||
|
||||
namespace aurora::vi {
|
||||
void configure(const GXRenderModeObj* rm) noexcept;
|
||||
Vec2<uint32_t> configured_fb_size() noexcept;
|
||||
} // namespace aurora::vi
|
||||
+23
-23
@@ -9,6 +9,7 @@
|
||||
#include "tex_palette_conv.hpp"
|
||||
#include "texture_replacement.hpp"
|
||||
#include "texture.hpp"
|
||||
#include "../window.hpp"
|
||||
|
||||
#include <optional>
|
||||
#include <ranges>
|
||||
@@ -46,15 +47,6 @@ enum class CommandType {
|
||||
Draw,
|
||||
DebugMarker,
|
||||
};
|
||||
struct SetScissorCommand {
|
||||
uint32_t x;
|
||||
uint32_t y;
|
||||
uint32_t w;
|
||||
uint32_t h;
|
||||
|
||||
bool operator==(const SetScissorCommand& rhs) const { return x == rhs.x && y == rhs.y && w == rhs.w && h == rhs.h; }
|
||||
bool operator!=(const SetScissorCommand& rhs) const { return !(*this == rhs); }
|
||||
};
|
||||
struct Command {
|
||||
CommandType type;
|
||||
#ifdef AURORA_GFX_DEBUG_GROUPS
|
||||
@@ -62,7 +54,7 @@ struct Command {
|
||||
#endif
|
||||
union Data {
|
||||
Viewport setViewport;
|
||||
SetScissorCommand setScissor;
|
||||
ClipRect setScissor;
|
||||
ShaderDrawCommand draw;
|
||||
size_t debugMarkerIndex;
|
||||
} data;
|
||||
@@ -149,7 +141,7 @@ static u32 g_currentRenderPass = UINT32_MAX;
|
||||
static bool g_inOffscreen = false;
|
||||
static std::optional<RenderPass> g_suspendedEfbPass;
|
||||
static Viewport g_suspendedEfbViewport;
|
||||
static SetScissorCommand g_suspendedEfbScissor;
|
||||
static ClipRect g_suspendedEfbScissor;
|
||||
static webgpu::TextureWithSampler g_offscreenColor;
|
||||
static webgpu::TextureWithSampler g_offscreenDepth;
|
||||
|
||||
@@ -214,20 +206,25 @@ static void push_draw_command(ShaderDrawCommand data) {
|
||||
++g_stats.drawCallCount;
|
||||
}
|
||||
|
||||
static Viewport g_cachedViewport;
|
||||
const Viewport& get_viewport() noexcept { return g_cachedViewport; }
|
||||
Vec2<uint32_t> get_render_target_size() noexcept {
|
||||
if (g_currentRenderPass < g_renderPasses.size()) {
|
||||
const auto& size = g_renderPasses[g_currentRenderPass].targetSize;
|
||||
return {size.width, size.height};
|
||||
}
|
||||
const auto windowSize = window::get_window_size();
|
||||
return {windowSize.fb_width, windowSize.fb_height};
|
||||
}
|
||||
|
||||
void set_viewport(float left, float top, float width, float height, float znear, float zfar) noexcept {
|
||||
Viewport cmd{left, top, width, height, znear, zfar};
|
||||
static Viewport g_cachedViewport;
|
||||
void set_viewport(const Viewport& cmd) noexcept {
|
||||
if (cmd != g_cachedViewport) {
|
||||
push_command(CommandType::SetViewport, Command::Data{.setViewport = cmd});
|
||||
g_cachedViewport = cmd;
|
||||
}
|
||||
}
|
||||
|
||||
static SetScissorCommand g_cachedScissor;
|
||||
void set_scissor(uint32_t x, uint32_t y, uint32_t w, uint32_t h) noexcept {
|
||||
SetScissorCommand cmd{x, y, w, h};
|
||||
static ClipRect g_cachedScissor;
|
||||
void set_scissor(const ClipRect& cmd) noexcept {
|
||||
if (cmd != g_cachedScissor) {
|
||||
push_command(CommandType::SetScissor, Command::Data{.setScissor = cmd});
|
||||
g_cachedScissor = cmd;
|
||||
@@ -407,7 +404,7 @@ void begin_offscreen(uint32_t width, uint32_t height) {
|
||||
g_inOffscreen = true;
|
||||
|
||||
g_cachedViewport = {0.f, 0.f, static_cast<float>(width), static_cast<float>(height), 0.f, 1.f};
|
||||
g_cachedScissor = {0, 0, width, height};
|
||||
g_cachedScissor = {0, 0, static_cast<int32_t>(width), static_cast<int32_t>(height)};
|
||||
push_command(CommandType::SetViewport, Command::Data{.setViewport = g_cachedViewport});
|
||||
push_command(CommandType::SetScissor, Command::Data{.setScissor = g_cachedScissor});
|
||||
}
|
||||
@@ -647,6 +644,9 @@ void begin_frame() {
|
||||
g_renderPasses[0].clearColorValue = gx::g_gxState.clearColor;
|
||||
g_renderPasses[0].clearDepthValue = gx::clear_depth_value();
|
||||
g_currentRenderPass = 0;
|
||||
// Refresh render viewport/scissor from logical in case FB size changed
|
||||
g_cachedViewport = gx::map_logical_viewport(gx::g_gxState.logicalViewport);
|
||||
g_cachedScissor = gx::map_logical_scissor(gx::g_gxState.logicalScissor);
|
||||
push_command(CommandType::SetViewport, Command::Data{.setViewport = g_cachedViewport});
|
||||
push_command(CommandType::SetScissor, Command::Data{.setScissor = g_cachedScissor});
|
||||
begin_pipeline_frame();
|
||||
@@ -857,10 +857,10 @@ void render_pass(const wgpu::RenderPassEncoder& pass, u32 idx) {
|
||||
case CommandType::SetScissor: {
|
||||
const auto& sc = cmd.data.setScissor;
|
||||
const auto& size = g_renderPasses[idx].targetSize;
|
||||
const auto x = std::clamp(sc.x, 0u, size.width);
|
||||
const auto y = std::clamp(sc.y, 0u, size.height);
|
||||
const auto w = std::clamp(sc.w, 0u, size.width - x);
|
||||
const auto h = std::clamp(sc.h, 0u, size.height - y);
|
||||
const auto x = std::clamp(static_cast<uint32_t>(sc.x), 0u, size.width);
|
||||
const auto y = std::clamp(static_cast<uint32_t>(sc.y), 0u, size.height);
|
||||
const auto w = std::clamp(static_cast<uint32_t>(sc.width), 0u, size.width - x);
|
||||
const auto h = std::clamp(static_cast<uint32_t>(sc.height), 0u, size.height - y);
|
||||
pass.SetScissorRect(x, y, w, h);
|
||||
} break;
|
||||
case CommandType::Draw: {
|
||||
|
||||
+3
-3
@@ -295,9 +295,9 @@ wgpu::Sampler& sampler_ref(const wgpu::SamplerDescriptor& descriptor);
|
||||
|
||||
uint32_t align_uniform(uint32_t value);
|
||||
|
||||
const Viewport& get_viewport() noexcept;
|
||||
void set_viewport(float left, float top, float width, float height, float znear, float zfar) noexcept;
|
||||
void set_scissor(uint32_t x, uint32_t y, uint32_t w, uint32_t h) noexcept;
|
||||
Vec2<uint32_t> get_render_target_size() noexcept;
|
||||
void set_viewport(const Viewport& viewport) noexcept;
|
||||
void set_scissor(const ClipRect& scissor) noexcept;
|
||||
|
||||
void push_debug_group(std::string label);
|
||||
void insert_debug_marker(std::string label);
|
||||
|
||||
@@ -632,9 +632,15 @@ static void handle_bp(u32 value, bool bigEndian) {
|
||||
// Scissor registers (0x20, 0x21)
|
||||
case 0x20:
|
||||
case 0x21: {
|
||||
#ifndef NDEBUG
|
||||
Log.debug("Unimplemented: BP register {:x} (scissor)", regId);
|
||||
#endif
|
||||
const u32 scis0 = g_gxState.bpRegCache[0x20];
|
||||
const u32 scis1 = g_gxState.bpRegCache[0x21];
|
||||
const int32_t tp = static_cast<int32_t>(bp_get(scis0, 11, 0));
|
||||
const int32_t lf = static_cast<int32_t>(bp_get(scis0, 11, 12));
|
||||
const int32_t bm = static_cast<int32_t>(bp_get(scis1, 11, 0));
|
||||
const int32_t rt = static_cast<int32_t>(bp_get(scis1, 11, 12));
|
||||
if (rt >= lf && bm >= tp) {
|
||||
set_logical_scissor({lf - 340, tp - 340, rt - lf + 1, bm - tp + 1});
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1380,11 +1386,14 @@ static void handle_xf(const u8* data, u32& pos, u32 size, bool bigEndian) {
|
||||
f32 oz = read_f32(xfData + 20, bigEndian);
|
||||
f32 width = sx * 2.0f;
|
||||
f32 height = -sy * 2.0f;
|
||||
f32 left = ox - 340.0f - width / 2.0f;
|
||||
f32 top = oy - 340.0f - height / 2.0f;
|
||||
f32 farZ = oz / 1.6777215e7f;
|
||||
f32 nearZ = (oz - sz) / 1.6777215e7f;
|
||||
gfx::set_viewport(left, top, width, height, nearZ, farZ);
|
||||
set_logical_viewport({
|
||||
.left = ox - 340.0f - width / 2.0f,
|
||||
.top = oy - 340.0f - height / 2.0f,
|
||||
.width = width,
|
||||
.height = height,
|
||||
.znear = (oz - sz) / 1.6777215e7f,
|
||||
.zfar = oz / 1.6777215e7f,
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1660,7 +1669,40 @@ void handle_aurora(const u8* data, u32& pos, u32 size, bool bigEndian) {
|
||||
pos += 2;
|
||||
|
||||
// Setting of vertex array bases.
|
||||
if (subCmd >= GX_LOAD_AURORA_ARRAYBASE && subCmd <= (GX_LOAD_AURORA_ARRAYBASE | 0x0f)) {
|
||||
if (subCmd == GX_LOAD_AURORA_VIEWPORT_RENDER) {
|
||||
CHECK(pos + 24 <= size, "GX_LOAD_AURORA_VIEWPORT_RENDER read overrun");
|
||||
const f32 left = read_f32(data + pos, bigEndian);
|
||||
pos += 4;
|
||||
const f32 top = read_f32(data + pos, bigEndian);
|
||||
pos += 4;
|
||||
const f32 width = read_f32(data + pos, bigEndian);
|
||||
pos += 4;
|
||||
const f32 height = read_f32(data + pos, bigEndian);
|
||||
pos += 4;
|
||||
const f32 nearZ = read_f32(data + pos, bigEndian);
|
||||
pos += 4;
|
||||
const f32 farZ = read_f32(data + pos, bigEndian);
|
||||
pos += 4;
|
||||
set_render_viewport({
|
||||
.left = left,
|
||||
.top = top,
|
||||
.width = width,
|
||||
.height = height,
|
||||
.znear = nearZ,
|
||||
.zfar = farZ,
|
||||
});
|
||||
} else if (subCmd == GX_LOAD_AURORA_SCISSOR_RENDER) {
|
||||
CHECK(pos + 16 <= size, "GX_LOAD_AURORA_SCISSOR_RENDER read overrun");
|
||||
const int32_t left = static_cast<int32_t>(read_u32(data + pos, bigEndian));
|
||||
pos += 4;
|
||||
const int32_t top = static_cast<int32_t>(read_u32(data + pos, bigEndian));
|
||||
pos += 4;
|
||||
const int32_t width = static_cast<int32_t>(read_u32(data + pos, bigEndian));
|
||||
pos += 4;
|
||||
const int32_t height = static_cast<int32_t>(read_u32(data + pos, bigEndian));
|
||||
pos += 4;
|
||||
set_render_scissor({left, top, width, height});
|
||||
} else if (subCmd >= GX_LOAD_AURORA_ARRAYBASE && subCmd <= (GX_LOAD_AURORA_ARRAYBASE | 0x0f)) {
|
||||
CHECK(pos + 13 <= size, "GX_LOAD_AURORA_ARRAYBASE read overrun");
|
||||
u32 attrIdx = subCmd - GX_LOAD_AURORA_ARRAYBASE + GX_VA_POS;
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "gx.hpp"
|
||||
|
||||
#include "pipeline.hpp"
|
||||
#include "../dolphin/vi/vi_internal.hpp"
|
||||
#include "../webgpu/gpu.hpp"
|
||||
#include "../internal.hpp"
|
||||
#include "../gfx/common.hpp"
|
||||
@@ -241,6 +242,103 @@ u32 resolved_format_for_handle(const gfx::TextureHandle& handle) {
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Vec2<uint32_t> logical_fb_size() noexcept {
|
||||
return gfx::is_offscreen() ? gfx::get_render_target_size() : vi::configured_fb_size();
|
||||
}
|
||||
|
||||
gfx::Viewport map_logical_viewport(const gfx::Viewport& logicalViewport) noexcept {
|
||||
if (g_gxState.viewportPolicy == AURORA_VIEWPORT_NATIVE) {
|
||||
return logicalViewport;
|
||||
}
|
||||
|
||||
const auto [logicalFbWidth, logicalFbHeight] = logical_fb_size();
|
||||
const auto [targetWidth, targetHeight] = gfx::get_render_target_size();
|
||||
if (logicalFbWidth == 0 || logicalFbHeight == 0 || targetWidth == 0 || targetHeight == 0) {
|
||||
return logicalViewport;
|
||||
}
|
||||
|
||||
const bool stretch = g_gxState.viewportPolicy == AURORA_VIEWPORT_STRETCH;
|
||||
const float scaleX = static_cast<float>(targetWidth) / static_cast<float>(logicalFbWidth);
|
||||
const float scaleY = static_cast<float>(targetHeight) / static_cast<float>(logicalFbHeight);
|
||||
const float scale = std::min(scaleX, scaleY);
|
||||
const float xOffset =
|
||||
stretch ? 0.f : (static_cast<float>(targetWidth) - static_cast<float>(logicalFbWidth) * scale) * 0.5f;
|
||||
const float yOffset =
|
||||
stretch ? 0.f : (static_cast<float>(targetHeight) - static_cast<float>(logicalFbHeight) * scale) * 0.5f;
|
||||
const float mappedScaleX = stretch ? scaleX : scale;
|
||||
const float mappedScaleY = stretch ? scaleY : scale;
|
||||
return {
|
||||
.left = xOffset + logicalViewport.left * mappedScaleX,
|
||||
.top = yOffset + logicalViewport.top * mappedScaleY,
|
||||
.width = logicalViewport.width * mappedScaleX,
|
||||
.height = logicalViewport.height * mappedScaleY,
|
||||
.znear = logicalViewport.znear,
|
||||
.zfar = logicalViewport.zfar,
|
||||
};
|
||||
}
|
||||
|
||||
gfx::ClipRect map_logical_scissor(const gfx::ClipRect& logicalScissor) noexcept {
|
||||
if (g_gxState.viewportPolicy == AURORA_VIEWPORT_NATIVE) {
|
||||
return logicalScissor;
|
||||
}
|
||||
|
||||
const auto [logicalFbWidth, logicalFbHeight] = logical_fb_size();
|
||||
const auto [targetWidth, targetHeight] = gfx::get_render_target_size();
|
||||
if (logicalFbWidth == 0 || logicalFbHeight == 0 || targetWidth == 0 || targetHeight == 0) {
|
||||
return logicalScissor;
|
||||
}
|
||||
|
||||
const bool stretch = g_gxState.viewportPolicy == AURORA_VIEWPORT_STRETCH;
|
||||
const float scaleX = static_cast<float>(targetWidth) / static_cast<float>(logicalFbWidth);
|
||||
const float scaleY = static_cast<float>(targetHeight) / static_cast<float>(logicalFbHeight);
|
||||
const float scale = std::min(scaleX, scaleY);
|
||||
const float xOffset =
|
||||
stretch ? 0.f : (static_cast<float>(targetWidth) - static_cast<float>(logicalFbWidth) * scale) * 0.5f;
|
||||
const float yOffset =
|
||||
stretch ? 0.f : (static_cast<float>(targetHeight) - static_cast<float>(logicalFbHeight) * scale) * 0.5f;
|
||||
const float mappedScaleX = stretch ? scaleX : scale;
|
||||
const float mappedScaleY = stretch ? scaleY : scale;
|
||||
|
||||
const float left = xOffset + static_cast<float>(logicalScissor.x) * mappedScaleX;
|
||||
const float top = yOffset + static_cast<float>(logicalScissor.y) * mappedScaleY;
|
||||
const float right = xOffset + static_cast<float>(logicalScissor.x + logicalScissor.width) * mappedScaleX;
|
||||
const float bottom = yOffset + static_cast<float>(logicalScissor.y + logicalScissor.height) * mappedScaleY;
|
||||
|
||||
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));
|
||||
const auto mappedRight =
|
||||
std::clamp(static_cast<int32_t>(std::ceil(right)), mappedLeft, static_cast<int32_t>(targetWidth));
|
||||
const auto mappedBottom =
|
||||
std::clamp(static_cast<int32_t>(std::ceil(bottom)), mappedTop, static_cast<int32_t>(targetHeight));
|
||||
|
||||
return {
|
||||
.x = mappedLeft,
|
||||
.y = mappedTop,
|
||||
.width = mappedRight - mappedLeft,
|
||||
.height = mappedBottom - mappedTop,
|
||||
};
|
||||
}
|
||||
|
||||
void set_logical_viewport(const gfx::Viewport& viewport) noexcept {
|
||||
g_gxState.logicalViewport = viewport;
|
||||
set_render_viewport(map_logical_viewport(viewport));
|
||||
}
|
||||
|
||||
void set_render_viewport(const gfx::Viewport& viewport) noexcept {
|
||||
g_gxState.renderViewport = viewport;
|
||||
gfx::set_viewport(viewport);
|
||||
}
|
||||
|
||||
void set_logical_scissor(const gfx::ClipRect& scissor) noexcept {
|
||||
g_gxState.logicalScissor = scissor;
|
||||
set_render_scissor(map_logical_scissor(g_gxState.logicalScissor));
|
||||
}
|
||||
|
||||
void set_render_scissor(const gfx::ClipRect& scissor) noexcept {
|
||||
g_gxState.renderScissor = scissor;
|
||||
gfx::set_scissor(scissor);
|
||||
}
|
||||
|
||||
const gfx::TextureBind& get_texture(GXTexMapID id) noexcept { return g_gxState.textures[static_cast<size_t>(id)]; }
|
||||
|
||||
void evict_texture_object(u32 texObjId) noexcept {
|
||||
|
||||
@@ -321,6 +321,11 @@ struct GXState {
|
||||
std::array<gfx::TextureBind, MaxTextures> textures;
|
||||
std::array<GXTexObj_, MaxTextures> loadedTextures;
|
||||
std::array<GXTlutObj_, MaxTluts> loadedTluts;
|
||||
AuroraViewportPolicy viewportPolicy = AURORA_VIEWPORT_FIT;
|
||||
gfx::Viewport logicalViewport{0.f, 0.f, 640.f, 480.f, 0.f, 1.f};
|
||||
gfx::Viewport renderViewport{0.f, 0.f, 640.f, 480.f, 0.f, 1.f};
|
||||
gfx::ClipRect logicalScissor{0, 0, 640, 480};
|
||||
gfx::ClipRect renderScissor{0, 0, 640, 480};
|
||||
std::array<Mat3x4<float>, MaxTexMtx> texMtxs;
|
||||
std::array<Mat3x4<float>, MaxPTTexMtx> ptTexMtxs;
|
||||
std::array<TcgConfig, MaxTexCoord> tcgs;
|
||||
@@ -385,6 +390,13 @@ void shutdown() noexcept;
|
||||
void clear_copy_texture_cache() noexcept;
|
||||
void evict_texture_object(u32 texObjId) noexcept;
|
||||
void evict_tlut_object(u32 tlutObjId) noexcept;
|
||||
Vec2<uint32_t> logical_fb_size() noexcept;
|
||||
gfx::Viewport map_logical_viewport(const gfx::Viewport& logicalViewport) noexcept;
|
||||
gfx::ClipRect map_logical_scissor(const gfx::ClipRect& logicalScissor) noexcept;
|
||||
void set_logical_viewport(const gfx::Viewport& viewport) noexcept;
|
||||
void set_render_viewport(const gfx::Viewport& viewport) noexcept;
|
||||
void set_logical_scissor(const gfx::ClipRect& scissor) noexcept;
|
||||
void set_render_scissor(const gfx::ClipRect& scissor) noexcept;
|
||||
const gfx::TextureBind& get_texture(GXTexMapID id) noexcept;
|
||||
void resolve_sampled_textures(const ShaderInfo& info) noexcept;
|
||||
|
||||
|
||||
+10
-6
@@ -828,11 +828,12 @@ wgpu::ShaderModule build_shader(const ShaderConfig& config) noexcept {
|
||||
// GX_POINTS: expand single vertex to axis-aligned screen-space square
|
||||
vtxXfrAttrsPre +=
|
||||
"\n let clip = vec4f(mv_pos, 1.0) * ubuf.proj;"
|
||||
"\n let point_size = ubuf.line_width * (ubuf.viewport_size.y / 528.0);"
|
||||
"\n let viewport_scale = ubuf.render_viewport_size / max(ubuf.logical_viewport_size, vec2f(1.0));"
|
||||
"\n let point_size = ubuf.line_width * min(viewport_scale.x, viewport_scale.y);"
|
||||
"\n let x_sign = select(-1.0, 1.0, (vidx & 1u) != 0u);"
|
||||
"\n let y_sign = select(-1.0, 1.0, vidx >= 2u);"
|
||||
"\n let offset_px = vec2f(x_sign, y_sign) * (point_size / 2.0);"
|
||||
"\n let offset_ndc = (offset_px * 2.0) / ubuf.viewport_size;"
|
||||
"\n let offset_ndc = (offset_px * 2.0) / ubuf.render_viewport_size;"
|
||||
"\n out.pos = vec4f(clip.xy + offset_ndc * clip.w, clip.zw);";
|
||||
} else {
|
||||
// GX_LINES / GX_LINESTRIP: expand line segment perpendicular to direction
|
||||
@@ -841,12 +842,13 @@ wgpu::ShaderModule build_shader(const ShaderConfig& config) noexcept {
|
||||
"\n let clip_b = vec4f(mv_pos_b, 1.0) * ubuf.proj;"
|
||||
"\n let ndc_a = clip_a.xy / clip_a.w;"
|
||||
"\n let ndc_b = clip_b.xy / clip_b.w;"
|
||||
"\n let delta_px = (ndc_b - ndc_a) / 2.0 * ubuf.viewport_size;"
|
||||
"\n let viewport_scale = ubuf.render_viewport_size / max(ubuf.logical_viewport_size, vec2f(1.0));"
|
||||
"\n let delta_px = (ndc_b - ndc_a) / 2.0 * ubuf.render_viewport_size;"
|
||||
"\n let dir_px = select(vec2f(1.0, 0.0), normalize(delta_px), dot(delta_px, delta_px) > 1e-10);"
|
||||
"\n let perp_px = vec2f(-dir_px.y, dir_px.x);"
|
||||
"\n let line_width = ubuf.line_width * (ubuf.viewport_size.y / 528.0);" // Scale line width based on viewport
|
||||
"\n let line_width = ubuf.line_width * min(viewport_scale.x, viewport_scale.y);"
|
||||
"\n let offset_px = perp_px * (line_width / 2.0) * select(-1.0, 1.0, (vidx & 1u) != 0u);"
|
||||
"\n let offset_ndc = (offset_px * 2.0) / ubuf.viewport_size;"
|
||||
"\n let offset_ndc = (offset_px * 2.0) / ubuf.render_viewport_size;"
|
||||
"\n let clip_base = select(clip_a, clip_b, use_b);"
|
||||
"\n out.pos = vec4f(clip_base.xy + offset_ndc * clip_base.w, clip_base.zw);";
|
||||
}
|
||||
@@ -1688,7 +1690,9 @@ fn tev_overflow_vec4f(in: vec4f) -> vec4f {{
|
||||
struct Uniform {{
|
||||
vtx_start: u32,
|
||||
current_pnmtx: u32,
|
||||
viewport_size: vec2f,
|
||||
render_viewport_size: vec2f,
|
||||
logical_viewport_size: vec2f,
|
||||
pad: vec2u,
|
||||
array_start: array<u32, 12>,{0}
|
||||
}};
|
||||
@group(0) @binding(0)
|
||||
|
||||
+16
-6
@@ -5,6 +5,9 @@
|
||||
#include <tracy/Tracy.hpp>
|
||||
|
||||
namespace aurora::gx {
|
||||
// TODO: remove, just for testing
|
||||
bool enableLodBias = true;
|
||||
|
||||
namespace {
|
||||
Module Log("aurora::gx");
|
||||
|
||||
@@ -13,7 +16,11 @@ bool is_alpha_bump_channel(GXChannelID id) { return id == GX_ALPHA_BUMP || id ==
|
||||
Vec4<float> texture_size_bias(const gfx::TextureBind& tex) {
|
||||
auto width = static_cast<float>(tex.texObj.width());
|
||||
auto height = static_cast<float>(tex.texObj.height());
|
||||
return {width, height, tex.texObj.lod_bias(), 0.0f};
|
||||
const auto vpBias =
|
||||
enableLodBias ? log2(std::min(g_gxState.renderViewport.width / std::max(g_gxState.logicalViewport.width, 1.f),
|
||||
g_gxState.renderViewport.height / std::max(g_gxState.logicalViewport.height, 1.f)))
|
||||
: 0.f;
|
||||
return {width, height, tex.texObj.lod_bias() + vpBias, 0.0f};
|
||||
}
|
||||
|
||||
void color_arg_reg_info(GXTevColorArg arg, const TevStage& stage, ShaderInfo& info) {
|
||||
@@ -169,7 +176,8 @@ ShaderInfo build_shader_info(const ShaderConfig& config) noexcept {
|
||||
ZoneScoped;
|
||||
|
||||
ShaderInfo info{
|
||||
.uniformSize = 4 + 4 + 8 + 48 + 64, // vtx_start, current_pnmtx, viewport_size, array_start, proj
|
||||
// vtx_start, current_pnmtx, render/logical viewport size, array_start, pad, proj
|
||||
.uniformSize = 4 + 4 + 8 + 8 + 8 + 48 + 64,
|
||||
};
|
||||
|
||||
if (config.lineMode != 0) {
|
||||
@@ -343,15 +351,17 @@ static u32 line_texcoord_mask() noexcept {
|
||||
return mask;
|
||||
}
|
||||
|
||||
gfx::Range build_uniform(const ShaderInfo& info, u32 vtxStart,
|
||||
const BindGroupRanges& ranges) noexcept {
|
||||
gfx::Range build_uniform(const ShaderInfo& info, u32 vtxStart, const BindGroupRanges& ranges) noexcept {
|
||||
ZoneScoped;
|
||||
|
||||
auto [buf, range] = gfx::map_uniform(info.uniformSize);
|
||||
buf.append(vtxStart);
|
||||
buf.append(g_gxState.currentPnMtx);
|
||||
buf.append<f32>(gfx::get_viewport().width);
|
||||
buf.append<f32>(gfx::get_viewport().height);
|
||||
buf.append<f32>(g_gxState.renderViewport.width);
|
||||
buf.append<f32>(g_gxState.renderViewport.height);
|
||||
buf.append<f32>(g_gxState.logicalViewport.width);
|
||||
buf.append<f32>(g_gxState.logicalViewport.height);
|
||||
buf.append_zeroes(8); // pad
|
||||
for (const auto& vaRange : ranges.vaRanges) {
|
||||
buf.append<u32>(vaRange.offset);
|
||||
}
|
||||
|
||||
+1
-1
@@ -591,7 +591,7 @@ bool initialize(AuroraBackend auroraBackend) {
|
||||
.textureAnisotropy = g_config.maxTextureAnisotropy,
|
||||
};
|
||||
create_copy_pipeline();
|
||||
resize_swapchain(size.fb_width, size.fb_height, size.native_fb_width, size.native_fb_width, true);
|
||||
resize_swapchain(size.fb_width, size.fb_height, size.native_fb_width, size.native_fb_height, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+45
-26
@@ -20,17 +20,19 @@
|
||||
#include <SDL3/SDL_stdinc.h>
|
||||
#include <SDL3/SDL_pixels.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
|
||||
#include "dolphin/vi/vi_internal.hpp"
|
||||
|
||||
namespace aurora::window {
|
||||
namespace {
|
||||
Module Log("aurora::window");
|
||||
|
||||
SDL_Window* g_window;
|
||||
SDL_Renderer* g_renderer;
|
||||
bool g_aspectRatioLocked;
|
||||
int g_aspectRatioW, g_aspectRatioH;
|
||||
float g_frameBufferScale = 0.f;
|
||||
AuroraWindowSize g_windowSize;
|
||||
std::vector<AuroraEvent> g_events;
|
||||
|
||||
@@ -40,6 +42,27 @@ inline bool operator==(const AuroraWindowSize& lhs, const AuroraWindowSize& rhs)
|
||||
lhs.native_fb_width == rhs.native_fb_width && lhs.scale == rhs.scale;
|
||||
}
|
||||
|
||||
Vec2<int> scale_frame_buffer_to_aspect(int base_width, int base_height, float scale, float aspect) {
|
||||
if (base_width <= 0 || base_height <= 0 || scale <= 0.f || aspect <= 0.f) {
|
||||
return {std::max(base_width, 1), std::max(base_height, 1)};
|
||||
}
|
||||
|
||||
const int scaled_base_width = std::max(1, static_cast<int>(std::lround(static_cast<float>(base_width) * scale)));
|
||||
const int scaled_base_height = std::max(1, static_cast<int>(std::lround(static_cast<float>(base_height) * scale)));
|
||||
const float base_aspect = static_cast<float>(base_width) / static_cast<float>(base_height);
|
||||
if (aspect >= base_aspect) {
|
||||
return {
|
||||
std::max(1, static_cast<int>(std::lround(static_cast<float>(scaled_base_height) * aspect))),
|
||||
scaled_base_height,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
scaled_base_width,
|
||||
std::max(1, static_cast<int>(std::lround(static_cast<float>(scaled_base_width) / aspect))),
|
||||
};
|
||||
}
|
||||
|
||||
void resize_swapchain() noexcept {
|
||||
const auto size = get_window_size();
|
||||
if (size == g_windowSize) {
|
||||
@@ -344,15 +367,13 @@ AuroraWindowSize get_window_size() {
|
||||
|
||||
int fb_w = native_fb_w;
|
||||
int fb_h = native_fb_h;
|
||||
|
||||
if (g_aspectRatioLocked) {
|
||||
// Try as if bounded on width.
|
||||
fb_h = fb_w * g_aspectRatioH / g_aspectRatioW;
|
||||
if (fb_h > native_fb_h) {
|
||||
// Bounded on height instead.
|
||||
fb_h = native_fb_h;
|
||||
fb_w = fb_h * g_aspectRatioW / g_aspectRatioH;
|
||||
}
|
||||
if (g_frameBufferScale > 0.f) {
|
||||
const auto [baseW, baseH] = vi::configured_fb_size();
|
||||
const auto [scaledW, scaledH] =
|
||||
scale_frame_buffer_to_aspect(static_cast<int>(baseW), static_cast<int>(baseH), g_frameBufferScale,
|
||||
static_cast<float>(fb_w) / static_cast<float>(fb_h));
|
||||
fb_w = scaledW;
|
||||
fb_h = scaledH;
|
||||
}
|
||||
|
||||
const float scale = SDL_GetWindowDisplayScale(g_window);
|
||||
@@ -400,20 +421,18 @@ static void push_future_resize_event() {
|
||||
TRY_WARN(SDL_PushEvent(&event), "Failed to push SDL event for future resize: {}", SDL_GetError());
|
||||
}
|
||||
|
||||
void lock_aspect_ratio(int width, int height) {
|
||||
g_aspectRatioLocked = true;
|
||||
g_aspectRatioW = width;
|
||||
g_aspectRatioH = height;
|
||||
|
||||
// Defer so that we don't try to reconfigure the surface in the middle of game logic.
|
||||
push_future_resize_event();
|
||||
}
|
||||
|
||||
void unlock_aspect_ratio() {
|
||||
g_aspectRatioLocked = false;
|
||||
|
||||
// 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;
|
||||
}
|
||||
if (g_frameBufferScale == 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();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace aurora::window
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user