Compare commits
2 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
27f8f3333f | ||
|
|
3c65c8580f |
@@ -18,7 +18,6 @@
|
||||
#include "common/fs/fs_paths.h"
|
||||
#include "common/fs/path_util.h"
|
||||
#include "common/literals.h"
|
||||
#include "common/thread.h"
|
||||
|
||||
#include "common/logging/backend.h"
|
||||
#include "common/logging/log.h"
|
||||
|
||||
@@ -111,7 +111,6 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
|
||||
SUB(Service, NCM) \
|
||||
SUB(Service, NFC) \
|
||||
SUB(Service, NFP) \
|
||||
SUB(Service, NGCT) \
|
||||
SUB(Service, NIFM) \
|
||||
SUB(Service, NIM) \
|
||||
SUB(Service, NPNS) \
|
||||
|
||||
@@ -81,7 +81,6 @@ enum class Class : u8 {
|
||||
Service_NCM, ///< The NCM service
|
||||
Service_NFC, ///< The NFC (Near-field communication) service
|
||||
Service_NFP, ///< The NFP service
|
||||
Service_NGCT, ///< The NGCT (No Good Content for Terra) service
|
||||
Service_NIFM, ///< The NIFM (Network interface) service
|
||||
Service_NIM, ///< The NIM service
|
||||
Service_NPNS, ///< The NPNS service
|
||||
|
||||
@@ -1,140 +0,0 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2+ or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
template <class Traits>
|
||||
class LeastRecentlyUsedCache {
|
||||
using ObjectType = typename Traits::ObjectType;
|
||||
using TickType = typename Traits::TickType;
|
||||
|
||||
struct Item {
|
||||
ObjectType obj;
|
||||
TickType tick;
|
||||
Item* next{};
|
||||
Item* prev{};
|
||||
};
|
||||
|
||||
public:
|
||||
LeastRecentlyUsedCache() : first_item{}, last_item{} {}
|
||||
~LeastRecentlyUsedCache() = default;
|
||||
|
||||
size_t Insert(ObjectType obj, TickType tick) {
|
||||
const auto new_id = Build();
|
||||
auto& item = item_pool[new_id];
|
||||
item.obj = obj;
|
||||
item.tick = tick;
|
||||
Attach(item);
|
||||
return new_id;
|
||||
}
|
||||
|
||||
void Touch(size_t id, TickType tick) {
|
||||
auto& item = item_pool[id];
|
||||
if (item.tick >= tick) {
|
||||
return;
|
||||
}
|
||||
item.tick = tick;
|
||||
if (&item == last_item) {
|
||||
return;
|
||||
}
|
||||
Detach(item);
|
||||
Attach(item);
|
||||
}
|
||||
|
||||
void Free(size_t id) {
|
||||
auto& item = item_pool[id];
|
||||
Detach(item);
|
||||
item.prev = nullptr;
|
||||
item.next = nullptr;
|
||||
free_items.push_back(id);
|
||||
}
|
||||
|
||||
template <typename Func>
|
||||
void ForEachItemBelow(TickType tick, Func&& func) {
|
||||
static constexpr bool RETURNS_BOOL =
|
||||
std::is_same_v<std::invoke_result<Func, ObjectType>, bool>;
|
||||
Item* iterator = first_item;
|
||||
while (iterator) {
|
||||
if (static_cast<s64>(tick) - static_cast<s64>(iterator->tick) < 0) {
|
||||
return;
|
||||
}
|
||||
Item* next = iterator->next;
|
||||
if constexpr (RETURNS_BOOL) {
|
||||
if (func(iterator->obj)) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
func(iterator->obj);
|
||||
}
|
||||
iterator = next;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
size_t Build() {
|
||||
if (free_items.empty()) {
|
||||
const size_t item_id = item_pool.size();
|
||||
auto& item = item_pool.emplace_back();
|
||||
item.next = nullptr;
|
||||
item.prev = nullptr;
|
||||
return item_id;
|
||||
}
|
||||
const size_t item_id = free_items.front();
|
||||
free_items.pop_front();
|
||||
auto& item = item_pool[item_id];
|
||||
item.next = nullptr;
|
||||
item.prev = nullptr;
|
||||
return item_id;
|
||||
}
|
||||
|
||||
void Attach(Item& item) {
|
||||
if (!first_item) {
|
||||
first_item = &item;
|
||||
}
|
||||
if (!last_item) {
|
||||
last_item = &item;
|
||||
} else {
|
||||
item.prev = last_item;
|
||||
last_item->next = &item;
|
||||
item.next = nullptr;
|
||||
last_item = &item;
|
||||
}
|
||||
}
|
||||
|
||||
void Detach(Item& item) {
|
||||
if (item.prev) {
|
||||
item.prev->next = item.next;
|
||||
}
|
||||
if (item.next) {
|
||||
item.next->prev = item.prev;
|
||||
}
|
||||
if (&item == first_item) {
|
||||
first_item = item.next;
|
||||
if (first_item) {
|
||||
first_item->prev = nullptr;
|
||||
}
|
||||
}
|
||||
if (&item == last_item) {
|
||||
last_item = item.prev;
|
||||
if (last_item) {
|
||||
last_item->next = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::deque<Item> item_pool;
|
||||
std::deque<size_t> free_items;
|
||||
Item* first_item{};
|
||||
Item* last_item{};
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
@@ -59,6 +59,7 @@ void LogSettings() {
|
||||
log_setting("Renderer_UseVsync", values.use_vsync.GetValue());
|
||||
log_setting("Renderer_ShaderBackend", values.shader_backend.GetValue());
|
||||
log_setting("Renderer_UseAsynchronousShaders", values.use_asynchronous_shaders.GetValue());
|
||||
log_setting("Renderer_UseGarbageCollection", values.use_caches_gc.GetValue());
|
||||
log_setting("Renderer_AnisotropicFilteringLevel", values.max_anisotropy.GetValue());
|
||||
log_setting("Audio_OutputEngine", values.sink_id.GetValue());
|
||||
log_setting("Audio_EnableAudioStretching", values.enable_audio_stretching.GetValue());
|
||||
@@ -142,6 +143,7 @@ void RestoreGlobalState(bool is_powered_on) {
|
||||
values.shader_backend.SetGlobal(true);
|
||||
values.use_asynchronous_shaders.SetGlobal(true);
|
||||
values.use_fast_gpu_time.SetGlobal(true);
|
||||
values.use_caches_gc.SetGlobal(true);
|
||||
values.bg_red.SetGlobal(true);
|
||||
values.bg_green.SetGlobal(true);
|
||||
values.bg_blue.SetGlobal(true);
|
||||
|
||||
@@ -475,6 +475,7 @@ struct Values {
|
||||
ShaderBackend::SPIRV, "shader_backend"};
|
||||
Setting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"};
|
||||
Setting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"};
|
||||
Setting<bool> use_caches_gc{false, "use_caches_gc"};
|
||||
|
||||
Setting<u8> bg_red{0, "bg_red"};
|
||||
Setting<u8> bg_green{0, "bg_green"};
|
||||
|
||||
@@ -452,8 +452,6 @@ add_library(core STATIC
|
||||
hle/service/nfp/nfp.h
|
||||
hle/service/nfp/nfp_user.cpp
|
||||
hle/service/nfp/nfp_user.h
|
||||
hle/service/ngct/ngct.cpp
|
||||
hle/service/ngct/ngct.h
|
||||
hle/service/nifm/nifm.cpp
|
||||
hle/service/nifm/nifm.h
|
||||
hle/service/nim/nim.cpp
|
||||
|
||||
@@ -507,12 +507,6 @@ const ARM_Interface& System::CurrentArmInterface() const {
|
||||
return impl->kernel.CurrentPhysicalCore().ArmInterface();
|
||||
}
|
||||
|
||||
std::size_t System::CurrentCoreIndex() const {
|
||||
std::size_t core = impl->kernel.GetCurrentHostThreadID();
|
||||
ASSERT(core < Core::Hardware::NUM_CPU_CORES);
|
||||
return core;
|
||||
}
|
||||
|
||||
Kernel::PhysicalCore& System::CurrentPhysicalCore() {
|
||||
return impl->kernel.CurrentPhysicalCore();
|
||||
}
|
||||
|
||||
@@ -205,9 +205,6 @@ public:
|
||||
/// Gets an ARM interface to the CPU core that is currently running
|
||||
[[nodiscard]] const ARM_Interface& CurrentArmInterface() const;
|
||||
|
||||
/// Gets the index of the currently running CPU core
|
||||
[[nodiscard]] std::size_t CurrentCoreIndex() const;
|
||||
|
||||
/// Gets the physical core for the CPU core that is currently running
|
||||
[[nodiscard]] Kernel::PhysicalCore& CurrentPhysicalCore();
|
||||
|
||||
|
||||
@@ -21,34 +21,25 @@ namespace Core {
|
||||
CpuManager::CpuManager(System& system_) : system{system_} {}
|
||||
CpuManager::~CpuManager() = default;
|
||||
|
||||
void CpuManager::ThreadStart(CpuManager& cpu_manager, std::size_t core) {
|
||||
cpu_manager.RunThread(core);
|
||||
void CpuManager::ThreadStart(std::stop_token stop_token, CpuManager& cpu_manager,
|
||||
std::size_t core) {
|
||||
cpu_manager.RunThread(stop_token, core);
|
||||
}
|
||||
|
||||
void CpuManager::Initialize() {
|
||||
running_mode = true;
|
||||
if (is_multicore) {
|
||||
for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
|
||||
core_data[core].host_thread =
|
||||
std::make_unique<std::thread>(ThreadStart, std::ref(*this), core);
|
||||
core_data[core].host_thread = std::jthread(ThreadStart, std::ref(*this), core);
|
||||
}
|
||||
} else {
|
||||
core_data[0].host_thread = std::make_unique<std::thread>(ThreadStart, std::ref(*this), 0);
|
||||
core_data[0].host_thread = std::jthread(ThreadStart, std::ref(*this), 0);
|
||||
}
|
||||
}
|
||||
|
||||
void CpuManager::Shutdown() {
|
||||
running_mode = false;
|
||||
Pause(false);
|
||||
if (is_multicore) {
|
||||
for (auto& data : core_data) {
|
||||
data.host_thread->join();
|
||||
data.host_thread.reset();
|
||||
}
|
||||
} else {
|
||||
core_data[0].host_thread->join();
|
||||
core_data[0].host_thread.reset();
|
||||
}
|
||||
}
|
||||
|
||||
std::function<void(void*)> CpuManager::GetGuestThreadStartFunc() {
|
||||
@@ -127,17 +118,18 @@ void CpuManager::MultiCoreRunGuestLoop() {
|
||||
physical_core = &kernel.CurrentPhysicalCore();
|
||||
}
|
||||
system.ExitDynarmicProfile();
|
||||
physical_core->ArmInterface().ClearExclusiveState();
|
||||
kernel.CurrentScheduler()->RescheduleCurrentCore();
|
||||
{
|
||||
Kernel::KScopedDisableDispatch dd(kernel);
|
||||
physical_core->ArmInterface().ClearExclusiveState();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CpuManager::MultiCoreRunIdleThread() {
|
||||
auto& kernel = system.Kernel();
|
||||
while (true) {
|
||||
auto& physical_core = kernel.CurrentPhysicalCore();
|
||||
physical_core.Idle();
|
||||
kernel.CurrentScheduler()->RescheduleCurrentCore();
|
||||
Kernel::KScopedDisableDispatch dd(kernel);
|
||||
kernel.CurrentPhysicalCore().Idle();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,12 +137,12 @@ void CpuManager::MultiCoreRunSuspendThread() {
|
||||
auto& kernel = system.Kernel();
|
||||
kernel.CurrentScheduler()->OnThreadStart();
|
||||
while (true) {
|
||||
auto core = kernel.GetCurrentHostThreadID();
|
||||
auto core = kernel.CurrentPhysicalCoreIndex();
|
||||
auto& scheduler = *kernel.CurrentScheduler();
|
||||
Kernel::KThread* current_thread = scheduler.GetCurrentThread();
|
||||
Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[core].host_context);
|
||||
ASSERT(scheduler.ContextSwitchPending());
|
||||
ASSERT(core == kernel.GetCurrentHostThreadID());
|
||||
ASSERT(core == kernel.CurrentPhysicalCoreIndex());
|
||||
scheduler.RescheduleCurrentCore();
|
||||
}
|
||||
}
|
||||
@@ -317,7 +309,7 @@ void CpuManager::Pause(bool paused) {
|
||||
}
|
||||
}
|
||||
|
||||
void CpuManager::RunThread(std::size_t core) {
|
||||
void CpuManager::RunThread(std::stop_token stop_token, std::size_t core) {
|
||||
/// Initialization
|
||||
system.RegisterCoreThread(core);
|
||||
std::string name;
|
||||
@@ -356,8 +348,8 @@ void CpuManager::RunThread(std::size_t core) {
|
||||
sc_sync_first_use = false;
|
||||
}
|
||||
|
||||
// Abort if emulation was killed before the session really starts
|
||||
if (!system.IsPoweredOn()) {
|
||||
// Emulation was stopped
|
||||
if (stop_token.stop_requested()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -78,9 +78,9 @@ private:
|
||||
void SingleCoreRunSuspendThread();
|
||||
void SingleCorePause(bool paused);
|
||||
|
||||
static void ThreadStart(CpuManager& cpu_manager, std::size_t core);
|
||||
static void ThreadStart(std::stop_token stop_token, CpuManager& cpu_manager, std::size_t core);
|
||||
|
||||
void RunThread(std::size_t core);
|
||||
void RunThread(std::stop_token stop_token, std::size_t core);
|
||||
|
||||
struct CoreData {
|
||||
std::shared_ptr<Common::Fiber> host_context;
|
||||
@@ -89,7 +89,7 @@ private:
|
||||
std::atomic<bool> is_running;
|
||||
std::atomic<bool> is_paused;
|
||||
std::atomic<bool> initialized;
|
||||
std::unique_ptr<std::thread> host_thread;
|
||||
std::jthread host_thread;
|
||||
};
|
||||
|
||||
std::atomic<bool> running_mode{};
|
||||
|
||||
@@ -28,7 +28,7 @@ bool ReadFromUser(Core::System& system, s32* out, VAddr address) {
|
||||
|
||||
bool DecrementIfLessThan(Core::System& system, s32* out, VAddr address, s32 value) {
|
||||
auto& monitor = system.Monitor();
|
||||
const auto current_core = system.CurrentCoreIndex();
|
||||
const auto current_core = system.Kernel().CurrentPhysicalCoreIndex();
|
||||
|
||||
// TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
|
||||
// TODO(bunnei): We should call CanAccessAtomic(..) here.
|
||||
@@ -58,7 +58,7 @@ bool DecrementIfLessThan(Core::System& system, s32* out, VAddr address, s32 valu
|
||||
|
||||
bool UpdateIfEqual(Core::System& system, s32* out, VAddr address, s32 value, s32 new_value) {
|
||||
auto& monitor = system.Monitor();
|
||||
const auto current_core = system.CurrentCoreIndex();
|
||||
const auto current_core = system.Kernel().CurrentPhysicalCoreIndex();
|
||||
|
||||
// TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
|
||||
// TODO(bunnei): We should call CanAccessAtomic(..) here.
|
||||
|
||||
@@ -170,6 +170,10 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
const std::string& GetName() const {
|
||||
return name;
|
||||
}
|
||||
|
||||
private:
|
||||
void RegisterWithKernel();
|
||||
void UnregisterWithKernel();
|
||||
|
||||
@@ -35,7 +35,7 @@ bool WriteToUser(Core::System& system, VAddr address, const u32* p) {
|
||||
bool UpdateLockAtomic(Core::System& system, u32* out, VAddr address, u32 if_zero,
|
||||
u32 new_orr_mask) {
|
||||
auto& monitor = system.Monitor();
|
||||
const auto current_core = system.CurrentCoreIndex();
|
||||
const auto current_core = system.Kernel().CurrentPhysicalCoreIndex();
|
||||
|
||||
// Load the value from the address.
|
||||
const auto expected = monitor.ExclusiveRead32(current_core, address);
|
||||
|
||||
@@ -13,6 +13,7 @@ ResultCode KHandleTable::Finalize() {
|
||||
// Get the table and clear our record of it.
|
||||
u16 saved_table_size = 0;
|
||||
{
|
||||
KScopedDisableDispatch dd(kernel);
|
||||
KScopedSpinLock lk(m_lock);
|
||||
|
||||
std::swap(m_table_size, saved_table_size);
|
||||
@@ -43,6 +44,7 @@ bool KHandleTable::Remove(Handle handle) {
|
||||
// Find the object and free the entry.
|
||||
KAutoObject* obj = nullptr;
|
||||
{
|
||||
KScopedDisableDispatch dd(kernel);
|
||||
KScopedSpinLock lk(m_lock);
|
||||
|
||||
if (this->IsValidHandle(handle)) {
|
||||
@@ -61,6 +63,7 @@ bool KHandleTable::Remove(Handle handle) {
|
||||
}
|
||||
|
||||
ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj, u16 type) {
|
||||
KScopedDisableDispatch dd(kernel);
|
||||
KScopedSpinLock lk(m_lock);
|
||||
|
||||
// Never exceed our capacity.
|
||||
@@ -83,6 +86,7 @@ ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj, u16 type) {
|
||||
}
|
||||
|
||||
ResultCode KHandleTable::Reserve(Handle* out_handle) {
|
||||
KScopedDisableDispatch dd(kernel);
|
||||
KScopedSpinLock lk(m_lock);
|
||||
|
||||
// Never exceed our capacity.
|
||||
@@ -93,6 +97,7 @@ ResultCode KHandleTable::Reserve(Handle* out_handle) {
|
||||
}
|
||||
|
||||
void KHandleTable::Unreserve(Handle handle) {
|
||||
KScopedDisableDispatch dd(kernel);
|
||||
KScopedSpinLock lk(m_lock);
|
||||
|
||||
// Unpack the handle.
|
||||
@@ -111,6 +116,7 @@ void KHandleTable::Unreserve(Handle handle) {
|
||||
}
|
||||
|
||||
void KHandleTable::Register(Handle handle, KAutoObject* obj, u16 type) {
|
||||
KScopedDisableDispatch dd(kernel);
|
||||
KScopedSpinLock lk(m_lock);
|
||||
|
||||
// Unpack the handle.
|
||||
|
||||
@@ -69,6 +69,7 @@ public:
|
||||
template <typename T = KAutoObject>
|
||||
KScopedAutoObject<T> GetObjectWithoutPseudoHandle(Handle handle) const {
|
||||
// Lock and look up in table.
|
||||
KScopedDisableDispatch dd(kernel);
|
||||
KScopedSpinLock lk(m_lock);
|
||||
|
||||
if constexpr (std::is_same_v<T, KAutoObject>) {
|
||||
@@ -123,6 +124,7 @@ public:
|
||||
size_t num_opened;
|
||||
{
|
||||
// Lock the table.
|
||||
KScopedDisableDispatch dd(kernel);
|
||||
KScopedSpinLock lk(m_lock);
|
||||
for (num_opened = 0; num_opened < num_handles; num_opened++) {
|
||||
// Get the current handle.
|
||||
|
||||
@@ -59,6 +59,7 @@ void SetupMainThread(Core::System& system, KProcess& owner_process, u32 priority
|
||||
thread->GetContext64().cpu_registers[0] = 0;
|
||||
thread->GetContext32().cpu_registers[1] = thread_handle;
|
||||
thread->GetContext64().cpu_registers[1] = thread_handle;
|
||||
thread->DisableDispatch();
|
||||
|
||||
auto& kernel = system.Kernel();
|
||||
// Threads by default are dormant, wake up the main thread so it runs when the scheduler fires
|
||||
|
||||
@@ -376,20 +376,18 @@ void KScheduler::ClearSchedulerUpdateNeeded(KernelCore& kernel) {
|
||||
}
|
||||
|
||||
void KScheduler::DisableScheduling(KernelCore& kernel) {
|
||||
if (auto* scheduler = kernel.CurrentScheduler(); scheduler) {
|
||||
ASSERT(scheduler->GetCurrentThread()->GetDisableDispatchCount() >= 0);
|
||||
scheduler->GetCurrentThread()->DisableDispatch();
|
||||
}
|
||||
ASSERT(GetCurrentThreadPointer(kernel)->GetDisableDispatchCount() >= 0);
|
||||
GetCurrentThreadPointer(kernel)->DisableDispatch();
|
||||
}
|
||||
|
||||
void KScheduler::EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling) {
|
||||
if (auto* scheduler = kernel.CurrentScheduler(); scheduler) {
|
||||
ASSERT(scheduler->GetCurrentThread()->GetDisableDispatchCount() >= 1);
|
||||
if (scheduler->GetCurrentThread()->GetDisableDispatchCount() >= 1) {
|
||||
scheduler->GetCurrentThread()->EnableDispatch();
|
||||
}
|
||||
ASSERT(GetCurrentThreadPointer(kernel)->GetDisableDispatchCount() >= 1);
|
||||
|
||||
if (GetCurrentThreadPointer(kernel)->GetDisableDispatchCount() > 1) {
|
||||
GetCurrentThreadPointer(kernel)->EnableDispatch();
|
||||
} else {
|
||||
RescheduleCores(kernel, cores_needing_scheduling);
|
||||
}
|
||||
RescheduleCores(kernel, cores_needing_scheduling);
|
||||
}
|
||||
|
||||
u64 KScheduler::UpdateHighestPriorityThreads(KernelCore& kernel) {
|
||||
@@ -617,13 +615,17 @@ KScheduler::KScheduler(Core::System& system_, s32 core_id_) : system{system_}, c
|
||||
state.highest_priority_thread = nullptr;
|
||||
}
|
||||
|
||||
KScheduler::~KScheduler() {
|
||||
void KScheduler::Finalize() {
|
||||
if (idle_thread) {
|
||||
idle_thread->Close();
|
||||
idle_thread = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
KScheduler::~KScheduler() {
|
||||
ASSERT(!idle_thread);
|
||||
}
|
||||
|
||||
KThread* KScheduler::GetCurrentThread() const {
|
||||
if (auto result = current_thread.load(); result) {
|
||||
return result;
|
||||
@@ -642,10 +644,12 @@ void KScheduler::RescheduleCurrentCore() {
|
||||
if (phys_core.IsInterrupted()) {
|
||||
phys_core.ClearInterrupt();
|
||||
}
|
||||
|
||||
guard.Lock();
|
||||
if (state.needs_scheduling.load()) {
|
||||
Schedule();
|
||||
} else {
|
||||
GetCurrentThread()->EnableDispatch();
|
||||
guard.Unlock();
|
||||
}
|
||||
}
|
||||
@@ -655,26 +659,33 @@ void KScheduler::OnThreadStart() {
|
||||
}
|
||||
|
||||
void KScheduler::Unload(KThread* thread) {
|
||||
ASSERT(thread);
|
||||
|
||||
LOG_TRACE(Kernel, "core {}, unload thread {}", core_id, thread ? thread->GetName() : "nullptr");
|
||||
|
||||
if (thread) {
|
||||
if (thread->IsCallingSvc()) {
|
||||
thread->ClearIsCallingSvc();
|
||||
}
|
||||
if (!thread->IsTerminationRequested()) {
|
||||
prev_thread = thread;
|
||||
|
||||
Core::ARM_Interface& cpu_core = system.ArmInterface(core_id);
|
||||
cpu_core.SaveContext(thread->GetContext32());
|
||||
cpu_core.SaveContext(thread->GetContext64());
|
||||
// Save the TPIDR_EL0 system register in case it was modified.
|
||||
thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0());
|
||||
cpu_core.ClearExclusiveState();
|
||||
} else {
|
||||
prev_thread = nullptr;
|
||||
}
|
||||
thread->context_guard.Unlock();
|
||||
if (thread->IsCallingSvc()) {
|
||||
thread->ClearIsCallingSvc();
|
||||
}
|
||||
|
||||
auto& physical_core = system.Kernel().PhysicalCore(core_id);
|
||||
if (!physical_core.IsInitialized()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Core::ARM_Interface& cpu_core = physical_core.ArmInterface();
|
||||
cpu_core.SaveContext(thread->GetContext32());
|
||||
cpu_core.SaveContext(thread->GetContext64());
|
||||
// Save the TPIDR_EL0 system register in case it was modified.
|
||||
thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0());
|
||||
cpu_core.ClearExclusiveState();
|
||||
|
||||
if (!thread->IsTerminationRequested() && thread->GetActiveCore() == core_id) {
|
||||
prev_thread = thread;
|
||||
} else {
|
||||
prev_thread = nullptr;
|
||||
}
|
||||
|
||||
thread->context_guard.Unlock();
|
||||
}
|
||||
|
||||
void KScheduler::Reload(KThread* thread) {
|
||||
@@ -683,11 +694,6 @@ void KScheduler::Reload(KThread* thread) {
|
||||
if (thread) {
|
||||
ASSERT_MSG(thread->GetState() == ThreadState::Runnable, "Thread must be runnable.");
|
||||
|
||||
auto* const thread_owner_process = thread->GetOwnerProcess();
|
||||
if (thread_owner_process != nullptr) {
|
||||
system.Kernel().MakeCurrentProcess(thread_owner_process);
|
||||
}
|
||||
|
||||
Core::ARM_Interface& cpu_core = system.ArmInterface(core_id);
|
||||
cpu_core.LoadContext(thread->GetContext32());
|
||||
cpu_core.LoadContext(thread->GetContext64());
|
||||
@@ -705,7 +711,7 @@ void KScheduler::SwitchContextStep2() {
|
||||
}
|
||||
|
||||
void KScheduler::ScheduleImpl() {
|
||||
KThread* previous_thread = current_thread.load();
|
||||
KThread* previous_thread = GetCurrentThread();
|
||||
KThread* next_thread = state.highest_priority_thread;
|
||||
|
||||
state.needs_scheduling = false;
|
||||
@@ -717,10 +723,15 @@ void KScheduler::ScheduleImpl() {
|
||||
|
||||
// If we're not actually switching thread, there's nothing to do.
|
||||
if (next_thread == current_thread.load()) {
|
||||
previous_thread->EnableDispatch();
|
||||
guard.Unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
if (next_thread->GetCurrentCore() != core_id) {
|
||||
next_thread->SetCurrentCore(core_id);
|
||||
}
|
||||
|
||||
current_thread.store(next_thread);
|
||||
|
||||
KProcess* const previous_process = system.Kernel().CurrentProcess();
|
||||
@@ -731,11 +742,7 @@ void KScheduler::ScheduleImpl() {
|
||||
Unload(previous_thread);
|
||||
|
||||
std::shared_ptr<Common::Fiber>* old_context;
|
||||
if (previous_thread != nullptr) {
|
||||
old_context = &previous_thread->GetHostContext();
|
||||
} else {
|
||||
old_context = &idle_thread->GetHostContext();
|
||||
}
|
||||
old_context = &previous_thread->GetHostContext();
|
||||
guard.Unlock();
|
||||
|
||||
Common::Fiber::YieldTo(*old_context, *switch_fiber);
|
||||
|
||||
@@ -33,6 +33,8 @@ public:
|
||||
explicit KScheduler(Core::System& system_, s32 core_id_);
|
||||
~KScheduler();
|
||||
|
||||
void Finalize();
|
||||
|
||||
/// Reschedules to the next available thread (call after current thread is suspended)
|
||||
void RescheduleCurrentCore();
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "common/fiber.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "common/settings.h"
|
||||
#include "common/thread_queue_list.h"
|
||||
#include "core/core.h"
|
||||
#include "core/cpu_manager.h"
|
||||
@@ -188,7 +189,7 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s
|
||||
// Setup the stack parameters.
|
||||
StackParameters& sp = GetStackParameters();
|
||||
sp.cur_thread = this;
|
||||
sp.disable_count = 1;
|
||||
sp.disable_count = 0;
|
||||
SetInExceptionHandler();
|
||||
|
||||
// Set thread ID.
|
||||
@@ -215,9 +216,10 @@ ResultCode KThread::InitializeThread(KThread* thread, KThreadFunction func, uint
|
||||
// Initialize the thread.
|
||||
R_TRY(thread->Initialize(func, arg, user_stack_top, prio, core, owner, type));
|
||||
|
||||
// Initialize host context.
|
||||
// Initialize emulation parameters.
|
||||
thread->host_context =
|
||||
std::make_shared<Common::Fiber>(std::move(init_func), init_func_parameter);
|
||||
thread->is_single_core = !Settings::values.use_multi_core.GetValue();
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
@@ -970,6 +972,9 @@ ResultCode KThread::Run() {
|
||||
|
||||
// Set our state and finish.
|
||||
SetState(ThreadState::Runnable);
|
||||
|
||||
DisableDispatch();
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
}
|
||||
@@ -1054,4 +1059,16 @@ s32 GetCurrentCoreId(KernelCore& kernel) {
|
||||
return GetCurrentThread(kernel).GetCurrentCore();
|
||||
}
|
||||
|
||||
KScopedDisableDispatch::~KScopedDisableDispatch() {
|
||||
if (GetCurrentThread(kernel).GetDisableDispatchCount() <= 1) {
|
||||
auto scheduler = kernel.CurrentScheduler();
|
||||
|
||||
if (scheduler) {
|
||||
scheduler->RescheduleCurrentCore();
|
||||
}
|
||||
} else {
|
||||
GetCurrentThread(kernel).EnableDispatch();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -450,16 +450,39 @@ public:
|
||||
sleeping_queue = q;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsKernelThread() const {
|
||||
return GetActiveCore() == 3;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsDispatchTrackingDisabled() const {
|
||||
return is_single_core || IsKernelThread();
|
||||
}
|
||||
|
||||
[[nodiscard]] s32 GetDisableDispatchCount() const {
|
||||
if (IsDispatchTrackingDisabled()) {
|
||||
// TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch.
|
||||
return 1;
|
||||
}
|
||||
|
||||
return this->GetStackParameters().disable_count;
|
||||
}
|
||||
|
||||
void DisableDispatch() {
|
||||
if (IsDispatchTrackingDisabled()) {
|
||||
// TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch.
|
||||
return;
|
||||
}
|
||||
|
||||
ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() >= 0);
|
||||
this->GetStackParameters().disable_count++;
|
||||
}
|
||||
|
||||
void EnableDispatch() {
|
||||
if (IsDispatchTrackingDisabled()) {
|
||||
// TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch.
|
||||
return;
|
||||
}
|
||||
|
||||
ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() > 0);
|
||||
this->GetStackParameters().disable_count--;
|
||||
}
|
||||
@@ -708,6 +731,7 @@ private:
|
||||
|
||||
// For emulation
|
||||
std::shared_ptr<Common::Fiber> host_context{};
|
||||
bool is_single_core{};
|
||||
|
||||
// For debugging
|
||||
std::vector<KSynchronizationObject*> wait_objects_for_debugging;
|
||||
@@ -752,4 +776,16 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class KScopedDisableDispatch {
|
||||
public:
|
||||
[[nodiscard]] explicit KScopedDisableDispatch(KernelCore& kernel_) : kernel{kernel_} {
|
||||
GetCurrentThread(kernel).DisableDispatch();
|
||||
}
|
||||
|
||||
~KScopedDisableDispatch();
|
||||
|
||||
private:
|
||||
KernelCore& kernel;
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -85,8 +85,9 @@ struct KernelCore::Impl {
|
||||
}
|
||||
|
||||
void InitializeCores() {
|
||||
for (auto& core : cores) {
|
||||
core.Initialize(current_process->Is64BitProcess());
|
||||
for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
|
||||
cores[core_id].Initialize(current_process->Is64BitProcess());
|
||||
system.Memory().SetCurrentPageTable(*current_process, core_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,15 +132,6 @@ struct KernelCore::Impl {
|
||||
next_user_process_id = KProcess::ProcessIDMin;
|
||||
next_thread_id = 1;
|
||||
|
||||
for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
|
||||
if (suspend_threads[core_id]) {
|
||||
suspend_threads[core_id]->Close();
|
||||
suspend_threads[core_id] = nullptr;
|
||||
}
|
||||
|
||||
schedulers[core_id].reset();
|
||||
}
|
||||
|
||||
cores.clear();
|
||||
|
||||
global_handle_table->Finalize();
|
||||
@@ -167,6 +159,16 @@ struct KernelCore::Impl {
|
||||
CleanupObject(time_shared_mem);
|
||||
CleanupObject(system_resource_limit);
|
||||
|
||||
for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
|
||||
if (suspend_threads[core_id]) {
|
||||
suspend_threads[core_id]->Close();
|
||||
suspend_threads[core_id] = nullptr;
|
||||
}
|
||||
|
||||
schedulers[core_id]->Finalize();
|
||||
schedulers[core_id].reset();
|
||||
}
|
||||
|
||||
// Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others
|
||||
next_host_thread_id = Core::Hardware::NUM_CPU_CORES;
|
||||
|
||||
@@ -257,14 +259,6 @@ struct KernelCore::Impl {
|
||||
|
||||
void MakeCurrentProcess(KProcess* process) {
|
||||
current_process = process;
|
||||
if (process == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const u32 core_id = GetCurrentHostThreadID();
|
||||
if (core_id < Core::Hardware::NUM_CPU_CORES) {
|
||||
system.Memory().SetCurrentPageTable(*process, core_id);
|
||||
}
|
||||
}
|
||||
|
||||
static inline thread_local u32 host_thread_id = UINT32_MAX;
|
||||
@@ -827,16 +821,20 @@ const Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) const {
|
||||
return impl->cores[id];
|
||||
}
|
||||
|
||||
size_t KernelCore::CurrentPhysicalCoreIndex() const {
|
||||
const u32 core_id = impl->GetCurrentHostThreadID();
|
||||
if (core_id >= Core::Hardware::NUM_CPU_CORES) {
|
||||
return Core::Hardware::NUM_CPU_CORES - 1;
|
||||
}
|
||||
return core_id;
|
||||
}
|
||||
|
||||
Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() {
|
||||
u32 core_id = impl->GetCurrentHostThreadID();
|
||||
ASSERT(core_id < Core::Hardware::NUM_CPU_CORES);
|
||||
return impl->cores[core_id];
|
||||
return impl->cores[CurrentPhysicalCoreIndex()];
|
||||
}
|
||||
|
||||
const Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() const {
|
||||
u32 core_id = impl->GetCurrentHostThreadID();
|
||||
ASSERT(core_id < Core::Hardware::NUM_CPU_CORES);
|
||||
return impl->cores[core_id];
|
||||
return impl->cores[CurrentPhysicalCoreIndex()];
|
||||
}
|
||||
|
||||
Kernel::KScheduler* KernelCore::CurrentScheduler() {
|
||||
@@ -1029,6 +1027,9 @@ void KernelCore::Suspend(bool in_suspention) {
|
||||
impl->suspend_threads[core_id]->SetState(state);
|
||||
impl->suspend_threads[core_id]->SetWaitReasonForDebugging(
|
||||
ThreadWaitReasonForDebugging::Suspended);
|
||||
if (!should_suspend) {
|
||||
impl->suspend_threads[core_id]->DisableDispatch();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1043,13 +1044,11 @@ void KernelCore::ExceptionalExit() {
|
||||
}
|
||||
|
||||
void KernelCore::EnterSVCProfile() {
|
||||
std::size_t core = impl->GetCurrentHostThreadID();
|
||||
impl->svc_ticks[core] = MicroProfileEnter(MICROPROFILE_TOKEN(Kernel_SVC));
|
||||
impl->svc_ticks[CurrentPhysicalCoreIndex()] = MicroProfileEnter(MICROPROFILE_TOKEN(Kernel_SVC));
|
||||
}
|
||||
|
||||
void KernelCore::ExitSVCProfile() {
|
||||
std::size_t core = impl->GetCurrentHostThreadID();
|
||||
MicroProfileLeave(MICROPROFILE_TOKEN(Kernel_SVC), impl->svc_ticks[core]);
|
||||
MicroProfileLeave(MICROPROFILE_TOKEN(Kernel_SVC), impl->svc_ticks[CurrentPhysicalCoreIndex()]);
|
||||
}
|
||||
|
||||
std::weak_ptr<Kernel::ServiceThread> KernelCore::CreateServiceThread(const std::string& name) {
|
||||
|
||||
@@ -146,6 +146,9 @@ public:
|
||||
/// Gets the an instance of the respective physical CPU core.
|
||||
const Kernel::PhysicalCore& PhysicalCore(std::size_t id) const;
|
||||
|
||||
/// Gets the current physical core index for the running host thread.
|
||||
std::size_t CurrentPhysicalCoreIndex() const;
|
||||
|
||||
/// Gets the sole instance of the Scheduler at the current running core.
|
||||
Kernel::KScheduler* CurrentScheduler();
|
||||
|
||||
|
||||
@@ -877,7 +877,7 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, Handle
|
||||
const u64 thread_ticks = current_thread->GetCpuTime();
|
||||
|
||||
out_ticks = thread_ticks + (core_timing.GetCPUTicks() - prev_ctx_ticks);
|
||||
} else if (same_thread && info_sub_id == system.CurrentCoreIndex()) {
|
||||
} else if (same_thread && info_sub_id == system.Kernel().CurrentPhysicalCoreIndex()) {
|
||||
out_ticks = core_timing.GetCPUTicks() - prev_ctx_ticks;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included
|
||||
|
||||
#include "common/string_util.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/service/ngct/ngct.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Service::NGCT {
|
||||
|
||||
class IService final : public ServiceFramework<IService> {
|
||||
public:
|
||||
explicit IService(Core::System& system_) : ServiceFramework{system_, "ngct:u"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "Match"},
|
||||
{1, &IService::Filter, "Filter"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
private:
|
||||
void Filter(Kernel::HLERequestContext& ctx) {
|
||||
const auto buffer = ctx.ReadBuffer();
|
||||
const auto text = Common::StringFromFixedZeroTerminatedBuffer(
|
||||
reinterpret_cast<const char*>(buffer.data()), buffer.size());
|
||||
|
||||
LOG_WARNING(Service_NGCT, "(STUBBED) called, text={}", text);
|
||||
|
||||
// Return the same string since we don't censor anything
|
||||
ctx.WriteBuffer(buffer);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
};
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
|
||||
std::make_shared<IService>(system)->InstallAsService(system.ServiceManager());
|
||||
}
|
||||
|
||||
} // namespace Service::NGCT
|
||||
@@ -1,20 +0,0 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Service::SM {
|
||||
class ServiceManager;
|
||||
}
|
||||
|
||||
namespace Service::NGCT {
|
||||
|
||||
/// Registers all NGCT services with the specified service manager.
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
|
||||
|
||||
} // namespace Service::NGCT
|
||||
@@ -277,45 +277,37 @@ private:
|
||||
void GetCurrentNetworkProfile(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_NIFM, "(STUBBED) called");
|
||||
|
||||
const auto net_iface = Network::GetSelectedNetworkInterface();
|
||||
|
||||
const SfNetworkProfileData network_profile_data = [&net_iface] {
|
||||
if (!net_iface) {
|
||||
return SfNetworkProfileData{};
|
||||
}
|
||||
|
||||
return SfNetworkProfileData{
|
||||
.ip_setting_data{
|
||||
.ip_address_setting{
|
||||
.is_automatic{true},
|
||||
.current_address{Network::TranslateIPv4(net_iface->ip_address)},
|
||||
.subnet_mask{Network::TranslateIPv4(net_iface->subnet_mask)},
|
||||
.gateway{Network::TranslateIPv4(net_iface->gateway)},
|
||||
},
|
||||
.dns_setting{
|
||||
.is_automatic{true},
|
||||
.primary_dns{1, 1, 1, 1},
|
||||
.secondary_dns{1, 0, 0, 1},
|
||||
},
|
||||
.proxy_setting{
|
||||
.enabled{false},
|
||||
.port{},
|
||||
.proxy_server{},
|
||||
.automatic_auth_enabled{},
|
||||
.user{},
|
||||
.password{},
|
||||
},
|
||||
.mtu{1500},
|
||||
const SfNetworkProfileData network_profile_data{
|
||||
.ip_setting_data{
|
||||
.ip_address_setting{
|
||||
.is_automatic{true},
|
||||
.current_address{192, 168, 1, 100},
|
||||
.subnet_mask{255, 255, 255, 0},
|
||||
.gateway{192, 168, 1, 1},
|
||||
},
|
||||
.uuid{0xdeadbeef, 0xdeadbeef},
|
||||
.network_name{"yuzu Network"},
|
||||
.wireless_setting_data{
|
||||
.ssid_length{12},
|
||||
.ssid{"yuzu Network"},
|
||||
.passphrase{"yuzupassword"},
|
||||
.dns_setting{
|
||||
.is_automatic{true},
|
||||
.primary_dns{1, 1, 1, 1},
|
||||
.secondary_dns{1, 0, 0, 1},
|
||||
},
|
||||
};
|
||||
}();
|
||||
.proxy_setting{
|
||||
.enabled{false},
|
||||
.port{},
|
||||
.proxy_server{},
|
||||
.automatic_auth_enabled{},
|
||||
.user{},
|
||||
.password{},
|
||||
},
|
||||
.mtu{1500},
|
||||
},
|
||||
.uuid{0xdeadbeef, 0xdeadbeef},
|
||||
.network_name{"yuzu Network"},
|
||||
.wireless_setting_data{
|
||||
.ssid_length{12},
|
||||
.ssid{"yuzu Network"},
|
||||
.passphrase{"yuzupassword"},
|
||||
},
|
||||
};
|
||||
|
||||
ctx.WriteBuffer(network_profile_data);
|
||||
|
||||
@@ -360,33 +352,38 @@ private:
|
||||
LOG_WARNING(Service_NIFM, "(STUBBED) called");
|
||||
|
||||
struct IpConfigInfo {
|
||||
IpAddressSetting ip_address_setting{};
|
||||
DnsSetting dns_setting{};
|
||||
IpAddressSetting ip_address_setting;
|
||||
DnsSetting dns_setting;
|
||||
};
|
||||
static_assert(sizeof(IpConfigInfo) == sizeof(IpAddressSetting) + sizeof(DnsSetting),
|
||||
"IpConfigInfo has incorrect size.");
|
||||
|
||||
const auto net_iface = Network::GetSelectedNetworkInterface();
|
||||
IpConfigInfo ip_config_info{
|
||||
.ip_address_setting{
|
||||
.is_automatic{true},
|
||||
.current_address{0, 0, 0, 0},
|
||||
.subnet_mask{255, 255, 255, 0},
|
||||
.gateway{192, 168, 1, 1},
|
||||
},
|
||||
.dns_setting{
|
||||
.is_automatic{true},
|
||||
.primary_dns{1, 1, 1, 1},
|
||||
.secondary_dns{1, 0, 0, 1},
|
||||
},
|
||||
};
|
||||
|
||||
const IpConfigInfo ip_config_info = [&net_iface] {
|
||||
if (!net_iface) {
|
||||
return IpConfigInfo{};
|
||||
}
|
||||
const auto iface = Network::GetSelectedNetworkInterface();
|
||||
if (iface) {
|
||||
ip_config_info.ip_address_setting =
|
||||
IpAddressSetting{.is_automatic{true},
|
||||
.current_address{Network::TranslateIPv4(iface->ip_address)},
|
||||
.subnet_mask{Network::TranslateIPv4(iface->subnet_mask)},
|
||||
.gateway{Network::TranslateIPv4(iface->gateway)}};
|
||||
|
||||
return IpConfigInfo{
|
||||
.ip_address_setting{
|
||||
.is_automatic{true},
|
||||
.current_address{Network::TranslateIPv4(net_iface->ip_address)},
|
||||
.subnet_mask{Network::TranslateIPv4(net_iface->subnet_mask)},
|
||||
.gateway{Network::TranslateIPv4(net_iface->gateway)},
|
||||
},
|
||||
.dns_setting{
|
||||
.is_automatic{true},
|
||||
.primary_dns{1, 1, 1, 1},
|
||||
.secondary_dns{1, 0, 0, 1},
|
||||
},
|
||||
};
|
||||
}();
|
||||
} else {
|
||||
LOG_ERROR(Service_NIFM,
|
||||
"Couldn't get host network configuration info, using default values");
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2 + (sizeof(IpConfigInfo) + 3) / sizeof(u32)};
|
||||
rb.Push(ResultSuccess);
|
||||
|
||||
@@ -9,17 +9,20 @@
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/k_writable_event.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
#include "core/hle/service/nvflinger/buffer_queue.h"
|
||||
|
||||
namespace Service::NVFlinger {
|
||||
|
||||
BufferQueue::BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_)
|
||||
: id(id_), layer_id(layer_id_), buffer_wait_event{kernel} {
|
||||
Kernel::KAutoObject::Create(std::addressof(buffer_wait_event));
|
||||
buffer_wait_event.Initialize("BufferQueue:WaitEvent");
|
||||
BufferQueue::BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_,
|
||||
KernelHelpers::ServiceContext& service_context_)
|
||||
: id(id_), layer_id(layer_id_), service_context{service_context_} {
|
||||
buffer_wait_event = service_context.CreateEvent("BufferQueue:WaitEvent");
|
||||
}
|
||||
|
||||
BufferQueue::~BufferQueue() = default;
|
||||
BufferQueue::~BufferQueue() {
|
||||
service_context.CloseEvent(buffer_wait_event);
|
||||
}
|
||||
|
||||
void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) {
|
||||
ASSERT(slot < buffer_slots);
|
||||
@@ -41,7 +44,7 @@ void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer)
|
||||
.multi_fence = {},
|
||||
};
|
||||
|
||||
buffer_wait_event.GetWritableEvent().Signal();
|
||||
buffer_wait_event->GetWritableEvent().Signal();
|
||||
}
|
||||
|
||||
std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::DequeueBuffer(u32 width,
|
||||
@@ -119,7 +122,7 @@ void BufferQueue::CancelBuffer(u32 slot, const Service::Nvidia::MultiFence& mult
|
||||
}
|
||||
free_buffers_condition.notify_one();
|
||||
|
||||
buffer_wait_event.GetWritableEvent().Signal();
|
||||
buffer_wait_event->GetWritableEvent().Signal();
|
||||
}
|
||||
|
||||
std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::AcquireBuffer() {
|
||||
@@ -154,7 +157,7 @@ void BufferQueue::ReleaseBuffer(u32 slot) {
|
||||
}
|
||||
free_buffers_condition.notify_one();
|
||||
|
||||
buffer_wait_event.GetWritableEvent().Signal();
|
||||
buffer_wait_event->GetWritableEvent().Signal();
|
||||
}
|
||||
|
||||
void BufferQueue::Connect() {
|
||||
@@ -169,7 +172,7 @@ void BufferQueue::Disconnect() {
|
||||
std::unique_lock lock{queue_sequence_mutex};
|
||||
queue_sequence.clear();
|
||||
}
|
||||
buffer_wait_event.GetWritableEvent().Signal();
|
||||
buffer_wait_event->GetWritableEvent().Signal();
|
||||
is_connect = false;
|
||||
free_buffers_condition.notify_one();
|
||||
}
|
||||
@@ -189,11 +192,11 @@ u32 BufferQueue::Query(QueryType type) {
|
||||
}
|
||||
|
||||
Kernel::KWritableEvent& BufferQueue::GetWritableBufferWaitEvent() {
|
||||
return buffer_wait_event.GetWritableEvent();
|
||||
return buffer_wait_event->GetWritableEvent();
|
||||
}
|
||||
|
||||
Kernel::KReadableEvent& BufferQueue::GetBufferWaitEvent() {
|
||||
return buffer_wait_event.GetReadableEvent();
|
||||
return buffer_wait_event->GetReadableEvent();
|
||||
}
|
||||
|
||||
} // namespace Service::NVFlinger
|
||||
|
||||
@@ -24,6 +24,10 @@ class KReadableEvent;
|
||||
class KWritableEvent;
|
||||
} // namespace Kernel
|
||||
|
||||
namespace Service::KernelHelpers {
|
||||
class ServiceContext;
|
||||
} // namespace Service::KernelHelpers
|
||||
|
||||
namespace Service::NVFlinger {
|
||||
|
||||
constexpr u32 buffer_slots = 0x40;
|
||||
@@ -54,7 +58,8 @@ public:
|
||||
NativeWindowFormat = 2,
|
||||
};
|
||||
|
||||
explicit BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_);
|
||||
explicit BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_,
|
||||
KernelHelpers::ServiceContext& service_context_);
|
||||
~BufferQueue();
|
||||
|
||||
enum class BufferTransformFlags : u32 {
|
||||
@@ -130,12 +135,14 @@ private:
|
||||
std::list<u32> free_buffers;
|
||||
std::array<Buffer, buffer_slots> buffers;
|
||||
std::list<u32> queue_sequence;
|
||||
Kernel::KEvent buffer_wait_event;
|
||||
Kernel::KEvent* buffer_wait_event{};
|
||||
|
||||
std::mutex free_buffers_mutex;
|
||||
std::condition_variable free_buffers_condition;
|
||||
|
||||
std::mutex queue_sequence_mutex;
|
||||
|
||||
KernelHelpers::ServiceContext& service_context;
|
||||
};
|
||||
|
||||
} // namespace Service::NVFlinger
|
||||
|
||||
@@ -147,7 +147,7 @@ std::optional<u64> NVFlinger::CreateLayer(u64 display_id) {
|
||||
void NVFlinger::CreateLayerAtId(VI::Display& display, u64 layer_id) {
|
||||
const u32 buffer_queue_id = next_buffer_queue_id++;
|
||||
buffer_queues.emplace_back(
|
||||
std::make_unique<BufferQueue>(system.Kernel(), buffer_queue_id, layer_id));
|
||||
std::make_unique<BufferQueue>(system.Kernel(), buffer_queue_id, layer_id, service_context));
|
||||
display.CreateLayer(layer_id, *buffer_queues.back());
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,6 @@
|
||||
#include "core/hle/service/ncm/ncm.h"
|
||||
#include "core/hle/service/nfc/nfc.h"
|
||||
#include "core/hle/service/nfp/nfp.h"
|
||||
#include "core/hle/service/ngct/ngct.h"
|
||||
#include "core/hle/service/nifm/nifm.h"
|
||||
#include "core/hle/service/nim/nim.h"
|
||||
#include "core/hle/service/npns/npns.h"
|
||||
@@ -272,7 +271,6 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
|
||||
NCM::InstallInterfaces(*sm, system);
|
||||
NFC::InstallInterfaces(*sm, system);
|
||||
NFP::InstallInterfaces(*sm, system);
|
||||
NGCT::InstallInterfaces(*sm, system);
|
||||
NIFM::InstallInterfaces(*sm, system);
|
||||
NIM::InstallInterfaces(*sm, system);
|
||||
NPNS::InstallInterfaces(*sm, system);
|
||||
|
||||
@@ -37,73 +37,73 @@ std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
|
||||
AF_INET, GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_INCLUDE_GATEWAYS,
|
||||
nullptr, adapter_addresses.data(), &buf_size);
|
||||
|
||||
if (ret != ERROR_BUFFER_OVERFLOW) {
|
||||
if (ret == ERROR_BUFFER_OVERFLOW) {
|
||||
adapter_addresses.resize((buf_size / sizeof(IP_ADAPTER_ADDRESSES)) + 1);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
adapter_addresses.resize((buf_size / sizeof(IP_ADAPTER_ADDRESSES)) + 1);
|
||||
}
|
||||
|
||||
if (ret != NO_ERROR) {
|
||||
if (ret == NO_ERROR) {
|
||||
std::vector<NetworkInterface> result;
|
||||
|
||||
for (auto current_address = adapter_addresses.data(); current_address != nullptr;
|
||||
current_address = current_address->Next) {
|
||||
if (current_address->FirstUnicastAddress == nullptr ||
|
||||
current_address->FirstUnicastAddress->Address.lpSockaddr == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (current_address->OperStatus != IfOperStatusUp) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto ip_addr = Common::BitCast<struct sockaddr_in>(
|
||||
*current_address->FirstUnicastAddress->Address.lpSockaddr)
|
||||
.sin_addr;
|
||||
|
||||
ULONG mask = 0;
|
||||
if (ConvertLengthToIpv4Mask(current_address->FirstUnicastAddress->OnLinkPrefixLength,
|
||||
&mask) != NO_ERROR) {
|
||||
LOG_ERROR(Network, "Failed to convert IPv4 prefix length to subnet mask");
|
||||
continue;
|
||||
}
|
||||
|
||||
struct in_addr gateway = {.S_un{.S_addr{0}}};
|
||||
if (current_address->FirstGatewayAddress != nullptr &&
|
||||
current_address->FirstGatewayAddress->Address.lpSockaddr != nullptr) {
|
||||
gateway = Common::BitCast<struct sockaddr_in>(
|
||||
*current_address->FirstGatewayAddress->Address.lpSockaddr)
|
||||
.sin_addr;
|
||||
}
|
||||
|
||||
result.push_back(NetworkInterface{
|
||||
.name{Common::UTF16ToUTF8(std::wstring{current_address->FriendlyName})},
|
||||
.ip_address{ip_addr},
|
||||
.subnet_mask = in_addr{.S_un{.S_addr{mask}}},
|
||||
.gateway = gateway});
|
||||
}
|
||||
|
||||
return result;
|
||||
} else {
|
||||
LOG_ERROR(Network, "Failed to get network interfaces with GetAdaptersAddresses");
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<NetworkInterface> result;
|
||||
|
||||
for (auto current_address = adapter_addresses.data(); current_address != nullptr;
|
||||
current_address = current_address->Next) {
|
||||
if (current_address->FirstUnicastAddress == nullptr ||
|
||||
current_address->FirstUnicastAddress->Address.lpSockaddr == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (current_address->OperStatus != IfOperStatusUp) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto ip_addr = Common::BitCast<struct sockaddr_in>(
|
||||
*current_address->FirstUnicastAddress->Address.lpSockaddr)
|
||||
.sin_addr;
|
||||
|
||||
ULONG mask = 0;
|
||||
if (ConvertLengthToIpv4Mask(current_address->FirstUnicastAddress->OnLinkPrefixLength,
|
||||
&mask) != NO_ERROR) {
|
||||
LOG_ERROR(Network, "Failed to convert IPv4 prefix length to subnet mask");
|
||||
continue;
|
||||
}
|
||||
|
||||
struct in_addr gateway = {.S_un{.S_addr{0}}};
|
||||
if (current_address->FirstGatewayAddress != nullptr &&
|
||||
current_address->FirstGatewayAddress->Address.lpSockaddr != nullptr) {
|
||||
gateway = Common::BitCast<struct sockaddr_in>(
|
||||
*current_address->FirstGatewayAddress->Address.lpSockaddr)
|
||||
.sin_addr;
|
||||
}
|
||||
|
||||
result.emplace_back(NetworkInterface{
|
||||
.name{Common::UTF16ToUTF8(std::wstring{current_address->FriendlyName})},
|
||||
.ip_address{ip_addr},
|
||||
.subnet_mask = in_addr{.S_un{.S_addr{mask}}},
|
||||
.gateway = gateway});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
|
||||
std::vector<NetworkInterface> result;
|
||||
|
||||
struct ifaddrs* ifaddr = nullptr;
|
||||
|
||||
if (getifaddrs(&ifaddr) != 0) {
|
||||
LOG_ERROR(Network, "Failed to get network interfaces with getifaddrs: {}",
|
||||
std::strerror(errno));
|
||||
return {};
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<NetworkInterface> result;
|
||||
|
||||
for (auto ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
|
||||
if (ifa->ifa_addr == nullptr || ifa->ifa_netmask == nullptr) {
|
||||
continue;
|
||||
@@ -117,62 +117,55 @@ std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
|
||||
continue;
|
||||
}
|
||||
|
||||
u32 gateway{};
|
||||
|
||||
std::uint32_t gateway{0};
|
||||
std::ifstream file{"/proc/net/route"};
|
||||
if (!file.is_open()) {
|
||||
if (file.is_open()) {
|
||||
|
||||
// ignore header
|
||||
file.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
|
||||
|
||||
bool gateway_found = false;
|
||||
|
||||
for (std::string line; std::getline(file, line);) {
|
||||
std::istringstream iss{line};
|
||||
|
||||
std::string iface_name{};
|
||||
iss >> iface_name;
|
||||
if (iface_name != ifa->ifa_name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
iss >> std::hex;
|
||||
|
||||
std::uint32_t dest{0};
|
||||
iss >> dest;
|
||||
if (dest != 0) {
|
||||
// not the default route
|
||||
continue;
|
||||
}
|
||||
|
||||
iss >> gateway;
|
||||
|
||||
std::uint16_t flags{0};
|
||||
iss >> flags;
|
||||
|
||||
// flag RTF_GATEWAY (defined in <linux/route.h>)
|
||||
if ((flags & 0x2) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
gateway_found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!gateway_found) {
|
||||
gateway = 0;
|
||||
}
|
||||
} else {
|
||||
LOG_ERROR(Network, "Failed to open \"/proc/net/route\"");
|
||||
|
||||
result.emplace_back(NetworkInterface{
|
||||
.name{ifa->ifa_name},
|
||||
.ip_address{Common::BitCast<struct sockaddr_in>(*ifa->ifa_addr).sin_addr},
|
||||
.subnet_mask{Common::BitCast<struct sockaddr_in>(*ifa->ifa_netmask).sin_addr},
|
||||
.gateway{in_addr{.s_addr = gateway}}});
|
||||
continue;
|
||||
}
|
||||
|
||||
// ignore header
|
||||
file.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
|
||||
|
||||
bool gateway_found = false;
|
||||
|
||||
for (std::string line; std::getline(file, line);) {
|
||||
std::istringstream iss{line};
|
||||
|
||||
std::string iface_name;
|
||||
iss >> iface_name;
|
||||
if (iface_name != ifa->ifa_name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
iss >> std::hex;
|
||||
|
||||
u32 dest{};
|
||||
iss >> dest;
|
||||
if (dest != 0) {
|
||||
// not the default route
|
||||
continue;
|
||||
}
|
||||
|
||||
iss >> gateway;
|
||||
|
||||
u16 flags{};
|
||||
iss >> flags;
|
||||
|
||||
// flag RTF_GATEWAY (defined in <linux/route.h>)
|
||||
if ((flags & 0x2) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
gateway_found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!gateway_found) {
|
||||
gateway = 0;
|
||||
}
|
||||
|
||||
result.emplace_back(NetworkInterface{
|
||||
result.push_back(NetworkInterface{
|
||||
.name{ifa->ifa_name},
|
||||
.ip_address{Common::BitCast<struct sockaddr_in>(*ifa->ifa_addr).sin_addr},
|
||||
.subnet_mask{Common::BitCast<struct sockaddr_in>(*ifa->ifa_netmask).sin_addr},
|
||||
@@ -187,11 +180,11 @@ std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
|
||||
#endif
|
||||
|
||||
std::optional<NetworkInterface> GetSelectedNetworkInterface() {
|
||||
const auto& selected_network_interface = Settings::values.network_interface.GetValue();
|
||||
const std::string& selected_network_interface = Settings::values.network_interface.GetValue();
|
||||
const auto network_interfaces = Network::GetAvailableNetworkInterfaces();
|
||||
if (network_interfaces.size() == 0) {
|
||||
LOG_ERROR(Network, "GetAvailableNetworkInterfaces returned no interfaces");
|
||||
return std::nullopt;
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto res =
|
||||
@@ -199,12 +192,12 @@ std::optional<NetworkInterface> GetSelectedNetworkInterface() {
|
||||
return iface.name == selected_network_interface;
|
||||
});
|
||||
|
||||
if (res == network_interfaces.end()) {
|
||||
if (res != network_interfaces.end()) {
|
||||
return *res;
|
||||
} else {
|
||||
LOG_ERROR(Network, "Couldn't find selected interface \"{}\"", selected_network_interface);
|
||||
return std::nullopt;
|
||||
return {};
|
||||
}
|
||||
|
||||
return *res;
|
||||
}
|
||||
|
||||
} // namespace Network
|
||||
|
||||
@@ -333,9 +333,8 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) {
|
||||
return ctx.OpBitcast(ctx.F32[1], ctx.OpISub(ctx.U32[1], index, base));
|
||||
}
|
||||
case IR::Attribute::FrontFace:
|
||||
return ctx.OpSelect(ctx.F32[1], ctx.OpLoad(ctx.U1, ctx.front_face),
|
||||
ctx.OpBitcast(ctx.F32[1], ctx.Const(std::numeric_limits<u32>::max())),
|
||||
ctx.f32_zero_value);
|
||||
return ctx.OpSelect(ctx.U32[1], ctx.OpLoad(ctx.U1, ctx.front_face),
|
||||
ctx.Const(std::numeric_limits<u32>::max()), ctx.u32_zero_value);
|
||||
case IR::Attribute::PointSpriteS:
|
||||
return ctx.OpLoad(ctx.F32[1],
|
||||
ctx.OpAccessChain(ctx.input_f32, ctx.point_coord, ctx.u32_zero_value));
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
#include "shader_recompiler/frontend/maxwell/decode.h"
|
||||
#include "shader_recompiler/frontend/maxwell/structured_control_flow.h"
|
||||
#include "shader_recompiler/frontend/maxwell/translate/translate.h"
|
||||
#include "shader_recompiler/host_translate_info.h"
|
||||
#include "shader_recompiler/object_pool.h"
|
||||
|
||||
namespace Shader::Maxwell {
|
||||
@@ -653,7 +652,7 @@ class TranslatePass {
|
||||
public:
|
||||
TranslatePass(ObjectPool<IR::Inst>& inst_pool_, ObjectPool<IR::Block>& block_pool_,
|
||||
ObjectPool<Statement>& stmt_pool_, Environment& env_, Statement& root_stmt,
|
||||
IR::AbstractSyntaxList& syntax_list_, const HostTranslateInfo& host_info)
|
||||
IR::AbstractSyntaxList& syntax_list_)
|
||||
: stmt_pool{stmt_pool_}, inst_pool{inst_pool_}, block_pool{block_pool_}, env{env_},
|
||||
syntax_list{syntax_list_} {
|
||||
Visit(root_stmt, nullptr, nullptr);
|
||||
@@ -661,9 +660,6 @@ public:
|
||||
IR::Block& first_block{*syntax_list.front().data.block};
|
||||
IR::IREmitter ir(first_block, first_block.begin());
|
||||
ir.Prologue();
|
||||
if (uses_demote_to_helper && host_info.needs_demote_reorder) {
|
||||
DemoteCombinationPass();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -813,14 +809,7 @@ private:
|
||||
}
|
||||
case StatementType::Return: {
|
||||
ensure_block();
|
||||
IR::Block* return_block{block_pool.Create(inst_pool)};
|
||||
IR::IREmitter{*return_block}.Epilogue();
|
||||
current_block->AddBranch(return_block);
|
||||
|
||||
auto& merge{syntax_list.emplace_back()};
|
||||
merge.type = IR::AbstractSyntaxNode::Type::Block;
|
||||
merge.data.block = return_block;
|
||||
|
||||
IR::IREmitter{*current_block}.Epilogue();
|
||||
current_block = nullptr;
|
||||
syntax_list.emplace_back().type = IR::AbstractSyntaxNode::Type::Return;
|
||||
break;
|
||||
@@ -835,7 +824,6 @@ private:
|
||||
auto& merge{syntax_list.emplace_back()};
|
||||
merge.type = IR::AbstractSyntaxNode::Type::Block;
|
||||
merge.data.block = demote_block;
|
||||
uses_demote_to_helper = true;
|
||||
break;
|
||||
}
|
||||
case StatementType::Unreachable: {
|
||||
@@ -867,117 +855,11 @@ private:
|
||||
return block_pool.Create(inst_pool);
|
||||
}
|
||||
|
||||
void DemoteCombinationPass() {
|
||||
using Type = IR::AbstractSyntaxNode::Type;
|
||||
std::vector<IR::Block*> demote_blocks;
|
||||
std::vector<IR::U1> demote_conds;
|
||||
u32 num_epilogues{};
|
||||
u32 branch_depth{};
|
||||
for (const IR::AbstractSyntaxNode& node : syntax_list) {
|
||||
if (node.type == Type::If) {
|
||||
++branch_depth;
|
||||
}
|
||||
if (node.type == Type::EndIf) {
|
||||
--branch_depth;
|
||||
}
|
||||
if (node.type != Type::Block) {
|
||||
continue;
|
||||
}
|
||||
if (branch_depth > 1) {
|
||||
// Skip reordering nested demote branches.
|
||||
continue;
|
||||
}
|
||||
for (const IR::Inst& inst : node.data.block->Instructions()) {
|
||||
const IR::Opcode op{inst.GetOpcode()};
|
||||
if (op == IR::Opcode::DemoteToHelperInvocation) {
|
||||
demote_blocks.push_back(node.data.block);
|
||||
break;
|
||||
}
|
||||
if (op == IR::Opcode::Epilogue) {
|
||||
++num_epilogues;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (demote_blocks.size() == 0) {
|
||||
return;
|
||||
}
|
||||
if (num_epilogues > 1) {
|
||||
LOG_DEBUG(Shader, "Combining demotes with more than one return is not implemented.");
|
||||
return;
|
||||
}
|
||||
s64 last_iterator_offset{};
|
||||
auto& asl{syntax_list};
|
||||
for (const IR::Block* demote_block : demote_blocks) {
|
||||
const auto start_it{asl.begin() + last_iterator_offset};
|
||||
auto asl_it{std::find_if(start_it, asl.end(), [&](const IR::AbstractSyntaxNode& asn) {
|
||||
return asn.type == Type::If && asn.data.if_node.body == demote_block;
|
||||
})};
|
||||
if (asl_it == asl.end()) {
|
||||
// Demote without a conditional branch.
|
||||
// No need to proceed since all fragment instances will be demoted regardless.
|
||||
return;
|
||||
}
|
||||
const IR::Block* const end_if = asl_it->data.if_node.merge;
|
||||
demote_conds.push_back(asl_it->data.if_node.cond);
|
||||
last_iterator_offset = std::distance(asl.begin(), asl_it);
|
||||
|
||||
asl_it = asl.erase(asl_it);
|
||||
asl_it = std::find_if(asl_it, asl.end(), [&](const IR::AbstractSyntaxNode& asn) {
|
||||
return asn.type == Type::Block && asn.data.block == demote_block;
|
||||
});
|
||||
|
||||
asl_it = asl.erase(asl_it);
|
||||
asl_it = std::find_if(asl_it, asl.end(), [&](const IR::AbstractSyntaxNode& asn) {
|
||||
return asn.type == Type::EndIf && asn.data.end_if.merge == end_if;
|
||||
});
|
||||
asl_it = asl.erase(asl_it);
|
||||
}
|
||||
const auto epilogue_func{[](const IR::AbstractSyntaxNode& asn) {
|
||||
if (asn.type != Type::Block) {
|
||||
return false;
|
||||
}
|
||||
for (const auto& inst : asn.data.block->Instructions()) {
|
||||
if (inst.GetOpcode() == IR::Opcode::Epilogue) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}};
|
||||
const auto reverse_it{std::find_if(asl.rbegin(), asl.rend(), epilogue_func)};
|
||||
const auto return_block_it{(reverse_it + 1).base()};
|
||||
|
||||
IR::IREmitter ir{*(return_block_it - 1)->data.block};
|
||||
IR::U1 cond(IR::Value(false));
|
||||
for (const auto& demote_cond : demote_conds) {
|
||||
cond = ir.LogicalOr(cond, demote_cond);
|
||||
}
|
||||
cond.Inst()->DestructiveAddUsage(1);
|
||||
|
||||
IR::AbstractSyntaxNode demote_if_node{};
|
||||
demote_if_node.type = Type::If;
|
||||
demote_if_node.data.if_node.cond = cond;
|
||||
demote_if_node.data.if_node.body = demote_blocks[0];
|
||||
demote_if_node.data.if_node.merge = return_block_it->data.block;
|
||||
|
||||
IR::AbstractSyntaxNode demote_node{};
|
||||
demote_node.type = Type::Block;
|
||||
demote_node.data.block = demote_blocks[0];
|
||||
|
||||
IR::AbstractSyntaxNode demote_endif_node{};
|
||||
demote_endif_node.type = Type::EndIf;
|
||||
demote_endif_node.data.end_if.merge = return_block_it->data.block;
|
||||
|
||||
asl.insert(return_block_it, demote_endif_node);
|
||||
asl.insert(return_block_it, demote_node);
|
||||
asl.insert(return_block_it, demote_if_node);
|
||||
}
|
||||
|
||||
ObjectPool<Statement>& stmt_pool;
|
||||
ObjectPool<IR::Inst>& inst_pool;
|
||||
ObjectPool<IR::Block>& block_pool;
|
||||
Environment& env;
|
||||
IR::AbstractSyntaxList& syntax_list;
|
||||
bool uses_demote_to_helper{};
|
||||
|
||||
// TODO: C++20 Remove this when all compilers support constexpr std::vector
|
||||
#if __cpp_lib_constexpr_vector >= 201907
|
||||
@@ -989,13 +871,12 @@ private:
|
||||
} // Anonymous namespace
|
||||
|
||||
IR::AbstractSyntaxList BuildASL(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Block>& block_pool,
|
||||
Environment& env, Flow::CFG& cfg,
|
||||
const HostTranslateInfo& host_info) {
|
||||
Environment& env, Flow::CFG& cfg) {
|
||||
ObjectPool<Statement> stmt_pool{64};
|
||||
GotoPass goto_pass{cfg, stmt_pool};
|
||||
Statement& root{goto_pass.RootStatement()};
|
||||
IR::AbstractSyntaxList syntax_list;
|
||||
TranslatePass{inst_pool, block_pool, stmt_pool, env, root, syntax_list, host_info};
|
||||
TranslatePass{inst_pool, block_pool, stmt_pool, env, root, syntax_list};
|
||||
return syntax_list;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,13 +11,10 @@
|
||||
#include "shader_recompiler/frontend/maxwell/control_flow.h"
|
||||
#include "shader_recompiler/object_pool.h"
|
||||
|
||||
namespace Shader {
|
||||
struct HostTranslateInfo;
|
||||
namespace Maxwell {
|
||||
namespace Shader::Maxwell {
|
||||
|
||||
[[nodiscard]] IR::AbstractSyntaxList BuildASL(ObjectPool<IR::Inst>& inst_pool,
|
||||
ObjectPool<IR::Block>& block_pool, Environment& env,
|
||||
Flow::CFG& cfg, const HostTranslateInfo& host_info);
|
||||
Flow::CFG& cfg);
|
||||
|
||||
} // namespace Maxwell
|
||||
} // namespace Shader
|
||||
} // namespace Shader::Maxwell
|
||||
|
||||
@@ -130,7 +130,7 @@ void AddNVNStorageBuffers(IR::Program& program) {
|
||||
IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Block>& block_pool,
|
||||
Environment& env, Flow::CFG& cfg, const HostTranslateInfo& host_info) {
|
||||
IR::Program program;
|
||||
program.syntax_list = BuildASL(inst_pool, block_pool, env, cfg, host_info);
|
||||
program.syntax_list = BuildASL(inst_pool, block_pool, env, cfg);
|
||||
program.blocks = GenerateBlocks(program.syntax_list);
|
||||
program.post_order_blocks = PostOrder(program.syntax_list.front());
|
||||
program.stage = env.ShaderStage();
|
||||
|
||||
@@ -11,9 +11,8 @@ namespace Shader {
|
||||
|
||||
/// Misc information about the host
|
||||
struct HostTranslateInfo {
|
||||
bool support_float16{}; ///< True when the device supports 16-bit floats
|
||||
bool support_int64{}; ///< True when the device supports 64-bit integers
|
||||
bool needs_demote_reorder{}; ///< True when the device needs DemoteToHelperInvocation reordered
|
||||
bool support_float16{}; ///< True when the device supports 16-bit floats
|
||||
bool support_int64{}; ///< True when the device supports 64-bit integers
|
||||
};
|
||||
|
||||
} // namespace Shader
|
||||
|
||||
@@ -261,6 +261,16 @@ public:
|
||||
stream_score += score;
|
||||
}
|
||||
|
||||
/// Sets the new frame tick
|
||||
void SetFrameTick(u64 new_frame_tick) noexcept {
|
||||
frame_tick = new_frame_tick;
|
||||
}
|
||||
|
||||
/// Returns the new frame tick
|
||||
[[nodiscard]] u64 FrameTick() const noexcept {
|
||||
return frame_tick;
|
||||
}
|
||||
|
||||
/// Returns the likeliness of this being a stream buffer
|
||||
[[nodiscard]] int StreamScore() const noexcept {
|
||||
return stream_score;
|
||||
@@ -297,14 +307,6 @@ public:
|
||||
return words.size_bytes;
|
||||
}
|
||||
|
||||
size_t getLRUID() const noexcept {
|
||||
return lru_id;
|
||||
}
|
||||
|
||||
void setLRUID(size_t lru_id_) {
|
||||
lru_id = lru_id_;
|
||||
}
|
||||
|
||||
private:
|
||||
template <Type type>
|
||||
u64* Array() noexcept {
|
||||
@@ -601,9 +603,9 @@ private:
|
||||
RasterizerInterface* rasterizer = nullptr;
|
||||
VAddr cpu_addr = 0;
|
||||
Words words;
|
||||
u64 frame_tick = 0;
|
||||
BufferFlagBits flags{};
|
||||
int stream_score = 0;
|
||||
size_t lru_id = SIZE_MAX;
|
||||
};
|
||||
|
||||
} // namespace VideoCommon
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/div_ceil.h"
|
||||
#include "common/literals.h"
|
||||
#include "common/lru_cache.h"
|
||||
#include "common/microprofile.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "common/settings.h"
|
||||
@@ -331,7 +330,7 @@ private:
|
||||
template <bool insert>
|
||||
void ChangeRegister(BufferId buffer_id);
|
||||
|
||||
void TouchBuffer(Buffer& buffer, BufferId buffer_id) noexcept;
|
||||
void TouchBuffer(Buffer& buffer) const noexcept;
|
||||
|
||||
bool SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size);
|
||||
|
||||
@@ -429,11 +428,7 @@ private:
|
||||
size_t immediate_buffer_capacity = 0;
|
||||
std::unique_ptr<u8[]> immediate_buffer_alloc;
|
||||
|
||||
struct LRUItemParams {
|
||||
using ObjectType = BufferId;
|
||||
using TickType = u64;
|
||||
};
|
||||
Common::LeastRecentlyUsedCache<LRUItemParams> lru_cache;
|
||||
typename SlotVector<Buffer>::Iterator deletion_iterator;
|
||||
u64 frame_tick = 0;
|
||||
u64 total_used_memory = 0;
|
||||
|
||||
@@ -450,6 +445,7 @@ BufferCache<P>::BufferCache(VideoCore::RasterizerInterface& rasterizer_,
|
||||
kepler_compute{kepler_compute_}, gpu_memory{gpu_memory_}, cpu_memory{cpu_memory_} {
|
||||
// Ensure the first slot is used for the null buffer
|
||||
void(slot_buffers.insert(runtime, NullBufferParams{}));
|
||||
deletion_iterator = slot_buffers.end();
|
||||
common_ranges.clear();
|
||||
}
|
||||
|
||||
@@ -458,17 +454,20 @@ void BufferCache<P>::RunGarbageCollector() {
|
||||
const bool aggressive_gc = total_used_memory >= CRITICAL_MEMORY;
|
||||
const u64 ticks_to_destroy = aggressive_gc ? 60 : 120;
|
||||
int num_iterations = aggressive_gc ? 64 : 32;
|
||||
const auto clean_up = [this, &num_iterations](BufferId buffer_id) {
|
||||
if (num_iterations == 0) {
|
||||
return true;
|
||||
for (; num_iterations > 0; --num_iterations) {
|
||||
if (deletion_iterator == slot_buffers.end()) {
|
||||
deletion_iterator = slot_buffers.begin();
|
||||
}
|
||||
--num_iterations;
|
||||
auto& buffer = slot_buffers[buffer_id];
|
||||
DownloadBufferMemory(buffer);
|
||||
DeleteBuffer(buffer_id);
|
||||
return false;
|
||||
};
|
||||
lru_cache.ForEachItemBelow(frame_tick - ticks_to_destroy, clean_up);
|
||||
++deletion_iterator;
|
||||
if (deletion_iterator == slot_buffers.end()) {
|
||||
break;
|
||||
}
|
||||
const auto [buffer_id, buffer] = *deletion_iterator;
|
||||
if (buffer->FrameTick() + ticks_to_destroy < frame_tick) {
|
||||
DownloadBufferMemory(*buffer);
|
||||
DeleteBuffer(buffer_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class P>
|
||||
@@ -486,7 +485,7 @@ void BufferCache<P>::TickFrame() {
|
||||
const bool skip_preferred = hits * 256 < shots * 251;
|
||||
uniform_buffer_skip_cache_size = skip_preferred ? DEFAULT_SKIP_CACHE_SIZE : 0;
|
||||
|
||||
if (total_used_memory >= EXPECTED_MEMORY) {
|
||||
if (Settings::values.use_caches_gc.GetValue() && total_used_memory >= EXPECTED_MEMORY) {
|
||||
RunGarbageCollector();
|
||||
}
|
||||
++frame_tick;
|
||||
@@ -955,7 +954,7 @@ bool BufferCache<P>::IsRegionCpuModified(VAddr addr, size_t size) {
|
||||
template <class P>
|
||||
void BufferCache<P>::BindHostIndexBuffer() {
|
||||
Buffer& buffer = slot_buffers[index_buffer.buffer_id];
|
||||
TouchBuffer(buffer, index_buffer.buffer_id);
|
||||
TouchBuffer(buffer);
|
||||
const u32 offset = buffer.Offset(index_buffer.cpu_addr);
|
||||
const u32 size = index_buffer.size;
|
||||
SynchronizeBuffer(buffer, index_buffer.cpu_addr, size);
|
||||
@@ -976,7 +975,7 @@ void BufferCache<P>::BindHostVertexBuffers() {
|
||||
for (u32 index = 0; index < NUM_VERTEX_BUFFERS; ++index) {
|
||||
const Binding& binding = vertex_buffers[index];
|
||||
Buffer& buffer = slot_buffers[binding.buffer_id];
|
||||
TouchBuffer(buffer, binding.buffer_id);
|
||||
TouchBuffer(buffer);
|
||||
SynchronizeBuffer(buffer, binding.cpu_addr, binding.size);
|
||||
if (!flags[Dirty::VertexBuffer0 + index]) {
|
||||
continue;
|
||||
@@ -1012,7 +1011,7 @@ void BufferCache<P>::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32
|
||||
const VAddr cpu_addr = binding.cpu_addr;
|
||||
const u32 size = std::min(binding.size, (*uniform_buffer_sizes)[stage][index]);
|
||||
Buffer& buffer = slot_buffers[binding.buffer_id];
|
||||
TouchBuffer(buffer, binding.buffer_id);
|
||||
TouchBuffer(buffer);
|
||||
const bool use_fast_buffer = binding.buffer_id != NULL_BUFFER_ID &&
|
||||
size <= uniform_buffer_skip_cache_size &&
|
||||
!buffer.IsRegionGpuModified(cpu_addr, size);
|
||||
@@ -1084,7 +1083,7 @@ void BufferCache<P>::BindHostGraphicsStorageBuffers(size_t stage) {
|
||||
ForEachEnabledBit(enabled_storage_buffers[stage], [&](u32 index) {
|
||||
const Binding& binding = storage_buffers[stage][index];
|
||||
Buffer& buffer = slot_buffers[binding.buffer_id];
|
||||
TouchBuffer(buffer, binding.buffer_id);
|
||||
TouchBuffer(buffer);
|
||||
const u32 size = binding.size;
|
||||
SynchronizeBuffer(buffer, binding.cpu_addr, size);
|
||||
|
||||
@@ -1129,7 +1128,7 @@ void BufferCache<P>::BindHostTransformFeedbackBuffers() {
|
||||
for (u32 index = 0; index < NUM_TRANSFORM_FEEDBACK_BUFFERS; ++index) {
|
||||
const Binding& binding = transform_feedback_buffers[index];
|
||||
Buffer& buffer = slot_buffers[binding.buffer_id];
|
||||
TouchBuffer(buffer, binding.buffer_id);
|
||||
TouchBuffer(buffer);
|
||||
const u32 size = binding.size;
|
||||
SynchronizeBuffer(buffer, binding.cpu_addr, size);
|
||||
|
||||
@@ -1149,7 +1148,7 @@ void BufferCache<P>::BindHostComputeUniformBuffers() {
|
||||
ForEachEnabledBit(enabled_compute_uniform_buffer_mask, [&](u32 index) {
|
||||
const Binding& binding = compute_uniform_buffers[index];
|
||||
Buffer& buffer = slot_buffers[binding.buffer_id];
|
||||
TouchBuffer(buffer, binding.buffer_id);
|
||||
TouchBuffer(buffer);
|
||||
const u32 size = std::min(binding.size, (*compute_uniform_buffer_sizes)[index]);
|
||||
SynchronizeBuffer(buffer, binding.cpu_addr, size);
|
||||
|
||||
@@ -1169,7 +1168,7 @@ void BufferCache<P>::BindHostComputeStorageBuffers() {
|
||||
ForEachEnabledBit(enabled_compute_storage_buffers, [&](u32 index) {
|
||||
const Binding& binding = compute_storage_buffers[index];
|
||||
Buffer& buffer = slot_buffers[binding.buffer_id];
|
||||
TouchBuffer(buffer, binding.buffer_id);
|
||||
TouchBuffer(buffer);
|
||||
const u32 size = binding.size;
|
||||
SynchronizeBuffer(buffer, binding.cpu_addr, size);
|
||||
|
||||
@@ -1514,11 +1513,11 @@ BufferId BufferCache<P>::CreateBuffer(VAddr cpu_addr, u32 wanted_size) {
|
||||
const OverlapResult overlap = ResolveOverlaps(cpu_addr, wanted_size);
|
||||
const u32 size = static_cast<u32>(overlap.end - overlap.begin);
|
||||
const BufferId new_buffer_id = slot_buffers.insert(runtime, rasterizer, overlap.begin, size);
|
||||
TouchBuffer(slot_buffers[new_buffer_id]);
|
||||
for (const BufferId overlap_id : overlap.ids) {
|
||||
JoinOverlap(new_buffer_id, overlap_id, !overlap.has_stream_leap);
|
||||
}
|
||||
Register(new_buffer_id);
|
||||
TouchBuffer(slot_buffers[new_buffer_id], new_buffer_id);
|
||||
return new_buffer_id;
|
||||
}
|
||||
|
||||
@@ -1535,14 +1534,12 @@ void BufferCache<P>::Unregister(BufferId buffer_id) {
|
||||
template <class P>
|
||||
template <bool insert>
|
||||
void BufferCache<P>::ChangeRegister(BufferId buffer_id) {
|
||||
Buffer& buffer = slot_buffers[buffer_id];
|
||||
const Buffer& buffer = slot_buffers[buffer_id];
|
||||
const auto size = buffer.SizeBytes();
|
||||
if (insert) {
|
||||
total_used_memory += Common::AlignUp(size, 1024);
|
||||
buffer.setLRUID(lru_cache.Insert(buffer_id, frame_tick));
|
||||
} else {
|
||||
total_used_memory -= Common::AlignUp(size, 1024);
|
||||
lru_cache.Free(buffer.getLRUID());
|
||||
}
|
||||
const VAddr cpu_addr_begin = buffer.CpuAddr();
|
||||
const VAddr cpu_addr_end = cpu_addr_begin + size;
|
||||
@@ -1558,10 +1555,8 @@ void BufferCache<P>::ChangeRegister(BufferId buffer_id) {
|
||||
}
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::TouchBuffer(Buffer& buffer, BufferId buffer_id) noexcept {
|
||||
if (buffer_id != NULL_BUFFER_ID) {
|
||||
lru_cache.Touch(buffer.getLRUID(), frame_tick);
|
||||
}
|
||||
void BufferCache<P>::TouchBuffer(Buffer& buffer) const noexcept {
|
||||
buffer.SetFrameTick(frame_tick);
|
||||
}
|
||||
|
||||
template <class P>
|
||||
|
||||
@@ -742,7 +742,6 @@ VpxBitStreamWriter VP9::ComposeUncompressedHeader() {
|
||||
uncomp_writer.WriteDeltaQ(current_frame_info.uv_dc_delta_q);
|
||||
uncomp_writer.WriteDeltaQ(current_frame_info.uv_ac_delta_q);
|
||||
|
||||
ASSERT(!current_frame_info.segment_enabled);
|
||||
uncomp_writer.WriteBit(false); // Segmentation enabled (TODO).
|
||||
|
||||
const s32 min_tile_cols_log2 = CalcMinLog2TileCols(current_frame_info.frame_size.width);
|
||||
|
||||
@@ -22,7 +22,7 @@ struct Vp9FrameDimensions {
|
||||
};
|
||||
static_assert(sizeof(Vp9FrameDimensions) == 0x8, "Vp9 Vp9FrameDimensions is an invalid size");
|
||||
|
||||
enum class FrameFlags : u32 {
|
||||
enum FrameFlags : u32 {
|
||||
IsKeyFrame = 1 << 0,
|
||||
LastFrameIsKeyFrame = 1 << 1,
|
||||
FrameSizeChanged = 1 << 2,
|
||||
@@ -30,7 +30,6 @@ enum class FrameFlags : u32 {
|
||||
LastShowFrame = 1 << 4,
|
||||
IntraOnly = 1 << 5,
|
||||
};
|
||||
DECLARE_ENUM_FLAG_OPERATORS(FrameFlags)
|
||||
|
||||
enum class TxSize {
|
||||
Tx4x4 = 0, // 4x4 transform
|
||||
@@ -93,34 +92,44 @@ struct Vp9EntropyProbs {
|
||||
static_assert(sizeof(Vp9EntropyProbs) == 0x7B4, "Vp9EntropyProbs is an invalid size");
|
||||
|
||||
struct Vp9PictureInfo {
|
||||
u32 bitstream_size;
|
||||
std::array<u64, 4> frame_offsets;
|
||||
bool is_key_frame;
|
||||
bool intra_only;
|
||||
bool last_frame_was_key;
|
||||
bool frame_size_changed;
|
||||
bool error_resilient_mode;
|
||||
bool last_frame_shown;
|
||||
bool show_frame;
|
||||
std::array<s8, 4> ref_frame_sign_bias;
|
||||
s32 base_q_index;
|
||||
s32 y_dc_delta_q;
|
||||
s32 uv_dc_delta_q;
|
||||
s32 uv_ac_delta_q;
|
||||
bool lossless;
|
||||
s32 transform_mode;
|
||||
bool allow_high_precision_mv;
|
||||
s32 interp_filter;
|
||||
s32 reference_mode;
|
||||
s8 comp_fixed_ref;
|
||||
std::array<s8, 2> comp_var_ref;
|
||||
s32 log2_tile_cols;
|
||||
s32 log2_tile_rows;
|
||||
bool segment_enabled;
|
||||
bool segment_map_update;
|
||||
bool segment_map_temporal_update;
|
||||
s32 segment_abs_delta;
|
||||
std::array<u32, 8> segment_feature_enable;
|
||||
std::array<std::array<s16, 4>, 8> segment_feature_data;
|
||||
bool mode_ref_delta_enabled;
|
||||
bool use_prev_in_find_mv_refs;
|
||||
std::array<s8, 4> ref_deltas;
|
||||
std::array<s8, 2> mode_deltas;
|
||||
Vp9EntropyProbs entropy;
|
||||
Vp9FrameDimensions frame_size;
|
||||
u8 first_level;
|
||||
u8 sharpness_level;
|
||||
bool is_key_frame;
|
||||
bool intra_only;
|
||||
bool last_frame_was_key;
|
||||
bool error_resilient_mode;
|
||||
bool last_frame_shown;
|
||||
bool show_frame;
|
||||
bool lossless;
|
||||
bool allow_high_precision_mv;
|
||||
bool segment_enabled;
|
||||
bool mode_ref_delta_enabled;
|
||||
u32 bitstream_size;
|
||||
std::array<u64, 4> frame_offsets;
|
||||
std::array<bool, 4> refresh_frame;
|
||||
};
|
||||
|
||||
struct Vp9FrameContainer {
|
||||
@@ -136,7 +145,7 @@ struct PictureInfo {
|
||||
Vp9FrameDimensions golden_frame_size; ///< 0x50
|
||||
Vp9FrameDimensions alt_frame_size; ///< 0x58
|
||||
Vp9FrameDimensions current_frame_size; ///< 0x60
|
||||
FrameFlags vp9_flags; ///< 0x68
|
||||
u32 vp9_flags; ///< 0x68
|
||||
std::array<s8, 4> ref_frame_sign_bias; ///< 0x6C
|
||||
u8 first_level; ///< 0x70
|
||||
u8 sharpness_level; ///< 0x71
|
||||
@@ -149,43 +158,60 @@ struct PictureInfo {
|
||||
u8 allow_high_precision_mv; ///< 0x78
|
||||
u8 interp_filter; ///< 0x79
|
||||
u8 reference_mode; ///< 0x7A
|
||||
INSERT_PADDING_BYTES_NOINIT(3); ///< 0x7B
|
||||
s8 comp_fixed_ref; ///< 0x7B
|
||||
std::array<s8, 2> comp_var_ref; ///< 0x7C
|
||||
u8 log2_tile_cols; ///< 0x7E
|
||||
u8 log2_tile_rows; ///< 0x7F
|
||||
Segmentation segmentation; ///< 0x80
|
||||
LoopFilter loop_filter; ///< 0xE4
|
||||
INSERT_PADDING_BYTES_NOINIT(21); ///< 0xEB
|
||||
INSERT_PADDING_BYTES_NOINIT(5); ///< 0xEB
|
||||
u32 surface_params; ///< 0xF0
|
||||
INSERT_PADDING_WORDS_NOINIT(3); ///< 0xF4
|
||||
|
||||
[[nodiscard]] Vp9PictureInfo Convert() const {
|
||||
return {
|
||||
.bitstream_size = bitstream_size,
|
||||
.frame_offsets{},
|
||||
.is_key_frame = (vp9_flags & FrameFlags::IsKeyFrame) != 0,
|
||||
.intra_only = (vp9_flags & FrameFlags::IntraOnly) != 0,
|
||||
.last_frame_was_key = (vp9_flags & FrameFlags::LastFrameIsKeyFrame) != 0,
|
||||
.frame_size_changed = (vp9_flags & FrameFlags::FrameSizeChanged) != 0,
|
||||
.error_resilient_mode = (vp9_flags & FrameFlags::ErrorResilientMode) != 0,
|
||||
.last_frame_shown = (vp9_flags & FrameFlags::LastShowFrame) != 0,
|
||||
.show_frame = true,
|
||||
.ref_frame_sign_bias = ref_frame_sign_bias,
|
||||
.base_q_index = base_q_index,
|
||||
.y_dc_delta_q = y_dc_delta_q,
|
||||
.uv_dc_delta_q = uv_dc_delta_q,
|
||||
.uv_ac_delta_q = uv_ac_delta_q,
|
||||
.lossless = lossless != 0,
|
||||
.transform_mode = tx_mode,
|
||||
.allow_high_precision_mv = allow_high_precision_mv != 0,
|
||||
.interp_filter = interp_filter,
|
||||
.reference_mode = reference_mode,
|
||||
.comp_fixed_ref = comp_fixed_ref,
|
||||
.comp_var_ref = comp_var_ref,
|
||||
.log2_tile_cols = log2_tile_cols,
|
||||
.log2_tile_rows = log2_tile_rows,
|
||||
.segment_enabled = segmentation.enabled != 0,
|
||||
.segment_map_update = segmentation.update_map != 0,
|
||||
.segment_map_temporal_update = segmentation.temporal_update != 0,
|
||||
.segment_abs_delta = segmentation.abs_delta,
|
||||
.segment_feature_enable = segmentation.feature_mask,
|
||||
.segment_feature_data = segmentation.feature_data,
|
||||
.mode_ref_delta_enabled = loop_filter.mode_ref_delta_enabled != 0,
|
||||
.use_prev_in_find_mv_refs = !(vp9_flags == (FrameFlags::ErrorResilientMode)) &&
|
||||
!(vp9_flags == (FrameFlags::FrameSizeChanged)) &&
|
||||
!(vp9_flags == (FrameFlags::IntraOnly)) &&
|
||||
(vp9_flags == (FrameFlags::LastShowFrame)) &&
|
||||
!(vp9_flags == (FrameFlags::LastFrameIsKeyFrame)),
|
||||
.ref_deltas = loop_filter.ref_deltas,
|
||||
.mode_deltas = loop_filter.mode_deltas,
|
||||
.entropy{},
|
||||
.frame_size = current_frame_size,
|
||||
.first_level = first_level,
|
||||
.sharpness_level = sharpness_level,
|
||||
.is_key_frame = True(vp9_flags & FrameFlags::IsKeyFrame),
|
||||
.intra_only = True(vp9_flags & FrameFlags::IntraOnly),
|
||||
.last_frame_was_key = True(vp9_flags & FrameFlags::LastFrameIsKeyFrame),
|
||||
.error_resilient_mode = True(vp9_flags & FrameFlags::ErrorResilientMode),
|
||||
.last_frame_shown = True(vp9_flags & FrameFlags::LastShowFrame),
|
||||
.show_frame = true,
|
||||
.lossless = lossless != 0,
|
||||
.allow_high_precision_mv = allow_high_precision_mv != 0,
|
||||
.segment_enabled = segmentation.enabled != 0,
|
||||
.mode_ref_delta_enabled = loop_filter.mode_ref_delta_enabled != 0,
|
||||
.bitstream_size = bitstream_size,
|
||||
.frame_offsets{},
|
||||
.refresh_frame{},
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -290,6 +316,7 @@ ASSERT_POSITION(last_frame_size, 0x48);
|
||||
ASSERT_POSITION(first_level, 0x70);
|
||||
ASSERT_POSITION(segmentation, 0x80);
|
||||
ASSERT_POSITION(loop_filter, 0xE4);
|
||||
ASSERT_POSITION(surface_params, 0xF0);
|
||||
#undef ASSERT_POSITION
|
||||
|
||||
#define ASSERT_POSITION(field_name, position) \
|
||||
|
||||
@@ -156,10 +156,6 @@ public:
|
||||
return shader_backend;
|
||||
}
|
||||
|
||||
bool IsAmd() const {
|
||||
return vendor_name == "ATI Technologies Inc.";
|
||||
}
|
||||
|
||||
private:
|
||||
static bool TestVariableAoffi();
|
||||
static bool TestPreciseBug();
|
||||
|
||||
@@ -219,7 +219,6 @@ ShaderCache::ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindo
|
||||
host_info{
|
||||
.support_float16 = false,
|
||||
.support_int64 = device.HasShaderInt64(),
|
||||
.needs_demote_reorder = device.IsAmd(),
|
||||
} {
|
||||
if (use_asynchronous_shaders) {
|
||||
workers = CreateWorkers();
|
||||
|
||||
@@ -159,13 +159,11 @@ VkSemaphore VKBlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer,
|
||||
|
||||
const VAddr framebuffer_addr = framebuffer.address + framebuffer.offset;
|
||||
const u8* const host_ptr = cpu_memory.GetPointer(framebuffer_addr);
|
||||
const size_t size_bytes = GetSizeInBytes(framebuffer);
|
||||
|
||||
// TODO(Rodrigo): Read this from HLE
|
||||
constexpr u32 block_height_log2 = 4;
|
||||
const u32 bytes_per_pixel = GetBytesPerPixel(framebuffer);
|
||||
const u64 size_bytes{Tegra::Texture::CalculateSize(true, bytes_per_pixel,
|
||||
framebuffer.stride, framebuffer.height,
|
||||
1, block_height_log2, 0)};
|
||||
Tegra::Texture::UnswizzleTexture(
|
||||
mapped_span.subspan(image_offset, size_bytes), std::span(host_ptr, size_bytes),
|
||||
bytes_per_pixel, framebuffer.width, framebuffer.height, 1, block_height_log2, 0);
|
||||
@@ -358,7 +356,7 @@ void VKBlitScreen::CreateDescriptorPool() {
|
||||
void VKBlitScreen::CreateRenderPass() {
|
||||
const VkAttachmentDescription color_attachment{
|
||||
.flags = 0,
|
||||
.format = swapchain.GetImageFormat(),
|
||||
.format = swapchain.GetImageViewFormat(),
|
||||
.samples = VK_SAMPLE_COUNT_1_BIT,
|
||||
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
|
||||
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
|
||||
|
||||
@@ -325,8 +325,6 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, Tegra::Engines::Maxw
|
||||
host_info = Shader::HostTranslateInfo{
|
||||
.support_float16 = device.IsFloat16Supported(),
|
||||
.support_int64 = device.IsShaderInt64Supported(),
|
||||
.needs_demote_reorder = driver_id == VK_DRIVER_ID_AMD_PROPRIETARY_KHR ||
|
||||
driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE_KHR,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -20,16 +20,15 @@ namespace Vulkan {
|
||||
|
||||
namespace {
|
||||
|
||||
VkSurfaceFormatKHR ChooseSwapSurfaceFormat(vk::Span<VkSurfaceFormatKHR> formats, bool srgb) {
|
||||
VkSurfaceFormatKHR ChooseSwapSurfaceFormat(vk::Span<VkSurfaceFormatKHR> formats) {
|
||||
if (formats.size() == 1 && formats[0].format == VK_FORMAT_UNDEFINED) {
|
||||
VkSurfaceFormatKHR format;
|
||||
format.format = VK_FORMAT_B8G8R8A8_UNORM;
|
||||
format.colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
|
||||
return format;
|
||||
}
|
||||
const auto& found = std::find_if(formats.begin(), formats.end(), [srgb](const auto& format) {
|
||||
const auto request_format = srgb ? VK_FORMAT_B8G8R8A8_SRGB : VK_FORMAT_B8G8R8A8_UNORM;
|
||||
return format.format == request_format &&
|
||||
const auto& found = std::find_if(formats.begin(), formats.end(), [](const auto& format) {
|
||||
return format.format == VK_FORMAT_B8G8R8A8_UNORM &&
|
||||
format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
|
||||
});
|
||||
return found != formats.end() ? *found : formats[0];
|
||||
@@ -145,7 +144,7 @@ void VKSwapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities,
|
||||
const auto formats{physical_device.GetSurfaceFormatsKHR(surface)};
|
||||
const auto present_modes{physical_device.GetSurfacePresentModesKHR(surface)};
|
||||
|
||||
const VkSurfaceFormatKHR surface_format{ChooseSwapSurfaceFormat(formats, srgb)};
|
||||
const VkSurfaceFormatKHR surface_format{ChooseSwapSurfaceFormat(formats)};
|
||||
const VkPresentModeKHR present_mode{ChooseSwapPresentMode(present_modes)};
|
||||
|
||||
u32 requested_image_count{capabilities.minImageCount + 1};
|
||||
@@ -180,6 +179,17 @@ void VKSwapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities,
|
||||
swapchain_ci.queueFamilyIndexCount = static_cast<u32>(queue_indices.size());
|
||||
swapchain_ci.pQueueFamilyIndices = queue_indices.data();
|
||||
}
|
||||
static constexpr std::array view_formats{VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_B8G8R8A8_SRGB};
|
||||
VkImageFormatListCreateInfo format_list{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO_KHR,
|
||||
.pNext = nullptr,
|
||||
.viewFormatCount = static_cast<u32>(view_formats.size()),
|
||||
.pViewFormats = view_formats.data(),
|
||||
};
|
||||
if (device.IsKhrSwapchainMutableFormatEnabled()) {
|
||||
format_list.pNext = std::exchange(swapchain_ci.pNext, &format_list);
|
||||
swapchain_ci.flags |= VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR;
|
||||
}
|
||||
// Request the size again to reduce the possibility of a TOCTOU race condition.
|
||||
const auto updated_capabilities = physical_device.GetSurfaceCapabilitiesKHR(surface);
|
||||
swapchain_ci.imageExtent = ChooseSwapExtent(updated_capabilities, width, height);
|
||||
@@ -191,7 +201,7 @@ void VKSwapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities,
|
||||
|
||||
images = swapchain.GetImages();
|
||||
image_count = static_cast<u32>(images.size());
|
||||
image_format = surface_format.format;
|
||||
image_view_format = srgb ? VK_FORMAT_B8G8R8A8_SRGB : VK_FORMAT_B8G8R8A8_UNORM;
|
||||
}
|
||||
|
||||
void VKSwapchain::CreateSemaphores() {
|
||||
@@ -207,7 +217,7 @@ void VKSwapchain::CreateImageViews() {
|
||||
.flags = 0,
|
||||
.image = {},
|
||||
.viewType = VK_IMAGE_VIEW_TYPE_2D,
|
||||
.format = image_format,
|
||||
.format = image_view_format,
|
||||
.components =
|
||||
{
|
||||
.r = VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
|
||||
@@ -68,8 +68,8 @@ public:
|
||||
return *image_views[index];
|
||||
}
|
||||
|
||||
VkFormat GetImageFormat() const {
|
||||
return image_format;
|
||||
VkFormat GetImageViewFormat() const {
|
||||
return image_view_format;
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -96,7 +96,7 @@ private:
|
||||
u32 image_index{};
|
||||
u32 frame_index{};
|
||||
|
||||
VkFormat image_format{};
|
||||
VkFormat image_view_format{};
|
||||
VkExtent2D extent{};
|
||||
|
||||
bool current_srgb{};
|
||||
|
||||
@@ -80,7 +80,7 @@ struct ImageBase {
|
||||
VAddr cpu_addr_end = 0;
|
||||
|
||||
u64 modification_tick = 0;
|
||||
size_t lru_index = SIZE_MAX;
|
||||
u64 frame_tick = 0;
|
||||
|
||||
std::array<u32, MAX_MIP_LEVELS> mip_level_offsets{};
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/settings.h"
|
||||
#include "video_core/dirty_flags.h"
|
||||
#include "video_core/texture_cache/samples_helper.h"
|
||||
#include "video_core/texture_cache/texture_cache_base.h"
|
||||
@@ -42,6 +43,8 @@ TextureCache<P>::TextureCache(Runtime& runtime_, VideoCore::RasterizerInterface&
|
||||
void(slot_image_views.insert(runtime, NullImageParams{}));
|
||||
void(slot_samplers.insert(runtime, sampler_descriptor));
|
||||
|
||||
deletion_iterator = slot_images.begin();
|
||||
|
||||
if constexpr (HAS_DEVICE_MEMORY_INFO) {
|
||||
const auto device_memory = runtime.GetDeviceLocalMemory();
|
||||
const u64 possible_expected_memory = (device_memory * 3) / 10;
|
||||
@@ -61,38 +64,70 @@ template <class P>
|
||||
void TextureCache<P>::RunGarbageCollector() {
|
||||
const bool high_priority_mode = total_used_memory >= expected_memory;
|
||||
const bool aggressive_mode = total_used_memory >= critical_memory;
|
||||
const u64 ticks_to_destroy = aggressive_mode ? 10ULL : high_priority_mode ? 25ULL : 100ULL;
|
||||
size_t num_iterations = aggressive_mode ? 10000 : (high_priority_mode ? 100 : 5);
|
||||
const auto clean_up = [this, &num_iterations, high_priority_mode](ImageId image_id) {
|
||||
if (num_iterations == 0) {
|
||||
return true;
|
||||
const u64 ticks_to_destroy = high_priority_mode ? 60 : 100;
|
||||
int num_iterations = aggressive_mode ? 256 : (high_priority_mode ? 128 : 64);
|
||||
for (; num_iterations > 0; --num_iterations) {
|
||||
if (deletion_iterator == slot_images.end()) {
|
||||
deletion_iterator = slot_images.begin();
|
||||
if (deletion_iterator == slot_images.end()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
--num_iterations;
|
||||
auto& image = slot_images[image_id];
|
||||
const bool must_download = image.IsSafeDownload();
|
||||
if (!high_priority_mode && must_download) {
|
||||
return false;
|
||||
auto [image_id, image_tmp] = *deletion_iterator;
|
||||
Image* image = image_tmp; // fix clang error.
|
||||
const bool is_alias = True(image->flags & ImageFlagBits::Alias);
|
||||
const bool is_bad_overlap = True(image->flags & ImageFlagBits::BadOverlap);
|
||||
const bool must_download = image->IsSafeDownload();
|
||||
bool should_care = is_bad_overlap || is_alias || (high_priority_mode && !must_download);
|
||||
const u64 ticks_needed =
|
||||
is_bad_overlap
|
||||
? ticks_to_destroy >> 4
|
||||
: ((should_care && aggressive_mode) ? ticks_to_destroy >> 1 : ticks_to_destroy);
|
||||
should_care |= aggressive_mode;
|
||||
if (should_care && image->frame_tick + ticks_needed < frame_tick) {
|
||||
if (is_bad_overlap) {
|
||||
const bool overlap_check = std::ranges::all_of(
|
||||
image->overlapping_images, [&, image](const ImageId& overlap_id) {
|
||||
auto& overlap = slot_images[overlap_id];
|
||||
return overlap.frame_tick >= image->frame_tick;
|
||||
});
|
||||
if (!overlap_check) {
|
||||
++deletion_iterator;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (!is_bad_overlap && must_download) {
|
||||
const bool alias_check = std::ranges::none_of(
|
||||
image->aliased_images, [&, image](const AliasedImage& alias) {
|
||||
auto& alias_image = slot_images[alias.id];
|
||||
return (alias_image.frame_tick < image->frame_tick) ||
|
||||
(alias_image.modification_tick < image->modification_tick);
|
||||
});
|
||||
|
||||
if (alias_check) {
|
||||
auto map = runtime.DownloadStagingBuffer(image->unswizzled_size_bytes);
|
||||
const auto copies = FullDownloadCopies(image->info);
|
||||
image->DownloadMemory(map, copies);
|
||||
runtime.Finish();
|
||||
SwizzleImage(gpu_memory, image->gpu_addr, image->info, copies, map.mapped_span);
|
||||
}
|
||||
}
|
||||
if (True(image->flags & ImageFlagBits::Tracked)) {
|
||||
UntrackImage(*image, image_id);
|
||||
}
|
||||
UnregisterImage(image_id);
|
||||
DeleteImage(image_id);
|
||||
if (is_bad_overlap) {
|
||||
++num_iterations;
|
||||
}
|
||||
}
|
||||
if (must_download) {
|
||||
auto map = runtime.DownloadStagingBuffer(image.unswizzled_size_bytes);
|
||||
const auto copies = FullDownloadCopies(image.info);
|
||||
image.DownloadMemory(map, copies);
|
||||
runtime.Finish();
|
||||
SwizzleImage(gpu_memory, image.gpu_addr, image.info, copies, map.mapped_span);
|
||||
}
|
||||
if (True(image.flags & ImageFlagBits::Tracked)) {
|
||||
UntrackImage(image, image_id);
|
||||
}
|
||||
UnregisterImage(image_id);
|
||||
DeleteImage(image_id);
|
||||
return false;
|
||||
};
|
||||
lru_cache.ForEachItemBelow(frame_tick - ticks_to_destroy, clean_up);
|
||||
++deletion_iterator;
|
||||
}
|
||||
}
|
||||
|
||||
template <class P>
|
||||
void TextureCache<P>::TickFrame() {
|
||||
if (total_used_memory > minimum_memory) {
|
||||
if (Settings::values.use_caches_gc.GetValue() && total_used_memory > minimum_memory) {
|
||||
RunGarbageCollector();
|
||||
}
|
||||
sentenced_images.Tick();
|
||||
@@ -1043,8 +1078,6 @@ void TextureCache<P>::RegisterImage(ImageId image_id) {
|
||||
tentative_size = EstimatedDecompressedSize(tentative_size, image.info.format);
|
||||
}
|
||||
total_used_memory += Common::AlignUp(tentative_size, 1024);
|
||||
image.lru_index = lru_cache.Insert(image_id, frame_tick);
|
||||
|
||||
ForEachGPUPage(image.gpu_addr, image.guest_size_bytes,
|
||||
[this, image_id](u64 page) { gpu_page_table[page].push_back(image_id); });
|
||||
if (False(image.flags & ImageFlagBits::Sparse)) {
|
||||
@@ -1082,7 +1115,6 @@ void TextureCache<P>::UnregisterImage(ImageId image_id) {
|
||||
tentative_size = EstimatedDecompressedSize(tentative_size, image.info.format);
|
||||
}
|
||||
total_used_memory -= Common::AlignUp(tentative_size, 1024);
|
||||
lru_cache.Free(image.lru_index);
|
||||
const auto& clear_page_table =
|
||||
[this, image_id](
|
||||
u64 page,
|
||||
@@ -1352,7 +1384,7 @@ void TextureCache<P>::PrepareImage(ImageId image_id, bool is_modification, bool
|
||||
if (is_modification) {
|
||||
MarkModification(image);
|
||||
}
|
||||
lru_cache.Touch(image.lru_index, frame_tick);
|
||||
image.frame_tick = frame_tick;
|
||||
}
|
||||
|
||||
template <class P>
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/literals.h"
|
||||
#include "common/lru_cache.h"
|
||||
#include "video_core/compatible_formats.h"
|
||||
#include "video_core/delayed_destruction_ring.h"
|
||||
#include "video_core/engines/fermi_2d.h"
|
||||
@@ -371,12 +370,6 @@ private:
|
||||
std::vector<ImageId> uncommitted_downloads;
|
||||
std::queue<std::vector<ImageId>> committed_downloads;
|
||||
|
||||
struct LRUItemParams {
|
||||
using ObjectType = ImageId;
|
||||
using TickType = u64;
|
||||
};
|
||||
Common::LeastRecentlyUsedCache<LRUItemParams> lru_cache;
|
||||
|
||||
static constexpr size_t TICKS_TO_DESTROY = 6;
|
||||
DelayedDestructionRing<Image, TICKS_TO_DESTROY> sentenced_images;
|
||||
DelayedDestructionRing<ImageView, TICKS_TO_DESTROY> sentenced_image_view;
|
||||
@@ -386,6 +379,7 @@ private:
|
||||
|
||||
u64 modification_tick = 0;
|
||||
u64 frame_tick = 0;
|
||||
typename SlotVector<Image>::Iterator deletion_iterator;
|
||||
};
|
||||
|
||||
} // namespace VideoCommon
|
||||
|
||||
@@ -63,6 +63,14 @@ void SwizzleImpl(std::span<u8> output, std::span<const u8> input, u32 width, u32
|
||||
const u32 unswizzled_offset =
|
||||
slice * pitch * height + line * pitch + column * BYTES_PER_PIXEL;
|
||||
|
||||
if (const auto offset = (TO_LINEAR ? unswizzled_offset : swizzled_offset);
|
||||
offset >= input.size()) {
|
||||
// TODO(Rodrigo): This is an out of bounds access that should never happen. To
|
||||
// avoid crashing the emulator, break.
|
||||
ASSERT_MSG(false, "offset {} exceeds input size {}!", offset, input.size());
|
||||
break;
|
||||
}
|
||||
|
||||
u8* const dst = &output[TO_LINEAR ? swizzled_offset : unswizzled_offset];
|
||||
const u8* const src = &input[TO_LINEAR ? unswizzled_offset : swizzled_offset];
|
||||
|
||||
|
||||
@@ -839,6 +839,8 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
|
||||
bool has_khr_shader_float16_int8{};
|
||||
bool has_khr_workgroup_memory_explicit_layout{};
|
||||
bool has_khr_pipeline_executable_properties{};
|
||||
bool has_khr_image_format_list{};
|
||||
bool has_khr_swapchain_mutable_format{};
|
||||
bool has_ext_subgroup_size_control{};
|
||||
bool has_ext_transform_feedback{};
|
||||
bool has_ext_custom_border_color{};
|
||||
@@ -888,6 +890,9 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
|
||||
test(has_ext_shader_atomic_int64, VK_KHR_SHADER_ATOMIC_INT64_EXTENSION_NAME, false);
|
||||
test(has_khr_workgroup_memory_explicit_layout,
|
||||
VK_KHR_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_EXTENSION_NAME, false);
|
||||
test(has_khr_image_format_list, VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME, false);
|
||||
test(has_khr_swapchain_mutable_format, VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_EXTENSION_NAME,
|
||||
false);
|
||||
test(has_ext_line_rasterization, VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME, false);
|
||||
if (Settings::values.enable_nsight_aftermath) {
|
||||
test(nv_device_diagnostics_config, VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME,
|
||||
@@ -1066,6 +1071,11 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
|
||||
khr_pipeline_executable_properties = true;
|
||||
}
|
||||
}
|
||||
if (has_khr_image_format_list && has_khr_swapchain_mutable_format) {
|
||||
extensions.push_back(VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME);
|
||||
extensions.push_back(VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_EXTENSION_NAME);
|
||||
khr_swapchain_mutable_format = true;
|
||||
}
|
||||
if (khr_push_descriptor) {
|
||||
VkPhysicalDevicePushDescriptorPropertiesKHR push_descriptor;
|
||||
push_descriptor.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR;
|
||||
|
||||
@@ -224,6 +224,11 @@ public:
|
||||
return khr_pipeline_executable_properties;
|
||||
}
|
||||
|
||||
/// Returns true if VK_KHR_swapchain_mutable_format is enabled.
|
||||
bool IsKhrSwapchainMutableFormatEnabled() const {
|
||||
return khr_swapchain_mutable_format;
|
||||
}
|
||||
|
||||
/// Returns true if the device supports VK_KHR_workgroup_memory_explicit_layout.
|
||||
bool IsKhrWorkgroupMemoryExplicitLayoutSupported() const {
|
||||
return khr_workgroup_memory_explicit_layout;
|
||||
@@ -390,6 +395,7 @@ private:
|
||||
bool khr_workgroup_memory_explicit_layout{}; ///< Support for explicit workgroup layouts.
|
||||
bool khr_push_descriptor{}; ///< Support for VK_KHR_push_descritor.
|
||||
bool khr_pipeline_executable_properties{}; ///< Support for executable properties.
|
||||
bool khr_swapchain_mutable_format{}; ///< Support for VK_KHR_swapchain_mutable_format.
|
||||
bool ext_index_type_uint8{}; ///< Support for VK_EXT_index_type_uint8.
|
||||
bool ext_sampler_filter_minmax{}; ///< Support for VK_EXT_sampler_filter_minmax.
|
||||
bool ext_depth_range_unrestricted{}; ///< Support for VK_EXT_depth_range_unrestricted.
|
||||
|
||||
@@ -818,6 +818,7 @@ void Config::ReadRendererValues() {
|
||||
ReadGlobalSetting(Settings::values.shader_backend);
|
||||
ReadGlobalSetting(Settings::values.use_asynchronous_shaders);
|
||||
ReadGlobalSetting(Settings::values.use_fast_gpu_time);
|
||||
ReadGlobalSetting(Settings::values.use_caches_gc);
|
||||
ReadGlobalSetting(Settings::values.bg_red);
|
||||
ReadGlobalSetting(Settings::values.bg_green);
|
||||
ReadGlobalSetting(Settings::values.bg_blue);
|
||||
@@ -1358,6 +1359,7 @@ void Config::SaveRendererValues() {
|
||||
Settings::values.shader_backend.UsingGlobal());
|
||||
WriteGlobalSetting(Settings::values.use_asynchronous_shaders);
|
||||
WriteGlobalSetting(Settings::values.use_fast_gpu_time);
|
||||
WriteGlobalSetting(Settings::values.use_caches_gc);
|
||||
WriteGlobalSetting(Settings::values.bg_red);
|
||||
WriteGlobalSetting(Settings::values.bg_green);
|
||||
WriteGlobalSetting(Settings::values.bg_blue);
|
||||
|
||||
@@ -156,7 +156,7 @@
|
||||
<item>
|
||||
<widget class="QCheckBox" name="use_disk_shader_cache">
|
||||
<property name="text">
|
||||
<string>Use disk pipeline cache</string>
|
||||
<string>Use disk shader cache</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@@ -28,6 +28,7 @@ void ConfigureGraphicsAdvanced::SetConfiguration() {
|
||||
|
||||
ui->use_vsync->setChecked(Settings::values.use_vsync.GetValue());
|
||||
ui->use_asynchronous_shaders->setChecked(Settings::values.use_asynchronous_shaders.GetValue());
|
||||
ui->use_caches_gc->setChecked(Settings::values.use_caches_gc.GetValue());
|
||||
ui->use_fast_gpu_time->setChecked(Settings::values.use_fast_gpu_time.GetValue());
|
||||
|
||||
if (Settings::IsConfiguringGlobal()) {
|
||||
@@ -54,6 +55,8 @@ void ConfigureGraphicsAdvanced::ApplyConfiguration() {
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_asynchronous_shaders,
|
||||
ui->use_asynchronous_shaders,
|
||||
use_asynchronous_shaders);
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_caches_gc, ui->use_caches_gc,
|
||||
use_caches_gc);
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_fast_gpu_time,
|
||||
ui->use_fast_gpu_time, use_fast_gpu_time);
|
||||
}
|
||||
@@ -78,6 +81,7 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {
|
||||
ui->use_asynchronous_shaders->setEnabled(
|
||||
Settings::values.use_asynchronous_shaders.UsingGlobal());
|
||||
ui->use_fast_gpu_time->setEnabled(Settings::values.use_fast_gpu_time.UsingGlobal());
|
||||
ui->use_caches_gc->setEnabled(Settings::values.use_caches_gc.UsingGlobal());
|
||||
ui->anisotropic_filtering_combobox->setEnabled(
|
||||
Settings::values.max_anisotropy.UsingGlobal());
|
||||
|
||||
@@ -90,6 +94,8 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {
|
||||
use_asynchronous_shaders);
|
||||
ConfigurationShared::SetColoredTristate(ui->use_fast_gpu_time,
|
||||
Settings::values.use_fast_gpu_time, use_fast_gpu_time);
|
||||
ConfigurationShared::SetColoredTristate(ui->use_caches_gc, Settings::values.use_caches_gc,
|
||||
use_caches_gc);
|
||||
ConfigurationShared::SetColoredComboBox(
|
||||
ui->gpu_accuracy, ui->label_gpu_accuracy,
|
||||
static_cast<int>(Settings::values.gpu_accuracy.GetValue(true)));
|
||||
|
||||
@@ -37,4 +37,5 @@ private:
|
||||
ConfigurationShared::CheckState use_vsync;
|
||||
ConfigurationShared::CheckState use_asynchronous_shaders;
|
||||
ConfigurationShared::CheckState use_fast_gpu_time;
|
||||
ConfigurationShared::CheckState use_caches_gc;
|
||||
};
|
||||
|
||||
@@ -82,7 +82,7 @@
|
||||
<string>Enables asynchronous shader compilation, which may reduce shader stutter. This feature is experimental.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Use asynchronous shader building (Hack)</string>
|
||||
<string>Use asynchronous shader building (hack)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -92,7 +92,17 @@
|
||||
<string>Enables Fast GPU Time. This option will force most games to run at their highest native resolution.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Use Fast GPU Time (Hack)</string>
|
||||
<string>Use Fast GPU Time (hack)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="use_caches_gc">
|
||||
<property name="toolTip">
|
||||
<string>Enables garbage collection for the GPU caches, this will try to keep VRAM within 3-4 GB by flushing the least used textures/buffers. May cause issues in a few games.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enable GPU cache garbage collection (experimental)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@@ -468,6 +468,7 @@ void Config::ReadValues() {
|
||||
ReadSetting("Renderer", Settings::values.use_nvdec_emulation);
|
||||
ReadSetting("Renderer", Settings::values.accelerate_astc);
|
||||
ReadSetting("Renderer", Settings::values.use_fast_gpu_time);
|
||||
ReadSetting("Renderer", Settings::values.use_caches_gc);
|
||||
|
||||
ReadSetting("Renderer", Settings::values.bg_red);
|
||||
ReadSetting("Renderer", Settings::values.bg_green);
|
||||
|
||||
Reference in New Issue
Block a user