eden-miror/src/video_core/gpu_thread.h
lizzie c9c136bea7
[texture_cache, buffer_cache] Added TLS handling + changed command queue for GPU threading. (#3579)
(Merge of #3495 + #3108)

This PR works around to simplify math operations on hot pointers inside the access and requests to the cache of buffers and texture cache, removing previous logic of indirection and replaced by a PoD approach.

This will ensure less CPU times spended on the same request and flow directly into another chain of the render, in the same way, command queue currently uses an internal mutex that constraints the flow of data within the GPU threads, we're moving over a single command, I verified to keep using mutexes instead of internal mutex + mutex per operation, which are resolved by themselves.

In simplier words, this aims to improve performance on those games and devices where the waits for next orders on GPU commands were heavier than a single verification.

Co-Authored-by: @CamilleLaVey
Co-Authored-by: @Lizzie
Co-authored-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3579
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2026-02-20 00:52:07 +01:00

139 lines
3.9 KiB
C++

// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <atomic>
#include <condition_variable>
#include <mutex>
#include <optional>
#include <thread>
#include <variant>
#include "common/bounded_threadsafe_queue.h"
#include "common/polyfill_thread.h"
#include "video_core/framebuffer_config.h"
namespace Tegra {
struct FramebufferConfig;
namespace Control {
class Scheduler;
}
} // namespace Tegra
namespace Core {
namespace Frontend {
class GraphicsContext;
}
class System;
} // namespace Core
namespace VideoCore {
class RasterizerInterface;
class RendererBase;
} // namespace VideoCore
namespace VideoCommon::GPUThread {
/// Command to signal to the GPU thread that a command list is ready for processing
struct SubmitListCommand final {
explicit SubmitListCommand(s32 channel_, Tegra::CommandList&& entries_)
: channel{channel_}, entries{std::move(entries_)} {}
s32 channel;
Tegra::CommandList entries;
};
/// Command to signal to the GPU thread to flush a region
struct FlushRegionCommand final {
explicit constexpr FlushRegionCommand(DAddr addr_, u64 size_) : addr{addr_}, size{size_} {}
DAddr addr;
u64 size;
};
/// Command to signal to the GPU thread to invalidate a region
struct InvalidateRegionCommand final {
explicit constexpr InvalidateRegionCommand(DAddr addr_, u64 size_) : addr{addr_}, size{size_} {}
DAddr addr;
u64 size;
};
/// Command to signal to the GPU thread to flush and invalidate a region
struct FlushAndInvalidateRegionCommand final {
explicit constexpr FlushAndInvalidateRegionCommand(DAddr addr_, u64 size_)
: addr{addr_}, size{size_} {}
DAddr addr;
u64 size;
};
/// Command to make the gpu look into pending requests
struct GPUTickCommand final {};
using CommandData =
std::variant<std::monostate, SubmitListCommand, FlushRegionCommand, InvalidateRegionCommand,
FlushAndInvalidateRegionCommand, GPUTickCommand>;
struct CommandDataContainer {
CommandDataContainer() = default;
explicit CommandDataContainer(CommandData&& data_, u64 next_fence_, bool block_)
: data{std::move(data_)}, fence{next_fence_}, block(block_) {}
CommandData data;
u64 fence{};
bool block{};
};
/// Struct used to synchronize the GPU thread
struct SynchState final {
using CommandQueue = Common::SPSCQueue<CommandDataContainer>;
std::mutex write_lock;
CommandQueue queue;
u64 last_fence{};
std::atomic<u64> signaled_fence{};
std::condition_variable_any cv;
};
/// Class used to manage the GPU thread
class ThreadManager final {
public:
explicit ThreadManager(Core::System& system_, bool is_async_);
~ThreadManager();
/// Creates and starts the GPU thread.
void StartThread(VideoCore::RendererBase& renderer, Core::Frontend::GraphicsContext& context,
Tegra::Control::Scheduler& scheduler);
/// Push GPU command entries to be processed
void SubmitList(s32 channel, Tegra::CommandList&& entries);
/// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
void FlushRegion(DAddr addr, u64 size);
/// Notify rasterizer that any caches of the specified region should be invalidated
void InvalidateRegion(DAddr addr, u64 size);
/// Notify rasterizer that any caches of the specified region should be flushed and invalidated
void FlushAndInvalidateRegion(DAddr addr, u64 size);
void TickGPU();
private:
/// Pushes a command to be executed by the GPU thread
u64 PushCommand(CommandData&& command_data, bool block = false);
Core::System& system;
const bool is_async;
VideoCore::RasterizerInterface* rasterizer = nullptr;
SynchState state;
std::jthread thread;
};
} // namespace VideoCommon::GPUThread