Compare commits
41 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8ad712b044 | ||
|
|
37199c5f90 | ||
|
|
8c089f4e2a | ||
|
|
12dc4d0527 | ||
|
|
0c1b954e07 | ||
|
|
e4c3565ebe | ||
|
|
9af501b75d | ||
|
|
7f11710e0a | ||
|
|
ce859cf526 | ||
|
|
119d1692c9 | ||
|
|
02473ea7d5 | ||
|
|
c7f3c2cedf | ||
|
|
494c41dd5a | ||
|
|
f114436120 | ||
|
|
e9cf2d43f1 | ||
|
|
2d1af6beee | ||
|
|
b5910ad0ba | ||
|
|
3e5469a974 | ||
|
|
979e53b87b | ||
|
|
084bd225dc | ||
|
|
c562c1d6be | ||
|
|
b4746529e1 | ||
|
|
7f1e66e94b | ||
|
|
1e47252214 | ||
|
|
0bcbe3a703 | ||
|
|
1cb83c91a6 | ||
|
|
f4004b1271 | ||
|
|
8c57de1605 | ||
|
|
0c214cb5b9 | ||
|
|
8dc1913db8 | ||
|
|
fdf4909f97 | ||
|
|
4036e37bbe | ||
|
|
30b07878ba | ||
|
|
7610554b1e | ||
|
|
f9371f36a4 | ||
|
|
1166c3910d | ||
|
|
faf6a9876c | ||
|
|
9346f0b33d | ||
|
|
ea7a0d4652 | ||
|
|
94967e0f6d | ||
|
|
3c8547160d |
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -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
|
||||
|
||||
3
externals/CMakeLists.txt
vendored
3
externals/CMakeLists.txt
vendored
@@ -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 "")
|
||||
|
||||
1
externals/soundtouch
vendored
1
externals/soundtouch
vendored
Submodule externals/soundtouch deleted from 060181eaf2
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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"};
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)));
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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{};
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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)};
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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});
|
||||
|
||||
@@ -52,6 +52,11 @@
|
||||
<string>Unsafe</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Paranoid (disables most optimizations)</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 =
|
||||
|
||||
Reference in New Issue
Block a user