diff --git a/test_pipelineTracker.cpp b/test_pipelineTracker.cpp new file mode 100644 index 0000000..e98c4b8 --- /dev/null +++ b/test_pipelineTracker.cpp @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2026 Mykhailo Mamedov. All rights reserved. + * + * RESEARCH PREVIEW / REFERENCE ONLY: + * This source code is provided solely for the purpose of reviewing + * the author's research methods and implementation. + * + * NO LICENSE GRANTED: + * This code is NOT for distribution, modification, or use in any + * project (commercial or otherwise). Unauthorized copying or + * use of this code is strictly prohibited. + * + * For inquiries regarding use or licensing, contact: ua.modin@gmail.com + * + * Minimal test. + * + */ + +#include "pch.h" +#include "common.h" +#include "pipelineTracker.hpp" + +using namespace M_CORE; + +// Fake hardware state registries +static std::unordered_map g_mockSems; +static uint64_t g_nextMockHandle = 0x1000; + +// --- VULKAN OVERRIDES VIA HOOKING +extern "C" { + VkResult vkGetSemaphoreCounterValue(VkDevice device, VkSemaphore semaphore, uint64_t* pValue) { + if (pValue && g_mockSems.count(semaphore)) { + *pValue = g_mockSems[semaphore]; + } + else if (pValue) { + *pValue = 0; // Default fallback + } + return VK_SUCCESS; + } + + VkResult vkSignalSemaphore(VkDevice device, const VkSemaphoreSignalInfo* pSignalInfo) { + if (pSignalInfo && pSignalInfo->semaphore) { + g_mockSems[pSignalInfo->semaphore] = pSignalInfo->value; + } + return VK_SUCCESS; + } + + VkResult vkCreateSemaphore(VkDevice device, const VkSemaphoreCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, VkSemaphore* pSemaphore) { + if (!pSemaphore) return VK_ERROR_INITIALIZATION_FAILED; + + // Generate a fake handle. + g_nextMockHandle += 8; + VkSemaphore fakeHandle = reinterpret_cast(g_nextMockHandle); + + // Parse the initial value. + uint64_t initialValue = 0; + if (pCreateInfo && pCreateInfo->pNext) { + auto* typeInfo = reinterpret_cast(pCreateInfo->pNext); + if (typeInfo->sType == VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO) { + initialValue = typeInfo->initialValue; + } + } + + // Register into map. + g_mockSems[fakeHandle] = initialValue; + + *pSemaphore = fakeHandle; + return VK_SUCCESS; + } + + void vkDestroySemaphore( + VkDevice device, + VkSemaphore semaphore, + const VkAllocationCallbacks* pAllocator) {} + + VkResult vkCreateQueryPool(VkDevice device, const VkQueryPoolCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, VkQueryPool* pQueryPool) { + *pQueryPool = (VkQueryPool)0x00123; + return VK_SUCCESS; + } +} + +class PipelineTrackerTestFixture : public ::testing::Test { +protected: + VulkanPipelineTracker* tracker = nullptr; + mCore m_vkCore; + mDevice devA; + mDevice devB; + + void SetUp() override { + g_mockSems.clear(); + devA.virtualDevice = (VkDevice)0x1; + devB.virtualDevice = (VkDevice)0x2; + std::vector stageMapping = { + {&devA, nullptr, "Stage0"}, + {&devA, &devB , "Stage1"}, + {&devB, &devA, "Stage2"}, + {&devB, nullptr, "Stage3"} + }; + tracker = new VulkanPipelineTracker(&m_vkCore, stageMapping); + } + + void TearDown() override { + delete tracker; + g_mockSems.clear(); + } +}; + +/** + * @brief Verifies stage completion status and sem evaluations. + * Validates that the tracking matrix accurately catches matching, + * mismatched, or uninitialized host counters against mock + * GPU exec timeline registers before flagging readiness. + */ +TEST_F(PipelineTrackerTestFixture, VerifyStageCompletion) { + u32 stageId = 0; + u32 consumerStageId = 1; + u64 latestStageSignaled = 0; + EXPECT_TRUE(tracker->IsStageComplete(stageId, latestStageSignaled)); + + StageContext* context0 = tracker->GetStageContext(stageId); + context0->m_gpuExecutionCounter = 1; + g_mockSems[context0->localTimelineSem.semaphore] = 1; + EXPECT_TRUE(tracker->IsStageComplete(stageId, latestStageSignaled)); + + context0->m_gpuExecutionCounter = 2; + EXPECT_FALSE(tracker->IsStageComplete(stageId, latestStageSignaled)); + + EXPECT_TRUE(tracker->IsIndexSafeToWrite(consumerStageId, 0)); + u64 targetLocalTimelineValue = 0; + sem* localSemaphore = nullptr; + tracker->AdvanceStageSync(stageId, targetLocalTimelineValue, localSemaphore, 0); + EXPECT_EQ(targetLocalTimelineValue, 3); + + EXPECT_EQ(tracker->GetStageDevice(stageId), devA.virtualDevice); + + u64 latestProducerFrame = 0; + EXPECT_EQ(tracker->getSafeIndex(stageId, latestProducerFrame), 1); + EXPECT_EQ(latestProducerFrame, 1); + +} + +/** + * @brief Verifies cross-device value replication. + * Validates that a multi-value producer jump forces the CPU + * mirroring loop to step the consumer timeline forward strictly + * by exactly +1 per call/frame without skipping. + */ +TEST_F(PipelineTrackerTestFixture, VerifySignalMirroring) { + u32 consumerStageId = 1; + StageContext* context1 = tracker->GetStageContext(consumerStageId); + g_mockSems[context1->localTimelineSem.semaphore] = 3; + tracker->MirrorSignals(); + tracker->MirrorSignals(); + tracker->MirrorSignals(); + EXPECT_EQ(g_mockSems[context1->localTimelineSem.semaphoreRemote], 3); +} + +int main(int argc, char** argv) { + testing::InitGoogleTest(&argc, argv); + assert(putenv(cfgTest::envSetting) == 0); + return RUN_ALL_TESTS(); +}