Compare commits
20 Commits
android-17
...
android-17
Author | SHA1 | Date | |
---|---|---|---|
d333b5e3b9 | |||
8cce3dba47 | |||
945dda8614 | |||
12178c694a | |||
de1e5584b3 | |||
1559984f77 | |||
467ac4fdfe | |||
69b7100dac | |||
14dc41d4b3 | |||
ad049f13aa | |||
4f569fd568 | |||
553dac2ae0 | |||
96abe0d7d3 | |||
47e44a6693 | |||
cf8c7d4ed3 | |||
5165ed9efd | |||
e3491a9ee8 | |||
6a1ddc5028 | |||
b1d4804c07 | |||
bbc0ed118d |
@ -1,9 +1,7 @@
|
||||
| Pull Request | Commit | Title | Author | Merged? |
|
||||
|----|----|----|----|----|
|
||||
| [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 |
|
||||
| [12466](https://github.com/yuzu-emu/yuzu//pull/12466) | [`ddda76f9b`](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 |
|
||||
|
||||
|
||||
End of merge log. You can find the original README.md below the break.
|
||||
|
@ -3,16 +3,19 @@
|
||||
|
||||
#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,14 +1,17 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/assert.h"
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#include "common/heap_tracker.h"
|
||||
#include "common/logging/log.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr size_t MaxResidentMapCount = 0x8000;
|
||||
constexpr s64 MaxResidentMapCount = 0x8000;
|
||||
|
||||
} // namespace
|
||||
|
||||
@ -27,17 +30,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* map = new SeparateHeapMap{
|
||||
auto* const 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);
|
||||
}
|
||||
|
||||
@ -48,11 +51,10 @@ 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_rebuild_lock, m_lock};
|
||||
std::scoped_lock lk{m_lock};
|
||||
|
||||
const SeparateHeapMap key{
|
||||
.vaddr = virtual_offset,
|
||||
.size = size,
|
||||
};
|
||||
|
||||
// Split at the boundaries of the region we are removing.
|
||||
@ -62,20 +64,18 @@ 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 pointer to item.
|
||||
SeparateHeapMap* const item = std::addressof(*it);
|
||||
// Get underlying item.
|
||||
auto* const item = std::addressof(*it);
|
||||
|
||||
// If resident, erase from resident map.
|
||||
if (item->is_resident) {
|
||||
// Unlink from resident tree.
|
||||
ASSERT(--m_resident_map_count >= 0);
|
||||
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);
|
||||
}
|
||||
|
||||
// Unlink from mapping tree and advance.
|
||||
// Erase from map.
|
||||
it = m_mappings.erase(it);
|
||||
ASSERT(--m_map_count >= 0);
|
||||
|
||||
// Free the item.
|
||||
delete item;
|
||||
@ -109,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(key);
|
||||
const auto it = m_mappings.nfind(key);
|
||||
|
||||
if (it == m_mappings.end()) {
|
||||
// There are no separate heap mappings remaining.
|
||||
@ -152,106 +152,61 @@ bool HeapTracker::DeferredMapSeparateHeap(u8* fault_address) {
|
||||
}
|
||||
|
||||
bool HeapTracker::DeferredMapSeparateHeap(size_t virtual_offset) {
|
||||
std::scoped_lock lk{m_lock};
|
||||
bool rebuild_required = false;
|
||||
|
||||
while (this->IsEvictRequiredLocked()) {
|
||||
// Unlock before we rebuild to ensure proper lock ordering.
|
||||
m_lock.unlock();
|
||||
{
|
||||
std::scoped_lock lk{m_lock};
|
||||
|
||||
// Evict four maps.
|
||||
for (size_t i = 0; i < 4; /* ... */) {
|
||||
i += this->EvictSingleSeparateHeapMap();
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Lock again.
|
||||
m_lock.lock();
|
||||
// Update tick before possible rebuild.
|
||||
it->tick = m_tick++;
|
||||
|
||||
// Check if we need to rebuild.
|
||||
if (m_resident_map_count > MaxResidentMapCount) {
|
||||
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);
|
||||
}
|
||||
|
||||
// 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;
|
||||
if (rebuild_required) {
|
||||
// A rebuild was required, so perform it now.
|
||||
this->RebuildSeparateHeapAddressSpace();
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
bool HeapTracker::EvictSingleSeparateHeapMap() {
|
||||
void HeapTracker::RebuildSeparateHeapAddressSpace() {
|
||||
std::scoped_lock lk{m_rebuild_lock, m_lock};
|
||||
|
||||
ASSERT(!m_resident_mappings.empty());
|
||||
|
||||
// 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);
|
||||
// Unmap so we have at least 4 maps available.
|
||||
const size_t desired_count = std::min(m_resident_map_count, MaxResidentMapCount - 4);
|
||||
const size_t evict_count = m_resident_map_count - desired_count;
|
||||
auto it = m_resident_mappings.begin();
|
||||
|
||||
// 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;
|
||||
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);
|
||||
|
||||
// Advance.
|
||||
it = this->MergeHeapMapForEvictLocked(it);
|
||||
ASSERT(--m_resident_map_count >= 0);
|
||||
it = m_resident_mappings.erase(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) {
|
||||
@ -268,11 +223,9 @@ void HeapTracker::SplitHeapMapLocked(VAddr offset) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the underlying item as the left.
|
||||
// Cache the original values.
|
||||
auto* const left = std::addressof(*it);
|
||||
|
||||
// Cache the original size values.
|
||||
const size_t size = left->size;
|
||||
const size_t orig_size = left->size;
|
||||
|
||||
// Adjust the left map.
|
||||
const size_t left_size = offset - left->vaddr;
|
||||
@ -282,74 +235,21 @@ void HeapTracker::SplitHeapMapLocked(VAddr offset) {
|
||||
auto* const right = new SeparateHeapMap{
|
||||
.vaddr = left->vaddr + left_size,
|
||||
.paddr = left->paddr + left_size,
|
||||
.size = size - left_size,
|
||||
.map_id = left->map_id,
|
||||
.size = orig_size - left_size,
|
||||
.tick = left->tick,
|
||||
.perm = left->perm,
|
||||
.is_resident = left->is_resident,
|
||||
};
|
||||
|
||||
// Insert the new right map.
|
||||
m_map_count++;
|
||||
m_mappings.insert(*right);
|
||||
|
||||
// If the original map was not resident, we are done.
|
||||
if (!left->is_resident) {
|
||||
return;
|
||||
// If resident, also insert into resident map.
|
||||
if (right->is_resident) {
|
||||
m_resident_mappings.insert(*right);
|
||||
m_resident_map_count++;
|
||||
}
|
||||
|
||||
// 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) {
|
||||
@ -360,26 +260,4 @@ 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,8 +3,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
#include <shared_mutex>
|
||||
|
||||
#include "common/host_memory.h"
|
||||
@ -18,7 +19,6 @@ struct SeparateHeapMap {
|
||||
VAddr vaddr{};
|
||||
PAddr paddr{};
|
||||
size_t size{};
|
||||
size_t map_id{};
|
||||
size_t tick{};
|
||||
MemoryPermission perm{};
|
||||
bool is_resident{};
|
||||
@ -64,14 +64,6 @@ 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>;
|
||||
@ -80,9 +72,7 @@ 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{};
|
||||
|
||||
@ -90,14 +80,18 @@ private:
|
||||
void SplitHeapMap(VAddr offset, size_t size);
|
||||
void SplitHeapMapLocked(VAddr offset);
|
||||
|
||||
AddrTree::iterator MergeHeapMapForEvictLocked(AddrTree::iterator cur);
|
||||
AddrTree::iterator GetNearestHeapMapLocked(VAddr offset);
|
||||
|
||||
bool EvictSingleSeparateHeapMap();
|
||||
void RebuildSeparateHeapAddressSpace();
|
||||
|
||||
void AddReferenceLocked(size_t map_id, size_t inc);
|
||||
bool RemoveReferenceLocked(MapCountTree::iterator map_id, size_t dec);
|
||||
bool IsEvictRequiredLocked();
|
||||
private:
|
||||
Common::HostMemory& m_buffer;
|
||||
|
||||
std::shared_mutex m_rebuild_lock{};
|
||||
std::mutex m_lock{};
|
||||
s64 m_map_count{};
|
||||
s64 m_resident_map_count{};
|
||||
size_t m_tick{};
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
|
@ -208,6 +208,10 @@ public:
|
||||
instance->StartBackendThread();
|
||||
}
|
||||
|
||||
static void Stop() {
|
||||
instance->StopBackendThread();
|
||||
}
|
||||
|
||||
Impl(const Impl&) = delete;
|
||||
Impl& operator=(const Impl&) = delete;
|
||||
|
||||
@ -259,6 +263,15 @@ 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;
|
||||
@ -313,6 +326,10 @@ void Start() {
|
||||
Impl::Start();
|
||||
}
|
||||
|
||||
void Stop() {
|
||||
Impl::Stop();
|
||||
}
|
||||
|
||||
void DisableLoggingInTests() {
|
||||
initialization_in_progress_suppress_logging = true;
|
||||
}
|
||||
|
@ -14,6 +14,9 @@ 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.
|
||||
#if defined(_MSC_VER) && _MSC_VER >= 1911
|
||||
#ifdef __cpp_lib_hardware_interference_size
|
||||
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
|
||||
|
@ -8,19 +8,22 @@
|
||||
namespace Kernel {
|
||||
|
||||
void KAutoObjectWithListContainer::Register(KAutoObjectWithList* obj) {
|
||||
KScopedLightLock lk(m_lock);
|
||||
// KScopedInterruptDisable di;
|
||||
KScopedSpinLock lk(m_lock);
|
||||
|
||||
m_object_list.insert_unique(*obj);
|
||||
}
|
||||
|
||||
void KAutoObjectWithListContainer::Unregister(KAutoObjectWithList* obj) {
|
||||
KScopedLightLock lk(m_lock);
|
||||
// KScopedInterruptDisable di;
|
||||
KScopedSpinLock lk(m_lock);
|
||||
|
||||
m_object_list.erase(*obj);
|
||||
}
|
||||
|
||||
size_t KAutoObjectWithListContainer::GetOwnedCount(KProcess* owner) {
|
||||
KScopedLightLock lk(m_lock);
|
||||
// KScopedInterruptDisable di;
|
||||
KScopedSpinLock 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_light_lock.h"
|
||||
#include "core/hle/kernel/k_spin_lock.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
@ -21,32 +21,7 @@ public:
|
||||
|
||||
using ListType = boost::intrusive::rbtree<KAutoObjectWithList>;
|
||||
|
||||
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() {}
|
||||
KAutoObjectWithListContainer(KernelCore& kernel) : m_lock(), m_object_list() {}
|
||||
|
||||
void Initialize() {}
|
||||
void Finalize() {}
|
||||
@ -56,7 +31,7 @@ public:
|
||||
size_t GetOwnedCount(KProcess* owner);
|
||||
|
||||
private:
|
||||
KLightLock m_lock;
|
||||
KSpinLock m_lock;
|
||||
ListType m_object_list;
|
||||
};
|
||||
|
||||
|
@ -30,7 +30,7 @@ public:
|
||||
public:
|
||||
explicit KHandleTable(KernelCore& kernel) : m_kernel(kernel) {}
|
||||
|
||||
Result Initialize(KProcess* owner, s32 size) {
|
||||
Result Initialize(s32 size) {
|
||||
// Check that the table size is valid.
|
||||
R_UNLESS(size <= static_cast<s32>(MaxTableSize), ResultOutOfMemory);
|
||||
|
||||
@ -44,7 +44,6 @@ 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) {
|
||||
@ -91,8 +90,7 @@ public:
|
||||
// Handle pseudo-handles.
|
||||
if constexpr (std::derived_from<KProcess, T>) {
|
||||
if (handle == Svc::PseudoHandle::CurrentProcess) {
|
||||
// TODO: this should be the current process
|
||||
auto* const cur_process = m_owner;
|
||||
auto* const cur_process = GetCurrentProcessPointer(m_kernel);
|
||||
ASSERT(cur_process != nullptr);
|
||||
return cur_process;
|
||||
}
|
||||
@ -302,7 +300,6 @@ 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;
|
||||
|
@ -552,7 +552,7 @@ private:
|
||||
|
||||
Result InitializeHandleTable(s32 size) {
|
||||
// Try to initialize the handle table.
|
||||
R_TRY(m_handle_table.Initialize(this, size));
|
||||
R_TRY(m_handle_table.Initialize(size));
|
||||
|
||||
// We succeeded, so note that we did.
|
||||
m_is_handle_table_initialized = true;
|
||||
|
@ -1147,8 +1147,7 @@ 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(*client_thread->GetOwnerProcess(), cmd_buf);
|
||||
(*out_context)->PopulateFromIncomingCommandBuffer(cmd_buf);
|
||||
// We succeeded.
|
||||
R_SUCCEED();
|
||||
} else {
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
#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"
|
||||
|
@ -1513,8 +1513,7 @@ void ILibraryAppletCreator::CreateTransferMemoryStorage(HLERequestContext& ctx)
|
||||
return;
|
||||
}
|
||||
|
||||
auto transfer_mem =
|
||||
system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(handle);
|
||||
auto transfer_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(handle);
|
||||
|
||||
if (transfer_mem.IsNull()) {
|
||||
LOG_ERROR(Service_AM, "transfer_mem is a nullptr for handle={:08X}", handle);
|
||||
@ -1524,8 +1523,7 @@ void ILibraryAppletCreator::CreateTransferMemoryStorage(HLERequestContext& ctx)
|
||||
}
|
||||
|
||||
std::vector<u8> memory(transfer_mem->GetSize());
|
||||
system.ApplicationMemory().ReadBlock(transfer_mem->GetSourceAddress(), memory.data(),
|
||||
memory.size());
|
||||
ctx.GetMemory().ReadBlock(transfer_mem->GetSourceAddress(), memory.data(), memory.size());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
@ -1547,8 +1545,7 @@ void ILibraryAppletCreator::CreateHandleStorage(HLERequestContext& ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto transfer_mem =
|
||||
system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(handle);
|
||||
auto transfer_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(handle);
|
||||
|
||||
if (transfer_mem.IsNull()) {
|
||||
LOG_ERROR(Service_AM, "transfer_mem is a nullptr for handle={:08X}", handle);
|
||||
@ -1558,8 +1555,7 @@ void ILibraryAppletCreator::CreateHandleStorage(HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
std::vector<u8> memory(transfer_mem->GetSize());
|
||||
system.ApplicationMemory().ReadBlock(transfer_mem->GetSourceAddress(), memory.data(),
|
||||
memory.size());
|
||||
ctx.GetMemory().ReadBlock(transfer_mem->GetSourceAddress(), memory.data(), memory.size());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
|
@ -454,10 +454,8 @@ void AudRenU::OpenAudioRenderer(HLERequestContext& ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
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)};
|
||||
auto process{ctx.GetObjectFromHandle<Kernel::KProcess>(process_handle)};
|
||||
auto transfer_memory{ctx.GetObjectFromHandle<Kernel::KTransferMemory>(transfer_memory_handle)};
|
||||
|
||||
const auto session_id{impl->GetSessionId()};
|
||||
if (session_id == -1) {
|
||||
|
@ -278,9 +278,7 @@ 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{
|
||||
system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
|
||||
transfer_memory_handle)};
|
||||
auto transfer_memory{ctx.GetObjectFromHandle<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);
|
||||
@ -323,9 +321,7 @@ void HwOpus::OpenHardwareOpusDecoderForMultiStream(HLERequestContext& ctx) {
|
||||
|
||||
auto transfer_memory_size{rp.Pop<u32>()};
|
||||
auto transfer_memory_handle{ctx.GetCopyHandle(0)};
|
||||
auto transfer_memory{
|
||||
system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
|
||||
transfer_memory_handle)};
|
||||
auto transfer_memory{ctx.GetObjectFromHandle<Kernel::KTransferMemory>(transfer_memory_handle)};
|
||||
|
||||
LOG_DEBUG(Service_Audio,
|
||||
"sample_rate {} channel_count {} total_stream_count {} stereo_stream_count {} "
|
||||
@ -374,9 +370,7 @@ 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{
|
||||
system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
|
||||
transfer_memory_handle)};
|
||||
auto transfer_memory{ctx.GetObjectFromHandle<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);
|
||||
@ -414,9 +408,7 @@ void HwOpus::OpenHardwareOpusDecoderForMultiStreamEx(HLERequestContext& ctx) {
|
||||
|
||||
auto transfer_memory_size{rp.Pop<u32>()};
|
||||
auto transfer_memory_handle{ctx.GetCopyHandle(0)};
|
||||
auto transfer_memory{
|
||||
system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
|
||||
transfer_memory_handle)};
|
||||
auto transfer_memory{ctx.GetObjectFromHandle<Kernel::KTransferMemory>(transfer_memory_handle)};
|
||||
|
||||
LOG_DEBUG(Service_Audio,
|
||||
"sample_rate {} channel_count {} total_stream_count {} stereo_stream_count {} "
|
||||
|
@ -1850,8 +1850,7 @@ 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 = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
|
||||
t_mem_1_handle);
|
||||
auto t_mem_1 = ctx.GetObjectFromHandle<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);
|
||||
@ -1860,8 +1859,7 @@ void IHidServer::InitializeSevenSixAxisSensor(HLERequestContext& ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto t_mem_2 = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
|
||||
t_mem_2_handle);
|
||||
auto t_mem_2 = ctx.GetObjectFromHandle<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);
|
||||
@ -2142,8 +2140,7 @@ void IHidServer::WritePalmaWaveEntry(HLERequestContext& ctx) {
|
||||
|
||||
ASSERT_MSG(t_mem_size == 0x3000, "t_mem_size is not 0x3000 bytes");
|
||||
|
||||
auto t_mem = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
|
||||
t_mem_handle);
|
||||
auto t_mem = ctx.GetObjectFromHandle<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);
|
||||
|
@ -448,8 +448,7 @@ void HidBus::EnableJoyPollingReceiveMode(HLERequestContext& ctx) {
|
||||
|
||||
ASSERT_MSG(t_mem_size == 0x1000, "t_mem_size is not 0x1000 bytes");
|
||||
|
||||
auto t_mem = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
|
||||
t_mem_handle);
|
||||
auto t_mem = ctx.GetObjectFromHandle<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);
|
||||
|
@ -197,8 +197,7 @@ void IRS::RunImageTransferProcessor(HLERequestContext& ctx) {
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
const auto t_mem_handle{ctx.GetCopyHandle(0)};
|
||||
|
||||
auto t_mem = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
|
||||
t_mem_handle);
|
||||
auto t_mem = ctx.GetObjectFromHandle<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);
|
||||
@ -444,8 +443,7 @@ void IRS::RunImageTransferExProcessor(HLERequestContext& ctx) {
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
const auto t_mem_handle{ctx.GetCopyHandle(0)};
|
||||
|
||||
auto t_mem = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
|
||||
t_mem_handle);
|
||||
auto t_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(t_mem_handle);
|
||||
|
||||
LOG_INFO(Service_IRS,
|
||||
"called, npad_type={}, npad_id={}, transfer_memory_size={}, "
|
||||
|
@ -146,10 +146,7 @@ HLERequestContext::HLERequestContext(Kernel::KernelCore& kernel_, Core::Memory::
|
||||
|
||||
HLERequestContext::~HLERequestContext() = default;
|
||||
|
||||
void HLERequestContext::ParseCommandBuffer(Kernel::KProcess& process, u32_le* src_cmdbuf,
|
||||
bool incoming) {
|
||||
client_handle_table = &process.GetHandleTable();
|
||||
|
||||
void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
|
||||
IPC::RequestParser rp(src_cmdbuf);
|
||||
command_header = rp.PopRaw<IPC::CommandHeader>();
|
||||
|
||||
@ -162,7 +159,7 @@ void HLERequestContext::ParseCommandBuffer(Kernel::KProcess& process, u32_le* sr
|
||||
if (command_header->enable_handle_descriptor) {
|
||||
handle_descriptor_header = rp.PopRaw<IPC::HandleDescriptorHeader>();
|
||||
if (handle_descriptor_header->send_current_pid) {
|
||||
pid = process.GetProcessId();
|
||||
pid = thread->GetOwnerProcess()->GetProcessId();
|
||||
rp.Skip(2, false);
|
||||
}
|
||||
if (incoming) {
|
||||
@ -270,9 +267,10 @@ void HLERequestContext::ParseCommandBuffer(Kernel::KProcess& process, u32_le* sr
|
||||
rp.Skip(1, false); // The command is actually an u64, but we don't use the high part.
|
||||
}
|
||||
|
||||
Result HLERequestContext::PopulateFromIncomingCommandBuffer(Kernel::KProcess& process,
|
||||
u32_le* src_cmdbuf) {
|
||||
ParseCommandBuffer(process, src_cmdbuf, true);
|
||||
Result HLERequestContext::PopulateFromIncomingCommandBuffer(u32_le* src_cmdbuf) {
|
||||
client_handle_table = &thread->GetOwnerProcess()->GetHandleTable();
|
||||
|
||||
ParseCommandBuffer(src_cmdbuf, true);
|
||||
|
||||
if (command_header->IsCloseCommand()) {
|
||||
// Close does not populate the rest of the IPC header
|
||||
@ -284,9 +282,9 @@ Result HLERequestContext::PopulateFromIncomingCommandBuffer(Kernel::KProcess& pr
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result HLERequestContext::WriteToOutgoingCommandBuffer(Kernel::KThread& requesting_thread) {
|
||||
Result HLERequestContext::WriteToOutgoingCommandBuffer() {
|
||||
auto current_offset = handles_offset;
|
||||
auto& owner_process = *requesting_thread.GetOwnerProcess();
|
||||
auto& owner_process = *thread->GetOwnerProcess();
|
||||
auto& handle_table = owner_process.GetHandleTable();
|
||||
|
||||
for (auto& object : outgoing_copy_objects) {
|
||||
@ -319,7 +317,7 @@ Result HLERequestContext::WriteToOutgoingCommandBuffer(Kernel::KThread& requesti
|
||||
}
|
||||
|
||||
// Copy the translated command buffer back into the thread's command buffer area.
|
||||
memory.WriteBlock(requesting_thread.GetTlsAddress(), cmd_buf.data(), write_size * sizeof(u32));
|
||||
memory.WriteBlock(thread->GetTlsAddress(), cmd_buf.data(), write_size * sizeof(u32));
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
@ -17,6 +17,7 @@
|
||||
#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;
|
||||
@ -196,10 +197,10 @@ public:
|
||||
}
|
||||
|
||||
/// Populates this context with data from the requesting process/thread.
|
||||
Result PopulateFromIncomingCommandBuffer(Kernel::KProcess& process, u32_le* src_cmdbuf);
|
||||
Result PopulateFromIncomingCommandBuffer(u32_le* src_cmdbuf);
|
||||
|
||||
/// Writes data from this context back to the requesting process/thread.
|
||||
Result WriteToOutgoingCommandBuffer(Kernel::KThread& requesting_thread);
|
||||
Result WriteToOutgoingCommandBuffer();
|
||||
|
||||
[[nodiscard]] u32_le GetHipcCommand() const {
|
||||
return command;
|
||||
@ -359,8 +360,17 @@ public:
|
||||
return *thread;
|
||||
}
|
||||
|
||||
Kernel::KHandleTable& GetClientHandleTable() {
|
||||
return *client_handle_table;
|
||||
[[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;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::shared_ptr<SessionRequestManager> GetManager() const {
|
||||
@ -378,7 +388,7 @@ public:
|
||||
private:
|
||||
friend class IPC::ResponseBuilder;
|
||||
|
||||
void ParseCommandBuffer(Kernel::KProcess& process, u32_le* src_cmdbuf, bool incoming);
|
||||
void ParseCommandBuffer(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{system_.ApplicationMemory()} {
|
||||
context{process->GetMemory()} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IJitEnvironment::GenerateCode, "GenerateCode"},
|
||||
@ -188,7 +188,7 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
auto tmem{process->GetHandleTable().GetObject<Kernel::KTransferMemory>(tmem_handle)};
|
||||
auto tmem{ctx.GetObjectFromHandle<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,11 +356,7 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
// 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)};
|
||||
auto process{ctx.GetObjectFromHandle<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};
|
||||
@ -368,7 +364,7 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
auto rx_mem{handle_table.GetObject<Kernel::KCodeMemory>(rx_mem_handle)};
|
||||
auto rx_mem{ctx.GetObjectFromHandle<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};
|
||||
@ -376,7 +372,7 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
auto ro_mem{handle_table.GetObject<Kernel::KCodeMemory>(ro_mem_handle)};
|
||||
auto ro_mem{ctx.GetObjectFromHandle<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};
|
||||
|
@ -651,10 +651,9 @@ private:
|
||||
void RegisterProcessHandle(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_LDR, "(called)");
|
||||
|
||||
auto process_h = ctx.GetClientHandleTable().GetObject(ctx.GetCopyHandle(0));
|
||||
auto process = ctx.GetObjectFromHandle<Kernel::KProcess>(ctx.GetCopyHandle(0));
|
||||
auto client_pid = ctx.GetPID();
|
||||
auto result = interface.RegisterProcessHandle(client_pid,
|
||||
process_h->DynamicCast<Kernel::KProcess*>());
|
||||
auto result = interface.RegisterProcessHandle(client_pid, process.GetPointerUnsafe());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
@ -671,12 +670,11 @@ private:
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto params = rp.PopRaw<InputParameters>();
|
||||
auto process_h = ctx.GetClientHandleTable().GetObject(ctx.GetCopyHandle(0));
|
||||
auto process = ctx.GetObjectFromHandle<Kernel::KProcess>(ctx.GetCopyHandle(0));
|
||||
|
||||
auto client_pid = ctx.GetPID();
|
||||
auto result =
|
||||
interface.RegisterProcessModuleInfo(client_pid, params.nrr_address, params.nrr_size,
|
||||
process_h->DynamicCast<Kernel::KProcess*>());
|
||||
auto result = interface.RegisterProcessModuleInfo(
|
||||
client_pid, params.nrr_address, params.nrr_size, process.GetPointerUnsafe());
|
||||
|
||||
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.GetThread());
|
||||
ctx.WriteToOutgoingCommandBuffer();
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -730,17 +730,6 @@ 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.
|
||||
*
|
||||
@ -1094,18 +1083,13 @@ bool Memory::InvalidateNCE(Common::ProcessAddress vaddr, size_t size) {
|
||||
rasterizer = true;
|
||||
});
|
||||
|
||||
const bool mapping_exists = mapped && ptr != nullptr;
|
||||
|
||||
#ifdef __linux__
|
||||
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));
|
||||
}
|
||||
if (!rasterizer && mapped) {
|
||||
impl->buffer->DeferredMapSeparateHeap(GetInteger(vaddr));
|
||||
}
|
||||
#endif
|
||||
|
||||
return mapping_exists;
|
||||
return mapped && ptr != nullptr;
|
||||
}
|
||||
|
||||
bool Memory::InvalidateSeparateHeap(void* fault_address) {
|
||||
|
@ -74,6 +74,11 @@ 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);
|
||||
}
|
||||
|
@ -1532,7 +1532,8 @@ void EmitContext::DefineOutputs(const IR::Program& program) {
|
||||
if (stage == Stage::Fragment) {
|
||||
throw NotImplementedException("Storing ClipDistance in fragment stage");
|
||||
}
|
||||
const Id type{TypeArray(F32[1], Const(8U))};
|
||||
const Id type{TypeArray(
|
||||
F32[1], Const(std::min(info.used_clip_distances, profile.max_user_clip_distances)))};
|
||||
clip_distances = DefineOutput(*this, type, invocations, spv::BuiltIn::ClipDistance);
|
||||
}
|
||||
if (info.stores[IR::Attribute::Layer] &&
|
||||
|
@ -913,7 +913,11 @@ void GatherInfoFromHeader(Environment& env, Info& info) {
|
||||
}
|
||||
for (size_t index = 0; index < 8; ++index) {
|
||||
const u16 mask{header.vtg.omap_systemc.clip_distances};
|
||||
info.stores.Set(IR::Attribute::ClipDistance0 + index, ((mask >> index) & 1) != 0);
|
||||
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::PrimitiveId,
|
||||
header.vtg.omap_systemb.primitive_array_id != 0);
|
||||
|
@ -87,6 +87,8 @@ struct Profile {
|
||||
bool has_broken_robust{};
|
||||
|
||||
u64 min_ssbo_alignment{};
|
||||
|
||||
u32 max_user_clip_distances{};
|
||||
};
|
||||
|
||||
} // namespace Shader
|
||||
|
@ -324,6 +324,8 @@ 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;
|
||||
|
@ -327,12 +327,13 @@ 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 (!maxwell3d.AnyParametersDirty() || !IsTopologySafe(topology)) {
|
||||
if (!force && (!maxwell3d.AnyParametersDirty() || !IsTopologySafe(topology))) {
|
||||
Fallback(parameters);
|
||||
return;
|
||||
}
|
||||
|
||||
auto& params = maxwell3d.draw_manager->GetIndirectParams();
|
||||
params.is_byte_count = true;
|
||||
params.is_indexed = false;
|
||||
@ -503,6 +504,8 @@ 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,5 +173,13 @@ 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,4 +376,15 @@ 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,6 +5,7 @@
|
||||
|
||||
#include <array>
|
||||
#include <span>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/buffer_cache/buffer_cache_base.h"
|
||||
@ -121,6 +122,9 @@ 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) {
|
||||
@ -233,6 +237,7 @@ private:
|
||||
u32 index_buffer_offset = 0;
|
||||
|
||||
u64 device_access_memory;
|
||||
std::unordered_map<GPUVAddr, OGLTransformFeedback> tfb_objects;
|
||||
};
|
||||
|
||||
struct BufferCacheParams {
|
||||
|
@ -309,6 +309,13 @@ 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));
|
||||
@ -1371,6 +1378,10 @@ 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,6 +139,12 @@ 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,4 +207,21 @@ 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,4 +323,31 @@ 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,6 +233,7 @@ 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,6 +374,7 @@ 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,66 +60,72 @@ u32 ConvertedBytesPerBlock(VideoCore::Surface::PixelFormat pixel_format) {
|
||||
}
|
||||
|
||||
template <auto decompress, PixelFormat pixel_format>
|
||||
void DecompressBlocks(std::span<const u8> input, std::span<u8> output, Extent3D extent,
|
||||
void DecompressBlocks(std::span<const u8> input, std::span<u8> output, BufferImageCopy& copy,
|
||||
bool is_signed = false) {
|
||||
const u32 out_bpp = ConvertedBytesPerBlock(pixel_format);
|
||||
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;
|
||||
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;
|
||||
size_t input_offset = 0;
|
||||
size_t output_offset = 0;
|
||||
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;
|
||||
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;
|
||||
if constexpr (IsSigned(pixel_format)) {
|
||||
decompress(src, dst, x, y, extent.width, extent.height, is_signed);
|
||||
decompress(src, dst, x, y, width, height, is_signed);
|
||||
} else {
|
||||
decompress(src, dst, x, y, extent.width, extent.height);
|
||||
decompress(src, dst, x, y, width, height);
|
||||
}
|
||||
input_offset += BlockSize(pixel_format);
|
||||
src_offset += block_size;
|
||||
dst_offset += block_width * out_bpp;
|
||||
}
|
||||
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, Extent3D extent,
|
||||
void DecompressBCn(std::span<const u8> input, std::span<u8> output, BufferImageCopy& copy,
|
||||
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, extent);
|
||||
DecompressBlocks<bcn::DecodeBc1, PixelFormat::BC1_RGBA_UNORM>(input, output, copy);
|
||||
break;
|
||||
case PixelFormat::BC2_UNORM:
|
||||
case PixelFormat::BC2_SRGB:
|
||||
DecompressBlocks<bcn::DecodeBc2, PixelFormat::BC2_UNORM>(input, output, extent);
|
||||
DecompressBlocks<bcn::DecodeBc2, PixelFormat::BC2_UNORM>(input, output, copy);
|
||||
break;
|
||||
case PixelFormat::BC3_UNORM:
|
||||
case PixelFormat::BC3_SRGB:
|
||||
DecompressBlocks<bcn::DecodeBc3, PixelFormat::BC3_UNORM>(input, output, extent);
|
||||
DecompressBlocks<bcn::DecodeBc3, PixelFormat::BC3_UNORM>(input, output, copy);
|
||||
break;
|
||||
case PixelFormat::BC4_SNORM:
|
||||
case PixelFormat::BC4_UNORM:
|
||||
DecompressBlocks<bcn::DecodeBc4, PixelFormat::BC4_UNORM>(
|
||||
input, output, extent, pixel_format == PixelFormat::BC4_SNORM);
|
||||
input, output, copy, pixel_format == PixelFormat::BC4_SNORM);
|
||||
break;
|
||||
case PixelFormat::BC5_SNORM:
|
||||
case PixelFormat::BC5_UNORM:
|
||||
DecompressBlocks<bcn::DecodeBc5, PixelFormat::BC5_UNORM>(
|
||||
input, output, extent, pixel_format == PixelFormat::BC5_SNORM);
|
||||
input, output, copy, pixel_format == PixelFormat::BC5_SNORM);
|
||||
break;
|
||||
case PixelFormat::BC6H_SFLOAT:
|
||||
case PixelFormat::BC6H_UFLOAT:
|
||||
DecompressBlocks<bcn::DecodeBc6, PixelFormat::BC6H_UFLOAT>(
|
||||
input, output, extent, pixel_format == PixelFormat::BC6H_SFLOAT);
|
||||
input, output, copy, pixel_format == PixelFormat::BC6H_SFLOAT);
|
||||
break;
|
||||
case PixelFormat::BC7_SRGB:
|
||||
case PixelFormat::BC7_UNORM:
|
||||
DecompressBlocks<bcn::DecodeBc7, PixelFormat::BC7_UNORM>(input, output, extent);
|
||||
DecompressBlocks<bcn::DecodeBc7, PixelFormat::BC7_UNORM>(input, output, copy);
|
||||
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, Extent3D extent,
|
||||
void DecompressBCn(std::span<const u8> input, std::span<u8> output, BufferImageCopy& copy,
|
||||
VideoCore::Surface::PixelFormat pixel_format);
|
||||
|
||||
} // namespace VideoCommon
|
||||
|
@ -837,6 +837,7 @@ 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) {
|
||||
@ -847,7 +848,7 @@ boost::container::small_vector<BufferImageCopy, 16> UnswizzleImage(Tegra::Memory
|
||||
return {{
|
||||
.buffer_offset = 0,
|
||||
.buffer_size = guest_size_bytes,
|
||||
.buffer_row_length = info.pitch >> bpp_log2,
|
||||
.buffer_row_length = info.pitch * tile_size.width >> bpp_log2,
|
||||
.buffer_image_height = size.height,
|
||||
.image_subresource =
|
||||
{
|
||||
@ -862,7 +863,6 @@ 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,8 +926,6 @@ 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);
|
||||
@ -972,16 +970,14 @@ void ConvertImage(std::span<const u8> input, const ImageInfo& info, std::span<u8
|
||||
bpp_div;
|
||||
output_offset += static_cast<u32>(copy.buffer_size);
|
||||
} else {
|
||||
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);
|
||||
DecompressBCn(input_offset, output.subspan(output_offset), copy, 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,6 +665,10 @@ public:
|
||||
return properties.properties.limits.maxViewports;
|
||||
}
|
||||
|
||||
u32 GetMaxUserClipDistances() const {
|
||||
return properties.properties.limits.maxClipDistances;
|
||||
}
|
||||
|
||||
bool SupportsConditionalBarriers() const {
|
||||
return supports_conditional_barriers;
|
||||
}
|
||||
|
@ -168,14 +168,6 @@ 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