Share the Vulkan swapchain tracking between the hook and the layer
vk_hook.cpp and coop_vk_layer.cpp each carried a verbatim copy of the swap-chain registry -- the SwapInfo struct, the vector+mutex, find_swap, the create-time de-dup + LRU cap, and the present-time lookup -- because they are two independent early-presence paths (inline hook vs implicit layer). The tracking logic is identical, so lift it into one VkSwapchainRegistry (hook/src/vk_swapchain_registry.hpp); each module owns an instance. add() de-dups + LRU-caps, lookup() copies the frame out under the lock, clear() resets -- same behavior, one definition. Net -45 lines. mock_game_test exercises both paths (the inline-hook vk storm and the implicit-layer capture) and passes.
This commit is contained in:
@@ -15,12 +15,10 @@
|
||||
// The loader/layer interface structs (VkLayer*CreateInfo, VkNegotiateLayerInterface) live in
|
||||
// vk_layer.h, which Vulkan-Headers doesn't ship, so they're declared here to the stable
|
||||
// loader-interface-version-2 ABI.
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -36,6 +34,7 @@
|
||||
#include "coop/shared_memory.hpp"
|
||||
#include "ipc_client.hpp"
|
||||
#include "vk_capture.hpp"
|
||||
#include "vk_swapchain_registry.hpp"
|
||||
|
||||
// --- Loader/layer interface (interface version 2) ---------------------------
|
||||
extern "C" {
|
||||
@@ -99,6 +98,7 @@ constexpr VkStructureType kLoaderDeviceCreateInfo = static_cast<VkStructureType>
|
||||
|
||||
using coop::hook::IpcClient;
|
||||
using coop::hook::VkCapture;
|
||||
using coop::hook::VkSwapchainRegistry;
|
||||
|
||||
IpcClient g_ipc;
|
||||
std::atomic<bool> g_ipc_tried{false}; // reaper-thread side: connect once on the first published frame
|
||||
@@ -118,19 +118,7 @@ std::uint32_t g_qfam = 0;
|
||||
|
||||
VkCapture g_cap; // the shared, off-present-thread read-back
|
||||
|
||||
struct SwapInfo {
|
||||
VkSwapchainKHR sc;
|
||||
VkFormat fmt;
|
||||
std::uint32_t w, h;
|
||||
std::vector<VkImage> images;
|
||||
};
|
||||
std::vector<SwapInfo> g_swaps;
|
||||
// Serialize all g_swaps access: create (push) and present (find) can run on different game threads,
|
||||
// and DestroyDevice clears it. We don't track swapchain destruction (forwarding a destroy wrong could
|
||||
// break the game); create de-dups by handle and an LRU cap bounds growth so repeated resize/recreate
|
||||
// can't grow g_swaps unbounded or match a recycled handle's stale images.
|
||||
std::mutex g_swaps_mutex;
|
||||
constexpr std::size_t kMaxTrackedSwaps = 8;
|
||||
VkSwapchainRegistry g_swaps; // create-swapchain records images here; present maps them back for capture
|
||||
|
||||
bool eq(const char* a, const char* b)
|
||||
{
|
||||
@@ -199,18 +187,6 @@ bool decide_active()
|
||||
return _stricmp(self8, want) == 0;
|
||||
}
|
||||
|
||||
// Caller must hold g_swaps_mutex; copy out what you need before unlocking (a concurrent push_back
|
||||
// can reallocate and dangle the returned pointer).
|
||||
const SwapInfo* find_swap(VkSwapchainKHR sc)
|
||||
{
|
||||
for (const SwapInfo& s : g_swaps) {
|
||||
if (s.sc == sc) {
|
||||
return &s;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Per-second present-rate trace (enable with COOP_VK_LAYER_LOG): confirms the game keeps its frame
|
||||
// rate while capturing (the whole point of the off-present-thread read-back) and how many of those
|
||||
// presents the reaper actually mirrored.
|
||||
@@ -252,26 +228,10 @@ VKAPI_ATTR VkResult VKAPI_CALL layer_QueuePresentKHR(VkQueue queue, const VkPres
|
||||
g_ipc.note_present();
|
||||
}
|
||||
if (g_active && pi != nullptr && pi->swapchainCount == 1) {
|
||||
// Copy the matched swapchain out under the lock, then capture without holding it.
|
||||
VkImage image = VK_NULL_HANDLE;
|
||||
VkFormat fmt = VK_FORMAT_UNDEFINED;
|
||||
std::uint32_t w = 0, h = 0;
|
||||
bool matched = false;
|
||||
{
|
||||
std::scoped_lock lock(g_swaps_mutex);
|
||||
const SwapInfo* s = find_swap(pi->pSwapchains[0]);
|
||||
const std::uint32_t idx = pi->pImageIndices[0];
|
||||
if (s != nullptr && idx < s->images.size()) {
|
||||
image = s->images[idx];
|
||||
fmt = s->fmt;
|
||||
w = s->w;
|
||||
h = s->h;
|
||||
matched = true;
|
||||
}
|
||||
}
|
||||
if (matched) {
|
||||
VkSwapchainRegistry::Frame f;
|
||||
if (g_swaps.lookup(pi->pSwapchains[0], pi->pImageIndices[0], f)) {
|
||||
VkSemaphore chained = VK_NULL_HANDLE;
|
||||
if (g_cap.present(image, fmt, w, h, pi->pWaitSemaphores, pi->waitSemaphoreCount, chained)) {
|
||||
if (g_cap.present(f.image, f.fmt, f.w, f.h, pi->pWaitSemaphores, pi->waitSemaphoreCount, chained)) {
|
||||
VkPresentInfoKHR p = *pi;
|
||||
p.waitSemaphoreCount = 1;
|
||||
p.pWaitSemaphores = &chained;
|
||||
@@ -292,25 +252,11 @@ VKAPI_ATTR VkResult VKAPI_CALL layer_CreateSwapchainKHR(VkDevice device, const V
|
||||
static_cast<int>(ci->presentMode), ci->imageExtent.width, ci->imageExtent.height, ci->minImageCount);
|
||||
const VkResult r = g_real_create_swapchain(device, ci, a, out);
|
||||
if (g_active && r == VK_SUCCESS && out && g_get_swapchain_images) {
|
||||
SwapInfo info{};
|
||||
info.sc = *out;
|
||||
info.fmt = ci->imageFormat;
|
||||
info.w = ci->imageExtent.width;
|
||||
info.h = ci->imageExtent.height;
|
||||
std::uint32_t n = 0;
|
||||
g_get_swapchain_images(device, *out, &n, nullptr);
|
||||
info.images.resize(n);
|
||||
g_get_swapchain_images(device, *out, &n, info.images.data());
|
||||
{
|
||||
std::scoped_lock lock(g_swaps_mutex);
|
||||
g_swaps.erase(
|
||||
std::remove_if(g_swaps.begin(), g_swaps.end(), [&](const SwapInfo& e) { return e.sc == info.sc; }),
|
||||
g_swaps.end());
|
||||
g_swaps.push_back(std::move(info));
|
||||
if (g_swaps.size() > kMaxTrackedSwaps) {
|
||||
g_swaps.erase(g_swaps.begin());
|
||||
}
|
||||
}
|
||||
std::vector<VkImage> images(n);
|
||||
g_get_swapchain_images(device, *out, &n, images.data());
|
||||
g_swaps.add(*out, ci->imageFormat, ci->imageExtent.width, ci->imageExtent.height, std::move(images));
|
||||
}
|
||||
return r;
|
||||
}
|
||||
@@ -428,10 +374,7 @@ VKAPI_ATTR void VKAPI_CALL layer_DestroyDevice(VkDevice device, const VkAllocati
|
||||
auto destroy = reinterpret_cast<PFN_vkDestroyDevice>(g_next_gdpa(device, "vkDestroyDevice"));
|
||||
if (g_active && device == g_device) {
|
||||
g_cap.shutdown(); // joins the reaper, drains the device, frees the read-back resources
|
||||
{
|
||||
std::scoped_lock lock(g_swaps_mutex);
|
||||
g_swaps.clear();
|
||||
}
|
||||
g_swaps.clear(); // registry locks internally
|
||||
g_device = VK_NULL_HANDLE;
|
||||
}
|
||||
destroy(device, a);
|
||||
|
||||
Reference in New Issue
Block a user