[MG_Backend, MG_Test] (P7): prove aggregate waits with a single-fence control

This commit is contained in:
2026-09-23 03:53:37 -04:00
parent a4c1bc8b1e
commit b19297d5e5
5 changed files with 117 additions and 7 deletions
@@ -0,0 +1,41 @@
// MobileGL - MobileGL/MG_Backend/DirectVulkan/Renderer/SubmitFencePrefix.h
// Copyright (c) 2025-2026 MobileGL-Dev
// Licensed under the GNU Lesser General Public License v3.0:
// https://www.gnu.org/licenses/gpl-3.0.txt
// https://www.gnu.org/licenses/lgpl-3.0.txt
// SPDX-License-Identifier: LGPL-3.0-only
// End of Source File Header
#pragma once
#include <cstdint>
#include <vector>
namespace MobileGL::MG_Backend::DirectVulkan {
// Collect every fence in the ordered submission prefix. Waiting on only the last matching
// frame-serial record is insufficient: the same serial may have an earlier in-flight submit.
template <typename Records, typename Fence>
bool CollectSubmitFencePrefix(const Records& records, std::uint64_t submitIndex,
Fence nullFence, std::vector<Fence>& fences) {
fences.clear();
bool foundTarget = false;
for (const auto& record : records) {
if (record.submitIndex > submitIndex) {
break;
}
if (record.fence == nullFence) {
fences.clear();
return false;
}
fences.push_back(record.fence);
foundTarget = foundTarget || record.submitIndex == submitIndex;
}
if (!foundTarget) {
fences.clear();
return false;
}
return true;
}
} // namespace MobileGL::MG_Backend::DirectVulkan
@@ -7,6 +7,7 @@
// End of Source File Header
#include "VulkanRenderer.h"
#include "SubmitFencePrefix.h"
#include "MG_Backend/DirectVulkan/SubgroupSupportPolicy.h"
#include "MG_Backend/DirectGLES/Utils.h"
@@ -13679,12 +13680,8 @@ void main() {
if (submitIndex <= m_completedSubmitCounter) return true;
if (submitIndex > m_submitCounter || m_device == VK_NULL_HANDLE) return false;
Vector<VkFence> fences;
for (const auto& record : m_inFlightSubmits) {
if (record.submitIndex > submitIndex) break;
if (record.fence == VK_NULL_HANDLE) return false;
fences.push_back(record.fence);
}
if (fences.empty()) return false;
if (!CollectSubmitFencePrefix(m_inFlightSubmits, submitIndex, VkFence{}, fences))
return false;
const VkResult result = vkWaitForFences(m_device, static_cast<Uint32>(fences.size()),
fences.data(), VK_TRUE, timeoutNs);
if (result == VK_SUCCESS) {
+1 -1
View File
@@ -174,7 +174,7 @@ namespace MobileGL::MG_Impl::GLXImpl {
}
struct ContextObject {
Display* XDisplay = nullptr;
::Display* XDisplay = nullptr;
EGLDisplay Display = EGL_NO_DISPLAY;
EGLConfig Config = nullptr;
EGLContext Context = EGL_NO_CONTEXT;
+6
View File
@@ -24,6 +24,12 @@ if (MSVC)
target_compile_options(PipeCatalogueTest PRIVATE /Zc:preprocessor)
endif()
# P7 B4 aggregate-wait regression control shares the production selector; no device is needed.
add_executable(MagmaAggregateWaitTest MagmaAggregateWaitTest.cpp)
target_include_directories(MagmaAggregateWaitTest PRIVATE ${MGL_ROOT}/include ${MGL_ROOT}/MobileGL)
target_link_libraries(MagmaAggregateWaitTest PRIVATE GTest::gtest_main ${LINK_LIBRARIES})
gtest_discover_tests(MagmaAggregateWaitTest DISCOVERY_TIMEOUT 30 PROPERTIES LABELS unit)
# The P1 poison and verify shapes at the block level: a fake GLContext, the real filler and
# accessors out of the static library. Links gtest (not gtest_main): the suite needs its own
# main() to point MOBILEGL_LOG_FILE_PATH at a temp file before anything logs, because its
@@ -0,0 +1,66 @@
// MobileGL - MobileGL/MG_Test/Pipe/MagmaAggregateWaitTest.cpp
// Copyright (c) 2025-2026 MobileGL-Dev
// Licensed under the GNU Lesser General Public License v3.0:
// https://www.gnu.org/licenses/gpl-3.0.txt
// https://www.gnu.org/licenses/lgpl-3.0.txt
// SPDX-License-Identifier: LGPL-3.0-only
// End of Source File Header
// Deterministic, device-free control for P7 B4's aggregate submit wait. Production and this
// test share CollectSubmitFencePrefix: two submissions can carry one frame serial, so the
// targeted serial stays incomplete when the old single-fence control retires only one.
#include <gtest/gtest.h>
#include <MG_Backend/DirectVulkan/Renderer/SubmitFencePrefix.h>
#include <algorithm>
#include <cstdint>
#include <vector>
namespace {
using MobileGL::MG_Backend::DirectVulkan::CollectSubmitFencePrefix;
struct Submit {
std::uint64_t submitIndex;
std::uint64_t frameSerial;
int fence;
};
bool SerialComplete(std::uint64_t serial, const std::vector<Submit>& pending) {
return std::none_of(pending.begin(), pending.end(), [serial](const Submit& submit) {
return submit.frameSerial == serial;
});
}
TEST(MagmaAggregateWaitTest, MissingTargetAfterAnEarlierPrefixIsRejected) {
const std::vector<Submit> gap{{10, 42, 101}, {12, 43, 103}};
std::vector<int> fences;
EXPECT_FALSE(CollectSubmitFencePrefix(gap, 11, 0, fences));
EXPECT_TRUE(fences.empty());
}
TEST(MagmaAggregateWaitTest, SingleFenceControlLeavesTheTargetFrameInFlight) {
const std::vector<Submit> pending{{10, 42, 101}, {11, 42, 102}, {12, 43, 103}};
// The old control waits only the first matching fence. The second submit still carries 42.
auto singleFenceControl = pending;
singleFenceControl.erase(singleFenceControl.begin());
EXPECT_FALSE(SerialComplete(42, singleFenceControl));
// The production selector includes both submissions through index 11, but not frame 43.
std::vector<int> fences;
ASSERT_TRUE(CollectSubmitFencePrefix(pending, 11, 0, fences));
ASSERT_EQ(fences, (std::vector<int>{101, 102}));
auto aggregateWait = pending;
aggregateWait.erase(
std::remove_if(aggregateWait.begin(), aggregateWait.end(), [&fences](const Submit& submit) {
return std::find(fences.begin(), fences.end(), submit.fence) != fences.end();
}),
aggregateWait.end());
EXPECT_TRUE(SerialComplete(42, aggregateWait));
ASSERT_EQ(aggregateWait.size(), 1u);
EXPECT_EQ(aggregateWait.front().frameSerial, 43u);
}
} // namespace