9 changed files with 238 additions and 95 deletions
-
1src/common/CMakeLists.txt
-
139src/common/range_map.h
-
1src/tests/CMakeLists.txt
-
70src/tests/common/range_map.cpp
-
3src/video_core/dma_pusher.cpp
-
15src/video_core/engines/maxwell_3d.cpp
-
2src/video_core/engines/maxwell_3d.h
-
91src/video_core/memory_manager.cpp
-
11src/video_core/memory_manager.h
@ -0,0 +1,139 @@ |
|||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project |
|||
// SPDX-License-Identifier: GPL-3.0-or-later |
|||
|
|||
#pragma once |
|||
|
|||
#include <map> |
|||
#include <type_traits> |
|||
|
|||
#include "common/common_types.h" |
|||
|
|||
namespace Common { |
|||
|
|||
template <typename KeyTBase, typename ValueT> |
|||
class RangeMap { |
|||
private: |
|||
using KeyT = std::conditional_t<std::is_signed_v<KeyTBase>, typename KeyTBase, |
|||
std::make_signed_t<KeyTBase>>; |
|||
|
|||
public: |
|||
explicit RangeMap(ValueT null_value_) : null_value{null_value_} { |
|||
container.emplace(std::numeric_limits<KeyT>::min(), null_value); |
|||
}; |
|||
~RangeMap() = default; |
|||
|
|||
void Map(KeyTBase address, KeyTBase address_end, ValueT value) { |
|||
KeyT new_address = static_cast<KeyT>(address); |
|||
KeyT new_address_end = static_cast<KeyT>(address_end); |
|||
if (new_address < 0) { |
|||
new_address = 0; |
|||
} |
|||
if (new_address_end < 0) { |
|||
new_address_end = 0; |
|||
} |
|||
InternalMap(new_address, new_address_end, value); |
|||
} |
|||
|
|||
void Unmap(KeyTBase address, KeyTBase address_end) { |
|||
Map(address, address_end, null_value); |
|||
} |
|||
|
|||
[[nodiscard]] size_t GetContinousSizeFrom(KeyTBase address) const { |
|||
const KeyT new_address = static_cast<KeyT>(address); |
|||
if (new_address < 0) { |
|||
return 0; |
|||
} |
|||
return ContinousSizeInternal(new_address); |
|||
} |
|||
|
|||
[[nodiscard]] ValueT GetValueAt(KeyT address) const { |
|||
const KeyT new_address = static_cast<KeyT>(address); |
|||
if (new_address < 0) { |
|||
return null_value; |
|||
} |
|||
return GetValueInternal(new_address); |
|||
} |
|||
|
|||
private: |
|||
using MapType = std::map<KeyT, ValueT>; |
|||
using IteratorType = MapType::iterator; |
|||
using ConstIteratorType = MapType::const_iterator; |
|||
|
|||
size_t ContinousSizeInternal(KeyT address) const { |
|||
const auto it = GetFirstElemnentBeforeOrOn(address); |
|||
if (it == container.end() || it->second == null_value) { |
|||
return 0; |
|||
} |
|||
const auto it_end = std::next(it); |
|||
if (it_end == container.end()) { |
|||
return std::numeric_limits<KeyT>::max() - address; |
|||
} |
|||
return it_end->first - address; |
|||
} |
|||
|
|||
ValueT GetValueInternal(KeyT address) const { |
|||
const auto it = GetFirstElemnentBeforeOrOn(address); |
|||
if (it == container.end()) { |
|||
return null_value; |
|||
} |
|||
return it->second; |
|||
} |
|||
|
|||
ConstIteratorType GetFirstElemnentBeforeOrOn(KeyT address) const { |
|||
auto it = container.lower_bound(address); |
|||
if (it == container.begin()) { |
|||
return it; |
|||
} |
|||
if (it != container.end() && (it->first == address)) { |
|||
return it; |
|||
} |
|||
--it; |
|||
return it; |
|||
} |
|||
|
|||
ValueT GetFirstValueWithin(KeyT address) { |
|||
auto it = container.lower_bound(address); |
|||
if (it == container.begin()) { |
|||
return it->second; |
|||
} |
|||
if (it == container.end()) [[unlikely]] { // this would be a bug |
|||
return null_value; |
|||
} |
|||
--it; |
|||
return it->second; |
|||
} |
|||
|
|||
ValueT GetLastValueWithin(KeyT address) { |
|||
auto it = container.upper_bound(address); |
|||
if (it == container.end()) { |
|||
return null_value; |
|||
} |
|||
if (it == container.begin()) [[unlikely]] { // this would be a bug |
|||
return it->second; |
|||
} |
|||
--it; |
|||
return it->second; |
|||
} |
|||
|
|||
void InternalMap(KeyT address, KeyT address_end, ValueT value) { |
|||
const bool must_add_start = GetFirstValueWithin(address) != value; |
|||
const ValueT last_value = GetLastValueWithin(address_end); |
|||
const bool must_add_end = last_value != value; |
|||
auto it = container.lower_bound(address); |
|||
const auto it_end = container.upper_bound(address_end); |
|||
while (it != it_end) { |
|||
it = container.erase(it); |
|||
} |
|||
if (must_add_start) { |
|||
container.emplace(address, value); |
|||
} |
|||
if (must_add_end) { |
|||
container.emplace(address_end, last_value); |
|||
} |
|||
} |
|||
|
|||
ValueT null_value; |
|||
MapType container; |
|||
}; |
|||
|
|||
} // namespace Common |
|||
@ -0,0 +1,70 @@ |
|||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|||
|
|||
#include <stdexcept>
|
|||
|
|||
#include <catch2/catch.hpp>
|
|||
|
|||
#include "common/range_map.h"
|
|||
|
|||
enum class MappedEnum : u32 { |
|||
Invalid = 0, |
|||
Valid_1 = 1, |
|||
Valid_2 = 2, |
|||
Valid_3 = 3, |
|||
}; |
|||
|
|||
TEST_CASE("Range Map: Setup", "[video_core]") { |
|||
Common::RangeMap<u64, MappedEnum> my_map(MappedEnum::Invalid); |
|||
my_map.Map(3000, 3500, MappedEnum::Valid_1); |
|||
my_map.Unmap(3200, 3600); |
|||
my_map.Map(4000, 4500, MappedEnum::Valid_2); |
|||
my_map.Map(4200, 4400, MappedEnum::Valid_2); |
|||
my_map.Map(4200, 4400, MappedEnum::Valid_1); |
|||
REQUIRE(my_map.GetContinousSizeFrom(4200) == 200); |
|||
REQUIRE(my_map.GetContinousSizeFrom(3000) == 200); |
|||
REQUIRE(my_map.GetContinousSizeFrom(2900) == 0); |
|||
|
|||
REQUIRE(my_map.GetValueAt(2900) == MappedEnum::Invalid); |
|||
REQUIRE(my_map.GetValueAt(3100) == MappedEnum::Valid_1); |
|||
REQUIRE(my_map.GetValueAt(3000) == MappedEnum::Valid_1); |
|||
REQUIRE(my_map.GetValueAt(3200) == MappedEnum::Invalid); |
|||
|
|||
REQUIRE(my_map.GetValueAt(4199) == MappedEnum::Valid_2); |
|||
REQUIRE(my_map.GetValueAt(4200) == MappedEnum::Valid_1); |
|||
REQUIRE(my_map.GetValueAt(4400) == MappedEnum::Valid_2); |
|||
REQUIRE(my_map.GetValueAt(4500) == MappedEnum::Invalid); |
|||
REQUIRE(my_map.GetValueAt(4600) == MappedEnum::Invalid); |
|||
|
|||
my_map.Unmap(0, 6000); |
|||
for (u64 address = 0; address < 10000; address += 1000) { |
|||
REQUIRE(my_map.GetContinousSizeFrom(address) == 0); |
|||
} |
|||
|
|||
my_map.Map(1000, 3000, MappedEnum::Valid_1); |
|||
my_map.Map(4000, 5000, MappedEnum::Valid_1); |
|||
my_map.Map(2500, 4100, MappedEnum::Valid_1); |
|||
REQUIRE(my_map.GetContinousSizeFrom(1000) == 4000); |
|||
|
|||
my_map.Map(1000, 3000, MappedEnum::Valid_1); |
|||
my_map.Map(4000, 5000, MappedEnum::Valid_2); |
|||
my_map.Map(2500, 4100, MappedEnum::Valid_3); |
|||
REQUIRE(my_map.GetContinousSizeFrom(1000) == 1500); |
|||
REQUIRE(my_map.GetContinousSizeFrom(2500) == 1600); |
|||
REQUIRE(my_map.GetContinousSizeFrom(4100) == 900); |
|||
REQUIRE(my_map.GetValueAt(900) == MappedEnum::Invalid); |
|||
REQUIRE(my_map.GetValueAt(1000) == MappedEnum::Valid_1); |
|||
REQUIRE(my_map.GetValueAt(2500) == MappedEnum::Valid_3); |
|||
REQUIRE(my_map.GetValueAt(4100) == MappedEnum::Valid_2); |
|||
REQUIRE(my_map.GetValueAt(5000) == MappedEnum::Invalid); |
|||
|
|||
my_map.Map(2000, 6000, MappedEnum::Valid_3); |
|||
REQUIRE(my_map.GetContinousSizeFrom(1000) == 1000); |
|||
REQUIRE(my_map.GetContinousSizeFrom(3000) == 3000); |
|||
REQUIRE(my_map.GetValueAt(1000) == MappedEnum::Valid_1); |
|||
REQUIRE(my_map.GetValueAt(1999) == MappedEnum::Valid_1); |
|||
REQUIRE(my_map.GetValueAt(1500) == MappedEnum::Valid_1); |
|||
REQUIRE(my_map.GetValueAt(2001) == MappedEnum::Valid_3); |
|||
REQUIRE(my_map.GetValueAt(5999) == MappedEnum::Valid_3); |
|||
REQUIRE(my_map.GetValueAt(6000) == MappedEnum::Invalid); |
|||
} |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue