diff --git a/core/libs/prx/libSceAgcDriver/tests/execution/NativeDepthBdaClear.cpp b/core/libs/prx/libSceAgcDriver/tests/execution/NativeDepthBdaClear.cpp index 6df1dfa3611..1ed28fe4138 100644 --- a/core/libs/prx/libSceAgcDriver/tests/execution/NativeDepthBdaClear.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/execution/NativeDepthBdaClear.cpp @@ -16,6 +16,12 @@ #include #else #include +#ifdef __linux__ +#include +#include +#include +#include +#endif #endif #include #include @@ -44,6 +50,34 @@ namespace Memory = AgcDriver::GuestMemory; constexpr std::size_t BlockBytes = 1048576; +#ifdef __linux__ +struct SharedBacking { + int file; + std::uint64_t offset; + std::size_t resolverReturns = 0; +}; +std::map sharedBlocks; + +bool ResolveSharedBlock(std::uintptr_t address, std::size_t bytes, int* file, std::uint64_t* offset) { + if (bytes == 0) return false; + const auto next = sharedBlocks.upper_bound(address); + if (next == sharedBlocks.begin()) return false; + const auto entry = std::prev(next); + const auto lead = address - entry->first; + if (lead >= BlockBytes || bytes > BlockBytes - lead) return false; + *file = fcntl(entry->second.file, F_DUPFD_CLOEXEC, 0); + *offset = entry->second.offset + lead; + if (*file < 0) return false; + ++entry->second.resolverReturns; + return true; +} + +struct SharedBackingScope { + SharedBackingScope() { GuestArena::GuestArenaSetSharedBacking_nid_postfix(ResolveSharedBlock); } + ~SharedBackingScope() { GuestArena::GuestArenaSetSharedBacking_nid_postfix(nullptr); } +}; +#endif + class GuestBlock { public: GuestBlock() { @@ -54,14 +88,28 @@ class GuestBlock { section = CreateFileMappingW(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, BlockBytes, nullptr); Require(section != nullptr, "cannot create the native depth BDA clear backing"); GuestArena::GuestArenaMapDirect_nid_postfix(block, BlockBytes, section, 0, PAGE_READWRITE); +#else +#ifdef __linux__ + backing = memfd_create("native-depth-bda-clear", MFD_CLOEXEC | MFD_ALLOW_SEALING); + Require(backing >= 0, "cannot create the native depth BDA clear backing"); + if (ftruncate(backing, 2 * BlockBytes) != 0 || fcntl(backing, F_ADD_SEALS, F_SEAL_SHRINK) != 0) { + close(backing); + Require(false, "cannot size or seal the native depth BDA clear backing"); + } + void* raw = mmap(nullptr, 2 * BlockBytes, PROT_READ | PROT_WRITE, MAP_SHARED, backing, 0); + if (raw == MAP_FAILED) close(backing); #else void* raw = mmap(nullptr, 2 * BlockBytes, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); +#endif Require(raw != MAP_FAILED, "cannot map the native depth BDA clear block"); const auto begin = reinterpret_cast(raw); const auto aligned = (begin + BlockBytes - 1) & ~(static_cast(BlockBytes) - 1); if (aligned != begin) munmap(raw, aligned - begin); if (aligned != begin + BlockBytes) munmap(reinterpret_cast(aligned + BlockBytes), begin + BlockBytes - aligned); block = reinterpret_cast(aligned); +#ifdef __linux__ + sharedBlocks.emplace(aligned, SharedBacking{backing, aligned - begin}); +#endif GuestWriteWatch::GuestWriteWatchRegister_nid_postfix(block, BlockBytes); #endif Require(Memory::Watched(Address(), BlockBytes), "the native depth BDA clear block is not watched"); @@ -77,8 +125,12 @@ class GuestBlock { GuestArena::GuestArenaForgetSection_nid_postfix(section); CloseHandle(section); #else - munmap(block, BlockBytes); GuestWriteWatch::GuestWriteWatchUnregister_nid_postfix(block, BlockBytes); +#ifdef __linux__ + sharedBlocks.erase(Address()); + close(backing); +#endif + munmap(block, BlockBytes); #endif } @@ -87,11 +139,16 @@ class GuestBlock { std::uint64_t Address() const { return reinterpret_cast(block); } const std::uint8_t* Data() const { return block; } void FillWith(std::uint8_t value) const { std::memset(block, value, BlockBytes); } +#ifdef __linux__ + std::size_t SharedBackingResolverReturns() const { return sharedBlocks.at(Address()).resolverReturns; } +#endif private: std::uint8_t* block = nullptr; #ifdef _WIN32 HANDLE section = nullptr; +#elif defined(__linux__) + int backing = -1; #endif }; @@ -130,8 +187,14 @@ int RunNativeClear(AgcDriver::VulkanDevice& device, ClearConsumer consumer) { device.Dispatch(compile(), groups, 1u, 1u, {}, reinterpret_cast(code.data())); device.WaitIdle(); Require(std::all_of(metadata.Data(), metadata.Data() + BlockBytes, [](auto value) { return value == 0u; }), "the warm-up native clear did not retain zero metadata"); - if (!Memory::Watched(depth.Address(), BlockBytes) || !Memory::Watched(metadata.Address(), BlockBytes)) { - std::puts("skipped, the device does not retain guest write watching for native clear imports"); + const bool depthWatched = Memory::Watched(depth.Address(), BlockBytes); + const bool metadataWatched = Memory::Watched(metadata.Address(), BlockBytes); + if (!depthWatched || !metadataWatched) { + std::printf("skipped, the device does not retain guest write watching for native clear imports after warm-up (depth watched=%u, metadata watched=%u", static_cast(depthWatched), static_cast(metadataWatched)); +#ifdef __linux__ + std::printf(", Linux shared backing present, resolver fd returns: depth=%zu metadata=%zu", depth.SharedBackingResolverReturns(), metadata.SharedBackingResolverReturns()); +#endif + std::puts(")"); return VulkanTestSkipped; } const auto context = AgcDriver::VulkanDeviceTestAccess::Context(device); @@ -199,6 +262,9 @@ int RunNativeClear(AgcDriver::VulkanDevice& device, ClearConsumer consumer) { int main() { try { +#ifdef __linux__ + const SharedBackingScope sharedBacking; +#endif const auto device = OpenVulkanTestDevice(); if (!device) return VulkanTestSkipped; for (const auto consumer : {ClearConsumer::Owner, ClearConsumer::Texture, ClearConsumer::Attachment}) { diff --git a/core/libs/prx/libSceAgcDriver/tests/execution/NativeMultisampleDepth.cpp b/core/libs/prx/libSceAgcDriver/tests/execution/NativeMultisampleDepth.cpp index 641eaf93b0d..8cd5755437d 100644 --- a/core/libs/prx/libSceAgcDriver/tests/execution/NativeMultisampleDepth.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/execution/NativeMultisampleDepth.cpp @@ -37,6 +37,12 @@ #include #else #include +#ifdef __linux__ +#include +#include +#include +#include +#endif #endif namespace AgcDriver::DriverDetail { @@ -145,20 +151,60 @@ class Device { Context context{}; }; +#ifdef __linux__ +struct SharedBacking { + int file; + std::size_t bytes; + std::uint64_t offset; +}; +std::map sharedBlocks; + +bool ResolveSharedBlock(std::uintptr_t address, std::size_t bytes, int* file, std::uint64_t* offset) { + if (bytes == 0) return false; + const auto next = sharedBlocks.upper_bound(address); + if (next == sharedBlocks.begin()) return false; + const auto entry = std::prev(next); + const auto lead = address - entry->first; + if (lead >= entry->second.bytes || bytes > entry->second.bytes - lead) return false; + *file = fcntl(entry->second.file, F_DUPFD_CLOEXEC, 0); + *offset = entry->second.offset + lead; + return *file >= 0; +} + +struct SharedBackingScope { + SharedBackingScope() { GuestArena::GuestArenaSetSharedBacking_nid_postfix(ResolveSharedBlock); } + ~SharedBackingScope() { GuestArena::GuestArenaSetSharedBacking_nid_postfix(nullptr); } +}; +#endif + class TargetMemory { public: explicit TargetMemory(std::size_t bytes, bool registerAllocation = false) : bytes(bytes), registered(registerAllocation) { #ifdef _WIN32 pointer = GuestArena::GuestArenaAllocate_nid_postfix(bytes, 65536u); GuestArena::GuestArenaCommit_nid_postfix(pointer, bytes, PAGE_READWRITE, bytes); +#else +#ifdef __linux__ + backing = memfd_create("native-depth-htile-clear", MFD_CLOEXEC | MFD_ALLOW_SEALING); + Require(backing >= 0, "cannot create the native HTILE backing"); + if (ftruncate(backing, bytes + 65536u) != 0 || fcntl(backing, F_ADD_SEALS, F_SEAL_SHRINK) != 0) { + close(backing); + Require(false, "cannot size or seal the native HTILE backing"); + } + void* raw = mmap(nullptr, bytes + 65536u, PROT_READ | PROT_WRITE, MAP_SHARED, backing, 0); + if (raw == MAP_FAILED) close(backing); #else void* raw = mmap(nullptr, bytes + 65536u, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); +#endif Require(raw != MAP_FAILED, "cannot allocate multisample target fixture"); const auto begin = reinterpret_cast(raw); const auto aligned = (begin + 65535u) & ~std::uintptr_t{65535u}; if (aligned != begin) munmap(raw, aligned - begin); if (aligned + bytes != begin + bytes + 65536u) munmap(reinterpret_cast(aligned + bytes), begin + 65536u - aligned); pointer = reinterpret_cast(aligned); +#ifdef __linux__ + sharedBlocks.emplace(aligned, SharedBacking{backing, bytes, aligned - begin}); +#endif GuestWriteWatch::GuestWriteWatchRegister_nid_postfix(pointer, bytes); #endif Require(pointer != nullptr, "multisample target fixture allocation failed"); @@ -172,6 +218,10 @@ class TargetMemory { GuestArena::GuestArenaRelease_nid_postfix(pointer, bytes); #else GuestWriteWatch::GuestWriteWatchUnregister_nid_postfix(pointer, bytes); +#ifdef __linux__ + sharedBlocks.erase(reinterpret_cast(pointer)); + close(backing); +#endif munmap(pointer, bytes); #endif } @@ -181,6 +231,9 @@ class TargetMemory { void* pointer = nullptr; std::size_t bytes; bool registered = false; +#ifdef __linux__ + int backing = -1; +#endif }; alignas(256) constexpr std::array HtileMaskKernel{ @@ -863,6 +916,7 @@ void CheckDepthTarget(Context context, VkFormat format, std::uint32_t fragments, } else { const bool storedAll = AgcDriver::GuestMemory::StoredAllSince_nid_no_patch(target.htileAddress, metadataBytes, generation); Require(storedAll, "driver idle returned before recording the completed HTILE dispatch"); + std::puts("Native HTILE tracked dispatch completed"); } } else { const auto nativeBytes = fault == ClearFault::UnprovenDispatch ? 0u : static_cast(metadataBytes); @@ -1295,6 +1349,9 @@ void CheckRetirement(Context context, VkFormat format, std::uint32_t samples, st int main(int argc, char** argv) { try { +#ifdef __linux__ + const SharedBackingScope sharedBacking; +#endif std::unique_ptr device; try { device = std::make_unique(); } catch (const std::exception& error) {