Compare commits

..

43 Commits

Author SHA1 Message Date
ReinUsesLisp
576ad9a012 gl_sampler_cache: Port sampler cache to OpenGL 2019-04-02 16:58:08 -03:00
ReinUsesLisp
c5047540c9 video_core: Abstract vk_sampler_cache into a templated class 2019-04-02 15:54:11 -03:00
Mat M
628153cccd Merge pull request #2316 from ReinUsesLisp/fixup-process
process: Fix up compilation
2019-04-02 00:46:00 -04:00
ReinUsesLisp
592a24ae53 process: Fix up compilation 2019-04-02 01:44:32 -03:00
bunnei
29df6bbbd3 Merge pull request #2281 from lioncash/memory
kernel/codeset: Make CodeSet's memory data member a regular std::vector
2019-04-01 22:20:05 -04:00
bunnei
62860dc0b0 Merge pull request #2301 from FearlessTobi/remove-amiibo-setting
core/yuzu: Remove enable_nfc setting
2019-04-01 15:02:08 -04:00
bunnei
ffc72c8f15 Merge pull request #2283 from FearlessTobi/port-4517
Port citra-emu/citra#4517 &  citra-emu/citra#4686: Changes to macOS buildscripts
2019-04-01 14:59:44 -04:00
bunnei
e0eee250bb Merge pull request #2312 from lioncash/locks
general: Use deducation guides for std::lock_guard and std::unique_lock
2019-04-01 14:36:24 -04:00
Lioncash
781ab8407b general: Use deducation guides for std::lock_guard and std::unique_lock
Since C++17, the introduction of deduction guides for locking facilities
means that we no longer need to hardcode the mutex type into the locks
themselves, making it easier to switch mutex types, should it ever be
necessary in the future.
2019-04-01 12:53:47 -04:00
bunnei
d9b7bc4474 Merge pull request #2304 from lioncash/memsize
kernel/process: Report total physical memory used to svcGetInfo slightly better
2019-03-30 20:11:17 -04:00
bunnei
a89266bc5e Merge pull request #2303 from lioncash/thread
common/thread: Remove unused functions
2019-03-30 20:10:32 -04:00
bunnei
1960164055 Merge pull request #2297 from lioncash/reorder
video_core: Amend constructor initializer list order where applicable
2019-03-30 20:00:26 -04:00
bunnei
e199d1e14f Merge pull request #2298 from lioncash/variable
video_core/{gl_rasterizer, gpu_thread}: Remove unused class variables where applicable
2019-03-30 19:59:45 -04:00
bunnei
ba3d95e550 Merge pull request #2308 from lioncash/deduction
kernel/scheduler: Minor tidying up
2019-03-30 19:59:10 -04:00
bunnei
f911d1f685 Merge pull request #2307 from lioncash/regnames
service/fatal: Name FatalInfo structure members
2019-03-30 19:57:21 -04:00
Lioncash
824b8e4086 kernel/scheduler: Remove unused parameter to AddThread()
This was made unused in b404fcdf14, but
the parameter itself wasn't removed.
2019-03-30 05:29:33 -04:00
Lioncash
cb805f45ae kernel/scheduler: Use deduction guides on mutex locks
Since C++17, we no longer need to explicitly specify the type of the
mutex within the lock_guard. The type system can now deduce these with
deduction guides.
2019-03-30 05:28:43 -04:00
Lioncash
4b33a346ed service/fatal: Mark local variables as const where applicable 2019-03-30 03:06:23 -04:00
Lioncash
11505d3d9f service/fatal: Remove unnecessary semicolon
Resolves a -Wextra-semi warning.
2019-03-30 03:04:16 -04:00
Lioncash
cc737e5832 service/fatal: Name FatalInfo structure members
Based off RE, most of these structure members are register values, which
makes, sense given this service is used to convey fatal errors.

One member indicates the program entry point address, one is a set of
bit flags used to determine which registers to print, and one member
indicates the architecture type.

The only member that still isn't determined is the final member within
the data structure.
2019-03-30 03:01:20 -04:00
fearlessTobi
ff7e6a42c1 core/yuzu: Remove enable_nfc setting
This was initially added to prevent problems from stubbed/not implemented NFC services, but as we never encountered such and as it's only used in a deprecated function anyway, I guess we can just remove it to prevent more clutter of the settings.
2019-03-29 15:02:28 +01:00
Lioncash
3a846aa80f kernel/process: Report total physical memory used to svcGetInfo
Reports the (mostly) correct size through svcGetInfo now for queries to
total used physical memory. This still doesn't correctly handle memory
allocated via svcMapPhysicalMemory, however, we don't currently handle
that case anyways.
2019-03-28 22:59:20 -04:00
Lioncash
2289e895aa kernel/process: Store the total size of the code memory loaded
This will be necessary to properly report the used memory size in
svcGetInfo.
2019-03-28 22:51:17 -04:00
bunnei
f770c17d01 Merge pull request #2266 from FernandoS27/arbitration
Kernel: Fixes to Arbitration and SignalProcessWideKey Management
2019-03-28 21:42:24 -04:00
bunnei
b404fcdf14 Merge pull request #2265 from FernandoS27/multilevelqueue
Replace old Thread Queue for a new Multi Level Queue
2019-03-28 21:41:40 -04:00
Lioncash
5d4ab5ec2f kernel/process: Store the main thread stack size to a data member
This will be necessary in order to properly report memory usage within
svcGetInfo.
2019-03-28 18:45:06 -04:00
Lioncash
427f1e3e3d kernel/process: Make Run's stack size parameter a u64
This will make operating with the process-related SVC commands much
nicer in the future (the parameter representing the stack size in
svcStartProcess is a 64-bit value).
2019-03-28 18:26:12 -04:00
Lioncash
2aca7b9e1e kernel/process: Ensure that given stack size is always page-aligned
The kernel always makes sure that the given stack size is aligned to
page boundaries.
2019-03-28 18:25:00 -04:00
Fernando Sahmkow
db42bcb306 Fixes and corrections on formatting. 2019-03-27 14:49:43 -04:00
Fernando Sahmkow
f35e09fe0d Fixes to multilevelqueue's iterator. 2019-03-27 14:34:33 -04:00
Fernando Sahmkow
dde0814837 Use MultiLevelQueue instead of old ThreadQueueList 2019-03-27 14:34:32 -04:00
Fernando Sahmkow
9dbba9240b Add MultiLevelQueue Tests 2019-03-27 14:34:31 -04:00
Fernando Sahmkow
3bc815a5dc Implement intrinsics CountTrailingZeroes and test it. 2019-03-27 14:34:29 -04:00
Fernando Sahmkow
522957f9f3 Implement a MultiLevelQueue 2019-03-27 14:33:44 -04:00
Lioncash
947d364dba gpu_thread: Remove unused dma_pusher class member variable from ThreadManager
The pusher instance is only ever used in the constructor of the
ThreadManager for creating the thread that the ThreadManager instance
contains. Aside from that, the member is unused, so it can be removed.
2019-03-27 12:51:21 -04:00
Lioncash
e2131f7310 gl_rasterizer: Remove unused reference member variable from RasterizerOpenGL
This member variable is no longer being used, so it can be removed,
removing a dependency on EmuWindow from the rasterizer's interface"
2019-03-27 12:45:59 -04:00
Lioncash
a5fa4b311e video_core: Amend constructor initializer list order where applicable
Specifies the members in the same order that initialization would take
place in.

This also silences -Wreorder warnings.
2019-03-27 12:37:53 -04:00
Alex James
a5dbda3f76 travis/macos: Use macpack to bundle dependencies
This appears to properly handle the ffmpeg libraries that dylibbundler
failed to patch.
2019-03-23 01:37:38 +01:00
MerryMage
2bcebcff2a travis: Simplify macos/upload.sh 2019-03-23 01:33:53 +01:00
Lioncash
c0a01f3adc kernel/codeset: Make CodeSet's memory data member a regular std::vector
The use of a shared_ptr is an implementation detail of the VMManager
itself when mapping memory. Because of that, we shouldn't require all
users of the CodeSet to have to allocate the shared_ptr ahead of time.
It's intended that CodeSet simply pass in the required direct data, and
that the memory manager takes care of it from that point on.

This means we just do the shared pointer allocation in a single place,
when loading modules, as opposed to in each loader.
2019-03-22 18:43:46 -04:00
Fernando Sahmkow
9c7319a4d4 Fix small bug that kept a thread as a condvar thread after being signalled. 2019-03-19 22:43:13 -04:00
Fernando Sahmkow
acbdfdae64 Add CondVar Thread State. 2019-03-19 20:32:47 -04:00
Fernando Sahmkow
774f139e65 Small fixes to address_arbiter to better match the IDB. 2019-03-19 20:32:46 -04:00
67 changed files with 901 additions and 494 deletions

View File

@@ -1,5 +1,6 @@
#!/bin/sh -ex
brew update
brew install dylibbundler p7zip qt5 sdl2 ccache
brew install p7zip qt5 sdl2 ccache
brew outdated cmake || brew upgrade cmake
pip3 install macpack

View File

@@ -11,92 +11,19 @@ mkdir "$REV_NAME"
cp build/bin/yuzu-cmd "$REV_NAME"
cp -r build/bin/yuzu.app "$REV_NAME"
# move qt libs into app bundle for deployment
$(brew --prefix)/opt/qt5/bin/macdeployqt "${REV_NAME}/yuzu.app"
# move libs into folder for deployment
macpack "${REV_NAME}/yuzu.app/Contents/MacOS/yuzu" -d "../Frameworks"
# move qt frameworks into app bundle for deployment
$(brew --prefix)/opt/qt5/bin/macdeployqt "${REV_NAME}/yuzu.app" -executable="${REV_NAME}/yuzu.app/Contents/MacOS/yuzu"
# move SDL2 libs into folder for deployment
dylibbundler -b -x "${REV_NAME}/yuzu-cmd" -cd -d "${REV_NAME}/libs" -p "@executable_path/libs/"
# Make the changes to make the yuzu app standalone (i.e. not dependent on the current brew installation).
# To do this, the absolute references to each and every QT framework must be re-written to point to the local frameworks
# (in the Contents/Frameworks folder).
# The "install_name_tool" is used to do so.
# Coreutils is a hack to coerce Homebrew to point to the absolute Cellar path (symlink dereferenced). i.e:
# ls -l /usr/local/opt/qt5:: /usr/local/opt/qt5 -> ../Cellar/qt5/5.6.1-1
# grealpath ../Cellar/qt5/5.6.1-1:: /usr/local/Cellar/qt5/5.6.1-1
brew install coreutils || brew upgrade coreutils || true
REV_NAME_ALT=$REV_NAME/
# grealpath is located in coreutils, there is no "realpath" for OS X :(
QT_BREWS_PATH=$(grealpath "$(brew --prefix qt5)")
BREW_PATH=$(brew --prefix)
QT_VERSION_NUM=5
$BREW_PATH/opt/qt5/bin/macdeployqt "${REV_NAME_ALT}yuzu.app" \
-executable="${REV_NAME_ALT}yuzu.app/Contents/MacOS/yuzu"
# These are the files that macdeployqt packed into Contents/Frameworks/ - we don't want those, so we replace them.
declare -a macos_libs=("QtCore" "QtWidgets" "QtGui" "QtOpenGL" "QtPrintSupport")
for macos_lib in "${macos_libs[@]}"
do
SC_FRAMEWORK_PART=$macos_lib.framework/Versions/$QT_VERSION_NUM/$macos_lib
# Replace macdeployqt versions of the Frameworks with our own (from /usr/local/opt/qt5/lib/)
cp "$BREW_PATH/opt/qt5/lib/$SC_FRAMEWORK_PART" "${REV_NAME_ALT}yuzu.app/Contents/Frameworks/$SC_FRAMEWORK_PART"
# Replace references within the embedded Framework files with "internal" versions.
for macos_lib2 in "${macos_libs[@]}"
do
# Since brew references both the non-symlinked and symlink paths of QT5, it needs to be duplicated.
# /usr/local/Cellar/qt5/5.6.1-1/lib and /usr/local/opt/qt5/lib both resolve to the same files.
# So the two lines below are effectively duplicates when resolved as a path, but as strings, they aren't.
RM_FRAMEWORK_PART=$macos_lib2.framework/Versions/$QT_VERSION_NUM/$macos_lib2
install_name_tool -change \
$QT_BREWS_PATH/lib/$RM_FRAMEWORK_PART \
@executable_path/../Frameworks/$RM_FRAMEWORK_PART \
"${REV_NAME_ALT}yuzu.app/Contents/Frameworks/$SC_FRAMEWORK_PART"
install_name_tool -change \
"$BREW_PATH/opt/qt5/lib/$RM_FRAMEWORK_PART" \
@executable_path/../Frameworks/$RM_FRAMEWORK_PART \
"${REV_NAME_ALT}yuzu.app/Contents/Frameworks/$SC_FRAMEWORK_PART"
done
done
# Handles `This application failed to start because it could not find or load the Qt platform plugin "cocoa"`
# Which manifests itself as:
# "Exception Type: EXC_CRASH (SIGABRT) | Exception Codes: 0x0000000000000000, 0x0000000000000000 | Exception Note: EXC_CORPSE_NOTIFY"
# There may be more dylibs needed to be fixed...
declare -a macos_plugins=("Plugins/platforms/libqcocoa.dylib")
for macos_lib in "${macos_plugins[@]}"
do
install_name_tool -id @executable_path/../$macos_lib "${REV_NAME_ALT}yuzu.app/Contents/$macos_lib"
for macos_lib2 in "${macos_libs[@]}"
do
RM_FRAMEWORK_PART=$macos_lib2.framework/Versions/$QT_VERSION_NUM/$macos_lib2
install_name_tool -change \
$QT_BREWS_PATH/lib/$RM_FRAMEWORK_PART \
@executable_path/../Frameworks/$RM_FRAMEWORK_PART \
"${REV_NAME_ALT}yuzu.app/Contents/$macos_lib"
install_name_tool -change \
"$BREW_PATH/opt/qt5/lib/$RM_FRAMEWORK_PART" \
@executable_path/../Frameworks/$RM_FRAMEWORK_PART \
"${REV_NAME_ALT}yuzu.app/Contents/$macos_lib"
done
done
for macos_lib in "${macos_libs[@]}"
do
# Debugging info for Travis-CI
otool -L "${REV_NAME_ALT}yuzu.app/Contents/Frameworks/$macos_lib.framework/Versions/$QT_VERSION_NUM/$macos_lib"
done
# move libs into folder for deployment
macpack "${REV_NAME}/yuzu-cmd" -d "libs"
# Make the yuzu.app application launch a debugging terminal.
# Store away the actual binary
mv ${REV_NAME_ALT}yuzu.app/Contents/MacOS/yuzu ${REV_NAME_ALT}yuzu.app/Contents/MacOS/yuzu-bin
mv ${REV_NAME}/yuzu.app/Contents/MacOS/yuzu ${REV_NAME}/yuzu.app/Contents/MacOS/yuzu-bin
cat > ${REV_NAME_ALT}yuzu.app/Contents/MacOS/yuzu <<EOL
cat > ${REV_NAME}/yuzu.app/Contents/MacOS/yuzu <<EOL
#!/usr/bin/env bash
cd "\`dirname "\$0"\`"
chmod +x yuzu-bin
@@ -105,6 +32,9 @@ EOL
# Content that will serve as the launching script for yuzu (within the .app folder)
# Make the launching script executable
chmod +x ${REV_NAME_ALT}yuzu.app/Contents/MacOS/yuzu
chmod +x ${REV_NAME}/yuzu.app/Contents/MacOS/yuzu
# Verify loader instructions
find "$REV_NAME" -exec otool -L {} \;
. .travis/common/post-upload.sh

