Compare commits

..

6 Commits

Author SHA1 Message Date
PavelBARABANOV 84ed9692f9 change cmake for android 2026-10-24 07:46:05 +03:00
PavelBARABANOV 9cdc391275 Revert "android cmake no found"
This reverts commit f0ffc497a8.
2026-10-23 19:11:01 +03:00
PavelBARABANOV f0ffc497a8 android cmake no found 2026-10-23 19:00:59 +03:00
PavelBARABANOV 128ecaf55a lic 2026-10-23 18:12:03 +03:00
PavelBARABANOV ed98b1127b Fix aarch builds 2026-10-23 18:05:45 +03:00
John 989768aa46 Fix [core, gpu, threads] Multithreading refactor (#4254) (#4548)
Performance loss regression fixed in Link's Awakening, Castlevania, Super Mario RPG, etc

Tested by Mr. Knobs on Android and myself on Linux

Multicore ON may be fixed for some users using Intel CPUs.
Games include: FF Tactics, Shredder's Revenge, Unity Engine Games.

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md).
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4548
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Reviewed-by: lizzie <lizzie@eden-emu.dev>
2026-10-19 15:58:04 +02:00
5 changed files with 57 additions and 81 deletions
+1 -1
View File
@@ -259,7 +259,7 @@ android {
externalNativeBuild {
cmake {
version = "3.31.6"
version = "3.31.5"
path = file("${edenDir}/CMakeLists.txt")
}
}
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden 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 */
@@ -5,9 +8,22 @@
#define __ASSEMBLY__
// Newer Linux UAPI headers check __ASSEMBLER__ instead of __ASSEMBLY__ to hide C declarations.
// Without it, asm-generic/signal.h redeclares sigset_t and clashes with glibc's. Only define it
// around these includes, since glibc headers also look at __ASSEMBLER__.
#ifndef __ASSEMBLER__
#define __ASSEMBLER__
#define EDEN_NCE_UNDEF_ASSEMBLER
#endif
#include <asm-generic/signal.h>
#include <asm-generic/unistd.h>
#ifdef EDEN_NCE_UNDEF_ASSEMBLER
#undef __ASSEMBLER__
#undef EDEN_NCE_UNDEF_ASSEMBLER
#endif
#define ReturnToRunCodeByExceptionLevelChangeSignal SIGUSR2
#define BreakFromRunCodeSignal SIGURG
#define GuestAccessFaultSignal SIGSEGV
@@ -57,8 +57,21 @@ u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, Display& display,
// Set default speed limit to 100%.
*out_speed_scale = 1.0f;
// If no layers are visible, skip the logic. Acquiring and holding buffers here would
// consume the producer's queue without ever presenting.
bool any_visible = false;
for (auto& layer : display.stack.layers) {
if (layer->visible) {
any_visible = true;
break;
}
}
if (!any_visible) {
return 1;
}
// Wait for the previous composite, bounding the number of in-flight composites.
nvdisp.WaitForComposite();
this->ReleaseFramebuffersLocked(display);
// Determine the number of vsync periods to wait before composing again.
std::optional<s32> swap_interval{};
@@ -152,6 +165,13 @@ u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, Display& display,
// Advance by 1 frame (60 FPS compositing)
m_frame_number += 1;
// Release framebuffers once the frame has been handed to the compositor. This happens
// within the same vsync period as the acquire (release_frame_number is checked against
// the advanced counter), so the producer gets its buffers back immediately instead of
// losing a queue slot for a whole frame.
// TODO: support release fences; releasing before the composite has finished can tear.
this->ReleaseFramebuffersLocked(display);
return 1;
}
@@ -1,12 +1,8 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <functional>
#include <optional>
#include <type_traits>
#include <queue>
@@ -17,10 +13,9 @@
namespace Shader::IR {
template <typename Pred, typename Resolve = std::identity>
auto BreadthFirstSearch(Value value, Pred&& pred, Resolve&& resolve = {})
template <typename Pred>
auto BreadthFirstSearch(const Value& value, Pred&& pred)
-> std::invoke_result_t<Pred, const Inst*> {
value = resolve(value);
if (value.IsImmediate()) {
// Nothing to do with immediates
return std::nullopt;
@@ -41,7 +36,7 @@ auto BreadthFirstSearch(Value value, Pred&& pred, Resolve&& resolve = {})
}
// Visit the right most arguments first
for (size_t arg = inst->NumArgs(); arg--;) {
const Value arg_value{resolve(inst->Arg(arg))};
const Value arg_value{inst->Arg(arg)};
if (arg_value.IsImmediate()) {
continue;
}
+16 -71
View File
@@ -31,60 +31,6 @@ struct TextureInst {
using TextureInstVector = boost::container::small_vector<TextureInst, 24>;
const IR::Block* ConditionalHeader(const IR::Block* body) {
const auto predecessors{body->ImmPredecessors()};
const auto successors{body->ImmSuccessors()};
if (predecessors.size() != 1 || successors.size() != 1) return nullptr;
const IR::Block* const header{predecessors[0]};
const auto targets{header->ImmSuccessors()};
if (targets.size() != 2 || targets[0] != body || targets[1] != successors[0] ||
targets[1]->ImmPredecessors().size() != 2 || header->empty() ||
header->back().GetOpcode() != IR::Opcode::ConditionRef) {
return nullptr;
}
return header;
}
std::pair<IR::Value, bool> Condition(IR::Value value) {
bool negated{};
while (const IR::Inst* inst = value.TryInstRecursive()) {
if (inst->GetOpcode() == IR::Opcode::LogicalNot) negated = !negated;
else if (inst->GetOpcode() != IR::Opcode::ConditionRef) break;
value = inst->Arg(0);
}
return {value.Resolve(), negated};
}
IR::Value ResolveConditionalPhi(IR::Value value, const IR::Block* block) {
if (!value.Resolve().IsPhi() || !(block = ConditionalHeader(block))) return value;
const auto [condition, negated]{Condition(block->back().Arg(0))};
boost::container::small_vector<const IR::Block*, 16> visited;
while (value.Resolve().IsPhi() && std::ranges::find(visited, block) == visited.end()) {
visited.push_back(block);
const IR::Inst* const phi{value.InstRecursive()};
const auto predecessors{block->ImmPredecessors()};
if (predecessors.size() == 1) {
block = predecessors[0];
continue;
}
if (predecessors.size() != 2 || phi->NumArgs() != 2) break;
const IR::Block* body{predecessors[0]};
const IR::Block* header{ConditionalHeader(body)};
if (header != predecessors[1]) {
body = predecessors[1];
header = ConditionalHeader(body);
if (header != predecessors[0]) break;
}
const auto [other_condition, other_negated]{Condition(header->back().Arg(0))};
if (condition == other_condition && (phi->PhiBlock(0) == header || phi->PhiBlock(0) == body)) {
const IR::Block* const incoming{negated == other_negated ? body : header};
value = phi->Arg(phi->PhiBlock(0) == incoming ? 0 : 1);
}
block = header;
}
return value;
}
constexpr u32 DESCRIPTOR_SIZE = 8;
constexpr u32 DESCRIPTOR_SIZE_SHIFT = u32(std::countr_zero(DESCRIPTOR_SIZE));
constexpr u32 DESCRIPTOR_MAX_COUNT = 1024;
@@ -353,23 +299,23 @@ static inline bool IsTexturePixelFormatIntegerCached(Environment& env,
}
std::optional<ConstBufferAddr> Track(const IR::Value& value, Environment& env, const HostTranslateInfo& host_info, const IR::Block* block);
static inline std::optional<ConstBufferAddr> TrackCached(const IR::Value& v, Environment& env, const HostTranslateInfo& host_info, const IR::Block* block) {
if (const IR::Inst* key = v.TryInstRecursive()) {
std::optional<ConstBufferAddr> Track(const IR::Value& value, Environment& env, const HostTranslateInfo& host_info);
static inline std::optional<ConstBufferAddr> TrackCached(const IR::Value& v, Environment& env, const HostTranslateInfo& host_info) {
if (const IR::Inst* key = v.InstRecursive()) {
if (auto it = env.track_cache.find(key); it != env.track_cache.end()) return it->second;
auto found = Track(v, env, host_info, block);
auto found = Track(v, env, host_info);
if (found) env.track_cache.emplace(key, *found);
return found;
}
return Track(v, env, host_info, block);
return Track(v, env, host_info);
}
std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environment& env, const HostTranslateInfo& host_info, const IR::Block* block);
std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environment& env, const HostTranslateInfo& host_info);
std::optional<ConstBufferAddr> Track(const IR::Value& value, Environment& env, const HostTranslateInfo& host_info, const IR::Block* block) {
return IR::BreadthFirstSearch(value, [&env, &host_info, block](const IR::Inst* inst) {
return TryGetConstBuffer(inst, env, host_info, block);
}, [&](IR::Value operand) { return ResolveConditionalPhi(operand, block); });
std::optional<ConstBufferAddr> Track(const IR::Value& value, Environment& env, const HostTranslateInfo& host_info) {
return IR::BreadthFirstSearch(value, [&env, &host_info](const IR::Inst* inst) {
return TryGetConstBuffer(inst, env, host_info);
});
}
std::optional<u32> TryGetConstant(IR::Value& value, Environment& env) {
@@ -393,13 +339,13 @@ std::optional<u32> TryGetConstant(IR::Value& value, Environment& env) {
return ReadCbufCached(env, index_number, offset_number);
}
std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environment& env, const HostTranslateInfo& host_info, const IR::Block* block) {
std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environment& env, const HostTranslateInfo& host_info) {
switch (inst->GetOpcode()) {
default:
return std::nullopt;
case IR::Opcode::BitwiseOr32: {
std::optional lhs{TrackCached(inst->Arg(0), env, host_info, block)};
std::optional rhs{TrackCached(inst->Arg(1), env, host_info, block)};
std::optional lhs{TrackCached(inst->Arg(0), env, host_info)};
std::optional rhs{TrackCached(inst->Arg(1), env, host_info)};
if (!lhs || !rhs) {
return std::nullopt;
}
@@ -429,7 +375,7 @@ std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environme
if (!shift.IsImmediate()) {
return std::nullopt;
}
std::optional lhs{TrackCached(inst->Arg(0), env, host_info, block)};
std::optional lhs{TrackCached(inst->Arg(0), env, host_info)};
if (lhs) {
lhs->shift_left = shift.U32();
}
@@ -457,7 +403,7 @@ std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environme
return std::nullopt;
} while (false);
}
std::optional lhs{TrackCached(op1, env, host_info, block)};
std::optional lhs{TrackCached(op1, env, host_info)};
if (lhs) {
lhs->shift_left = static_cast<u32>(std::countr_zero(op2.U32()));
}
@@ -523,7 +469,7 @@ std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environme
TextureInst MakeInst(Environment& env, IR::Block* block, IR::Inst& inst, const HostTranslateInfo& host_info) {
ConstBufferAddr addr;
if (IsBindless(inst)) {
const std::optional<ConstBufferAddr> track_addr{TrackCached(inst.Arg(0), env, host_info, block)};
const std::optional<ConstBufferAddr> track_addr{TrackCached(inst.Arg(0), env, host_info)};
if (!track_addr) {
throw NotImplementedException("Failed to track bindless texture constant buffer");
@@ -725,7 +671,6 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
env.track_cache.clear();
TextureInstVector to_replace;
for (IR::Block* const block : program.post_order_blocks) {
env.track_cache.clear();
for (IR::Inst& inst : block->Instructions()) {
if (!IsTextureInstruction(inst)) {
continue;