mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-21 03:16:54 +02:00
Fix unnecessary queue drains in predication and suspend points
This commit is contained in:
@@ -62,7 +62,6 @@ public:
|
||||
|
||||
void BufferInit();
|
||||
void BufferFlush();
|
||||
void BufferFlushAndWait();
|
||||
void BufferWait();
|
||||
HW::Context& GetCtx() { return m_ctx; }
|
||||
HW::UserConfig& GetUcfg() { return m_ucfg; }
|
||||
|
||||
@@ -35,7 +35,6 @@ namespace Libs::Graphics {
|
||||
|
||||
static thread_local CommandProcessor* g_current_processor = nullptr;
|
||||
static thread_local Pm4Execution* g_current_execution = nullptr;
|
||||
static thread_local bool g_gpu_mutex_owned = false;
|
||||
static thread_local bool g_gpu_thread = false;
|
||||
static thread_local GuestGpu* g_gpu_state = nullptr;
|
||||
|
||||
@@ -54,27 +53,6 @@ struct DrawIndexedIndirectArgs {
|
||||
uint32_t start_instance_location;
|
||||
};
|
||||
|
||||
class GpuMutexLock final {
|
||||
public:
|
||||
explicit GpuMutexLock(Common::Mutex& mutex): m_mutex(mutex) {
|
||||
if (g_gpu_mutex_owned) {
|
||||
EXIT("recursive GPU mutex acquisition\n");
|
||||
}
|
||||
g_gpu_mutex_owned = true;
|
||||
m_mutex.Lock();
|
||||
}
|
||||
~GpuMutexLock() {
|
||||
if (!g_gpu_mutex_owned) {
|
||||
EXIT("invalid GPU mutex release\n");
|
||||
}
|
||||
m_mutex.Unlock();
|
||||
g_gpu_mutex_owned = false;
|
||||
}
|
||||
|
||||
private:
|
||||
Common::Mutex& m_mutex;
|
||||
};
|
||||
|
||||
static bool GraphicsRunDebugDumpEnabled() {
|
||||
return Config::GraphicsDebugDumpEnabled() &&
|
||||
Config::GetPrintfDirection() != Config::LogDirection::Silent;
|
||||
@@ -84,6 +62,7 @@ GuestGpu::GuestGpu(RenderContext& renderer): m_renderer(renderer) {
|
||||
EXIT_NOT_IMPLEMENTED(!Common::Thread::IsMainThread());
|
||||
GraphicsInitJmpTables();
|
||||
m_gfx_cp = std::make_unique<CommandProcessor>(renderer, 0);
|
||||
m_gfx_cp->Reset();
|
||||
m_thread = std::jthread(ThreadRun, this);
|
||||
}
|
||||
|
||||
@@ -160,20 +139,16 @@ void GuestGpu::Submit(std::span<const uint32_t> draw_commands,
|
||||
if (draw_commands.empty()) {
|
||||
return;
|
||||
}
|
||||
GpuMutexLock lock(m_submission_mutex);
|
||||
Submission submission;
|
||||
Submission submission;
|
||||
submission.type = SubmissionType::Graphics;
|
||||
submission.queue_id = 0;
|
||||
submission.commands = draw_commands;
|
||||
submission.constant_commands = constant_commands;
|
||||
submission.reset_processor = m_graphics_done;
|
||||
m_graphics_done = false;
|
||||
Enqueue(std::move(submission));
|
||||
}
|
||||
|
||||
void GuestGpu::SubmitCompute(uint32_t queue, std::span<const uint32_t> commands) {
|
||||
EXIT_IF(commands.empty());
|
||||
GpuMutexLock lock(m_submission_mutex);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(queue < ComputeQueueBase || queue >= ComputeQueueBase + ComputeQueueCount);
|
||||
|
||||
@@ -186,22 +161,21 @@ void GuestGpu::SubmitCompute(uint32_t queue, std::span<const uint32_t> commands)
|
||||
}
|
||||
|
||||
void GuestGpu::SubmitFlipPreparation(uint64_t request_id) {
|
||||
GpuMutexLock lock(m_submission_mutex);
|
||||
Submission submission;
|
||||
Submission submission;
|
||||
submission.type = SubmissionType::FlipPreparation;
|
||||
submission.queue_id = 0;
|
||||
submission.reset_processor = m_graphics_done;
|
||||
submission.flip_request_id = request_id;
|
||||
m_graphics_done = false;
|
||||
Enqueue(std::move(submission));
|
||||
}
|
||||
|
||||
void GuestGpu::Done() {
|
||||
GpuMutexLock lock(m_submission_mutex);
|
||||
if (!IsGpuThread()) {
|
||||
WaitForIdle();
|
||||
}
|
||||
m_graphics_done = true;
|
||||
void GuestGpu::SuspendPoint() {
|
||||
EXIT_IF(IsGpuThread() || CommandScheduler::InDeferredOperation());
|
||||
// Do not hold a queue lock while waiting: asynchronous work may be needed to
|
||||
// finish the preceding graphics frame. The first point returns immediately.
|
||||
m_suspend_point_ready->acquire();
|
||||
Submission submission;
|
||||
submission.type = SubmissionType::SuspendPoint;
|
||||
Enqueue(std::move(submission));
|
||||
m_done_num++;
|
||||
}
|
||||
|
||||
@@ -229,6 +203,9 @@ void CommandProcessor::Reset() {
|
||||
m_context_state_pushed = false;
|
||||
m_index_type_and_size = 0;
|
||||
m_index_buffer_size = 0;
|
||||
m_index_base_addr = 0;
|
||||
m_num_instances = 1;
|
||||
m_predicate_skip = false;
|
||||
m_user_data_marker = HW::UserSgprType::Unknown;
|
||||
m_draw_indirect_args_base_addr = 0;
|
||||
m_dispatch_indirect_args_base_addr = 0;
|
||||
@@ -268,10 +245,6 @@ void CommandProcessor::BufferFlush() {
|
||||
GetScheduler().Flush();
|
||||
}
|
||||
|
||||
void CommandProcessor::BufferFlushAndWait() {
|
||||
GetScheduler().FlushAndWait();
|
||||
}
|
||||
|
||||
void CommandProcessor::BufferWait() {
|
||||
BufferInit();
|
||||
GetScheduler().Finish();
|
||||
@@ -459,6 +432,7 @@ void GuestGpu::Enqueue(Submission submission) {
|
||||
}
|
||||
|
||||
void GuestGpu::WaitForIdle() {
|
||||
EXIT_IF(IsGpuThread() || CommandScheduler::InDeferredOperation());
|
||||
Common::LockGuard lock(m_queue_mutex);
|
||||
while (m_processing || !m_commands.empty() || m_submission_count != 0) {
|
||||
m_idle.Wait(&m_queue_mutex);
|
||||
@@ -564,11 +538,7 @@ void GuestGpu::ThreadRun(void* data) {
|
||||
|
||||
bool GuestGpu::Process(Submission& submission) {
|
||||
const bool first_slice = !submission.started;
|
||||
auto& cp = GetProcessor(submission.queue_id);
|
||||
|
||||
if (first_slice && submission.reset_processor) {
|
||||
cp.Reset();
|
||||
}
|
||||
auto& cp = GetProcessor(submission.queue_id);
|
||||
|
||||
if (first_slice) {
|
||||
submission.started = true;
|
||||
@@ -645,6 +615,13 @@ bool GuestGpu::Process(Submission& submission) {
|
||||
m_renderer.RunGarbageCollector();
|
||||
cp.PrepareCpuFlip(submission.flip_request_id);
|
||||
break;
|
||||
case SubmissionType::SuspendPoint:
|
||||
cp.EmitGlobalBarrier();
|
||||
m_renderer.GetCommandScheduler().DeferPriorityOperation(
|
||||
[ready = m_suspend_point_ready] { ready->release(); });
|
||||
cp.BufferFlush();
|
||||
cp.Reset();
|
||||
break;
|
||||
}
|
||||
|
||||
return complete;
|
||||
@@ -849,9 +826,7 @@ void CommandProcessor::SetPredication(uint32_t condition, uint32_t op, uint32_t
|
||||
}
|
||||
} break;
|
||||
case 0x03:
|
||||
if (wait_op != 0) {
|
||||
BufferFlushAndWait();
|
||||
}
|
||||
// The wait selector applies only to Z-pass query readiness.
|
||||
EXIT_NOT_IMPLEMENTED(address == nullptr);
|
||||
value = *reinterpret_cast<const volatile uint64_t*>(address);
|
||||
break;
|
||||
@@ -1034,9 +1009,6 @@ void CommandProcessor::DispatchDirect(uint32_t thread_group_x, uint32_t thread_g
|
||||
m_sh_ctx.SetCsWaveSize(Pm4::ComputeWaveSize(mode));
|
||||
|
||||
uint32_t frame_num = 0;
|
||||
// uint32_t local_x = 1;
|
||||
// uint32_t local_y = 1;
|
||||
// uint32_t local_z = 1;
|
||||
|
||||
{
|
||||
frame_num = m_renderer.GetGpu().GetFrameNum();
|
||||
@@ -1058,35 +1030,9 @@ void CommandProcessor::DispatchDirect(uint32_t thread_group_x, uint32_t thread_g
|
||||
}
|
||||
}
|
||||
|
||||
const auto& cs = m_sh_ctx.GetCs().cs_regs;
|
||||
// local_x = std::max(cs.num_thread_x, 1u);
|
||||
// local_y = std::max(cs.num_thread_y, 1u);
|
||||
// local_z = std::max(cs.num_thread_z, 1u);
|
||||
m_renderer.GetRenderExecutor().DispatchDirect(m_submit_id, CurrentBuffer(), thread_group_x,
|
||||
thread_group_y, thread_group_z, mode);
|
||||
}
|
||||
|
||||
/*constexpr uint32_t DispatchInitiatorUseThreadDimensions = 1u << 5u;
|
||||
auto group_count = [](uint32_t threads, uint32_t group_size) {
|
||||
return (threads == 0
|
||||
? 0u
|
||||
: (threads + std::max(group_size, 1u) - 1u) / std::max(group_size, 1u));
|
||||
};
|
||||
|
||||
auto groups_x = thread_group_x;
|
||||
auto groups_y = thread_group_y;
|
||||
auto groups_z = thread_group_z;
|
||||
if ((mode & DispatchInitiatorUseThreadDimensions) != 0) {
|
||||
groups_x = group_count(thread_group_x, local_x);
|
||||
groups_y = group_count(thread_group_y, local_y);
|
||||
groups_z = group_count(thread_group_z, local_z);
|
||||
}
|
||||
|
||||
const uint64_t invocations =
|
||||
static_cast<uint64_t>(groups_x) * groups_y * groups_z * local_x * local_y * local_z;
|
||||
if (invocations != 0) {
|
||||
BufferFlushAndWait();
|
||||
}*/
|
||||
}
|
||||
|
||||
void CommandProcessor::DispatchIndirect(uint64_t args_addr, uint32_t mode) {
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <semaphore>
|
||||
#include <span>
|
||||
#include <thread>
|
||||
|
||||
@@ -32,11 +33,14 @@ public:
|
||||
void SendCommandSync(Common::UniqueFunction<void>&& command);
|
||||
|
||||
// Submitted command memory is borrowed and must remain valid until GPU execution completes.
|
||||
void Submit(std::span<const uint32_t> draw_commands,
|
||||
std::span<const uint32_t> constant_commands);
|
||||
void SubmitCompute(uint32_t queue, std::span<const uint32_t> commands);
|
||||
void SubmitFlipPreparation(uint64_t request_id);
|
||||
void Done();
|
||||
void Submit(std::span<const uint32_t> draw_commands,
|
||||
std::span<const uint32_t> constant_commands);
|
||||
void SubmitCompute(uint32_t queue, std::span<const uint32_t> commands);
|
||||
void SubmitFlipPreparation(uint64_t request_id);
|
||||
// Insert an ordered graphics drain; only a previous suspend point can block the caller.
|
||||
void SuspendPoint();
|
||||
// Wait for guest command processing, including all compute queues (not native GPU completion).
|
||||
void WaitForIdle();
|
||||
[[nodiscard]] int GetFrameNum() const;
|
||||
|
||||
[[nodiscard]] static bool IsGpuThread() noexcept;
|
||||
@@ -48,7 +52,7 @@ private:
|
||||
static constexpr uint32_t ComputeQueueBase = 0x20;
|
||||
static constexpr uint32_t QueueCount = 1 + ComputeQueueCount;
|
||||
|
||||
enum class SubmissionType { Graphics, Compute, FlipPreparation };
|
||||
enum class SubmissionType { Graphics, Compute, FlipPreparation, SuspendPoint };
|
||||
|
||||
struct Submission {
|
||||
SubmissionType type = SubmissionType::Graphics;
|
||||
@@ -57,7 +61,6 @@ private:
|
||||
std::span<const uint32_t> constant_commands;
|
||||
Pm4Execution command_execution;
|
||||
Pm4Execution constant_execution;
|
||||
bool reset_processor = false;
|
||||
bool started = false;
|
||||
bool command_complete = false;
|
||||
bool constant_complete = false;
|
||||
@@ -66,14 +69,12 @@ private:
|
||||
};
|
||||
|
||||
void Enqueue(Submission submission);
|
||||
void WaitForIdle();
|
||||
void ProcessCommands();
|
||||
bool Process(Submission& submission);
|
||||
static void ThreadRun(void* data);
|
||||
CommandProcessor& GetProcessor(uint32_t queue_id);
|
||||
|
||||
RenderContext& m_renderer;
|
||||
Common::Mutex m_submission_mutex;
|
||||
Common::Mutex m_queue_mutex;
|
||||
std::mutex m_shutdown_mutex;
|
||||
Common::CondVar m_work_available;
|
||||
@@ -84,10 +85,12 @@ private:
|
||||
uint32_t m_next_queue = 0;
|
||||
uint32_t m_submission_count = 0;
|
||||
bool m_processing = false;
|
||||
bool m_graphics_done = true;
|
||||
bool m_accepting = true;
|
||||
bool m_stopping = false;
|
||||
bool m_shutdown_complete = false;
|
||||
// Completion callbacks can outlive GuestGpu during renderer shutdown.
|
||||
std::shared_ptr<std::binary_semaphore> m_suspend_point_ready =
|
||||
std::make_shared<std::binary_semaphore>(1);
|
||||
|
||||
std::unique_ptr<CommandProcessor> m_gfx_cp;
|
||||
std::array<std::unique_ptr<CommandProcessor>, ComputeQueueCount> m_compute_cp;
|
||||
|
||||
+1
-1
@@ -1552,7 +1552,7 @@ int KYTY_SYSV_ABI AgcSuspendPoint() {
|
||||
PRINT_NAME();
|
||||
|
||||
EXIT_IF(g_renderer == nullptr);
|
||||
g_renderer->GetGpu().Done();
|
||||
g_renderer->GetGpu().SuspendPoint();
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
@@ -2402,6 +2402,232 @@ public:
|
||||
std::printf("[host] %-32s ok\n", "StreamBufferRing");
|
||||
}
|
||||
|
||||
void CheckGpuSuspendPoint() {
|
||||
constexpr const char *name = "GpuSuspendPoint";
|
||||
EnsureRuntimeContext();
|
||||
auto &context = Renderer();
|
||||
context.InitializeGpu(nullptr);
|
||||
auto &gpu = context.GetGpu();
|
||||
auto &scheduler = context.GetCommandScheduler();
|
||||
CommandProcessor setup(context, 0);
|
||||
|
||||
vk::SemaphoreTypeCreateInfo timeline_type{};
|
||||
timeline_type.sType = vk::StructureType::eSemaphoreTypeCreateInfo;
|
||||
timeline_type.semaphoreType = vk::SemaphoreType::eTimeline;
|
||||
vk::SemaphoreCreateInfo timeline_create{};
|
||||
timeline_create.sType = vk::StructureType::eSemaphoreCreateInfo;
|
||||
timeline_create.pNext = &timeline_type;
|
||||
vk::Semaphore gate = nullptr;
|
||||
Require(name, "timeline create",
|
||||
m_runtime_context.device.createSemaphore(
|
||||
&timeline_create, nullptr, &gate) == vk::Result::eSuccess,
|
||||
"failed to create the native completion gate");
|
||||
const auto signal = [&](uint64_t value) {
|
||||
vk::SemaphoreSignalInfo info{};
|
||||
info.sType = vk::StructureType::eSemaphoreSignalInfo;
|
||||
info.semaphore = gate;
|
||||
info.value = value;
|
||||
Require(name, "timeline signal",
|
||||
m_runtime_context.device.signalSemaphore(&info) ==
|
||||
vk::Result::eSuccess,
|
||||
"failed to release native GPU work");
|
||||
};
|
||||
const auto finish = [&] {
|
||||
gpu.WaitForIdle();
|
||||
gpu.SendCommandSync([&] {
|
||||
scheduler.Finish();
|
||||
scheduler.WaitPriorityOperations(scheduler.CurrentTick() - 1);
|
||||
});
|
||||
};
|
||||
const auto write = [](uint32_t *packet, uint32_t *destination,
|
||||
uint32_t value, bool predicated = false) {
|
||||
const auto address = reinterpret_cast<uint64_t>(destination);
|
||||
packet[0] = KYTY_PM4(5, Pm4::IT_WRITE_DATA, 0) | uint32_t{predicated};
|
||||
packet[1] = 0;
|
||||
packet[2] = static_cast<uint32_t>(address);
|
||||
packet[3] = static_cast<uint32_t>(address >> 32u);
|
||||
packet[4] = value;
|
||||
};
|
||||
|
||||
gpu.SendCommandSync([&] {
|
||||
setup.BufferInit();
|
||||
SubmitInfo blocked;
|
||||
blocked.AddWait(gate, 1);
|
||||
scheduler.Flush(blocked);
|
||||
});
|
||||
std::binary_semaphore first_returned{0};
|
||||
std::jthread first([&] {
|
||||
gpu.SuspendPoint();
|
||||
first_returned.release();
|
||||
});
|
||||
const bool first_nonblocking =
|
||||
first_returned.try_acquire_for(std::chrono::seconds(2));
|
||||
if (!first_nonblocking) {
|
||||
signal(1);
|
||||
}
|
||||
first.join();
|
||||
Require(name, "first call", first_nonblocking,
|
||||
"the first suspend point waited for native GPU completion");
|
||||
|
||||
// Let the marker reach Vulkan while its completion remains gated.
|
||||
std::binary_semaphore marker_parsed{0};
|
||||
std::jthread parser([&] {
|
||||
gpu.WaitForIdle();
|
||||
marker_parsed.release();
|
||||
});
|
||||
const bool parser_nonblocking =
|
||||
marker_parsed.try_acquire_for(std::chrono::seconds(2));
|
||||
if (!parser_nonblocking) {
|
||||
signal(1);
|
||||
}
|
||||
parser.join();
|
||||
Require(name, "marker insertion", parser_nonblocking,
|
||||
"inserting a suspend point blocked the GPU command thread");
|
||||
|
||||
std::binary_semaphore second_entered{0};
|
||||
std::binary_semaphore second_returned{0};
|
||||
std::jthread second([&] {
|
||||
second_entered.release();
|
||||
gpu.SuspendPoint();
|
||||
second_returned.release();
|
||||
});
|
||||
second_entered.acquire();
|
||||
const bool second_overtook_completion =
|
||||
second_returned.try_acquire_for(std::chrono::milliseconds(100));
|
||||
|
||||
uint32_t compute_value = 0;
|
||||
std::array<uint32_t, 5> compute_commands{};
|
||||
write(compute_commands.data(), &compute_value, 33);
|
||||
bool host_lane_responded = false;
|
||||
std::binary_semaphore compute_progress{0};
|
||||
std::jthread compute([&] {
|
||||
gpu.SubmitCompute(0x20, compute_commands);
|
||||
gpu.SendCommandSync([&] { host_lane_responded = true; });
|
||||
gpu.WaitForIdle();
|
||||
compute_progress.release();
|
||||
});
|
||||
const bool compute_did_not_wait =
|
||||
compute_progress.try_acquire_for(std::chrono::seconds(2));
|
||||
signal(1);
|
||||
second.join();
|
||||
compute.join();
|
||||
finish();
|
||||
Require(name, "one outstanding point", !second_overtook_completion,
|
||||
"a second suspend point returned before the first native completion");
|
||||
Require(name, "independent queue progress",
|
||||
compute_did_not_wait && host_lane_responded && compute_value == 33,
|
||||
"waiting for a previous suspend point blocked compute submissions "
|
||||
"or the host command lane");
|
||||
Require(name, "frame count", gpu.GetFrameNum() == 2,
|
||||
"suspend-point insertion did not advance the frame counter once");
|
||||
|
||||
// A marker must stay behind a stalled graphics stream, then clear its
|
||||
// predicate before the next stream. Async compute releases that stream.
|
||||
alignas(16) uint64_t predicate = 0;
|
||||
uint32_t label = 0;
|
||||
uint32_t prefix = 0;
|
||||
uint32_t skipped = 0;
|
||||
uint32_t after_reset = 0;
|
||||
const auto predicate_address = reinterpret_cast<uint64_t>(&predicate);
|
||||
const auto label_address = reinterpret_cast<uint64_t>(&label);
|
||||
std::array<uint32_t, 21> before{};
|
||||
before[0] = KYTY_PM4(4, Pm4::IT_SET_PREDICATION, 0);
|
||||
before[1] = (3u << 16u) | (1u << 8u);
|
||||
before[2] = static_cast<uint32_t>(predicate_address);
|
||||
before[3] = static_cast<uint32_t>(predicate_address >> 32u);
|
||||
write(before.data() + 4, &skipped, 11, true);
|
||||
write(before.data() + 9, &prefix, 22);
|
||||
before[14] = KYTY_PM4(7, Pm4::IT_WAIT_REG_MEM, 0);
|
||||
before[15] = 0x10u | 3u;
|
||||
before[16] = static_cast<uint32_t>(label_address);
|
||||
before[17] = static_cast<uint32_t>(label_address >> 32u);
|
||||
before[18] = 1;
|
||||
before[19] = UINT32_MAX;
|
||||
std::array<uint32_t, 5> after{};
|
||||
write(after.data(), &after_reset, 44, true);
|
||||
std::binary_semaphore lane_entered{0};
|
||||
std::binary_semaphore release_lane{0};
|
||||
gpu.SendCommand([&] {
|
||||
lane_entered.release();
|
||||
release_lane.acquire();
|
||||
});
|
||||
lane_entered.acquire();
|
||||
gpu.Submit(before, {});
|
||||
std::binary_semaphore queued_returned{0};
|
||||
std::jthread queued([&] {
|
||||
gpu.SuspendPoint();
|
||||
queued_returned.release();
|
||||
});
|
||||
const bool queued_nonblocking =
|
||||
queued_returned.try_acquire_for(std::chrono::seconds(2));
|
||||
if (!queued_nonblocking) {
|
||||
release_lane.release();
|
||||
gpu.SendCommandSync([&] { label = 1; });
|
||||
queued.join();
|
||||
finish();
|
||||
Require(name, "queued insertion", false,
|
||||
"suspend-point insertion drained pending guest graphics");
|
||||
}
|
||||
queued.join();
|
||||
gpu.Submit(after, {});
|
||||
release_lane.release();
|
||||
bool prefix_reached = false;
|
||||
bool graphics_ordered = false;
|
||||
const auto deadline = std::chrono::steady_clock::now() +
|
||||
std::chrono::seconds(2);
|
||||
do {
|
||||
gpu.SendCommandSync([&] {
|
||||
prefix_reached = prefix == 22;
|
||||
graphics_ordered = skipped == 0 && after_reset == 0;
|
||||
});
|
||||
std::this_thread::yield();
|
||||
} while (!prefix_reached && std::chrono::steady_clock::now() < deadline);
|
||||
std::array<uint32_t, 5> release_graphics{};
|
||||
write(release_graphics.data(), &label, 1);
|
||||
gpu.SubmitCompute(0x20, release_graphics);
|
||||
finish();
|
||||
Require(name, "graphics FIFO",
|
||||
prefix_reached && graphics_ordered && skipped == 0,
|
||||
"suspend point or later graphics overtook a stalled graphics stream");
|
||||
Require(name, "graphics reset", after_reset == 44,
|
||||
"suspend point preserved the previous frame's predicate");
|
||||
|
||||
// Delay the completion callback beyond the owner's shutdown when possible.
|
||||
// Its credit must remain alive independently of the GuestGpu object.
|
||||
std::binary_semaphore priority_entered{0};
|
||||
std::binary_semaphore release_priority{0};
|
||||
gpu.SendCommandSync([&] {
|
||||
scheduler.DeferPriorityOperation([&] {
|
||||
priority_entered.release();
|
||||
release_priority.acquire();
|
||||
});
|
||||
SubmitInfo blocked;
|
||||
blocked.AddWait(gate, 2);
|
||||
scheduler.Flush(blocked);
|
||||
});
|
||||
gpu.SuspendPoint();
|
||||
gpu.WaitForIdle();
|
||||
std::binary_semaphore shutdown_returned{0};
|
||||
std::jthread shutdown([&] {
|
||||
context.ShutdownGpu();
|
||||
shutdown_returned.release();
|
||||
});
|
||||
const bool shutdown_overtook_native =
|
||||
shutdown_returned.try_acquire_for(std::chrono::milliseconds(100));
|
||||
signal(2);
|
||||
priority_entered.acquire();
|
||||
if (!shutdown_overtook_native) {
|
||||
(void)shutdown_returned.try_acquire_for(std::chrono::seconds(2));
|
||||
}
|
||||
release_priority.release();
|
||||
shutdown.join();
|
||||
scheduler.DrainPriorityOperations();
|
||||
m_runtime_context.device.destroySemaphore(gate, nullptr);
|
||||
Require(name, "shutdown native completion", !shutdown_overtook_native,
|
||||
"shutdown returned while the suspend point was still executing");
|
||||
std::printf("[host] %-32s ok\n", name);
|
||||
}
|
||||
|
||||
void CheckGpuCommandLane() {
|
||||
EnsureRuntimeContext();
|
||||
auto &context = Renderer();
|
||||
@@ -2647,7 +2873,7 @@ public:
|
||||
graphics_gate_entered.acquire();
|
||||
live_graphics_commands[4] = 22;
|
||||
graphics_gate_release.release();
|
||||
gpu.Done();
|
||||
gpu.WaitForIdle();
|
||||
Require("GpuCommandLane", "borrowed graphics commands",
|
||||
live_graphics_value == 22,
|
||||
"graphics submission executed a copied PM4 stream");
|
||||
@@ -2665,7 +2891,7 @@ public:
|
||||
compute_gate_entered.acquire();
|
||||
live_compute_commands[4] = 44;
|
||||
compute_gate_release.release();
|
||||
gpu.Done();
|
||||
gpu.WaitForIdle();
|
||||
Require("GpuCommandLane", "borrowed compute commands",
|
||||
live_compute_value == 44,
|
||||
"compute submission executed a copied PM4 stream");
|
||||
@@ -2721,7 +2947,7 @@ public:
|
||||
write_packet(no_interrupt_graphics_commands.data(),
|
||||
&no_interrupt_graphics_value, 55);
|
||||
gpu.Submit(no_interrupt_graphics_commands, {});
|
||||
gpu.Done();
|
||||
gpu.WaitForIdle();
|
||||
finish_gpu();
|
||||
|
||||
LibKernel::EventQueue::KernelEvent interrupt_event{};
|
||||
@@ -2745,7 +2971,7 @@ public:
|
||||
interrupt_gate_entered.acquire();
|
||||
live_interrupt_commands[2] = 0;
|
||||
interrupt_gate_release.release();
|
||||
gpu.Done();
|
||||
gpu.WaitForIdle();
|
||||
finish_gpu();
|
||||
|
||||
interrupt_count = 0;
|
||||
@@ -2761,7 +2987,7 @@ public:
|
||||
auto graphics_interrupt_commands = make_interrupt_packet(
|
||||
1, 1, &graphics_interrupt_label, 0x11223344u, 0);
|
||||
gpu.Submit(graphics_interrupt_commands, {});
|
||||
gpu.Done();
|
||||
gpu.WaitForIdle();
|
||||
finish_gpu();
|
||||
|
||||
interrupt_count = 0;
|
||||
@@ -2787,7 +3013,7 @@ public:
|
||||
compute_interrupt_gate_entered.acquire();
|
||||
compute_interrupt_commands[2] |= 1u << 24u;
|
||||
compute_interrupt_gate_release.release();
|
||||
gpu.Done();
|
||||
gpu.WaitForIdle();
|
||||
finish_gpu();
|
||||
|
||||
interrupt_count = 0;
|
||||
@@ -2803,7 +3029,7 @@ public:
|
||||
auto compute_clock_commands = make_interrupt_packet(
|
||||
3, 1, &compute_clock_label, 0, 0x567u);
|
||||
gpu.SubmitCompute(0x20, compute_clock_commands);
|
||||
gpu.Done();
|
||||
gpu.WaitForIdle();
|
||||
finish_gpu();
|
||||
|
||||
interrupt_count = 0;
|
||||
@@ -2821,7 +3047,7 @@ public:
|
||||
2, 2, &compute_done_label, compute_done_value, 0x678u);
|
||||
compute_done_commands[1] = 0x62fu; // CS_DONE, shader-done index, no GCR action.
|
||||
gpu.SubmitCompute(0x20, compute_done_commands);
|
||||
gpu.Done();
|
||||
gpu.WaitForIdle();
|
||||
finish_gpu();
|
||||
|
||||
interrupt_count = 0;
|
||||
@@ -2896,7 +3122,7 @@ public:
|
||||
});
|
||||
std::this_thread::yield();
|
||||
}
|
||||
gpu.Done();
|
||||
gpu.WaitForIdle();
|
||||
Require("GpuCommandLane", "packet-boundary command polling",
|
||||
packet_marker_a_at_callback == 11 &&
|
||||
packet_marker_b_at_callback == 0 && packet_marker_a == 11 &&
|
||||
@@ -2934,21 +3160,21 @@ public:
|
||||
uint32_t ordered_suffix = 0;
|
||||
std::jthread ordered([&] {
|
||||
ordered_started.release();
|
||||
gpu.Done();
|
||||
gpu.WaitForIdle();
|
||||
ordered_suffix = suffix;
|
||||
ordered_finished = true;
|
||||
});
|
||||
ordered_started.acquire();
|
||||
gpu.SendCommandSync([&] {
|
||||
Require("GpuCommandLane", "submit done barrier", !ordered_finished.load(),
|
||||
"submit done returned before a queued submission");
|
||||
Require("GpuCommandLane", "guest queue idle fence", !ordered_finished.load(),
|
||||
"idle fence returned before a queued submission");
|
||||
label = 1;
|
||||
});
|
||||
ordered.join();
|
||||
Require("GpuCommandLane", "ordered completion",
|
||||
prefix == 11 && suffix == 22 && ordered_suffix == 22 &&
|
||||
ordered_finished.load(),
|
||||
"submit done did not drain prior PM4 work");
|
||||
"idle fence did not drain prior PM4 work");
|
||||
|
||||
auto &resources = context;
|
||||
constexpr uint64_t empty_unmap_base = 0x0000000200400000ull;
|
||||
@@ -2971,7 +3197,7 @@ public:
|
||||
unmap_complete.acquire();
|
||||
}
|
||||
unmap_thread.join();
|
||||
gpu.Done();
|
||||
gpu.WaitForIdle();
|
||||
Require("GpuCommandLane", "unmap queue progress",
|
||||
unmap_returned &&
|
||||
!resources.IsMapped(empty_unmap_base, empty_unmap_size) &&
|
||||
@@ -3098,7 +3324,7 @@ public:
|
||||
dma_cursor == dma_commands.size(),
|
||||
"DMA_DATA packet stream has the wrong size");
|
||||
gpu.Submit(dma_commands, {});
|
||||
gpu.Done();
|
||||
gpu.WaitForIdle();
|
||||
constexpr uint32_t clean_fill_value = 0xdecafbad;
|
||||
gpu.SendCommandSync([&] {
|
||||
auto &buffer_cache = resources.GetBufferCache();
|
||||
@@ -3336,15 +3562,12 @@ public:
|
||||
std::binary_semaphore command_entered{0};
|
||||
std::binary_semaphore release_command{0};
|
||||
std::atomic<bool> shutdown_complete{false};
|
||||
std::atomic<bool> submission_lane_entered{false};
|
||||
bool shutdown_owner_visible = false;
|
||||
resources.MapMemory(empty_unmap_base, empty_unmap_size);
|
||||
gpu.SendCommand([&] {
|
||||
command_entered.release();
|
||||
release_command.acquire();
|
||||
shutdown_owner_visible = &context.GetGpu() == &gpu;
|
||||
gpu.Done();
|
||||
submission_lane_entered = true;
|
||||
});
|
||||
command_entered.acquire();
|
||||
std::jthread shutdown_thread([&] {
|
||||
@@ -3360,8 +3583,7 @@ public:
|
||||
shutdown_thread.join();
|
||||
Require(
|
||||
"GpuCommandLane", "owned shutdown completion",
|
||||
shutdown_complete.load() && submission_lane_entered.load() &&
|
||||
shutdown_owner_visible,
|
||||
shutdown_complete.load() && shutdown_owner_visible,
|
||||
"GPU owner did not remain available while draining queued work");
|
||||
resources.UnmapMemory(empty_unmap_base, empty_unmap_size);
|
||||
Require("GpuCommandLane", "unmap after shutdown",
|
||||
@@ -33981,12 +34203,15 @@ void CheckPm4Predication(RenderContext &renderer) {
|
||||
uint32_t wait, const void *source, bool skip) {
|
||||
predicated = unconditional = 0;
|
||||
const auto packet = commands(op, condition, wait, source);
|
||||
const auto tick = renderer.GetCommandScheduler().CurrentTick();
|
||||
Pm4Execution execution;
|
||||
Require(name, stage,
|
||||
processor.Process(execution, packet) == Pm4ProcessResult::Complete &&
|
||||
processor.ShouldSkipPredicatedPackets() == skip &&
|
||||
predicated == (skip ? 0u : 11u) && unconditional == 22,
|
||||
"predicate polarity, tagged packet execution, or packet consumption is wrong");
|
||||
Require(name, stage, renderer.GetCommandScheduler().CurrentTick() == tick,
|
||||
"predication unnecessarily submitted and drained the host queue");
|
||||
};
|
||||
struct Case {
|
||||
uint64_t delta;
|
||||
@@ -35506,6 +35731,11 @@ int main(int argc, char **argv) {
|
||||
CheckPm4CeCompletion(vulkan.RuntimeRenderer());
|
||||
return 0;
|
||||
}
|
||||
if (argc == 2 && std::strcmp(argv[1], "--suspend-point-only") == 0) {
|
||||
VulkanHarness vulkan;
|
||||
vulkan.CheckGpuSuspendPoint();
|
||||
return 0;
|
||||
}
|
||||
if (argc == 2 && std::strcmp(argv[1], "--alignbyte-only") == 0) {
|
||||
VulkanHarness vulkan;
|
||||
RunCase(&vulkan, VectorAlignByteUsesFiveBitByteOffset());
|
||||
@@ -35915,6 +36145,7 @@ int main(int argc, char **argv) {
|
||||
for (const auto &test : graphics_tests) {
|
||||
RunGraphicsCase(&vulkan, test);
|
||||
}
|
||||
vulkan.CheckGpuSuspendPoint();
|
||||
vulkan.CheckGpuCommandLane();
|
||||
if (skipped_device_checks) {
|
||||
std::printf(
|
||||
|
||||
Reference in New Issue
Block a user