Compare commits
5 Commits
android-17
...
android-17
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ab2af23e47 | ||
|
|
52d3d87d09 | ||
|
|
30f76ed9c2 | ||
|
|
c85f87ffa4 | ||
|
|
7778f1906d |
@@ -1,9 +1,9 @@
|
||||
| Pull Request | Commit | Title | Author | Merged? |
|
||||
|----|----|----|----|----|
|
||||
| [12454](https://github.com/yuzu-emu/yuzu//pull/12454) | [`3a4e7d45f`](https://github.com/yuzu-emu/yuzu//pull/12454/files) | core_timing: minor refactors | [liamwhite](https://github.com/liamwhite/) | Yes |
|
||||
| [12466](https://github.com/yuzu-emu/yuzu//pull/12466) | [`adb2af0a2`](https://github.com/yuzu-emu/yuzu//pull/12466/files) | core: track separate heap allocation for linux | [liamwhite](https://github.com/liamwhite/) | Yes |
|
||||
| [12479](https://github.com/yuzu-emu/yuzu//pull/12479) | [`20e040723`](https://github.com/yuzu-emu/yuzu//pull/12479/files) | video_core: Fix buffer_row_length for linear compressed textures | [GPUCode](https://github.com/GPUCode/) | Yes |
|
||||
| [12487](https://github.com/yuzu-emu/yuzu//pull/12487) | [`d0c60605a`](https://github.com/yuzu-emu/yuzu//pull/12487/files) | shader_recompiler: use default value for clip distances array | [liamwhite](https://github.com/liamwhite/) | Yes |
|
||||
| [12448](https://github.com/yuzu-emu/yuzu//pull/12448) | [`b1d4804c0`](https://github.com/yuzu-emu/yuzu//pull/12448/files) | renderer_vulkan: demote format assert to error log | [liamwhite](https://github.com/liamwhite/) | Yes |
|
||||
| [12449](https://github.com/yuzu-emu/yuzu//pull/12449) | [`6a1ddc502`](https://github.com/yuzu-emu/yuzu//pull/12449/files) | renderer_vulkan: skip SetObjectNameEXT on unsupported driver | [liamwhite](https://github.com/liamwhite/) | Yes |
|
||||
| [12466](https://github.com/yuzu-emu/yuzu//pull/12466) | [`5f3720138`](https://github.com/yuzu-emu/yuzu//pull/12466/files) | core: track separate heap allocation for linux | [liamwhite](https://github.com/liamwhite/) | Yes |
|
||||
| [12467](https://github.com/yuzu-emu/yuzu//pull/12467) | [`cfc6c5f8f`](https://github.com/yuzu-emu/yuzu//pull/12467/files) | Revert " shader_recompiler: use minimal clip distance array " | [liamwhite](https://github.com/liamwhite/) | Yes |
|
||||
|
||||
|
||||
End of merge log. You can find the original README.md below the break.
|
||||
|
||||
@@ -18,7 +18,9 @@ constexpr auto INCREMENT_TIME{5ms};
|
||||
DeviceSession::DeviceSession(Core::System& system_)
|
||||
: system{system_}, thread_event{Core::Timing::CreateEvent(
|
||||
"AudioOutSampleTick",
|
||||
[this](s64 time, std::chrono::nanoseconds) { return ThreadFunc(); })} {}
|
||||
[this](std::uintptr_t, s64 time, std::chrono::nanoseconds) {
|
||||
return ThreadFunc();
|
||||
})} {}
|
||||
|
||||
DeviceSession::~DeviceSession() {
|
||||
Finalize();
|
||||
|
||||
@@ -3,19 +3,16 @@
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/logging/backend.h"
|
||||
|
||||
#include "common/settings.h"
|
||||
|
||||
void assert_fail_impl() {
|
||||
if (Settings::values.use_debug_asserts) {
|
||||
Common::Log::Stop();
|
||||
Crash();
|
||||
}
|
||||
}
|
||||
|
||||
[[noreturn]] void unreachable_impl() {
|
||||
Common::Log::Stop();
|
||||
Crash();
|
||||
throw std::runtime_error("Unreachable code");
|
||||
}
|
||||
|
||||
@@ -1,35 +1,18 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/heap_tracker.h"
|
||||
#include "common/logging/log.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
namespace {
|
||||
|
||||
s64 GetMaxPermissibleResidentMapCount() {
|
||||
// Default value.
|
||||
s64 value = 65530;
|
||||
|
||||
// Try to read how many mappings we can make.
|
||||
std::ifstream s("/proc/sys/vm/max_map_count");
|
||||
s >> value;
|
||||
|
||||
// Print, for debug.
|
||||
LOG_INFO(HW_Memory, "Current maximum map count: {}", value);
|
||||
|
||||
// Allow 20000 maps for other code and to account for split inaccuracy.
|
||||
return std::max<s64>(value - 20000, 0);
|
||||
}
|
||||
constexpr size_t MaxResidentMapCount = 0x8000;
|
||||
|
||||
} // namespace
|
||||
|
||||
HeapTracker::HeapTracker(Common::HostMemory& buffer)
|
||||
: m_buffer(buffer), m_max_resident_map_count(GetMaxPermissibleResidentMapCount()) {}
|
||||
HeapTracker::HeapTracker(Common::HostMemory& buffer) : m_buffer(buffer) {}
|
||||
HeapTracker::~HeapTracker() = default;
|
||||
|
||||
void HeapTracker::Map(size_t virtual_offset, size_t host_offset, size_t length,
|
||||
@@ -44,17 +27,17 @@ void HeapTracker::Map(size_t virtual_offset, size_t host_offset, size_t length,
|
||||
// We are mapping part of a separate heap.
|
||||
std::scoped_lock lk{m_lock};
|
||||
|
||||
auto* const map = new SeparateHeapMap{
|
||||
auto* map = new SeparateHeapMap{
|
||||
.vaddr = virtual_offset,
|
||||
.paddr = host_offset,
|
||||
.size = length,
|
||||
.map_id = m_next_map_id++,
|
||||
.tick = m_tick++,
|
||||
.perm = perm,
|
||||
.is_resident = false,
|
||||
};
|
||||
|
||||
// Insert into mappings.
|
||||
m_map_count++;
|
||||
m_mappings.insert(*map);
|
||||
}
|
||||
|
||||
@@ -65,10 +48,11 @@ void HeapTracker::Map(size_t virtual_offset, size_t host_offset, size_t length,
|
||||
void HeapTracker::Unmap(size_t virtual_offset, size_t size, bool is_separate_heap) {
|
||||
// If this is a separate heap...
|
||||
if (is_separate_heap) {
|
||||
std::scoped_lock lk{m_lock};
|
||||
std::scoped_lock lk{m_rebuild_lock, m_lock};
|
||||
|
||||
const SeparateHeapMap key{
|
||||
.vaddr = virtual_offset,
|
||||
.size = size,
|
||||
};
|
||||
|
||||
// Split at the boundaries of the region we are removing.
|
||||
@@ -78,17 +62,19 @@ void HeapTracker::Unmap(size_t virtual_offset, size_t size, bool is_separate_hea
|
||||
// Erase all mappings in range.
|
||||
auto it = m_mappings.find(key);
|
||||
while (it != m_mappings.end() && it->vaddr < virtual_offset + size) {
|
||||
// Get underlying item.
|
||||
auto* const item = std::addressof(*it);
|
||||
// Get pointer to item.
|
||||
SeparateHeapMap* const item = std::addressof(*it);
|
||||
|
||||
// If resident, erase from resident map.
|
||||
if (item->is_resident) {
|
||||
ASSERT(--m_resident_map_count >= 0);
|
||||
// Unlink from resident tree.
|
||||
m_resident_mappings.erase(m_resident_mappings.iterator_to(*item));
|
||||
|
||||
// Decrease reference count.
|
||||
const auto count_it = m_resident_map_counts.find(item->map_id);
|
||||
this->RemoveReferenceLocked(count_it, 1);
|
||||
}
|
||||
|
||||
// Erase from map.
|
||||
ASSERT(--m_map_count >= 0);
|
||||
// Unlink from mapping tree and advance.
|
||||
it = m_mappings.erase(it);
|
||||
|
||||
// Free the item.
|
||||
@@ -108,8 +94,8 @@ void HeapTracker::Protect(size_t virtual_offset, size_t size, MemoryPermission p
|
||||
this->SplitHeapMap(virtual_offset, size);
|
||||
|
||||
// Declare tracking variables.
|
||||
const VAddr end = virtual_offset + size;
|
||||
VAddr cur = virtual_offset;
|
||||
VAddr end = virtual_offset + size;
|
||||
|
||||
while (cur < end) {
|
||||
VAddr next = cur;
|
||||
@@ -123,7 +109,7 @@ void HeapTracker::Protect(size_t virtual_offset, size_t size, MemoryPermission p
|
||||
};
|
||||
|
||||
// Try to get the next mapping corresponding to this address.
|
||||
const auto it = m_mappings.nfind(key);
|
||||
const auto it = m_mappings.nfind_key(key);
|
||||
|
||||
if (it == m_mappings.end()) {
|
||||
// There are no separate heap mappings remaining.
|
||||
@@ -166,65 +152,106 @@ bool HeapTracker::DeferredMapSeparateHeap(u8* fault_address) {
|
||||
}
|
||||
|
||||
bool HeapTracker::DeferredMapSeparateHeap(size_t virtual_offset) {
|
||||
bool rebuild_required = false;
|
||||
std::scoped_lock lk{m_lock};
|
||||
|
||||
{
|
||||
std::scoped_lock lk{m_lock};
|
||||
while (this->IsEvictRequiredLocked()) {
|
||||
// Unlock before we rebuild to ensure proper lock ordering.
|
||||
m_lock.unlock();
|
||||
|
||||
// Check to ensure this was a non-resident separate heap mapping.
|
||||
const auto it = this->GetNearestHeapMapLocked(virtual_offset);
|
||||
if (it == m_mappings.end() || it->is_resident) {
|
||||
return false;
|
||||
// Evict four maps.
|
||||
for (size_t i = 0; i < 4; /* ... */) {
|
||||
i += this->EvictSingleSeparateHeapMap();
|
||||
}
|
||||
|
||||
// Update tick before possible rebuild.
|
||||
it->tick = m_tick++;
|
||||
|
||||
// Check if we need to rebuild.
|
||||
if (m_resident_map_count > m_max_resident_map_count) {
|
||||
rebuild_required = true;
|
||||
}
|
||||
|
||||
// Map the area.
|
||||
m_buffer.Map(it->vaddr, it->paddr, it->size, it->perm, false);
|
||||
|
||||
// This map is now resident.
|
||||
it->is_resident = true;
|
||||
m_resident_map_count++;
|
||||
m_resident_mappings.insert(*it);
|
||||
// Lock again.
|
||||
m_lock.lock();
|
||||
}
|
||||
|
||||
if (rebuild_required) {
|
||||
// A rebuild was required, so perform it now.
|
||||
this->RebuildSeparateHeapAddressSpace();
|
||||
// Check to ensure this was a non-resident separate heap mapping.
|
||||
const auto it = this->GetNearestHeapMapLocked(virtual_offset);
|
||||
if (it == m_mappings.end()) {
|
||||
// Not in any separate heap.
|
||||
return false;
|
||||
}
|
||||
if (it->is_resident) {
|
||||
// Already mapped and shouldn't be considered again.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Map the area.
|
||||
m_buffer.Map(it->vaddr, it->paddr, it->size, it->perm, false);
|
||||
|
||||
// This map is now resident.
|
||||
this->AddReferenceLocked(it->map_id, 1);
|
||||
it->is_resident = true;
|
||||
it->tick = m_tick++;
|
||||
|
||||
// Insert into resident maps.
|
||||
m_resident_mappings.insert(*it);
|
||||
|
||||
// We succeeded.
|
||||
return true;
|
||||
}
|
||||
|
||||
void HeapTracker::RebuildSeparateHeapAddressSpace() {
|
||||
bool HeapTracker::EvictSingleSeparateHeapMap() {
|
||||
std::scoped_lock lk{m_rebuild_lock, m_lock};
|
||||
|
||||
ASSERT(!m_resident_mappings.empty());
|
||||
|
||||
// Dump half of the mappings.
|
||||
//
|
||||
// Despite being worse in theory, this has proven to be better in practice than more
|
||||
// regularly dumping a smaller amount, because it significantly reduces average case
|
||||
// lock contention.
|
||||
const size_t desired_count = std::min(m_resident_map_count, m_max_resident_map_count) / 2;
|
||||
const size_t evict_count = m_resident_map_count - desired_count;
|
||||
auto it = m_resident_mappings.begin();
|
||||
// Select the item with the lowest tick to evict.
|
||||
auto* const item = std::addressof(*m_resident_mappings.begin());
|
||||
auto it = m_mappings.iterator_to(*item);
|
||||
|
||||
for (size_t i = 0; i < evict_count && it != m_resident_mappings.end(); i++) {
|
||||
// Unmark and unmap.
|
||||
it->is_resident = false;
|
||||
m_buffer.Unmap(it->vaddr, it->size, false);
|
||||
// Track the map ID.
|
||||
const size_t map_id = it->map_id;
|
||||
|
||||
// Walk backwards until we find the first entry.
|
||||
while (it != m_mappings.begin()) {
|
||||
// If the previous element does not have the same map ID, stop.
|
||||
const auto prev = std::prev(it);
|
||||
if (prev->map_id != map_id) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Continue.
|
||||
it = prev;
|
||||
}
|
||||
|
||||
// Track the begin and end address.
|
||||
const VAddr begin_vaddr = it->vaddr;
|
||||
VAddr end_vaddr = begin_vaddr;
|
||||
|
||||
// Get the count iterator.
|
||||
const auto count_it = m_resident_map_counts.find(map_id);
|
||||
|
||||
// Declare whether we have erased an underlying mapping.
|
||||
bool was_erased = false;
|
||||
|
||||
// Unmark and merge everything in range.
|
||||
while (it != m_mappings.end() && it->map_id == map_id) {
|
||||
if (it->is_resident) {
|
||||
// Remove from resident tree.
|
||||
m_resident_mappings.erase(m_resident_mappings.iterator_to(*it));
|
||||
it->is_resident = false;
|
||||
|
||||
// Remove reference count.
|
||||
was_erased |= this->RemoveReferenceLocked(count_it, 1);
|
||||
}
|
||||
|
||||
// Update the end address.
|
||||
end_vaddr = it->vaddr + it->size;
|
||||
|
||||
// Advance.
|
||||
ASSERT(--m_resident_map_count >= 0);
|
||||
it = m_resident_mappings.erase(it);
|
||||
it = this->MergeHeapMapForEvictLocked(it);
|
||||
}
|
||||
|
||||
// Finally, unmap.
|
||||
ASSERT(end_vaddr >= begin_vaddr);
|
||||
m_buffer.Unmap(begin_vaddr, end_vaddr - begin_vaddr, false);
|
||||
|
||||
// Return whether we actually removed a mapping.
|
||||
// This will be true if there were no holes, which is likely.
|
||||
return was_erased;
|
||||
}
|
||||
|
||||
void HeapTracker::SplitHeapMap(VAddr offset, size_t size) {
|
||||
@@ -241,9 +268,11 @@ void HeapTracker::SplitHeapMapLocked(VAddr offset) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Cache the original values.
|
||||
// Get the underlying item as the left.
|
||||
auto* const left = std::addressof(*it);
|
||||
const size_t orig_size = left->size;
|
||||
|
||||
// Cache the original size values.
|
||||
const size_t size = left->size;
|
||||
|
||||
// Adjust the left map.
|
||||
const size_t left_size = offset - left->vaddr;
|
||||
@@ -253,21 +282,74 @@ void HeapTracker::SplitHeapMapLocked(VAddr offset) {
|
||||
auto* const right = new SeparateHeapMap{
|
||||
.vaddr = left->vaddr + left_size,
|
||||
.paddr = left->paddr + left_size,
|
||||
.size = orig_size - left_size,
|
||||
.size = size - left_size,
|
||||
.map_id = left->map_id,
|
||||
.tick = left->tick,
|
||||
.perm = left->perm,
|
||||
.is_resident = left->is_resident,
|
||||
};
|
||||
|
||||
// Insert the new right map.
|
||||
m_map_count++;
|
||||
m_mappings.insert(*right);
|
||||
|
||||
// If resident, also insert into resident map.
|
||||
if (right->is_resident) {
|
||||
m_resident_map_count++;
|
||||
m_resident_mappings.insert(*right);
|
||||
// If the original map was not resident, we are done.
|
||||
if (!left->is_resident) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update reference count.
|
||||
this->AddReferenceLocked(left->map_id, 1);
|
||||
|
||||
// Insert right into resident map.
|
||||
m_resident_mappings.insert(*right);
|
||||
}
|
||||
|
||||
HeapTracker::AddrTree::iterator HeapTracker::MergeHeapMapForEvictLocked(AddrTree::iterator it) {
|
||||
if (it == m_mappings.end()) {
|
||||
// Not contained.
|
||||
return it;
|
||||
}
|
||||
|
||||
if (it == m_mappings.begin()) {
|
||||
// Nothing to merge with.
|
||||
return std::next(it);
|
||||
}
|
||||
|
||||
// Get the left and right items.
|
||||
auto* const right = std::addressof(*it);
|
||||
auto* const left = std::addressof(*std::prev(it));
|
||||
|
||||
if (left->vaddr + left->size != right->vaddr) {
|
||||
// Virtual range not contiguous, cannot merge.
|
||||
return std::next(it);
|
||||
}
|
||||
|
||||
if (left->paddr + left->size != right->paddr) {
|
||||
// Physical range not contiguous, cannot merge.
|
||||
return std::next(it);
|
||||
}
|
||||
|
||||
if (left->perm != right->perm) {
|
||||
// Permissions mismatch, cannot merge.
|
||||
return std::next(it);
|
||||
}
|
||||
|
||||
if (left->map_id != right->map_id) {
|
||||
// Map ID mismatch, cannot merge.
|
||||
return std::next(it);
|
||||
}
|
||||
|
||||
// Merge size to the left.
|
||||
left->size += right->size;
|
||||
|
||||
// Erase the right element.
|
||||
const auto next_it = m_mappings.erase(it);
|
||||
|
||||
// Free the right element.
|
||||
delete right;
|
||||
|
||||
// Return the iterator to the next position.
|
||||
return next_it;
|
||||
}
|
||||
|
||||
HeapTracker::AddrTree::iterator HeapTracker::GetNearestHeapMapLocked(VAddr offset) {
|
||||
@@ -278,4 +360,26 @@ HeapTracker::AddrTree::iterator HeapTracker::GetNearestHeapMapLocked(VAddr offse
|
||||
return m_mappings.find(key);
|
||||
}
|
||||
|
||||
void HeapTracker::AddReferenceLocked(size_t map_id, size_t inc) {
|
||||
m_resident_map_counts[map_id]++;
|
||||
}
|
||||
|
||||
bool HeapTracker::RemoveReferenceLocked(MapCountTree::iterator it, size_t dec) {
|
||||
ASSERT(it != m_resident_map_counts.end());
|
||||
|
||||
const auto new_value = it->second -= dec;
|
||||
ASSERT(new_value >= 0);
|
||||
|
||||
if (new_value <= 0) {
|
||||
m_resident_map_counts.erase(it);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HeapTracker::IsEvictRequiredLocked() {
|
||||
return m_resident_map_counts.size() > MaxResidentMapCount;
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -3,9 +3,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
#include <shared_mutex>
|
||||
|
||||
#include "common/host_memory.h"
|
||||
@@ -19,6 +18,7 @@ struct SeparateHeapMap {
|
||||
VAddr vaddr{};
|
||||
PAddr paddr{};
|
||||
size_t size{};
|
||||
size_t map_id{};
|
||||
size_t tick{};
|
||||
MemoryPermission perm{};
|
||||
bool is_resident{};
|
||||
@@ -64,6 +64,14 @@ public:
|
||||
bool DeferredMapSeparateHeap(u8* fault_address);
|
||||
bool DeferredMapSeparateHeap(size_t virtual_offset);
|
||||
|
||||
private:
|
||||
Common::HostMemory& m_buffer;
|
||||
|
||||
std::shared_mutex m_rebuild_lock{};
|
||||
std::mutex m_lock{};
|
||||
size_t m_next_map_id{};
|
||||
size_t m_tick{};
|
||||
|
||||
private:
|
||||
using AddrTreeTraits =
|
||||
Common::IntrusiveRedBlackTreeMemberTraitsDeferredAssert<&SeparateHeapMap::addr_node>;
|
||||
@@ -72,7 +80,9 @@ private:
|
||||
using TickTreeTraits =
|
||||
Common::IntrusiveRedBlackTreeMemberTraitsDeferredAssert<&SeparateHeapMap::tick_node>;
|
||||
using TickTree = TickTreeTraits::TreeType<SeparateHeapMapTickComparator>;
|
||||
using MapCountTree = std::map<size_t, s64>;
|
||||
|
||||
MapCountTree m_resident_map_counts{};
|
||||
AddrTree m_mappings{};
|
||||
TickTree m_resident_mappings{};
|
||||
|
||||
@@ -80,19 +90,14 @@ private:
|
||||
void SplitHeapMap(VAddr offset, size_t size);
|
||||
void SplitHeapMapLocked(VAddr offset);
|
||||
|
||||
AddrTree::iterator MergeHeapMapForEvictLocked(AddrTree::iterator cur);
|
||||
AddrTree::iterator GetNearestHeapMapLocked(VAddr offset);
|
||||
|
||||
void RebuildSeparateHeapAddressSpace();
|
||||
bool EvictSingleSeparateHeapMap();
|
||||
|
||||
private:
|
||||
Common::HostMemory& m_buffer;
|
||||
const s64 m_max_resident_map_count;
|
||||
|
||||
std::shared_mutex m_rebuild_lock{};
|
||||
std::mutex m_lock{};
|
||||
s64 m_map_count{};
|
||||
s64 m_resident_map_count{};
|
||||
size_t m_tick{};
|
||||
void AddReferenceLocked(size_t map_id, size_t inc);
|
||||
bool RemoveReferenceLocked(MapCountTree::iterator map_id, size_t dec);
|
||||
bool IsEvictRequiredLocked();
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -208,10 +208,6 @@ public:
|
||||
instance->StartBackendThread();
|
||||
}
|
||||
|
||||
static void Stop() {
|
||||
instance->StopBackendThread();
|
||||
}
|
||||
|
||||
Impl(const Impl&) = delete;
|
||||
Impl& operator=(const Impl&) = delete;
|
||||
|
||||
@@ -263,15 +259,6 @@ private:
|
||||
});
|
||||
}
|
||||
|
||||
void StopBackendThread() {
|
||||
backend_thread.request_stop();
|
||||
if (backend_thread.joinable()) {
|
||||
backend_thread.join();
|
||||
}
|
||||
|
||||
ForEachBackend([](Backend& backend) { backend.Flush(); });
|
||||
}
|
||||
|
||||
Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsigned int line_nr,
|
||||
const char* function, std::string&& message) const {
|
||||
using std::chrono::duration_cast;
|
||||
@@ -326,10 +313,6 @@ void Start() {
|
||||
Impl::Start();
|
||||
}
|
||||
|
||||
void Stop() {
|
||||
Impl::Stop();
|
||||
}
|
||||
|
||||
void DisableLoggingInTests() {
|
||||
initialization_in_progress_suppress_logging = true;
|
||||
}
|
||||
|
||||
@@ -14,9 +14,6 @@ void Initialize();
|
||||
|
||||
void Start();
|
||||
|
||||
/// Explicitly stops the logger thread and flushes the buffers
|
||||
void Stop();
|
||||
|
||||
void DisableLoggingInTests();
|
||||
|
||||
/**
|
||||
|
||||
@@ -103,7 +103,7 @@ private:
|
||||
// Having them on the same cache-line would result in false-sharing between them.
|
||||
// TODO: Remove this ifdef whenever clang and GCC support
|
||||
// std::hardware_destructive_interference_size.
|
||||
#ifdef __cpp_lib_hardware_interference_size
|
||||
#if defined(_MSC_VER) && _MSC_VER >= 1911
|
||||
alignas(std::hardware_destructive_interference_size) std::atomic_size_t m_read_index{0};
|
||||
alignas(std::hardware_destructive_interference_size) std::atomic_size_t m_write_index{0};
|
||||
#else
|
||||
|
||||
@@ -29,6 +29,7 @@ std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callbac
|
||||
struct CoreTiming::Event {
|
||||
s64 time;
|
||||
u64 fifo_order;
|
||||
std::uintptr_t user_data;
|
||||
std::weak_ptr<EventType> type;
|
||||
s64 reschedule_time;
|
||||
heap_t::handle_type handle{};
|
||||
@@ -66,15 +67,17 @@ void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) {
|
||||
event_fifo_id = 0;
|
||||
shutting_down = false;
|
||||
cpu_ticks = 0;
|
||||
const auto empty_timed_callback = [](std::uintptr_t, u64, std::chrono::nanoseconds)
|
||||
-> std::optional<std::chrono::nanoseconds> { return std::nullopt; };
|
||||
ev_lost = CreateEvent("_lost_event", empty_timed_callback);
|
||||
if (is_multicore) {
|
||||
timer_thread = std::make_unique<std::jthread>(ThreadEntry, std::ref(*this));
|
||||
}
|
||||
}
|
||||
|
||||
void CoreTiming::ClearPendingEvents() {
|
||||
std::scoped_lock lock{advance_lock, basic_lock};
|
||||
std::scoped_lock lock{basic_lock};
|
||||
event_queue.clear();
|
||||
event.Set();
|
||||
}
|
||||
|
||||
void CoreTiming::Pause(bool is_paused) {
|
||||
@@ -116,12 +119,14 @@ bool CoreTiming::HasPendingEvents() const {
|
||||
}
|
||||
|
||||
void CoreTiming::ScheduleEvent(std::chrono::nanoseconds ns_into_future,
|
||||
const std::shared_ptr<EventType>& event_type, bool absolute_time) {
|
||||
const std::shared_ptr<EventType>& event_type,
|
||||
std::uintptr_t user_data, bool absolute_time) {
|
||||
{
|
||||
std::scoped_lock scope{basic_lock};
|
||||
const auto next_time{absolute_time ? ns_into_future : GetGlobalTimeNs() + ns_into_future};
|
||||
|
||||
auto h{event_queue.emplace(Event{next_time.count(), event_fifo_id++, event_type, 0})};
|
||||
auto h{event_queue.emplace(
|
||||
Event{next_time.count(), event_fifo_id++, user_data, event_type, 0})};
|
||||
(*h).handle = h;
|
||||
}
|
||||
|
||||
@@ -131,13 +136,13 @@ void CoreTiming::ScheduleEvent(std::chrono::nanoseconds ns_into_future,
|
||||
void CoreTiming::ScheduleLoopingEvent(std::chrono::nanoseconds start_time,
|
||||
std::chrono::nanoseconds resched_time,
|
||||
const std::shared_ptr<EventType>& event_type,
|
||||
bool absolute_time) {
|
||||
std::uintptr_t user_data, bool absolute_time) {
|
||||
{
|
||||
std::scoped_lock scope{basic_lock};
|
||||
const auto next_time{absolute_time ? start_time : GetGlobalTimeNs() + start_time};
|
||||
|
||||
auto h{event_queue.emplace(
|
||||
Event{next_time.count(), event_fifo_id++, event_type, resched_time.count()})};
|
||||
auto h{event_queue.emplace(Event{next_time.count(), event_fifo_id++, user_data, event_type,
|
||||
resched_time.count()})};
|
||||
(*h).handle = h;
|
||||
}
|
||||
|
||||
@@ -145,14 +150,14 @@ void CoreTiming::ScheduleLoopingEvent(std::chrono::nanoseconds start_time,
|
||||
}
|
||||
|
||||
void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type,
|
||||
UnscheduleEventType type) {
|
||||
std::uintptr_t user_data, bool wait) {
|
||||
{
|
||||
std::scoped_lock lk{basic_lock};
|
||||
|
||||
std::vector<heap_t::handle_type> to_remove;
|
||||
for (auto itr = event_queue.begin(); itr != event_queue.end(); itr++) {
|
||||
const Event& e = *itr;
|
||||
if (e.type.lock().get() == event_type.get()) {
|
||||
if (e.type.lock().get() == event_type.get() && e.user_data == user_data) {
|
||||
to_remove.push_back(itr->handle);
|
||||
}
|
||||
}
|
||||
@@ -160,12 +165,10 @@ void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type,
|
||||
for (auto h : to_remove) {
|
||||
event_queue.erase(h);
|
||||
}
|
||||
|
||||
event_type->sequence_number++;
|
||||
}
|
||||
|
||||
// Force any in-progress events to finish
|
||||
if (type == UnscheduleEventType::Wait) {
|
||||
if (wait) {
|
||||
std::scoped_lock lk{advance_lock};
|
||||
}
|
||||
}
|
||||
@@ -205,31 +208,28 @@ std::optional<s64> CoreTiming::Advance() {
|
||||
const Event& evt = event_queue.top();
|
||||
|
||||
if (const auto event_type{evt.type.lock()}) {
|
||||
const auto evt_time = evt.time;
|
||||
const auto evt_sequence_num = event_type->sequence_number;
|
||||
|
||||
if (evt.reschedule_time == 0) {
|
||||
const auto evt_user_data = evt.user_data;
|
||||
const auto evt_time = evt.time;
|
||||
|
||||
event_queue.pop();
|
||||
|
||||
basic_lock.unlock();
|
||||
|
||||
event_type->callback(
|
||||
evt_time, std::chrono::nanoseconds{GetGlobalTimeNs().count() - evt_time});
|
||||
evt_user_data, evt_time,
|
||||
std::chrono::nanoseconds{GetGlobalTimeNs().count() - evt_time});
|
||||
|
||||
basic_lock.lock();
|
||||
} else {
|
||||
basic_lock.unlock();
|
||||
|
||||
const auto new_schedule_time{event_type->callback(
|
||||
evt_time, std::chrono::nanoseconds{GetGlobalTimeNs().count() - evt_time})};
|
||||
evt.user_data, evt.time,
|
||||
std::chrono::nanoseconds{GetGlobalTimeNs().count() - evt.time})};
|
||||
|
||||
basic_lock.lock();
|
||||
|
||||
if (evt_sequence_num != event_type->sequence_number) {
|
||||
// Heap handle is invalidated after external modification.
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto next_schedule_time{new_schedule_time.has_value()
|
||||
? new_schedule_time.value().count()
|
||||
: evt.reschedule_time};
|
||||
@@ -241,8 +241,8 @@ std::optional<s64> CoreTiming::Advance() {
|
||||
next_time = pause_end_time + next_schedule_time;
|
||||
}
|
||||
|
||||
event_queue.update(evt.handle, Event{next_time, event_fifo_id++, evt.type,
|
||||
next_schedule_time, evt.handle});
|
||||
event_queue.update(evt.handle, Event{next_time, event_fifo_id++, evt.user_data,
|
||||
evt.type, next_schedule_time, evt.handle});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,25 +22,17 @@ namespace Core::Timing {
|
||||
|
||||
/// A callback that may be scheduled for a particular core timing event.
|
||||
using TimedCallback = std::function<std::optional<std::chrono::nanoseconds>(
|
||||
s64 time, std::chrono::nanoseconds ns_late)>;
|
||||
std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late)>;
|
||||
|
||||
/// Contains the characteristics of a particular event.
|
||||
struct EventType {
|
||||
explicit EventType(TimedCallback&& callback_, std::string&& name_)
|
||||
: callback{std::move(callback_)}, name{std::move(name_)}, sequence_number{0} {}
|
||||
: callback{std::move(callback_)}, name{std::move(name_)} {}
|
||||
|
||||
/// The event's callback function.
|
||||
TimedCallback callback;
|
||||
/// A pointer to the name of the event.
|
||||
const std::string name;
|
||||
/// A monotonic sequence number, incremented when this event is
|
||||
/// changed externally.
|
||||
size_t sequence_number;
|
||||
};
|
||||
|
||||
enum class UnscheduleEventType {
|
||||
Wait,
|
||||
NoWait,
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -97,17 +89,23 @@ public:
|
||||
|
||||
/// Schedules an event in core timing
|
||||
void ScheduleEvent(std::chrono::nanoseconds ns_into_future,
|
||||
const std::shared_ptr<EventType>& event_type, bool absolute_time = false);
|
||||
const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data = 0,
|
||||
bool absolute_time = false);
|
||||
|
||||
/// Schedules an event which will automatically re-schedule itself with the given time, until
|
||||
/// unscheduled
|
||||
void ScheduleLoopingEvent(std::chrono::nanoseconds start_time,
|
||||
std::chrono::nanoseconds resched_time,
|
||||
const std::shared_ptr<EventType>& event_type,
|
||||
bool absolute_time = false);
|
||||
std::uintptr_t user_data = 0, bool absolute_time = false);
|
||||
|
||||
void UnscheduleEvent(const std::shared_ptr<EventType>& event_type,
|
||||
UnscheduleEventType type = UnscheduleEventType::Wait);
|
||||
void UnscheduleEvent(const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data,
|
||||
bool wait = true);
|
||||
|
||||
void UnscheduleEventWithoutWait(const std::shared_ptr<EventType>& event_type,
|
||||
std::uintptr_t user_data) {
|
||||
UnscheduleEvent(event_type, user_data, false);
|
||||
}
|
||||
|
||||
void AddTicks(u64 ticks_to_add);
|
||||
|
||||
@@ -160,6 +158,7 @@ private:
|
||||
heap_t event_queue;
|
||||
u64 event_fifo_id = 0;
|
||||
|
||||
std::shared_ptr<EventType> ev_lost;
|
||||
Common::Event event{};
|
||||
Common::Event pause_event{};
|
||||
mutable std::mutex basic_lock;
|
||||
|
||||
@@ -8,22 +8,19 @@
|
||||
namespace Kernel {
|
||||
|
||||
void KAutoObjectWithListContainer::Register(KAutoObjectWithList* obj) {
|
||||
// KScopedInterruptDisable di;
|
||||
KScopedSpinLock lk(m_lock);
|
||||
KScopedLightLock lk(m_lock);
|
||||
|
||||
m_object_list.insert_unique(*obj);
|
||||
}
|
||||
|
||||
void KAutoObjectWithListContainer::Unregister(KAutoObjectWithList* obj) {
|
||||
// KScopedInterruptDisable di;
|
||||
KScopedSpinLock lk(m_lock);
|
||||
KScopedLightLock lk(m_lock);
|
||||
|
||||
m_object_list.erase(*obj);
|
||||
}
|
||||
|
||||
size_t KAutoObjectWithListContainer::GetOwnedCount(KProcess* owner) {
|
||||
// KScopedInterruptDisable di;
|
||||
KScopedSpinLock lk(m_lock);
|
||||
KScopedLightLock lk(m_lock);
|
||||
|
||||
return std::count_if(m_object_list.begin(), m_object_list.end(),
|
||||
[&](const auto& obj) { return obj.GetOwner() == owner; });
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "core/hle/kernel/k_auto_object.h"
|
||||
#include "core/hle/kernel/k_spin_lock.h"
|
||||
#include "core/hle/kernel/k_light_lock.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
@@ -21,7 +21,32 @@ public:
|
||||
|
||||
using ListType = boost::intrusive::rbtree<KAutoObjectWithList>;
|
||||
|
||||
KAutoObjectWithListContainer(KernelCore& kernel) : m_lock(), m_object_list() {}
|
||||
class ListAccessor : public KScopedLightLock {
|
||||
public:
|
||||
explicit ListAccessor(KAutoObjectWithListContainer* container)
|
||||
: KScopedLightLock(container->m_lock), m_list(container->m_object_list) {}
|
||||
explicit ListAccessor(KAutoObjectWithListContainer& container)
|
||||
: KScopedLightLock(container.m_lock), m_list(container.m_object_list) {}
|
||||
|
||||
typename ListType::iterator begin() const {
|
||||
return m_list.begin();
|
||||
}
|
||||
|
||||
typename ListType::iterator end() const {
|
||||
return m_list.end();
|
||||
}
|
||||
|
||||
typename ListType::iterator find(typename ListType::const_reference ref) const {
|
||||
return m_list.find(ref);
|
||||
}
|
||||
|
||||
private:
|
||||
ListType& m_list;
|
||||
};
|
||||
|
||||
friend class ListAccessor;
|
||||
|
||||
KAutoObjectWithListContainer(KernelCore& kernel) : m_lock(kernel), m_object_list() {}
|
||||
|
||||
void Initialize() {}
|
||||
void Finalize() {}
|
||||
@@ -31,7 +56,7 @@ public:
|
||||
size_t GetOwnedCount(KProcess* owner);
|
||||
|
||||
private:
|
||||
KSpinLock m_lock;
|
||||
KLightLock m_lock;
|
||||
ListType m_object_list;
|
||||
};
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ public:
|
||||
public:
|
||||
explicit KHandleTable(KernelCore& kernel) : m_kernel(kernel) {}
|
||||
|
||||
Result Initialize(s32 size) {
|
||||
Result Initialize(KProcess* owner, s32 size) {
|
||||
// Check that the table size is valid.
|
||||
R_UNLESS(size <= static_cast<s32>(MaxTableSize), ResultOutOfMemory);
|
||||
|
||||
@@ -44,6 +44,7 @@ public:
|
||||
m_next_linear_id = MinLinearId;
|
||||
m_count = 0;
|
||||
m_free_head_index = -1;
|
||||
m_owner = owner;
|
||||
|
||||
// Free all entries.
|
||||
for (s32 i = 0; i < static_cast<s32>(m_table_size); ++i) {
|
||||
@@ -90,7 +91,8 @@ public:
|
||||
// Handle pseudo-handles.
|
||||
if constexpr (std::derived_from<KProcess, T>) {
|
||||
if (handle == Svc::PseudoHandle::CurrentProcess) {
|
||||
auto* const cur_process = GetCurrentProcessPointer(m_kernel);
|
||||
// TODO: this should be the current process
|
||||
auto* const cur_process = m_owner;
|
||||
ASSERT(cur_process != nullptr);
|
||||
return cur_process;
|
||||
}
|
||||
@@ -300,6 +302,7 @@ private:
|
||||
|
||||
private:
|
||||
KernelCore& m_kernel;
|
||||
KProcess* m_owner{};
|
||||
std::array<EntryInfo, MaxTableSize> m_entry_infos{};
|
||||
std::array<KAutoObject*, MaxTableSize> m_objects{};
|
||||
mutable KSpinLock m_lock;
|
||||
|
||||
@@ -10,15 +10,15 @@ namespace Kernel {
|
||||
|
||||
void KHardwareTimer::Initialize() {
|
||||
// Create the timing callback to register with CoreTiming.
|
||||
m_event_type = Core::Timing::CreateEvent("KHardwareTimer::Callback",
|
||||
[this](s64, std::chrono::nanoseconds) {
|
||||
this->DoTask();
|
||||
return std::nullopt;
|
||||
});
|
||||
m_event_type = Core::Timing::CreateEvent(
|
||||
"KHardwareTimer::Callback", [](std::uintptr_t timer_handle, s64, std::chrono::nanoseconds) {
|
||||
reinterpret_cast<KHardwareTimer*>(timer_handle)->DoTask();
|
||||
return std::nullopt;
|
||||
});
|
||||
}
|
||||
|
||||
void KHardwareTimer::Finalize() {
|
||||
m_kernel.System().CoreTiming().UnscheduleEvent(m_event_type);
|
||||
m_kernel.System().CoreTiming().UnscheduleEvent(m_event_type, reinterpret_cast<uintptr_t>(this));
|
||||
m_wakeup_time = std::numeric_limits<s64>::max();
|
||||
m_event_type.reset();
|
||||
}
|
||||
@@ -57,12 +57,13 @@ void KHardwareTimer::EnableInterrupt(s64 wakeup_time) {
|
||||
|
||||
m_wakeup_time = wakeup_time;
|
||||
m_kernel.System().CoreTiming().ScheduleEvent(std::chrono::nanoseconds{m_wakeup_time},
|
||||
m_event_type, true);
|
||||
m_event_type, reinterpret_cast<uintptr_t>(this),
|
||||
true);
|
||||
}
|
||||
|
||||
void KHardwareTimer::DisableInterrupt() {
|
||||
m_kernel.System().CoreTiming().UnscheduleEvent(m_event_type,
|
||||
Core::Timing::UnscheduleEventType::NoWait);
|
||||
m_kernel.System().CoreTiming().UnscheduleEventWithoutWait(m_event_type,
|
||||
reinterpret_cast<uintptr_t>(this));
|
||||
m_wakeup_time = std::numeric_limits<s64>::max();
|
||||
}
|
||||
|
||||
|
||||
@@ -552,7 +552,7 @@ private:
|
||||
|
||||
Result InitializeHandleTable(s32 size) {
|
||||
// Try to initialize the handle table.
|
||||
R_TRY(m_handle_table.Initialize(size));
|
||||
R_TRY(m_handle_table.Initialize(this, size));
|
||||
|
||||
// We succeeded, so note that we did.
|
||||
m_is_handle_table_initialized = true;
|
||||
|
||||
@@ -1147,7 +1147,8 @@ Result KServerSession::ReceiveRequest(uintptr_t server_message, uintptr_t server
|
||||
*out_context =
|
||||
std::make_shared<Service::HLERequestContext>(m_kernel, memory, this, client_thread);
|
||||
(*out_context)->SetSessionRequestManager(manager);
|
||||
(*out_context)->PopulateFromIncomingCommandBuffer(cmd_buf);
|
||||
(*out_context)
|
||||
->PopulateFromIncomingCommandBuffer(*client_thread->GetOwnerProcess(), cmd_buf);
|
||||
// We succeeded.
|
||||
R_SUCCEED();
|
||||
} else {
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "core/hle/kernel/k_light_lock.h"
|
||||
#include "core/hle/kernel/k_page_group.h"
|
||||
#include "core/hle/kernel/slab_helpers.h"
|
||||
#include "core/hle/kernel/svc_types.h"
|
||||
|
||||
@@ -238,7 +238,7 @@ struct KernelCore::Impl {
|
||||
void InitializePreemption(KernelCore& kernel) {
|
||||
preemption_event = Core::Timing::CreateEvent(
|
||||
"PreemptionCallback",
|
||||
[this, &kernel](s64 time,
|
||||
[this, &kernel](std::uintptr_t, s64 time,
|
||||
std::chrono::nanoseconds) -> std::optional<std::chrono::nanoseconds> {
|
||||
{
|
||||
KScopedSchedulerLock lock(kernel);
|
||||
|
||||
@@ -1513,7 +1513,8 @@ void ILibraryAppletCreator::CreateTransferMemoryStorage(HLERequestContext& ctx)
|
||||
return;
|
||||
}
|
||||
|
||||
auto transfer_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(handle);
|
||||
auto transfer_mem =
|
||||
system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(handle);
|
||||
|
||||
if (transfer_mem.IsNull()) {
|
||||
LOG_ERROR(Service_AM, "transfer_mem is a nullptr for handle={:08X}", handle);
|
||||
@@ -1523,7 +1524,8 @@ void ILibraryAppletCreator::CreateTransferMemoryStorage(HLERequestContext& ctx)
|
||||
}
|
||||
|
||||
std::vector<u8> memory(transfer_mem->GetSize());
|
||||
ctx.GetMemory().ReadBlock(transfer_mem->GetSourceAddress(), memory.data(), memory.size());
|
||||
system.ApplicationMemory().ReadBlock(transfer_mem->GetSourceAddress(), memory.data(),
|
||||
memory.size());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
@@ -1545,7 +1547,8 @@ void ILibraryAppletCreator::CreateHandleStorage(HLERequestContext& ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto transfer_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(handle);
|
||||
auto transfer_mem =
|
||||
system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(handle);
|
||||
|
||||
if (transfer_mem.IsNull()) {
|
||||
LOG_ERROR(Service_AM, "transfer_mem is a nullptr for handle={:08X}", handle);
|
||||
@@ -1555,7 +1558,8 @@ void ILibraryAppletCreator::CreateHandleStorage(HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
std::vector<u8> memory(transfer_mem->GetSize());
|
||||
ctx.GetMemory().ReadBlock(transfer_mem->GetSourceAddress(), memory.data(), memory.size());
|
||||
system.ApplicationMemory().ReadBlock(transfer_mem->GetSourceAddress(), memory.data(),
|
||||
memory.size());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
|
||||
@@ -454,8 +454,10 @@ void AudRenU::OpenAudioRenderer(HLERequestContext& ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto process{ctx.GetObjectFromHandle<Kernel::KProcess>(process_handle)};
|
||||
auto transfer_memory{ctx.GetObjectFromHandle<Kernel::KTransferMemory>(transfer_memory_handle)};
|
||||
const auto& handle_table{system.ApplicationProcess()->GetHandleTable()};
|
||||
auto process{handle_table.GetObject<Kernel::KProcess>(process_handle)};
|
||||
auto transfer_memory{
|
||||
process->GetHandleTable().GetObject<Kernel::KTransferMemory>(transfer_memory_handle)};
|
||||
|
||||
const auto session_id{impl->GetSessionId()};
|
||||
if (session_id == -1) {
|
||||
|
||||
@@ -278,7 +278,9 @@ void HwOpus::OpenHardwareOpusDecoder(HLERequestContext& ctx) {
|
||||
auto params = rp.PopRaw<OpusParameters>();
|
||||
auto transfer_memory_size{rp.Pop<u32>()};
|
||||
auto transfer_memory_handle{ctx.GetCopyHandle(0)};
|
||||
auto transfer_memory{ctx.GetObjectFromHandle<Kernel::KTransferMemory>(transfer_memory_handle)};
|
||||
auto transfer_memory{
|
||||
system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
|
||||
transfer_memory_handle)};
|
||||
|
||||
LOG_DEBUG(Service_Audio, "sample_rate {} channel_count {} transfer_memory_size 0x{:X}",
|
||||
params.sample_rate, params.channel_count, transfer_memory_size);
|
||||
@@ -321,7 +323,9 @@ void HwOpus::OpenHardwareOpusDecoderForMultiStream(HLERequestContext& ctx) {
|
||||
|
||||
auto transfer_memory_size{rp.Pop<u32>()};
|
||||
auto transfer_memory_handle{ctx.GetCopyHandle(0)};
|
||||
auto transfer_memory{ctx.GetObjectFromHandle<Kernel::KTransferMemory>(transfer_memory_handle)};
|
||||
auto transfer_memory{
|
||||
system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
|
||||
transfer_memory_handle)};
|
||||
|
||||
LOG_DEBUG(Service_Audio,
|
||||
"sample_rate {} channel_count {} total_stream_count {} stereo_stream_count {} "
|
||||
@@ -370,7 +374,9 @@ void HwOpus::OpenHardwareOpusDecoderEx(HLERequestContext& ctx) {
|
||||
auto params = rp.PopRaw<OpusParametersEx>();
|
||||
auto transfer_memory_size{rp.Pop<u32>()};
|
||||
auto transfer_memory_handle{ctx.GetCopyHandle(0)};
|
||||
auto transfer_memory{ctx.GetObjectFromHandle<Kernel::KTransferMemory>(transfer_memory_handle)};
|
||||
auto transfer_memory{
|
||||
system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
|
||||
transfer_memory_handle)};
|
||||
|
||||
LOG_DEBUG(Service_Audio, "sample_rate {} channel_count {} transfer_memory_size 0x{:X}",
|
||||
params.sample_rate, params.channel_count, transfer_memory_size);
|
||||
@@ -408,7 +414,9 @@ void HwOpus::OpenHardwareOpusDecoderForMultiStreamEx(HLERequestContext& ctx) {
|
||||
|
||||
auto transfer_memory_size{rp.Pop<u32>()};
|
||||
auto transfer_memory_handle{ctx.GetCopyHandle(0)};
|
||||
auto transfer_memory{ctx.GetObjectFromHandle<Kernel::KTransferMemory>(transfer_memory_handle)};
|
||||
auto transfer_memory{
|
||||
system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
|
||||
transfer_memory_handle)};
|
||||
|
||||
LOG_DEBUG(Service_Audio,
|
||||
"sample_rate {} channel_count {} total_stream_count {} stereo_stream_count {} "
|
||||
|
||||
@@ -1850,7 +1850,8 @@ void IHidServer::InitializeSevenSixAxisSensor(HLERequestContext& ctx) {
|
||||
ASSERT_MSG(t_mem_1_size == 0x1000, "t_mem_1_size is not 0x1000 bytes");
|
||||
ASSERT_MSG(t_mem_2_size == 0x7F000, "t_mem_2_size is not 0x7F000 bytes");
|
||||
|
||||
auto t_mem_1 = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(t_mem_1_handle);
|
||||
auto t_mem_1 = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
|
||||
t_mem_1_handle);
|
||||
|
||||
if (t_mem_1.IsNull()) {
|
||||
LOG_ERROR(Service_HID, "t_mem_1 is a nullptr for handle=0x{:08X}", t_mem_1_handle);
|
||||
@@ -1859,7 +1860,8 @@ void IHidServer::InitializeSevenSixAxisSensor(HLERequestContext& ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto t_mem_2 = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(t_mem_2_handle);
|
||||
auto t_mem_2 = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
|
||||
t_mem_2_handle);
|
||||
|
||||
if (t_mem_2.IsNull()) {
|
||||
LOG_ERROR(Service_HID, "t_mem_2 is a nullptr for handle=0x{:08X}", t_mem_2_handle);
|
||||
@@ -2140,7 +2142,8 @@ void IHidServer::WritePalmaWaveEntry(HLERequestContext& ctx) {
|
||||
|
||||
ASSERT_MSG(t_mem_size == 0x3000, "t_mem_size is not 0x3000 bytes");
|
||||
|
||||
auto t_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(t_mem_handle);
|
||||
auto t_mem = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
|
||||
t_mem_handle);
|
||||
|
||||
if (t_mem.IsNull()) {
|
||||
LOG_ERROR(Service_HID, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle);
|
||||
|
||||
@@ -49,10 +49,10 @@ HidBus::HidBus(Core::System& system_)
|
||||
// Register update callbacks
|
||||
hidbus_update_event = Core::Timing::CreateEvent(
|
||||
"Hidbus::UpdateCallback",
|
||||
[this](s64 time,
|
||||
[this](std::uintptr_t user_data, s64 time,
|
||||
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
||||
const auto guard = LockService();
|
||||
UpdateHidbus(ns_late);
|
||||
UpdateHidbus(user_data, ns_late);
|
||||
return std::nullopt;
|
||||
});
|
||||
|
||||
@@ -61,10 +61,10 @@ HidBus::HidBus(Core::System& system_)
|
||||
}
|
||||
|
||||
HidBus::~HidBus() {
|
||||
system.CoreTiming().UnscheduleEvent(hidbus_update_event);
|
||||
system.CoreTiming().UnscheduleEvent(hidbus_update_event, 0);
|
||||
}
|
||||
|
||||
void HidBus::UpdateHidbus(std::chrono::nanoseconds ns_late) {
|
||||
void HidBus::UpdateHidbus(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
|
||||
if (is_hidbus_enabled) {
|
||||
for (std::size_t i = 0; i < devices.size(); ++i) {
|
||||
if (!devices[i].is_device_initializated) {
|
||||
@@ -448,7 +448,8 @@ void HidBus::EnableJoyPollingReceiveMode(HLERequestContext& ctx) {
|
||||
|
||||
ASSERT_MSG(t_mem_size == 0x1000, "t_mem_size is not 0x1000 bytes");
|
||||
|
||||
auto t_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(t_mem_handle);
|
||||
auto t_mem = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
|
||||
t_mem_handle);
|
||||
|
||||
if (t_mem.IsNull()) {
|
||||
LOG_ERROR(Service_HID, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle);
|
||||
|
||||
@@ -108,7 +108,7 @@ private:
|
||||
void DisableJoyPollingReceiveMode(HLERequestContext& ctx);
|
||||
void SetStatusManagerType(HLERequestContext& ctx);
|
||||
|
||||
void UpdateHidbus(std::chrono::nanoseconds ns_late);
|
||||
void UpdateHidbus(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
|
||||
std::optional<std::size_t> GetDeviceIndexFromHandle(BusHandle handle) const;
|
||||
|
||||
template <typename T>
|
||||
|
||||
@@ -197,7 +197,8 @@ void IRS::RunImageTransferProcessor(HLERequestContext& ctx) {
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
const auto t_mem_handle{ctx.GetCopyHandle(0)};
|
||||
|
||||
auto t_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(t_mem_handle);
|
||||
auto t_mem = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
|
||||
t_mem_handle);
|
||||
|
||||
if (t_mem.IsNull()) {
|
||||
LOG_ERROR(Service_IRS, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle);
|
||||
@@ -443,7 +444,8 @@ void IRS::RunImageTransferExProcessor(HLERequestContext& ctx) {
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
const auto t_mem_handle{ctx.GetCopyHandle(0)};
|
||||
|
||||
auto t_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(t_mem_handle);
|
||||
auto t_mem = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
|
||||
t_mem_handle);
|
||||
|
||||
LOG_INFO(Service_IRS,
|
||||
"called, npad_type={}, npad_id={}, transfer_memory_size={}, "
|
||||
|
||||
@@ -227,7 +227,8 @@ void ResourceManager::EnableTouchScreen(u64 aruid, bool is_enabled) {
|
||||
applet_resource->EnableTouchScreen(aruid, is_enabled);
|
||||
}
|
||||
|
||||
void ResourceManager::UpdateControllers(std::chrono::nanoseconds ns_late) {
|
||||
void ResourceManager::UpdateControllers(std::uintptr_t user_data,
|
||||
std::chrono::nanoseconds ns_late) {
|
||||
auto& core_timing = system.CoreTiming();
|
||||
debug_pad->OnUpdate(core_timing);
|
||||
digitizer->OnUpdate(core_timing);
|
||||
@@ -240,19 +241,20 @@ void ResourceManager::UpdateControllers(std::chrono::nanoseconds ns_late) {
|
||||
capture_button->OnUpdate(core_timing);
|
||||
}
|
||||
|
||||
void ResourceManager::UpdateNpad(std::chrono::nanoseconds ns_late) {
|
||||
void ResourceManager::UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
|
||||
auto& core_timing = system.CoreTiming();
|
||||
npad->OnUpdate(core_timing);
|
||||
}
|
||||
|
||||
void ResourceManager::UpdateMouseKeyboard(std::chrono::nanoseconds ns_late) {
|
||||
void ResourceManager::UpdateMouseKeyboard(std::uintptr_t user_data,
|
||||
std::chrono::nanoseconds ns_late) {
|
||||
auto& core_timing = system.CoreTiming();
|
||||
mouse->OnUpdate(core_timing);
|
||||
debug_mouse->OnUpdate(core_timing);
|
||||
keyboard->OnUpdate(core_timing);
|
||||
}
|
||||
|
||||
void ResourceManager::UpdateMotion(std::chrono::nanoseconds ns_late) {
|
||||
void ResourceManager::UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
|
||||
auto& core_timing = system.CoreTiming();
|
||||
six_axis->OnUpdate(core_timing);
|
||||
seven_six_axis->OnUpdate(core_timing);
|
||||
@@ -271,34 +273,34 @@ IAppletResource::IAppletResource(Core::System& system_, std::shared_ptr<Resource
|
||||
// Register update callbacks
|
||||
npad_update_event = Core::Timing::CreateEvent(
|
||||
"HID::UpdatePadCallback",
|
||||
[this, resource](
|
||||
s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
||||
[this, resource](std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late)
|
||||
-> std::optional<std::chrono::nanoseconds> {
|
||||
const auto guard = LockService();
|
||||
resource->UpdateNpad(ns_late);
|
||||
resource->UpdateNpad(user_data, ns_late);
|
||||
return std::nullopt;
|
||||
});
|
||||
default_update_event = Core::Timing::CreateEvent(
|
||||
"HID::UpdateDefaultCallback",
|
||||
[this, resource](
|
||||
s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
||||
[this, resource](std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late)
|
||||
-> std::optional<std::chrono::nanoseconds> {
|
||||
const auto guard = LockService();
|
||||
resource->UpdateControllers(ns_late);
|
||||
resource->UpdateControllers(user_data, ns_late);
|
||||
return std::nullopt;
|
||||
});
|
||||
mouse_keyboard_update_event = Core::Timing::CreateEvent(
|
||||
"HID::UpdateMouseKeyboardCallback",
|
||||
[this, resource](
|
||||
s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
||||
[this, resource](std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late)
|
||||
-> std::optional<std::chrono::nanoseconds> {
|
||||
const auto guard = LockService();
|
||||
resource->UpdateMouseKeyboard(ns_late);
|
||||
resource->UpdateMouseKeyboard(user_data, ns_late);
|
||||
return std::nullopt;
|
||||
});
|
||||
motion_update_event = Core::Timing::CreateEvent(
|
||||
"HID::UpdateMotionCallback",
|
||||
[this, resource](
|
||||
s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
||||
[this, resource](std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late)
|
||||
-> std::optional<std::chrono::nanoseconds> {
|
||||
const auto guard = LockService();
|
||||
resource->UpdateMotion(ns_late);
|
||||
resource->UpdateMotion(user_data, ns_late);
|
||||
return std::nullopt;
|
||||
});
|
||||
|
||||
@@ -312,10 +314,10 @@ IAppletResource::IAppletResource(Core::System& system_, std::shared_ptr<Resource
|
||||
}
|
||||
|
||||
IAppletResource::~IAppletResource() {
|
||||
system.CoreTiming().UnscheduleEvent(npad_update_event);
|
||||
system.CoreTiming().UnscheduleEvent(default_update_event);
|
||||
system.CoreTiming().UnscheduleEvent(mouse_keyboard_update_event);
|
||||
system.CoreTiming().UnscheduleEvent(motion_update_event);
|
||||
system.CoreTiming().UnscheduleEvent(npad_update_event, 0);
|
||||
system.CoreTiming().UnscheduleEvent(default_update_event, 0);
|
||||
system.CoreTiming().UnscheduleEvent(mouse_keyboard_update_event, 0);
|
||||
system.CoreTiming().UnscheduleEvent(motion_update_event, 0);
|
||||
resource_manager->FreeAppletResourceId(aruid);
|
||||
}
|
||||
|
||||
|
||||
@@ -81,10 +81,10 @@ public:
|
||||
void EnablePadInput(u64 aruid, bool is_enabled);
|
||||
void EnableTouchScreen(u64 aruid, bool is_enabled);
|
||||
|
||||
void UpdateControllers(std::chrono::nanoseconds ns_late);
|
||||
void UpdateNpad(std::chrono::nanoseconds ns_late);
|
||||
void UpdateMouseKeyboard(std::chrono::nanoseconds ns_late);
|
||||
void UpdateMotion(std::chrono::nanoseconds ns_late);
|
||||
void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
|
||||
void UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
|
||||
void UpdateMouseKeyboard(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
|
||||
void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
|
||||
|
||||
private:
|
||||
Result CreateAppletResourceImpl(u64 aruid);
|
||||
|
||||
@@ -146,7 +146,10 @@ HLERequestContext::HLERequestContext(Kernel::KernelCore& kernel_, Core::Memory::
|
||||
|
||||
HLERequestContext::~HLERequestContext() = default;
|
||||
|
||||
void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
|
||||
void HLERequestContext::ParseCommandBuffer(Kernel::KProcess& process, u32_le* src_cmdbuf,
|
||||
bool incoming) {
|
||||
client_handle_table = &process.GetHandleTable();
|
||||
|
||||
IPC::RequestParser rp(src_cmdbuf);
|
||||
command_header = rp.PopRaw<IPC::CommandHeader>();
|
||||
|
||||
@@ -159,7 +162,7 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
|
||||
if (command_header->enable_handle_descriptor) {
|
||||
handle_descriptor_header = rp.PopRaw<IPC::HandleDescriptorHeader>();
|
||||
if (handle_descriptor_header->send_current_pid) {
|
||||
pid = thread->GetOwnerProcess()->GetProcessId();
|
||||
pid = process.GetProcessId();
|
||||
rp.Skip(2, false);
|
||||
}
|
||||
if (incoming) {
|
||||
@@ -267,10 +270,9 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
|
||||
rp.Skip(1, false); // The command is actually an u64, but we don't use the high part.
|
||||
}
|
||||
|
||||
Result HLERequestContext::PopulateFromIncomingCommandBuffer(u32_le* src_cmdbuf) {
|
||||
client_handle_table = &thread->GetOwnerProcess()->GetHandleTable();
|
||||
|
||||
ParseCommandBuffer(src_cmdbuf, true);
|
||||
Result HLERequestContext::PopulateFromIncomingCommandBuffer(Kernel::KProcess& process,
|
||||
u32_le* src_cmdbuf) {
|
||||
ParseCommandBuffer(process, src_cmdbuf, true);
|
||||
|
||||
if (command_header->IsCloseCommand()) {
|
||||
// Close does not populate the rest of the IPC header
|
||||
@@ -282,9 +284,9 @@ Result HLERequestContext::PopulateFromIncomingCommandBuffer(u32_le* src_cmdbuf)
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result HLERequestContext::WriteToOutgoingCommandBuffer() {
|
||||
Result HLERequestContext::WriteToOutgoingCommandBuffer(Kernel::KThread& requesting_thread) {
|
||||
auto current_offset = handles_offset;
|
||||
auto& owner_process = *thread->GetOwnerProcess();
|
||||
auto& owner_process = *requesting_thread.GetOwnerProcess();
|
||||
auto& handle_table = owner_process.GetHandleTable();
|
||||
|
||||
for (auto& object : outgoing_copy_objects) {
|
||||
@@ -317,7 +319,7 @@ Result HLERequestContext::WriteToOutgoingCommandBuffer() {
|
||||
}
|
||||
|
||||
// Copy the translated command buffer back into the thread's command buffer area.
|
||||
memory.WriteBlock(thread->GetTlsAddress(), cmd_buf.data(), write_size * sizeof(u32));
|
||||
memory.WriteBlock(requesting_thread.GetTlsAddress(), cmd_buf.data(), write_size * sizeof(u32));
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
#include "common/concepts.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/hle/ipc.h"
|
||||
#include "core/hle/kernel/k_handle_table.h"
|
||||
#include "core/hle/kernel/svc_common.h"
|
||||
|
||||
union Result;
|
||||
@@ -197,10 +196,10 @@ public:
|
||||
}
|
||||
|
||||
/// Populates this context with data from the requesting process/thread.
|
||||
Result PopulateFromIncomingCommandBuffer(u32_le* src_cmdbuf);
|
||||
Result PopulateFromIncomingCommandBuffer(Kernel::KProcess& process, u32_le* src_cmdbuf);
|
||||
|
||||
/// Writes data from this context back to the requesting process/thread.
|
||||
Result WriteToOutgoingCommandBuffer();
|
||||
Result WriteToOutgoingCommandBuffer(Kernel::KThread& requesting_thread);
|
||||
|
||||
[[nodiscard]] u32_le GetHipcCommand() const {
|
||||
return command;
|
||||
@@ -360,17 +359,8 @@ public:
|
||||
return *thread;
|
||||
}
|
||||
|
||||
[[nodiscard]] Core::Memory::Memory& GetMemory() const {
|
||||
return memory;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Kernel::KScopedAutoObject<T> GetObjectFromHandle(u32 handle) {
|
||||
auto obj = client_handle_table->GetObjectForIpc(handle, thread);
|
||||
if (obj.IsNotNull()) {
|
||||
return obj->DynamicCast<T*>();
|
||||
}
|
||||
return nullptr;
|
||||
Kernel::KHandleTable& GetClientHandleTable() {
|
||||
return *client_handle_table;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::shared_ptr<SessionRequestManager> GetManager() const {
|
||||
@@ -388,7 +378,7 @@ public:
|
||||
private:
|
||||
friend class IPC::ResponseBuilder;
|
||||
|
||||
void ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming);
|
||||
void ParseCommandBuffer(Kernel::KProcess& process, u32_le* src_cmdbuf, bool incoming);
|
||||
|
||||
std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf;
|
||||
Kernel::KServerSession* server_session{};
|
||||
|
||||
@@ -26,7 +26,7 @@ public:
|
||||
explicit IJitEnvironment(Core::System& system_, Kernel::KProcess& process_, CodeRange user_rx,
|
||||
CodeRange user_ro)
|
||||
: ServiceFramework{system_, "IJitEnvironment"}, process{&process_},
|
||||
context{process->GetMemory()} {
|
||||
context{system_.ApplicationMemory()} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IJitEnvironment::GenerateCode, "GenerateCode"},
|
||||
@@ -188,7 +188,7 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
auto tmem{ctx.GetObjectFromHandle<Kernel::KTransferMemory>(tmem_handle)};
|
||||
auto tmem{process->GetHandleTable().GetObject<Kernel::KTransferMemory>(tmem_handle)};
|
||||
if (tmem.IsNull()) {
|
||||
LOG_ERROR(Service_JIT, "attempted to load plugin with invalid transfer memory handle");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
@@ -356,7 +356,11 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
auto process{ctx.GetObjectFromHandle<Kernel::KProcess>(process_handle)};
|
||||
// Fetch using the handle table for the application process here,
|
||||
// since we are not multiprocess yet.
|
||||
const auto& handle_table{system.ApplicationProcess()->GetHandleTable()};
|
||||
|
||||
auto process{handle_table.GetObject<Kernel::KProcess>(process_handle)};
|
||||
if (process.IsNull()) {
|
||||
LOG_ERROR(Service_JIT, "process is null for handle=0x{:08X}", process_handle);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
@@ -364,7 +368,7 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
auto rx_mem{ctx.GetObjectFromHandle<Kernel::KCodeMemory>(rx_mem_handle)};
|
||||
auto rx_mem{handle_table.GetObject<Kernel::KCodeMemory>(rx_mem_handle)};
|
||||
if (rx_mem.IsNull()) {
|
||||
LOG_ERROR(Service_JIT, "rx_mem is null for handle=0x{:08X}", rx_mem_handle);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
@@ -372,7 +376,7 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
auto ro_mem{ctx.GetObjectFromHandle<Kernel::KCodeMemory>(ro_mem_handle)};
|
||||
auto ro_mem{handle_table.GetObject<Kernel::KCodeMemory>(ro_mem_handle)};
|
||||
if (ro_mem.IsNull()) {
|
||||
LOG_ERROR(Service_JIT, "ro_mem is null for handle=0x{:08X}", ro_mem_handle);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
|
||||
@@ -67,7 +67,7 @@ Nvnflinger::Nvnflinger(Core::System& system_, HosBinderDriverServer& hos_binder_
|
||||
// Schedule the screen composition events
|
||||
multi_composition_event = Core::Timing::CreateEvent(
|
||||
"ScreenComposition",
|
||||
[this](s64 time,
|
||||
[this](std::uintptr_t, s64 time,
|
||||
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
||||
vsync_signal.Set();
|
||||
return std::chrono::nanoseconds(GetNextTicks());
|
||||
@@ -75,7 +75,7 @@ Nvnflinger::Nvnflinger(Core::System& system_, HosBinderDriverServer& hos_binder_
|
||||
|
||||
single_composition_event = Core::Timing::CreateEvent(
|
||||
"ScreenComposition",
|
||||
[this](s64 time,
|
||||
[this](std::uintptr_t, s64 time,
|
||||
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
||||
const auto lock_guard = Lock();
|
||||
Compose();
|
||||
@@ -93,11 +93,11 @@ Nvnflinger::Nvnflinger(Core::System& system_, HosBinderDriverServer& hos_binder_
|
||||
|
||||
Nvnflinger::~Nvnflinger() {
|
||||
if (system.IsMulticore()) {
|
||||
system.CoreTiming().UnscheduleEvent(multi_composition_event);
|
||||
system.CoreTiming().UnscheduleEvent(multi_composition_event, {});
|
||||
vsync_thread.request_stop();
|
||||
vsync_signal.Set();
|
||||
} else {
|
||||
system.CoreTiming().UnscheduleEvent(single_composition_event);
|
||||
system.CoreTiming().UnscheduleEvent(single_composition_event, {});
|
||||
}
|
||||
|
||||
ShutdownLayers();
|
||||
|
||||
@@ -651,9 +651,10 @@ private:
|
||||
void RegisterProcessHandle(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_LDR, "(called)");
|
||||
|
||||
auto process = ctx.GetObjectFromHandle<Kernel::KProcess>(ctx.GetCopyHandle(0));
|
||||
auto process_h = ctx.GetClientHandleTable().GetObject(ctx.GetCopyHandle(0));
|
||||
auto client_pid = ctx.GetPID();
|
||||
auto result = interface.RegisterProcessHandle(client_pid, process.GetPointerUnsafe());
|
||||
auto result = interface.RegisterProcessHandle(client_pid,
|
||||
process_h->DynamicCast<Kernel::KProcess*>());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
@@ -670,11 +671,12 @@ private:
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto params = rp.PopRaw<InputParameters>();
|
||||
auto process = ctx.GetObjectFromHandle<Kernel::KProcess>(ctx.GetCopyHandle(0));
|
||||
auto process_h = ctx.GetClientHandleTable().GetObject(ctx.GetCopyHandle(0));
|
||||
|
||||
auto client_pid = ctx.GetPID();
|
||||
auto result = interface.RegisterProcessModuleInfo(
|
||||
client_pid, params.nrr_address, params.nrr_size, process.GetPointerUnsafe());
|
||||
auto result =
|
||||
interface.RegisterProcessModuleInfo(client_pid, params.nrr_address, params.nrr_size,
|
||||
process_h->DynamicCast<Kernel::KProcess*>());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
|
||||
@@ -203,7 +203,7 @@ Result ServiceFrameworkBase::HandleSyncRequest(Kernel::KServerSession& session,
|
||||
// If emulation was shutdown, we are closing service threads, do not write the response back to
|
||||
// memory that may be shutting down as well.
|
||||
if (system.IsPoweredOn()) {
|
||||
ctx.WriteToOutgoingCommandBuffer();
|
||||
ctx.WriteToOutgoingCommandBuffer(ctx.GetThread());
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
@@ -730,6 +730,17 @@ struct Memory::Impl {
|
||||
GetInteger(vaddr), []() {}, []() {});
|
||||
}
|
||||
|
||||
void FixPageProtection(u64 vaddr) {
|
||||
vaddr = Common::AlignDown(vaddr, YUZU_PAGESIZE);
|
||||
|
||||
if (!AddressSpaceContains(*current_page_table, vaddr, 1)) [[unlikely]] {
|
||||
return;
|
||||
}
|
||||
|
||||
ProtectRegion(*current_page_table, vaddr, YUZU_PAGESIZE,
|
||||
Common::MemoryPermission::ReadWrite);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a particular data type out of memory at the given virtual address.
|
||||
*
|
||||
@@ -1083,13 +1094,18 @@ bool Memory::InvalidateNCE(Common::ProcessAddress vaddr, size_t size) {
|
||||
rasterizer = true;
|
||||
});
|
||||
|
||||
const bool mapping_exists = mapped && ptr != nullptr;
|
||||
|
||||
#ifdef __linux__
|
||||
if (!rasterizer && mapped) {
|
||||
impl->buffer->DeferredMapSeparateHeap(GetInteger(vaddr));
|
||||
if (mapping_exists && !rasterizer) {
|
||||
if (!impl->buffer->DeferredMapSeparateHeap(GetInteger(vaddr))) {
|
||||
// GPU may have raced reprotecting this page, try to fix it.
|
||||
impl->FixPageProtection(GetInteger(vaddr));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return mapped && ptr != nullptr;
|
||||
return mapping_exists;
|
||||
}
|
||||
|
||||
bool Memory::InvalidateSeparateHeap(void* fault_address) {
|
||||
|
||||
@@ -190,15 +190,15 @@ CheatEngine::CheatEngine(System& system_, std::vector<CheatEntry> cheats_,
|
||||
}
|
||||
|
||||
CheatEngine::~CheatEngine() {
|
||||
core_timing.UnscheduleEvent(event);
|
||||
core_timing.UnscheduleEvent(event, 0);
|
||||
}
|
||||
|
||||
void CheatEngine::Initialize() {
|
||||
event = Core::Timing::CreateEvent(
|
||||
"CheatEngine::FrameCallback::" + Common::HexToString(metadata.main_nso_build_id),
|
||||
[this](s64 time,
|
||||
[this](std::uintptr_t user_data, s64 time,
|
||||
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
||||
FrameCallback(ns_late);
|
||||
FrameCallback(user_data, ns_late);
|
||||
return std::nullopt;
|
||||
});
|
||||
core_timing.ScheduleLoopingEvent(CHEAT_ENGINE_NS, CHEAT_ENGINE_NS, event);
|
||||
@@ -239,7 +239,7 @@ void CheatEngine::Reload(std::vector<CheatEntry> reload_cheats) {
|
||||
|
||||
MICROPROFILE_DEFINE(Cheat_Engine, "Add-Ons", "Cheat Engine", MP_RGB(70, 200, 70));
|
||||
|
||||
void CheatEngine::FrameCallback(std::chrono::nanoseconds ns_late) {
|
||||
void CheatEngine::FrameCallback(std::uintptr_t, std::chrono::nanoseconds ns_late) {
|
||||
if (is_pending_reload.exchange(false)) {
|
||||
vm.LoadProgram(cheats);
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ public:
|
||||
void Reload(std::vector<CheatEntry> reload_cheats);
|
||||
|
||||
private:
|
||||
void FrameCallback(std::chrono::nanoseconds ns_late);
|
||||
void FrameCallback(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
|
||||
|
||||
DmntCheatVm vm;
|
||||
CheatProcessMetadata metadata;
|
||||
|
||||
@@ -51,17 +51,18 @@ void MemoryWriteWidth(Core::Memory::Memory& memory, u32 width, VAddr addr, u64 v
|
||||
|
||||
Freezer::Freezer(Core::Timing::CoreTiming& core_timing_, Core::Memory::Memory& memory_)
|
||||
: core_timing{core_timing_}, memory{memory_} {
|
||||
event = Core::Timing::CreateEvent("MemoryFreezer::FrameCallback",
|
||||
[this](s64 time, std::chrono::nanoseconds ns_late)
|
||||
-> std::optional<std::chrono::nanoseconds> {
|
||||
FrameCallback(ns_late);
|
||||
return std::nullopt;
|
||||
});
|
||||
event = Core::Timing::CreateEvent(
|
||||
"MemoryFreezer::FrameCallback",
|
||||
[this](std::uintptr_t user_data, s64 time,
|
||||
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
||||
FrameCallback(user_data, ns_late);
|
||||
return std::nullopt;
|
||||
});
|
||||
core_timing.ScheduleEvent(memory_freezer_ns, event);
|
||||
}
|
||||
|
||||
Freezer::~Freezer() {
|
||||
core_timing.UnscheduleEvent(event);
|
||||
core_timing.UnscheduleEvent(event, 0);
|
||||
}
|
||||
|
||||
void Freezer::SetActive(bool is_active) {
|
||||
@@ -158,7 +159,7 @@ Freezer::Entries::const_iterator Freezer::FindEntry(VAddr address) const {
|
||||
[address](const Entry& entry) { return entry.address == address; });
|
||||
}
|
||||
|
||||
void Freezer::FrameCallback(std::chrono::nanoseconds ns_late) {
|
||||
void Freezer::FrameCallback(std::uintptr_t, std::chrono::nanoseconds ns_late) {
|
||||
if (!IsActive()) {
|
||||
LOG_DEBUG(Common_Memory, "Memory freezer has been deactivated, ending callback events.");
|
||||
return;
|
||||
|
||||
@@ -77,7 +77,7 @@ private:
|
||||
Entries::iterator FindEntry(VAddr address);
|
||||
Entries::const_iterator FindEntry(VAddr address) const;
|
||||
|
||||
void FrameCallback(std::chrono::nanoseconds ns_late);
|
||||
void FrameCallback(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
|
||||
void FillEntryReads();
|
||||
|
||||
std::atomic_bool active{false};
|
||||
|
||||
@@ -74,11 +74,6 @@ std::optional<OutAttr> OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) {
|
||||
case IR::Attribute::ClipDistance7: {
|
||||
const u32 base{static_cast<u32>(IR::Attribute::ClipDistance0)};
|
||||
const u32 index{static_cast<u32>(attr) - base};
|
||||
if (index >= ctx.profile.max_user_clip_distances) {
|
||||
LOG_WARNING(Shader, "Ignoring clip distance store {} >= {} supported", index,
|
||||
ctx.profile.max_user_clip_distances);
|
||||
return std::nullopt;
|
||||
}
|
||||
const Id clip_num{ctx.Const(index)};
|
||||
return OutputAccessChain(ctx, ctx.output_f32, ctx.clip_distances, clip_num);
|
||||
}
|
||||
|
||||
@@ -96,9 +96,9 @@ Id ImageType(EmitContext& ctx, const ImageDescriptor& desc, Id sampled_type) {
|
||||
}
|
||||
|
||||
Id DefineVariable(EmitContext& ctx, Id type, std::optional<spv::BuiltIn> builtin,
|
||||
spv::StorageClass storage_class, std::optional<Id> initializer = std::nullopt) {
|
||||
spv::StorageClass storage_class) {
|
||||
const Id pointer_type{ctx.TypePointer(storage_class, type)};
|
||||
const Id id{ctx.AddGlobalVariable(pointer_type, storage_class, initializer)};
|
||||
const Id id{ctx.AddGlobalVariable(pointer_type, storage_class)};
|
||||
if (builtin) {
|
||||
ctx.Decorate(id, spv::Decoration::BuiltIn, *builtin);
|
||||
}
|
||||
@@ -144,12 +144,11 @@ Id DefineInput(EmitContext& ctx, Id type, bool per_invocation,
|
||||
}
|
||||
|
||||
Id DefineOutput(EmitContext& ctx, Id type, std::optional<u32> invocations,
|
||||
std::optional<spv::BuiltIn> builtin = std::nullopt,
|
||||
std::optional<Id> initializer = std::nullopt) {
|
||||
std::optional<spv::BuiltIn> builtin = std::nullopt) {
|
||||
if (invocations && ctx.stage == Stage::TessellationControl) {
|
||||
type = ctx.TypeArray(type, ctx.Const(*invocations));
|
||||
}
|
||||
return DefineVariable(ctx, type, builtin, spv::StorageClass::Output, initializer);
|
||||
return DefineVariable(ctx, type, builtin, spv::StorageClass::Output);
|
||||
}
|
||||
|
||||
void DefineGenericOutput(EmitContext& ctx, size_t index, std::optional<u32> invocations) {
|
||||
@@ -812,14 +811,10 @@ void EmitContext::DefineAttributeMemAccess(const Info& info) {
|
||||
labels.push_back(OpLabel());
|
||||
}
|
||||
if (info.stores.ClipDistances()) {
|
||||
if (profile.max_user_clip_distances >= 4) {
|
||||
literals.push_back(static_cast<u32>(IR::Attribute::ClipDistance0) >> 2);
|
||||
labels.push_back(OpLabel());
|
||||
}
|
||||
if (profile.max_user_clip_distances >= 8) {
|
||||
literals.push_back(static_cast<u32>(IR::Attribute::ClipDistance4) >> 2);
|
||||
labels.push_back(OpLabel());
|
||||
}
|
||||
literals.push_back(static_cast<u32>(IR::Attribute::ClipDistance0) >> 2);
|
||||
labels.push_back(OpLabel());
|
||||
literals.push_back(static_cast<u32>(IR::Attribute::ClipDistance4) >> 2);
|
||||
labels.push_back(OpLabel());
|
||||
}
|
||||
OpSelectionMerge(end_block, spv::SelectionControlMask::MaskNone);
|
||||
OpSwitch(compare_index, default_label, literals, labels);
|
||||
@@ -848,21 +843,17 @@ void EmitContext::DefineAttributeMemAccess(const Info& info) {
|
||||
++label_index;
|
||||
}
|
||||
if (info.stores.ClipDistances()) {
|
||||
if (profile.max_user_clip_distances >= 4) {
|
||||
AddLabel(labels[label_index]);
|
||||
const Id pointer{OpAccessChain(output_f32, clip_distances, masked_index)};
|
||||
OpStore(pointer, store_value);
|
||||
OpReturn();
|
||||
++label_index;
|
||||
}
|
||||
if (profile.max_user_clip_distances >= 8) {
|
||||
AddLabel(labels[label_index]);
|
||||
const Id fixed_index{OpIAdd(U32[1], masked_index, Const(4U))};
|
||||
const Id pointer{OpAccessChain(output_f32, clip_distances, fixed_index)};
|
||||
OpStore(pointer, store_value);
|
||||
OpReturn();
|
||||
++label_index;
|
||||
}
|
||||
AddLabel(labels[label_index]);
|
||||
const Id pointer{OpAccessChain(output_f32, clip_distances, masked_index)};
|
||||
OpStore(pointer, store_value);
|
||||
OpReturn();
|
||||
++label_index;
|
||||
AddLabel(labels[label_index]);
|
||||
const Id fixed_index{OpIAdd(U32[1], masked_index, Const(4U))};
|
||||
const Id pointer2{OpAccessChain(output_f32, clip_distances, fixed_index)};
|
||||
OpStore(pointer2, store_value);
|
||||
OpReturn();
|
||||
++label_index;
|
||||
}
|
||||
AddLabel(end_block);
|
||||
OpUnreachable();
|
||||
@@ -1541,16 +1532,8 @@ void EmitContext::DefineOutputs(const IR::Program& program) {
|
||||
if (stage == Stage::Fragment) {
|
||||
throw NotImplementedException("Storing ClipDistance in fragment stage");
|
||||
}
|
||||
if (profile.max_user_clip_distances > 0) {
|
||||
const u32 used{std::min(profile.max_user_clip_distances, 8u)};
|
||||
const std::array<Id, 8> zero{f32_zero_value, f32_zero_value, f32_zero_value,
|
||||
f32_zero_value, f32_zero_value, f32_zero_value,
|
||||
f32_zero_value, f32_zero_value};
|
||||
const Id type{TypeArray(F32[1], Const(used))};
|
||||
const Id initializer{ConstantComposite(type, std::span(zero).subspan(0, used))};
|
||||
clip_distances =
|
||||
DefineOutput(*this, type, invocations, spv::BuiltIn::ClipDistance, initializer);
|
||||
}
|
||||
const Id type{TypeArray(F32[1], Const(8U))};
|
||||
clip_distances = DefineOutput(*this, type, invocations, spv::BuiltIn::ClipDistance);
|
||||
}
|
||||
if (info.stores[IR::Attribute::Layer] &&
|
||||
(profile.support_viewport_index_layer_non_geometry || stage == Stage::Geometry)) {
|
||||
|
||||
@@ -913,11 +913,7 @@ void GatherInfoFromHeader(Environment& env, Info& info) {
|
||||
}
|
||||
for (size_t index = 0; index < 8; ++index) {
|
||||
const u16 mask{header.vtg.omap_systemc.clip_distances};
|
||||
const bool used{((mask >> index) & 1) != 0};
|
||||
info.stores.Set(IR::Attribute::ClipDistance0 + index, used);
|
||||
if (used) {
|
||||
info.used_clip_distances = static_cast<u32>(index) + 1;
|
||||
}
|
||||
info.stores.Set(IR::Attribute::ClipDistance0 + index, ((mask >> index) & 1) != 0);
|
||||
}
|
||||
info.stores.Set(IR::Attribute::PrimitiveId,
|
||||
header.vtg.omap_systemb.primitive_array_id != 0);
|
||||
|
||||
@@ -87,8 +87,6 @@ struct Profile {
|
||||
bool has_broken_robust{};
|
||||
|
||||
u64 min_ssbo_alignment{};
|
||||
|
||||
u32 max_user_clip_distances{};
|
||||
};
|
||||
|
||||
} // namespace Shader
|
||||
|
||||
@@ -324,8 +324,6 @@ struct Info {
|
||||
bool requires_layer_emulation{};
|
||||
IR::Attribute emulated_layer{};
|
||||
|
||||
u32 used_clip_distances{};
|
||||
|
||||
boost::container::static_vector<ConstantBufferDescriptor, MAX_CBUFS>
|
||||
constant_buffer_descriptors;
|
||||
boost::container::static_vector<StorageBufferDescriptor, MAX_SSBOS> storage_buffers_descriptors;
|
||||
|
||||
@@ -16,16 +16,20 @@
|
||||
|
||||
namespace {
|
||||
// Numbers are chosen randomly to make sure the correct one is given.
|
||||
constexpr std::array<u64, 5> CB_IDS{{42, 144, 93, 1026, UINT64_C(0xFFFF7FFFF7FFFF)}};
|
||||
constexpr std::array<u64, 5> calls_order{{2, 0, 1, 4, 3}};
|
||||
std::array<s64, 5> delays{};
|
||||
std::bitset<5> callbacks_ran_flags;
|
||||
|
||||
std::bitset<CB_IDS.size()> callbacks_ran_flags;
|
||||
u64 expected_callback = 0;
|
||||
|
||||
template <unsigned int IDX>
|
||||
std::optional<std::chrono::nanoseconds> HostCallbackTemplate(s64 time,
|
||||
std::optional<std::chrono::nanoseconds> HostCallbackTemplate(std::uintptr_t user_data, s64 time,
|
||||
std::chrono::nanoseconds ns_late) {
|
||||
static_assert(IDX < callbacks_ran_flags.size(), "IDX out of range");
|
||||
static_assert(IDX < CB_IDS.size(), "IDX out of range");
|
||||
callbacks_ran_flags.set(IDX);
|
||||
REQUIRE(CB_IDS[IDX] == user_data);
|
||||
REQUIRE(CB_IDS[IDX] == CB_IDS[calls_order[expected_callback]]);
|
||||
delays[IDX] = ns_late.count();
|
||||
++expected_callback;
|
||||
return std::nullopt;
|
||||
@@ -72,7 +76,7 @@ TEST_CASE("CoreTiming[BasicOrder]", "[core]") {
|
||||
const u64 order = calls_order[i];
|
||||
const auto future_ns = std::chrono::nanoseconds{static_cast<s64>(i * one_micro + 100)};
|
||||
|
||||
core_timing.ScheduleEvent(future_ns, events[order]);
|
||||
core_timing.ScheduleEvent(future_ns, events[order], CB_IDS[order]);
|
||||
}
|
||||
/// test pause
|
||||
REQUIRE(callbacks_ran_flags.none());
|
||||
@@ -114,7 +118,7 @@ TEST_CASE("CoreTiming[BasicOrderNoPausing]", "[core]") {
|
||||
for (std::size_t i = 0; i < events.size(); i++) {
|
||||
const u64 order = calls_order[i];
|
||||
const auto future_ns = std::chrono::nanoseconds{static_cast<s64>(i * one_micro + 100)};
|
||||
core_timing.ScheduleEvent(future_ns, events[order]);
|
||||
core_timing.ScheduleEvent(future_ns, events[order], CB_IDS[order]);
|
||||
}
|
||||
|
||||
const u64 end = core_timing.GetGlobalTimeNs().count();
|
||||
|
||||
@@ -327,13 +327,12 @@ public:
|
||||
explicit HLE_DrawIndirectByteCount(Maxwell3D& maxwell3d_) : HLEMacroImpl(maxwell3d_) {}
|
||||
|
||||
void Execute(const std::vector<u32>& parameters, [[maybe_unused]] u32 method) override {
|
||||
const bool force = maxwell3d.Rasterizer().HasDrawTransformFeedback();
|
||||
|
||||
auto topology = static_cast<Maxwell3D::Regs::PrimitiveTopology>(parameters[0] & 0xFFFFU);
|
||||
if (!force && (!maxwell3d.AnyParametersDirty() || !IsTopologySafe(topology))) {
|
||||
if (!maxwell3d.AnyParametersDirty() || !IsTopologySafe(topology)) {
|
||||
Fallback(parameters);
|
||||
return;
|
||||
}
|
||||
|
||||
auto& params = maxwell3d.draw_manager->GetIndirectParams();
|
||||
params.is_byte_count = true;
|
||||
params.is_indexed = false;
|
||||
@@ -504,8 +503,6 @@ public:
|
||||
maxwell3d.CallMethod(static_cast<size_t>(MAXWELL3D_REG_INDEX(launch_dma)), 0x1011, true);
|
||||
maxwell3d.CallMethod(static_cast<size_t>(MAXWELL3D_REG_INDEX(inline_data)),
|
||||
regs.transform_feedback.controls[0].stride, true);
|
||||
|
||||
maxwell3d.Rasterizer().RegisterTransformFeedback(regs.upload.dest.Address());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -173,13 +173,5 @@ public:
|
||||
virtual void BindChannel(Tegra::Control::ChannelState& channel) {}
|
||||
|
||||
virtual void ReleaseChannel(s32 channel_id) {}
|
||||
|
||||
/// Register the address as a Transform Feedback Object
|
||||
virtual void RegisterTransformFeedback(GPUVAddr tfb_object_addr) {}
|
||||
|
||||
/// Returns true when the rasterizer has Draw Transform Feedback capabilities
|
||||
virtual bool HasDrawTransformFeedback() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
} // namespace VideoCore
|
||||
|
||||
@@ -376,15 +376,4 @@ void BufferCacheRuntime::BindImageBuffer(Buffer& buffer, u32 offset, u32 size, P
|
||||
*image_handles++ = buffer.View(offset, size, format);
|
||||
}
|
||||
|
||||
void BufferCacheRuntime::BindTransformFeedbackObject(GPUVAddr tfb_object_addr) {
|
||||
OGLTransformFeedback& tfb_object = tfb_objects[tfb_object_addr];
|
||||
tfb_object.Create();
|
||||
glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, tfb_object.handle);
|
||||
}
|
||||
|
||||
GLuint BufferCacheRuntime::GetTransformFeedbackObject(GPUVAddr tfb_object_addr) {
|
||||
ASSERT(tfb_objects.contains(tfb_object_addr));
|
||||
return tfb_objects[tfb_object_addr].handle;
|
||||
}
|
||||
|
||||
} // namespace OpenGL
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
#include <array>
|
||||
#include <span>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/buffer_cache/buffer_cache_base.h"
|
||||
@@ -122,9 +121,6 @@ public:
|
||||
void BindImageBuffer(Buffer& buffer, u32 offset, u32 size,
|
||||
VideoCore::Surface::PixelFormat format);
|
||||
|
||||
void BindTransformFeedbackObject(GPUVAddr tfb_object_addr);
|
||||
GLuint GetTransformFeedbackObject(GPUVAddr tfb_object_addr);
|
||||
|
||||
u64 GetDeviceMemoryUsage() const;
|
||||
|
||||
void BindFastUniformBuffer(size_t stage, u32 binding_index, u32 size) {
|
||||
@@ -237,7 +233,6 @@ private:
|
||||
u32 index_buffer_offset = 0;
|
||||
|
||||
u64 device_access_memory;
|
||||
std::unordered_map<GPUVAddr, OGLTransformFeedback> tfb_objects;
|
||||
};
|
||||
|
||||
struct BufferCacheParams {
|
||||
|
||||
@@ -309,13 +309,6 @@ void RasterizerOpenGL::DrawIndirect() {
|
||||
const auto& params = maxwell3d->draw_manager->GetIndirectParams();
|
||||
buffer_cache.SetDrawIndirect(¶ms);
|
||||
PrepareDraw(params.is_indexed, [this, ¶ms](GLenum primitive_mode) {
|
||||
if (params.is_byte_count) {
|
||||
const GPUVAddr tfb_object_base_addr = params.indirect_start_address - 4U;
|
||||
const GLuint tfb_object =
|
||||
buffer_cache_runtime.GetTransformFeedbackObject(tfb_object_base_addr);
|
||||
glDrawTransformFeedback(primitive_mode, tfb_object);
|
||||
return;
|
||||
}
|
||||
const auto [buffer, offset] = buffer_cache.GetDrawIndirectBuffer();
|
||||
const GLvoid* const gl_offset =
|
||||
reinterpret_cast<const GLvoid*>(static_cast<uintptr_t>(offset));
|
||||
@@ -1378,10 +1371,6 @@ void RasterizerOpenGL::ReleaseChannel(s32 channel_id) {
|
||||
query_cache.EraseChannel(channel_id);
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::RegisterTransformFeedback(GPUVAddr tfb_object_addr) {
|
||||
buffer_cache_runtime.BindTransformFeedbackObject(tfb_object_addr);
|
||||
}
|
||||
|
||||
AccelerateDMA::AccelerateDMA(BufferCache& buffer_cache_, TextureCache& texture_cache_)
|
||||
: buffer_cache{buffer_cache_}, texture_cache{texture_cache_} {}
|
||||
|
||||
|
||||
@@ -139,12 +139,6 @@ public:
|
||||
|
||||
void ReleaseChannel(s32 channel_id) override;
|
||||
|
||||
void RegisterTransformFeedback(GPUVAddr tfb_object_addr) override;
|
||||
|
||||
bool HasDrawTransformFeedback() override {
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr size_t MAX_TEXTURES = 192;
|
||||
static constexpr size_t MAX_IMAGES = 48;
|
||||
|
||||
@@ -207,21 +207,4 @@ void OGLQuery::Release() {
|
||||
handle = 0;
|
||||
}
|
||||
|
||||
void OGLTransformFeedback::Create() {
|
||||
if (handle != 0)
|
||||
return;
|
||||
|
||||
MICROPROFILE_SCOPE(OpenGL_ResourceCreation);
|
||||
glCreateTransformFeedbacks(1, &handle);
|
||||
}
|
||||
|
||||
void OGLTransformFeedback::Release() {
|
||||
if (handle == 0)
|
||||
return;
|
||||
|
||||
MICROPROFILE_SCOPE(OpenGL_ResourceDeletion);
|
||||
glDeleteTransformFeedbacks(1, &handle);
|
||||
handle = 0;
|
||||
}
|
||||
|
||||
} // namespace OpenGL
|
||||
|
||||
@@ -323,31 +323,4 @@ public:
|
||||
GLuint handle = 0;
|
||||
};
|
||||
|
||||
class OGLTransformFeedback final {
|
||||
public:
|
||||
YUZU_NON_COPYABLE(OGLTransformFeedback);
|
||||
|
||||
OGLTransformFeedback() = default;
|
||||
|
||||
OGLTransformFeedback(OGLTransformFeedback&& o) noexcept : handle(std::exchange(o.handle, 0)) {}
|
||||
|
||||
~OGLTransformFeedback() {
|
||||
Release();
|
||||
}
|
||||
|
||||
OGLTransformFeedback& operator=(OGLTransformFeedback&& o) noexcept {
|
||||
Release();
|
||||
handle = std::exchange(o.handle, 0);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Creates a new internal OpenGL resource and stores the handle
|
||||
void Create();
|
||||
|
||||
/// Deletes the internal OpenGL resource
|
||||
void Release();
|
||||
|
||||
GLuint handle = 0;
|
||||
};
|
||||
|
||||
} // namespace OpenGL
|
||||
|
||||
@@ -233,7 +233,6 @@ ShaderCache::ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindo
|
||||
.ignore_nan_fp_comparisons = true,
|
||||
.gl_max_compute_smem_size = device.GetMaxComputeSharedMemorySize(),
|
||||
.min_ssbo_alignment = device.GetShaderStorageBufferAlignment(),
|
||||
.max_user_clip_distances = 8,
|
||||
},
|
||||
host_info{
|
||||
.support_float64 = true,
|
||||
|
||||
@@ -374,7 +374,6 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device
|
||||
.has_broken_robust =
|
||||
device.IsNvidia() && device.GetNvidiaArch() <= NvidiaArchitecture::Arch_Pascal,
|
||||
.min_ssbo_alignment = device.GetStorageBufferAlignment(),
|
||||
.max_user_clip_distances = device.GetMaxUserClipDistances(),
|
||||
};
|
||||
|
||||
host_info = Shader::HostTranslateInfo{
|
||||
|
||||
@@ -60,72 +60,66 @@ u32 ConvertedBytesPerBlock(VideoCore::Surface::PixelFormat pixel_format) {
|
||||
}
|
||||
|
||||
template <auto decompress, PixelFormat pixel_format>
|
||||
void DecompressBlocks(std::span<const u8> input, std::span<u8> output, BufferImageCopy& copy,
|
||||
void DecompressBlocks(std::span<const u8> input, std::span<u8> output, Extent3D extent,
|
||||
bool is_signed = false) {
|
||||
const u32 out_bpp = ConvertedBytesPerBlock(pixel_format);
|
||||
const u32 block_size = BlockSize(pixel_format);
|
||||
const u32 width = copy.image_extent.width;
|
||||
const u32 height = copy.image_extent.height * copy.image_subresource.num_layers;
|
||||
const u32 depth = copy.image_extent.depth;
|
||||
const u32 block_width = std::min(width, BLOCK_SIZE);
|
||||
const u32 block_height = std::min(height, BLOCK_SIZE);
|
||||
const u32 pitch = width * out_bpp;
|
||||
const u32 block_width = std::min(extent.width, BLOCK_SIZE);
|
||||
const u32 block_height = std::min(extent.height, BLOCK_SIZE);
|
||||
const u32 pitch = extent.width * out_bpp;
|
||||
size_t input_offset = 0;
|
||||
size_t output_offset = 0;
|
||||
for (u32 slice = 0; slice < depth; ++slice) {
|
||||
for (u32 y = 0; y < height; y += block_height) {
|
||||
size_t src_offset = input_offset;
|
||||
size_t dst_offset = output_offset;
|
||||
for (u32 x = 0; x < width; x += block_width) {
|
||||
const u8* src = input.data() + src_offset;
|
||||
u8* const dst = output.data() + dst_offset;
|
||||
for (u32 slice = 0; slice < extent.depth; ++slice) {
|
||||
for (u32 y = 0; y < extent.height; y += block_height) {
|
||||
size_t row_offset = 0;
|
||||
for (u32 x = 0; x < extent.width;
|
||||
x += block_width, row_offset += block_width * out_bpp) {
|
||||
const u8* src = input.data() + input_offset;
|
||||
u8* const dst = output.data() + output_offset + row_offset;
|
||||
if constexpr (IsSigned(pixel_format)) {
|
||||
decompress(src, dst, x, y, width, height, is_signed);
|
||||
decompress(src, dst, x, y, extent.width, extent.height, is_signed);
|
||||
} else {
|
||||
decompress(src, dst, x, y, width, height);
|
||||
decompress(src, dst, x, y, extent.width, extent.height);
|
||||
}
|
||||
src_offset += block_size;
|
||||
dst_offset += block_width * out_bpp;
|
||||
input_offset += BlockSize(pixel_format);
|
||||
}
|
||||
input_offset += copy.buffer_row_length * block_size / block_width;
|
||||
output_offset += block_height * pitch;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DecompressBCn(std::span<const u8> input, std::span<u8> output, BufferImageCopy& copy,
|
||||
void DecompressBCn(std::span<const u8> input, std::span<u8> output, Extent3D extent,
|
||||
VideoCore::Surface::PixelFormat pixel_format) {
|
||||
switch (pixel_format) {
|
||||
case PixelFormat::BC1_RGBA_UNORM:
|
||||
case PixelFormat::BC1_RGBA_SRGB:
|
||||
DecompressBlocks<bcn::DecodeBc1, PixelFormat::BC1_RGBA_UNORM>(input, output, copy);
|
||||
DecompressBlocks<bcn::DecodeBc1, PixelFormat::BC1_RGBA_UNORM>(input, output, extent);
|
||||
break;
|
||||
case PixelFormat::BC2_UNORM:
|
||||
case PixelFormat::BC2_SRGB:
|
||||
DecompressBlocks<bcn::DecodeBc2, PixelFormat::BC2_UNORM>(input, output, copy);
|
||||
DecompressBlocks<bcn::DecodeBc2, PixelFormat::BC2_UNORM>(input, output, extent);
|
||||
break;
|
||||
case PixelFormat::BC3_UNORM:
|
||||
case PixelFormat::BC3_SRGB:
|
||||
DecompressBlocks<bcn::DecodeBc3, PixelFormat::BC3_UNORM>(input, output, copy);
|
||||
DecompressBlocks<bcn::DecodeBc3, PixelFormat::BC3_UNORM>(input, output, extent);
|
||||
break;
|
||||
case PixelFormat::BC4_SNORM:
|
||||
case PixelFormat::BC4_UNORM:
|
||||
DecompressBlocks<bcn::DecodeBc4, PixelFormat::BC4_UNORM>(
|
||||
input, output, copy, pixel_format == PixelFormat::BC4_SNORM);
|
||||
input, output, extent, pixel_format == PixelFormat::BC4_SNORM);
|
||||
break;
|
||||
case PixelFormat::BC5_SNORM:
|
||||
case PixelFormat::BC5_UNORM:
|
||||
DecompressBlocks<bcn::DecodeBc5, PixelFormat::BC5_UNORM>(
|
||||
input, output, copy, pixel_format == PixelFormat::BC5_SNORM);
|
||||
input, output, extent, pixel_format == PixelFormat::BC5_SNORM);
|
||||
break;
|
||||
case PixelFormat::BC6H_SFLOAT:
|
||||
case PixelFormat::BC6H_UFLOAT:
|
||||
DecompressBlocks<bcn::DecodeBc6, PixelFormat::BC6H_UFLOAT>(
|
||||
input, output, copy, pixel_format == PixelFormat::BC6H_SFLOAT);
|
||||
input, output, extent, pixel_format == PixelFormat::BC6H_SFLOAT);
|
||||
break;
|
||||
case PixelFormat::BC7_SRGB:
|
||||
case PixelFormat::BC7_UNORM:
|
||||
DecompressBlocks<bcn::DecodeBc7, PixelFormat::BC7_UNORM>(input, output, copy);
|
||||
DecompressBlocks<bcn::DecodeBc7, PixelFormat::BC7_UNORM>(input, output, extent);
|
||||
break;
|
||||
default:
|
||||
LOG_WARNING(HW_GPU, "Unimplemented BCn decompression {}", pixel_format);
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace VideoCommon {
|
||||
|
||||
[[nodiscard]] u32 ConvertedBytesPerBlock(VideoCore::Surface::PixelFormat pixel_format);
|
||||
|
||||
void DecompressBCn(std::span<const u8> input, std::span<u8> output, BufferImageCopy& copy,
|
||||
void DecompressBCn(std::span<const u8> input, std::span<u8> output, Extent3D extent,
|
||||
VideoCore::Surface::PixelFormat pixel_format);
|
||||
|
||||
} // namespace VideoCommon
|
||||
|
||||
@@ -837,7 +837,6 @@ boost::container::small_vector<BufferImageCopy, 16> UnswizzleImage(Tegra::Memory
|
||||
std::span<u8> output) {
|
||||
const size_t guest_size_bytes = input.size_bytes();
|
||||
const u32 bpp_log2 = BytesPerBlockLog2(info.format);
|
||||
const Extent2D tile_size = DefaultBlockSize(info.format);
|
||||
const Extent3D size = info.size;
|
||||
|
||||
if (info.type == ImageType::Linear) {
|
||||
@@ -848,7 +847,7 @@ boost::container::small_vector<BufferImageCopy, 16> UnswizzleImage(Tegra::Memory
|
||||
return {{
|
||||
.buffer_offset = 0,
|
||||
.buffer_size = guest_size_bytes,
|
||||
.buffer_row_length = info.pitch * tile_size.width >> bpp_log2,
|
||||
.buffer_row_length = info.pitch >> bpp_log2,
|
||||
.buffer_image_height = size.height,
|
||||
.image_subresource =
|
||||
{
|
||||
@@ -863,6 +862,7 @@ boost::container::small_vector<BufferImageCopy, 16> UnswizzleImage(Tegra::Memory
|
||||
const LevelInfo level_info = MakeLevelInfo(info);
|
||||
const s32 num_layers = info.resources.layers;
|
||||
const s32 num_levels = info.resources.levels;
|
||||
const Extent2D tile_size = DefaultBlockSize(info.format);
|
||||
const std::array level_sizes = CalculateLevelSizes(level_info, num_levels);
|
||||
const Extent2D gob = GobSize(bpp_log2, info.block.height, info.tile_width_spacing);
|
||||
const u32 layer_size = CalculateLevelBytes(level_sizes, num_levels);
|
||||
@@ -926,6 +926,8 @@ void ConvertImage(std::span<const u8> input, const ImageInfo& info, std::span<u8
|
||||
|
||||
const auto input_offset = input.subspan(copy.buffer_offset);
|
||||
copy.buffer_offset = output_offset;
|
||||
copy.buffer_row_length = mip_size.width;
|
||||
copy.buffer_image_height = mip_size.height;
|
||||
|
||||
const auto recompression_setting = Settings::values.astc_recompression.GetValue();
|
||||
const bool astc = IsPixelFormatASTC(info.format);
|
||||
@@ -970,14 +972,16 @@ void ConvertImage(std::span<const u8> input, const ImageInfo& info, std::span<u8
|
||||
bpp_div;
|
||||
output_offset += static_cast<u32>(copy.buffer_size);
|
||||
} else {
|
||||
DecompressBCn(input_offset, output.subspan(output_offset), copy, info.format);
|
||||
const Extent3D image_extent{
|
||||
.width = copy.image_extent.width,
|
||||
.height = copy.image_extent.height * copy.image_subresource.num_layers,
|
||||
.depth = copy.image_extent.depth,
|
||||
};
|
||||
DecompressBCn(input_offset, output.subspan(output_offset), image_extent, info.format);
|
||||
output_offset += copy.image_extent.width * copy.image_extent.height *
|
||||
copy.image_subresource.num_layers *
|
||||
ConvertedBytesPerBlock(info.format);
|
||||
}
|
||||
|
||||
copy.buffer_row_length = mip_size.width;
|
||||
copy.buffer_image_height = mip_size.height;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -665,10 +665,6 @@ public:
|
||||
return properties.properties.limits.maxViewports;
|
||||
}
|
||||
|
||||
u32 GetMaxUserClipDistances() const {
|
||||
return properties.properties.limits.maxClipDistances;
|
||||
}
|
||||
|
||||
bool SupportsConditionalBarriers() const {
|
||||
return supports_conditional_barriers;
|
||||
}
|
||||
|
||||
@@ -168,6 +168,14 @@ class GMainWindow : public QMainWindow {
|
||||
/// Max number of recently loaded items to keep track of
|
||||
static const int max_recent_files_item = 10;
|
||||
|
||||
// TODO: Make use of this!
|
||||
enum {
|
||||
UI_IDLE,
|
||||
UI_EMU_BOOTING,
|
||||
UI_EMU_RUNNING,
|
||||
UI_EMU_STOPPING,
|
||||
};
|
||||
|
||||
enum {
|
||||
CREATE_SHORTCUT_MSGBOX_FULLSCREEN_YES,
|
||||
CREATE_SHORTCUT_MSGBOX_SUCCESS,
|
||||
|
||||
Reference in New Issue
Block a user