Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@
#include <windows.h>
#else
#include <sys/mman.h>
#ifdef __linux__
#include <fcntl.h>
#include <unistd.h>
#include <map>
#include <iterator>
#endif
#endif
#include <algorithm>
#include <array>
Expand Down Expand Up @@ -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<std::uintptr_t, SharedBacking> 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() {
Expand All @@ -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<std::uintptr_t>(raw);
const auto aligned = (begin + BlockBytes - 1) & ~(static_cast<std::uintptr_t>(BlockBytes) - 1);
if (aligned != begin) munmap(raw, aligned - begin);
if (aligned != begin + BlockBytes) munmap(reinterpret_cast<void*>(aligned + BlockBytes), begin + BlockBytes - aligned);
block = reinterpret_cast<std::uint8_t*>(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");
Expand All @@ -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
}

Expand All @@ -87,11 +139,16 @@ class GuestBlock {
std::uint64_t Address() const { return reinterpret_cast<std::uintptr_t>(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
};

Expand Down Expand Up @@ -130,8 +187,14 @@ int RunNativeClear(AgcDriver::VulkanDevice& device, ClearConsumer consumer) {
device.Dispatch(compile(), groups, 1u, 1u, {}, reinterpret_cast<std::uintptr_t>(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<unsigned>(depthWatched), static_cast<unsigned>(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);
Expand Down Expand Up @@ -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}) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@
#include <windows.h>
#else
#include <sys/mman.h>
#ifdef __linux__
#include <fcntl.h>
#include <unistd.h>
#include <map>
#include <iterator>
#endif
#endif

namespace AgcDriver::DriverDetail {
Expand Down Expand Up @@ -145,20 +151,60 @@ class Device {
Context context{};
};

#ifdef __linux__
struct SharedBacking {
int file;
std::size_t bytes;
std::uint64_t offset;
};
std::map<std::uintptr_t, SharedBacking> 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<std::uintptr_t>(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<void*>(aligned + bytes), begin + 65536u - aligned);
pointer = reinterpret_cast<void*>(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");
Expand All @@ -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<std::uintptr_t>(pointer));
close(backing);
#endif
munmap(pointer, bytes);
#endif
}
Expand All @@ -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<std::uint32_t, 17> HtileMaskKernel{
Expand Down Expand Up @@ -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<std::size_t>(metadataBytes);
Expand Down Expand Up @@ -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> device;
try { device = std::make_unique<Device>(); }
catch (const std::exception& error) {
Expand Down
Loading