SMMU: Initial adaptation to video_core.
parent
c85d7ccd79
commit
0a2536a0df
@ -0,0 +1,218 @@
|
|||||||
|
// 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/scratch_buffer.h"
|
||||||
|
#include "core/memory.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
|
||||||
|
|
||||||
|
template <typename T, GuestMemoryFlags FLAGS>
|
||||||
|
using CpuGuestMemory = GuestMemory<Core::Memory::Memory, T, FLAGS>;
|
||||||
|
template <typename T, GuestMemoryFlags FLAGS>
|
||||||
|
using CpuGuestMemoryScoped = GuestMemoryScoped<Core::Memory::Memory, T, FLAGS>;
|
||||||
|
|
||||||
|
} // namespace Tegra::Memory
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,29 @@
|
|||||||
|
// 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/scratch_buffer.h"
|
||||||
|
#include "core/guest_memory.h"
|
||||||
|
#include "video_core/memory_manager.h"
|
||||||
|
|
||||||
|
namespace Tegra::Memory {
|
||||||
|
|
||||||
|
using GuestMemoryFlags = Core::Memory::GuestMemoryFlags;
|
||||||
|
|
||||||
|
template <typename T, GuestMemoryFlags FLAGS>
|
||||||
|
using DeviceGuestMemory = Core::Memory::GuestMemory<Tegra::MaxwellDeviceMemoryManager, T, FLAGS>;
|
||||||
|
template <typename T, GuestMemoryFlags FLAGS>
|
||||||
|
using DeviceGuestMemoryScoped = Core::Memory::GuestMemoryScoped<Tegra::MaxwellDeviceMemoryManager, T, FLAGS>;
|
||||||
|
template <typename T, GuestMemoryFlags FLAGS>
|
||||||
|
using GpuGuestMemory = Core::Memory::GuestMemory<Tegra::MemoryManager, T, FLAGS>;
|
||||||
|
template <typename T, GuestMemoryFlags FLAGS>
|
||||||
|
using GpuGuestMemoryScoped = Core::Memory::GuestMemoryScoped<Tegra::MemoryManager, T, FLAGS>;
|
||||||
|
|
||||||
|
} // namespace Tegra::Memory
|
@ -1,72 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
|
|
||||||
#include <atomic>
|
|
||||||
|
|
||||||
#include "common/assert.h"
|
|
||||||
#include "common/common_types.h"
|
|
||||||
#include "common/div_ceil.h"
|
|
||||||
#include "core/memory.h"
|
|
||||||
#include "video_core/rasterizer_accelerated.h"
|
|
||||||
|
|
||||||
namespace VideoCore {
|
|
||||||
|
|
||||||
using namespace Core::Memory;
|
|
||||||
|
|
||||||
RasterizerAccelerated::RasterizerAccelerated(Memory& cpu_memory_)
|
|
||||||
: cached_pages(std::make_unique<CachedPages>()), cpu_memory{cpu_memory_} {}
|
|
||||||
|
|
||||||
RasterizerAccelerated::~RasterizerAccelerated() = default;
|
|
||||||
|
|
||||||
void RasterizerAccelerated::UpdatePagesCachedCount(VAddr addr, u64 size, int delta) {
|
|
||||||
u64 uncache_begin = 0;
|
|
||||||
u64 cache_begin = 0;
|
|
||||||
u64 uncache_bytes = 0;
|
|
||||||
u64 cache_bytes = 0;
|
|
||||||
|
|
||||||
std::atomic_thread_fence(std::memory_order_acquire);
|
|
||||||
const u64 page_end = Common::DivCeil(addr + size, YUZU_PAGESIZE);
|
|
||||||
for (u64 page = addr >> YUZU_PAGEBITS; page != page_end; ++page) {
|
|
||||||
std::atomic_uint16_t& count = cached_pages->at(page >> 2).Count(page);
|
|
||||||
|
|
||||||
if (delta > 0) {
|
|
||||||
ASSERT_MSG(count.load(std::memory_order::relaxed) < UINT16_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<u16>(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 = page;
|
|
||||||
}
|
|
||||||
uncache_bytes += YUZU_PAGESIZE;
|
|
||||||
} else if (uncache_bytes > 0) {
|
|
||||||
cpu_memory.RasterizerMarkRegionCached(uncache_begin << YUZU_PAGEBITS, uncache_bytes,
|
|
||||||
false);
|
|
||||||
uncache_bytes = 0;
|
|
||||||
}
|
|
||||||
if (count.load(std::memory_order::relaxed) == 1 && delta > 0) {
|
|
||||||
if (cache_bytes == 0) {
|
|
||||||
cache_begin = page;
|
|
||||||
}
|
|
||||||
cache_bytes += YUZU_PAGESIZE;
|
|
||||||
} else if (cache_bytes > 0) {
|
|
||||||
cpu_memory.RasterizerMarkRegionCached(cache_begin << YUZU_PAGEBITS, cache_bytes, true);
|
|
||||||
cache_bytes = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (uncache_bytes > 0) {
|
|
||||||
cpu_memory.RasterizerMarkRegionCached(uncache_begin << YUZU_PAGEBITS, uncache_bytes, false);
|
|
||||||
}
|
|
||||||
if (cache_bytes > 0) {
|
|
||||||
cpu_memory.RasterizerMarkRegionCached(cache_begin << YUZU_PAGEBITS, cache_bytes, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace VideoCore
|
|
@ -1,49 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <atomic>
|
|
||||||
|
|
||||||
#include "common/common_types.h"
|
|
||||||
#include "video_core/rasterizer_interface.h"
|
|
||||||
|
|
||||||
namespace Core::Memory {
|
|
||||||
class Memory;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace VideoCore {
|
|
||||||
|
|
||||||
/// Implements the shared part in GPU accelerated rasterizers in RasterizerInterface.
|
|
||||||
class RasterizerAccelerated : public RasterizerInterface {
|
|
||||||
public:
|
|
||||||
explicit RasterizerAccelerated(Core::Memory::Memory& cpu_memory_);
|
|
||||||
~RasterizerAccelerated() override;
|
|
||||||
|
|
||||||
void UpdatePagesCachedCount(VAddr addr, u64 size, int delta) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
class CacheEntry final {
|
|
||||||
public:
|
|
||||||
CacheEntry() = default;
|
|
||||||
|
|
||||||
std::atomic_uint16_t& Count(std::size_t page) {
|
|
||||||
return values[page & 3];
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::atomic_uint16_t& Count(std::size_t page) const {
|
|
||||||
return values[page & 3];
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::array<std::atomic_uint16_t, 4> values{};
|
|
||||||
};
|
|
||||||
static_assert(sizeof(CacheEntry) == 8, "CacheEntry should be 8 bytes!");
|
|
||||||
|
|
||||||
using CachedPages = std::array<CacheEntry, 0x2000000>;
|
|
||||||
std::unique_ptr<CachedPages> cached_pages;
|
|
||||||
Core::Memory::Memory& cpu_memory;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace VideoCore
|
|
Loading…
Reference in New Issue