graphics: streamline staging-buffer writes

This commit is contained in:
nmzik
2026-10-07 22:27:36 +04:00
parent 006875ed4f
commit 4d10ea17a0
2 changed files with 103 additions and 2 deletions
+74
View File
@@ -164,6 +164,13 @@ struct BufferCacheTestAccess {
return cache.m_download_buffer;
}
static void SetUnusedStagingBufferSize(BufferCache &cache, uint64_t size) {
// Called immediately after cache construction, before any staging reservation.
std::destroy_at(&cache.m_staging_buffer);
std::construct_at(&cache.m_staging_buffer, cache.m_graphics, cache.m_scheduler,
MemoryUsage::Upload, size);
}
static bool SynchronizeBufferFromImage(BufferCache &cache, Buffer &buffer,
uint64_t address, uint64_t size) {
return cache.SynchronizeBufferFromImage(buffer, address, size);
@@ -5109,6 +5116,67 @@ public:
std::printf("[host] %-32s ok\n", name);
}
void CheckBdaPageTableUploads() {
constexpr const char *name = "BdaPageTableUploads";
constexpr uintptr_t base = 0x0000000207b00000ull;
constexpr uint64_t page = BufferCache::CACHING_PAGESIZE;
constexpr std::array<uint64_t, 3> page_counts{2, 521, 525};
constexpr uint64_t result_entries = page_counts[0] + page_counts[1] + page_counts[2];
EnsureRuntimeContext();
RenderContext context(m_runtime_context);
auto &scheduler = context.GetCommandScheduler();
HW::Context registers{};
HW::UserConfig user_config{};
HW::Shader shaders{};
scheduler.Begin(registers, user_config, shaders);
auto &cache = context.GetBufferCache();
// A small real ring exercises chunking and waits for same-command-buffer reuse.
BufferCacheTestAccess::SetUnusedStagingBufferSize(cache, 4096);
auto &table = *cache.GetBdaPageTableBuffer();
const auto table_offset = BufferCache::PageIndex(base) * sizeof(vk::DeviceAddress);
auto readback = CreateHostBuffer(name, result_entries * sizeof(vk::DeviceAddress),
vk::BufferUsageFlagBits::eTransferDst, {});
std::vector<uint64_t> expected;
expected.reserve(result_entries);
for (const auto pages : page_counts) {
// The destination has a preceding GPU write and, after the first iteration,
// a preceding read. Registration must preserve both ordering requirements.
table.Fill(table_offset, pages * sizeof(vk::DeviceAddress), 0xa5a5a5a5u);
const auto previous_tick = scheduler.CurrentTick();
const auto id = cache.FindBuffer(base, pages * page);
const auto &buffer = cache.GetBuffer(id);
const auto destination_offset = expected.size() * sizeof(vk::DeviceAddress);
for (uint64_t index = 0; index < pages; ++index) {
expected.push_back(buffer.BufferDeviceAddress() + index * page);
}
const vk::BufferCopy copy{table_offset, destination_offset,
pages * sizeof(vk::DeviceAddress)};
scheduler.Current().Handle().copyBuffer(table.Handle(), readback.buffer, 1, &copy);
Require(name, "chunked registration",
buffer.CpuAddress() == base && buffer.Size() == pages * page &&
(pages == page_counts.front() || scheduler.CurrentTick() > previous_tick),
"BDA registration failed to replace its owner or wait for staging-ring reuse");
}
vk::BufferMemoryBarrier barrier{};
barrier.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
barrier.dstAccessMask = vk::AccessFlagBits::eHostRead;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.buffer = readback.buffer;
barrier.size = readback.size;
scheduler.Current().Handle().pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
vk::PipelineStageFlagBits::eHost, {}, 0, nullptr, 1, &barrier, 0, nullptr);
scheduler.Finish();
const auto words = ReadBuffer(name, readback, result_entries * 2);
for (size_t index = 0; index < expected.size(); ++index) {
const auto actual = uint64_t{words[index * 2]} | (uint64_t{words[index * 2 + 1]} << 32);
Require(name, "GPU page-table snapshot", actual == expected[index],
"staged BDA updates were stale, overwritten before their read, or incompletely flushed");
}
DestroyBuffer(&readback);
std::printf("[gpu] %-32s ok\n", name);
}
void CheckUnifiedTextureCacheFlow() {
constexpr const char *name = "UnifiedTextureCacheFlow";
constexpr uintptr_t base = 0x0000000200600000ull;
@@ -41922,6 +41990,11 @@ int main(int argc, char **argv) {
CheckDepthTargetFootprints();
return 0;
}
if (argc == 2 && std::strcmp(argv[1], "--bda-page-table-only") == 0) {
VulkanHarness vulkan;
vulkan.CheckBdaPageTableUploads();
return 0;
}
if (argc == 2 && std::strcmp(argv[1], "--buffer-cache-range-only") == 0) {
VulkanHarness vulkan;
vulkan.CheckUnifiedTextureCacheFlow();
@@ -42123,6 +42196,7 @@ int main(int argc, char **argv) {
vulkan.CheckGpuTilerCpuParity();
vulkan.CheckNativeIndirectDispatch();
CheckComputeLdsLimit(vulkan);
vulkan.CheckBdaPageTableUploads();
vulkan.CheckUnifiedTextureCacheFlow();
vulkan.CheckUnifiedImageViewCache();
vulkan.CheckPackedTextureComponents();