Compare commits

..

41 Commits

Author SHA1 Message Date
tech-ticks
8ad712b044 hle: kernel: Invalidate icache in UnmapProcessMemory (fixes #8174) 2022-04-08 21:31:56 +02:00
bunnei
37199c5f90 Merge pull request #8162 from german77/bombslinger
service: hid: Partially revert #8123
2022-04-05 21:15:38 -07:00
german77
8c089f4e2a service: hid: Partially revert #8123 2022-04-05 22:35:38 -05:00
bunnei
12dc4d0527 Merge pull request #8137 from bunnei/improve-nvflinger-2
Follow-up fixes for NVFlinger rewrite (Part 2)
2022-04-05 19:11:28 -07:00
bunnei
0c1b954e07 Merge pull request #8100 from Morph1984/registered-crash
registered_cache: Prevent nullptr dereference when accumulating files
2022-04-05 18:18:41 -07:00
Mai M
e4c3565ebe Merge pull request #8159 from merryhime/pst
dynarmic: Print stack trace on unrecognised instruction or other exception
2022-04-05 17:18:31 -04:00
Mai M
9af501b75d Merge pull request #8158 from Tachi107/patch-1
build: remove -fconcepts
2022-04-05 17:17:22 -04:00
merry
7f11710e0a dynarmic: Print stack trace on unrecognised instruction or other exception 2022-04-05 20:40:20 +01:00
Andrea Pappacoda
ce859cf526 build: remove -fconcepts
It was needed on GCC versions not supporting `-std=c++20`, but GCC 10 and newer (required to compile yuzu) don't need it anymore
2022-04-05 20:52:11 +02:00
bunnei
119d1692c9 Merge pull request #8156 from yuzu-emu/revert-8154-unneeded-read-block
Revert "texture_cache/util: Remove unneeded ReadBlockUnsafe"
2022-04-04 16:27:13 -07:00
bunnei
02473ea7d5 Revert "texture_cache/util: Remove unneeded ReadBlockUnsafe" 2022-04-04 16:26:53 -07:00
bunnei
c7f3c2cedf Merge pull request #8154 from ameerj/unneeded-read-block
texture_cache/util: Remove unneeded ReadBlockUnsafe
2022-04-04 13:46:44 -07:00
ameerj
494c41dd5a texture_cache/util: Remove unneeded ReadBlockUnsafe
This call was reading GPU memory into the dst buffer, which is then overwritten by the SwizzleTexture call.
2022-04-04 15:57:54 -04:00
bunnei
f114436120 Merge pull request #8089 from merryhime/paranoia
configuration: Add Paranoid CPU accuracy level
2022-04-04 11:07:38 -07:00
bunnei
e9cf2d43f1 Merge pull request #8105 from merryhime/atomicload128
native_clock: Use AtomicLoad128
2022-04-03 14:26:28 -07:00
bunnei
2d1af6beee Merge pull request #8135 from Morph1984/websession-hack
applets/web: Keep foreground (websession) web applet open
2022-04-02 20:49:51 -07:00
bunnei
b5910ad0ba Merge pull request #8123 from german77/bombslinger
service: hid: Remove inaccurate behavior on initialization
2022-04-02 20:12:45 -07:00
german77
3e5469a974 service: npad: Default initialize shared memory 2022-04-02 19:43:49 -06:00
merry
979e53b87b native_clock: Use writeback from CAS to avoid double-loading 2022-04-02 22:22:48 +01:00
merry
084bd225dc atomic_ops: Implement AtomicCompareAndSwap with writeback 2022-04-02 22:22:48 +01:00
Merry
c562c1d6be native_clock: Use AtomicLoad128 2022-04-02 20:55:36 +01:00
Merry
b4746529e1 atomic_ops: Implement AtomicLoad128 2022-04-02 20:55:36 +01:00
merry
7f1e66e94b Merge pull request #8134 from Tachi107/remove-time-stretcher
audio_core: remove time stretcher
2022-04-02 20:49:21 +01:00
Morph
1e47252214 Merge pull request #8141 from merryhime/configure-hotkeys-columns
configure_hotkeys: Make first column stretch and not last column
2022-04-02 15:35:31 -04:00
Morph
0bcbe3a703 Merge pull request #8140 from merryhime/per-game-addon-columns
configure_per_game_addons: Stretch first column and not last
2022-04-02 15:35:26 -04:00
Morph
1cb83c91a6 Merge pull request #8142 from Tachi107/typos
fix: typos
2022-04-02 15:35:14 -04:00
merry
f4004b1271 configure_per_game_addons: Set tree view minimum section size to 150px 2022-04-02 17:19:25 +01:00
merry
8c57de1605 configure_hotkeys: Make first column stretch and not last column
Also configure minimum width of columns to be 150px.
2022-04-02 17:17:13 +01:00
Andrea Pappacoda
0c214cb5b9 fix: typos 2022-04-02 16:34:07 +02:00
merry
8dc1913db8 configure_per_game_addons: Stretch first column and not last
This provides more sensible column widths.
2022-04-02 13:46:16 +01:00
bunnei
fdf4909f97 hle: service: nvflinger: buffer_queue_producer: Cleanup & fixes. 2022-04-01 22:59:35 -07:00
bunnei
4036e37bbe hle: service: nvflinger: consumer_base: Cleanup & fixes. 2022-04-01 22:58:40 -07:00
bunnei
30b07878ba hle: service: nvflinger: buffer_queue_producer: Cleanup & add GetReleasedBuffers. 2022-04-01 22:58:02 -07:00
bunnei
7610554b1e hle: service: nvflinger: buffer_queue_core: Cleanup & fixes. 2022-04-01 22:56:32 -07:00
bunnei
f9371f36a4 hle: service: nvflinger: Use correct logger namespace. 2022-04-01 22:55:44 -07:00
Morph
1166c3910d applets/web: Keep foreground (websession) web applet open
This is a hack to keep the foreground (websession) web applet open in games using these such as Super Mario 3D All-Stars.
2022-04-01 22:50:01 -04:00
Andrea Pappacoda
faf6a9876c audio_core: remove time stretcher
Also drop the SoundTouch dependency
2022-04-01 19:54:58 +02:00
german77
9346f0b33d service: hid: Remove inaccurate behavior on initialization 2022-03-31 12:35:57 -06:00
Morph
ea7a0d4652 registered_cache: Prevent nullptr dereference when accumulating files
For whatever reason, nca_file/dir can be nullptr in the list of files/dirs. I have not determined the cause of this yet, so add a nullptr check for these prior to dereferencing them.
2022-03-27 17:06:27 -04:00
merry
94967e0f6d configure_cpu: More descriptive text for Paranoid option 2022-03-26 08:56:23 +00:00
merry
3c8547160d configuration: Add Paranoid CPU accuracy level
Disables most optimizations for the paranoid.
2022-03-26 08:46:25 +00:00
42 changed files with 393 additions and 390 deletions

3
.gitmodules vendored
View File

@@ -7,9 +7,6 @@
[submodule "dynarmic"]
path = externals/dynarmic
url = https://github.com/MerryMage/dynarmic.git
[submodule "soundtouch"]
path = externals/soundtouch
url = https://github.com/citra-emu/ext-soundtouch.git
[submodule "libressl"]
path = externals/libressl
url = https://github.com/citra-emu/ext-libressl-portable.git

View File

@@ -68,9 +68,6 @@ if (YUZU_USE_EXTERNAL_SDL2)
add_library(SDL2 ALIAS SDL2-static)
endif()
# SoundTouch
add_subdirectory(soundtouch)
# Cubeb
if(ENABLE_CUBEB)
set(BUILD_TESTS OFF CACHE BOOL "")

Submodule externals/soundtouch deleted from 060181eaf2

View File

@@ -103,12 +103,6 @@ else()
-Wno-unused-parameter
)
# TODO: Remove when we update to a GCC compiler that enables this
# by default (i.e. GCC 10 or newer).
if (CMAKE_CXX_COMPILER_ID STREQUAL GNU)
add_compile_options(-fconcepts)
endif()
if (ARCHITECTURE_x86_64)
add_compile_options("-mcx16")
endif()