View File

@@ -98,6 +98,7 @@ add_library(common STATIC
microprofile.h
microprofileui.h
misc.cpp
multi_level_queue.h
page_table.cpp
page_table.h
param_package.cpp

View File

@@ -58,4 +58,43 @@ inline u64 CountLeadingZeroes64(u64 value) {
return __builtin_clzll(value);
}
#endif
#ifdef _MSC_VER
inline u32 CountTrailingZeroes32(u32 value) {
unsigned long trailing_zero = 0;
if (_BitScanForward(&trailing_zero, value) != 0) {
return trailing_zero;
}
return 32;
}
inline u64 CountTrailingZeroes64(u64 value) {
unsigned long trailing_zero = 0;
if (_BitScanForward64(&trailing_zero, value) != 0) {
return trailing_zero;
}
return 64;
}
#else
inline u32 CountTrailingZeroes32(u32 value) {
if (value == 0) {
return 32;
}
return __builtin_ctz(value);
}
inline u64 CountTrailingZeroes64(u64 value) {
if (value == 0) {
return 64;
}
return __builtin_ctzll(value);
}
#endif
} // namespace Common

View File

@@ -16,22 +16,22 @@ DetachedTasks::DetachedTasks() {
}
void DetachedTasks::WaitForAllTasks() {
std::unique_lock<std::mutex> lock(mutex);
std::unique_lock lock{mutex};
cv.wait(lock, [this]() { return count == 0; });
}
DetachedTasks::~DetachedTasks() {
std::unique_lock<std::mutex> lock(mutex);
std::unique_lock lock{mutex};
ASSERT(count == 0);
instance = nullptr;
}
void DetachedTasks::AddTask(std::function<void()> task) {
std::unique_lock<std::mutex> lock(instance->mutex);
std::unique_lock lock{instance->mutex};
++instance->count;
std::thread([task{std::move(task)}]() {
task();
std::unique_lock<std::mutex> lock(instance->mutex);
std::unique_lock lock{instance->mutex};
--instance->count;
std::notify_all_at_thread_exit(instance->cv, std::move(lock));
})

View File

@@ -46,12 +46,12 @@ public:
}
void AddBackend(std::unique_ptr<Backend> backend) {
std::lock_guard<std::mutex> lock(writing_mutex);
std::lock_guard lock{writing_mutex};
backends.push_back(std::move(backend));
}
void RemoveBackend(std::string_view backend_name) {
std::lock_guard<std::mutex> lock(writing_mutex);
std::lock_guard lock{writing_mutex};
const auto it =
std::remove_if(backends.begin(), backends.end(),
[&backend_name](const auto& i) { return backend_name == i->GetName(); });
@@ -80,7 +80,7 @@ private:
backend_thread = std::thread([&] {
Entry entry;
auto write_logs = [&](Entry& e) {
std::lock_guard<std::mutex> lock(writing_mutex);
std::lock_guard lock{writing_mutex};
for (const auto& backend : backends) {
backend->Write(e);
}

View File

@@ -0,0 +1,337 @@
// Copyright 2019 TuxSH
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <iterator>
#include <list>
#include <utility>
#include "common/bit_util.h"
#include "common/common_types.h"
namespace Common {
/**
* A MultiLevelQueue is a type of priority queue which has the following characteristics:
* - iteratable through each of its elements.
* - back can be obtained.
* - O(1) add, lookup (both front and back)
* - discrete priorities and a max of 64 priorities (limited domain)
* This type of priority queue is normaly used for managing threads within an scheduler
*/
template <typename T, std::size_t Depth>
class MultiLevelQueue {
public:
using value_type = T;
using reference = value_type&;
using const_reference = const value_type&;
using pointer = value_type*;
using const_pointer = const value_type*;
using difference_type = typename std::pointer_traits<pointer>::difference_type;
using size_type = std::size_t;
template <bool is_constant>
class iterator_impl {
public:
using iterator_category = std::bidirectional_iterator_tag;
using value_type = T;
using pointer = std::conditional_t<is_constant, T*, const T*>;
using reference = std::conditional_t<is_constant, const T&, T&>;
using difference_type = typename std::pointer_traits<pointer>::difference_type;
friend bool operator==(const iterator_impl& lhs, const iterator_impl& rhs) {
if (lhs.IsEnd() && rhs.IsEnd())
return true;
return std::tie(lhs.current_priority, lhs.it) == std::tie(rhs.current_priority, rhs.it);
}
friend bool operator!=(const iterator_impl& lhs, const iterator_impl& rhs) {
return !operator==(lhs, rhs);
}
reference operator*() const {
return *it;
}
pointer operator->() const {
return it.operator->();
}
iterator_impl& operator++() {
if (IsEnd()) {
return *this;
}
++it;
if (it == GetEndItForPrio()) {
u64 prios = mlq.used_priorities;
prios &= ~((1ULL << (current_priority + 1)) - 1);
if (prios == 0) {
current_priority = mlq.depth();
} else {
current_priority = CountTrailingZeroes64(prios);
it = GetBeginItForPrio();
}
}
return *this;
}
iterator_impl& operator--() {
if (IsEnd()) {
if (mlq.used_priorities != 0) {
current_priority = 63 - CountLeadingZeroes64(mlq.used_priorities);
it = GetEndItForPrio();
--it;
}
} else if (it == GetBeginItForPrio()) {
u64 prios = mlq.used_priorities;
prios &= (1ULL << current_priority) - 1;
if (prios != 0) {
current_priority = CountTrailingZeroes64(prios);
it = GetEndItForPrio();
--it;
}
} else {
--it;
}
return *this;
}
iterator_impl operator++(int) {
const iterator_impl v{*this};
++(*this);
return v;
}
iterator_impl operator--(int) {
const iterator_impl v{*this};
--(*this);
return v;
}
// allow implicit const->non-const
iterator_impl(const iterator_impl<false>& other)
: mlq(other.mlq), it(other.it), current_priority(other.current_priority) {}
iterator_impl(const iterator_impl<true>& other)
: mlq(other.mlq), it(other.it), current_priority(other.current_priority) {}
iterator_impl& operator=(const iterator_impl<false>& other) {
mlq = other.mlq;
it = other.it;
current_priority = other.current_priority;
return *this;
}
friend class iterator_impl<true>;
iterator_impl() = default;
private:
friend class MultiLevelQueue;
using container_ref =
std::conditional_t<is_constant, const MultiLevelQueue&, MultiLevelQueue&>;
using list_iterator = std::conditional_t<is_constant, typename std::list<T>::const_iterator,
typename std::list<T>::iterator>;
explicit iterator_impl(container_ref mlq, list_iterator it, u32 current_priority)
: mlq(mlq), it(it), current_priority(current_priority) {}
explicit iterator_impl(container_ref mlq, u32 current_priority)
: mlq(mlq), it(), current_priority(current_priority) {}
bool IsEnd() const {
return current_priority == mlq.depth();
}
list_iterator GetBeginItForPrio() const {
return mlq.levels[current_priority].begin();
}
list_iterator GetEndItForPrio() const {
return mlq.levels[current_priority].end();
}
container_ref mlq;
list_iterator it;
u32 current_priority;
};
using iterator = iterator_impl<false>;
using const_iterator = iterator_impl<true>;
void add(const T& element, u32 priority, bool send_back = true) {
if (send_back)
levels[priority].push_back(element);
else
levels[priority].push_front(element);
used_priorities |= 1ULL << priority;
}
void remove(const T& element, u32 priority) {
auto it = ListIterateTo(levels[priority], element);
if (it == levels[priority].end())
return;
levels[priority].erase(it);
if (levels[priority].empty()) {
used_priorities &= ~(1ULL << priority);
}
}
void adjust(const T& element, u32 old_priority, u32 new_priority, bool adjust_front = false) {
remove(element, old_priority);
add(element, new_priority, !adjust_front);
}
void adjust(const_iterator it, u32 old_priority, u32 new_priority, bool adjust_front = false) {
adjust(*it, old_priority, new_priority, adjust_front);
}
void transfer_to_front(const T& element, u32 priority, MultiLevelQueue& other) {
ListSplice(other.levels[priority], other.levels[priority].begin(), levels[priority],
ListIterateTo(levels[priority], element));
other.used_priorities |= 1ULL << priority;
if (levels[priority].empty()) {
used_priorities &= ~(1ULL << priority);
}
}
void transfer_to_front(const_iterator it, u32 priority, MultiLevelQueue& other) {
transfer_to_front(*it, priority, other);
}
void transfer_to_back(const T& element, u32 priority, MultiLevelQueue& other) {
ListSplice(other.levels[priority], other.levels[priority].end(), levels[priority],
ListIterateTo(levels[priority], element));
other.used_priorities |= 1ULL << priority;
if (levels[priority].empty()) {
used_priorities &= ~(1ULL << priority);
}
}
void transfer_to_back(const_iterator it, u32 priority, MultiLevelQueue& other) {
transfer_to_back(*it, priority, other);
}
void yield(u32 priority, std::size_t n = 1) {
ListShiftForward(levels[priority], n);
}
std::size_t depth() const {
return Depth;
}
std::size_t size(u32 priority) const {
return levels[priority].size();
}
std::size_t size() const {
u64 priorities = used_priorities;
std::size_t size = 0;
while (priorities != 0) {
const u64 current_priority = CountTrailingZeroes64(priorities);
size += levels[current_priority].size();
priorities &= ~(1ULL << current_priority);
}
return size;
}
bool empty() const {
return used_priorities == 0;
}
bool empty(u32 priority) const {
return (used_priorities & (1ULL << priority)) == 0;
}
u32 highest_priority_set(u32 max_priority = 0) const {
const u64 priorities =
max_priority == 0 ? used_priorities : (used_priorities & ~((1ULL << max_priority) - 1));
return priorities == 0 ? Depth : static_cast<u32>(CountTrailingZeroes64(priorities));
}
u32 lowest_priority_set(u32 min_priority = Depth - 1) const {
const u64 priorities = min_priority >= Depth - 1
? used_priorities
: (used_priorities & ((1ULL << (min_priority + 1)) - 1));
return priorities == 0 ? Depth : 63 - CountLeadingZeroes64(priorities);
}
const_iterator cbegin(u32 max_prio = 0) const {
const u32 priority = highest_priority_set(max_prio);
return priority == Depth ? cend()
: const_iterator{*this, levels[priority].cbegin(), priority};
}
const_iterator begin(u32 max_prio = 0) const {
return cbegin(max_prio);
}
iterator begin(u32 max_prio = 0) {
const u32 priority = highest_priority_set(max_prio);
return priority == Depth ? end() : iterator{*this, levels[priority].begin(), priority};
}
const_iterator cend(u32 min_prio = Depth - 1) const {
return min_prio == Depth - 1 ? const_iterator{*this, Depth} : cbegin(min_prio + 1);
}
const_iterator end(u32 min_prio = Depth - 1) const {
return cend(min_prio);
}
iterator end(u32 min_prio = Depth - 1) {
return min_prio == Depth - 1 ? iterator{*this, Depth} : begin(min_prio + 1);
}
T& front(u32 max_priority = 0) {
const u32 priority = highest_priority_set(max_priority);
return levels[priority == Depth ? 0 : priority].front();
}
const T& front(u32 max_priority = 0) const {
const u32 priority = highest_priority_set(max_priority);
return levels[priority == Depth ? 0 : priority].front();
}
T back(u32 min_priority = Depth - 1) {
const u32 priority = lowest_priority_set(min_priority); // intended
return levels[priority == Depth ? 63 : priority].back();
}
const T& back(u32 min_priority = Depth - 1) const {
const u32 priority = lowest_priority_set(min_priority); // intended
return levels[priority == Depth ? 63 : priority].back();
}
private:
using const_list_iterator = typename std::list<T>::const_iterator;
static void ListShiftForward(std::list<T>& list, const std::size_t shift = 1) {
if (shift >= list.size()) {
return;
}
const auto begin_range = list.begin();
const auto end_range = std::next(begin_range, shift);
list.splice(list.end(), list, begin_range, end_range);
}
static void ListSplice(std::list<T>& in_list, const_list_iterator position,
std::list<T>& out_list, const_list_iterator element) {
in_list.splice(position, out_list, element);
}
static const_list_iterator ListIterateTo(const std::list<T>& list, const T& element) {
auto it = list.cbegin();
while (it != list.cend() && *it != element) {
++it;
}
return it;
}
std::array<std::list<T>, Depth> levels;
u64 used_priorities = 0;
};
} // namespace Common

View File

@@ -15,7 +15,7 @@ namespace Common {
class Event {
public:
void Set() {
std::lock_guard<std::mutex> lk(mutex);
std::lock_guard lk{mutex};
if (!is_set) {
is_set = true;
condvar.notify_one();
@@ -23,14 +23,14 @@ public:
}
void Wait() {
std::unique_lock<std::mutex> lk(mutex);
std::unique_lock lk{mutex};
condvar.wait(lk, [&] { return is_set; });
is_set = false;
}
template <class Clock, class Duration>
bool WaitUntil(const std::chrono::time_point<Clock, Duration>& time) {
std::unique_lock<std::mutex> lk(mutex);
std::unique_lock lk{mutex};
if (!condvar.wait_until(lk, time, [this] { return is_set; }))
return false;
is_set = false;
@@ -38,7 +38,7 @@ public:
}
void Reset() {
std::unique_lock<std::mutex> lk(mutex);
std::unique_lock lk{mutex};
// no other action required, since wait loops on the predicate and any lingering signal will
// get cleared on the first iteration
is_set = false;
@@ -56,7 +56,7 @@ public:
/// Blocks until all "count" threads have called Sync()
void Sync() {
std::unique_lock<std::mutex> lk(mutex);
std::unique_lock lk{mutex};
const std::size_t current_generation = generation;
if (++waiting == count) {

View File

@@ -78,7 +78,7 @@ public:
T PopWait() {
if (Empty()) {
std::unique_lock<std::mutex> lock(cv_mutex);
std::unique_lock lock{cv_mutex};
cv.wait(lock, [this]() { return !Empty(); });
}
T t;
@@ -137,7 +137,7 @@ public:
template <typename Arg>
void Push(Arg&& t) {
std::lock_guard<std::mutex> lock(write_lock);
std::lock_guard lock{write_lock};
spsc_queue.Push(t);
}

View File

@@ -22,7 +22,7 @@
namespace Core {
void CpuBarrier::NotifyEnd() {
std::unique_lock<std::mutex> lock(mutex);
std::unique_lock lock{mutex};
end = true;
condition.notify_all();
}
@@ -34,7 +34,7 @@ bool CpuBarrier::Rendezvous() {
}
if (!end) {
std::unique_lock<std::mutex> lock(mutex);
std::unique_lock lock{mutex};
--cores_waiting;
if (!cores_waiting) {
@@ -131,7 +131,7 @@ void Cpu::Reschedule() {
reschedule_pending = false;
// Lock the global kernel mutex when we manipulate the HLE state
std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
std::lock_guard lock{HLE::g_hle_lock};
scheduler->Reschedule();
}

View File

@@ -30,7 +30,7 @@ private:
explicit Device(std::weak_ptr<TouchState>&& touch_state) : touch_state(touch_state) {}
std::tuple<float, float, bool> GetStatus() const override {
if (auto state = touch_state.lock()) {
std::lock_guard<std::mutex> guard(state->mutex);
std::lock_guard guard{state->mutex};
return std::make_tuple(state->touch_x, state->touch_y, state->touch_pressed);
}
return std::make_tuple(0.0f, 0.0f, false);
@@ -81,7 +81,7 @@ void EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) {
if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y))
return;
std::lock_guard<std::mutex> guard(touch_state->mutex);
std::lock_guard guard{touch_state->mutex};
touch_state->touch_x = static_cast<float>(framebuffer_x - framebuffer_layout.screen.left) /
(framebuffer_layout.screen.right - framebuffer_layout.screen.left);
touch_state->touch_y = static_cast<float>(framebuffer_y - framebuffer_layout.screen.top) /
@@ -91,7 +91,7 @@ void EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) {
}
void EmuWindow::TouchReleased() {
std::lock_guard<std::mutex> guard(touch_state->mutex);
std::lock_guard guard{touch_state->mutex};
touch_state->touch_pressed = false;
touch_state->touch_x = 0;
touch_state->touch_y = 0;

View File

@@ -26,7 +26,7 @@ void WakeThreads(const std::vector<SharedPtr<Thread>>& waiting_threads, s32 num_
// them all.
std::size_t last = waiting_threads.size();
if (num_to_wake > 0) {
last = num_to_wake;
last = std::min(last, static_cast<std::size_t>(num_to_wake));
}
// Signal the waiting threads.
@@ -90,9 +90,9 @@ ResultCode AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr a
// Determine the modified value depending on the waiting count.
s32 updated_value;
if (waiting_threads.empty()) {
updated_value = value - 1;
} else if (num_to_wake <= 0 || waiting_threads.size() <= static_cast<u32>(num_to_wake)) {
updated_value = value + 1;
} else if (num_to_wake <= 0 || waiting_threads.size() <= static_cast<u32>(num_to_wake)) {
updated_value = value - 1;
} else {
updated_value = value;
}

View File

@@ -5,7 +5,6 @@
#pragma once
#include <cstddef>
#include <memory>
#include <vector>
#include "common/common_types.h"
@@ -78,7 +77,7 @@ struct CodeSet final {
}
/// The overall data that backs this code set.
std::shared_ptr<std::vector<u8>> memory;
std::vector<u8> memory;
/// The segments that comprise this code set.
std::array<Segment, 3> segments;

View File

@@ -34,7 +34,7 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] s64 cycles_
const auto& system = Core::System::GetInstance();
// Lock the global kernel mutex when we enter the kernel HLE.
std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
std::lock_guard lock{HLE::g_hle_lock};
SharedPtr<Thread> thread =
system.Kernel().RetrieveThreadFromWakeupCallbackHandleTable(proper_handle);
@@ -62,7 +62,8 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] s64 cycles_
if (thread->GetMutexWaitAddress() != 0 || thread->GetCondVarWaitAddress() != 0 ||
thread->GetWaitHandle() != 0) {
ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex);
ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex ||
thread->GetStatus() == ThreadStatus::WaitCondVar);
thread->SetMutexWaitAddress(0);
thread->SetCondVarWaitAddress(0);
thread->SetWaitHandle(0);

View File

@@ -5,6 +5,7 @@
#include <algorithm>
#include <memory>
#include <random>
#include "common/alignment.h"
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/core.h"
@@ -75,6 +76,10 @@ SharedPtr<ResourceLimit> Process::GetResourceLimit() const {
return resource_limit;
}
u64 Process::GetTotalPhysicalMemoryUsed() const {
return vm_manager.GetCurrentHeapSize() + main_thread_stack_size + code_memory_size;
}
ResultCode Process::ClearSignalState() {
if (status == ProcessStatus::Exited) {
LOG_ERROR(Kernel, "called on a terminated process instance.");
@@ -107,14 +112,17 @@ ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) {
return handle_table.SetSize(capabilities.GetHandleTableSize());
}
void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) {
void Process::Run(VAddr entry_point, s32 main_thread_priority, u64 stack_size) {
// The kernel always ensures that the given stack size is page aligned.
main_thread_stack_size = Common::AlignUp(stack_size, Memory::PAGE_SIZE);
// Allocate and map the main thread stack
// TODO(bunnei): This is heap area that should be allocated by the kernel and not mapped as part
// of the user address space.
const VAddr mapping_address = vm_manager.GetTLSIORegionEndAddress() - main_thread_stack_size;
vm_manager
.MapMemoryBlock(vm_manager.GetTLSIORegionEndAddress() - stack_size,
std::make_shared<std::vector<u8>>(stack_size, 0), 0, stack_size,
MemoryState::Stack)
.MapMemoryBlock(mapping_address, std::make_shared<std::vector<u8>>(main_thread_stack_size),
0, main_thread_stack_size, MemoryState::Stack)
.Unwrap();
vm_manager.LogLayout();
@@ -210,11 +218,13 @@ void Process::FreeTLSSlot(VAddr tls_address) {
}
void Process::LoadModule(CodeSet module_, VAddr base_addr) {
const auto memory = std::make_shared<std::vector<u8>>(std::move(module_.memory));
const auto MapSegment = [&](const CodeSet::Segment& segment, VMAPermission permissions,
MemoryState memory_state) {
const auto vma = vm_manager
.MapMemoryBlock(segment.addr + base_addr, module_.memory,
segment.offset, segment.size, memory_state)
.MapMemoryBlock(segment.addr + base_addr, memory, segment.offset,
segment.size, memory_state)
.Unwrap();
vm_manager.Reprotect(vma, permissions);
};
@@ -224,6 +234,8 @@ void Process::LoadModule(CodeSet module_, VAddr base_addr) {
MapSegment(module_.RODataSegment(), VMAPermission::Read, MemoryState::CodeData);
MapSegment(module_.DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeData);
code_memory_size += module_.memory.size();
// Clear instruction cache in CPU JIT
system.InvalidateCpuInstructionCaches();
}

View File

@@ -186,6 +186,9 @@ public:
return random_entropy.at(index);
}
/// Retrieves the total physical memory used by this process in bytes.
u64 GetTotalPhysicalMemoryUsed() const;
/// Clears the signaled state of the process if and only if it's signaled.
///
/// @pre The process must not be already terminated. If this is called on a
@@ -210,7 +213,7 @@ public:
/**
* Applies address space changes and launches the process main thread.
*/
void Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size);
void Run(VAddr entry_point, s32 main_thread_priority, u64 stack_size);
/**
* Prepares a process for termination by stopping all of its threads
@@ -247,6 +250,12 @@ private:
/// Memory manager for this process.
Kernel::VMManager vm_manager;
/// Size of the main thread's stack in bytes.
u64 main_thread_stack_size = 0;
/// Size of the loaded code memory in bytes.
u64 code_memory_size = 0;
/// Current status of the process
ProcessStatus status;

View File

@@ -29,8 +29,8 @@ Scheduler::~Scheduler() {
}
bool Scheduler::HaveReadyThreads() const {
std::lock_guard<std::mutex> lock(scheduler_mutex);
return ready_queue.get_first() != nullptr;
std::lock_guard lock{scheduler_mutex};
return !ready_queue.empty();
}
Thread* Scheduler::GetCurrentThread() const {
@@ -46,22 +46,27 @@ Thread* Scheduler::PopNextReadyThread() {
Thread* thread = GetCurrentThread();
if (thread && thread->GetStatus() == ThreadStatus::Running) {
if (ready_queue.empty()) {
return thread;
}
// We have to do better than the current thread.
// This call returns null when that's not possible.
next = ready_queue.pop_first_better(thread->GetPriority());
if (!next) {
// Otherwise just keep going with the current thread
next = ready_queue.front();
if (next == nullptr || next->GetPriority() >= thread->GetPriority()) {
next = thread;
}
} else {
next = ready_queue.pop_first();
if (ready_queue.empty()) {
return nullptr;
}
next = ready_queue.front();
}
return next;
}
void Scheduler::SwitchContext(Thread* new_thread) {
Thread* const previous_thread = GetCurrentThread();
Thread* previous_thread = GetCurrentThread();
Process* const previous_process = system.Kernel().CurrentProcess();
UpdateLastContextSwitchTime(previous_thread, previous_process);
@@ -75,7 +80,7 @@ void Scheduler::SwitchContext(Thread* new_thread) {
if (previous_thread->GetStatus() == ThreadStatus::Running) {
// This is only the case when a reschedule is triggered without the current thread
// yielding execution (i.e. an event triggered, system core time-sliced, etc)
ready_queue.push_front(previous_thread->GetPriority(), previous_thread);
ready_queue.add(previous_thread, previous_thread->GetPriority(), false);
previous_thread->SetStatus(ThreadStatus::Ready);
}
}
@@ -90,7 +95,7 @@ void Scheduler::SwitchContext(Thread* new_thread) {
current_thread = new_thread;
ready_queue.remove(new_thread->GetPriority(), new_thread);
ready_queue.remove(new_thread, new_thread->GetPriority());
new_thread->SetStatus(ThreadStatus::Running);
auto* const thread_owner_process = current_thread->GetOwnerProcess();
@@ -127,7 +132,7 @@ void Scheduler::UpdateLastContextSwitchTime(Thread* thread, Process* process) {
}
void Scheduler::Reschedule() {
std::lock_guard<std::mutex> lock(scheduler_mutex);
std::lock_guard lock{scheduler_mutex};
Thread* cur = GetCurrentThread();
Thread* next = PopNextReadyThread();
@@ -143,51 +148,54 @@ void Scheduler::Reschedule() {
SwitchContext(next);
}
void Scheduler::AddThread(SharedPtr<Thread> thread, u32 priority) {
std::lock_guard<std::mutex> lock(scheduler_mutex);
void Scheduler::AddThread(SharedPtr<Thread> thread) {
std::lock_guard lock{scheduler_mutex};
thread_list.push_back(std::move(thread));
ready_queue.prepare(priority);
}
void Scheduler::RemoveThread(Thread* thread) {
std::lock_guard<std::mutex> lock(scheduler_mutex);
std::lock_guard lock{scheduler_mutex};
thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread),
thread_list.end());
}
void Scheduler::ScheduleThread(Thread* thread, u32 priority) {
std::lock_guard<std::mutex> lock(scheduler_mutex);
std::lock_guard lock{scheduler_mutex};
ASSERT(thread->GetStatus() == ThreadStatus::Ready);
ready_queue.push_back(priority, thread);
ready_queue.add(thread, priority);
}
void Scheduler::UnscheduleThread(Thread* thread, u32 priority) {
std::lock_guard<std::mutex> lock(scheduler_mutex);
std::lock_guard lock{scheduler_mutex};
ASSERT(thread->GetStatus() == ThreadStatus::Ready);
ready_queue.remove(priority, thread);
ready_queue.remove(thread, priority);
}
void Scheduler::SetThreadPriority(Thread* thread, u32 priority) {
std::lock_guard<std::mutex> lock(scheduler_mutex);
std::lock_guard lock{scheduler_mutex};
if (thread->GetPriority() == priority) {
return;
}
// If thread was ready, adjust queues
if (thread->GetStatus() == ThreadStatus::Ready)
ready_queue.move(thread, thread->GetPriority(), priority);
else
ready_queue.prepare(priority);
ready_queue.adjust(thread, thread->GetPriority(), priority);
}
Thread* Scheduler::GetNextSuggestedThread(u32 core, u32 maximum_priority) const {
std::lock_guard<std::mutex> lock(scheduler_mutex);
std::lock_guard lock{scheduler_mutex};
const u32 mask = 1U << core;
return ready_queue.get_first_filter([mask, maximum_priority](Thread const* thread) {
return (thread->GetAffinityMask() & mask) != 0 && thread->GetPriority() < maximum_priority;
});
for (auto* thread : ready_queue) {
if ((thread->GetAffinityMask() & mask) != 0 && thread->GetPriority() < maximum_priority) {
return thread;
}
}
return nullptr;
}
void Scheduler::YieldWithoutLoadBalancing(Thread* thread) {

View File

@@ -7,7 +7,7 @@
#include <mutex>
#include <vector>
#include "common/common_types.h"
#include "common/thread_queue_list.h"
#include "common/multi_level_queue.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/thread.h"
@@ -38,7 +38,7 @@ public:
u64 GetLastContextSwitchTicks() const;
/// Adds a new thread to the scheduler
void AddThread(SharedPtr<Thread> thread, u32 priority);
void AddThread(SharedPtr<Thread> thread);
/// Removes a thread from the scheduler
void RemoveThread(Thread* thread);
@@ -156,7 +156,7 @@ private:
std::vector<SharedPtr<Thread>> thread_list;
/// Lists only ready thread ids.
Common::ThreadQueueList<Thread*, THREADPRIO_LOWEST + 1> ready_queue;
Common::MultiLevelQueue<Thread*, THREADPRIO_LOWEST + 1> ready_queue;
SharedPtr<Thread> current_thread = nullptr;

View File

@@ -709,7 +709,7 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
HeapRegionBaseAddr = 4,
HeapRegionSize = 5,
TotalMemoryUsage = 6,
TotalHeapUsage = 7,
TotalPhysicalMemoryUsed = 7,
IsCurrentProcessBeingDebugged = 8,
RegisterResourceLimit = 9,
IdleTickCount = 10,
@@ -745,7 +745,7 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
case GetInfoType::NewMapRegionBaseAddr:
case GetInfoType::NewMapRegionSize:
case GetInfoType::TotalMemoryUsage:
case GetInfoType::TotalHeapUsage:
case GetInfoType::TotalPhysicalMemoryUsed:
case GetInfoType::IsVirtualAddressMemoryEnabled:
case GetInfoType::PersonalMmHeapUsage:
case GetInfoType::TitleId:
@@ -805,8 +805,8 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
*result = process->VMManager().GetTotalMemoryUsage();
return RESULT_SUCCESS;
case GetInfoType::TotalHeapUsage:
*result = process->VMManager().GetCurrentHeapSize();
case GetInfoType::TotalPhysicalMemoryUsed:
*result = process->GetTotalPhysicalMemoryUsed();
return RESULT_SUCCESS;
case GetInfoType::IsVirtualAddressMemoryEnabled:
@@ -1353,7 +1353,7 @@ static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_var
current_thread->SetCondVarWaitAddress(condition_variable_addr);
current_thread->SetMutexWaitAddress(mutex_addr);
current_thread->SetWaitHandle(thread_handle);
current_thread->SetStatus(ThreadStatus::WaitMutex);
current_thread->SetStatus(ThreadStatus::WaitCondVar);
current_thread->InvalidateWakeupCallback();
current_thread->WakeAfterDelay(nano_seconds);
@@ -1397,10 +1397,10 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
// them all.
std::size_t last = waiting_threads.size();
if (target != -1)
last = target;
last = std::min(waiting_threads.size(), static_cast<std::size_t>(target));
// If there are no threads waiting on this condition variable, just exit
if (last > waiting_threads.size())
if (last == 0)
return RESULT_SUCCESS;
for (std::size_t index = 0; index < last; ++index) {
@@ -1408,6 +1408,9 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
ASSERT(thread->GetCondVarWaitAddress() == condition_variable_addr);
// liberate Cond Var Thread.
thread->SetCondVarWaitAddress(0);
std::size_t current_core = Core::System::GetInstance().CurrentCoreIndex();
auto& monitor = Core::System::GetInstance().Monitor();
@@ -1426,10 +1429,9 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
}
} while (!monitor.ExclusiveWrite32(current_core, thread->GetMutexWaitAddress(),
thread->GetWaitHandle()));
if (mutex_val == 0) {
// We were able to acquire the mutex, resume this thread.
ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex);
ASSERT(thread->GetStatus() == ThreadStatus::WaitCondVar);
thread->ResumeFromWait();
auto* const lock_owner = thread->GetLockOwner();
@@ -1439,8 +1441,8 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
thread->SetLockOwner(nullptr);
thread->SetMutexWaitAddress(0);
thread->SetCondVarWaitAddress(0);
thread->SetWaitHandle(0);
Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule();
} else {
// Atomically signal that the mutex now has a waiting thread.
do {
@@ -1459,12 +1461,11 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
auto owner = handle_table.Get<Thread>(owner_handle);
ASSERT(owner);
ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex);
ASSERT(thread->GetStatus() == ThreadStatus::WaitCondVar);
thread->InvalidateWakeupCallback();
thread->SetStatus(ThreadStatus::WaitMutex);
owner->AddMutexWaiter(thread);
Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule();
}
}
@@ -2137,7 +2138,7 @@ void CallSVC(u32 immediate) {
MICROPROFILE_SCOPE(Kernel_SVC);
// Lock the global kernel mutex when we enter the kernel HLE.
std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
std::lock_guard lock{HLE::g_hle_lock};
const FunctionDef* info = GetSVCInfo(immediate);
if (info) {

View File

@@ -105,6 +105,7 @@ void Thread::ResumeFromWait() {
case ThreadStatus::WaitSleep:
case ThreadStatus::WaitIPC:
case ThreadStatus::WaitMutex:
case ThreadStatus::WaitCondVar:
case ThreadStatus::WaitArb:
break;
@@ -198,7 +199,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name
thread->callback_handle = kernel.ThreadWakeupCallbackHandleTable().Create(thread).Unwrap();
thread->owner_process = &owner_process;
thread->scheduler = &system.Scheduler(processor_id);
thread->scheduler->AddThread(thread, priority);
thread->scheduler->AddThread(thread);
thread->tls_address = thread->owner_process->MarkNextAvailableTLSSlotAsUsed(*thread);
// TODO(peachum): move to ScheduleThread() when scheduler is added so selected core is used
@@ -351,7 +352,7 @@ void Thread::ChangeScheduler() {
if (*new_processor_id != processor_id) {
// Remove thread from previous core's scheduler
scheduler->RemoveThread(this);
next_scheduler.AddThread(this, current_priority);
next_scheduler.AddThread(this);
}
processor_id = *new_processor_id;

View File

@@ -51,7 +51,8 @@ enum class ThreadStatus {
WaitIPC, ///< Waiting for the reply from an IPC request
WaitSynchAny, ///< Waiting due to WaitSynch1 or WaitSynchN with wait_all = false
WaitSynchAll, ///< Waiting due to WaitSynchronizationN with wait_all = true
WaitMutex, ///< Waiting due to an ArbitrateLock/WaitProcessWideKey svc
WaitMutex, ///< Waiting due to an ArbitrateLock svc
WaitCondVar, ///< Waiting due to an WaitProcessWideKey svc
WaitArb, ///< Waiting due to a SignalToAddress/WaitForAddress svc
Dormant, ///< Created but not yet made ready
Dead ///< Run to completion, or forcefully terminated

View File

@@ -25,21 +25,34 @@ Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
Module::Interface::~Interface() = default;
struct FatalInfo {
std::array<u64_le, 31> registers{}; // TODO(ogniK): See if this actually is registers or
// not(find a game which has non zero valeus)
u64_le unk0{};
u64_le unk1{};
u64_le unk2{};
u64_le unk3{};
u64_le unk4{};
u64_le unk5{};
u64_le unk6{};
enum class Architecture : s32 {
AArch64,
AArch32,
};
const char* ArchAsString() const {
return arch == Architecture::AArch64 ? "AArch64" : "AArch32";
}
std::array<u64_le, 31> registers{};
u64_le sp{};
u64_le pc{};
u64_le pstate{};
u64_le afsr0{};
u64_le afsr1{};
u64_le esr{};
u64_le far{};
std::array<u64_le, 32> backtrace{};
u64_le unk7{};
u64_le unk8{};
u64_le program_entry_point{};
// Bit flags that indicate which registers have been set with values
// for this context. The service itself uses these to determine which
// registers to specifically print out.
u64_le set_flags{};
u32_le backtrace_size{};
u32_le unk9{};
Architecture arch{};
u32_le unk10{}; // TODO(ogniK): Is this even used or is it just padding?
};
static_assert(sizeof(FatalInfo) == 0x250, "FatalInfo is an invalid size");
@@ -52,36 +65,36 @@ enum class FatalType : u32 {
static void GenerateErrorReport(ResultCode error_code, const FatalInfo& info) {
const auto title_id = Core::CurrentProcess()->GetTitleID();
std::string crash_report =
fmt::format("Yuzu {}-{} crash report\n"
"Title ID: {:016x}\n"
"Result: 0x{:X} ({:04}-{:04d})\n"
"\n",
Common::g_scm_branch, Common::g_scm_desc, title_id, error_code.raw,
2000 + static_cast<u32>(error_code.module.Value()),
static_cast<u32>(error_code.description.Value()), info.unk8, info.unk7);
std::string crash_report = fmt::format(
"Yuzu {}-{} crash report\n"
"Title ID: {:016x}\n"
"Result: 0x{:X} ({:04}-{:04d})\n"
"Set flags: 0x{:16X}\n"
"Program entry point: 0x{:16X}\n"
"\n",
Common::g_scm_branch, Common::g_scm_desc, title_id, error_code.raw,
2000 + static_cast<u32>(error_code.module.Value()),
static_cast<u32>(error_code.description.Value()), info.set_flags, info.program_entry_point);
if (info.backtrace_size != 0x0) {
crash_report += "Registers:\n";
// TODO(ogniK): This is just a guess, find a game which actually has non zero values
for (size_t i = 0; i < info.registers.size(); i++) {
crash_report +=
fmt::format(" X[{:02d}]: {:016x}\n", i, info.registers[i]);
}
crash_report += fmt::format(" Unknown 0: {:016x}\n", info.unk0);
crash_report += fmt::format(" Unknown 1: {:016x}\n", info.unk1);
crash_report += fmt::format(" Unknown 2: {:016x}\n", info.unk2);
crash_report += fmt::format(" Unknown 3: {:016x}\n", info.unk3);
crash_report += fmt::format(" Unknown 4: {:016x}\n", info.unk4);
crash_report += fmt::format(" Unknown 5: {:016x}\n", info.unk5);
crash_report += fmt::format(" Unknown 6: {:016x}\n", info.unk6);
crash_report += fmt::format(" SP: {:016x}\n", info.sp);
crash_report += fmt::format(" PC: {:016x}\n", info.pc);
crash_report += fmt::format(" PSTATE: {:016x}\n", info.pstate);
crash_report += fmt::format(" AFSR0: {:016x}\n", info.afsr0);
crash_report += fmt::format(" AFSR1: {:016x}\n", info.afsr1);
crash_report += fmt::format(" ESR: {:016x}\n", info.esr);
crash_report += fmt::format(" FAR: {:016x}\n", info.far);
crash_report += "\nBacktrace:\n";
for (size_t i = 0; i < info.backtrace_size; i++) {
crash_report +=
fmt::format(" Backtrace[{:02d}]: {:016x}\n", i, info.backtrace[i]);
}
crash_report += fmt::format("\nUnknown 7: 0x{:016x}\n", info.unk7);
crash_report += fmt::format("Unknown 8: 0x{:016x}\n", info.unk8);
crash_report += fmt::format("Unknown 9: 0x{:016x}\n", info.unk9);
crash_report += fmt::format("Architecture: {}\n", info.ArchAsString());
crash_report += fmt::format("Unknown 10: 0x{:016x}\n", info.unk10);
}
@@ -125,13 +138,13 @@ static void ThrowFatalError(ResultCode error_code, FatalType fatal_type, const F
case FatalType::ErrorReport:
GenerateErrorReport(error_code, info);
break;
};
}
}
void Module::Interface::ThrowFatal(Kernel::HLERequestContext& ctx) {
LOG_ERROR(Service_Fatal, "called");
IPC::RequestParser rp{ctx};
auto error_code = rp.Pop<ResultCode>();
const auto error_code = rp.Pop<ResultCode>();
ThrowFatalError(error_code, FatalType::ErrorScreen, {});
IPC::ResponseBuilder rb{ctx, 2};
@@ -141,8 +154,8 @@ void Module::Interface::ThrowFatal(Kernel::HLERequestContext& ctx) {
void Module::Interface::ThrowFatalWithPolicy(Kernel::HLERequestContext& ctx) {
LOG_ERROR(Service_Fatal, "called");
IPC::RequestParser rp(ctx);
auto error_code = rp.Pop<ResultCode>();
auto fatal_type = rp.PopEnum<FatalType>();
const auto error_code = rp.Pop<ResultCode>();
const auto fatal_type = rp.PopEnum<FatalType>();
ThrowFatalError(error_code, fatal_type, {}); // No info is passed with ThrowFatalWithPolicy
IPC::ResponseBuilder rb{ctx, 2};
@@ -152,9 +165,9 @@ void Module::Interface::ThrowFatalWithPolicy(Kernel::HLERequestContext& ctx) {
void Module::Interface::ThrowFatalWithCpuContext(Kernel::HLERequestContext& ctx) {
LOG_ERROR(Service_Fatal, "called");
IPC::RequestParser rp(ctx);
auto error_code = rp.Pop<ResultCode>();
auto fatal_type = rp.PopEnum<FatalType>();
auto fatal_info = ctx.ReadBuffer();
const auto error_code = rp.Pop<ResultCode>();
const auto fatal_type = rp.PopEnum<FatalType>();
const auto fatal_info = ctx.ReadBuffer();
FatalInfo info{};
ASSERT_MSG(fatal_info.size() == sizeof(FatalInfo), "Invalid fatal info buffer size!");

View File

@@ -150,7 +150,7 @@ private:
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.PushRaw<u8>(Settings::values.enable_nfc);
rb.PushRaw<u8>(true);
}
void GetStateOld(Kernel::HLERequestContext& ctx) {

View File

@@ -335,7 +335,7 @@ void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) {
}
bool Module::Interface::LoadAmiibo(const std::vector<u8>& buffer) {
std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
std::lock_guard lock{HLE::g_hle_lock};
if (buffer.size() < sizeof(AmiiboFile)) {
return false;
}

View File

@@ -341,7 +341,7 @@ Kernel::CodeSet ElfReader::LoadInto(VAddr vaddr) {
}
codeset.entrypoint = base_addr + header->e_entry;
codeset.memory = std::make_shared<std::vector<u8>>(std::move(program_image));
codeset.memory = std::move(program_image);
LOG_DEBUG(Loader, "Done loading.");

View File

@@ -187,7 +187,7 @@ static bool LoadNroImpl(Kernel::Process& process, const std::vector<u8>& data,
program_image.resize(static_cast<u32>(program_image.size()) + bss_size);
// Load codeset for current process
codeset.memory = std::make_shared<std::vector<u8>>(std::move(program_image));
codeset.memory = std::move(program_image);
process.LoadModule(std::move(codeset), load_base);
// Register module with GDBStub

View File

@@ -161,7 +161,7 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
}
// Load codeset for current process
codeset.memory = std::make_shared<std::vector<u8>>(std::move(program_image));
codeset.memory = std::move(program_image);
process.LoadModule(std::move(codeset), load_base);
// Register module with GDBStub

View File

@@ -18,13 +18,13 @@ using std::chrono::microseconds;
namespace Core {
void PerfStats::BeginSystemFrame() {
std::lock_guard<std::mutex> lock(object_mutex);
std::lock_guard lock{object_mutex};
frame_begin = Clock::now();
}
void PerfStats::EndSystemFrame() {
std::lock_guard<std::mutex> lock(object_mutex);
std::lock_guard lock{object_mutex};
auto frame_end = Clock::now();
accumulated_frametime += frame_end - frame_begin;
@@ -35,13 +35,13 @@ void PerfStats::EndSystemFrame() {
}
void PerfStats::EndGameFrame() {
std::lock_guard<std::mutex> lock(object_mutex);
std::lock_guard lock{object_mutex};
game_frames += 1;
}
PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us) {
std::lock_guard<std::mutex> lock(object_mutex);
std::lock_guard lock{object_mutex};
const auto now = Clock::now();
// Walltime elapsed since stats were reset
@@ -67,7 +67,7 @@ PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us
}
double PerfStats::GetLastFrameTimeScale() {
std::lock_guard<std::mutex> lock(object_mutex);
std::lock_guard lock{object_mutex};
constexpr double FRAME_LENGTH = 1.0 / 60;
return duration_cast<DoubleSecs>(previous_frame_length).count() / FRAME_LENGTH;

View File

@@ -82,7 +82,6 @@ void LogSetting(const std::string& name, const T& value) {
void LogSettings() {
LOG_INFO(Config, "yuzu Configuration:");
LogSetting("System_UseDockedMode", Settings::values.use_docked_mode);
LogSetting("System_EnableNfc", Settings::values.enable_nfc);
LogSetting("System_RngSeed", Settings::values.rng_seed.value_or(0));
LogSetting("System_CurrentUser", Settings::values.current_user);
LogSetting("System_LanguageIndex", Settings::values.language_index);

View File

@@ -349,7 +349,6 @@ struct TouchscreenInput {
struct Values {
// System
bool use_docked_mode;
bool enable_nfc;
std::optional<u32> rng_seed;
// Measured in seconds since epoch
std::optional<std::chrono::seconds> custom_rtc;

View File

@@ -36,18 +36,18 @@ struct KeyButtonPair {
class KeyButtonList {
public:
void AddKeyButton(int key_code, KeyButton* key_button) {
std::lock_guard<std::mutex> guard(mutex);
std::lock_guard guard{mutex};
list.push_back(KeyButtonPair{key_code, key_button});
}
void RemoveKeyButton(const KeyButton* key_button) {
std::lock_guard<std::mutex> guard(mutex);
std::lock_guard guard{mutex};
list.remove_if(
[key_button](const KeyButtonPair& pair) { return pair.key_button == key_button; });
}
void ChangeKeyStatus(int key_code, bool pressed) {
std::lock_guard<std::mutex> guard(mutex);
std::lock_guard guard{mutex};
for (const KeyButtonPair& pair : list) {
if (pair.key_code == key_code)
pair.key_button->status.store(pressed);
@@ -55,7 +55,7 @@ public:
}
void ChangeAllKeyStatus(bool pressed) {
std::lock_guard<std::mutex> guard(mutex);
std::lock_guard guard{mutex};
for (const KeyButtonPair& pair : list) {
pair.key_button->status.store(pressed);
}

View File

@@ -39,7 +39,7 @@ public:
void Tilt(int x, int y) {
auto mouse_move = Common::MakeVec(x, y) - mouse_origin;
if (is_tilting) {
std::lock_guard<std::mutex> guard(tilt_mutex);
std::lock_guard guard{tilt_mutex};
if (mouse_move.x == 0 && mouse_move.y == 0) {
tilt_angle = 0;
} else {
@@ -51,13 +51,13 @@ public:
}
void EndTilt() {
std::lock_guard<std::mutex> guard(tilt_mutex);
std::lock_guard guard{tilt_mutex};
tilt_angle = 0;
is_tilting = false;
}
std::tuple<Common::Vec3<float>, Common::Vec3<float>> GetStatus() {
std::lock_guard<std::mutex> guard(status_mutex);
std::lock_guard guard{status_mutex};
return status;
}
@@ -93,7 +93,7 @@ private:
old_q = q;
{
std::lock_guard<std::mutex> guard(tilt_mutex);
std::lock_guard guard{tilt_mutex};
// Find the quaternion describing current 3DS tilting
q = Common::MakeQuaternion(
@@ -115,7 +115,7 @@ private:
// Update the sensor state
{
std::lock_guard<std::mutex> guard(status_mutex);
std::lock_guard guard{status_mutex};
status = std::make_tuple(gravity, angular_rate);
}
}

View File

@@ -55,22 +55,22 @@ public:
: guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, deleter} {}
void SetButton(int button, bool value) {
std::lock_guard<std::mutex> lock(mutex);
std::lock_guard lock{mutex};
state.buttons[button] = value;
}
bool GetButton(int button) const {
std::lock_guard<std::mutex> lock(mutex);
std::lock_guard lock{mutex};
return state.buttons.at(button);
}
void SetAxis(int axis, Sint16 value) {
std::lock_guard<std::mutex> lock(mutex);
std::lock_guard lock{mutex};
state.axes[axis] = value;
}
float GetAxis(int axis) const {
std::lock_guard<std::mutex> lock(mutex);
std::lock_guard lock{mutex};
return state.axes.at(axis) / 32767.0f;
}
@@ -92,12 +92,12 @@ public:
}
void SetHat(int hat, Uint8 direction) {
std::lock_guard<std::mutex> lock(mutex);
std::lock_guard lock{mutex};
state.hats[hat] = direction;
}
bool GetHatDirection(int hat, Uint8 direction) const {
std::lock_guard<std::mutex> lock(mutex);
std::lock_guard lock{mutex};
return (state.hats.at(hat) & direction) != 0;
}
/**
@@ -140,7 +140,7 @@ private:
* Get the nth joystick with the corresponding GUID
*/
std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickByGUID(const std::string& guid, int port) {
std::lock_guard<std::mutex> lock(joystick_map_mutex);
std::lock_guard lock{joystick_map_mutex};
const auto it = joystick_map.find(guid);
if (it != joystick_map.end()) {
while (it->second.size() <= port) {
@@ -161,7 +161,8 @@ std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickByGUID(const std::string& g
std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickBySDLID(SDL_JoystickID sdl_id) {
auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id);
const std::string guid = GetGUID(sdl_joystick);
std::lock_guard<std::mutex> lock(joystick_map_mutex);
std::lock_guard lock{joystick_map_mutex};
auto map_it = joystick_map.find(guid);
if (map_it != joystick_map.end()) {
auto vec_it = std::find_if(map_it->second.begin(), map_it->second.end(),
@@ -198,8 +199,9 @@ void SDLState::InitJoystick(int joystick_index) {
LOG_ERROR(Input, "failed to open joystick {}", joystick_index);
return;
}
std::string guid = GetGUID(sdl_joystick);
std::lock_guard<std::mutex> lock(joystick_map_mutex);
const std::string guid = GetGUID(sdl_joystick);
std::lock_guard lock{joystick_map_mutex};
if (joystick_map.find(guid) == joystick_map.end()) {
auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick);
joystick_map[guid].emplace_back(std::move(joystick));
@@ -221,7 +223,7 @@ void SDLState::CloseJoystick(SDL_Joystick* sdl_joystick) {
std::string guid = GetGUID(sdl_joystick);
std::shared_ptr<SDLJoystick> joystick;
{
std::lock_guard<std::mutex> lock(joystick_map_mutex);
std::lock_guard lock{joystick_map_mutex};
// This call to guid is safe since the joystick is guaranteed to be in the map
auto& joystick_guid_list = joystick_map[guid];
const auto joystick_it =
@@ -274,7 +276,7 @@ void SDLState::HandleGameControllerEvent(const SDL_Event& event) {
}
void SDLState::CloseJoysticks() {
std::lock_guard<std::mutex> lock(joystick_map_mutex);
std::lock_guard lock{joystick_map_mutex};
joystick_map.clear();
}

View File

@@ -1,5 +1,7 @@
add_executable(tests
common/bit_field.cpp
common/bit_utils.cpp
common/multi_level_queue.cpp
common/param_package.cpp
common/ring_buffer.cpp
core/arm/arm_test_common.cpp

View File

@@ -0,0 +1,23 @@
// Copyright 2017 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <catch2/catch.hpp>
#include <math.h>
#include "common/bit_util.h"
namespace Common {
TEST_CASE("BitUtils::CountTrailingZeroes", "[common]") {
REQUIRE(Common::CountTrailingZeroes32(0) == 32);
REQUIRE(Common::CountTrailingZeroes64(0) == 64);
REQUIRE(Common::CountTrailingZeroes32(9) == 0);
REQUIRE(Common::CountTrailingZeroes32(8) == 3);
REQUIRE(Common::CountTrailingZeroes32(0x801000) == 12);
REQUIRE(Common::CountTrailingZeroes64(9) == 0);
REQUIRE(Common::CountTrailingZeroes64(8) == 3);
REQUIRE(Common::CountTrailingZeroes64(0x801000) == 12);
REQUIRE(Common::CountTrailingZeroes64(0x801000000000UL) == 36);
}
} // namespace Common

View File

@@ -0,0 +1,55 @@
// Copyright 2019 Yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <catch2/catch.hpp>
#include <math.h>
#include "common/common_types.h"
#include "common/multi_level_queue.h"
namespace Common {
TEST_CASE("MultiLevelQueue", "[common]") {
std::array<f32, 8> values = {0.0, 5.0, 1.0, 9.0, 8.0, 2.0, 6.0, 7.0};
Common::MultiLevelQueue<f32, 64> mlq;
REQUIRE(mlq.empty());
mlq.add(values[2], 2);
mlq.add(values[7], 7);
mlq.add(values[3], 3);
mlq.add(values[4], 4);
mlq.add(values[0], 0);
mlq.add(values[5], 5);
mlq.add(values[6], 6);
mlq.add(values[1], 1);
u32 index = 0;
bool all_set = true;
for (auto& f : mlq) {
all_set &= (f == values[index]);
index++;
}
REQUIRE(all_set);
REQUIRE(!mlq.empty());
f32 v = 8.0;
mlq.add(v, 2);
v = -7.0;
mlq.add(v, 2, false);
REQUIRE(mlq.front(2) == -7.0);
mlq.yield(2);
REQUIRE(mlq.front(2) == values[2]);
REQUIRE(mlq.back(2) == -7.0);
REQUIRE(mlq.empty(8));
v = 10.0;
mlq.add(v, 8);
mlq.adjust(v, 8, 9);
REQUIRE(mlq.front(9) == v);
REQUIRE(mlq.empty(8));
REQUIRE(!mlq.empty(9));
mlq.adjust(values[0], 0, 9);
REQUIRE(mlq.highest_priority_set() == 1);
REQUIRE(mlq.lowest_priority_set() == 9);
mlq.remove(values[1], 1);
REQUIRE(mlq.highest_priority_set() == 2);
REQUIRE(mlq.empty(1));
}
} // namespace Common

View File

@@ -46,6 +46,8 @@ add_library(video_core STATIC
renderer_opengl/gl_rasterizer_cache.h
renderer_opengl/gl_resource_manager.cpp
renderer_opengl/gl_resource_manager.h
renderer_opengl/gl_sampler_cache.cpp
renderer_opengl/gl_sampler_cache.h
renderer_opengl/gl_shader_cache.cpp
renderer_opengl/gl_shader_cache.h
renderer_opengl/gl_shader_decompiler.cpp
@@ -67,6 +69,8 @@ add_library(video_core STATIC
renderer_opengl/renderer_opengl.h
renderer_opengl/utils.cpp
renderer_opengl/utils.h
sampler_cache.cpp
sampler_cache.h
shader/decode/arithmetic.cpp
shader/decode/arithmetic_immediate.cpp
shader/decode/bfe.cpp

View File

@@ -10,7 +10,7 @@ namespace Tegra {
void DebugContext::DoOnEvent(Event event, void* data) {
{
std::unique_lock<std::mutex> lock(breakpoint_mutex);
std::unique_lock lock{breakpoint_mutex};
// TODO(Subv): Commit the rasterizer's caches so framebuffers, render targets, etc. will
// show on debug widgets
@@ -32,7 +32,7 @@ void DebugContext::DoOnEvent(Event event, void* data) {
void DebugContext::Resume() {
{
std::lock_guard<std::mutex> lock(breakpoint_mutex);
std::lock_guard lock{breakpoint_mutex};
// Tell all observers that we are about to resume
for (auto& breakpoint_observer : breakpoint_observers) {

View File

@@ -40,7 +40,7 @@ public:
/// Constructs the object such that it observes events of the given DebugContext.
explicit BreakPointObserver(std::shared_ptr<DebugContext> debug_context)
: context_weak(debug_context) {
std::unique_lock<std::mutex> lock(debug_context->breakpoint_mutex);
std::unique_lock lock{debug_context->breakpoint_mutex};
debug_context->breakpoint_observers.push_back(this);
}
@@ -48,7 +48,7 @@ public:
auto context = context_weak.lock();
if (context) {
{
std::unique_lock<std::mutex> lock(context->breakpoint_mutex);
std::unique_lock lock{context->breakpoint_mutex};
context->breakpoint_observers.remove(this);
}

View File

@@ -52,8 +52,8 @@ static void RunThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_p
}
ThreadManager::ThreadManager(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher)
: renderer{renderer}, dma_pusher{dma_pusher}, thread{RunThread, std::ref(renderer),
std::ref(dma_pusher), std::ref(state)} {}
: renderer{renderer}, thread{RunThread, std::ref(renderer), std::ref(dma_pusher),
std::ref(state)} {}
ThreadManager::~ThreadManager() {
// Notify GPU thread that a shutdown is pending

View File

@@ -4,10 +4,8 @@
#pragma once
#include <array>
#include <atomic>
#include <condition_variable>
#include <memory>
#include <mutex>
#include <optional>
#include <thread>
@@ -97,13 +95,13 @@ struct SynchState final {
std::condition_variable frames_condition;
void IncrementFramesCounter() {
std::lock_guard<std::mutex> lock{frames_mutex};
std::lock_guard lock{frames_mutex};
++queued_frame_count;
}
void DecrementFramesCounter() {
{
std::lock_guard<std::mutex> lock{frames_mutex};
std::lock_guard lock{frames_mutex};
--queued_frame_count;
if (queued_frame_count) {
@@ -115,7 +113,7 @@ struct SynchState final {
void WaitForFrames() {
{
std::lock_guard<std::mutex> lock{frames_mutex};
std::lock_guard lock{frames_mutex};
if (!queued_frame_count) {
return;
}
@@ -123,14 +121,14 @@ struct SynchState final {
// Wait for the GPU to be idle (all commands to be executed)
{
std::unique_lock<std::mutex> lock{frames_mutex};
std::unique_lock lock{frames_mutex};
frames_condition.wait(lock, [this] { return !queued_frame_count; });
}
}
void SignalCommands() {
{
std::unique_lock<std::mutex> lock{commands_mutex};
std::unique_lock lock{commands_mutex};
if (queue.Empty()) {
return;
}
@@ -140,7 +138,7 @@ struct SynchState final {
}
void WaitForCommands() {
std::unique_lock<std::mutex> lock{commands_mutex};
std::unique_lock lock{commands_mutex};
commands_condition.wait(lock, [this] { return !queue.Empty(); });
}
@@ -177,7 +175,6 @@ private:
private:
SynchState state;
VideoCore::RendererBase& renderer;
Tegra::DmaPusher& dma_pusher;
std::thread thread;
std::thread::id thread_id;
};

View File

@@ -71,8 +71,8 @@ private:
bool is_registered{}; ///< Whether the object is currently registered with the cache
bool is_dirty{}; ///< Whether the object is dirty (out of sync with guest memory)
u64 last_modified_ticks{}; ///< When the object was last modified, used for in-order flushing
CacheAddr cache_addr{}; ///< Cache address memory, unique from emulated virtual address space
const u8* host_ptr{}; ///< Pointer to the memory backing this cached region
CacheAddr cache_addr{}; ///< Cache address memory, unique from emulated virtual address space
};
template <class T>
@@ -84,7 +84,7 @@ public:
/// Write any cached resources overlapping the specified region back to memory
void FlushRegion(CacheAddr addr, std::size_t size) {
std::lock_guard<std::recursive_mutex> lock{mutex};
std::lock_guard lock{mutex};
const auto& objects{GetSortedObjectsFromRegion(addr, size)};
for (auto& object : objects) {
@@ -94,7 +94,7 @@ public:
/// Mark the specified region as being invalidated
void InvalidateRegion(CacheAddr addr, u64 size) {
std::lock_guard<std::recursive_mutex> lock{mutex};
std::lock_guard lock{mutex};
const auto& objects{GetSortedObjectsFromRegion(addr, size)};
for (auto& object : objects) {
@@ -108,7 +108,7 @@ public:
/// Invalidates everything in the cache
void InvalidateAll() {
std::lock_guard<std::recursive_mutex> lock{mutex};
std::lock_guard lock{mutex};
while (interval_cache.begin() != interval_cache.end()) {
Unregister(*interval_cache.begin()->second.begin());
@@ -133,7 +133,7 @@ protected:
/// Register an object into the cache
virtual void Register(const T& object) {
std::lock_guard<std::recursive_mutex> lock{mutex};
std::lock_guard lock{mutex};
object->SetIsRegistered(true);
interval_cache.add({GetInterval(object), ObjectSet{object}});
@@ -143,7 +143,7 @@ protected:
/// Unregisters an object from the cache
virtual void Unregister(const T& object) {
std::lock_guard<std::recursive_mutex> lock{mutex};
std::lock_guard lock{mutex};
object->SetIsRegistered(false);
rasterizer.UpdatePagesCachedCount(object->GetCpuAddr(), object->GetSizeInBytes(), -1);
@@ -153,14 +153,14 @@ protected:
/// Returns a ticks counter used for tracking when cached objects were last modified
u64 GetModifiedTicks() {
std::lock_guard<std::recursive_mutex> lock{mutex};
std::lock_guard lock{mutex};
return ++modified_ticks;
}
/// Flushes the specified object, updating appropriate cache state as needed
void FlushObject(const T& object) {
std::lock_guard<std::recursive_mutex> lock{mutex};
std::lock_guard lock{mutex};
if (!object->IsDirty()) {
return;

View File

@@ -15,8 +15,8 @@ namespace OpenGL {
CachedBufferEntry::CachedBufferEntry(VAddr cpu_addr, std::size_t size, GLintptr offset,
std::size_t alignment, u8* host_ptr)
: cpu_addr{cpu_addr}, size{size}, offset{offset}, alignment{alignment}, RasterizerCacheObject{
host_ptr} {}
: RasterizerCacheObject{host_ptr}, cpu_addr{cpu_addr}, size{size}, offset{offset},
alignment{alignment} {}
OGLBufferCache::OGLBufferCache(RasterizerOpenGL& rasterizer, std::size_t size)
: RasterizerCache{rasterizer}, stream_buffer(size, true) {}

View File

@@ -15,7 +15,7 @@
namespace OpenGL {
CachedGlobalRegion::CachedGlobalRegion(VAddr cpu_addr, u32 size, u8* host_ptr)
: cpu_addr{cpu_addr}, size{size}, RasterizerCacheObject{host_ptr} {
: RasterizerCacheObject{host_ptr}, cpu_addr{cpu_addr}, size{size} {
buffer.Create();
// Bind and unbind the buffer so it gets allocated by the driver
glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffer.handle);

View File

@@ -100,17 +100,9 @@ struct FramebufferCacheKey {
}
};
RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, Core::System& system,
ScreenInfo& info)
: res_cache{*this}, shader_cache{*this, system}, global_cache{*this},
emu_window{window}, system{system}, screen_info{info},
buffer_cache(*this, STREAM_BUFFER_SIZE) {
// Create sampler objects
for (std::size_t i = 0; i < texture_samplers.size(); ++i) {
texture_samplers[i].Create();
state.texture_units[i].sampler = texture_samplers[i].sampler.handle;
}
RasterizerOpenGL::RasterizerOpenGL(Core::System& system, ScreenInfo& info)
: res_cache{*this}, shader_cache{*this, system}, global_cache{*this}, system{system},
screen_info{info}, buffer_cache(*this, STREAM_BUFFER_SIZE) {
OpenGLState::ApplyDefaultState();
shader_program_manager = std::make_unique<GLShader::ProgramManager>();
@@ -809,92 +801,6 @@ bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& config,
return true;
}
void RasterizerOpenGL::SamplerInfo::Create() {
sampler.Create();
mag_filter = Tegra::Texture::TextureFilter::Linear;
min_filter = Tegra::Texture::TextureFilter::Linear;
wrap_u = Tegra::Texture::WrapMode::Wrap;
wrap_v = Tegra::Texture::WrapMode::Wrap;
wrap_p = Tegra::Texture::WrapMode::Wrap;
use_depth_compare = false;
depth_compare_func = Tegra::Texture::DepthCompareFunc::Never;
// OpenGL's default is GL_LINEAR_MIPMAP_LINEAR
glSamplerParameteri(sampler.handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glSamplerParameteri(sampler.handle, GL_TEXTURE_COMPARE_FUNC, GL_NEVER);
// Other attributes have correct defaults
}
void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntry& config) {
const GLuint sampler_id = sampler.handle;
if (mag_filter != config.mag_filter) {
mag_filter = config.mag_filter;
glSamplerParameteri(
sampler_id, GL_TEXTURE_MAG_FILTER,
MaxwellToGL::TextureFilterMode(mag_filter, Tegra::Texture::TextureMipmapFilter::None));
}
if (min_filter != config.min_filter || mipmap_filter != config.mipmap_filter) {
min_filter = config.min_filter;
mipmap_filter = config.mipmap_filter;
glSamplerParameteri(sampler_id, GL_TEXTURE_MIN_FILTER,
MaxwellToGL::TextureFilterMode(min_filter, mipmap_filter));
}
if (wrap_u != config.wrap_u) {
wrap_u = config.wrap_u;
glSamplerParameteri(sampler_id, GL_TEXTURE_WRAP_S, MaxwellToGL::WrapMode(wrap_u));
}
if (wrap_v != config.wrap_v) {
wrap_v = config.wrap_v;
glSamplerParameteri(sampler_id, GL_TEXTURE_WRAP_T, MaxwellToGL::WrapMode(wrap_v));
}
if (wrap_p != config.wrap_p) {
wrap_p = config.wrap_p;
glSamplerParameteri(sampler_id, GL_TEXTURE_WRAP_R, MaxwellToGL::WrapMode(wrap_p));
}
if (const bool enabled = config.depth_compare_enabled == 1; use_depth_compare != enabled) {
use_depth_compare = enabled;
glSamplerParameteri(sampler_id, GL_TEXTURE_COMPARE_MODE,
use_depth_compare ? GL_COMPARE_REF_TO_TEXTURE : GL_NONE);
}
if (depth_compare_func != config.depth_compare_func) {
depth_compare_func = config.depth_compare_func;
glSamplerParameteri(sampler_id, GL_TEXTURE_COMPARE_FUNC,
MaxwellToGL::DepthCompareFunc(depth_compare_func));
}
if (const auto new_border_color = config.GetBorderColor(); border_color != new_border_color) {
border_color = new_border_color;
glSamplerParameterfv(sampler_id, GL_TEXTURE_BORDER_COLOR, border_color.data());
}
if (const float anisotropic = config.GetMaxAnisotropy(); max_anisotropic != anisotropic) {
max_anisotropic = anisotropic;
if (GLAD_GL_ARB_texture_filter_anisotropic) {
glSamplerParameterf(sampler_id, GL_TEXTURE_MAX_ANISOTROPY, max_anisotropic);
} else if (GLAD_GL_EXT_texture_filter_anisotropic) {
glSamplerParameterf(sampler_id, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropic);
}
}
if (const float min = config.GetMinLod(); min_lod != min) {
min_lod = min;
glSamplerParameterf(sampler_id, GL_TEXTURE_MIN_LOD, min_lod);
}
if (const float max = config.GetMaxLod(); max_lod != max) {
max_lod = max;
glSamplerParameterf(sampler_id, GL_TEXTURE_MAX_LOD, max_lod);
}
if (const float bias = config.GetLodBias(); lod_bias != bias) {
lod_bias = bias;
glSamplerParameterf(sampler_id, GL_TEXTURE_LOD_BIAS, lod_bias);
}
}
void RasterizerOpenGL::SetupConstBuffers(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage,
const Shader& shader, GLuint program_handle,
BaseBindings base_bindings) {
@@ -990,7 +896,7 @@ void RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, const Shader& s
const auto texture = maxwell3d.GetStageTexture(stage, entry.GetOffset());
const u32 current_bindpoint = base_bindings.sampler + bindpoint;
texture_samplers[current_bindpoint].SyncWithConfig(texture.tsc);
state.texture_units[current_bindpoint].sampler = sampler_cache.GetSampler(texture.tsc);
if (Surface surface = res_cache.GetTextureSurface(texture, entry); surface) {
state.texture_units[current_bindpoint].texture =

View File

@@ -28,6 +28,7 @@
#include "video_core/renderer_opengl/gl_primitive_assembler.h"
#include "video_core/renderer_opengl/gl_rasterizer_cache.h"
#include "video_core/renderer_opengl/gl_resource_manager.h"
#include "video_core/renderer_opengl/gl_sampler_cache.h"
#include "video_core/renderer_opengl/gl_shader_cache.h"
#include "video_core/renderer_opengl/gl_shader_gen.h"
#include "video_core/renderer_opengl/gl_shader_manager.h"
@@ -50,8 +51,7 @@ struct FramebufferCacheKey;
class RasterizerOpenGL : public VideoCore::RasterizerInterface {
public:
explicit RasterizerOpenGL(Core::Frontend::EmuWindow& window, Core::System& system,
ScreenInfo& info);
explicit RasterizerOpenGL(Core::System& system, ScreenInfo& info);
~RasterizerOpenGL() override;
void DrawArrays() override;
@@ -81,34 +81,6 @@ public:
"The maximum size of a global memory must be a multiple of the size of float");
private:
class SamplerInfo {
public:
OGLSampler sampler;
/// Creates the sampler object, initializing its state so that it's in sync with the
/// SamplerInfo struct.
void Create();
/// Syncs the sampler object with the config, updating any necessary state.
void SyncWithConfig(const Tegra::Texture::TSCEntry& info);
private:
Tegra::Texture::TextureFilter mag_filter = Tegra::Texture::TextureFilter::Nearest;
Tegra::Texture::TextureFilter min_filter = Tegra::Texture::TextureFilter::Nearest;
Tegra::Texture::TextureMipmapFilter mipmap_filter =
Tegra::Texture::TextureMipmapFilter::None;
Tegra::Texture::WrapMode wrap_u = Tegra::Texture::WrapMode::ClampToEdge;
Tegra::Texture::WrapMode wrap_v = Tegra::Texture::WrapMode::ClampToEdge;
Tegra::Texture::WrapMode wrap_p = Tegra::Texture::WrapMode::ClampToEdge;
bool use_depth_compare = false;
Tegra::Texture::DepthCompareFunc depth_compare_func =
Tegra::Texture::DepthCompareFunc::Always;
GLvec4 border_color = {};
float min_lod = 0.0f;
float max_lod = 16.0f;
float lod_bias = 0.0f;
float max_anisotropic = 1.0f;
};
struct FramebufferConfigState {
bool using_color_fb{};
bool using_depth_fb{};
@@ -213,8 +185,8 @@ private:
RasterizerCacheOpenGL res_cache;
ShaderCacheOpenGL shader_cache;
GlobalRegionCacheOpenGL global_cache;
SamplerCacheOpenGL sampler_cache;
Core::Frontend::EmuWindow& emu_window;
Core::System& system;
ScreenInfo& screen_info;
@@ -229,8 +201,6 @@ private:
FramebufferConfigState current_framebuffer_config_state;
std::pair<bool, bool> current_depth_stencil_usage{};
std::array<SamplerInfo, Tegra::Engines::Maxwell3D::Regs::NumTextureSamplers> texture_samplers;
static constexpr std::size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024;
OGLBufferCache buffer_cache;
PrimitiveAssembler primitive_assembler{buffer_cache};

View File

@@ -562,8 +562,8 @@ void RasterizerCacheOpenGL::CopySurface(const Surface& src_surface, const Surfac
}
CachedSurface::CachedSurface(const SurfaceParams& params)
: params{params}, gl_target{SurfaceTargetToGL(params.target)},
cached_size_in_bytes{params.size_in_bytes}, RasterizerCacheObject{params.host_ptr} {
: RasterizerCacheObject{params.host_ptr}, params{params},
gl_target{SurfaceTargetToGL(params.target)}, cached_size_in_bytes{params.size_in_bytes} {
const auto optional_cpu_addr{
Core::System::GetInstance().GPU().MemoryManager().GpuToCpuAddress(params.gpu_addr)};

View File

@@ -0,0 +1,52 @@
// Copyright 2019 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/logging/log.h"
#include "video_core/renderer_opengl/gl_resource_manager.h"
#include "video_core/renderer_opengl/gl_sampler_cache.h"
#include "video_core/renderer_opengl/maxwell_to_gl.h"
namespace OpenGL {
SamplerCacheOpenGL::SamplerCacheOpenGL() = default;
SamplerCacheOpenGL::~SamplerCacheOpenGL() = default;
OGLSampler SamplerCacheOpenGL::CreateSampler(const Tegra::Texture::TSCEntry& tsc) const {
OGLSampler sampler;
sampler.Create();
const GLuint sampler_id{sampler.handle};
glSamplerParameteri(
sampler_id, GL_TEXTURE_MAG_FILTER,
MaxwellToGL::TextureFilterMode(tsc.mag_filter, Tegra::Texture::TextureMipmapFilter::None));
glSamplerParameteri(sampler_id, GL_TEXTURE_MIN_FILTER,
MaxwellToGL::TextureFilterMode(tsc.min_filter, tsc.mipmap_filter));
glSamplerParameteri(sampler_id, GL_TEXTURE_WRAP_S, MaxwellToGL::WrapMode(tsc.wrap_u));
glSamplerParameteri(sampler_id, GL_TEXTURE_WRAP_T, MaxwellToGL::WrapMode(tsc.wrap_v));
glSamplerParameteri(sampler_id, GL_TEXTURE_WRAP_R, MaxwellToGL::WrapMode(tsc.wrap_p));
glSamplerParameteri(sampler_id, GL_TEXTURE_COMPARE_MODE,
tsc.depth_compare_enabled == 1 ? GL_COMPARE_REF_TO_TEXTURE : GL_NONE);
glSamplerParameteri(sampler_id, GL_TEXTURE_COMPARE_FUNC,
MaxwellToGL::DepthCompareFunc(tsc.depth_compare_func));
glSamplerParameterfv(sampler_id, GL_TEXTURE_BORDER_COLOR, tsc.GetBorderColor().data());
glSamplerParameterf(sampler_id, GL_TEXTURE_MIN_LOD, tsc.GetMinLod());
glSamplerParameterf(sampler_id, GL_TEXTURE_MAX_LOD, tsc.GetMaxLod());
glSamplerParameterf(sampler_id, GL_TEXTURE_LOD_BIAS, tsc.GetLodBias());
if (GLAD_GL_ARB_texture_filter_anisotropic) {
glSamplerParameterf(sampler_id, GL_TEXTURE_MAX_ANISOTROPY, tsc.GetMaxAnisotropy());
} else if (GLAD_GL_EXT_texture_filter_anisotropic) {
glSamplerParameterf(sampler_id, GL_TEXTURE_MAX_ANISOTROPY_EXT, tsc.GetMaxAnisotropy());
} else if (tsc.GetMaxAnisotropy() != 1) {
LOG_WARNING(Render_OpenGL, "Anisotropy not supported by host GPU driver");
}
return sampler;
}
GLuint SamplerCacheOpenGL::ToSamplerType(const OGLSampler& sampler) const {
return sampler.handle;
}
} // namespace OpenGL

View File

@@ -0,0 +1,25 @@
// Copyright 2019 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <glad/glad.h>
#include "video_core/renderer_opengl/gl_resource_manager.h"
#include "video_core/sampler_cache.h"
namespace OpenGL {
class SamplerCacheOpenGL final : public VideoCommon::SamplerCache<GLuint, OGLSampler> {
public:
explicit SamplerCacheOpenGL();
~SamplerCacheOpenGL();
protected:
OGLSampler CreateSampler(const Tegra::Texture::TSCEntry& tsc) const;
GLuint ToSamplerType(const OGLSampler& sampler) const;
};
} // namespace OpenGL

View File

@@ -215,9 +215,9 @@ CachedShader::CachedShader(VAddr cpu_addr, u64 unique_identifier,
Maxwell::ShaderProgram program_type, ShaderDiskCacheOpenGL& disk_cache,
const PrecompiledPrograms& precompiled_programs,
ProgramCode&& program_code, ProgramCode&& program_code_b, u8* host_ptr)
: host_ptr{host_ptr}, cpu_addr{cpu_addr}, unique_identifier{unique_identifier},
program_type{program_type}, disk_cache{disk_cache},
precompiled_programs{precompiled_programs}, RasterizerCacheObject{host_ptr} {
: RasterizerCacheObject{host_ptr}, host_ptr{host_ptr}, cpu_addr{cpu_addr},
unique_identifier{unique_identifier}, program_type{program_type}, disk_cache{disk_cache},
precompiled_programs{precompiled_programs} {
const std::size_t code_size = CalculateProgramSize(program_code);
const std::size_t code_size_b =
@@ -245,9 +245,9 @@ CachedShader::CachedShader(VAddr cpu_addr, u64 unique_identifier,
Maxwell::ShaderProgram program_type, ShaderDiskCacheOpenGL& disk_cache,
const PrecompiledPrograms& precompiled_programs,
GLShader::ProgramResult result, u8* host_ptr)
: cpu_addr{cpu_addr}, unique_identifier{unique_identifier}, program_type{program_type},
disk_cache{disk_cache}, precompiled_programs{precompiled_programs}, RasterizerCacheObject{
host_ptr} {
: RasterizerCacheObject{host_ptr}, cpu_addr{cpu_addr}, unique_identifier{unique_identifier},
program_type{program_type}, disk_cache{disk_cache}, precompiled_programs{
precompiled_programs} {
code = std::move(result.first);
entries = result.second;

View File

@@ -266,7 +266,7 @@ void RendererOpenGL::CreateRasterizer() {
}
// Initialize sRGB Usage
OpenGLState::ClearsRGBUsed();
rasterizer = std::make_unique<RasterizerOpenGL>(render_window, system, screen_info);
rasterizer = std::make_unique<RasterizerOpenGL>(system, screen_info);
}
void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture,

View File

@@ -19,8 +19,8 @@ namespace Vulkan {
CachedBufferEntry::CachedBufferEntry(VAddr cpu_addr, std::size_t size, u64 offset,
std::size_t alignment, u8* host_ptr)
: cpu_addr{cpu_addr}, size{size}, offset{offset}, alignment{alignment}, RasterizerCacheObject{
host_ptr} {}
: RasterizerCacheObject{host_ptr}, cpu_addr{cpu_addr}, size{size}, offset{offset},
alignment{alignment} {}
VKBufferCache::VKBufferCache(Tegra::MemoryManager& tegra_memory_manager,
VideoCore::RasterizerInterface& rasterizer, const VKDevice& device,

View File

@@ -7,7 +7,6 @@
#include <unordered_map>
#include "common/assert.h"
#include "common/cityhash.h"
#include "video_core/renderer_vulkan/declarations.h"
#include "video_core/renderer_vulkan/maxwell_to_vk.h"
#include "video_core/renderer_vulkan/vk_sampler_cache.h"
@@ -28,39 +27,20 @@ static std::optional<vk::BorderColor> TryConvertBorderColor(std::array<float, 4>
}
}
std::size_t SamplerCacheKey::Hash() const {
static_assert(sizeof(raw) % sizeof(u64) == 0);
return static_cast<std::size_t>(
Common::CityHash64(reinterpret_cast<const char*>(raw.data()), sizeof(raw) / sizeof(u64)));
}
bool SamplerCacheKey::operator==(const SamplerCacheKey& rhs) const {
return raw == rhs.raw;
}
VKSamplerCache::VKSamplerCache(const VKDevice& device) : device{device} {}
VKSamplerCache::~VKSamplerCache() = default;
vk::Sampler VKSamplerCache::GetSampler(const Tegra::Texture::TSCEntry& tsc) {
const auto [entry, is_cache_miss] = cache.try_emplace(SamplerCacheKey{tsc});
auto& sampler = entry->second;
if (is_cache_miss) {
sampler = CreateSampler(tsc);
}
return *sampler;
}
UniqueSampler VKSamplerCache::CreateSampler(const Tegra::Texture::TSCEntry& tsc) const {
const float max_anisotropy{tsc.GetMaxAnisotropy()};
const bool has_anisotropy{max_anisotropy > 1.0f};
UniqueSampler VKSamplerCache::CreateSampler(const Tegra::Texture::TSCEntry& tsc) {
const float max_anisotropy = tsc.GetMaxAnisotropy();
const bool has_anisotropy = max_anisotropy > 1.0f;
const auto border_color = tsc.GetBorderColor();
const auto vk_border_color = TryConvertBorderColor(border_color);
const auto border_color{tsc.GetBorderColor()};
const auto vk_border_color{TryConvertBorderColor(border_color)};
UNIMPLEMENTED_IF_MSG(!vk_border_color, "Unimplemented border color {} {} {} {}",
border_color[0], border_color[1], border_color[2], border_color[3]);
constexpr bool unnormalized_coords = false;
constexpr bool unnormalized_coords{false};
const vk::SamplerCreateInfo sampler_ci(
{}, MaxwellToVK::Sampler::Filter(tsc.mag_filter),
@@ -73,9 +53,13 @@ UniqueSampler VKSamplerCache::CreateSampler(const Tegra::Texture::TSCEntry& tsc)
tsc.GetMaxLod(), vk_border_color.value_or(vk::BorderColor::eFloatTransparentBlack),
unnormalized_coords);
const auto& dld = device.GetDispatchLoader();
const auto dev = device.GetLogical();
const auto& dld{device.GetDispatchLoader()};
const auto dev{device.GetLogical()};
return dev.createSamplerUnique(sampler_ci, nullptr, dld);
}
vk::Sampler VKSamplerCache::ToSamplerType(const UniqueSampler& sampler) const {
return *sampler;
}
} // namespace Vulkan

View File

@@ -8,49 +8,25 @@
#include "common/common_types.h"
#include "video_core/renderer_vulkan/declarations.h"
#include "video_core/sampler_cache.h"
#include "video_core/textures/texture.h"
namespace Vulkan {
class VKDevice;
struct SamplerCacheKey final : public Tegra::Texture::TSCEntry {
std::size_t Hash() const;
bool operator==(const SamplerCacheKey& rhs) const;
bool operator!=(const SamplerCacheKey& rhs) const {
return !operator==(rhs);
}
};
} // namespace Vulkan
namespace std {
template <>
struct hash<Vulkan::SamplerCacheKey> {
std::size_t operator()(const Vulkan::SamplerCacheKey& k) const noexcept {
return k.Hash();
}
};
} // namespace std
namespace Vulkan {
class VKSamplerCache {
class VKSamplerCache final : public VideoCommon::SamplerCache<vk::Sampler, UniqueSampler> {
public:
explicit VKSamplerCache(const VKDevice& device);
~VKSamplerCache();
vk::Sampler GetSampler(const Tegra::Texture::TSCEntry& tsc);
protected:
UniqueSampler CreateSampler(const Tegra::Texture::TSCEntry& tsc) const;
vk::Sampler ToSamplerType(const UniqueSampler& sampler) const;
private:
UniqueSampler CreateSampler(const Tegra::Texture::TSCEntry& tsc);
const VKDevice& device;
std::unordered_map<SamplerCacheKey, UniqueSampler> cache;
};
} // namespace Vulkan

View File

@@ -0,0 +1,21 @@
// Copyright 2019 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/cityhash.h"
#include "common/common_types.h"
#include "video_core/sampler_cache.h"
namespace VideoCommon {
std::size_t SamplerCacheKey::Hash() const {
static_assert(sizeof(raw) % sizeof(u64) == 0);
return static_cast<std::size_t>(
Common::CityHash64(reinterpret_cast<const char*>(raw.data()), sizeof(raw) / sizeof(u64)));
}
bool SamplerCacheKey::operator==(const SamplerCacheKey& rhs) const {
return raw == rhs.raw;
}
} // namespace VideoCommon

View File

@@ -0,0 +1,60 @@
// Copyright 2019 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <cstddef>
#include <unordered_map>
#include "video_core/textures/texture.h"
namespace VideoCommon {
struct SamplerCacheKey final : public Tegra::Texture::TSCEntry {
std::size_t Hash() const;
bool operator==(const SamplerCacheKey& rhs) const;
bool operator!=(const SamplerCacheKey& rhs) const {
return !operator==(rhs);
}
};
} // namespace VideoCommon
namespace std {
template <>
struct hash<VideoCommon::SamplerCacheKey> {
std::size_t operator()(const VideoCommon::SamplerCacheKey& k) const noexcept {
return k.Hash();
}
};
} // namespace std
namespace VideoCommon {
template <typename SamplerType, typename SamplerStorageType>
class SamplerCache {
public:
SamplerType GetSampler(const Tegra::Texture::TSCEntry& tsc) {
const auto [entry, is_cache_miss] = cache.try_emplace(SamplerCacheKey{tsc});
auto& sampler = entry->second;
if (is_cache_miss) {
sampler = CreateSampler(tsc);
}
return ToSamplerType(sampler);
}
protected:
virtual SamplerStorageType CreateSampler(const Tegra::Texture::TSCEntry& tsc) const = 0;
virtual SamplerType ToSamplerType(const SamplerStorageType& sampler) const = 0;
private:
std::unordered_map<SamplerCacheKey, SamplerStorageType> cache;
};
} // namespace VideoCommon

View File

@@ -24,7 +24,7 @@ constexpr u32 TIMEOUT_SECONDS = 30;
struct Client::Impl {
Impl(std::string host, std::string username, std::string token)
: host{std::move(host)}, username{std::move(username)}, token{std::move(token)} {
std::lock_guard<std::mutex> lock(jwt_cache.mutex);
std::lock_guard lock{jwt_cache.mutex};
if (this->username == jwt_cache.username && this->token == jwt_cache.token) {
jwt = jwt_cache.jwt;
}
@@ -151,7 +151,7 @@ struct Client::Impl {
if (result.result_code != Common::WebResult::Code::Success) {
LOG_ERROR(WebService, "UpdateJWT failed");
} else {
std::lock_guard<std::mutex> lock(jwt_cache.mutex);
std::lock_guard lock{jwt_cache.mutex};
jwt_cache.username = username;
jwt_cache.token = token;
jwt_cache.jwt = jwt = result.returned_data;

View File

@@ -163,6 +163,6 @@ void QtProfileSelector::SelectProfile(
void QtProfileSelector::MainWindowFinishedSelection(std::optional<Service::Account::UUID> uuid) {
// Acquire the HLE mutex
std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
std::lock_guard lock{HLE::g_hle_lock};
callback(uuid);
}

View File

@@ -141,12 +141,12 @@ void QtSoftwareKeyboard::SendTextCheckDialog(std::u16string error_message,
void QtSoftwareKeyboard::MainWindowFinishedText(std::optional<std::u16string> text) {
// Acquire the HLE mutex
std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
std::lock_guard lock{HLE::g_hle_lock};
text_output(text);
}
void QtSoftwareKeyboard::MainWindowFinishedCheckDialog() {
// Acquire the HLE mutex
std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
std::lock_guard lock{HLE::g_hle_lock};
finished_check();
}

View File

@@ -104,12 +104,12 @@ void QtWebBrowser::OpenPage(std::string_view url, std::function<void()> unpack_r
void QtWebBrowser::MainWindowUnpackRomFS() {
// Acquire the HLE mutex
std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
std::lock_guard lock{HLE::g_hle_lock};
unpack_romfs_callback();
}
void QtWebBrowser::MainWindowFinishedBrowsing() {
// Acquire the HLE mutex
std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
std::lock_guard lock{HLE::g_hle_lock};
finished_callback();
}

View File

@@ -67,7 +67,7 @@ void EmuThread::run() {
was_active = false;
} else {
std::unique_lock<std::mutex> lock(running_mutex);
std::unique_lock lock{running_mutex};
running_cv.wait(lock, [this] { return IsRunning() || exec_step || stop_run; });
}
}

View File

@@ -53,7 +53,7 @@ public:
* @note This function is thread-safe
*/
void SetRunning(bool running) {
std::unique_lock<std::mutex> lock(running_mutex);
std::unique_lock lock{running_mutex};
this->running = running;
lock.unlock();
running_cv.notify_all();

View File

@@ -413,7 +413,6 @@ void Config::ReadValues() {
qt_config->beginGroup("System");
Settings::values.use_docked_mode = ReadSetting("use_docked_mode", false).toBool();
Settings::values.enable_nfc = ReadSetting("enable_nfc", true).toBool();
Settings::values.current_user =
std::clamp<int>(ReadSetting("current_user", 0).toInt(), 0, Service::Account::MAX_USERS - 1);
@@ -675,7 +674,6 @@ void Config::SaveValues() {
qt_config->beginGroup("System");
WriteSetting("use_docked_mode", Settings::values.use_docked_mode, false);
WriteSetting("enable_nfc", Settings::values.enable_nfc, true);
WriteSetting("current_user", Settings::values.current_user, 0);
WriteSetting("language_index", Settings::values.language_index, 1);

View File

@@ -33,7 +33,6 @@ void ConfigureGeneral::setConfiguration() {
ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot);
ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme));
ui->use_cpu_jit->setChecked(Settings::values.use_cpu_jit);
ui->enable_nfc->setChecked(Settings::values.enable_nfc);
}
void ConfigureGeneral::PopulateHotkeyList(const HotkeyRegistry& registry) {
@@ -48,5 +47,4 @@ void ConfigureGeneral::applyConfiguration() {
ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString();
Settings::values.use_cpu_jit = ui->use_cpu_jit->isChecked();
Settings::values.enable_nfc = ui->enable_nfc->isChecked();
}

View File

@@ -70,26 +70,6 @@
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="EmulationGroupBox">
<property name="title">
<string>Emulation</string>
</property>
<layout class="QHBoxLayout" name="EmulationHorizontalLayout">
<item>
<layout class="QVBoxLayout" name="EmulationVerticalLayout">
<item>
<widget class="QCheckBox" name="enable_nfc">
<property name="text">
<string>Enable NFC</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="theme_group_box">
<property name="title">

View File

@@ -234,6 +234,9 @@ QString WaitTreeThread::GetText() const {
case Kernel::ThreadStatus::WaitMutex:
status = tr("waiting for mutex");
break;
case Kernel::ThreadStatus::WaitCondVar:
status = tr("waiting for condition variable");
break;
case Kernel::ThreadStatus::WaitArb:
status = tr("waiting for address arbiter");
break;
@@ -269,6 +272,7 @@ QColor WaitTreeThread::GetColor() const {
case Kernel::ThreadStatus::WaitSynchAll:
case Kernel::ThreadStatus::WaitSynchAny:
case Kernel::ThreadStatus::WaitMutex:
case Kernel::ThreadStatus::WaitCondVar:
case Kernel::ThreadStatus::WaitArb:
return QColor(Qt::GlobalColor::red);
case Kernel::ThreadStatus::Dormant:

View File

@@ -319,7 +319,6 @@ void Config::ReadValues() {
// System
Settings::values.use_docked_mode = sdl2_config->GetBoolean("System", "use_docked_mode", false);
Settings::values.enable_nfc = sdl2_config->GetBoolean("System", "enable_nfc", true);
const auto size = sdl2_config->GetInteger("System", "users_size", 0);
Settings::values.current_user = std::clamp<int>(