diff --git a/core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp b/core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp index 4153efa14..21eb476fa 100644 --- a/core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp +++ b/core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp @@ -2556,9 +2556,23 @@ bool VulkanDevice::PresentDisplayBuffer(const DisplayBuffer& buffer) { static const bool storeAtFlip = Graphics::StorageTexture::StoreAtFlipRequested(std::getenv("APS5_STORE_AT_FLIP")); if (storeAtFlip) Graphics::StorageTexture::FlushAllPending("flip"); else Graphics::StorageTexture::FlushDumpSelected("flip"); + auto& dumps = Dumps(); if (buffer.tilingMode == 1) { const auto pixels = ReadDisplayBuffer(buffer); - return present(buffer.width, buffer.height, true, pixels); + bool dumpFrame = false; + if (dumps.dumped < dumps.limit && ++dumps.presents % static_cast(dumps.every) == 0) { + if (dumps.cpu) { + WriteFrameBmp(dumps.dumped++, buffer.width, buffer.height, pixels, DumpScale()); + } else { + state->nextDumpIndex = dumps.dumped++; + dumpFrame = true; + } + } + if (!present(buffer.width, buffer.height, true, pixels, nullptr, nullptr, VK_FILTER_LINEAR, dumpFrame)) { + if (dumpFrame) --dumps.dumped; + return false; + } + return true; } static const bool profile = std::getenv("APS5_PROFILE_DRAW") != nullptr; // The display buffer is usually a resident render target whose results are still on the GPU: it @@ -2597,7 +2611,6 @@ bool VulkanDevice::PresentDisplayBuffer(const DisplayBuffer& buffer) { } } const auto cleared = buffer.dccAddress != 0 && resident == nullptr ? CompressedClearPixel(buffer, bytes) : std::nullopt; - auto& dumps = Dumps(); bool dumpFrame = false; if (dumps.dumped < dumps.limit && ++dumps.presents % static_cast(dumps.every) == 0) { if (dumps.cpu) { diff --git a/core/libs/prx/libSceVideoOut/tests/FrameDumpQueue.cpp b/core/libs/prx/libSceVideoOut/tests/FrameDumpQueue.cpp index 2c5b74ad1..a5105da79 100644 --- a/core/libs/prx/libSceVideoOut/tests/FrameDumpQueue.cpp +++ b/core/libs/prx/libSceVideoOut/tests/FrameDumpQueue.cpp @@ -96,31 +96,52 @@ void checkDump(std::span dump, std::span decoded) { void testDumpQueue() { const int handle = sceVideoOutOpen(255, 0, 0, nullptr); auto cfg = VideoOutDriver::Get().GetConfig(handle); - std::vector allocation(static_cast((Width + 127) / 128) * ((Height + 127) / 128) * 65536 + 65535); - const auto address = reinterpret_cast(allocation.data()); - const std::span storage{allocation.data() + ((65536u - (address & 65535u)) & 65535u), allocation.size() - 65535u}; - const AgcDriver::DisplayBuffer display{reinterpret_cast(storage.data()), 0x8000000000000000ull, Width, Height}; - check(AgcDriver::DisplayBufferSize(display) == storage.size(), "incorrect padded display footprint"); - for (std::uint32_t y = 0; y < Height; ++y) { - for (std::uint32_t x = 0; x < Width; ++x) { - const auto offset = AgcDriver::DisplayBufferOffset(display, x, y); - storage[offset] = static_cast(x & 255u); - storage[offset + 1] = static_cast(y & 255u); - storage[offset + 2] = static_cast((x >> 8u) | ((y >> 8u) << 4u)); - storage[offset + 3] = std::byte{255}; + constexpr auto tiledBytes = static_cast((Width + 127) / 128) * ((Height + 127) / 128) * 65536; + constexpr auto linearBytes = static_cast(Width) * Height * 4; + const auto makeStorage = [](std::size_t bytes, std::vector& allocation) { + allocation.resize(bytes + 65535); + const auto address = reinterpret_cast(allocation.data()); + auto* aligned = allocation.data() + ((65536u - (address & 65535u)) & 65535u); + return std::span{aligned, bytes}; + }; + std::vector tiledAllocation; + std::vector linearAllocation; + const auto tiledStorage = makeStorage(tiledBytes, tiledAllocation); + const auto linearStorage = makeStorage(linearBytes, linearAllocation); + const AgcDriver::DisplayBuffer tiled{reinterpret_cast(tiledStorage.data()), 0x8000000000000000ull, Width, Height}; + const AgcDriver::DisplayBuffer linear{reinterpret_cast(linearStorage.data()), 0x8000000000000000ull, Width, Height, 1}; + check(AgcDriver::DisplayBufferSize(tiled) == tiledStorage.size(), "incorrect padded tiled display footprint"); + check(AgcDriver::DisplayBufferSize(linear) == linearStorage.size(), "incorrect linear display footprint"); + const auto fill = [](const AgcDriver::DisplayBuffer& display, std::span storage) { + for (std::uint32_t y = 0; y < Height; ++y) { + for (std::uint32_t x = 0; x < Width; ++x) { + const auto offset = AgcDriver::DisplayBufferOffset(display, x, y); + storage[offset] = static_cast(x & 255u); + storage[offset + 1] = static_cast(y & 255u); + storage[offset + 2] = static_cast((x >> 8u) | ((y >> 8u) << 4u)); + storage[offset + 3] = std::byte{255}; + } } - } - const auto decoded = AgcDriver::ReadDisplayBuffer(display); - VideoOutBuffers buffer{storage.data(), nullptr, {nullptr, nullptr}}; - VideoOutBufferAttribute2 attribute{}; - sceVideoOutSetBufferAttribute2(&attribute, display.pixelFormat, 0, Width, Height, 0, 0, 0); - check(sceVideoOutRegisterBuffers2(handle, 0, 0, &buffer, 1, &attribute, 0, nullptr) == 0, "buffer registration failed"); + }; + fill(tiled, tiledStorage); + fill(linear, linearStorage); + const auto tiledPixels = AgcDriver::ReadDisplayBuffer(tiled); + const auto linearPixels = AgcDriver::ReadDisplayBuffer(linear); + VideoOutBuffers tiledBuffer{tiledStorage.data(), nullptr, {nullptr, nullptr}}; + VideoOutBuffers linearBuffer{linearStorage.data(), nullptr, {nullptr, nullptr}}; + VideoOutBufferAttribute2 tiledAttribute{}; + VideoOutBufferAttribute2 linearAttribute{}; + sceVideoOutSetBufferAttribute2(&tiledAttribute, tiled.pixelFormat, 0, Width, Height, 0, 0, 0); + sceVideoOutSetBufferAttribute2(&linearAttribute, linear.pixelFormat, 1, Width, Height, 0, 0, 0); + check(sceVideoOutRegisterBuffers2(handle, 0, 0, &tiledBuffer, 1, &tiledAttribute, 0, nullptr) == 0, "tiled buffer registration failed"); + check(sceVideoOutRegisterBuffers2(handle, 1, 1, &linearBuffer, 1, &linearAttribute, 0, nullptr) == 0, "linear buffer registration failed"); const HeldDump first(0); const HeldDump last(Frames - 1); std::exception_ptr failure; try { for (int frame = 0; frame < Frames; ++frame) { - check(sceVideoOutSubmitFlip(handle, 0, 1, frame) == 0, "the flip was not accepted"); + const int index = frame % 2; + check(sceVideoOutSubmitFlip(handle, index, 1, frame) == 0, "the flip was not accepted"); std::unique_lock lock(cfg->mutex); check(cfg->vblankCond.wait_for(lock, std::chrono::seconds(15), [&] { return (cfg->failure && cfg->flipStatus.flipPendingNum == 0) || cfg->flipStatus.count == static_cast(frame) + 1; }), "the flip did not complete"); if (cfg->failure) std::rethrow_exception(cfg->failure); @@ -130,11 +151,13 @@ void testDumpQueue() { } const auto firstDump = first.Release(); if (failure) std::rethrow_exception(failure); - checkDump(firstDump, decoded); + checkDump(firstDump, tiledPixels); const auto lastDump = last.Release(); - checkDump(lastDump, decoded); - for (int frame = 1; frame < Frames - 1; ++frame) checkDump(readDump(frame), decoded); + checkDump(lastDump, linearPixels); + checkDump(readDump(1), linearPixels); + checkDump(readDump(2), tiledPixels); sceVideoOutUnregisterBuffers(handle, 0); + sceVideoOutUnregisterBuffers(handle, 1); sceVideoOutClose(handle); LibcRunShutdown_nid_postfix(); }