View File

@@ -36,8 +36,6 @@ add_library(audio_core STATIC
splitter_context.h
stream.cpp
stream.h
time_stretch.cpp
time_stretch.h
voice_context.cpp
voice_context.h
@@ -63,7 +61,6 @@ if (NOT MSVC)
endif()
target_link_libraries(audio_core PUBLIC common core)
target_link_libraries(audio_core PRIVATE SoundTouch)
if(ENABLE_CUBEB)
target_link_libraries(audio_core PRIVATE cubeb)

View File

@@ -7,7 +7,6 @@
#include <cstring>
#include "audio_core/cubeb_sink.h"
#include "audio_core/stream.h"
#include "audio_core/time_stretch.h"
#include "common/assert.h"
#include "common/logging/log.h"
#include "common/ring_buffer.h"
@@ -23,8 +22,7 @@ class CubebSinkStream final : public SinkStream {
public:
CubebSinkStream(cubeb* ctx_, u32 sample_rate, u32 num_channels_, cubeb_devid output_device,
const std::string& name)
: ctx{ctx_}, num_channels{std::min(num_channels_, 6u)}, time_stretch{sample_rate,
num_channels} {
: ctx{ctx_}, num_channels{std::min(num_channels_, 6u)} {
cubeb_stream_params params{};
params.rate = sample_rate;
@@ -131,7 +129,6 @@ private:
Common::RingBuffer<s16, 0x10000> queue;
std::array<s16, 2> last_frame{};
std::atomic<bool> should_flush{};
TimeStretcher time_stretch;
static long DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer,
void* output_buffer, long num_frames);
@@ -205,25 +202,7 @@ long CubebSinkStream::DataCallback([[maybe_unused]] cubeb_stream* stream, void*
const std::size_t num_channels = impl->GetNumChannels();
const std::size_t samples_to_write = num_channels * num_frames;
std::size_t samples_written;
/*
if (Settings::values.enable_audio_stretching.GetValue()) {
const std::vector<s16> in{impl->queue.Pop()};
const std::size_t num_in{in.size() / num_channels};
s16* const out{reinterpret_cast<s16*>(buffer)};
const std::size_t out_frames =
impl->time_stretch.Process(in.data(), num_in, out, num_frames);
samples_written = out_frames * num_channels;
if (impl->should_flush) {
impl->time_stretch.Flush();
impl->should_flush = false;
}
} else {
samples_written = impl->queue.Pop(buffer, samples_to_write);
}*/
samples_written = impl->queue.Pop(buffer, samples_to_write);
const std::size_t samples_written = impl->queue.Pop(buffer, samples_to_write);
if (samples_written >= num_channels) {
std::memcpy(&impl->last_frame[0], buffer + (samples_written - num_channels) * sizeof(s16),

View File

@@ -7,7 +7,6 @@
#include <cstring>
#include "audio_core/sdl2_sink.h"
#include "audio_core/stream.h"
#include "audio_core/time_stretch.h"
#include "common/assert.h"
#include "common/logging/log.h"
//#include "common/settings.h"
@@ -27,7 +26,7 @@ namespace AudioCore {
class SDLSinkStream final : public SinkStream {
public:
SDLSinkStream(u32 sample_rate, u32 num_channels_, const std::string& output_device)
: num_channels{std::min(num_channels_, 6u)}, time_stretch{sample_rate, num_channels} {
: num_channels{std::min(num_channels_, 6u)} {
SDL_AudioSpec spec;
spec.freq = sample_rate;
@@ -116,7 +115,6 @@ private:
SDL_AudioDeviceID dev = 0;
u32 num_channels{};
std::atomic<bool> should_flush{};
TimeStretcher time_stretch;
};
SDLSink::SDLSink(std::string_view target_device_name) {

View File

@@ -1,68 +0,0 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <cmath>
#include <cstddef>
#include "audio_core/time_stretch.h"
#include "common/logging/log.h"
namespace AudioCore {
TimeStretcher::TimeStretcher(u32 sample_rate, u32 channel_count) : m_sample_rate{sample_rate} {
m_sound_touch.setChannels(channel_count);
m_sound_touch.setSampleRate(sample_rate);
m_sound_touch.setPitch(1.0);
m_sound_touch.setTempo(1.0);
}
void TimeStretcher::Clear() {
m_sound_touch.clear();
}
void TimeStretcher::Flush() {
m_sound_touch.flush();
}
std::size_t TimeStretcher::Process(const s16* in, std::size_t num_in, s16* out,
std::size_t num_out) {
const double time_delta = static_cast<double>(num_out) / m_sample_rate; // seconds
// We were given actual_samples number of samples, and num_samples were requested from us.
double current_ratio = static_cast<double>(num_in) / static_cast<double>(num_out);
const double max_latency = 0.25; // seconds
const double max_backlog = m_sample_rate * max_latency;
const double backlog_fullness = m_sound_touch.numSamples() / max_backlog;
if (backlog_fullness > 4.0) {
// Too many samples in backlog: Don't push anymore on
num_in = 0;
}
// We ideally want the backlog to be about 50% full.
// This gives some headroom both ways to prevent underflow and overflow.
// We tweak current_ratio to encourage this.
constexpr double tweak_time_scale = 0.05; // seconds
const double tweak_correction = (backlog_fullness - 0.5) * (time_delta / tweak_time_scale);
current_ratio *= std::pow(1.0 + 2.0 * tweak_correction, tweak_correction < 0 ? 3.0 : 1.0);
// This low-pass filter smoothes out variance in the calculated stretch ratio.
// The time-scale determines how responsive this filter is.
constexpr double lpf_time_scale = 0.712; // seconds
const double lpf_gain = 1.0 - std::exp(-time_delta / lpf_time_scale);
m_stretch_ratio += lpf_gain * (current_ratio - m_stretch_ratio);
// Place a lower limit of 5% speed. When a game boots up, there will be
// many silence samples. These do not need to be timestretched.
m_stretch_ratio = std::max(m_stretch_ratio, 0.05);
m_sound_touch.setTempo(m_stretch_ratio);
LOG_TRACE(Audio, "{:5}/{:5} ratio:{:0.6f} backlog:{:0.6f}", num_in, num_out, m_stretch_ratio,
backlog_fullness);
m_sound_touch.putSamples(in, static_cast<u32>(num_in));
return m_sound_touch.receiveSamples(out, static_cast<u32>(num_out));
}
} // namespace AudioCore

View File

@@ -1,34 +0,0 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <cstddef>
#include <SoundTouch.h>
#include "common/common_types.h"
namespace AudioCore {
class TimeStretcher {
public:
TimeStretcher(u32 sample_rate, u32 channel_count);
/// @param in Input sample buffer
/// @param num_in Number of input frames in `in`
/// @param out Output sample buffer
/// @param num_out Desired number of output frames in `out`
/// @returns Actual number of frames written to `out`
std::size_t Process(const s16* in, std::size_t num_in, s16* out, std::size_t num_out);
void Clear();
void Flush();
private:
u32 m_sample_rate;
soundtouch::SoundTouch m_sound_touch;
double m_stretch_ratio = 1.0;
};
} // namespace AudioCore

View File

@@ -46,6 +46,50 @@ namespace Common {
reinterpret_cast<__int64*>(expected.data())) != 0;
}
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected,
u8& actual) {
actual =
_InterlockedCompareExchange8(reinterpret_cast<volatile char*>(pointer), value, expected);
return actual == expected;
}
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected,
u16& actual) {
actual =
_InterlockedCompareExchange16(reinterpret_cast<volatile short*>(pointer), value, expected);
return actual == expected;
}
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected,
u32& actual) {
actual =
_InterlockedCompareExchange(reinterpret_cast<volatile long*>(pointer), value, expected);
return actual == expected;
}
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected,
u64& actual) {
actual = _InterlockedCompareExchange64(reinterpret_cast<volatile __int64*>(pointer), value,
expected);
return actual == expected;
}
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected,
u128& actual) {
const bool result =
_InterlockedCompareExchange128(reinterpret_cast<volatile __int64*>(pointer), value[1],
value[0], reinterpret_cast<__int64*>(expected.data())) != 0;
actual = expected;
return result;
}
[[nodiscard]] inline u128 AtomicLoad128(volatile u64* pointer) {
u128 result{};
_InterlockedCompareExchange128(reinterpret_cast<volatile __int64*>(pointer), result[1],
result[0], reinterpret_cast<__int64*>(result.data()));
return result;
}
#else
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected) {
@@ -72,6 +116,52 @@ namespace Common {
return __sync_bool_compare_and_swap((unsigned __int128*)pointer, expected_a, value_a);
}
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected,
u8& actual) {
actual = __sync_val_compare_and_swap(pointer, expected, value);
return actual == expected;
}
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected,
u16& actual) {
actual = __sync_val_compare_and_swap(pointer, expected, value);
return actual == expected;
}
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected,
u32& actual) {
actual = __sync_val_compare_and_swap(pointer, expected, value);
return actual == expected;
}
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected,
u64& actual) {
actual = __sync_val_compare_and_swap(pointer, expected, value);
return actual == expected;
}
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected,
u128& actual) {
unsigned __int128 value_a;
unsigned __int128 expected_a;
unsigned __int128 actual_a;
std::memcpy(&value_a, value.data(), sizeof(u128));
std::memcpy(&expected_a, expected.data(), sizeof(u128));
actual_a = __sync_val_compare_and_swap((unsigned __int128*)pointer, expected_a, value_a);
std::memcpy(actual.data(), &actual_a, sizeof(u128));
return actual_a == expected_a;
}
[[nodiscard]] inline u128 AtomicLoad128(volatile u64* pointer) {
unsigned __int128 zeros_a = 0;
unsigned __int128 result_a =
__sync_val_compare_and_swap((unsigned __int128*)pointer, zeros_a, zeros_a);
u128 result;
std::memcpy(result.data(), &result_a, sizeof(u128));
return result;
}
#endif
} // namespace Common

