gpu: skip drains for CPU-only memory unmaps

This commit is contained in:
nmzik
2026-10-05 21:00:12 +04:00
parent 5b826c6e4a
commit e6fce45d3a
6 changed files with 81 additions and 37 deletions
+46 -10
View File
@@ -2341,6 +2341,7 @@ public:
constexpr uint64_t base = 0x0000000200000000ull;
constexpr uint64_t page = 0x4000;
const auto cpu_only_tick = scheduler.CurrentTick();
resources.MapMemory(base, page * 4);
resources.MapMemory(base + page * 2, page * 4);
Require("GpuMappedRangeLifecycle", "union",
@@ -2373,8 +2374,9 @@ public:
resources.MapMemory(new_prt, page * 6);
Require("GpuMappedRangeLifecycle", "PRT replacement",
!resources.IsMapped(old_prt, page * 4) &&
resources.IsMapped(new_prt, page * 6),
"old-unmap/new-map did not replace full PRT coverage");
resources.IsMapped(new_prt, page * 6) &&
scheduler.CurrentTick() == cpu_only_tick,
"CPU-only unmaps submitted GPU work or lost full PRT coverage");
scheduler.Finish();
context.ShutdownGpu();
@@ -2818,10 +2820,20 @@ public:
alignas(uint64_t) uint64_t interrupt_only_gds_label = UINT64_MAX;
alignas(uint64_t) uint64_t cb_db_release_label = UINT64_MAX;
bool parser_kept_nonblocking_boundaries = false;
bool host_resource_retired = false;
bool cpu_unmap_did_not_submit = false;
std::jthread no_gpu_wait([&] {
gpu.SendCommandSync([&] {
processor->BufferInit();
const auto tick_before = gpu_scheduler.CurrentTick();
gpu_scheduler.DeferOperation([&] { host_resource_retired = true; });
constexpr uint64_t cpu_only_address = 0x0000000200400000ull;
context.MapMemory(cpu_only_address, 0x4000);
context.UnmapMemory(cpu_only_address, 0x4000);
cpu_unmap_did_not_submit =
gpu_scheduler.CurrentTick() == tick_before &&
!host_resource_retired &&
!context.IsMapped(cpu_only_address, 0x4000);
processor->TriggerEvent(0x16u, 0u);
auto packet = make_release_mem(0, 0, nullptr, 0);
Pm4Execution execution;
@@ -2887,6 +2899,10 @@ public:
no_gpu_wait.join();
gpu.SendCommandSync([&] { gpu_scheduler.Finish(); });
m_runtime_context.device.destroySemaphore(blocked_timeline, nullptr);
Require("GpuCommandLane", "CPU-only unmap with pending host retirement",
parser_did_not_wait_gpu && cpu_unmap_did_not_submit &&
host_resource_retired,
"CPU-only unmap submitted or waited for unrelated native work");
Require("GpuCommandLane", "nonblocking GPU packets",
parser_did_not_wait_gpu && parser_kept_nonblocking_boundaries,
"a cache event, GL2 writeback, DE counter, or interrupt boundary "
@@ -3321,13 +3337,14 @@ public:
"an unrelated unmap waited for a blocked PM4 submission");
auto &scheduler = context.GetCommandScheduler();
std::atomic<bool> normal_completed{false};
gpu.SendCommandSync(
[&] { scheduler.DeferOperation([&] { normal_completed = true; }); });
std::atomic<bool> completion_published{false};
gpu.SendCommandSync([&] {
scheduler.DeferPriorityOperation([&] { completion_published = true; });
});
resources.MapMemory(empty_unmap_base, empty_unmap_size);
resources.UnmapMemory(empty_unmap_base, empty_unmap_size);
Require("GpuCommandLane", "unmap native completion",
normal_completed.load() &&
completion_published.load() &&
!resources.IsMapped(empty_unmap_base, empty_unmap_size),
"unmap returned before an earlier native guest-memory callback");
@@ -4100,6 +4117,13 @@ public:
cache.IsRegionRegistered(index_begin - 1, index_span + 2),
"indexed lookup mishandled a half-open boundary, gap, or broad "
"overlap");
const auto clean_unmap_tick = scheduler.CurrentTick();
resources.UnmapMemory(index_begin, index_page);
Require(name, "clean cached buffer unmap drain",
scheduler.CurrentTick() == clean_unmap_tick + 1 &&
!resources.IsMapped(index_begin, index_page),
"unmapping a clean cached buffer did not drain native work");
resources.MapMemory(index_begin, index_page);
const auto index_bridge =
cache.FindBuffer(index_begin + index_page - 1, index_page + 2);
Require(name, "registered-range merge",
@@ -7138,12 +7162,24 @@ public:
Prospero::ImageType::kColor2D, {2048, 1, 1}, 1, 4, 1);
const auto partial_unmap_image_id =
texture_cache.FindImage(partial_unmap_image);
texture_cache.UnmapMemory(partial_unmap_image.info.data.address, 0x1000);
const auto image_address = partial_unmap_image.info.data.address;
Require(name, "registered image boundaries",
texture_cache.IsRegionRegistered(image_address, 1) &&
texture_cache.IsRegionRegistered(image_address + 0x1fff, 1) &&
!texture_cache.IsRegionRegistered(image_address - 1, 1) &&
!texture_cache.IsRegionRegistered(image_address + 0x2000, 1) &&
texture_cache.IsRegionRegistered(image_address - 1, 0x2002),
"image lookup mishandled a partial overlap or adjacent range");
scheduler.Finish();
scheduler.DrainPriorityOperations();
const auto image_unmap_tick = scheduler.CurrentTick();
resources.UnmapMemory(image_address + 0x1000, 0x1000);
Require(name, "partial image unmap tracking",
partial_unmap_image_id &&
!texture_cache.FindImageFromRange(
partial_unmap_image.info.data.address, 0x2000, false),
"partial unmap left the deleted image's mapped tail tracked");
scheduler.CurrentTick() == image_unmap_tick + 1 &&
!texture_cache.IsRegionRegistered(image_address, 0x2000) &&
!texture_cache.FindImageFromRange(image_address, 0x2000, false),
"partial image unmap skipped its drain or retained the image");
constexpr uint64_t unformatted_alias_offset = 0x2500000;
auto unformatted_alias =