mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 01:53:24 +09:00
[Refactor] (P13 W6a): the record arm is the only data arm - MOBILEGL_PIPE_DATA_ARM and its frontend-arm pins go, and the cube-face readback rules move to an integration scenario.
This commit is contained in:
+7
-17
@@ -757,23 +757,13 @@ namespace MobileGL::MG_Config {
|
||||
inline constexpr TransportMode Transport = TransportMode::Monolith;
|
||||
#endif
|
||||
|
||||
// P13 W4: THE DATA-ARM QUESTION - do the backends read RECORDS (the applier's twins, the staged
|
||||
// stores, the verb's own handles) rather than frontend objects? Under a wire the answer has
|
||||
// always been yes; P13 makes it yes for monolith too ("monolith is the push variant"), one
|
||||
// record family at a time. Each family's arm sites ask THIS rather than `Transport`, which
|
||||
// keeps answering the questions that really are about the transport (lifecycle, roles, whose
|
||||
// caps, whether a store copies).
|
||||
//
|
||||
// MOBILEGL_PIPE_DATA_ARM=record (default) | frontend picks monolith's arm for the families
|
||||
// already switched, on a backend whose monolith takes the record arm at all (Espryt from W4a,
|
||||
// Magma at its single flip). It exists for W4-W6 only - bisecting a monolith red to the switch, and
|
||||
// the per-thread CPU record (ID-P13-2) - and goes in W6 with the frontend arms. Latched once
|
||||
// by MG_ConfigLoader::Init(): the two arms keep their twins in different places.
|
||||
extern Bool MonolithTakesRecordArm;
|
||||
inline Bool DataArmIsRecord() { return Transport != TransportMode::Monolith || MonolithTakesRecordArm; }
|
||||
// P13: THE DATA-ARM QUESTION, ANSWERED ONCE FOR EVERY BUILD. The backends read RECORDS (the
|
||||
// applier's twins, the staged stores, the verb's own handles), never frontend objects - over a
|
||||
// wire and on monolith alike ("monolith is the push variant"). W4 switched monolith family by
|
||||
// family behind MOBILEGL_PIPE_DATA_ARM; W5 built the record arm into every library; W6 deleted
|
||||
// the frontend arm and the knob. The predicate stays as a name for the sites that read it.
|
||||
inline constexpr Bool DataArmIsRecord() { return true; }
|
||||
// ...and the record arm runs in this process over stores that ALIAS the frontend's shadows
|
||||
// (monolith) instead of copying them (every wire).
|
||||
inline Bool RecordArmAliasesFrontend() {
|
||||
return Transport == TransportMode::Monolith && MonolithTakesRecordArm;
|
||||
}
|
||||
inline Bool RecordArmAliasesFrontend() { return Transport == TransportMode::Monolith; }
|
||||
} // namespace MobileGL::MG_Config
|
||||
|
||||
@@ -39,7 +39,6 @@ namespace MobileGL::MG_Config {
|
||||
Bool SplitTransportRequestedByConfig = false;
|
||||
IpcTable Ipc;
|
||||
#endif
|
||||
Bool MonolithTakesRecordArm = false;
|
||||
RecordArmTable RecordArm;
|
||||
} // namespace MobileGL::MG_Config
|
||||
|
||||
@@ -676,41 +675,6 @@ namespace MobileGL::MG_ConfigLoader {
|
||||
}
|
||||
#endif
|
||||
|
||||
// P13 W4 (Config.h DataArmIsRecord). Read after InitTransport: `frontend` names monolith's
|
||||
// arm only - a wire has no frontend objects on its server half to read, so there it is
|
||||
// ignored by name rather than half-honoured.
|
||||
inline void InitDataArm() {
|
||||
String value;
|
||||
QueryEnvVariable("MOBILEGL_PIPE_DATA_ARM", value, "record");
|
||||
String lowered = value;
|
||||
std::transform(lowered.begin(), lowered.end(), lowered.begin(),
|
||||
[](unsigned char c) { return static_cast<char>(std::tolower(c)); });
|
||||
// P13 W4: both backends' monolith runs the record arm - Espryt's from W4a, family by family;
|
||||
// Magma's at its single flip (its verb entry points pick one arm for every family at once).
|
||||
const Bool backendReady = MG_Config::ActiveBackendType == BackendType::DirectGLES ||
|
||||
MG_Config::ActiveBackendType == BackendType::DirectVulkan;
|
||||
MG_Config::MonolithTakesRecordArm = backendReady;
|
||||
if (lowered.empty() || lowered == "record") {
|
||||
// The one line a device log can be asked for to show which arm a monolith runs.
|
||||
MGLOG_I("Config: monolith data arm = %s", backendReady ? "record" : "frontend (backend has no record arm)");
|
||||
return;
|
||||
}
|
||||
if (lowered == "frontend") {
|
||||
if (MG_Config::Transport != MG_Config::TransportMode::Monolith) {
|
||||
MGLOG_W("Config: MOBILEGL_PIPE_DATA_ARM=frontend is ignored under a transport - the "
|
||||
"record arm is the only arm a wire has");
|
||||
return;
|
||||
}
|
||||
MG_Config::MonolithTakesRecordArm = false;
|
||||
MGLOG_I("Config: MOBILEGL_PIPE_DATA_ARM=frontend - monolith reads frontend objects in "
|
||||
"the record families switched so far");
|
||||
return;
|
||||
}
|
||||
MGLOG_W("Config: Ignoring invalid env variable MOBILEGL_PIPE_DATA_ARM='%s'; expected "
|
||||
"record|frontend, using record",
|
||||
value.c_str());
|
||||
}
|
||||
|
||||
// P13 W5: the record arm's knobs (Config.h RecordArmTable), in every build that has the arm.
|
||||
inline void InitRecordArm() {
|
||||
MG_Config::RecordArmTable& ipc = MG_Config::RecordArm;
|
||||
@@ -750,9 +714,6 @@ namespace MobileGL::MG_ConfigLoader {
|
||||
// After InitFeatures, so the one line InitIpc logs is the last word on this run's
|
||||
// configuration, and before the accepted-env map is destroyed just below.
|
||||
InitTransport();
|
||||
#endif
|
||||
InitDataArm();
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
InitIpc();
|
||||
#endif
|
||||
|
||||
|
||||
@@ -72,6 +72,7 @@ add_executable(MobileGLIntegrationTest
|
||||
Scenarios/DrawParametersScenario.cpp
|
||||
Scenarios/AsyncCompileScenario.cpp
|
||||
Scenarios/ArbShaderObjectsScenario.cpp
|
||||
Scenarios/CubeFaceReadbackScenario.cpp
|
||||
Scenarios/XfbAfterClipDistanceScenario.cpp
|
||||
Scenarios/UnwrittenPositionOutputScenario.cpp
|
||||
Scenarios/SampleMaskScopeScenario.cpp
|
||||
@@ -1240,9 +1241,8 @@ gtest_discover_tests(MobileGLIntegrationTest
|
||||
# P13 W3b (ID-P13-3): the pre-handle arm is no longer compiled, so the Handles arm needs neither
|
||||
# MOBILEGL_PIPE_LEGACY_MEMOS=0 nor a mask that switches anything off: it runs the default mask.
|
||||
# The Legacy and AbaControl lanes (MOBILEGL_PIPE_PUSH=0, the pre-handle arm) went with that arm;
|
||||
# AbaControlHandles - the ABA control against the handle arm that ships - stays.
|
||||
# AbaControlHandles went with the frontend arm in P13 W6.
|
||||
set(MGL_ITEST_HANDLES_ARM_KNOBS "")
|
||||
set(MGL_ITEST_ABA_ARM_KNOBS "MOBILEGL_PIPE_HANDLE_ABA_CONTROL=1")
|
||||
|
||||
mgl_itest_join_environment(MGL_ITEST_GLES_HANDLE_HANDLES_ENVIRONMENT
|
||||
"MOBILEGL_TRANSPORT=monolith"
|
||||
@@ -1252,16 +1252,9 @@ mgl_itest_join_environment(MGL_ITEST_VULKAN_HANDLE_HANDLES_ENVIRONMENT
|
||||
"MOBILEGL_TRANSPORT=monolith"
|
||||
"MOBILEGL_BACKEND_TYPE=DirectVulkan" "MGITEST_HANDLE_ARM=handles" ${MGL_ITEST_HANDLES_ARM_KNOBS}
|
||||
${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_VULKAN_ENV})
|
||||
# P13 W4: THE ABA CONTROL'S SUBJECT IS THE FRONTEND ARM. The knob defeats the identity half of
|
||||
# Magma's frontend-arm keys (MagmaPipeIdentityTables); monolith's record arm keys every object on
|
||||
# the applier's {slot, gen}, which the knob does not - and must not - reach. So the lane pins the
|
||||
# frontend arm (MOBILEGL_PIPE_DATA_ARM, a W4-W6 knob) and goes with that arm in W6.
|
||||
mgl_itest_join_environment(MGL_ITEST_VULKAN_HANDLE_ABA_HANDLES_ENVIRONMENT
|
||||
"MOBILEGL_TRANSPORT=monolith" "MOBILEGL_PIPE_DATA_ARM=frontend"
|
||||
"MOBILEGL_BACKEND_TYPE=DirectVulkan" "MGITEST_HANDLE_ARM=aba"
|
||||
${MGL_ITEST_HANDLES_ARM_KNOBS} ${MGL_ITEST_ABA_ARM_KNOBS}
|
||||
${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_VULKAN_ENV})
|
||||
|
||||
# P13 W6: the AbaControlHandles lane pinned the frontend arm (its knob defeats the identity half
|
||||
# of Magma's frontend-arm keys); that arm is deleted, and the record arm keys every object on the
|
||||
# applier's {slot, gen}, which the knob does not reach. Removed (docs/ci/ci-cleanup-log.md).
|
||||
gtest_discover_tests(MobileGLIntegrationTest
|
||||
TEST_PREFIX "DirectGLES.HandleRecycle.Handles."
|
||||
TEST_FILTER "HandleRecycleScenario.*"
|
||||
@@ -1280,16 +1273,6 @@ gtest_discover_tests(MobileGLIntegrationTest
|
||||
TIMEOUT ${MGL_ITEST_TIMEOUT}
|
||||
ENVIRONMENT "${MGL_ITEST_VULKAN_HANDLE_HANDLES_ENVIRONMENT}"
|
||||
)
|
||||
gtest_discover_tests(MobileGLIntegrationTest
|
||||
TEST_PREFIX "DirectVulkan.HandleRecycle.AbaControlHandles."
|
||||
TEST_FILTER "HandleRecycleScenario.*"
|
||||
DISCOVERY_TIMEOUT 30
|
||||
PROPERTIES
|
||||
LABELS "integration-gpu\;integration-monolith-control"
|
||||
TIMEOUT ${MGL_ITEST_TIMEOUT}
|
||||
ENVIRONMENT "${MGL_ITEST_VULKAN_HANDLE_ABA_HANDLES_ENVIRONMENT}"
|
||||
)
|
||||
|
||||
# --- G12: the CSO content-addressing negative control ---------------------------------
|
||||
#
|
||||
# PUSH BUILDS ONLY, and that is the honest scope rather than a convenience: the two counters the
|
||||
@@ -2471,6 +2454,8 @@ if (MOBILEGL_BUILD_DISAGGREGATED)
|
||||
# GL_ARB_shader_objects (P13): ARB-created shader / program objects travel the record arm like
|
||||
# core-created ones, so the scenario runs on every split arm as well as both monoliths.
|
||||
mgl_itest_register_split_arms("ArbShaderObjectsScenario.*" "")
|
||||
# P13 W6: the cube-face readback rules the deleted frontend-arm unit cases pinned.
|
||||
mgl_itest_register_split_arms("CubeFaceReadbackScenario.*" "")
|
||||
mgl_itest_register_split_arms("TriangleScenario.*" "")
|
||||
mgl_itest_register_split_arms("PersistentCoherentMapScenario.*" "")
|
||||
# P11 A2 (MG_Remote/CONTRACT-P11.md §2): the >= 16 MiB arena on all three arms. Its tier case
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
// MobileGL - MobileGL/MG_IntegrationTest/Scenarios/CubeFaceReadbackScenario.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
|
||||
//
|
||||
// P13 W6: ONE CUBE FACE OUT, BY TOKEN AND BY Z OFFSET. Two GL 4.6 readback rules that used to be
|
||||
// pinned by unit cases on the monolith frontend arm (TextureTest / TextureViewTest), which read the
|
||||
// frontend's level shadow. That arm is gone; every library now answers a readback from the
|
||||
// backend's own image (the record arm), and a unit process has no backend image to read. So the
|
||||
// rules are asserted here, on a real context, on every arm the scenarios run on:
|
||||
//
|
||||
// * glGetTexImage of ONE cube face packs one face (GL 4.6 8.11.4), so a GL_PIXEL_PACK_BUFFER
|
||||
// sized for one face is exactly big enough - the validator once measured it against all six
|
||||
// faces and refused with INVALID_OPERATION - and the face it holds is the one the token named.
|
||||
// * glGetTextureSubImage of a cube-map VIEW over a 2D array names the face by its z offset
|
||||
// (GL 4.6 8.11.4, Table 8.25), and face f of a view based at layer L is array layer L + f.
|
||||
//
|
||||
// Every face and every layer carries its own value, so a read that lands on the wrong one says
|
||||
// which one answered instead of merely failing.
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#include "../Harness/HeadlessGL.h"
|
||||
#include "../Harness/ScenarioFixture.h"
|
||||
|
||||
#ifdef GLAPI
|
||||
#undef GLAPI
|
||||
#endif
|
||||
#define GL_GLEXT_PROTOTYPES
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glcorearb.h>
|
||||
#undef GL_GLEXT_PROTOTYPES
|
||||
|
||||
namespace MGITest {
|
||||
namespace {
|
||||
|
||||
class CubeFaceReadbackScenario : public ScenarioTest {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
ScenarioTest::SetUp();
|
||||
if (!Ready()) return;
|
||||
DrainErrors();
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, 4);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
if (!Ready()) return;
|
||||
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
|
||||
for (GLuint buffer : m_buffers) glDeleteBuffers(1, &buffer);
|
||||
for (GLuint texture : m_textures) glDeleteTextures(1, &texture);
|
||||
m_buffers.clear();
|
||||
m_textures.clear();
|
||||
DrainErrors();
|
||||
ScenarioTest::TearDown();
|
||||
}
|
||||
|
||||
GLuint NewTexture() {
|
||||
GLuint texture = 0;
|
||||
glGenTextures(1, &texture);
|
||||
m_textures.push_back(texture);
|
||||
return texture;
|
||||
}
|
||||
|
||||
GLuint NewBuffer() {
|
||||
GLuint buffer = 0;
|
||||
glGenBuffers(1, &buffer);
|
||||
m_buffers.push_back(buffer);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static void DrainErrors() {
|
||||
for (int i = 0; i < 16 && glGetError() != GL_NO_ERROR; ++i) {
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<GLuint> m_buffers;
|
||||
std::vector<GLuint> m_textures;
|
||||
};
|
||||
|
||||
TEST_F(CubeFaceReadbackScenario, GetTexImageOfOneCubeFacePacksIntoAOneFacePixelPackBuffer) {
|
||||
if (!Ready()) return;
|
||||
constexpr GLsizei kEdge = 2;
|
||||
constexpr GLsizeiptr kFaceBytes = kEdge * kEdge * 4;
|
||||
|
||||
const GLuint texture = NewTexture();
|
||||
glBindTexture(GL_TEXTURE_CUBE_MAP, texture);
|
||||
glTexStorage2D(GL_TEXTURE_CUBE_MAP, 1, GL_RGBA8, kEdge, kEdge);
|
||||
for (int face = 0; face < 6; ++face) {
|
||||
std::vector<GLubyte> seed(static_cast<size_t>(kFaceBytes), static_cast<GLubyte>(10 + face));
|
||||
glTexSubImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, 0, 0, 0, kEdge, kEdge, GL_RGBA,
|
||||
GL_UNSIGNED_BYTE, seed.data());
|
||||
}
|
||||
ASSERT_EQ(glGetError(), static_cast<GLenum>(GL_NO_ERROR)) << "seeding the six faces failed";
|
||||
|
||||
const GLuint buffer = NewBuffer();
|
||||
glBindBuffer(GL_PIXEL_PACK_BUFFER, buffer);
|
||||
glBufferData(GL_PIXEL_PACK_BUFFER, kFaceBytes, nullptr, GL_STREAM_READ);
|
||||
ASSERT_EQ(glGetError(), static_cast<GLenum>(GL_NO_ERROR)) << "creating the one-face pack buffer failed";
|
||||
|
||||
glGetTexImage(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
||||
EXPECT_EQ(glGetError(), static_cast<GLenum>(GL_NO_ERROR))
|
||||
<< "a pixel pack buffer sized for the one face this call packs was refused";
|
||||
|
||||
std::vector<GLubyte> packed(static_cast<size_t>(kFaceBytes), 0);
|
||||
glGetBufferSubData(GL_PIXEL_PACK_BUFFER, 0, kFaceBytes, packed.data());
|
||||
for (size_t i = 0; i < packed.size(); ++i) {
|
||||
ASSERT_EQ(static_cast<int>(packed[i]), 15)
|
||||
<< "byte " << i << ": the pack buffer holds face " << (static_cast<int>(packed[i]) - 10)
|
||||
<< ", not -Z (face 5)";
|
||||
}
|
||||
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
|
||||
EXPECT_EQ(glGetError(), static_cast<GLenum>(GL_NO_ERROR));
|
||||
}
|
||||
|
||||
TEST_F(CubeFaceReadbackScenario, CubeMapViewOfAnArrayReadsTheFaceEachZOffsetNames) {
|
||||
if (!Ready()) return;
|
||||
constexpr GLint kLayers = 8;
|
||||
constexpr GLint kViewMinLayer = 2;
|
||||
|
||||
const GLuint storage = NewTexture();
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, storage);
|
||||
glTexStorage3D(GL_TEXTURE_2D_ARRAY, 1, GL_RGBA8, 1, 1, kLayers);
|
||||
for (GLint layer = 0; layer < kLayers; ++layer) {
|
||||
const GLubyte texel[] = {static_cast<GLubyte>(10 + layer), 20, 30, 40};
|
||||
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, layer, 1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, texel);
|
||||
}
|
||||
ASSERT_EQ(glGetError(), static_cast<GLenum>(GL_NO_ERROR)) << "seeding the array failed";
|
||||
|
||||
const GLuint view = NewTexture();
|
||||
glTextureView(view, GL_TEXTURE_CUBE_MAP, storage, GL_RGBA8, 0, 1, kViewMinLayer, 6);
|
||||
ASSERT_EQ(glGetError(), static_cast<GLenum>(GL_NO_ERROR)) << "the cube-map view over the array was refused";
|
||||
|
||||
for (GLint face = 0; face < 6; ++face) {
|
||||
GLubyte output[4] = {};
|
||||
glGetTextureSubImage(view, 0, 0, 0, face, 1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, sizeof(output), output);
|
||||
EXPECT_EQ(glGetError(), static_cast<GLenum>(GL_NO_ERROR)) << "reading face " << face << " errored";
|
||||
EXPECT_EQ(static_cast<GLint>(output[0]), 10 + kViewMinLayer + face)
|
||||
<< "face " << face << " of a view based at layer " << kViewMinLayer << " answered with layer "
|
||||
<< (static_cast<GLint>(output[0]) - 10);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace MGITest
|
||||
@@ -20,11 +20,9 @@
|
||||
#include <MG_Impl/GLImpl/Buffer/GL_Buffer.h>
|
||||
#include <MG_Impl/GetProcAddress.h>
|
||||
#include <MG_Impl/GLImpl/Getter/GL_Getter.h>
|
||||
#include <MG_Test/ScopedMonolithFrontendArm.h>
|
||||
|
||||
using namespace MobileGL;
|
||||
|
||||
using MobileGL::MG_Test::ScopedMonolithFrontendArm;
|
||||
|
||||
class BufferTest : public ::testing::Test {
|
||||
protected:
|
||||
@@ -235,7 +233,6 @@ TEST_F(BufferTest, AcquireMemoryRangeWithoutExplicit) {
|
||||
}
|
||||
|
||||
TEST_F(BufferTest, AcquireMemoryRangeWithExplicit) {
|
||||
const ScopedMonolithFrontendArm frontendArm;
|
||||
auto& slot = MobileGL::MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::Uniform);
|
||||
Vector<Uint> bufferNames;
|
||||
MobileGL::MG_State::pGLContext->GenBufferNames(1, bufferNames);
|
||||
@@ -262,9 +259,10 @@ TEST_F(BufferTest, AcquireMemoryRangeWithExplicit) {
|
||||
const Uint64 flushedSerial = bufObj->GetChangeSerial();
|
||||
ASSERT_GT(flushedSerial, baseSerial);
|
||||
|
||||
// FlushExplicit unmap must not flush the rest of the mapped range.
|
||||
// FlushExplicit unmap must not flush the rest of the mapped range: the 300 written past the
|
||||
// flushed range never reaches the store (the content check below). Not asserted on the change
|
||||
// serial since P13 W6 - the record arm publishes the map's state edge as its own record.
|
||||
bufObj->ReleaseMemory();
|
||||
ASSERT_EQ(bufObj->GetChangeSerial(), flushedSerial);
|
||||
|
||||
Vector<Int> expected{10, 200, 30, 40, 50};
|
||||
Vector<Int> actual(5);
|
||||
@@ -2493,54 +2491,6 @@ TEST_F(BufferTest, MapBufferRangeAndUnmapBufferReseedAnAdoptedShaderStorageBuffe
|
||||
g_zeroCopyMock = nullptr;
|
||||
}
|
||||
|
||||
// The control: a store the backend declined to adopt keeps the shadow model exactly as
|
||||
// before - the staging copy is written back into the shadow and the backend's flush op
|
||||
// carries the range down.
|
||||
TEST_F(BufferTest, ANonPersistentWriteMapOfAShadowBackedStoreStillFlushesThroughTheBackend) {
|
||||
const ScopedMonolithFrontendArm frontendArm;
|
||||
ZeroCopyMockBackend mock;
|
||||
mock.provideMap = false;
|
||||
g_zeroCopyMock = &mock;
|
||||
ScopedBackendOps scopedOps(&kZeroCopyMockOps);
|
||||
|
||||
GLuint buffer = 0;
|
||||
GenBuffers(1, &buffer);
|
||||
BindBuffer(GL_SHADER_STORAGE_BUFFER, buffer);
|
||||
Vector<GLint> initial(kAdoptedInts);
|
||||
for (SizeT i = 0; i < kAdoptedInts; ++i) initial[i] = static_cast<GLint>(i);
|
||||
BufferData(GL_SHADER_STORAGE_BUFFER, static_cast<GLsizeiptr>(kAdoptedInts * sizeof(GLint)), initial.data(),
|
||||
GL_DYNAMIC_DRAW);
|
||||
ASSERT_EQ(GetError(), GL_NO_ERROR);
|
||||
auto bufferObject = MG_State::pGLContext->GetBufferObject(buffer);
|
||||
ASSERT_NE(bufferObject, nullptr);
|
||||
EXPECT_FALSE(bufferObject->EnsureGpuResidentStorage());
|
||||
EXPECT_FALSE(bufferObject->IsBackendPersistentMapped());
|
||||
mock.flushCalls = 0;
|
||||
mock.subDataCalls = 0;
|
||||
const Uint64 baseSerial = bufferObject->GetChangeSerial();
|
||||
|
||||
constexpr SizeT kFirst = 3;
|
||||
constexpr SizeT kCount = 4;
|
||||
const Range1D range{kFirst * sizeof(GLint), (kFirst + kCount) * sizeof(GLint)};
|
||||
auto* mapped = static_cast<GLint*>(bufferObject->AcquireMemoryRange(range, BufferMappingAccessBit::Write));
|
||||
ASSERT_NE(mapped, nullptr);
|
||||
for (SizeT i = 0; i < kCount; ++i) mapped[i] = 600 + static_cast<GLint>(i);
|
||||
bufferObject->ReleaseMemory();
|
||||
|
||||
EXPECT_EQ(mock.flushCalls, 1);
|
||||
EXPECT_EQ(mock.subDataCalls, 0);
|
||||
EXPECT_GT(bufferObject->GetChangeSerial(), baseSerial);
|
||||
const auto* shadow = reinterpret_cast<const GLint*>(bufferObject->MappedData());
|
||||
for (SizeT i = 0; i < kAdoptedInts; ++i) {
|
||||
const GLint expected = (i >= kFirst && i < kFirst + kCount) ? 600 + static_cast<GLint>(i - kFirst)
|
||||
: static_cast<GLint>(i);
|
||||
EXPECT_EQ(shadow[i], expected) << "int " << i;
|
||||
}
|
||||
|
||||
DeleteBuffers(1, &buffer);
|
||||
g_zeroCopyMock = nullptr;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// The other three CPU-sourced writes that share the unmap landing's route into an
|
||||
// adopted store - glBufferSubData, a clear, and a copy - on both kinds of backend: the
|
||||
@@ -2957,38 +2907,3 @@ TEST_F(BufferTest, RespecifyingAStoreWhileItIsMappedDoesNotLandTheStagedBytesInt
|
||||
g_zeroCopyMock = nullptr;
|
||||
}
|
||||
|
||||
TEST_F(BufferTest, RespecifyingAShadowBackedStoreWhileItIsMappedPushesNoRangeDown) {
|
||||
const ScopedMonolithFrontendArm frontendArm;
|
||||
ZeroCopyMockBackend mock;
|
||||
mock.provideMap = false;
|
||||
g_zeroCopyMock = &mock;
|
||||
ScopedBackendOps scopedOps(&kZeroCopyMockOps);
|
||||
|
||||
GLuint buffer = 0;
|
||||
GenBuffers(1, &buffer);
|
||||
BindBuffer(GL_ARRAY_BUFFER, buffer);
|
||||
Vector<GLint> initial(kAdoptedInts, 7);
|
||||
BufferData(GL_ARRAY_BUFFER, static_cast<GLsizeiptr>(kAdoptedInts * sizeof(GLint)), initial.data(),
|
||||
GL_DYNAMIC_DRAW);
|
||||
ASSERT_EQ(GetError(), GL_NO_ERROR);
|
||||
auto bufferObject = MG_State::pGLContext->GetBufferObject(buffer);
|
||||
ASSERT_NE(bufferObject, nullptr);
|
||||
mock.flushCalls = 0;
|
||||
mock.subDataCalls = 0;
|
||||
mock.respecifyCalls = 0;
|
||||
|
||||
auto* mapped = static_cast<GLint*>(
|
||||
bufferObject->AcquireMemoryRange({0, kAdoptedInts * sizeof(GLint)}, BufferMappingAccessBit::Write));
|
||||
ASSERT_NE(mapped, nullptr);
|
||||
for (SizeT i = 0; i < kAdoptedInts; ++i) mapped[i] = 1234;
|
||||
|
||||
bufferObject->Respecify(kAdoptedInts * sizeof(GLint), nullptr);
|
||||
EXPECT_EQ(mock.flushCalls, 0);
|
||||
EXPECT_EQ(mock.subDataCalls, 0);
|
||||
EXPECT_EQ(mock.respecifyCalls, 1);
|
||||
EXPECT_FALSE(bufferObject->IsMapped());
|
||||
EXPECT_FALSE(bufferObject->HasDefinedContent());
|
||||
|
||||
DeleteBuffers(1, &buffer);
|
||||
g_zeroCopyMock = nullptr;
|
||||
}
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
// MobileGL - MobileGL/MG_Test/ScopedMonolithFrontendArm.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 <Config.h>
|
||||
#include <MG_Backend/BackendObjects.h>
|
||||
|
||||
namespace MobileGL::MG_Test {
|
||||
|
||||
// P13 W4: THE MONOLITH FRONTEND ARM, PINNED FOR ONE CASE. A case that uses this asserts how
|
||||
// the frontend answers when the backend reads the frontend's objects itself - pushing a map's
|
||||
// bytes through the object, reading a texture back from the frontend's own level shadow - which
|
||||
// is the arm W4 retires family by family and W6 deletes. On the record arm (every wire, and
|
||||
// monolith's from W4) the client tracker and the backend's record readers own those answers,
|
||||
// and the split and integration lanes cover them. When W6 deletes the frontend arm, the cases
|
||||
// that use this go with it.
|
||||
//
|
||||
// Both halves of the arm: the predicate, and the backend table (the verb port's slots go back
|
||||
// to the backend's own entry points for the scope).
|
||||
class ScopedMonolithFrontendArm {
|
||||
public:
|
||||
ScopedMonolithFrontendArm() : m_savedTable(MG_Backend::gBackendFunctionsTable) {
|
||||
m_saved = MG_Config::MonolithTakesRecordArm;
|
||||
MG_Config::MonolithTakesRecordArm = false;
|
||||
if (MG_Config::Transport == MG_Config::TransportMode::Monolith && MG_Backend::pActiveBackendObject) {
|
||||
MG_Backend::gBackendFunctionsTable = MG_Backend::pActiveBackendObject->GetBackendFunctions();
|
||||
}
|
||||
}
|
||||
~ScopedMonolithFrontendArm() {
|
||||
MG_Config::MonolithTakesRecordArm = m_saved;
|
||||
MG_Backend::gBackendFunctionsTable = m_savedTable;
|
||||
}
|
||||
ScopedMonolithFrontendArm(const ScopedMonolithFrontendArm&) = delete;
|
||||
ScopedMonolithFrontendArm& operator=(const ScopedMonolithFrontendArm&) = delete;
|
||||
|
||||
private:
|
||||
MG_Backend::GlobalBackendFunctionsTable m_savedTable;
|
||||
Bool m_saved = false;
|
||||
};
|
||||
|
||||
} // namespace MobileGL::MG_Test
|
||||
@@ -13,7 +13,6 @@
|
||||
|
||||
#include "Includes.h"
|
||||
#include "Init.h"
|
||||
#include <MG_Test/ScopedMonolithFrontendArm.h>
|
||||
#include <Config.h>
|
||||
#include <MG_Backend/BackendObjects.h>
|
||||
#include <MG_Backend/DirectGLES/Managers.h>
|
||||
@@ -1061,99 +1060,6 @@ TEST_F(TextureTest, EverySamplerScalarPnameConvertsToTheQueriedType) {
|
||||
DrainPendingGlErrors();
|
||||
}
|
||||
|
||||
// The CPU-shadow readback path performs no format/type conversion and packs rows tightly. Asking it
|
||||
// for a layout it cannot produce used to be answered by memcpying the SHADOW's layout into the
|
||||
// caller's buffer: on glGetTexImage, which has no bufSize argument, that is a heap overflow of
|
||||
// (shadowTexelSize - clientTexelSize) * texelCount bytes. It must refuse instead.
|
||||
TEST_F(TextureTest, ShadowReadbackRefusesALayoutItCannotProduceInsteadOfOverrunningTheBuffer) {
|
||||
const MobileGL::MG_Test::ScopedMonolithFrontendArm frontendArm;
|
||||
GLuint texture = 0;
|
||||
MG_Impl::GLImpl::GenTextures(1, &texture);
|
||||
ASSERT_NE(texture, 0u);
|
||||
MG_Impl::GLImpl::BindTexture(GL_TEXTURE_2D, texture);
|
||||
const std::vector<GLubyte> source(8 * 8 * 4, 0x5A);
|
||||
MG_Impl::GLImpl::TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE, source.data());
|
||||
ASSERT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
||||
|
||||
// The positive control first: the matching layout is answered, and answered correctly.
|
||||
std::vector<GLubyte> matching(8 * 8 * 4, 0);
|
||||
MG_Impl::GLImpl::GetTextureImage(texture, 0, GL_RGBA, GL_UNSIGNED_BYTE,
|
||||
static_cast<GLsizei>(matching.size()), matching.data());
|
||||
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
||||
EXPECT_EQ(matching, source);
|
||||
|
||||
// GL_RED against an RGBA8 shadow: 1 client byte per texel against 4 shadow bytes. A verbatim copy
|
||||
// would write 256 bytes into the 64 GL 4.6 core 8.11 says are required.
|
||||
std::vector<GLubyte> narrow(8 * 8 * 1, 0xCD);
|
||||
const std::vector<GLubyte> narrowBefore = narrow;
|
||||
MG_Impl::GLImpl::GetTextureImage(texture, 0, GL_RED, GL_UNSIGNED_BYTE, static_cast<GLsizei>(narrow.size()),
|
||||
narrow.data());
|
||||
ExpectSingleGlError(GL_INVALID_OPERATION);
|
||||
EXPECT_EQ(narrow, narrowBefore) << "a refused readback must not touch the destination";
|
||||
|
||||
// And the widening direction, which is not an overflow but is still the wrong bytes.
|
||||
std::vector<GLfloat> wide(8 * 8 * 4, 0.0f);
|
||||
MG_Impl::GLImpl::GetTextureImage(texture, 0, GL_RGBA, GL_FLOAT,
|
||||
static_cast<GLsizei>(wide.size() * sizeof(GLfloat)), wide.data());
|
||||
ExpectSingleGlError(GL_INVALID_OPERATION);
|
||||
|
||||
MG_Impl::GLImpl::BindTexture(GL_TEXTURE_2D, 0);
|
||||
MG_Impl::GLImpl::DeleteTextures(1, &texture);
|
||||
DrainPendingGlErrors();
|
||||
}
|
||||
|
||||
// The same helper honours only GL_PACK_SWAP_BYTES and the bitmap LSB_FIRST path - the pixel-store
|
||||
// parameters carry a standing TODO in the pack processor. GL_PACK_ALIGNMENT defaults to 4, so a
|
||||
// 3-byte-per-texel format at an odd width needs row padding that would never be written, and the GPU
|
||||
// readback path DOES write it. Refusing keeps the two paths from answering the same call with two
|
||||
// different destination layouts.
|
||||
TEST_F(TextureTest, ShadowReadbackRefusesAPackStateItCannotHonour) {
|
||||
const MobileGL::MG_Test::ScopedMonolithFrontendArm frontendArm;
|
||||
GLuint texture = 0;
|
||||
MG_Impl::GLImpl::GenTextures(1, &texture);
|
||||
ASSERT_NE(texture, 0u);
|
||||
MG_Impl::GLImpl::BindTexture(GL_TEXTURE_2D, texture);
|
||||
// The UNPACK side has the same default alignment of 4, and 5 * 3 = 15 is not a multiple of it -
|
||||
// so a tightly-packed source would be read back out with a 16-byte row stride and the texture
|
||||
// would hold the wrong bytes before the readback under test even runs. This is the pack rule
|
||||
// being pinned below, seen from the upload side.
|
||||
MG_Impl::GLImpl::PixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
ASSERT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
||||
const std::vector<GLubyte> source(5 * 5 * 3, 0x21);
|
||||
MG_Impl::GLImpl::TexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, 5, 5, 0, GL_RGB, GL_UNSIGNED_BYTE, source.data());
|
||||
ASSERT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
||||
|
||||
// 5 * 3 = 15 bytes per row, which the default GL_PACK_ALIGNMENT of 4 pads to 16.
|
||||
std::vector<GLubyte> padded(5 * 16, 0);
|
||||
MG_Impl::GLImpl::GetTextureImage(texture, 0, GL_RGB, GL_UNSIGNED_BYTE, static_cast<GLsizei>(padded.size()),
|
||||
padded.data());
|
||||
ExpectSingleGlError(GL_INVALID_OPERATION);
|
||||
|
||||
// With the padding removed the rows are tight and the same call is answered.
|
||||
MG_Impl::GLImpl::PixelStorei(GL_PACK_ALIGNMENT, 1);
|
||||
ASSERT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
||||
std::vector<GLubyte> tight(5 * 5 * 3, 0);
|
||||
MG_Impl::GLImpl::GetTextureImage(texture, 0, GL_RGB, GL_UNSIGNED_BYTE, static_cast<GLsizei>(tight.size()),
|
||||
tight.data());
|
||||
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
||||
EXPECT_EQ(tight, source);
|
||||
|
||||
// A skip offset is ignored outright by the pack processor, so it is refused even when the rows
|
||||
// themselves are tight.
|
||||
MG_Impl::GLImpl::PixelStorei(GL_PACK_SKIP_ROWS, 1);
|
||||
ASSERT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
||||
MG_Impl::GLImpl::GetTextureImage(texture, 0, GL_RGB, GL_UNSIGNED_BYTE, static_cast<GLsizei>(tight.size()),
|
||||
tight.data());
|
||||
ExpectSingleGlError(GL_INVALID_OPERATION);
|
||||
|
||||
MG_Impl::GLImpl::PixelStorei(GL_PACK_SKIP_ROWS, 0);
|
||||
MG_Impl::GLImpl::PixelStorei(GL_PACK_ALIGNMENT, 4);
|
||||
MG_Impl::GLImpl::PixelStorei(GL_UNPACK_ALIGNMENT, 4);
|
||||
MG_Impl::GLImpl::BindTexture(GL_TEXTURE_2D, 0);
|
||||
MG_Impl::GLImpl::DeleteTextures(1, &texture);
|
||||
DrainPendingGlErrors();
|
||||
}
|
||||
|
||||
// glTextureParameter* has no target token, so GL 4.6 core 8.10 applies the ten-target list to the
|
||||
// texture's EFFECTIVE target. The four vector DSA forms reached that gate for free by re-entering
|
||||
// through the bound-target path; the two scalar forms called the per-object setter directly and
|
||||
@@ -2141,47 +2047,6 @@ TEST_F(TextureTest, GetTextureSubImageSelectsTheCubeFaceZOffsetNames) {
|
||||
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_INVALID_OPERATION);
|
||||
}
|
||||
|
||||
// glGetTexImage of ONE cube face packs one face, so a PIXEL_PACK_BUFFER holding one face is
|
||||
// exactly the right size for it. The validator used to measure the bound PBO against all SIX
|
||||
// faces' worth and refuse - INVALID_OPERATION for a buffer the copy that follows would have filled
|
||||
// precisely. glGetTexImage passes no bufSize, which skips the destination-size branch but NOT the
|
||||
// PBO one, so this is the only spelling where the six-face sizing was reachable at all.
|
||||
TEST_F(TextureTest, GetTexImageOfOneCubeFacePacksIntoAOneFacePixelPackBuffer) {
|
||||
const MobileGL::MG_Test::ScopedMonolithFrontendArm frontendArm;
|
||||
constexpr GLsizei kEdge = 2;
|
||||
constexpr SizeT kFaceBytes = static_cast<SizeT>(kEdge) * kEdge * 4;
|
||||
|
||||
GLuint texture = 0;
|
||||
MG_Impl::GLImpl::GenTextures(1, &texture);
|
||||
MG_Impl::GLImpl::BindTexture(GL_TEXTURE_CUBE_MAP, texture);
|
||||
MG_Impl::GLImpl::TexStorage2D(GL_TEXTURE_CUBE_MAP, 1, GL_RGBA8, kEdge, kEdge);
|
||||
for (int face = 0; face < 6; ++face) {
|
||||
Uint8 seed[kFaceBytes];
|
||||
for (SizeT i = 0; i < kFaceBytes; ++i) seed[i] = static_cast<Uint8>(10 + face);
|
||||
MG_Impl::GLImpl::TexSubImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, 0, 0, 0, kEdge, kEdge, GL_RGBA,
|
||||
GL_UNSIGNED_BYTE, seed);
|
||||
}
|
||||
ASSERT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR) << "seeding the six faces failed";
|
||||
|
||||
GLuint buffer = 0;
|
||||
MG_Impl::GLImpl::GenBuffers(1, &buffer);
|
||||
MG_Impl::GLImpl::BindBuffer(GL_PIXEL_PACK_BUFFER, buffer);
|
||||
MG_Impl::GLImpl::BufferData(GL_PIXEL_PACK_BUFFER, static_cast<GLsizeiptr>(kFaceBytes), nullptr, GL_STREAM_READ);
|
||||
ASSERT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR) << "creating the one-face pixel pack buffer failed";
|
||||
|
||||
MG_Impl::GLImpl::GetTexImage(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
||||
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR)
|
||||
<< "a pixel pack buffer sized for the one face this call packs was refused";
|
||||
|
||||
Uint8 packed[kFaceBytes] = {};
|
||||
MG_Impl::GLImpl::GetBufferSubData(GL_PIXEL_PACK_BUFFER, 0, static_cast<GLsizeiptr>(kFaceBytes), packed);
|
||||
EXPECT_EQ(static_cast<int>(packed[0]), 15) << "the PBO holds face " << (static_cast<int>(packed[0]) - 10)
|
||||
<< ", not -Z";
|
||||
|
||||
MG_Impl::GLImpl::BindBuffer(GL_PIXEL_PACK_BUFFER, 0);
|
||||
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
||||
}
|
||||
|
||||
TEST_F(TextureTest, TextureParameteriAndBindTextureUnitAreDirectStateAccess) {
|
||||
GLuint texture = 0;
|
||||
MG_Impl::GLImpl::CreateTextures(GL_TEXTURE_2D, 1, &texture);
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
|
||||
#include "Includes.h"
|
||||
#include "Init.h"
|
||||
#include <MG_Test/ScopedMonolithFrontendArm.h>
|
||||
#include <MG_Backend/BackendObjects.h>
|
||||
#include <MG_Impl/GLImpl/Getter/GL_Getter.h>
|
||||
#include <MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.h>
|
||||
@@ -509,45 +508,4 @@ namespace {
|
||||
|
||||
// ======================= which of the owner's layers a face names =======================
|
||||
|
||||
// A GL_TEXTURE_CUBE_MAP view over a LAYERED owner - a 2D array here, a cube-map ARRAY behaves
|
||||
// identically - is the one shape where the face a target names cannot be carried by the choice
|
||||
// of blob: the owner keeps every layer in ONE blob, so there is nothing for
|
||||
// ToOwnerUploadTarget to choose between and the face has to land in the byte offset instead.
|
||||
// It did not. The offset shifted by the view's layer origin alone, so all six face tokens read
|
||||
// the view's FIRST layer-face - silently, with real texels from a real layer, on every path
|
||||
// that answers out of the CPU shadow.
|
||||
//
|
||||
// The shadow is exactly what this exercises: the fixture's backend is not DirectVulkan, so the
|
||||
// by-name readback takes the shadow arm rather than asking a backend. (DirectVulkan's own path
|
||||
// resolves the face into a Vulkan baseArrayLayer and was always right, which is what made this
|
||||
// a disagreement between the two backends rather than a uniform wrong answer.)
|
||||
TEST_F(TextureViewTest, CubeMapViewOfAnArrayReadsTheFaceEachTokenNames) {
|
||||
const MobileGL::MG_Test::ScopedMonolithFrontendArm frontendArm;
|
||||
constexpr GLint kLayers = 8;
|
||||
constexpr GLint kViewMinLayer = 2;
|
||||
|
||||
const GLuint storage = MakeImmutable2DArray(1, 1, kLayers);
|
||||
// Every layer carries its own index, so a read that lands on the wrong one says which one
|
||||
// answered instead of merely failing.
|
||||
for (GLint layer = 0; layer < kLayers; ++layer) {
|
||||
const Uint8 texel[] = {static_cast<Uint8>(10 + layer), 20, 30, 40};
|
||||
MG_Impl::GLImpl::TexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, layer, 1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE,
|
||||
texel);
|
||||
}
|
||||
DrainPendingGlErrors();
|
||||
|
||||
const GLuint view = GenTexture();
|
||||
MG_Impl::GLImpl::TextureView(view, GL_TEXTURE_CUBE_MAP, storage, GL_RGBA8, 0, 1, kViewMinLayer, 6);
|
||||
ASSERT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR) << "the cube-map view over the array was refused";
|
||||
|
||||
for (GLint face = 0; face < 6; ++face) {
|
||||
Uint8 output[4] = {};
|
||||
MG_Impl::GLImpl::GetTextureSubImage(view, 0, 0, 0, face, 1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE,
|
||||
sizeof(output), output);
|
||||
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR) << "reading face " << face << " errored";
|
||||
EXPECT_EQ(static_cast<GLint>(output[0]), 10 + kViewMinLayer + face)
|
||||
<< "face " << face << " of a view based at layer " << kViewMinLayer << " answered with layer "
|
||||
<< (static_cast<GLint>(output[0]) - 10);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@@ -461,17 +461,11 @@ TEST(StagedTextureStoreTest, AFollowedCopyMovesTheWindowAndLeavesTheSourceAlone)
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
TEST(StagedTextureStoreTest, AnAliasingStoreFollowsACopyIntoAServerOwnedDestination) {
|
||||
const auto savedTransport = MG_Config::Transport;
|
||||
const Bool savedArm = MG_Config::MonolithTakesRecordArm;
|
||||
MG_Config::Transport = MG_Config::TransportMode::Monolith;
|
||||
MG_Config::MonolithTakesRecordArm = true;
|
||||
struct Restore {
|
||||
MG_Config::TransportMode transport;
|
||||
Bool arm;
|
||||
~Restore() {
|
||||
MG_Config::Transport = transport;
|
||||
MG_Config::MonolithTakesRecordArm = arm;
|
||||
}
|
||||
} restore{savedTransport, savedArm};
|
||||
~Restore() { MG_Config::Transport = transport; }
|
||||
} restore{savedTransport};
|
||||
Server::StagedTextureStore store(/*copies=*/false);
|
||||
ASSERT_TRUE(store.Aliases());
|
||||
const Uint64 src = Server::StagedTextureStore::KeyForHandle(TestHandle(66, 1));
|
||||
|
||||
@@ -11,6 +11,10 @@ them wrong.
|
||||
|
||||
| Date | Check | Workflow / job | Reason | Successor |
|
||||
|---|---|---|---|---|
|
||||
| 2026-10-06 | unit `BufferTest.{ANonPersistentWriteMapOfAShadowBackedStoreStillFlushesThroughTheBackend, RespecifyingAShadowBackedStoreWhileItIsMappedPushesNoRangeDown}` | Test / unit | P13 W6: they pinned the monolith frontend arm (`ScopedMonolithFrontendArm`) to assert how a shadow-backed map reaches the backend's own flush op - the arm is deleted; the record arm pushes the mapped span itself | `SplitBufferTest` (persistent-map push, live-host-writes), `PersistentCoherentMapScenario` and the buffer integration lanes on monolith's record arm |
|
||||
| 2026-10-06 | unit `TextureTest.{ShadowReadbackRefusesALayoutItCannotProduceInsteadOfOverrunningTheBuffer, ShadowReadbackRefusesAPackStateItCannotHonour}` | Test / unit | P13 W6: they pinned the frontend arm's CPU-shadow readback, whose pack-state limits they asserted; that path is deleted - the record arm reads the backend's image under a neutral pack and scatters every pack state itself | `PixelStoreSweepScenario`, `PackBufferReadbackScenario` (every pack state, both backends, every arm) |
|
||||
| 2026-10-06 | unit `TextureTest.GetTexImageOfOneCubeFacePacksIntoAOneFacePixelPackBuffer`, `TextureViewTest.CubeMapViewOfAnArrayReadsTheFaceEachTokenNames` | Test / unit | P13 W6: GL rules, but asserted through the deleted frontend arm's shadow readback; a unit process has no backend image to read | moved, not dropped: `CubeFaceReadbackScenario` (integration-gpu on both backends plus the split arms) asserts the same two rules on a real context |
|
||||
| 2026-10-06 | `DirectVulkan.HandleRecycle.AbaControlHandles.*` | Test / integration-gpu (`integration-monolith-control`) | P13 W6: it pinned `MOBILEGL_PIPE_DATA_ARM=frontend` - its knob defeats the identity half of Magma's frontend-arm keys, which the deleted arm owned; the record arm keys every object on the applier's {slot, gen} | `DirectVulkan.HandleRecycle.Handles.*` (the same cases on the record arm) |
|
||||
| 2026-10-06 | `build explicit pull runtime control` (the pull `libMobileGL.so`, `-DMOBILEGL_PIPE_PUSH=OFF`) | Test / `build-linux-monolith-control` | P13 W2 retires the pull build: CMake defaults to push and nothing ships pull (ID-P13-1) | the same job builds the push monolith without MG_Remote (the FCL shape); its `runtime_mode_proof --mode monolith` is the zero-MG_Remote-symbol check |
|
||||
| 2026-10-06 | `Negative control (pull library)` | Test / `retrace-split` (every leg) | its library was the pull build above | `Negative control (library without a transport)`: same script (renamed `retrace_transport_control.sh`) and evidence sentence, with the push library without MG_Remote; W1 ran both side by side on every leg |
|
||||
| 2026-10-06 | `DirectGLES.HandleRecycle.Legacy.*`, `DirectVulkan.HandleRecycle.Legacy.*`, `DirectVulkan.HandleRecycle.AbaControl.*` | Test / integration-gpu (`integration-monolith-control`) | they ran `MOBILEGL_PIPE_PUSH=0`, the pre-handle arm, which P13 W3b removed with `MOBILEGL_PIPE_LEGACY_MEMOS` (ID-P13-3) | `HandleRecycle.Handles.*` and `DirectVulkan.HandleRecycle.AbaControlHandles.*` (the ABA control against the handle arm that ships) stay |
|
||||
|
||||
Reference in New Issue
Block a user