Compare commits

..

5 Commits

Author SHA1 Message Date
ab2af23e47 Android #172 2023-12-26 00:56:59 +00:00
52d3d87d09 Merge PR 12467 2023-12-26 00:56:59 +00:00
30f76ed9c2 Merge PR 12466 2023-12-26 00:56:59 +00:00
c85f87ffa4 Merge PR 12449 2023-12-26 00:56:59 +00:00
7778f1906d Merge PR 12448 2023-12-26 00:56:59 +00:00
45 changed files with 375 additions and 311 deletions

View File

@ -1,7 +1,9 @@
| Pull Request | Commit | Title | Author | Merged? |
|----|----|----|----|----|
| [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 |
| [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.

View File

@ -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");
}

View File

@ -1,17 +1,14 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <vector>
#include "common/assert.h"
#include "common/heap_tracker.h"
#include "common/logging/log.h"
namespace Common {
namespace {
constexpr s64 MaxResidentMapCount = 0x8000;
constexpr size_t MaxResidentMapCount = 0x8000;
} // namespace
@ -30,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);
}
@ -51,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.
@ -64,18 +62,20 @@ 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.
// Unlink from mapping tree and advance.
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);
const auto it = m_mappings.nfind_key(key);
if (it == m_mappings.end()) {
// There are no separate heap mappings remaining.
@ -152,61 +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 > 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);
// 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());
// 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();
// 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) {
@ -223,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;
@ -235,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_mappings.insert(*right);
m_resident_map_count++;
// 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) {
@ -260,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

View File

@ -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,18 +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;
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

View File

@ -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;
}

View File

@ -14,9 +14,6 @@ void Initialize();
void Start();
/// Explicitly stops the logger thread and flushes the buffers
void Stop();
void DisableLoggingInTests();
/**

View File

@ -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

View File

@ -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; });

View File

@ -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;
};

View File

@ -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;

View File

@ -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;

View File

@ -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 {

View File

@ -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"

View File

@ -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);

View File

@ -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) {

View File

@ -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 {} "

View File

@ -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);

View File

@ -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);

View File

@ -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={}, "

View File

@ -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;
}

View File

@ -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{};

View File

@ -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};

View File

@ -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);

View File

@ -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;

View File

@ -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) {

View File

@ -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);
}

View File

@ -1532,8 +1532,7 @@ 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(std::min(info.used_clip_distances, profile.max_user_clip_distances)))};
const Id type{TypeArray(F32[1], Const(8U))};
clip_distances = DefineOutput(*this, type, invocations, spv::BuiltIn::ClipDistance);
}
if (info.stores[IR::Attribute::Layer] &&

View File

@ -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);

View File

@ -87,8 +87,6 @@ struct Profile {
bool has_broken_robust{};
u64 min_ssbo_alignment{};
u32 max_user_clip_distances{};
};
} // namespace Shader

View File

@ -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;

View File

@ -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());
}
};

View File

@ -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

View File

@ -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

View File

@ -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 {

View File

@ -309,13 +309,6 @@ void RasterizerOpenGL::DrawIndirect() {
const auto& params = maxwell3d->draw_manager->GetIndirectParams();
buffer_cache.SetDrawIndirect(&params);
PrepareDraw(params.is_indexed, [this, &params](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_} {}

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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{

View File

@ -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);

View File

@ -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

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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,