Fix unnecessary queue drains in predication and suspend points

This commit is contained in:
nmzik
2026-09-30 16:51:40 +04:00
parent 05057c9441
commit 840b9f5753
5 changed files with 289 additions and 110 deletions
@@ -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; }
+24 -78
View File
@@ -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 -10
View File
@@ -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
View File
@@ -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;
}
+251 -20
View File
@@ -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(