Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions runtime/include/aurora_events.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,8 @@
#endif

extern "C" bool g_dynamicAspectRatioEnabled;
void ConfigureMkwDynamicAspect(bool widescreen, bool forceAspect169, uint32_t surfaceWidth, uint32_t surfaceHeight);
void SetMkwForceAspect169(bool enabled);
bool MkwForceAspect169Requested();
void ConfigureMkwDynamicAspect(bool widescreen, bool forceAspect, uint32_t surfaceWidth, uint32_t surfaceHeight);
void SetMkwForceAspect(bool enabled);
void UpdateMkwDynamicAspectSurface(uint32_t surfaceWidth, uint32_t surfaceHeight);
// Arms the "keep EGG::Frustum's projection scale" flag on every screen that
// renders to a fixed-size offscreen target. Cheap and idempotent; called from
Expand Down
42 changes: 34 additions & 8 deletions runtime/include/runtime_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@

struct RuntimeUserConfig {
std::optional<bool> widescreen;
std::optional<bool> forceAspect169;
std::optional<bool> forceAspect;
bool legacyForceAspect = false;
std::optional<int32_t> windowPosX;
std::optional<int32_t> windowPosY;
std::optional<uint32_t> windowWidth;
Expand Down Expand Up @@ -271,7 +272,7 @@ inline void EnsureConfigFile() {
"# Set paths.dvd_root to an extracted Mario Kart Wii DATA directory.\n\n"
"[video]\n"
"widescreen = true\n"
"force_16_9 = false\n"
"force_aspect = false\n"
"resolution_multiplier = 1.0\n"
"frame_interpolation_fps = 0\n"
"display_mode = \"windowed\"\n"
Expand Down Expand Up @@ -403,7 +404,15 @@ inline RuntimeUserConfig ParseConfigDocument(const toml::value& document) {
}

config.widescreen = FindConfigValue<bool>(document, "video", "widescreen");
config.forceAspect169 = FindConfigValue<bool>(document, "video", "force_16_9");
config.forceAspect = FindConfigValue<bool>(document, "video", "force_aspect");
if (!config.forceAspect) {
// Legacy key; it always forced a 16:9 game.
config.forceAspect = FindConfigValue<bool>(document, "video", "force_16_9");
config.legacyForceAspect = config.forceAspect.has_value();
if (config.forceAspect.value_or(false)) {
config.widescreen = true;
}
}
config.windowPosX = FindConfigInt(document, "video", "window_x");
config.windowPosY = FindConfigInt(document, "video", "window_y");
if (auto value = FindConfigUint(document, "video", "window_width"); value && *value != 0) {
Expand Down Expand Up @@ -760,13 +769,30 @@ inline bool WidescreenEnabled(bool fallback = false) {
return Get().widescreen.value_or(fallback);
}

inline bool ForceAspect169Enabled(bool fallback = false) {
return Get().forceAspect169.value_or(fallback);
inline bool ForceAspectEnabled(bool fallback = false) {
return Get().forceAspect.value_or(fallback);
}

inline bool SetForceAspect169(bool value) {
Mutable().forceAspect169 = value;
return WriteSetting("video", "force_16_9", value ? "true" : "false");
inline bool SetForceAspect(bool value) {
Mutable().forceAspect = value;
return WriteSetting("video", "force_aspect", value ? "true" : "false");
}

// The in-memory value stays as booted; SCGetAspectRatio must not change mid-session.
inline bool SetWidescreen(bool value) {
return WriteSetting("video", "widescreen", value ? "true" : "false");
}

// Rewrites a legacy force_16_9 as force_aspect plus the widescreen it implied.
inline void MigrateLegacyForceAspect() {
if (!Get().legacyForceAspect) {
return;
}
const bool force = ForceAspectEnabled();
if ((!force || WriteSetting("video", "widescreen", "true")) &&
WriteSetting("video", "force_aspect", force ? "true" : "false")) {
Mutable().legacyForceAspect = false;
}
}

inline bool WindowPosition(int32_t& x, int32_t& y) {
Expand Down
76 changes: 28 additions & 48 deletions runtime/src/dynamic_aspect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,9 @@ extern "C" int g_gxFrameCount;

namespace {

bool g_widescreenConfigured = false;
bool g_widescreenSetting = false;
bool g_forceAspect169 = false;
std::atomic_bool g_requestedForceAspect169{false};
bool g_widescreen = true;
bool g_forceAspect = false;
std::atomic_bool g_requestedForceAspect{false};
bool g_policyDirty = false;
uint32_t g_lastEggWidth43 = 0;
uint32_t g_lastEggWidth169 = 0;
Expand Down Expand Up @@ -130,19 +129,20 @@ void WriteEggScreenRecord(uint32_t recordAddr, uint32_t width) {

void ApplyEggScreenRecords(uint32_t surfaceWidth, uint32_t surfaceHeight) {
using namespace MkwDynamicAspect;
if (g_forceAspect) {
surfaceWidth = g_widescreen ? 16u : 4u;
surfaceHeight = g_widescreen ? 9u : 3u;
}
const float referenceAspect = g_widescreen ? kPresentationAspect169 : kPresentationAspect43;
const uint32_t width43 =
g_widescreenConfigured ? EggRecordWidth(kEggRecordWidth43, surfaceWidth, surfaceHeight)
: kEggRecordWidth43;
EggRecordWidth(kEggRecordWidth43, surfaceWidth, surfaceHeight, referenceAspect);
const uint32_t width169 =
g_widescreenConfigured ? EggRecordWidth(kEggRecordWidth169, surfaceWidth, surfaceHeight)
: kEggRecordWidth169;
EggRecordWidth(kEggRecordWidth169, surfaceWidth, surfaceHeight);
WriteEggScreenRecord(kEggScreenRecord43, width43);
WriteEggScreenRecord(kEggScreenRecord169, width169);
Memory::WriteFloat32(kEggScreenProjScaleX, 1.0f);
Memory::WriteFloat32(kEggScreenProjScaleY,
g_widescreenConfigured
? VerticalExpansion(surfaceWidth, surfaceHeight)
: 1.0f);
VerticalExpansion(surfaceWidth, surfaceHeight, referenceAspect));
AssertOffscreenScreenBypass();
if (width43 == g_lastEggWidth43 && width169 == g_lastEggWidth169) {
return;
Expand Down Expand Up @@ -172,58 +172,38 @@ void AssertMkwOffscreenScreenBypass() {
}

void UpdateMkwDynamicAspectSurface(uint32_t surfaceWidth, uint32_t surfaceHeight) {
const bool requestedForceAspect169 = g_requestedForceAspect169.load(std::memory_order_acquire);
if (g_forceAspect169 != requestedForceAspect169) {
g_forceAspect169 = requestedForceAspect169;
g_widescreenConfigured = g_widescreenSetting || g_forceAspect169;
g_dynamicAspectRatioEnabled = g_widescreenConfigured;
const bool requestedForceAspect = g_requestedForceAspect.load(std::memory_order_acquire);
if (g_forceAspect != requestedForceAspect) {
g_forceAspect = requestedForceAspect;
g_policyDirty = true;
}
if (surfaceWidth == 0 || surfaceHeight == 0) {
return;
}
if (!g_widescreenConfigured) {
if (g_policyDirty) {
if (g_policyDirty) {
if (g_forceAspect && !g_widescreen) {
AuroraSetViewportPolicy(AURORA_VIEWPORT_FIT);
VILockAspectRatio(4, 3);
ApplyEggScreenRecords(surfaceWidth, surfaceHeight);
g_policyDirty = false;
} else {
AuroraSetViewportPolicy(g_forceAspect ? AURORA_VIEWPORT_16_9 : AURORA_VIEWPORT_STRETCH);
VIUnlockAspectRatio();
}
return;
}
if (g_policyDirty) {
VIUnlockAspectRatio();
g_policyDirty = false;
}
AuroraSetViewportPolicy(g_forceAspect169 ? AURORA_VIEWPORT_16_9 : AURORA_VIEWPORT_STRETCH);
ApplyEggScreenRecords(g_forceAspect169 ? 16u : surfaceWidth,
g_forceAspect169 ? 9u : surfaceHeight);
}

void SetMkwForceAspect169(bool enabled) {
g_requestedForceAspect169.store(enabled, std::memory_order_release);
ApplyEggScreenRecords(surfaceWidth, surfaceHeight);
}

bool MkwForceAspect169Requested() {
return g_requestedForceAspect169.load(std::memory_order_acquire);
void SetMkwForceAspect(bool enabled) {
g_requestedForceAspect.store(enabled, std::memory_order_release);
}

void ConfigureMkwDynamicAspect(bool widescreen, bool forceAspect169, uint32_t surfaceWidth, uint32_t surfaceHeight) {
g_widescreenSetting = widescreen;
g_widescreenConfigured = widescreen || forceAspect169;
g_forceAspect169 = forceAspect169;
g_requestedForceAspect169.store(forceAspect169, std::memory_order_relaxed);
g_dynamicAspectRatioEnabled = g_widescreenConfigured;
void ConfigureMkwDynamicAspect(bool widescreen, bool forceAspect, uint32_t surfaceWidth, uint32_t surfaceHeight) {
g_widescreen = widescreen;
g_forceAspect = forceAspect;
g_requestedForceAspect.store(forceAspect, std::memory_order_relaxed);
g_policyDirty = true;
g_dynamicAspectRatioEnabled = true;
g_lastEggWidth43 = 0;
g_lastEggWidth169 = 0;
if (g_widescreenConfigured) {
VIUnlockAspectRatio();
ApplyEggScreenRecords(forceAspect169 ? 16u : surfaceWidth,
forceAspect169 ? 9u : surfaceHeight);
return;
}

AuroraSetViewportPolicy(AURORA_VIEWPORT_FIT);
VILockAspectRatio(4, 3);
ApplyEggScreenRecords(surfaceWidth, surfaceHeight);
}
36 changes: 20 additions & 16 deletions runtime/src/hle/gx/gx_dynamic_aspect.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
namespace MkwDynamicAspect {

inline constexpr float kPresentationAspect169 = 16.0f / 9.0f;
inline constexpr float kPresentationAspect43 = 4.0f / 3.0f;
inline constexpr float kEggFramebufferWidth = 608.0f;
inline constexpr uint32_t kEggScreenHeight = 456u;
inline constexpr uint32_t kEggRecordWidth43 = 608u;
Expand All @@ -31,32 +32,35 @@ inline float SurfaceAspect(uint32_t width, uint32_t height) noexcept {
static_cast<float>(width) / static_cast<float>(height));
}

// surfaceAspect / (16:9). Above 1 the surface is wider than 16:9, below 1 it
// is tighter (4:3, portrait).
inline float AspectRatioFactor(uint32_t width, uint32_t height) noexcept {
return SurfaceAspect(width, height) / kPresentationAspect169;
// surfaceAspect / referenceAspect. Above 1 the surface is wider than the
// reference, below 1 it is tighter (4:3, portrait).
inline float AspectRatioFactor(uint32_t width, uint32_t height,
float referenceAspect = kPresentationAspect169) noexcept {
return SurfaceAspect(width, height) / referenceAspect;
}

// Hor+ factor, clamped at 1 so a tighter-than-16:9 surface never narrows the
// canvas (that would crop away scene the 16:9 view showed).
inline float HorizontalExpansion(uint32_t width, uint32_t height) noexcept {
return std::max(1.0f, AspectRatioFactor(width, height));
// Hor+ factor, clamped at 1 so a tighter surface never narrows the canvas
// (that would crop away scene the reference view showed).
inline float HorizontalExpansion(uint32_t width, uint32_t height,
float referenceAspect = kPresentationAspect169) noexcept {
return std::max(1.0f, AspectRatioFactor(width, height, referenceAspect));
}

// Vert+ factor, clamped at 1 so a wider-than-16:9 surface never shortens the
// frustum. Exactly one of the two expansions is ever greater than 1.
inline float VerticalExpansion(uint32_t width, uint32_t height) noexcept {
const float factor = AspectRatioFactor(width, height);
// Vert+ factor, clamped at 1 so a wider surface never shortens the frustum.
// Exactly one of the two expansions is ever greater than 1.
inline float VerticalExpansion(uint32_t width, uint32_t height,
float referenceAspect = kPresentationAspect169) noexcept {
const float factor = AspectRatioFactor(width, height, referenceAspect);
if (!(factor > 0.0f)) {
return 1.0f;
}
return std::clamp(1.0f / factor, 1.0f, kMaxVerticalExpansion);
}

inline uint32_t EggRecordWidth(uint32_t baseWidth, uint32_t surfaceWidth,
uint32_t surfaceHeight) noexcept {
const float scaledWidth =
static_cast<float>(baseWidth) * HorizontalExpansion(surfaceWidth, surfaceHeight);
inline uint32_t EggRecordWidth(uint32_t baseWidth, uint32_t surfaceWidth, uint32_t surfaceHeight,
float referenceAspect = kPresentationAspect169) noexcept {
const float scaledWidth = static_cast<float>(baseWidth) *
HorizontalExpansion(surfaceWidth, surfaceHeight, referenceAspect);
return std::clamp<uint32_t>(
static_cast<uint32_t>(std::lround(scaledWidth / 4.0f)) * 4u, 4u, 0xFFFCu);
}
Expand Down
2 changes: 1 addition & 1 deletion runtime/src/hle/sc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ PPC_NATIVE_OVERRIDE(801B0220, SCCheckStatus_HLE, uint32_t, (), ());
// Returns: 0 = 4:3, 1 = 16:9
extern "C" uint32_t SCGetAspectRatio_HLE()
{
return (RuntimeConfigFile::WidescreenEnabled(true) || MkwForceAspect169Requested()) ? 1u : 0u;
return RuntimeConfigFile::WidescreenEnabled(true) ? 1u : 0u;
}

PPC_NATIVE_OVERRIDE(801B1BE4, SCGetAspectRatio_HLE, uint32_t, (), ());
Expand Down
15 changes: 8 additions & 7 deletions runtime/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1391,9 +1391,10 @@ int RuntimeMain(int argc, char** argv) {
auroraConfig.cachePath = auroraCachePath.c_str();
auroraConfig.logCallback = &RuntimeAuroraLogCallback;
auroraConfig.logLevel = LOG_DEBUG;
RuntimeConfigFile::MigrateLegacyForceAspect();
const bool configWidescreen = RuntimeConfigFile::WidescreenEnabled(true);
const bool forceAspect169 = RuntimeConfigFile::ForceAspect169Enabled();
auroraConfig.windowWidth = (configWidescreen || forceAspect169) ? 854 : 640;
const bool forceAspect = RuntimeConfigFile::ForceAspectEnabled();
auroraConfig.windowWidth = configWidescreen ? 854 : 640;
auroraConfig.windowHeight = 480;
auroraConfig.windowWidth = RuntimeConfigFile::WindowWidth(auroraConfig.windowWidth);
auroraConfig.windowHeight = RuntimeConfigFile::WindowHeight(auroraConfig.windowHeight);
Expand All @@ -1412,7 +1413,7 @@ int RuntimeMain(int argc, char** argv) {
auroraConfig.vsync = RuntimeConfigFile::Vsync();
auroraConfig.desiredBackend = BACKEND_AUTO;
const float resolutionMultiplier = RuntimeConfigFile::ResolutionMultiplier(1.0f);
ConfigureMkwDynamicAspect(configWidescreen, forceAspect169,
ConfigureMkwDynamicAspect(configWidescreen, forceAspect,
auroraConfig.windowWidth, auroraConfig.windowHeight);
VISetFrameBufferScale(resolutionMultiplier);
// One table for both directions. RuntimeConfigFile::IsSupportedGraphicsApi
Expand Down Expand Up @@ -1483,15 +1484,15 @@ int RuntimeMain(int argc, char** argv) {
aurora_set_skip_unready_pipelines(false);
}
RT_LOG(RT_TAG_CONFIG) << "video.widescreen=" << (configWidescreen ? "true" : "false")
<< " force_16_9=" << (forceAspect169 ? "true" : "false")
<< " SCGetAspectRatio=" << (configWidescreen || forceAspect169 ? 1 : 0)
<< " force_aspect=" << (forceAspect ? "true" : "false")
<< " SCGetAspectRatio=" << (configWidescreen ? 1 : 0)
<< " resolutionMultiplier=" << resolutionMultiplier
<< " window=" << auroraInfo.windowSize.width << "x" << auroraInfo.windowSize.height
<< " native=" << auroraInfo.windowSize.native_fb_width << "x"
<< auroraInfo.windowSize.native_fb_height
<< " viewportPolicy=" << (forceAspect169 ? "16:9" : (configWidescreen ? "stretch" : "fit"))
<< " viewportPolicy=" << (forceAspect ? (configWidescreen ? "16:9" : "fit") : "stretch")
<< " presentAspect="
<< (forceAspect169 ? "16:9" : (configWidescreen ? "surface (dynamic EGG canvas)" : "4:3"))
<< (forceAspect ? (configWidescreen ? "16:9" : "4:3") : "surface (dynamic EGG canvas)")
<< std::endl;
g_auroraInitialized.store(true, std::memory_order_release);
GxWorker::SetWaitCallback(ServiceGuestTimingDuringAuroraFrameWait);
Expand Down
22 changes: 17 additions & 5 deletions runtime/src/settings_overlay.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,8 @@ bool g_skipUnreadyPipelines = RuntimeConfigFile::SkipUnreadyPipelines(true);
bool g_vsync = RuntimeConfigFile::Vsync();
bool g_disableCopyFilter = RuntimeConfigFile::DisableCopyFilter(true);
bool g_showFps = RuntimeConfigFile::ShowFps(true);
bool g_forceAspect169 = RuntimeConfigFile::ForceAspect169Enabled();
bool g_forceAspect = RuntimeConfigFile::ForceAspectEnabled();
bool g_widescreen = RuntimeConfigFile::WidescreenEnabled(true);
#if defined(__APPLE__)
bool g_metalFxSpatialUpscaling = RuntimeConfigFile::MetalFxSpatialUpscaling(false);
#endif
Expand Down Expand Up @@ -1080,11 +1081,22 @@ void DrawAudioSettings() {

void DrawGraphicsSettings() {
g_displayMode = static_cast<int>(aurora_get_display_mode());
if (ImGui::Checkbox("Force 16:9", &g_forceAspect169)) {
SetMkwForceAspect169(g_forceAspect169);
RuntimeConfigFile::SetForceAspect169(g_forceAspect169);
if (ImGui::BeginCombo("Aspect ratio (restart required)", g_widescreen ? "16:9" : "4:3")) {
if (ImGui::Selectable("16:9", g_widescreen)) {
g_widescreen = true;
RuntimeConfigFile::SetWidescreen(true);
}
if (ImGui::Selectable("4:3", !g_widescreen)) {
g_widescreen = false;
RuntimeConfigFile::SetWidescreen(false);
}
ImGui::EndCombo();
}
if (ImGui::Checkbox("Force aspect ratio", &g_forceAspect)) {
SetMkwForceAspect(g_forceAspect);
RuntimeConfigFile::SetForceAspect(g_forceAspect);
}
ImGui::TextDisabled("Keep a 16:9 image with black bars when the window has another shape.");
ImGui::TextDisabled("Keep the current aspect ratio with black bars when the window has another shape.");
ImGui::Separator();
struct EffectFlag {
const char* label;
Expand Down
14 changes: 14 additions & 0 deletions runtime/tests/runtime_config_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,20 @@ int main() {
const RuntimeUserConfig config = RuntimeConfigFile::ParseConfig(input, "invalid.toml");
if (!Require(!config.metalFxSpatialUpscaling, "invalid MetalFX setting was accepted")) return 1;
}
{
std::istringstream input("[video]\nwidescreen = false\nforce_16_9 = true\n");
const RuntimeUserConfig config = RuntimeConfigFile::ParseConfig(input, "legacy.toml");
if (!Require(config.forceAspect == std::optional<bool>{true} &&
config.widescreen == std::optional<bool>{true} && config.legacyForceAspect,
"legacy force_16_9 did not force a 16:9 game")) return 1;
}
{
std::istringstream input("[video]\nwidescreen = false\nforce_aspect = true\nforce_16_9 = true\n");
const RuntimeUserConfig config = RuntimeConfigFile::ParseConfig(input, "migrated.toml");
if (!Require(config.forceAspect == std::optional<bool>{true} &&
config.widescreen == std::optional<bool>{false} && !config.legacyForceAspect,
"legacy force_16_9 overrode force_aspect")) return 1;
}
if (!SetsFps("[video]\nshow_fps = false\n", "bare key was not replaced")) return 1;
if (!SetsFps("[video]\n\"show_fps\" = false\n", "basic-quoted key was not replaced")) return 1;
if (!SetsFps("[video]\n'show_fps'=false\n", "literal-quoted key was not replaced")) return 1;
Expand Down
Loading