View File

@@ -38,6 +38,7 @@ enum class CPUAccuracy : u32 {
Auto = 0,
Accurate = 1,
Unsafe = 2,
Paranoid = 3,
};
enum class FullscreenMode : u32 {
@@ -470,7 +471,7 @@ struct Values {
// Cpu
RangedSetting<CPUAccuracy> cpu_accuracy{CPUAccuracy::Auto, CPUAccuracy::Auto,
CPUAccuracy::Unsafe, "cpu_accuracy"};
CPUAccuracy::Paranoid, "cpu_accuracy"};
// TODO: remove cpu_accuracy_first_time, migration setting added 8 July 2021
BasicSetting<bool> cpu_accuracy_first_time{true, "cpu_accuracy_first_time"};
BasicSetting<bool> cpu_debug_mode{false, "cpu_debug_mode"};

View File

@@ -55,8 +55,9 @@ NativeClock::NativeClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequen
u64 NativeClock::GetRTSC() {
TimePoint new_time_point{};
TimePoint current_time_point{};
current_time_point.pack = Common::AtomicLoad128(time_point.pack.data());
do {
current_time_point.pack = time_point.pack;
_mm_mfence();
const u64 current_measure = __rdtsc();
u64 diff = current_measure - current_time_point.inner.last_measure;
@@ -66,7 +67,7 @@ u64 NativeClock::GetRTSC() {
: current_time_point.inner.last_measure;
new_time_point.inner.accumulated_ticks = current_time_point.inner.accumulated_ticks + diff;
} while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack,
current_time_point.pack));
current_time_point.pack, current_time_point.pack));
/// The clock cannot be more precise than the guest timer, remove the lower bits
return new_time_point.inner.accumulated_ticks & inaccuracy_mask;
}
@@ -75,13 +76,14 @@ void NativeClock::Pause(bool is_paused) {
if (!is_paused) {
TimePoint current_time_point{};
TimePoint new_time_point{};
current_time_point.pack = Common::AtomicLoad128(time_point.pack.data());
do {
current_time_point.pack = time_point.pack;
new_time_point.pack = current_time_point.pack;
_mm_mfence();
new_time_point.inner.last_measure = __rdtsc();
} while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack,
current_time_point.pack));
current_time_point.pack, current_time_point.pack));
}
}

View File

@@ -70,11 +70,13 @@ public:
}
void InterpreterFallback(u32 pc, std::size_t num_instructions) override {
parent.LogBacktrace();
UNIMPLEMENTED_MSG("This should never happen, pc = {:08X}, code = {:08X}", pc,
MemoryReadCode(pc));
}
void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override {
parent.LogBacktrace();
LOG_CRITICAL(Core_ARM,
"ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X}, thumb = {})",
exception, pc, MemoryReadCode(pc), parent.IsInThumbMode());
@@ -186,35 +188,41 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
if (!Settings::values.cpuopt_recompile_exclusives) {
config.recompile_on_exclusive_fastmem_failure = false;
}
}
} else {
// Unsafe optimizations
if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) {
config.unsafe_optimizations = true;
if (Settings::values.cpuopt_unsafe_unfuse_fma) {
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
}
if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
}
if (Settings::values.cpuopt_unsafe_ignore_standard_fpcr) {
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue;
}
if (Settings::values.cpuopt_unsafe_inaccurate_nan) {
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
}
if (Settings::values.cpuopt_unsafe_ignore_global_monitor) {
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
}
}
// Unsafe optimizations
if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) {
config.unsafe_optimizations = true;
if (Settings::values.cpuopt_unsafe_unfuse_fma) {
// Curated optimizations
if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) {
config.unsafe_optimizations = true;
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
}
if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
}
if (Settings::values.cpuopt_unsafe_ignore_standard_fpcr) {
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue;
}
if (Settings::values.cpuopt_unsafe_inaccurate_nan) {
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
}
if (Settings::values.cpuopt_unsafe_ignore_global_monitor) {
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
}
}
// Curated optimizations
if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) {
config.unsafe_optimizations = true;
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue;
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
// Paranoia mode for debugging optimizations
if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Paranoid) {
config.unsafe_optimizations = false;
config.optimizations = Dynarmic::no_optimizations;
}
}
return std::make_unique<Dynarmic::A32::Jit>(config);

View File

@@ -81,6 +81,7 @@ public:
}
void InterpreterFallback(u64 pc, std::size_t num_instructions) override {
parent.LogBacktrace();
LOG_ERROR(Core_ARM,
"Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc,
num_instructions, MemoryReadCode(pc));
@@ -118,6 +119,7 @@ public:
return;
case Dynarmic::A64::Exception::Breakpoint:
default:
parent.LogBacktrace();
ASSERT_MSG(false, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})",
static_cast<std::size_t>(exception), pc, MemoryReadCode(pc));
}
@@ -248,35 +250,41 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
if (!Settings::values.cpuopt_recompile_exclusives) {
config.recompile_on_exclusive_fastmem_failure = false;
}
}
} else {
// Unsafe optimizations
if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) {
config.unsafe_optimizations = true;
if (Settings::values.cpuopt_unsafe_unfuse_fma) {
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
}
if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
}
if (Settings::values.cpuopt_unsafe_inaccurate_nan) {
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
}
if (Settings::values.cpuopt_unsafe_fastmem_check) {
config.fastmem_address_space_bits = 64;
}
if (Settings::values.cpuopt_unsafe_ignore_global_monitor) {
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
}
}
// Unsafe optimizations
if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) {
config.unsafe_optimizations = true;
if (Settings::values.cpuopt_unsafe_unfuse_fma) {
// Curated optimizations
if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) {
config.unsafe_optimizations = true;
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
}
if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
}
if (Settings::values.cpuopt_unsafe_inaccurate_nan) {
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
}
if (Settings::values.cpuopt_unsafe_fastmem_check) {
config.fastmem_address_space_bits = 64;
}
if (Settings::values.cpuopt_unsafe_ignore_global_monitor) {
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
}
}
// Curated optimizations
if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) {
config.unsafe_optimizations = true;
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
config.fastmem_address_space_bits = 64;
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
// Paranoia mode for debugging optimizations
if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Paranoid) {
config.unsafe_optimizations = false;
config.optimizations = Dynarmic::no_optimizations;
}
}
return std::make_shared<Dynarmic::A64::Jit>(config);

