Merge pull request #4291 from diversao/fix/agc-release-parked-imports

fix(agc): release parked host imports a pinned guest range waits for
This commit is contained in:
Dean 2026-10-12 23:52:40 +13:00 committed by GitHub
commit 090e26aac1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 95 additions and 1 deletions

View File

@ -811,6 +811,8 @@ if(BUILD_TESTING)
endif()
add_test(NAME agc_host_import_unmap COMMAND agc_driver_recorder_tests --host-import-unmap-only)
set_tests_properties(agc_host_import_unmap PROPERTIES SKIP_RETURN_CODE 77)
add_test(NAME agc_parked_import_unmap COMMAND agc_driver_recorder_tests --parked-import-unmap-only)
set_tests_properties(agc_parked_import_unmap PROPERTIES SKIP_RETURN_CODE 77 TIMEOUT 30 ENVIRONMENT "APS5_PIN_WAIT_MS=3000")
add_test(NAME agc_unit_shadow COMMAND agc_driver_recorder_tests --unit-shadow-only)
set_tests_properties(agc_unit_shadow PROPERTIES SKIP_RETURN_CODE 77 TIMEOUT 30 ENVIRONMENT "APS5_PIN_WAIT_MS=3000")
add_test(NAME agc_descriptor_pool_recycling COMMAND agc_driver_recorder_tests --descriptor-pool-only)
@ -822,7 +824,7 @@ if(BUILD_TESTING)
add_test(NAME agc_draw_inputs_in_place COMMAND agc_driver_recorder_tests --draw-inputs-in-place-only)
set_tests_properties(agc_draw_inputs_in_place PROPERTIES SKIP_RETURN_CODE 77)
if(WIN32)
set_tests_properties(agc_host_import_unmap agc_unit_shadow agc_descriptor_pool_recycling agc_draw_snapshot_cap agc_multisample_guest_access agc_draw_inputs_in_place PROPERTIES ENVIRONMENT_MODIFICATION
set_tests_properties(agc_host_import_unmap agc_parked_import_unmap agc_unit_shadow agc_descriptor_pool_recycling agc_draw_snapshot_cap agc_multisample_guest_access agc_draw_inputs_in_place PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
endif()

View File

@ -219,6 +219,26 @@ void retireImport(VkDevice device, HostImports& state, std::map<std::uint64_t, H
state.imports.erase(it);
}
bool releaseParkedImports(HostImports& state, std::uint64_t begin, std::uint64_t end) {
std::vector<std::shared_ptr<RetiredImport>> released;
{
std::lock_guard lock(state.mutex);
for (auto device = state.parked.begin(); device != state.parked.end();) {
auto& list = device->second;
for (auto it = list.begin(); it != list.end();) {
if ((*it)->entry.base < end && begin < (*it)->entry.base + (*it)->entry.bytes) {
released.push_back(std::move(*it));
it = list.erase(it);
} else {
++it;
}
}
device = list.empty() ? state.parked.erase(device) : std::next(device);
}
}
return !released.empty();
}
const char* bindImport(const Context& context, HostImport& entry, VkExternalMemoryHandleTypeFlagBits handleType, const void* import, std::uint32_t importTypes, bool& allocated, VkResult& failure) {
const auto failed = [&](const char* step, VkResult result) -> const char* {
if (entry.buffer) context.Function<PFN_vkDestroyBuffer>("vkDestroyBuffer")(context.device, entry.buffer, nullptr);
@ -1534,6 +1554,7 @@ bool WaitForLeasesAndImports(std::uintptr_t address, std::size_t bytes) noexcept
if (recorder != nullptr) recorder->FlushDeferredReleases();
const auto end = static_cast<std::uint64_t>(address) + bytes;
auto& imports = Imports();
if (releaseParkedImports(imports, address, end)) retiredImport = true;
const auto lease = GuestAllocations::GuestAllocationsAcquire_nid_postfix();
{
std::lock_guard importsLock(imports.mutex);
@ -1585,6 +1606,7 @@ bool WaitForLeasesAndImports(std::uintptr_t address, std::size_t bytes) noexcept
const auto end = static_cast<std::uint64_t>(address) + bytes;
auto& imports = Imports();
if (releaseParkedImports(imports, address, end)) retiredImport = true;
bool overlapsImport = false;
{
std::lock_guard importsLock(imports.mutex);

View File

@ -2567,6 +2567,71 @@ void hostImportUnmapTests(const Device& device, Recorder& recorder) {
std::cout << "host import unmap lifetime: ok\n";
}
void parkedImportUnmapTests(const Device& device) {
const auto& context = device.GetContext();
if (context.hostImportAlignment == 0) {
std::cout << "host imports unavailable: parked import unmap not tested\n";
return;
}
const auto alignment = static_cast<std::size_t>(std::max<VkDeviceSize>(65536u, context.hostImportAlignment));
const auto bytes = alignment * 4u;
#ifdef _WIN32
void* block = VirtualAlloc(nullptr, bytes, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
#else
void* block = std::aligned_alloc(alignment, bytes);
#endif
Require(block != nullptr, "cannot allocate the parked import unmap block");
const auto release = [&] {
#ifdef _WIN32
VirtualFree(block, 0, MEM_RELEASE);
#else
std::free(block);
#endif
};
const auto address = reinterpret_cast<std::uint64_t>(block);
{
GuestAllocations::Mutation mutation;
mutation.Add(block, bytes, true, true, true);
}
std::unique_ptr<Device> replacement;
bool imported = false;
{
std::lock_guard gpu(GpuMutex());
imported = HostImportFor(context, address, bytes) != nullptr;
}
if (imported) {
replacement = std::make_unique<Device>();
std::lock_guard gpu(GpuMutex());
imported = HostImportFor(replacement->GetContext(), address, bytes) != nullptr;
}
if (!imported) {
GuestAllocations::Mutation mutation;
mutation.Remove(block);
if (replacement) ClearHostImports(replacement->GetContext().device);
release();
std::cout << "host import refused: parked import unmap not tested\n";
return;
}
bool unmapped = false;
try {
GuestAllocations::Mutation mutation;
mutation.Unmap(block, bytes, [&](const void*, std::size_t, const void*, bool) { unmapped = true; });
} catch (const std::exception& error) {
ClearHostImports(replacement->GetContext().device);
ClearHostImports(context.device);
GuestAllocations::Mutation mutation;
mutation.Remove(block);
release();
throw std::runtime_error(std::string("guest unmap after a device replacement: ") + error.what());
}
ClearHostImports(replacement->GetContext().device);
ClearImageMirrors(replacement->GetContext().device);
replacement.reset();
release();
Require(unmapped, "guest unmap of a range a replaced device imported did not run");
std::cout << "parked import unmap: ok\n";
}
void importWatchTests(const Device& device) {
using namespace AgcDriver::GuestMemory;
const auto& context = device.GetContext();
@ -5431,6 +5496,11 @@ int main(int argc, char** argv) {
hostImportUnmapTests(device, recorder);
return 0;
}
if (argc == 2 && std::string_view(argv[1]) == "--parked-import-unmap-only") {
gpu.unlock();
parkedImportUnmapTests(device);
return 0;
}
if (argc == 2 && std::string_view(argv[1]) == "--unit-shadow-only") {
if (!AgcDriver::Graphics::UnitShadowEnabled() || device.GetContext().hostImportAlignment == 0) {
std::cout << "skipped, unit shadows or host imports are unavailable\n";