mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2025-10-05 15:52:45 +02:00
Merge branch 'master' of eden:eden-emu/eden into netconnect
This commit is contained in:
@@ -512,6 +512,8 @@ if (APPLE)
|
||||
# Umbrella framework for everything GUI-related
|
||||
find_library(COCOA_LIBRARY Cocoa)
|
||||
set(PLATFORM_LIBRARIES ${COCOA_LIBRARY} ${IOKIT_LIBRARY} ${COREVIDEO_LIBRARY})
|
||||
find_library(ICONV_LIBRARY iconv REQUIRED)
|
||||
list(APPEND PLATFORM_LIBRARIES ${ICONV_LIBRARY})
|
||||
elseif (WIN32)
|
||||
# Target Windows 10
|
||||
add_definitions(-D_WIN32_WINNT=0x0A00 -DWINVER=0x0A00)
|
||||
|
BIN
dist/eden.icns
vendored
Normal file
BIN
dist/eden.icns
vendored
Normal file
Binary file not shown.
@@ -127,7 +127,7 @@ android {
|
||||
applicationIdSuffix = ".relWithDebInfo"
|
||||
isJniDebuggable = true
|
||||
}
|
||||
|
||||
|
||||
// Signed by debug key disallowing distribution on Play Store.
|
||||
// Attaches 'debug' suffix to version and package name, allowing installation alongside the release build.
|
||||
debug {
|
||||
@@ -140,14 +140,22 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
flavorDimensions.add("version")
|
||||
productFlavors {
|
||||
create("mainline") {
|
||||
isDefault = true
|
||||
dimension = "version"
|
||||
android {
|
||||
flavorDimensions.add("version")
|
||||
productFlavors {
|
||||
create("mainline") {
|
||||
dimension = "version"
|
||||
// No need to set applicationId here
|
||||
}
|
||||
|
||||
create("genshinSpoof") {
|
||||
dimension = "version"
|
||||
applicationId = "com.miHoYo.Yuanshen" // Correct use of applicationId inside the flavor block
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
version = "3.22.1"
|
||||
|
@@ -220,6 +220,7 @@ class SettingsFragmentPresenter(
|
||||
|
||||
private fun addGraphicsSettings(sl: ArrayList<SettingsItem>) {
|
||||
sl.apply {
|
||||
// TODO(crueter): reorganize this, this is awful
|
||||
add(IntSetting.RENDERER_ACCURACY.key)
|
||||
add(IntSetting.RENDERER_RESOLUTION.key)
|
||||
add(IntSetting.RENDERER_VSYNC.key)
|
||||
|
@@ -250,6 +250,7 @@
|
||||
<item>@string/scaling_filter_gaussian</item>
|
||||
<item>@string/scaling_filter_scale_force</item>
|
||||
<item>@string/scaling_filter_fsr</item>
|
||||
<item>@string/scaling_filter_area</item>
|
||||
</string-array>
|
||||
|
||||
<integer-array name="rendererScalingFilterValues">
|
||||
@@ -259,6 +260,7 @@
|
||||
<item>3</item>
|
||||
<item>4</item>
|
||||
<item>5</item>
|
||||
<item>6</item>
|
||||
</integer-array>
|
||||
|
||||
<string-array name="rendererAntiAliasingNames">
|
||||
|
@@ -863,6 +863,7 @@
|
||||
<string name="scaling_filter_gaussian">Gaussian</string>
|
||||
<string name="scaling_filter_scale_force">ScaleForce</string>
|
||||
<string name="scaling_filter_fsr">AMD FidelityFX™ Super Resolution</string>
|
||||
<string name="scaling_filter_area">Area</string>
|
||||
|
||||
<!-- Anti-Aliasing -->
|
||||
<string name="anti_aliasing_none">None</string>
|
||||
|
@@ -1,3 +1,9 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 Torzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -158,7 +164,7 @@ ENUM(ResolutionSetup,
|
||||
Res7X,
|
||||
Res8X);
|
||||
|
||||
ENUM(ScalingFilter, NearestNeighbor, Bilinear, Bicubic, Gaussian, ScaleForce, Fsr, MaxEnum);
|
||||
ENUM(ScalingFilter, NearestNeighbor, Bilinear, Bicubic, Gaussian, ScaleForce, Fsr, Area, MaxEnum);
|
||||
|
||||
ENUM(AntiAliasing, None, Fxaa, Smaa, MaxEnum);
|
||||
|
||||
|
@@ -1,136 +1,187 @@
|
||||
#pragma once
|
||||
// SPDX-FileCopyrightText: 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
#include <list>
|
||||
#include <unordered_map>
|
||||
#include <optional>
|
||||
#include <shared_mutex>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
|
||||
template<typename KeyType, typename ValueType>
|
||||
template <typename KeyType, typename ValueType>
|
||||
class LRUCache {
|
||||
private:
|
||||
bool enabled = true;
|
||||
size_t capacity;
|
||||
std::list<KeyType> cache_list;
|
||||
std::unordered_map<KeyType, std::pair<typename std::list<KeyType>::iterator, ValueType>> cache_map;
|
||||
|
||||
public:
|
||||
explicit LRUCache(size_t capacity, bool enabled = true) : enabled(enabled), capacity(capacity) {
|
||||
cache_map.reserve(capacity);
|
||||
LOG_WARNING(Core, "LRU Cache initialized with state: {}", enabled ? "enabled" : "disabled");
|
||||
using key_type = KeyType;
|
||||
using value_type = ValueType;
|
||||
using size_type = std::size_t;
|
||||
|
||||
struct Statistics {
|
||||
size_type hits = 0;
|
||||
size_type misses = 0;
|
||||
void reset() noexcept { hits = misses = 0; }
|
||||
};
|
||||
|
||||
explicit LRUCache(size_type capacity, bool enabled = true)
|
||||
: enabled_{enabled}, capacity_{capacity} {
|
||||
cache_map_.reserve(capacity_);
|
||||
LOG_WARNING(Core, "LRU Cache initialised (state: {} | capacity: {})", enabled_ ? "enabled" : "disabled", capacity_);
|
||||
}
|
||||
|
||||
// Returns pointer to value if found, nullptr otherwise
|
||||
ValueType* get(const KeyType& key) {
|
||||
if (!enabled) return nullptr;
|
||||
// Non-movable copy semantics
|
||||
LRUCache(const LRUCache&) = delete;
|
||||
LRUCache& operator=(const LRUCache&) = delete;
|
||||
LRUCache(LRUCache&& other) noexcept { *this = std::move(other); }
|
||||
LRUCache& operator=(LRUCache&& other) noexcept {
|
||||
if (this == &other) return *this;
|
||||
std::unique_lock this_lock(mutex_, std::defer_lock);
|
||||
std::unique_lock other_lock(other.mutex_, std::defer_lock);
|
||||
std::lock(this_lock, other_lock);
|
||||
enabled_ = other.enabled_;
|
||||
capacity_ = other.capacity_;
|
||||
cache_list_ = std::move(other.cache_list_);
|
||||
cache_map_ = std::move(other.cache_map_);
|
||||
stats_ = other.stats_;
|
||||
return *this;
|
||||
}
|
||||
~LRUCache() = default;
|
||||
|
||||
auto it = cache_map.find(key);
|
||||
if (it == cache_map.end()) {
|
||||
[[nodiscard]] value_type* get(const key_type& key) {
|
||||
if (!enabled_) [[unlikely]] return nullptr;
|
||||
std::unique_lock lock(mutex_);
|
||||
auto it = cache_map_.find(key);
|
||||
if (it == cache_map_.end()) {
|
||||
++stats_.misses;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Move the accessed item to the front of the list (most recently used)
|
||||
cache_list.splice(cache_list.begin(), cache_list, it->second.first);
|
||||
return &(it->second.second);
|
||||
move_to_front(it);
|
||||
++stats_.hits;
|
||||
return &it->second.second;
|
||||
}
|
||||
|
||||
// Returns pointer to value if found (without promoting it), nullptr otherwise
|
||||
ValueType* peek(const KeyType& key) const {
|
||||
if (!enabled) return nullptr;
|
||||
|
||||
auto it = cache_map.find(key);
|
||||
return it != cache_map.end() ? &(it->second.second) : nullptr;
|
||||
[[nodiscard]] value_type* peek(const key_type& key) const {
|
||||
if (!enabled_) [[unlikely]] return nullptr;
|
||||
std::shared_lock lock(mutex_);
|
||||
auto it = cache_map_.find(key);
|
||||
return it == cache_map_.end() ? nullptr : &it->second.second;
|
||||
}
|
||||
|
||||
// Inserts or updates a key-value pair
|
||||
void put(const KeyType& key, const ValueType& value) {
|
||||
if (!enabled) return;
|
||||
template <typename V>
|
||||
void put(const key_type& key, V&& value) {
|
||||
if (!enabled_) [[unlikely]] return;
|
||||
std::unique_lock lock(mutex_);
|
||||
insert_or_update(key, std::forward<V>(value));
|
||||
}
|
||||
|
||||
auto it = cache_map.find(key);
|
||||
|
||||
if (it != cache_map.end()) {
|
||||
// Key exists, update value and move to front
|
||||
it->second.second = value;
|
||||
cache_list.splice(cache_list.begin(), cache_list, it->second.first);
|
||||
return;
|
||||
template <typename ValueFactory>
|
||||
value_type& get_or_emplace(const key_type& key, ValueFactory&& factory) {
|
||||
std::unique_lock lock(mutex_);
|
||||
auto it = cache_map_.find(key);
|
||||
if (it != cache_map_.end()) {
|
||||
move_to_front(it);
|
||||
return it->second.second;
|
||||
}
|
||||
|
||||
// Remove the least recently used item if cache is full
|
||||
if (cache_map.size() >= capacity) {
|
||||
auto last = cache_list.back();
|
||||
cache_map.erase(last);
|
||||
cache_list.pop_back();
|
||||
}
|
||||
|
||||
// Insert new item at the front
|
||||
cache_list.push_front(key);
|
||||
cache_map[key] = {cache_list.begin(), value};
|
||||
value_type new_value = factory();
|
||||
insert_or_update(key, std::move(new_value));
|
||||
return cache_map_.find(key)->second.second;
|
||||
}
|
||||
|
||||
// Enable or disable the LRU cache
|
||||
void setEnabled(bool state) {
|
||||
enabled = state;
|
||||
LOG_WARNING(Core, "LRU Cache state changed to: {}", state ? "enabled" : "disabled");
|
||||
if (!enabled) {
|
||||
clear();
|
||||
}
|
||||
[[nodiscard]] bool contains(const key_type& key) const {
|
||||
if (!enabled_) return false;
|
||||
std::shared_lock lock(mutex_);
|
||||
return cache_map_.find(key) != cache_map_.end();
|
||||
}
|
||||
|
||||
// Check if the cache is enabled
|
||||
bool isEnabled() const {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
// Attempts to get value, returns std::nullopt if not found
|
||||
std::optional<ValueType> try_get(const KeyType& key) {
|
||||
auto* val = get(key);
|
||||
return val ? std::optional<ValueType>(*val) : std::nullopt;
|
||||
}
|
||||
|
||||
// Checks if key exists in cache
|
||||
bool contains(const KeyType& key) const {
|
||||
if (!enabled) return false;
|
||||
return cache_map.find(key) != cache_map.end();
|
||||
}
|
||||
|
||||
// Removes a key from the cache if it exists
|
||||
bool erase(const KeyType& key) {
|
||||
if (!enabled) return false;
|
||||
|
||||
auto it = cache_map.find(key);
|
||||
if (it == cache_map.end()) {
|
||||
return false;
|
||||
}
|
||||
cache_list.erase(it->second.first);
|
||||
cache_map.erase(it);
|
||||
bool erase(const key_type& key) {
|
||||
if (!enabled_) return false;
|
||||
std::unique_lock lock(mutex_);
|
||||
auto it = cache_map_.find(key);
|
||||
if (it == cache_map_.end()) return false;
|
||||
cache_list_.erase(it->second.first);
|
||||
cache_map_.erase(it);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Removes all elements from the cache
|
||||
void clear() {
|
||||
cache_map.clear();
|
||||
cache_list.clear();
|
||||
std::unique_lock lock(mutex_);
|
||||
cache_list_.clear();
|
||||
cache_map_.clear();
|
||||
stats_.reset();
|
||||
}
|
||||
|
||||
// Returns current number of elements in cache
|
||||
size_t size() const {
|
||||
return enabled ? cache_map.size() : 0;
|
||||
[[nodiscard]] size_type size() const {
|
||||
if (!enabled_) return 0;
|
||||
std::shared_lock lock(mutex_);
|
||||
return cache_map_.size();
|
||||
}
|
||||
|
||||
// Returns maximum capacity of cache
|
||||
size_t get_capacity() const {
|
||||
return capacity;
|
||||
[[nodiscard]] size_type get_capacity() const { return capacity_; }
|
||||
|
||||
void resize(size_type new_capacity) {
|
||||
if (!enabled_) return;
|
||||
std::unique_lock lock(mutex_);
|
||||
capacity_ = new_capacity;
|
||||
shrink_if_needed();
|
||||
cache_map_.reserve(capacity_);
|
||||
}
|
||||
|
||||
// Resizes the cache, evicting LRU items if new capacity is smaller
|
||||
void resize(size_t new_capacity) {
|
||||
if (!enabled) return;
|
||||
void setEnabled(bool state) {
|
||||
std::unique_lock lock(mutex_);
|
||||
enabled_ = state;
|
||||
LOG_WARNING(Core, "LRU Cache state changed to: {}", state ? "enabled" : "disabled");
|
||||
if (!enabled_) clear();
|
||||
}
|
||||
|
||||
capacity = new_capacity;
|
||||
while (cache_map.size() > capacity) {
|
||||
auto last = cache_list.back();
|
||||
cache_map.erase(last);
|
||||
cache_list.pop_back();
|
||||
[[nodiscard]] bool isEnabled() const { return enabled_; }
|
||||
|
||||
[[nodiscard]] Statistics stats() const {
|
||||
std::shared_lock lock(mutex_);
|
||||
return stats_;
|
||||
}
|
||||
|
||||
private:
|
||||
using list_type = std::list<key_type>;
|
||||
using list_iterator = typename list_type::iterator;
|
||||
using map_value_type = std::pair<list_iterator, value_type>;
|
||||
using map_type = std::unordered_map<key_type, map_value_type>;
|
||||
|
||||
template <typename V>
|
||||
void insert_or_update(const key_type& key, V&& value) {
|
||||
auto it = cache_map_.find(key);
|
||||
if (it != cache_map_.end()) {
|
||||
it->second.second = std::forward<V>(value);
|
||||
move_to_front(it);
|
||||
return;
|
||||
}
|
||||
cache_map.reserve(capacity);
|
||||
// evict LRU if full
|
||||
if (cache_map_.size() >= capacity_) {
|
||||
const auto& lru_key = cache_list_.back();
|
||||
cache_map_.erase(lru_key);
|
||||
cache_list_.pop_back();
|
||||
}
|
||||
cache_list_.push_front(key);
|
||||
cache_map_[key] = {cache_list_.begin(), std::forward<V>(value)};
|
||||
}
|
||||
|
||||
void move_to_front(typename map_type::iterator it) {
|
||||
cache_list_.splice(cache_list_.begin(), cache_list_, it->second.first);
|
||||
it->second.first = cache_list_.begin();
|
||||
}
|
||||
|
||||
void shrink_if_needed() {
|
||||
while (cache_map_.size() > capacity_) {
|
||||
const auto& lru_key = cache_list_.back();
|
||||
cache_map_.erase(lru_key);
|
||||
cache_list_.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
mutable std::shared_mutex mutex_;
|
||||
bool enabled_{true};
|
||||
size_type capacity_;
|
||||
list_type cache_list_;
|
||||
map_type cache_map_;
|
||||
mutable Statistics stats_;
|
||||
};
|
@@ -1,5 +1,5 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
// SPDX-FileCopyrightText: 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "common/arm64/native_clock.h"
|
||||
#include "common/bit_cast.h"
|
||||
@@ -14,6 +14,23 @@
|
||||
|
||||
namespace Core::NCE {
|
||||
|
||||
Patcher::Patcher(Patcher&& other) noexcept
|
||||
: patch_cache(std::move(other.patch_cache)),
|
||||
m_patch_instructions(std::move(other.m_patch_instructions)),
|
||||
c(m_patch_instructions),
|
||||
m_save_context(other.m_save_context),
|
||||
m_load_context(other.m_load_context),
|
||||
mode(other.mode),
|
||||
total_program_size(other.total_program_size),
|
||||
m_relocate_module_index(other.m_relocate_module_index),
|
||||
modules(std::move(other.modules)),
|
||||
curr_patch(nullptr) {
|
||||
if (!modules.empty()) {
|
||||
curr_patch = &modules.back();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
using namespace Common::Literals;
|
||||
using namespace oaknut::util;
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
// SPDX-FileCopyrightText: 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "core/hle/kernel/k_typed_address.h"
|
||||
#include "core/hle/kernel/physical_memory.h"
|
||||
#include "lru_cache.h"
|
||||
#include <utility>
|
||||
|
||||
namespace Core::NCE {
|
||||
|
||||
@@ -30,6 +31,10 @@ using EntryTrampolines = std::unordered_map<ModuleTextAddress, PatchTextAddress>
|
||||
|
||||
class Patcher {
|
||||
public:
|
||||
Patcher(const Patcher&) = delete;
|
||||
Patcher& operator=(const Patcher&) = delete;
|
||||
Patcher(Patcher&& other) noexcept;
|
||||
Patcher& operator=(Patcher&&) noexcept = delete;
|
||||
explicit Patcher();
|
||||
~Patcher();
|
||||
|
||||
@@ -62,7 +67,7 @@ private:
|
||||
void WriteCntpctHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg);
|
||||
|
||||
private:
|
||||
static constexpr size_t CACHE_SIZE = 4096; // Cache size for patch entries
|
||||
static constexpr size_t CACHE_SIZE = 16384; // Cache size for patch entries
|
||||
LRUCache<uintptr_t, PatchTextAddress> patch_cache{CACHE_SIZE, Settings::values.lru_cache_enabled.GetValue()};
|
||||
|
||||
void BranchToPatch(uintptr_t module_dest) {
|
||||
@@ -70,7 +75,7 @@ private:
|
||||
LOG_DEBUG(Core_ARM, "LRU cache lookup for address {:#x}", module_dest);
|
||||
// Try to get existing patch entry from cache
|
||||
if (auto* cached_patch = patch_cache.get(module_dest)) {
|
||||
LOG_DEBUG(Core_ARM, "LRU cache hit for address {:#x}", module_dest);
|
||||
LOG_WARNING(Core_ARM, "LRU cache hit for address {:#x}", module_dest);
|
||||
curr_patch->m_branch_to_patch_relocations.push_back({c.offset(), *cached_patch});
|
||||
return;
|
||||
}
|
||||
|
@@ -48,7 +48,7 @@ IHidServer::IHidServer(Core::System& system_, std::shared_ptr<ResourceManager> r
|
||||
{1, C<&IHidServer::ActivateDebugPad>, "ActivateDebugPad"},
|
||||
{11, C<&IHidServer::ActivateTouchScreen>, "ActivateTouchScreen"},
|
||||
{21, C<&IHidServer::ActivateMouse>, "ActivateMouse"},
|
||||
{26, nullptr, "ActivateDebugMouse"},
|
||||
{26, C<&IHidServer::ActivateDebugMouse>, "ActivateDebugMouse"},
|
||||
{31, C<&IHidServer::ActivateKeyboard>, "ActivateKeyboard"},
|
||||
{32, C<&IHidServer::SendKeyboardLockKeyEvent>, "SendKeyboardLockKeyEvent"},
|
||||
{40, C<&IHidServer::AcquireXpadIdEventHandle>, "AcquireXpadIdEventHandle"},
|
||||
@@ -234,6 +234,11 @@ Result IHidServer::ActivateMouse(ClientAppletResourceUserId aruid) {
|
||||
R_RETURN(GetResourceManager()->GetMouse()->Activate(aruid.pid));
|
||||
}
|
||||
|
||||
Result IHidServer::ActivateDebugMouse(ClientAppletResourceUserId aruid) {
|
||||
LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", aruid.pid);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IHidServer::ActivateKeyboard(ClientAppletResourceUserId aruid) {
|
||||
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", aruid.pid);
|
||||
|
||||
|
@@ -37,6 +37,7 @@ private:
|
||||
Result ActivateDebugPad(ClientAppletResourceUserId aruid);
|
||||
Result ActivateTouchScreen(ClientAppletResourceUserId aruid);
|
||||
Result ActivateMouse(ClientAppletResourceUserId aruid);
|
||||
Result ActivateDebugMouse(ClientAppletResourceUserId aruid);
|
||||
Result ActivateKeyboard(ClientAppletResourceUserId aruid);
|
||||
Result SendKeyboardLockKeyEvent(u32 flags);
|
||||
Result AcquireXpadIdEventHandle(OutCopyHandle<Kernel::KReadableEvent> out_event,
|
||||
|
@@ -42,6 +42,7 @@ set(SHADER_FILES
|
||||
opengl_present_scaleforce.frag
|
||||
opengl_smaa.glsl
|
||||
pitch_unswizzle.comp
|
||||
present_area.frag
|
||||
present_bicubic.frag
|
||||
present_gaussian.frag
|
||||
queries_prefix_scan_sum.comp
|
||||
|
107
src/video_core/host_shaders/present_area.frag
Normal file
107
src/video_core/host_shaders/present_area.frag
Normal file
@@ -0,0 +1,107 @@
|
||||
#version 460 core
|
||||
|
||||
layout(location = 0) in vec2 frag_tex_coord;
|
||||
layout(location = 0) out vec4 color;
|
||||
layout(binding = 0) uniform sampler2D color_texture;
|
||||
|
||||
#ifdef VULKAN
|
||||
|
||||
struct ScreenRectVertex {
|
||||
vec2 position;
|
||||
vec2 tex_coord;
|
||||
};
|
||||
layout (push_constant) uniform PushConstants {
|
||||
mat4 modelview_matrix;
|
||||
ScreenRectVertex vertices[4];
|
||||
};
|
||||
|
||||
#else // OpenGL
|
||||
|
||||
layout(location = 1) uniform uvec2 screen_size;
|
||||
|
||||
#endif
|
||||
|
||||
/***** Area Sampling *****/
|
||||
|
||||
// By Sam Belliveau and Filippo Tarpini. Public Domain license.
|
||||
// Effectively a more accurate sharp bilinear filter when upscaling,
|
||||
// that also works as a mathematically perfect downscale filter.
|
||||
// https://entropymine.com/imageworsener/pixelmixing/
|
||||
// https://github.com/obsproject/obs-studio/pull/1715
|
||||
// https://legacy.imagemagick.org/Usage/filter/
|
||||
vec4 AreaSampling(sampler2D textureSampler, vec2 texCoords, vec2 source_size, vec2 target_size) {
|
||||
// Determine the sizes of the source and target images.
|
||||
vec2 inverted_target_size = vec2(1.0) / target_size;
|
||||
|
||||
// Determine the range of the source image that the target pixel will cover.
|
||||
vec2 range = source_size * inverted_target_size;
|
||||
vec2 beg = (texCoords.xy * source_size) - (range * 0.5);
|
||||
vec2 end = beg + range;
|
||||
|
||||
// Compute the top-left and bottom-right corners of the pixel box.
|
||||
ivec2 f_beg = ivec2(floor(beg));
|
||||
ivec2 f_end = ivec2(floor(end));
|
||||
|
||||
// Compute how much of the start and end pixels are covered horizontally & vertically.
|
||||
float area_w = 1.0 - fract(beg.x);
|
||||
float area_n = 1.0 - fract(beg.y);
|
||||
float area_e = fract(end.x);
|
||||
float area_s = fract(end.y);
|
||||
|
||||
// Compute the areas of the corner pixels in the pixel box.
|
||||
float area_nw = area_n * area_w;
|
||||
float area_ne = area_n * area_e;
|
||||
float area_sw = area_s * area_w;
|
||||
float area_se = area_s * area_e;
|
||||
|
||||
// Initialize the color accumulator.
|
||||
vec4 avg_color = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
|
||||
// Accumulate corner pixels.
|
||||
avg_color += area_nw * texelFetch(textureSampler, ivec2(f_beg.x, f_beg.y), 0);
|
||||
avg_color += area_ne * texelFetch(textureSampler, ivec2(f_end.x, f_beg.y), 0);
|
||||
avg_color += area_sw * texelFetch(textureSampler, ivec2(f_beg.x, f_end.y), 0);
|
||||
avg_color += area_se * texelFetch(textureSampler, ivec2(f_end.x, f_end.y), 0);
|
||||
|
||||
// Determine the size of the pixel box.
|
||||
int x_range = int(f_end.x - f_beg.x - 0.5);
|
||||
int y_range = int(f_end.y - f_beg.y - 0.5);
|
||||
|
||||
// Accumulate top and bottom edge pixels.
|
||||
for (int x = f_beg.x + 1; x <= f_beg.x + x_range; ++x) {
|
||||
avg_color += area_n * texelFetch(textureSampler, ivec2(x, f_beg.y), 0);
|
||||
avg_color += area_s * texelFetch(textureSampler, ivec2(x, f_end.y), 0);
|
||||
}
|
||||
|
||||
// Accumulate left and right edge pixels and all the pixels in between.
|
||||
for (int y = f_beg.y + 1; y <= f_beg.y + y_range; ++y) {
|
||||
avg_color += area_w * texelFetch(textureSampler, ivec2(f_beg.x, y), 0);
|
||||
avg_color += area_e * texelFetch(textureSampler, ivec2(f_end.x, y), 0);
|
||||
|
||||
for (int x = f_beg.x + 1; x <= f_beg.x + x_range; ++x) {
|
||||
avg_color += texelFetch(textureSampler, ivec2(x, y), 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the area of the pixel box that was sampled.
|
||||
float area_corners = area_nw + area_ne + area_sw + area_se;
|
||||
float area_edges = float(x_range) * (area_n + area_s) + float(y_range) * (area_w + area_e);
|
||||
float area_center = float(x_range) * float(y_range);
|
||||
|
||||
// Return the normalized average color.
|
||||
return avg_color / (area_corners + area_edges + area_center);
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 source_image_size = textureSize(color_texture, 0);
|
||||
vec2 window_size;
|
||||
|
||||
#ifdef VULKAN
|
||||
window_size.x = vertices[1].position.x - vertices[0].position.x;
|
||||
window_size.y = vertices[2].position.y - vertices[0].position.y;
|
||||
#else // OpenGL
|
||||
window_size = screen_size;
|
||||
#endif
|
||||
|
||||
color = AreaSampling(color_texture, frag_tex_coord, source_image_size, window_size);
|
||||
}
|
@@ -1,3 +1,9 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 Torzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -86,6 +92,9 @@ void BlitScreen::CreateWindowAdapt() {
|
||||
case Settings::ScalingFilter::ScaleForce:
|
||||
window_adapt = MakeScaleForce(device);
|
||||
break;
|
||||
case Settings::ScalingFilter::Area:
|
||||
window_adapt = MakeArea(device);
|
||||
break;
|
||||
case Settings::ScalingFilter::Fsr:
|
||||
case Settings::ScalingFilter::Bilinear:
|
||||
default:
|
||||
|
@@ -1,8 +1,15 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 Torzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "video_core/host_shaders/opengl_present_frag.h"
|
||||
#include "video_core/host_shaders/opengl_present_scaleforce_frag.h"
|
||||
#include "video_core/host_shaders/present_area_frag.h"
|
||||
#include "video_core/host_shaders/present_bicubic_frag.h"
|
||||
#include "video_core/host_shaders/present_gaussian_frag.h"
|
||||
#include "video_core/renderer_opengl/present/filters.h"
|
||||
@@ -36,4 +43,9 @@ std::unique_ptr<WindowAdaptPass> MakeScaleForce(const Device& device) {
|
||||
fmt::format("#version 460\n{}", HostShaders::OPENGL_PRESENT_SCALEFORCE_FRAG));
|
||||
}
|
||||
|
||||
std::unique_ptr<WindowAdaptPass> MakeArea(const Device& device) {
|
||||
return std::make_unique<WindowAdaptPass>(device, CreateBilinearSampler(),
|
||||
HostShaders::PRESENT_AREA_FRAG);
|
||||
}
|
||||
|
||||
} // namespace OpenGL
|
||||
|
@@ -1,3 +1,9 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 Torzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -13,5 +19,6 @@ std::unique_ptr<WindowAdaptPass> MakeBilinear(const Device& device);
|
||||
std::unique_ptr<WindowAdaptPass> MakeBicubic(const Device& device);
|
||||
std::unique_ptr<WindowAdaptPass> MakeGaussian(const Device& device);
|
||||
std::unique_ptr<WindowAdaptPass> MakeScaleForce(const Device& device);
|
||||
std::unique_ptr<WindowAdaptPass> MakeArea(const Device& device);
|
||||
|
||||
} // namespace OpenGL
|
||||
|
@@ -1,3 +1,9 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 Torzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -10,6 +16,7 @@ namespace OpenGL {
|
||||
constexpr GLint PositionLocation = 0;
|
||||
constexpr GLint TexCoordLocation = 1;
|
||||
constexpr GLint ModelViewMatrixLocation = 0;
|
||||
constexpr GLint ScreenSizeLocation = 1;
|
||||
|
||||
struct ScreenRectVertex {
|
||||
constexpr ScreenRectVertex() = default;
|
||||
|
@@ -1,3 +1,9 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 Torzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -110,6 +116,9 @@ void WindowAdaptPass::DrawToFramebuffer(ProgramManager& program_manager, std::li
|
||||
glBindTextureUnit(0, textures[i]);
|
||||
glProgramUniformMatrix3x2fv(vert.handle, ModelViewMatrixLocation, 1, GL_FALSE,
|
||||
matrices[i].data());
|
||||
glProgramUniform2ui(frag.handle, ScreenSizeLocation,
|
||||
static_cast<GLuint>(layout.screen.GetWidth()),
|
||||
static_cast<GLuint>(layout.screen.GetHeight()));
|
||||
glNamedBufferSubData(vertex_buffer.handle, 0, sizeof(vertices[i]), std::data(vertices[i]));
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
}
|
||||
|
@@ -1,8 +1,15 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 Torzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
#include "video_core/host_shaders/present_area_frag_spv.h"
|
||||
#include "video_core/host_shaders/present_bicubic_frag_spv.h"
|
||||
#include "video_core/host_shaders/present_gaussian_frag_spv.h"
|
||||
#include "video_core/host_shaders/vulkan_present_frag_spv.h"
|
||||
@@ -53,4 +60,9 @@ std::unique_ptr<WindowAdaptPass> MakeScaleForce(const Device& device, VkFormat f
|
||||
SelectScaleForceShader(device));
|
||||
}
|
||||
|
||||
std::unique_ptr<WindowAdaptPass> MakeArea(const Device& device, VkFormat frame_format) {
|
||||
return std::make_unique<WindowAdaptPass>(device, frame_format, CreateBilinearSampler(device),
|
||||
BuildShader(device, PRESENT_AREA_FRAG_SPV));
|
||||
}
|
||||
|
||||
} // namespace Vulkan
|
||||
|
@@ -1,3 +1,9 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 Torzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -14,5 +20,6 @@ std::unique_ptr<WindowAdaptPass> MakeBilinear(const Device& device, VkFormat fra
|
||||
std::unique_ptr<WindowAdaptPass> MakeBicubic(const Device& device, VkFormat frame_format);
|
||||
std::unique_ptr<WindowAdaptPass> MakeGaussian(const Device& device, VkFormat frame_format);
|
||||
std::unique_ptr<WindowAdaptPass> MakeScaleForce(const Device& device, VkFormat frame_format);
|
||||
std::unique_ptr<WindowAdaptPass> MakeArea(const Device& device, VkFormat frame_format);
|
||||
|
||||
} // namespace Vulkan
|
||||
|
@@ -1,3 +1,9 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 Torzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -43,6 +49,9 @@ void BlitScreen::SetWindowAdaptPass() {
|
||||
case Settings::ScalingFilter::ScaleForce:
|
||||
window_adapt = MakeScaleForce(device, swapchain_view_format);
|
||||
break;
|
||||
case Settings::ScalingFilter::Area:
|
||||
window_adapt = MakeArea(device, swapchain_view_format);
|
||||
break;
|
||||
case Settings::ScalingFilter::Fsr:
|
||||
case Settings::ScalingFilter::Bilinear:
|
||||
default:
|
||||
|
@@ -366,7 +366,7 @@ target_sources(yuzu
|
||||
)
|
||||
|
||||
if (APPLE)
|
||||
set(MACOSX_ICON "../../dist/yuzu.icns")
|
||||
set(MACOSX_ICON "../../dist/eden.icns")
|
||||
set_source_files_properties(${MACOSX_ICON} PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
|
||||
target_sources(yuzu PRIVATE ${MACOSX_ICON})
|
||||
set_target_properties(yuzu PROPERTIES MACOSX_BUNDLE TRUE)
|
||||
@@ -374,7 +374,7 @@ if (APPLE)
|
||||
|
||||
if (NOT USE_SYSTEM_MOLTENVK)
|
||||
set(MOLTENVK_PLATFORM "macOS")
|
||||
set(MOLTENVK_VERSION "v1.2.7")
|
||||
set(MOLTENVK_VERSION "v1.3.0")
|
||||
download_moltenvk_external(${MOLTENVK_PLATFORM} ${MOLTENVK_VERSION})
|
||||
endif()
|
||||
find_library(MOLTENVK_LIBRARY MoltenVK REQUIRED)
|
||||
|
@@ -1,3 +1,8 @@
|
||||
<!--
|
||||
SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
-->
|
||||
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!--
|
||||
@@ -15,7 +20,7 @@ SPDX-License-Identifier: GPL-2.0-or-later
|
||||
<key>CFBundleGetInfoString</key>
|
||||
<string></string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>yuzu.icns</string>
|
||||
<string>eden.icns</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.yuzu-emu.yuzu</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
@@ -23,7 +28,7 @@ SPDX-License-Identifier: GPL-2.0-or-later
|
||||
<key>CFBundleLongVersionString</key>
|
||||
<string></string>
|
||||
<key>CFBundleName</key>
|
||||
<string>yuzu</string>
|
||||
<string>eden</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
|
@@ -1,3 +1,9 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 Torzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -536,6 +542,7 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent)
|
||||
PAIR(ScalingFilter, Gaussian, tr("Gaussian")),
|
||||
PAIR(ScalingFilter, ScaleForce, tr("ScaleForce")),
|
||||
PAIR(ScalingFilter, Fsr, tr("AMD FidelityFX™️ Super Resolution")),
|
||||
PAIR(ScalingFilter, Area, tr("Area")),
|
||||
}});
|
||||
translations->insert({Settings::EnumMetadata<Settings::AntiAliasing>::Index(),
|
||||
{
|
||||
|
@@ -1,3 +1,9 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 Torzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -40,6 +46,7 @@ static const std::map<Settings::ScalingFilter, QString> scaling_filter_texts_map
|
||||
{Settings::ScalingFilter::ScaleForce,
|
||||
QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "ScaleForce"))},
|
||||
{Settings::ScalingFilter::Fsr, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "FSR"))},
|
||||
{Settings::ScalingFilter::Area, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Area"))},
|
||||
};
|
||||
|
||||
static const std::map<Settings::ConsoleMode, QString> use_docked_mode_texts_map = {
|
||||
|
Reference in New Issue
Block a user