View File

@@ -387,15 +387,17 @@ std::vector<NcaID> RegisteredCache::AccumulateFiles() const {
continue;
for (const auto& nca_dir : d2_dir->GetSubdirectories()) {
if (!FollowsNcaIdFormat(nca_dir->GetName()))
if (nca_dir == nullptr || !FollowsNcaIdFormat(nca_dir->GetName())) {
continue;
}
ids.push_back(Common::HexStringToArray<0x10, true>(nca_dir->GetName().substr(0, 0x20)));
}
for (const auto& nca_file : d2_dir->GetFiles()) {
if (!FollowsNcaIdFormat(nca_file->GetName()))
if (nca_file == nullptr || !FollowsNcaIdFormat(nca_file->GetName())) {
continue;
}
ids.push_back(
Common::HexStringToArray<0x10, true>(nca_file->GetName().substr(0, 0x20)));

View File

@@ -563,6 +563,8 @@ ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size,
block_manager->Update(dst_addr, num_pages, KMemoryState::Free, KMemoryPermission::None,
KMemoryAttribute::None);
system.InvalidateCpuInstructionCaches();
return ResultSuccess;
}

View File

@@ -446,6 +446,14 @@ void WebBrowser::ExecuteLogin() {
}
void WebBrowser::ExecuteOffline() {
// TODO (Morph): This is a hack for WebSession foreground web applets such as those used by
// Super Mario 3D All-Stars.
// TODO (Morph): Implement WebSession.
if (applet_mode == LibraryAppletMode::AllForegroundInitiallyHidden) {
LOG_WARNING(Service_AM, "WebSession is not implemented");
return;
}
const auto main_url = GetMainURL(Common::FS::PathToUTF8String(offline_document));
if (!Common::FS::Exists(main_url)) {

View File

@@ -174,7 +174,7 @@ ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_,
ASSERT_MSG(dest != nullptr, "Newly created file with success cannot be found.");
ASSERT_MSG(dest->WriteBytes(src->ReadAllBytes()) == src->GetSize(),
"Could not write all of the bytes but everything else has succeded.");
"Could not write all of the bytes but everything else has succeeded.");
if (!src->GetContainingDirectory()->DeleteFile(Common::FS::GetFilename(src_path))) {
// TODO(DarkLordZach): Find a better error code for this

View File

@@ -262,11 +262,6 @@ void Controller_NPad::OnInit() {
service_context.CreateEvent(fmt::format("npad:NpadStyleSetChanged_{}", i));
}
if (hid_core.GetSupportedStyleTag().raw == Core::HID::NpadStyleSet::None) {
// We want to support all controllers
hid_core.SetSupportedStyleTag({Core::HID::NpadStyleSet::All});
}
supported_npad_id_types.resize(npad_id_list.size());
std::memcpy(supported_npad_id_types.data(), npad_id_list.data(),
npad_id_list.size() * sizeof(Core::HID::NpadIdType));
@@ -288,14 +283,6 @@ void Controller_NPad::OnInit() {
WriteEmptyEntry(npad);
}
}
// Connect controllers
for (auto& controller : controller_data) {
const auto& device = controller.device;
if (device->IsConnected()) {
AddNewControllerAt(device->GetNpadStyleIndex(), device->GetNpadIdType());
}
}
}
void Controller_NPad::WriteEmptyEntry(NpadInternalState& npad) {
@@ -320,6 +307,7 @@ void Controller_NPad::WriteEmptyEntry(NpadInternalState& npad) {
}
void Controller_NPad::OnRelease() {
is_controller_initialized = false;
for (std::size_t i = 0; i < controller_data.size(); ++i) {
auto& controller = controller_data[i];
service_context.CloseEvent(controller.styleset_changed_event);
@@ -651,9 +639,27 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing
void Controller_NPad::SetSupportedStyleSet(Core::HID::NpadStyleTag style_set) {
hid_core.SetSupportedStyleTag(style_set);
if (is_controller_initialized) {
return;
}
// Once SetSupportedStyleSet is called controllers are fully initialized
is_controller_initialized = true;
// Connect all active controllers
for (auto& controller : controller_data) {
const auto& device = controller.device;
if (device->IsConnected()) {
AddNewControllerAt(device->GetNpadStyleIndex(), device->GetNpadIdType());
}
}
}
Core::HID::NpadStyleTag Controller_NPad::GetSupportedStyleSet() const {
if (!is_controller_initialized) {
return {Core::HID::NpadStyleSet::None};
}
return hid_core.GetSupportedStyleTag();
}

View File

@@ -191,16 +191,16 @@ private:
// This is nn::hid::detail::NpadFullKeyColorState
struct NpadFullKeyColorState {
ColorAttribute attribute;
Core::HID::NpadControllerColor fullkey;
ColorAttribute attribute{ColorAttribute::NoController};
Core::HID::NpadControllerColor fullkey{};
};
static_assert(sizeof(NpadFullKeyColorState) == 0xC, "NpadFullKeyColorState is an invalid size");
// This is nn::hid::detail::NpadJoyColorState
struct NpadJoyColorState {
ColorAttribute attribute;
Core::HID::NpadControllerColor left;
Core::HID::NpadControllerColor right;
ColorAttribute attribute{ColorAttribute::NoController};
Core::HID::NpadControllerColor left{};
Core::HID::NpadControllerColor right{};
};
static_assert(sizeof(NpadJoyColorState) == 0x14, "NpadJoyColorState is an invalid size");
@@ -226,11 +226,11 @@ private:
// This is nn::hid::NpadPalmaState
// This is nn::hid::NpadSystemExtState
struct NPadGenericState {
s64_le sampling_number;
Core::HID::NpadButtonState npad_buttons;
Core::HID::AnalogStickState l_stick;
Core::HID::AnalogStickState r_stick;
NpadAttribute connection_status;
s64_le sampling_number{};
Core::HID::NpadButtonState npad_buttons{};
Core::HID::AnalogStickState l_stick{};
Core::HID::AnalogStickState r_stick{};
NpadAttribute connection_status{};
INSERT_PADDING_BYTES(4); // Reserved
};
static_assert(sizeof(NPadGenericState) == 0x28, "NPadGenericState is an invalid size");
@@ -253,7 +253,7 @@ private:
Common::Vec3f gyro{};
Common::Vec3f rotation{};
std::array<Common::Vec3f, 3> orientation{};
SixAxisSensorAttribute attribute;
SixAxisSensorAttribute attribute{};
INSERT_PADDING_BYTES(4); // Reserved
};
static_assert(sizeof(SixAxisSensorState) == 0x60, "SixAxisSensorState is an invalid size");
@@ -325,11 +325,11 @@ private:
// This is nn::hid::detail::NfcXcdDeviceHandleStateImpl
struct NfcXcdDeviceHandleStateImpl {
u64 handle;
bool is_available;
bool is_activated;
u64 handle{};
bool is_available{};
bool is_activated{};
INSERT_PADDING_BYTES(0x6); // Reserved
u64 sampling_number;
u64 sampling_number{};
};
static_assert(sizeof(NfcXcdDeviceHandleStateImpl) == 0x18,
"NfcXcdDeviceHandleStateImpl is an invalid size");
@@ -366,8 +366,8 @@ private:
};
struct AppletFooterUi {
AppletFooterUiAttributes attributes;
AppletFooterUiType type;
AppletFooterUiAttributes attributes{};
AppletFooterUiType type{AppletFooterUiType::None};
INSERT_PADDING_BYTES(0x5B); // Reserved
};
static_assert(sizeof(AppletFooterUi) == 0x60, "AppletFooterUi is an invalid size");
@@ -404,41 +404,41 @@ private:
// This is nn::hid::detail::NpadInternalState
struct NpadInternalState {
Core::HID::NpadStyleTag style_tag;
NpadJoyAssignmentMode assignment_mode;
NpadFullKeyColorState fullkey_color;
NpadJoyColorState joycon_color;
Lifo<NPadGenericState, hid_entry_count> fullkey_lifo;
Lifo<NPadGenericState, hid_entry_count> handheld_lifo;
Lifo<NPadGenericState, hid_entry_count> joy_dual_lifo;
Lifo<NPadGenericState, hid_entry_count> joy_left_lifo;
Lifo<NPadGenericState, hid_entry_count> joy_right_lifo;
Lifo<NPadGenericState, hid_entry_count> palma_lifo;
Lifo<NPadGenericState, hid_entry_count> system_ext_lifo;
Lifo<SixAxisSensorState, hid_entry_count> sixaxis_fullkey_lifo;
Lifo<SixAxisSensorState, hid_entry_count> sixaxis_handheld_lifo;
Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_left_lifo;
Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_right_lifo;
Lifo<SixAxisSensorState, hid_entry_count> sixaxis_left_lifo;
Lifo<SixAxisSensorState, hid_entry_count> sixaxis_right_lifo;
DeviceType device_type;
Core::HID::NpadStyleTag style_tag{Core::HID::NpadStyleSet::None};
NpadJoyAssignmentMode assignment_mode{NpadJoyAssignmentMode::Dual};
NpadFullKeyColorState fullkey_color{};
NpadJoyColorState joycon_color{};
Lifo<NPadGenericState, hid_entry_count> fullkey_lifo{};
Lifo<NPadGenericState, hid_entry_count> handheld_lifo{};
Lifo<NPadGenericState, hid_entry_count> joy_dual_lifo{};
Lifo<NPadGenericState, hid_entry_count> joy_left_lifo{};
Lifo<NPadGenericState, hid_entry_count> joy_right_lifo{};
Lifo<NPadGenericState, hid_entry_count> palma_lifo{};
Lifo<NPadGenericState, hid_entry_count> system_ext_lifo{};
Lifo<SixAxisSensorState, hid_entry_count> sixaxis_fullkey_lifo{};
Lifo<SixAxisSensorState, hid_entry_count> sixaxis_handheld_lifo{};
Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_left_lifo{};
Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_right_lifo{};
Lifo<SixAxisSensorState, hid_entry_count> sixaxis_left_lifo{};
Lifo<SixAxisSensorState, hid_entry_count> sixaxis_right_lifo{};
DeviceType device_type{};
INSERT_PADDING_BYTES(0x4); // Reserved
NPadSystemProperties system_properties;
NpadSystemButtonProperties button_properties;
Core::HID::NpadBatteryLevel battery_level_dual;
Core::HID::NpadBatteryLevel battery_level_left;
Core::HID::NpadBatteryLevel battery_level_right;
NPadSystemProperties system_properties{};
NpadSystemButtonProperties button_properties{};
Core::HID::NpadBatteryLevel battery_level_dual{};
Core::HID::NpadBatteryLevel battery_level_left{};
Core::HID::NpadBatteryLevel battery_level_right{};
union {
Lifo<NfcXcdDeviceHandleStateImpl, 0x2> nfc_xcd_device_lifo{};
AppletFooterUi applet_footer;
AppletFooterUi applet_footer{};
Lifo<NfcXcdDeviceHandleStateImpl, 0x2> nfc_xcd_device_lifo;
};
INSERT_PADDING_BYTES(0x20); // Unknown
Lifo<NpadGcTriggerState, hid_entry_count> gc_trigger_lifo;
NpadLarkType lark_type_l_and_main;
NpadLarkType lark_type_r;
NpadLuciaType lucia_type;
NpadLagonType lagon_type;
NpadLagerType lager_type;
Lifo<NpadGcTriggerState, hid_entry_count> gc_trigger_lifo{};
NpadLarkType lark_type_l_and_main{};
NpadLarkType lark_type_r{};
NpadLuciaType lucia_type{};
NpadLagonType lagon_type{};
NpadLagerType lager_type{};
// FW 13.x Investigate there is some sort of bitflag related to joycons
INSERT_PADDING_BYTES(0x4);
INSERT_PADDING_BYTES(0xc08); // Unknown
@@ -511,7 +511,8 @@ private:
NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual};
NpadCommunicationMode communication_mode{NpadCommunicationMode::Default};
bool permit_vibration_session_enabled{false};
bool analog_stick_use_center_clamp{};
bool analog_stick_use_center_clamp{false};
bool is_in_lr_assignment_mode{false};
bool is_controller_initialized{false};
};
} // namespace Service::HID

