mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-08-28 15:31:12 +02:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1439a8041b | |||
| 6c3ea34293 | |||
| bca75e8c21 | |||
| 7572632afa | |||
| 641a430463 | |||
| 60328b0bd9 | |||
| e875a3196b | |||
| 4eb082485d |
@@ -14,17 +14,12 @@
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
#include <span>
|
||||
#include <utility>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/range_mutex.h"
|
||||
#include "common/scratch_buffer.h"
|
||||
#include "common/virtual_buffer.h"
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
namespace Core {
|
||||
|
||||
constexpr size_t DEVICE_PAGEBITS = 12ULL;
|
||||
@@ -50,74 +45,6 @@ class DeviceMemoryManager {
|
||||
using DeviceMethods = typename Traits::DeviceMethods;
|
||||
|
||||
public:
|
||||
class MirrorMapping {
|
||||
public:
|
||||
MirrorMapping() = default;
|
||||
MirrorMapping(u8* mapped_base_, size_t mapped_size_, size_t data_offset_)
|
||||
: mapped_base{mapped_base_}, mapped_size{mapped_size_}, data_offset{data_offset_} {}
|
||||
|
||||
MirrorMapping(const MirrorMapping&) = delete;
|
||||
MirrorMapping& operator=(const MirrorMapping&) = delete;
|
||||
|
||||
MirrorMapping(MirrorMapping&& other) noexcept {
|
||||
MoveFrom(other);
|
||||
}
|
||||
|
||||
MirrorMapping& operator=(MirrorMapping&& other) noexcept {
|
||||
if (this != &other) {
|
||||
Release();
|
||||
MoveFrom(other);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
~MirrorMapping() {
|
||||
Release();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsValid() const noexcept {
|
||||
return mapped_base != nullptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] explicit operator bool() const noexcept {
|
||||
return IsValid();
|
||||
}
|
||||
|
||||
[[nodiscard]] u8* Data() noexcept {
|
||||
return mapped_base ? mapped_base + data_offset : nullptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] const u8* Data() const noexcept {
|
||||
return mapped_base ? mapped_base + data_offset : nullptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] size_t Size() const noexcept {
|
||||
return mapped_size >= data_offset ? mapped_size - data_offset : 0;
|
||||
}
|
||||
|
||||
private:
|
||||
void MoveFrom(MirrorMapping& other) noexcept {
|
||||
mapped_base = std::exchange(other.mapped_base, nullptr);
|
||||
mapped_size = std::exchange(other.mapped_size, 0);
|
||||
data_offset = std::exchange(other.data_offset, 0);
|
||||
}
|
||||
|
||||
void Release() noexcept {
|
||||
#if defined(__linux__)
|
||||
if (mapped_base) {
|
||||
munmap(mapped_base, mapped_size);
|
||||
}
|
||||
#endif
|
||||
mapped_base = nullptr;
|
||||
mapped_size = 0;
|
||||
data_offset = 0;
|
||||
}
|
||||
|
||||
u8* mapped_base{};
|
||||
size_t mapped_size{};
|
||||
size_t data_offset{};
|
||||
};
|
||||
|
||||
DeviceMemoryManager(const DeviceMemory& device_memory);
|
||||
~DeviceMemoryManager();
|
||||
|
||||
@@ -191,11 +118,6 @@ public:
|
||||
void WriteBlock(DAddr address, const void* src_pointer, size_t size);
|
||||
void WriteBlockUnsafe(DAddr address, const void* src_pointer, size_t size);
|
||||
|
||||
[[nodiscard]] MirrorMapping CreateMirrorMapping(DAddr address, size_t size) const;
|
||||
[[nodiscard]] u64 GetMappingVersion() const noexcept {
|
||||
return mapping_version.load(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
Asid RegisterProcess(Memory::Memory* memory);
|
||||
void UnregisterProcess(Asid id);
|
||||
|
||||
@@ -314,7 +236,6 @@ private:
|
||||
std::unique_ptr<CachedPages> cached_pages;
|
||||
Common::RangeMutex counter_guard;
|
||||
std::mutex mapping_guard;
|
||||
std::atomic<u64> mapping_version{1};
|
||||
|
||||
|
||||
};
|
||||
|
||||
@@ -4,10 +4,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#if defined(__linux__) && !defined(_GNU_SOURCE)
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
#include <atomic>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
@@ -15,17 +11,6 @@
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <sys/mman.h>
|
||||
#ifndef MREMAP_MAYMOVE
|
||||
#define MREMAP_MAYMOVE 1
|
||||
#endif
|
||||
#ifndef MREMAP_FIXED
|
||||
#define MREMAP_FIXED 2
|
||||
#endif
|
||||
extern "C" void* mremap(void* old_address, size_t old_size, size_t new_size, int flags, ...);
|
||||
#endif
|
||||
|
||||
#include "common/address_space.h"
|
||||
#include "common/address_space.inc"
|
||||
#include "common/alignment.h"
|
||||
@@ -255,7 +240,6 @@ void DeviceMemoryManager<Traits>::Map(DAddr address, VAddr virtual_address, size
|
||||
impl->multi_dev_address.Register(new_dev, start_id);
|
||||
}
|
||||
t_slot = {};
|
||||
mapping_version.fetch_add(1, std::memory_order_release);
|
||||
if (track) {
|
||||
TrackContinuityImpl(address, virtual_address, size, asid);
|
||||
}
|
||||
@@ -288,7 +272,6 @@ void DeviceMemoryManager<Traits>::Unmap(DAddr address, size_t size) {
|
||||
}
|
||||
}
|
||||
t_slot = {};
|
||||
mapping_version.fetch_add(1, std::memory_order_release);
|
||||
}
|
||||
template <typename Traits>
|
||||
void DeviceMemoryManager<Traits>::TrackContinuityImpl(DAddr address, VAddr virtual_address,
|
||||
@@ -332,78 +315,6 @@ const u8* DeviceMemoryManager<Traits>::GetSpan(const DAddr src_addr, const std::
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <typename Traits>
|
||||
typename DeviceMemoryManager<Traits>::MirrorMapping DeviceMemoryManager<Traits>::CreateMirrorMapping(
|
||||
DAddr address, size_t size) const {
|
||||
#if !defined(__linux__)
|
||||
return {};
|
||||
#else
|
||||
if (size == 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const DAddr aligned_address = Common::AlignDown(address, DAddr{page_size});
|
||||
const size_t data_offset = static_cast<size_t>(address - aligned_address);
|
||||
const size_t mapped_size = Common::AlignUp(size + data_offset, page_size);
|
||||
|
||||
struct Segment {
|
||||
const u8* source;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
std::vector<Segment> segments;
|
||||
segments.reserve(Common::DivCeil(mapped_size, page_size));
|
||||
|
||||
size_t remaining_size = mapped_size;
|
||||
size_t page_index = aligned_address >> page_bits;
|
||||
while (remaining_size > 0) {
|
||||
const size_t next_pages = std::size_t(tracked_entries[page_index].continuity_tracker);
|
||||
const size_t copy_amount = (std::min)(next_pages << page_bits, remaining_size);
|
||||
|
||||
const auto phys_addr = tracked_entries[page_index].compressed_physical_ptr;
|
||||
if (phys_addr == 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto* source =
|
||||
GetPointerFromRaw<u8>(PAddr(phys_addr - 1U) << Memory::YUZU_PAGEBITS);
|
||||
|
||||
if (!segments.empty() && segments.back().source + segments.back().size == source) {
|
||||
segments.back().size += copy_amount;
|
||||
} else {
|
||||
segments.push_back({source, copy_amount});
|
||||
}
|
||||
|
||||
page_index += next_pages;
|
||||
remaining_size -= copy_amount;
|
||||
}
|
||||
|
||||
void* const mirror_base =
|
||||
mmap(nullptr, mapped_size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
if (mirror_base == MAP_FAILED) {
|
||||
return {};
|
||||
}
|
||||
|
||||
size_t mirror_offset = 0;
|
||||
for (const auto& segment : segments) {
|
||||
void* const target = static_cast<u8*>(mirror_base) + mirror_offset;
|
||||
void* const result = mremap(const_cast<u8*>(segment.source), 0, segment.size,
|
||||
MREMAP_MAYMOVE | MREMAP_FIXED, target);
|
||||
if (result == MAP_FAILED) {
|
||||
munmap(mirror_base, mapped_size);
|
||||
return {};
|
||||
}
|
||||
if (mprotect(result, segment.size, PROT_READ | PROT_WRITE) != 0) {
|
||||
munmap(mirror_base, mapped_size);
|
||||
return {};
|
||||
}
|
||||
mirror_offset += segment.size;
|
||||
}
|
||||
|
||||
return MirrorMapping{static_cast<u8*>(mirror_base), mapped_size, data_offset};
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename Traits>
|
||||
void DeviceMemoryManager<Traits>::InnerGatherDeviceAddresses(Common::ScratchBuffer<u32>& buffer,
|
||||
PAddr address) {
|
||||
|
||||
@@ -25,42 +25,33 @@ NvMap::Handle::Handle(u64 size_, Id id_)
|
||||
flags.raw = 0;
|
||||
}
|
||||
|
||||
NvResult NvMap::Handle::Alloc(Flags pFlags, u32 pAlign, u8 pKind, u64 pAddress,
|
||||
NvCore::SessionId pSessionId) {
|
||||
std::scoped_lock lock(mutex);
|
||||
NvResult NvMap::Handle::Alloc(Flags pFlags, u32 pAlign, u8 pKind, u64 pAddress, NvCore::SessionId pSessionId) {
|
||||
// Handles cannot be allocated twice
|
||||
if (allocated) {
|
||||
return NvResult::AccessDenied;
|
||||
}
|
||||
|
||||
flags = pFlags;
|
||||
kind = pKind;
|
||||
align = pAlign < YUZU_PAGESIZE ? YUZU_PAGESIZE : pAlign;
|
||||
session_id = pSessionId;
|
||||
|
||||
// This flag is only applicable for handles with an address passed
|
||||
if (pAddress) {
|
||||
flags.keep_uncached_after_free.Assign(0);
|
||||
} else {
|
||||
LOG_CRITICAL(Service_NVDRV,
|
||||
"Mapping nvmap handles without a CPU side address is unimplemented!");
|
||||
LOG_CRITICAL(Service_NVDRV, "Mapping nvmap handles without a CPU side address is unimplemented!");
|
||||
}
|
||||
|
||||
size = Common::AlignUp(size, YUZU_PAGESIZE);
|
||||
aligned_size = Common::AlignUp(size, align);
|
||||
address = pAddress;
|
||||
allocated = true;
|
||||
|
||||
return NvResult::Success;
|
||||
}
|
||||
|
||||
NvResult NvMap::Handle::Duplicate(bool internal_session) {
|
||||
std::scoped_lock lock(mutex);
|
||||
// Unallocated handles cannot be duplicated as duplication requires memory accounting (in HOS)
|
||||
if (!allocated) [[unlikely]] {
|
||||
return NvResult::BadValue;
|
||||
}
|
||||
|
||||
// If we internally use FromId the duplication tracking of handles won't work accurately due to
|
||||
// us not implementing per-process handle refs.
|
||||
if (internal_session) {
|
||||
@@ -68,16 +59,14 @@ NvResult NvMap::Handle::Duplicate(bool internal_session) {
|
||||
} else {
|
||||
dupes++;
|
||||
}
|
||||
|
||||
return NvResult::Success;
|
||||
}
|
||||
|
||||
NvMap::NvMap(Container& core_, Tegra::Host1x::Host1x& host1x_) : host1x{host1x_}, core{core_} {}
|
||||
|
||||
void NvMap::AddHandle(std::shared_ptr<Handle> handle_description) {
|
||||
std::scoped_lock lock(handles_lock);
|
||||
|
||||
handles.emplace(handle_description->id, std::move(handle_description));
|
||||
void NvMap::AddHandle(Handle&& handle_description) {
|
||||
std::scoped_lock l(handles_lock);
|
||||
handles.insert_or_assign(handle_description.id, std::move(handle_description));
|
||||
}
|
||||
|
||||
void NvMap::UnmapHandle(Handle& handle_description) {
|
||||
@@ -116,57 +105,48 @@ void NvMap::UnmapHandle(Handle& handle_description) {
|
||||
bool NvMap::TryRemoveHandle(const Handle& handle_description) {
|
||||
// No dupes left, we can remove from handle map
|
||||
if (handle_description.dupes == 0 && handle_description.internal_dupes == 0) {
|
||||
std::scoped_lock lock(handles_lock);
|
||||
|
||||
auto it{handles.find(handle_description.id)};
|
||||
std::scoped_lock l(handles_lock);
|
||||
auto it = handles.find(handle_description.id);
|
||||
if (it != handles.end()) {
|
||||
handles.erase(it);
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
NvResult NvMap::CreateHandle(u64 size, std::shared_ptr<NvMap::Handle>& result_out) {
|
||||
if (!size) [[unlikely]] {
|
||||
NvResult NvMap::CreateHandle(u64 size, Handle::Id& out_handle) {
|
||||
if (!Common::AlignUp(size, YUZU_PAGESIZE)) {
|
||||
return NvResult::BadValue;
|
||||
}
|
||||
|
||||
u32 id{next_handle_id.fetch_add(HandleIdIncrement, std::memory_order_relaxed)};
|
||||
auto handle_description{std::make_shared<Handle>(size, id)};
|
||||
AddHandle(handle_description);
|
||||
|
||||
result_out = handle_description;
|
||||
u32 id = next_handle_id.fetch_add(HandleIdIncrement, std::memory_order_relaxed);
|
||||
AddHandle(Handle(size, id));
|
||||
out_handle = id;
|
||||
return NvResult::Success;
|
||||
}
|
||||
|
||||
std::shared_ptr<NvMap::Handle> NvMap::GetHandle(Handle::Id handle) {
|
||||
std::scoped_lock lock(handles_lock);
|
||||
try {
|
||||
return handles.at(handle);
|
||||
} catch (std::out_of_range&) {
|
||||
return nullptr;
|
||||
}
|
||||
std::optional<std::reference_wrapper<NvMap::Handle>> NvMap::GetHandle(Handle::Id handle) {
|
||||
if (auto const it = handles.find(handle); it != handles.end())
|
||||
return {it->second};
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
DAddr NvMap::GetHandleAddress(Handle::Id handle) {
|
||||
std::scoped_lock lock(handles_lock);
|
||||
try {
|
||||
return handles.at(handle)->d_address;
|
||||
} catch (std::out_of_range&) {
|
||||
return 0;
|
||||
}
|
||||
if (auto const it = handles.find(handle); it != handles.end())
|
||||
return it->second.d_address;
|
||||
return 0;
|
||||
}
|
||||
|
||||
DAddr NvMap::PinHandle(NvMap::Handle::Id handle, bool low_area_pin) {
|
||||
auto handle_description{GetHandle(handle)};
|
||||
if (!handle_description) [[unlikely]] {
|
||||
std::scoped_lock lock(handles_lock);
|
||||
auto o = GetHandle(handle);
|
||||
if (!o) [[unlikely]] {
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::scoped_lock lock(handle_description->mutex);
|
||||
auto handle_description = &o->get();
|
||||
|
||||
const auto map_low_area = [&] {
|
||||
if (handle_description->pin_virt_address == 0) {
|
||||
u32 address = host1x.Allocator().Allocate(u32(handle_description->aligned_size));
|
||||
@@ -180,17 +160,15 @@ DAddr NvMap::PinHandle(NvMap::Handle::Id handle, bool low_area_pin) {
|
||||
{
|
||||
// Lock now to prevent our queue entry from being removed for allocation in-between the
|
||||
// following check and erase
|
||||
std::scoped_lock queueLock(unmap_queue_lock);
|
||||
std::scoped_lock ql(unmap_queue_lock);
|
||||
if (handle_description->unmap_queue_entry) {
|
||||
unmap_queue.erase(*handle_description->unmap_queue_entry);
|
||||
handle_description->unmap_queue_entry.reset();
|
||||
|
||||
if (low_area_pin) {
|
||||
map_low_area();
|
||||
handle_description->pins++;
|
||||
return static_cast<DAddr>(handle_description->pin_virt_address);
|
||||
return DAddr(handle_description->pin_virt_address);
|
||||
}
|
||||
|
||||
handle_description->pins++;
|
||||
return handle_description->d_address;
|
||||
}
|
||||
@@ -211,12 +189,11 @@ DAddr NvMap::PinHandle(NvMap::Handle::Id handle, bool low_area_pin) {
|
||||
while ((address = smmu.Allocate(aligned_up)) == 0) {
|
||||
// Free handles until the allocation succeeds
|
||||
std::scoped_lock queueLock(unmap_queue_lock);
|
||||
if (auto freeHandleDesc{unmap_queue.front()}) {
|
||||
if (auto free_handle = handles.find(unmap_queue.front()); free_handle != handles.end()) {
|
||||
// Handles in the unmap queue are guaranteed not to be pinned so don't bother
|
||||
// checking if they are before unmapping
|
||||
std::scoped_lock freeLock(freeHandleDesc->mutex);
|
||||
if (handle_description->d_address)
|
||||
UnmapHandle(*freeHandleDesc);
|
||||
UnmapHandle(free_handle->second);
|
||||
} else {
|
||||
LOG_CRITICAL(Service_NVDRV, "Ran out of SMMU address space!");
|
||||
}
|
||||
@@ -234,51 +211,44 @@ DAddr NvMap::PinHandle(NvMap::Handle::Id handle, bool low_area_pin) {
|
||||
|
||||
handle_description->pins++;
|
||||
if (low_area_pin) {
|
||||
return static_cast<DAddr>(handle_description->pin_virt_address);
|
||||
return DAddr(handle_description->pin_virt_address);
|
||||
}
|
||||
return handle_description->d_address;
|
||||
}
|
||||
|
||||
void NvMap::UnpinHandle(Handle::Id handle) {
|
||||
auto handle_description{GetHandle(handle)};
|
||||
if (!handle_description) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::scoped_lock lock(handle_description->mutex);
|
||||
if (--handle_description->pins < 0) {
|
||||
LOG_WARNING(Service_NVDRV, "Pin count imbalance detected!");
|
||||
} else if (!handle_description->pins) {
|
||||
std::scoped_lock queueLock(unmap_queue_lock);
|
||||
|
||||
// Add to the unmap queue allowing this handle's memory to be freed if needed
|
||||
unmap_queue.push_back(handle_description);
|
||||
handle_description->unmap_queue_entry = std::prev(unmap_queue.end());
|
||||
std::scoped_lock lock(handles_lock);
|
||||
if (auto o = GetHandle(handle); o) {
|
||||
auto handle_description = &o->get();
|
||||
if (--handle_description->pins < 0) {
|
||||
LOG_WARNING(Service_NVDRV, "Pin count imbalance detected!");
|
||||
} else if (!handle_description->pins) {
|
||||
std::scoped_lock ql(unmap_queue_lock);
|
||||
// Add to the unmap queue allowing this handle's memory to be freed if needed
|
||||
unmap_queue.push_back(handle);
|
||||
handle_description->unmap_queue_entry = std::prev(unmap_queue.end());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NvMap::DuplicateHandle(Handle::Id handle, bool internal_session) {
|
||||
auto handle_description{GetHandle(handle)};
|
||||
if (!handle_description) {
|
||||
std::scoped_lock lock(handles_lock);
|
||||
auto o = GetHandle(handle);
|
||||
if (!o) {
|
||||
LOG_CRITICAL(Service_NVDRV, "Unregistered handle!");
|
||||
return;
|
||||
}
|
||||
|
||||
auto result = handle_description->Duplicate(internal_session);
|
||||
auto result = o->get().Duplicate(internal_session);
|
||||
if (result != NvResult::Success) {
|
||||
LOG_CRITICAL(Service_NVDRV, "Could not duplicate handle!");
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<NvMap::FreeInfo> NvMap::FreeHandle(Handle::Id handle, bool internal_session) {
|
||||
std::weak_ptr<Handle> hWeak{GetHandle(handle)};
|
||||
FreeInfo freeInfo;
|
||||
|
||||
// We use a weak ptr here so we can tell when the handle has been freed and report that back to
|
||||
// guest
|
||||
if (auto handle_description = hWeak.lock()) {
|
||||
std::scoped_lock lock(handle_description->mutex);
|
||||
|
||||
// We use a weak ptr here so we can tell when the handle has been freed and report that back to guest
|
||||
std::scoped_lock lock(handles_lock);
|
||||
if (auto o = GetHandle(handle); o) {
|
||||
auto handle_description = &o->get();
|
||||
if (internal_session) {
|
||||
if (--handle_description->internal_dupes < 0)
|
||||
LOG_WARNING(Service_NVDRV, "Internal duplicate count imbalance detected!");
|
||||
@@ -288,25 +258,25 @@ std::optional<NvMap::FreeInfo> NvMap::FreeHandle(Handle::Id handle, bool interna
|
||||
} else if (handle_description->dupes == 0) {
|
||||
// Force unmap the handle
|
||||
if (handle_description->d_address) {
|
||||
std::scoped_lock queueLock(unmap_queue_lock);
|
||||
std::scoped_lock ql(unmap_queue_lock);
|
||||
UnmapHandle(*handle_description);
|
||||
}
|
||||
|
||||
handle_description->pins = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Try to remove the shared ptr to the handle from the map, if nothing else is using the
|
||||
// handle then it will now be freed when `handle_description` goes out of scope
|
||||
if (TryRemoveHandle(*handle_description)) {
|
||||
LOG_DEBUG(Service_NVDRV, "Removed nvmap handle: {}", handle);
|
||||
} else {
|
||||
LOG_DEBUG(Service_NVDRV,
|
||||
"Tried to free nvmap handle: {} but didn't as it still has duplicates",
|
||||
handle);
|
||||
LOG_DEBUG(Service_NVDRV, "Tried to free nvmap handle: {} but didn't as it still has duplicates", handle);
|
||||
}
|
||||
|
||||
freeInfo = {
|
||||
// // If the handle hasn't been freed from memory, mark that
|
||||
// if (!hWeak.expired()) {
|
||||
// LOG_DEBUG(Service_NVDRV, "nvmap handle: {} wasn't freed as it is still in use", handle);
|
||||
// freeInfo.can_unlock = false;
|
||||
// }
|
||||
return FreeInfo{
|
||||
.address = handle_description->address,
|
||||
.size = handle_description->size,
|
||||
.was_uncached = handle_description->flags.map_uncached.Value() != 0,
|
||||
@@ -315,30 +285,15 @@ std::optional<NvMap::FreeInfo> NvMap::FreeHandle(Handle::Id handle, bool interna
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// If the handle hasn't been freed from memory, mark that
|
||||
if (!hWeak.expired()) {
|
||||
LOG_DEBUG(Service_NVDRV, "nvmap handle: {} wasn't freed as it is still in use", handle);
|
||||
freeInfo.can_unlock = false;
|
||||
}
|
||||
|
||||
return freeInfo;
|
||||
}
|
||||
|
||||
void NvMap::UnmapAllHandles(NvCore::SessionId session_id) {
|
||||
auto handles_copy = [&] {
|
||||
std::scoped_lock lk{handles_lock};
|
||||
return handles;
|
||||
}();
|
||||
|
||||
for (auto& [id, handle] : handles_copy) {
|
||||
{
|
||||
std::scoped_lock lk{handle->mutex};
|
||||
if (handle->session_id.id != session_id.id || handle->dupes <= 0) {
|
||||
continue;
|
||||
}
|
||||
std::scoped_lock lk{handles_lock};
|
||||
for (auto it = handles.begin(); it != handles.end(); ++it) {
|
||||
if (it->second.session_id.id != session_id.id || it->second.dupes <= 0) {
|
||||
continue;
|
||||
}
|
||||
FreeHandle(id, false);
|
||||
FreeHandle(it->first, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,11 @@
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#if BOOST_VERSION >= 109000
|
||||
#include <boost/unordered/unordered_node_map.hpp>
|
||||
#else
|
||||
#include <unordered_map>
|
||||
#endif
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <assert.h>
|
||||
|
||||
@@ -31,54 +36,36 @@ class Host1x;
|
||||
namespace Service::Nvidia::NvCore {
|
||||
|
||||
class Container;
|
||||
/**
|
||||
* @brief The nvmap core class holds the global state for nvmap and provides methods to manage
|
||||
* handles
|
||||
*/
|
||||
/// @brief The nvmap core class holds the global state for nvmap and provides methods to manage handles
|
||||
class NvMap {
|
||||
public:
|
||||
/**
|
||||
* @brief A handle to a contiguous block of memory in an application's address space
|
||||
*/
|
||||
/// @brief A handle to a contiguous block of memory in an application's address space
|
||||
struct Handle {
|
||||
std::mutex mutex;
|
||||
|
||||
using Id = u32;
|
||||
std::optional<typename std::list<Handle::Id>::iterator> unmap_queue_entry{};
|
||||
u64 align{}; //!< The alignment to use when pinning the handle onto the SMMU
|
||||
u64 size; //!< Page-aligned size of the memory the handle refers to
|
||||
u64 aligned_size; //!< `align`-aligned size of the memory the handle refers to
|
||||
u64 orig_size; //!< Original unaligned size of the memory this handle refers to
|
||||
|
||||
DAddr d_address{}; //!< The memory location in the device's AS that this handle corresponds to, this can also be in the nvdrv tmem
|
||||
VAddr address{}; //!< The memory location in the guest's AS that this handle corresponds to, this can also be in the nvdrv tmem
|
||||
s64 pins{};
|
||||
s32 dupes{1}; //!< How many guest references there are to this handle
|
||||
s32 internal_dupes{0}; //!< How many emulator-internal references there are to this handle
|
||||
|
||||
using Id = u32;
|
||||
Id id; //!< A globally unique identifier for this handle
|
||||
|
||||
s64 pins{};
|
||||
u32 pin_virt_address{};
|
||||
std::optional<typename std::list<std::shared_ptr<Handle>>::iterator> unmap_queue_entry{};
|
||||
|
||||
union Flags {
|
||||
u32 raw;
|
||||
BitField<0, 1, u32> map_uncached; //!< If the handle should be mapped as uncached
|
||||
BitField<2, 1, u32> keep_uncached_after_free; //!< Only applicable when the handle was
|
||||
//!< allocated with a fixed address
|
||||
BitField<4, 1, u32> _unk0_; //!< Passed to IOVMM for pins
|
||||
BitField<2, 1, u32> keep_uncached_after_free; //!< Only applicable when the handle was allocated with a fixed address
|
||||
BitField<4, 1, u32> _unk0_; //!< Passed to IOVMM for pins
|
||||
} flags{};
|
||||
static_assert(sizeof(Flags) == sizeof(u32));
|
||||
|
||||
VAddr address{}; //!< The memory location in the guest's AS that this handle corresponds to,
|
||||
//!< this can also be in the nvdrv tmem
|
||||
bool is_shared_mem_mapped{}; //!< If this nvmap has been mapped with the MapSharedMem IPC
|
||||
//!< call
|
||||
|
||||
u8 kind{}; //!< Used for memory compression
|
||||
bool allocated{}; //!< If the handle has been allocated with `Alloc`
|
||||
bool in_heap{};
|
||||
NvCore::SessionId session_id{};
|
||||
|
||||
DAddr d_address{}; //!< The memory location in the device's AS that this handle corresponds
|
||||
//!< to, this can also be in the nvdrv tmem
|
||||
u8 kind{}; //!< Used for memory compression
|
||||
bool allocated : 1 = false; //!< If the handle has been allocated with `Alloc`
|
||||
bool in_heap : 1 = false;
|
||||
bool is_shared_mem_mapped : 1 = false; //!< If this nvmap has been mapped with the MapSharedMem IPC < call
|
||||
|
||||
Handle(u64 size, Id id);
|
||||
|
||||
@@ -123,9 +110,9 @@ public:
|
||||
/**
|
||||
* @brief Creates an unallocated handle of the given size
|
||||
*/
|
||||
[[nodiscard]] NvResult CreateHandle(u64 size, std::shared_ptr<NvMap::Handle>& result_out);
|
||||
[[nodiscard]] NvResult CreateHandle(u64 size, Handle::Id& out_handle);
|
||||
|
||||
std::shared_ptr<Handle> GetHandle(Handle::Id handle);
|
||||
std::optional<std::reference_wrapper<Handle>> GetHandle(Handle::Id handle);
|
||||
|
||||
DAddr GetHandleAddress(Handle::Id handle);
|
||||
|
||||
@@ -157,20 +144,21 @@ public:
|
||||
|
||||
void UnmapAllHandles(NvCore::SessionId session_id);
|
||||
|
||||
private:
|
||||
std::list<std::shared_ptr<Handle>> unmap_queue{};
|
||||
std::list<Handle::Id> unmap_queue{};
|
||||
/// Main owning map of handles
|
||||
#if BOOST_VERSION >= 109000
|
||||
boost::unordered_node_map<Handle::Id, Handle> handles{};
|
||||
#else
|
||||
std::unordered_map<Handle::Id, Handle> handles{};
|
||||
#endif
|
||||
std::mutex unmap_queue_lock{}; //!< Protects access to `unmap_queue`
|
||||
|
||||
ankerl::unordered_dense::map<Handle::Id, std::shared_ptr<Handle>>
|
||||
handles{}; //!< Main owning map of handles
|
||||
std::mutex handles_lock; //!< Protects access to `handles`
|
||||
|
||||
static constexpr u32 HandleIdIncrement{
|
||||
4}; //!< Each new handle ID is an increment of 4 from the previous
|
||||
static constexpr u32 HandleIdIncrement{4}; //!< Each new handle ID is an increment of 4 from the previous
|
||||
std::atomic<u32> next_handle_id{HandleIdIncrement};
|
||||
Tegra::Host1x::Host1x& host1x;
|
||||
Container& core;
|
||||
|
||||
void AddHandle(std::shared_ptr<Handle> handle);
|
||||
void AddHandle(Handle&& handle);
|
||||
|
||||
/**
|
||||
* @brief Unmaps and frees the SMMU memory region a handle is mapped to
|
||||
@@ -184,7 +172,5 @@ private:
|
||||
* @return If the handle was removed from the map
|
||||
*/
|
||||
bool TryRemoveHandle(const Handle& handle_description);
|
||||
|
||||
Container& core;
|
||||
};
|
||||
} // namespace Service::Nvidia::NvCore
|
||||
|
||||
@@ -329,10 +329,11 @@ NvResult nvhost_as_gpu::MapBufferEx(IoctlMapBufferEx& params) {
|
||||
}
|
||||
}
|
||||
|
||||
auto handle{nvmap.GetHandle(params.handle)};
|
||||
if (!handle) {
|
||||
auto o = nvmap.GetHandle(params.handle);
|
||||
if (!o) {
|
||||
return NvResult::BadValue;
|
||||
}
|
||||
auto handle = &o->get();
|
||||
|
||||
DAddr device_address = DAddr(nvmap.PinHandle(params.handle, false) + params.buffer_offset);
|
||||
u64 size{params.mapping_size ? params.mapping_size : handle->orig_size};
|
||||
|
||||
@@ -103,17 +103,13 @@ NvResult nvhost_nvdec_common::Submit(IoctlSubmit& params, std::span<u8> data, De
|
||||
|
||||
for (std::size_t i = 0; i < syncpt_increments.size(); i++) {
|
||||
const SyncptIncr& syncpt_incr = syncpt_increments[i];
|
||||
fence_thresholds[i] =
|
||||
syncpoint_manager.IncrementSyncpointMaxExt(syncpt_incr.id, syncpt_incr.increments);
|
||||
fence_thresholds[i] = syncpoint_manager.IncrementSyncpointMaxExt(syncpt_incr.id, syncpt_incr.increments);
|
||||
}
|
||||
|
||||
for (const auto& cmd_buffer : command_buffers) {
|
||||
const auto object = nvmap.GetHandle(cmd_buffer.memory_id);
|
||||
ASSERT_OR_EXECUTE(object, return NvResult::InvalidState;);
|
||||
Core::Memory::CpuGuestMemory<Tegra::ChCommandHeader,
|
||||
Core::Memory::GuestMemoryFlags::SafeRead>
|
||||
cmdlist(session->process->GetMemory(), object->address + cmd_buffer.offset,
|
||||
cmd_buffer.word_count);
|
||||
Core::Memory::CpuGuestMemory<Tegra::ChCommandHeader, Core::Memory::GuestMemoryFlags::SafeRead> cmdlist(session->process->GetMemory(), object->get().address + cmd_buffer.offset, cmd_buffer.word_count);
|
||||
host1x.PushEntries(fd, std::move(cmdlist));
|
||||
}
|
||||
|
||||
|
||||
@@ -83,17 +83,14 @@ void nvmap::OnClose(DeviceFD fd) {
|
||||
NvResult nvmap::IocCreate(IocCreateParams& params) {
|
||||
LOG_DEBUG(Service_NVDRV, "called, size=0x{:08X}", params.size);
|
||||
|
||||
std::shared_ptr<NvCore::NvMap::Handle> handle_description{};
|
||||
auto result =
|
||||
file.CreateHandle(Common::AlignUp(params.size, YUZU_PAGESIZE), handle_description);
|
||||
NvCore::NvMap::Handle handle_description(0, 0);
|
||||
// Orig size is the unaligned size, set the handle to that
|
||||
auto result = file.CreateHandle(params.size, params.handle);
|
||||
if (result != NvResult::Success) {
|
||||
LOG_CRITICAL(Service_NVDRV, "Failed to create Object");
|
||||
return result;
|
||||
}
|
||||
handle_description->orig_size = params.size; // Orig size is the unaligned size
|
||||
params.handle = handle_description->id;
|
||||
LOG_DEBUG(Service_NVDRV, "handle: {}, size: {:#X}", handle_description->id, params.size);
|
||||
|
||||
LOG_DEBUG(Service_NVDRV, "handle: {}, size: {:#X}", params.handle, params.size);
|
||||
return NvResult::Success;
|
||||
}
|
||||
|
||||
@@ -115,30 +112,27 @@ NvResult nvmap::IocAlloc(IocAllocParams& params, DeviceFD fd) {
|
||||
params.align = YUZU_PAGESIZE;
|
||||
}
|
||||
|
||||
auto handle_description{file.GetHandle(params.handle)};
|
||||
if (!handle_description) {
|
||||
std::scoped_lock lock(file.handles_lock);
|
||||
auto o = file.GetHandle(params.handle);
|
||||
if (!o) {
|
||||
LOG_CRITICAL(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle);
|
||||
return NvResult::BadValue;
|
||||
}
|
||||
|
||||
auto handle_description = &o->get();
|
||||
if (handle_description->allocated) {
|
||||
LOG_CRITICAL(Service_NVDRV, "Object is already allocated, handle={:08X}", params.handle);
|
||||
return NvResult::InsufficientMemory;
|
||||
}
|
||||
|
||||
const auto result = handle_description->Alloc(params.flags, params.align, params.kind,
|
||||
params.address, sessions[fd]);
|
||||
const auto result = handle_description->Alloc(params.flags, params.align, params.kind, params.address, sessions[fd]);
|
||||
if (result != NvResult::Success) {
|
||||
LOG_CRITICAL(Service_NVDRV, "Object failed to allocate, handle={:08X}", params.handle);
|
||||
return result;
|
||||
}
|
||||
bool is_out_io{};
|
||||
auto process = container.GetSession(sessions[fd])->process;
|
||||
ASSERT(process->GetPageTable()
|
||||
.LockForMapDeviceAddressSpace(&is_out_io, handle_description->address,
|
||||
handle_description->size,
|
||||
Kernel::KMemoryPermission::None, true, false)
|
||||
.IsSuccess());
|
||||
ASSERT(process->GetPageTable().LockForMapDeviceAddressSpace(&is_out_io, handle_description->address, handle_description->size, Kernel::KMemoryPermission::None, true, false).IsSuccess());
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -151,13 +145,13 @@ NvResult nvmap::IocGetId(IocGetIdParams& params) {
|
||||
return NvResult::BadValue;
|
||||
}
|
||||
|
||||
auto handle_description{file.GetHandle(params.handle)};
|
||||
if (!handle_description) {
|
||||
std::scoped_lock lock(file.handles_lock);
|
||||
auto o = file.GetHandle(params.handle);
|
||||
if (!o) {
|
||||
LOG_CRITICAL(Service_NVDRV, "Error!");
|
||||
return NvResult::AccessDenied; // This will always return EPERM irrespective of if the
|
||||
// handle exists or not
|
||||
return NvResult::AccessDenied; // This will always return EPERM irrespective of if the handle exists or not
|
||||
}
|
||||
|
||||
auto handle_description = &o->get();
|
||||
params.id = handle_description->id;
|
||||
return NvResult::Success;
|
||||
}
|
||||
@@ -174,12 +168,14 @@ NvResult nvmap::IocFromId(IocFromIdParams& params) {
|
||||
return NvResult::BadValue;
|
||||
}
|
||||
|
||||
auto handle_description{file.GetHandle(params.id)};
|
||||
if (!handle_description) {
|
||||
std::scoped_lock lock(file.handles_lock);
|
||||
auto o = file.GetHandle(params.id);
|
||||
if (!o) {
|
||||
LOG_CRITICAL(Service_NVDRV, "Unregistered handle!");
|
||||
return NvResult::BadValue;
|
||||
}
|
||||
|
||||
auto handle_description = &o->get();
|
||||
auto result = handle_description->Duplicate(false);
|
||||
if (result != NvResult::Success) {
|
||||
LOG_CRITICAL(Service_NVDRV, "Could not duplicate handle!");
|
||||
@@ -199,12 +195,14 @@ NvResult nvmap::IocParam(IocParamParams& params) {
|
||||
return NvResult::BadValue;
|
||||
}
|
||||
|
||||
auto handle_description{file.GetHandle(params.handle)};
|
||||
if (!handle_description) {
|
||||
std::scoped_lock lock(file.handles_lock);
|
||||
auto o = file.GetHandle(params.handle);
|
||||
if (!o) {
|
||||
LOG_CRITICAL(Service_NVDRV, "Not registered handle!");
|
||||
return NvResult::BadValue;
|
||||
}
|
||||
|
||||
auto handle_description = &o->get();
|
||||
switch (params.param) {
|
||||
case HandleParameterType::Size:
|
||||
params.result = static_cast<u32_le>(handle_description->orig_size);
|
||||
|
||||
@@ -629,7 +629,7 @@ Errno BSD::BindImpl(s32 fd, std::span<const u8> addr) {
|
||||
if (!IsFileDescriptorValid(fd)) {
|
||||
return Errno::BADF;
|
||||
}
|
||||
ASSERT(addr.size() == sizeof(SockAddrIn));
|
||||
ASSERT(addr.size() >= 16);
|
||||
auto addr_in = GetValue<SockAddrIn>(addr);
|
||||
|
||||
return Translate(file_descriptors[fd]->socket->Bind(Translate(addr_in)));
|
||||
@@ -640,7 +640,7 @@ Errno BSD::ConnectImpl(s32 fd, std::span<const u8> addr) {
|
||||
return Errno::BADF;
|
||||
}
|
||||
|
||||
UNIMPLEMENTED_IF(addr.size() != sizeof(SockAddrIn));
|
||||
ASSERT(addr.size() >= 16);
|
||||
auto addr_in = GetValue<SockAddrIn>(addr);
|
||||
|
||||
const Errno result = Translate(file_descriptors[fd]->socket->Connect(Translate(addr_in)));
|
||||
@@ -874,7 +874,7 @@ std::pair<s32, Errno> BSD::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& mess
|
||||
if (ret < 0) {
|
||||
addr.clear();
|
||||
} else {
|
||||
ASSERT(addr.size() == sizeof(SockAddrIn));
|
||||
ASSERT(addr.size() >= 16);
|
||||
const SockAddrIn result = Translate(addr_in);
|
||||
PutValue(addr, result);
|
||||
}
|
||||
@@ -899,7 +899,7 @@ std::pair<s32, Errno> BSD::SendToImpl(s32 fd, u32 flags, std::span<const u8> mes
|
||||
Network::SockAddrIn addr_in;
|
||||
Network::SockAddrIn* p_addr_in = nullptr;
|
||||
if (!addr.empty()) {
|
||||
ASSERT(addr.size() == sizeof(SockAddrIn));
|
||||
ASSERT(addr.size() >= 16);
|
||||
auto guest_addr_in = GetValue<SockAddrIn>(addr);
|
||||
addr_in = Translate(guest_addr_in);
|
||||
p_addr_in = &addr_in;
|
||||
|
||||
@@ -238,7 +238,7 @@ static std::vector<u8> SerializeAddrInfo(const std::vector<Network::AddrInfo>& v
|
||||
Append<u32_be>(data, static_cast<u32>(Translate(addrinfo.family))); // ai_family
|
||||
Append<u32_be>(data, static_cast<u32>(Translate(addrinfo.socket_type))); // ai_socktype
|
||||
Append<u32_be>(data, static_cast<u32>(Translate(addrinfo.protocol))); // ai_protocol
|
||||
Append<u32_be>(data, sizeof(SockAddrIn)); // ai_addrlen
|
||||
Append<u32_be>(data, 16); // ai_addrlen
|
||||
// ^ *not* sizeof(SerializedSockAddrIn), not that it matters since they're the same size
|
||||
|
||||
// ai_addr:
|
||||
|
||||
@@ -110,8 +110,9 @@ struct SockAddrIn {
|
||||
u8 family;
|
||||
u16 portno;
|
||||
std::array<u8, 4> ip;
|
||||
std::array<u8, 8> zeroes;
|
||||
std::array<u8, 248> zeroes;
|
||||
};
|
||||
static_assert(sizeof(SockAddrIn) == 0x100);
|
||||
|
||||
enum class PollEvents : u16 {
|
||||
// Using Pascal case because IN is a macro on Windows.
|
||||
|
||||
@@ -265,13 +265,9 @@ PollEvents Translate(Network::PollEvents flags) {
|
||||
}
|
||||
|
||||
Network::SockAddrIn Translate(SockAddrIn value) {
|
||||
if (value.len != 0 && value.len != sizeof(value) && value.len != 6) {
|
||||
LOG_WARNING(Service, "Unexpected SockAddrIn len={}, expected 0, {}, or 6",
|
||||
value.len, sizeof(value));
|
||||
}
|
||||
|
||||
// All lengths are valid, from [0 upto 256]
|
||||
return {
|
||||
.family = Translate(static_cast<Domain>(value.family)),
|
||||
.family = Translate(Domain(value.family)),
|
||||
.ip = value.ip,
|
||||
.portno = static_cast<u16>(value.portno >> 8 | value.portno << 8),
|
||||
};
|
||||
@@ -279,7 +275,7 @@ Network::SockAddrIn Translate(SockAddrIn value) {
|
||||
|
||||
SockAddrIn Translate(Network::SockAddrIn value) {
|
||||
return {
|
||||
.len = sizeof(SockAddrIn),
|
||||
.len = 16,
|
||||
.family = static_cast<u8>(Translate(value.family)),
|
||||
.portno = static_cast<u16>(value.portno >> 8 | value.portno << 8),
|
||||
.ip = value.ip,
|
||||
|
||||
@@ -104,51 +104,6 @@ void BufferCache<P>::TickFrame() {
|
||||
RunGarbageCollector();
|
||||
}
|
||||
++frame_tick;
|
||||
static constexpr u64 mirror_stats_log_interval = 300;
|
||||
if ((frame_tick % mirror_stats_log_interval) == 0) {
|
||||
const u64 upload_hit_copies = mirror_upload_hit_copies - mirror_upload_hit_copies_last;
|
||||
const u64 upload_miss_copies = mirror_upload_miss_copies - mirror_upload_miss_copies_last;
|
||||
const u64 upload_hit_bytes = mirror_upload_hit_bytes - mirror_upload_hit_bytes_last;
|
||||
const u64 upload_miss_bytes = mirror_upload_miss_bytes - mirror_upload_miss_bytes_last;
|
||||
const u64 download_hit_copies =
|
||||
mirror_download_hit_copies - mirror_download_hit_copies_last;
|
||||
const u64 download_miss_copies =
|
||||
mirror_download_miss_copies - mirror_download_miss_copies_last;
|
||||
const u64 download_hit_bytes = mirror_download_hit_bytes - mirror_download_hit_bytes_last;
|
||||
const u64 download_miss_bytes =
|
||||
mirror_download_miss_bytes - mirror_download_miss_bytes_last;
|
||||
|
||||
const u64 upload_total_copies = upload_hit_copies + upload_miss_copies;
|
||||
const u64 download_total_copies = download_hit_copies + download_miss_copies;
|
||||
if (upload_total_copies > 0 || download_total_copies > 0) {
|
||||
const double upload_hit_ratio = upload_total_copies > 0
|
||||
? (100.0 * static_cast<double>(upload_hit_copies) /
|
||||
static_cast<double>(upload_total_copies))
|
||||
: 0.0;
|
||||
const double download_hit_ratio =
|
||||
download_total_copies > 0
|
||||
? (100.0 * static_cast<double>(download_hit_copies) /
|
||||
static_cast<double>(download_total_copies))
|
||||
: 0.0;
|
||||
LOG_INFO(HW_GPU,
|
||||
"Buffer mirror counters (last {} frames): upload hit/miss copies = {}/{}, "
|
||||
"hit ratio = {:.2f}%, bytes hit/miss = {}/{}, download hit/miss copies = "
|
||||
"{}/{}, hit ratio = {:.2f}%, bytes hit/miss = {}/{}",
|
||||
mirror_stats_log_interval, upload_hit_copies, upload_miss_copies,
|
||||
upload_hit_ratio, upload_hit_bytes, upload_miss_bytes, download_hit_copies,
|
||||
download_miss_copies, download_hit_ratio, download_hit_bytes,
|
||||
download_miss_bytes);
|
||||
}
|
||||
|
||||
mirror_upload_hit_copies_last = mirror_upload_hit_copies;
|
||||
mirror_upload_miss_copies_last = mirror_upload_miss_copies;
|
||||
mirror_upload_hit_bytes_last = mirror_upload_hit_bytes;
|
||||
mirror_upload_miss_bytes_last = mirror_upload_miss_bytes;
|
||||
mirror_download_hit_copies_last = mirror_download_hit_copies;
|
||||
mirror_download_miss_copies_last = mirror_download_miss_copies;
|
||||
mirror_download_hit_bytes_last = mirror_download_hit_bytes;
|
||||
mirror_download_miss_bytes_last = mirror_download_miss_bytes;
|
||||
}
|
||||
delayed_destruction_ring.Tick();
|
||||
|
||||
for (auto& buffer : async_buffers_death_ring) {
|
||||
@@ -1609,21 +1564,6 @@ BufferId BufferCache<P>::CreateBuffer(DAddr device_addr, u32 wanted_size) {
|
||||
const u32 size = static_cast<u32>(overlap.end - overlap.begin);
|
||||
const BufferId new_buffer_id = slot_buffers.insert(runtime, overlap.begin, size);
|
||||
auto& new_buffer = slot_buffers[new_buffer_id];
|
||||
const u64 current_mapping_version = device_memory.GetMappingVersion();
|
||||
if (mirror_mapping_version != current_mapping_version) {
|
||||
buffer_mirrors.clear();
|
||||
mirror_mapping_version = current_mapping_version;
|
||||
}
|
||||
buffer_mirrors.erase(new_buffer.CpuAddr());
|
||||
if (auto mirror =
|
||||
device_memory.CreateMirrorMapping(new_buffer.CpuAddr(), new_buffer.SizeBytes());
|
||||
mirror) {
|
||||
buffer_mirrors.emplace(new_buffer.CpuAddr(), std::move(mirror));
|
||||
if (!mirror_creation_logged) [[unlikely]] {
|
||||
LOG_INFO(HW_GPU, "Buffer mirror mapping enabled (first successful mapping)");
|
||||
mirror_creation_logged = true;
|
||||
}
|
||||
}
|
||||
const size_t size_bytes = new_buffer.SizeBytes();
|
||||
runtime.ClearBuffer(new_buffer, 0, size_bytes, 0);
|
||||
new_buffer.MarkUsage(0, size_bytes);
|
||||
@@ -1717,52 +1657,15 @@ void BufferCache<P>::ImmediateUploadMemory([[maybe_unused]] Buffer& buffer,
|
||||
[[maybe_unused]] std::span<const BufferCopy> copies) {
|
||||
if constexpr (!USE_MEMORY_MAPS_FOR_UPLOADS) {
|
||||
std::span<u8> immediate_buffer;
|
||||
const auto resolve_mirror_pointer = [&]() -> const u8* {
|
||||
const u64 current_mapping_version = device_memory.GetMappingVersion();
|
||||
if (mirror_mapping_version != current_mapping_version) {
|
||||
buffer_mirrors.clear();
|
||||
mirror_mapping_version = current_mapping_version;
|
||||
}
|
||||
|
||||
auto mirror_it = buffer_mirrors.find(buffer.CpuAddr());
|
||||
if (mirror_it == buffer_mirrors.end()) {
|
||||
if (auto mirror =
|
||||
device_memory.CreateMirrorMapping(buffer.CpuAddr(), buffer.SizeBytes());
|
||||
mirror) {
|
||||
auto [it, inserted] =
|
||||
buffer_mirrors.emplace(buffer.CpuAddr(), std::move(mirror));
|
||||
mirror_it = it;
|
||||
if (inserted && !mirror_creation_logged) [[unlikely]] {
|
||||
LOG_INFO(HW_GPU, "Buffer mirror mapping enabled (first successful mapping)");
|
||||
mirror_creation_logged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return mirror_it != buffer_mirrors.end() ? mirror_it->second.Data() : nullptr;
|
||||
};
|
||||
const u8* const mirror_pointer = resolve_mirror_pointer();
|
||||
for (const BufferCopy& copy : copies) {
|
||||
std::span<const u8> upload_span;
|
||||
const DAddr device_addr = buffer.CpuAddr() + copy.dst_offset;
|
||||
if (mirror_pointer != nullptr) {
|
||||
mirror_upload_hit_copies++;
|
||||
mirror_upload_hit_bytes += copy.size;
|
||||
if (!mirror_upload_logged) [[unlikely]] {
|
||||
LOG_INFO(HW_GPU, "Buffer mirror fast path active for upload sync");
|
||||
mirror_upload_logged = true;
|
||||
}
|
||||
upload_span =
|
||||
std::span(mirror_pointer + static_cast<size_t>(copy.dst_offset), copy.size);
|
||||
} else if (IsRangeGranular(device_addr, copy.size)) {
|
||||
mirror_upload_miss_copies++;
|
||||
mirror_upload_miss_bytes += copy.size;
|
||||
if (IsRangeGranular(device_addr, copy.size)) {
|
||||
auto* const ptr = device_memory.GetPointer<u8>(device_addr);
|
||||
if (ptr != nullptr) {
|
||||
upload_span = std::span(ptr, copy.size);
|
||||
}
|
||||
} else {
|
||||
mirror_upload_miss_copies++;
|
||||
mirror_upload_miss_bytes += copy.size;
|
||||
if (immediate_buffer.empty()) {
|
||||
immediate_buffer = ImmediateBuffer(largest_copy);
|
||||
}
|
||||
@@ -1781,47 +1684,10 @@ void BufferCache<P>::MappedUploadMemory([[maybe_unused]] Buffer& buffer,
|
||||
if constexpr (USE_MEMORY_MAPS) {
|
||||
auto upload_staging = runtime.UploadStagingBuffer(total_size_bytes);
|
||||
const std::span<u8> staging_pointer = upload_staging.mapped_span;
|
||||
const auto resolve_mirror_pointer = [&]() -> const u8* {
|
||||
const u64 current_mapping_version = device_memory.GetMappingVersion();
|
||||
if (mirror_mapping_version != current_mapping_version) {
|
||||
buffer_mirrors.clear();
|
||||
mirror_mapping_version = current_mapping_version;
|
||||
}
|
||||
|
||||
auto mirror_it = buffer_mirrors.find(buffer.CpuAddr());
|
||||
if (mirror_it == buffer_mirrors.end()) {
|
||||
if (auto mirror =
|
||||
device_memory.CreateMirrorMapping(buffer.CpuAddr(), buffer.SizeBytes());
|
||||
mirror) {
|
||||
auto [it, inserted] =
|
||||
buffer_mirrors.emplace(buffer.CpuAddr(), std::move(mirror));
|
||||
mirror_it = it;
|
||||
if (inserted && !mirror_creation_logged) [[unlikely]] {
|
||||
LOG_INFO(HW_GPU, "Buffer mirror mapping enabled (first successful mapping)");
|
||||
mirror_creation_logged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return mirror_it != buffer_mirrors.end() ? mirror_it->second.Data() : nullptr;
|
||||
};
|
||||
const u8* const mirror_pointer = resolve_mirror_pointer();
|
||||
for (BufferCopy& copy : copies) {
|
||||
u8* const src_pointer = staging_pointer.data() + copy.src_offset;
|
||||
const DAddr device_addr = buffer.CpuAddr() + copy.dst_offset;
|
||||
if (mirror_pointer != nullptr) {
|
||||
mirror_upload_hit_copies++;
|
||||
mirror_upload_hit_bytes += copy.size;
|
||||
if (!mirror_upload_logged) [[unlikely]] {
|
||||
LOG_INFO(HW_GPU, "Buffer mirror fast path active for upload sync");
|
||||
mirror_upload_logged = true;
|
||||
}
|
||||
std::memcpy(src_pointer, mirror_pointer + static_cast<size_t>(copy.dst_offset),
|
||||
copy.size);
|
||||
} else {
|
||||
mirror_upload_miss_copies++;
|
||||
mirror_upload_miss_bytes += copy.size;
|
||||
device_memory.ReadBlockUnsafe(device_addr, src_pointer, copy.size);
|
||||
}
|
||||
device_memory.ReadBlockUnsafe(device_addr, src_pointer, copy.size);
|
||||
|
||||
// Apply the staging offset
|
||||
copy.src_offset += upload_staging.offset;
|
||||
@@ -1914,30 +1780,6 @@ void BufferCache<P>::DownloadBufferMemory(Buffer& buffer, DAddr device_addr, u64
|
||||
if constexpr (USE_MEMORY_MAPS) {
|
||||
auto download_staging = runtime.DownloadStagingBuffer(total_size_bytes);
|
||||
const u8* const mapped_memory = download_staging.mapped_span.data();
|
||||
const auto resolve_mirror_pointer = [&]() -> u8* {
|
||||
const u64 current_mapping_version = device_memory.GetMappingVersion();
|
||||
if (mirror_mapping_version != current_mapping_version) {
|
||||
buffer_mirrors.clear();
|
||||
mirror_mapping_version = current_mapping_version;
|
||||
}
|
||||
|
||||
auto mirror_it = buffer_mirrors.find(buffer.CpuAddr());
|
||||
if (mirror_it == buffer_mirrors.end()) {
|
||||
if (auto mirror =
|
||||
device_memory.CreateMirrorMapping(buffer.CpuAddr(), buffer.SizeBytes());
|
||||
mirror) {
|
||||
auto [it, inserted] =
|
||||
buffer_mirrors.emplace(buffer.CpuAddr(), std::move(mirror));
|
||||
mirror_it = it;
|
||||
if (inserted && !mirror_creation_logged) [[unlikely]] {
|
||||
LOG_INFO(HW_GPU, "Buffer mirror mapping enabled (first successful mapping)");
|
||||
mirror_creation_logged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return mirror_it != buffer_mirrors.end() ? mirror_it->second.Data() : nullptr;
|
||||
};
|
||||
u8* const mirror_pointer = resolve_mirror_pointer();
|
||||
const std::span<BufferCopy> copies_span(copies.data(), copies.data() + copies.size());
|
||||
for (BufferCopy& copy : copies) {
|
||||
// Modify copies to have the staging offset in mind
|
||||
@@ -1951,65 +1793,14 @@ void BufferCache<P>::DownloadBufferMemory(Buffer& buffer, DAddr device_addr, u64
|
||||
// Undo the modified offset
|
||||
const u64 dst_offset = copy.dst_offset - download_staging.offset;
|
||||
const u8* copy_mapped_memory = mapped_memory + dst_offset;
|
||||
if (mirror_pointer != nullptr) {
|
||||
mirror_download_hit_copies++;
|
||||
mirror_download_hit_bytes += copy.size;
|
||||
if (!mirror_download_logged) [[unlikely]] {
|
||||
LOG_INFO(HW_GPU, "Buffer mirror fast path active for download sync");
|
||||
mirror_download_logged = true;
|
||||
}
|
||||
std::memcpy(mirror_pointer + static_cast<size_t>(copy.src_offset),
|
||||
copy_mapped_memory, copy.size);
|
||||
} else {
|
||||
mirror_download_miss_copies++;
|
||||
mirror_download_miss_bytes += copy.size;
|
||||
device_memory.WriteBlockUnsafe(copy_device_addr, copy_mapped_memory, copy.size);
|
||||
}
|
||||
device_memory.WriteBlockUnsafe(copy_device_addr, copy_mapped_memory, copy.size);
|
||||
}
|
||||
} else {
|
||||
const std::span<u8> immediate_buffer = ImmediateBuffer(largest_copy);
|
||||
const auto resolve_mirror_pointer = [&]() -> u8* {
|
||||
const u64 current_mapping_version = device_memory.GetMappingVersion();
|
||||
if (mirror_mapping_version != current_mapping_version) {
|
||||
buffer_mirrors.clear();
|
||||
mirror_mapping_version = current_mapping_version;
|
||||
}
|
||||
|
||||
auto mirror_it = buffer_mirrors.find(buffer.CpuAddr());
|
||||
if (mirror_it == buffer_mirrors.end()) {
|
||||
if (auto mirror =
|
||||
device_memory.CreateMirrorMapping(buffer.CpuAddr(), buffer.SizeBytes());
|
||||
mirror) {
|
||||
auto [it, inserted] =
|
||||
buffer_mirrors.emplace(buffer.CpuAddr(), std::move(mirror));
|
||||
mirror_it = it;
|
||||
if (inserted && !mirror_creation_logged) [[unlikely]] {
|
||||
LOG_INFO(HW_GPU, "Buffer mirror mapping enabled (first successful mapping)");
|
||||
mirror_creation_logged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return mirror_it != buffer_mirrors.end() ? mirror_it->second.Data() : nullptr;
|
||||
};
|
||||
u8* const mirror_pointer = resolve_mirror_pointer();
|
||||
for (const BufferCopy& copy : copies) {
|
||||
buffer.ImmediateDownload(copy.src_offset, immediate_buffer.subspan(0, copy.size));
|
||||
const DAddr copy_device_addr = buffer.CpuAddr() + copy.src_offset;
|
||||
if (mirror_pointer != nullptr) {
|
||||
mirror_download_hit_copies++;
|
||||
mirror_download_hit_bytes += copy.size;
|
||||
if (!mirror_download_logged) [[unlikely]] {
|
||||
LOG_INFO(HW_GPU, "Buffer mirror fast path active for download sync");
|
||||
mirror_download_logged = true;
|
||||
}
|
||||
std::memcpy(mirror_pointer + static_cast<size_t>(copy.src_offset),
|
||||
immediate_buffer.data(), copy.size);
|
||||
} else {
|
||||
mirror_download_miss_copies++;
|
||||
mirror_download_miss_bytes += copy.size;
|
||||
device_memory.WriteBlockUnsafe(copy_device_addr, immediate_buffer.data(),
|
||||
copy.size);
|
||||
}
|
||||
device_memory.WriteBlockUnsafe(copy_device_addr, immediate_buffer.data(), copy.size);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2050,10 +1841,6 @@ void BufferCache<P>::DeleteBuffer(BufferId buffer_id, bool do_not_mark) {
|
||||
if (!do_not_mark) {
|
||||
Buffer& buffer = slot_buffers[buffer_id];
|
||||
memory_tracker.MarkRegionAsCpuModified(buffer.CpuAddr(), buffer.SizeBytes());
|
||||
buffer_mirrors.erase(buffer.CpuAddr());
|
||||
} else {
|
||||
const Buffer& buffer = slot_buffers[buffer_id];
|
||||
buffer_mirrors.erase(buffer.CpuAddr());
|
||||
}
|
||||
|
||||
Unregister(buffer_id);
|
||||
|
||||
@@ -473,8 +473,6 @@ private:
|
||||
Tegra::MaxwellDeviceMemoryManager& device_memory;
|
||||
|
||||
Common::SlotVector<Buffer> slot_buffers;
|
||||
ankerl::unordered_dense::map<DAddr, Tegra::MaxwellDeviceMemoryManager::MirrorMapping>
|
||||
buffer_mirrors;
|
||||
#ifdef YUZU_LEGACY
|
||||
static constexpr size_t TICKS_TO_DESTROY = 6;
|
||||
#else
|
||||
@@ -524,26 +522,6 @@ private:
|
||||
|
||||
std::array<BufferId, ((1ULL << 34) >> CACHING_PAGEBITS)> page_table;
|
||||
Common::ScratchBuffer<u8> tmp_buffer;
|
||||
bool mirror_creation_logged = false;
|
||||
bool mirror_upload_logged = false;
|
||||
bool mirror_download_logged = false;
|
||||
u64 mirror_mapping_version = 0;
|
||||
u64 mirror_upload_hit_copies = 0;
|
||||
u64 mirror_upload_miss_copies = 0;
|
||||
u64 mirror_upload_hit_bytes = 0;
|
||||
u64 mirror_upload_miss_bytes = 0;
|
||||
u64 mirror_download_hit_copies = 0;
|
||||
u64 mirror_download_miss_copies = 0;
|
||||
u64 mirror_download_hit_bytes = 0;
|
||||
u64 mirror_download_miss_bytes = 0;
|
||||
u64 mirror_upload_hit_copies_last = 0;
|
||||
u64 mirror_upload_miss_copies_last = 0;
|
||||
u64 mirror_upload_hit_bytes_last = 0;
|
||||
u64 mirror_upload_miss_bytes_last = 0;
|
||||
u64 mirror_download_hit_copies_last = 0;
|
||||
u64 mirror_download_miss_copies_last = 0;
|
||||
u64 mirror_download_hit_bytes_last = 0;
|
||||
u64 mirror_download_miss_bytes_last = 0;
|
||||
};
|
||||
|
||||
} // namespace VideoCommon
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#undef PIXEL_FORMAT_LIST
|
||||
|
||||
#include <climits>
|
||||
#include <utility>
|
||||
#include "common/assert.h"
|
||||
@@ -128,7 +130,7 @@ namespace VideoCore::Surface {
|
||||
PIXEL_FORMAT_ELEM(S8_UINT_D24_UNORM, 1, 1, 32) \
|
||||
PIXEL_FORMAT_ELEM(D32_FLOAT_S8_UINT, 1, 1, 64)
|
||||
|
||||
enum class PixelFormat {
|
||||
enum class PixelFormat : u32 {
|
||||
#define PIXEL_FORMAT_ELEM(name, ...) name,
|
||||
PIXEL_FORMAT_LIST
|
||||
#undef PIXEL_FORMAT_ELEM
|
||||
@@ -192,6 +194,13 @@ constexpr u32 BytesPerBlock(PixelFormat pixel_format) {
|
||||
return BitsPerBlock(pixel_format) / CHAR_BIT;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include "video_core/gpu.h"
|
||||
#include "video_core/textures/texture.h"
|
||||
|
||||
namespace VideoCore::Surface {
|
||||
|
||||
SurfaceTarget SurfaceTargetFromTextureType(Tegra::Texture::TextureType texture_type);
|
||||
bool SurfaceTargetIsLayered(SurfaceTarget target);
|
||||
bool SurfaceTargetIsArray(SurfaceTarget target);
|
||||
|
||||
Reference in New Issue
Block a user