mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-08 10:33:24 +09:00
[Test] (P13 W3b): retire the two P2-mask texture-sync probes and log every W3b removal
This commit is contained in:
@@ -3040,144 +3040,11 @@ TEST(UnorderedMapSanity, ErasingTheOnlyElementReturnsEnd) {
|
||||
EXPECT_TRUE(map.empty());
|
||||
}
|
||||
|
||||
namespace {
|
||||
// Records what the per-unit texture sync actually pushed at the driver: which backend
|
||||
// texture id was current when each glTexImage2D landed, and the shape it was given.
|
||||
struct TexSpecCall {
|
||||
GLuint texture;
|
||||
GLsizei width;
|
||||
GLsizei height;
|
||||
};
|
||||
MobileGL::Vector<TexSpecCall>* g_texSpecCalls = nullptr;
|
||||
GLuint g_texSpecBoundTexture = 0;
|
||||
|
||||
void TS_BindTexture(GLenum, GLuint texture) { g_texSpecBoundTexture = texture; }
|
||||
void TS_ActiveTexture(GLenum) {}
|
||||
void TS_TexParameteri(GLenum, GLenum, GLint) {}
|
||||
void TS_TexParameterf(GLenum, GLenum, GLfloat) {}
|
||||
void TS_TexParameterfv(GLenum, GLenum, const GLfloat*) {}
|
||||
void TS_PixelStorei(GLenum, GLint) {}
|
||||
void TS_BindBuffer(GLenum, GLuint) {}
|
||||
void TS_TexImage2D(GLenum, GLint level, GLint, GLsizei width, GLsizei height, GLint, GLenum, GLenum,
|
||||
const void*) {
|
||||
if (g_texSpecCalls && level == 0) {
|
||||
g_texSpecCalls->push_back({g_texSpecBoundTexture, width, height});
|
||||
}
|
||||
}
|
||||
|
||||
// Clears the recording hook even when a gtest assertion unwinds the test body.
|
||||
struct ScopedTexSpecRecording {
|
||||
explicit ScopedTexSpecRecording(MobileGL::Vector<TexSpecCall>& sink) {
|
||||
g_texSpecCalls = &sink;
|
||||
g_texSpecBoundTexture = 0;
|
||||
}
|
||||
~ScopedTexSpecRecording() { g_texSpecCalls = nullptr; }
|
||||
ScopedTexSpecRecording(const ScopedTexSpecRecording&) = delete;
|
||||
ScopedTexSpecRecording& operator=(const ScopedTexSpecRecording&) = delete;
|
||||
};
|
||||
|
||||
// Gives `name` a complete single-level 2D image of the requested size without going through
|
||||
// the frontend upload path (the mock table below wires only the state-pushing entry points).
|
||||
MobileGL::SharedPtr<MobileGL::MG_State::GLState::ITextureObject> MakeComplete2DTexture(GLuint name,
|
||||
MobileGL::Int size) {
|
||||
using namespace MobileGL;
|
||||
MG_Impl::GLImpl::BindTexture(GL_TEXTURE_2D, name);
|
||||
auto object = MG_State::pGLContext->GetTextureUnitObject(0)
|
||||
.GetBindingSlot(TextureTarget::Texture2D)
|
||||
.GetBoundObject();
|
||||
object->SetInternalFormat(TextureInternalFormat::RGBA8);
|
||||
MG_State::GLState::AsMipmapTexture(object.get())
|
||||
->AllocateStorage(TextureUploadTarget::Texture2D, 0, {{size, size, 1}, 4});
|
||||
return object;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// The per-unit texture sync memo BORROWS the binding slot: an entry holds a pointer to the
|
||||
// slot's shared_ptr plus the backend twin of whatever was in it when the entry was built. Its
|
||||
// keys (context id, bind-generation epoch, high-water mark, sampling generation) are the primary
|
||||
// guard, but they are all derived state - so the memo also has to survive a slot swap that never
|
||||
// reached them.
|
||||
// P5e (tx2), CONTRACT-P5E §4.3: THE HANDLE ARM'S SIBLING OF UnitMemoRefusesToDriveATwinFromAnotherTexture,
|
||||
// WITH ABA AS THE HAZARD INSTEAD OF A SILENT SLOT SWAP. (P13 W3b retired that case: it ran only at
|
||||
// the P2 mask, whose legacy unit arm is gone now that subsystem bits 0-13 are fixed on.)
|
||||
//
|
||||
// It did not. The DSA by-name emulation swapped a slot silently, every key still matched, and
|
||||
// the replay drove texture A's backend twin from texture B's frontend object: A's backend
|
||||
// storage was re-specified with B's shape, destroying anything A only ever had on the GPU. On
|
||||
// Espryt + Iris/BSL that blanked Minecraft's 16x16 lightmap the moment a 2048x2048 shadow map
|
||||
// was uploaded through a by-name call, and since the text shader multiplies by the lightmap,
|
||||
// `if (color.a < 0.1) discard` then threw away every glyph in the process - HUD, menus and the
|
||||
// vanilla title screen alike.
|
||||
TEST(DirectGLESTextureSync, UnitMemoRefusesToDriveATwinFromAnotherTexture) {
|
||||
using namespace MobileGL;
|
||||
ScopedDirectGLESTextureBindings scoped; // fresh GLContext + registry + binding caches
|
||||
Vector<TexSpecCall> specs;
|
||||
ScopedTexSpecRecording recording(specs);
|
||||
|
||||
auto functions = MG_Backend::DirectGLES::g_GLESFuncs;
|
||||
functions.glBindTexture = TS_BindTexture;
|
||||
functions.glActiveTexture = TS_ActiveTexture;
|
||||
functions.glTexImage2D = TS_TexImage2D;
|
||||
functions.glTexParameteri = TS_TexParameteri;
|
||||
functions.glTexParameterf = TS_TexParameterf;
|
||||
functions.glTexParameterfv = TS_TexParameterfv;
|
||||
functions.glPixelStorei = TS_PixelStorei;
|
||||
functions.glBindBuffer = TS_BindBuffer;
|
||||
MG_Backend::DirectGLES::SetGLESFuncsTable(functions);
|
||||
|
||||
GLuint names[2] = {};
|
||||
MG_Impl::GLImpl::GenTextures(2, names);
|
||||
// `foreign` stands in for the shadow map, `resident` for the lightmap. Both are fully
|
||||
// specified BEFORE the first sync so that nothing between the two syncs can move the
|
||||
// sampling-resolution generation and invalidate the memo for an unrelated reason.
|
||||
const auto foreign = MakeComplete2DTexture(names[1], 32);
|
||||
const auto resident = MakeComplete2DTexture(names[0], 16);
|
||||
ASSERT_NE(foreign, nullptr);
|
||||
ASSERT_NE(resident, nullptr);
|
||||
|
||||
// First sync: builds the memo with unit 0 -> `resident`, and gives `resident`'s twin its
|
||||
// 16x16 backend storage. The unit walk is the per-draw one, so both syncs below stand in a
|
||||
// draw - the fill is what a real glDraw* would have done before reaching this helper.
|
||||
MG_Test::ScopedPipeVerb draw(MG_Pipe::MGPipeVerb::DrawArrays);
|
||||
MG_Backend::DirectGLES::TextureImpl::SyncNeccessaryTextures();
|
||||
auto* residentSlot = MG_Backend::DirectGLES::TextureImpl::g_backendTextureObjects.Find(resident.get());
|
||||
ASSERT_NE(residentSlot, nullptr);
|
||||
ASSERT_NE(*residentSlot, nullptr);
|
||||
const GLuint residentBackendId = (*residentSlot)->GetBackendTextureId();
|
||||
ASSERT_NE(residentBackendId, 0u);
|
||||
ASSERT_FALSE(specs.empty());
|
||||
EXPECT_EQ(specs.back().texture, residentBackendId);
|
||||
EXPECT_EQ(specs.back().width, 16);
|
||||
|
||||
// The hazard, reproduced at the state level: put `foreign` on the slot the memo borrows
|
||||
// WITHOUT telling the binding accounting, exactly as the by-name emulation used to.
|
||||
MG_State::pGLContext->GetTextureUnitObject(0).GetBindingSlot(TextureTarget::Texture2D).Bind(foreign);
|
||||
|
||||
const SizeT specsBeforeReplay = specs.size();
|
||||
// The second draw. Its fill re-reads the memo's keys off the live context, which is the
|
||||
// premise being tested: the silent slot swap moved none of them.
|
||||
draw.Renew();
|
||||
MG_Backend::DirectGLES::TextureImpl::SyncNeccessaryTextures();
|
||||
|
||||
// `foreign` must have been synced through its OWN twin...
|
||||
auto* foreignSlot = MG_Backend::DirectGLES::TextureImpl::g_backendTextureObjects.Find(foreign.get());
|
||||
ASSERT_NE(foreignSlot, nullptr);
|
||||
ASSERT_NE(*foreignSlot, nullptr);
|
||||
const GLuint foreignBackendId = (*foreignSlot)->GetBackendTextureId();
|
||||
EXPECT_NE(foreignBackendId, residentBackendId);
|
||||
|
||||
// ...and above all, nothing may have re-specified the RESIDENT texture's backend storage.
|
||||
// That single call is what destroyed the lightmap.
|
||||
for (SizeT i = specsBeforeReplay; i < specs.size(); ++i) {
|
||||
EXPECT_NE(specs[i].texture, residentBackendId)
|
||||
<< "the stale memo entry re-specified the resident texture's backend storage with "
|
||||
<< specs[i].width << "x" << specs[i].height;
|
||||
}
|
||||
|
||||
MG_Impl::GLImpl::BindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
// P5e (tx2), CONTRACT-P5E §4.3: THE HANDLE ARM'S SIBLING OF THE CASE ABOVE, WITH ABA AS THE
|
||||
// HAZARD INSTEAD OF A SILENT SLOT SWAP.
|
||||
//
|
||||
// The case above pins what the BORROWED-SLOT work list needs: a pointer compare per entry,
|
||||
// That case pinned what the BORROWED-SLOT work list needs: a pointer compare per entry,
|
||||
// because its key is derived state and a slot swap that never reached the key would drive
|
||||
// texture A's twin from texture B. The by-handle work list carries no such pointer and no
|
||||
// PairingsIntact, and this is the argument for why it does not have to:
|
||||
@@ -3256,173 +3123,6 @@ TEST(DirectGLESTextureSync, ARecycledTextureSlotReMintsTheTwinOnTheHandleArm) {
|
||||
MG_Pipe::MGPipeSlots().Free(MG_Pipe::MGPipeKind::Texture, second);
|
||||
}
|
||||
|
||||
namespace {
|
||||
// What glTexParameteri actually reached the driver, and which backend texture was bound
|
||||
// when it did. The G9 probe below is a WHITE-BOX assertion (ID-19): the parameter push is
|
||||
// not observable through public GL without creating the very sampler view whose absence is
|
||||
// the subject, so the observation is taken at the driver boundary instead.
|
||||
struct TexParamCall {
|
||||
GLuint texture;
|
||||
GLenum pname;
|
||||
GLint value;
|
||||
};
|
||||
MobileGL::Vector<TexParamCall>* g_texParamCalls = nullptr;
|
||||
GLuint g_texParamBoundTexture = 0;
|
||||
|
||||
void TP_BindTexture(GLenum, GLuint texture) { g_texParamBoundTexture = texture; }
|
||||
void TP_ActiveTexture(GLenum) {}
|
||||
void TP_TexParameteri(GLenum, GLenum pname, GLint value) {
|
||||
if (g_texParamCalls) {
|
||||
g_texParamCalls->push_back({g_texParamBoundTexture, pname, value});
|
||||
}
|
||||
}
|
||||
void TP_TexParameterf(GLenum, GLenum, GLfloat) {}
|
||||
void TP_TexParameterfv(GLenum, GLenum, const GLfloat*) {}
|
||||
void TP_PixelStorei(GLenum, GLint) {}
|
||||
void TP_BindBuffer(GLenum, GLuint) {}
|
||||
|
||||
struct ScopedTexParamRecording {
|
||||
explicit ScopedTexParamRecording(MobileGL::Vector<TexParamCall>& sink) {
|
||||
g_texParamCalls = &sink;
|
||||
g_texParamBoundTexture = 0;
|
||||
}
|
||||
~ScopedTexParamRecording() { g_texParamCalls = nullptr; }
|
||||
ScopedTexParamRecording(const ScopedTexParamRecording&) = delete;
|
||||
ScopedTexParamRecording& operator=(const ScopedTexParamRecording&) = delete;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
// G9 AS A WHITE-BOX ASSERTION (gates review R1, ID-19), AND IT COVERS THE HALF THE PUBLIC-GL
|
||||
// SCENARIO CANNOT.
|
||||
//
|
||||
// TextureParamsWithoutASamplerViewScenario catches "the parameters were EMITTED and marked
|
||||
// synced but never applied": its observation is a sample, the sample creates the sampler view,
|
||||
// and IsDrawSyncClean then skips the sync. What it cannot catch is a backend that merely DEFERS
|
||||
// the apply to the first sampler view - the observation creates that view, the parameters land
|
||||
// at that moment, and the case is green. Here the reading is taken while the texture is still
|
||||
// attachment-only: nothing is ever bound to a unit, no sampler view is minted, and the
|
||||
// assertion is that the parameter reached the driver ANYWAY.
|
||||
//
|
||||
// RED ON THE PRE-P4a BEHAVIOUR: with set_texture_params addressed by resource, this twin's
|
||||
// parameter push no longer needs anything to be bound. A backend that reinstated the deferral -
|
||||
// resolving the params through a sampler view, or gating the push on a unit binding - leaves
|
||||
// the recording empty and this case fails, which is exactly the regression the scenario's
|
||||
// self-repair hides.
|
||||
//
|
||||
// WHAT IT DOES NOT COVER, AND THE NEXT READER MUST NOT OVER-TRUST IT (review N-9): the probe
|
||||
// drives SyncTextureParamsToBackend DIRECTLY, so the only deferral shape it can see is one
|
||||
// INSIDE that function. A regression that gates the CALL on a sampler view existing - in
|
||||
// SyncNeccessaryTextures, or in package E's per-unit walk - leaves this case green. That
|
||||
// caller-level half is a scenario's job and the scenario is package F's (G9's scenario half,
|
||||
// gates review R1); this is the backend-side probe R1 asked for and nothing wider.
|
||||
TEST(DirectGLESTextureSync, AnAttachmentOnlyTexturesParametersReachTheDriverWithNoSamplerView) {
|
||||
using namespace MobileGL;
|
||||
ScopedDirectGLESTextureBindings scoped; // fresh GLContext + registry + binding caches
|
||||
Vector<TexParamCall> params;
|
||||
ScopedTexParamRecording recording(params);
|
||||
|
||||
auto functions = MG_Backend::DirectGLES::g_GLESFuncs;
|
||||
functions.glBindTexture = TP_BindTexture;
|
||||
functions.glActiveTexture = TP_ActiveTexture;
|
||||
functions.glTexParameteri = TP_TexParameteri;
|
||||
functions.glTexParameterf = TP_TexParameterf;
|
||||
functions.glTexParameterfv = TP_TexParameterfv;
|
||||
functions.glPixelStorei = TP_PixelStorei;
|
||||
functions.glBindBuffer = TP_BindBuffer;
|
||||
MG_Backend::DirectGLES::SetGLESFuncsTable(functions);
|
||||
|
||||
GLuint name = 0;
|
||||
MG_Impl::GLImpl::GenTextures(1, &name);
|
||||
const auto texture = MakeComplete2DTexture(name, 8);
|
||||
ASSERT_NE(texture, nullptr);
|
||||
// ATTACHMENT-ONLY from here on: the unit that specified it is released, so nothing in this
|
||||
// test ever binds this texture for sampling and nothing mints a sampler view for it.
|
||||
MG_Impl::GLImpl::BindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
// The parameter that has to travel. Red -> Green is not any texture's default, so a driver
|
||||
// that never hears about it is distinguishable from one that does.
|
||||
texture->SetSwizzleParam(TextureSwizzleParam::Red, TextureSwizzleParam::Green);
|
||||
|
||||
auto& registry = MG_Backend::DirectGLES::TextureImpl::g_backendTextureObjects;
|
||||
auto& twin = registry.GetOrCreate(texture);
|
||||
if (!twin) {
|
||||
twin = MakeShared<MG_Backend::DirectGLES::TextureImpl::BackendTextureObject>();
|
||||
}
|
||||
ASSERT_NE(twin, nullptr);
|
||||
|
||||
const MG_Pipe::MGPipeHandle res = registry.HandleOf(texture.get());
|
||||
MG_Pipe::MGPipeHandle builtinSampler = MG_Pipe::kMGPipeNullHandle;
|
||||
if (MG_Backend::DirectGLES::TextureResourceSubsystemEnabled()) {
|
||||
// The handle arm reads the applier, so the applier is what this probe writes - which is
|
||||
// also what makes it a white-box test rather than a scenario: no client emitter exists
|
||||
// on this tree, and the point is Espryt's behaviour given a record, not the client's.
|
||||
ASSERT_FALSE(MG_Pipe::MGPipeHandleIsNull(res));
|
||||
MG_Pipe::MGPResourceDesc desc{};
|
||||
desc.Resource = res;
|
||||
desc.Target = static_cast<Uint8>(MG_Pipe::MGPipeResourceTarget::Tex2D);
|
||||
desc.StorageKind = static_cast<Uint8>(TextureStorageType::Mipmap);
|
||||
desc.InternalFormat = static_cast<Uint32>(TextureInternalFormat::RGBA8);
|
||||
desc.Width = 8;
|
||||
desc.Height = 8;
|
||||
desc.Depth = 1;
|
||||
desc.ArrayLayers = 1;
|
||||
desc.Levels = 1;
|
||||
EXPECT_TRUE(MG_Pipe::MGPipeApplyResourceCreate(desc));
|
||||
|
||||
// Every ITextureObject owns a sampler object, so the built-in sampler CSO is not
|
||||
// optional (a null one is Fatal{ProtocolCorruption} in the applier). It is a SAMPLER
|
||||
// CSO and not a sampler VIEW - the distinction this case exists for.
|
||||
auto samplerOwner = MakeShared<MG_State::GLState::SamplerObject>(0u);
|
||||
builtinSampler = MG_Pipe::MGPipeSlots().Acquire(MG_Pipe::MGPipeKind::SamplerCso,
|
||||
samplerOwner->GetLifetimeId());
|
||||
ASSERT_FALSE(MG_Pipe::MGPipeHandleIsNull(builtinSampler));
|
||||
MG_Pipe::MGPSamplerDesc samplerDesc{};
|
||||
samplerDesc.Cso = builtinSampler;
|
||||
SamplerParameters samplerParams{};
|
||||
MG_Pipe::MGPipeApplyCreateSamplerState(samplerDesc, &samplerParams);
|
||||
|
||||
MG_Pipe::MGPTextureParams pushed{};
|
||||
pushed.Res = res;
|
||||
pushed.BuiltinSampler = builtinSampler;
|
||||
pushed.MaxLevel = 0;
|
||||
pushed.Swizzle[0] = static_cast<Uint8>(TextureSwizzleParam::Green);
|
||||
pushed.Swizzle[1] = static_cast<Uint8>(TextureSwizzleParam::Green);
|
||||
pushed.Swizzle[2] = static_cast<Uint8>(TextureSwizzleParam::Blue);
|
||||
pushed.Swizzle[3] = static_cast<Uint8>(TextureSwizzleParam::Alpha);
|
||||
MG_Pipe::MGPipeApplySetTextureParams(pushed);
|
||||
|
||||
// THE OBSERVATION IS TAKEN WHILE THE TEXTURE IS STILL ATTACHMENT-ONLY: no sampler view
|
||||
// record exists, and no sampler-view twin does either. This is the assertion the public
|
||||
// scenario cannot make, because making it there would create the view.
|
||||
EXPECT_EQ(MG_Backend::DirectGLES::SamplerViewImpl::g_backendSamplerViews.FindByHandle(
|
||||
MG_Pipe::MGPipeSlots().FindByLifetimeId(MG_Pipe::MGPipeKind::SamplerViewCso,
|
||||
texture->GetLifetimeId())),
|
||||
nullptr)
|
||||
<< "a sampler view was minted for a texture nothing sampled";
|
||||
}
|
||||
|
||||
const SizeT before = params.size();
|
||||
twin->SyncTextureParamsToBackend(texture);
|
||||
|
||||
Bool sawSwizzleR = false;
|
||||
for (SizeT i = before; i < params.size(); ++i) {
|
||||
if (params[i].pname == GL_TEXTURE_SWIZZLE_R) {
|
||||
sawSwizzleR = true;
|
||||
EXPECT_EQ(params[i].value, static_cast<GLint>(GL_GREEN))
|
||||
<< "the swizzle reached the driver with the wrong value";
|
||||
}
|
||||
}
|
||||
EXPECT_TRUE(sawSwizzleR)
|
||||
<< "an attachment-only texture's GL_TEXTURE_SWIZZLE_R never reached the driver: the "
|
||||
"parameter push is gated on something being bound, which is the deferral G9's public "
|
||||
"scenario cannot observe";
|
||||
|
||||
if (!MG_Pipe::MGPipeHandleIsNull(builtinSampler)) {
|
||||
MG_Pipe::MGPipeApplyDeleteSamplerState(MG_Pipe::MGPHandleOnly{builtinSampler});
|
||||
MG_Pipe::MGPipeSlots().Free(MG_Pipe::MGPipeKind::SamplerCso, builtinSampler);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(DirectVulkanSanity, GraphicsSamplerFeedbackOnlyAliasesWritableOverlappingMip) {
|
||||
using MobileGL::MG_Backend::DirectVulkan::UniformManager;
|
||||
|
||||
|
||||
@@ -13,6 +13,10 @@ them wrong.
|
||||
|---|---|---|---|---|
|
||||
| 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 |
|
||||
| 2026-10-06 | `DirectGLES.ResourceSubsystemControl.Off.*`, `DirectGLES.ResourceSubsystemOff.*`, `DirectGLES.ObjectSubsystemControl.{Off,Refused,RefusedTexture}.*` | Test / integration-gpu | they cleared subsystem bits (masks 0x7f / 0x1ff / 0x9ff / 0x5ff) to run legacy arms; since W3b bits 0-13 are fixed on (ID-P13-3) | one named-refusal control per lane, `<lane>.RefusedAtStartup`: the same scenario and mask must go red with `Fatal{PipeSubsystemsFixedOn}` naming that mask (`Harness/RefusedMaskControl.cmake`); the `On` lanes now run 0x3fff |
|
||||
| 2026-10-06 | `SanityTest`: `DirectGLESSlotTable.{AnArmlessKnobCombinationStopsInsteadOfSkippingTheLane, TheArmlessCasesLeaveTheLogPathAndTheConfigAsTheyFoundThem, EglBringUpUnderTheArmlessKnobPairReturnsInsteadOfStopping}` | Test / unit | they pinned the `MOBILEGL_PIPE_LEGACY_MEMOS=0` + cleared-bit knob pair; the knob is gone and the mask is refused at startup | the RefusedAtStartup controls above |
|
||||
| 2026-10-06 | `SanityTest`: `DirectGLESTextureSync.{UnitMemoRefusesToDriveATwinFromAnotherTexture, AnAttachmentOnlyTexturesParametersReachTheDriverWithNoSamplerView}` | Test / unit | white-box probes that only ever ran at the P2 mask (0x7f): the borrowed-slot unit memo and the pre-handle texture-parameter push; both arms are gone with the legacy memos, and at the shipping mask the fixture cannot produce the applier records the handle arm reads | `DirectGLESTextureSync`'s handle-arm ABA sibling (P5e tx2) and `TextureParamsWithoutASamplerViewScenario` (G9, every lane) |
|
||||
|
||||
## Kept after investigation
|
||||
|
||||
|
||||
Reference in New Issue
Block a user