View File

@@ -26,7 +26,7 @@ void NVDRV::Open(Kernel::HLERequestContext& ctx) {
rb.Push<DeviceFD>(0);
rb.PushEnum(NvResult::NotInitialized);
LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
LOG_ERROR(Service_NVDRV, "NvServices is not initialized!");
return;
}
@@ -61,7 +61,7 @@ void NVDRV::Ioctl1(Kernel::HLERequestContext& ctx) {
if (!is_initialized) {
ServiceError(ctx, NvResult::NotInitialized);
LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
LOG_ERROR(Service_NVDRV, "NvServices is not initialized!");
return;
}
@@ -87,7 +87,7 @@ void NVDRV::Ioctl2(Kernel::HLERequestContext& ctx) {
if (!is_initialized) {
ServiceError(ctx, NvResult::NotInitialized);
LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
LOG_ERROR(Service_NVDRV, "NvServices is not initialized!");
return;
}
@@ -114,7 +114,7 @@ void NVDRV::Ioctl3(Kernel::HLERequestContext& ctx) {
if (!is_initialized) {
ServiceError(ctx, NvResult::NotInitialized);
LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
LOG_ERROR(Service_NVDRV, "NvServices is not initialized!");
return;
}
@@ -139,7 +139,7 @@ void NVDRV::Close(Kernel::HLERequestContext& ctx) {
if (!is_initialized) {
ServiceError(ctx, NvResult::NotInitialized);
LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
LOG_ERROR(Service_NVDRV, "NvServices is not initialized!");
return;
}
@@ -170,7 +170,7 @@ void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) {
if (!is_initialized) {
ServiceError(ctx, NvResult::NotInitialized);
LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
LOG_ERROR(Service_NVDRV, "NvServices is not initialized!");
return;
}

View File

@@ -18,8 +18,7 @@ BufferQueueConsumer::BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_)
BufferQueueConsumer::~BufferQueueConsumer() = default;
Status BufferQueueConsumer::AcquireBuffer(BufferItem* out_buffer,
std::chrono::nanoseconds expected_present,
u64 max_frame_number) {
std::chrono::nanoseconds expected_present) {
std::scoped_lock lock(core->mutex);
// Check that the consumer doesn't currently have the maximum number of buffers acquired.
@@ -50,12 +49,6 @@ Status BufferQueueConsumer::AcquireBuffer(BufferItem* out_buffer,
while (core->queue.size() > 1 && !core->queue[0].is_auto_timestamp) {
const auto& buffer_item{core->queue[1]};
// If dropping entry[0] would leave us with a buffer that the consumer is not yet ready
// for, don't drop it.
if (max_frame_number && buffer_item.frame_number > max_frame_number) {
break;
}
// If entry[1] is timely, drop entry[0] (and repeat).
const auto desired_present = buffer_item.timestamp;
if (desired_present < expected_present.count() - MAX_REASONABLE_NSEC ||
@@ -200,4 +193,39 @@ Status BufferQueueConsumer::Connect(std::shared_ptr<IConsumerListener> consumer_
return Status::NoError;
}
Status BufferQueueConsumer::GetReleasedBuffers(u64* out_slot_mask) {
if (out_slot_mask == nullptr) {
LOG_ERROR(Service_NVFlinger, "out_slot_mask may not be nullptr");
return Status::BadValue;
}
std::scoped_lock lock(core->mutex);
if (core->is_abandoned) {
LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
return Status::NoInit;
}
u64 mask = 0;
for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
if (!slots[s].acquire_called) {
mask |= (1ULL << s);
}
}
// Remove from the mask queued buffers for which acquire has been called, since the consumer
// will not receive their buffer addresses and so must retain their cached information
auto current(core->queue.begin());
while (current != core->queue.end()) {
if (current->acquire_called) {
mask &= ~(1ULL << current->slot);
}
++current;
}
LOG_DEBUG(Service_NVFlinger, "returning mask {}", mask);
*out_slot_mask = mask;
return Status::NoError;
}
} // namespace Service::android

View File

@@ -24,10 +24,10 @@ public:
explicit BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_);
~BufferQueueConsumer();
Status AcquireBuffer(BufferItem* out_buffer, std::chrono::nanoseconds expected_present,
u64 max_frame_number = 0);
Status AcquireBuffer(BufferItem* out_buffer, std::chrono::nanoseconds expected_present);
Status ReleaseBuffer(s32 slot, u64 frame_number, const Fence& release_fence);
Status Connect(std::shared_ptr<IConsumerListener> consumer_listener, bool controlled_by_app);
Status GetReleasedBuffers(u64* out_slot_mask);
private:
std::shared_ptr<BufferQueueCore> core;

View File

@@ -95,7 +95,6 @@ void BufferQueueCore::FreeBufferLocked(s32 slot) {
}
void BufferQueueCore::FreeAllBuffersLocked() {
queue.clear();
buffer_has_been_queued = false;
for (s32 slot = 0; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) {

View File

@@ -73,8 +73,6 @@ private:
u32 transform_hint{};
bool is_allocating{};
mutable std::condition_variable_any is_allocating_condition;
bool allow_allocation{true};
u64 buffer_age{};
bool is_shutting_down{};
};

View File

@@ -62,11 +62,12 @@ Status BufferQueueProducer::RequestBuffer(s32 slot, std::shared_ptr<GraphicBuffe
Status BufferQueueProducer::SetBufferCount(s32 buffer_count) {
LOG_DEBUG(Service_NVFlinger, "count = {}", buffer_count);
std::shared_ptr<IConsumerListener> listener;
std::shared_ptr<IConsumerListener> listener;
{
std::scoped_lock lock(core->mutex);
core->WaitWhileAllocatingLocked();
if (core->is_abandoned) {
LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
return Status::NoInit;
@@ -120,7 +121,7 @@ Status BufferQueueProducer::SetBufferCount(s32 buffer_count) {
}
Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found,
Status* returnFlags) const {
Status* return_flags) const {
bool try_again = true;
while (try_again) {
@@ -142,10 +143,12 @@ Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found,
ASSERT(slots[s].buffer_state == BufferState::Free);
if (slots[s].graphic_buffer != nullptr) {
core->FreeBufferLocked(s);
*returnFlags |= Status::ReleaseAllBuffers;
*return_flags |= Status::ReleaseAllBuffers;
}
}
// Look for a free buffer to give to the client
*found = BufferQueueCore::INVALID_BUFFER_SLOT;
s32 dequeued_count{};
s32 acquired_count{};
for (s32 s{}; s < max_buffer_count; ++s) {
@@ -235,68 +238,50 @@ Status BufferQueueProducer::DequeueBuffer(s32* out_slot, Fence* out_fence, bool
{
std::scoped_lock lock(core->mutex);
core->WaitWhileAllocatingLocked();
if (format == PixelFormat::NoFormat) {
format = core->default_buffer_format;
}
// Enable the usage bits the consumer requested
usage |= core->consumer_usage_bit;
s32 found{};
Status status = WaitForFreeSlotThenRelock(async, &found, &return_flags);
if (status != Status::NoError) {
return status;
}
// This should not happen
if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
LOG_ERROR(Service_NVFlinger, "no available buffer slots");
return Status::Busy;
}
*out_slot = found;
attached_by_consumer = slots[found].attached_by_consumer;
const bool use_default_size = !width && !height;
if (use_default_size) {
width = core->default_width;
height = core->default_height;
}
s32 found = BufferItem::INVALID_BUFFER_SLOT;
while (found == BufferItem::INVALID_BUFFER_SLOT) {
Status status = WaitForFreeSlotThenRelock(async, &found, &return_flags);
if (status != Status::NoError) {
return status;
}
// This should not happen
if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
LOG_DEBUG(Service_NVFlinger, "no available buffer slots");
return Status::Busy;
}
const std::shared_ptr<GraphicBuffer>& buffer(slots[found].graphic_buffer);
// If we are not allowed to allocate new buffers, WaitForFreeSlotThenRelock must have
// returned a slot containing a buffer. If this buffer would require reallocation to
// meet the requested attributes, we free it and attempt to get another one.
if (!core->allow_allocation) {
if (buffer->NeedsReallocation(width, height, format, usage)) {
core->FreeBufferLocked(found);
found = BufferItem::INVALID_BUFFER_SLOT;
continue;
}
}
}
*out_slot = found;
attached_by_consumer = slots[found].attached_by_consumer;
slots[found].buffer_state = BufferState::Dequeued;
const std::shared_ptr<GraphicBuffer>& buffer(slots[found].graphic_buffer);
if ((buffer == nullptr) || buffer->NeedsReallocation(width, height, format, usage)) {
if ((buffer == nullptr) || (buffer->Width() != width) || (buffer->Height() != height) ||
(buffer->Format() != format) || ((buffer->Usage() & usage) != usage)) {
slots[found].acquire_called = false;
slots[found].graphic_buffer = nullptr;
slots[found].request_buffer_called = false;
slots[found].fence = Fence::NoFence();
core->buffer_age = 0;
return_flags |= Status::BufferNeedsReallocation;
} else {
// We add 1 because that will be the frame number when this buffer
// is queued
core->buffer_age = core->frame_counter + 1 - slots[found].frame_number;
}
LOG_DEBUG(Service_NVFlinger, "setting buffer age to {}", core->buffer_age);
*out_fence = slots[found].fence;
slots[found].fence = Fence::NoFence();
}
@@ -311,6 +296,7 @@ Status BufferQueueProducer::DequeueBuffer(s32* out_slot, Fence* out_fence, bool
{
std::scoped_lock lock(core->mutex);
if (core->is_abandoned) {
LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
return Status::NoInit;
@@ -327,6 +313,7 @@ Status BufferQueueProducer::DequeueBuffer(s32* out_slot, Fence* out_fence, bool
LOG_DEBUG(Service_NVFlinger, "returning slot={} frame={}, flags={}", *out_slot,
slots[*out_slot].frame_number, return_flags);
return return_flags;
}
@@ -334,6 +321,7 @@ Status BufferQueueProducer::DetachBuffer(s32 slot) {
LOG_DEBUG(Service_NVFlinger, "slot {}", slot);
std::scoped_lock lock(core->mutex);
if (core->is_abandoned) {
LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
return Status::NoInit;
@@ -369,7 +357,6 @@ Status BufferQueueProducer::DetachNextBuffer(std::shared_ptr<GraphicBuffer>* out
}
std::scoped_lock lock(core->mutex);
core->WaitWhileAllocatingLocked();
if (core->is_abandoned) {
@@ -423,6 +410,7 @@ Status BufferQueueProducer::AttachBuffer(s32* out_slot,
return status;
}
// This should not happen
if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
LOG_ERROR(Service_NVFlinger, "No available buffer slots");
return Status::Busy;
@@ -466,8 +454,8 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input,
return Status::BadValue;
}
std::shared_ptr<IConsumerListener> frameAvailableListener;
std::shared_ptr<IConsumerListener> frameReplacedListener;
std::shared_ptr<IConsumerListener> frame_available_listener;
std::shared_ptr<IConsumerListener> frame_replaced_listener;
s32 callback_ticket{};
BufferItem item;
@@ -541,12 +529,13 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input,
item.fence = fence;
item.is_droppable = core->dequeue_buffer_cannot_block || async;
item.swap_interval = swap_interval;
sticky_transform = sticky_transform_;
if (core->queue.empty()) {
// When the queue is empty, we can simply queue this buffer
core->queue.push_back(item);
frameAvailableListener = core->consumer_listener;
frame_available_listener = core->consumer_listener;
} else {
// When the queue is not empty, we need to look at the front buffer
// state to see if we need to replace it
@@ -563,10 +552,10 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input,
}
// Overwrite the droppable buffer with the incoming one
*front = item;
frameReplacedListener = core->consumer_listener;
frame_replaced_listener = core->consumer_listener;
} else {
core->queue.push_back(item);
frameAvailableListener = core->consumer_listener;
frame_available_listener = core->consumer_listener;
}
}
@@ -592,10 +581,10 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input,
callback_condition.wait(callback_mutex);
}
if (frameAvailableListener != nullptr) {
frameAvailableListener->OnFrameAvailable(item);
} else if (frameReplacedListener != nullptr) {
frameReplacedListener->OnFrameReplaced(item);
if (frame_available_listener != nullptr) {
frame_available_listener->OnFrameAvailable(item);
} else if (frame_replaced_listener != nullptr) {
frame_replaced_listener->OnFrameReplaced(item);
}
++current_callback_ticket;
@@ -669,13 +658,6 @@ Status BufferQueueProducer::Query(NativeWindow what, s32* out_value) {
case NativeWindow::ConsumerUsageBits:
value = core->consumer_usage_bit;
break;
case NativeWindow::BufferAge:
if (core->buffer_age > INT32_MAX) {
value = 0;
} else {
value = static_cast<u32>(core->buffer_age);
}
break;
default:
UNREACHABLE();
return Status::BadValue;
@@ -737,7 +719,6 @@ Status BufferQueueProducer::Connect(const std::shared_ptr<IProducerListener>& li
core->buffer_has_been_queued = false;
core->dequeue_buffer_cannot_block =
core->consumer_controlled_by_app && producer_controlled_by_app;
core->allow_allocation = true;
return status;
}
@@ -770,7 +751,7 @@ Status BufferQueueProducer::Disconnect(NativeWindowApi api) {
core->SignalDequeueCondition();
buffer_wait_event->GetWritableEvent().Signal();
listener = core->consumer_listener;
} else if (core->connected_api != NativeWindowApi::NoConnectedApi) {
} else {
LOG_ERROR(Service_NVFlinger, "still connected to another api (cur = {} req = {})",
core->connected_api, api);
status = Status::BadValue;

View File

@@ -66,7 +66,7 @@ public:
private:
BufferQueueProducer(const BufferQueueProducer&) = delete;
Status WaitForFreeSlotThenRelock(bool async, s32* found, Status* returnFlags) const;
Status WaitForFreeSlotThenRelock(bool async, s32* found, Status* return_flags) const;
Kernel::KEvent* buffer_wait_event{};
Service::KernelHelpers::ServiceContext& service_context;

View File

@@ -36,38 +36,41 @@ void ConsumerBase::FreeBufferLocked(s32 slot_index) {
}
void ConsumerBase::OnFrameAvailable(const BufferItem& item) {
std::scoped_lock lock(mutex);
LOG_DEBUG(Service_NVFlinger, "called");
}
void ConsumerBase::OnFrameReplaced(const BufferItem& item) {
std::scoped_lock lock(mutex);
LOG_DEBUG(Service_NVFlinger, "called");
}
void ConsumerBase::OnBuffersReleased() {
std::scoped_lock lock(mutex);
LOG_DEBUG(Service_NVFlinger, "called");
if (is_abandoned) {
// Nothing to do if we're already abandoned.
return;
}
u64 mask = 0;
consumer->GetReleasedBuffers(&mask);
for (int i = 0; i < BufferQueueDefs::NUM_BUFFER_SLOTS; i++) {
if (mask & (1ULL << i)) {
FreeBufferLocked(i);
}
}
}
void ConsumerBase::OnSidebandStreamChanged() {}
Status ConsumerBase::AcquireBufferLocked(BufferItem* item, std::chrono::nanoseconds present_when,
u64 max_frame_number) {
if (is_abandoned) {
LOG_ERROR(Service_NVFlinger, "consumer is abandoned!");
return Status::NoInit;
}
Status err = consumer->AcquireBuffer(item, present_when, max_frame_number);
Status ConsumerBase::AcquireBufferLocked(BufferItem* item, std::chrono::nanoseconds present_when) {
Status err = consumer->AcquireBuffer(item, present_when);
if (err != Status::NoError) {
return err;
}
if (item->graphic_buffer != nullptr) {
if (slots[item->slot].graphic_buffer != nullptr) {
FreeBufferLocked(item->slot);
}
slots[item->slot].graphic_buffer = item->graphic_buffer;
}

View File

@@ -35,8 +35,7 @@ protected:
virtual void OnSidebandStreamChanged() override;
void FreeBufferLocked(s32 slot_index);
Status AcquireBufferLocked(BufferItem* item, std::chrono::nanoseconds present_when,
u64 max_frame_number = 0);
Status AcquireBufferLocked(BufferItem* item, std::chrono::nanoseconds present_when);
Status ReleaseBufferLocked(s32 slot, const std::shared_ptr<GraphicBuffer> graphic_buffer);
bool StillTracking(s32 slot, const std::shared_ptr<GraphicBuffer> graphic_buffer) const;
Status AddReleaseFenceLocked(s32 slot, const std::shared_ptr<GraphicBuffer> graphic_buffer,

View File

@@ -104,7 +104,7 @@ void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) {
std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) {
const auto lock_guard = Lock();
LOG_DEBUG(Service, "Opening \"{}\" display", name);
LOG_DEBUG(Service_NVFlinger, "Opening \"{}\" display", name);
const auto itr =
std::find_if(displays.begin(), displays.end(),
@@ -219,7 +219,7 @@ VI::Layer* NVFlinger::FindOrCreateLayer(u64 display_id, u64 layer_id) {
auto* layer = display->FindLayer(layer_id);
if (layer == nullptr) {
LOG_DEBUG(Service, "Layer at id {} not found. Trying to create it.", layer_id);
LOG_DEBUG(Service_NVFlinger, "Layer at id {} not found. Trying to create it.", layer_id);
CreateLayerAtId(*display, layer_id);
return display->FindLayer(layer_id);
}

View File

@@ -132,7 +132,7 @@ void Impl(TranslatorVisitor& v, u64 insn, bool is_bindless) {
multisample = v.X(meta_reg++);
}
if (tld.clamp != 0) {
throw NotImplementedException("TLD.CL - CLAMP is not implmented");
throw NotImplementedException("TLD.CL - CLAMP is not implemented");
}
IR::TextureInstInfo info{};
info.type.Assign(GetType(tld.type));

View File

@@ -81,7 +81,7 @@ void Impl(TranslatorVisitor& v, u64 insn, bool is_bindless) {
} const tmml{insn};
if ((tmml.mask & 0b1100) != 0) {
throw NotImplementedException("TMML BA results are not implmented");
throw NotImplementedException("TMML BA results are not implemented");
}
const IR::Value coords{MakeCoords(v, tmml.coord_reg, tmml.type)};

View File

@@ -200,7 +200,7 @@ void ComputePipeline::Configure(Tegra::Engines::KeplerCompute& kepler_compute,
});
}
const void* const descriptor_data{update_descriptor_queue.UpdateData()};
const bool is_rescaling = info.uses_rescaling_uniform;
const bool is_rescaling = !info.texture_descriptors.empty() || !info.image_descriptors.empty();
scheduler.Record([this, descriptor_data, is_rescaling,
rescaling_data = rescaling.Data()](vk::CommandBuffer cmdbuf) {
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline);

View File

@@ -238,7 +238,6 @@ GraphicsPipeline::GraphicsPipeline(
enabled_uniform_buffer_masks[stage] = info->constant_buffer_mask;
std::ranges::copy(info->constant_buffer_used_sizes, uniform_buffer_sizes[stage].begin());
num_textures += Shader::NumDescriptors(info->texture_descriptors);
uses_rescale_unfiorm |= info->uses_rescaling_uniform;
}
auto func{[this, shader_notify, &render_pass_cache, &descriptor_pool, pipeline_statistics] {
DescriptorLayoutBuilder builder{MakeBuilder(device, stage_infos)};
@@ -472,8 +471,7 @@ void GraphicsPipeline::ConfigureDraw(const RescalingPushConstant& rescaling) {
});
}
const bool is_rescaling{texture_cache.IsRescaling()};
const bool update_rescaling{uses_rescale_unfiorm ? scheduler.UpdateRescaling(is_rescaling)
: false};
const bool update_rescaling{scheduler.UpdateRescaling(is_rescaling)};
const bool bind_pipeline{scheduler.UpdateGraphicsPipeline(this)};
const void* const descriptor_data{update_descriptor_queue.UpdateData()};
scheduler.Record([this, descriptor_data, bind_pipeline, rescaling_data = rescaling.Data(),
@@ -481,12 +479,10 @@ void GraphicsPipeline::ConfigureDraw(const RescalingPushConstant& rescaling) {
if (bind_pipeline) {
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
}
if (uses_rescale_unfiorm) {
cmdbuf.PushConstants(*pipeline_layout, VK_SHADER_STAGE_ALL_GRAPHICS,
RESCALING_LAYOUT_WORDS_OFFSET, sizeof(rescaling_data),
rescaling_data.data());
}
if (uses_rescale_unfiorm && update_rescaling) {
cmdbuf.PushConstants(*pipeline_layout, VK_SHADER_STAGE_ALL_GRAPHICS,
RESCALING_LAYOUT_WORDS_OFFSET, sizeof(rescaling_data),
rescaling_data.data());
if (update_rescaling) {
const f32 config_down_factor{Settings::values.resolution_info.down_factor};
const f32 scale_down_factor{is_rescaling ? config_down_factor : 1.0f};
cmdbuf.PushConstants(*pipeline_layout, VK_SHADER_STAGE_ALL_GRAPHICS,

View File

@@ -151,7 +151,6 @@ private:
std::mutex build_mutex;
std::atomic_bool is_built{false};
bool uses_push_descriptor{false};
bool uses_rescale_unfiorm{false};
};
} // namespace Vulkan

View File

@@ -234,9 +234,12 @@ void RasterizerVulkan::Clear() {
const VkExtent2D render_area = framebuffer->RenderArea();
scheduler.RequestRenderpass(framebuffer);
const bool is_rescaling = texture_cache.IsRescaling();
const u32 up_scale = is_rescaling ? Settings::values.resolution_info.up_scale : 1U;
const u32 down_shift = is_rescaling ? Settings::values.resolution_info.down_shift : 0U;
u32 up_scale = 1;
u32 down_shift = 0;
if (texture_cache.IsRescaling()) {
up_scale = Settings::values.resolution_info.up_scale;
down_shift = Settings::values.resolution_info.down_shift;
}
UpdateViewportsState(regs);
VkClearRect clear_rect{
@@ -692,9 +695,12 @@ void RasterizerVulkan::UpdateScissorsState(Tegra::Engines::Maxwell3D::Regs& regs
if (!state_tracker.TouchScissors()) {
return;
}
const bool is_rescaling = texture_cache.IsRescaling();
const u32 up_scale = is_rescaling ? Settings::values.resolution_info.up_scale : 1U;
const u32 down_shift = is_rescaling ? Settings::values.resolution_info.down_shift : 0U;
u32 up_scale = 1;
u32 down_shift = 0;
if (texture_cache.IsRescaling()) {
up_scale = Settings::values.resolution_info.up_scale;
down_shift = Settings::values.resolution_info.down_shift;
}
const std::array scissors{
GetScissorState(regs, 0, up_scale, down_shift),
GetScissorState(regs, 1, up_scale, down_shift),

View File

@@ -328,8 +328,7 @@ void TextureCache<P>::UpdateRenderTargets(bool is_clear) {
}
const bool rescaled = RescaleRenderTargets(is_clear);
const auto& resolution_info = Settings::values.resolution_info;
if (resolution_info.active && is_rescaling != rescaled) {
if (is_rescaling != rescaled) {
flags[Dirty::RescaleViewports] = true;
flags[Dirty::RescaleScissors] = true;
is_rescaling = rescaled;
@@ -346,8 +345,12 @@ void TextureCache<P>::UpdateRenderTargets(bool is_clear) {
for (size_t index = 0; index < NUM_RT; ++index) {
render_targets.draw_buffers[index] = static_cast<u8>(maxwell3d.regs.rt_control.Map(index));
}
const u32 up_scale = is_rescaling ? resolution_info.up_scale : 1U;
const u32 down_shift = is_rescaling ? resolution_info.down_shift : 0U;
u32 up_scale = 1;
u32 down_shift = 0;
if (is_rescaling) {
up_scale = Settings::values.resolution_info.up_scale;
down_shift = Settings::values.resolution_info.down_shift;
}
render_targets.size = Extent2D{
(maxwell3d.regs.render_area.width * up_scale) >> down_shift,
(maxwell3d.regs.render_area.height * up_scale) >> down_shift,

View File

@@ -736,7 +736,7 @@ VkFormat Device::GetSupportedFormat(VkFormat wanted_format, VkFormatFeatureFlags
}
void Device::ReportLoss() const {
LOG_CRITICAL(Render_Vulkan, "Device loss occured!");
LOG_CRITICAL(Render_Vulkan, "Device loss occurred!");
// Wait for the log to flush and for Nsight Aftermath to dump the results
std::this_thread::sleep_for(std::chrono::seconds{15});

View File

@@ -52,6 +52,11 @@
<string>Unsafe</string>
</property>
</item>
<item>
<property name="text">
<string>Paranoid (disables most optimizations)</string>
</property>
</item>
</widget>
</item>
</layout>

View File

@@ -35,8 +35,9 @@ ConfigureHotkeys::ConfigureHotkeys(Core::HID::HIDCore& hid_core, QWidget* parent
ui->hotkey_list->setContextMenuPolicy(Qt::CustomContextMenu);
ui->hotkey_list->setModel(model);
ui->hotkey_list->setColumnWidth(name_column, 200);
ui->hotkey_list->resizeColumnToContents(hotkey_column);
ui->hotkey_list->header()->setStretchLastSection(false);
ui->hotkey_list->header()->setSectionResizeMode(name_column, QHeaderView::ResizeMode::Stretch);
ui->hotkey_list->header()->setMinimumSectionSize(150);
connect(ui->button_restore_defaults, &QPushButton::clicked, this,
&ConfigureHotkeys::RestoreDefaults);
@@ -76,8 +77,8 @@ void ConfigureHotkeys::Populate(const HotkeyRegistry& registry) {
}
ui->hotkey_list->expandAll();
ui->hotkey_list->resizeColumnToContents(name_column);
ui->hotkey_list->resizeColumnToContents(hotkey_column);
ui->hotkey_list->resizeColumnToContents(controller_column);
}
void ConfigureHotkeys::changeEvent(QEvent* event) {

View File

@@ -47,6 +47,10 @@ ConfigurePerGameAddons::ConfigurePerGameAddons(Core::System& system_, QWidget* p
item_model->setHeaderData(0, Qt::Horizontal, tr("Patch Name"));
item_model->setHeaderData(1, Qt::Horizontal, tr("Version"));
tree_view->header()->setStretchLastSection(false);
tree_view->header()->setSectionResizeMode(0, QHeaderView::ResizeMode::Stretch);
tree_view->header()->setMinimumSectionSize(150);
// We must register all custom types with the Qt Automoc system so that we are able to use it
// with signals/slots. In this case, QList falls under the umbrella of custom types.
qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>");
@@ -138,5 +142,5 @@ void ConfigurePerGameAddons::LoadConfiguration() {
item_model->appendRow(list_items.back());
}
tree_view->setColumnWidth(0, 5 * tree_view->width() / 16);
tree_view->resizeColumnToContents(1);
}

View File

@@ -342,12 +342,6 @@ fps_cap =
# null: No audio output
output_engine =
# Whether or not to enable the audio-stretching post-processing effect.
# This effect adjusts audio speed to match emulation speed and helps prevent audio stutter,
# at the cost of increasing audio latency.
# 0: No, 1 (default): Yes
enable_audio_stretching =
# Which audio device to use.
# auto (default): Auto-select
output_device =