commit
25d607f5f6
@ -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
|
@ -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
|
@ -0,0 +1,110 @@
|
|||||||
|
// Copyright 2020 yuzu emulator team
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <functional>
|
||||||
|
#include <mutex>
|
||||||
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
#include <queue>
|
||||||
|
|
||||||
|
#include "common/assert.h"
|
||||||
|
#include "common/scope_exit.h"
|
||||||
|
#include "common/thread.h"
|
||||||
|
#include "core/core.h"
|
||||||
|
#include "core/hle/kernel/kernel.h"
|
||||||
|
#include "core/hle/kernel/server_session.h"
|
||||||
|
#include "core/hle/kernel/service_thread.h"
|
||||||
|
#include "core/hle/lock.h"
|
||||||
|
#include "video_core/renderer_base.h"
|
||||||
|
|
||||||
|
namespace Kernel {
|
||||||
|
|
||||||
|
class ServiceThread::Impl final {
|
||||||
|
public:
|
||||||
|
explicit Impl(KernelCore& kernel, std::size_t num_threads, const std::string& name);
|
||||||
|
~Impl();
|
||||||
|
|
||||||
|
void QueueSyncRequest(ServerSession& session, std::shared_ptr<HLERequestContext>&& context);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<std::thread> threads;
|
||||||
|
std::queue<std::function<void()>> requests;
|
||||||
|
std::mutex queue_mutex;
|
||||||
|
std::condition_variable condition;
|
||||||
|
const std::string service_name;
|
||||||
|
bool stop{};
|
||||||
|
};
|
||||||
|
|
||||||
|
ServiceThread::Impl::Impl(KernelCore& kernel, std::size_t num_threads, const std::string& name)
|
||||||
|
: service_name{name} {
|
||||||
|
for (std::size_t i = 0; i < num_threads; ++i)
|
||||||
|
threads.emplace_back([this, &kernel] {
|
||||||
|
Common::SetCurrentThreadName(std::string{"yuzu:HleService:" + service_name}.c_str());
|
||||||
|
|
||||||
|
// Wait for first request before trying to acquire a render context
|
||||||
|
{
|
||||||
|
std::unique_lock lock{queue_mutex};
|
||||||
|
condition.wait(lock, [this] { return stop || !requests.empty(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
kernel.RegisterHostThread();
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void ServiceThread::Impl::QueueSyncRequest(ServerSession& session,
|
||||||
|
std::shared_ptr<HLERequestContext>&& context) {
|
||||||
|
{
|
||||||
|
std::unique_lock lock{queue_mutex};
|
||||||
|
|
||||||
|
// ServerSession owns the service thread, so we cannot caption a strong pointer here in the
|
||||||
|
// event that the ServerSession is terminated.
|
||||||
|
std::weak_ptr<ServerSession> weak_ptr{SharedFrom(&session)};
|
||||||
|
requests.emplace([weak_ptr, context{std::move(context)}]() {
|
||||||
|
if (auto strong_ptr = weak_ptr.lock()) {
|
||||||
|
strong_ptr->CompleteSyncRequest(*context);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
condition.notify_one();
|
||||||
|
}
|
||||||
|
|
||||||
|
ServiceThread::Impl::~Impl() {
|
||||||
|
{
|
||||||
|
std::unique_lock lock{queue_mutex};
|
||||||
|
stop = true;
|
||||||
|
}
|
||||||
|
condition.notify_all();
|
||||||
|
for (std::thread& thread : threads) {
|
||||||
|
thread.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ServiceThread::ServiceThread(KernelCore& kernel, std::size_t num_threads, const std::string& name)
|
||||||
|
: impl{std::make_unique<Impl>(kernel, num_threads, name)} {}
|
||||||
|
|
||||||
|
ServiceThread::~ServiceThread() = default;
|
||||||
|
|
||||||
|
void ServiceThread::QueueSyncRequest(ServerSession& session,
|
||||||
|
std::shared_ptr<HLERequestContext>&& context) {
|
||||||
|
impl->QueueSyncRequest(session, std::move(context));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Kernel
|
@ -0,0 +1,28 @@
|
|||||||
|
// Copyright 2020 yuzu emulator team
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace Kernel {
|
||||||
|
|
||||||
|
class HLERequestContext;
|
||||||
|
class KernelCore;
|
||||||
|
class ServerSession;
|
||||||
|
|
||||||
|
class ServiceThread final {
|
||||||
|
public:
|
||||||
|
explicit ServiceThread(KernelCore& kernel, std::size_t num_threads, const std::string& name);
|
||||||
|
~ServiceThread();
|
||||||
|
|
||||||
|
void QueueSyncRequest(ServerSession& session, std::shared_ptr<HLERequestContext>&& context);
|
||||||
|
|
||||||
|
private:
|
||||||
|
class Impl;
|
||||||
|
std::unique_ptr<Impl> impl;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Kernel
|
@ -1,161 +0,0 @@
|
|||||||
// Copyright 2020 yuzu emulator team
|
|
||||||
// Licensed under GPLv2 or any later version
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <atomic>
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
#include <string_view>
|
|
||||||
#include <thread>
|
|
||||||
#include <variant>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <fmt/format.h>
|
|
||||||
|
|
||||||
#include "common/assert.h"
|
|
||||||
#include "common/microprofile.h"
|
|
||||||
#include "common/thread.h"
|
|
||||||
#include "core/core.h"
|
|
||||||
#include "core/hle/kernel/hle_ipc.h"
|
|
||||||
#include "core/hle/kernel/kernel.h"
|
|
||||||
#include "core/hle/kernel/thread.h"
|
|
||||||
#include "core/hle/kernel/writable_event.h"
|
|
||||||
|
|
||||||
namespace Service::Sockets {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Worker abstraction to execute blocking calls on host without blocking the guest thread
|
|
||||||
*
|
|
||||||
* @tparam Service Service where the work is executed
|
|
||||||
* @tparam Types Types of work to execute
|
|
||||||
*/
|
|
||||||
template <class Service, class... Types>
|
|
||||||
class BlockingWorker {
|
|
||||||
using This = BlockingWorker<Service, Types...>;
|
|
||||||
using WorkVariant = std::variant<std::monostate, Types...>;
|
|
||||||
|
|
||||||
public:
|
|
||||||
/// Create a new worker
|
|
||||||
static std::unique_ptr<This> Create(Core::System& system, Service* service,
|
|
||||||
std::string_view name) {
|
|
||||||
return std::unique_ptr<This>(new This(system, service, name));
|
|
||||||
}
|
|
||||||
|
|
||||||
~BlockingWorker() {
|
|
||||||
while (!is_available.load(std::memory_order_relaxed)) {
|
|
||||||
// Busy wait until work is finished
|
|
||||||
std::this_thread::yield();
|
|
||||||
}
|
|
||||||
// Monostate means to exit the thread
|
|
||||||
work = std::monostate{};
|
|
||||||
work_event.Set();
|
|
||||||
thread.join();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Try to capture the worker to send work after a success
|
|
||||||
* @returns True when the worker has been successfully captured
|
|
||||||
*/
|
|
||||||
bool TryCapture() {
|
|
||||||
bool expected = true;
|
|
||||||
return is_available.compare_exchange_weak(expected, false, std::memory_order_relaxed,
|
|
||||||
std::memory_order_relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send work to this worker abstraction
|
|
||||||
* @see TryCapture must be called before attempting to call this function
|
|
||||||
*/
|
|
||||||
template <class Work>
|
|
||||||
void SendWork(Work new_work) {
|
|
||||||
ASSERT_MSG(!is_available, "Trying to send work on a worker that's not captured");
|
|
||||||
work = std::move(new_work);
|
|
||||||
work_event.Set();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generate a callback for @see SleepClientThread
|
|
||||||
template <class Work>
|
|
||||||
auto Callback() {
|
|
||||||
return [this](std::shared_ptr<Kernel::Thread>, Kernel::HLERequestContext& ctx,
|
|
||||||
Kernel::ThreadWakeupReason reason) {
|
|
||||||
ASSERT(reason == Kernel::ThreadWakeupReason::Signal);
|
|
||||||
std::get<Work>(work).Response(ctx);
|
|
||||||
is_available.store(true);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get kernel event that will be signalled by the worker when the host operation finishes
|
|
||||||
std::shared_ptr<Kernel::WritableEvent> KernelEvent() const {
|
|
||||||
return kernel_event;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
explicit BlockingWorker(Core::System& system, Service* service, std::string_view name) {
|
|
||||||
auto pair = Kernel::WritableEvent::CreateEventPair(system.Kernel(), std::string(name));
|
|
||||||
kernel_event = std::move(pair.writable);
|
|
||||||
thread = std::thread([this, &system, service, name] { Run(system, service, name); });
|
|
||||||
}
|
|
||||||
|
|
||||||
void Run(Core::System& system, Service* service, std::string_view name) {
|
|
||||||
system.RegisterHostThread();
|
|
||||||
|
|
||||||
const std::string thread_name = fmt::format("yuzu:{}", name);
|
|
||||||
MicroProfileOnThreadCreate(thread_name.c_str());
|
|
||||||
Common::SetCurrentThreadName(thread_name.c_str());
|
|
||||||
|
|
||||||
bool keep_running = true;
|
|
||||||
while (keep_running) {
|
|
||||||
work_event.Wait();
|
|
||||||
|
|
||||||
const auto visit_fn = [service, &keep_running]<typename T>(T&& w) {
|
|
||||||
if constexpr (std::is_same_v<std::decay_t<T>, std::monostate>) {
|
|
||||||
keep_running = false;
|
|
||||||
} else {
|
|
||||||
w.Execute(service);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
std::visit(visit_fn, work);
|
|
||||||
|
|
||||||
kernel_event->Signal();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::thread thread;
|
|
||||||
WorkVariant work;
|
|
||||||
Common::Event work_event;
|
|
||||||
std::shared_ptr<Kernel::WritableEvent> kernel_event;
|
|
||||||
std::atomic_bool is_available{true};
|
|
||||||
};
|
|
||||||
|
|
||||||
template <class Service, class... Types>
|
|
||||||
class BlockingWorkerPool {
|
|
||||||
using Worker = BlockingWorker<Service, Types...>;
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit BlockingWorkerPool(Core::System& system_, Service* service_)
|
|
||||||
: system{system_}, service{service_} {}
|
|
||||||
|
|
||||||
/// Returns a captured worker thread, creating new ones if necessary
|
|
||||||
Worker* CaptureWorker() {
|
|
||||||
for (auto& worker : workers) {
|
|
||||||
if (worker->TryCapture()) {
|
|
||||||
return worker.get();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
auto new_worker = Worker::Create(system, service, fmt::format("BSD:{}", workers.size()));
|
|
||||||
[[maybe_unused]] const bool success = new_worker->TryCapture();
|
|
||||||
ASSERT(success);
|
|
||||||
|
|
||||||
return workers.emplace_back(std::move(new_worker)).get();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
Core::System& system;
|
|
||||||
Service* const service;
|
|
||||||
|
|
||||||
std::vector<std::unique_ptr<Worker>> workers;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace Service::Sockets
|
|
@ -0,0 +1,31 @@
|
|||||||
|
// Copyright 2020 yuzu Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace Tegra {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 Tegra
|
@ -1,86 +0,0 @@
|
|||||||
// Copyright 2019 yuzu Emulator Project
|
|
||||||
// Licensed under GPLv2 or any later version
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#include "core/core.h"
|
|
||||||
#include "core/hardware_interrupt_manager.h"
|
|
||||||
#include "video_core/gpu_asynch.h"
|
|
||||||
#include "video_core/gpu_thread.h"
|
|
||||||
#include "video_core/renderer_base.h"
|
|
||||||
|
|
||||||
namespace VideoCommon {
|
|
||||||
|
|
||||||
GPUAsynch::GPUAsynch(Core::System& system_, bool use_nvdec_)
|
|
||||||
: GPU{system_, true, use_nvdec_}, gpu_thread{system_} {}
|
|
||||||
|
|
||||||
GPUAsynch::~GPUAsynch() = default;
|
|
||||||
|
|
||||||
void GPUAsynch::Start() {
|
|
||||||
gpu_thread.StartThread(*renderer, renderer->Context(), *dma_pusher, *cdma_pusher);
|
|
||||||
cpu_context = renderer->GetRenderWindow().CreateSharedContext();
|
|
||||||
cpu_context->MakeCurrent();
|
|
||||||
}
|
|
||||||
|
|
||||||
void GPUAsynch::ObtainContext() {
|
|
||||||
cpu_context->MakeCurrent();
|
|
||||||
}
|
|
||||||
|
|
||||||
void GPUAsynch::ReleaseContext() {
|
|
||||||
cpu_context->DoneCurrent();
|
|
||||||
}
|
|
||||||
|
|
||||||
void GPUAsynch::PushGPUEntries(Tegra::CommandList&& entries) {
|
|
||||||
gpu_thread.SubmitList(std::move(entries));
|
|
||||||
}
|
|
||||||
|
|
||||||
void GPUAsynch::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 GPUAsynch::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
|
|
||||||
gpu_thread.SwapBuffers(framebuffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GPUAsynch::FlushRegion(VAddr addr, u64 size) {
|
|
||||||
gpu_thread.FlushRegion(addr, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GPUAsynch::InvalidateRegion(VAddr addr, u64 size) {
|
|
||||||
gpu_thread.InvalidateRegion(addr, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GPUAsynch::FlushAndInvalidateRegion(VAddr addr, u64 size) {
|
|
||||||
gpu_thread.FlushAndInvalidateRegion(addr, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GPUAsynch::TriggerCpuInterrupt(const u32 syncpoint_id, const u32 value) const {
|
|
||||||
auto& interrupt_manager = system.InterruptManager();
|
|
||||||
interrupt_manager.GPUInterruptSyncpt(syncpoint_id, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GPUAsynch::WaitIdle() const {
|
|
||||||
gpu_thread.WaitIdle();
|
|
||||||
}
|
|
||||||
|
|
||||||
void GPUAsynch::OnCommandListEnd() {
|
|
||||||
gpu_thread.OnCommandListEnd();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace VideoCommon
|
|
@ -1,47 +0,0 @@
|
|||||||
// Copyright 2019 yuzu Emulator Project
|
|
||||||
// Licensed under GPLv2 or any later version
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "video_core/gpu.h"
|
|
||||||
#include "video_core/gpu_thread.h"
|
|
||||||
|
|
||||||
namespace Core::Frontend {
|
|
||||||
class GraphicsContext;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace VideoCore {
|
|
||||||
class RendererBase;
|
|
||||||
} // namespace VideoCore
|
|
||||||
|
|
||||||
namespace VideoCommon {
|
|
||||||
|
|
||||||
/// Implementation of GPU interface that runs the GPU asynchronously
|
|
||||||
class GPUAsynch final : public Tegra::GPU {
|
|
||||||
public:
|
|
||||||
explicit GPUAsynch(Core::System& system_, bool use_nvdec_);
|
|
||||||
~GPUAsynch() override;
|
|
||||||
|
|
||||||
void Start() override;
|
|
||||||
void ObtainContext() override;
|
|
||||||
void ReleaseContext() override;
|
|
||||||
void PushGPUEntries(Tegra::CommandList&& entries) override;
|
|
||||||
void PushCommandBuffer(Tegra::ChCommandHeaderList& entries) override;
|
|
||||||
void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override;
|
|
||||||
void FlushRegion(VAddr addr, u64 size) override;
|
|
||||||
void InvalidateRegion(VAddr addr, u64 size) override;
|
|
||||||
void FlushAndInvalidateRegion(VAddr addr, u64 size) override;
|
|
||||||
void WaitIdle() const override;
|
|
||||||
|
|
||||||
void OnCommandListEnd() override;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void TriggerCpuInterrupt(u32 syncpoint_id, u32 value) const override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
GPUThread::ThreadManager gpu_thread;
|
|
||||||
std::unique_ptr<Core::Frontend::GraphicsContext> cpu_context;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace VideoCommon
|
|
@ -1,61 +0,0 @@
|
|||||||
// Copyright 2019 yuzu Emulator Project
|
|
||||||
// Licensed under GPLv2 or any later version
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#include "video_core/gpu_synch.h"
|
|
||||||
#include "video_core/renderer_base.h"
|
|
||||||
|
|
||||||
namespace VideoCommon {
|
|
||||||
|
|
||||||
GPUSynch::GPUSynch(Core::System& system_, bool use_nvdec_) : GPU{system_, false, use_nvdec_} {}
|
|
||||||
|
|
||||||
GPUSynch::~GPUSynch() = default;
|
|
||||||
|
|
||||||
void GPUSynch::Start() {}
|
|
||||||
|
|
||||||
void GPUSynch::ObtainContext() {
|
|
||||||
renderer->Context().MakeCurrent();
|
|
||||||
}
|
|
||||||
|
|
||||||
void GPUSynch::ReleaseContext() {
|
|
||||||
renderer->Context().DoneCurrent();
|
|
||||||
}
|
|
||||||
|
|
||||||
void GPUSynch::PushGPUEntries(Tegra::CommandList&& entries) {
|
|
||||||
dma_pusher->Push(std::move(entries));
|
|
||||||
dma_pusher->DispatchCalls();
|
|
||||||
}
|
|
||||||
|
|
||||||
void GPUSynch::PushCommandBuffer(Tegra::ChCommandHeaderList& entries) {
|
|
||||||
if (!use_nvdec) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// This condition fires when a video stream ends, clears all intermediary data
|
|
||||||
if (entries[0].raw == 0xDEADB33F) {
|
|
||||||
cdma_pusher.reset();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!cdma_pusher) {
|
|
||||||
cdma_pusher = std::make_unique<Tegra::CDmaPusher>(*this);
|
|
||||||
}
|
|
||||||
cdma_pusher->Push(std::move(entries));
|
|
||||||
cdma_pusher->DispatchCalls();
|
|
||||||
}
|
|
||||||
|
|
||||||
void GPUSynch::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
|
|
||||||
renderer->SwapBuffers(framebuffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GPUSynch::FlushRegion(VAddr addr, u64 size) {
|
|
||||||
renderer->Rasterizer().FlushRegion(addr, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GPUSynch::InvalidateRegion(VAddr addr, u64 size) {
|
|
||||||
renderer->Rasterizer().InvalidateRegion(addr, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GPUSynch::FlushAndInvalidateRegion(VAddr addr, u64 size) {
|
|
||||||
renderer->Rasterizer().FlushAndInvalidateRegion(addr, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace VideoCommon
|
|
@ -1,41 +0,0 @@
|
|||||||
// Copyright 2019 yuzu Emulator Project
|
|
||||||
// Licensed under GPLv2 or any later version
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "video_core/gpu.h"
|
|
||||||
|
|
||||||
namespace Core::Frontend {
|
|
||||||
class GraphicsContext;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace VideoCore {
|
|
||||||
class RendererBase;
|
|
||||||
} // namespace VideoCore
|
|
||||||
|
|
||||||
namespace VideoCommon {
|
|
||||||
|
|
||||||
/// Implementation of GPU interface that runs the GPU synchronously
|
|
||||||
class GPUSynch final : public Tegra::GPU {
|
|
||||||
public:
|
|
||||||
explicit GPUSynch(Core::System& system_, bool use_nvdec_);
|
|
||||||
~GPUSynch() override;
|
|
||||||
|
|
||||||
void Start() override;
|
|
||||||
void ObtainContext() override;
|
|
||||||
void ReleaseContext() override;
|
|
||||||
void PushGPUEntries(Tegra::CommandList&& entries) override;
|
|
||||||
void PushCommandBuffer(Tegra::ChCommandHeaderList& entries) override;
|
|
||||||
void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override;
|
|
||||||
void FlushRegion(VAddr addr, u64 size) override;
|
|
||||||
void InvalidateRegion(VAddr addr, u64 size) override;
|
|
||||||
void FlushAndInvalidateRegion(VAddr addr, u64 size) override;
|
|
||||||
void WaitIdle() const override {}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void TriggerCpuInterrupt([[maybe_unused]] u32 syncpoint_id,
|
|
||||||
[[maybe_unused]] u32 value) const override {}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace VideoCommon
|
|
Loading…
Reference in New Issue