Compare commits

...

5 commits

Author SHA1 Message Date
yuzubot 757375d0ac "Merge Tagged PR 12173" 2024-01-20 13:02:58 +00:00
yuzubot 1dca3ee459 "Merge Tagged PR 12579" 2024-01-20 13:02:58 +00:00
yuzubot 704fcefdc5 "Merge Tagged PR 12660" 2024-01-20 13:02:57 +00:00
yuzubot 9116c7479c "Merge Tagged PR 12688" 2024-01-20 13:02:56 +00:00
yuzubot 8ae078ab06 "Merge Tagged PR 12701" 2024-01-20 13:02:56 +00:00
160 changed files with 3694 additions and 1925 deletions

View file

@ -8,6 +8,7 @@
#include "audio_core/sink/sink_stream.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/guest_memory.h"
#include "core/memory.h"
#include "core/hle/kernel/k_process.h"

View file

@ -9,6 +9,7 @@
#include "common/fixed_point.h"
#include "common/logging/log.h"
#include "common/scratch_buffer.h"
#include "core/guest_memory.h"
#include "core/memory.h"
namespace AudioCore::Renderer {

View file

@ -45,6 +45,7 @@ using f32 = float; ///< 32-bit floating point
using f64 = double; ///< 64-bit floating point
using VAddr = u64; ///< Represents a pointer in the userspace virtual address space.
using DAddr = u64; ///< Represents a pointer in the device specific virtual address space.
using PAddr = u64; ///< Represents a pointer in the ARM11 physical address space.
using GPUVAddr = u64; ///< Represents a pointer in the GPU virtual address space.

View file

@ -37,6 +37,8 @@ add_library(core STATIC
debugger/gdbstub_arch.h
debugger/gdbstub.cpp
debugger/gdbstub.h
device_memory_manager.h
device_memory_manager.inc
device_memory.cpp
device_memory.h
file_sys/fssystem/fs_i_storage.h
@ -609,6 +611,8 @@ add_library(core STATIC
hle/service/ns/pdm_qry.h
hle/service/nvdrv/core/container.cpp
hle/service/nvdrv/core/container.h
hle/service/nvdrv/core/heap_mapper.cpp
hle/service/nvdrv/core/heap_mapper.h
hle/service/nvdrv/core/nvmap.cpp
hle/service/nvdrv/core/nvmap.h
hle/service/nvdrv/core/syncpoint_manager.cpp

View file

@ -28,6 +28,7 @@
#include "core/file_sys/savedata_factory.h"
#include "core/file_sys/vfs_concat.h"
#include "core/file_sys/vfs_real.h"
#include "core/gpu_dirty_memory_manager.h"
#include "core/hle/kernel/k_memory_manager.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_resource_limit.h"
@ -565,6 +566,9 @@ struct System::Impl {
std::array<u64, Core::Hardware::NUM_CPU_CORES> dynarmic_ticks{};
std::array<MicroProfileToken, Core::Hardware::NUM_CPU_CORES> microprofile_cpu{};
std::array<Core::GPUDirtyMemoryManager, Core::Hardware::NUM_CPU_CORES>
gpu_dirty_memory_managers;
std::deque<std::vector<u8>> user_channel;
};
@ -651,8 +655,14 @@ size_t System::GetCurrentHostThreadID() const {
return impl->kernel.GetCurrentHostThreadID();
}
void System::GatherGPUDirtyMemory(std::function<void(VAddr, size_t)>& callback) {
return this->ApplicationProcess()->GatherGPUDirtyMemory(callback);
std::span<GPUDirtyMemoryManager> System::GetGPUDirtyMemoryManager() {
return impl->gpu_dirty_memory_managers;
}
void System::GatherGPUDirtyMemory(std::function<void(PAddr, size_t)>& callback) {
for (auto& manager : impl->gpu_dirty_memory_managers) {
manager.Gather(callback);
}
}
PerfStatsResults System::GetAndResetPerfStats() {

View file

@ -8,6 +8,7 @@
#include <functional>
#include <memory>
#include <mutex>
#include <span>
#include <string>
#include <vector>
@ -116,6 +117,7 @@ class CpuManager;
class Debugger;
class DeviceMemory;
class ExclusiveMonitor;
class GPUDirtyMemoryManager;
class PerfStats;
class Reporter;
class SpeedLimiter;
@ -224,7 +226,9 @@ public:
/// Prepare the core emulation for a reschedule
void PrepareReschedule(u32 core_index);
void GatherGPUDirtyMemory(std::function<void(VAddr, size_t)>& callback);
std::span<GPUDirtyMemoryManager> GetGPUDirtyMemoryManager();
void GatherGPUDirtyMemory(std::function<void(PAddr, size_t)>& callback);
[[nodiscard]] size_t GetCurrentHostThreadID() const;

View file

@ -31,6 +31,12 @@ public:
DramMemoryMap::Base;
}
template <typename T>
PAddr GetRawPhysicalAddr(const T* ptr) const {
return static_cast<PAddr>(reinterpret_cast<uintptr_t>(ptr) -
reinterpret_cast<uintptr_t>(buffer.BackingBasePointer()));
}
template <typename T>
T* GetPointer(Common::PhysicalAddress addr) {
return reinterpret_cast<T*>(buffer.BackingBasePointer() +
@ -43,6 +49,16 @@ public:
(GetInteger(addr) - DramMemoryMap::Base));
}
template <typename T>
T* GetPointerFromRaw(PAddr addr) {
return reinterpret_cast<T*>(buffer.BackingBasePointer() + addr);
}
template <typename T>
const T* GetPointerFromRaw(PAddr addr) const {
return reinterpret_cast<T*>(buffer.BackingBasePointer() + addr);
}
Common::HostMemory buffer;
};

View file

@ -0,0 +1,211 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <atomic>
#include <deque>
#include <memory>
#include <mutex>
#include "common/common_types.h"
#include "common/scratch_buffer.h"
#include "common/virtual_buffer.h"
namespace Core {
constexpr size_t DEVICE_PAGEBITS = 12ULL;
constexpr size_t DEVICE_PAGESIZE = 1ULL << DEVICE_PAGEBITS;
constexpr size_t DEVICE_PAGEMASK = DEVICE_PAGESIZE - 1ULL;
class DeviceMemory;
namespace Memory {
class Memory;
}
template <typename DTraits>
struct DeviceMemoryManagerAllocator;
struct Asid {
size_t id;
};
template <typename Traits>
class DeviceMemoryManager {
using DeviceInterface = typename Traits::DeviceInterface;
using DeviceMethods = typename Traits::DeviceMethods;
public:
DeviceMemoryManager(const DeviceMemory& device_memory);
~DeviceMemoryManager();
void BindInterface(DeviceInterface* device_inter);
DAddr Allocate(size_t size);
void AllocateFixed(DAddr start, size_t size);
void Free(DAddr start, size_t size);
void Map(DAddr address, VAddr virtual_address, size_t size, Asid asid, bool track = false);
void Unmap(DAddr address, size_t size);
void TrackContinuityImpl(DAddr address, VAddr virtual_address, size_t size, Asid asid);
void TrackContinuity(DAddr address, VAddr virtual_address, size_t size, Asid asid) {
std::scoped_lock lk(mapping_guard);
TrackContinuityImpl(address, virtual_address, size, asid);
}
// Write / Read
template <typename T>
T* GetPointer(DAddr address);
template <typename T>
const T* GetPointer(DAddr address) const;
template <typename Func>
void ApplyOpOnPAddr(PAddr address, Common::ScratchBuffer<u32>& buffer, Func&& operation) {
DAddr subbits = static_cast<DAddr>(address & page_mask);
const u32 base = compressed_device_addr[(address >> page_bits)];
if ((base >> MULTI_FLAG_BITS) == 0) [[likely]] {
const DAddr d_address = (static_cast<DAddr>(base) << page_bits) + subbits;
operation(d_address);
return;
}
InnerGatherDeviceAddresses(buffer, address);
for (u32 value : buffer) {
operation((static_cast<DAddr>(value) << page_bits) + subbits);
}
}
template <typename Func>
void ApplyOpOnPointer(const u8* p, Common::ScratchBuffer<u32>& buffer, Func&& operation) {
PAddr address = GetRawPhysicalAddr<u8>(p);
ApplyOpOnPAddr(address, buffer, operation);
}
PAddr GetPhysicalRawAddressFromDAddr(DAddr address) const {
PAddr subbits = static_cast<PAddr>(address & page_mask);
auto paddr = compressed_physical_ptr[(address >> page_bits)];
if (paddr == 0) {
return 0;
}
return (static_cast<PAddr>(paddr - 1) << page_bits) + subbits;
}
template <typename T>
void Write(DAddr address, T value);
template <typename T>
T Read(DAddr address) const;
u8* GetSpan(const DAddr src_addr, const std::size_t size);
const u8* GetSpan(const DAddr src_addr, const std::size_t size) const;
void ReadBlock(DAddr address, void* dest_pointer, size_t size);
void ReadBlockUnsafe(DAddr address, void* dest_pointer, size_t size);
void WriteBlock(DAddr address, const void* src_pointer, size_t size);
void WriteBlockUnsafe(DAddr address, const void* src_pointer, size_t size);
Asid RegisterProcess(Memory::Memory* memory);
void UnregisterProcess(Asid id);
void UpdatePagesCachedCount(DAddr addr, size_t size, s32 delta);
static constexpr size_t AS_BITS = Traits::device_virtual_bits;
private:
static constexpr size_t device_virtual_bits = Traits::device_virtual_bits;
static constexpr size_t device_as_size = 1ULL << device_virtual_bits;
static constexpr size_t physical_min_bits = 32;
static constexpr size_t physical_max_bits = 33;
static constexpr size_t page_bits = 12;
static constexpr size_t page_size = 1ULL << page_bits;
static constexpr size_t page_mask = page_size - 1ULL;
static constexpr u32 physical_address_base = 1U << page_bits;
static constexpr u32 MULTI_FLAG_BITS = 31;
static constexpr u32 MULTI_FLAG = 1U << MULTI_FLAG_BITS;
static constexpr u32 MULTI_MASK = ~MULTI_FLAG;
template <typename T>
T* GetPointerFromRaw(PAddr addr) {
return reinterpret_cast<T*>(physical_base + addr);
}
template <typename T>
const T* GetPointerFromRaw(PAddr addr) const {
return reinterpret_cast<T*>(physical_base + addr);
}
template <typename T>
PAddr GetRawPhysicalAddr(const T* ptr) const {
return static_cast<PAddr>(reinterpret_cast<uintptr_t>(ptr) - physical_base);
}
void WalkBlock(const DAddr addr, const std::size_t size, auto on_unmapped, auto on_memory,
auto increment);
void InnerGatherDeviceAddresses(Common::ScratchBuffer<u32>& buffer, PAddr address);
std::unique_ptr<DeviceMemoryManagerAllocator<Traits>> impl;
const uintptr_t physical_base;
DeviceInterface* device_inter;
Common::VirtualBuffer<u32> compressed_physical_ptr;
Common::VirtualBuffer<u32> compressed_device_addr;
Common::VirtualBuffer<u32> continuity_tracker;
// Process memory interfaces
std::deque<size_t> id_pool;
std::deque<Memory::Memory*> registered_processes;
// Memory protection management
static constexpr size_t guest_max_as_bits = 39;
static constexpr size_t guest_as_size = 1ULL << guest_max_as_bits;
static constexpr size_t guest_mask = guest_as_size - 1ULL;
static constexpr size_t asid_start_bit = guest_max_as_bits;
std::pair<Asid, VAddr> ExtractCPUBacking(size_t page_index) {
auto content = cpu_backing_address[page_index];
const VAddr address = content & guest_mask;
const Asid asid{static_cast<size_t>(content >> asid_start_bit)};
return std::make_pair(asid, address);
}
void InsertCPUBacking(size_t page_index, VAddr address, Asid asid) {
cpu_backing_address[page_index] = address | (asid.id << asid_start_bit);
}
Common::VirtualBuffer<VAddr> cpu_backing_address;
static constexpr size_t subentries = 8 / sizeof(u8);
static constexpr size_t subentries_mask = subentries - 1;
class CounterEntry final {
public:
CounterEntry() = default;
std::atomic_uint8_t& Count(std::size_t page) {
return values[page & subentries_mask];
}
const std::atomic_uint8_t& Count(std::size_t page) const {
return values[page & subentries_mask];
}
private:
std::array<std::atomic_uint8_t, subentries> values{};
};
static_assert(sizeof(CounterEntry) == subentries * sizeof(u8),
"CounterEntry should be 8 bytes!");
static constexpr size_t num_counter_entries =
(1ULL << (device_virtual_bits - page_bits)) / subentries;
using CachedPages = std::array<CounterEntry, num_counter_entries>;
std::unique_ptr<CachedPages> cached_pages;
std::mutex counter_guard;
std::mutex mapping_guard;
};
} // namespace Core

View file

@ -0,0 +1,582 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <atomic>
#include <limits>
#include <memory>
#include <type_traits>
#include "common/address_space.h"
#include "common/address_space.inc"
#include "common/alignment.h"
#include "common/assert.h"
#include "common/div_ceil.h"
#include "common/scope_exit.h"
#include "common/settings.h"
#include "core/device_memory.h"
#include "core/device_memory_manager.h"
#include "core/memory.h"
namespace Core {
namespace {
class MultiAddressContainer {
public:
MultiAddressContainer() = default;
~MultiAddressContainer() = default;
void GatherValues(u32 start_entry, Common::ScratchBuffer<u32>& buffer) {
buffer.resize(8);
buffer.resize(0);
size_t index = 0;
const auto add_value = [&](u32 value) {
buffer[index] = value;
index++;
buffer.resize(index);
};
u32 iter_entry = start_entry;
Entry* current = &storage[iter_entry - 1];
add_value(current->value);
while (current->next_entry != 0) {
iter_entry = current->next_entry;
current = &storage[iter_entry - 1];
add_value(current->value);
}
}
u32 Register(u32 value) {
return RegisterImplementation(value);
}
void Register(u32 value, u32 start_entry) {
auto entry_id = RegisterImplementation(value);
u32 iter_entry = start_entry;
Entry* current = &storage[iter_entry - 1];
while (current->next_entry != 0) {
iter_entry = current->next_entry;
current = &storage[iter_entry - 1];
}
current->next_entry = entry_id;
}
std::pair<bool, u32> Unregister(u32 value, u32 start_entry) {
u32 iter_entry = start_entry;
Entry* previous{};
Entry* current = &storage[iter_entry - 1];
Entry* next{};
bool more_than_one_remaining = false;
u32 result_start{start_entry};
size_t count = 0;
while (current->value != value) {
count++;
previous = current;
iter_entry = current->next_entry;
current = &storage[iter_entry - 1];
}
// Find next
u32 next_entry = current->next_entry;
if (next_entry != 0) {
next = &storage[next_entry - 1];
more_than_one_remaining = next->next_entry != 0 || previous != nullptr;
}
if (previous) {
previous->next_entry = next_entry;
} else {
result_start = next_entry;
}
free_entries.emplace_back(iter_entry);
return std::make_pair(more_than_one_remaining || count > 1, result_start);
}
u32 ReleaseEntry(u32 start_entry) {
Entry* current = &storage[start_entry - 1];
free_entries.emplace_back(start_entry);
return current->value;
}
private:
u32 RegisterImplementation(u32 value) {
auto entry_id = GetNewEntry();
auto& entry = storage[entry_id - 1];
entry.next_entry = 0;
entry.value = value;
return entry_id;
}
u32 GetNewEntry() {
if (!free_entries.empty()) {
u32 result = free_entries.front();
free_entries.pop_front();
return result;
}
storage.emplace_back();
u32 new_entry = static_cast<u32>(storage.size());
return new_entry;
}
struct Entry {
u32 next_entry{};
u32 value{};
};
std::deque<Entry> storage;
std::deque<u32> free_entries;
};
struct EmptyAllocator {
EmptyAllocator([[maybe_unused]] DAddr address) {}
};
} // namespace
template <typename DTraits>
struct DeviceMemoryManagerAllocator {
static constexpr size_t device_virtual_bits = DTraits::device_virtual_bits;
static constexpr DAddr first_address = 1ULL << Memory::YUZU_PAGEBITS;
static constexpr DAddr max_device_area = 1ULL << device_virtual_bits;
DeviceMemoryManagerAllocator() : main_allocator(first_address) {}
Common::FlatAllocator<DAddr, 0, device_virtual_bits> main_allocator;
MultiAddressContainer multi_dev_address;
/// Returns true when vaddr -> vaddr+size is fully contained in the buffer
template <bool pin_area>
[[nodiscard]] bool IsInBounds(VAddr addr, u64 size) const noexcept {
return addr >= 0 && addr + size <= max_device_area;
}
DAddr Allocate(size_t size) {
return main_allocator.Allocate(size);
}
void AllocateFixed(DAddr b_address, size_t b_size) {
main_allocator.AllocateFixed(b_address, b_size);
}
void Free(DAddr b_address, size_t b_size) {
main_allocator.Free(b_address, b_size);
}
};
template <typename Traits>
DeviceMemoryManager<Traits>::DeviceMemoryManager(const DeviceMemory& device_memory_)
: physical_base{reinterpret_cast<const uintptr_t>(device_memory_.buffer.BackingBasePointer())},
device_inter{nullptr}, compressed_physical_ptr(device_as_size >> Memory::YUZU_PAGEBITS),
compressed_device_addr(1ULL << ((Settings::values.memory_layout_mode.GetValue() ==
Settings::MemoryLayout::Memory_4Gb
? physical_min_bits
: physical_max_bits) -
Memory::YUZU_PAGEBITS)),
continuity_tracker(device_as_size >> Memory::YUZU_PAGEBITS),
cpu_backing_address(device_as_size >> Memory::YUZU_PAGEBITS) {
impl = std::make_unique<DeviceMemoryManagerAllocator<Traits>>();
cached_pages = std::make_unique<CachedPages>();
const size_t total_virtual = device_as_size >> Memory::YUZU_PAGEBITS;
for (size_t i = 0; i < total_virtual; i++) {
compressed_physical_ptr[i] = 0;
continuity_tracker[i] = 1;
cpu_backing_address[i] = 0;
}
const size_t total_phys = 1ULL << ((Settings::values.memory_layout_mode.GetValue() ==
Settings::MemoryLayout::Memory_4Gb
? physical_min_bits
: physical_max_bits) -
Memory::YUZU_PAGEBITS);
for (size_t i = 0; i < total_phys; i++) {
compressed_device_addr[i] = 0;
}
}
template <typename Traits>
DeviceMemoryManager<Traits>::~DeviceMemoryManager() = default;
template <typename Traits>
void DeviceMemoryManager<Traits>::BindInterface(DeviceInterface* device_inter_) {
device_inter = device_inter_;
}
template <typename Traits>
DAddr DeviceMemoryManager<Traits>::Allocate(size_t size) {
return impl->Allocate(size);
}
template <typename Traits>
void DeviceMemoryManager<Traits>::AllocateFixed(DAddr start, size_t size) {
return impl->AllocateFixed(start, size);
}
template <typename Traits>
void DeviceMemoryManager<Traits>::Free(DAddr start, size_t size) {
impl->Free(start, size);
}
template <typename Traits>
void DeviceMemoryManager<Traits>::Map(DAddr address, VAddr virtual_address, size_t size,
Asid asid, bool track) {
Core::Memory::Memory* process_memory = registered_processes[asid.id];
size_t start_page_d = address >> Memory::YUZU_PAGEBITS;
size_t num_pages = Common::AlignUp(size, Memory::YUZU_PAGESIZE) >> Memory::YUZU_PAGEBITS;
std::scoped_lock lk(mapping_guard);
for (size_t i = 0; i < num_pages; i++) {
const VAddr new_vaddress = virtual_address + i * Memory::YUZU_PAGESIZE;
auto* ptr = process_memory->GetPointerSilent(Common::ProcessAddress(new_vaddress));
if (ptr == nullptr) [[unlikely]] {
compressed_physical_ptr[start_page_d + i] = 0;
continue;
}
auto phys_addr = static_cast<u32>(GetRawPhysicalAddr(ptr) >> Memory::YUZU_PAGEBITS) + 1U;
compressed_physical_ptr[start_page_d + i] = phys_addr;
InsertCPUBacking(start_page_d + i, new_vaddress, asid);
const u32 base_dev = compressed_device_addr[phys_addr - 1U];
const u32 new_dev = static_cast<u32>(start_page_d + i);
if (base_dev == 0) [[likely]] {
compressed_device_addr[phys_addr - 1U] = new_dev;
continue;
}
u32 start_id = base_dev & MULTI_MASK;
if ((base_dev >> MULTI_FLAG_BITS) == 0) {
start_id = impl->multi_dev_address.Register(base_dev);
compressed_device_addr[phys_addr - 1U] = MULTI_FLAG | start_id;
}
impl->multi_dev_address.Register(new_dev, start_id);
}
if (track) {
TrackContinuityImpl(address, virtual_address, size, asid);
}
}
template <typename Traits>
void DeviceMemoryManager<Traits>::Unmap(DAddr address, size_t size) {
size_t start_page_d = address >> Memory::YUZU_PAGEBITS;
size_t num_pages = Common::AlignUp(size, Memory::YUZU_PAGESIZE) >> Memory::YUZU_PAGEBITS;
device_inter->InvalidateRegion(address, size);
std::scoped_lock lk(mapping_guard);
for (size_t i = 0; i < num_pages; i++) {
auto phys_addr = compressed_physical_ptr[start_page_d + i];
compressed_physical_ptr[start_page_d + i] = 0;
cpu_backing_address[start_page_d + i] = 0;
if (phys_addr != 0) [[likely]] {
const u32 base_dev = compressed_device_addr[phys_addr - 1U];
if ((base_dev >> MULTI_FLAG_BITS) == 0) [[likely]] {
compressed_device_addr[phys_addr - 1] = 0;
continue;
}
const auto [more_entries, new_start] = impl->multi_dev_address.Unregister(
static_cast<u32>(start_page_d + i), base_dev & MULTI_MASK);
if (!more_entries) {
compressed_device_addr[phys_addr - 1] =
impl->multi_dev_address.ReleaseEntry(new_start);
continue;
}
compressed_device_addr[phys_addr - 1] = new_start | MULTI_FLAG;
}
}
}
template <typename Traits>
void DeviceMemoryManager<Traits>::TrackContinuityImpl(DAddr address, VAddr virtual_address,
size_t size, Asid asid) {
Core::Memory::Memory* process_memory = registered_processes[asid.id];
size_t start_page_d = address >> Memory::YUZU_PAGEBITS;
size_t num_pages = Common::AlignUp(size, Memory::YUZU_PAGESIZE) >> Memory::YUZU_PAGEBITS;
uintptr_t last_ptr = 0;
size_t page_count = 1;
for (size_t i = num_pages; i > 0; i--) {
size_t index = i - 1;
const VAddr new_vaddress = virtual_address + index * Memory::YUZU_PAGESIZE;
const uintptr_t new_ptr = reinterpret_cast<uintptr_t>(
process_memory->GetPointerSilent(Common::ProcessAddress(new_vaddress)));
if (new_ptr + page_size == last_ptr) {
page_count++;
} else {
page_count = 1;
}
last_ptr = new_ptr;
continuity_tracker[start_page_d + index] = static_cast<u32>(page_count);
}
}
template <typename Traits>
u8* DeviceMemoryManager<Traits>::GetSpan(const DAddr src_addr, const std::size_t size) {
size_t page_index = src_addr >> page_bits;
size_t subbits = src_addr & page_mask;
if ((static_cast<size_t>(continuity_tracker[page_index]) << page_bits) >= size + subbits) {
return GetPointer<u8>(src_addr);
}
return nullptr;
}
template <typename Traits>
const u8* DeviceMemoryManager<Traits>::GetSpan(const DAddr src_addr, const std::size_t size) const {
size_t page_index = src_addr >> page_bits;
size_t subbits = src_addr & page_mask;
if ((static_cast<size_t>(continuity_tracker[page_index]) << page_bits) >= size + subbits) {
return GetPointer<u8>(src_addr);
}
return nullptr;
}
template <typename Traits>
void DeviceMemoryManager<Traits>::InnerGatherDeviceAddresses(Common::ScratchBuffer<u32>& buffer,
PAddr address) {
size_t phys_addr = address >> page_bits;
std::scoped_lock lk(mapping_guard);
u32 backing = compressed_device_addr[phys_addr];
if ((backing >> MULTI_FLAG_BITS) != 0) {
impl->multi_dev_address.GatherValues(backing & MULTI_MASK, buffer);
return;
}
buffer.resize(1);
buffer[0] = backing;
}
template <typename Traits>
template <typename T>
T* DeviceMemoryManager<Traits>::GetPointer(DAddr address) {
const size_t index = address >> Memory::YUZU_PAGEBITS;
const size_t offset = address & Memory::YUZU_PAGEMASK;
auto phys_addr = compressed_physical_ptr[index];
if (phys_addr == 0) [[unlikely]] {
return nullptr;
}
return GetPointerFromRaw<T>((static_cast<PAddr>(phys_addr - 1) << Memory::YUZU_PAGEBITS) +
offset);
}
template <typename Traits>
template <typename T>
const T* DeviceMemoryManager<Traits>::GetPointer(DAddr address) const {
const size_t index = address >> Memory::YUZU_PAGEBITS;
const size_t offset = address & Memory::YUZU_PAGEMASK;
auto phys_addr = compressed_physical_ptr[index];
if (phys_addr == 0) [[unlikely]] {
return nullptr;
}
return GetPointerFromRaw<T>((static_cast<PAddr>(phys_addr - 1) << Memory::YUZU_PAGEBITS) +
offset);
}
template <typename Traits>
template <typename T>
void DeviceMemoryManager<Traits>::Write(DAddr address, T value) {
T* ptr = GetPointer<T>(address);
if (!ptr) [[unlikely]] {
return;
}
std::memcpy(ptr, &value, sizeof(T));
}
template <typename Traits>
template <typename T>
T DeviceMemoryManager<Traits>::Read(DAddr address) const {
const T* ptr = GetPointer<T>(address);
T result{};
if (!ptr) [[unlikely]] {
return result;
}
std::memcpy(&result, ptr, sizeof(T));
return result;
}
template <typename Traits>
void DeviceMemoryManager<Traits>::WalkBlock(DAddr addr, std::size_t size, auto on_unmapped,
auto on_memory, auto increment) {
std::size_t remaining_size = size;
std::size_t page_index = addr >> Memory::YUZU_PAGEBITS;
std::size_t page_offset = addr & Memory::YUZU_PAGEMASK;
while (remaining_size) {
const size_t next_pages = static_cast<std::size_t>(continuity_tracker[page_index]);
const std::size_t copy_amount =
std::min((next_pages << Memory::YUZU_PAGEBITS) - page_offset, remaining_size);
const auto current_vaddr =
static_cast<u64>((page_index << Memory::YUZU_PAGEBITS) + page_offset);
SCOPE_EXIT({
page_index += next_pages;
page_offset = 0;
increment(copy_amount);
remaining_size -= copy_amount;
});
auto phys_addr = compressed_physical_ptr[page_index];
if (phys_addr == 0) {
on_unmapped(copy_amount, current_vaddr);
continue;
}
auto* mem_ptr = GetPointerFromRaw<u8>(
(static_cast<PAddr>(phys_addr - 1) << Memory::YUZU_PAGEBITS) + page_offset);
on_memory(copy_amount, mem_ptr);
}
}
template <typename Traits>
void DeviceMemoryManager<Traits>::ReadBlock(DAddr address, void* dest_pointer, size_t size) {
device_inter->FlushRegion(address, size);
WalkBlock(
address, size,
[&](size_t copy_amount, DAddr current_vaddr) {
LOG_ERROR(
HW_Memory,
"Unmapped Device ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
current_vaddr, address, size);
std::memset(dest_pointer, 0, copy_amount);
},
[&](size_t copy_amount, const u8* const src_ptr) {
std::memcpy(dest_pointer, src_ptr, copy_amount);
},
[&](const std::size_t copy_amount) {
dest_pointer = static_cast<u8*>(dest_pointer) + copy_amount;
});
}
template <typename Traits>
void DeviceMemoryManager<Traits>::WriteBlock(DAddr address, const void* src_pointer, size_t size) {
WalkBlock(
address, size,
[&](size_t copy_amount, DAddr current_vaddr) {
LOG_ERROR(
HW_Memory,
"Unmapped Device WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
current_vaddr, address, size);
},
[&](size_t copy_amount, u8* const dst_ptr) {
std::memcpy(dst_ptr, src_pointer, copy_amount);
},
[&](const std::size_t copy_amount) {
src_pointer = static_cast<const u8*>(src_pointer) + copy_amount;
});
device_inter->InvalidateRegion(address, size);
}
template <typename Traits>
void DeviceMemoryManager<Traits>::ReadBlockUnsafe(DAddr address, void* dest_pointer, size_t size) {
WalkBlock(
address, size,
[&](size_t copy_amount, DAddr current_vaddr) {
LOG_ERROR(
HW_Memory,
"Unmapped Device ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
current_vaddr, address, size);
std::memset(dest_pointer, 0, copy_amount);
},
[&](size_t copy_amount, const u8* const src_ptr) {
std::memcpy(dest_pointer, src_ptr, copy_amount);
},
[&](const std::size_t copy_amount) {
dest_pointer = static_cast<u8*>(dest_pointer) + copy_amount;
});
}
template <typename Traits>
void DeviceMemoryManager<Traits>::WriteBlockUnsafe(DAddr address, const void* src_pointer,
size_t size) {
WalkBlock(
address, size,
[&](size_t copy_amount, DAddr current_vaddr) {
LOG_ERROR(
HW_Memory,
"Unmapped Device WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
current_vaddr, address, size);
},
[&](size_t copy_amount, u8* const dst_ptr) {
std::memcpy(dst_ptr, src_pointer, copy_amount);
},
[&](const std::size_t copy_amount) {
src_pointer = static_cast<const u8*>(src_pointer) + copy_amount;
});
}
template <typename Traits>
Asid DeviceMemoryManager<Traits>::RegisterProcess(Memory::Memory* memory_device_inter) {
size_t new_id{};
if (!id_pool.empty()) {
new_id = id_pool.front();
id_pool.pop_front();
registered_processes[new_id] = memory_device_inter;
} else {
registered_processes.emplace_back(memory_device_inter);
new_id = registered_processes.size() - 1U;
}
return Asid{new_id};
}
template <typename Traits>
void DeviceMemoryManager<Traits>::UnregisterProcess(Asid asid) {
registered_processes[asid.id] = nullptr;
id_pool.push_front(asid.id);
}
template <typename Traits>
void DeviceMemoryManager<Traits>::UpdatePagesCachedCount(DAddr addr, size_t size, s32 delta) {
std::unique_lock<std::mutex> lk(counter_guard, std::defer_lock);
const auto Lock = [&] {
if (!lk) {
lk.lock();
}
};
u64 uncache_begin = 0;
u64 cache_begin = 0;
u64 uncache_bytes = 0;
u64 cache_bytes = 0;
const auto MarkRegionCaching = &DeviceMemoryManager<Traits>::DeviceMethods::MarkRegionCaching;
std::atomic_thread_fence(std::memory_order_acquire);
const size_t page_end = Common::DivCeil(addr + size, Memory::YUZU_PAGESIZE);
size_t page = addr >> Memory::YUZU_PAGEBITS;
auto [asid, base_vaddress] = ExtractCPUBacking(page);
size_t vpage = base_vaddress >> Memory::YUZU_PAGEBITS;
auto* memory_device_inter = registered_processes[asid.id];
for (; page != page_end; ++page) {
std::atomic_uint8_t& count = cached_pages->at(page >> 3).Count(page);
if (delta > 0) {
ASSERT_MSG(count.load(std::memory_order::relaxed) < std::numeric_limits<u8>::max(),
"Count may overflow!");
} else if (delta < 0) {
ASSERT_MSG(count.load(std::memory_order::relaxed) > 0, "Count may underflow!");
} else {
ASSERT_MSG(false, "Delta must be non-zero!");
}
// Adds or subtracts 1, as count is a unsigned 8-bit value
count.fetch_add(static_cast<u8>(delta), std::memory_order_release);
// Assume delta is either -1 or 1
if (count.load(std::memory_order::relaxed) == 0) {
if (uncache_bytes == 0) {
uncache_begin = vpage;
}
uncache_bytes += Memory::YUZU_PAGESIZE;
} else if (uncache_bytes > 0) {
Lock();
MarkRegionCaching(memory_device_inter, uncache_begin << Memory::YUZU_PAGEBITS,
uncache_bytes, false);
uncache_bytes = 0;
}
if (count.load(std::memory_order::relaxed) == 1 && delta > 0) {
if (cache_bytes == 0) {
cache_begin = vpage;
}
cache_bytes += Memory::YUZU_PAGESIZE;
} else if (cache_bytes > 0) {
Lock();
MarkRegionCaching(memory_device_inter, cache_begin << Memory::YUZU_PAGEBITS, cache_bytes,
true);
cache_bytes = 0;
}
vpage++;
}
if (uncache_bytes > 0) {
Lock();
MarkRegionCaching(memory_device_inter, uncache_begin << Memory::YUZU_PAGEBITS, uncache_bytes,
false);
}
if (cache_bytes > 0) {
Lock();
MarkRegionCaching(memory_device_inter, cache_begin << Memory::YUZU_PAGEBITS, cache_bytes,
true);
}
}
} // namespace Core

View file

@ -10,7 +10,7 @@
#include <utility>
#include <vector>
#include "core/memory.h"
#include "core/device_memory_manager.h"
namespace Core {
@ -23,7 +23,7 @@ public:
~GPUDirtyMemoryManager() = default;
void Collect(VAddr address, size_t size) {
void Collect(PAddr address, size_t size) {
TransformAddress t = BuildTransform(address, size);
TransformAddress tmp, original;
do {
@ -47,7 +47,7 @@ public:
std::memory_order_relaxed));
}
void Gather(std::function<void(VAddr, size_t)>& callback) {
void Gather(std::function<void(PAddr, size_t)>& callback) {
{
std::scoped_lock lk(guard);
TransformAddress t = current.exchange(default_transform, std::memory_order_relaxed);
@ -65,7 +65,7 @@ public:
mask = mask >> empty_bits;
const size_t continuous_bits = std::countr_one(mask);
callback((static_cast<VAddr>(transform.address) << page_bits) + offset,
callback((static_cast<PAddr>(transform.address) << page_bits) + offset,
continuous_bits << align_bits);
mask = continuous_bits < align_size ? (mask >> continuous_bits) : 0;
offset += continuous_bits << align_bits;
@ -80,7 +80,7 @@ private:
u32 mask;
};
constexpr static size_t page_bits = Memory::YUZU_PAGEBITS - 1;
constexpr static size_t page_bits = DEVICE_PAGEBITS - 1;
constexpr static size_t page_size = 1ULL << page_bits;
constexpr static size_t page_mask = page_size - 1;
@ -89,7 +89,7 @@ private:
constexpr static size_t align_mask = align_size - 1;
constexpr static TransformAddress default_transform = {.address = ~0U, .mask = 0U};
bool IsValid(VAddr address) {
bool IsValid(PAddr address) {
return address < (1ULL << 39);
}
@ -103,7 +103,7 @@ private:
return mask;
}
TransformAddress BuildTransform(VAddr address, size_t size) {
TransformAddress BuildTransform(PAddr address, size_t size) {
const size_t minor_address = address & page_mask;
const size_t minor_bit = minor_address >> align_bits;
const size_t top_bit = (minor_address + size + align_mask) >> align_bits;

214
src/core/guest_memory.h Normal file
View file

@ -0,0 +1,214 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <iterator>
#include <memory>
#include <optional>
#include <span>
#include <vector>
#include "common/assert.h"
#include "common/scratch_buffer.h"
namespace Core::Memory {
enum GuestMemoryFlags : u32 {
Read = 1 << 0,
Write = 1 << 1,
Safe = 1 << 2,
Cached = 1 << 3,
SafeRead = Read | Safe,
SafeWrite = Write | Safe,
SafeReadWrite = SafeRead | SafeWrite,
SafeReadCachedWrite = SafeReadWrite | Cached,
UnsafeRead = Read,
UnsafeWrite = Write,
UnsafeReadWrite = UnsafeRead | UnsafeWrite,
UnsafeReadCachedWrite = UnsafeReadWrite | Cached,
};
namespace {
template <typename M, typename T, GuestMemoryFlags FLAGS>
class GuestMemory {
using iterator = T*;
using const_iterator = const T*;
using value_type = T;
using element_type = T;
using iterator_category = std::contiguous_iterator_tag;
public:
GuestMemory() = delete;
explicit GuestMemory(M& memory, u64 addr, std::size_t size,
Common::ScratchBuffer<T>* backup = nullptr)
: m_memory{memory}, m_addr{addr}, m_size{size} {
static_assert(FLAGS & GuestMemoryFlags::Read || FLAGS & GuestMemoryFlags::Write);
if constexpr (FLAGS & GuestMemoryFlags::Read) {
Read(addr, size, backup);
}
}
~GuestMemory() = default;
T* data() noexcept {
return m_data_span.data();
}
const T* data() const noexcept {
return m_data_span.data();
}
size_t size() const noexcept {
return m_size;
}
size_t size_bytes() const noexcept {
return this->size() * sizeof(T);
}
[[nodiscard]] T* begin() noexcept {
return this->data();
}
[[nodiscard]] const T* begin() const noexcept {
return this->data();
}
[[nodiscard]] T* end() noexcept {
return this->data() + this->size();
}
[[nodiscard]] const T* end() const noexcept {
return this->data() + this->size();
}
T& operator[](size_t index) noexcept {
return m_data_span[index];
}
const T& operator[](size_t index) const noexcept {
return m_data_span[index];
}
void SetAddressAndSize(u64 addr, std::size_t size) noexcept {
m_addr = addr;
m_size = size;
m_addr_changed = true;
}
std::span<T> Read(u64 addr, std::size_t size,
Common::ScratchBuffer<T>* backup = nullptr) noexcept {
m_addr = addr;
m_size = size;
if (m_size == 0) {
m_is_data_copy = true;
return {};
}
if (this->TrySetSpan()) {
if constexpr (FLAGS & GuestMemoryFlags::Safe) {
m_memory.FlushRegion(m_addr, this->size_bytes());
}
} else {
if (backup) {
backup->resize_destructive(this->size());
m_data_span = *backup;
} else {
m_data_copy.resize(this->size());
m_data_span = std::span(m_data_copy);
}
m_is_data_copy = true;
m_span_valid = true;
if constexpr (FLAGS & GuestMemoryFlags::Safe) {
m_memory.ReadBlock(m_addr, this->data(), this->size_bytes());
} else {
m_memory.ReadBlockUnsafe(m_addr, this->data(), this->size_bytes());
}
}
return m_data_span;
}
void Write(std::span<T> write_data) noexcept {
if constexpr (FLAGS & GuestMemoryFlags::Cached) {
m_memory.WriteBlockCached(m_addr, write_data.data(), this->size_bytes());
} else if constexpr (FLAGS & GuestMemoryFlags::Safe) {
m_memory.WriteBlock(m_addr, write_data.data(), this->size_bytes());
} else {
m_memory.WriteBlockUnsafe(m_addr, write_data.data(), this->size_bytes());
}
}
bool TrySetSpan() noexcept {
if (u8* ptr = m_memory.GetSpan(m_addr, this->size_bytes()); ptr) {
m_data_span = {reinterpret_cast<T*>(ptr), this->size()};
m_span_valid = true;
return true;
}
return false;
}
protected:
bool IsDataCopy() const noexcept {
return m_is_data_copy;
}
bool AddressChanged() const noexcept {
return m_addr_changed;
}
M& m_memory;
u64 m_addr{};
size_t m_size{};
std::span<T> m_data_span{};
std::vector<T> m_data_copy{};
bool m_span_valid{false};
bool m_is_data_copy{false};
bool m_addr_changed{false};
};
template <typename M, typename T, GuestMemoryFlags FLAGS>
class GuestMemoryScoped : public GuestMemory<M, T, FLAGS> {
public:
GuestMemoryScoped() = delete;
explicit GuestMemoryScoped(M& memory, u64 addr, std::size_t size,
Common::ScratchBuffer<T>* backup = nullptr)
: GuestMemory<M, T, FLAGS>(memory, addr, size, backup) {
if constexpr (!(FLAGS & GuestMemoryFlags::Read)) {
if (!this->TrySetSpan()) {
if (backup) {
this->m_data_span = *backup;
this->m_span_valid = true;
this->m_is_data_copy = true;
}
}
}
}
~GuestMemoryScoped() {
if constexpr (FLAGS & GuestMemoryFlags::Write) {
if (this->size() == 0) [[unlikely]] {
return;
}
if (this->AddressChanged() || this->IsDataCopy()) {
ASSERT(this->m_span_valid);
if constexpr (FLAGS & GuestMemoryFlags::Cached) {
this->m_memory.WriteBlockCached(this->m_addr, this->data(), this->size_bytes());
} else if constexpr (FLAGS & GuestMemoryFlags::Safe) {
this->m_memory.WriteBlock(this->m_addr, this->data(), this->size_bytes());
} else {
this->m_memory.WriteBlockUnsafe(this->m_addr, this->data(), this->size_bytes());
}
} else if constexpr ((FLAGS & GuestMemoryFlags::Safe) ||
(FLAGS & GuestMemoryFlags::Cached)) {
this->m_memory.InvalidateRegion(this->m_addr, this->size_bytes());
}
}
}
};
} // namespace
} // namespace Core::Memory

View file

@ -5,6 +5,7 @@
#include "common/scope_exit.h"
#include "common/settings.h"
#include "core/core.h"
#include "core/gpu_dirty_memory_manager.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_scoped_resource_reservation.h"
#include "core/hle/kernel/k_shared_memory.h"
@ -320,7 +321,7 @@ Result KProcess::Initialize(const Svc::CreateProcessParameter& params, const KPa
// Ensure our memory is initialized.
m_memory.SetCurrentPageTable(*this);
m_memory.SetGPUDirtyManagers(m_dirty_memory_managers);
m_memory.SetGPUDirtyManagers(m_kernel.System().GetGPUDirtyMemoryManager());
// Ensure we can insert the code region.
R_UNLESS(m_page_table.CanContain(params.code_address, params.code_num_pages * PageSize,
@ -417,7 +418,7 @@ Result KProcess::Initialize(const Svc::CreateProcessParameter& params,
// Ensure our memory is initialized.
m_memory.SetCurrentPageTable(*this);
m_memory.SetGPUDirtyManagers(m_dirty_memory_managers);
m_memory.SetGPUDirtyManagers(m_kernel.System().GetGPUDirtyMemoryManager());
// Ensure we can insert the code region.
R_UNLESS(m_page_table.CanContain(params.code_address, code_size, KMemoryState::Code),
@ -1141,8 +1142,7 @@ void KProcess::Switch(KProcess* cur_process, KProcess* next_process) {}
KProcess::KProcess(KernelCore& kernel)
: KAutoObjectWithSlabHeapAndContainer(kernel), m_page_table{kernel}, m_state_lock{kernel},
m_list_lock{kernel}, m_cond_var{kernel.System()}, m_address_arbiter{kernel.System()},
m_handle_table{kernel}, m_dirty_memory_managers{},
m_exclusive_monitor{}, m_memory{kernel.System()} {}
m_handle_table{kernel}, m_exclusive_monitor{}, m_memory{kernel.System()} {}
KProcess::~KProcess() = default;
Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size,
@ -1324,10 +1324,4 @@ bool KProcess::RemoveWatchpoint(KProcessAddress addr, u64 size, DebugWatchpointT
return true;
}
void KProcess::GatherGPUDirtyMemory(std::function<void(VAddr, size_t)>& callback) {
for (auto& manager : m_dirty_memory_managers) {
manager.Gather(callback);
}
}
} // namespace Kernel

View file

@ -7,7 +7,6 @@
#include "core/arm/arm_interface.h"
#include "core/file_sys/program_metadata.h"
#include "core/gpu_dirty_memory_manager.h"
#include "core/hle/kernel/code_set.h"
#include "core/hle/kernel/k_address_arbiter.h"
#include "core/hle/kernel/k_capabilities.h"
@ -128,7 +127,6 @@ private:
#ifdef HAS_NCE
std::unordered_map<u64, u64> m_post_handlers{};
#endif
std::array<Core::GPUDirtyMemoryManager, Core::Hardware::NUM_CPU_CORES> m_dirty_memory_managers;
std::unique_ptr<Core::ExclusiveMonitor> m_exclusive_monitor;
Core::Memory::Memory m_memory;
@ -511,8 +509,6 @@ public:
return m_memory;
}
void GatherGPUDirtyMemory(std::function<void(VAddr, size_t)>& callback);
Core::ExclusiveMonitor& GetExclusiveMonitor() const {
return *m_exclusive_monitor;
}

View file

@ -26,6 +26,7 @@ void LoopProcess(Core::System& system) {
resource_manager->Initialize();
resource_manager->RegisterAppletResourceUserId(system.ApplicationProcess()->GetProcessId(),
true);
resource_manager->SetAruidValidForVibration(system.ApplicationProcess()->GetProcessId(), true);
server_manager->RegisterNamedService(
"hid", std::make_shared<IHidServer>(system, resource_manager, firmware_settings));

View file

@ -22,12 +22,16 @@
#include "hid_core/resources/mouse/mouse.h"
#include "hid_core/resources/npad/npad.h"
#include "hid_core/resources/npad/npad_types.h"
#include "hid_core/resources/npad/npad_vibration.h"
#include "hid_core/resources/palma/palma.h"
#include "hid_core/resources/six_axis/console_six_axis.h"
#include "hid_core/resources/six_axis/seven_six_axis.h"
#include "hid_core/resources/six_axis/six_axis.h"
#include "hid_core/resources/touch_screen/gesture.h"
#include "hid_core/resources/touch_screen/touch_screen.h"
#include "hid_core/resources/vibration/gc_vibration_device.h"
#include "hid_core/resources/vibration/n64_vibration_device.h"
#include "hid_core/resources/vibration/vibration_device.h"
namespace Service::HID {
@ -38,7 +42,7 @@ public:
: ServiceFramework{system_, "IActiveVibrationDeviceList"}, resource_manager(resource) {
// clang-format off
static const FunctionInfo functions[] = {
{0, &IActiveVibrationDeviceList::InitializeVibrationDevice, "InitializeVibrationDevice"},
{0, &IActiveVibrationDeviceList::ActivateVibrationDevice, "ActivateVibrationDevice"},
};
// clang-format on
@ -46,22 +50,49 @@ public:
}
private:
void InitializeVibrationDevice(HLERequestContext& ctx) {
void ActivateVibrationDevice(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()};
if (resource_manager != nullptr && resource_manager->GetNpad()) {
resource_manager->GetNpad()->InitializeVibrationDevice(vibration_device_handle);
}
LOG_DEBUG(Service_HID, "called, npad_type={}, npad_id={}, device_index={}",
vibration_device_handle.npad_type, vibration_device_handle.npad_id,
vibration_device_handle.device_index);
const auto result = ActivateVibrationDeviceImpl(vibration_device_handle);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
rb.Push(result);
}
Result ActivateVibrationDeviceImpl(const Core::HID::VibrationDeviceHandle& handle) {
std::scoped_lock lock{mutex};
const Result is_valid = IsVibrationHandleValid(handle);
if (is_valid.IsError()) {
return is_valid;
}
for (std::size_t i = 0; i < list_size; i++) {
if (handle.device_index == vibration_device_list[i].device_index &&
handle.npad_id == vibration_device_list[i].npad_id &&
handle.npad_type == vibration_device_list[i].npad_type) {
return ResultSuccess;
}
}
if (list_size == vibration_device_list.size()) {
return ResultVibrationDeviceIndexOutOfRange;
}
const Result result = resource_manager->GetVibrationDevice(handle)->Activate();
if (result.IsError()) {
return result;
}
vibration_device_list[list_size++] = handle;
return ResultSuccess;
}
mutable std::mutex mutex;
std::size_t list_size{};
std::array<Core::HID::VibrationDeviceHandle, 0x100> vibration_device_list{};
std::shared_ptr<ResourceManager> resource_manager;
};
@ -153,7 +184,7 @@ IHidServer::IHidServer(Core::System& system_, std::shared_ptr<ResourceManager> r
{209, &IHidServer::BeginPermitVibrationSession, "BeginPermitVibrationSession"},
{210, &IHidServer::EndPermitVibrationSession, "EndPermitVibrationSession"},
{211, &IHidServer::IsVibrationDeviceMounted, "IsVibrationDeviceMounted"},
{212, nullptr, "SendVibrationValueInBool"},
{212, &IHidServer::SendVibrationValueInBool, "SendVibrationValueInBool"},
{300, &IHidServer::ActivateConsoleSixAxisSensor, "ActivateConsoleSixAxisSensor"},
{301, &IHidServer::StartConsoleSixAxisSensor, "StartConsoleSixAxisSensor"},
{302, &IHidServer::StopConsoleSixAxisSensor, "StopConsoleSixAxisSensor"},
@ -1492,59 +1523,13 @@ void IHidServer::ClearNpadCaptureButtonAssignment(HLERequestContext& ctx) {
void IHidServer::GetVibrationDeviceInfo(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()};
const auto controller = GetResourceManager()->GetNpad();
Core::HID::VibrationDeviceInfo vibration_device_info;
bool check_device_index = false;
switch (vibration_device_handle.npad_type) {
case Core::HID::NpadStyleIndex::Fullkey:
case Core::HID::NpadStyleIndex::Handheld:
case Core::HID::NpadStyleIndex::JoyconDual:
case Core::HID::NpadStyleIndex::JoyconLeft:
case Core::HID::NpadStyleIndex::JoyconRight:
vibration_device_info.type = Core::HID::VibrationDeviceType::LinearResonantActuator;
check_device_index = true;
break;
case Core::HID::NpadStyleIndex::GameCube:
vibration_device_info.type = Core::HID::VibrationDeviceType::GcErm;
break;
case Core::HID::NpadStyleIndex::N64:
vibration_device_info.type = Core::HID::VibrationDeviceType::N64;
break;
default:
vibration_device_info.type = Core::HID::VibrationDeviceType::Unknown;
break;
}
vibration_device_info.position = Core::HID::VibrationDevicePosition::None;
if (check_device_index) {
switch (vibration_device_handle.device_index) {
case Core::HID::DeviceIndex::Left:
vibration_device_info.position = Core::HID::VibrationDevicePosition::Left;
break;
case Core::HID::DeviceIndex::Right:
vibration_device_info.position = Core::HID::VibrationDevicePosition::Right;
break;
case Core::HID::DeviceIndex::None:
default:
ASSERT_MSG(false, "DeviceIndex should never be None!");
break;
}
}
LOG_DEBUG(Service_HID, "called, vibration_device_type={}, vibration_device_position={}",
vibration_device_info.type, vibration_device_info.position);
const auto result = IsVibrationHandleValid(vibration_device_handle);
if (result.IsError()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
return;
}
Core::HID::VibrationDeviceInfo vibration_device_info{};
const auto result = GetResourceManager()->GetVibrationDeviceInfo(vibration_device_info,
vibration_device_handle);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push(result);
rb.PushRaw(vibration_device_info);
}
@ -1560,16 +1545,16 @@ void IHidServer::SendVibrationValue(HLERequestContext& ctx) {
const auto parameters{rp.PopRaw<Parameters>()};
GetResourceManager()->GetNpad()->VibrateController(parameters.applet_resource_user_id,
parameters.vibration_device_handle,
parameters.vibration_value);
LOG_DEBUG(Service_HID,
"called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
parameters.vibration_device_handle.npad_type,
parameters.vibration_device_handle.npad_id,
parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id);
GetResourceManager()->SendVibrationValue(parameters.applet_resource_user_id,
parameters.vibration_device_handle,
parameters.vibration_value);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
@ -1591,10 +1576,28 @@ void IHidServer::GetActualVibrationValue(HLERequestContext& ctx) {
parameters.vibration_device_handle.npad_id,
parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id);
bool has_active_aruid{};
NpadVibrationDevice* device{nullptr};
Core::HID::VibrationValue vibration_value{};
Result result = GetResourceManager()->IsVibrationAruidActive(parameters.applet_resource_user_id,
has_active_aruid);
if (result.IsSuccess() && has_active_aruid) {
result = IsVibrationHandleValid(parameters.vibration_device_handle);
}
if (result.IsSuccess() && has_active_aruid) {
device = GetResourceManager()->GetNSVibrationDevice(parameters.vibration_device_handle);
}
if (device != nullptr) {
result = device->GetActualVibrationValue(vibration_value);
}
if (result.IsError()) {
vibration_value = Core::HID::DEFAULT_VIBRATION_VALUE;
}
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(ResultSuccess);
rb.PushRaw(GetResourceManager()->GetNpad()->GetLastVibration(
parameters.applet_resource_user_id, parameters.vibration_device_handle));
rb.PushRaw(vibration_value);
}
void IHidServer::CreateActiveVibrationDeviceList(HLERequestContext& ctx) {
@ -1609,25 +1612,27 @@ void IHidServer::PermitVibration(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto can_vibrate{rp.Pop<bool>()};
// nnSDK saves this value as a float. Since it can only be 1.0f or 0.0f we simplify this value
// by converting it to a bool
Settings::values.vibration_enabled.SetValue(can_vibrate);
LOG_DEBUG(Service_HID, "called, can_vibrate={}", can_vibrate);
const auto result =
GetResourceManager()->GetNpad()->GetVibrationHandler()->SetVibrationMasterVolume(
can_vibrate ? 1.0f : 0.0f);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
rb.Push(result);
}
void IHidServer::IsVibrationPermitted(HLERequestContext& ctx) {
LOG_DEBUG(Service_HID, "called");
// nnSDK checks if a float is greater than zero. We return the bool we stored earlier
const auto is_enabled = Settings::values.vibration_enabled.GetValue();
f32 master_volume{};
const auto result =
GetResourceManager()->GetNpad()->GetVibrationHandler()->GetVibrationMasterVolume(
master_volume);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(is_enabled);
rb.Push(result);
rb.Push(master_volume > 0.0f);
}
void IHidServer::SendVibrationValues(HLERequestContext& ctx) {
@ -1645,13 +1650,22 @@ void IHidServer::SendVibrationValues(HLERequestContext& ctx) {
auto vibration_values = std::span(
reinterpret_cast<const Core::HID::VibrationValue*>(vibration_data.data()), vibration_count);
GetResourceManager()->GetNpad()->VibrateControllers(applet_resource_user_id,
vibration_device_handles, vibration_values);
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
Result result = ResultSuccess;
if (handle_count != vibration_count) {
result = ResultVibrationArraySizeMismatch;
}
for (std::size_t i = 0; i < handle_count; i++) {
if (result.IsSuccess()) {
result = GetResourceManager()->SendVibrationValue(
applet_resource_user_id, vibration_device_handles[i], vibration_values[i]);
}
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
rb.Push(result);
}
void IHidServer::SendVibrationGcErmCommand(HLERequestContext& ctx) {
@ -1666,43 +1680,6 @@ void IHidServer::SendVibrationGcErmCommand(HLERequestContext& ctx) {
const auto parameters{rp.PopRaw<Parameters>()};
/**
* Note: This uses yuzu-specific behavior such that the StopHard command produces
* vibrations where freq_low == 0.0f and freq_high == 0.0f, as defined below,
* in order to differentiate between Stop and StopHard commands.
* This is done to reuse the controller vibration functions made for regular controllers.
*/
const auto vibration_value = [parameters] {
switch (parameters.gc_erm_command) {
case Core::HID::VibrationGcErmCommand::Stop:
return Core::HID::VibrationValue{
.low_amplitude = 0.0f,
.low_frequency = 160.0f,
.high_amplitude = 0.0f,
.high_frequency = 320.0f,
};
case Core::HID::VibrationGcErmCommand::Start:
return Core::HID::VibrationValue{
.low_amplitude = 1.0f,
.low_frequency = 160.0f,
.high_amplitude = 1.0f,
.high_frequency = 320.0f,
};
case Core::HID::VibrationGcErmCommand::StopHard:
return Core::HID::VibrationValue{
.low_amplitude = 0.0f,
.low_frequency = 0.0f,
.high_amplitude = 0.0f,
.high_frequency = 0.0f,
};
default:
return Core::HID::DEFAULT_VIBRATION_VALUE;
}
}();
GetResourceManager()->GetNpad()->VibrateController(
parameters.applet_resource_user_id, parameters.vibration_device_handle, vibration_value);
LOG_DEBUG(Service_HID,
"called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}, "
"gc_erm_command={}",
@ -1711,8 +1688,23 @@ void IHidServer::SendVibrationGcErmCommand(HLERequestContext& ctx) {
parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id,
parameters.gc_erm_command);
bool has_active_aruid{};
NpadGcVibrationDevice* gc_device{nullptr};
Result result = GetResourceManager()->IsVibrationAruidActive(parameters.applet_resource_user_id,
has_active_aruid);
if (result.IsSuccess() && has_active_aruid) {
result = IsVibrationHandleValid(parameters.vibration_device_handle);
}
if (result.IsSuccess() && has_active_aruid) {
gc_device = GetResourceManager()->GetGcVibrationDevice(parameters.vibration_device_handle);
}
if (gc_device != nullptr) {
result = gc_device->SendVibrationGcErmCommand(parameters.gc_erm_command);
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
rb.Push(result);
}
void IHidServer::GetActualVibrationGcErmCommand(HLERequestContext& ctx) {
@ -1725,33 +1717,31 @@ void IHidServer::GetActualVibrationGcErmCommand(HLERequestContext& ctx) {
const auto parameters{rp.PopRaw<Parameters>()};
const auto last_vibration = GetResourceManager()->GetNpad()->GetLastVibration(
parameters.applet_resource_user_id, parameters.vibration_device_handle);
const auto gc_erm_command = [last_vibration] {
if (last_vibration.low_amplitude != 0.0f || last_vibration.high_amplitude != 0.0f) {
return Core::HID::VibrationGcErmCommand::Start;
}
/**
* Note: This uses yuzu-specific behavior such that the StopHard command produces
* vibrations where freq_low == 0.0f and freq_high == 0.0f, as defined in the HID function
* SendVibrationGcErmCommand, in order to differentiate between Stop and StopHard commands.
* This is done to reuse the controller vibration functions made for regular controllers.
*/
if (last_vibration.low_frequency == 0.0f && last_vibration.high_frequency == 0.0f) {
return Core::HID::VibrationGcErmCommand::StopHard;
}
return Core::HID::VibrationGcErmCommand::Stop;
}();
LOG_DEBUG(Service_HID,
"called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
parameters.vibration_device_handle.npad_type,
parameters.vibration_device_handle.npad_id,
parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id);
bool has_active_aruid{};
NpadGcVibrationDevice* gc_device{nullptr};
Core::HID::VibrationGcErmCommand gc_erm_command{};
Result result = GetResourceManager()->IsVibrationAruidActive(parameters.applet_resource_user_id,
has_active_aruid);
if (result.IsSuccess() && has_active_aruid) {
result = IsVibrationHandleValid(parameters.vibration_device_handle);
}
if (result.IsSuccess() && has_active_aruid) {
gc_device = GetResourceManager()->GetGcVibrationDevice(parameters.vibration_device_handle);
}
if (gc_device != nullptr) {
result = gc_device->GetActualVibrationGcErmCommand(gc_erm_command);
}
if (result.IsError()) {
gc_erm_command = Core::HID::VibrationGcErmCommand::Stop;
}
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.PushEnum(gc_erm_command);
@ -1761,21 +1751,24 @@ void IHidServer::BeginPermitVibrationSession(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
GetResourceManager()->GetNpad()->SetPermitVibrationSession(true);
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
const auto result =
GetResourceManager()->GetNpad()->GetVibrationHandler()->BeginPermitVibrationSession(
applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
rb.Push(result);
}
void IHidServer::EndPermitVibrationSession(HLERequestContext& ctx) {
GetResourceManager()->GetNpad()->SetPermitVibrationSession(false);
LOG_DEBUG(Service_HID, "called");
const auto result =
GetResourceManager()->GetNpad()->GetVibrationHandler()->EndPermitVibrationSession();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
rb.Push(result);
}
void IHidServer::IsVibrationDeviceMounted(HLERequestContext& ctx) {
@ -1795,10 +1788,61 @@ void IHidServer::IsVibrationDeviceMounted(HLERequestContext& ctx) {
parameters.vibration_device_handle.npad_id,
parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id);
bool is_mounted{};
NpadVibrationBase* device{nullptr};
Result result = IsVibrationHandleValid(parameters.vibration_device_handle);
if (result.IsSuccess()) {
device = GetResourceManager()->GetVibrationDevice(parameters.vibration_device_handle);
}
if (device != nullptr) {
is_mounted = device->IsVibrationMounted();
}
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(GetResourceManager()->GetNpad()->IsVibrationDeviceMounted(
parameters.applet_resource_user_id, parameters.vibration_device_handle));
rb.Push(result);
rb.Push(is_mounted);
}
void IHidServer::SendVibrationValueInBool(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Core::HID::VibrationDeviceHandle vibration_device_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
bool is_vibrating;
};
static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
LOG_DEBUG(Service_HID,
"called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}, "
"is_vibrating={}",
parameters.vibration_device_handle.npad_type,
parameters.vibration_device_handle.npad_id,
parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id,
parameters.is_vibrating);
bool has_active_aruid{};
NpadN64VibrationDevice* n64_device{nullptr};
Result result = GetResourceManager()->IsVibrationAruidActive(parameters.applet_resource_user_id,
has_active_aruid);
if (result.IsSuccess() && has_active_aruid) {
result = IsVibrationHandleValid(parameters.vibration_device_handle);
}
if (result.IsSuccess() && has_active_aruid) {
n64_device =
GetResourceManager()->GetN64VibrationDevice(parameters.vibration_device_handle);
}
if (n64_device != nullptr) {
result = n64_device->SendValueInBool(parameters.is_vibrating);
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void IHidServer::ActivateConsoleSixAxisSensor(HLERequestContext& ctx) {

View file

@ -97,6 +97,7 @@ private:
void BeginPermitVibrationSession(HLERequestContext& ctx);
void EndPermitVibrationSession(HLERequestContext& ctx);
void IsVibrationDeviceMounted(HLERequestContext& ctx);
void SendVibrationValueInBool(HLERequestContext& ctx);
void ActivateConsoleSixAxisSensor(HLERequestContext& ctx);
void StartConsoleSixAxisSensor(HLERequestContext& ctx);
void StopConsoleSixAxisSensor(HLERequestContext& ctx);

View file

@ -7,6 +7,7 @@
#include "hid_core/resource_manager.h"
#include "hid_core/resources/npad/npad.h"
#include "hid_core/resources/npad/npad_types.h"
#include "hid_core/resources/npad/npad_vibration.h"
#include "hid_core/resources/palma/palma.h"
#include "hid_core/resources/touch_screen/touch_screen.h"
@ -67,14 +68,14 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<Resour
{501, &IHidSystemServer::RegisterAppletResourceUserId, "RegisterAppletResourceUserId"},
{502, &IHidSystemServer::UnregisterAppletResourceUserId, "UnregisterAppletResourceUserId"},
{503, &IHidSystemServer::EnableAppletToGetInput, "EnableAppletToGetInput"},
{504, nullptr, "SetAruidValidForVibration"},
{504, &IHidSystemServer::SetAruidValidForVibration, "SetAruidValidForVibration"},
{505, &IHidSystemServer::EnableAppletToGetSixAxisSensor, "EnableAppletToGetSixAxisSensor"},
{506, &IHidSystemServer::EnableAppletToGetPadInput, "EnableAppletToGetPadInput"},
{507, &IHidSystemServer::EnableAppletToGetTouchScreen, "EnableAppletToGetTouchScreen"},
{510, nullptr, "SetVibrationMasterVolume"},
{511, nullptr, "GetVibrationMasterVolume"},
{512, nullptr, "BeginPermitVibrationSession"},
{513, nullptr, "EndPermitVibrationSession"},
{510, &IHidSystemServer::SetVibrationMasterVolume, "SetVibrationMasterVolume"},
{511, &IHidSystemServer::GetVibrationMasterVolume, "GetVibrationMasterVolume"},
{512, &IHidSystemServer::BeginPermitVibrationSession, "BeginPermitVibrationSession"},
{513, &IHidSystemServer::EndPermitVibrationSession, "EndPermitVibrationSession"},
{514, nullptr, "Unknown514"},
{520, nullptr, "EnableHandheldHids"},
{521, nullptr, "DisableHandheldHids"},
@ -156,7 +157,7 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<Resour
{1152, nullptr, "SetTouchScreenDefaultConfiguration"},
{1153, &IHidSystemServer::GetTouchScreenDefaultConfiguration, "GetTouchScreenDefaultConfiguration"},
{1154, nullptr, "IsFirmwareAvailableForNotification"},
{1155, nullptr, "SetForceHandheldStyleVibration"},
{1155, &IHidSystemServer::SetForceHandheldStyleVibration, "SetForceHandheldStyleVibration"},
{1156, nullptr, "SendConnectionTriggerWithoutTimeoutEvent"},
{1157, nullptr, "CancelConnectionTrigger"},
{1200, nullptr, "IsButtonConfigSupported"},
@ -538,6 +539,27 @@ void IHidSystemServer::EnableAppletToGetInput(HLERequestContext& ctx) {
rb.Push(ResultSuccess);
}
void IHidSystemServer::SetAruidValidForVibration(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
bool is_enabled;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
LOG_INFO(Service_HID, "called, is_enabled={}, applet_resource_user_id={}",
parameters.is_enabled, parameters.applet_resource_user_id);
GetResourceManager()->SetAruidValidForVibration(parameters.applet_resource_user_id,
parameters.is_enabled);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IHidSystemServer::EnableAppletToGetSixAxisSensor(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
@ -601,6 +623,57 @@ void IHidSystemServer::EnableAppletToGetTouchScreen(HLERequestContext& ctx) {
rb.Push(ResultSuccess);
}
void IHidSystemServer::SetVibrationMasterVolume(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto master_volume{rp.Pop<f32>()};
LOG_INFO(Service_HID, "called, volume={}", master_volume);
const auto result =
GetResourceManager()->GetNpad()->GetVibrationHandler()->SetVibrationMasterVolume(
master_volume);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void IHidSystemServer::GetVibrationMasterVolume(HLERequestContext& ctx) {
f32 master_volume{};
const auto result =
GetResourceManager()->GetNpad()->GetVibrationHandler()->GetVibrationMasterVolume(
master_volume);
LOG_INFO(Service_HID, "called, volume={}", master_volume);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(result);
rb.Push(master_volume);
}
void IHidSystemServer::BeginPermitVibrationSession(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
const auto result =
GetResourceManager()->GetNpad()->GetVibrationHandler()->BeginPermitVibrationSession(
applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void IHidSystemServer::EndPermitVibrationSession(HLERequestContext& ctx) {
LOG_INFO(Service_HID, "called");
const auto result =
GetResourceManager()->GetNpad()->GetVibrationHandler()->EndPermitVibrationSession();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void IHidSystemServer::IsJoyConAttachedOnAllRail(HLERequestContext& ctx) {
const bool is_attached = true;
@ -749,6 +822,19 @@ void IHidSystemServer::GetTouchScreenDefaultConfiguration(HLERequestContext& ctx
rb.PushRaw(touchscreen_config);
}
void IHidSystemServer::SetForceHandheldStyleVibration(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto is_forced{rp.Pop<bool>()};
LOG_INFO(Service_HID, "called, is_forced={}", is_forced);
GetResourceManager()->SetForceHandheldStyleVibration(is_forced);
GetResourceManager()->GetNpad()->UpdateHandheldAbstractState();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IHidSystemServer::IsUsingCustomButtonConfig(HLERequestContext& ctx) {
const bool is_enabled = false;

View file

@ -42,9 +42,14 @@ private:
void RegisterAppletResourceUserId(HLERequestContext& ctx);
void UnregisterAppletResourceUserId(HLERequestContext& ctx);
void EnableAppletToGetInput(HLERequestContext& ctx);
void SetAruidValidForVibration(HLERequestContext& ctx);
void EnableAppletToGetSixAxisSensor(HLERequestContext& ctx);
void EnableAppletToGetPadInput(HLERequestContext& ctx);
void EnableAppletToGetTouchScreen(HLERequestContext& ctx);
void SetVibrationMasterVolume(HLERequestContext& ctx);
void GetVibrationMasterVolume(HLERequestContext& ctx);
void BeginPermitVibrationSession(HLERequestContext& ctx);
void EndPermitVibrationSession(HLERequestContext& ctx);
void IsJoyConAttachedOnAllRail(HLERequestContext& ctx);
void AcquireConnectionTriggerTimeoutEvent(HLERequestContext& ctx);
void AcquireDeviceRegisteredEventForControllerSupport(HLERequestContext& ctx);
@ -61,6 +66,7 @@ private:
void FinalizeUsbFirmwareUpdate(HLERequestContext& ctx);
void InitializeUsbFirmwareUpdateWithoutMemory(HLERequestContext& ctx);
void GetTouchScreenDefaultConfiguration(HLERequestContext& ctx);
void SetForceHandheldStyleVibration(HLERequestContext& ctx);
void IsUsingCustomButtonConfig(HLERequestContext& ctx);
std::shared_ptr<ResourceManager> GetResourceManager();

View file

@ -12,6 +12,7 @@
#include "common/common_types.h"
#include "common/logging/log.h"
#include "common/scratch_buffer.h"
#include "core/guest_memory.h"
#include "core/hle/kernel/k_auto_object.h"
#include "core/hle/kernel/k_handle_table.h"
#include "core/hle/kernel/k_process.h"
@ -23,19 +24,6 @@
#include "core/hle/service/ipc_helpers.h"
#include "core/memory.h"
namespace {
static thread_local std::array read_buffer_data_a{
Common::ScratchBuffer<u8>(),
Common::ScratchBuffer<u8>(),
Common::ScratchBuffer<u8>(),
};
static thread_local std::array read_buffer_data_x{
Common::ScratchBuffer<u8>(),
Common::ScratchBuffer<u8>(),
Common::ScratchBuffer<u8>(),
};
} // Anonymous namespace
namespace Service {
SessionRequestHandler::SessionRequestHandler(Kernel::KernelCore& kernel_, const char* service_name_)
@ -343,48 +331,27 @@ std::vector<u8> HLERequestContext::ReadBufferCopy(std::size_t buffer_index) cons
}
std::span<const u8> HLERequestContext::ReadBufferA(std::size_t buffer_index) const {
static thread_local std::array read_buffer_a{
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0),
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0),
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0),
};
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::UnsafeRead> gm(memory, 0, 0);
ASSERT_OR_EXECUTE_MSG(
BufferDescriptorA().size() > buffer_index, { return {}; },
"BufferDescriptorA invalid buffer_index {}", buffer_index);
auto& read_buffer = read_buffer_a[buffer_index];
return read_buffer.Read(BufferDescriptorA()[buffer_index].Address(),
BufferDescriptorA()[buffer_index].Size(),
&read_buffer_data_a[buffer_index]);
return gm.Read(BufferDescriptorA()[buffer_index].Address(),
BufferDescriptorA()[buffer_index].Size(), &read_buffer_data_a[buffer_index]);
}
std::span<const u8> HLERequestContext::ReadBufferX(std::size_t buffer_index) const {
static thread_local std::array read_buffer_x{
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0),
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0),
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0),
};
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::UnsafeRead> gm(memory, 0, 0);
ASSERT_OR_EXECUTE_MSG(
BufferDescriptorX().size() > buffer_index, { return {}; },
"BufferDescriptorX invalid buffer_index {}", buffer_index);
auto& read_buffer = read_buffer_x[buffer_index];
return read_buffer.Read(BufferDescriptorX()[buffer_index].Address(),
BufferDescriptorX()[buffer_index].Size(),
&read_buffer_data_x[buffer_index]);
return gm.Read(BufferDescriptorX()[buffer_index].Address(),
BufferDescriptorX()[buffer_index].Size(), &read_buffer_data_x[buffer_index]);
}
std::span<const u8> HLERequestContext::ReadBuffer(std::size_t buffer_index) const {
static thread_local std::array read_buffer_a{
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0),
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0),
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0),
};
static thread_local std::array read_buffer_x{
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0),
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0),
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0),
};
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::UnsafeRead> gm(memory, 0, 0);
const bool is_buffer_a{BufferDescriptorA().size() > buffer_index &&
BufferDescriptorA()[buffer_index].Size()};
@ -401,18 +368,14 @@ std::span<const u8> HLERequestContext::ReadBuffer(std::size_t buffer_index) cons
ASSERT_OR_EXECUTE_MSG(
BufferDescriptorA().size() > buffer_index, { return {}; },
"BufferDescriptorA invalid buffer_index {}", buffer_index);
auto& read_buffer = read_buffer_a[buffer_index];
return read_buffer.Read(BufferDescriptorA()[buffer_index].Address(),
BufferDescriptorA()[buffer_index].Size(),
&read_buffer_data_a[buffer_index]);
return gm.Read(BufferDescriptorA()[buffer_index].Address(),
BufferDescriptorA()[buffer_index].Size(), &read_buffer_data_a[buffer_index]);
} else {
ASSERT_OR_EXECUTE_MSG(
BufferDescriptorX().size() > buffer_index, { return {}; },
"BufferDescriptorX invalid buffer_index {}", buffer_index);
auto& read_buffer = read_buffer_x[buffer_index];
return read_buffer.Read(BufferDescriptorX()[buffer_index].Address(),
BufferDescriptorX()[buffer_index].Size(),
&read_buffer_data_x[buffer_index]);
return gm.Read(BufferDescriptorX()[buffer_index].Address(),
BufferDescriptorX()[buffer_index].Size(), &read_buffer_data_x[buffer_index]);
}
}

View file

@ -41,6 +41,8 @@ class KernelCore;
class KHandleTable;
class KProcess;
class KServerSession;
template <typename T>
class KScopedAutoObject;
class KThread;
} // namespace Kernel
@ -424,6 +426,9 @@ private:
Kernel::KernelCore& kernel;
Core::Memory::Memory& memory;
mutable std::array<Common::ScratchBuffer<u8>, 3> read_buffer_data_a{};
mutable std::array<Common::ScratchBuffer<u8>, 3> read_buffer_data_x{};
};
} // namespace Service

View file

@ -2,27 +2,135 @@
// SPDX-FileCopyrightText: 2022 Skyline Team and Contributors
// SPDX-License-Identifier: GPL-3.0-or-later
#include <atomic>
#include <deque>
#include <mutex>
#include "core/hle/kernel/k_process.h"
#include "core/hle/service/nvdrv/core/container.h"
#include "core/hle/service/nvdrv/core/heap_mapper.h"
#include "core/hle/service/nvdrv/core/nvmap.h"
#include "core/hle/service/nvdrv/core/syncpoint_manager.h"
#include "core/memory.h"
#include "video_core/host1x/host1x.h"
namespace Service::Nvidia::NvCore {
Session::Session(SessionId id_, Kernel::KProcess* process_, Core::Asid asid_)
: id{id_}, process{process_}, asid{asid_}, has_preallocated_area{}, mapper{}, is_active{} {}
Session::~Session() = default;
struct ContainerImpl {
explicit ContainerImpl(Tegra::Host1x::Host1x& host1x_)
: file{host1x_}, manager{host1x_}, device_file_data{} {}
explicit ContainerImpl(Container& core, Tegra::Host1x::Host1x& host1x_)
: host1x{host1x_}, file{core, host1x_}, manager{host1x_}, device_file_data{} {}
Tegra::Host1x::Host1x& host1x;
NvMap file;
SyncpointManager manager;
Container::Host1xDeviceFileData device_file_data;
std::deque<Session> sessions;
size_t new_ids{};
std::deque<size_t> id_pool;
std::mutex session_guard;
};
Container::Container(Tegra::Host1x::Host1x& host1x_) {
impl = std::make_unique<ContainerImpl>(host1x_);
impl = std::make_unique<ContainerImpl>(*this, host1x_);
}
Container::~Container() = default;
SessionId Container::OpenSession(Kernel::KProcess* process) {
using namespace Common::Literals;
std::scoped_lock lk(impl->session_guard);
for (auto& session : impl->sessions) {
if (!session.is_active) {
continue;
}
if (session.process == process) {
return session.id;
}
}
size_t new_id{};
auto* memory_interface = &process->GetMemory();
auto& smmu = impl->host1x.MemoryManager();
auto asid = smmu.RegisterProcess(memory_interface);
if (!impl->id_pool.empty()) {
new_id = impl->id_pool.front();
impl->id_pool.pop_front();
impl->sessions[new_id] = Session{SessionId{new_id}, process, asid};
} else {
new_id = impl->new_ids++;
impl->sessions.emplace_back(SessionId{new_id}, process, asid);
}
auto& session = impl->sessions[new_id];
session.is_active = true;
// Optimization
if (process->IsApplication()) {
auto& page_table = process->GetPageTable().GetBasePageTable();
auto heap_start = page_table.GetHeapRegionStart();
Kernel::KProcessAddress cur_addr = heap_start;
size_t region_size = 0;
VAddr region_start = 0;
while (true) {
Kernel::KMemoryInfo mem_info{};
Kernel::Svc::PageInfo page_info{};
R_ASSERT(page_table.QueryInfo(std::addressof(mem_info), std::addressof(page_info),
cur_addr));
auto svc_mem_info = mem_info.GetSvcMemoryInfo();
// Check if this memory block is heap.
if (svc_mem_info.state == Kernel::Svc::MemoryState::Normal) {
if (svc_mem_info.size > region_size) {
region_size = svc_mem_info.size;
region_start = svc_mem_info.base_address;
}
}
// Check if we're done.
const uintptr_t next_address = svc_mem_info.base_address + svc_mem_info.size;
if (next_address <= GetInteger(cur_addr)) {
break;
}
cur_addr = next_address;
}
session.has_preallocated_area = false;
auto start_region = region_size >= 32_MiB ? smmu.Allocate(region_size) : 0;
if (start_region != 0) {
session.mapper = std::make_unique<HeapMapper>(region_start, start_region, region_size,
asid, impl->host1x);
smmu.TrackContinuity(start_region, region_start, region_size, asid);
session.has_preallocated_area = true;
LOG_DEBUG(Debug, "Preallocation created!");
}
}
return SessionId{new_id};
}
void Container::CloseSession(SessionId session_id) {
std::scoped_lock lk(impl->session_guard);
auto& session = impl->sessions[session_id.id];
auto& smmu = impl->host1x.MemoryManager();
if (session.has_preallocated_area) {
const DAddr region_start = session.mapper->GetRegionStart();
const size_t region_size = session.mapper->GetRegionSize();
session.mapper.reset();
smmu.Free(region_start, region_size);
session.has_preallocated_area = false;
}
session.is_active = false;
smmu.UnregisterProcess(impl->sessions[session_id.id].asid);
impl->id_pool.emplace_front(session_id.id);
}
Session* Container::GetSession(SessionId session_id) {
std::atomic_thread_fence(std::memory_order_acquire);
return &impl->sessions[session_id.id];
}
NvMap& Container::GetNvMapFile() {
return impl->file;
}

View file

@ -8,24 +8,56 @@
#include <memory>
#include <unordered_map>
#include "core/device_memory_manager.h"
#include "core/hle/service/nvdrv/nvdata.h"
namespace Kernel {
class KProcess;
}
namespace Tegra::Host1x {
class Host1x;
} // namespace Tegra::Host1x
namespace Service::Nvidia::NvCore {
class HeapMapper;
class NvMap;
class SyncpointManager;
struct ContainerImpl;
struct SessionId {
size_t id;
};
struct Session {
Session(SessionId id_, Kernel::KProcess* process_, Core::Asid asid_);
~Session();
Session(const Session&) = delete;
Session& operator=(const Session&) = delete;
Session(Session&&) = default;
Session& operator=(Session&&) = default;
SessionId id;
Kernel::KProcess* process;
Core::Asid asid;
bool has_preallocated_area{};
std::unique_ptr<HeapMapper> mapper{};
bool is_active{};
};
class Container {
public:
explicit Container(Tegra::Host1x::Host1x& host1x);
~Container();
SessionId OpenSession(Kernel::KProcess* process);
void CloseSession(SessionId id);
Session* GetSession(SessionId id);
NvMap& GetNvMapFile();
const NvMap& GetNvMapFile() const;

View file

@ -0,0 +1,175 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <mutex>
#include <boost/container/small_vector.hpp>
#define BOOST_NO_MT
#include <boost/pool/detail/mutex.hpp>
#undef BOOST_NO_MT
#include <boost/icl/interval.hpp>
#include <boost/icl/interval_base_set.hpp>
#include <boost/icl/interval_set.hpp>
#include <boost/icl/split_interval_map.hpp>
#include <boost/pool/pool.hpp>
#include <boost/pool/pool_alloc.hpp>
#include <boost/pool/poolfwd.hpp>
#include "core/hle/service/nvdrv/core/heap_mapper.h"
#include "video_core/host1x/host1x.h"
namespace boost {
template <typename T>
class fast_pool_allocator<T, default_user_allocator_new_delete, details::pool::null_mutex, 4096, 0>;
}
namespace Service::Nvidia::NvCore {
using IntervalCompare = std::less<DAddr>;
using IntervalInstance = boost::icl::interval_type_default<DAddr, std::less>;
using IntervalAllocator = boost::fast_pool_allocator<DAddr>;
using IntervalSet = boost::icl::interval_set<DAddr>;
using IntervalType = typename IntervalSet::interval_type;
template <typename Type>
struct counter_add_functor : public boost::icl::identity_based_inplace_combine<Type> {
// types
typedef counter_add_functor<Type> type;
typedef boost::icl::identity_based_inplace_combine<Type> base_type;
// public member functions
void operator()(Type& current, const Type& added) const {
current += added;
if (current < base_type::identity_element()) {
current = base_type::identity_element();
}
}
// public static functions
static void version(Type&){};
};
using OverlapCombine = counter_add_functor<int>;
using OverlapSection = boost::icl::inter_section<int>;
using OverlapCounter = boost::icl::split_interval_map<DAddr, int>;
struct HeapMapper::HeapMapperInternal {
HeapMapperInternal(Tegra::Host1x::Host1x& host1x) : device_memory{host1x.MemoryManager()} {}
~HeapMapperInternal() = default;
template <typename Func>
void ForEachInOverlapCounter(OverlapCounter& current_range, VAddr cpu_addr, u64 size,
Func&& func) {
const DAddr start_address = cpu_addr;
const DAddr end_address = start_address + size;
const IntervalType search_interval{start_address, end_address};
auto it = current_range.lower_bound(search_interval);
if (it == current_range.end()) {
return;
}
auto end_it = current_range.upper_bound(search_interval);
for (; it != end_it; it++) {
auto& inter = it->first;
DAddr inter_addr_end = inter.upper();
DAddr inter_addr = inter.lower();
if (inter_addr_end > end_address) {
inter_addr_end = end_address;
}
if (inter_addr < start_address) {
inter_addr = start_address;
}
func(inter_addr, inter_addr_end, it->second);
}
}
void RemoveEachInOverlapCounter(OverlapCounter& current_range,
const IntervalType search_interval, int subtract_value) {
bool any_removals = false;
current_range.add(std::make_pair(search_interval, subtract_value));
do {
any_removals = false;
auto it = current_range.lower_bound(search_interval);
if (it == current_range.end()) {
return;
}
auto end_it = current_range.upper_bound(search_interval);
for (; it != end_it; it++) {
if (it->second <= 0) {
any_removals = true;
current_range.erase(it);
break;
}
}
} while (any_removals);
}
IntervalSet base_set;
OverlapCounter mapping_overlaps;
Tegra::MaxwellDeviceMemoryManager& device_memory;
std::mutex guard;
};
HeapMapper::HeapMapper(VAddr start_vaddress, DAddr start_daddress, size_t size, Core::Asid asid,
Tegra::Host1x::Host1x& host1x)
: m_vaddress{start_vaddress}, m_daddress{start_daddress}, m_size{size}, m_asid{asid} {
m_internal = std::make_unique<HeapMapperInternal>(host1x);
}
HeapMapper::~HeapMapper() {
m_internal->device_memory.Unmap(m_daddress, m_size);
}
DAddr HeapMapper::Map(VAddr start, size_t size) {
std::scoped_lock lk(m_internal->guard);
m_internal->base_set.clear();
const IntervalType interval{start, start + size};
m_internal->base_set.insert(interval);
m_internal->ForEachInOverlapCounter(m_internal->mapping_overlaps, start, size,
[this](VAddr start_addr, VAddr end_addr, int) {
const IntervalType other{start_addr, end_addr};
m_internal->base_set.subtract(other);
});
if (!m_internal->base_set.empty()) {
auto it = m_internal->base_set.begin();
auto end_it = m_internal->base_set.end();
for (; it != end_it; it++) {
const VAddr inter_addr_end = it->upper();
const VAddr inter_addr = it->lower();
const size_t offset = inter_addr - m_vaddress;
const size_t sub_size = inter_addr_end - inter_addr;
m_internal->device_memory.Map(m_daddress + offset, m_vaddress + offset, sub_size,
m_asid);
}
}
m_internal->mapping_overlaps += std::make_pair(interval, 1);
m_internal->base_set.clear();
return m_daddress + (start - m_vaddress);
}
void HeapMapper::Unmap(VAddr start, size_t size) {
std::scoped_lock lk(m_internal->guard);
m_internal->base_set.clear();
m_internal->ForEachInOverlapCounter(m_internal->mapping_overlaps, start, size,
[this](VAddr start_addr, VAddr end_addr, int value) {
if (value <= 1) {
const IntervalType other{start_addr, end_addr};
m_internal->base_set.insert(other);
}
});
if (!m_internal->base_set.empty()) {
auto it = m_internal->base_set.begin();
auto end_it = m_internal->base_set.end();
for (; it != end_it; it++) {
const VAddr inter_addr_end = it->upper();
const VAddr inter_addr = it->lower();
const size_t offset = inter_addr - m_vaddress;
const size_t sub_size = inter_addr_end - inter_addr;
m_internal->device_memory.Unmap(m_daddress + offset, sub_size);
}
}
const IntervalType to_remove{start, start + size};
m_internal->RemoveEachInOverlapCounter(m_internal->mapping_overlaps, to_remove, -1);
m_internal->base_set.clear();
}
} // namespace Service::Nvidia::NvCore

View file

@ -0,0 +1,49 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <memory>
#include "common/common_types.h"
#include "core/device_memory_manager.h"
namespace Tegra::Host1x {
class Host1x;
} // namespace Tegra::Host1x
namespace Service::Nvidia::NvCore {
class HeapMapper {
public:
HeapMapper(VAddr start_vaddress, DAddr start_daddress, size_t size, Core::Asid asid,
Tegra::Host1x::Host1x& host1x);
~HeapMapper();
bool IsInBounds(VAddr start, size_t size) const {
VAddr end = start + size;
return start >= m_vaddress && end <= (m_vaddress + m_size);
}
DAddr Map(VAddr start, size_t size);
void Unmap(VAddr start, size_t size);
DAddr GetRegionStart() const {
return m_daddress;
}
size_t GetRegionSize() const {
return m_size;
}
private:
struct HeapMapperInternal;
VAddr m_vaddress;
DAddr m_daddress;
size_t m_size;
Core::Asid m_asid;
std::unique_ptr<HeapMapperInternal> m_internal;
};
} // namespace Service::Nvidia::NvCore

View file

@ -2,14 +2,19 @@
// SPDX-FileCopyrightText: 2022 Skyline Team and Contributors
// SPDX-License-Identifier: GPL-3.0-or-later
#include <functional>
#include "common/alignment.h"
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/hle/service/nvdrv/core/container.h"
#include "core/hle/service/nvdrv/core/heap_mapper.h"
#include "core/hle/service/nvdrv/core/nvmap.h"
#include "core/memory.h"
#include "video_core/host1x/host1x.h"
using Core::Memory::YUZU_PAGESIZE;
constexpr size_t BIG_PAGE_SIZE = YUZU_PAGESIZE * 16;
namespace Service::Nvidia::NvCore {
NvMap::Handle::Handle(u64 size_, Id id_)
@ -17,9 +22,9 @@ NvMap::Handle::Handle(u64 size_, Id id_)
flags.raw = 0;
}
NvResult NvMap::Handle::Alloc(Flags pFlags, u32 pAlign, u8 pKind, u64 pAddress) {
NvResult NvMap::Handle::Alloc(Flags pFlags, u32 pAlign, u8 pKind, u64 pAddress,
NvCore::SessionId pSessionId) {
std::scoped_lock lock(mutex);
// Handles cannot be allocated twice
if (allocated) {
return NvResult::AccessDenied;
@ -28,6 +33,7 @@ NvResult NvMap::Handle::Alloc(Flags pFlags, u32 pAlign, u8 pKind, u64 pAddress)
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) {
@ -63,7 +69,7 @@ NvResult NvMap::Handle::Duplicate(bool internal_session) {
return NvResult::Success;
}
NvMap::NvMap(Tegra::Host1x::Host1x& host1x_) : host1x{host1x_} {}
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);
@ -78,12 +84,30 @@ void NvMap::UnmapHandle(Handle& handle_description) {
handle_description.unmap_queue_entry.reset();
}
// Free and unmap the handle from Host1x GMMU
if (handle_description.pin_virt_address) {
host1x.GMMU().Unmap(static_cast<GPUVAddr>(handle_description.pin_virt_address),
handle_description.aligned_size);
host1x.Allocator().Free(handle_description.pin_virt_address,
static_cast<u32>(handle_description.aligned_size));
handle_description.pin_virt_address = 0;
}
// Free and unmap the handle from the SMMU
host1x.MemoryManager().Unmap(static_cast<GPUVAddr>(handle_description.pin_virt_address),
handle_description.aligned_size);
host1x.Allocator().Free(handle_description.pin_virt_address,
static_cast<u32>(handle_description.aligned_size));
handle_description.pin_virt_address = 0;
const size_t map_size = handle_description.aligned_size;
if (!handle_description.in_heap) {
auto& smmu = host1x.MemoryManager();
size_t aligned_up = Common::AlignUp(map_size, BIG_PAGE_SIZE);
smmu.Unmap(handle_description.d_address, map_size);
smmu.Free(handle_description.d_address, static_cast<size_t>(aligned_up));
handle_description.d_address = 0;
return;
}
const VAddr vaddress = handle_description.address;
auto* session = core.GetSession(handle_description.session_id);
session->mapper->Unmap(vaddress, map_size);
handle_description.d_address = 0;
handle_description.in_heap = false;
}
bool NvMap::TryRemoveHandle(const Handle& handle_description) {
@ -124,22 +148,33 @@ std::shared_ptr<NvMap::Handle> NvMap::GetHandle(Handle::Id handle) {
}
}
VAddr NvMap::GetHandleAddress(Handle::Id handle) {
DAddr NvMap::GetHandleAddress(Handle::Id handle) {
std::scoped_lock lock(handles_lock);
try {
return handles.at(handle)->address;
return handles.at(handle)->d_address;
} catch (std::out_of_range&) {
return 0;
}
}
u32 NvMap::PinHandle(NvMap::Handle::Id handle) {
DAddr NvMap::PinHandle(NvMap::Handle::Id handle, bool low_area_pin) {
auto handle_description{GetHandle(handle)};
if (!handle_description) [[unlikely]] {
return 0;
}
std::scoped_lock lock(handle_description->mutex);
const auto map_low_area = [&] {
if (handle_description->pin_virt_address == 0) {
auto& gmmu_allocator = host1x.Allocator();
auto& gmmu = host1x.GMMU();
u32 address =
gmmu_allocator.Allocate(static_cast<u32>(handle_description->aligned_size));
gmmu.Map(static_cast<GPUVAddr>(address), handle_description->d_address,
handle_description->aligned_size);
handle_description->pin_virt_address = address;
}
};
if (!handle_description->pins) {
// If we're in the unmap queue we can just remove ourselves and return since we're already
// mapped
@ -151,37 +186,58 @@ u32 NvMap::PinHandle(NvMap::Handle::Id handle) {
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);
}
handle_description->pins++;
return handle_description->pin_virt_address;
return handle_description->d_address;
}
}
using namespace std::placeholders;
// If not then allocate some space and map it
u32 address{};
auto& smmu_allocator = host1x.Allocator();
auto& smmu_memory_manager = host1x.MemoryManager();
while ((address = smmu_allocator.Allocate(
static_cast<u32>(handle_description->aligned_size))) == 0) {
// Free handles until the allocation succeeds
std::scoped_lock queueLock(unmap_queue_lock);
if (auto freeHandleDesc{unmap_queue.front()}) {
// 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->pin_virt_address)
UnmapHandle(*freeHandleDesc);
} else {
LOG_CRITICAL(Service_NVDRV, "Ran out of SMMU address space!");
DAddr address{};
auto& smmu = host1x.MemoryManager();
auto* session = core.GetSession(handle_description->session_id);
const VAddr vaddress = handle_description->address;
const size_t map_size = handle_description->aligned_size;
if (session->has_preallocated_area && session->mapper->IsInBounds(vaddress, map_size)) {
handle_description->d_address = session->mapper->Map(vaddress, map_size);
handle_description->in_heap = true;
} else {
size_t aligned_up = Common::AlignUp(map_size, BIG_PAGE_SIZE);
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()}) {
// 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);
} else {
LOG_CRITICAL(Service_NVDRV, "Ran out of SMMU address space!");
}
}
}
smmu_memory_manager.Map(static_cast<GPUVAddr>(address), handle_description->address,
handle_description->aligned_size);
handle_description->pin_virt_address = address;
handle_description->d_address = address;
smmu.Map(address, vaddress, map_size, session->asid, true);
handle_description->in_heap = false;
}
}
if (low_area_pin) {
map_low_area();
}
handle_description->pins++;
return handle_description->pin_virt_address;
if (low_area_pin) {
return static_cast<DAddr>(handle_description->pin_virt_address);
}
return handle_description->d_address;
}
void NvMap::UnpinHandle(Handle::Id handle) {
@ -232,7 +288,7 @@ std::optional<NvMap::FreeInfo> NvMap::FreeHandle(Handle::Id handle, bool interna
LOG_WARNING(Service_NVDRV, "User duplicate count imbalance detected!");
} else if (handle_description->dupes == 0) {
// Force unmap the handle
if (handle_description->pin_virt_address) {
if (handle_description->d_address) {
std::scoped_lock queueLock(unmap_queue_lock);
UnmapHandle(*handle_description);
}

View file

@ -14,6 +14,7 @@
#include "common/bit_field.h"
#include "common/common_types.h"
#include "core/hle/service/nvdrv/core/container.h"
#include "core/hle/service/nvdrv/nvdata.h"
namespace Tegra {
@ -25,6 +26,8 @@ class Host1x;
} // namespace Tegra
namespace Service::Nvidia::NvCore {
class Container;
/**
* @brief The nvmap core class holds the global state for nvmap and provides methods to manage
* handles
@ -48,7 +51,7 @@ public:
using Id = u32;
Id id; //!< A globally unique identifier for this handle
s32 pins{};
s64 pins{};
u32 pin_virt_address{};
std::optional<typename std::list<std::shared_ptr<Handle>>::iterator> unmap_queue_entry{};
@ -61,15 +64,18 @@ public:
} flags{};
static_assert(sizeof(Flags) == sizeof(u32));
u64 address{}; //!< The memory location in the guest'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
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{};
u64 dma_map_addr{}; //! remove me after implementing pinning.
DAddr d_address{}; //!< The memory location in the device's AS that this handle corresponds
//!< to, this can also be in the nvdrv tmem
Handle(u64 size, Id id);
@ -77,7 +83,8 @@ public:
* @brief Sets up the handle with the given memory config, can allocate memory from the tmem
* if a 0 address is passed
*/
[[nodiscard]] NvResult Alloc(Flags pFlags, u32 pAlign, u8 pKind, u64 pAddress);
[[nodiscard]] NvResult Alloc(Flags pFlags, u32 pAlign, u8 pKind, u64 pAddress,
NvCore::SessionId pSessionId);
/**
* @brief Increases the dupe counter of the handle for the given session
@ -108,7 +115,7 @@ public:
bool can_unlock; //!< If the address region is ready to be unlocked
};
explicit NvMap(Tegra::Host1x::Host1x& host1x);
explicit NvMap(Container& core, Tegra::Host1x::Host1x& host1x);
/**
* @brief Creates an unallocated handle of the given size
@ -117,7 +124,7 @@ public:
std::shared_ptr<Handle> GetHandle(Handle::Id handle);
VAddr GetHandleAddress(Handle::Id handle);
DAddr GetHandleAddress(Handle::Id handle);
/**
* @brief Maps a handle into the SMMU address space
@ -125,7 +132,7 @@ public:
* number of calls to `UnpinHandle`
* @return The SMMU virtual address that the handle has been mapped to
*/
u32 PinHandle(Handle::Id handle);
DAddr PinHandle(Handle::Id handle, bool low_area_pin);
/**
* @brief When this has been called an equal number of times to `PinHandle` for the supplied
@ -172,5 +179,7 @@ private:
* @return If the handle was removed from the map
*/
bool TryRemoveHandle(const Handle& handle_description);
Container& core;
};
} // namespace Service::Nvidia::NvCore

View file

@ -7,6 +7,7 @@
#include <vector>
#include "common/common_types.h"
#include "core/hle/service/nvdrv/core/container.h"
#include "core/hle/service/nvdrv/nvdata.h"
namespace Core {
@ -62,7 +63,7 @@ public:
* Called once a device is opened
* @param fd The device fd
*/
virtual void OnOpen(DeviceFD fd) = 0;
virtual void OnOpen(NvCore::SessionId session_id, DeviceFD fd) = 0;
/**
* Called once a device is closed

View file

@ -35,14 +35,14 @@ NvResult nvdisp_disp0::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> in
return NvResult::NotImplemented;
}
void nvdisp_disp0::OnOpen(DeviceFD fd) {}
void nvdisp_disp0::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {}
void nvdisp_disp0::OnClose(DeviceFD fd) {}
void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, android::PixelFormat format, u32 width,
u32 height, u32 stride, android::BufferTransformFlags transform,
const Common::Rectangle<int>& crop_rect,
std::array<Service::Nvidia::NvFence, 4>& fences, u32 num_fences) {
const VAddr addr = nvmap.GetHandleAddress(buffer_handle);
const DAddr addr = nvmap.GetHandleAddress(buffer_handle);
LOG_TRACE(Service,
"Drawing from address {:X} offset {:08X} Width {} Height {} Stride {} Format {}",
addr, offset, width, height, stride, format);

View file

@ -32,7 +32,7 @@ public:
NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output,
std::span<u8> inline_output) override;
void OnOpen(DeviceFD fd) override;
void OnOpen(NvCore::SessionId session_id, DeviceFD fd) override;
void OnClose(DeviceFD fd) override;
/// Performs a screen flip, drawing the buffer pointed to by the handle.

View file

@ -86,7 +86,7 @@ NvResult nvhost_as_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> i
return NvResult::NotImplemented;
}
void nvhost_as_gpu::OnOpen(DeviceFD fd) {}
void nvhost_as_gpu::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {}
void nvhost_as_gpu::OnClose(DeviceFD fd) {}
NvResult nvhost_as_gpu::AllocAsEx(IoctlAllocAsEx& params) {
@ -206,6 +206,8 @@ void nvhost_as_gpu::FreeMappingLocked(u64 offset) {
static_cast<u32>(aligned_size >> page_size_bits));
}
nvmap.UnpinHandle(mapping->handle);
// Sparse mappings shouldn't be fully unmapped, just returned to their sparse state
// Only FreeSpace can unmap them fully
if (mapping->sparse_alloc) {
@ -293,12 +295,12 @@ NvResult nvhost_as_gpu::Remap(std::span<IoctlRemapEntry> entries) {
return NvResult::BadValue;
}
VAddr cpu_address{static_cast<VAddr>(
handle->address +
(static_cast<u64>(entry.handle_offset_big_pages) << vm.big_page_size_bits))};
DAddr base = nvmap.PinHandle(entry.handle, false);
DAddr device_address{static_cast<DAddr>(
base + (static_cast<u64>(entry.handle_offset_big_pages) << vm.big_page_size_bits))};
gmmu->Map(virtual_address, cpu_address, size, static_cast<Tegra::PTEKind>(entry.kind),
use_big_pages);
gmmu->Map(virtual_address, device_address, size,
static_cast<Tegra::PTEKind>(entry.kind), use_big_pages);
}
}
@ -331,9 +333,9 @@ NvResult nvhost_as_gpu::MapBufferEx(IoctlMapBufferEx& params) {
}
u64 gpu_address{static_cast<u64>(params.offset + params.buffer_offset)};
VAddr cpu_address{mapping->ptr + params.buffer_offset};
VAddr device_address{mapping->ptr + params.buffer_offset};
gmmu->Map(gpu_address, cpu_address, params.mapping_size,
gmmu->Map(gpu_address, device_address, params.mapping_size,
static_cast<Tegra::PTEKind>(params.kind), mapping->big_page);
return NvResult::Success;
@ -349,7 +351,8 @@ NvResult nvhost_as_gpu::MapBufferEx(IoctlMapBufferEx& params) {
return NvResult::BadValue;
}
VAddr cpu_address{static_cast<VAddr>(handle->address + params.buffer_offset)};
DAddr device_address{
static_cast<DAddr>(nvmap.PinHandle(params.handle, false) + params.buffer_offset)};
u64 size{params.mapping_size ? params.mapping_size : handle->orig_size};
bool big_page{[&]() {
@ -373,15 +376,14 @@ NvResult nvhost_as_gpu::MapBufferEx(IoctlMapBufferEx& params) {
}
const bool use_big_pages = alloc->second.big_pages && big_page;
gmmu->Map(params.offset, cpu_address, size, static_cast<Tegra::PTEKind>(params.kind),
gmmu->Map(params.offset, device_address, size, static_cast<Tegra::PTEKind>(params.kind),
use_big_pages);
auto mapping{std::make_shared<Mapping>(cpu_address, params.offset, size, true,
use_big_pages, alloc->second.sparse)};
auto mapping{std::make_shared<Mapping>(params.handle, device_address, params.offset, size,
true, use_big_pages, alloc->second.sparse)};
alloc->second.mappings.push_back(mapping);
mapping_map[params.offset] = mapping;
} else {
auto& allocator{big_page ? *vm.big_page_allocator : *vm.small_page_allocator};
u32 page_size{big_page ? vm.big_page_size : VM::YUZU_PAGESIZE};
u32 page_size_bits{big_page ? vm.big_page_size_bits : VM::PAGE_SIZE_BITS};
@ -394,11 +396,11 @@ NvResult nvhost_as_gpu::MapBufferEx(IoctlMapBufferEx& params) {
return NvResult::InsufficientMemory;
}
gmmu->Map(params.offset, cpu_address, Common::AlignUp(size, page_size),
gmmu->Map(params.offset, device_address, Common::AlignUp(size, page_size),
static_cast<Tegra::PTEKind>(params.kind), big_page);
auto mapping{
std::make_shared<Mapping>(cpu_address, params.offset, size, false, big_page, false)};
auto mapping{std::make_shared<Mapping>(params.handle, device_address, params.offset, size,
false, big_page, false)};
mapping_map[params.offset] = mapping;
}
@ -433,6 +435,8 @@ NvResult nvhost_as_gpu::UnmapBuffer(IoctlUnmapBuffer& params) {
gmmu->Unmap(params.offset, mapping->size);
}
nvmap.UnpinHandle(mapping->handle);
mapping_map.erase(params.offset);
} catch (const std::out_of_range&) {
LOG_WARNING(Service_NVDRV, "Couldn't find region to unmap at 0x{:X}", params.offset);

View file

@ -55,7 +55,7 @@ public:
NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output,
std::span<u8> inline_output) override;
void OnOpen(DeviceFD fd) override;
void OnOpen(NvCore::SessionId session_id, DeviceFD fd) override;
void OnClose(DeviceFD fd) override;
Kernel::KEvent* QueryEvent(u32 event_id) override;
@ -159,16 +159,18 @@ private:
NvCore::NvMap& nvmap;
struct Mapping {
VAddr ptr;
NvCore::NvMap::Handle::Id handle;
DAddr ptr;
u64 offset;
u64 size;
bool fixed;
bool big_page; // Only valid if fixed == false
bool sparse_alloc;
Mapping(VAddr ptr_, u64 offset_, u64 size_, bool fixed_, bool big_page_, bool sparse_alloc_)
: ptr(ptr_), offset(offset_), size(size_), fixed(fixed_), big_page(big_page_),
sparse_alloc(sparse_alloc_) {}
Mapping(NvCore::NvMap::Handle::Id handle_, DAddr ptr_, u64 offset_, u64 size_, bool fixed_,
bool big_page_, bool sparse_alloc_)
: handle(handle_), ptr(ptr_), offset(offset_), size(size_), fixed(fixed_),
big_page(big_page_), sparse_alloc(sparse_alloc_) {}
};
struct Allocation {
@ -212,9 +214,6 @@ private:
bool initialised{};
} vm;
std::shared_ptr<Tegra::MemoryManager> gmmu;
// s32 channel{};
// u32 big_page_size{VM::DEFAULT_BIG_PAGE_SIZE};
};
} // namespace Service::Nvidia::Devices

View file

@ -76,7 +76,7 @@ NvResult nvhost_ctrl::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> inp
return NvResult::NotImplemented;
}
void nvhost_ctrl::OnOpen(DeviceFD fd) {}
void nvhost_ctrl::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {}
void nvhost_ctrl::OnClose(DeviceFD fd) {}

View file

@ -32,7 +32,7 @@ public:
NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output,
std::span<u8> inline_output) override;
void OnOpen(DeviceFD fd) override;
void OnOpen(NvCore::SessionId session_id, DeviceFD fd) override;
void OnClose(DeviceFD fd) override;
Kernel::KEvent* QueryEvent(u32 event_id) override;

View file

@ -82,7 +82,7 @@ NvResult nvhost_ctrl_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8>
return NvResult::NotImplemented;
}
void nvhost_ctrl_gpu::OnOpen(DeviceFD fd) {}
void nvhost_ctrl_gpu::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {}
void nvhost_ctrl_gpu::OnClose(DeviceFD fd) {}
NvResult nvhost_ctrl_gpu::GetCharacteristics1(IoctlCharacteristics& params) {

View file

@ -28,7 +28,7 @@ public:
NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output,
std::span<u8> inline_output) override;
void OnOpen(DeviceFD fd) override;
void OnOpen(NvCore::SessionId session_id, DeviceFD fd) override;
void OnClose(DeviceFD fd) override;
Kernel::KEvent* QueryEvent(u32 event_id) override;

View file

@ -120,7 +120,7 @@ NvResult nvhost_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> inpu
return NvResult::NotImplemented;
}
void nvhost_gpu::OnOpen(DeviceFD fd) {}
void nvhost_gpu::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {}
void nvhost_gpu::OnClose(DeviceFD fd) {}
NvResult nvhost_gpu::SetNVMAPfd(IoctlSetNvmapFD& params) {

View file

@ -47,7 +47,7 @@ public:
NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output,
std::span<u8> inline_output) override;
void OnOpen(DeviceFD fd) override;
void OnOpen(NvCore::SessionId session_id, DeviceFD fd) override;
void OnClose(DeviceFD fd) override;
Kernel::KEvent* QueryEvent(u32 event_id) override;

View file

@ -35,7 +35,7 @@ NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> in
case 0x7:
return WrapFixed(this, &nvhost_nvdec::SetSubmitTimeout, input, output);
case 0x9:
return WrapFixedVariable(this, &nvhost_nvdec::MapBuffer, input, output);
return WrapFixedVariable(this, &nvhost_nvdec::MapBuffer, input, output, fd);
case 0xa:
return WrapFixedVariable(this, &nvhost_nvdec::UnmapBuffer, input, output);
default:
@ -68,9 +68,10 @@ NvResult nvhost_nvdec::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> in
return NvResult::NotImplemented;
}
void nvhost_nvdec::OnOpen(DeviceFD fd) {
void nvhost_nvdec::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {
LOG_INFO(Service_NVDRV, "NVDEC video stream started");
system.SetNVDECActive(true);
sessions[fd] = session_id;
}
void nvhost_nvdec::OnClose(DeviceFD fd) {
@ -81,6 +82,10 @@ void nvhost_nvdec::OnClose(DeviceFD fd) {
system.GPU().ClearCdmaInstance(iter->second);
}
system.SetNVDECActive(false);
auto it = sessions.find(fd);
if (it != sessions.end()) {
sessions.erase(it);
}
}
} // namespace Service::Nvidia::Devices

View file

@ -20,7 +20,7 @@ public:
NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output,
std::span<u8> inline_output) override;
void OnOpen(DeviceFD fd) override;
void OnOpen(NvCore::SessionId session_id, DeviceFD fd) override;
void OnClose(DeviceFD fd) override;
};

View file

@ -8,6 +8,7 @@
#include "common/common_types.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/service/nvdrv/core/container.h"
#include "core/hle/service/nvdrv/core/nvmap.h"
#include "core/hle/service/nvdrv/core/syncpoint_manager.h"
@ -95,6 +96,8 @@ NvResult nvhost_nvdec_common::Submit(IoctlSubmit& params, std::span<u8> data, De
offset += SliceVectors(data, fence_thresholds, params.fence_count, offset);
auto& gpu = system.GPU();
auto* session = core.GetSession(sessions[fd]);
if (gpu.UseNvdec()) {
for (std::size_t i = 0; i < syncpt_increments.size(); i++) {
const SyncptIncr& syncpt_incr = syncpt_increments[i];
@ -106,8 +109,8 @@ NvResult nvhost_nvdec_common::Submit(IoctlSubmit& params, std::span<u8> data, De
const auto object = nvmap.GetHandle(cmd_buffer.memory_id);
ASSERT_OR_EXECUTE(object, return NvResult::InvalidState;);
Tegra::ChCommandHeaderList cmdlist(cmd_buffer.word_count);
system.ApplicationMemory().ReadBlock(object->address + cmd_buffer.offset, cmdlist.data(),
cmdlist.size() * sizeof(u32));
session->process->GetMemory().ReadBlock(object->address + cmd_buffer.offset, cmdlist.data(),
cmdlist.size() * sizeof(u32));
gpu.PushCommandBuffer(core.Host1xDeviceFile().fd_to_id[fd], cmdlist);
}
// Some games expect command_buffers to be written back
@ -133,10 +136,12 @@ NvResult nvhost_nvdec_common::GetWaitbase(IoctlGetWaitbase& params) {
return NvResult::Success;
}
NvResult nvhost_nvdec_common::MapBuffer(IoctlMapBuffer& params, std::span<MapBufferEntry> entries) {
NvResult nvhost_nvdec_common::MapBuffer(IoctlMapBuffer& params, std::span<MapBufferEntry> entries,
DeviceFD fd) {
const size_t num_entries = std::min(params.num_entries, static_cast<u32>(entries.size()));
for (size_t i = 0; i < num_entries; i++) {
entries[i].map_address = nvmap.PinHandle(entries[i].map_handle);
DAddr pin_address = nvmap.PinHandle(entries[i].map_handle, true);
entries[i].map_address = static_cast<u32>(pin_address);
}
return NvResult::Success;

View file

@ -4,7 +4,9 @@
#pragma once
#include <deque>
#include <unordered_map>
#include <vector>
#include "common/common_types.h"
#include "common/swap.h"
#include "core/hle/service/nvdrv/core/syncpoint_manager.h"
@ -111,7 +113,7 @@ protected:
NvResult Submit(IoctlSubmit& params, std::span<u8> input, DeviceFD fd);
NvResult GetSyncpoint(IoctlGetSyncpoint& params);
NvResult GetWaitbase(IoctlGetWaitbase& params);
NvResult MapBuffer(IoctlMapBuffer& params, std::span<MapBufferEntry> entries);
NvResult MapBuffer(IoctlMapBuffer& params, std::span<MapBufferEntry> entries, DeviceFD fd);
NvResult UnmapBuffer(IoctlMapBuffer& params, std::span<MapBufferEntry> entries);
NvResult SetSubmitTimeout(u32 timeout);
@ -125,6 +127,7 @@ protected:
NvCore::NvMap& nvmap;
NvCore::ChannelType channel_type;
std::array<u32, MaxSyncPoints> device_syncpoints{};
std::unordered_map<DeviceFD, NvCore::SessionId> sessions;
};
}; // namespace Devices
} // namespace Service::Nvidia

View file

@ -44,7 +44,7 @@ NvResult nvhost_nvjpg::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> in
return NvResult::NotImplemented;
}
void nvhost_nvjpg::OnOpen(DeviceFD fd) {}
void nvhost_nvjpg::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {}
void nvhost_nvjpg::OnClose(DeviceFD fd) {}
NvResult nvhost_nvjpg::SetNVMAPfd(IoctlSetNvmapFD& params) {

View file

@ -22,7 +22,7 @@ public:
NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output,
std::span<u8> inline_output) override;
void OnOpen(DeviceFD fd) override;
void OnOpen(NvCore::SessionId session_id, DeviceFD fd) override;
void OnClose(DeviceFD fd) override;
private:

View file

@ -33,7 +33,7 @@ NvResult nvhost_vic::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> inpu
case 0x3:
return WrapFixed(this, &nvhost_vic::GetWaitbase, input, output);
case 0x9:
return WrapFixedVariable(this, &nvhost_vic::MapBuffer, input, output);
return WrapFixedVariable(this, &nvhost_vic::MapBuffer, input, output, fd);
case 0xa:
return WrapFixedVariable(this, &nvhost_vic::UnmapBuffer, input, output);
default:
@ -68,7 +68,9 @@ NvResult nvhost_vic::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> inpu
return NvResult::NotImplemented;
}
void nvhost_vic::OnOpen(DeviceFD fd) {}
void nvhost_vic::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {
sessions[fd] = session_id;
}
void nvhost_vic::OnClose(DeviceFD fd) {
auto& host1x_file = core.Host1xDeviceFile();
@ -76,6 +78,7 @@ void nvhost_vic::OnClose(DeviceFD fd) {
if (iter != host1x_file.fd_to_id.end()) {
system.GPU().ClearCdmaInstance(iter->second);
}
sessions.erase(fd);
}
} // namespace Service::Nvidia::Devices

View file

@ -19,7 +19,7 @@ public:
NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output,
std::span<u8> inline_output) override;
void OnOpen(DeviceFD fd) override;
void OnOpen(NvCore::SessionId session_id, DeviceFD fd) override;
void OnClose(DeviceFD fd) override;
};
} // namespace Service::Nvidia::Devices

View file

@ -36,9 +36,9 @@ NvResult nvmap::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
case 0x3:
return WrapFixed(this, &nvmap::IocFromId, input, output);
case 0x4:
return WrapFixed(this, &nvmap::IocAlloc, input, output);
return WrapFixed(this, &nvmap::IocAlloc, input, output, fd);
case 0x5:
return WrapFixed(this, &nvmap::IocFree, input, output);
return WrapFixed(this, &nvmap::IocFree, input, output, fd);
case 0x9:
return WrapFixed(this, &nvmap::IocParam, input, output);
case 0xe:
@ -67,8 +67,15 @@ NvResult nvmap::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, st
return NvResult::NotImplemented;
}
void nvmap::OnOpen(DeviceFD fd) {}
void nvmap::OnClose(DeviceFD fd) {}
void nvmap::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {
sessions[fd] = session_id;
}
void nvmap::OnClose(DeviceFD fd) {
auto it = sessions.find(fd);
if (it != sessions.end()) {
sessions.erase(it);
}
}
NvResult nvmap::IocCreate(IocCreateParams& params) {
LOG_DEBUG(Service_NVDRV, "called, size=0x{:08X}", params.size);
@ -87,7 +94,7 @@ NvResult nvmap::IocCreate(IocCreateParams& params) {
return NvResult::Success;
}
NvResult nvmap::IocAlloc(IocAllocParams& params) {
NvResult nvmap::IocAlloc(IocAllocParams& params, DeviceFD fd) {
LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.address);
if (!params.handle) {
@ -116,15 +123,15 @@ NvResult nvmap::IocAlloc(IocAllocParams& params) {
return NvResult::InsufficientMemory;
}
const auto result =
handle_description->Alloc(params.flags, params.align, params.kind, params.address);
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{};
ASSERT(system.ApplicationProcess()
->GetPageTable()
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)
@ -224,7 +231,7 @@ NvResult nvmap::IocParam(IocParamParams& params) {
return NvResult::Success;
}
NvResult nvmap::IocFree(IocFreeParams& params) {
NvResult nvmap::IocFree(IocFreeParams& params, DeviceFD fd) {
LOG_DEBUG(Service_NVDRV, "called");
if (!params.handle) {
@ -233,9 +240,9 @@ NvResult nvmap::IocFree(IocFreeParams& params) {
}
if (auto freeInfo{file.FreeHandle(params.handle, false)}) {
auto process = container.GetSession(sessions[fd])->process;
if (freeInfo->can_unlock) {
ASSERT(system.ApplicationProcess()
->GetPageTable()
ASSERT(process->GetPageTable()
.UnlockForDeviceAddressSpace(freeInfo->address, freeInfo->size)
.IsSuccess());
}

View file

@ -33,7 +33,7 @@ public:
NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output,
std::span<u8> inline_output) override;
void OnOpen(DeviceFD fd) override;
void OnOpen(NvCore::SessionId session_id, DeviceFD fd) override;
void OnClose(DeviceFD fd) override;
enum class HandleParameterType : u32_le {
@ -100,11 +100,11 @@ public:
static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size");
NvResult IocCreate(IocCreateParams& params);
NvResult IocAlloc(IocAllocParams& params);
NvResult IocAlloc(IocAllocParams& params, DeviceFD fd);
NvResult IocGetId(IocGetIdParams& params);
NvResult IocFromId(IocFromIdParams& params);
NvResult IocParam(IocParamParams& params);
NvResult IocFree(IocFreeParams& params);
NvResult IocFree(IocFreeParams& params, DeviceFD fd);
private:
/// Id to use for the next handle that is created.
@ -115,6 +115,7 @@ private:
NvCore::Container& container;
NvCore::NvMap& file;
std::unordered_map<DeviceFD, NvCore::SessionId> sessions;
};
} // namespace Service::Nvidia::Devices

View file

@ -45,13 +45,22 @@ void EventInterface::FreeEvent(Kernel::KEvent* event) {
void LoopProcess(Nvnflinger::Nvnflinger& nvnflinger, Core::System& system) {
auto server_manager = std::make_unique<ServerManager>(system);
auto module = std::make_shared<Module>(system);
server_manager->RegisterNamedService("nvdrv", std::make_shared<NVDRV>(system, module, "nvdrv"));
server_manager->RegisterNamedService("nvdrv:a",
std::make_shared<NVDRV>(system, module, "nvdrv:a"));
server_manager->RegisterNamedService("nvdrv:s",
std::make_shared<NVDRV>(system, module, "nvdrv:s"));
server_manager->RegisterNamedService("nvdrv:t",
std::make_shared<NVDRV>(system, module, "nvdrv:t"));
const auto NvdrvInterfaceFactoryForApplication = [&, module] {
return std::make_shared<NVDRV>(system, module, "nvdrv");
};
const auto NvdrvInterfaceFactoryForApplets = [&, module] {
return std::make_shared<NVDRV>(system, module, "nvdrv:a");
};
const auto NvdrvInterfaceFactoryForSysmodules = [&, module] {
return std::make_shared<NVDRV>(system, module, "nvdrv:s");
};
const auto NvdrvInterfaceFactoryForTesting = [&, module] {
return std::make_shared<NVDRV>(system, module, "nvdrv:t");
};
server_manager->RegisterNamedService("nvdrv", NvdrvInterfaceFactoryForApplication);
server_manager->RegisterNamedService("nvdrv:a", NvdrvInterfaceFactoryForApplets);
server_manager->RegisterNamedService("nvdrv:s", NvdrvInterfaceFactoryForSysmodules);
server_manager->RegisterNamedService("nvdrv:t", NvdrvInterfaceFactoryForTesting);
server_manager->RegisterNamedService("nvmemp", std::make_shared<NVMEMP>(system));
nvnflinger.SetNVDrvInstance(module);
ServerManager::RunServer(std::move(server_manager));
@ -113,7 +122,7 @@ NvResult Module::VerifyFD(DeviceFD fd) const {
return NvResult::Success;
}
DeviceFD Module::Open(const std::string& device_name) {
DeviceFD Module::Open(const std::string& device_name, NvCore::SessionId session_id) {
auto it = builders.find(device_name);
if (it == builders.end()) {
LOG_ERROR(Service_NVDRV, "Trying to open unknown device {}", device_name);
@ -124,7 +133,7 @@ DeviceFD Module::Open(const std::string& device_name) {
auto& builder = it->second;
auto device = builder(fd)->second;
device->OnOpen(fd);
device->OnOpen(session_id, fd);
return fd;
}

View file

@ -77,7 +77,7 @@ public:
NvResult VerifyFD(DeviceFD fd) const;
/// Opens a device node and returns a file descriptor to it.
DeviceFD Open(const std::string& device_name);
DeviceFD Open(const std::string& device_name, NvCore::SessionId session_id);
/// Sends an ioctl command to the specified file descriptor.
NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output);
@ -93,6 +93,10 @@ public:
NvResult QueryEvent(DeviceFD fd, u32 event_id, Kernel::KEvent*& event);
NvCore::Container& GetContainer() {
return container;
}
private:
friend class EventInterface;
friend class Service::Nvnflinger::Nvnflinger;

View file

@ -3,8 +3,10 @@
// SPDX-License-Identifier: GPL-3.0-or-later
#include "common/logging/log.h"
#include "common/scope_exit.h"
#include "core/core.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/nvdrv/nvdata.h"
@ -37,7 +39,7 @@ void NVDRV::Open(HLERequestContext& ctx) {
return;
}
DeviceFD fd = nvdrv->Open(device_name);
DeviceFD fd = nvdrv->Open(device_name, session_id);
rb.Push<DeviceFD>(fd);
rb.PushEnum(fd != INVALID_NVDRV_FD ? NvResult::Success : NvResult::FileOperationFailed);
@ -150,12 +152,29 @@ void NVDRV::Close(HLERequestContext& ctx) {
void NVDRV::Initialize(HLERequestContext& ctx) {
LOG_WARNING(Service_NVDRV, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
SCOPE_EXIT({
rb.Push(ResultSuccess);
rb.PushEnum(NvResult::Success);
});
if (is_initialized) {
// No need to initialize again
return;
}
IPC::RequestParser rp{ctx};
const auto process_handle{ctx.GetCopyHandle(0)};
// The transfer memory is lent to nvdrv as a work buffer since nvdrv is
// unable to allocate as much memory on its own. For HLE it's unnecessary to handle it
[[maybe_unused]] const auto transfer_memory_handle{ctx.GetCopyHandle(1)};
[[maybe_unused]] const auto transfer_memory_size = rp.Pop<u32>();
auto& container = nvdrv->GetContainer();
auto process = ctx.GetObjectFromHandle<Kernel::KProcess>(process_handle);
session_id = container.OpenSession(process.GetPointerUnsafe());
is_initialized = true;
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushEnum(NvResult::Success);
}
void NVDRV::QueryEvent(HLERequestContext& ctx) {
@ -242,6 +261,9 @@ NVDRV::NVDRV(Core::System& system_, std::shared_ptr<Module> nvdrv_, const char*
RegisterHandlers(functions);
}
NVDRV::~NVDRV() = default;
NVDRV::~NVDRV() {
auto& container = nvdrv->GetContainer();
container.CloseSession(session_id);
}
} // namespace Service::Nvidia

View file

@ -35,6 +35,7 @@ private:
u64 pid{};
bool is_initialized{};
NvCore::SessionId session_id{};
Common::ScratchBuffer<u8> output_buffer;
Common::ScratchBuffer<u8> inline_output_buffer;
};

View file

@ -87,19 +87,20 @@ Result CreateNvMapHandle(u32* out_nv_map_handle, Nvidia::Devices::nvmap& nvmap,
R_SUCCEED();
}
Result FreeNvMapHandle(Nvidia::Devices::nvmap& nvmap, u32 handle) {
Result FreeNvMapHandle(Nvidia::Devices::nvmap& nvmap, u32 handle, Nvidia::DeviceFD nvmap_fd) {
// Free the handle.
Nvidia::Devices::nvmap::IocFreeParams free_params{
.handle = handle,
};
R_UNLESS(nvmap.IocFree(free_params) == Nvidia::NvResult::Success, VI::ResultOperationFailed);
R_UNLESS(nvmap.IocFree(free_params, nvmap_fd) == Nvidia::NvResult::Success,
VI::ResultOperationFailed);
// We succeeded.
R_SUCCEED();
}
Result AllocNvMapHandle(Nvidia::Devices::nvmap& nvmap, u32 handle, Common::ProcessAddress buffer,
u32 size) {
u32 size, Nvidia::DeviceFD nvmap_fd) {
// Assign the allocated memory to the handle.
Nvidia::Devices::nvmap::IocAllocParams alloc_params{
.handle = handle,
@ -109,16 +110,16 @@ Result AllocNvMapHandle(Nvidia::Devices::nvmap& nvmap, u32 handle, Common::Proce
.kind = 0,
.address = GetInteger(buffer),
};
R_UNLESS(nvmap.IocAlloc(alloc_params) == Nvidia::NvResult::Success, VI::ResultOperationFailed);
R_UNLESS(nvmap.IocAlloc(alloc_params, nvmap_fd) == Nvidia::NvResult::Success,
VI::ResultOperationFailed);
// We succeeded.
R_SUCCEED();
}
Result AllocateHandleForBuffer(u32* out_handle, Nvidia::Module& nvdrv,
Result AllocateHandleForBuffer(u32* out_handle, Nvidia::Module& nvdrv, Nvidia::DeviceFD nvmap_fd,
Common::ProcessAddress buffer, u32 size) {
// Get the nvmap device.
auto nvmap_fd = nvdrv.Open("/dev/nvmap");
auto nvmap = nvdrv.GetDevice<Nvidia::Devices::nvmap>(nvmap_fd);
ASSERT(nvmap != nullptr);
@ -127,11 +128,11 @@ Result AllocateHandleForBuffer(u32* out_handle, Nvidia::Module& nvdrv,
// Ensure we maintain a clean state on failure.
ON_RESULT_FAILURE {
ASSERT(R_SUCCEEDED(FreeNvMapHandle(*nvmap, *out_handle)));
ASSERT(R_SUCCEEDED(FreeNvMapHandle(*nvmap, *out_handle, nvmap_fd)));
};
// Assign the allocated memory to the handle.
R_RETURN(AllocNvMapHandle(*nvmap, *out_handle, buffer, size));
R_RETURN(AllocNvMapHandle(*nvmap, *out_handle, buffer, size, nvmap_fd));
}
constexpr auto SharedBufferBlockLinearFormat = android::PixelFormat::Rgba8888;
@ -197,9 +198,13 @@ Result FbShareBufferManager::Initialize(u64* out_buffer_id, u64* out_layer_id, u
std::addressof(m_buffer_page_group), m_system,
SharedBufferSize));
auto& container = m_nvdrv->GetContainer();
m_session_id = container.OpenSession(m_system.ApplicationProcess());
m_nvmap_fd = m_nvdrv->Open("/dev/nvmap", m_session_id);
// Create an nvmap handle for the buffer and assign the memory to it.
R_TRY(AllocateHandleForBuffer(std::addressof(m_buffer_nvmap_handle), *m_nvdrv, map_address,
SharedBufferSize));
R_TRY(AllocateHandleForBuffer(std::addressof(m_buffer_nvmap_handle), *m_nvdrv, m_nvmap_fd,
map_address, SharedBufferSize));
// Record the display id.
m_display_id = display_id;

View file

@ -4,6 +4,8 @@
#pragma once
#include "common/math_util.h"
#include "core/hle/service/nvdrv/core/container.h"
#include "core/hle/service/nvdrv/nvdata.h"
#include "core/hle/service/nvnflinger/nvnflinger.h"
#include "core/hle/service/nvnflinger/ui/fence.h"
@ -53,7 +55,8 @@ private:
u64 m_layer_id = 0;
u32 m_buffer_nvmap_handle = 0;
SharedMemoryPoolLayout m_pool_layout = {};
Nvidia::DeviceFD m_nvmap_fd = {};
Nvidia::NvCore::SessionId m_session_id = {};
std::unique_ptr<Kernel::KPageGroup> m_buffer_page_group;
std::mutex m_guard;

View file

@ -112,9 +112,7 @@ void Nvnflinger::ShutdownLayers() {
{
const auto lock_guard = Lock();
for (auto& display : displays) {
for (size_t layer = 0; layer < display.GetNumLayers(); ++layer) {
display.GetLayer(layer).GetConsumer().Abandon();
}
display.Abandon();
}
is_abandoned = true;
@ -126,7 +124,7 @@ void Nvnflinger::ShutdownLayers() {
void Nvnflinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) {
nvdrv = std::move(instance);
disp_fd = nvdrv->Open("/dev/nvdisp_disp0");
disp_fd = nvdrv->Open("/dev/nvdisp_disp0", {});
}
std::optional<u64> Nvnflinger::OpenDisplay(std::string_view name) {
@ -176,24 +174,28 @@ void Nvnflinger::CreateLayerAtId(VI::Display& display, u64 layer_id) {
display.CreateLayer(layer_id, buffer_id, nvdrv->container);
}
void Nvnflinger::OpenLayer(u64 layer_id) {
bool Nvnflinger::OpenLayer(u64 layer_id) {
const auto lock_guard = Lock();
for (auto& display : displays) {
if (auto* layer = display.FindLayer(layer_id); layer) {
layer->Open();
return layer->Open();
}
}
return false;
}
void Nvnflinger::CloseLayer(u64 layer_id) {
bool Nvnflinger::CloseLayer(u64 layer_id) {
const auto lock_guard = Lock();
for (auto& display : displays) {
if (auto* layer = display.FindLayer(layer_id); layer) {
layer->Close();
return layer->Close();
}
}
return false;
}
void Nvnflinger::DestroyLayer(u64 layer_id) {

View file

@ -74,10 +74,10 @@ public:
[[nodiscard]] std::optional<u64> CreateLayer(u64 display_id);
/// Opens a layer on all displays for the given layer ID.
void OpenLayer(u64 layer_id);
bool OpenLayer(u64 layer_id);
/// Closes a layer on all displays for the given layer ID.
void CloseLayer(u64 layer_id);
bool CloseLayer(u64 layer_id);
/// Destroys the given layer ID.
void DestroyLayer(u64 layer_id);

View file

@ -22,11 +22,13 @@ GraphicBuffer::GraphicBuffer(Service::Nvidia::NvCore::NvMap& nvmap,
: NvGraphicBuffer(GetBuffer(buffer)), m_nvmap(std::addressof(nvmap)) {
if (this->BufferId() > 0) {
m_nvmap->DuplicateHandle(this->BufferId(), true);
m_nvmap->PinHandle(this->BufferId(), false);
}
}
GraphicBuffer::~GraphicBuffer() {
if (m_nvmap != nullptr && this->BufferId() > 0) {
m_nvmap->UnpinHandle(this->BufferId());
m_nvmap->FreeHandle(this->BufferId(), true);
}
}

View file

@ -91,6 +91,10 @@ void Display::CreateLayer(u64 layer_id, u32 binder_id,
layers.emplace_back(std::make_unique<Layer>(layer_id, binder_id, *core, *producer,
std::move(buffer_item_consumer)));
if (is_abandoned) {
this->FindLayer(layer_id)->GetConsumer().Abandon();
}
hos_binder_driver_server.RegisterProducer(std::move(producer));
}
@ -103,6 +107,13 @@ void Display::DestroyLayer(u64 layer_id) {
[layer_id](const auto& layer) { return layer->GetLayerId() == layer_id; });
}
void Display::Abandon() {
for (auto& layer : layers) {
layer->GetConsumer().Abandon();
}
is_abandoned = true;
}
Layer* Display::FindLayer(u64 layer_id) {
const auto itr =
std::find_if(layers.begin(), layers.end(), [layer_id](const std::unique_ptr<Layer>& layer) {

View file

@ -98,6 +98,8 @@ public:
layers.clear();
}
void Abandon();
/// Attempts to find a layer with the given ID.
///
/// @param layer_id The layer ID.
@ -124,6 +126,7 @@ private:
std::vector<std::unique_ptr<Layer>> layers;
Kernel::KEvent* vsync_event{};
bool is_abandoned{};
};
} // namespace Service::VI

View file

@ -4,6 +4,7 @@
#pragma once
#include <memory>
#include <utility>
#include "common/common_types.h"
@ -75,12 +76,12 @@ public:
return open;
}
void Close() {
open = false;
bool Close() {
return std::exchange(open, false);
}
void Open() {
open = true;
bool Open() {
return !std::exchange(open, true);
}
private:

View file

@ -719,7 +719,12 @@ private:
return;
}
nvnflinger.OpenLayer(layer_id);
if (!nvnflinger.OpenLayer(layer_id)) {
LOG_WARNING(Service_VI, "Tried to open layer which was already open");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultOperationFailed);
return;
}
android::OutputParcel parcel;
parcel.WriteInterface(NativeWindow{*buffer_queue_id});
@ -737,7 +742,12 @@ private:
LOG_DEBUG(Service_VI, "called. layer_id=0x{:016X}", layer_id);
nvnflinger.CloseLayer(layer_id);
if (!nvnflinger.CloseLayer(layer_id)) {
LOG_WARNING(Service_VI, "Tried to close layer which was not open");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultOperationFailed);
return;
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);

View file

@ -24,6 +24,8 @@
#include "core/hle/kernel/k_process.h"
#include "core/memory.h"
#include "video_core/gpu.h"
#include "video_core/host1x/gpu_device_memory_manager.h"
#include "video_core/host1x/host1x.h"
#include "video_core/rasterizer_download_area.h"
namespace Core::Memory {
@ -637,17 +639,6 @@ struct Memory::Impl {
LOG_DEBUG(HW_Memory, "Mapping {:016X} onto {:016X}-{:016X}", GetInteger(target),
base * YUZU_PAGESIZE, (base + size) * YUZU_PAGESIZE);
// During boot, current_page_table might not be set yet, in which case we need not flush
if (system.IsPoweredOn()) {
auto& gpu = system.GPU();
for (u64 i = 0; i < size; i++) {
const auto page = base + i;
if (page_table.pointers[page].Type() == Common::PageType::RasterizerCachedMemory) {
gpu.FlushAndInvalidateRegion(page << YUZU_PAGEBITS, YUZU_PAGESIZE);
}
}
}
const auto end = base + size;
ASSERT_MSG(end <= page_table.pointers.size(), "out of range mapping at {:016X}",
base + page_table.pointers.size());
@ -811,21 +802,33 @@ struct Memory::Impl {
return true;
}
void HandleRasterizerDownload(VAddr address, size_t size) {
void HandleRasterizerDownload(VAddr v_address, size_t size) {
const auto* p = GetPointerImpl(
v_address, []() {}, []() {});
if (!gpu_device_memory) [[unlikely]] {
gpu_device_memory = &system.Host1x().MemoryManager();
}
const size_t core = system.GetCurrentHostThreadID();
auto& current_area = rasterizer_read_areas[core];
const VAddr end_address = address + size;
if (current_area.start_address <= address && end_address <= current_area.end_address)
[[likely]] {
return;
}
current_area = system.GPU().OnCPURead(address, size);
gpu_device_memory->ApplyOpOnPointer(p, scratch_buffers[core], [&](DAddr address) {
const DAddr end_address = address + size;
if (current_area.start_address <= address && end_address <= current_area.end_address)
[[likely]] {
return;
}
current_area = system.GPU().OnCPURead(address, size);
});
}
void HandleRasterizerWrite(VAddr address, size_t size) {
void HandleRasterizerWrite(VAddr v_address, size_t size) {
const auto* p = GetPointerImpl(
v_address, []() {}, []() {});
constexpr size_t sys_core = Core::Hardware::NUM_CPU_CORES - 1;
const size_t core = std::min(system.GetCurrentHostThreadID(),
sys_core); // any other calls threads go to syscore.
if (!gpu_device_memory) [[unlikely]] {
gpu_device_memory = &system.Host1x().MemoryManager();
}
// Guard on sys_core;
if (core == sys_core) [[unlikely]] {
sys_core_guard.lock();
@ -835,36 +838,53 @@ struct Memory::Impl {
sys_core_guard.unlock();
}
});
auto& current_area = rasterizer_write_areas[core];
VAddr subaddress = address >> YUZU_PAGEBITS;
bool do_collection = current_area.last_address == subaddress;
if (!do_collection) [[unlikely]] {
do_collection = system.GPU().OnCPUWrite(address, size);
if (!do_collection) {
return;
gpu_device_memory->ApplyOpOnPointer(p, scratch_buffers[core], [&](DAddr address) {
auto& current_area = rasterizer_write_areas[core];
PAddr subaddress = address >> YUZU_PAGEBITS;
bool do_collection = current_area.last_address == subaddress;
if (!do_collection) [[unlikely]] {
do_collection = system.GPU().OnCPUWrite(address, size);
if (!do_collection) {
return;
}
current_area.last_address = subaddress;
}
current_area.last_address = subaddress;
}
gpu_dirty_managers[core].Collect(address, size);
gpu_dirty_managers[core].Collect(address, size);
});
}
struct GPUDirtyState {
VAddr last_address;
PAddr last_address;
};
void InvalidateRegion(Common::ProcessAddress dest_addr, size_t size) {
system.GPU().InvalidateRegion(GetInteger(dest_addr), size);
}
void FlushRegion(Common::ProcessAddress dest_addr, size_t size) {
system.GPU().FlushRegion(GetInteger(dest_addr), size);
void InvalidateGPUMemory(u8* p, size_t size) {
constexpr size_t sys_core = Core::Hardware::NUM_CPU_CORES - 1;
const size_t core = std::min(system.GetCurrentHostThreadID(),
sys_core); // any other calls threads go to syscore.
if (!gpu_device_memory) [[unlikely]] {
gpu_device_memory = &system.Host1x().MemoryManager();
}
// Guard on sys_core;
if (core == sys_core) [[unlikely]] {
sys_core_guard.lock();
}
SCOPE_EXIT({
if (core == sys_core) [[unlikely]] {
sys_core_guard.unlock();
}
});
auto& gpu = system.GPU();
gpu_device_memory->ApplyOpOnPointer(
p, scratch_buffers[core], [&](DAddr address) { gpu.InvalidateRegion(address, size); });
}
Core::System& system;
Tegra::MaxwellDeviceMemoryManager* gpu_device_memory{};
Common::PageTable* current_page_table = nullptr;
std::array<VideoCore::RasterizerDownloadArea, Core::Hardware::NUM_CPU_CORES>
rasterizer_read_areas{};
std::array<GPUDirtyState, Core::Hardware::NUM_CPU_CORES> rasterizer_write_areas{};
std::array<Common::ScratchBuffer<u32>, Core::Hardware::NUM_CPU_CORES> scratch_buffers{};
std::span<Core::GPUDirtyMemoryManager> gpu_dirty_managers;
std::mutex sys_core_guard;
@ -1059,14 +1079,6 @@ void Memory::MarkRegionDebug(Common::ProcessAddress vaddr, u64 size, bool debug)
impl->MarkRegionDebug(GetInteger(vaddr), size, debug);
}
void Memory::InvalidateRegion(Common::ProcessAddress dest_addr, size_t size) {
impl->InvalidateRegion(dest_addr, size);
}
void Memory::FlushRegion(Common::ProcessAddress dest_addr, size_t size) {
impl->FlushRegion(dest_addr, size);
}
bool Memory::InvalidateNCE(Common::ProcessAddress vaddr, size_t size) {
[[maybe_unused]] bool mapped = true;
[[maybe_unused]] bool rasterizer = false;
@ -1078,10 +1090,10 @@ bool Memory::InvalidateNCE(Common::ProcessAddress vaddr, size_t size) {
GetInteger(vaddr));
mapped = false;
},
[&] {
impl->system.GPU().InvalidateRegion(GetInteger(vaddr), size);
rasterizer = true;
});
[&] { rasterizer = true; });
if (rasterizer) {
impl->InvalidateGPUMemory(ptr, size);
}
#ifdef __linux__
if (!rasterizer && mapped) {

View file

@ -12,6 +12,7 @@
#include "common/scratch_buffer.h"
#include "common/typed_address.h"
#include "core/guest_memory.h"
#include "core/hle/result.h"
namespace Common {
@ -486,10 +487,10 @@ public:
void MarkRegionDebug(Common::ProcessAddress vaddr, u64 size, bool debug);
void SetGPUDirtyManagers(std::span<Core::GPUDirtyMemoryManager> managers);
void InvalidateRegion(Common::ProcessAddress dest_addr, size_t size);
bool InvalidateNCE(Common::ProcessAddress vaddr, size_t size);
bool InvalidateSeparateHeap(void* fault_address);
void FlushRegion(Common::ProcessAddress dest_addr, size_t size);
private:
Core::System& system;
@ -498,209 +499,9 @@ private:
std::unique_ptr<Impl> impl;
};
enum GuestMemoryFlags : u32 {
Read = 1 << 0,
Write = 1 << 1,
Safe = 1 << 2,
Cached = 1 << 3,
SafeRead = Read | Safe,
SafeWrite = Write | Safe,
SafeReadWrite = SafeRead | SafeWrite,
SafeReadCachedWrite = SafeReadWrite | Cached,
UnsafeRead = Read,
UnsafeWrite = Write,
UnsafeReadWrite = UnsafeRead | UnsafeWrite,
UnsafeReadCachedWrite = UnsafeReadWrite | Cached,
};
namespace {
template <typename M, typename T, GuestMemoryFlags FLAGS>
class GuestMemory {
using iterator = T*;
using const_iterator = const T*;
using value_type = T;
using element_type = T;
using iterator_category = std::contiguous_iterator_tag;
public:
GuestMemory() = delete;
explicit GuestMemory(M& memory, u64 addr, std::size_t size,
Common::ScratchBuffer<T>* backup = nullptr)
: m_memory{memory}, m_addr{addr}, m_size{size} {
static_assert(FLAGS & GuestMemoryFlags::Read || FLAGS & GuestMemoryFlags::Write);
if constexpr (FLAGS & GuestMemoryFlags::Read) {
Read(addr, size, backup);
}
}
~GuestMemory() = default;
T* data() noexcept {
return m_data_span.data();
}
const T* data() const noexcept {
return m_data_span.data();
}
size_t size() const noexcept {
return m_size;
}
size_t size_bytes() const noexcept {
return this->size() * sizeof(T);
}
[[nodiscard]] T* begin() noexcept {
return this->data();
}
[[nodiscard]] const T* begin() const noexcept {
return this->data();
}
[[nodiscard]] T* end() noexcept {
return this->data() + this->size();
}
[[nodiscard]] const T* end() const noexcept {
return this->data() + this->size();
}
T& operator[](size_t index) noexcept {
return m_data_span[index];
}
const T& operator[](size_t index) const noexcept {
return m_data_span[index];
}
void SetAddressAndSize(u64 addr, std::size_t size) noexcept {
m_addr = addr;
m_size = size;
m_addr_changed = true;
}
std::span<T> Read(u64 addr, std::size_t size,
Common::ScratchBuffer<T>* backup = nullptr) noexcept {
m_addr = addr;
m_size = size;
if (m_size == 0) {
m_is_data_copy = true;
return {};
}
if (this->TrySetSpan()) {
if constexpr (FLAGS & GuestMemoryFlags::Safe) {
m_memory.FlushRegion(m_addr, this->size_bytes());
}
} else {
if (backup) {
backup->resize_destructive(this->size());
m_data_span = *backup;
} else {
m_data_copy.resize(this->size());
m_data_span = std::span(m_data_copy);
}
m_is_data_copy = true;
m_span_valid = true;
if constexpr (FLAGS & GuestMemoryFlags::Safe) {
m_memory.ReadBlock(m_addr, this->data(), this->size_bytes());
} else {
m_memory.ReadBlockUnsafe(m_addr, this->data(), this->size_bytes());
}
}
return m_data_span;
}
void Write(std::span<T> write_data) noexcept {
if constexpr (FLAGS & GuestMemoryFlags::Cached) {
m_memory.WriteBlockCached(m_addr, write_data.data(), this->size_bytes());
} else if constexpr (FLAGS & GuestMemoryFlags::Safe) {
m_memory.WriteBlock(m_addr, write_data.data(), this->size_bytes());
} else {
m_memory.WriteBlockUnsafe(m_addr, write_data.data(), this->size_bytes());
}
}
bool TrySetSpan() noexcept {
if (u8* ptr = m_memory.GetSpan(m_addr, this->size_bytes()); ptr) {
m_data_span = {reinterpret_cast<T*>(ptr), this->size()};
m_span_valid = true;
return true;
}
return false;
}
protected:
bool IsDataCopy() const noexcept {
return m_is_data_copy;
}
bool AddressChanged() const noexcept {
return m_addr_changed;
}
M& m_memory;
u64 m_addr{};
size_t m_size{};
std::span<T> m_data_span{};
std::vector<T> m_data_copy{};
bool m_span_valid{false};
bool m_is_data_copy{false};
bool m_addr_changed{false};
};
template <typename M, typename T, GuestMemoryFlags FLAGS>
class GuestMemoryScoped : public GuestMemory<M, T, FLAGS> {
public:
GuestMemoryScoped() = delete;
explicit GuestMemoryScoped(M& memory, u64 addr, std::size_t size,
Common::ScratchBuffer<T>* backup = nullptr)
: GuestMemory<M, T, FLAGS>(memory, addr, size, backup) {
if constexpr (!(FLAGS & GuestMemoryFlags::Read)) {
if (!this->TrySetSpan()) {
if (backup) {
this->m_data_span = *backup;
this->m_span_valid = true;
this->m_is_data_copy = true;
}
}
}
}
~GuestMemoryScoped() {
if constexpr (FLAGS & GuestMemoryFlags::Write) {
if (this->size() == 0) [[unlikely]] {
return;
}
if (this->AddressChanged() || this->IsDataCopy()) {
ASSERT(this->m_span_valid);
if constexpr (FLAGS & GuestMemoryFlags::Cached) {
this->m_memory.WriteBlockCached(this->m_addr, this->data(), this->size_bytes());
} else if constexpr (FLAGS & GuestMemoryFlags::Safe) {
this->m_memory.WriteBlock(this->m_addr, this->data(), this->size_bytes());
} else {
this->m_memory.WriteBlockUnsafe(this->m_addr, this->data(), this->size_bytes());
}
} else if constexpr ((FLAGS & GuestMemoryFlags::Safe) ||
(FLAGS & GuestMemoryFlags::Cached)) {
this->m_memory.InvalidateRegion(this->m_addr, this->size_bytes());
}
}
}
};
} // namespace
template <typename T, GuestMemoryFlags FLAGS>
using CpuGuestMemory = GuestMemory<Memory, T, FLAGS>;
using CpuGuestMemory = GuestMemory<Core::Memory::Memory, T, FLAGS>;
template <typename T, GuestMemoryFlags FLAGS>
using CpuGuestMemoryScoped = GuestMemoryScoped<Memory, T, FLAGS>;
template <typename T, GuestMemoryFlags FLAGS>
using GpuGuestMemory = GuestMemory<Tegra::MemoryManager, T, FLAGS>;
template <typename T, GuestMemoryFlags FLAGS>
using GpuGuestMemoryScoped = GuestMemoryScoped<Tegra::MemoryManager, T, FLAGS>;
using CpuGuestMemoryScoped = GuestMemoryScoped<Core::Memory::Memory, T, FLAGS>;
} // namespace Core::Memory

View file

@ -145,8 +145,8 @@ void EmulatedController::ReloadColorsFromSettings() {
void EmulatedController::LoadDevices() {
// TODO(german77): Use more buttons to detect the correct device
const auto left_joycon = button_params[Settings::NativeButton::DRight];
const auto right_joycon = button_params[Settings::NativeButton::A];
const auto& left_joycon = button_params[Settings::NativeButton::DRight];
const auto& right_joycon = button_params[Settings::NativeButton::A];
// Triggers for GC controllers
trigger_params[LeftIndex] = button_params[Settings::NativeButton::ZL];
@ -1209,20 +1209,43 @@ void EmulatedController::SetNfc(const Common::Input::CallbackStatus& callback) {
controller.nfc_state = controller.nfc_values;
}
bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) {
bool EmulatedController::SetVibration(bool should_vibrate) {
VibrationValue vibration_value = DEFAULT_VIBRATION_VALUE;
if (should_vibrate) {
vibration_value.high_amplitude = 1.0f;
vibration_value.low_amplitude = 1.0f;
}
return SetVibration(DeviceIndex::Left, vibration_value);
}
bool EmulatedController::SetVibration(u32 slot, Core::HID::VibrationGcErmCommand erm_command) {
VibrationValue vibration_value = DEFAULT_VIBRATION_VALUE;
if (erm_command == Core::HID::VibrationGcErmCommand::Start) {
vibration_value.high_amplitude = 1.0f;
vibration_value.low_amplitude = 1.0f;
}
return SetVibration(DeviceIndex::Left, vibration_value);
}
bool EmulatedController::SetVibration(DeviceIndex device_index, const VibrationValue& vibration) {
if (!is_initialized) {
return false;
}
if (device_index >= output_devices.size()) {
if (device_index >= DeviceIndex::MaxDeviceIndex) {
return false;
}
if (!output_devices[device_index]) {
const std::size_t index = static_cast<std::size_t>(device_index);
if (!output_devices[index]) {
return false;
}
const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type);
const auto& player = Settings::values.players.GetValue()[player_index];
const f32 strength = static_cast<f32>(player.vibration_strength) / 100.0f;
last_vibration_value = vibration;
if (!player.vibration_enabled) {
return false;
}
@ -1240,8 +1263,11 @@ bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue v
.high_frequency = vibration.high_frequency,
.type = type,
};
return output_devices[device_index]->SetVibration(status) ==
Common::Input::DriverResult::Success;
return output_devices[index]->SetVibration(status) == Common::Input::DriverResult::Success;
}
VibrationValue EmulatedController::GetActualVibrationValue(DeviceIndex device_index) const {
return last_vibration_value;
}
bool EmulatedController::IsVibrationEnabled(std::size_t device_index) {

View file

@ -355,11 +355,28 @@ public:
/// Returns the latest ntag status from the controller
const NfcState& GetNfc() const;
/**
* Sends an on/off vibration to the left device
* @return true if vibration had no errors
*/
bool SetVibration(bool should_vibrate);
/**
* Sends an GC vibration to the left device
* @return true if vibration had no errors
*/
bool SetVibration(u32 slot, Core::HID::VibrationGcErmCommand erm_command);
/**
* Sends a specific vibration to the output device
* @return true if vibration had no errors
*/
bool SetVibration(std::size_t device_index, VibrationValue vibration);
bool SetVibration(DeviceIndex device_index, const VibrationValue& vibration);
/**
* @return The last sent vibration
*/
VibrationValue GetActualVibrationValue(DeviceIndex device_index) const;
/**
* Sends a small vibration to the output device
@ -564,6 +581,7 @@ private:
f32 motion_sensitivity{Core::HID::MotionInput::IsAtRestStandard};
u32 turbo_button_state{0};
std::size_t nfc_handles{0};
VibrationValue last_vibration_value{DEFAULT_VIBRATION_VALUE};
// Temporary values to avoid doing changes while the controller is in configuring mode
NpadStyleIndex tmp_npad_type{NpadStyleIndex::None};

View file

@ -7,6 +7,7 @@
#include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/service/ipc_helpers.h"
#include "hid_core/hid_core.h"
#include "hid_core/hid_util.h"
#include "hid_core/resource_manager.h"
#include "hid_core/resources/applet_resource.h"
@ -27,6 +28,10 @@
#include "hid_core/resources/touch_screen/gesture.h"
#include "hid_core/resources/touch_screen/touch_screen.h"
#include "hid_core/resources/unique_pad/unique_pad.h"
#include "hid_core/resources/vibration/gc_vibration_device.h"
#include "hid_core/resources/vibration/n64_vibration_device.h"
#include "hid_core/resources/vibration/vibration_base.h"
#include "hid_core/resources/vibration/vibration_device.h"
namespace Service::HID {
@ -52,6 +57,7 @@ void ResourceManager::Initialize() {
system.HIDCore().ReloadInputDevices();
handheld_config = std::make_shared<HandheldConfig>();
InitializeHidCommonSampler();
InitializeTouchScreenSampler();
InitializeConsoleSixAxisSampler();
@ -174,7 +180,7 @@ void ResourceManager::InitializeHidCommonSampler() {
debug_pad->SetAppletResource(applet_resource, &shared_mutex);
digitizer->SetAppletResource(applet_resource, &shared_mutex);
keyboard->SetAppletResource(applet_resource, &shared_mutex);
npad->SetNpadExternals(applet_resource, &shared_mutex);
npad->SetNpadExternals(applet_resource, &shared_mutex, handheld_config);
six_axis->SetAppletResource(applet_resource, &shared_mutex);
mouse->SetAppletResource(applet_resource, &shared_mutex);
debug_mouse->SetAppletResource(applet_resource, &shared_mutex);
@ -257,6 +263,121 @@ void ResourceManager::EnableTouchScreen(u64 aruid, bool is_enabled) {
applet_resource->EnableTouchScreen(aruid, is_enabled);
}
NpadVibrationBase* ResourceManager::GetVibrationDevice(
const Core::HID::VibrationDeviceHandle& handle) {
return npad->GetVibrationDevice(handle);
}
NpadN64VibrationDevice* ResourceManager::GetN64VibrationDevice(
const Core::HID::VibrationDeviceHandle& handle) {
return npad->GetN64VibrationDevice(handle);
}
NpadVibrationDevice* ResourceManager::GetNSVibrationDevice(
const Core::HID::VibrationDeviceHandle& handle) {
return npad->GetNSVibrationDevice(handle);
}
NpadGcVibrationDevice* ResourceManager::GetGcVibrationDevice(
const Core::HID::VibrationDeviceHandle& handle) {
return npad->GetGcVibrationDevice(handle);
}
Result ResourceManager::SetAruidValidForVibration(u64 aruid, bool is_enabled) {
std::scoped_lock lock{shared_mutex};
const bool has_changed = applet_resource->SetAruidValidForVibration(aruid, is_enabled);
if (has_changed) {
auto devices = npad->GetAllVibrationDevices();
for ([[maybe_unused]] auto* device : devices) {
// TODO
}
}
auto* vibration_handler = npad->GetVibrationHandler();
if (aruid != vibration_handler->GetSessionAruid()) {
vibration_handler->EndPermitVibrationSession();
}
return ResultSuccess;
}
void ResourceManager::SetForceHandheldStyleVibration(bool is_forced) {
handheld_config->is_force_handheld_style_vibration = is_forced;
}
Result ResourceManager::IsVibrationAruidActive(u64 aruid, bool& is_active) const {
std::scoped_lock lock{shared_mutex};
is_active = applet_resource->IsVibrationAruidActive(aruid);
return ResultSuccess;
}
Result ResourceManager::GetVibrationDeviceInfo(Core::HID::VibrationDeviceInfo& device_info,
const Core::HID::VibrationDeviceHandle& handle) {
bool check_device_index = false;
const Result is_valid = IsVibrationHandleValid(handle);
if (is_valid.IsError()) {
return is_valid;
}
switch (handle.npad_type) {
case Core::HID::NpadStyleIndex::Fullkey:
case Core::HID::NpadStyleIndex::Handheld:
case Core::HID::NpadStyleIndex::JoyconDual:
case Core::HID::NpadStyleIndex::JoyconLeft:
case Core::HID::NpadStyleIndex::JoyconRight:
device_info.type = Core::HID::VibrationDeviceType::LinearResonantActuator;
check_device_index = true;
break;
case Core::HID::NpadStyleIndex::GameCube:
device_info.type = Core::HID::VibrationDeviceType::GcErm;
break;
case Core::HID::NpadStyleIndex::N64:
device_info.type = Core::HID::VibrationDeviceType::N64;
break;
default:
device_info.type = Core::HID::VibrationDeviceType::Unknown;
break;
}
device_info.position = Core::HID::VibrationDevicePosition::None;
if (check_device_index) {
switch (handle.device_index) {
case Core::HID::DeviceIndex::Left:
device_info.position = Core::HID::VibrationDevicePosition::Left;
break;
case Core::HID::DeviceIndex::Right:
device_info.position = Core::HID::VibrationDevicePosition::Right;
break;
case Core::HID::DeviceIndex::None:
default:
ASSERT_MSG(false, "DeviceIndex should never be None!");
break;
}
}
return ResultSuccess;
}
Result ResourceManager::SendVibrationValue(u64 aruid,
const Core::HID::VibrationDeviceHandle& handle,
const Core::HID::VibrationValue& value) {
bool has_active_aruid{};
NpadVibrationDevice* device{nullptr};
Result result = IsVibrationAruidActive(aruid, has_active_aruid);
if (result.IsSuccess() && has_active_aruid) {
result = IsVibrationHandleValid(handle);
}
if (result.IsSuccess() && has_active_aruid) {
device = GetNSVibrationDevice(handle);
}
if (device != nullptr) {
result = device->SendVibrationValue(value);
}
return result;
}
void ResourceManager::UpdateControllers(std::chrono::nanoseconds ns_late) {
auto& core_timing = system.CoreTiming();
debug_pad->OnUpdate(core_timing);

View file

@ -10,6 +10,12 @@ namespace Core {
class System;
}
namespace Core::HID {
struct VibrationDeviceHandle;
struct VibrationValue;
struct VibrationDeviceInfo;
} // namespace Core::HID
namespace Core::Timing {
struct EventType;
}
@ -37,6 +43,11 @@ class SixAxis;
class SleepButton;
class TouchScreen;
class UniquePad;
class NpadVibrationBase;
class NpadN64VibrationDevice;
class NpadGcVibrationDevice;
class NpadVibrationDevice;
struct HandheldConfig;
class ResourceManager {
@ -79,6 +90,18 @@ public:
void EnablePadInput(u64 aruid, bool is_enabled);
void EnableTouchScreen(u64 aruid, bool is_enabled);
NpadVibrationBase* GetVibrationDevice(const Core::HID::VibrationDeviceHandle& handle);
NpadN64VibrationDevice* GetN64VibrationDevice(const Core::HID::VibrationDeviceHandle& handle);
NpadVibrationDevice* GetNSVibrationDevice(const Core::HID::VibrationDeviceHandle& handle);
NpadGcVibrationDevice* GetGcVibrationDevice(const Core::HID::VibrationDeviceHandle& handle);
Result SetAruidValidForVibration(u64 aruid, bool is_enabled);
void SetForceHandheldStyleVibration(bool is_forced);
Result IsVibrationAruidActive(u64 aruid, bool& is_active) const;
Result GetVibrationDeviceInfo(Core::HID::VibrationDeviceInfo& device_info,
const Core::HID::VibrationDeviceHandle& handle);
Result SendVibrationValue(u64 aruid, const Core::HID::VibrationDeviceHandle& handle,
const Core::HID::VibrationValue& value);
void UpdateControllers(std::chrono::nanoseconds ns_late);
void UpdateNpad(std::chrono::nanoseconds ns_late);
void UpdateMouseKeyboard(std::chrono::nanoseconds ns_late);
@ -113,6 +136,8 @@ private:
std::shared_ptr<TouchScreen> touch_screen = nullptr;
std::shared_ptr<UniquePad> unique_pad = nullptr;
std::shared_ptr<HandheldConfig> handheld_config = nullptr;
// TODO: Create these resources
// std::shared_ptr<AudioControl> audio_control = nullptr;
// std::shared_ptr<ButtonConfig> button_config = nullptr;

View file

@ -115,7 +115,7 @@ Result NpadAbstractIrSensorHandler::GetXcdHandleForNpadWithIrSensor(u64& handle)
if (sensor_state < NpadIrSensorState::Available) {
return ResultIrSensorIsNotReady;
}
handle = xcd_handle;
// handle = xcd_handle;
return ResultSuccess;
}

View file

@ -7,6 +7,10 @@
#include "core/hle/result.h"
#include "hid_core/hid_types.h"
namespace Core::HID {
class EmulatedController;
}
namespace Kernel {
class KEvent;
class KReadableEvent;
@ -50,7 +54,7 @@ private:
s32 ref_counter{};
Kernel::KEvent* ir_sensor_event{nullptr};
u64 xcd_handle{};
Core::HID::EmulatedController* xcd_handle{};
NpadIrSensorState sensor_state{};
};
} // namespace Service::HID

View file

@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "hid_core/hid_core.h"
#include "hid_core/hid_result.h"
#include "hid_core/resources/abstracted_pad/abstract_pad.h"
#include "hid_core/resources/applet_resource.h"
@ -16,7 +17,7 @@ void AbstractPad::SetExternals(AppletResourceHolder* applet_resource,
CaptureButtonResource* capture_button_resource,
HomeButtonResource* home_button_resource,
SixAxisResource* sixaxis_resource, PalmaResource* palma_resource,
VibrationHandler* vibration) {
NpadVibration* vibration, Core::HID::HIDCore* core) {
applet_resource_holder = applet_resource;
properties_handler.SetAppletResource(applet_resource_holder);
@ -35,13 +36,14 @@ void AbstractPad::SetExternals(AppletResourceHolder* applet_resource,
mcu_handler.SetAbstractPadHolder(&abstract_pad_holder);
mcu_handler.SetPropertiesHandler(&properties_handler);
std::array<NpadVibrationDevice*, 2> vibration_devices{&vibration_left, &vibration_right};
vibration_handler.SetAppletResource(applet_resource_holder);
vibration_handler.SetAbstractPadHolder(&abstract_pad_holder);
vibration_handler.SetPropertiesHandler(&properties_handler);
vibration_handler.SetN64Vibration(&vibration_n64);
vibration_handler.SetVibration(vibration_devices);
vibration_handler.SetVibration(&vibration_left, &vibration_right);
vibration_handler.SetGcVibration(&vibration_gc);
vibration_handler.SetVibrationHandler(vibration);
vibration_handler.SetHidCore(core);
sixaxis_handler.SetAppletResource(applet_resource_holder);
sixaxis_handler.SetAbstractPadHolder(&abstract_pad_holder);
@ -239,11 +241,6 @@ NpadVibrationDevice* AbstractPad::GetVibrationDevice(Core::HID::DeviceIndex devi
return &vibration_left;
}
void AbstractPad::GetLeftRightVibrationDevice(std::vector<NpadVibrationDevice*> list) {
list.emplace_back(&vibration_left);
list.emplace_back(&vibration_right);
}
NpadGcVibrationDevice* AbstractPad::GetGCVibrationDevice() {
return &vibration_gc;
}

View file

@ -32,7 +32,6 @@ class AppletResource;
class SixAxisResource;
class PalmaResource;
class NPadResource;
class AbstractPad;
class NpadLastActiveHandler;
class NpadIrNfcHandler;
class UniquePads;
@ -44,7 +43,6 @@ class NpadGcVibration;
class CaptureButtonResource;
class HomeButtonResource;
class VibrationHandler;
struct HandheldConfig;
@ -57,7 +55,8 @@ public:
void SetExternals(AppletResourceHolder* applet_resource,
CaptureButtonResource* capture_button_resource,
HomeButtonResource* home_button_resource, SixAxisResource* sixaxis_resource,
PalmaResource* palma_resource, VibrationHandler* vibration);
PalmaResource* palma_resource, NpadVibration* vibration,
Core::HID::HIDCore* core);
void SetNpadId(Core::HID::NpadIdType npad_id);
Result Activate();
@ -78,7 +77,6 @@ public:
NpadN64VibrationDevice* GetN64VibrationDevice();
NpadVibrationDevice* GetVibrationDevice(Core::HID::DeviceIndex device_index);
void GetLeftRightVibrationDevice(std::vector<NpadVibrationDevice*> list);
NpadGcVibrationDevice* GetGCVibrationDevice();
Core::HID::NpadIdType GetLastActiveNpad();

View file

@ -1,6 +1,8 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "hid_core/frontend/emulated_controller.h"
#include "hid_core/hid_core.h"
#include "hid_core/hid_result.h"
#include "hid_core/hid_util.h"
#include "hid_core/resources/abstracted_pad/abstract_pad_holder.h"
@ -30,14 +32,22 @@ void NpadAbstractVibrationHandler::SetPropertiesHandler(NpadAbstractPropertiesHa
properties_handler = handler;
}
void NpadAbstractVibrationHandler::SetVibrationHandler(NpadVibration* handler) {
vibration_handler = handler;
}
void NpadAbstractVibrationHandler::SetHidCore(Core::HID::HIDCore* core) {
hid_core = core;
}
void NpadAbstractVibrationHandler::SetN64Vibration(NpadN64VibrationDevice* n64_device) {
n64_vibration_device = n64_device;
}
void NpadAbstractVibrationHandler::SetVibration(std::span<NpadVibrationDevice*> device) {
for (std::size_t i = 0; i < device.size() && i < vibration_device.size(); i++) {
vibration_device[i] = device[i];
}
void NpadAbstractVibrationHandler::SetVibration(NpadVibrationDevice* left_device,
NpadVibrationDevice* right_device) {
left_vibration_device = left_device;
right_vibration_device = right_device;
}
void NpadAbstractVibrationHandler::SetGcVibration(NpadGcVibrationDevice* gc_device) {
@ -69,5 +79,29 @@ void NpadAbstractVibrationHandler::UpdateVibrationState() {
if (!is_handheld_hid_enabled && is_force_handheld_style_vibration) {
// TODO
}
// TODO: This function isn't accurate. It's supposed to get 5 abstracted pads from the
// NpadAbstractPropertiesHandler but this handler isn't fully implemented yet
IAbstractedPad abstracted_pad{};
const auto npad_id = properties_handler->GetNpadId();
abstracted_pad.xcd_handle = hid_core->GetEmulatedController(npad_id);
abstracted_pad.internal_flags.is_connected.Assign(abstracted_pad.xcd_handle->IsConnected());
if (abstracted_pad.internal_flags.is_connected) {
left_vibration_device->Mount(abstracted_pad, Core::HID::DeviceIndex::Left,
vibration_handler);
right_vibration_device->Mount(abstracted_pad, Core::HID::DeviceIndex::Right,
vibration_handler);
gc_vibration_device->Mount(abstracted_pad, 0, vibration_handler);
gc_vibration_device->Mount(abstracted_pad, 0, vibration_handler);
n64_vibration_device->Mount(abstracted_pad, vibration_handler);
return;
}
left_vibration_device->Unmount();
right_vibration_device->Unmount();
gc_vibration_device->Unmount();
gc_vibration_device->Unmount();
n64_vibration_device->Unmount();
}
} // namespace Service::HID

View file

@ -9,6 +9,10 @@
#include "core/hle/result.h"
#include "hid_core/hid_types.h"
namespace Core::HID {
class HIDCore;
}
namespace Service::HID {
struct AppletResourceHolder;
class NpadAbstractedPadHolder;
@ -27,9 +31,11 @@ public:
void SetAbstractPadHolder(NpadAbstractedPadHolder* holder);
void SetAppletResource(AppletResourceHolder* applet_resource);
void SetPropertiesHandler(NpadAbstractPropertiesHandler* handler);
void SetVibrationHandler(NpadVibration* handler);
void SetHidCore(Core::HID::HIDCore* core);
void SetN64Vibration(NpadN64VibrationDevice* n64_device);
void SetVibration(std::span<NpadVibrationDevice*> device);
void SetVibration(NpadVibrationDevice* left_device, NpadVibrationDevice* right_device);
void SetGcVibration(NpadGcVibrationDevice* gc_device);
Result IncrementRefCounter();
@ -41,9 +47,11 @@ private:
AppletResourceHolder* applet_resource_holder{nullptr};
NpadAbstractedPadHolder* abstract_pad_holder{nullptr};
NpadAbstractPropertiesHandler* properties_handler{nullptr};
Core::HID::HIDCore* hid_core{nullptr};
NpadN64VibrationDevice* n64_vibration_device{nullptr};
std::array<NpadVibrationDevice*, 2> vibration_device{};
NpadVibrationDevice* left_vibration_device{};
NpadVibrationDevice* right_vibration_device{};
NpadGcVibrationDevice* gc_vibration_device{nullptr};
NpadVibration* vibration_handler{nullptr};
s32 ref_counter{};

View file

@ -200,6 +200,25 @@ void AppletResource::EnableInput(u64 aruid, bool is_enabled) {
data[index].flag.enable_touchscreen.Assign(is_enabled);
}
bool AppletResource::SetAruidValidForVibration(u64 aruid, bool is_enabled) {
const u64 index = GetIndexFromAruid(aruid);
if (index >= AruidIndexMax) {
return false;
}
if (!is_enabled && aruid == active_vibration_aruid) {
active_vibration_aruid = SystemAruid;
return true;
}
if (is_enabled && aruid != active_vibration_aruid) {
active_vibration_aruid = aruid;
return true;
}
return false;
}
void AppletResource::EnableSixAxisSensor(u64 aruid, bool is_enabled) {
const u64 index = GetIndexFromAruid(aruid);
if (index >= AruidIndexMax) {

View file

@ -101,6 +101,7 @@ public:
Result DestroySevenSixAxisTransferMemory();
void EnableInput(u64 aruid, bool is_enabled);
bool SetAruidValidForVibration(u64 aruid, bool is_enabled);
void EnableSixAxisSensor(u64 aruid, bool is_enabled);
void EnablePadInput(u64 aruid, bool is_enabled);
void EnableTouchScreen(u64 aruid, bool is_enabled);

View file

@ -21,6 +21,7 @@
#include "hid_core/hid_util.h"
#include "hid_core/resources/applet_resource.h"
#include "hid_core/resources/npad/npad.h"
#include "hid_core/resources/npad/npad_vibration.h"
#include "hid_core/resources/shared_memory_format.h"
namespace Service::HID {
@ -31,10 +32,6 @@ NPad::NPad(Core::HID::HIDCore& hid_core_, KernelHelpers::ServiceContext& service
for (std::size_t i = 0; i < controller_data[aruid_index].size(); ++i) {
auto& controller = controller_data[aruid_index][i];
controller.device = hid_core.GetEmulatedControllerByIndex(i);
controller.vibration[Core::HID::EmulatedDeviceIndex::LeftIndex].latest_vibration_value =
Core::HID::DEFAULT_VIBRATION_VALUE;
controller.vibration[Core::HID::EmulatedDeviceIndex::RightIndex]
.latest_vibration_value = Core::HID::DEFAULT_VIBRATION_VALUE;
Core::HID::ControllerUpdateCallback engine_callback{
.on_change =
[this, i](Core::HID::ControllerTriggerType type) { ControllerUpdate(type, i); },
@ -43,6 +40,10 @@ NPad::NPad(Core::HID::HIDCore& hid_core_, KernelHelpers::ServiceContext& service
controller.callback_key = controller.device->SetCallback(engine_callback);
}
}
for (std::size_t i = 0; i < abstracted_pads.size(); ++i) {
abstracted_pads[i] = AbstractPad{};
abstracted_pads[i].SetNpadId(IndexToNpadIdType(i));
}
}
NPad::~NPad() {
@ -359,6 +360,7 @@ void NPad::InitNewlyAddedController(u64 aruid, Core::HID::NpadIdType npad_id) {
npad_resource.SignalStyleSetUpdateEvent(aruid, npad_id);
WriteEmptyEntry(controller.shared_memory);
hid_core.SetLastActiveController(npad_id);
abstracted_pads[NpadIdTypeToIndex(npad_id)].Update();
}
void NPad::WriteEmptyEntry(NpadInternalState* npad) {
@ -740,171 +742,6 @@ bool NPad::SetNpadMode(u64 aruid, Core::HID::NpadIdType& new_npad_id, Core::HID:
return true;
}
bool NPad::VibrateControllerAtIndex(u64 aruid, Core::HID::NpadIdType npad_id,
std::size_t device_index,
const Core::HID::VibrationValue& vibration_value) {
auto& controller = GetControllerFromNpadIdType(aruid, npad_id);
if (!controller.device->IsConnected()) {
return false;
}
if (!controller.device->IsVibrationEnabled(device_index)) {
if (controller.vibration[device_index].latest_vibration_value.low_amplitude != 0.0f ||
controller.vibration[device_index].latest_vibration_value.high_amplitude != 0.0f) {
// Send an empty vibration to stop any vibrations.
Core::HID::VibrationValue vibration{0.0f, 160.0f, 0.0f, 320.0f};
controller.device->SetVibration(device_index, vibration);
// Then reset the vibration value to its default value.
controller.vibration[device_index].latest_vibration_value =
Core::HID::DEFAULT_VIBRATION_VALUE;
}
return false;
}
if (!Settings::values.enable_accurate_vibrations.GetValue()) {
using std::chrono::duration_cast;
using std::chrono::milliseconds;
using std::chrono::steady_clock;
const auto now = steady_clock::now();
// Filter out non-zero vibrations that are within 15ms of each other.
if ((vibration_value.low_amplitude != 0.0f || vibration_value.high_amplitude != 0.0f) &&
duration_cast<milliseconds>(
now - controller.vibration[device_index].last_vibration_timepoint) <
milliseconds(15)) {
return false;
}
controller.vibration[device_index].last_vibration_timepoint = now;
}
Core::HID::VibrationValue vibration{
vibration_value.low_amplitude, vibration_value.low_frequency,
vibration_value.high_amplitude, vibration_value.high_frequency};
return controller.device->SetVibration(device_index, vibration);
}
void NPad::VibrateController(u64 aruid,
const Core::HID::VibrationDeviceHandle& vibration_device_handle,
const Core::HID::VibrationValue& vibration_value) {
if (IsVibrationHandleValid(vibration_device_handle).IsError()) {
return;
}
if (!Settings::values.vibration_enabled.GetValue() && !permit_vibration_session_enabled) {
return;
}
auto& controller = GetControllerFromHandle(aruid, vibration_device_handle);
const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index);
if (!controller.vibration[device_index].device_mounted || !controller.device->IsConnected()) {
return;
}
if (vibration_device_handle.device_index == Core::HID::DeviceIndex::None) {
ASSERT_MSG(false, "DeviceIndex should never be None!");
return;
}
// Some games try to send mismatched parameters in the device handle, block these.
if ((controller.device->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconLeft &&
(vibration_device_handle.npad_type == Core::HID::NpadStyleIndex::JoyconRight ||
vibration_device_handle.device_index == Core::HID::DeviceIndex::Right)) ||
(controller.device->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconRight &&
(vibration_device_handle.npad_type == Core::HID::NpadStyleIndex::JoyconLeft ||
vibration_device_handle.device_index == Core::HID::DeviceIndex::Left))) {
return;
}
// Filter out vibrations with equivalent values to reduce unnecessary state changes.
if (vibration_value.low_amplitude ==
controller.vibration[device_index].latest_vibration_value.low_amplitude &&
vibration_value.high_amplitude ==
controller.vibration[device_index].latest_vibration_value.high_amplitude) {
return;
}
if (VibrateControllerAtIndex(aruid, controller.device->GetNpadIdType(), device_index,
vibration_value)) {
controller.vibration[device_index].latest_vibration_value = vibration_value;
}
}
void NPad::VibrateControllers(
u64 aruid, std::span<const Core::HID::VibrationDeviceHandle> vibration_device_handles,
std::span<const Core::HID::VibrationValue> vibration_values) {
if (!Settings::values.vibration_enabled.GetValue() && !permit_vibration_session_enabled) {
return;
}
ASSERT_OR_EXECUTE_MSG(
vibration_device_handles.size() == vibration_values.size(), { return; },
"The amount of device handles does not match with the amount of vibration values,"
"this is undefined behavior!");
for (std::size_t i = 0; i < vibration_device_handles.size(); ++i) {
VibrateController(aruid, vibration_device_handles[i], vibration_values[i]);
}
}
Core::HID::VibrationValue NPad::GetLastVibration(
u64 aruid, const Core::HID::VibrationDeviceHandle& vibration_device_handle) const {
if (IsVibrationHandleValid(vibration_device_handle).IsError()) {
return {};
}
const auto& controller = GetControllerFromHandle(aruid, vibration_device_handle);
const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index);
return controller.vibration[device_index].latest_vibration_value;
}
void NPad::InitializeVibrationDevice(
const Core::HID::VibrationDeviceHandle& vibration_device_handle) {
if (IsVibrationHandleValid(vibration_device_handle).IsError()) {
return;
}
const auto aruid = applet_resource_holder.applet_resource->GetActiveAruid();
const auto npad_index = static_cast<Core::HID::NpadIdType>(vibration_device_handle.npad_id);
const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index);
if (aruid == 0) {
return;
}
InitializeVibrationDeviceAtIndex(aruid, npad_index, device_index);
}
void NPad::InitializeVibrationDeviceAtIndex(u64 aruid, Core::HID::NpadIdType npad_id,
std::size_t device_index) {
auto& controller = GetControllerFromNpadIdType(aruid, npad_id);
if (!Settings::values.vibration_enabled.GetValue()) {
controller.vibration[device_index].device_mounted = false;
return;
}
controller.vibration[device_index].device_mounted =
controller.device->IsVibrationEnabled(device_index);
}
void NPad::SetPermitVibrationSession(bool permit_vibration_session) {
permit_vibration_session_enabled = permit_vibration_session;
}
bool NPad::IsVibrationDeviceMounted(
u64 aruid, const Core::HID::VibrationDeviceHandle& vibration_device_handle) const {
if (IsVibrationHandleValid(vibration_device_handle).IsError()) {
return false;
}
const auto& controller = GetControllerFromHandle(aruid, vibration_device_handle);
const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index);
return controller.vibration[device_index].device_mounted;
}
Result NPad::AcquireNpadStyleSetUpdateEventHandle(u64 aruid, Kernel::KReadableEvent** out_event,
Core::HID::NpadIdType npad_id) {
std::scoped_lock lock{mutex};
@ -936,11 +773,6 @@ Result NPad::DisconnectNpad(u64 aruid, Core::HID::NpadIdType npad_id) {
LOG_DEBUG(Service_HID, "Npad disconnected {}", npad_id);
auto& controller = GetControllerFromNpadIdType(aruid, npad_id);
for (std::size_t device_idx = 0; device_idx < controller.vibration.size(); ++device_idx) {
// Send an empty vibration to stop any vibrations.
VibrateControllerAtIndex(aruid, npad_id, device_idx, {});
controller.vibration[device_idx].device_mounted = false;
}
auto* shared_memory = controller.shared_memory;
// Don't reset shared_memory->assignment_mode this value is persistent
@ -1243,22 +1075,17 @@ void NPad::UnregisterAppletResourceUserId(u64 aruid) {
}
void NPad::SetNpadExternals(std::shared_ptr<AppletResource> resource,
std::recursive_mutex* shared_mutex) {
std::recursive_mutex* shared_mutex,
std::shared_ptr<HandheldConfig> handheld_config) {
applet_resource_holder.applet_resource = resource;
applet_resource_holder.shared_mutex = shared_mutex;
applet_resource_holder.shared_npad_resource = &npad_resource;
}
applet_resource_holder.handheld_config = handheld_config;
NPad::NpadControllerData& NPad::GetControllerFromHandle(
u64 aruid, const Core::HID::VibrationDeviceHandle& device_handle) {
const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
return GetControllerFromNpadIdType(aruid, npad_id);
}
const NPad::NpadControllerData& NPad::GetControllerFromHandle(
u64 aruid, const Core::HID::VibrationDeviceHandle& device_handle) const {
const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
return GetControllerFromNpadIdType(aruid, npad_id);
for (auto& abstract_pad : abstracted_pads) {
abstract_pad.SetExternals(&applet_resource_holder, nullptr, nullptr, nullptr, nullptr,
&vibration_handler, &hid_core);
}
}
NPad::NpadControllerData& NPad::GetControllerFromHandle(
@ -1396,4 +1223,97 @@ Result NPad::GetLastActiveNpad(Core::HID::NpadIdType& out_npad_id) const {
return ResultSuccess;
}
NpadVibration* NPad::GetVibrationHandler() {
return &vibration_handler;
}
std::vector<NpadVibrationBase*> NPad::GetAllVibrationDevices() {
std::vector<NpadVibrationBase*> vibration_devices;
for (auto& abstract_pad : abstracted_pads) {
auto* left_device = abstract_pad.GetVibrationDevice(Core::HID::DeviceIndex::Left);
auto* right_device = abstract_pad.GetVibrationDevice(Core::HID::DeviceIndex::Right);
auto* n64_device = abstract_pad.GetGCVibrationDevice();
auto* gc_device = abstract_pad.GetGCVibrationDevice();
if (left_device != nullptr) {
vibration_devices.emplace_back(left_device);
}
if (right_device != nullptr) {
vibration_devices.emplace_back(right_device);
}
if (n64_device != nullptr) {
vibration_devices.emplace_back(n64_device);
}
if (gc_device != nullptr) {
vibration_devices.emplace_back(gc_device);
}
}
return vibration_devices;
}
NpadVibrationBase* NPad::GetVibrationDevice(const Core::HID::VibrationDeviceHandle& handle) {
if (IsVibrationHandleValid(handle).IsError()) {
return nullptr;
}
const auto npad_index = NpadIdTypeToIndex(static_cast<Core::HID::NpadIdType>(handle.npad_id));
const auto style_inde = static_cast<Core::HID::NpadStyleIndex>(handle.npad_type);
if (style_inde == Core::HID::NpadStyleIndex::GameCube) {
return abstracted_pads[npad_index].GetGCVibrationDevice();
}
if (style_inde == Core::HID::NpadStyleIndex::N64) {
return abstracted_pads[npad_index].GetN64VibrationDevice();
}
return abstracted_pads[npad_index].GetVibrationDevice(handle.device_index);
}
NpadN64VibrationDevice* NPad::GetN64VibrationDevice(
const Core::HID::VibrationDeviceHandle& handle) {
if (IsVibrationHandleValid(handle).IsError()) {
return nullptr;
}
const auto npad_index = NpadIdTypeToIndex(static_cast<Core::HID::NpadIdType>(handle.npad_id));
const auto style_inde = static_cast<Core::HID::NpadStyleIndex>(handle.npad_type);
if (style_inde != Core::HID::NpadStyleIndex::N64) {
return nullptr;
}
return abstracted_pads[npad_index].GetN64VibrationDevice();
}
NpadVibrationDevice* NPad::GetNSVibrationDevice(const Core::HID::VibrationDeviceHandle& handle) {
if (IsVibrationHandleValid(handle).IsError()) {
return nullptr;
}
const auto npad_index = NpadIdTypeToIndex(static_cast<Core::HID::NpadIdType>(handle.npad_id));
const auto style_inde = static_cast<Core::HID::NpadStyleIndex>(handle.npad_type);
if (style_inde == Core::HID::NpadStyleIndex::GameCube ||
style_inde == Core::HID::NpadStyleIndex::N64) {
return nullptr;
}
return abstracted_pads[npad_index].GetVibrationDevice(handle.device_index);
}
NpadGcVibrationDevice* NPad::GetGcVibrationDevice(const Core::HID::VibrationDeviceHandle& handle) {
if (IsVibrationHandleValid(handle).IsError()) {
return nullptr;
}
const auto npad_index = NpadIdTypeToIndex(static_cast<Core::HID::NpadIdType>(handle.npad_id));
const auto style_inde = static_cast<Core::HID::NpadStyleIndex>(handle.npad_type);
if (style_inde != Core::HID::NpadStyleIndex::GameCube) {
return nullptr;
}
return abstracted_pads[npad_index].GetGCVibrationDevice();
}
void NPad::UpdateHandheldAbstractState() {
std::scoped_lock lock{mutex};
abstracted_pads[NpadIdTypeToIndex(Core::HID::NpadIdType::Handheld)].Update();
}
} // namespace Service::HID

View file

@ -10,9 +10,15 @@
#include "common/common_types.h"
#include "hid_core/hid_types.h"
#include "hid_core/resources/abstracted_pad/abstract_pad.h"
#include "hid_core/resources/controller_base.h"
#include "hid_core/resources/npad/npad_resource.h"
#include "hid_core/resources/npad/npad_types.h"
#include "hid_core/resources/npad/npad_vibration.h"
#include "hid_core/resources/vibration/gc_vibration_device.h"
#include "hid_core/resources/vibration/n64_vibration_device.h"
#include "hid_core/resources/vibration/vibration_base.h"
#include "hid_core/resources/vibration/vibration_device.h"
namespace Core::HID {
class EmulatedController;
@ -32,6 +38,7 @@ union Result;
namespace Service::HID {
class AppletResource;
struct HandheldConfig;
struct NpadInternalState;
struct NpadSixAxisSensorLifo;
struct NpadSharedMemoryFormat;
@ -68,31 +75,6 @@ public:
bool SetNpadMode(u64 aruid, Core::HID::NpadIdType& new_npad_id, Core::HID::NpadIdType npad_id,
NpadJoyDeviceType npad_device_type, NpadJoyAssignmentMode assignment_mode);
bool VibrateControllerAtIndex(u64 aruid, Core::HID::NpadIdType npad_id,
std::size_t device_index,
const Core::HID::VibrationValue& vibration_value);
void VibrateController(u64 aruid,
const Core::HID::VibrationDeviceHandle& vibration_device_handle,
const Core::HID::VibrationValue& vibration_value);
void VibrateControllers(
u64 aruid, std::span<const Core::HID::VibrationDeviceHandle> vibration_device_handles,
std::span<const Core::HID::VibrationValue> vibration_values);
Core::HID::VibrationValue GetLastVibration(
u64 aruid, const Core::HID::VibrationDeviceHandle& vibration_device_handle) const;
void InitializeVibrationDevice(const Core::HID::VibrationDeviceHandle& vibration_device_handle);
void InitializeVibrationDeviceAtIndex(u64 aruid, Core::HID::NpadIdType npad_id,
std::size_t device_index);
void SetPermitVibrationSession(bool permit_vibration_session);
bool IsVibrationDeviceMounted(
u64 aruid, const Core::HID::VibrationDeviceHandle& vibration_device_handle) const;
Result AcquireNpadStyleSetUpdateEventHandle(u64 aruid, Kernel::KReadableEvent** out_event,
Core::HID::NpadIdType npad_id);
@ -145,7 +127,8 @@ public:
Result RegisterAppletResourceUserId(u64 aruid);
void UnregisterAppletResourceUserId(u64 aruid);
void SetNpadExternals(std::shared_ptr<AppletResource> resource,
std::recursive_mutex* shared_mutex);
std::recursive_mutex* shared_mutex,
std::shared_ptr<HandheldConfig> handheld_config);
AppletDetailedUiType GetAppletDetailedUiType(Core::HID::NpadIdType npad_id);
@ -161,18 +144,20 @@ public:
Result GetLastActiveNpad(Core::HID::NpadIdType& out_npad_id) const;
private:
struct VibrationData {
bool device_mounted{};
Core::HID::VibrationValue latest_vibration_value{};
std::chrono::steady_clock::time_point last_vibration_timepoint{};
};
NpadVibration* GetVibrationHandler();
std::vector<NpadVibrationBase*> GetAllVibrationDevices();
NpadVibrationBase* GetVibrationDevice(const Core::HID::VibrationDeviceHandle& handle);
NpadN64VibrationDevice* GetN64VibrationDevice(const Core::HID::VibrationDeviceHandle& handle);
NpadVibrationDevice* GetNSVibrationDevice(const Core::HID::VibrationDeviceHandle& handle);
NpadGcVibrationDevice* GetGcVibrationDevice(const Core::HID::VibrationDeviceHandle& handle);
void UpdateHandheldAbstractState();
private:
struct NpadControllerData {
NpadInternalState* shared_memory = nullptr;
Core::HID::EmulatedController* device = nullptr;
std::array<VibrationData, 2> vibration{};
bool is_connected{};
// Dual joycons can have only one side connected
@ -191,10 +176,6 @@ private:
void RequestPadStateUpdate(u64 aruid, Core::HID::NpadIdType npad_id);
void WriteEmptyEntry(NpadInternalState* npad);
NpadControllerData& GetControllerFromHandle(
u64 aruid, const Core::HID::VibrationDeviceHandle& device_handle);
const NpadControllerData& GetControllerFromHandle(
u64 aruid, const Core::HID::VibrationDeviceHandle& device_handle) const;
NpadControllerData& GetControllerFromHandle(
u64 aruid, const Core::HID::SixAxisSensorHandle& device_handle);
const NpadControllerData& GetControllerFromHandle(
@ -215,11 +196,13 @@ private:
mutable std::mutex mutex;
NPadResource npad_resource;
AppletResourceHolder applet_resource_holder{};
std::array<AbstractPad, MaxSupportedNpadIdTypes> abstracted_pads;
NpadVibration vibration_handler{};
Kernel::KEvent* input_event{nullptr};
std::mutex* input_mutex{nullptr};
std::atomic<u64> press_state{};
bool permit_vibration_session_enabled;
std::array<std::array<NpadControllerData, MaxSupportedNpadIdTypes>, AruidIndexMax>
controller_data{};
};

View file

@ -8,6 +8,10 @@
#include "common/common_types.h"
#include "hid_core/hid_types.h"
namespace Core::HID {
class EmulatedController;
}
namespace Service::HID {
static constexpr std::size_t MaxSupportedNpadIdTypes = 10;
static constexpr std::size_t StyleIndexCount = 7;
@ -348,7 +352,7 @@ struct IAbstractedPad {
u8 indicator;
std::vector<f32> virtual_six_axis_sensor_acceleration;
std::vector<f32> virtual_six_axis_sensor_angle;
u64 xcd_handle;
Core::HID::EmulatedController* xcd_handle;
u64 color;
};
} // namespace Service::HID

View file

@ -77,4 +77,8 @@ Result NpadVibration::EndPermitVibrationSession() {
return ResultSuccess;
}
u64 NpadVibration::GetSessionAruid() const {
return session_aruid;
}
} // namespace Service::HID

View file

@ -25,6 +25,8 @@ public:
Result BeginPermitVibrationSession(u64 aruid);
Result EndPermitVibrationSession();
u64 GetSessionAruid() const;
private:
f32 volume{};
u64 session_aruid{};

View file

@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "hid_core/frontend/emulated_controller.h"
#include "hid_core/hid_result.h"
#include "hid_core/resources/npad/npad_types.h"
#include "hid_core/resources/npad/npad_vibration.h"
@ -10,24 +11,25 @@ namespace Service::HID {
NpadGcVibrationDevice::NpadGcVibrationDevice() {}
Result NpadGcVibrationDevice::IncrementRefCounter() {
Result NpadGcVibrationDevice::Activate() {
if (ref_counter == 0 && is_mounted) {
f32 volume = 1.0f;
const auto result = vibration_handler->GetVibrationVolume(volume);
if (result.IsSuccess()) {
// TODO: SendVibrationGcErmCommand
xcd_handle->SetVibration(adapter_slot, Core::HID::VibrationGcErmCommand::Stop);
}
}
ref_counter++;
return ResultSuccess;
}
Result NpadGcVibrationDevice::DecrementRefCounter() {
if (ref_counter == 1 && !is_mounted) {
Result NpadGcVibrationDevice::Deactivate() {
if (ref_counter == 1 && is_mounted) {
f32 volume = 1.0f;
const auto result = vibration_handler->GetVibrationVolume(volume);
if (result.IsSuccess()) {
// TODO: SendVibrationGcErmCommand
xcd_handle->SetVibration(adapter_slot, Core::HID::VibrationGcErmCommand::Stop);
}
}
@ -38,6 +40,48 @@ Result NpadGcVibrationDevice::DecrementRefCounter() {
return ResultSuccess;
}
Result NpadGcVibrationDevice::Mount(IAbstractedPad& abstracted_pad, u32 slot,
NpadVibration* handler) {
if (!abstracted_pad.internal_flags.is_connected) {
return ResultSuccess;
}
// TODO: This device doesn't use a xcd handle instead has an GC adapter handle. This is just to
// keep compatibility with the front end.
xcd_handle = abstracted_pad.xcd_handle;
adapter_slot = slot;
vibration_handler = handler;
is_mounted = true;
if (ref_counter == 0) {
return ResultSuccess;
}
f32 volume{1.0f};
const auto result = vibration_handler->GetVibrationVolume(volume);
if (result.IsSuccess()) {
xcd_handle->SetVibration(adapter_slot, Core::HID::VibrationGcErmCommand::Stop);
}
return ResultSuccess;
}
Result NpadGcVibrationDevice::Unmount() {
if (ref_counter == 0 || !is_mounted) {
is_mounted = false;
return ResultSuccess;
}
f32 volume{1.0f};
const auto result = vibration_handler->GetVibrationVolume(volume);
if (result.IsSuccess()) {
xcd_handle->SetVibration(adapter_slot, Core::HID::VibrationGcErmCommand::Stop);
}
is_mounted = false;
return ResultSuccess;
}
Result NpadGcVibrationDevice::SendVibrationGcErmCommand(Core::HID::VibrationGcErmCommand command) {
if (!is_mounted) {
return ResultSuccess;
@ -55,7 +99,7 @@ Result NpadGcVibrationDevice::SendVibrationGcErmCommand(Core::HID::VibrationGcEr
return ResultSuccess;
}
}
// TODO: SendVibrationGcErmCommand
xcd_handle->SetVibration(adapter_slot, command);
return ResultSuccess;
}

View file

@ -20,12 +20,18 @@ class NpadGcVibrationDevice final : public NpadVibrationBase {
public:
explicit NpadGcVibrationDevice();
Result IncrementRefCounter() override;
Result DecrementRefCounter() override;
Result Activate() override;
Result Deactivate() override;
Result Mount(IAbstractedPad& abstracted_pad, u32 slot, NpadVibration* handler);
Result Unmount();
Result SendVibrationGcErmCommand(Core::HID::VibrationGcErmCommand command);
Result GetActualVibrationGcErmCommand(Core::HID::VibrationGcErmCommand& out_command);
Result SendVibrationNotificationPattern(Core::HID::VibrationGcErmCommand command);
private:
u32 adapter_slot;
};
} // namespace Service::HID

View file

@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "hid_core/frontend/emulated_controller.h"
#include "hid_core/hid_result.h"
#include "hid_core/resources/npad/npad_types.h"
#include "hid_core/resources/npad/npad_vibration.h"
@ -10,12 +11,12 @@ namespace Service::HID {
NpadN64VibrationDevice::NpadN64VibrationDevice() {}
Result NpadN64VibrationDevice::IncrementRefCounter() {
Result NpadN64VibrationDevice::Activate() {
if (ref_counter == 0 && is_mounted) {
f32 volume = 1.0f;
const auto result = vibration_handler->GetVibrationVolume(volume);
if (result.IsSuccess()) {
// TODO: SendVibrationInBool
xcd_handle->SetVibration(false);
}
}
@ -23,19 +24,12 @@ Result NpadN64VibrationDevice::IncrementRefCounter() {
return ResultSuccess;
}
Result NpadN64VibrationDevice::DecrementRefCounter() {
if (ref_counter == 1) {
if (!is_mounted) {
ref_counter = 0;
if (is_mounted != false) {
// TODO: SendVibrationInBool
}
return ResultSuccess;
}
Result NpadN64VibrationDevice::Deactivate() {
if (ref_counter == 1 && is_mounted) {
f32 volume = 1.0f;
const auto result = vibration_handler->GetVibrationVolume(volume);
if (result.IsSuccess()) {
// TODO
xcd_handle->SetVibration(false);
}
}
@ -46,6 +40,43 @@ Result NpadN64VibrationDevice::DecrementRefCounter() {
return ResultSuccess;
}
Result NpadN64VibrationDevice::Mount(IAbstractedPad& abstracted_pad, NpadVibration* handler) {
if (!abstracted_pad.internal_flags.is_connected) {
return ResultSuccess;
}
xcd_handle = abstracted_pad.xcd_handle;
vibration_handler = handler;
is_mounted = true;
if (ref_counter == 0) {
return ResultSuccess;
}
f32 volume{1.0f};
const auto result = vibration_handler->GetVibrationVolume(volume);
if (result.IsSuccess()) {
xcd_handle->SetVibration(false);
}
return ResultSuccess;
}
Result NpadN64VibrationDevice::Unmount() {
if (ref_counter == 0 || !is_mounted) {
is_mounted = false;
return ResultSuccess;
}
f32 volume{1.0f};
const auto result = vibration_handler->GetVibrationVolume(volume);
if (result.IsSuccess()) {
xcd_handle->SetVibration(false);
}
is_mounted = false;
return ResultSuccess;
}
Result NpadN64VibrationDevice::SendValueInBool(bool is_vibrating) {
if (ref_counter < 1) {
return ResultVibrationNotInitialized;
@ -56,7 +87,7 @@ Result NpadN64VibrationDevice::SendValueInBool(bool is_vibrating) {
if (result.IsError()) {
return result;
}
// TODO: SendVibrationInBool
xcd_handle->SetVibration(false);
}
return ResultSuccess;
}

View file

@ -14,14 +14,18 @@
namespace Service::HID {
class NpadVibration;
struct IAbstractedPad;
/// Handles Npad request from HID interfaces
class NpadN64VibrationDevice final : public NpadVibrationBase {
public:
explicit NpadN64VibrationDevice();
Result IncrementRefCounter() override;
Result DecrementRefCounter() override;
Result Activate() override;
Result Deactivate() override;
Result Mount(IAbstractedPad& abstracted_pad, NpadVibration* handler);
Result Unmount();
Result SendValueInBool(bool is_vibrating);
Result SendVibrationNotificationPattern(u32 pattern);

View file

@ -10,12 +10,12 @@ namespace Service::HID {
NpadVibrationBase::NpadVibrationBase() {}
Result NpadVibrationBase::IncrementRefCounter() {
Result NpadVibrationBase::Activate() {
ref_counter++;
return ResultSuccess;
}
Result NpadVibrationBase::DecrementRefCounter() {
Result NpadVibrationBase::Deactivate() {
if (ref_counter > 0) {
ref_counter--;
}

View file

@ -6,6 +6,10 @@
#include "common/common_types.h"
#include "core/hle/result.h"
namespace Core::HID {
class EmulatedController;
}
namespace Service::HID {
class NpadVibration;
@ -14,13 +18,13 @@ class NpadVibrationBase {
public:
explicit NpadVibrationBase();
virtual Result IncrementRefCounter();
virtual Result DecrementRefCounter();
virtual Result Activate();
virtual Result Deactivate();
bool IsVibrationMounted() const;
protected:
u64 xcd_handle{};
Core::HID::EmulatedController* xcd_handle{nullptr};
s32 ref_counter{};
bool is_mounted{};
NpadVibration* vibration_handler{nullptr};

View file

@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "hid_core/frontend/emulated_controller.h"
#include "hid_core/hid_result.h"
#include "hid_core/resources/npad/npad_types.h"
#include "hid_core/resources/npad/npad_vibration.h"
@ -10,12 +11,30 @@ namespace Service::HID {
NpadVibrationDevice::NpadVibrationDevice() {}
Result NpadVibrationDevice::IncrementRefCounter() {
Result NpadVibrationDevice::Activate() {
if (ref_counter == 0 && is_mounted) {
f32 volume = 1.0f;
const auto result = vibration_handler->GetVibrationVolume(volume);
if (result.IsSuccess()) {
xcd_handle->SetVibration(device_index, Core::HID::DEFAULT_VIBRATION_VALUE);
// TODO: SendNotificationPattern;
}
}
ref_counter++;
return ResultSuccess;
}
Result NpadVibrationDevice::DecrementRefCounter() {
Result NpadVibrationDevice::Deactivate() {
if (ref_counter == 1 && is_mounted) {
f32 volume = 1.0f;
const auto result = vibration_handler->GetVibrationVolume(volume);
if (result.IsSuccess()) {
xcd_handle->SetVibration(device_index, Core::HID::DEFAULT_VIBRATION_VALUE);
// TODO: SendNotificationPattern;
}
}
if (ref_counter > 0) {
ref_counter--;
}
@ -23,6 +42,45 @@ Result NpadVibrationDevice::DecrementRefCounter() {
return ResultSuccess;
}
Result NpadVibrationDevice::Mount(IAbstractedPad& abstracted_pad, Core::HID::DeviceIndex index,
NpadVibration* handler) {
if (!abstracted_pad.internal_flags.is_connected) {
return ResultSuccess;
}
xcd_handle = abstracted_pad.xcd_handle;
device_index = index;
vibration_handler = handler;
is_mounted = true;
if (ref_counter == 0) {
return ResultSuccess;
}
f32 volume{1.0f};
const auto result = vibration_handler->GetVibrationVolume(volume);
if (result.IsSuccess()) {
xcd_handle->SetVibration(false);
}
return ResultSuccess;
}
Result NpadVibrationDevice::Unmount() {
if (ref_counter == 0 || !is_mounted) {
is_mounted = false;
return ResultSuccess;
}
f32 volume{1.0f};
const auto result = vibration_handler->GetVibrationVolume(volume);
if (result.IsSuccess()) {
xcd_handle->SetVibration(device_index, Core::HID::DEFAULT_VIBRATION_VALUE);
}
is_mounted = false;
return ResultSuccess;
}
Result NpadVibrationDevice::SendVibrationValue(const Core::HID::VibrationValue& value) {
if (ref_counter == 0) {
return ResultVibrationNotInitialized;
@ -37,7 +95,7 @@ Result NpadVibrationDevice::SendVibrationValue(const Core::HID::VibrationValue&
return result;
}
if (volume <= 0.0f) {
// TODO: SendVibrationValue
xcd_handle->SetVibration(device_index, Core::HID::DEFAULT_VIBRATION_VALUE);
return ResultSuccess;
}
@ -45,7 +103,7 @@ Result NpadVibrationDevice::SendVibrationValue(const Core::HID::VibrationValue&
vibration_value.high_amplitude *= volume;
vibration_value.low_amplitude *= volume;
// TODO: SendVibrationValue
xcd_handle->SetVibration(device_index, vibration_value);
return ResultSuccess;
}
@ -63,11 +121,11 @@ Result NpadVibrationDevice::SendVibrationNotificationPattern([[maybe_unused]] u3
pattern = 0;
}
// return xcd_handle->SendVibrationNotificationPattern(pattern);
// TODO: SendVibrationNotificationPattern;
return ResultSuccess;
}
Result NpadVibrationDevice::GetActualVibrationValue(Core::HID::VibrationValue& out_value) {
Result NpadVibrationDevice::GetActualVibrationValue(Core::HID::VibrationValue& out_value) const {
if (ref_counter < 1) {
return ResultVibrationNotInitialized;
}
@ -77,7 +135,7 @@ Result NpadVibrationDevice::GetActualVibrationValue(Core::HID::VibrationValue& o
return ResultSuccess;
}
// TODO: SendVibrationValue
out_value = xcd_handle->GetActualVibrationValue(device_index);
return ResultSuccess;
}

View file

@ -12,6 +12,10 @@
#include "hid_core/resources/npad/npad_types.h"
#include "hid_core/resources/vibration/vibration_base.h"
namespace Core::HID {
enum class DeviceIndex : u8;
}
namespace Service::HID {
class NpadVibration;
@ -20,16 +24,20 @@ class NpadVibrationDevice final : public NpadVibrationBase {
public:
explicit NpadVibrationDevice();
Result IncrementRefCounter();
Result DecrementRefCounter();
Result Activate();
Result Deactivate();
Result Mount(IAbstractedPad& abstracted_pad, Core::HID::DeviceIndex index,
NpadVibration* handler);
Result Unmount();
Result SendVibrationValue(const Core::HID::VibrationValue& value);
Result SendVibrationNotificationPattern(u32 pattern);
Result GetActualVibrationValue(Core::HID::VibrationValue& out_value);
Result GetActualVibrationValue(Core::HID::VibrationValue& out_value) const;
private:
u32 device_index{};
Core::HID::DeviceIndex device_index{};
};
} // namespace Service::HID

View file

@ -24,9 +24,8 @@ constexpr VAddr c = 16 * HIGH_PAGE_SIZE;
class RasterizerInterface {
public:
void UpdatePagesCachedCount(VAddr addr, u64 size, int delta) {
const u64 page_start{addr >> Core::Memory::YUZU_PAGEBITS};
const u64 page_end{(addr + size + Core::Memory::YUZU_PAGESIZE - 1) >>
Core::Memory::YUZU_PAGEBITS};
const u64 page_start{addr >> Core::DEVICE_PAGEBITS};
const u64 page_end{(addr + size + Core::DEVICE_PAGESIZE - 1) >> Core::DEVICE_PAGEBITS};
for (u64 page = page_start; page < page_end; ++page) {
int& value = page_table[page];
value += delta;
@ -40,7 +39,7 @@ public:
}
[[nodiscard]] int Count(VAddr addr) const noexcept {
const auto it = page_table.find(addr >> Core::Memory::YUZU_PAGEBITS);
const auto it = page_table.find(addr >> Core::DEVICE_PAGEBITS);
return it == page_table.end() ? 0 : it->second;
}

View file

@ -71,6 +71,8 @@ add_library(video_core STATIC
host1x/ffmpeg/ffmpeg.h
host1x/control.cpp
host1x/control.h
host1x/gpu_device_memory_manager.cpp
host1x/gpu_device_memory_manager.h
host1x/host1x.cpp
host1x/host1x.h
host1x/nvdec.cpp
@ -93,6 +95,7 @@ add_library(video_core STATIC
gpu.h
gpu_thread.cpp
gpu_thread.h
guest_memory.h
invalidation_accumulator.h
memory_manager.cpp
memory_manager.h
@ -105,8 +108,6 @@ add_library(video_core STATIC
query_cache/query_stream.h
query_cache/types.h
query_cache.h
rasterizer_accelerated.cpp
rasterizer_accelerated.h
rasterizer_interface.h
renderer_base.cpp
renderer_base.h

View file

@ -33,13 +33,12 @@ struct NullBufferParams {};
*
* The buffer size and address is forcefully aligned to CPU page boundaries.
*/
template <class RasterizerInterface>
class BufferBase {
public:
static constexpr u64 BASE_PAGE_BITS = 16;
static constexpr u64 BASE_PAGE_SIZE = 1ULL << BASE_PAGE_BITS;
explicit BufferBase(RasterizerInterface& rasterizer_, VAddr cpu_addr_, u64 size_bytes_)
explicit BufferBase(VAddr cpu_addr_, u64 size_bytes_)
: cpu_addr{cpu_addr_}, size_bytes{size_bytes_} {}
explicit BufferBase(NullBufferParams) {}

File diff suppressed because it is too large Load diff

View file

@ -32,7 +32,6 @@
#include "common/microprofile.h"
#include "common/scope_exit.h"
#include "common/settings.h"
#include "core/memory.h"
#include "video_core/buffer_cache/buffer_base.h"
#include "video_core/control/channel_state_cache.h"
#include "video_core/delayed_destruction_ring.h"
@ -41,7 +40,6 @@
#include "video_core/engines/kepler_compute.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/memory_manager.h"
#include "video_core/rasterizer_interface.h"
#include "video_core/surface.h"
#include "video_core/texture_cache/slot_vector.h"
#include "video_core/texture_cache/types.h"
@ -94,7 +92,7 @@ static constexpr BufferId NULL_BUFFER_ID{0};
static constexpr u32 DEFAULT_SKIP_CACHE_SIZE = static_cast<u32>(4_KiB);
struct Binding {
VAddr cpu_addr{};
DAddr device_addr{};
u32 size{};
BufferId buffer_id;
};
@ -104,7 +102,7 @@ struct TextureBufferBinding : Binding {
};
static constexpr Binding NULL_BINDING{
.cpu_addr = 0,
.device_addr = 0,
.size = 0,
.buffer_id = NULL_BUFFER_ID,
};
@ -204,10 +202,10 @@ class BufferCache : public VideoCommon::ChannelSetupCaches<BufferCacheChannelInf
using Async_Buffer = typename P::Async_Buffer;
using MemoryTracker = typename P::MemoryTracker;
using IntervalCompare = std::less<VAddr>;
using IntervalInstance = boost::icl::interval_type_default<VAddr, std::less>;
using IntervalAllocator = boost::fast_pool_allocator<VAddr>;
using IntervalSet = boost::icl::interval_set<VAddr>;
using IntervalCompare = std::less<DAddr>;
using IntervalInstance = boost::icl::interval_type_default<DAddr, std::less>;
using IntervalAllocator = boost::fast_pool_allocator<DAddr>;
using IntervalSet = boost::icl::interval_set<DAddr>;
using IntervalType = typename IntervalSet::interval_type;
template <typename Type>
@ -230,32 +228,31 @@ class BufferCache : public VideoCommon::ChannelSetupCaches<BufferCacheChannelInf
using OverlapCombine = counter_add_functor<int>;
using OverlapSection = boost::icl::inter_section<int>;
using OverlapCounter = boost::icl::split_interval_map<VAddr, int>;
using OverlapCounter = boost::icl::split_interval_map<DAddr, int>;
struct OverlapResult {
boost::container::small_vector<BufferId, 16> ids;
VAddr begin;
VAddr end;
DAddr begin;
DAddr end;
bool has_stream_leap = false;
};
public:
explicit BufferCache(VideoCore::RasterizerInterface& rasterizer_,
Core::Memory::Memory& cpu_memory_, Runtime& runtime_);
explicit BufferCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, Runtime& runtime_);
void TickFrame();
void WriteMemory(VAddr cpu_addr, u64 size);
void WriteMemory(DAddr device_addr, u64 size);
void CachedWriteMemory(VAddr cpu_addr, u64 size);
void CachedWriteMemory(DAddr device_addr, u64 size);
bool OnCPUWrite(VAddr cpu_addr, u64 size);
bool OnCPUWrite(DAddr device_addr, u64 size);
void DownloadMemory(VAddr cpu_addr, u64 size);
void DownloadMemory(DAddr device_addr, u64 size);
std::optional<VideoCore::RasterizerDownloadArea> GetFlushArea(VAddr cpu_addr, u64 size);
std::optional<VideoCore::RasterizerDownloadArea> GetFlushArea(DAddr device_addr, u64 size);
bool InlineMemory(VAddr dest_address, size_t copy_size, std::span<const u8> inlined_buffer);
bool InlineMemory(DAddr dest_address, size_t copy_size, std::span<const u8> inlined_buffer);
void BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, u32 size);
@ -300,7 +297,7 @@ public:
ObtainBufferSynchronize sync_info,
ObtainBufferOperation post_op);
[[nodiscard]] std::pair<Buffer*, u32> ObtainCPUBuffer(VAddr gpu_addr, u32 size,
[[nodiscard]] std::pair<Buffer*, u32> ObtainCPUBuffer(DAddr gpu_addr, u32 size,
ObtainBufferSynchronize sync_info,
ObtainBufferOperation post_op);
void FlushCachedWrites();
@ -326,13 +323,13 @@ public:
bool DMAClear(GPUVAddr src_address, u64 amount, u32 value);
/// Return true when a CPU region is modified from the GPU
[[nodiscard]] bool IsRegionGpuModified(VAddr addr, size_t size);
[[nodiscard]] bool IsRegionGpuModified(DAddr addr, size_t size);
/// Return true when a region is registered on the cache
[[nodiscard]] bool IsRegionRegistered(VAddr addr, size_t size);
[[nodiscard]] bool IsRegionRegistered(DAddr addr, size_t size);
/// Return true when a CPU region is modified from the CPU
[[nodiscard]] bool IsRegionCpuModified(VAddr addr, size_t size);
[[nodiscard]] bool IsRegionCpuModified(DAddr addr, size_t size);
void SetDrawIndirect(
const Tegra::Engines::DrawManager::IndirectParams* current_draw_indirect_) {
@ -366,9 +363,9 @@ private:
}
template <typename Func>
void ForEachBufferInRange(VAddr cpu_addr, u64 size, Func&& func) {
const u64 page_end = Common::DivCeil(cpu_addr + size, CACHING_PAGESIZE);
for (u64 page = cpu_addr >> CACHING_PAGEBITS; page < page_end;) {
void ForEachBufferInRange(DAddr device_addr, u64 size, Func&& func) {
const u64 page_end = Common::DivCeil(device_addr + size, CACHING_PAGESIZE);
for (u64 page = device_addr >> CACHING_PAGEBITS; page < page_end;) {
const BufferId buffer_id = page_table[page];
if (!buffer_id) {
++page;
@ -377,15 +374,15 @@ private:
Buffer& buffer = slot_buffers[buffer_id];
func(buffer_id, buffer);
const VAddr end_addr = buffer.CpuAddr() + buffer.SizeBytes();
const DAddr end_addr = buffer.CpuAddr() + buffer.SizeBytes();
page = Common::DivCeil(end_addr, CACHING_PAGESIZE);
}
}
template <typename Func>
void ForEachInRangeSet(IntervalSet& current_range, VAddr cpu_addr, u64 size, Func&& func) {
const VAddr start_address = cpu_addr;
const VAddr end_address = start_address + size;
void ForEachInRangeSet(IntervalSet& current_range, DAddr device_addr, u64 size, Func&& func) {
const DAddr start_address = device_addr;
const DAddr end_address = start_address + size;
const IntervalType search_interval{start_address, end_address};
auto it = current_range.lower_bound(search_interval);
if (it == current_range.end()) {
@ -393,8 +390,8 @@ private:
}
auto end_it = current_range.upper_bound(search_interval);
for (; it != end_it; it++) {
VAddr inter_addr_end = it->upper();
VAddr inter_addr = it->lower();
DAddr inter_addr_end = it->upper();
DAddr inter_addr = it->lower();
if (inter_addr_end > end_address) {
inter_addr_end = end_address;
}
@ -406,10 +403,10 @@ private:
}
template <typename Func>
void ForEachInOverlapCounter(OverlapCounter& current_range, VAddr cpu_addr, u64 size,
void ForEachInOverlapCounter(OverlapCounter& current_range, DAddr device_addr, u64 size,
Func&& func) {
const VAddr start_address = cpu_addr;
const VAddr end_address = start_address + size;
const DAddr start_address = device_addr;
const DAddr end_address = start_address + size;
const IntervalType search_interval{start_address, end_address};
auto it = current_range.lower_bound(search_interval);
if (it == current_range.end()) {
@ -418,8 +415,8 @@ private:
auto end_it = current_range.upper_bound(search_interval);
for (; it != end_it; it++) {
auto& inter = it->first;
VAddr inter_addr_end = inter.upper();
VAddr inter_addr = inter.lower();
DAddr inter_addr_end = inter.upper();
DAddr inter_addr = inter.lower();
if (inter_addr_end > end_address) {
inter_addr_end = end_address;
}
@ -451,9 +448,9 @@ private:
} while (any_removals);
}
static bool IsRangeGranular(VAddr cpu_addr, size_t size) {
return (cpu_addr & ~Core::Memory::YUZU_PAGEMASK) ==
((cpu_addr + size) & ~Core::Memory::YUZU_PAGEMASK);
static bool IsRangeGranular(DAddr device_addr, size_t size) {
return (device_addr & ~Core::DEVICE_PAGEMASK) ==
((device_addr + size) & ~Core::DEVICE_PAGEMASK);
}
void RunGarbageCollector();
@ -508,15 +505,15 @@ private:
void UpdateComputeTextureBuffers();
void MarkWrittenBuffer(BufferId buffer_id, VAddr cpu_addr, u32 size);
void MarkWrittenBuffer(BufferId buffer_id, DAddr device_addr, u32 size);
[[nodiscard]] BufferId FindBuffer(VAddr cpu_addr, u32 size);
[[nodiscard]] BufferId FindBuffer(DAddr device_addr, u32 size);
[[nodiscard]] OverlapResult ResolveOverlaps(VAddr cpu_addr, u32 wanted_size);
[[nodiscard]] OverlapResult ResolveOverlaps(DAddr device_addr, u32 wanted_size);
void JoinOverlap(BufferId new_buffer_id, BufferId overlap_id, bool accumulate_stream_score);
[[nodiscard]] BufferId CreateBuffer(VAddr cpu_addr, u32 wanted_size);
[[nodiscard]] BufferId CreateBuffer(DAddr device_addr, u32 wanted_size);
void Register(BufferId buffer_id);
@ -527,7 +524,7 @@ private:
void TouchBuffer(Buffer& buffer, BufferId buffer_id) noexcept;
bool SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size);
bool SynchronizeBuffer(Buffer& buffer, DAddr device_addr, u32 size);
void UploadMemory(Buffer& buffer, u64 total_size_bytes, u64 largest_copy,
std::span<BufferCopy> copies);
@ -539,7 +536,7 @@ private:
void DownloadBufferMemory(Buffer& buffer_id);
void DownloadBufferMemory(Buffer& buffer_id, VAddr cpu_addr, u64 size);
void DownloadBufferMemory(Buffer& buffer_id, DAddr device_addr, u64 size);
void DeleteBuffer(BufferId buffer_id, bool do_not_mark = false);
@ -549,7 +546,7 @@ private:
[[nodiscard]] TextureBufferBinding GetTextureBufferBinding(GPUVAddr gpu_addr, u32 size,
PixelFormat format);
[[nodiscard]] std::span<const u8> ImmediateBufferWithData(VAddr cpu_addr, size_t size);
[[nodiscard]] std::span<const u8> ImmediateBufferWithData(DAddr device_addr, size_t size);
[[nodiscard]] std::span<u8> ImmediateBuffer(size_t wanted_capacity);
@ -557,11 +554,10 @@ private:
void ClearDownload(IntervalType subtract_interval);
void InlineMemoryImplementation(VAddr dest_address, size_t copy_size,
void InlineMemoryImplementation(DAddr dest_address, size_t copy_size,
std::span<const u8> inlined_buffer);
VideoCore::RasterizerInterface& rasterizer;
Core::Memory::Memory& cpu_memory;
Tegra::MaxwellDeviceMemoryManager& device_memory;
SlotVector<Buffer> slot_buffers;
DelayedDestructionRing<Buffer, 8> delayed_destruction_ring;
@ -598,7 +594,7 @@ private:
u64 critical_memory = 0;
BufferId inline_buffer_id;
std::array<BufferId, ((1ULL << 39) >> CACHING_PAGEBITS)> page_table;
std::array<BufferId, ((1ULL << 34) >> CACHING_PAGEBITS)> page_table;
Common::ScratchBuffer<u8> tmp_buffer;
};

View file

@ -17,19 +17,19 @@
namespace VideoCommon {
template <class RasterizerInterface>
template <typename DeviceTracker>
class MemoryTrackerBase {
static constexpr size_t MAX_CPU_PAGE_BITS = 39;
static constexpr size_t MAX_CPU_PAGE_BITS = 34;
static constexpr size_t HIGHER_PAGE_BITS = 22;
static constexpr size_t HIGHER_PAGE_SIZE = 1ULL << HIGHER_PAGE_BITS;
static constexpr size_t HIGHER_PAGE_MASK = HIGHER_PAGE_SIZE - 1ULL;
static constexpr size_t NUM_HIGH_PAGES = 1ULL << (MAX_CPU_PAGE_BITS - HIGHER_PAGE_BITS);
static constexpr size_t MANAGER_POOL_SIZE = 32;
static constexpr size_t WORDS_STACK_NEEDED = HIGHER_PAGE_SIZE / BYTES_PER_WORD;
using Manager = WordManager<RasterizerInterface, WORDS_STACK_NEEDED>;
using Manager = WordManager<DeviceTracker, WORDS_STACK_NEEDED>;
public:
MemoryTrackerBase(RasterizerInterface& rasterizer_) : rasterizer{&rasterizer_} {}
MemoryTrackerBase(DeviceTracker& device_tracker_) : device_tracker{&device_tracker_} {}
~MemoryTrackerBase() = default;
/// Returns the inclusive CPU modified range in a begin end pair
@ -74,7 +74,7 @@ public:
});
}
/// Mark region as CPU modified, notifying the rasterizer about this change
/// Mark region as CPU modified, notifying the device_tracker about this change
void MarkRegionAsCpuModified(VAddr dirty_cpu_addr, u64 query_size) {
IteratePages<true>(dirty_cpu_addr, query_size,
[](Manager* manager, u64 offset, size_t size) {
@ -83,7 +83,7 @@ public:
});
}
/// Unmark region as CPU modified, notifying the rasterizer about this change
/// Unmark region as CPU modified, notifying the device_tracker about this change
void UnmarkRegionAsCpuModified(VAddr dirty_cpu_addr, u64 query_size) {
IteratePages<true>(dirty_cpu_addr, query_size,
[](Manager* manager, u64 offset, size_t size) {
@ -139,7 +139,7 @@ public:
});
}
/// Flushes cached CPU writes, and notify the rasterizer about the deltas
/// Flushes cached CPU writes, and notify the device_tracker about the deltas
void FlushCachedWrites(VAddr query_cpu_addr, u64 query_size) noexcept {
IteratePages<false>(query_cpu_addr, query_size,
[](Manager* manager, [[maybe_unused]] u64 offset,
@ -280,7 +280,7 @@ private:
manager_pool.emplace_back();
auto& last_pool = manager_pool.back();
for (size_t i = 0; i < MANAGER_POOL_SIZE; i++) {
new (&last_pool[i]) Manager(0, *rasterizer, HIGHER_PAGE_SIZE);
new (&last_pool[i]) Manager(0, *device_tracker, HIGHER_PAGE_SIZE);
free_managers.push_back(&last_pool[i]);
}
return on_return();
@ -293,7 +293,7 @@ private:
std::unordered_set<u32> cached_pages;
RasterizerInterface* rasterizer = nullptr;
DeviceTracker* device_tracker = nullptr;
};
} // namespace VideoCommon

View file

@ -13,12 +13,12 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/div_ceil.h"
#include "core/memory.h"
#include "video_core/host1x/gpu_device_memory_manager.h"
namespace VideoCommon {
constexpr u64 PAGES_PER_WORD = 64;
constexpr u64 BYTES_PER_PAGE = Core::Memory::YUZU_PAGESIZE;
constexpr u64 BYTES_PER_PAGE = Core::DEVICE_PAGESIZE;
constexpr u64 BYTES_PER_WORD = PAGES_PER_WORD * BYTES_PER_PAGE;
enum class Type {
@ -163,11 +163,11 @@ struct Words {
WordsArray<stack_words> preflushable;
};
template <class RasterizerInterface, size_t stack_words = 1>
template <class DeviceTracker, size_t stack_words = 1>
class WordManager {
public:
explicit WordManager(VAddr cpu_addr_, RasterizerInterface& rasterizer_, u64 size_bytes)
: cpu_addr{cpu_addr_}, rasterizer{&rasterizer_}, words{size_bytes} {}
explicit WordManager(VAddr cpu_addr_, DeviceTracker& tracker_, u64 size_bytes)
: cpu_addr{cpu_addr_}, tracker{&tracker_}, words{size_bytes} {}
explicit WordManager() = default;
@ -279,7 +279,7 @@ public:
}
/**
* Loop over each page in the given range, turn off those bits and notify the rasterizer if
* Loop over each page in the given range, turn off those bits and notify the tracker if
* needed. Call the given function on each turned off range.
*
* @param query_cpu_range Base CPU address to loop over
@ -459,26 +459,26 @@ private:
}
/**
* Notify rasterizer about changes in the CPU tracking state of a word in the buffer
* Notify tracker about changes in the CPU tracking state of a word in the buffer
*
* @param word_index Index to the word to notify to the rasterizer
* @param word_index Index to the word to notify to the tracker
* @param current_bits Current state of the word
* @param new_bits New state of the word
*
* @tparam add_to_rasterizer True when the rasterizer should start tracking the new pages
* @tparam add_to_tracker True when the tracker should start tracking the new pages
*/
template <bool add_to_rasterizer>
template <bool add_to_tracker>
void NotifyRasterizer(u64 word_index, u64 current_bits, u64 new_bits) const {
u64 changed_bits = (add_to_rasterizer ? current_bits : ~current_bits) & new_bits;
u64 changed_bits = (add_to_tracker ? current_bits : ~current_bits) & new_bits;
VAddr addr = cpu_addr + word_index * BYTES_PER_WORD;
IteratePages(changed_bits, [&](size_t offset, size_t size) {
rasterizer->UpdatePagesCachedCount(addr + offset * BYTES_PER_PAGE,
size * BYTES_PER_PAGE, add_to_rasterizer ? 1 : -1);
tracker->UpdatePagesCachedCount(addr + offset * BYTES_PER_PAGE, size * BYTES_PER_PAGE,
add_to_tracker ? 1 : -1);
});
}
VAddr cpu_addr = 0;
RasterizerInterface* rasterizer = nullptr;
DeviceTracker* tracker = nullptr;
Words<stack_words> words;
};

View file

@ -5,10 +5,10 @@
#include "common/microprofile.h"
#include "common/settings.h"
#include "core/core.h"
#include "core/memory.h"
#include "video_core/dma_pusher.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/gpu.h"
#include "video_core/guest_memory.h"
#include "video_core/memory_manager.h"
namespace Tegra {
@ -85,15 +85,15 @@ bool DmaPusher::Step() {
}
}
const auto safe_process = [&] {
Core::Memory::GpuGuestMemory<Tegra::CommandHeader,
Core::Memory::GuestMemoryFlags::SafeRead>
Tegra::Memory::GpuGuestMemory<Tegra::CommandHeader,
Tegra::Memory::GuestMemoryFlags::SafeRead>
headers(memory_manager, dma_state.dma_get, command_list_header.size,
&command_headers);
ProcessCommands(headers);
};
const auto unsafe_process = [&] {
Core::Memory::GpuGuestMemory<Tegra::CommandHeader,
Core::Memory::GuestMemoryFlags::UnsafeRead>
Tegra::Memory::GpuGuestMemory<Tegra::CommandHeader,
Tegra::Memory::GuestMemoryFlags::UnsafeRead>
headers(memory_manager, dma_state.dma_get, command_list_header.size,
&command_headers);
ProcessCommands(headers);

View file

@ -5,8 +5,8 @@
#include "common/algorithm.h"
#include "common/assert.h"
#include "core/memory.h"
#include "video_core/engines/engine_upload.h"
#include "video_core/guest_memory.h"
#include "video_core/memory_manager.h"
#include "video_core/rasterizer_interface.h"
#include "video_core/textures/decoders.h"
@ -68,7 +68,8 @@ void State::ProcessData(std::span<const u8> read_buffer) {
true, bytes_per_pixel, width, regs.dest.height, regs.dest.depth,
regs.dest.BlockHeight(), regs.dest.BlockDepth());
Core::Memory::GpuGuestMemoryScoped<u8, Core::Memory::GuestMemoryFlags::SafeReadCachedWrite>
Tegra::Memory::GpuGuestMemoryScoped<u8,
Tegra::Memory::GuestMemoryFlags::SafeReadCachedWrite>
tmp(memory_manager, address, dst_size, &tmp_buffer);
Tegra::Texture::SwizzleSubrect(tmp, read_buffer, bytes_per_pixel, width, regs.dest.height,

View file

@ -9,7 +9,6 @@
#include "common/settings.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/memory.h"
#include "video_core/dirty_flags.h"
#include "video_core/engines/draw_manager.h"
#include "video_core/engines/maxwell_3d.h"

View file

@ -8,9 +8,9 @@
#include "common/polyfill_ranges.h"
#include "common/settings.h"
#include "core/core.h"
#include "core/memory.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/engines/maxwell_dma.h"
#include "video_core/guest_memory.h"
#include "video_core/memory_manager.h"
#include "video_core/renderer_base.h"
#include "video_core/textures/decoders.h"
@ -133,8 +133,8 @@ void MaxwellDMA::Launch() {
UNIMPLEMENTED_IF(regs.offset_out % 16 != 0);
read_buffer.resize_destructive(16);
for (u32 offset = 0; offset < regs.line_length_in; offset += 16) {
Core::Memory::GpuGuestMemoryScoped<
u8, Core::Memory::GuestMemoryFlags::SafeReadCachedWrite>
Tegra::Memory::GpuGuestMemoryScoped<
u8, Tegra::Memory::GuestMemoryFlags::SafeReadCachedWrite>
tmp_write_buffer(memory_manager,
convert_linear_2_blocklinear_addr(regs.offset_in + offset),
16, &read_buffer);
@ -146,16 +146,16 @@ void MaxwellDMA::Launch() {
UNIMPLEMENTED_IF(regs.offset_out % 16 != 0);
read_buffer.resize_destructive(16);
for (u32 offset = 0; offset < regs.line_length_in; offset += 16) {
Core::Memory::GpuGuestMemoryScoped<
u8, Core::Memory::GuestMemoryFlags::SafeReadCachedWrite>
Tegra::Memory::GpuGuestMemoryScoped<
u8, Tegra::Memory::GuestMemoryFlags::SafeReadCachedWrite>
tmp_write_buffer(memory_manager, regs.offset_in + offset, 16, &read_buffer);
tmp_write_buffer.SetAddressAndSize(
convert_linear_2_blocklinear_addr(regs.offset_out + offset), 16);
}
} else {
if (!accelerate.BufferCopy(regs.offset_in, regs.offset_out, regs.line_length_in)) {
Core::Memory::GpuGuestMemoryScoped<
u8, Core::Memory::GuestMemoryFlags::SafeReadCachedWrite>
Tegra::Memory::GpuGuestMemoryScoped<
u8, Tegra::Memory::GuestMemoryFlags::SafeReadCachedWrite>
tmp_write_buffer(memory_manager, regs.offset_in, regs.line_length_in,
&read_buffer);
tmp_write_buffer.SetAddressAndSize(regs.offset_out, regs.line_length_in);
@ -226,9 +226,9 @@ void MaxwellDMA::CopyBlockLinearToPitch() {
const size_t dst_size = dst_operand.pitch * regs.line_count;
Core::Memory::GpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead> tmp_read_buffer(
Tegra::Memory::GpuGuestMemory<u8, Tegra::Memory::GuestMemoryFlags::SafeRead> tmp_read_buffer(
memory_manager, src_operand.address, src_size, &read_buffer);
Core::Memory::GpuGuestMemoryScoped<u8, Core::Memory::GuestMemoryFlags::UnsafeReadCachedWrite>
Tegra::Memory::GpuGuestMemoryScoped<u8, Tegra::Memory::GuestMemoryFlags::UnsafeReadCachedWrite>
tmp_write_buffer(memory_manager, dst_operand.address, dst_size, &write_buffer);
UnswizzleSubrect(tmp_write_buffer, tmp_read_buffer, bytes_per_pixel, width, height, depth,
@ -290,9 +290,9 @@ void MaxwellDMA::CopyPitchToBlockLinear() {
GPUVAddr src_addr = regs.offset_in;
GPUVAddr dst_addr = regs.offset_out;
Core::Memory::GpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead> tmp_read_buffer(
Tegra::Memory::GpuGuestMemory<u8, Tegra::Memory::GuestMemoryFlags::SafeRead> tmp_read_buffer(
memory_manager, src_addr, src_size, &read_buffer);
Core::Memory::GpuGuestMemoryScoped<u8, Core::Memory::GuestMemoryFlags::UnsafeReadCachedWrite>
Tegra::Memory::GpuGuestMemoryScoped<u8, Tegra::Memory::GuestMemoryFlags::UnsafeReadCachedWrite>
tmp_write_buffer(memory_manager, dst_addr, dst_size, &write_buffer);
// If the input is linear and the output is tiled, swizzle the input and copy it over.
@ -344,9 +344,9 @@ void MaxwellDMA::CopyBlockLinearToBlockLinear() {
intermediate_buffer.resize_destructive(mid_buffer_size);
Core::Memory::GpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead> tmp_read_buffer(
Tegra::Memory::GpuGuestMemory<u8, Tegra::Memory::GuestMemoryFlags::SafeRead> tmp_read_buffer(
memory_manager, regs.offset_in, src_size, &read_buffer);
Core::Memory::GpuGuestMemoryScoped<u8, Core::Memory::GuestMemoryFlags::SafeReadCachedWrite>
Tegra::Memory::GpuGuestMemoryScoped<u8, Tegra::Memory::GuestMemoryFlags::SafeReadCachedWrite>
tmp_write_buffer(memory_manager, regs.offset_out, dst_size, &write_buffer);
UnswizzleSubrect(intermediate_buffer, tmp_read_buffer, bytes_per_pixel, src_width, src.height,

View file

@ -8,6 +8,7 @@
#include "common/scratch_buffer.h"
#include "video_core/engines/sw_blitter/blitter.h"
#include "video_core/engines/sw_blitter/converter.h"
#include "video_core/guest_memory.h"
#include "video_core/memory_manager.h"
#include "video_core/surface.h"
#include "video_core/textures/decoders.h"
@ -160,7 +161,7 @@ bool SoftwareBlitEngine::Blit(Fermi2D::Surface& src, Fermi2D::Surface& dst,
const auto dst_bytes_per_pixel = BytesPerBlock(PixelFormatFromRenderTargetFormat(dst.format));
const size_t src_size = get_surface_size(src, src_bytes_per_pixel);
Core::Memory::GpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead> tmp_buffer(
Tegra::Memory::GpuGuestMemory<u8, Tegra::Memory::GuestMemoryFlags::SafeRead> tmp_buffer(
memory_manager, src.Address(), src_size, &impl->tmp_buffer);
const size_t src_copy_size = src_extent_x * src_extent_y * src_bytes_per_pixel;
@ -220,7 +221,7 @@ bool SoftwareBlitEngine::Blit(Fermi2D::Surface& src, Fermi2D::Surface& dst,
}
const size_t dst_size = get_surface_size(dst, dst_bytes_per_pixel);
Core::Memory::GpuGuestMemoryScoped<u8, Core::Memory::GuestMemoryFlags::SafeReadWrite>
Tegra::Memory::GpuGuestMemoryScoped<u8, Tegra::Memory::GuestMemoryFlags::SafeReadWrite>
tmp_buffer2(memory_manager, dst.Address(), dst_size, &impl->tmp_buffer);
if (dst.linear == Fermi2D::MemoryLayout::BlockLinear) {

View file

@ -14,7 +14,7 @@ namespace Tegra {
* Struct describing framebuffer configuration
*/
struct FramebufferConfig {
VAddr address{};
DAddr address{};
u32 offset{};
u32 width{};
u32 height{};

Some files were not shown because too many files have changed in this diff Show more