early-access version 1264
This commit is contained in:
parent
ac593731e0
commit
1ecd107637
71 changed files with 745 additions and 757 deletions
|
@ -1,7 +1,7 @@
|
||||||
yuzu emulator early access
|
yuzu emulator early access
|
||||||
=============
|
=============
|
||||||
|
|
||||||
This is the source code for early-access 1262.
|
This is the source code for early-access 1264.
|
||||||
|
|
||||||
## Legal Notice
|
## Legal Notice
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
#include "audio_core/info_updater.h"
|
#include "audio_core/info_updater.h"
|
||||||
#include "audio_core/voice_context.h"
|
#include "audio_core/voice_context.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "core/hle/kernel/writable_event.h"
|
|
||||||
#include "core/memory.h"
|
#include "core/memory.h"
|
||||||
#include "core/settings.h"
|
#include "core/settings.h"
|
||||||
|
|
||||||
|
@ -71,10 +70,9 @@ namespace {
|
||||||
namespace AudioCore {
|
namespace AudioCore {
|
||||||
AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_,
|
AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_,
|
||||||
AudioCommon::AudioRendererParameter params,
|
AudioCommon::AudioRendererParameter params,
|
||||||
std::shared_ptr<Kernel::WritableEvent> buffer_event_,
|
Stream::ReleaseCallback&& release_callback,
|
||||||
std::size_t instance_number)
|
std::size_t instance_number)
|
||||||
: worker_params{params}, buffer_event{buffer_event_},
|
: worker_params{params}, memory_pool_info(params.effect_count + params.voice_count * 4),
|
||||||
memory_pool_info(params.effect_count + params.voice_count * 4),
|
|
||||||
voice_context(params.voice_count), effect_context(params.effect_count), mix_context(),
|
voice_context(params.voice_count), effect_context(params.effect_count), mix_context(),
|
||||||
sink_context(params.sink_count), splitter_context(),
|
sink_context(params.sink_count), splitter_context(),
|
||||||
voices(params.voice_count), memory{memory_},
|
voices(params.voice_count), memory{memory_},
|
||||||
|
@ -85,10 +83,9 @@ AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory
|
||||||
params.num_splitter_send_channels);
|
params.num_splitter_send_channels);
|
||||||
mix_context.Initialize(behavior_info, params.submix_count + 1, params.effect_count);
|
mix_context.Initialize(behavior_info, params.submix_count + 1, params.effect_count);
|
||||||
audio_out = std::make_unique<AudioCore::AudioOut>();
|
audio_out = std::make_unique<AudioCore::AudioOut>();
|
||||||
stream =
|
stream = audio_out->OpenStream(
|
||||||
audio_out->OpenStream(core_timing, params.sample_rate, AudioCommon::STREAM_NUM_CHANNELS,
|
core_timing, params.sample_rate, AudioCommon::STREAM_NUM_CHANNELS,
|
||||||
fmt::format("AudioRenderer-Instance{}", instance_number),
|
fmt::format("AudioRenderer-Instance{}", instance_number), std::move(release_callback));
|
||||||
[=]() { buffer_event_->Signal(); });
|
|
||||||
audio_out->StartStream(stream);
|
audio_out->StartStream(stream);
|
||||||
|
|
||||||
QueueMixedBuffer(0);
|
QueueMixedBuffer(0);
|
||||||
|
|
|
@ -27,10 +27,6 @@ namespace Core::Timing {
|
||||||
class CoreTiming;
|
class CoreTiming;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Kernel {
|
|
||||||
class WritableEvent;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace Core::Memory {
|
namespace Core::Memory {
|
||||||
class Memory;
|
class Memory;
|
||||||
}
|
}
|
||||||
|
@ -44,8 +40,7 @@ class AudioRenderer {
|
||||||
public:
|
public:
|
||||||
AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_,
|
AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_,
|
||||||
AudioCommon::AudioRendererParameter params,
|
AudioCommon::AudioRendererParameter params,
|
||||||
std::shared_ptr<Kernel::WritableEvent> buffer_event_,
|
Stream::ReleaseCallback&& release_callback, std::size_t instance_number);
|
||||||
std::size_t instance_number);
|
|
||||||
~AudioRenderer();
|
~AudioRenderer();
|
||||||
|
|
||||||
[[nodiscard]] ResultCode UpdateAudioRenderer(const std::vector<u8>& input_params,
|
[[nodiscard]] ResultCode UpdateAudioRenderer(const std::vector<u8>& input_params,
|
||||||
|
@ -61,7 +56,6 @@ private:
|
||||||
BehaviorInfo behavior_info{};
|
BehaviorInfo behavior_info{};
|
||||||
|
|
||||||
AudioCommon::AudioRendererParameter worker_params;
|
AudioCommon::AudioRendererParameter worker_params;
|
||||||
std::shared_ptr<Kernel::WritableEvent> buffer_event;
|
|
||||||
std::vector<ServerMemoryPoolInfo> memory_pool_info;
|
std::vector<ServerMemoryPoolInfo> memory_pool_info;
|
||||||
VoiceContext voice_context;
|
VoiceContext voice_context;
|
||||||
EffectContext effect_context;
|
EffectContext effect_context;
|
||||||
|
|
|
@ -130,7 +130,11 @@ bool Stream::ContainsBuffer([[maybe_unused]] Buffer::Tag tag) const {
|
||||||
std::vector<Buffer::Tag> Stream::GetTagsAndReleaseBuffers(std::size_t max_count) {
|
std::vector<Buffer::Tag> Stream::GetTagsAndReleaseBuffers(std::size_t max_count) {
|
||||||
std::vector<Buffer::Tag> tags;
|
std::vector<Buffer::Tag> tags;
|
||||||
for (std::size_t count = 0; count < max_count && !released_buffers.empty(); ++count) {
|
for (std::size_t count = 0; count < max_count && !released_buffers.empty(); ++count) {
|
||||||
tags.push_back(released_buffers.front()->GetTag());
|
if (released_buffers.front()) {
|
||||||
|
tags.push_back(released_buffers.front()->GetTag());
|
||||||
|
} else {
|
||||||
|
ASSERT_MSG(false, "Invalid tag in released_buffers!");
|
||||||
|
}
|
||||||
released_buffers.pop();
|
released_buffers.pop();
|
||||||
}
|
}
|
||||||
return tags;
|
return tags;
|
||||||
|
@ -140,7 +144,11 @@ std::vector<Buffer::Tag> Stream::GetTagsAndReleaseBuffers() {
|
||||||
std::vector<Buffer::Tag> tags;
|
std::vector<Buffer::Tag> tags;
|
||||||
tags.reserve(released_buffers.size());
|
tags.reserve(released_buffers.size());
|
||||||
while (!released_buffers.empty()) {
|
while (!released_buffers.empty()) {
|
||||||
tags.push_back(released_buffers.front()->GetTag());
|
if (released_buffers.front()) {
|
||||||
|
tags.push_back(released_buffers.front()->GetTag());
|
||||||
|
} else {
|
||||||
|
ASSERT_MSG(false, "Invalid tag in released_buffers!");
|
||||||
|
}
|
||||||
released_buffers.pop();
|
released_buffers.pop();
|
||||||
}
|
}
|
||||||
return tags;
|
return tags;
|
||||||
|
|
|
@ -162,6 +162,8 @@ add_library(common STATIC
|
||||||
thread.cpp
|
thread.cpp
|
||||||
thread.h
|
thread.h
|
||||||
thread_queue_list.h
|
thread_queue_list.h
|
||||||
|
thread_worker.cpp
|
||||||
|
thread_worker.h
|
||||||
threadsafe_queue.h
|
threadsafe_queue.h
|
||||||
time_zone.cpp
|
time_zone.cpp
|
||||||
time_zone.h
|
time_zone.h
|
||||||
|
|
|
@ -31,4 +31,8 @@ concept DerivedFrom = requires {
|
||||||
std::is_convertible_v<const volatile Derived*, const volatile Base*>;
|
std::is_convertible_v<const volatile Derived*, const volatile Base*>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO: Replace with std::convertible_to when libc++ implements it.
|
||||||
|
template <typename From, typename To>
|
||||||
|
concept ConvertibleTo = std::is_convertible_v<From, To>;
|
||||||
|
|
||||||
} // namespace Common
|
} // namespace Common
|
||||||
|
|
|
@ -10,16 +10,10 @@ PageTable::PageTable() = default;
|
||||||
|
|
||||||
PageTable::~PageTable() noexcept = default;
|
PageTable::~PageTable() noexcept = default;
|
||||||
|
|
||||||
void PageTable::Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits,
|
void PageTable::Resize(size_t address_space_width_in_bits, size_t page_size_in_bits) {
|
||||||
bool has_attribute) {
|
const size_t num_page_table_entries{1ULL << (address_space_width_in_bits - page_size_in_bits)};
|
||||||
const std::size_t num_page_table_entries{1ULL
|
|
||||||
<< (address_space_width_in_bits - page_size_in_bits)};
|
|
||||||
pointers.resize(num_page_table_entries);
|
pointers.resize(num_page_table_entries);
|
||||||
backing_addr.resize(num_page_table_entries);
|
backing_addr.resize(num_page_table_entries);
|
||||||
|
|
||||||
if (has_attribute) {
|
|
||||||
attributes.resize(num_page_table_entries);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Common
|
} // namespace Common
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
@ -20,10 +21,6 @@ enum class PageType : u8 {
|
||||||
/// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and
|
/// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and
|
||||||
/// invalidation
|
/// invalidation
|
||||||
RasterizerCachedMemory,
|
RasterizerCachedMemory,
|
||||||
/// Page is mapped to a I/O region. Writing and reading to this page is handled by functions.
|
|
||||||
Special,
|
|
||||||
/// Page is allocated for use.
|
|
||||||
Allocated,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SpecialRegion {
|
struct SpecialRegion {
|
||||||
|
@ -48,6 +45,59 @@ struct SpecialRegion {
|
||||||
* mimics the way a real CPU page table works.
|
* mimics the way a real CPU page table works.
|
||||||
*/
|
*/
|
||||||
struct PageTable {
|
struct PageTable {
|
||||||
|
/// Number of bits reserved for attribute tagging.
|
||||||
|
/// This can be at most the guaranteed alignment of the pointers in the page table.
|
||||||
|
static constexpr int ATTRIBUTE_BITS = 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pair of host pointer and page type attribute.
|
||||||
|
* This uses the lower bits of a given pointer to store the attribute tag.
|
||||||
|
* Writing and reading the pointer attribute pair is guaranteed to be atomic for the same method
|
||||||
|
* call. In other words, they are guaranteed to be synchronized at all times.
|
||||||
|
*/
|
||||||
|
class PageInfo {
|
||||||
|
public:
|
||||||
|
/// Returns the page pointer
|
||||||
|
[[nodiscard]] u8* Pointer() const noexcept {
|
||||||
|
return ExtractPointer(raw.load(std::memory_order_relaxed));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the page type attribute
|
||||||
|
[[nodiscard]] PageType Type() const noexcept {
|
||||||
|
return ExtractType(raw.load(std::memory_order_relaxed));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the page pointer and attribute pair, extracted from the same atomic read
|
||||||
|
[[nodiscard]] std::pair<u8*, PageType> PointerType() const noexcept {
|
||||||
|
const uintptr_t non_atomic_raw = raw.load(std::memory_order_relaxed);
|
||||||
|
return {ExtractPointer(non_atomic_raw), ExtractType(non_atomic_raw)};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the raw representation of the page information.
|
||||||
|
/// Use ExtractPointer and ExtractType to unpack the value.
|
||||||
|
[[nodiscard]] uintptr_t Raw() const noexcept {
|
||||||
|
return raw.load(std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write a page pointer and type pair atomically
|
||||||
|
void Store(u8* pointer, PageType type) noexcept {
|
||||||
|
raw.store(reinterpret_cast<uintptr_t>(pointer) | static_cast<uintptr_t>(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unpack a pointer from a page info raw representation
|
||||||
|
[[nodiscard]] static u8* ExtractPointer(uintptr_t raw) noexcept {
|
||||||
|
return reinterpret_cast<u8*>(raw & (~uintptr_t{0} << ATTRIBUTE_BITS));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unpack a page type from a page info raw representation
|
||||||
|
[[nodiscard]] static PageType ExtractType(uintptr_t raw) noexcept {
|
||||||
|
return static_cast<PageType>(raw & ((uintptr_t{1} << ATTRIBUTE_BITS) - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::atomic<uintptr_t> raw;
|
||||||
|
};
|
||||||
|
|
||||||
PageTable();
|
PageTable();
|
||||||
~PageTable() noexcept;
|
~PageTable() noexcept;
|
||||||
|
|
||||||
|
@ -63,20 +113,16 @@ struct PageTable {
|
||||||
*
|
*
|
||||||
* @param address_space_width_in_bits The address size width in bits.
|
* @param address_space_width_in_bits The address size width in bits.
|
||||||
* @param page_size_in_bits The page size in bits.
|
* @param page_size_in_bits The page size in bits.
|
||||||
* @param has_attribute Whether or not this page has any backing attributes.
|
|
||||||
*/
|
*/
|
||||||
void Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits,
|
void Resize(size_t address_space_width_in_bits, size_t page_size_in_bits);
|
||||||
bool has_attribute);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Vector of memory pointers backing each page. An entry can only be non-null if the
|
* Vector of memory pointers backing each page. An entry can only be non-null if the
|
||||||
* corresponding entry in the `attributes` vector is of type `Memory`.
|
* corresponding attribute element is of type `Memory`.
|
||||||
*/
|
*/
|
||||||
VirtualBuffer<u8*> pointers;
|
VirtualBuffer<PageInfo> pointers;
|
||||||
|
|
||||||
VirtualBuffer<u64> backing_addr;
|
VirtualBuffer<u64> backing_addr;
|
||||||
|
|
||||||
VirtualBuffer<PageType> attributes;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Common
|
} // namespace Common
|
||||||
|
|
58
src/common/thread_worker.cpp
Executable file
58
src/common/thread_worker.cpp
Executable file
|
@ -0,0 +1,58 @@
|
||||||
|
// Copyright 2020 yuzu emulator team
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include "common/thread.h"
|
||||||
|
#include "common/thread_worker.h"
|
||||||
|
|
||||||
|
namespace Common {
|
||||||
|
|
||||||
|
ThreadWorker::ThreadWorker(std::size_t num_workers, const std::string& name) {
|
||||||
|
for (std::size_t i = 0; i < num_workers; ++i)
|
||||||
|
threads.emplace_back([this, thread_name{std::string{name}}] {
|
||||||
|
Common::SetCurrentThreadName(thread_name.c_str());
|
||||||
|
|
||||||
|
// Wait for first request
|
||||||
|
{
|
||||||
|
std::unique_lock lock{queue_mutex};
|
||||||
|
condition.wait(lock, [this] { return stop || !requests.empty(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
std::function<void()> task;
|
||||||
|
|
||||||
|
{
|
||||||
|
std::unique_lock lock{queue_mutex};
|
||||||
|
condition.wait(lock, [this] { return stop || !requests.empty(); });
|
||||||
|
if (stop || requests.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
task = std::move(requests.front());
|
||||||
|
requests.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
task();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ThreadWorker::~ThreadWorker() {
|
||||||
|
{
|
||||||
|
std::unique_lock lock{queue_mutex};
|
||||||
|
stop = true;
|
||||||
|
}
|
||||||
|
condition.notify_all();
|
||||||
|
for (std::thread& thread : threads) {
|
||||||
|
thread.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ThreadWorker::QueueWork(std::function<void()>&& work) {
|
||||||
|
{
|
||||||
|
std::unique_lock lock{queue_mutex};
|
||||||
|
requests.emplace(work);
|
||||||
|
}
|
||||||
|
condition.notify_one();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Common
|
30
src/common/thread_worker.h
Executable file
30
src/common/thread_worker.h
Executable file
|
@ -0,0 +1,30 @@
|
||||||
|
// Copyright 2020 yuzu emulator team
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <functional>
|
||||||
|
#include <mutex>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <queue>
|
||||||
|
|
||||||
|
namespace Common {
|
||||||
|
|
||||||
|
class ThreadWorker final {
|
||||||
|
public:
|
||||||
|
explicit ThreadWorker(std::size_t num_workers, const std::string& name);
|
||||||
|
~ThreadWorker();
|
||||||
|
void QueueWork(std::function<void()>&& work);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<std::thread> threads;
|
||||||
|
std::queue<std::function<void()>> requests;
|
||||||
|
std::mutex queue_mutex;
|
||||||
|
std::condition_variable condition;
|
||||||
|
std::atomic_bool stop{};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Common
|
|
@ -15,10 +15,12 @@ void FreeMemoryPages(void* base, std::size_t size) noexcept;
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class VirtualBuffer final {
|
class VirtualBuffer final {
|
||||||
public:
|
public:
|
||||||
static_assert(
|
// TODO: Uncomment this and change Common::PageTable::PageInfo to be trivially constructible
|
||||||
std::is_trivially_constructible_v<T>,
|
// using std::atomic_ref once libc++ has support for it
|
||||||
"T must be trivially constructible, as non-trivial constructors will not be executed "
|
// static_assert(
|
||||||
"with the current allocator");
|
// std::is_trivially_constructible_v<T>,
|
||||||
|
// "T must be trivially constructible, as non-trivial constructors will not be executed "
|
||||||
|
// "with the current allocator");
|
||||||
|
|
||||||
constexpr VirtualBuffer() = default;
|
constexpr VirtualBuffer() = default;
|
||||||
explicit VirtualBuffer(std::size_t count) : alloc_size{count * sizeof(T)} {
|
explicit VirtualBuffer(std::size_t count) : alloc_size{count * sizeof(T)} {
|
||||||
|
|
|
@ -201,6 +201,8 @@ add_library(core STATIC
|
||||||
hle/kernel/server_port.h
|
hle/kernel/server_port.h
|
||||||
hle/kernel/server_session.cpp
|
hle/kernel/server_session.cpp
|
||||||
hle/kernel/server_session.h
|
hle/kernel/server_session.h
|
||||||
|
hle/kernel/service_thread.cpp
|
||||||
|
hle/kernel/service_thread.h
|
||||||
hle/kernel/session.cpp
|
hle/kernel/session.cpp
|
||||||
hle/kernel/session.h
|
hle/kernel/session.h
|
||||||
hle/kernel/shared_memory.cpp
|
hle/kernel/shared_memory.cpp
|
||||||
|
@ -499,7 +501,6 @@ add_library(core STATIC
|
||||||
hle/service/sm/controller.h
|
hle/service/sm/controller.h
|
||||||
hle/service/sm/sm.cpp
|
hle/service/sm/sm.cpp
|
||||||
hle/service/sm/sm.h
|
hle/service/sm/sm.h
|
||||||
hle/service/sockets/blocking_worker.h
|
|
||||||
hle/service/sockets/bsd.cpp
|
hle/service/sockets/bsd.cpp
|
||||||
hle/service/sockets/bsd.h
|
hle/service/sockets/bsd.h
|
||||||
hle/service/sockets/ethc.cpp
|
hle/service/sockets/ethc.cpp
|
||||||
|
|
|
@ -133,6 +133,7 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable&
|
||||||
config.page_table = reinterpret_cast<std::array<std::uint8_t*, NUM_PAGE_TABLE_ENTRIES>*>(
|
config.page_table = reinterpret_cast<std::array<std::uint8_t*, NUM_PAGE_TABLE_ENTRIES>*>(
|
||||||
page_table.pointers.data());
|
page_table.pointers.data());
|
||||||
config.absolute_offset_page_table = true;
|
config.absolute_offset_page_table = true;
|
||||||
|
config.page_table_pointer_mask_bits = Common::PageTable::ATTRIBUTE_BITS;
|
||||||
config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
|
config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
|
||||||
config.only_detect_misalignment_via_page_table_on_page_boundary = true;
|
config.only_detect_misalignment_via_page_table_on_page_boundary = true;
|
||||||
|
|
||||||
|
|
|
@ -152,6 +152,7 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable&
|
||||||
// Memory
|
// Memory
|
||||||
config.page_table = reinterpret_cast<void**>(page_table.pointers.data());
|
config.page_table = reinterpret_cast<void**>(page_table.pointers.data());
|
||||||
config.page_table_address_space_bits = address_space_bits;
|
config.page_table_address_space_bits = address_space_bits;
|
||||||
|
config.page_table_pointer_mask_bits = Common::PageTable::ATTRIBUTE_BITS;
|
||||||
config.silently_mirror_page_table = false;
|
config.silently_mirror_page_table = false;
|
||||||
config.absolute_offset_page_table = true;
|
config.absolute_offset_page_table = true;
|
||||||
config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
|
config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
|
||||||
|
|
|
@ -159,7 +159,7 @@ struct System::Impl {
|
||||||
device_memory = std::make_unique<Core::DeviceMemory>();
|
device_memory = std::make_unique<Core::DeviceMemory>();
|
||||||
|
|
||||||
is_multicore = Settings::values.use_multi_core.GetValue();
|
is_multicore = Settings::values.use_multi_core.GetValue();
|
||||||
is_async_gpu = is_multicore || Settings::values.use_asynchronous_gpu_emulation.GetValue();
|
is_async_gpu = Settings::values.use_asynchronous_gpu_emulation.GetValue();
|
||||||
|
|
||||||
kernel.SetMulticore(is_multicore);
|
kernel.SetMulticore(is_multicore);
|
||||||
cpu_manager.SetMulticore(is_multicore);
|
cpu_manager.SetMulticore(is_multicore);
|
||||||
|
@ -307,7 +307,6 @@ struct System::Impl {
|
||||||
service_manager.reset();
|
service_manager.reset();
|
||||||
cheat_engine.reset();
|
cheat_engine.reset();
|
||||||
telemetry_session.reset();
|
telemetry_session.reset();
|
||||||
device_memory.reset();
|
|
||||||
|
|
||||||
// Close all CPU/threading state
|
// Close all CPU/threading state
|
||||||
cpu_manager.Shutdown();
|
cpu_manager.Shutdown();
|
||||||
|
|
|
@ -46,43 +46,6 @@ void SessionRequestHandler::ClientDisconnected(
|
||||||
boost::range::remove_erase(connected_sessions, server_session);
|
boost::range::remove_erase(connected_sessions, server_session);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<WritableEvent> HLERequestContext::SleepClientThread(
|
|
||||||
const std::string& reason, u64 timeout, WakeupCallback&& callback,
|
|
||||||
std::shared_ptr<WritableEvent> writable_event) {
|
|
||||||
// Put the client thread to sleep until the wait event is signaled or the timeout expires.
|
|
||||||
|
|
||||||
if (!writable_event) {
|
|
||||||
// Create event if not provided
|
|
||||||
const auto pair = WritableEvent::CreateEventPair(kernel, "HLE Pause Event: " + reason);
|
|
||||||
writable_event = pair.writable;
|
|
||||||
}
|
|
||||||
|
|
||||||
Handle event_handle = InvalidHandle;
|
|
||||||
{
|
|
||||||
KScopedSchedulerLockAndSleep lock(kernel, event_handle, thread.get(), timeout);
|
|
||||||
thread->SetHLECallback(
|
|
||||||
[context = *this, callback](std::shared_ptr<Thread> thread) mutable -> bool {
|
|
||||||
ThreadWakeupReason reason = thread->GetSignalingResult() == RESULT_TIMEOUT
|
|
||||||
? ThreadWakeupReason::Timeout
|
|
||||||
: ThreadWakeupReason::Signal;
|
|
||||||
callback(thread, context, reason);
|
|
||||||
context.WriteToOutgoingCommandBuffer(*thread);
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
const auto readable_event{writable_event->GetReadableEvent()};
|
|
||||||
writable_event->Clear();
|
|
||||||
thread->SetHLESyncObject(readable_event.get());
|
|
||||||
thread->SetStatus(ThreadStatus::WaitHLEEvent);
|
|
||||||
thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT);
|
|
||||||
readable_event->AddWaitingThread(thread);
|
|
||||||
}
|
|
||||||
thread->SetHLETimeEvent(event_handle);
|
|
||||||
|
|
||||||
is_thread_waiting = true;
|
|
||||||
|
|
||||||
return writable_event;
|
|
||||||
}
|
|
||||||
|
|
||||||
HLERequestContext::HLERequestContext(KernelCore& kernel, Core::Memory::Memory& memory,
|
HLERequestContext::HLERequestContext(KernelCore& kernel, Core::Memory::Memory& memory,
|
||||||
std::shared_ptr<ServerSession> server_session,
|
std::shared_ptr<ServerSession> server_session,
|
||||||
std::shared_ptr<Thread> thread)
|
std::shared_ptr<Thread> thread)
|
||||||
|
|
|
@ -129,23 +129,6 @@ public:
|
||||||
using WakeupCallback = std::function<void(
|
using WakeupCallback = std::function<void(
|
||||||
std::shared_ptr<Thread> thread, HLERequestContext& context, ThreadWakeupReason reason)>;
|
std::shared_ptr<Thread> thread, HLERequestContext& context, ThreadWakeupReason reason)>;
|
||||||
|
|
||||||
/**
|
|
||||||
* Puts the specified guest thread to sleep until the returned event is signaled or until the
|
|
||||||
* specified timeout expires.
|
|
||||||
* @param reason Reason for pausing the thread, to be used for debugging purposes.
|
|
||||||
* @param timeout Timeout in nanoseconds after which the thread will be awoken and the callback
|
|
||||||
* invoked with a Timeout reason.
|
|
||||||
* @param callback Callback to be invoked when the thread is resumed. This callback must write
|
|
||||||
* the entire command response once again, regardless of the state of it before this function
|
|
||||||
* was called.
|
|
||||||
* @param writable_event Event to use to wake up the thread. If unspecified, an event will be
|
|
||||||
* created.
|
|
||||||
* @returns Event that when signaled will resume the thread and call the callback function.
|
|
||||||
*/
|
|
||||||
std::shared_ptr<WritableEvent> SleepClientThread(
|
|
||||||
const std::string& reason, u64 timeout, WakeupCallback&& callback,
|
|
||||||
std::shared_ptr<WritableEvent> writable_event = nullptr);
|
|
||||||
|
|
||||||
/// Populates this context with data from the requesting process/thread.
|
/// Populates this context with data from the requesting process/thread.
|
||||||
ResultCode PopulateFromIncomingCommandBuffer(const HandleTable& handle_table,
|
ResultCode PopulateFromIncomingCommandBuffer(const HandleTable& handle_table,
|
||||||
u32_le* src_cmdbuf);
|
u32_le* src_cmdbuf);
|
||||||
|
|
|
@ -8,11 +8,13 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <concepts>
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/bit_set.h"
|
#include "common/bit_set.h"
|
||||||
#include "common/bit_util.h"
|
#include "common/bit_util.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
#include "common/concepts.h"
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
|
||||||
|
@ -21,7 +23,7 @@ class Thread;
|
||||||
template <typename T>
|
template <typename T>
|
||||||
concept KPriorityQueueAffinityMask = !std::is_reference_v<T> && requires(T & t) {
|
concept KPriorityQueueAffinityMask = !std::is_reference_v<T> && requires(T & t) {
|
||||||
{ t.GetAffinityMask() }
|
{ t.GetAffinityMask() }
|
||||||
->std::convertible_to<u64>;
|
->Common::ConvertibleTo<u64>;
|
||||||
{t.SetAffinityMask(std::declval<u64>())};
|
{t.SetAffinityMask(std::declval<u64>())};
|
||||||
|
|
||||||
{ t.GetAffinity(std::declval<int32_t>()) }
|
{ t.GetAffinity(std::declval<int32_t>()) }
|
||||||
|
@ -48,9 +50,9 @@ concept KPriorityQueueMember = !std::is_reference_v<T> && requires(T & t) {
|
||||||
->KPriorityQueueAffinityMask;
|
->KPriorityQueueAffinityMask;
|
||||||
|
|
||||||
{ t.GetActiveCore() }
|
{ t.GetActiveCore() }
|
||||||
->std::convertible_to<s32>;
|
->Common::ConvertibleTo<s32>;
|
||||||
{ t.GetPriority() }
|
{ t.GetPriority() }
|
||||||
->std::convertible_to<s32>;
|
->Common::ConvertibleTo<s32>;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Member, size_t _NumCores, int LowestPriority, int HighestPriority>
|
template <typename Member, size_t _NumCores, int LowestPriority, int HighestPriority>
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/spin_lock.h"
|
#include "common/spin_lock.h"
|
||||||
#include "core/hardware_properties.h"
|
#include "core/hardware_properties.h"
|
||||||
|
#include "core/hle/kernel/kernel.h"
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
|
||||||
|
|
|
@ -8,13 +8,14 @@
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <unordered_map>
|
#include <unordered_set>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "common/microprofile.h"
|
#include "common/microprofile.h"
|
||||||
#include "common/thread.h"
|
#include "common/thread.h"
|
||||||
|
#include "common/thread_worker.h"
|
||||||
#include "core/arm/arm_interface.h"
|
#include "core/arm/arm_interface.h"
|
||||||
#include "core/arm/cpu_interrupt_handler.h"
|
#include "core/arm/cpu_interrupt_handler.h"
|
||||||
#include "core/arm/exclusive_monitor.h"
|
#include "core/arm/exclusive_monitor.h"
|
||||||
|
@ -35,6 +36,7 @@
|
||||||
#include "core/hle/kernel/physical_core.h"
|
#include "core/hle/kernel/physical_core.h"
|
||||||
#include "core/hle/kernel/process.h"
|
#include "core/hle/kernel/process.h"
|
||||||
#include "core/hle/kernel/resource_limit.h"
|
#include "core/hle/kernel/resource_limit.h"
|
||||||
|
#include "core/hle/kernel/service_thread.h"
|
||||||
#include "core/hle/kernel/shared_memory.h"
|
#include "core/hle/kernel/shared_memory.h"
|
||||||
#include "core/hle/kernel/synchronization.h"
|
#include "core/hle/kernel/synchronization.h"
|
||||||
#include "core/hle/kernel/thread.h"
|
#include "core/hle/kernel/thread.h"
|
||||||
|
@ -60,6 +62,8 @@ struct KernelCore::Impl {
|
||||||
RegisterHostThread();
|
RegisterHostThread();
|
||||||
|
|
||||||
global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel);
|
global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel);
|
||||||
|
service_thread_manager =
|
||||||
|
std::make_unique<Common::ThreadWorker>(1, "yuzu:ServiceThreadManager");
|
||||||
|
|
||||||
InitializePhysicalCores();
|
InitializePhysicalCores();
|
||||||
InitializeSystemResourceLimit(kernel);
|
InitializeSystemResourceLimit(kernel);
|
||||||
|
@ -76,6 +80,12 @@ struct KernelCore::Impl {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Shutdown() {
|
void Shutdown() {
|
||||||
|
process_list.clear();
|
||||||
|
|
||||||
|
// Ensures all service threads gracefully shutdown
|
||||||
|
service_thread_manager.reset();
|
||||||
|
service_threads.clear();
|
||||||
|
|
||||||
next_object_id = 0;
|
next_object_id = 0;
|
||||||
next_kernel_process_id = Process::InitialKIPIDMin;
|
next_kernel_process_id = Process::InitialKIPIDMin;
|
||||||
next_user_process_id = Process::ProcessIDMin;
|
next_user_process_id = Process::ProcessIDMin;
|
||||||
|
@ -89,8 +99,6 @@ struct KernelCore::Impl {
|
||||||
|
|
||||||
cores.clear();
|
cores.clear();
|
||||||
|
|
||||||
process_list.clear();
|
|
||||||
|
|
||||||
current_process = nullptr;
|
current_process = nullptr;
|
||||||
|
|
||||||
system_resource_limit = nullptr;
|
system_resource_limit = nullptr;
|
||||||
|
@ -103,10 +111,8 @@ struct KernelCore::Impl {
|
||||||
|
|
||||||
exclusive_monitor.reset();
|
exclusive_monitor.reset();
|
||||||
|
|
||||||
num_host_threads = 0;
|
// Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others
|
||||||
std::fill(register_host_thread_keys.begin(), register_host_thread_keys.end(),
|
next_host_thread_id = Core::Hardware::NUM_CPU_CORES;
|
||||||
std::thread::id{});
|
|
||||||
std::fill(register_host_thread_values.begin(), register_host_thread_values.end(), 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void InitializePhysicalCores() {
|
void InitializePhysicalCores() {
|
||||||
|
@ -186,52 +192,46 @@ struct KernelCore::Impl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a new host thread ID, should only be called by GetHostThreadId
|
||||||
|
u32 AllocateHostThreadId(std::optional<std::size_t> core_id) {
|
||||||
|
if (core_id) {
|
||||||
|
// The first for slots are reserved for CPU core threads
|
||||||
|
ASSERT(*core_id < Core::Hardware::NUM_CPU_CORES);
|
||||||
|
return static_cast<u32>(*core_id);
|
||||||
|
} else {
|
||||||
|
return next_host_thread_id++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the host thread ID for the caller, allocating a new one if this is the first time
|
||||||
|
u32 GetHostThreadId(std::optional<std::size_t> core_id = std::nullopt) {
|
||||||
|
const thread_local auto host_thread_id{AllocateHostThreadId(core_id)};
|
||||||
|
return host_thread_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Registers a CPU core thread by allocating a host thread ID for it
|
||||||
void RegisterCoreThread(std::size_t core_id) {
|
void RegisterCoreThread(std::size_t core_id) {
|
||||||
const std::thread::id this_id = std::this_thread::get_id();
|
ASSERT(core_id < Core::Hardware::NUM_CPU_CORES);
|
||||||
|
const auto this_id = GetHostThreadId(core_id);
|
||||||
if (!is_multicore) {
|
if (!is_multicore) {
|
||||||
single_core_thread_id = this_id;
|
single_core_thread_id = this_id;
|
||||||
}
|
}
|
||||||
const auto end =
|
|
||||||
register_host_thread_keys.begin() + static_cast<ptrdiff_t>(num_host_threads);
|
|
||||||
const auto it = std::find(register_host_thread_keys.begin(), end, this_id);
|
|
||||||
ASSERT(core_id < Core::Hardware::NUM_CPU_CORES);
|
|
||||||
ASSERT(it == end);
|
|
||||||
InsertHostThread(static_cast<u32>(core_id));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Registers a new host thread by allocating a host thread ID for it
|
||||||
void RegisterHostThread() {
|
void RegisterHostThread() {
|
||||||
const std::thread::id this_id = std::this_thread::get_id();
|
[[maybe_unused]] const auto this_id = GetHostThreadId();
|
||||||
const auto end =
|
|
||||||
register_host_thread_keys.begin() + static_cast<ptrdiff_t>(num_host_threads);
|
|
||||||
const auto it = std::find(register_host_thread_keys.begin(), end, this_id);
|
|
||||||
if (it == end) {
|
|
||||||
InsertHostThread(registered_thread_ids++);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void InsertHostThread(u32 value) {
|
[[nodiscard]] u32 GetCurrentHostThreadID() {
|
||||||
const size_t index = num_host_threads++;
|
const auto this_id = GetHostThreadId();
|
||||||
ASSERT_MSG(index < NUM_REGISTRABLE_HOST_THREADS, "Too many host threads");
|
|
||||||
register_host_thread_values[index] = value;
|
|
||||||
register_host_thread_keys[index] = std::this_thread::get_id();
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] u32 GetCurrentHostThreadID() const {
|
|
||||||
const std::thread::id this_id = std::this_thread::get_id();
|
|
||||||
if (!is_multicore && single_core_thread_id == this_id) {
|
if (!is_multicore && single_core_thread_id == this_id) {
|
||||||
return static_cast<u32>(system.GetCpuManager().CurrentCore());
|
return static_cast<u32>(system.GetCpuManager().CurrentCore());
|
||||||
}
|
}
|
||||||
const auto end =
|
return this_id;
|
||||||
register_host_thread_keys.begin() + static_cast<ptrdiff_t>(num_host_threads);
|
|
||||||
const auto it = std::find(register_host_thread_keys.begin(), end, this_id);
|
|
||||||
if (it == end) {
|
|
||||||
return Core::INVALID_HOST_THREAD_ID;
|
|
||||||
}
|
|
||||||
return register_host_thread_values[static_cast<size_t>(
|
|
||||||
std::distance(register_host_thread_keys.begin(), it))];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Core::EmuThreadHandle GetCurrentEmuThreadID() const {
|
[[nodiscard]] Core::EmuThreadHandle GetCurrentEmuThreadID() {
|
||||||
Core::EmuThreadHandle result = Core::EmuThreadHandle::InvalidHandle();
|
Core::EmuThreadHandle result = Core::EmuThreadHandle::InvalidHandle();
|
||||||
result.host_handle = GetCurrentHostThreadID();
|
result.host_handle = GetCurrentHostThreadID();
|
||||||
if (result.host_handle >= Core::Hardware::NUM_CPU_CORES) {
|
if (result.host_handle >= Core::Hardware::NUM_CPU_CORES) {
|
||||||
|
@ -325,15 +325,8 @@ struct KernelCore::Impl {
|
||||||
std::unique_ptr<Core::ExclusiveMonitor> exclusive_monitor;
|
std::unique_ptr<Core::ExclusiveMonitor> exclusive_monitor;
|
||||||
std::vector<Kernel::PhysicalCore> cores;
|
std::vector<Kernel::PhysicalCore> cores;
|
||||||
|
|
||||||
// 0-3 IDs represent core threads, >3 represent others
|
// Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others
|
||||||
std::atomic<u32> registered_thread_ids{Core::Hardware::NUM_CPU_CORES};
|
std::atomic<u32> next_host_thread_id{Core::Hardware::NUM_CPU_CORES};
|
||||||
|
|
||||||
// Number of host threads is a relatively high number to avoid overflowing
|
|
||||||
static constexpr size_t NUM_REGISTRABLE_HOST_THREADS = 64;
|
|
||||||
std::atomic<size_t> num_host_threads{0};
|
|
||||||
std::array<std::atomic<std::thread::id>, NUM_REGISTRABLE_HOST_THREADS>
|
|
||||||
register_host_thread_keys{};
|
|
||||||
std::array<std::atomic<u32>, NUM_REGISTRABLE_HOST_THREADS> register_host_thread_values{};
|
|
||||||
|
|
||||||
// Kernel memory management
|
// Kernel memory management
|
||||||
std::unique_ptr<Memory::MemoryManager> memory_manager;
|
std::unique_ptr<Memory::MemoryManager> memory_manager;
|
||||||
|
@ -345,12 +338,19 @@ struct KernelCore::Impl {
|
||||||
std::shared_ptr<Kernel::SharedMemory> irs_shared_mem;
|
std::shared_ptr<Kernel::SharedMemory> irs_shared_mem;
|
||||||
std::shared_ptr<Kernel::SharedMemory> time_shared_mem;
|
std::shared_ptr<Kernel::SharedMemory> time_shared_mem;
|
||||||
|
|
||||||
|
// Threads used for services
|
||||||
|
std::unordered_set<std::shared_ptr<Kernel::ServiceThread>> service_threads;
|
||||||
|
|
||||||
|
// Service threads are managed by a worker thread, so that a calling service thread can queue up
|
||||||
|
// the release of itself
|
||||||
|
std::unique_ptr<Common::ThreadWorker> service_thread_manager;
|
||||||
|
|
||||||
std::array<std::shared_ptr<Thread>, Core::Hardware::NUM_CPU_CORES> suspend_threads{};
|
std::array<std::shared_ptr<Thread>, Core::Hardware::NUM_CPU_CORES> suspend_threads{};
|
||||||
std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES> interrupts{};
|
std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES> interrupts{};
|
||||||
std::array<std::unique_ptr<Kernel::KScheduler>, Core::Hardware::NUM_CPU_CORES> schedulers{};
|
std::array<std::unique_ptr<Kernel::KScheduler>, Core::Hardware::NUM_CPU_CORES> schedulers{};
|
||||||
|
|
||||||
bool is_multicore{};
|
bool is_multicore{};
|
||||||
std::thread::id single_core_thread_id{};
|
u32 single_core_thread_id{};
|
||||||
|
|
||||||
std::array<u64, Core::Hardware::NUM_CPU_CORES> svc_ticks{};
|
std::array<u64, Core::Hardware::NUM_CPU_CORES> svc_ticks{};
|
||||||
|
|
||||||
|
@ -639,4 +639,19 @@ void KernelCore::ExitSVCProfile() {
|
||||||
MicroProfileLeave(MICROPROFILE_TOKEN(Kernel_SVC), impl->svc_ticks[core]);
|
MicroProfileLeave(MICROPROFILE_TOKEN(Kernel_SVC), impl->svc_ticks[core]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::weak_ptr<Kernel::ServiceThread> KernelCore::CreateServiceThread(const std::string& name) {
|
||||||
|
auto service_thread = std::make_shared<Kernel::ServiceThread>(*this, 1, name);
|
||||||
|
impl->service_thread_manager->QueueWork(
|
||||||
|
[this, service_thread] { impl->service_threads.emplace(service_thread); });
|
||||||
|
return service_thread;
|
||||||
|
}
|
||||||
|
|
||||||
|
void KernelCore::ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread) {
|
||||||
|
impl->service_thread_manager->QueueWork([this, service_thread] {
|
||||||
|
if (auto strong_ptr = service_thread.lock()) {
|
||||||
|
impl->service_threads.erase(strong_ptr);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Kernel
|
} // namespace Kernel
|
||||||
|
|
|
@ -42,6 +42,7 @@ class Process;
|
||||||
class ResourceLimit;
|
class ResourceLimit;
|
||||||
class KScheduler;
|
class KScheduler;
|
||||||
class SharedMemory;
|
class SharedMemory;
|
||||||
|
class ServiceThread;
|
||||||
class Synchronization;
|
class Synchronization;
|
||||||
class Thread;
|
class Thread;
|
||||||
class TimeManager;
|
class TimeManager;
|
||||||
|
@ -227,6 +228,22 @@ public:
|
||||||
|
|
||||||
void ExitSVCProfile();
|
void ExitSVCProfile();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an HLE service thread, which are used to execute service routines asynchronously.
|
||||||
|
* While these are allocated per ServerSession, these need to be owned and managed outside of
|
||||||
|
* ServerSession to avoid a circular dependency.
|
||||||
|
* @param name String name for the ServerSession creating this thread, used for debug purposes.
|
||||||
|
* @returns The a weak pointer newly created service thread.
|
||||||
|
*/
|
||||||
|
std::weak_ptr<Kernel::ServiceThread> CreateServiceThread(const std::string& name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Releases a HLE service thread, instructing KernelCore to free it. This should be called when
|
||||||
|
* the ServerSession associated with the thread is destroyed.
|
||||||
|
* @param service_thread Service thread to release.
|
||||||
|
*/
|
||||||
|
void ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class Object;
|
friend class Object;
|
||||||
friend class Process;
|
friend class Process;
|
||||||
|
|
|
@ -265,7 +265,7 @@ ResultCode PageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_t
|
||||||
physical_memory_usage = 0;
|
physical_memory_usage = 0;
|
||||||
memory_pool = pool;
|
memory_pool = pool;
|
||||||
|
|
||||||
page_table_impl.Resize(address_space_width, PageBits, true);
|
page_table_impl.Resize(address_space_width, PageBits);
|
||||||
|
|
||||||
return InitializeMemoryLayout(start, end);
|
return InitializeMemoryLayout(start, end);
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,19 +25,19 @@
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
|
||||||
ServerSession::ServerSession(KernelCore& kernel) : SynchronizationObject{kernel} {}
|
ServerSession::ServerSession(KernelCore& kernel) : SynchronizationObject{kernel} {}
|
||||||
ServerSession::~ServerSession() = default;
|
|
||||||
|
ServerSession::~ServerSession() {
|
||||||
|
kernel.ReleaseServiceThread(service_thread);
|
||||||
|
}
|
||||||
|
|
||||||
ResultVal<std::shared_ptr<ServerSession>> ServerSession::Create(KernelCore& kernel,
|
ResultVal<std::shared_ptr<ServerSession>> ServerSession::Create(KernelCore& kernel,
|
||||||
std::shared_ptr<Session> parent,
|
std::shared_ptr<Session> parent,
|
||||||
std::string name) {
|
std::string name) {
|
||||||
std::shared_ptr<ServerSession> session{std::make_shared<ServerSession>(kernel)};
|
std::shared_ptr<ServerSession> session{std::make_shared<ServerSession>(kernel)};
|
||||||
|
|
||||||
session->request_event =
|
|
||||||
Core::Timing::CreateEvent(name, [session](std::uintptr_t, std::chrono::nanoseconds) {
|
|
||||||
session->CompleteSyncRequest();
|
|
||||||
});
|
|
||||||
session->name = std::move(name);
|
session->name = std::move(name);
|
||||||
session->parent = std::move(parent);
|
session->parent = std::move(parent);
|
||||||
|
session->service_thread = kernel.CreateServiceThread(session->name);
|
||||||
|
|
||||||
return MakeResult(std::move(session));
|
return MakeResult(std::move(session));
|
||||||
}
|
}
|
||||||
|
@ -142,16 +142,16 @@ ResultCode ServerSession::QueueSyncRequest(std::shared_ptr<Thread> thread,
|
||||||
std::make_shared<HLERequestContext>(kernel, memory, SharedFrom(this), std::move(thread));
|
std::make_shared<HLERequestContext>(kernel, memory, SharedFrom(this), std::move(thread));
|
||||||
|
|
||||||
context->PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf);
|
context->PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf);
|
||||||
request_queue.Push(std::move(context));
|
|
||||||
|
if (auto strong_ptr = service_thread.lock()) {
|
||||||
|
strong_ptr->QueueSyncRequest(*this, std::move(context));
|
||||||
|
return RESULT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
return RESULT_SUCCESS;
|
return RESULT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
ResultCode ServerSession::CompleteSyncRequest() {
|
ResultCode ServerSession::CompleteSyncRequest(HLERequestContext& context) {
|
||||||
ASSERT(!request_queue.Empty());
|
|
||||||
|
|
||||||
auto& context = *request_queue.Front();
|
|
||||||
|
|
||||||
ResultCode result = RESULT_SUCCESS;
|
ResultCode result = RESULT_SUCCESS;
|
||||||
// If the session has been converted to a domain, handle the domain request
|
// If the session has been converted to a domain, handle the domain request
|
||||||
if (IsDomain() && context.HasDomainMessageHeader()) {
|
if (IsDomain() && context.HasDomainMessageHeader()) {
|
||||||
|
@ -177,18 +177,13 @@ ResultCode ServerSession::CompleteSyncRequest() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
request_queue.Pop();
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<Thread> thread,
|
ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<Thread> thread,
|
||||||
Core::Memory::Memory& memory,
|
Core::Memory::Memory& memory,
|
||||||
Core::Timing::CoreTiming& core_timing) {
|
Core::Timing::CoreTiming& core_timing) {
|
||||||
const ResultCode result = QueueSyncRequest(std::move(thread), memory);
|
return QueueSyncRequest(std::move(thread), memory);
|
||||||
const auto delay = std::chrono::nanoseconds{kernel.IsMulticore() ? 0 : 20000};
|
|
||||||
core_timing.ScheduleEvent(delay, request_event, {});
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Kernel
|
} // namespace Kernel
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "common/threadsafe_queue.h"
|
#include "common/threadsafe_queue.h"
|
||||||
|
#include "core/hle/kernel/service_thread.h"
|
||||||
#include "core/hle/kernel/synchronization_object.h"
|
#include "core/hle/kernel/synchronization_object.h"
|
||||||
#include "core/hle/result.h"
|
#include "core/hle/result.h"
|
||||||
|
|
||||||
|
@ -43,6 +44,8 @@ class Thread;
|
||||||
* TLS buffer and control is transferred back to it.
|
* TLS buffer and control is transferred back to it.
|
||||||
*/
|
*/
|
||||||
class ServerSession final : public SynchronizationObject {
|
class ServerSession final : public SynchronizationObject {
|
||||||
|
friend class ServiceThread;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit ServerSession(KernelCore& kernel);
|
explicit ServerSession(KernelCore& kernel);
|
||||||
~ServerSession() override;
|
~ServerSession() override;
|
||||||
|
@ -132,7 +135,7 @@ private:
|
||||||
ResultCode QueueSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory);
|
ResultCode QueueSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory);
|
||||||
|
|
||||||
/// Completes a sync request from the emulated application.
|
/// Completes a sync request from the emulated application.
|
||||||
ResultCode CompleteSyncRequest();
|
ResultCode CompleteSyncRequest(HLERequestContext& context);
|
||||||
|
|
||||||
/// Handles a SyncRequest to a domain, forwarding the request to the proper object or closing an
|
/// Handles a SyncRequest to a domain, forwarding the request to the proper object or closing an
|
||||||
/// object handle.
|
/// object handle.
|
||||||
|
@ -163,11 +166,8 @@ private:
|
||||||
/// The name of this session (optional)
|
/// The name of this session (optional)
|
||||||
std::string name;
|
std::string name;
|
||||||
|
|
||||||
/// Core timing event used to schedule the service request at some point in the future
|
/// Thread to dispatch service requests
|
||||||
std::shared_ptr<Core::Timing::EventType> request_event;
|
std::weak_ptr<ServiceThread> service_thread;
|
||||||
|
|
||||||
/// Queue of scheduled service requests
|
|
||||||
Common::MPSCQueue<std::shared_ptr<Kernel::HLERequestContext>> request_queue;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Kernel
|
} // namespace Kernel
|
||||||
|
|
|
@ -41,7 +41,7 @@ ServiceThread::Impl::Impl(KernelCore& kernel, std::size_t num_threads, const std
|
||||||
: service_name{name} {
|
: service_name{name} {
|
||||||
for (std::size_t i = 0; i < num_threads; ++i)
|
for (std::size_t i = 0; i < num_threads; ++i)
|
||||||
threads.emplace_back([this, &kernel] {
|
threads.emplace_back([this, &kernel] {
|
||||||
Common::SetCurrentThreadName(std::string{"Hle_" + service_name}.c_str());
|
Common::SetCurrentThreadName(std::string{"yuzu:HleService:" + service_name}.c_str());
|
||||||
|
|
||||||
// Wait for first request before trying to acquire a render context
|
// Wait for first request before trying to acquire a render context
|
||||||
{
|
{
|
||||||
|
|
|
@ -70,8 +70,10 @@ public:
|
||||||
Kernel::WritableEvent::CreateEventPair(system.Kernel(), "IAudioOutBufferReleased");
|
Kernel::WritableEvent::CreateEventPair(system.Kernel(), "IAudioOutBufferReleased");
|
||||||
|
|
||||||
stream = audio_core.OpenStream(system.CoreTiming(), audio_params.sample_rate,
|
stream = audio_core.OpenStream(system.CoreTiming(), audio_params.sample_rate,
|
||||||
audio_params.channel_count, std::move(unique_name),
|
audio_params.channel_count, std::move(unique_name), [this] {
|
||||||
[this] { buffer_event.writable->Signal(); });
|
const auto guard = LockService();
|
||||||
|
buffer_event.writable->Signal();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -49,16 +49,16 @@ public:
|
||||||
|
|
||||||
system_event =
|
system_event =
|
||||||
Kernel::WritableEvent::CreateEventPair(system.Kernel(), "IAudioRenderer:SystemEvent");
|
Kernel::WritableEvent::CreateEventPair(system.Kernel(), "IAudioRenderer:SystemEvent");
|
||||||
renderer = std::make_unique<AudioCore::AudioRenderer>(system.CoreTiming(), system.Memory(),
|
renderer = std::make_unique<AudioCore::AudioRenderer>(
|
||||||
audren_params, system_event.writable,
|
system.CoreTiming(), system.Memory(), audren_params,
|
||||||
instance_number);
|
[this]() {
|
||||||
|
const auto guard = LockService();
|
||||||
|
system_event.writable->Signal();
|
||||||
|
},
|
||||||
|
instance_number);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void UpdateAudioCallback() {
|
|
||||||
system_event.writable->Signal();
|
|
||||||
}
|
|
||||||
|
|
||||||
void GetSampleRate(Kernel::HLERequestContext& ctx) {
|
void GetSampleRate(Kernel::HLERequestContext& ctx) {
|
||||||
LOG_DEBUG(Service_Audio, "called");
|
LOG_DEBUG(Service_Audio, "called");
|
||||||
|
|
||||||
|
|
|
@ -78,11 +78,13 @@ IAppletResource::IAppletResource(Core::System& system_)
|
||||||
pad_update_event = Core::Timing::CreateEvent(
|
pad_update_event = Core::Timing::CreateEvent(
|
||||||
"HID::UpdatePadCallback",
|
"HID::UpdatePadCallback",
|
||||||
[this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
|
[this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
|
||||||
|
const auto guard = LockService();
|
||||||
UpdateControllers(user_data, ns_late);
|
UpdateControllers(user_data, ns_late);
|
||||||
});
|
});
|
||||||
motion_update_event = Core::Timing::CreateEvent(
|
motion_update_event = Core::Timing::CreateEvent(
|
||||||
"HID::MotionPadCallback",
|
"HID::MotionPadCallback",
|
||||||
[this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
|
[this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
|
||||||
|
const auto guard = LockService();
|
||||||
UpdateMotion(user_data, ns_late);
|
UpdateMotion(user_data, ns_late);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -31,8 +31,8 @@ public:
|
||||||
* @param output A buffer where the output data will be written to.
|
* @param output A buffer where the output data will be written to.
|
||||||
* @returns The result code of the ioctl.
|
* @returns The result code of the ioctl.
|
||||||
*/
|
*/
|
||||||
virtual NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
virtual NvResult Ioctl1(Ioctl command, const std::vector<u8>& input,
|
||||||
IoctlCtrl& ctrl) = 0;
|
std::vector<u8>& output) = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles an ioctl2 request.
|
* Handles an ioctl2 request.
|
||||||
|
@ -43,8 +43,7 @@ public:
|
||||||
* @returns The result code of the ioctl.
|
* @returns The result code of the ioctl.
|
||||||
*/
|
*/
|
||||||
virtual NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
|
virtual NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
|
||||||
const std::vector<u8>& inline_input, std::vector<u8>& output,
|
const std::vector<u8>& inline_input, std::vector<u8>& output) = 0;
|
||||||
IoctlCtrl& ctrl) = 0;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles an ioctl3 request.
|
* Handles an ioctl3 request.
|
||||||
|
@ -55,7 +54,7 @@ public:
|
||||||
* @returns The result code of the ioctl.
|
* @returns The result code of the ioctl.
|
||||||
*/
|
*/
|
||||||
virtual NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
virtual NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||||
std::vector<u8>& inline_output, IoctlCtrl& ctrl) = 0;
|
std::vector<u8>& inline_output) = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Core::System& system;
|
Core::System& system;
|
||||||
|
|
|
@ -18,21 +18,20 @@ nvdisp_disp0::nvdisp_disp0(Core::System& system, std::shared_ptr<nvmap> nvmap_de
|
||||||
: nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
|
: nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
|
||||||
nvdisp_disp0 ::~nvdisp_disp0() = default;
|
nvdisp_disp0 ::~nvdisp_disp0() = default;
|
||||||
|
|
||||||
NvResult nvdisp_disp0::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
NvResult nvdisp_disp0::Ioctl1(Ioctl command, const std::vector<u8>& input,
|
||||||
IoctlCtrl& ctrl) {
|
std::vector<u8>& output) {
|
||||||
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
|
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
|
||||||
return NvResult::NotImplemented;
|
return NvResult::NotImplemented;
|
||||||
}
|
}
|
||||||
|
|
||||||
NvResult nvdisp_disp0::Ioctl2(Ioctl command, const std::vector<u8>& input,
|
NvResult nvdisp_disp0::Ioctl2(Ioctl command, const std::vector<u8>& input,
|
||||||
const std::vector<u8>& inline_input, std::vector<u8>& output,
|
const std::vector<u8>& inline_input, std::vector<u8>& output) {
|
||||||
IoctlCtrl& ctrl) {
|
|
||||||
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
|
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
|
||||||
return NvResult::NotImplemented;
|
return NvResult::NotImplemented;
|
||||||
}
|
}
|
||||||
|
|
||||||
NvResult nvdisp_disp0::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
NvResult nvdisp_disp0::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||||
std::vector<u8>& inline_output, IoctlCtrl& ctrl) {
|
std::vector<u8>& inline_output) {
|
||||||
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
|
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
|
||||||
return NvResult::NotImplemented;
|
return NvResult::NotImplemented;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,13 +20,11 @@ public:
|
||||||
explicit nvdisp_disp0(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
|
explicit nvdisp_disp0(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
|
||||||
~nvdisp_disp0() override;
|
~nvdisp_disp0() override;
|
||||||
|
|
||||||
NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
|
||||||
IoctlCtrl& ctrl) override;
|
|
||||||
NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
|
NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
|
||||||
const std::vector<u8>& inline_input, std::vector<u8>& output,
|
const std::vector<u8>& inline_input, std::vector<u8>& output) override;
|
||||||
IoctlCtrl& ctrl) override;
|
|
||||||
NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||||
std::vector<u8>& inline_output, IoctlCtrl& ctrl) override;
|
std::vector<u8>& inline_output) override;
|
||||||
|
|
||||||
/// Performs a screen flip, drawing the buffer pointed to by the handle.
|
/// Performs a screen flip, drawing the buffer pointed to by the handle.
|
||||||
void flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, u32 stride,
|
void flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, u32 stride,
|
||||||
|
|
|
@ -21,8 +21,8 @@ nvhost_as_gpu::nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_
|
||||||
: nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
|
: nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
|
||||||
nvhost_as_gpu::~nvhost_as_gpu() = default;
|
nvhost_as_gpu::~nvhost_as_gpu() = default;
|
||||||
|
|
||||||
NvResult nvhost_as_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
NvResult nvhost_as_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input,
|
||||||
IoctlCtrl& ctrl) {
|
std::vector<u8>& output) {
|
||||||
switch (command.group) {
|
switch (command.group) {
|
||||||
case 'A':
|
case 'A':
|
||||||
switch (command.cmd) {
|
switch (command.cmd) {
|
||||||
|
@ -55,14 +55,13 @@ NvResult nvhost_as_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input, std:
|
||||||
}
|
}
|
||||||
|
|
||||||
NvResult nvhost_as_gpu::Ioctl2(Ioctl command, const std::vector<u8>& input,
|
NvResult nvhost_as_gpu::Ioctl2(Ioctl command, const std::vector<u8>& input,
|
||||||
const std::vector<u8>& inline_input, std::vector<u8>& output,
|
const std::vector<u8>& inline_input, std::vector<u8>& output) {
|
||||||
IoctlCtrl& ctrl) {
|
|
||||||
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
|
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
|
||||||
return NvResult::NotImplemented;
|
return NvResult::NotImplemented;
|
||||||
}
|
}
|
||||||
|
|
||||||
NvResult nvhost_as_gpu::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
NvResult nvhost_as_gpu::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||||
std::vector<u8>& inline_output, IoctlCtrl& ctrl) {
|
std::vector<u8>& inline_output) {
|
||||||
switch (command.group) {
|
switch (command.group) {
|
||||||
case 'A':
|
case 'A':
|
||||||
switch (command.cmd) {
|
switch (command.cmd) {
|
||||||
|
|
|
@ -30,13 +30,11 @@ public:
|
||||||
explicit nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
|
explicit nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
|
||||||
~nvhost_as_gpu() override;
|
~nvhost_as_gpu() override;
|
||||||
|
|
||||||
NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
|
||||||
IoctlCtrl& ctrl) override;
|
|
||||||
NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
|
NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
|
||||||
const std::vector<u8>& inline_input, std::vector<u8>& output,
|
const std::vector<u8>& inline_input, std::vector<u8>& output) override;
|
||||||
IoctlCtrl& ctrl) override;
|
|
||||||
NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||||
std::vector<u8>& inline_output, IoctlCtrl& ctrl) override;
|
std::vector<u8>& inline_output) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class BufferMap final {
|
class BufferMap final {
|
||||||
|
|
|
@ -20,8 +20,7 @@ nvhost_ctrl::nvhost_ctrl(Core::System& system, EventInterface& events_interface,
|
||||||
: nvdevice(system), events_interface{events_interface}, syncpoint_manager{syncpoint_manager} {}
|
: nvdevice(system), events_interface{events_interface}, syncpoint_manager{syncpoint_manager} {}
|
||||||
nvhost_ctrl::~nvhost_ctrl() = default;
|
nvhost_ctrl::~nvhost_ctrl() = default;
|
||||||
|
|
||||||
NvResult nvhost_ctrl::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
NvResult nvhost_ctrl::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
|
||||||
IoctlCtrl& ctrl) {
|
|
||||||
switch (command.group) {
|
switch (command.group) {
|
||||||
case 0x0:
|
case 0x0:
|
||||||
switch (command.cmd) {
|
switch (command.cmd) {
|
||||||
|
@ -30,9 +29,9 @@ NvResult nvhost_ctrl::Ioctl1(Ioctl command, const std::vector<u8>& input, std::v
|
||||||
case 0x1c:
|
case 0x1c:
|
||||||
return IocCtrlClearEventWait(input, output);
|
return IocCtrlClearEventWait(input, output);
|
||||||
case 0x1d:
|
case 0x1d:
|
||||||
return IocCtrlEventWait(input, output, false, ctrl);
|
return IocCtrlEventWait(input, output, false);
|
||||||
case 0x1e:
|
case 0x1e:
|
||||||
return IocCtrlEventWait(input, output, true, ctrl);
|
return IocCtrlEventWait(input, output, true);
|
||||||
case 0x1f:
|
case 0x1f:
|
||||||
return IocCtrlEventRegister(input, output);
|
return IocCtrlEventRegister(input, output);
|
||||||
case 0x20:
|
case 0x20:
|
||||||
|
@ -48,14 +47,13 @@ NvResult nvhost_ctrl::Ioctl1(Ioctl command, const std::vector<u8>& input, std::v
|
||||||
}
|
}
|
||||||
|
|
||||||
NvResult nvhost_ctrl::Ioctl2(Ioctl command, const std::vector<u8>& input,
|
NvResult nvhost_ctrl::Ioctl2(Ioctl command, const std::vector<u8>& input,
|
||||||
const std::vector<u8>& inline_input, std::vector<u8>& output,
|
const std::vector<u8>& inline_input, std::vector<u8>& output) {
|
||||||
IoctlCtrl& ctrl) {
|
|
||||||
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
|
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
|
||||||
return NvResult::NotImplemented;
|
return NvResult::NotImplemented;
|
||||||
}
|
}
|
||||||
|
|
||||||
NvResult nvhost_ctrl::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
NvResult nvhost_ctrl::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||||
std::vector<u8>& inline_output, IoctlCtrl& ctrl) {
|
std::vector<u8>& inline_outpu) {
|
||||||
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
|
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
|
||||||
return NvResult::NotImplemented;
|
return NvResult::NotImplemented;
|
||||||
}
|
}
|
||||||
|
@ -69,7 +67,7 @@ NvResult nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector
|
||||||
}
|
}
|
||||||
|
|
||||||
NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output,
|
NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output,
|
||||||
bool is_async, IoctlCtrl& ctrl) {
|
bool is_async) {
|
||||||
IocCtrlEventWaitParams params{};
|
IocCtrlEventWaitParams params{};
|
||||||
std::memcpy(¶ms, input.data(), sizeof(params));
|
std::memcpy(¶ms, input.data(), sizeof(params));
|
||||||
LOG_DEBUG(Service_NVDRV, "syncpt_id={}, threshold={}, timeout={}, is_async={}",
|
LOG_DEBUG(Service_NVDRV, "syncpt_id={}, threshold={}, timeout={}, is_async={}",
|
||||||
|
@ -141,12 +139,6 @@ NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector
|
||||||
params.value |= event_id;
|
params.value |= event_id;
|
||||||
event.event.writable->Clear();
|
event.event.writable->Clear();
|
||||||
gpu.RegisterSyncptInterrupt(params.syncpt_id, target_value);
|
gpu.RegisterSyncptInterrupt(params.syncpt_id, target_value);
|
||||||
if (!is_async && ctrl.fresh_call) {
|
|
||||||
ctrl.must_delay = true;
|
|
||||||
ctrl.timeout = params.timeout;
|
|
||||||
ctrl.event_id = event_id;
|
|
||||||
return NvResult::Timeout;
|
|
||||||
}
|
|
||||||
std::memcpy(output.data(), ¶ms, sizeof(params));
|
std::memcpy(output.data(), ¶ms, sizeof(params));
|
||||||
return NvResult::Timeout;
|
return NvResult::Timeout;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,13 +18,11 @@ public:
|
||||||
SyncpointManager& syncpoint_manager);
|
SyncpointManager& syncpoint_manager);
|
||||||
~nvhost_ctrl() override;
|
~nvhost_ctrl() override;
|
||||||
|
|
||||||
NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
|
||||||
IoctlCtrl& ctrl) override;
|
|
||||||
NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
|
NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
|
||||||
const std::vector<u8>& inline_input, std::vector<u8>& output,
|
const std::vector<u8>& inline_input, std::vector<u8>& output) override;
|
||||||
IoctlCtrl& ctrl) override;
|
|
||||||
NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||||
std::vector<u8>& inline_output, IoctlCtrl& ctrl) override;
|
std::vector<u8>& inline_output) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct IocSyncptReadParams {
|
struct IocSyncptReadParams {
|
||||||
|
@ -123,8 +121,7 @@ private:
|
||||||
static_assert(sizeof(IocCtrlEventKill) == 8, "IocCtrlEventKill is incorrect size");
|
static_assert(sizeof(IocCtrlEventKill) == 8, "IocCtrlEventKill is incorrect size");
|
||||||
|
|
||||||
NvResult NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output);
|
NvResult NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output);
|
||||||
NvResult IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, bool is_async,
|
NvResult IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, bool is_async);
|
||||||
IoctlCtrl& ctrl);
|
|
||||||
NvResult IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output);
|
NvResult IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output);
|
||||||
NvResult IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output);
|
NvResult IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output);
|
||||||
NvResult IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output);
|
NvResult IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output);
|
||||||
|
|
|
@ -16,7 +16,7 @@ nvhost_ctrl_gpu::nvhost_ctrl_gpu(Core::System& system) : nvdevice(system) {}
|
||||||
nvhost_ctrl_gpu::~nvhost_ctrl_gpu() = default;
|
nvhost_ctrl_gpu::~nvhost_ctrl_gpu() = default;
|
||||||
|
|
||||||
NvResult nvhost_ctrl_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input,
|
NvResult nvhost_ctrl_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input,
|
||||||
std::vector<u8>& output, IoctlCtrl& ctrl) {
|
std::vector<u8>& output) {
|
||||||
switch (command.group) {
|
switch (command.group) {
|
||||||
case 'G':
|
case 'G':
|
||||||
switch (command.cmd) {
|
switch (command.cmd) {
|
||||||
|
@ -48,15 +48,13 @@ NvResult nvhost_ctrl_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input,
|
||||||
}
|
}
|
||||||
|
|
||||||
NvResult nvhost_ctrl_gpu::Ioctl2(Ioctl command, const std::vector<u8>& input,
|
NvResult nvhost_ctrl_gpu::Ioctl2(Ioctl command, const std::vector<u8>& input,
|
||||||
const std::vector<u8>& inline_input, std::vector<u8>& output,
|
const std::vector<u8>& inline_input, std::vector<u8>& output) {
|
||||||
IoctlCtrl& ctrl) {
|
|
||||||
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
|
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
|
||||||
return NvResult::NotImplemented;
|
return NvResult::NotImplemented;
|
||||||
}
|
}
|
||||||
|
|
||||||
NvResult nvhost_ctrl_gpu::Ioctl3(Ioctl command, const std::vector<u8>& input,
|
NvResult nvhost_ctrl_gpu::Ioctl3(Ioctl command, const std::vector<u8>& input,
|
||||||
std::vector<u8>& output, std::vector<u8>& inline_output,
|
std::vector<u8>& output, std::vector<u8>& inline_output) {
|
||||||
IoctlCtrl& ctrl) {
|
|
||||||
switch (command.group) {
|
switch (command.group) {
|
||||||
case 'G':
|
case 'G':
|
||||||
switch (command.cmd) {
|
switch (command.cmd) {
|
||||||
|
|
|
@ -16,13 +16,11 @@ public:
|
||||||
explicit nvhost_ctrl_gpu(Core::System& system);
|
explicit nvhost_ctrl_gpu(Core::System& system);
|
||||||
~nvhost_ctrl_gpu() override;
|
~nvhost_ctrl_gpu() override;
|
||||||
|
|
||||||
NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
|
||||||
IoctlCtrl& ctrl) override;
|
|
||||||
NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
|
NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
|
||||||
const std::vector<u8>& inline_input, std::vector<u8>& output,
|
const std::vector<u8>& inline_input, std::vector<u8>& output) override;
|
||||||
IoctlCtrl& ctrl) override;
|
|
||||||
NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||||
std::vector<u8>& inline_output, IoctlCtrl& ctrl) override;
|
std::vector<u8>& inline_output) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct IoctlGpuCharacteristics {
|
struct IoctlGpuCharacteristics {
|
||||||
|
|
|
@ -23,8 +23,7 @@ nvhost_gpu::nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev,
|
||||||
|
|
||||||
nvhost_gpu::~nvhost_gpu() = default;
|
nvhost_gpu::~nvhost_gpu() = default;
|
||||||
|
|
||||||
NvResult nvhost_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
NvResult nvhost_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
|
||||||
IoctlCtrl& ctrl) {
|
|
||||||
switch (command.group) {
|
switch (command.group) {
|
||||||
case 0x0:
|
case 0x0:
|
||||||
switch (command.cmd) {
|
switch (command.cmd) {
|
||||||
|
@ -76,8 +75,7 @@ NvResult nvhost_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input, std::ve
|
||||||
};
|
};
|
||||||
|
|
||||||
NvResult nvhost_gpu::Ioctl2(Ioctl command, const std::vector<u8>& input,
|
NvResult nvhost_gpu::Ioctl2(Ioctl command, const std::vector<u8>& input,
|
||||||
const std::vector<u8>& inline_input, std::vector<u8>& output,
|
const std::vector<u8>& inline_input, std::vector<u8>& output) {
|
||||||
IoctlCtrl& ctrl) {
|
|
||||||
switch (command.group) {
|
switch (command.group) {
|
||||||
case 'H':
|
case 'H':
|
||||||
switch (command.cmd) {
|
switch (command.cmd) {
|
||||||
|
@ -91,7 +89,7 @@ NvResult nvhost_gpu::Ioctl2(Ioctl command, const std::vector<u8>& input,
|
||||||
}
|
}
|
||||||
|
|
||||||
NvResult nvhost_gpu::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
NvResult nvhost_gpu::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||||
std::vector<u8>& inline_output, IoctlCtrl& ctrl) {
|
std::vector<u8>& inline_output) {
|
||||||
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
|
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
|
||||||
return NvResult::NotImplemented;
|
return NvResult::NotImplemented;
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,13 +26,11 @@ public:
|
||||||
SyncpointManager& syncpoint_manager);
|
SyncpointManager& syncpoint_manager);
|
||||||
~nvhost_gpu() override;
|
~nvhost_gpu() override;
|
||||||
|
|
||||||
NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
|
||||||
IoctlCtrl& ctrl) override;
|
|
||||||
NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
|
NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
|
||||||
const std::vector<u8>& inline_input, std::vector<u8>& output,
|
const std::vector<u8>& inline_input, std::vector<u8>& output) override;
|
||||||
IoctlCtrl& ctrl) override;
|
|
||||||
NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||||
std::vector<u8>& inline_output, IoctlCtrl& ctrl) override;
|
std::vector<u8>& inline_output) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum class CtxObjects : u32_le {
|
enum class CtxObjects : u32_le {
|
||||||
|
|
|
@ -15,8 +15,8 @@ nvhost_nvdec::nvhost_nvdec(Core::System& system, std::shared_ptr<nvmap> nvmap_de
|
||||||
: nvhost_nvdec_common(system, std::move(nvmap_dev)) {}
|
: nvhost_nvdec_common(system, std::move(nvmap_dev)) {}
|
||||||
nvhost_nvdec::~nvhost_nvdec() = default;
|
nvhost_nvdec::~nvhost_nvdec() = default;
|
||||||
|
|
||||||
NvResult nvhost_nvdec::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
NvResult nvhost_nvdec::Ioctl1(Ioctl command, const std::vector<u8>& input,
|
||||||
IoctlCtrl& ctrl) {
|
std::vector<u8>& output) {
|
||||||
switch (command.group) {
|
switch (command.group) {
|
||||||
case 0x0:
|
case 0x0:
|
||||||
switch (command.cmd) {
|
switch (command.cmd) {
|
||||||
|
@ -59,14 +59,13 @@ NvResult nvhost_nvdec::Ioctl1(Ioctl command, const std::vector<u8>& input, std::
|
||||||
}
|
}
|
||||||
|
|
||||||
NvResult nvhost_nvdec::Ioctl2(Ioctl command, const std::vector<u8>& input,
|
NvResult nvhost_nvdec::Ioctl2(Ioctl command, const std::vector<u8>& input,
|
||||||
const std::vector<u8>& inline_input, std::vector<u8>& output,
|
const std::vector<u8>& inline_input, std::vector<u8>& output) {
|
||||||
IoctlCtrl& ctrl) {
|
|
||||||
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
|
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
|
||||||
return NvResult::NotImplemented;
|
return NvResult::NotImplemented;
|
||||||
}
|
}
|
||||||
|
|
||||||
NvResult nvhost_nvdec::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
NvResult nvhost_nvdec::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||||
std::vector<u8>& inline_output, IoctlCtrl& ctrl) {
|
std::vector<u8>& inline_output) {
|
||||||
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
|
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
|
||||||
return NvResult::NotImplemented;
|
return NvResult::NotImplemented;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,13 +14,11 @@ public:
|
||||||
explicit nvhost_nvdec(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
|
explicit nvhost_nvdec(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
|
||||||
~nvhost_nvdec() override;
|
~nvhost_nvdec() override;
|
||||||
|
|
||||||
NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
|
||||||
IoctlCtrl& ctrl) override;
|
|
||||||
NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
|
NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
|
||||||
const std::vector<u8>& inline_input, std::vector<u8>& output,
|
const std::vector<u8>& inline_input, std::vector<u8>& output) override;
|
||||||
IoctlCtrl& ctrl) override;
|
|
||||||
NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||||
std::vector<u8>& inline_output, IoctlCtrl& ctrl) override;
|
std::vector<u8>& inline_output) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Service::Nvidia::Devices
|
} // namespace Service::Nvidia::Devices
|
||||||
|
|
|
@ -13,8 +13,8 @@ namespace Service::Nvidia::Devices {
|
||||||
nvhost_nvjpg::nvhost_nvjpg(Core::System& system) : nvdevice(system) {}
|
nvhost_nvjpg::nvhost_nvjpg(Core::System& system) : nvdevice(system) {}
|
||||||
nvhost_nvjpg::~nvhost_nvjpg() = default;
|
nvhost_nvjpg::~nvhost_nvjpg() = default;
|
||||||
|
|
||||||
NvResult nvhost_nvjpg::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
NvResult nvhost_nvjpg::Ioctl1(Ioctl command, const std::vector<u8>& input,
|
||||||
IoctlCtrl& ctrl) {
|
std::vector<u8>& output) {
|
||||||
switch (command.group) {
|
switch (command.group) {
|
||||||
case 'H':
|
case 'H':
|
||||||
switch (command.cmd) {
|
switch (command.cmd) {
|
||||||
|
@ -33,14 +33,13 @@ NvResult nvhost_nvjpg::Ioctl1(Ioctl command, const std::vector<u8>& input, std::
|
||||||
}
|
}
|
||||||
|
|
||||||
NvResult nvhost_nvjpg::Ioctl2(Ioctl command, const std::vector<u8>& input,
|
NvResult nvhost_nvjpg::Ioctl2(Ioctl command, const std::vector<u8>& input,
|
||||||
const std::vector<u8>& inline_input, std::vector<u8>& output,
|
const std::vector<u8>& inline_input, std::vector<u8>& output) {
|
||||||
IoctlCtrl& ctrl) {
|
|
||||||
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
|
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
|
||||||
return NvResult::NotImplemented;
|
return NvResult::NotImplemented;
|
||||||
}
|
}
|
||||||
|
|
||||||
NvResult nvhost_nvjpg::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
NvResult nvhost_nvjpg::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||||
std::vector<u8>& inline_output, IoctlCtrl& ctrl) {
|
std::vector<u8>& inline_output) {
|
||||||
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
|
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
|
||||||
return NvResult::NotImplemented;
|
return NvResult::NotImplemented;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,13 +16,11 @@ public:
|
||||||
explicit nvhost_nvjpg(Core::System& system);
|
explicit nvhost_nvjpg(Core::System& system);
|
||||||
~nvhost_nvjpg() override;
|
~nvhost_nvjpg() override;
|
||||||
|
|
||||||
NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
|
||||||
IoctlCtrl& ctrl) override;
|
|
||||||
NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
|
NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
|
||||||
const std::vector<u8>& inline_input, std::vector<u8>& output,
|
const std::vector<u8>& inline_input, std::vector<u8>& output) override;
|
||||||
IoctlCtrl& ctrl) override;
|
|
||||||
NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||||
std::vector<u8>& inline_output, IoctlCtrl& ctrl) override;
|
std::vector<u8>& inline_output) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct IoctlSetNvmapFD {
|
struct IoctlSetNvmapFD {
|
||||||
|
|
|
@ -16,8 +16,7 @@ nvhost_vic::nvhost_vic(Core::System& system, std::shared_ptr<nvmap> nvmap_dev)
|
||||||
}
|
}
|
||||||
nvhost_vic::~nvhost_vic() = default;
|
nvhost_vic::~nvhost_vic() = default;
|
||||||
|
|
||||||
NvResult nvhost_vic::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
NvResult nvhost_vic::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
|
||||||
IoctlCtrl& ctrl) {
|
|
||||||
switch (command.group) {
|
switch (command.group) {
|
||||||
case 0x0:
|
case 0x0:
|
||||||
switch (command.cmd) {
|
switch (command.cmd) {
|
||||||
|
@ -52,14 +51,13 @@ NvResult nvhost_vic::Ioctl1(Ioctl command, const std::vector<u8>& input, std::ve
|
||||||
}
|
}
|
||||||
|
|
||||||
NvResult nvhost_vic::Ioctl2(Ioctl command, const std::vector<u8>& input,
|
NvResult nvhost_vic::Ioctl2(Ioctl command, const std::vector<u8>& input,
|
||||||
const std::vector<u8>& inline_input, std::vector<u8>& output,
|
const std::vector<u8>& inline_input, std::vector<u8>& output) {
|
||||||
IoctlCtrl& ctrl) {
|
|
||||||
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
|
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
|
||||||
return NvResult::NotImplemented;
|
return NvResult::NotImplemented;
|
||||||
}
|
}
|
||||||
|
|
||||||
NvResult nvhost_vic::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
NvResult nvhost_vic::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||||
std::vector<u8>& inline_output, IoctlCtrl& ctrl) {
|
std::vector<u8>& inline_output) {
|
||||||
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
|
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
|
||||||
return NvResult::NotImplemented;
|
return NvResult::NotImplemented;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,12 +14,10 @@ public:
|
||||||
explicit nvhost_vic(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
|
explicit nvhost_vic(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
|
||||||
~nvhost_vic();
|
~nvhost_vic();
|
||||||
|
|
||||||
NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
|
||||||
IoctlCtrl& ctrl) override;
|
|
||||||
NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
|
NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
|
||||||
const std::vector<u8>& inline_input, std::vector<u8>& output,
|
const std::vector<u8>& inline_input, std::vector<u8>& output) override;
|
||||||
IoctlCtrl& ctrl) override;
|
|
||||||
NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||||
std::vector<u8>& inline_output, IoctlCtrl& ctrl) override;
|
std::vector<u8>& inline_output) override;
|
||||||
};
|
};
|
||||||
} // namespace Service::Nvidia::Devices
|
} // namespace Service::Nvidia::Devices
|
||||||
|
|
|
@ -19,8 +19,7 @@ nvmap::nvmap(Core::System& system) : nvdevice(system) {
|
||||||
|
|
||||||
nvmap::~nvmap() = default;
|
nvmap::~nvmap() = default;
|
||||||
|
|
||||||
NvResult nvmap::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
NvResult nvmap::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
|
||||||
IoctlCtrl& ctrl) {
|
|
||||||
switch (command.group) {
|
switch (command.group) {
|
||||||
case 0x1:
|
case 0x1:
|
||||||
switch (command.cmd) {
|
switch (command.cmd) {
|
||||||
|
@ -49,14 +48,13 @@ NvResult nvmap::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<
|
||||||
}
|
}
|
||||||
|
|
||||||
NvResult nvmap::Ioctl2(Ioctl command, const std::vector<u8>& input,
|
NvResult nvmap::Ioctl2(Ioctl command, const std::vector<u8>& input,
|
||||||
const std::vector<u8>& inline_input, std::vector<u8>& output,
|
const std::vector<u8>& inline_input, std::vector<u8>& output) {
|
||||||
IoctlCtrl& ctrl) {
|
|
||||||
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
|
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
|
||||||
return NvResult::NotImplemented;
|
return NvResult::NotImplemented;
|
||||||
}
|
}
|
||||||
|
|
||||||
NvResult nvmap::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
NvResult nvmap::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||||
std::vector<u8>& inline_output, IoctlCtrl& ctrl) {
|
std::vector<u8>& inline_output) {
|
||||||
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
|
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
|
||||||
return NvResult::NotImplemented;
|
return NvResult::NotImplemented;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,13 +19,11 @@ public:
|
||||||
explicit nvmap(Core::System& system);
|
explicit nvmap(Core::System& system);
|
||||||
~nvmap() override;
|
~nvmap() override;
|
||||||
|
|
||||||
NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
|
||||||
IoctlCtrl& ctrl) override;
|
|
||||||
NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
|
NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
|
||||||
const std::vector<u8>& inline_input, std::vector<u8>& output,
|
const std::vector<u8>& inline_input, std::vector<u8>& output) override;
|
||||||
IoctlCtrl& ctrl) override;
|
|
||||||
NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||||
std::vector<u8>& inline_output, IoctlCtrl& ctrl) override;
|
std::vector<u8>& inline_output) override;
|
||||||
|
|
||||||
/// Returns the allocated address of an nvmap object given its handle.
|
/// Returns the allocated address of an nvmap object given its handle.
|
||||||
VAddr GetObjectAddress(u32 handle) const;
|
VAddr GetObjectAddress(u32 handle) const;
|
||||||
|
|
|
@ -61,32 +61,9 @@ void NVDRV::Ioctl1(Kernel::HLERequestContext& ctx) {
|
||||||
std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0));
|
std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0));
|
||||||
const auto input_buffer = ctx.ReadBuffer(0);
|
const auto input_buffer = ctx.ReadBuffer(0);
|
||||||
|
|
||||||
IoctlCtrl ctrl{};
|
const auto nv_result = nvdrv->Ioctl1(fd, command, input_buffer, output_buffer);
|
||||||
|
if (command.is_out != 0) {
|
||||||
const auto nv_result = nvdrv->Ioctl1(fd, command, input_buffer, output_buffer, ctrl);
|
ctx.WriteBuffer(output_buffer);
|
||||||
if (ctrl.must_delay) {
|
|
||||||
ctrl.fresh_call = false;
|
|
||||||
ctx.SleepClientThread(
|
|
||||||
"NVServices::DelayedResponse", ctrl.timeout,
|
|
||||||
[=, this](std::shared_ptr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx_,
|
|
||||||
Kernel::ThreadWakeupReason reason) {
|
|
||||||
IoctlCtrl ctrl2{ctrl};
|
|
||||||
std::vector<u8> tmp_output = output_buffer;
|
|
||||||
const auto nv_result2 = nvdrv->Ioctl1(fd, command, input_buffer, tmp_output, ctrl2);
|
|
||||||
|
|
||||||
if (command.is_out != 0) {
|
|
||||||
ctx.WriteBuffer(tmp_output);
|
|
||||||
}
|
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx_, 3};
|
|
||||||
rb.Push(RESULT_SUCCESS);
|
|
||||||
rb.PushEnum(nv_result2);
|
|
||||||
},
|
|
||||||
nvdrv->GetEventWriteable(ctrl.event_id));
|
|
||||||
} else {
|
|
||||||
if (command.is_out != 0) {
|
|
||||||
ctx.WriteBuffer(output_buffer);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 3};
|
IPC::ResponseBuilder rb{ctx, 3};
|
||||||
|
@ -110,36 +87,8 @@ void NVDRV::Ioctl2(Kernel::HLERequestContext& ctx) {
|
||||||
const auto input_inlined_buffer = ctx.ReadBuffer(1);
|
const auto input_inlined_buffer = ctx.ReadBuffer(1);
|
||||||
std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0));
|
std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0));
|
||||||
|
|
||||||
IoctlCtrl ctrl{};
|
|
||||||
|
|
||||||
const auto nv_result =
|
const auto nv_result =
|
||||||
nvdrv->Ioctl2(fd, command, input_buffer, input_inlined_buffer, output_buffer, ctrl);
|
nvdrv->Ioctl2(fd, command, input_buffer, input_inlined_buffer, output_buffer);
|
||||||
if (ctrl.must_delay) {
|
|
||||||
ctrl.fresh_call = false;
|
|
||||||
ctx.SleepClientThread(
|
|
||||||
"NVServices::DelayedResponse", ctrl.timeout,
|
|
||||||
[=, this](std::shared_ptr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx_,
|
|
||||||
Kernel::ThreadWakeupReason reason) {
|
|
||||||
IoctlCtrl ctrl2{ctrl};
|
|
||||||
std::vector<u8> tmp_output = output_buffer;
|
|
||||||
const auto nv_result2 = nvdrv->Ioctl2(fd, command, input_buffer,
|
|
||||||
input_inlined_buffer, tmp_output, ctrl2);
|
|
||||||
|
|
||||||
if (command.is_out != 0) {
|
|
||||||
ctx.WriteBuffer(tmp_output);
|
|
||||||
}
|
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx_, 3};
|
|
||||||
rb.Push(RESULT_SUCCESS);
|
|
||||||
rb.PushEnum(nv_result2);
|
|
||||||
},
|
|
||||||
nvdrv->GetEventWriteable(ctrl.event_id));
|
|
||||||
} else {
|
|
||||||
if (command.is_out != 0) {
|
|
||||||
ctx.WriteBuffer(output_buffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (command.is_out != 0) {
|
if (command.is_out != 0) {
|
||||||
ctx.WriteBuffer(output_buffer);
|
ctx.WriteBuffer(output_buffer);
|
||||||
}
|
}
|
||||||
|
@ -165,36 +114,11 @@ void NVDRV::Ioctl3(Kernel::HLERequestContext& ctx) {
|
||||||
std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0));
|
std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0));
|
||||||
std::vector<u8> output_buffer_inline(ctx.GetWriteBufferSize(1));
|
std::vector<u8> output_buffer_inline(ctx.GetWriteBufferSize(1));
|
||||||
|
|
||||||
IoctlCtrl ctrl{};
|
|
||||||
const auto nv_result =
|
const auto nv_result =
|
||||||
nvdrv->Ioctl3(fd, command, input_buffer, output_buffer, output_buffer_inline, ctrl);
|
nvdrv->Ioctl3(fd, command, input_buffer, output_buffer, output_buffer_inline);
|
||||||
if (ctrl.must_delay) {
|
if (command.is_out != 0) {
|
||||||
ctrl.fresh_call = false;
|
ctx.WriteBuffer(output_buffer, 0);
|
||||||
ctx.SleepClientThread(
|
ctx.WriteBuffer(output_buffer_inline, 1);
|
||||||
"NVServices::DelayedResponse", ctrl.timeout,
|
|
||||||
[=, this](std::shared_ptr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx_,
|
|
||||||
Kernel::ThreadWakeupReason reason) {
|
|
||||||
IoctlCtrl ctrl2{ctrl};
|
|
||||||
std::vector<u8> tmp_output = output_buffer;
|
|
||||||
std::vector<u8> tmp_output2 = output_buffer;
|
|
||||||
const auto nv_result2 =
|
|
||||||
nvdrv->Ioctl3(fd, command, input_buffer, tmp_output, tmp_output2, ctrl2);
|
|
||||||
|
|
||||||
if (command.is_out != 0) {
|
|
||||||
ctx.WriteBuffer(tmp_output, 0);
|
|
||||||
ctx.WriteBuffer(tmp_output2, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx_, 3};
|
|
||||||
rb.Push(RESULT_SUCCESS);
|
|
||||||
rb.PushEnum(nv_result2);
|
|
||||||
},
|
|
||||||
nvdrv->GetEventWriteable(ctrl.event_id));
|
|
||||||
} else {
|
|
||||||
if (command.is_out != 0) {
|
|
||||||
ctx.WriteBuffer(output_buffer, 0);
|
|
||||||
ctx.WriteBuffer(output_buffer_inline, 1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 3};
|
IPC::ResponseBuilder rb{ctx, 3};
|
||||||
|
|
|
@ -97,15 +97,4 @@ union Ioctl {
|
||||||
BitField<31, 1, u32> is_out;
|
BitField<31, 1, u32> is_out;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct IoctlCtrl {
|
|
||||||
// First call done to the servioce for services that call itself again after a call.
|
|
||||||
bool fresh_call{true};
|
|
||||||
// Tells the Ioctl Wrapper that it must delay the IPC response and send the thread to sleep
|
|
||||||
bool must_delay{};
|
|
||||||
// Timeout for the delay
|
|
||||||
s64 timeout{};
|
|
||||||
// NV Event Id
|
|
||||||
s32 event_id{-1};
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace Service::Nvidia
|
} // namespace Service::Nvidia
|
||||||
|
|
|
@ -91,7 +91,7 @@ DeviceFD Module::Open(const std::string& device_name) {
|
||||||
}
|
}
|
||||||
|
|
||||||
NvResult Module::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
|
NvResult Module::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
|
||||||
std::vector<u8>& output, IoctlCtrl& ctrl) {
|
std::vector<u8>& output) {
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);
|
LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);
|
||||||
return NvResult::InvalidState;
|
return NvResult::InvalidState;
|
||||||
|
@ -104,12 +104,11 @@ NvResult Module::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input
|
||||||
return NvResult::NotImplemented;
|
return NvResult::NotImplemented;
|
||||||
}
|
}
|
||||||
|
|
||||||
return itr->second->Ioctl1(command, input, output, ctrl);
|
return itr->second->Ioctl1(command, input, output);
|
||||||
}
|
}
|
||||||
|
|
||||||
NvResult Module::Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
|
NvResult Module::Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
|
||||||
const std::vector<u8>& inline_input, std::vector<u8>& output,
|
const std::vector<u8>& inline_input, std::vector<u8>& output) {
|
||||||
IoctlCtrl& ctrl) {
|
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);
|
LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);
|
||||||
return NvResult::InvalidState;
|
return NvResult::InvalidState;
|
||||||
|
@ -122,11 +121,11 @@ NvResult Module::Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input
|
||||||
return NvResult::NotImplemented;
|
return NvResult::NotImplemented;
|
||||||
}
|
}
|
||||||
|
|
||||||
return itr->second->Ioctl2(command, input, inline_input, output, ctrl);
|
return itr->second->Ioctl2(command, input, inline_input, output);
|
||||||
}
|
}
|
||||||
|
|
||||||
NvResult Module::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
|
NvResult Module::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
|
||||||
std::vector<u8>& output, std::vector<u8>& inline_output, IoctlCtrl& ctrl) {
|
std::vector<u8>& output, std::vector<u8>& inline_output) {
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);
|
LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);
|
||||||
return NvResult::InvalidState;
|
return NvResult::InvalidState;
|
||||||
|
@ -139,7 +138,7 @@ NvResult Module::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input
|
||||||
return NvResult::NotImplemented;
|
return NvResult::NotImplemented;
|
||||||
}
|
}
|
||||||
|
|
||||||
return itr->second->Ioctl3(command, input, output, inline_output, ctrl);
|
return itr->second->Ioctl3(command, input, output, inline_output);
|
||||||
}
|
}
|
||||||
|
|
||||||
NvResult Module::Close(DeviceFD fd) {
|
NvResult Module::Close(DeviceFD fd) {
|
||||||
|
|
|
@ -119,13 +119,13 @@ public:
|
||||||
|
|
||||||
/// Sends an ioctl command to the specified file descriptor.
|
/// Sends an ioctl command to the specified file descriptor.
|
||||||
NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
|
NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
|
||||||
std::vector<u8>& output, IoctlCtrl& ctrl);
|
std::vector<u8>& output);
|
||||||
|
|
||||||
NvResult Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
|
NvResult Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
|
||||||
const std::vector<u8>& inline_input, std::vector<u8>& output, IoctlCtrl& ctrl);
|
const std::vector<u8>& inline_input, std::vector<u8>& output);
|
||||||
|
|
||||||
NvResult Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
|
NvResult Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
|
||||||
std::vector<u8>& output, std::vector<u8>& inline_output, IoctlCtrl& ctrl);
|
std::vector<u8>& output, std::vector<u8>& inline_output);
|
||||||
|
|
||||||
/// Closes a device file descriptor and returns operation success.
|
/// Closes a device file descriptor and returns operation success.
|
||||||
NvResult Close(DeviceFD fd);
|
NvResult Close(DeviceFD fd);
|
||||||
|
|
|
@ -25,7 +25,12 @@ void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer)
|
||||||
ASSERT(slot < buffer_slots);
|
ASSERT(slot < buffer_slots);
|
||||||
LOG_WARNING(Service, "Adding graphics buffer {}", slot);
|
LOG_WARNING(Service, "Adding graphics buffer {}", slot);
|
||||||
|
|
||||||
free_buffers.push_back(slot);
|
{
|
||||||
|
std::unique_lock lock{queue_mutex};
|
||||||
|
free_buffers.push_back(slot);
|
||||||
|
}
|
||||||
|
condition.notify_one();
|
||||||
|
|
||||||
buffers[slot] = {
|
buffers[slot] = {
|
||||||
.slot = slot,
|
.slot = slot,
|
||||||
.status = Buffer::Status::Free,
|
.status = Buffer::Status::Free,
|
||||||
|
@ -41,10 +46,20 @@ void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer)
|
||||||
|
|
||||||
std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::DequeueBuffer(u32 width,
|
std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::DequeueBuffer(u32 width,
|
||||||
u32 height) {
|
u32 height) {
|
||||||
|
// Wait for first request before trying to dequeue
|
||||||
|
{
|
||||||
|
std::unique_lock lock{queue_mutex};
|
||||||
|
condition.wait(lock, [this] { return !free_buffers.empty() || !is_connect; });
|
||||||
|
}
|
||||||
|
|
||||||
if (free_buffers.empty()) {
|
if (!is_connect) {
|
||||||
|
// Buffer was disconnected while the thread was blocked, this is most likely due to
|
||||||
|
// emulation being stopped
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unique_lock lock{queue_mutex};
|
||||||
|
|
||||||
auto f_itr = free_buffers.begin();
|
auto f_itr = free_buffers.begin();
|
||||||
auto slot = buffers.size();
|
auto slot = buffers.size();
|
||||||
|
|
||||||
|
@ -97,7 +112,11 @@ void BufferQueue::CancelBuffer(u32 slot, const Service::Nvidia::MultiFence& mult
|
||||||
buffers[slot].multi_fence = multi_fence;
|
buffers[slot].multi_fence = multi_fence;
|
||||||
buffers[slot].swap_interval = 0;
|
buffers[slot].swap_interval = 0;
|
||||||
|
|
||||||
free_buffers.push_back(slot);
|
{
|
||||||
|
std::unique_lock lock{queue_mutex};
|
||||||
|
free_buffers.push_back(slot);
|
||||||
|
}
|
||||||
|
condition.notify_one();
|
||||||
|
|
||||||
buffer_wait_event.writable->Signal();
|
buffer_wait_event.writable->Signal();
|
||||||
}
|
}
|
||||||
|
@ -127,15 +146,28 @@ void BufferQueue::ReleaseBuffer(u32 slot) {
|
||||||
ASSERT(buffers[slot].slot == slot);
|
ASSERT(buffers[slot].slot == slot);
|
||||||
|
|
||||||
buffers[slot].status = Buffer::Status::Free;
|
buffers[slot].status = Buffer::Status::Free;
|
||||||
free_buffers.push_back(slot);
|
{
|
||||||
|
std::unique_lock lock{queue_mutex};
|
||||||
|
free_buffers.push_back(slot);
|
||||||
|
}
|
||||||
|
condition.notify_one();
|
||||||
|
|
||||||
buffer_wait_event.writable->Signal();
|
buffer_wait_event.writable->Signal();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BufferQueue::Connect() {
|
||||||
|
queue_sequence.clear();
|
||||||
|
id = 1;
|
||||||
|
layer_id = 1;
|
||||||
|
is_connect = true;
|
||||||
|
}
|
||||||
|
|
||||||
void BufferQueue::Disconnect() {
|
void BufferQueue::Disconnect() {
|
||||||
buffers.fill({});
|
buffers.fill({});
|
||||||
queue_sequence.clear();
|
queue_sequence.clear();
|
||||||
buffer_wait_event.writable->Signal();
|
buffer_wait_event.writable->Signal();
|
||||||
|
is_connect = false;
|
||||||
|
condition.notify_one();
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 BufferQueue::Query(QueryType type) {
|
u32 BufferQueue::Query(QueryType type) {
|
||||||
|
|
|
@ -4,7 +4,9 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <condition_variable>
|
||||||
#include <list>
|
#include <list>
|
||||||
|
#include <mutex>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
@ -99,6 +101,7 @@ public:
|
||||||
void CancelBuffer(u32 slot, const Service::Nvidia::MultiFence& multi_fence);
|
void CancelBuffer(u32 slot, const Service::Nvidia::MultiFence& multi_fence);
|
||||||
std::optional<std::reference_wrapper<const Buffer>> AcquireBuffer();
|
std::optional<std::reference_wrapper<const Buffer>> AcquireBuffer();
|
||||||
void ReleaseBuffer(u32 slot);
|
void ReleaseBuffer(u32 slot);
|
||||||
|
void Connect();
|
||||||
void Disconnect();
|
void Disconnect();
|
||||||
u32 Query(QueryType type);
|
u32 Query(QueryType type);
|
||||||
|
|
||||||
|
@ -106,18 +109,28 @@ public:
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsConnected() const {
|
||||||
|
return is_connect;
|
||||||
|
}
|
||||||
|
|
||||||
std::shared_ptr<Kernel::WritableEvent> GetWritableBufferWaitEvent() const;
|
std::shared_ptr<Kernel::WritableEvent> GetWritableBufferWaitEvent() const;
|
||||||
|
|
||||||
std::shared_ptr<Kernel::ReadableEvent> GetBufferWaitEvent() const;
|
std::shared_ptr<Kernel::ReadableEvent> GetBufferWaitEvent() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
u32 id;
|
BufferQueue(const BufferQueue&) = delete;
|
||||||
u64 layer_id;
|
|
||||||
|
u32 id{};
|
||||||
|
u64 layer_id{};
|
||||||
|
std::atomic_bool is_connect{};
|
||||||
|
|
||||||
std::list<u32> free_buffers;
|
std::list<u32> free_buffers;
|
||||||
std::array<Buffer, buffer_slots> buffers;
|
std::array<Buffer, buffer_slots> buffers;
|
||||||
std::list<u32> queue_sequence;
|
std::list<u32> queue_sequence;
|
||||||
Kernel::EventPair buffer_wait_event;
|
Kernel::EventPair buffer_wait_event;
|
||||||
|
|
||||||
|
std::mutex queue_mutex;
|
||||||
|
std::condition_variable condition;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Service::NVFlinger
|
} // namespace Service::NVFlinger
|
||||||
|
|
|
@ -88,6 +88,10 @@ NVFlinger::NVFlinger(Core::System& system) : system(system) {
|
||||||
}
|
}
|
||||||
|
|
||||||
NVFlinger::~NVFlinger() {
|
NVFlinger::~NVFlinger() {
|
||||||
|
for (auto& buffer_queue : buffer_queues) {
|
||||||
|
buffer_queue->Disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
if (system.IsMulticore()) {
|
if (system.IsMulticore()) {
|
||||||
is_running = false;
|
is_running = false;
|
||||||
wait_event->Set();
|
wait_event->Set();
|
||||||
|
@ -104,6 +108,8 @@ void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) {
|
std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) {
|
||||||
|
const auto guard = Lock();
|
||||||
|
|
||||||
LOG_DEBUG(Service, "Opening \"{}\" display", name);
|
LOG_DEBUG(Service, "Opening \"{}\" display", name);
|
||||||
|
|
||||||
// TODO(Subv): Currently we only support the Default display.
|
// TODO(Subv): Currently we only support the Default display.
|
||||||
|
@ -121,6 +127,7 @@ std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<u64> NVFlinger::CreateLayer(u64 display_id) {
|
std::optional<u64> NVFlinger::CreateLayer(u64 display_id) {
|
||||||
|
const auto guard = Lock();
|
||||||
auto* const display = FindDisplay(display_id);
|
auto* const display = FindDisplay(display_id);
|
||||||
|
|
||||||
if (display == nullptr) {
|
if (display == nullptr) {
|
||||||
|
@ -129,18 +136,22 @@ std::optional<u64> NVFlinger::CreateLayer(u64 display_id) {
|
||||||
|
|
||||||
const u64 layer_id = next_layer_id++;
|
const u64 layer_id = next_layer_id++;
|
||||||
const u32 buffer_queue_id = next_buffer_queue_id++;
|
const u32 buffer_queue_id = next_buffer_queue_id++;
|
||||||
buffer_queues.emplace_back(system.Kernel(), buffer_queue_id, layer_id);
|
buffer_queues.emplace_back(
|
||||||
display->CreateLayer(layer_id, buffer_queues.back());
|
std::make_unique<BufferQueue>(system.Kernel(), buffer_queue_id, layer_id));
|
||||||
|
display->CreateLayer(layer_id, *buffer_queues.back());
|
||||||
return layer_id;
|
return layer_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NVFlinger::CloseLayer(u64 layer_id) {
|
void NVFlinger::CloseLayer(u64 layer_id) {
|
||||||
|
const auto guard = Lock();
|
||||||
|
|
||||||
for (auto& display : displays) {
|
for (auto& display : displays) {
|
||||||
display.CloseLayer(layer_id);
|
display.CloseLayer(layer_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<u32> NVFlinger::FindBufferQueueId(u64 display_id, u64 layer_id) const {
|
std::optional<u32> NVFlinger::FindBufferQueueId(u64 display_id, u64 layer_id) const {
|
||||||
|
const auto guard = Lock();
|
||||||
const auto* const layer = FindLayer(display_id, layer_id);
|
const auto* const layer = FindLayer(display_id, layer_id);
|
||||||
|
|
||||||
if (layer == nullptr) {
|
if (layer == nullptr) {
|
||||||
|
@ -151,6 +162,7 @@ std::optional<u32> NVFlinger::FindBufferQueueId(u64 display_id, u64 layer_id) co
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Kernel::ReadableEvent> NVFlinger::FindVsyncEvent(u64 display_id) const {
|
std::shared_ptr<Kernel::ReadableEvent> NVFlinger::FindVsyncEvent(u64 display_id) const {
|
||||||
|
const auto guard = Lock();
|
||||||
auto* const display = FindDisplay(display_id);
|
auto* const display = FindDisplay(display_id);
|
||||||
|
|
||||||
if (display == nullptr) {
|
if (display == nullptr) {
|
||||||
|
@ -160,20 +172,16 @@ std::shared_ptr<Kernel::ReadableEvent> NVFlinger::FindVsyncEvent(u64 display_id)
|
||||||
return display->GetVSyncEvent();
|
return display->GetVSyncEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
BufferQueue& NVFlinger::FindBufferQueue(u32 id) {
|
BufferQueue* NVFlinger::FindBufferQueue(u32 id) {
|
||||||
|
const auto guard = Lock();
|
||||||
const auto itr = std::find_if(buffer_queues.begin(), buffer_queues.end(),
|
const auto itr = std::find_if(buffer_queues.begin(), buffer_queues.end(),
|
||||||
[id](const auto& queue) { return queue.GetId() == id; });
|
[id](const auto& queue) { return queue->GetId() == id; });
|
||||||
|
|
||||||
ASSERT(itr != buffer_queues.end());
|
if (itr == buffer_queues.end()) {
|
||||||
return *itr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
const BufferQueue& NVFlinger::FindBufferQueue(u32 id) const {
|
return itr->get();
|
||||||
const auto itr = std::find_if(buffer_queues.begin(), buffer_queues.end(),
|
|
||||||
[id](const auto& queue) { return queue.GetId() == id; });
|
|
||||||
|
|
||||||
ASSERT(itr != buffer_queues.end());
|
|
||||||
return *itr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
VI::Display* NVFlinger::FindDisplay(u64 display_id) {
|
VI::Display* NVFlinger::FindDisplay(u64 display_id) {
|
||||||
|
|
|
@ -75,10 +75,7 @@ public:
|
||||||
[[nodiscard]] std::shared_ptr<Kernel::ReadableEvent> FindVsyncEvent(u64 display_id) const;
|
[[nodiscard]] std::shared_ptr<Kernel::ReadableEvent> FindVsyncEvent(u64 display_id) const;
|
||||||
|
|
||||||
/// Obtains a buffer queue identified by the ID.
|
/// Obtains a buffer queue identified by the ID.
|
||||||
[[nodiscard]] BufferQueue& FindBufferQueue(u32 id);
|
[[nodiscard]] BufferQueue* FindBufferQueue(u32 id);
|
||||||
|
|
||||||
/// Obtains a buffer queue identified by the ID.
|
|
||||||
[[nodiscard]] const BufferQueue& FindBufferQueue(u32 id) const;
|
|
||||||
|
|
||||||
/// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when
|
/// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when
|
||||||
/// finished.
|
/// finished.
|
||||||
|
@ -86,11 +83,11 @@ public:
|
||||||
|
|
||||||
[[nodiscard]] s64 GetNextTicks() const;
|
[[nodiscard]] s64 GetNextTicks() const;
|
||||||
|
|
||||||
|
private:
|
||||||
[[nodiscard]] std::unique_lock<std::mutex> Lock() const {
|
[[nodiscard]] std::unique_lock<std::mutex> Lock() const {
|
||||||
return std::unique_lock{*guard};
|
return std::unique_lock{*guard};
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
|
||||||
/// Finds the display identified by the specified ID.
|
/// Finds the display identified by the specified ID.
|
||||||
[[nodiscard]] VI::Display* FindDisplay(u64 display_id);
|
[[nodiscard]] VI::Display* FindDisplay(u64 display_id);
|
||||||
|
|
||||||
|
@ -110,7 +107,7 @@ private:
|
||||||
std::shared_ptr<Nvidia::Module> nvdrv;
|
std::shared_ptr<Nvidia::Module> nvdrv;
|
||||||
|
|
||||||
std::vector<VI::Display> displays;
|
std::vector<VI::Display> displays;
|
||||||
std::vector<BufferQueue> buffer_queues;
|
std::vector<std::unique_ptr<BufferQueue>> buffer_queues;
|
||||||
|
|
||||||
/// Id to use for the next layer that is created, this counter is shared among all displays.
|
/// Id to use for the next layer that is created, this counter is shared among all displays.
|
||||||
u64 next_layer_id = 1;
|
u64 next_layer_id = 1;
|
||||||
|
|
|
@ -95,9 +95,14 @@ ServiceFrameworkBase::ServiceFrameworkBase(Core::System& system_, const char* se
|
||||||
: system{system_}, service_name{service_name_}, max_sessions{max_sessions_},
|
: system{system_}, service_name{service_name_}, max_sessions{max_sessions_},
|
||||||
handler_invoker{handler_invoker_} {}
|
handler_invoker{handler_invoker_} {}
|
||||||
|
|
||||||
ServiceFrameworkBase::~ServiceFrameworkBase() = default;
|
ServiceFrameworkBase::~ServiceFrameworkBase() {
|
||||||
|
// Wait for other threads to release access before destroying
|
||||||
|
const auto guard = LockService();
|
||||||
|
}
|
||||||
|
|
||||||
void ServiceFrameworkBase::InstallAsService(SM::ServiceManager& service_manager) {
|
void ServiceFrameworkBase::InstallAsService(SM::ServiceManager& service_manager) {
|
||||||
|
const auto guard = LockService();
|
||||||
|
|
||||||
ASSERT(!port_installed);
|
ASSERT(!port_installed);
|
||||||
|
|
||||||
auto port = service_manager.RegisterService(service_name, max_sessions).Unwrap();
|
auto port = service_manager.RegisterService(service_name, max_sessions).Unwrap();
|
||||||
|
@ -106,6 +111,8 @@ void ServiceFrameworkBase::InstallAsService(SM::ServiceManager& service_manager)
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServiceFrameworkBase::InstallAsNamedPort(Kernel::KernelCore& kernel) {
|
void ServiceFrameworkBase::InstallAsNamedPort(Kernel::KernelCore& kernel) {
|
||||||
|
const auto guard = LockService();
|
||||||
|
|
||||||
ASSERT(!port_installed);
|
ASSERT(!port_installed);
|
||||||
|
|
||||||
auto [server_port, client_port] =
|
auto [server_port, client_port] =
|
||||||
|
@ -115,17 +122,6 @@ void ServiceFrameworkBase::InstallAsNamedPort(Kernel::KernelCore& kernel) {
|
||||||
port_installed = true;
|
port_installed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Kernel::ClientPort> ServiceFrameworkBase::CreatePort(Kernel::KernelCore& kernel) {
|
|
||||||
ASSERT(!port_installed);
|
|
||||||
|
|
||||||
auto [server_port, client_port] =
|
|
||||||
Kernel::ServerPort::CreatePortPair(kernel, max_sessions, service_name);
|
|
||||||
auto port = MakeResult(std::move(server_port)).Unwrap();
|
|
||||||
port->SetHleHandler(shared_from_this());
|
|
||||||
port_installed = true;
|
|
||||||
return client_port;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ServiceFrameworkBase::RegisterHandlersBase(const FunctionInfoBase* functions, std::size_t n) {
|
void ServiceFrameworkBase::RegisterHandlersBase(const FunctionInfoBase* functions, std::size_t n) {
|
||||||
handlers.reserve(handlers.size() + n);
|
handlers.reserve(handlers.size() + n);
|
||||||
for (std::size_t i = 0; i < n; ++i) {
|
for (std::size_t i = 0; i < n; ++i) {
|
||||||
|
@ -164,6 +160,8 @@ void ServiceFrameworkBase::InvokeRequest(Kernel::HLERequestContext& ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& context) {
|
ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& context) {
|
||||||
|
const auto guard = LockService();
|
||||||
|
|
||||||
switch (context.GetCommandType()) {
|
switch (context.GetCommandType()) {
|
||||||
case IPC::CommandType::Close: {
|
case IPC::CommandType::Close: {
|
||||||
IPC::ResponseBuilder rb{context, 2};
|
IPC::ResponseBuilder rb{context, 2};
|
||||||
|
@ -184,7 +182,11 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& co
|
||||||
UNIMPLEMENTED_MSG("command_type={}", context.GetCommandType());
|
UNIMPLEMENTED_MSG("command_type={}", context.GetCommandType());
|
||||||
}
|
}
|
||||||
|
|
||||||
context.WriteToOutgoingCommandBuffer(context.GetThread());
|
// If emulation was shutdown, we are closing service threads, do not write the response back to
|
||||||
|
// memory that may be shutting down as well.
|
||||||
|
if (system.IsPoweredOn()) {
|
||||||
|
context.WriteToOutgoingCommandBuffer(context.GetThread());
|
||||||
|
}
|
||||||
|
|
||||||
return RESULT_SUCCESS;
|
return RESULT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,9 +5,11 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
#include <mutex>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <boost/container/flat_map.hpp>
|
#include <boost/container/flat_map.hpp>
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
#include "common/spin_lock.h"
|
||||||
#include "core/hle/kernel/hle_ipc.h"
|
#include "core/hle/kernel/hle_ipc.h"
|
||||||
#include "core/hle/kernel/object.h"
|
#include "core/hle/kernel/object.h"
|
||||||
|
|
||||||
|
@ -68,11 +70,9 @@ public:
|
||||||
void InstallAsService(SM::ServiceManager& service_manager);
|
void InstallAsService(SM::ServiceManager& service_manager);
|
||||||
/// Creates a port pair and registers it on the kernel's global port registry.
|
/// Creates a port pair and registers it on the kernel's global port registry.
|
||||||
void InstallAsNamedPort(Kernel::KernelCore& kernel);
|
void InstallAsNamedPort(Kernel::KernelCore& kernel);
|
||||||
/// Creates and returns an unregistered port for the service.
|
/// Invokes a service request routine.
|
||||||
std::shared_ptr<Kernel::ClientPort> CreatePort(Kernel::KernelCore& kernel);
|
|
||||||
|
|
||||||
void InvokeRequest(Kernel::HLERequestContext& ctx);
|
void InvokeRequest(Kernel::HLERequestContext& ctx);
|
||||||
|
/// Handles a synchronization request for the service.
|
||||||
ResultCode HandleSyncRequest(Kernel::HLERequestContext& context) override;
|
ResultCode HandleSyncRequest(Kernel::HLERequestContext& context) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -80,6 +80,11 @@ protected:
|
||||||
template <typename Self>
|
template <typename Self>
|
||||||
using HandlerFnP = void (Self::*)(Kernel::HLERequestContext&);
|
using HandlerFnP = void (Self::*)(Kernel::HLERequestContext&);
|
||||||
|
|
||||||
|
/// Used to gain exclusive access to the service members, e.g. from CoreTiming thread.
|
||||||
|
[[nodiscard]] std::scoped_lock<Common::SpinLock> LockService() {
|
||||||
|
return std::scoped_lock{lock_service};
|
||||||
|
}
|
||||||
|
|
||||||
/// System context that the service operates under.
|
/// System context that the service operates under.
|
||||||
Core::System& system;
|
Core::System& system;
|
||||||
|
|
||||||
|
@ -115,6 +120,9 @@ private:
|
||||||
/// Function used to safely up-cast pointers to the derived class before invoking a handler.
|
/// Function used to safely up-cast pointers to the derived class before invoking a handler.
|
||||||
InvokerFn* handler_invoker;
|
InvokerFn* handler_invoker;
|
||||||
boost::container::flat_map<u32, FunctionInfoBase> handlers;
|
boost::container::flat_map<u32, FunctionInfoBase> handlers;
|
||||||
|
|
||||||
|
/// Used to gain exclusive access to the service members, e.g. from CoreTiming thread.
|
||||||
|
Common::SpinLock lock_service;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -178,13 +178,12 @@ void BSD::Poll(Kernel::HLERequestContext& ctx) {
|
||||||
|
|
||||||
LOG_DEBUG(Service, "called. nfds={} timeout={}", nfds, timeout);
|
LOG_DEBUG(Service, "called. nfds={} timeout={}", nfds, timeout);
|
||||||
|
|
||||||
ExecuteWork(ctx, "BSD:Poll", timeout != 0,
|
ExecuteWork(ctx, PollWork{
|
||||||
PollWork{
|
.nfds = nfds,
|
||||||
.nfds = nfds,
|
.timeout = timeout,
|
||||||
.timeout = timeout,
|
.read_buffer = ctx.ReadBuffer(),
|
||||||
.read_buffer = ctx.ReadBuffer(),
|
.write_buffer = std::vector<u8>(ctx.GetWriteBufferSize()),
|
||||||
.write_buffer = std::vector<u8>(ctx.GetWriteBufferSize()),
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BSD::Accept(Kernel::HLERequestContext& ctx) {
|
void BSD::Accept(Kernel::HLERequestContext& ctx) {
|
||||||
|
@ -193,11 +192,10 @@ void BSD::Accept(Kernel::HLERequestContext& ctx) {
|
||||||
|
|
||||||
LOG_DEBUG(Service, "called. fd={}", fd);
|
LOG_DEBUG(Service, "called. fd={}", fd);
|
||||||
|
|
||||||
ExecuteWork(ctx, "BSD:Accept", IsBlockingSocket(fd),
|
ExecuteWork(ctx, AcceptWork{
|
||||||
AcceptWork{
|
.fd = fd,
|
||||||
.fd = fd,
|
.write_buffer = std::vector<u8>(ctx.GetWriteBufferSize()),
|
||||||
.write_buffer = std::vector<u8>(ctx.GetWriteBufferSize()),
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BSD::Bind(Kernel::HLERequestContext& ctx) {
|
void BSD::Bind(Kernel::HLERequestContext& ctx) {
|
||||||
|
@ -215,11 +213,10 @@ void BSD::Connect(Kernel::HLERequestContext& ctx) {
|
||||||
|
|
||||||
LOG_DEBUG(Service, "called. fd={} addrlen={}", fd, ctx.GetReadBufferSize());
|
LOG_DEBUG(Service, "called. fd={} addrlen={}", fd, ctx.GetReadBufferSize());
|
||||||
|
|
||||||
ExecuteWork(ctx, "BSD:Connect", IsBlockingSocket(fd),
|
ExecuteWork(ctx, ConnectWork{
|
||||||
ConnectWork{
|
.fd = fd,
|
||||||
.fd = fd,
|
.addr = ctx.ReadBuffer(),
|
||||||
.addr = ctx.ReadBuffer(),
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BSD::GetPeerName(Kernel::HLERequestContext& ctx) {
|
void BSD::GetPeerName(Kernel::HLERequestContext& ctx) {
|
||||||
|
@ -327,12 +324,11 @@ void BSD::Recv(Kernel::HLERequestContext& ctx) {
|
||||||
|
|
||||||
LOG_DEBUG(Service, "called. fd={} flags=0x{:x} len={}", fd, flags, ctx.GetWriteBufferSize());
|
LOG_DEBUG(Service, "called. fd={} flags=0x{:x} len={}", fd, flags, ctx.GetWriteBufferSize());
|
||||||
|
|
||||||
ExecuteWork(ctx, "BSD:Recv", IsBlockingSocket(fd),
|
ExecuteWork(ctx, RecvWork{
|
||||||
RecvWork{
|
.fd = fd,
|
||||||
.fd = fd,
|
.flags = flags,
|
||||||
.flags = flags,
|
.message = std::vector<u8>(ctx.GetWriteBufferSize()),
|
||||||
.message = std::vector<u8>(ctx.GetWriteBufferSize()),
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BSD::RecvFrom(Kernel::HLERequestContext& ctx) {
|
void BSD::RecvFrom(Kernel::HLERequestContext& ctx) {
|
||||||
|
@ -344,13 +340,12 @@ void BSD::RecvFrom(Kernel::HLERequestContext& ctx) {
|
||||||
LOG_DEBUG(Service, "called. fd={} flags=0x{:x} len={} addrlen={}", fd, flags,
|
LOG_DEBUG(Service, "called. fd={} flags=0x{:x} len={} addrlen={}", fd, flags,
|
||||||
ctx.GetWriteBufferSize(0), ctx.GetWriteBufferSize(1));
|
ctx.GetWriteBufferSize(0), ctx.GetWriteBufferSize(1));
|
||||||
|
|
||||||
ExecuteWork(ctx, "BSD:RecvFrom", IsBlockingSocket(fd),
|
ExecuteWork(ctx, RecvFromWork{
|
||||||
RecvFromWork{
|
.fd = fd,
|
||||||
.fd = fd,
|
.flags = flags,
|
||||||
.flags = flags,
|
.message = std::vector<u8>(ctx.GetWriteBufferSize(0)),
|
||||||
.message = std::vector<u8>(ctx.GetWriteBufferSize(0)),
|
.addr = std::vector<u8>(ctx.GetWriteBufferSize(1)),
|
||||||
.addr = std::vector<u8>(ctx.GetWriteBufferSize(1)),
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BSD::Send(Kernel::HLERequestContext& ctx) {
|
void BSD::Send(Kernel::HLERequestContext& ctx) {
|
||||||
|
@ -361,12 +356,11 @@ void BSD::Send(Kernel::HLERequestContext& ctx) {
|
||||||
|
|
||||||
LOG_DEBUG(Service, "called. fd={} flags=0x{:x} len={}", fd, flags, ctx.GetReadBufferSize());
|
LOG_DEBUG(Service, "called. fd={} flags=0x{:x} len={}", fd, flags, ctx.GetReadBufferSize());
|
||||||
|
|
||||||
ExecuteWork(ctx, "BSD:Send", IsBlockingSocket(fd),
|
ExecuteWork(ctx, SendWork{
|
||||||
SendWork{
|
.fd = fd,
|
||||||
.fd = fd,
|
.flags = flags,
|
||||||
.flags = flags,
|
.message = ctx.ReadBuffer(),
|
||||||
.message = ctx.ReadBuffer(),
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BSD::SendTo(Kernel::HLERequestContext& ctx) {
|
void BSD::SendTo(Kernel::HLERequestContext& ctx) {
|
||||||
|
@ -377,13 +371,12 @@ void BSD::SendTo(Kernel::HLERequestContext& ctx) {
|
||||||
LOG_DEBUG(Service, "called. fd={} flags=0x{} len={} addrlen={}", fd, flags,
|
LOG_DEBUG(Service, "called. fd={} flags=0x{} len={} addrlen={}", fd, flags,
|
||||||
ctx.GetReadBufferSize(0), ctx.GetReadBufferSize(1));
|
ctx.GetReadBufferSize(0), ctx.GetReadBufferSize(1));
|
||||||
|
|
||||||
ExecuteWork(ctx, "BSD:SendTo", IsBlockingSocket(fd),
|
ExecuteWork(ctx, SendToWork{
|
||||||
SendToWork{
|
.fd = fd,
|
||||||
.fd = fd,
|
.flags = flags,
|
||||||
.flags = flags,
|
.message = ctx.ReadBuffer(0),
|
||||||
.message = ctx.ReadBuffer(0),
|
.addr = ctx.ReadBuffer(1),
|
||||||
.addr = ctx.ReadBuffer(1),
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BSD::Write(Kernel::HLERequestContext& ctx) {
|
void BSD::Write(Kernel::HLERequestContext& ctx) {
|
||||||
|
@ -392,12 +385,11 @@ void BSD::Write(Kernel::HLERequestContext& ctx) {
|
||||||
|
|
||||||
LOG_DEBUG(Service, "called. fd={} len={}", fd, ctx.GetReadBufferSize());
|
LOG_DEBUG(Service, "called. fd={} len={}", fd, ctx.GetReadBufferSize());
|
||||||
|
|
||||||
ExecuteWork(ctx, "BSD:Write", IsBlockingSocket(fd),
|
ExecuteWork(ctx, SendWork{
|
||||||
SendWork{
|
.fd = fd,
|
||||||
.fd = fd,
|
.flags = 0,
|
||||||
.flags = 0,
|
.message = ctx.ReadBuffer(),
|
||||||
.message = ctx.ReadBuffer(),
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BSD::Close(Kernel::HLERequestContext& ctx) {
|
void BSD::Close(Kernel::HLERequestContext& ctx) {
|
||||||
|
@ -410,24 +402,9 @@ void BSD::Close(Kernel::HLERequestContext& ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Work>
|
template <typename Work>
|
||||||
void BSD::ExecuteWork(Kernel::HLERequestContext& ctx, std::string_view sleep_reason,
|
void BSD::ExecuteWork(Kernel::HLERequestContext& ctx, Work work) {
|
||||||
bool is_blocking, Work work) {
|
work.Execute(this);
|
||||||
if (!is_blocking) {
|
|
||||||
work.Execute(this);
|
|
||||||
work.Response(ctx);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Signal a dummy response to make IPC validation happy
|
|
||||||
// This will be overwritten by the SleepClientThread callback
|
|
||||||
work.Response(ctx);
|
work.Response(ctx);
|
||||||
|
|
||||||
auto worker = worker_pool.CaptureWorker();
|
|
||||||
|
|
||||||
ctx.SleepClientThread(std::string(sleep_reason), std::numeric_limits<u64>::max(),
|
|
||||||
worker->Callback<Work>(), worker->KernelEvent());
|
|
||||||
|
|
||||||
worker->SendWork(std::move(work));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<s32, Errno> BSD::SocketImpl(Domain domain, Type type, Protocol protocol) {
|
std::pair<s32, Errno> BSD::SocketImpl(Domain domain, Type type, Protocol protocol) {
|
||||||
|
@ -807,18 +784,6 @@ bool BSD::IsFileDescriptorValid(s32 fd) const noexcept {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BSD::IsBlockingSocket(s32 fd) const noexcept {
|
|
||||||
// Inform invalid sockets as non-blocking
|
|
||||||
// This way we avoid using a worker thread as it will fail without blocking host
|
|
||||||
if (fd > static_cast<s32>(MAX_FD) || fd < 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!file_descriptors[fd]) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return (file_descriptors[fd]->flags & FLAG_O_NONBLOCK) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BSD::BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) const noexcept {
|
void BSD::BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) const noexcept {
|
||||||
IPC::ResponseBuilder rb{ctx, 4};
|
IPC::ResponseBuilder rb{ctx, 4};
|
||||||
|
|
||||||
|
@ -827,8 +792,7 @@ void BSD::BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) co
|
||||||
rb.PushEnum(bsd_errno);
|
rb.PushEnum(bsd_errno);
|
||||||
}
|
}
|
||||||
|
|
||||||
BSD::BSD(Core::System& system_, const char* name)
|
BSD::BSD(Core::System& system_, const char* name) : ServiceFramework{system_, name} {
|
||||||
: ServiceFramework{system_, name}, worker_pool{system_, this} {
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
static const FunctionInfo functions[] = {
|
static const FunctionInfo functions[] = {
|
||||||
{0, &BSD::RegisterClient, "RegisterClient"},
|
{0, &BSD::RegisterClient, "RegisterClient"},
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "core/hle/kernel/hle_ipc.h"
|
#include "core/hle/kernel/hle_ipc.h"
|
||||||
#include "core/hle/service/service.h"
|
#include "core/hle/service/service.h"
|
||||||
#include "core/hle/service/sockets/blocking_worker.h"
|
|
||||||
#include "core/hle/service/sockets/sockets.h"
|
#include "core/hle/service/sockets/sockets.h"
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
@ -138,8 +137,7 @@ private:
|
||||||
void Close(Kernel::HLERequestContext& ctx);
|
void Close(Kernel::HLERequestContext& ctx);
|
||||||
|
|
||||||
template <typename Work>
|
template <typename Work>
|
||||||
void ExecuteWork(Kernel::HLERequestContext& ctx, std::string_view sleep_reason,
|
void ExecuteWork(Kernel::HLERequestContext& ctx, Work work);
|
||||||
bool is_blocking, Work work);
|
|
||||||
|
|
||||||
std::pair<s32, Errno> SocketImpl(Domain domain, Type type, Protocol protocol);
|
std::pair<s32, Errno> SocketImpl(Domain domain, Type type, Protocol protocol);
|
||||||
std::pair<s32, Errno> PollImpl(std::vector<u8>& write_buffer, std::vector<u8> read_buffer,
|
std::pair<s32, Errno> PollImpl(std::vector<u8>& write_buffer, std::vector<u8> read_buffer,
|
||||||
|
@ -163,15 +161,10 @@ private:
|
||||||
|
|
||||||
s32 FindFreeFileDescriptorHandle() noexcept;
|
s32 FindFreeFileDescriptorHandle() noexcept;
|
||||||
bool IsFileDescriptorValid(s32 fd) const noexcept;
|
bool IsFileDescriptorValid(s32 fd) const noexcept;
|
||||||
bool IsBlockingSocket(s32 fd) const noexcept;
|
|
||||||
|
|
||||||
void BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) const noexcept;
|
void BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) const noexcept;
|
||||||
|
|
||||||
std::array<std::optional<FileDescriptor>, MAX_FD> file_descriptors;
|
std::array<std::optional<FileDescriptor>, MAX_FD> file_descriptors;
|
||||||
|
|
||||||
BlockingWorkerPool<BSD, PollWork, AcceptWork, ConnectWork, RecvWork, RecvFromWork, SendWork,
|
|
||||||
SendToWork>
|
|
||||||
worker_pool;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class BSDCFG final : public ServiceFramework<BSDCFG> {
|
class BSDCFG final : public ServiceFramework<BSDCFG> {
|
||||||
|
|
|
@ -536,8 +536,7 @@ private:
|
||||||
LOG_DEBUG(Service_VI, "called. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id,
|
LOG_DEBUG(Service_VI, "called. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id,
|
||||||
transaction, flags);
|
transaction, flags);
|
||||||
|
|
||||||
const auto guard = nv_flinger.Lock();
|
auto& buffer_queue = *nv_flinger.FindBufferQueue(id);
|
||||||
auto& buffer_queue = nv_flinger.FindBufferQueue(id);
|
|
||||||
|
|
||||||
switch (transaction) {
|
switch (transaction) {
|
||||||
case TransactionId::Connect: {
|
case TransactionId::Connect: {
|
||||||
|
@ -547,6 +546,9 @@ private:
|
||||||
Settings::values.resolution_factor.GetValue()),
|
Settings::values.resolution_factor.GetValue()),
|
||||||
static_cast<u32>(static_cast<u32>(DisplayResolution::UndockedHeight) *
|
static_cast<u32>(static_cast<u32>(DisplayResolution::UndockedHeight) *
|
||||||
Settings::values.resolution_factor.GetValue())};
|
Settings::values.resolution_factor.GetValue())};
|
||||||
|
|
||||||
|
buffer_queue.Connect();
|
||||||
|
|
||||||
ctx.WriteBuffer(response.Serialize());
|
ctx.WriteBuffer(response.Serialize());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -563,40 +565,25 @@ private:
|
||||||
IGBPDequeueBufferRequestParcel request{ctx.ReadBuffer()};
|
IGBPDequeueBufferRequestParcel request{ctx.ReadBuffer()};
|
||||||
const u32 width{request.data.width};
|
const u32 width{request.data.width};
|
||||||
const u32 height{request.data.height};
|
const u32 height{request.data.height};
|
||||||
auto result = buffer_queue.DequeueBuffer(width, height);
|
|
||||||
|
|
||||||
if (result) {
|
do {
|
||||||
// Buffer is available
|
if (auto result = buffer_queue.DequeueBuffer(width, height); result) {
|
||||||
IGBPDequeueBufferResponseParcel response{result->first, *result->second};
|
// Buffer is available
|
||||||
ctx.WriteBuffer(response.Serialize());
|
IGBPDequeueBufferResponseParcel response{result->first, *result->second};
|
||||||
} else {
|
ctx.WriteBuffer(response.Serialize());
|
||||||
// Wait the current thread until a buffer becomes available
|
break;
|
||||||
ctx.SleepClientThread(
|
}
|
||||||
"IHOSBinderDriver::DequeueBuffer", UINT64_MAX,
|
} while (buffer_queue.IsConnected());
|
||||||
[=, this](std::shared_ptr<Kernel::Thread> thread,
|
|
||||||
Kernel::HLERequestContext& ctx, Kernel::ThreadWakeupReason reason) {
|
|
||||||
// Repeat TransactParcel DequeueBuffer when a buffer is available
|
|
||||||
const auto guard = nv_flinger.Lock();
|
|
||||||
auto& buffer_queue = nv_flinger.FindBufferQueue(id);
|
|
||||||
auto result = buffer_queue.DequeueBuffer(width, height);
|
|
||||||
ASSERT_MSG(result != std::nullopt, "Could not dequeue buffer.");
|
|
||||||
|
|
||||||
IGBPDequeueBufferResponseParcel response{result->first, *result->second};
|
|
||||||
ctx.WriteBuffer(response.Serialize());
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
|
||||||
rb.Push(RESULT_SUCCESS);
|
|
||||||
},
|
|
||||||
buffer_queue.GetWritableBufferWaitEvent());
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case TransactionId::RequestBuffer: {
|
case TransactionId::RequestBuffer: {
|
||||||
IGBPRequestBufferRequestParcel request{ctx.ReadBuffer()};
|
IGBPRequestBufferRequestParcel request{ctx.ReadBuffer()};
|
||||||
|
|
||||||
auto& buffer = buffer_queue.RequestBuffer(request.slot);
|
auto& buffer = buffer_queue.RequestBuffer(request.slot);
|
||||||
|
|
||||||
IGBPRequestBufferResponseParcel response{buffer};
|
IGBPRequestBufferResponseParcel response{buffer};
|
||||||
ctx.WriteBuffer(response.Serialize());
|
ctx.WriteBuffer(response.Serialize());
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case TransactionId::QueueBuffer: {
|
case TransactionId::QueueBuffer: {
|
||||||
|
@ -682,7 +669,7 @@ private:
|
||||||
|
|
||||||
LOG_WARNING(Service_VI, "(STUBBED) called id={}, unknown={:08X}", id, unknown);
|
LOG_WARNING(Service_VI, "(STUBBED) called id={}, unknown={:08X}", id, unknown);
|
||||||
|
|
||||||
const auto& buffer_queue = nv_flinger.FindBufferQueue(id);
|
const auto& buffer_queue = *nv_flinger.FindBufferQueue(id);
|
||||||
|
|
||||||
// TODO(Subv): Find out what this actually is.
|
// TODO(Subv): Find out what this actually is.
|
||||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <mutex>
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
@ -68,21 +67,8 @@ struct Memory::Impl {
|
||||||
|
|
||||||
bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr) const {
|
bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr) const {
|
||||||
const auto& page_table = process.PageTable().PageTableImpl();
|
const auto& page_table = process.PageTable().PageTableImpl();
|
||||||
|
const auto [pointer, type] = page_table.pointers[vaddr >> PAGE_BITS].PointerType();
|
||||||
const u8* const page_pointer = page_table.pointers[vaddr >> PAGE_BITS];
|
return pointer != nullptr || type == Common::PageType::RasterizerCachedMemory;
|
||||||
if (page_pointer != nullptr) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (page_table.attributes[vaddr >> PAGE_BITS] == Common::PageType::RasterizerCachedMemory) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (page_table.attributes[vaddr >> PAGE_BITS] != Common::PageType::Special) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsValidVirtualAddress(VAddr vaddr) const {
|
bool IsValidVirtualAddress(VAddr vaddr) const {
|
||||||
|
@ -100,17 +86,15 @@ struct Memory::Impl {
|
||||||
}
|
}
|
||||||
|
|
||||||
u8* GetPointer(const VAddr vaddr) const {
|
u8* GetPointer(const VAddr vaddr) const {
|
||||||
u8* const page_pointer{current_page_table->pointers[vaddr >> PAGE_BITS]};
|
const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw();
|
||||||
if (page_pointer) {
|
if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) {
|
||||||
return page_pointer + vaddr;
|
return pointer + vaddr;
|
||||||
}
|
}
|
||||||
|
const auto type = Common::PageTable::PageInfo::ExtractType(raw_pointer);
|
||||||
if (current_page_table->attributes[vaddr >> PAGE_BITS] ==
|
if (type == Common::PageType::RasterizerCachedMemory) {
|
||||||
Common::PageType::RasterizerCachedMemory) {
|
|
||||||
return GetPointerFromRasterizerCachedMemory(vaddr);
|
return GetPointerFromRasterizerCachedMemory(vaddr);
|
||||||
}
|
}
|
||||||
|
return nullptr;
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u8 Read8(const VAddr addr) {
|
u8 Read8(const VAddr addr) {
|
||||||
|
@ -222,7 +206,8 @@ struct Memory::Impl {
|
||||||
std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
|
std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
|
||||||
const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
|
const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
|
||||||
|
|
||||||
switch (page_table.attributes[page_index]) {
|
const auto [pointer, type] = page_table.pointers[page_index].PointerType();
|
||||||
|
switch (type) {
|
||||||
case Common::PageType::Unmapped: {
|
case Common::PageType::Unmapped: {
|
||||||
LOG_ERROR(HW_Memory,
|
LOG_ERROR(HW_Memory,
|
||||||
"Unmapped ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
|
"Unmapped ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
|
||||||
|
@ -231,10 +216,8 @@ struct Memory::Impl {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Common::PageType::Memory: {
|
case Common::PageType::Memory: {
|
||||||
DEBUG_ASSERT(page_table.pointers[page_index]);
|
DEBUG_ASSERT(pointer);
|
||||||
|
const u8* const src_ptr = pointer + page_offset + (page_index << PAGE_BITS);
|
||||||
const u8* const src_ptr =
|
|
||||||
page_table.pointers[page_index] + page_offset + (page_index << PAGE_BITS);
|
|
||||||
std::memcpy(dest_buffer, src_ptr, copy_amount);
|
std::memcpy(dest_buffer, src_ptr, copy_amount);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -268,7 +251,8 @@ struct Memory::Impl {
|
||||||
std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
|
std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
|
||||||
const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
|
const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
|
||||||
|
|
||||||
switch (page_table.attributes[page_index]) {
|
const auto [pointer, type] = page_table.pointers[page_index].PointerType();
|
||||||
|
switch (type) {
|
||||||
case Common::PageType::Unmapped: {
|
case Common::PageType::Unmapped: {
|
||||||
LOG_ERROR(HW_Memory,
|
LOG_ERROR(HW_Memory,
|
||||||
"Unmapped ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
|
"Unmapped ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
|
||||||
|
@ -277,10 +261,8 @@ struct Memory::Impl {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Common::PageType::Memory: {
|
case Common::PageType::Memory: {
|
||||||
DEBUG_ASSERT(page_table.pointers[page_index]);
|
DEBUG_ASSERT(pointer);
|
||||||
|
const u8* const src_ptr = pointer + page_offset + (page_index << PAGE_BITS);
|
||||||
const u8* const src_ptr =
|
|
||||||
page_table.pointers[page_index] + page_offset + (page_index << PAGE_BITS);
|
|
||||||
std::memcpy(dest_buffer, src_ptr, copy_amount);
|
std::memcpy(dest_buffer, src_ptr, copy_amount);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -320,7 +302,8 @@ struct Memory::Impl {
|
||||||
std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
|
std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
|
||||||
const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
|
const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
|
||||||
|
|
||||||
switch (page_table.attributes[page_index]) {
|
const auto [pointer, type] = page_table.pointers[page_index].PointerType();
|
||||||
|
switch (type) {
|
||||||
case Common::PageType::Unmapped: {
|
case Common::PageType::Unmapped: {
|
||||||
LOG_ERROR(HW_Memory,
|
LOG_ERROR(HW_Memory,
|
||||||
"Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
|
"Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
|
||||||
|
@ -328,10 +311,8 @@ struct Memory::Impl {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Common::PageType::Memory: {
|
case Common::PageType::Memory: {
|
||||||
DEBUG_ASSERT(page_table.pointers[page_index]);
|
DEBUG_ASSERT(pointer);
|
||||||
|
u8* const dest_ptr = pointer + page_offset + (page_index << PAGE_BITS);
|
||||||
u8* const dest_ptr =
|
|
||||||
page_table.pointers[page_index] + page_offset + (page_index << PAGE_BITS);
|
|
||||||
std::memcpy(dest_ptr, src_buffer, copy_amount);
|
std::memcpy(dest_ptr, src_buffer, copy_amount);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -364,7 +345,8 @@ struct Memory::Impl {
|
||||||
std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
|
std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
|
||||||
const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
|
const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
|
||||||
|
|
||||||
switch (page_table.attributes[page_index]) {
|
const auto [pointer, type] = page_table.pointers[page_index].PointerType();
|
||||||
|
switch (type) {
|
||||||
case Common::PageType::Unmapped: {
|
case Common::PageType::Unmapped: {
|
||||||
LOG_ERROR(HW_Memory,
|
LOG_ERROR(HW_Memory,
|
||||||
"Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
|
"Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
|
||||||
|
@ -372,10 +354,8 @@ struct Memory::Impl {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Common::PageType::Memory: {
|
case Common::PageType::Memory: {
|
||||||
DEBUG_ASSERT(page_table.pointers[page_index]);
|
DEBUG_ASSERT(pointer);
|
||||||
|
u8* const dest_ptr = pointer + page_offset + (page_index << PAGE_BITS);
|
||||||
u8* const dest_ptr =
|
|
||||||
page_table.pointers[page_index] + page_offset + (page_index << PAGE_BITS);
|
|
||||||
std::memcpy(dest_ptr, src_buffer, copy_amount);
|
std::memcpy(dest_ptr, src_buffer, copy_amount);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -414,7 +394,8 @@ struct Memory::Impl {
|
||||||
std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
|
std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
|
||||||
const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
|
const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
|
||||||
|
|
||||||
switch (page_table.attributes[page_index]) {
|
const auto [pointer, type] = page_table.pointers[page_index].PointerType();
|
||||||
|
switch (type) {
|
||||||
case Common::PageType::Unmapped: {
|
case Common::PageType::Unmapped: {
|
||||||
LOG_ERROR(HW_Memory,
|
LOG_ERROR(HW_Memory,
|
||||||
"Unmapped ZeroBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
|
"Unmapped ZeroBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
|
||||||
|
@ -422,10 +403,8 @@ struct Memory::Impl {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Common::PageType::Memory: {
|
case Common::PageType::Memory: {
|
||||||
DEBUG_ASSERT(page_table.pointers[page_index]);
|
DEBUG_ASSERT(pointer);
|
||||||
|
u8* const dest_ptr = pointer + page_offset + (page_index << PAGE_BITS);
|
||||||
u8* dest_ptr =
|
|
||||||
page_table.pointers[page_index] + page_offset + (page_index << PAGE_BITS);
|
|
||||||
std::memset(dest_ptr, 0, copy_amount);
|
std::memset(dest_ptr, 0, copy_amount);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -461,7 +440,8 @@ struct Memory::Impl {
|
||||||
std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
|
std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
|
||||||
const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
|
const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
|
||||||
|
|
||||||
switch (page_table.attributes[page_index]) {
|
const auto [pointer, type] = page_table.pointers[page_index].PointerType();
|
||||||
|
switch (type) {
|
||||||
case Common::PageType::Unmapped: {
|
case Common::PageType::Unmapped: {
|
||||||
LOG_ERROR(HW_Memory,
|
LOG_ERROR(HW_Memory,
|
||||||
"Unmapped CopyBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
|
"Unmapped CopyBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
|
||||||
|
@ -470,9 +450,8 @@ struct Memory::Impl {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Common::PageType::Memory: {
|
case Common::PageType::Memory: {
|
||||||
DEBUG_ASSERT(page_table.pointers[page_index]);
|
DEBUG_ASSERT(pointer);
|
||||||
const u8* src_ptr =
|
const u8* src_ptr = pointer + page_offset + (page_index << PAGE_BITS);
|
||||||
page_table.pointers[page_index] + page_offset + (page_index << PAGE_BITS);
|
|
||||||
WriteBlock(process, dest_addr, src_ptr, copy_amount);
|
WriteBlock(process, dest_addr, src_ptr, copy_amount);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -498,34 +477,19 @@ struct Memory::Impl {
|
||||||
return CopyBlock(*system.CurrentProcess(), dest_addr, src_addr, size);
|
return CopyBlock(*system.CurrentProcess(), dest_addr, src_addr, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct PageEntry {
|
|
||||||
u8* const pointer;
|
|
||||||
const Common::PageType attribute;
|
|
||||||
};
|
|
||||||
|
|
||||||
PageEntry SafePageEntry(std::size_t base) const {
|
|
||||||
std::lock_guard lock{rasterizer_cache_guard};
|
|
||||||
return {
|
|
||||||
.pointer = current_page_table->pointers[base],
|
|
||||||
.attribute = current_page_table->attributes[base],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) {
|
void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) {
|
||||||
std::lock_guard lock{rasterizer_cache_guard};
|
|
||||||
if (vaddr == 0) {
|
if (vaddr == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Iterate over a contiguous CPU address space, which corresponds to the specified GPU
|
// Iterate over a contiguous CPU address space, which corresponds to the specified GPU
|
||||||
// address space, marking the region as un/cached. The region is marked un/cached at a
|
// address space, marking the region as un/cached. The region is marked un/cached at a
|
||||||
// granularity of CPU pages, hence why we iterate on a CPU page basis (note: GPU page size
|
// granularity of CPU pages, hence why we iterate on a CPU page basis (note: GPU page size
|
||||||
// is different). This assumes the specified GPU address region is contiguous as well.
|
// is different). This assumes the specified GPU address region is contiguous as well.
|
||||||
|
|
||||||
u64 num_pages = ((vaddr + size - 1) >> PAGE_BITS) - (vaddr >> PAGE_BITS) + 1;
|
const u64 num_pages = ((vaddr + size - 1) >> PAGE_BITS) - (vaddr >> PAGE_BITS) + 1;
|
||||||
for (unsigned i = 0; i < num_pages; ++i, vaddr += PAGE_SIZE) {
|
for (u64 i = 0; i < num_pages; ++i, vaddr += PAGE_SIZE) {
|
||||||
Common::PageType& page_type{current_page_table->attributes[vaddr >> PAGE_BITS]};
|
const Common::PageType page_type{
|
||||||
|
current_page_table->pointers[vaddr >> PAGE_BITS].Type()};
|
||||||
if (cached) {
|
if (cached) {
|
||||||
// Switch page type to cached if now cached
|
// Switch page type to cached if now cached
|
||||||
switch (page_type) {
|
switch (page_type) {
|
||||||
|
@ -534,8 +498,8 @@ struct Memory::Impl {
|
||||||
// space, for example, a system module need not have a VRAM mapping.
|
// space, for example, a system module need not have a VRAM mapping.
|
||||||
break;
|
break;
|
||||||
case Common::PageType::Memory:
|
case Common::PageType::Memory:
|
||||||
page_type = Common::PageType::RasterizerCachedMemory;
|
current_page_table->pointers[vaddr >> PAGE_BITS].Store(
|
||||||
current_page_table->pointers[vaddr >> PAGE_BITS] = nullptr;
|
nullptr, Common::PageType::RasterizerCachedMemory);
|
||||||
break;
|
break;
|
||||||
case Common::PageType::RasterizerCachedMemory:
|
case Common::PageType::RasterizerCachedMemory:
|
||||||
// There can be more than one GPU region mapped per CPU region, so it's common
|
// There can be more than one GPU region mapped per CPU region, so it's common
|
||||||
|
@ -556,16 +520,16 @@ struct Memory::Impl {
|
||||||
// that this area is already unmarked as cached.
|
// that this area is already unmarked as cached.
|
||||||
break;
|
break;
|
||||||
case Common::PageType::RasterizerCachedMemory: {
|
case Common::PageType::RasterizerCachedMemory: {
|
||||||
u8* pointer{GetPointerFromRasterizerCachedMemory(vaddr & ~PAGE_MASK)};
|
u8* const pointer{GetPointerFromRasterizerCachedMemory(vaddr & ~PAGE_MASK)};
|
||||||
if (pointer == nullptr) {
|
if (pointer == nullptr) {
|
||||||
// It's possible that this function has been called while updating the
|
// It's possible that this function has been called while updating the
|
||||||
// pagetable after unmapping a VMA. In that case the underlying VMA will no
|
// pagetable after unmapping a VMA. In that case the underlying VMA will no
|
||||||
// longer exist, and we should just leave the pagetable entry blank.
|
// longer exist, and we should just leave the pagetable entry blank.
|
||||||
page_type = Common::PageType::Unmapped;
|
current_page_table->pointers[vaddr >> PAGE_BITS].Store(
|
||||||
|
nullptr, Common::PageType::Unmapped);
|
||||||
} else {
|
} else {
|
||||||
current_page_table->pointers[vaddr >> PAGE_BITS] =
|
current_page_table->pointers[vaddr >> PAGE_BITS].Store(
|
||||||
pointer - (vaddr & ~PAGE_MASK);
|
pointer - (vaddr & ~PAGE_MASK), Common::PageType::Memory);
|
||||||
page_type = Common::PageType::Memory;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -595,7 +559,7 @@ struct Memory::Impl {
|
||||||
auto& gpu = system.GPU();
|
auto& gpu = system.GPU();
|
||||||
for (u64 i = 0; i < size; i++) {
|
for (u64 i = 0; i < size; i++) {
|
||||||
const auto page = base + i;
|
const auto page = base + i;
|
||||||
if (page_table.attributes[page] == Common::PageType::RasterizerCachedMemory) {
|
if (page_table.pointers[page].Type() == Common::PageType::RasterizerCachedMemory) {
|
||||||
gpu.FlushAndInvalidateRegion(page << PAGE_BITS, PAGE_SIZE);
|
gpu.FlushAndInvalidateRegion(page << PAGE_BITS, PAGE_SIZE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -610,20 +574,18 @@ struct Memory::Impl {
|
||||||
"Mapping memory page without a pointer @ {:016x}", base * PAGE_SIZE);
|
"Mapping memory page without a pointer @ {:016x}", base * PAGE_SIZE);
|
||||||
|
|
||||||
while (base != end) {
|
while (base != end) {
|
||||||
page_table.attributes[base] = type;
|
page_table.pointers[base].Store(nullptr, type);
|
||||||
page_table.pointers[base] = nullptr;
|
|
||||||
page_table.backing_addr[base] = 0;
|
page_table.backing_addr[base] = 0;
|
||||||
|
|
||||||
base += 1;
|
base += 1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
while (base != end) {
|
while (base != end) {
|
||||||
page_table.pointers[base] =
|
page_table.pointers[base].Store(
|
||||||
system.DeviceMemory().GetPointer(target) - (base << PAGE_BITS);
|
system.DeviceMemory().GetPointer(target) - (base << PAGE_BITS), type);
|
||||||
page_table.attributes[base] = type;
|
|
||||||
page_table.backing_addr[base] = target - (base << PAGE_BITS);
|
page_table.backing_addr[base] = target - (base << PAGE_BITS);
|
||||||
|
|
||||||
ASSERT_MSG(page_table.pointers[base],
|
ASSERT_MSG(page_table.pointers[base].Pointer(),
|
||||||
"memory mapping base yield a nullptr within the table");
|
"memory mapping base yield a nullptr within the table");
|
||||||
|
|
||||||
base += 1;
|
base += 1;
|
||||||
|
@ -646,21 +608,13 @@ struct Memory::Impl {
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T Read(const VAddr vaddr) {
|
T Read(const VAddr vaddr) {
|
||||||
// Avoid adding any extra logic to this fast-path block
|
// Avoid adding any extra logic to this fast-path block
|
||||||
if (const u8* const pointer = current_page_table->pointers[vaddr >> PAGE_BITS]) {
|
const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw();
|
||||||
|
if (const u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) {
|
||||||
T value;
|
T value;
|
||||||
std::memcpy(&value, &pointer[vaddr], sizeof(T));
|
std::memcpy(&value, &pointer[vaddr], sizeof(T));
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
switch (Common::PageTable::PageInfo::ExtractType(raw_pointer)) {
|
||||||
// Otherwise, we need to grab the page with a lock, in case it is currently being modified
|
|
||||||
const auto entry = SafePageEntry(vaddr >> PAGE_BITS);
|
|
||||||
if (entry.pointer) {
|
|
||||||
T value;
|
|
||||||
std::memcpy(&value, &entry.pointer[vaddr], sizeof(T));
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (entry.attribute) {
|
|
||||||
case Common::PageType::Unmapped:
|
case Common::PageType::Unmapped:
|
||||||
LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:08X}", sizeof(T) * 8, vaddr);
|
LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:08X}", sizeof(T) * 8, vaddr);
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -692,20 +646,12 @@ struct Memory::Impl {
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void Write(const VAddr vaddr, const T data) {
|
void Write(const VAddr vaddr, const T data) {
|
||||||
// Avoid adding any extra logic to this fast-path block
|
// Avoid adding any extra logic to this fast-path block
|
||||||
if (u8* const pointer = current_page_table->pointers[vaddr >> PAGE_BITS]) {
|
const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw();
|
||||||
|
if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) {
|
||||||
std::memcpy(&pointer[vaddr], &data, sizeof(T));
|
std::memcpy(&pointer[vaddr], &data, sizeof(T));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
switch (Common::PageTable::PageInfo::ExtractType(raw_pointer)) {
|
||||||
// Otherwise, we need to grab the page with a lock, in case it is currently being modified
|
|
||||||
const auto entry = SafePageEntry(vaddr >> PAGE_BITS);
|
|
||||||
if (entry.pointer) {
|
|
||||||
// Memory was mapped, we are done
|
|
||||||
std::memcpy(&entry.pointer[vaddr], &data, sizeof(T));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (entry.attribute) {
|
|
||||||
case Common::PageType::Unmapped:
|
case Common::PageType::Unmapped:
|
||||||
LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8,
|
LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8,
|
||||||
static_cast<u32>(data), vaddr);
|
static_cast<u32>(data), vaddr);
|
||||||
|
@ -726,15 +672,13 @@ struct Memory::Impl {
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
bool WriteExclusive(const VAddr vaddr, const T data, const T expected) {
|
bool WriteExclusive(const VAddr vaddr, const T data, const T expected) {
|
||||||
u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS];
|
const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw();
|
||||||
if (page_pointer != nullptr) {
|
if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) {
|
||||||
// NOTE: Avoid adding any extra logic to this fast-path block
|
// NOTE: Avoid adding any extra logic to this fast-path block
|
||||||
auto* pointer = reinterpret_cast<volatile T*>(&page_pointer[vaddr]);
|
const auto volatile_pointer = reinterpret_cast<volatile T*>(&pointer[vaddr]);
|
||||||
return Common::AtomicCompareAndSwap(pointer, data, expected);
|
return Common::AtomicCompareAndSwap(volatile_pointer, data, expected);
|
||||||
}
|
}
|
||||||
|
switch (Common::PageTable::PageInfo::ExtractType(raw_pointer)) {
|
||||||
const Common::PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
|
|
||||||
switch (type) {
|
|
||||||
case Common::PageType::Unmapped:
|
case Common::PageType::Unmapped:
|
||||||
LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8,
|
LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8,
|
||||||
static_cast<u32>(data), vaddr);
|
static_cast<u32>(data), vaddr);
|
||||||
|
@ -755,15 +699,13 @@ struct Memory::Impl {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WriteExclusive128(const VAddr vaddr, const u128 data, const u128 expected) {
|
bool WriteExclusive128(const VAddr vaddr, const u128 data, const u128 expected) {
|
||||||
u8* const page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS];
|
const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw();
|
||||||
if (page_pointer != nullptr) {
|
if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) {
|
||||||
// NOTE: Avoid adding any extra logic to this fast-path block
|
// NOTE: Avoid adding any extra logic to this fast-path block
|
||||||
auto* pointer = reinterpret_cast<volatile u64*>(&page_pointer[vaddr]);
|
const auto volatile_pointer = reinterpret_cast<volatile u64*>(&pointer[vaddr]);
|
||||||
return Common::AtomicCompareAndSwap(pointer, data, expected);
|
return Common::AtomicCompareAndSwap(volatile_pointer, data, expected);
|
||||||
}
|
}
|
||||||
|
switch (Common::PageTable::PageInfo::ExtractType(raw_pointer)) {
|
||||||
const Common::PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
|
|
||||||
switch (type) {
|
|
||||||
case Common::PageType::Unmapped:
|
case Common::PageType::Unmapped:
|
||||||
LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}{:016X}", sizeof(data) * 8,
|
LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}{:016X}", sizeof(data) * 8,
|
||||||
static_cast<u64>(data[1]), static_cast<u64>(data[0]), vaddr);
|
static_cast<u64>(data[1]), static_cast<u64>(data[0]), vaddr);
|
||||||
|
@ -783,7 +725,6 @@ struct Memory::Impl {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
mutable std::mutex rasterizer_cache_guard;
|
|
||||||
Common::PageTable* current_page_table = nullptr;
|
Common::PageTable* current_page_table = nullptr;
|
||||||
Core::System& system;
|
Core::System& system;
|
||||||
};
|
};
|
||||||
|
|
|
@ -148,9 +148,4 @@ void RestoreGlobalState(bool is_powered_on) {
|
||||||
values.motion_enabled.SetGlobal(true);
|
values.motion_enabled.SetGlobal(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Sanitize() {
|
|
||||||
values.use_asynchronous_gpu_emulation.SetValue(
|
|
||||||
values.use_asynchronous_gpu_emulation.GetValue() || values.use_multi_core.GetValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Settings
|
} // namespace Settings
|
||||||
|
|
|
@ -257,7 +257,4 @@ void LogSettings();
|
||||||
// Restore the global state of all applicable settings in the Values struct
|
// Restore the global state of all applicable settings in the Values struct
|
||||||
void RestoreGlobalState(bool is_powered_on);
|
void RestoreGlobalState(bool is_powered_on);
|
||||||
|
|
||||||
// Fixes settings that are known to cause issues with the emulator
|
|
||||||
void Sanitize();
|
|
||||||
|
|
||||||
} // namespace Settings
|
} // namespace Settings
|
||||||
|
|
|
@ -48,6 +48,7 @@ add_library(video_core STATIC
|
||||||
engines/shader_bytecode.h
|
engines/shader_bytecode.h
|
||||||
engines/shader_header.h
|
engines/shader_header.h
|
||||||
engines/shader_type.h
|
engines/shader_type.h
|
||||||
|
framebuffer_config.h
|
||||||
macro/macro.cpp
|
macro/macro.cpp
|
||||||
macro/macro.h
|
macro/macro.h
|
||||||
macro/macro_hle.cpp
|
macro/macro_hle.cpp
|
||||||
|
@ -59,10 +60,6 @@ add_library(video_core STATIC
|
||||||
fence_manager.h
|
fence_manager.h
|
||||||
gpu.cpp
|
gpu.cpp
|
||||||
gpu.h
|
gpu.h
|
||||||
gpu_asynch.cpp
|
|
||||||
gpu_asynch.h
|
|
||||||
gpu_synch.cpp
|
|
||||||
gpu_synch.h
|
|
||||||
gpu_thread.cpp
|
gpu_thread.cpp
|
||||||
gpu_thread.h
|
gpu_thread.h
|
||||||
guest_driver.cpp
|
guest_driver.cpp
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include "core/core_timing.h"
|
#include "core/core_timing.h"
|
||||||
#include "core/core_timing_util.h"
|
#include "core/core_timing_util.h"
|
||||||
#include "core/frontend/emu_window.h"
|
#include "core/frontend/emu_window.h"
|
||||||
|
#include "core/hardware_interrupt_manager.h"
|
||||||
#include "core/memory.h"
|
#include "core/memory.h"
|
||||||
#include "core/settings.h"
|
#include "core/settings.h"
|
||||||
#include "video_core/engines/fermi_2d.h"
|
#include "video_core/engines/fermi_2d.h"
|
||||||
|
@ -36,7 +37,8 @@ GPU::GPU(Core::System& system_, bool is_async_, bool use_nvdec_)
|
||||||
kepler_compute{std::make_unique<Engines::KeplerCompute>(system, *memory_manager)},
|
kepler_compute{std::make_unique<Engines::KeplerCompute>(system, *memory_manager)},
|
||||||
maxwell_dma{std::make_unique<Engines::MaxwellDMA>(system, *memory_manager)},
|
maxwell_dma{std::make_unique<Engines::MaxwellDMA>(system, *memory_manager)},
|
||||||
kepler_memory{std::make_unique<Engines::KeplerMemory>(system, *memory_manager)},
|
kepler_memory{std::make_unique<Engines::KeplerMemory>(system, *memory_manager)},
|
||||||
shader_notify{std::make_unique<VideoCore::ShaderNotify>()}, is_async{is_async_} {}
|
shader_notify{std::make_unique<VideoCore::ShaderNotify>()}, is_async{is_async_},
|
||||||
|
gpu_thread{system_, is_async_} {}
|
||||||
|
|
||||||
GPU::~GPU() = default;
|
GPU::~GPU() = default;
|
||||||
|
|
||||||
|
@ -198,10 +200,6 @@ void GPU::SyncGuestHost() {
|
||||||
renderer->Rasterizer().SyncGuestHost();
|
renderer->Rasterizer().SyncGuestHost();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GPU::OnCommandListEnd() {
|
|
||||||
renderer->Rasterizer().ReleaseFences();
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class GpuSemaphoreOperation {
|
enum class GpuSemaphoreOperation {
|
||||||
AcquireEqual = 0x1,
|
AcquireEqual = 0x1,
|
||||||
WriteLong = 0x2,
|
WriteLong = 0x2,
|
||||||
|
@ -461,4 +459,75 @@ void GPU::ProcessSemaphoreAcquire() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GPU::Start() {
|
||||||
|
gpu_thread.StartThread(*renderer, renderer->Context(), *dma_pusher, *cdma_pusher);
|
||||||
|
cpu_context = renderer->GetRenderWindow().CreateSharedContext();
|
||||||
|
cpu_context->MakeCurrent();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GPU::ObtainContext() {
|
||||||
|
cpu_context->MakeCurrent();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GPU::ReleaseContext() {
|
||||||
|
cpu_context->DoneCurrent();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GPU::PushGPUEntries(Tegra::CommandList&& entries) {
|
||||||
|
gpu_thread.SubmitList(std::move(entries));
|
||||||
|
}
|
||||||
|
|
||||||
|
void GPU::PushCommandBuffer(Tegra::ChCommandHeaderList& entries) {
|
||||||
|
if (!use_nvdec) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// This condition fires when a video stream ends, clear all intermediary data
|
||||||
|
if (entries[0].raw == 0xDEADB33F) {
|
||||||
|
cdma_pusher.reset();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!cdma_pusher) {
|
||||||
|
cdma_pusher = std::make_unique<Tegra::CDmaPusher>(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubmitCommandBuffer would make the nvdec operations async, this is not currently working
|
||||||
|
// TODO(ameerj): RE proper async nvdec operation
|
||||||
|
// gpu_thread.SubmitCommandBuffer(std::move(entries));
|
||||||
|
|
||||||
|
cdma_pusher->Push(std::move(entries));
|
||||||
|
cdma_pusher->DispatchCalls();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GPU::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
|
||||||
|
gpu_thread.SwapBuffers(framebuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GPU::FlushRegion(VAddr addr, u64 size) {
|
||||||
|
gpu_thread.FlushRegion(addr, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GPU::InvalidateRegion(VAddr addr, u64 size) {
|
||||||
|
gpu_thread.InvalidateRegion(addr, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GPU::FlushAndInvalidateRegion(VAddr addr, u64 size) {
|
||||||
|
gpu_thread.FlushAndInvalidateRegion(addr, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GPU::TriggerCpuInterrupt(const u32 syncpoint_id, const u32 value) const {
|
||||||
|
auto& interrupt_manager = system.InterruptManager();
|
||||||
|
interrupt_manager.GPUInterruptSyncpt(syncpoint_id, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GPU::WaitIdle() const {
|
||||||
|
gpu_thread.WaitIdle();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GPU::OnCommandListEnd() {
|
||||||
|
if (is_async) {
|
||||||
|
// This command only applies to asynchronous GPU mode
|
||||||
|
gpu_thread.OnCommandListEnd();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Tegra
|
} // namespace Tegra
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
#include "core/hle/service/nvflinger/buffer_queue.h"
|
#include "core/hle/service/nvflinger/buffer_queue.h"
|
||||||
#include "video_core/cdma_pusher.h"
|
#include "video_core/cdma_pusher.h"
|
||||||
#include "video_core/dma_pusher.h"
|
#include "video_core/dma_pusher.h"
|
||||||
|
#include "video_core/framebuffer_config.h"
|
||||||
|
#include "video_core/gpu_thread.h"
|
||||||
|
|
||||||
using CacheAddr = std::uintptr_t;
|
using CacheAddr = std::uintptr_t;
|
||||||
[[nodiscard]] inline CacheAddr ToCacheAddr(const void* host_ptr) {
|
[[nodiscard]] inline CacheAddr ToCacheAddr(const void* host_ptr) {
|
||||||
|
@ -101,28 +103,6 @@ enum class DepthFormat : u32 {
|
||||||
struct CommandListHeader;
|
struct CommandListHeader;
|
||||||
class DebugContext;
|
class DebugContext;
|
||||||
|
|
||||||
/**
|
|
||||||
* Struct describing framebuffer configuration
|
|
||||||
*/
|
|
||||||
struct FramebufferConfig {
|
|
||||||
enum class PixelFormat : u32 {
|
|
||||||
A8B8G8R8_UNORM = 1,
|
|
||||||
RGB565_UNORM = 4,
|
|
||||||
B8G8R8A8_UNORM = 5,
|
|
||||||
};
|
|
||||||
|
|
||||||
VAddr address;
|
|
||||||
u32 offset;
|
|
||||||
u32 width;
|
|
||||||
u32 height;
|
|
||||||
u32 stride;
|
|
||||||
PixelFormat pixel_format;
|
|
||||||
|
|
||||||
using TransformFlags = Service::NVFlinger::BufferQueue::BufferTransformFlags;
|
|
||||||
TransformFlags transform_flags;
|
|
||||||
Common::Rectangle<int> crop_rect;
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace Engines {
|
namespace Engines {
|
||||||
class Fermi2D;
|
class Fermi2D;
|
||||||
class Maxwell3D;
|
class Maxwell3D;
|
||||||
|
@ -141,7 +121,7 @@ enum class EngineID {
|
||||||
|
|
||||||
class MemoryManager;
|
class MemoryManager;
|
||||||
|
|
||||||
class GPU {
|
class GPU final {
|
||||||
public:
|
public:
|
||||||
struct MethodCall {
|
struct MethodCall {
|
||||||
u32 method{};
|
u32 method{};
|
||||||
|
@ -159,7 +139,7 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit GPU(Core::System& system_, bool is_async_, bool use_nvdec_);
|
explicit GPU(Core::System& system_, bool is_async_, bool use_nvdec_);
|
||||||
virtual ~GPU();
|
~GPU();
|
||||||
|
|
||||||
/// Binds a renderer to the GPU.
|
/// Binds a renderer to the GPU.
|
||||||
void BindRenderer(std::unique_ptr<VideoCore::RendererBase> renderer);
|
void BindRenderer(std::unique_ptr<VideoCore::RendererBase> renderer);
|
||||||
|
@ -176,7 +156,7 @@ public:
|
||||||
/// Synchronizes CPU writes with Host GPU memory.
|
/// Synchronizes CPU writes with Host GPU memory.
|
||||||
void SyncGuestHost();
|
void SyncGuestHost();
|
||||||
/// Signal the ending of command list.
|
/// Signal the ending of command list.
|
||||||
virtual void OnCommandListEnd();
|
void OnCommandListEnd();
|
||||||
|
|
||||||
/// Request a host GPU memory flush from the CPU.
|
/// Request a host GPU memory flush from the CPU.
|
||||||
[[nodiscard]] u64 RequestFlush(VAddr addr, std::size_t size);
|
[[nodiscard]] u64 RequestFlush(VAddr addr, std::size_t size);
|
||||||
|
@ -240,7 +220,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
// Waits for the GPU to finish working
|
// Waits for the GPU to finish working
|
||||||
virtual void WaitIdle() const = 0;
|
void WaitIdle() const;
|
||||||
|
|
||||||
/// Allows the CPU/NvFlinger to wait on the GPU before presenting a frame.
|
/// Allows the CPU/NvFlinger to wait on the GPU before presenting a frame.
|
||||||
void WaitFence(u32 syncpoint_id, u32 value);
|
void WaitFence(u32 syncpoint_id, u32 value);
|
||||||
|
@ -330,34 +310,34 @@ public:
|
||||||
/// Performs any additional setup necessary in order to begin GPU emulation.
|
/// Performs any additional setup necessary in order to begin GPU emulation.
|
||||||
/// This can be used to launch any necessary threads and register any necessary
|
/// This can be used to launch any necessary threads and register any necessary
|
||||||
/// core timing events.
|
/// core timing events.
|
||||||
virtual void Start() = 0;
|
void Start();
|
||||||
|
|
||||||
/// Obtain the CPU Context
|
/// Obtain the CPU Context
|
||||||
virtual void ObtainContext() = 0;
|
void ObtainContext();
|
||||||
|
|
||||||
/// Release the CPU Context
|
/// Release the CPU Context
|
||||||
virtual void ReleaseContext() = 0;
|
void ReleaseContext();
|
||||||
|
|
||||||
/// Push GPU command entries to be processed
|
/// Push GPU command entries to be processed
|
||||||
virtual void PushGPUEntries(Tegra::CommandList&& entries) = 0;
|
void PushGPUEntries(Tegra::CommandList&& entries);
|
||||||
|
|
||||||
/// Push GPU command buffer entries to be processed
|
/// Push GPU command buffer entries to be processed
|
||||||
virtual void PushCommandBuffer(Tegra::ChCommandHeaderList& entries) = 0;
|
void PushCommandBuffer(Tegra::ChCommandHeaderList& entries);
|
||||||
|
|
||||||
/// Swap buffers (render frame)
|
/// Swap buffers (render frame)
|
||||||
virtual void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) = 0;
|
void SwapBuffers(const Tegra::FramebufferConfig* framebuffer);
|
||||||
|
|
||||||
/// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
|
/// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
|
||||||
virtual void FlushRegion(VAddr addr, u64 size) = 0;
|
void FlushRegion(VAddr addr, u64 size);
|
||||||
|
|
||||||
/// Notify rasterizer that any caches of the specified region should be invalidated
|
/// Notify rasterizer that any caches of the specified region should be invalidated
|
||||||
virtual void InvalidateRegion(VAddr addr, u64 size) = 0;
|
void InvalidateRegion(VAddr addr, u64 size);
|
||||||
|
|
||||||
/// Notify rasterizer that any caches of the specified region should be flushed and invalidated
|
/// Notify rasterizer that any caches of the specified region should be flushed and invalidated
|
||||||
virtual void FlushAndInvalidateRegion(VAddr addr, u64 size) = 0;
|
void FlushAndInvalidateRegion(VAddr addr, u64 size);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void TriggerCpuInterrupt(u32 syncpoint_id, u32 value) const = 0;
|
void TriggerCpuInterrupt(u32 syncpoint_id, u32 value) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void ProcessBindMethod(const MethodCall& method_call);
|
void ProcessBindMethod(const MethodCall& method_call);
|
||||||
|
@ -427,6 +407,9 @@ private:
|
||||||
std::mutex flush_request_mutex;
|
std::mutex flush_request_mutex;
|
||||||
|
|
||||||
const bool is_async;
|
const bool is_async;
|
||||||
|
|
||||||
|
VideoCommon::GPUThread::ThreadManager gpu_thread;
|
||||||
|
std::unique_ptr<Core::Frontend::GraphicsContext> cpu_context;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define ASSERT_REG_POSITION(field_name, position) \
|
#define ASSERT_REG_POSITION(field_name, position) \
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/microprofile.h"
|
#include "common/microprofile.h"
|
||||||
|
#include "common/scope_exit.h"
|
||||||
#include "common/thread.h"
|
#include "common/thread.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/frontend/emu_window.h"
|
#include "core/frontend/emu_window.h"
|
||||||
|
@ -21,6 +22,8 @@ static void RunThread(Core::System& system, VideoCore::RendererBase& renderer,
|
||||||
SynchState& state, Tegra::CDmaPusher& cdma_pusher) {
|
SynchState& state, Tegra::CDmaPusher& cdma_pusher) {
|
||||||
std::string name = "yuzu:GPU";
|
std::string name = "yuzu:GPU";
|
||||||
MicroProfileOnThreadCreate(name.c_str());
|
MicroProfileOnThreadCreate(name.c_str());
|
||||||
|
SCOPE_EXIT({ MicroProfileOnThreadExit(); });
|
||||||
|
|
||||||
Common::SetCurrentThreadName(name.c_str());
|
Common::SetCurrentThreadName(name.c_str());
|
||||||
Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
|
Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
|
||||||
system.RegisterHostThread();
|
system.RegisterHostThread();
|
||||||
|
@ -65,7 +68,8 @@ static void RunThread(Core::System& system, VideoCore::RendererBase& renderer,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ThreadManager::ThreadManager(Core::System& system_) : system{system_} {}
|
ThreadManager::ThreadManager(Core::System& system_, bool is_async_)
|
||||||
|
: system{system_}, is_async{is_async_} {}
|
||||||
|
|
||||||
ThreadManager::~ThreadManager() {
|
ThreadManager::~ThreadManager() {
|
||||||
if (!thread.joinable()) {
|
if (!thread.joinable()) {
|
||||||
|
@ -97,19 +101,30 @@ void ThreadManager::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThreadManager::FlushRegion(VAddr addr, u64 size) {
|
void ThreadManager::FlushRegion(VAddr addr, u64 size) {
|
||||||
if (!Settings::IsGPULevelHigh()) {
|
if (!is_async) {
|
||||||
|
// Always flush with synchronous GPU mode
|
||||||
PushCommand(FlushRegionCommand(addr, size));
|
PushCommand(FlushRegionCommand(addr, size));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!Settings::IsGPULevelExtreme()) {
|
|
||||||
return;
|
// Asynchronous GPU mode
|
||||||
}
|
switch (Settings::values.gpu_accuracy.GetValue()) {
|
||||||
if (system.Renderer().Rasterizer().MustFlushRegion(addr, size)) {
|
case Settings::GPUAccuracy::Normal:
|
||||||
|
PushCommand(FlushRegionCommand(addr, size));
|
||||||
|
break;
|
||||||
|
case Settings::GPUAccuracy::High:
|
||||||
|
// TODO(bunnei): Is this right? Preserving existing behavior for now
|
||||||
|
break;
|
||||||
|
case Settings::GPUAccuracy::Extreme: {
|
||||||
auto& gpu = system.GPU();
|
auto& gpu = system.GPU();
|
||||||
u64 fence = gpu.RequestFlush(addr, size);
|
u64 fence = gpu.RequestFlush(addr, size);
|
||||||
PushCommand(GPUTickCommand());
|
PushCommand(GPUTickCommand());
|
||||||
while (fence > gpu.CurrentFlushRequestFence()) {
|
while (fence > gpu.CurrentFlushRequestFence()) {
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
UNIMPLEMENTED_MSG("Unsupported gpu_accuracy {}", Settings::values.gpu_accuracy.GetValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,7 +138,8 @@ void ThreadManager::FlushAndInvalidateRegion(VAddr addr, u64 size) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThreadManager::WaitIdle() const {
|
void ThreadManager::WaitIdle() const {
|
||||||
while (state.last_fence > state.signaled_fence.load(std::memory_order_relaxed)) {
|
while (state.last_fence > state.signaled_fence.load(std::memory_order_relaxed) &&
|
||||||
|
system.IsPoweredOn()) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,6 +150,12 @@ void ThreadManager::OnCommandListEnd() {
|
||||||
u64 ThreadManager::PushCommand(CommandData&& command_data) {
|
u64 ThreadManager::PushCommand(CommandData&& command_data) {
|
||||||
const u64 fence{++state.last_fence};
|
const u64 fence{++state.last_fence};
|
||||||
state.queue.Push(CommandDataContainer(std::move(command_data), fence));
|
state.queue.Push(CommandDataContainer(std::move(command_data), fence));
|
||||||
|
|
||||||
|
if (!is_async) {
|
||||||
|
// In synchronous GPU mode, block the caller until the command has executed
|
||||||
|
WaitIdle();
|
||||||
|
}
|
||||||
|
|
||||||
return fence;
|
return fence;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,8 +10,9 @@
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <variant>
|
#include <variant>
|
||||||
|
|
||||||
#include "common/threadsafe_queue.h"
|
#include "common/threadsafe_queue.h"
|
||||||
#include "video_core/gpu.h"
|
#include "video_core/framebuffer_config.h"
|
||||||
|
|
||||||
namespace Tegra {
|
namespace Tegra {
|
||||||
struct FramebufferConfig;
|
struct FramebufferConfig;
|
||||||
|
@ -25,6 +26,10 @@ class GraphicsContext;
|
||||||
class System;
|
class System;
|
||||||
} // namespace Core
|
} // namespace Core
|
||||||
|
|
||||||
|
namespace VideoCore {
|
||||||
|
class RendererBase;
|
||||||
|
} // namespace VideoCore
|
||||||
|
|
||||||
namespace VideoCommon::GPUThread {
|
namespace VideoCommon::GPUThread {
|
||||||
|
|
||||||
/// Command to signal to the GPU thread that processing has ended
|
/// Command to signal to the GPU thread that processing has ended
|
||||||
|
@ -112,7 +117,7 @@ struct SynchState final {
|
||||||
/// Class used to manage the GPU thread
|
/// Class used to manage the GPU thread
|
||||||
class ThreadManager final {
|
class ThreadManager final {
|
||||||
public:
|
public:
|
||||||
explicit ThreadManager(Core::System& system_);
|
explicit ThreadManager(Core::System& system_, bool is_async_);
|
||||||
~ThreadManager();
|
~ThreadManager();
|
||||||
|
|
||||||
/// Creates and starts the GPU thread.
|
/// Creates and starts the GPU thread.
|
||||||
|
@ -150,6 +155,7 @@ private:
|
||||||
Core::System& system;
|
Core::System& system;
|
||||||
std::thread thread;
|
std::thread thread;
|
||||||
std::thread::id thread_id;
|
std::thread::id thread_id;
|
||||||
|
const bool is_async;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace VideoCommon::GPUThread
|
} // namespace VideoCommon::GPUThread
|
||||||
|
|
|
@ -7,8 +7,6 @@
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/settings.h"
|
#include "core/settings.h"
|
||||||
#include "video_core/gpu_asynch.h"
|
|
||||||
#include "video_core/gpu_synch.h"
|
|
||||||
#include "video_core/renderer_base.h"
|
#include "video_core/renderer_base.h"
|
||||||
#include "video_core/renderer_opengl/renderer_opengl.h"
|
#include "video_core/renderer_opengl/renderer_opengl.h"
|
||||||
#include "video_core/renderer_vulkan/renderer_vulkan.h"
|
#include "video_core/renderer_vulkan/renderer_vulkan.h"
|
||||||
|
@ -39,13 +37,9 @@ std::unique_ptr<VideoCore::RendererBase> CreateRenderer(
|
||||||
namespace VideoCore {
|
namespace VideoCore {
|
||||||
|
|
||||||
std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Core::System& system) {
|
std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Core::System& system) {
|
||||||
std::unique_ptr<Tegra::GPU> gpu;
|
|
||||||
const bool use_nvdec = Settings::values.use_nvdec_emulation.GetValue();
|
const bool use_nvdec = Settings::values.use_nvdec_emulation.GetValue();
|
||||||
if (Settings::values.use_asynchronous_gpu_emulation.GetValue()) {
|
std::unique_ptr<Tegra::GPU> gpu = std::make_unique<Tegra::GPU>(
|
||||||
gpu = std::make_unique<VideoCommon::GPUAsynch>(system, use_nvdec);
|
system, Settings::values.use_asynchronous_gpu_emulation.GetValue(), use_nvdec);
|
||||||
} else {
|
|
||||||
gpu = std::make_unique<VideoCommon::GPUSynch>(system, use_nvdec);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto context = emu_window.CreateSharedContext();
|
auto context = emu_window.CreateSharedContext();
|
||||||
const auto scope = context->Acquire();
|
const auto scope = context->Acquire();
|
||||||
|
|
|
@ -1589,14 +1589,12 @@ void Config::WriteSettingGlobal(const QString& name, const QVariant& value, bool
|
||||||
|
|
||||||
void Config::Reload() {
|
void Config::Reload() {
|
||||||
ReadValues();
|
ReadValues();
|
||||||
Settings::Sanitize();
|
|
||||||
// To apply default value changes
|
// To apply default value changes
|
||||||
SaveValues();
|
SaveValues();
|
||||||
Settings::Apply(Core::System::GetInstance());
|
Settings::Apply(Core::System::GetInstance());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Config::Save() {
|
void Config::Save() {
|
||||||
Settings::Sanitize();
|
|
||||||
SaveValues();
|
SaveValues();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -580,9 +580,8 @@ void GMainWindow::InitializeWidgets() {
|
||||||
if (emulation_running) {
|
if (emulation_running) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const bool is_async = !Settings::values.use_asynchronous_gpu_emulation.GetValue() ||
|
Settings::values.use_asynchronous_gpu_emulation.SetValue(
|
||||||
Settings::values.use_multi_core.GetValue();
|
!Settings::values.use_asynchronous_gpu_emulation.GetValue());
|
||||||
Settings::values.use_asynchronous_gpu_emulation.SetValue(is_async);
|
|
||||||
async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation.GetValue());
|
async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation.GetValue());
|
||||||
Settings::Apply(Core::System::GetInstance());
|
Settings::Apply(Core::System::GetInstance());
|
||||||
});
|
});
|
||||||
|
@ -599,16 +598,13 @@ void GMainWindow::InitializeWidgets() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Settings::values.use_multi_core.SetValue(!Settings::values.use_multi_core.GetValue());
|
Settings::values.use_multi_core.SetValue(!Settings::values.use_multi_core.GetValue());
|
||||||
const bool is_async = Settings::values.use_asynchronous_gpu_emulation.GetValue() ||
|
|
||||||
Settings::values.use_multi_core.GetValue();
|
|
||||||
Settings::values.use_asynchronous_gpu_emulation.SetValue(is_async);
|
|
||||||
async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation.GetValue());
|
|
||||||
multicore_status_button->setChecked(Settings::values.use_multi_core.GetValue());
|
multicore_status_button->setChecked(Settings::values.use_multi_core.GetValue());
|
||||||
Settings::Apply(Core::System::GetInstance());
|
Settings::Apply(Core::System::GetInstance());
|
||||||
});
|
});
|
||||||
multicore_status_button->setText(tr("MULTICORE"));
|
multicore_status_button->setText(tr("MULTICORE"));
|
||||||
multicore_status_button->setCheckable(true);
|
multicore_status_button->setCheckable(true);
|
||||||
multicore_status_button->setChecked(Settings::values.use_multi_core.GetValue());
|
multicore_status_button->setChecked(Settings::values.use_multi_core.GetValue());
|
||||||
|
|
||||||
statusBar()->insertPermanentWidget(0, multicore_status_button);
|
statusBar()->insertPermanentWidget(0, multicore_status_button);
|
||||||
statusBar()->insertPermanentWidget(0, async_status_button);
|
statusBar()->insertPermanentWidget(0, async_status_button);
|
||||||
|
|
||||||
|
@ -2533,9 +2529,6 @@ void GMainWindow::UpdateStatusBar() {
|
||||||
void GMainWindow::UpdateStatusButtons() {
|
void GMainWindow::UpdateStatusButtons() {
|
||||||
dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue());
|
dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue());
|
||||||
multicore_status_button->setChecked(Settings::values.use_multi_core.GetValue());
|
multicore_status_button->setChecked(Settings::values.use_multi_core.GetValue());
|
||||||
Settings::values.use_asynchronous_gpu_emulation.SetValue(
|
|
||||||
Settings::values.use_asynchronous_gpu_emulation.GetValue() ||
|
|
||||||
Settings::values.use_multi_core.GetValue());
|
|
||||||
async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation.GetValue());
|
async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation.GetValue());
|
||||||
renderer_status_button->setChecked(Settings::values.renderer_backend.GetValue() ==
|
renderer_status_button->setChecked(Settings::values.renderer_backend.GetValue() ==
|
||||||
Settings::RendererBackend::Vulkan);
|
Settings::RendererBackend::Vulkan);
|
||||||
|
|
Loading…
Reference in a new issue