early-access version 4115
This commit is contained in:
parent
edd2e2886b
commit
3882b7c96c
18 changed files with 782 additions and 453 deletions
|
@ -1,7 +1,7 @@
|
||||||
yuzu emulator early access
|
yuzu emulator early access
|
||||||
=============
|
=============
|
||||||
|
|
||||||
This is the source code for early-access 4114.
|
This is the source code for early-access 4115.
|
||||||
|
|
||||||
## Legal Notice
|
## Legal Notice
|
||||||
|
|
||||||
|
|
|
@ -107,6 +107,8 @@ add_library(common STATIC
|
||||||
quaternion.h
|
quaternion.h
|
||||||
range_map.h
|
range_map.h
|
||||||
range_mutex.h
|
range_mutex.h
|
||||||
|
range_sets.h
|
||||||
|
range_sets.inc
|
||||||
reader_writer_queue.h
|
reader_writer_queue.h
|
||||||
ring_buffer.h
|
ring_buffer.h
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/scm_rev.cpp
|
${CMAKE_CURRENT_BINARY_DIR}/scm_rev.cpp
|
||||||
|
@ -121,6 +123,7 @@ add_library(common STATIC
|
||||||
settings_input.cpp
|
settings_input.cpp
|
||||||
settings_input.h
|
settings_input.h
|
||||||
settings_setting.h
|
settings_setting.h
|
||||||
|
slot_vector.h
|
||||||
socket_types.h
|
socket_types.h
|
||||||
spin_lock.cpp
|
spin_lock.cpp
|
||||||
spin_lock.h
|
spin_lock.h
|
||||||
|
|
73
src/common/range_sets.h
Executable file
73
src/common/range_sets.h
Executable file
|
@ -0,0 +1,73 @@
|
||||||
|
// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "common/common_types.h"
|
||||||
|
|
||||||
|
namespace Common {
|
||||||
|
|
||||||
|
template <typename AddressType>
|
||||||
|
class RangeSet {
|
||||||
|
public:
|
||||||
|
RangeSet();
|
||||||
|
~RangeSet();
|
||||||
|
|
||||||
|
RangeSet(RangeSet const&) = delete;
|
||||||
|
RangeSet& operator=(RangeSet const&) = delete;
|
||||||
|
|
||||||
|
RangeSet(RangeSet&& other);
|
||||||
|
RangeSet& operator=(RangeSet&& other);
|
||||||
|
|
||||||
|
void Add(AddressType base_address, size_t size);
|
||||||
|
void Subtract(AddressType base_address, size_t size);
|
||||||
|
void Clear();
|
||||||
|
bool Empty() const;
|
||||||
|
|
||||||
|
template <typename Func>
|
||||||
|
void ForEach(Func&& func) const;
|
||||||
|
|
||||||
|
template <typename Func>
|
||||||
|
void ForEachInRange(AddressType device_addr, size_t size, Func&& func) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct RangeSetImpl;
|
||||||
|
std::unique_ptr<RangeSetImpl> m_impl;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename AddressType>
|
||||||
|
class OverlapRangeSet {
|
||||||
|
public:
|
||||||
|
OverlapRangeSet();
|
||||||
|
~OverlapRangeSet();
|
||||||
|
|
||||||
|
OverlapRangeSet(OverlapRangeSet const&) = delete;
|
||||||
|
OverlapRangeSet& operator=(OverlapRangeSet const&) = delete;
|
||||||
|
|
||||||
|
OverlapRangeSet(OverlapRangeSet&& other);
|
||||||
|
OverlapRangeSet& operator=(OverlapRangeSet&& other);
|
||||||
|
|
||||||
|
void Add(AddressType base_address, size_t size);
|
||||||
|
void Subtract(AddressType base_address, size_t size);
|
||||||
|
|
||||||
|
template <typename Func>
|
||||||
|
void Subtract(AddressType base_address, size_t size, Func&& on_delete);
|
||||||
|
|
||||||
|
void DeleteAll(AddressType base_address, size_t size);
|
||||||
|
void Clear();
|
||||||
|
bool Empty() const;
|
||||||
|
|
||||||
|
template <typename Func>
|
||||||
|
void ForEach(Func&& func) const;
|
||||||
|
|
||||||
|
template <typename Func>
|
||||||
|
void ForEachInRange(AddressType device_addr, size_t size, Func&& func) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct OverlapRangeSetImpl;
|
||||||
|
std::unique_ptr<OverlapRangeSetImpl> m_impl;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Common
|
304
src/common/range_sets.inc
Executable file
304
src/common/range_sets.inc
Executable file
|
@ -0,0 +1,304 @@
|
||||||
|
// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include <boost/icl/interval.hpp>
|
||||||
|
#include <boost/icl/interval_base_set.hpp>
|
||||||
|
#include <boost/icl/interval_map.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 "common/range_sets.h"
|
||||||
|
|
||||||
|
namespace Common {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
template <class T>
|
||||||
|
using RangeSetsAllocator =
|
||||||
|
boost::fast_pool_allocator<T, boost::default_user_allocator_new_delete,
|
||||||
|
boost::details::pool::default_mutex, 1024, 2048>;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename AddressType>
|
||||||
|
struct RangeSet<AddressType>::RangeSetImpl {
|
||||||
|
using IntervalSet = boost::icl::interval_set<
|
||||||
|
AddressType, std::less, ICL_INTERVAL_INSTANCE(ICL_INTERVAL_DEFAULT, AddressType, std::less),
|
||||||
|
RangeSetsAllocator>;
|
||||||
|
using IntervalType = typename IntervalSet::interval_type;
|
||||||
|
|
||||||
|
RangeSetImpl() = default;
|
||||||
|
~RangeSetImpl() = default;
|
||||||
|
|
||||||
|
void Add(AddressType base_address, size_t size) {
|
||||||
|
AddressType end_address = base_address + static_cast<AddressType>(size);
|
||||||
|
IntervalType interval{base_address, end_address};
|
||||||
|
m_ranges_set.add(interval);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Subtract(AddressType base_address, size_t size) {
|
||||||
|
AddressType end_address = base_address + static_cast<AddressType>(size);
|
||||||
|
IntervalType interval{base_address, end_address};
|
||||||
|
m_ranges_set.subtract(interval);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Func>
|
||||||
|
void ForEach(Func&& func) const {
|
||||||
|
if (m_ranges_set.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto it = m_ranges_set.begin();
|
||||||
|
auto end_it = m_ranges_set.end();
|
||||||
|
for (; it != end_it; it++) {
|
||||||
|
const AddressType inter_addr_end = it->upper();
|
||||||
|
const AddressType inter_addr = it->lower();
|
||||||
|
func(inter_addr, inter_addr_end);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Func>
|
||||||
|
void ForEachInRange(AddressType base_addr, size_t size, Func&& func) const {
|
||||||
|
if (m_ranges_set.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const AddressType start_address = base_addr;
|
||||||
|
const AddressType end_address = start_address + size;
|
||||||
|
const RangeSetImpl::IntervalType search_interval{start_address, end_address};
|
||||||
|
auto it = m_ranges_set.lower_bound(search_interval);
|
||||||
|
if (it == m_ranges_set.end()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto end_it = m_ranges_set.upper_bound(search_interval);
|
||||||
|
for (; it != end_it; it++) {
|
||||||
|
AddressType inter_addr_end = it->upper();
|
||||||
|
AddressType inter_addr = it->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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IntervalSet m_ranges_set;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename AddressType>
|
||||||
|
struct OverlapRangeSet<AddressType>::OverlapRangeSetImpl {
|
||||||
|
using IntervalSet = boost::icl::split_interval_map<
|
||||||
|
AddressType, s32, boost::icl::partial_enricher, std::less, boost::icl::inplace_plus,
|
||||||
|
boost::icl::inter_section,
|
||||||
|
ICL_INTERVAL_INSTANCE(ICL_INTERVAL_DEFAULT, AddressType, std::less), RangeSetsAllocator>;
|
||||||
|
using IntervalType = typename IntervalSet::interval_type;
|
||||||
|
|
||||||
|
OverlapRangeSetImpl() = default;
|
||||||
|
~OverlapRangeSetImpl() = default;
|
||||||
|
|
||||||
|
void Add(AddressType base_address, size_t size) {
|
||||||
|
AddressType end_address = base_address + static_cast<AddressType>(size);
|
||||||
|
IntervalType interval{base_address, end_address};
|
||||||
|
m_split_ranges_set += std::make_pair(interval, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <bool has_on_delete, typename Func>
|
||||||
|
void Subtract(AddressType base_address, size_t size, s32 amount,
|
||||||
|
[[maybe_unused]] Func&& on_delete) {
|
||||||
|
if (m_split_ranges_set.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
AddressType end_address = base_address + static_cast<AddressType>(size);
|
||||||
|
IntervalType interval{base_address, end_address};
|
||||||
|
bool any_removals = false;
|
||||||
|
m_split_ranges_set += std::make_pair(interval, -amount);
|
||||||
|
do {
|
||||||
|
any_removals = false;
|
||||||
|
auto it = m_split_ranges_set.lower_bound(interval);
|
||||||
|
if (it == m_split_ranges_set.end()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto end_it = m_split_ranges_set.upper_bound(interval);
|
||||||
|
for (; it != end_it; it++) {
|
||||||
|
if (it->second <= 0) {
|
||||||
|
if constexpr (has_on_delete) {
|
||||||
|
if (it->second == 0) {
|
||||||
|
on_delete(it->first.lower(), it->first.upper());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
any_removals = true;
|
||||||
|
m_split_ranges_set.erase(it);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (any_removals);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Func>
|
||||||
|
void ForEach(Func&& func) const {
|
||||||
|
if (m_split_ranges_set.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto it = m_split_ranges_set.begin();
|
||||||
|
auto end_it = m_split_ranges_set.end();
|
||||||
|
for (; it != end_it; it++) {
|
||||||
|
const AddressType inter_addr_end = it->first.upper();
|
||||||
|
const AddressType inter_addr = it->first.lower();
|
||||||
|
func(inter_addr, inter_addr_end, it->second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Func>
|
||||||
|
void ForEachInRange(AddressType base_address, size_t size, Func&& func) const {
|
||||||
|
if (m_split_ranges_set.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const AddressType start_address = base_address;
|
||||||
|
const AddressType end_address = start_address + size;
|
||||||
|
const OverlapRangeSetImpl::IntervalType search_interval{start_address, end_address};
|
||||||
|
auto it = m_split_ranges_set.lower_bound(search_interval);
|
||||||
|
if (it == m_split_ranges_set.end()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto end_it = m_split_ranges_set.upper_bound(search_interval);
|
||||||
|
for (; it != end_it; it++) {
|
||||||
|
auto& inter = it->first;
|
||||||
|
AddressType inter_addr_end = inter.upper();
|
||||||
|
AddressType 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IntervalSet m_split_ranges_set;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename AddressType>
|
||||||
|
RangeSet<AddressType>::RangeSet() {
|
||||||
|
m_impl = std::make_unique<RangeSet<AddressType>::RangeSetImpl>();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename AddressType>
|
||||||
|
RangeSet<AddressType>::~RangeSet() = default;
|
||||||
|
|
||||||
|
template <typename AddressType>
|
||||||
|
RangeSet<AddressType>::RangeSet(RangeSet&& other) {
|
||||||
|
m_impl = std::make_unique<RangeSet<AddressType>::RangeSetImpl>();
|
||||||
|
m_impl->m_ranges_set = std::move(other.m_impl->m_ranges_set);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename AddressType>
|
||||||
|
RangeSet<AddressType>& RangeSet<AddressType>::operator=(RangeSet&& other) {
|
||||||
|
m_impl->m_ranges_set = std::move(other.m_impl->m_ranges_set);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename AddressType>
|
||||||
|
void RangeSet<AddressType>::Add(AddressType base_address, size_t size) {
|
||||||
|
m_impl->Add(base_address, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename AddressType>
|
||||||
|
void RangeSet<AddressType>::Subtract(AddressType base_address, size_t size) {
|
||||||
|
m_impl->Subtract(base_address, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename AddressType>
|
||||||
|
void RangeSet<AddressType>::Clear() {
|
||||||
|
m_impl->m_ranges_set.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename AddressType>
|
||||||
|
bool RangeSet<AddressType>::Empty() const {
|
||||||
|
return m_impl->m_ranges_set.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename AddressType>
|
||||||
|
template <typename Func>
|
||||||
|
void RangeSet<AddressType>::ForEach(Func&& func) const {
|
||||||
|
m_impl->ForEach(std::move(func));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename AddressType>
|
||||||
|
template <typename Func>
|
||||||
|
void RangeSet<AddressType>::ForEachInRange(AddressType base_address, size_t size,
|
||||||
|
Func&& func) const {
|
||||||
|
m_impl->ForEachInRange(base_address, size, std::move(func));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename AddressType>
|
||||||
|
OverlapRangeSet<AddressType>::OverlapRangeSet() {
|
||||||
|
m_impl = std::make_unique<OverlapRangeSet<AddressType>::OverlapRangeSetImpl>();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename AddressType>
|
||||||
|
OverlapRangeSet<AddressType>::~OverlapRangeSet() = default;
|
||||||
|
|
||||||
|
template <typename AddressType>
|
||||||
|
OverlapRangeSet<AddressType>::OverlapRangeSet(OverlapRangeSet&& other) {
|
||||||
|
m_impl = std::make_unique<OverlapRangeSet<AddressType>::OverlapRangeSetImpl>();
|
||||||
|
m_impl->m_split_ranges_set = std::move(other.m_impl->m_split_ranges_set);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename AddressType>
|
||||||
|
OverlapRangeSet<AddressType>& OverlapRangeSet<AddressType>::operator=(OverlapRangeSet&& other) {
|
||||||
|
m_impl->m_split_ranges_set = std::move(other.m_impl->m_split_ranges_set);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename AddressType>
|
||||||
|
void OverlapRangeSet<AddressType>::Add(AddressType base_address, size_t size) {
|
||||||
|
m_impl->Add(base_address, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename AddressType>
|
||||||
|
void OverlapRangeSet<AddressType>::Subtract(AddressType base_address, size_t size) {
|
||||||
|
m_impl->template Subtract<false>(base_address, size, 1, [](AddressType, AddressType) {});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename AddressType>
|
||||||
|
template <typename Func>
|
||||||
|
void OverlapRangeSet<AddressType>::Subtract(AddressType base_address, size_t size,
|
||||||
|
Func&& on_delete) {
|
||||||
|
m_impl->template Subtract<true, Func>(base_address, size, 1, std::move(on_delete));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename AddressType>
|
||||||
|
void OverlapRangeSet<AddressType>::DeleteAll(AddressType base_address, size_t size) {
|
||||||
|
m_impl->template Subtract<false>(base_address, size, std::numeric_limits<s32>::max(),
|
||||||
|
[](AddressType, AddressType) {});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename AddressType>
|
||||||
|
void OverlapRangeSet<AddressType>::Clear() {
|
||||||
|
m_impl->m_split_ranges_set.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename AddressType>
|
||||||
|
bool OverlapRangeSet<AddressType>::Empty() const {
|
||||||
|
return m_impl->m_split_ranges_set.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename AddressType>
|
||||||
|
template <typename Func>
|
||||||
|
void OverlapRangeSet<AddressType>::ForEach(Func&& func) const {
|
||||||
|
m_impl->ForEach(func);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename AddressType>
|
||||||
|
template <typename Func>
|
||||||
|
void OverlapRangeSet<AddressType>::ForEachInRange(AddressType base_address, size_t size,
|
||||||
|
Func&& func) const {
|
||||||
|
m_impl->ForEachInRange(base_address, size, std::move(func));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Common
|
227
src/common/slot_vector.h
Executable file
227
src/common/slot_vector.h
Executable file
|
@ -0,0 +1,227 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <bit>
|
||||||
|
#include <numeric>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "common/assert.h"
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "common/polyfill_ranges.h"
|
||||||
|
|
||||||
|
namespace Common {
|
||||||
|
|
||||||
|
struct SlotId {
|
||||||
|
static constexpr u32 INVALID_INDEX = std::numeric_limits<u32>::max();
|
||||||
|
|
||||||
|
constexpr auto operator<=>(const SlotId&) const noexcept = default;
|
||||||
|
|
||||||
|
constexpr explicit operator bool() const noexcept {
|
||||||
|
return index != INVALID_INDEX;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 index = INVALID_INDEX;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
requires std::is_nothrow_move_assignable_v<T> && std::is_nothrow_move_constructible_v<T>
|
||||||
|
class SlotVector {
|
||||||
|
public:
|
||||||
|
class Iterator {
|
||||||
|
friend SlotVector<T>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
constexpr Iterator() = default;
|
||||||
|
|
||||||
|
Iterator& operator++() noexcept {
|
||||||
|
const u64* const bitset = slot_vector->stored_bitset.data();
|
||||||
|
const u32 size = static_cast<u32>(slot_vector->stored_bitset.size()) * 64;
|
||||||
|
if (id.index < size) {
|
||||||
|
do {
|
||||||
|
++id.index;
|
||||||
|
} while (id.index < size && !IsValid(bitset));
|
||||||
|
if (id.index == size) {
|
||||||
|
id.index = SlotId::INVALID_INDEX;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Iterator operator++(int) noexcept {
|
||||||
|
const Iterator copy{*this};
|
||||||
|
++*this;
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const Iterator& other) const noexcept {
|
||||||
|
return id.index == other.id.index;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const Iterator& other) const noexcept {
|
||||||
|
return id.index != other.id.index;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<SlotId, T*> operator*() const noexcept {
|
||||||
|
return {id, std::addressof((*slot_vector)[id])};
|
||||||
|
}
|
||||||
|
|
||||||
|
T* operator->() const noexcept {
|
||||||
|
return std::addressof((*slot_vector)[id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Iterator(SlotVector<T>* slot_vector_, SlotId id_) noexcept
|
||||||
|
: slot_vector{slot_vector_}, id{id_} {}
|
||||||
|
|
||||||
|
bool IsValid(const u64* bitset) const noexcept {
|
||||||
|
return ((bitset[id.index / 64] >> (id.index % 64)) & 1) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SlotVector<T>* slot_vector;
|
||||||
|
SlotId id;
|
||||||
|
};
|
||||||
|
|
||||||
|
~SlotVector() noexcept {
|
||||||
|
size_t index = 0;
|
||||||
|
for (u64 bits : stored_bitset) {
|
||||||
|
for (size_t bit = 0; bits; ++bit, bits >>= 1) {
|
||||||
|
if ((bits & 1) != 0) {
|
||||||
|
values[index + bit].object.~T();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
index += 64;
|
||||||
|
}
|
||||||
|
delete[] values;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] T& operator[](SlotId id) noexcept {
|
||||||
|
ValidateIndex(id);
|
||||||
|
return values[id.index].object;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] const T& operator[](SlotId id) const noexcept {
|
||||||
|
ValidateIndex(id);
|
||||||
|
return values[id.index].object;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
[[nodiscard]] SlotId insert(Args&&... args) noexcept {
|
||||||
|
const u32 index = FreeValueIndex();
|
||||||
|
new (&values[index].object) T(std::forward<Args>(args)...);
|
||||||
|
SetStorageBit(index);
|
||||||
|
|
||||||
|
return SlotId{index};
|
||||||
|
}
|
||||||
|
|
||||||
|
void erase(SlotId id) noexcept {
|
||||||
|
values[id.index].object.~T();
|
||||||
|
free_list.push_back(id.index);
|
||||||
|
ResetStorageBit(id.index);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] Iterator begin() noexcept {
|
||||||
|
const auto it = std::ranges::find_if(stored_bitset, [](u64 value) { return value != 0; });
|
||||||
|
if (it == stored_bitset.end()) {
|
||||||
|
return end();
|
||||||
|
}
|
||||||
|
const u32 word_index = static_cast<u32>(std::distance(it, stored_bitset.begin()));
|
||||||
|
const SlotId first_id{word_index * 64 + static_cast<u32>(std::countr_zero(*it))};
|
||||||
|
return Iterator(this, first_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] Iterator end() noexcept {
|
||||||
|
return Iterator(this, SlotId{SlotId::INVALID_INDEX});
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] size_t size() const noexcept {
|
||||||
|
return values_capacity - free_list.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct NonTrivialDummy {
|
||||||
|
NonTrivialDummy() noexcept {}
|
||||||
|
};
|
||||||
|
|
||||||
|
union Entry {
|
||||||
|
Entry() noexcept : dummy{} {}
|
||||||
|
~Entry() noexcept {}
|
||||||
|
|
||||||
|
NonTrivialDummy dummy;
|
||||||
|
T object;
|
||||||
|
};
|
||||||
|
|
||||||
|
void SetStorageBit(u32 index) noexcept {
|
||||||
|
stored_bitset[index / 64] |= u64(1) << (index % 64);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResetStorageBit(u32 index) noexcept {
|
||||||
|
stored_bitset[index / 64] &= ~(u64(1) << (index % 64));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ReadStorageBit(u32 index) noexcept {
|
||||||
|
return ((stored_bitset[index / 64] >> (index % 64)) & 1) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ValidateIndex(SlotId id) const noexcept {
|
||||||
|
DEBUG_ASSERT(id);
|
||||||
|
DEBUG_ASSERT(id.index / 64 < stored_bitset.size());
|
||||||
|
DEBUG_ASSERT(((stored_bitset[id.index / 64] >> (id.index % 64)) & 1) != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] u32 FreeValueIndex() noexcept {
|
||||||
|
if (free_list.empty()) {
|
||||||
|
Reserve(values_capacity ? (values_capacity << 1) : 1);
|
||||||
|
}
|
||||||
|
const u32 free_index = free_list.back();
|
||||||
|
free_list.pop_back();
|
||||||
|
return free_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Reserve(size_t new_capacity) noexcept {
|
||||||
|
Entry* const new_values = new Entry[new_capacity];
|
||||||
|
size_t index = 0;
|
||||||
|
for (u64 bits : stored_bitset) {
|
||||||
|
for (size_t bit = 0; bits; ++bit, bits >>= 1) {
|
||||||
|
const size_t i = index + bit;
|
||||||
|
if ((bits & 1) == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
T& old_value = values[i].object;
|
||||||
|
new (&new_values[i].object) T(std::move(old_value));
|
||||||
|
old_value.~T();
|
||||||
|
}
|
||||||
|
index += 64;
|
||||||
|
}
|
||||||
|
|
||||||
|
stored_bitset.resize((new_capacity + 63) / 64);
|
||||||
|
|
||||||
|
const size_t old_free_size = free_list.size();
|
||||||
|
free_list.resize(old_free_size + (new_capacity - values_capacity));
|
||||||
|
std::iota(free_list.begin() + old_free_size, free_list.end(),
|
||||||
|
static_cast<u32>(values_capacity));
|
||||||
|
|
||||||
|
delete[] values;
|
||||||
|
values = new_values;
|
||||||
|
values_capacity = new_capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entry* values = nullptr;
|
||||||
|
size_t values_capacity = 0;
|
||||||
|
|
||||||
|
std::vector<u64> stored_bitset;
|
||||||
|
std::vector<u32> free_list;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Common
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct std::hash<Common::SlotId> {
|
||||||
|
size_t operator()(const Common::SlotId& id) const noexcept {
|
||||||
|
return std::hash<u32>{}(id.index);
|
||||||
|
}
|
||||||
|
};
|
|
@ -3,110 +3,21 @@
|
||||||
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
#include <boost/container/small_vector.hpp>
|
#include "common/range_sets.h"
|
||||||
#define BOOST_NO_MT
|
#include "common/range_sets.inc"
|
||||||
#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 "core/hle/service/nvdrv/core/heap_mapper.h"
|
||||||
#include "video_core/host1x/host1x.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 {
|
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 {
|
struct HeapMapper::HeapMapperInternal {
|
||||||
HeapMapperInternal(Tegra::Host1x::Host1x& host1x) : device_memory{host1x.MemoryManager()} {}
|
HeapMapperInternal(Tegra::Host1x::Host1x& host1x) : m_device_memory{host1x.MemoryManager()} {}
|
||||||
~HeapMapperInternal() = default;
|
~HeapMapperInternal() = default;
|
||||||
|
|
||||||
template <typename Func>
|
Common::RangeSet<VAddr> m_temporary_set;
|
||||||
void ForEachInOverlapCounter(OverlapCounter& current_range, VAddr cpu_addr, u64 size,
|
Common::OverlapRangeSet<VAddr> m_mapped_ranges;
|
||||||
Func&& func) {
|
Tegra::MaxwellDeviceMemoryManager& m_device_memory;
|
||||||
const DAddr start_address = cpu_addr;
|
std::mutex m_guard;
|
||||||
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,
|
HeapMapper::HeapMapper(VAddr start_vaddress, DAddr start_daddress, size_t size, Core::Asid asid,
|
||||||
|
@ -116,60 +27,48 @@ HeapMapper::HeapMapper(VAddr start_vaddress, DAddr start_daddress, size_t size,
|
||||||
}
|
}
|
||||||
|
|
||||||
HeapMapper::~HeapMapper() {
|
HeapMapper::~HeapMapper() {
|
||||||
m_internal->device_memory.Unmap(m_daddress, m_size);
|
// Unmap whatever has been mapped.
|
||||||
|
m_internal->m_mapped_ranges.ForEach([this](VAddr start_addr, VAddr end_addr, s32 count) {
|
||||||
|
const size_t sub_size = end_addr - start_addr;
|
||||||
|
const size_t offset = start_addr - m_vaddress;
|
||||||
|
m_internal->m_device_memory.Unmap(m_daddress + offset, sub_size);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
DAddr HeapMapper::Map(VAddr start, size_t size) {
|
DAddr HeapMapper::Map(VAddr start, size_t size) {
|
||||||
std::scoped_lock lk(m_internal->guard);
|
std::scoped_lock lk(m_internal->m_guard);
|
||||||
m_internal->base_set.clear();
|
// Add the mapping range to a temporary range set.
|
||||||
const IntervalType interval{start, start + size};
|
m_internal->m_temporary_set.Clear();
|
||||||
m_internal->base_set.insert(interval);
|
m_internal->m_temporary_set.Add(start, size);
|
||||||
m_internal->ForEachInOverlapCounter(m_internal->mapping_overlaps, start, size,
|
|
||||||
[this](VAddr start_addr, VAddr end_addr, int) {
|
// Remove anything that's already mapped from the temporary range set.
|
||||||
const IntervalType other{start_addr, end_addr};
|
m_internal->m_mapped_ranges.ForEachInRange(
|
||||||
m_internal->base_set.subtract(other);
|
start, size, [this](VAddr start_addr, VAddr end_addr, s32) {
|
||||||
});
|
m_internal->m_temporary_set.Subtract(start_addr, end_addr - start_addr);
|
||||||
if (!m_internal->base_set.empty()) {
|
});
|
||||||
auto it = m_internal->base_set.begin();
|
|
||||||
auto end_it = m_internal->base_set.end();
|
// Map anything that has not been mapped yet.
|
||||||
for (; it != end_it; it++) {
|
m_internal->m_temporary_set.ForEach([this](VAddr start_addr, VAddr end_addr) {
|
||||||
const VAddr inter_addr_end = it->upper();
|
const size_t sub_size = end_addr - start_addr;
|
||||||
const VAddr inter_addr = it->lower();
|
const size_t offset = start_addr - m_vaddress;
|
||||||
const size_t offset = inter_addr - m_vaddress;
|
m_internal->m_device_memory.Map(m_daddress + offset, m_vaddress + offset, sub_size, m_asid);
|
||||||
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);
|
// Add the mapping range to the split map, to register the map and overlaps.
|
||||||
}
|
m_internal->m_mapped_ranges.Add(start, size);
|
||||||
}
|
m_internal->m_temporary_set.Clear();
|
||||||
m_internal->mapping_overlaps += std::make_pair(interval, 1);
|
return m_daddress + static_cast<DAddr>(start - m_vaddress);
|
||||||
m_internal->base_set.clear();
|
|
||||||
return m_daddress + (start - m_vaddress);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void HeapMapper::Unmap(VAddr start, size_t size) {
|
void HeapMapper::Unmap(VAddr start, size_t size) {
|
||||||
std::scoped_lock lk(m_internal->guard);
|
std::scoped_lock lk(m_internal->m_guard);
|
||||||
m_internal->base_set.clear();
|
|
||||||
m_internal->ForEachInOverlapCounter(m_internal->mapping_overlaps, start, size,
|
// Just subtract the range and whatever is deleted, unmap it.
|
||||||
[this](VAddr start_addr, VAddr end_addr, int value) {
|
m_internal->m_mapped_ranges.Subtract(start, size, [this](VAddr start_addr, VAddr end_addr) {
|
||||||
if (value <= 1) {
|
const size_t sub_size = end_addr - start_addr;
|
||||||
const IntervalType other{start_addr, end_addr};
|
const size_t offset = start_addr - m_vaddress;
|
||||||
m_internal->base_set.insert(other);
|
m_internal->m_device_memory.Unmap(m_daddress + offset, sub_size);
|
||||||
}
|
});
|
||||||
});
|
|
||||||
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
|
} // namespace Service::Nvidia::NvCore
|
||||||
|
|
|
@ -274,7 +274,6 @@ add_library(video_core STATIC
|
||||||
texture_cache/image_view_info.h
|
texture_cache/image_view_info.h
|
||||||
texture_cache/render_targets.h
|
texture_cache/render_targets.h
|
||||||
texture_cache/samples_helper.h
|
texture_cache/samples_helper.h
|
||||||
texture_cache/slot_vector.h
|
|
||||||
texture_cache/texture_cache.cpp
|
texture_cache/texture_cache.cpp
|
||||||
texture_cache/texture_cache.h
|
texture_cache/texture_cache.h
|
||||||
texture_cache/texture_cache_base.h
|
texture_cache/texture_cache_base.h
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
|
|
||||||
|
#include "common/range_sets.inc"
|
||||||
#include "video_core/buffer_cache/buffer_cache_base.h"
|
#include "video_core/buffer_cache/buffer_cache_base.h"
|
||||||
#include "video_core/guest_memory.h"
|
#include "video_core/guest_memory.h"
|
||||||
#include "video_core/host1x/gpu_device_memory_manager.h"
|
#include "video_core/host1x/gpu_device_memory_manager.h"
|
||||||
|
@ -20,7 +21,7 @@ BufferCache<P>::BufferCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, R
|
||||||
: runtime{runtime_}, device_memory{device_memory_}, memory_tracker{device_memory} {
|
: runtime{runtime_}, device_memory{device_memory_}, memory_tracker{device_memory} {
|
||||||
// Ensure the first slot is used for the null buffer
|
// Ensure the first slot is used for the null buffer
|
||||||
void(slot_buffers.insert(runtime, NullBufferParams{}));
|
void(slot_buffers.insert(runtime, NullBufferParams{}));
|
||||||
common_ranges.clear();
|
gpu_modified_ranges.Clear();
|
||||||
inline_buffer_id = NULL_BUFFER_ID;
|
inline_buffer_id = NULL_BUFFER_ID;
|
||||||
|
|
||||||
if (!runtime.CanReportMemoryUsage()) {
|
if (!runtime.CanReportMemoryUsage()) {
|
||||||
|
@ -43,6 +44,9 @@ BufferCache<P>::BufferCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, R
|
||||||
DEFAULT_CRITICAL_MEMORY));
|
DEFAULT_CRITICAL_MEMORY));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <class P>
|
||||||
|
BufferCache<P>::~BufferCache() = default;
|
||||||
|
|
||||||
template <class P>
|
template <class P>
|
||||||
void BufferCache<P>::RunGarbageCollector() {
|
void BufferCache<P>::RunGarbageCollector() {
|
||||||
const bool aggressive_gc = total_used_memory >= critical_memory;
|
const bool aggressive_gc = total_used_memory >= critical_memory;
|
||||||
|
@ -96,20 +100,17 @@ void BufferCache<P>::TickFrame() {
|
||||||
++frame_tick;
|
++frame_tick;
|
||||||
delayed_destruction_ring.Tick();
|
delayed_destruction_ring.Tick();
|
||||||
|
|
||||||
if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) {
|
for (auto& buffer : async_buffers_death_ring) {
|
||||||
for (auto& buffer : async_buffers_death_ring) {
|
runtime.FreeDeferredStagingBuffer(buffer);
|
||||||
runtime.FreeDeferredStagingBuffer(buffer);
|
|
||||||
}
|
|
||||||
async_buffers_death_ring.clear();
|
|
||||||
}
|
}
|
||||||
|
async_buffers_death_ring.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class P>
|
template <class P>
|
||||||
void BufferCache<P>::WriteMemory(DAddr device_addr, u64 size) {
|
void BufferCache<P>::WriteMemory(DAddr device_addr, u64 size) {
|
||||||
if (memory_tracker.IsRegionGpuModified(device_addr, size)) {
|
if (memory_tracker.IsRegionGpuModified(device_addr, size)) {
|
||||||
const IntervalType subtract_interval{device_addr, device_addr + size};
|
ClearDownload(device_addr, size);
|
||||||
ClearDownload(subtract_interval);
|
gpu_modified_ranges.Subtract(device_addr, size);
|
||||||
common_ranges.subtract(subtract_interval);
|
|
||||||
}
|
}
|
||||||
memory_tracker.MarkRegionAsCpuModified(device_addr, size);
|
memory_tracker.MarkRegionAsCpuModified(device_addr, size);
|
||||||
}
|
}
|
||||||
|
@ -174,11 +175,11 @@ void BufferCache<P>::DownloadMemory(DAddr device_addr, u64 size) {
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class P>
|
template <class P>
|
||||||
void BufferCache<P>::ClearDownload(IntervalType subtract_interval) {
|
void BufferCache<P>::ClearDownload(DAddr device_addr, u64 size) {
|
||||||
RemoveEachInOverlapCounter(async_downloads, subtract_interval, -1024);
|
async_downloads.DeleteAll(device_addr, size);
|
||||||
uncommitted_ranges.subtract(subtract_interval);
|
uncommitted_gpu_modified_ranges.Subtract(device_addr, size);
|
||||||
for (auto& interval_set : committed_ranges) {
|
for (auto& interval_set : committed_gpu_modified_ranges) {
|
||||||
interval_set.subtract(subtract_interval);
|
interval_set.Subtract(device_addr, size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -195,8 +196,7 @@ bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const IntervalType subtract_interval{*cpu_dest_address, *cpu_dest_address + amount};
|
ClearDownload(*cpu_dest_address, amount);
|
||||||
ClearDownload(subtract_interval);
|
|
||||||
|
|
||||||
BufferId buffer_a;
|
BufferId buffer_a;
|
||||||
BufferId buffer_b;
|
BufferId buffer_b;
|
||||||
|
@ -215,21 +215,20 @@ bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am
|
||||||
.size = amount,
|
.size = amount,
|
||||||
}};
|
}};
|
||||||
|
|
||||||
boost::container::small_vector<IntervalType, 4> tmp_intervals;
|
boost::container::small_vector<std::pair<DAddr, size_t>, 4> tmp_intervals;
|
||||||
auto mirror = [&](DAddr base_address, DAddr base_address_end) {
|
auto mirror = [&](DAddr base_address, DAddr base_address_end) {
|
||||||
const u64 size = base_address_end - base_address;
|
const u64 size = base_address_end - base_address;
|
||||||
const DAddr diff = base_address - *cpu_src_address;
|
const DAddr diff = base_address - *cpu_src_address;
|
||||||
const DAddr new_base_address = *cpu_dest_address + diff;
|
const DAddr new_base_address = *cpu_dest_address + diff;
|
||||||
const IntervalType add_interval{new_base_address, new_base_address + size};
|
tmp_intervals.push_back({new_base_address, size});
|
||||||
tmp_intervals.push_back(add_interval);
|
uncommitted_gpu_modified_ranges.Add(new_base_address, size);
|
||||||
uncommitted_ranges.add(add_interval);
|
|
||||||
};
|
};
|
||||||
ForEachInRangeSet(common_ranges, *cpu_src_address, amount, mirror);
|
gpu_modified_ranges.ForEachInRange(*cpu_src_address, amount, mirror);
|
||||||
// This subtraction in this order is important for overlapping copies.
|
// This subtraction in this order is important for overlapping copies.
|
||||||
common_ranges.subtract(subtract_interval);
|
gpu_modified_ranges.Subtract(*cpu_dest_address, amount);
|
||||||
const bool has_new_downloads = tmp_intervals.size() != 0;
|
const bool has_new_downloads = tmp_intervals.size() != 0;
|
||||||
for (const IntervalType& add_interval : tmp_intervals) {
|
for (const auto& pair : tmp_intervals) {
|
||||||
common_ranges.add(add_interval);
|
gpu_modified_ranges.Add(pair.first, pair.second);
|
||||||
}
|
}
|
||||||
const auto& copy = copies[0];
|
const auto& copy = copies[0];
|
||||||
src_buffer.MarkUsage(copy.src_offset, copy.size);
|
src_buffer.MarkUsage(copy.src_offset, copy.size);
|
||||||
|
@ -257,9 +256,8 @@ bool BufferCache<P>::DMAClear(GPUVAddr dst_address, u64 amount, u32 value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const size_t size = amount * sizeof(u32);
|
const size_t size = amount * sizeof(u32);
|
||||||
const IntervalType subtract_interval{*cpu_dst_address, *cpu_dst_address + size};
|
ClearDownload(*cpu_dst_address, size);
|
||||||
ClearDownload(subtract_interval);
|
gpu_modified_ranges.Subtract(*cpu_dst_address, size);
|
||||||
common_ranges.subtract(subtract_interval);
|
|
||||||
|
|
||||||
const BufferId buffer = FindBuffer(*cpu_dst_address, static_cast<u32>(size));
|
const BufferId buffer = FindBuffer(*cpu_dst_address, static_cast<u32>(size));
|
||||||
Buffer& dest_buffer = slot_buffers[buffer];
|
Buffer& dest_buffer = slot_buffers[buffer];
|
||||||
|
@ -300,11 +298,11 @@ std::pair<typename P::Buffer*, u32> BufferCache<P>::ObtainCPUBuffer(
|
||||||
MarkWrittenBuffer(buffer_id, device_addr, size);
|
MarkWrittenBuffer(buffer_id, device_addr, size);
|
||||||
break;
|
break;
|
||||||
case ObtainBufferOperation::DiscardWrite: {
|
case ObtainBufferOperation::DiscardWrite: {
|
||||||
DAddr device_addr_start = Common::AlignDown(device_addr, 64);
|
const DAddr device_addr_start = Common::AlignDown(device_addr, 64);
|
||||||
DAddr device_addr_end = Common::AlignUp(device_addr + size, 64);
|
const DAddr device_addr_end = Common::AlignUp(device_addr + size, 64);
|
||||||
IntervalType interval{device_addr_start, device_addr_end};
|
const size_t new_size = device_addr_end - device_addr_start;
|
||||||
ClearDownload(interval);
|
ClearDownload(device_addr_start, new_size);
|
||||||
common_ranges.subtract(interval);
|
gpu_modified_ranges.Subtract(device_addr_start, new_size);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
@ -504,46 +502,40 @@ void BufferCache<P>::FlushCachedWrites() {
|
||||||
|
|
||||||
template <class P>
|
template <class P>
|
||||||
bool BufferCache<P>::HasUncommittedFlushes() const noexcept {
|
bool BufferCache<P>::HasUncommittedFlushes() const noexcept {
|
||||||
return !uncommitted_ranges.empty() || !committed_ranges.empty();
|
return !uncommitted_gpu_modified_ranges.Empty() || !committed_gpu_modified_ranges.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class P>
|
template <class P>
|
||||||
void BufferCache<P>::AccumulateFlushes() {
|
void BufferCache<P>::AccumulateFlushes() {
|
||||||
if (uncommitted_ranges.empty()) {
|
if (uncommitted_gpu_modified_ranges.Empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
committed_ranges.emplace_back(std::move(uncommitted_ranges));
|
committed_gpu_modified_ranges.emplace_back(std::move(uncommitted_gpu_modified_ranges));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class P>
|
template <class P>
|
||||||
bool BufferCache<P>::ShouldWaitAsyncFlushes() const noexcept {
|
bool BufferCache<P>::ShouldWaitAsyncFlushes() const noexcept {
|
||||||
if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) {
|
return (!async_buffers.empty() && async_buffers.front().has_value());
|
||||||
return (!async_buffers.empty() && async_buffers.front().has_value());
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class P>
|
template <class P>
|
||||||
void BufferCache<P>::CommitAsyncFlushesHigh() {
|
void BufferCache<P>::CommitAsyncFlushesHigh() {
|
||||||
AccumulateFlushes();
|
AccumulateFlushes();
|
||||||
|
|
||||||
if (committed_ranges.empty()) {
|
if (committed_gpu_modified_ranges.empty()) {
|
||||||
if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) {
|
async_buffers.emplace_back(std::optional<Async_Buffer>{});
|
||||||
async_buffers.emplace_back(std::optional<Async_Buffer>{});
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
MICROPROFILE_SCOPE(GPU_DownloadMemory);
|
MICROPROFILE_SCOPE(GPU_DownloadMemory);
|
||||||
|
|
||||||
auto it = committed_ranges.begin();
|
auto it = committed_gpu_modified_ranges.begin();
|
||||||
while (it != committed_ranges.end()) {
|
while (it != committed_gpu_modified_ranges.end()) {
|
||||||
auto& current_intervals = *it;
|
auto& current_intervals = *it;
|
||||||
auto next_it = std::next(it);
|
auto next_it = std::next(it);
|
||||||
while (next_it != committed_ranges.end()) {
|
while (next_it != committed_gpu_modified_ranges.end()) {
|
||||||
for (auto& interval : *next_it) {
|
next_it->ForEach([¤t_intervals](DAddr start, DAddr end) {
|
||||||
current_intervals.subtract(interval);
|
current_intervals.Subtract(start, end - start);
|
||||||
}
|
});
|
||||||
next_it++;
|
next_it++;
|
||||||
}
|
}
|
||||||
it++;
|
it++;
|
||||||
|
@ -552,10 +544,10 @@ void BufferCache<P>::CommitAsyncFlushesHigh() {
|
||||||
boost::container::small_vector<std::pair<BufferCopy, BufferId>, 16> downloads;
|
boost::container::small_vector<std::pair<BufferCopy, BufferId>, 16> downloads;
|
||||||
u64 total_size_bytes = 0;
|
u64 total_size_bytes = 0;
|
||||||
u64 largest_copy = 0;
|
u64 largest_copy = 0;
|
||||||
for (const IntervalSet& intervals : committed_ranges) {
|
for (const Common::RangeSet<DAddr>& range_set : committed_gpu_modified_ranges) {
|
||||||
for (auto& interval : intervals) {
|
range_set.ForEach([&](DAddr interval_lower, DAddr interval_upper) {
|
||||||
const std::size_t size = interval.upper() - interval.lower();
|
const std::size_t size = interval_upper - interval_lower;
|
||||||
const DAddr device_addr = interval.lower();
|
const DAddr device_addr = interval_lower;
|
||||||
ForEachBufferInRange(device_addr, size, [&](BufferId buffer_id, Buffer& buffer) {
|
ForEachBufferInRange(device_addr, size, [&](BufferId buffer_id, Buffer& buffer) {
|
||||||
const DAddr buffer_start = buffer.CpuAddr();
|
const DAddr buffer_start = buffer.CpuAddr();
|
||||||
const DAddr buffer_end = buffer_start + buffer.SizeBytes();
|
const DAddr buffer_end = buffer_start + buffer.SizeBytes();
|
||||||
|
@ -583,77 +575,35 @@ void BufferCache<P>::CommitAsyncFlushesHigh() {
|
||||||
largest_copy = std::max(largest_copy, new_size);
|
largest_copy = std::max(largest_copy, new_size);
|
||||||
};
|
};
|
||||||
|
|
||||||
ForEachInRangeSet(common_ranges, device_addr_out, range_size, add_download);
|
gpu_modified_ranges.ForEachInRange(device_addr_out, range_size,
|
||||||
|
add_download);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
committed_ranges.clear();
|
committed_gpu_modified_ranges.clear();
|
||||||
if (downloads.empty()) {
|
if (downloads.empty()) {
|
||||||
if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) {
|
async_buffers.emplace_back(std::optional<Async_Buffer>{});
|
||||||
async_buffers.emplace_back(std::optional<Async_Buffer>{});
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) {
|
auto download_staging = runtime.DownloadStagingBuffer(total_size_bytes, true);
|
||||||
auto download_staging = runtime.DownloadStagingBuffer(total_size_bytes, true);
|
boost::container::small_vector<BufferCopy, 4> normalized_copies;
|
||||||
boost::container::small_vector<BufferCopy, 4> normalized_copies;
|
runtime.PreCopyBarrier();
|
||||||
IntervalSet new_async_range{};
|
for (auto& [copy, buffer_id] : downloads) {
|
||||||
runtime.PreCopyBarrier();
|
copy.dst_offset += download_staging.offset;
|
||||||
for (auto& [copy, buffer_id] : downloads) {
|
const std::array copies{copy};
|
||||||
copy.dst_offset += download_staging.offset;
|
BufferCopy second_copy{copy};
|
||||||
const std::array copies{copy};
|
Buffer& buffer = slot_buffers[buffer_id];
|
||||||
BufferCopy second_copy{copy};
|
second_copy.src_offset = static_cast<size_t>(buffer.CpuAddr()) + copy.src_offset;
|
||||||
Buffer& buffer = slot_buffers[buffer_id];
|
const DAddr orig_device_addr = static_cast<DAddr>(second_copy.src_offset);
|
||||||
second_copy.src_offset = static_cast<size_t>(buffer.CpuAddr()) + copy.src_offset;
|
async_downloads.Add(orig_device_addr, copy.size);
|
||||||
DAddr orig_device_addr = static_cast<DAddr>(second_copy.src_offset);
|
buffer.MarkUsage(copy.src_offset, copy.size);
|
||||||
const IntervalType base_interval{orig_device_addr, orig_device_addr + copy.size};
|
runtime.CopyBuffer(download_staging.buffer, buffer, copies, false);
|
||||||
async_downloads += std::make_pair(base_interval, 1);
|
normalized_copies.push_back(second_copy);
|
||||||
buffer.MarkUsage(copy.src_offset, copy.size);
|
|
||||||
runtime.CopyBuffer(download_staging.buffer, buffer, copies, false);
|
|
||||||
normalized_copies.push_back(second_copy);
|
|
||||||
}
|
|
||||||
runtime.PostCopyBarrier();
|
|
||||||
pending_downloads.emplace_back(std::move(normalized_copies));
|
|
||||||
async_buffers.emplace_back(download_staging);
|
|
||||||
} else {
|
|
||||||
if (!Settings::IsGPULevelHigh()) {
|
|
||||||
committed_ranges.clear();
|
|
||||||
uncommitted_ranges.clear();
|
|
||||||
} else {
|
|
||||||
if constexpr (USE_MEMORY_MAPS) {
|
|
||||||
auto download_staging = runtime.DownloadStagingBuffer(total_size_bytes);
|
|
||||||
runtime.PreCopyBarrier();
|
|
||||||
for (auto& [copy, buffer_id] : downloads) {
|
|
||||||
// Have in mind the staging buffer offset for the copy
|
|
||||||
copy.dst_offset += download_staging.offset;
|
|
||||||
const std::array copies{copy};
|
|
||||||
Buffer& buffer = slot_buffers[buffer_id];
|
|
||||||
buffer.MarkUsage(copy.src_offset, copy.size);
|
|
||||||
runtime.CopyBuffer(download_staging.buffer, buffer, copies, false);
|
|
||||||
}
|
|
||||||
runtime.PostCopyBarrier();
|
|
||||||
runtime.Finish();
|
|
||||||
for (const auto& [copy, buffer_id] : downloads) {
|
|
||||||
const Buffer& buffer = slot_buffers[buffer_id];
|
|
||||||
const DAddr device_addr = buffer.CpuAddr() + copy.src_offset;
|
|
||||||
// Undo the modified offset
|
|
||||||
const u64 dst_offset = copy.dst_offset - download_staging.offset;
|
|
||||||
const u8* read_mapped_memory = download_staging.mapped_span.data() + dst_offset;
|
|
||||||
device_memory.WriteBlockUnsafe(device_addr, read_mapped_memory, copy.size);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const std::span<u8> immediate_buffer = ImmediateBuffer(largest_copy);
|
|
||||||
for (const auto& [copy, buffer_id] : downloads) {
|
|
||||||
Buffer& buffer = slot_buffers[buffer_id];
|
|
||||||
buffer.ImmediateDownload(copy.src_offset,
|
|
||||||
immediate_buffer.subspan(0, copy.size));
|
|
||||||
const DAddr device_addr = buffer.CpuAddr() + copy.src_offset;
|
|
||||||
device_memory.WriteBlockUnsafe(device_addr, immediate_buffer.data(), copy.size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
runtime.PostCopyBarrier();
|
||||||
|
pending_downloads.emplace_back(std::move(normalized_copies));
|
||||||
|
async_buffers.emplace_back(download_staging);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class P>
|
template <class P>
|
||||||
|
@ -676,37 +626,31 @@ void BufferCache<P>::PopAsyncBuffers() {
|
||||||
async_buffers.pop_front();
|
async_buffers.pop_front();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) {
|
auto& downloads = pending_downloads.front();
|
||||||
auto& downloads = pending_downloads.front();
|
auto& async_buffer = async_buffers.front();
|
||||||
auto& async_buffer = async_buffers.front();
|
u8* base = async_buffer->mapped_span.data();
|
||||||
u8* base = async_buffer->mapped_span.data();
|
const size_t base_offset = async_buffer->offset;
|
||||||
const size_t base_offset = async_buffer->offset;
|
for (const auto& copy : downloads) {
|
||||||
for (const auto& copy : downloads) {
|
const DAddr device_addr = static_cast<DAddr>(copy.src_offset);
|
||||||
const DAddr device_addr = static_cast<DAddr>(copy.src_offset);
|
const u64 dst_offset = copy.dst_offset - base_offset;
|
||||||
const u64 dst_offset = copy.dst_offset - base_offset;
|
const u8* read_mapped_memory = base + dst_offset;
|
||||||
const u8* read_mapped_memory = base + dst_offset;
|
async_downloads.ForEachInRange(device_addr, copy.size, [&](DAddr start, DAddr end, s32) {
|
||||||
ForEachInOverlapCounter(
|
device_memory.WriteBlockUnsafe(start, &read_mapped_memory[start - device_addr],
|
||||||
async_downloads, device_addr, copy.size, [&](DAddr start, DAddr end, int count) {
|
end - start);
|
||||||
device_memory.WriteBlockUnsafe(start, &read_mapped_memory[start - device_addr],
|
});
|
||||||
end - start);
|
async_downloads.Subtract(device_addr, copy.size, [&](DAddr start, DAddr end) {
|
||||||
if (count == 1) {
|
gpu_modified_ranges.Subtract(start, end - start);
|
||||||
const IntervalType base_interval{start, end};
|
});
|
||||||
common_ranges.subtract(base_interval);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const IntervalType subtract_interval{device_addr, device_addr + copy.size};
|
|
||||||
RemoveEachInOverlapCounter(async_downloads, subtract_interval, -1);
|
|
||||||
}
|
|
||||||
async_buffers_death_ring.emplace_back(*async_buffer);
|
|
||||||
async_buffers.pop_front();
|
|
||||||
pending_downloads.pop_front();
|
|
||||||
}
|
}
|
||||||
|
async_buffers_death_ring.emplace_back(*async_buffer);
|
||||||
|
async_buffers.pop_front();
|
||||||
|
pending_downloads.pop_front();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class P>
|
template <class P>
|
||||||
bool BufferCache<P>::IsRegionGpuModified(DAddr addr, size_t size) {
|
bool BufferCache<P>::IsRegionGpuModified(DAddr addr, size_t size) {
|
||||||
bool is_dirty = false;
|
bool is_dirty = false;
|
||||||
ForEachInRangeSet(common_ranges, addr, size, [&](DAddr, DAddr) { is_dirty = true; });
|
gpu_modified_ranges.ForEachInRange(addr, size, [&](DAddr, DAddr) { is_dirty = true; });
|
||||||
return is_dirty;
|
return is_dirty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1320,10 +1264,8 @@ void BufferCache<P>::UpdateComputeTextureBuffers() {
|
||||||
template <class P>
|
template <class P>
|
||||||
void BufferCache<P>::MarkWrittenBuffer(BufferId buffer_id, DAddr device_addr, u32 size) {
|
void BufferCache<P>::MarkWrittenBuffer(BufferId buffer_id, DAddr device_addr, u32 size) {
|
||||||
memory_tracker.MarkRegionAsGpuModified(device_addr, size);
|
memory_tracker.MarkRegionAsGpuModified(device_addr, size);
|
||||||
|
gpu_modified_ranges.Add(device_addr, size);
|
||||||
const IntervalType base_interval{device_addr, device_addr + size};
|
uncommitted_gpu_modified_ranges.Add(device_addr, size);
|
||||||
common_ranges.add(base_interval);
|
|
||||||
uncommitted_ranges.add(base_interval);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class P>
|
template <class P>
|
||||||
|
@ -1603,9 +1545,8 @@ bool BufferCache<P>::InlineMemory(DAddr dest_address, size_t copy_size,
|
||||||
template <class P>
|
template <class P>
|
||||||
void BufferCache<P>::InlineMemoryImplementation(DAddr dest_address, size_t copy_size,
|
void BufferCache<P>::InlineMemoryImplementation(DAddr dest_address, size_t copy_size,
|
||||||
std::span<const u8> inlined_buffer) {
|
std::span<const u8> inlined_buffer) {
|
||||||
const IntervalType subtract_interval{dest_address, dest_address + copy_size};
|
ClearDownload(dest_address, copy_size);
|
||||||
ClearDownload(subtract_interval);
|
gpu_modified_ranges.Subtract(dest_address, copy_size);
|
||||||
common_ranges.subtract(subtract_interval);
|
|
||||||
|
|
||||||
BufferId buffer_id = FindBuffer(dest_address, static_cast<u32>(copy_size));
|
BufferId buffer_id = FindBuffer(dest_address, static_cast<u32>(copy_size));
|
||||||
auto& buffer = slot_buffers[buffer_id];
|
auto& buffer = slot_buffers[buffer_id];
|
||||||
|
@ -1655,12 +1596,9 @@ void BufferCache<P>::DownloadBufferMemory(Buffer& buffer, DAddr device_addr, u64
|
||||||
largest_copy = std::max(largest_copy, new_size);
|
largest_copy = std::max(largest_copy, new_size);
|
||||||
};
|
};
|
||||||
|
|
||||||
const DAddr start_address = device_addr_out;
|
gpu_modified_ranges.ForEachInRange(device_addr_out, range_size, add_download);
|
||||||
const DAddr end_address = start_address + range_size;
|
ClearDownload(device_addr_out, range_size);
|
||||||
ForEachInRangeSet(common_ranges, start_address, range_size, add_download);
|
gpu_modified_ranges.Subtract(device_addr_out, range_size);
|
||||||
const IntervalType subtract_interval{start_address, end_address};
|
|
||||||
ClearDownload(subtract_interval);
|
|
||||||
common_ranges.subtract(subtract_interval);
|
|
||||||
});
|
});
|
||||||
if (total_size_bytes == 0) {
|
if (total_size_bytes == 0) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -13,25 +13,15 @@
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#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 "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/div_ceil.h"
|
#include "common/div_ceil.h"
|
||||||
#include "common/literals.h"
|
#include "common/literals.h"
|
||||||
#include "common/lru_cache.h"
|
#include "common/lru_cache.h"
|
||||||
#include "common/microprofile.h"
|
#include "common/microprofile.h"
|
||||||
|
#include "common/range_sets.h"
|
||||||
#include "common/scope_exit.h"
|
#include "common/scope_exit.h"
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
|
#include "common/slot_vector.h"
|
||||||
#include "video_core/buffer_cache/buffer_base.h"
|
#include "video_core/buffer_cache/buffer_base.h"
|
||||||
#include "video_core/control/channel_state_cache.h"
|
#include "video_core/control/channel_state_cache.h"
|
||||||
#include "video_core/delayed_destruction_ring.h"
|
#include "video_core/delayed_destruction_ring.h"
|
||||||
|
@ -41,21 +31,15 @@
|
||||||
#include "video_core/engines/maxwell_3d.h"
|
#include "video_core/engines/maxwell_3d.h"
|
||||||
#include "video_core/memory_manager.h"
|
#include "video_core/memory_manager.h"
|
||||||
#include "video_core/surface.h"
|
#include "video_core/surface.h"
|
||||||
#include "video_core/texture_cache/slot_vector.h"
|
|
||||||
#include "video_core/texture_cache/types.h"
|
#include "video_core/texture_cache/types.h"
|
||||||
|
|
||||||
namespace boost {
|
|
||||||
template <typename T>
|
|
||||||
class fast_pool_allocator<T, default_user_allocator_new_delete, details::pool::null_mutex, 4096, 0>;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace VideoCommon {
|
namespace VideoCommon {
|
||||||
|
|
||||||
MICROPROFILE_DECLARE(GPU_PrepareBuffers);
|
MICROPROFILE_DECLARE(GPU_PrepareBuffers);
|
||||||
MICROPROFILE_DECLARE(GPU_BindUploadBuffers);
|
MICROPROFILE_DECLARE(GPU_BindUploadBuffers);
|
||||||
MICROPROFILE_DECLARE(GPU_DownloadMemory);
|
MICROPROFILE_DECLARE(GPU_DownloadMemory);
|
||||||
|
|
||||||
using BufferId = SlotId;
|
using BufferId = Common::SlotId;
|
||||||
|
|
||||||
using VideoCore::Surface::PixelFormat;
|
using VideoCore::Surface::PixelFormat;
|
||||||
using namespace Common::Literals;
|
using namespace Common::Literals;
|
||||||
|
@ -184,7 +168,6 @@ class BufferCache : public VideoCommon::ChannelSetupCaches<BufferCacheChannelInf
|
||||||
static constexpr bool NEEDS_BIND_STORAGE_INDEX = P::NEEDS_BIND_STORAGE_INDEX;
|
static constexpr bool NEEDS_BIND_STORAGE_INDEX = P::NEEDS_BIND_STORAGE_INDEX;
|
||||||
static constexpr bool USE_MEMORY_MAPS = P::USE_MEMORY_MAPS;
|
static constexpr bool USE_MEMORY_MAPS = P::USE_MEMORY_MAPS;
|
||||||
static constexpr bool SEPARATE_IMAGE_BUFFERS_BINDINGS = P::SEPARATE_IMAGE_BUFFER_BINDINGS;
|
static constexpr bool SEPARATE_IMAGE_BUFFERS_BINDINGS = P::SEPARATE_IMAGE_BUFFER_BINDINGS;
|
||||||
static constexpr bool IMPLEMENTS_ASYNC_DOWNLOADS = P::IMPLEMENTS_ASYNC_DOWNLOADS;
|
|
||||||
static constexpr bool USE_MEMORY_MAPS_FOR_UPLOADS = P::USE_MEMORY_MAPS_FOR_UPLOADS;
|
static constexpr bool USE_MEMORY_MAPS_FOR_UPLOADS = P::USE_MEMORY_MAPS_FOR_UPLOADS;
|
||||||
|
|
||||||
static constexpr s64 DEFAULT_EXPECTED_MEMORY = 512_MiB;
|
static constexpr s64 DEFAULT_EXPECTED_MEMORY = 512_MiB;
|
||||||
|
@ -202,34 +185,6 @@ class BufferCache : public VideoCommon::ChannelSetupCaches<BufferCacheChannelInf
|
||||||
using Async_Buffer = typename P::Async_Buffer;
|
using Async_Buffer = typename P::Async_Buffer;
|
||||||
using MemoryTracker = typename P::MemoryTracker;
|
using MemoryTracker = typename P::MemoryTracker;
|
||||||
|
|
||||||
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 OverlapResult {
|
struct OverlapResult {
|
||||||
boost::container::small_vector<BufferId, 16> ids;
|
boost::container::small_vector<BufferId, 16> ids;
|
||||||
DAddr begin;
|
DAddr begin;
|
||||||
|
@ -240,6 +195,8 @@ class BufferCache : public VideoCommon::ChannelSetupCaches<BufferCacheChannelInf
|
||||||
public:
|
public:
|
||||||
explicit BufferCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, Runtime& runtime_);
|
explicit BufferCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, Runtime& runtime_);
|
||||||
|
|
||||||
|
~BufferCache();
|
||||||
|
|
||||||
void TickFrame();
|
void TickFrame();
|
||||||
|
|
||||||
void WriteMemory(DAddr device_addr, u64 size);
|
void WriteMemory(DAddr device_addr, u64 size);
|
||||||
|
@ -379,75 +336,6 @@ private:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Func>
|
|
||||||
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()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto end_it = current_range.upper_bound(search_interval);
|
|
||||||
for (; it != end_it; it++) {
|
|
||||||
DAddr inter_addr_end = it->upper();
|
|
||||||
DAddr inter_addr = it->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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Func>
|
|
||||||
void ForEachInOverlapCounter(OverlapCounter& 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()) {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool IsRangeGranular(DAddr device_addr, size_t size) {
|
static bool IsRangeGranular(DAddr device_addr, size_t size) {
|
||||||
return (device_addr & ~Core::DEVICE_PAGEMASK) ==
|
return (device_addr & ~Core::DEVICE_PAGEMASK) ==
|
||||||
((device_addr + size) & ~Core::DEVICE_PAGEMASK);
|
((device_addr + size) & ~Core::DEVICE_PAGEMASK);
|
||||||
|
@ -552,14 +440,14 @@ private:
|
||||||
|
|
||||||
[[nodiscard]] bool HasFastUniformBufferBound(size_t stage, u32 binding_index) const noexcept;
|
[[nodiscard]] bool HasFastUniformBufferBound(size_t stage, u32 binding_index) const noexcept;
|
||||||
|
|
||||||
void ClearDownload(IntervalType subtract_interval);
|
void ClearDownload(DAddr base_addr, u64 size);
|
||||||
|
|
||||||
void InlineMemoryImplementation(DAddr dest_address, size_t copy_size,
|
void InlineMemoryImplementation(DAddr dest_address, size_t copy_size,
|
||||||
std::span<const u8> inlined_buffer);
|
std::span<const u8> inlined_buffer);
|
||||||
|
|
||||||
Tegra::MaxwellDeviceMemoryManager& device_memory;
|
Tegra::MaxwellDeviceMemoryManager& device_memory;
|
||||||
|
|
||||||
SlotVector<Buffer> slot_buffers;
|
Common::SlotVector<Buffer> slot_buffers;
|
||||||
DelayedDestructionRing<Buffer, 8> delayed_destruction_ring;
|
DelayedDestructionRing<Buffer, 8> delayed_destruction_ring;
|
||||||
|
|
||||||
const Tegra::Engines::DrawManager::IndirectParams* current_draw_indirect{};
|
const Tegra::Engines::DrawManager::IndirectParams* current_draw_indirect{};
|
||||||
|
@ -567,13 +455,12 @@ private:
|
||||||
u32 last_index_count = 0;
|
u32 last_index_count = 0;
|
||||||
|
|
||||||
MemoryTracker memory_tracker;
|
MemoryTracker memory_tracker;
|
||||||
IntervalSet uncommitted_ranges;
|
Common::RangeSet<DAddr> uncommitted_gpu_modified_ranges;
|
||||||
IntervalSet common_ranges;
|
Common::RangeSet<DAddr> gpu_modified_ranges;
|
||||||
IntervalSet cached_ranges;
|
std::deque<Common::RangeSet<DAddr>> committed_gpu_modified_ranges;
|
||||||
std::deque<IntervalSet> committed_ranges;
|
|
||||||
|
|
||||||
// Async Buffers
|
// Async Buffers
|
||||||
OverlapCounter async_downloads;
|
Common::OverlapRangeSet<DAddr> async_downloads;
|
||||||
std::deque<std::optional<Async_Buffer>> async_buffers;
|
std::deque<std::optional<Async_Buffer>> async_buffers;
|
||||||
std::deque<boost::container::small_vector<BufferCopy, 4>> pending_downloads;
|
std::deque<boost::container::small_vector<BufferCopy, 4>> pending_downloads;
|
||||||
std::optional<Async_Buffer> current_buffer;
|
std::optional<Async_Buffer> current_buffer;
|
||||||
|
|
|
@ -50,6 +50,7 @@ void Scheduler::Resume() {
|
||||||
void Scheduler::Yield() {
|
void Scheduler::Yield() {
|
||||||
ASSERT(current_fifo != nullptr);
|
ASSERT(current_fifo != nullptr);
|
||||||
Common::Fiber::YieldTo(current_fifo->context, *master_control);
|
Common::Fiber::YieldTo(current_fifo->context, *master_control);
|
||||||
|
gpu.BindChannel(current_fifo->bind_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scheduler::Push(s32 channel, CommandList&& entries) {
|
void Scheduler::Push(s32 channel, CommandList&& entries) {
|
||||||
|
|
|
@ -18,12 +18,12 @@
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
|
#include "common/slot_vector.h"
|
||||||
#include "video_core/control/channel_state_cache.h"
|
#include "video_core/control/channel_state_cache.h"
|
||||||
#include "video_core/engines/maxwell_3d.h"
|
#include "video_core/engines/maxwell_3d.h"
|
||||||
#include "video_core/host1x/gpu_device_memory_manager.h"
|
#include "video_core/host1x/gpu_device_memory_manager.h"
|
||||||
#include "video_core/memory_manager.h"
|
#include "video_core/memory_manager.h"
|
||||||
#include "video_core/rasterizer_interface.h"
|
#include "video_core/rasterizer_interface.h"
|
||||||
#include "video_core/texture_cache/slot_vector.h"
|
|
||||||
|
|
||||||
namespace VideoCore {
|
namespace VideoCore {
|
||||||
enum class QueryType {
|
enum class QueryType {
|
||||||
|
@ -37,7 +37,7 @@ constexpr std::size_t NumQueryTypes = static_cast<size_t>(QueryType::Count);
|
||||||
|
|
||||||
namespace VideoCommon {
|
namespace VideoCommon {
|
||||||
|
|
||||||
using AsyncJobId = SlotId;
|
using AsyncJobId = Common::SlotId;
|
||||||
|
|
||||||
static constexpr AsyncJobId NULL_ASYNC_JOB_ID{0};
|
static constexpr AsyncJobId NULL_ASYNC_JOB_ID{0};
|
||||||
|
|
||||||
|
@ -341,7 +341,7 @@ private:
|
||||||
static constexpr std::uintptr_t YUZU_PAGESIZE = 4096;
|
static constexpr std::uintptr_t YUZU_PAGESIZE = 4096;
|
||||||
static constexpr unsigned YUZU_PAGEBITS = 12;
|
static constexpr unsigned YUZU_PAGEBITS = 12;
|
||||||
|
|
||||||
SlotVector<AsyncJob> slot_async_jobs;
|
Common::SlotVector<AsyncJob> slot_async_jobs;
|
||||||
|
|
||||||
VideoCore::RasterizerInterface& rasterizer;
|
VideoCore::RasterizerInterface& rasterizer;
|
||||||
Tegra::MaxwellDeviceMemoryManager& device_memory;
|
Tegra::MaxwellDeviceMemoryManager& device_memory;
|
||||||
|
|
|
@ -90,7 +90,7 @@ public:
|
||||||
void PostCopyBarrier();
|
void PostCopyBarrier();
|
||||||
void Finish();
|
void Finish();
|
||||||
|
|
||||||
void TickFrame(VideoCommon::SlotVector<Buffer>&) noexcept {}
|
void TickFrame(Common::SlotVector<Buffer>&) noexcept {}
|
||||||
|
|
||||||
void ClearBuffer(Buffer& dest_buffer, u32 offset, size_t size, u32 value);
|
void ClearBuffer(Buffer& dest_buffer, u32 offset, size_t size, u32 value);
|
||||||
|
|
||||||
|
@ -251,7 +251,6 @@ struct BufferCacheParams {
|
||||||
static constexpr bool NEEDS_BIND_STORAGE_INDEX = true;
|
static constexpr bool NEEDS_BIND_STORAGE_INDEX = true;
|
||||||
static constexpr bool USE_MEMORY_MAPS = true;
|
static constexpr bool USE_MEMORY_MAPS = true;
|
||||||
static constexpr bool SEPARATE_IMAGE_BUFFER_BINDINGS = true;
|
static constexpr bool SEPARATE_IMAGE_BUFFER_BINDINGS = true;
|
||||||
static constexpr bool IMPLEMENTS_ASYNC_DOWNLOADS = true;
|
|
||||||
|
|
||||||
// TODO: Investigate why OpenGL seems to perform worse with persistently mapped buffer uploads
|
// TODO: Investigate why OpenGL seems to perform worse with persistently mapped buffer uploads
|
||||||
static constexpr bool USE_MEMORY_MAPS_FOR_UPLOADS = false;
|
static constexpr bool USE_MEMORY_MAPS_FOR_UPLOADS = false;
|
||||||
|
|
|
@ -30,13 +30,13 @@ class Image;
|
||||||
class ImageView;
|
class ImageView;
|
||||||
class Sampler;
|
class Sampler;
|
||||||
|
|
||||||
|
using Common::SlotVector;
|
||||||
using VideoCommon::ImageId;
|
using VideoCommon::ImageId;
|
||||||
using VideoCommon::ImageViewId;
|
using VideoCommon::ImageViewId;
|
||||||
using VideoCommon::ImageViewType;
|
using VideoCommon::ImageViewType;
|
||||||
using VideoCommon::NUM_RT;
|
using VideoCommon::NUM_RT;
|
||||||
using VideoCommon::Region2D;
|
using VideoCommon::Region2D;
|
||||||
using VideoCommon::RenderTargets;
|
using VideoCommon::RenderTargets;
|
||||||
using VideoCommon::SlotVector;
|
|
||||||
|
|
||||||
struct FormatProperties {
|
struct FormatProperties {
|
||||||
GLenum compatibility_class;
|
GLenum compatibility_class;
|
||||||
|
|
|
@ -368,7 +368,7 @@ u32 BufferCacheRuntime::GetStorageBufferAlignment() const {
|
||||||
return static_cast<u32>(device.GetStorageBufferAlignment());
|
return static_cast<u32>(device.GetStorageBufferAlignment());
|
||||||
}
|
}
|
||||||
|
|
||||||
void BufferCacheRuntime::TickFrame(VideoCommon::SlotVector<Buffer>& slot_buffers) noexcept {
|
void BufferCacheRuntime::TickFrame(Common::SlotVector<Buffer>& slot_buffers) noexcept {
|
||||||
for (auto it = slot_buffers.begin(); it != slot_buffers.end(); it++) {
|
for (auto it = slot_buffers.begin(); it != slot_buffers.end(); it++) {
|
||||||
it->ResetUsageTracking();
|
it->ResetUsageTracking();
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,7 +81,7 @@ public:
|
||||||
ComputePassDescriptorQueue& compute_pass_descriptor_queue,
|
ComputePassDescriptorQueue& compute_pass_descriptor_queue,
|
||||||
DescriptorPool& descriptor_pool);
|
DescriptorPool& descriptor_pool);
|
||||||
|
|
||||||
void TickFrame(VideoCommon::SlotVector<Buffer>& slot_buffers) noexcept;
|
void TickFrame(Common::SlotVector<Buffer>& slot_buffers) noexcept;
|
||||||
|
|
||||||
void Finish();
|
void Finish();
|
||||||
|
|
||||||
|
@ -181,7 +181,6 @@ struct BufferCacheParams {
|
||||||
static constexpr bool NEEDS_BIND_STORAGE_INDEX = false;
|
static constexpr bool NEEDS_BIND_STORAGE_INDEX = false;
|
||||||
static constexpr bool USE_MEMORY_MAPS = true;
|
static constexpr bool USE_MEMORY_MAPS = true;
|
||||||
static constexpr bool SEPARATE_IMAGE_BUFFER_BINDINGS = false;
|
static constexpr bool SEPARATE_IMAGE_BUFFER_BINDINGS = false;
|
||||||
static constexpr bool IMPLEMENTS_ASYNC_DOWNLOADS = true;
|
|
||||||
static constexpr bool USE_MEMORY_MAPS_FOR_UPLOADS = true;
|
static constexpr bool USE_MEMORY_MAPS_FOR_UPLOADS = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -20,11 +20,11 @@ struct ResolutionScalingInfo;
|
||||||
|
|
||||||
namespace Vulkan {
|
namespace Vulkan {
|
||||||
|
|
||||||
|
using Common::SlotVector;
|
||||||
using VideoCommon::ImageId;
|
using VideoCommon::ImageId;
|
||||||
using VideoCommon::NUM_RT;
|
using VideoCommon::NUM_RT;
|
||||||
using VideoCommon::Region2D;
|
using VideoCommon::Region2D;
|
||||||
using VideoCommon::RenderTargets;
|
using VideoCommon::RenderTargets;
|
||||||
using VideoCommon::SlotVector;
|
|
||||||
using VideoCore::Surface::PixelFormat;
|
using VideoCore::Surface::PixelFormat;
|
||||||
|
|
||||||
class BlitImageHelper;
|
class BlitImageHelper;
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include "common/lru_cache.h"
|
#include "common/lru_cache.h"
|
||||||
#include "common/polyfill_ranges.h"
|
#include "common/polyfill_ranges.h"
|
||||||
#include "common/scratch_buffer.h"
|
#include "common/scratch_buffer.h"
|
||||||
|
#include "common/slot_vector.h"
|
||||||
#include "common/thread_worker.h"
|
#include "common/thread_worker.h"
|
||||||
#include "video_core/compatible_formats.h"
|
#include "video_core/compatible_formats.h"
|
||||||
#include "video_core/control/channel_state_cache.h"
|
#include "video_core/control/channel_state_cache.h"
|
||||||
|
@ -32,7 +33,6 @@
|
||||||
#include "video_core/texture_cache/image_info.h"
|
#include "video_core/texture_cache/image_info.h"
|
||||||
#include "video_core/texture_cache/image_view_base.h"
|
#include "video_core/texture_cache/image_view_base.h"
|
||||||
#include "video_core/texture_cache/render_targets.h"
|
#include "video_core/texture_cache/render_targets.h"
|
||||||
#include "video_core/texture_cache/slot_vector.h"
|
|
||||||
#include "video_core/texture_cache/types.h"
|
#include "video_core/texture_cache/types.h"
|
||||||
#include "video_core/textures/texture.h"
|
#include "video_core/textures/texture.h"
|
||||||
|
|
||||||
|
@ -451,16 +451,16 @@ private:
|
||||||
struct PendingDownload {
|
struct PendingDownload {
|
||||||
bool is_swizzle;
|
bool is_swizzle;
|
||||||
size_t async_buffer_id;
|
size_t async_buffer_id;
|
||||||
SlotId object_id;
|
Common::SlotId object_id;
|
||||||
};
|
};
|
||||||
|
|
||||||
SlotVector<Image> slot_images;
|
Common::SlotVector<Image> slot_images;
|
||||||
SlotVector<ImageMapView> slot_map_views;
|
Common::SlotVector<ImageMapView> slot_map_views;
|
||||||
SlotVector<ImageView> slot_image_views;
|
Common::SlotVector<ImageView> slot_image_views;
|
||||||
SlotVector<ImageAlloc> slot_image_allocs;
|
Common::SlotVector<ImageAlloc> slot_image_allocs;
|
||||||
SlotVector<Sampler> slot_samplers;
|
Common::SlotVector<Sampler> slot_samplers;
|
||||||
SlotVector<Framebuffer> slot_framebuffers;
|
Common::SlotVector<Framebuffer> slot_framebuffers;
|
||||||
SlotVector<BufferDownload> slot_buffer_downloads;
|
Common::SlotVector<BufferDownload> slot_buffer_downloads;
|
||||||
|
|
||||||
// TODO: This data structure is not optimal and it should be reworked
|
// TODO: This data structure is not optimal and it should be reworked
|
||||||
|
|
||||||
|
|
|
@ -5,21 +5,21 @@
|
||||||
|
|
||||||
#include "common/common_funcs.h"
|
#include "common/common_funcs.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "video_core/texture_cache/slot_vector.h"
|
#include "common/slot_vector.h"
|
||||||
|
|
||||||
namespace VideoCommon {
|
namespace VideoCommon {
|
||||||
|
|
||||||
constexpr size_t NUM_RT = 8;
|
constexpr size_t NUM_RT = 8;
|
||||||
constexpr size_t MAX_MIP_LEVELS = 14;
|
constexpr size_t MAX_MIP_LEVELS = 14;
|
||||||
|
|
||||||
constexpr SlotId CORRUPT_ID{0xfffffffe};
|
constexpr Common::SlotId CORRUPT_ID{0xfffffffe};
|
||||||
|
|
||||||
using ImageId = SlotId;
|
using ImageId = Common::SlotId;
|
||||||
using ImageMapId = SlotId;
|
using ImageMapId = Common::SlotId;
|
||||||
using ImageViewId = SlotId;
|
using ImageViewId = Common::SlotId;
|
||||||
using ImageAllocId = SlotId;
|
using ImageAllocId = Common::SlotId;
|
||||||
using SamplerId = SlotId;
|
using SamplerId = Common::SlotId;
|
||||||
using FramebufferId = SlotId;
|
using FramebufferId = Common::SlotId;
|
||||||
|
|
||||||
/// Fake image ID for null image views
|
/// Fake image ID for null image views
|
||||||
constexpr ImageId NULL_IMAGE_ID{0};
|
constexpr ImageId NULL_IMAGE_ID{0};
|
||||||
|
|
Loading…
Reference in a new issue