TLS: pGLContext and gPipeInputs become constant-initialized pointer slots.

This commit is contained in:
2026-10-10 04:14:09 -04:00
parent c9de510a1a
commit fd60f9a720
34 changed files with 505 additions and 401 deletions
+39 -39
View File
@@ -115,7 +115,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// addresses again - the cached pointers cannot go stale. Invalidation is
// exactly the pointer compare below.
using FbBindingSlot =
std::remove_reference_t<decltype(MG_Pipe::gPipeInputs.GetFramebufferBindingSlot(FramebufferTarget::Draw))>;
std::remove_reference_t<decltype(MG_Pipe::gPipeInputs->GetFramebufferBindingSlot(FramebufferTarget::Draw))>;
// P2 step e4. On the {slot, gen} arm this is an ORDINARY read of pushed state and the cache
// above is not consulted, which closes the P1 accessor bypass: the cached raw pointer ran
// the checked accessor once per context change and then handed out the pointee forever, so
@@ -132,10 +132,10 @@ namespace MobileGL::MG_Backend::DirectGLES {
// frontend getter still linear-scans, so the cache is exactly the code it was.
static inline FbBindingSlot& GetFramebufferBindingSlotChecked(FramebufferTarget target) {
if (EsprytSlotTablesEnabled()) {
return MG_Pipe::gPipeInputs.GetFramebufferBindingSlot(target);
return MG_Pipe::gPipeInputs->GetFramebufferBindingSlot(target);
}
// No legacy arm compiled: EsprytSlotTablesEnabled() is unconditionally true above.
return MG_Pipe::gPipeInputs.GetFramebufferBindingSlot(target);
return MG_Pipe::gPipeInputs->GetFramebufferBindingSlot(target);
}
// A4, TAKEN AT THE VERIFICATION ROUND, AND THE FOUR TEMPORARY LATCHES ARE GONE WITH IT.
@@ -181,7 +181,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
[[noreturn]] static void RefuseFramebufferBindingSlotRead() {
MG_Pipe::MGPipeInputPoisonFatalForVerb(MG_Pipe::MGPipeInputField::GetFramebufferBindingSlot,
MG_Pipe::gPipeInputs.CurrentVerb());
MG_Pipe::gPipeInputs->CurrentVerb());
}
static Bool IsDualSourceBlendFactor(BlendFactor v) {
@@ -1146,7 +1146,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
const SizeT packedStride = program->GetTransformFeedbackPackedStride();
const SizeT modelledVertices =
static_cast<SizeT>(MG_Pipe::gPipeInputs.GetTransformFeedbackCapturedVertices());
static_cast<SizeT>(MG_Pipe::gPipeInputs->GetTransformFeedbackCapturedVertices());
const SizeT vertices = std::min<SizeT>(modelledVertices, xfb.scatterCapacityVertices);
if (packedStride == 0 || vertices == 0) {
// The scatter path redirected the DRIVER's capture into the scratch buffer,
@@ -1669,7 +1669,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
}
auto* twin = ResolveVaoTwin(MG_Pipe::MGPipeApplier().BoundVertexElements);
if (twin == nullptr) return;
const auto& currentVAO = MG_Pipe::gPipeInputs.GetBoundVertexArray();
const auto& currentVAO = MG_Pipe::gPipeInputs->GetBoundVertexArray();
if (!currentVAO) return;
twin->SyncClientSideAttributesForDrawArrays(currentVAO, first, count);
return;
@@ -1766,7 +1766,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
for (Uint32 remaining = memo.pendingMask; remaining != 0; remaining &= remaining - 1) {
const Uint32 location = static_cast<Uint32>(std::countr_zero(remaining));
const auto& currentValue = MG_Pipe::gPipeInputs.GetCurrentVertexAttribute(location);
const auto& currentValue = MG_Pipe::gPipeInputs->GetCurrentVertexAttribute(location);
const auto typeInfo = MG_State::GLState::ClassifyVertexAttribType(program->GetAttribType(location));
switch (typeInfo.baseType) {
case MG_State::GLState::VertexAttribBaseType::Float:
@@ -1909,7 +1909,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// below rather than reading past the end.
const GLenum attribType =
location < link.attribTypes.size() ? link.attribTypes[location] : 0;
const auto& currentValue = MG_Pipe::gPipeInputs.GetCurrentVertexAttribute(location);
const auto& currentValue = MG_Pipe::gPipeInputs->GetCurrentVertexAttribute(location);
const auto typeInfo = MG_State::GLState::ClassifyVertexAttribType(attribType);
switch (typeInfo.baseType) {
case MG_State::GLState::VertexAttribBaseType::Float:
@@ -1986,7 +1986,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
static void CaptureUnitBindings(Int maxTouchedUnit, Vector<UnitBindingsSnapshot>& out) {
out.resize(static_cast<SizeT>(maxTouchedUnit + 1));
for (Int unit = 0; unit <= maxTouchedUnit; ++unit) {
auto& textureUnit = MG_Pipe::gPipeInputs.GetTextureUnitObject(unit);
auto& textureUnit = MG_Pipe::gPipeInputs->GetTextureUnitObject(unit);
auto& snapshot = out[static_cast<SizeT>(unit)];
const auto& slots = textureUnit.GetAllBindingSlots();
for (SizeT i = 0; i < slots.size(); ++i) {
@@ -1999,7 +1999,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
static Bool UnitBindingsUnchanged(Int maxTouchedUnit, const Vector<UnitBindingsSnapshot>& snapshots) {
if (snapshots.size() != static_cast<SizeT>(maxTouchedUnit + 1)) return false;
for (Int unit = 0; unit <= maxTouchedUnit; ++unit) {
auto& textureUnit = MG_Pipe::gPipeInputs.GetTextureUnitObject(unit);
auto& textureUnit = MG_Pipe::gPipeInputs->GetTextureUnitObject(unit);
const auto& snapshot = snapshots[static_cast<SizeT>(unit)];
const auto& slots = textureUnit.GetAllBindingSlots();
for (SizeT i = 0; i < slots.size(); ++i) {
@@ -2146,8 +2146,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
MG_Util::PipeStats::CountGate(MG_Util::PipeStats::Gate::EsprytUnitBindingsEpoch,
/*hit=*/false);
}
const Uint64 contextId = MG_Pipe::gPipeInputs.GetTextureContextId();
const Uint64 bindGeneration = MG_Pipe::gPipeInputs.GetTextureBindGeneration();
const Uint64 contextId = MG_Pipe::gPipeInputs->GetTextureContextId();
const Uint64 bindGeneration = MG_Pipe::gPipeInputs->GetTextureBindGeneration();
if (MG_Util::PipeStats::Enabled()) {
// Two accessor calls whichever way the shutter goes; only the unit WALK is
// gated, and that walk reads no GLContext accessor of its own.
@@ -2366,7 +2366,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
DrawTextureSyncKeys CaptureDrawTextureSyncKeys() {
DrawTextureSyncKeys keys;
keys.contextId = MG_Pipe::gPipeInputs.GetTextureContextId();
keys.contextId = MG_Pipe::gPipeInputs->GetTextureContextId();
// Units past the frontend's high-water mark have provably-empty slots.
//
// P4a e2 DELIBERATELY DOES NOT take this off the sampler-view window's Count, and
@@ -2379,8 +2379,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
// CLIENT as the count argument and says nothing about re-deriving it server-side.
// It is one accessor read per draw; the integrator can move it in one line once a
// tree exists where the two can be compared.
keys.maxTouchedUnit = MG_Pipe::gPipeInputs.GetMaxTouchedTextureUnit();
keys.samplingGeneration = MG_Pipe::gPipeInputs.GetSamplingResolutionGeneration();
keys.maxTouchedUnit = MG_Pipe::gPipeInputs->GetMaxTouchedTextureUnit();
keys.samplingGeneration = MG_Pipe::gPipeInputs->GetSamplingResolutionGeneration();
keys.unitBindingsEpoch = CurrentUnitBindingsEpoch(keys.maxTouchedUnit);
if (MG_Util::PipeStats::Enabled()) {
// The three reads above; CurrentUnitBindingsEpoch counts its own two when it
@@ -3455,7 +3455,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
#ifdef TRACY_ENABLE
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
#endif
Uint16 currentRenderStateVersion = MG_Pipe::gPipeInputs.GetRenderStateParametersVersion();
Uint16 currentRenderStateVersion = MG_Pipe::gPipeInputs->GetRenderStateParametersVersion();
const Bool forceFullPush = g_forceFullRenderStateResync;
g_forceFullRenderStateResync = false;
// The alpha discipline for widened colour attachments (see the header comment on
@@ -3483,7 +3483,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
MG_Util::PipeStats::AddCalls(MG_Util::PipeStats::CallClass::AccessorCalls, 3);
}
const auto& parameters = MG_Pipe::gPipeInputs.GetRenderStateParameters();
const auto& parameters = MG_Pipe::gPipeInputs->GetRenderStateParameters();
// The frontend has ONE version for the whole parameter block, so a per-draw blend
// toggle used to re-diff all ~40 pieces of state field by field on every draw
@@ -3512,7 +3512,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
!g_hasSyncedRenderState || std::memcmp(currentBytes + kBlendSpanEnd, syncedBytes + kBlendSpanEnd,
sizeof(RenderStateParameters) - kBlendSpanEnd) != 0;
IntVec4 backendViewport = MG_Pipe::gPipeInputs.GetViewport();
IntVec4 backendViewport = MG_Pipe::gPipeInputs->GetViewport();
if (backendViewport.z() <= 0 || backendViewport.w() <= 0) {
Int surfaceWidth = 0;
Int surfaceHeight = 0;
@@ -3595,7 +3595,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// never turns it on, so the driver has to be told to write raw. Without this a render
// into an sRGB colour buffer comes back encoded once too often (the shader's own
// decode on the next fetch then leaves the value one conversion short).
const Bool srgbWrites = MG_Pipe::gPipeInputs.IsCapabilityEnabled(CapabilityInput::FramebufferSrgb);
const Bool srgbWrites = MG_Pipe::gPipeInputs->IsCapabilityEnabled(CapabilityInput::FramebufferSrgb);
if (g_GLESCapabilities.SupportsSrgbWriteControl &&
(forceFullPush || srgbWrites != g_syncedSrgbFramebufferWrites)) {
srgbWrites ? g_GLESFuncs.glEnable(GL_FRAMEBUFFER_SRGB)
@@ -4505,11 +4505,11 @@ namespace MobileGL::MG_Backend::DirectGLES {
!twin->ImageUnitFormatsStillMatch() ||
(twin->GetPassthroughTessControlPatchVertices() >= 0 &&
(twin->GetPassthroughTessControlPatchVertices() !=
static_cast<Int>(MG_Pipe::gPipeInputs.GetPatchVertices()) ||
static_cast<Int>(MG_Pipe::gPipeInputs->GetPatchVertices()) ||
!BitwiseEqual(twin->GetPassthroughTessControlOuterLevel(),
MG_Pipe::gPipeInputs.GetPatchDefaultOuterLevel()) ||
MG_Pipe::gPipeInputs->GetPatchDefaultOuterLevel()) ||
!BitwiseEqual(twin->GetPassthroughTessControlInnerLevel(),
MG_Pipe::gPipeInputs.GetPatchDefaultInnerLevel())))) {
MG_Pipe::gPipeInputs->GetPatchDefaultInnerLevel())))) {
twin->SyncToBackendByHandle(cso);
}
g_currentDrawProgramHandle = cso;
@@ -5846,12 +5846,12 @@ namespace MobileGL::MG_Backend::DirectGLES {
// times; rasterizer discard means there are no fragments to gate at all, so replaying
// would be pure cost with nothing to show for it. Both fall back to a single pass with an
// open gate, i.e. to the pre-emulation behaviour, rather than to wrong data.
if (MG_Pipe::gPipeInputs.IsTransformFeedbackActive() ||
MG_Pipe::gPipeInputs.IsCapabilityEnabled(CapabilityInput::RasterizerDiscard)) {
if (MG_Pipe::gPipeInputs->IsTransformFeedbackActive() ||
MG_Pipe::gPipeInputs->IsCapabilityEnabled(CapabilityInput::RasterizerDiscard)) {
return 1;
}
const auto& parameters = MG_Pipe::gPipeInputs.GetRenderStateParameters();
const auto& parameters = MG_Pipe::gPipeInputs->GetRenderStateParameters();
Int surfaceWidth = 0;
Int surfaceHeight = 0;
if (!QueryCurrentSurfaceSize(surfaceWidth, surfaceHeight)) {
@@ -6228,7 +6228,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// color must go through glClearBufferfv, which GLES does not clamp.
GLbitfield remainingMask = mask;
if ((mask & GL_COLOR_BUFFER_BIT) != 0) {
const FloatVec4& cc = MG_Pipe::gPipeInputs.GetRenderStateParameters().ClearColor;
const FloatVec4& cc = MG_Pipe::gPipeInputs->GetRenderStateParameters().ClearColor;
const Bool outOfRange = cc.x() < 0.f || cc.x() > 1.f || cc.y() < 0.f || cc.y() > 1.f || cc.z() < 0.f ||
cc.z() > 1.f || cc.w() < 0.f || cc.w() > 1.f;
// A widened attachment's stored alpha has to end up 1.0, and glClear applies ONE
@@ -6456,13 +6456,13 @@ namespace MobileGL::MG_Backend::DirectGLES {
} // namespace
RestartSubstitutionKind ResolveRestartSubstitution(GLenum indexType) {
if (!MG_Pipe::gPipeInputs.IsCapabilityEnabled(CapabilityInput::PrimitiveRestart) ||
MG_Pipe::gPipeInputs.IsCapabilityEnabled(CapabilityInput::PrimitiveRestartFixedIndex)) {
if (!MG_Pipe::gPipeInputs->IsCapabilityEnabled(CapabilityInput::PrimitiveRestart) ||
MG_Pipe::gPipeInputs->IsCapabilityEnabled(CapabilityInput::PrimitiveRestartFixedIndex)) {
return RestartSubstitutionKind::None;
}
const Uint32 fixedMax = MG_Util::FixedRestartIndexForGLType(indexType);
if (fixedMax == 0) return RestartSubstitutionKind::None;
const Uint32 restartIndex = MG_Pipe::gPipeInputs.GetPrimitiveRestartIndex();
const Uint32 restartIndex = MG_Pipe::gPipeInputs->GetPrimitiveRestartIndex();
if (restartIndex == fixedMax) return RestartSubstitutionKind::None;
// Strictly greater, never truncated. GL 4.6 core 10.3.6 compares the fetched index
// zero-extended against the full 32-bit state, so an index this type cannot hold matches
@@ -6502,7 +6502,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
}
const SizeT sourceIndexSize = MG_Util::GetGLTypeSize(indexType);
const Uint32 fixedMax = MG_Util::FixedRestartIndexForGLType(indexType);
const Uint32 applicationRestartIndex = MG_Pipe::gPipeInputs.GetPrimitiveRestartIndex();
const Uint32 applicationRestartIndex = MG_Pipe::gPipeInputs->GetPrimitiveRestartIndex();
const Uint8* source = nullptr;
SizeT indexCount = 0;
@@ -7395,8 +7395,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
// restore. Drop the flag so it is not misattributed to the emulation's own work.
DrainBlitErrors();
if (MG_Pipe::gPipeInputs.IsTransformFeedbackActive() &&
!MG_Pipe::gPipeInputs.IsTransformFeedbackPaused() && g_GLESFuncs.glPauseTransformFeedback) {
if (MG_Pipe::gPipeInputs->IsTransformFeedbackActive() &&
!MG_Pipe::gPipeInputs->IsTransformFeedbackPaused() && g_GLESFuncs.glPauseTransformFeedback) {
g_GLESFuncs.glPauseTransformFeedback();
m_pausedTransformFeedback = true;
DrainBlitErrors();
@@ -8618,7 +8618,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
#ifdef TRACY_ENABLE
ZoneScopedNC(__func__, TRACY_ZONECOLOR_BACKEND);
#endif
auto unit = MG_Pipe::gPipeInputs.GetActiveTextureUnit();
auto unit = MG_Pipe::gPipeInputs->GetActiveTextureUnit();
auto textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
if (!TextureImpl::IsSupportedTextureTarget(textureTarget)) {
@@ -8695,7 +8695,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// The frontend's current PACK parameters, for readbacks the ES driver serves
// directly with the client's layout.
static PixelStoreImpl::PackState PackStateFromContext() {
const auto packParams = MG_Pipe::gPipeInputs.GetPixelStoreParameters(false);
const auto packParams = MG_Pipe::gPipeInputs->GetPixelStoreParameters(false);
return {static_cast<GLint>(packParams.Alignment), static_cast<GLint>(packParams.RowLength),
static_cast<GLint>(packParams.SkipRows), static_cast<GLint>(packParams.SkipPixels)};
}
@@ -8891,7 +8891,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
MG_Util::ConvertGLEnumToString(err).c_str(),
MG_Util::ConvertGLEnumToString(target).c_str(),
MG_Util::ConvertTextureInternalFormatToString(format).c_str());
MG_Pipe::gPipeInputs.RecordError(
MG_Pipe::gPipeInputs->RecordError(
ConvertGLESErrorToErrorCode(err),
MakeUnique<GenericErrorInfo>("DirectGLES", operation,
MG_Util::ConvertGLEnumToString(err)));
@@ -9407,7 +9407,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// Bind necessary FBO and texture
BindCurrentFBO(FramebufferTarget::Read);
Uint activeTextureUnit = MG_Pipe::gPipeInputs.GetActiveTextureUnit();
Uint activeTextureUnit = MG_Pipe::gPipeInputs->GetActiveTextureUnit();
TextureImpl::BackendTextureObject* dstBackendTexture = nullptr;
TextureInternalFormat mgInternalFormat{};
// P5c (hd, CONTRACT-P5C §3.4): with an active transport the destination resolves from
@@ -9516,7 +9516,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// Bind necessary FBO and texture
BindCurrentFBO(FramebufferTarget::Read);
auto activeTextureUnit = MG_Pipe::gPipeInputs.GetActiveTextureUnit();
auto activeTextureUnit = MG_Pipe::gPipeInputs->GetActiveTextureUnit();
TextureImpl::BackendTextureObject* dstBackendTexture = nullptr;
// P5c (hd, CONTRACT-P5C §3.4): see CopyTexImage2D - the record's Dst handle, never the
// client's unit binding slot or the client allocator (T2/T4).
@@ -10347,7 +10347,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
template <typename FillRow>
static Bool StoreReadbackRowsToClient(GLsizei width, GLsizei height, SizeT dstPixelBytes, void* pixels,
const char* what, FillRow&& fillRow) {
const auto packParams = MG_Pipe::gPipeInputs.GetPixelStoreParameters(false);
const auto packParams = MG_Pipe::gPipeInputs->GetPixelStoreParameters(false);
const SizeT rowPixels = static_cast<SizeT>(packParams.RowLength > 0 ? packParams.RowLength : width);
const SizeT dstRowStride = AlignPixelRow(rowPixels * dstPixelBytes, packParams.Alignment);
const SizeT dstOffset = static_cast<SizeT>(std::max(packParams.SkipRows, 0)) * dstRowStride +
@@ -11811,7 +11811,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// and legacy GL_RED reads) goes through the wide-format conversion, which picks a wide type
// the driver accepts for the current attachment. GL_PACK_SWAP_BYTES has no ES equivalent, so
// it always takes the conversion path (which swaps on the CPU).
const Bool packSwapBytes = MG_Pipe::gPipeInputs.GetPixelStoreParameters(false).SwapBytes;
const Bool packSwapBytes = MG_Pipe::gPipeInputs->GetPixelStoreParameters(false).SwapBytes;
// The read buffer is what glReadPixels reads, so the frontend's READ binding is exactly
// the right thing to ask here.
const Bool forceOpaqueAlpha = FramebufferImpl::IsAlphaWidenedFallbackReadAttachment();
+19 -19
View File
@@ -7198,9 +7198,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
// that needs no work costs the same nothing per draw that any other synced texture does.
void BackendTextureObject::StampViewSyncKeys(
const SharedPtr<MG_State::GLState::ITextureObject>& stateTextureObject) {
if (MG_Pipe::gPipeInputs.IsLive()) {
m_syncedShapeContextId = MG_Pipe::gPipeInputs.GetTextureContextId();
m_syncedShapeGeneration = MG_Pipe::gPipeInputs.GetSamplingResolutionGeneration();
if (MG_Pipe::gPipeInputs->IsLive()) {
m_syncedShapeContextId = MG_Pipe::gPipeInputs->GetTextureContextId();
m_syncedShapeGeneration = MG_Pipe::gPipeInputs->GetSamplingResolutionGeneration();
m_syncedShapeParamsVersion = stateTextureObject->GetTextureParamsVersion();
}
m_syncedContentVersion = stateTextureObject->GetContentVersion();
@@ -7349,9 +7349,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
// LEGACY ARM ONLY from P4a on: the memos it reads are the pre-handle ones and the
// record above answers the same question in one compare.
if (pushedStorage == nullptr)
if (m_isInitialized && m_syncedShapeContextId != 0 && MG_Pipe::gPipeInputs.IsLive() &&
m_syncedShapeContextId == MG_Pipe::gPipeInputs.GetTextureContextId() &&
m_syncedShapeGeneration == MG_Pipe::gPipeInputs.GetSamplingResolutionGeneration() &&
if (m_isInitialized && m_syncedShapeContextId != 0 && MG_Pipe::gPipeInputs->IsLive() &&
m_syncedShapeContextId == MG_Pipe::gPipeInputs->GetTextureContextId() &&
m_syncedShapeGeneration == MG_Pipe::gPipeInputs->GetSamplingResolutionGeneration() &&
m_syncedContentVersion == stateTextureObject->GetContentVersion() &&
m_syncedShapeParamsVersion == stateTextureObject->GetTextureParamsVersion() &&
stateTextureObject->GetStorageType() == TextureStorageType::Mipmap) {
@@ -7467,9 +7467,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
// The probe just proved "fully synced" from the real state, so the cheap
// gate may be (re)stamped here: the coarse generation only ever goes stale
// from OTHER textures' churn, and this draw re-validated this one.
if (MG_Pipe::gPipeInputs.IsLive()) {
m_syncedShapeContextId = MG_Pipe::gPipeInputs.GetTextureContextId();
m_syncedShapeGeneration = MG_Pipe::gPipeInputs.GetSamplingResolutionGeneration();
if (MG_Pipe::gPipeInputs->IsLive()) {
m_syncedShapeContextId = MG_Pipe::gPipeInputs->GetTextureContextId();
m_syncedShapeGeneration = MG_Pipe::gPipeInputs->GetSamplingResolutionGeneration();
m_syncedShapeParamsVersion = stateTextureObject->GetTextureParamsVersion();
}
return;
@@ -8764,9 +8764,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
// Same instant, so the cheap gate's keys describe exactly this synced state.
// Only Mipmap storage may arm it - the gate refuses other storage types anyway,
// but a stale trio must not linger on an object that later switches type.
if (MG_Pipe::gPipeInputs.IsLive() && stateTextureObject->GetStorageType() == TextureStorageType::Mipmap) {
m_syncedShapeContextId = MG_Pipe::gPipeInputs.GetTextureContextId();
m_syncedShapeGeneration = MG_Pipe::gPipeInputs.GetSamplingResolutionGeneration();
if (MG_Pipe::gPipeInputs->IsLive() && stateTextureObject->GetStorageType() == TextureStorageType::Mipmap) {
m_syncedShapeContextId = MG_Pipe::gPipeInputs->GetTextureContextId();
m_syncedShapeGeneration = MG_Pipe::gPipeInputs->GetSamplingResolutionGeneration();
m_syncedShapeParamsVersion = stateTextureObject->GetTextureParamsVersion();
} else {
m_syncedShapeContextId = 0;
@@ -9905,7 +9905,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
return record && record->ReadSurface.Kind != MG_Pipe::kMGPipeSurfaceKindNone &&
IsSnormFormat(static_cast<TextureInternalFormat>(record->ReadSurface.InternalFormat));
const auto& readFBO =
MG_Pipe::gPipeInputs.GetFramebufferBindingSlot(FramebufferTarget::Read).GetBoundObject();
MG_Pipe::gPipeInputs->GetFramebufferBindingSlot(FramebufferTarget::Read).GetBoundObject();
if (!readFBO) {
return false;
}
@@ -11980,19 +11980,19 @@ namespace MobileGL::MG_Backend::DirectGLES {
// patch size - so a program built for one value is stale for another. Recorded here
// and compared on the draw path (SyncCurrentProgram), the same shape as the
// storage-block and image-format signatures next to it.
const Uint patchVertices = MG_Pipe::gPipeInputs.IsLive()
? MG_Pipe::gPipeInputs.GetPatchVertices()
const Uint patchVertices = MG_Pipe::gPipeInputs->IsLive()
? MG_Pipe::gPipeInputs->GetPatchVertices()
: 3u;
m_passthroughTessControlPatchVertices = static_cast<Int>(patchVertices);
// PATCH_DEFAULT_{OUTER,INNER}_LEVEL are the same kind of dynamic state and are baked
// into the same stage (ES has no such state and no entry point to forward them to), so
// they are recorded and compared alongside the patch size - the two move together, as
// BuildPassthroughTessControlEssl's contract says.
m_passthroughTessControlOuterLevel = MG_Pipe::gPipeInputs.IsLive()
? MG_Pipe::gPipeInputs.GetPatchDefaultOuterLevel()
m_passthroughTessControlOuterLevel = MG_Pipe::gPipeInputs->IsLive()
? MG_Pipe::gPipeInputs->GetPatchDefaultOuterLevel()
: FloatVec4(1.0f, 1.0f, 1.0f, 1.0f);
m_passthroughTessControlInnerLevel = MG_Pipe::gPipeInputs.IsLive()
? MG_Pipe::gPipeInputs.GetPatchDefaultInnerLevel()
m_passthroughTessControlInnerLevel = MG_Pipe::gPipeInputs->IsLive()
? MG_Pipe::gPipeInputs->GetPatchDefaultInnerLevel()
: FloatVec2(1.0f, 1.0f);
if (tessEvalShaderIndex < 0 ||
+3 -3
View File
@@ -44,14 +44,14 @@ namespace MobileGL::MG_Backend::DirectGLES::MultiDrawImpl {
// verbatim is already "this batch restarts nowhere".
Uint32 RestartSentinelFor(GLenum type) {
if (ResolveRestartSubstitution(type) != RestartSubstitutionKind::None) {
return MG_Pipe::gPipeInputs.GetPrimitiveRestartIndex();
return MG_Pipe::gPipeInputs->GetPrimitiveRestartIndex();
}
return MG_Util::FixedRestartIndexForGLType(type);
}
Bool RestartActive() {
return MG_Pipe::gPipeInputs.IsCapabilityEnabled(CapabilityInput::PrimitiveRestart) ||
MG_Pipe::gPipeInputs.IsCapabilityEnabled(CapabilityInput::PrimitiveRestartFixedIndex);
return MG_Pipe::gPipeInputs->IsCapabilityEnabled(CapabilityInput::PrimitiveRestart) ||
MG_Pipe::gPipeInputs->IsCapabilityEnabled(CapabilityInput::PrimitiveRestartFixedIndex);
}
// Vertices per primitive for the modes whose sub-draws may be concatenated into a
+1 -1
View File
@@ -2338,7 +2338,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// Destination layout is computed from the client-side PACK parameters; only the actual pixel
// rows are written so skip regions of the destination stay untouched.
const auto packParams = MG_Pipe::gPipeInputs.GetPixelStoreParameters(false);
const auto packParams = MG_Pipe::gPipeInputs->GetPixelStoreParameters(false);
const SizeT rowPixels = static_cast<SizeT>(packParams.RowLength > 0 ? packParams.RowLength : width);
const SizeT dstRowStride = AlignReadbackRow(rowPixels * dstPixelBytes, packParams.Alignment);
const SizeT imageRows =
@@ -291,25 +291,25 @@ namespace MobileGL::MG_Backend::DirectVulkan {
void ClearBufferfi(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil) {
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::ClearBufferfi called with null VulkanRenderer");
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs.IsLive(), "DirectVulkan::ClearBufferfi called with null GL context");
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs->IsLive(), "DirectVulkan::ClearBufferfi called with null GL context");
pVulkanRenderer->ClearBufferfi(buffer, drawbuffer, depth, stencil);
}
void ClearBufferfv(GLenum buffer, GLint drawbuffer, const GLfloat* value) {
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::ClearBufferfv called with null VulkanRenderer");
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs.IsLive(), "DirectVulkan::ClearBufferfv called with null GL context");
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs->IsLive(), "DirectVulkan::ClearBufferfv called with null GL context");
pVulkanRenderer->ClearBufferfv(buffer, drawbuffer, value);
}
void ClearBufferuiv(GLenum buffer, GLint drawbuffer, const GLuint* value) {
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::ClearBufferuiv called with null VulkanRenderer");
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs.IsLive(), "DirectVulkan::ClearBufferuiv called with null GL context");
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs->IsLive(), "DirectVulkan::ClearBufferuiv called with null GL context");
pVulkanRenderer->ClearBufferuiv(buffer, drawbuffer, value);
}
void ClearBufferiv(GLenum buffer, GLint drawbuffer, const GLint* value) {
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::ClearBufferiv called with null VulkanRenderer");
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs.IsLive(), "DirectVulkan::ClearBufferiv called with null GL context");
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs->IsLive(), "DirectVulkan::ClearBufferiv called with null GL context");
pVulkanRenderer->ClearBufferiv(buffer, drawbuffer, value);
}
@@ -345,7 +345,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
void DrawElementsInstancedBaseVertexBaseInstance(GLenum mode, GLsizei count, GLenum type, const void* indices,
GLsizei instancecount, GLint basevertex, GLuint baseinstance) {
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::DrawElementsInstancedBaseVertexBaseInstance called with null VulkanRenderer");
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs.IsLive(), "DirectVulkan::DrawElementsInstancedBaseVertexBaseInstance called with null GL context");
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs->IsLive(), "DirectVulkan::DrawElementsInstancedBaseVertexBaseInstance called with null GL context");
DrawIndexedCmd payload{};
payload.mode = mode;
@@ -377,7 +377,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
void DrawArraysInstancedBaseInstance(GLenum mode, GLint first, GLsizei count, GLsizei instancecount,
GLuint baseinstance) {
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::DrawArraysInstancedBaseInstance called with null VulkanRenderer");
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs.IsLive(), "DirectVulkan::DrawArraysInstancedBaseInstance called with null GL context");
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs->IsLive(), "DirectVulkan::DrawArraysInstancedBaseInstance called with null GL context");
DrawCmd payload{};
payload.mode = mode;
@@ -397,13 +397,13 @@ namespace MobileGL::MG_Backend::DirectVulkan {
void CopyTexImage2D(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width,
GLsizei height, GLint border) {
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::CopyTexImage2D called with null VulkanRenderer");
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs.IsLive(), "DirectVulkan::CopyTexImage2D called with null GL context");
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs->IsLive(), "DirectVulkan::CopyTexImage2D called with null GL context");
pVulkanRenderer->CopyTexSubImage2D(target, level, 0, 0, x, y, width, height);
}
void CopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width,
GLsizei height) {
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::CopyTexSubImage2D called with null VulkanRenderer");
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs.IsLive(), "DirectVulkan::CopyTexSubImage2D called with null GL context");
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs->IsLive(), "DirectVulkan::CopyTexSubImage2D called with null GL context");
pVulkanRenderer->CopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height);
}
void CopyImageSubData(const CopyImageEndpoint& src,
@@ -412,20 +412,20 @@ namespace MobileGL::MG_Backend::DirectVulkan {
GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ,
GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth) {
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::CopyImageSubData called with null VulkanRenderer");
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs.IsLive(), "DirectVulkan::CopyImageSubData called with null GL context");
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs->IsLive(), "DirectVulkan::CopyImageSubData called with null GL context");
pVulkanRenderer->CopyImageSubData(src, srcTarget, srcLevel, srcX, srcY, srcZ,
dst, dstTarget, dstLevel, dstX, dstY, dstZ,
srcWidth, srcHeight, srcDepth);
}
void GenerateMipmap(GLenum target) {
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::GenerateMipmap called with null VulkanRenderer");
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs.IsLive(), "DirectVulkan::GenerateMipmap called with null GL context");
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs->IsLive(), "DirectVulkan::GenerateMipmap called with null GL context");
pVulkanRenderer->GenerateMipmap(target);
}
void DispatchCompute(GLuint numGroupsX, GLuint numGroupsY, GLuint numGroupsZ) {
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::DispatchCompute called with null VulkanRenderer");
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs.IsLive(), "DirectVulkan::DispatchCompute called with null GL context");
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs->IsLive(), "DirectVulkan::DispatchCompute called with null GL context");
pVulkanRenderer->DispatchCompute(numGroupsX, numGroupsY, numGroupsZ);
}
@@ -440,7 +440,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
void MemoryBarrier(GLbitfield barriers) {
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::MemoryBarrier called with null VulkanRenderer");
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs.IsLive(), "DirectVulkan::MemoryBarrier called with null GL context");
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs->IsLive(), "DirectVulkan::MemoryBarrier called with null GL context");
pVulkanRenderer->MemoryBarrier(barriers);
}
@@ -530,12 +530,12 @@ namespace MobileGL::MG_Backend::DirectVulkan {
}
void ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* pixels) {
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::ReadPixels called with null VulkanRenderer");
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs.IsLive(), "DirectVulkan::ReadPixels called with null GL context");
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs->IsLive(), "DirectVulkan::ReadPixels called with null GL context");
pVulkanRenderer->ReadPixels(x, y, width, height, format, type, pixels);
}
void Clear(GLbitfield mask) {
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::Clear called with null VulkanRenderer");
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs.IsLive(), "DirectVulkan::Clear called with null GL context");
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs->IsLive(), "DirectVulkan::Clear called with null GL context");
pVulkanRenderer->Clear(mask);
}
@@ -582,7 +582,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
void DrawArrays(GLenum mode, GLint first, GLsizei count) {
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::DrawArrays called with null VulkanRenderer");
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs.IsLive(), "DirectVulkan::DrawArrays called with null GL context");
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs->IsLive(), "DirectVulkan::DrawArrays called with null GL context");
if (mode == GL_LINE_LOOP) {
if (count < 2) {
@@ -607,7 +607,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
void DrawElements(GLenum mode, GLsizei count, GLenum type, const void* indices) {
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::DrawElements called with null VulkanRenderer");
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs.IsLive(), "DirectVulkan::DrawElements called with null GL context");
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs->IsLive(), "DirectVulkan::DrawElements called with null GL context");
if (mode == GL_LINE_LOOP) {
Vector<Uint32> closedIndices;
@@ -637,7 +637,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
void MultiDrawArrays(GLenum mode, const GLint* first, const GLsizei* count, GLsizei drawcount) {
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::MultiDrawArrays called with null VulkanRenderer");
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs.IsLive(), "DirectVulkan::MultiDrawArrays called with null GL context");
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs->IsLive(), "DirectVulkan::MultiDrawArrays called with null GL context");
if (drawcount <= 0) {
return;
}
@@ -743,7 +743,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
void DrawElementsBaseVertex(GLenum mode, GLsizei count, GLenum type, const GLvoid* indices, GLint basevertex) {
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::DrawElementsBaseVertex called with null VulkanRenderer");
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs.IsLive(), "DirectVulkan::DrawElementsBaseVertex called with null GL context");
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs->IsLive(), "DirectVulkan::DrawElementsBaseVertex called with null GL context");
if (mode == GL_LINE_LOOP) {
Vector<Uint32> closedIndices;
if (BuildClosedLineLoopIndices(count, type, indices, closedIndices)) {
@@ -767,14 +767,14 @@ namespace MobileGL::MG_Backend::DirectVulkan {
void MultiDrawElementsBaseVertex(GLenum mode, const GLsizei* count, GLenum type, const GLvoid* const* indices,
GLsizei drawcount, const GLint* basevertex) {
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::MultiDrawElementsBaseVertex called with null VulkanRenderer");
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs.IsLive(), "DirectVulkan::MultiDrawElementsBaseVertex called with null GL context");
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs->IsLive(), "DirectVulkan::MultiDrawElementsBaseVertex called with null GL context");
MultiDrawElementsImpl(mode, count, type, indices, drawcount, basevertex);
}
void BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1,
GLint dstY1, GLbitfield mask, GLenum filter) {
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::BlitFramebuffer called with null VulkanRenderer");
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs.IsLive(), "DirectVulkan::BlitFramebuffer called with null GL context");
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs->IsLive(), "DirectVulkan::BlitFramebuffer called with null GL context");
pVulkanRenderer->BlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
}
@@ -1018,8 +1018,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
#if MOBILEGL_BUILD_DISAGGREGATED
MG_Config::Transport == MG_Config::TransportMode::Monolith &&
#endif
MG_Pipe::gPipeInputs.IsLive()) {
primitives += MG_Pipe::gPipeInputs.GetTransformFeedbackPausedPrimitiveCounter() -
MG_Pipe::gPipeInputs->IsLive()) {
primitives += MG_Pipe::gPipeInputs->GetTransformFeedbackPausedPrimitiveCounter() -
query->pausedPrimitiveSnapshot;
}
*outNanoseconds = primitives;
@@ -1069,7 +1069,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
#if MOBILEGL_BUILD_DISAGGREGATED
MG_Config::Transport == MG_Config::TransportMode::Monolith &&
#endif
MG_Pipe::gPipeInputs.IsLive() ? MG_Pipe::gPipeInputs.GetTransformFeedbackPausedPrimitiveCounter() : 0;
MG_Pipe::gPipeInputs->IsLive() ? MG_Pipe::gPipeInputs->GetTransformFeedbackPausedPrimitiveCounter() : 0;
// Read AFTER StartXfbQueryCapture, which is where a failed reroute-pool creation
// disarms: the answer is then what this span will actually do for every draw.
query->pausedPrimitivesCountedByGpu = generated && pVulkanRenderer->ArePausedDrawsGpuCounted();
@@ -858,7 +858,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VkDescriptorBufferInfo& outBufferInfo) const {
outBufferInfo = {};
MOBILEGL_ASSERT(m_bufferManager != nullptr, "ResolveStorageBufferDescriptor: buffer manager is null");
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs.IsLive(), "ResolveStorageBufferDescriptor: GL context is null");
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs->IsLive(), "ResolveStorageBufferDescriptor: GL context is null");
MOBILEGL_ASSERT(binding < programObj.storageBlockIndexByBinding.size(),
"ResolveStorageBufferDescriptor: binding %u out of range", binding);
if ((binding >= programObj.storageBlockIndexByBinding.size() ||
@@ -1155,7 +1155,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
const void* outData = nullptr;
VkDeviceSize outSize = 0;
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs.IsLive(), "ResolveUniformBufferPayload: GL context is null");
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs->IsLive(), "ResolveUniformBufferPayload: GL context is null");
MOBILEGL_ASSERT(binding < programObj.bindingKinds.size(),
"ResolveUniformBufferPayload: binding %u out of range", binding);
MOBILEGL_ASSERT(programObj.bindingKinds[binding] == ProgramFactory::DescriptorBindingKind::UniformBufferDynamic,
@@ -55,7 +55,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
if (payload.colorEncoding != ClearColorEncoding::Float) return;
// With GL_FRAMEBUFFER_SRGB enabled GL performs the encoding itself, so the driver doing it
// is exactly right and there is nothing to undo.
if (MG_Pipe::gPipeInputs.IsCapabilityEnabled(MobileGL::CapabilityInput::FramebufferSrgb)) return;
if (MG_Pipe::gPipeInputs->IsCapabilityEnabled(MobileGL::CapabilityInput::FramebufferSrgb)) return;
if (ResolveSrgbAttachmentWriteFormat(destinationFormat, false) == destinationFormat) return;
// sRGB -> linear (GL 4.6 core 8.24), applied to the colour channels only: alpha is stored
@@ -526,12 +526,12 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// round trip), which is why the honest-but-lossy path was kept over widening every
// default-framebuffer Y-flip/pre-transform helper to floats. See the KNOWN INFIDELITY
// note in MG_IntegrationTest/Scenarios/AdvertisedLimitsScenario.cpp.
const FloatVec4& stored = MG_Pipe::gPipeInputs.GetViewportIndexed(index);
const FloatVec4& stored = MG_Pipe::gPipeInputs->GetViewportIndexed(index);
const IntVec4 viewportState(static_cast<Int>(std::lround(stored.x())),
static_cast<Int>(std::lround(stored.y())),
static_cast<Int>(std::lround(stored.z())),
static_cast<Int>(std::lround(stored.w())));
const FloatVec2& depthRange = MG_Pipe::gPipeInputs.GetDepthRangeIndexed(index);
const FloatVec2& depthRange = MG_Pipe::gPipeInputs->GetDepthRangeIndexed(index);
const IntVec2 logicalExtent = isDefaultFramebuffer
? ResolveDefaultFramebufferLogicalExtent(preTransform, framebufferExtent)
: framebufferExtent;
@@ -586,7 +586,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
}
static void ApplyBlendConstants(VkCommandBuffer commandBuffer) {
const FloatVec4& blendColor = MG_Pipe::gPipeInputs.GetBlendColor();
const FloatVec4& blendColor = MG_Pipe::gPipeInputs->GetBlendColor();
const float blendConstants[4] = {
blendColor.x(),
blendColor.y(),
@@ -619,8 +619,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
}
static void ApplyPolygonOffsetState(VkCommandBuffer commandBuffer) {
const Float constantFactor = MG_Pipe::gPipeInputs.GetPolygonOffsetUnits();
const Float slopeFactor = MG_Pipe::gPipeInputs.GetPolygonOffsetFactor();
const Float constantFactor = MG_Pipe::gPipeInputs->GetPolygonOffsetUnits();
const Float slopeFactor = MG_Pipe::gPipeInputs->GetPolygonOffsetFactor();
auto& shadow = *g_dynamicStateShadow;
if (shadow.depthBiasValid && shadow.depthBiasConstantFactor == constantFactor &&
shadow.depthBiasSlopeFactor == slopeFactor) {
@@ -633,7 +633,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
}
static void ApplyLineWidthState(VkCommandBuffer commandBuffer) {
Float lineWidth = MG_Pipe::gPipeInputs.GetLineWidth();
Float lineWidth = MG_Pipe::gPipeInputs->GetLineWidth();
#if MOBILEGL_BUILD_DISAGGREGATED
// P5c (hd, CONTRACT-P5C §3.7): with an active transport the dynamic parameters are the
// SERVER's own backend's - the client caps mirror is client memory (rule E). Monolith
@@ -677,7 +677,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// reader of the box asks this first. The resolution matches the viewport's (ComputeGLViewport
// resolves a never-written viewport to the current framebuffer's extent).
static Bool GLScissorTestClips(Uint32 index = 0) {
const RenderStateParameters& parameters = MG_Pipe::gPipeInputs.GetRenderStateParameters();
const RenderStateParameters& parameters = MG_Pipe::gPipeInputs->GetRenderStateParameters();
const Uint32 bit = 1u << index;
return (parameters.ScissorTestEnabledMask & bit) != 0 && (parameters.ScissorBoxWrittenMask & bit) != 0;
}
@@ -748,8 +748,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
}
static void ApplyStencilState(VkCommandBuffer commandBuffer) {
const StencilFaceState& frontStencil = MG_Pipe::gPipeInputs.GetStencilState(StencilFace::Front);
const StencilFaceState& backStencil = MG_Pipe::gPipeInputs.GetStencilState(StencilFace::Back);
const StencilFaceState& frontStencil = MG_Pipe::gPipeInputs->GetStencilState(StencilFace::Front);
const StencilFaceState& backStencil = MG_Pipe::gPipeInputs->GetStencilState(StencilFace::Back);
const Uint32 frontReference = static_cast<Uint32>(std::max(frontStencil.Ref, 0));
const Uint32 backReference = static_cast<Uint32>(std::max(backStencil.Ref, 0));
@@ -1116,7 +1116,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
}
static void RecordTextureCopyError(const char* func, ErrorCode code, const char* message) {
MG_Pipe::gPipeInputs.RecordError(code, MakeUnique<GenericErrorInfo>("DirectVulkan", func, message));
MG_Pipe::gPipeInputs->RecordError(code, MakeUnique<GenericErrorInfo>("DirectVulkan", func, message));
}
namespace {
@@ -1894,7 +1894,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// glReadPixels final conversion: GL_CLAMP_READ_COLOR defaults to GL_FIXED_ONLY,
// clamping fixed-point (normalized) buffers to [0,1] - visible for SNORM reads.
if (applyReadColorClamp && wideType == GL_FLOAT) {
const GLenum clampMode = MG_Pipe::gPipeInputs.GetClampReadColor();
const GLenum clampMode = MG_Pipe::gPipeInputs->GetClampReadColor();
const Bool clamp = clampMode == GL_TRUE ||
(clampMode == GL_FIXED_ONLY && !IsFloatingPointReadbackFormat(srcFormat));
if (clamp) {
@@ -2699,9 +2699,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
Uint32 VulkanRenderer::ResolveEffectiveSampleMask(VkSampleCountFlagBits rasterizationSamples) const {
constexpr Uint32 kFullCoverage = 0xffffffffu;
if (rasterizationSamples == VK_SAMPLE_COUNT_1_BIT) return kFullCoverage;
if (!MG_Pipe::gPipeInputs.IsCapabilityEnabled(CapabilityInput::Multisample)) return kFullCoverage;
if (!MG_Pipe::gPipeInputs.IsCapabilityEnabled(CapabilityInput::SampleMask)) return kFullCoverage;
return MG_Pipe::gPipeInputs.GetRenderStateParameters().SampleMaskValue;
if (!MG_Pipe::gPipeInputs->IsCapabilityEnabled(CapabilityInput::Multisample)) return kFullCoverage;
if (!MG_Pipe::gPipeInputs->IsCapabilityEnabled(CapabilityInput::SampleMask)) return kFullCoverage;
return MG_Pipe::gPipeInputs->GetRenderStateParameters().SampleMaskValue;
}
@@ -2716,7 +2716,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
//
// Only reached with no render-state CSO bound, and only in a build with no pre-handle
// arm to fall back to instead.
return MG_Pipe::MGPipeComputePipelineSubsetHash(MG_Pipe::gPipeInputs.GetRenderStateParameters());
return MG_Pipe::MGPipeComputePipelineSubsetHash(MG_Pipe::gPipeInputs->GetRenderStateParameters());
}
// A program that runs a geometry shader AND captures transform feedback. Both halves are
@@ -2748,7 +2748,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
if (!(aspects & DrawSetupAspect::IndexBuffer) || pIndexBufferView == nullptr) {
return false;
}
const RenderStateParameters& rsp = MG_Pipe::gPipeInputs.GetRenderStateParameters();
const RenderStateParameters& rsp = MG_Pipe::gPipeInputs->GetRenderStateParameters();
if (rsp.PrimitiveRestartFixedIndexEnabled) {
return true;
}
@@ -2803,7 +2803,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// the VALUE hash of that subset, never the version itself: the version is monotonic, so
// per-draw state flips (GL_BLEND toggles) would otherwise miss entries the memo holds.
// The version only guards recomputing the hash - unchanged version, unchanged bytes.
const Uint renderStateVersion = MG_Pipe::gPipeInputs.GetPipelineStateVersion();
const Uint renderStateVersion = MG_Pipe::gPipeInputs->GetPipelineStateVersion();
// P2 D12.1. Non-null means the client's render-state CSO handle is this draw's state
// key and the hash below is not computed at all; null means the pre-handle arm. The
// two arms' entries can never match each other: the handle arm stores hash 0 and a
@@ -2996,16 +2996,16 @@ namespace MobileGL::MG_Backend::DirectVulkan {
syntheticVertexInputState.pNext = vis.state.pNext;
pipelineVertexInputState = &syntheticVertexInputState;
}
auto cullFaceEnabled = MG_Pipe::gPipeInputs.IsCapabilityEnabled(CapabilityInput::CullFace);
auto depthTestEnabled = MG_Pipe::gPipeInputs.IsCapabilityEnabled(CapabilityInput::DepthTest);
auto cullFaceEnabled = MG_Pipe::gPipeInputs->IsCapabilityEnabled(CapabilityInput::CullFace);
auto depthTestEnabled = MG_Pipe::gPipeInputs->IsCapabilityEnabled(CapabilityInput::DepthTest);
auto polygonOffsetFillEnabled =
MG_Pipe::gPipeInputs.IsCapabilityEnabled(CapabilityInput::PolygonOffsetFill) &&
MG_Pipe::gPipeInputs->IsCapabilityEnabled(CapabilityInput::PolygonOffsetFill) &&
DrawModeUsesPolygonFill(mode);
auto rasterizerDiscardEnabled =
MG_Pipe::gPipeInputs.IsCapabilityEnabled(CapabilityInput::RasterizerDiscard);
MG_Pipe::gPipeInputs->IsCapabilityEnabled(CapabilityInput::RasterizerDiscard);
auto colorLogicOpEnabled =
MG_Pipe::gPipeInputs.IsCapabilityEnabled(CapabilityInput::ColorLogicOp) && m_logicOpFeatureEnabled;
auto stencilTestEnabled = MG_Pipe::gPipeInputs.IsCapabilityEnabled(CapabilityInput::StencilTest);
MG_Pipe::gPipeInputs->IsCapabilityEnabled(CapabilityInput::ColorLogicOp) && m_logicOpFeatureEnabled;
auto stencilTestEnabled = MG_Pipe::gPipeInputs->IsCapabilityEnabled(CapabilityInput::StencilTest);
// A framebuffer without a depth (stencil) attachment behaves as if the depth
// (stencil) test always passes and nothing is written - even when the bound
// image is a packed depth-stencil texture attached through only one half.
@@ -3016,10 +3016,10 @@ namespace MobileGL::MG_Backend::DirectVulkan {
if (fbo->Stencil.Kind == MG_Pipe::kMGPipeSurfaceKindNone) stencilTestEnabled = false;
}
}
const StencilFaceState& frontStencil = MG_Pipe::gPipeInputs.GetStencilState(StencilFace::Front);
const StencilFaceState& backStencil = MG_Pipe::gPipeInputs.GetStencilState(StencilFace::Back);
const StencilFaceState& frontStencil = MG_Pipe::gPipeInputs->GetStencilState(StencilFace::Front);
const StencilFaceState& backStencil = MG_Pipe::gPipeInputs->GetStencilState(StencilFace::Back);
const VkPolygonMode requestedPolygonMode =
MG_Util::ConvertPolygonModeToVkEnum(MG_Pipe::gPipeInputs.GetPolygonModeFront());
MG_Util::ConvertPolygonModeToVkEnum(MG_Pipe::gPipeInputs->GetPolygonModeFront());
// VK_POLYGON_MODE_LINE/_POINT require the fillModeNonSolid device feature; fall back to
// VK_POLYGON_MODE_FILL when the device lacks it.
const VkPolygonMode effectivePolygonMode =
@@ -3105,18 +3105,18 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// driver's own rate. Both halves move the render state's PIPELINE version, so a cached
// pipeline built at the old rate cannot be handed back for the new one.
.sampleShadingEnable = m_sampleRateShadingFeatureEnabled &&
MG_Pipe::gPipeInputs.IsCapabilityEnabled(CapabilityInput::SampleShading),
.minSampleShading = MG_Pipe::gPipeInputs.GetMinSampleShadingValue(),
MG_Pipe::gPipeInputs->IsCapabilityEnabled(CapabilityInput::SampleShading),
.minSampleShading = MG_Pipe::gPipeInputs->GetMinSampleShadingValue(),
// Word 1 keeps its all-ones initialiser: GL has no state for samples 32..63.
.sampleMask = {ResolveEffectiveSampleMask(renderPassEntry.sampleCount), 0xffffffffu},
.subpass = 0,
.topology = vkTopology,
.primitiveRestartEnable = primitiveRestartEnabled,
.patchControlPoints = static_cast<Uint32>(MG_Pipe::gPipeInputs.GetPatchVertices()),
.patchControlPoints = static_cast<Uint32>(MG_Pipe::gPipeInputs->GetPatchVertices()),
.viewportCount = ResolveDrawViewportCount(programObj.writesViewportIndexBuiltin),
.polygonMode = effectivePolygonMode,
.cullMode = cullFaceEnabled
? MG_Util::ConvertCullFaceModeToVkEnum(MG_Pipe::gPipeInputs.GetCullFaceMode(), invertClockwise)
? MG_Util::ConvertCullFaceModeToVkEnum(MG_Pipe::gPipeInputs->GetCullFaceMode(), invertClockwise)
: VK_CULL_MODE_NONE,
.frontFace = VK_FRONT_FACE_CLOCKWISE,
// Read the geometry stage off the program's own shader list rather than
@@ -3130,13 +3130,13 @@ namespace MobileGL::MG_Backend::DirectVulkan {
.provokingVertexMode = SelectProvokingVertexMode(
vkTopology, ProgramCapturesXfbFromGeometryStage(program)),
.depthTestEnable = depthTestEnabled,
.depthWriteEnable = depthTestEnabled && MG_Pipe::gPipeInputs.GetDepthMask(),
.depthWriteEnable = depthTestEnabled && MG_Pipe::gPipeInputs->GetDepthMask(),
.depthBiasEnable = polygonOffsetFillEnabled,
.rasterizerDiscardEnable = rasterizerDiscardEnabled,
.logicOpEnable = colorLogicOpEnabled,
.stencilTestEnable = stencilTestEnabled,
.depthCompareOp = MG_Util::ConvertDepthTestFuncToVkEnum(MG_Pipe::gPipeInputs.GetDepthFunc()),
.logicOp = MG_Util::ConvertLogicOperationToVkEnum(MG_Pipe::gPipeInputs.GetLogicOp()),
.depthCompareOp = MG_Util::ConvertDepthTestFuncToVkEnum(MG_Pipe::gPipeInputs->GetDepthFunc()),
.logicOp = MG_Util::ConvertLogicOperationToVkEnum(MG_Pipe::gPipeInputs->GetLogicOp()),
.frontStencilFailOp = MG_Util::ConvertStencilOperationToVkEnum(frontStencil.FailOp),
.frontStencilPassOp = MG_Util::ConvertStencilOperationToVkEnum(frontStencil.PassDepthPassOp),
.frontStencilDepthFailOp = MG_Util::ConvertStencilOperationToVkEnum(frontStencil.PassDepthFailOp),
@@ -3172,8 +3172,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// handed back after the application changed them.
if (programObj.needsPassthroughTessControl && programObj.passthroughTessControlEmulatable &&
vkTopology == VK_PRIMITIVE_TOPOLOGY_PATCH_LIST) {
const FloatVec4& defaultOuterLevel = MG_Pipe::gPipeInputs.GetPatchDefaultOuterLevel();
const FloatVec2& defaultInnerLevel = MG_Pipe::gPipeInputs.GetPatchDefaultInnerLevel();
const FloatVec4& defaultOuterLevel = MG_Pipe::gPipeInputs->GetPatchDefaultOuterLevel();
const FloatVec2& defaultInnerLevel = MG_Pipe::gPipeInputs->GetPatchDefaultInnerLevel();
payload.passthroughTessControlKey = ProgramFactory::ComputePassthroughTessControlKey(
payload.patchControlPoints, defaultOuterLevel, defaultInnerLevel,
programObj.passthroughPerVertexMembers);
@@ -3243,14 +3243,14 @@ namespace MobileGL::MG_Backend::DirectVulkan {
BlendFactor dstAlpha = BlendFactor::Zero;
BlendEquation colorEquation = BlendEquation::Add;
BlendEquation alphaEquation = BlendEquation::Add;
MG_Pipe::gPipeInputs.GetBlendFuncIndexed(i, srcRGB, dstRGB, srcAlpha, dstAlpha);
MG_Pipe::gPipeInputs.GetBlendEquationIndexed(i, colorEquation, alphaEquation);
const Bool blendEnabled = MG_Pipe::gPipeInputs.IsCapabilityEnabledIndexed(CapabilityInput::Blend, i);
MG_Pipe::gPipeInputs->GetBlendFuncIndexed(i, srcRGB, dstRGB, srcAlpha, dstAlpha);
MG_Pipe::gPipeInputs->GetBlendEquationIndexed(i, colorEquation, alphaEquation);
const Bool blendEnabled = MG_Pipe::gPipeInputs->IsCapabilityEnabledIndexed(CapabilityInput::Blend, i);
// Per-draw-buffer color write mask (glColorMaski). Divergent per-attachment masks require
// the independentBlend device feature; when it is absent, fall back to draw buffer 0's
// mask for every attachment (matching the non-indexed glColorMask broadcast).
const BoolVec4 bufferMask =
MG_Pipe::gPipeInputs.GetColorMaskIndexed(m_independentBlendFeatureEnabled ? i : 0);
MG_Pipe::gPipeInputs->GetColorMaskIndexed(m_independentBlendFeatureEnabled ? i : 0);
VkColorComponentFlags attachmentColorWriteMask = static_cast<VkColorComponentFlags>(
(bufferMask.r() ? VK_COLOR_COMPONENT_R_BIT : 0u) |
(bufferMask.g() ? VK_COLOR_COMPONENT_G_BIT : 0u) |
@@ -3404,7 +3404,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VkRect2D VulkanRenderer::ComputeGLScissorRect(Uint32 index, const IntVec2& extent,
VkSurfaceTransformFlagBitsKHR preTransform,
Bool isDefaultFbo) const {
const auto& parameters = MG_Pipe::gPipeInputs.GetRenderStateParameters();
const auto& parameters = MG_Pipe::gPipeInputs->GetRenderStateParameters();
if (!GLScissorTestClips(index)) {
VkRect2D full{};
full.offset = {0, 0};
@@ -3480,7 +3480,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// counter still would make Espryt skip re-syncing the blend state it just changed.
// The second-level DynamicTailKey compare below is therefore what actually absorbs a
// pipeline-only change, exactly as it did before P2: one key build, no vkCmd*.
const Uint paramsVersion = MG_Pipe::gPipeInputs.GetRenderStateParametersVersion();
const Uint paramsVersion = MG_Pipe::gPipeInputs->GetRenderStateParametersVersion();
if (shadow.dynamicTailValid && shadow.dynamicTailParamsVersion == paramsVersion &&
shadow.dynamicTailExtentX == extent.x() && shadow.dynamicTailExtentY == extent.y() &&
shadow.dynamicTailIsDefaultFbo == isDefaultFbo) {
@@ -3504,7 +3504,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// re-derive the value its shadow already holds.
DynamicStateShadow::DynamicTailKey key;
{
const RenderStateParameters& p = MG_Pipe::gPipeInputs.GetRenderStateParameters();
const RenderStateParameters& p = MG_Pipe::gPipeInputs->GetRenderStateParameters();
// Viewport 0 and its depth range: ApplyGLViewportState reads exactly those two
// (per-index state for indices > 0 is keyed separately, see multiViewportKey below).
key.viewport[0] = p.Viewports[0].x();
@@ -3763,9 +3763,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
if (!fbo) MagmaWireFatal("clear-framebuffer-record");
ClearAttachmentPayload payload{};
payload.mask = mask;
payload.color = MG_Pipe::gPipeInputs.GetClearColor();
payload.depth = MG_Pipe::gPipeInputs.GetClearDepth();
payload.stencil = MG_Pipe::gPipeInputs.GetClearStencil();
payload.color = MG_Pipe::gPipeInputs->GetClearColor();
payload.depth = MG_Pipe::gPipeInputs->GetClearDepth();
payload.stencil = MG_Pipe::gPipeInputs->GetClearStencil();
ClearWireFramebuffer(*fbo, payload);
return;
}
@@ -4846,7 +4846,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
const SharedPtr<MG_State::GLState::BufferObject> wireReplyHasNoPackBuffer;
const auto& pixelPackBufferObject =
wireReplyHasNoPackBuffer;
const auto packParams = MG_Pipe::gPipeInputs.GetPixelStoreParameters(false);
const auto packParams = MG_Pipe::gPipeInputs->GetPixelStoreParameters(false);
const SizeT rowPixels = static_cast<SizeT>(packParams.RowLength > 0 ? packParams.RowLength : width);
const SizeT packAlignment = packParams.Alignment > 0 ? static_cast<SizeT>(packParams.Alignment) : 1;
const SizeT dstRowStride = ((rowPixels * dstPixelBytes) + packAlignment - 1) / packAlignment * packAlignment;
@@ -4894,7 +4894,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// resets its counter state, because those bytes describe the previous owner's span.
Uint32 VulkanRenderer::CurrentXfbCounterSlot() {
constexpr Uint32 kNoSlot = static_cast<Uint32>(kXfbCounterObjectSlots);
const Uint64 identity = MG_Pipe::gPipeInputs.GetBoundTransformFeedbackLifetimeId();
const Uint64 identity = MG_Pipe::gPipeInputs->GetBoundTransformFeedbackLifetimeId();
MOBILEGL_ASSERT(identity != 0,
"transform feedback object reported the free-slot sentinel (0) as its identity - "
"every slot would then read as 'mine' without ever being claimed");
@@ -4943,14 +4943,14 @@ namespace MobileGL::MG_Backend::DirectVulkan {
}
Bool VulkanRenderer::BeginXfbCaptureForDraw(FrameContext::FrameData& frame) {
if (!m_transformFeedbackFeatureEnabled || !MG_Pipe::gPipeInputs.IsLive() ||
!MG_Pipe::gPipeInputs.IsTransformFeedbackActive()) {
if (!m_transformFeedbackFeatureEnabled || !MG_Pipe::gPipeInputs->IsLive() ||
!MG_Pipe::gPipeInputs->IsTransformFeedbackActive()) {
return false;
}
// A paused span captures nothing, and the counter buffers keep their values, so the
// next resumed draw appends exactly where the last captured one stopped - which is
// what pause/resume means (ARB_transform_feedback2).
if (MG_Pipe::gPipeInputs.IsTransformFeedbackPaused()) {
if (MG_Pipe::gPipeInputs->IsTransformFeedbackPaused()) {
return false;
}
// The bound pipeline's last pre-rasterization stage has to have been declared with Xfb
@@ -4993,7 +4993,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
}
const Uint32 counterSlot = CurrentXfbCounterSlot();
const Uint64 generation = MG_Pipe::gPipeInputs.GetTransformFeedbackGeneration();
const Uint64 generation = MG_Pipe::gPipeInputs->GetTransformFeedbackGeneration();
const Bool resume = m_xfbCountersValid[counterSlot] && m_xfbLastSeenGeneration[counterSlot] == generation;
m_xfbLastSeenGeneration[counterSlot] = generation;
@@ -5667,7 +5667,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
default: break;
}
if (mergeGranularity != 0) {
const RenderStateParameters& rsp = MG_Pipe::gPipeInputs.GetRenderStateParameters();
const RenderStateParameters& rsp = MG_Pipe::gPipeInputs->GetRenderStateParameters();
if (rsp.PrimitiveRestartEnabled || rsp.PrimitiveRestartFixedIndexEnabled) {
mergeGranularity = 0;
}
@@ -6428,8 +6428,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
if (!m_provokingVertexModePerPipeline) {
return VK_PROVOKING_VERTEX_MODE_LAST_VERTEX_EXT;
}
return (MG_Pipe::gPipeInputs.IsLive() &&
MG_Pipe::gPipeInputs.GetProvokingVertexMode() == ProvokingVertexMode::FirstVertex)
return (MG_Pipe::gPipeInputs->IsLive() &&
MG_Pipe::gPipeInputs->GetProvokingVertexMode() == ProvokingVertexMode::FirstVertex)
? VK_PROVOKING_VERTEX_MODE_FIRST_VERTEX_EXT
: VK_PROVOKING_VERTEX_MODE_LAST_VERTEX_EXT;
}
@@ -238,7 +238,7 @@
// Mip generation samples and writes the texture's own color space;
// GL_FRAMEBUFFER_SRGB only controls framebuffer operations.
if (!mipmap) destination.format = ResolveSrgbAttachmentWriteFormat(destination.format,
MG_Pipe::gPipeInputs.IsCapabilityEnabled(CapabilityInput::FramebufferSrgb));
MG_Pipe::gPipeInputs->IsCapabilityEnabled(CapabilityInput::FramebufferSrgb));
VkFormatProperties destinationProperties{};
vkGetPhysicalDeviceFormatProperties(m_physicalDevice.handle, destination.format, &destinationProperties);
if (!(destinationProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT)) return false;
@@ -248,7 +248,7 @@
!(memo.drawFramebuffer == st.BoundFramebuffer[SizeT(MG_Pipe::MGPipeFramebufferTarget::Draw)]) ||
memo.textureImageEpoch != m_textureManager->GetTextureImageEpoch() ||
memo.resourceEraseEpoch != m_textureManager->GetResourceEraseEpoch() ||
memo.framebufferSrgb != MG_Pipe::gPipeInputs.IsCapabilityEnabled(CapabilityInput::FramebufferSrgb))
memo.framebufferSrgb != MG_Pipe::gPipeInputs->IsCapabilityEnabled(CapabilityInput::FramebufferSrgb))
return false;
// The resources are good while the erase epoch holds. Each must still be what the walk
// left: in GENERAL and synced to its record's serial with nothing pending.
@@ -300,7 +300,7 @@
auto& passKey = scratch.passKey;
passKey.ResetKeepingCapacity();
passKey.isDefault = fbo.IsDefault;
passKey.framebufferSrgb = MG_Pipe::gPipeInputs.IsCapabilityEnabled(CapabilityInput::FramebufferSrgb);
passKey.framebufferSrgb = MG_Pipe::gPipeInputs->IsCapabilityEnabled(CapabilityInput::FramebufferSrgb);
if (fbo.IsDefault) {
passKey.swapchain = m_swapchainObject.GetHandle();
// A drawing pass on the default framebuffer is what makes the freshly acquired
@@ -326,7 +326,7 @@
{image.resource, image.storage, surface.Kind == MG_Pipe::kMGPipeSurfaceKindRenderbuffer});
if (aspect == VK_IMAGE_ASPECT_COLOR_BIT)
image.format = ResolveSrgbAttachmentWriteFormat(image.format,
MG_Pipe::gPipeInputs.IsCapabilityEnabled(CapabilityInput::FramebufferSrgb));
MG_Pipe::gPipeInputs->IsCapabilityEnabled(CapabilityInput::FramebufferSrgb));
const VkImageLayout layout = aspect == VK_IMAGE_ASPECT_COLOR_BIT ?
VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_GENERAL;
// Already in its attachment layout: its barrier (which closes any open pass) waits until
@@ -470,7 +470,7 @@
memo.drawFramebuffer = st.BoundFramebuffer[SizeT(MG_Pipe::MGPipeFramebufferTarget::Draw)];
memo.textureImageEpoch = m_textureManager->GetTextureImageEpoch();
memo.resourceEraseEpoch = m_textureManager->GetResourceEraseEpoch();
memo.framebufferSrgb = MG_Pipe::gPipeInputs.IsCapabilityEnabled(CapabilityInput::FramebufferSrgb);
memo.framebufferSrgb = MG_Pipe::gPipeInputs->IsCapabilityEnabled(CapabilityInput::FramebufferSrgb);
memo.passHash = m_wireDrawPass->hash;
memo.marks.assign(scratch.gpuWriteMarks.begin(), scratch.gpuWriteMarks.end());
}
@@ -522,7 +522,7 @@
ProgramFactory::CompileOptionFlags flags =
ProgramFactory::CompileOptionFlags(GetBaseTransformFlagsRaw(fbo->IsDefault));
if (m_transformFeedbackFeatureEnabled && MG_Pipe::gPipeInputs.IsTransformFeedbackActive() &&
if (m_transformFeedbackFeatureEnabled && MG_Pipe::gPipeInputs->IsTransformFeedbackActive() &&
program.GetTransformFeedbackVaryingCount() > 0)
flags |= ProgramFactory::CompileOptionBit::XfbCapture;
const auto* programObject = &m_programFactory->GetOrCreateProgram(program, flags);
@@ -727,7 +727,7 @@
return false;
}
indexType = width == 4 ? VK_INDEX_TYPE_UINT32 : width == 2 ? VK_INDEX_TYPE_UINT16 : VK_INDEX_TYPE_UINT8;
const auto& restart = MG_Pipe::gPipeInputs.GetRenderStateParameters();
const auto& restart = MG_Pipe::gPipeInputs->GetRenderStateParameters();
const Uint32 fixed = MG_Util::FixedRestartIndexForGLType(indices->indexType);
const Bool replaceRestart = restart.PrimitiveRestartEnabled && !restart.PrimitiveRestartFixedIndexEnabled &&
restart.PrimitiveRestartIndex < fixed;
@@ -864,7 +864,7 @@
VkFormat format = VK_FORMAT_UNDEFINED;
const void* data = nullptr;
VkDeviceSize size = 0;
if (!TryGetCurrentVertexAttributeUploadPayload(MG_Pipe::gPipeInputs.GetCurrentVertexAttribute(location),
if (!TryGetCurrentVertexAttributeUploadPayload(MG_Pipe::gPipeInputs->GetCurrentVertexAttribute(location),
programObj.vertexInputTypes[location], format, data, size)) {
MGL_WIRE_DECLINE_AT(CurrentAttributePayload, "a disabled array's current attribute value has no upload payload");
return false;
@@ -987,7 +987,7 @@
const Int32 attachment = fbo.DrawBuffers[i];
if (attachment < 0 || attachment >= static_cast<Int32>(MG_Pipe::kMGPipeMaxColorAttachments)) continue;
if (!fbo.IsDefault && fbo.Color[attachment].Kind == MG_Pipe::kMGPipeSurfaceKindNone) continue;
const auto mask = MG_Pipe::gPipeInputs.GetColorMaskIndexed(i);
const auto mask = MG_Pipe::gPipeInputs->GetColorMaskIndexed(i);
if (!mask.r() && !mask.g() && !mask.b() && !mask.a()) continue;
if (!mask.r() || !mask.g() || !mask.b() || !mask.a()) { all = false; break; }
if (i >= key.colorReferences.size() || key.colorReferences[i] == VK_ATTACHMENT_UNUSED ||
@@ -1007,7 +1007,7 @@
: nullptr;
VkImageAspectFlags depthStencilAspects = 0;
if (remaining & GL_DEPTH_BUFFER_BIT) {
if (!MG_Pipe::gPipeInputs.GetDepthMask()) {
if (!MG_Pipe::gPipeInputs->GetDepthMask()) {
handled |= GL_DEPTH_BUFFER_BIT; // nothing to write, as on the standalone path
} else if (!fbo.IsDefault && fbo.Depth.Kind == MG_Pipe::kMGPipeSurfaceKindNone) {
handled |= GL_DEPTH_BUFFER_BIT; // no depth surface: nothing to clear
@@ -1018,7 +1018,7 @@
}
}
if (remaining & GL_STENCIL_BUFFER_BIT) {
const Uint32 mask = MG_Pipe::gPipeInputs.GetStencilState(StencilFace::Front).WriteMask & 255u;
const Uint32 mask = MG_Pipe::gPipeInputs->GetStencilState(StencilFace::Front).WriteMask & 255u;
if (mask == 0) {
handled |= GL_STENCIL_BUFFER_BIT;
} else if (!fbo.IsDefault && fbo.Stencil.Kind == MG_Pipe::kMGPipeSurfaceKindNone) {
@@ -1047,7 +1047,7 @@
VkRect2D area{{0, 0}, {key.width, key.height}};
if (GLScissorTestClips()) {
const auto& box = MG_Pipe::gPipeInputs.GetScissorBox();
const auto& box = MG_Pipe::gPipeInputs->GetScissorBox();
const Int32 x0 = std::max(box.x(), 0), y0 = std::max(box.y(), 0);
const Int32 x1 = std::min(box.x() + box.z(), static_cast<Int32>(key.width));
const Int32 y1 = std::min(box.y() + box.w(), static_cast<Int32>(key.height));
@@ -370,7 +370,7 @@
const Uint64 passHash = active->hash;
VkRect2D area{{0, 0}, {key.width, key.height}};
if (GLScissorTestClips()) {
const auto& box = MG_Pipe::gPipeInputs.GetScissorBox();
const auto& box = MG_Pipe::gPipeInputs->GetScissorBox();
const Int32 x0 = std::max(box.x(), 0), y0 = std::max(box.y(), 0);
const Int32 x1 = std::min(box.x() + box.z(), static_cast<Int32>(key.width));
const Int32 y1 = std::min(box.y() + box.w(), static_cast<Int32>(key.height));
@@ -394,7 +394,7 @@
const Int32 slot = fbo.DrawBuffers[i];
if (slot < 0 || slot >= static_cast<Int32>(MG_Pipe::kMGPipeMaxColorAttachments)) continue;
if (fbo.Color[slot].Kind == MG_Pipe::kMGPipeSurfaceKindNone) continue;
const auto mask = MG_Pipe::gPipeInputs.GetColorMaskIndexed(i);
const auto mask = MG_Pipe::gPipeInputs->GetColorMaskIndexed(i);
if (!mask.r() && !mask.g() && !mask.b() && !mask.a()) continue;
if (!mask.r() || !mask.g() || !mask.b() || !mask.a() || payload.colorEncoding != ClearColorEncoding::Float) {
all = false;
@@ -429,7 +429,7 @@
(key.attachments[key.depthReference].aspect & aspect) != 0;
};
if (remaining & GL_DEPTH_BUFFER_BIT) {
if (!MG_Pipe::gPipeInputs.GetDepthMask() || fbo.Depth.Kind == MG_Pipe::kMGPipeSurfaceKindNone) {
if (!MG_Pipe::gPipeInputs->GetDepthMask() || fbo.Depth.Kind == MG_Pipe::kMGPipeSurfaceKindNone) {
handled |= GL_DEPTH_BUFFER_BIT;
} else if (depthStencilHeld(fbo.Depth, VK_IMAGE_ASPECT_DEPTH_BIT)) {
depthStencilAspects |= VK_IMAGE_ASPECT_DEPTH_BIT;
@@ -437,7 +437,7 @@
}
}
if (remaining & GL_STENCIL_BUFFER_BIT) {
const Uint32 mask = MG_Pipe::gPipeInputs.GetStencilState(StencilFace::Front).WriteMask & 255u;
const Uint32 mask = MG_Pipe::gPipeInputs->GetStencilState(StencilFace::Front).WriteMask & 255u;
if (mask == 0 || fbo.Stencil.Kind == MG_Pipe::kMGPipeSurfaceKindNone) {
handled |= GL_STENCIL_BUFFER_BIT;
} else if (mask == 255u && depthStencilHeld(fbo.Stencil, VK_IMAGE_ASPECT_STENCIL_BIT)) {
@@ -472,7 +472,7 @@
const Bool scissor = GLScissorTestClips();
const auto wholeImage = [&](const WireImage& image) {
if (!scissor) return true;
const auto& box = MG_Pipe::gPipeInputs.GetScissorBox();
const auto& box = MG_Pipe::gPipeInputs->GetScissorBox();
return box.x() <= 0 && box.y() <= 0 && box.x() + box.z() >= static_cast<Int32>(image.extent.width) &&
box.y() + box.w() >= static_cast<Int32>(image.extent.height);
};
@@ -498,7 +498,7 @@
if (slot < 0 || slot >= static_cast<Int32>(MG_Pipe::kMGPipeMaxColorAttachments)) continue;
const auto& surface = fbo.Color[slot];
if (surface.Kind == MG_Pipe::kMGPipeSurfaceKindNone) continue;
const auto mask = MG_Pipe::gPipeInputs.GetColorMaskIndexed(i);
const auto mask = MG_Pipe::gPipeInputs->GetColorMaskIndexed(i);
if (!mask.r() && !mask.g() && !mask.b() && !mask.a()) continue;
if (!mask.r() || !mask.g() || !mask.b() || !mask.a()) {
all = false;
@@ -538,13 +538,13 @@
return true;
};
if (remaining & GL_DEPTH_BUFFER_BIT) {
if (!MG_Pipe::gPipeInputs.GetDepthMask() || fbo.Depth.Kind == MG_Pipe::kMGPipeSurfaceKindNone)
if (!MG_Pipe::gPipeInputs->GetDepthMask() || fbo.Depth.Kind == MG_Pipe::kMGPipeSurfaceKindNone)
handled |= GL_DEPTH_BUFFER_BIT;
else if (depthStencil(fbo.Depth, VK_IMAGE_ASPECT_DEPTH_BIT))
handled |= GL_DEPTH_BUFFER_BIT;
}
if (remaining & GL_STENCIL_BUFFER_BIT) {
const Uint32 mask = MG_Pipe::gPipeInputs.GetStencilState(StencilFace::Front).WriteMask & 255u;
const Uint32 mask = MG_Pipe::gPipeInputs->GetStencilState(StencilFace::Front).WriteMask & 255u;
if (mask == 0 || fbo.Stencil.Kind == MG_Pipe::kMGPipeSurfaceKindNone)
handled |= GL_STENCIL_BUFFER_BIT;
else if (mask == 255u && depthStencil(fbo.Stencil, VK_IMAGE_ASPECT_STENCIL_BIT))
@@ -564,7 +564,7 @@
void VulkanRenderer::ClearWireFramebuffer(const MG_Pipe::MGPFramebufferState& fbo,
const ClearAttachmentPayload& clearRequest, GLint drawbuffer) {
if (MG_Pipe::gPipeInputs.IsCapabilityEnabled(CapabilityInput::RasterizerDiscard)) return;
if (MG_Pipe::gPipeInputs->IsCapabilityEnabled(CapabilityInput::RasterizerDiscard)) return;
// P15 S0: what the draw pass can take is cleared inside it (ClearWireFramebufferInPass,
// WireDraw.inc); only the buffers it hands back take the standalone pass below.
// P15: in the open pass when it is this framebuffer's (the in-pass clear) or merely holds
@@ -586,12 +586,12 @@
if (m_wireRetiredObjects.size() >= kRetiredObjectsBeforeSubmit && m_wirePreparationDepth == 0 &&
HasPendingRecordedWork())
(void)FlushPendingCommands();
if (MG_Pipe::gPipeInputs.IsCapabilityEnabled(CapabilityInput::RasterizerDiscard)) return;
if (MG_Pipe::gPipeInputs->IsCapabilityEnabled(CapabilityInput::RasterizerDiscard)) return;
const auto clearSurface = [&](const MG_Pipe::MGPSurface& surface, VkImageAspectFlags aspect,
Uint32 colorIndex) {
if (!fbo.IsDefault && surface.Kind == MG_Pipe::kMGPipeSurfaceKindNone) return;
if (aspect == VK_IMAGE_ASPECT_COLOR_BIT) {
const auto mask = MG_Pipe::gPipeInputs.GetColorMaskIndexed(colorIndex);
const auto mask = MG_Pipe::gPipeInputs->GetColorMaskIndexed(colorIndex);
if (!mask.r() && !mask.g() && !mask.b() && !mask.a()) return;
if (!mask.r() || !mask.g() || !mask.b() || !mask.a()) {
MGLOG_W_ONCE("DirectVulkan: glClear with a partial color mask is not supported");
@@ -599,10 +599,10 @@
}
}
if (aspect & VK_IMAGE_ASPECT_DEPTH_BIT) {
if (!MG_Pipe::gPipeInputs.GetDepthMask()) aspect &= ~VK_IMAGE_ASPECT_DEPTH_BIT;
if (!MG_Pipe::gPipeInputs->GetDepthMask()) aspect &= ~VK_IMAGE_ASPECT_DEPTH_BIT;
}
if (aspect & VK_IMAGE_ASPECT_STENCIL_BIT) {
const Uint32 mask = MG_Pipe::gPipeInputs.GetStencilState(StencilFace::Front).WriteMask & 255u;
const Uint32 mask = MG_Pipe::gPipeInputs->GetStencilState(StencilFace::Front).WriteMask & 255u;
if (!mask) aspect &= ~VK_IMAGE_ASPECT_STENCIL_BIT;
else if (mask != 255u) {
MGLOG_W_ONCE("DirectVulkan: glClear with a partial stencil write mask is not supported");
@@ -615,7 +615,7 @@
m_textureManager->FlushPendingUploads();
VkRect2D area{{0, 0}, image.extent};
if (GLScissorTestClips()) {
const auto& box = MG_Pipe::gPipeInputs.GetScissorBox();
const auto& box = MG_Pipe::gPipeInputs->GetScissorBox();
const Int32 x0 = std::max(box.x(), 0), y0 = std::max(box.y(), 0);
const Int32 x1 = std::min(box.x() + box.z(), static_cast<Int32>(image.extent.width));
const Int32 y1 = std::min(box.y() + box.w(), static_cast<Int32>(image.extent.height));
@@ -1264,7 +1264,7 @@
// The scissor test clips blit writes: intersect the destination rectangle with
// the scissor box and shrink the source proportionally.
if (GLScissorTestClips()) {
const IntVec4& scissor = MG_Pipe::gPipeInputs.GetScissorBox();
const IntVec4& scissor = MG_Pipe::gPipeInputs->GetScissorBox();
const auto clipAxis = [](GLint& d0, GLint& d1, GLint& s0, GLint& s1, GLint clipLo, GLint clipHi) -> Bool {
const Bool dstFlipped = d1 < d0;
GLint lo = dstFlipped ? d1 : d0;
+4 -4
View File
@@ -442,7 +442,7 @@ namespace MobileGL::MG_Pipe {
// D10: THE WIDER PREDICATE, and the narrow one was a latent crash rather than a missing
// optimisation. Gated on the REHEARSAL, this early-returned for the whole life of a
// spawn server - because that knob is off by default and nothing in the spawn shape
// turns it on - so the server's gPipeInputs.ContextIdentity() stayed nullptr forever.
// turns it on - so the server's gPipeInputs->ContextIdentity() stayed nullptr forever.
// DirectGLES' fb-slot memo compares that identity FIRST and with no generation, so a
// nullptr against its own nullptr initialiser reads as a CACHE HIT and hands out a slot
// that was never filled. `st` lands the named Fatal at that site; this is the gate that
@@ -596,12 +596,12 @@ namespace MobileGL::MG_Pipe {
// The seven carry no MGP_INPUT_CHECK at all (the declared exception argued at
// PipeInputs.h's F-class block), so freshness can never reach them and neither can the
// stamp's withdrawal. This is the only thing that puts them in `rsp`.
if (!gPipeInputs.ServerStampedVerb()) return;
if (!gPipeInputs->ServerStampedVerb()) return;
const auto ownership = MGPipeFieldOwnershipOf(field);
if (ownership == MGPipeFieldOwnership::kFatal)
MGPipeInputPoisonFatalForVerb(field, gPipeInputs.CurrentVerb());
MGPipeInputPoisonFatalForVerb(field, gPipeInputs->CurrentVerb());
if (ownership == MGPipeFieldOwnership::kBarrierPulled)
CountBarrierPull(field, gPipeInputs.CurrentVerb());
CountBarrierPull(field, gPipeInputs->CurrentVerb());
}
#endif // MOBILEGL_BUILD_RECORD_ARM && MOBILEGL_PIPE_POISON
+33 -7
View File
@@ -832,12 +832,18 @@ namespace MobileGL::MG_Pipe {
// have. Nothing about the block's contents changed; what changed is that the name resolves to
// the SESSION'S block on a thread that belongs to one.
//
// WHY A THREAD-LOCAL REFERENCE, AND NOT SOMETHING CLEVERER. gPipeInputs is spelled at ~380
// sites in MG_Backend/MG_Impl (they read `MG_Pipe::gPipeInputs.` directly since P13 retired the
// pull/push switch macro); a reference whose TARGET varies with the calling thread is the
// only shape that keeps all of them compiling and all of them correct. The cost is one TLS
// address computation per access site - not one per field, and with no guard: the reference is
// bound once per thread on its first use.
// WHY A THREAD-LOCAL SLOT, AND NOT SOMETHING CLEVERER. gPipeInputs is spelled at ~380
// sites in MG_Backend/MG_Impl (they read `MG_Pipe::gPipeInputs->` directly since P13 retired the
// pull/push switch macro); a name whose TARGET varies with the calling thread is the only
// shape that keeps all of them compiling and all of them correct.
//
// P15: A POINTER SLOT, NOT A REFERENCE. It used to be a `thread_local PipeInputs&` bound by a
// dynamic initializer, and a thread_local with a dynamic initializer is reached through its
// TLS init function, which tests two more thread_local guards on every read: three TLS
// address calls per access in a dlopen'ed library, at every API level. The slot below is
// constant-initialized and trivially destructible, so an access is one TLS address
// computation, a load and a null test; the binding itself is unchanged - once per thread, at
// its first use, through the same resolver.
//
// THE RESOLVER IS A HOOK because this header may not know about sessions (MG_Remote/Server
// owns them and includes THIS header, not the other way round). It is installed once by the
@@ -860,7 +866,27 @@ namespace MobileGL::MG_Pipe {
return gPipeInputsDefault;
}
inline thread_local PipeInputs& gPipeInputs = MGPipeServerInputsForCallingThread();
class PipeInputsThreadSlot {
public:
constexpr PipeInputsThreadSlot() = default;
PipeInputsThreadSlot(const PipeInputsThreadSlot&) = delete;
PipeInputsThreadSlot& operator=(const PipeInputsThreadSlot&) = delete;
// The block's address is `&*gPipeInputs`; the slot's own address names nothing.
void operator&() const = delete;
PipeInputs& Get() {
PipeInputs* bound = m_bound;
if (bound == nullptr) [[unlikely]] bound = m_bound = &MGPipeServerInputsForCallingThread();
return *bound;
}
PipeInputs* operator->() { return &Get(); }
PipeInputs& operator*() { return Get(); }
operator PipeInputs&() { return Get(); }
private:
PipeInputs* m_bound = nullptr;
};
inline constinit thread_local PipeInputsThreadSlot gPipeInputs;
#if MOBILEGL_BUILD_DISAGGREGATED
// ============================================================================
@@ -275,11 +275,11 @@ namespace MobileGL::MG_Record {
// Save the server-visible pack state, force neutral for the read, restore. Both go through
// the applier's own set_pixel_pack_state entry point (MGPipeApplySetPixelPackState writes
// gPipeInputs.m_pixelStore[0], which the backend's ReadPixels reads via
// gPipeInputs->m_pixelStore[0], which the backend's ReadPixels reads via
// gPipeInputs->GetPixelStoreParameters); the read is synchronous on this thread, so the window
// in which the pack state is neutral does not outlive the call.
const MG_Pipe::PixelStoreParameters savedPack =
MG_Pipe::gPipeInputs.GetPixelStoreParameters(/*isUnpack=*/false);
MG_Pipe::gPipeInputs->GetPixelStoreParameters(/*isUnpack=*/false);
// MG_Pipe owns the constant (MGPipeTypes.h): in a verify build the compare-at-read hook's
// oracle for the pack half inside this window is the same value, and a second hand-typed
// copy would drift without a build break.
@@ -566,7 +566,7 @@ namespace MobileGL::MG_Record {
// session child (false = latched, the verb declines); unarmed they still die.
Bool CheckUnitWindows(const MG_Pipe::MGPipeApplierState& st, const char* verb) {
if (st.SamplerViewCount == 0 && st.SamplerStateCount == 0) return true;
const Int maxTouched = MG_Pipe::gPipeInputs.GetMaxTouchedTextureUnit();
const Int maxTouched = MG_Pipe::gPipeInputs->GetMaxTouchedTextureUnit();
if (maxTouched < 0) return true;
const Uint32 required = static_cast<Uint32>(maxTouched) + 1u;
if (st.SamplerViewStart != 0 || st.SamplerViewCount < required) {
+3 -3
View File
@@ -671,7 +671,7 @@ namespace MobileGL::MG_Pipe {
}
void MGPipeVerifyReadHook(const PipeInputs& self, MGPipeInputField field, Uint index0, Uint index1) {
if (&self != &gPipeInputs || !g_verify.Enabled) return;
if (&self != &*gPipeInputs || !g_verify.Enabled) return;
const auto index = static_cast<SizeT>(field);
if (kMGPipeInputFieldSticky[index]) return;
// P13 W5: the monolith verb port applies records without a server stamp, and its readback
@@ -2256,7 +2256,7 @@ namespace MobileGL::MG_Pipe {
// static initialisation, by the translation unit that owns the forward.
const Bool kCompileEnvHookRegistered = [] {
MGPipeSetCompileEnvChangedHook([] {
if (gPipeInputs.IsLive()) gPipeInputs.InvalidateCompileEnv();
if (gPipeInputs->IsLive()) gPipeInputs->InvalidateCompileEnv();
});
return true;
}();
@@ -3134,7 +3134,7 @@ namespace MobileGL::MG_Pipe {
// GLContext's m_currentVertexAttributes hold exactly those, so the diff below is
// EMPTY on the one walk that must publish everything. The server's mirror is not
// default: MGPipeApplierReset() clears the CSO store and the residual block and
// leaves gPipeInputs.m_currentVertexAttribute holding the PREVIOUS context's
// leaves gPipeInputs->m_currentVertexAttribute holding the PREVIOUS context's
// defaults. So the InvalidateAll() a fresh context does to the set-hash
// suppressor would have been cancelled two lines later by this diff, and the one
// call P2 fully owns would publish nothing across a context change - exactly the
@@ -32,10 +32,10 @@ namespace MGITest {
out->peekAvailable = true;
out->roleSplitActive = MGP::MGPipeRoleSplitRehearsalActive();
MGP::PipeInputs& client = MGP::MGPipeClientInputs();
out->blocksDistinct = &client != &MGP::gPipeInputs;
out->blocksDistinct = &client != &*MGP::gPipeInputs;
out->clientSerial = client.FilledState().CurrentVerbSerial;
out->serverSerial = MGP::gPipeInputs.FilledState().CurrentVerbSerial;
out->serverIdentity = MGP::gPipeInputs.ContextIdentity();
out->serverSerial = MGP::gPipeInputs->FilledState().CurrentVerbSerial;
out->serverIdentity = MGP::gPipeInputs->ContextIdentity();
return true;
}
@@ -39,7 +39,7 @@ namespace MGITest {
// verb boundaries; the server block's moves only on a server verb stamp.
unsigned long long clientSerial = 0;
unsigned long long serverSerial = 0;
// gPipeInputs.ContextIdentity(): non-null once the server has stamped a verb boundary
// gPipeInputs->ContextIdentity(): non-null once the server has stamped a verb boundary
// under the rehearsal (CONTRACT-P5E §3.2's server-owned identity).
const void* serverIdentity = nullptr;
};
+34 -2
View File
@@ -1655,8 +1655,40 @@ namespace MobileGL::MG_State {
} // namespace GLState
// Leak-at-exit storage; see GlobalObjects.cpp.
thread_local SharedPtr<GLState::GLContext> pGLContext;
constinit thread_local CurrentGLContextSlot pGLContext;
// The reference that keeps the calling thread's pGLContext alive (Core.h). Registered for
// destruction on the thread's first assignment; at thread exit it clears the slot first, so
// whatever the context's destructor reaches sees no current context rather than one that is
// being destroyed.
struct CurrentGLContextOwner {
SharedPtr<GLState::GLContext> context;
~CurrentGLContextOwner() { pGLContext.m_context = nullptr; }
};
namespace {
thread_local CurrentGLContextOwner tCurrentGLContextOwner;
}
const SharedPtr<GLState::GLContext>& CurrentGLContextSlot::Shared() const {
return tCurrentGLContextOwner.context;
}
SharedPtr<GLState::GLContext> CurrentGLContextSlot::Take() {
m_context = nullptr;
return Move(tCurrentGLContextOwner.context);
}
CurrentGLContextSlot& CurrentGLContextSlot::operator=(SharedPtr<GLState::GLContext> context) {
auto& owner = tCurrentGLContextOwner;
m_context = context.get();
// The old context's last reference may run destructors that read pGLContext: the slot
// already names the new one, as it did when this was a SharedPtr assignment.
SharedPtr<GLState::GLContext> previous = Move(owner.context);
owner.context = Move(context);
return *this;
}
#if MOBILEGL_BUILD_DISAGGREGATED
thread_local Bool tThreadReleasedLostContext = false;
constinit thread_local Bool tThreadReleasedLostContext = false;
#endif
} // namespace MobileGL::MG_State
+48 -2
View File
@@ -782,7 +782,53 @@ namespace MobileGL {
// thread that never crossed an eglMakeCurrent keeps the process default MG_State::Init()
// gave it. NULL on a thread for which neither has happened, the same answer a process
// that never reached MG_State::Init() (the spawn server image) has always given.
extern thread_local SharedPtr<GLState::GLContext> pGLContext;
//
// P15: A PLAIN POINTER IN TLS, WITH THE OWNING REFERENCE KEPT BESIDE IT. Every GL entry
// point reads this, often several times. A thread_local with a destructor (the SharedPtr
// it used to be) is reached through its TLS init function, which tests a second
// thread_local guard first: two TLS address calls per read in a dlopen'ed library, at
// every API level. This object is trivially destructible and constant-initialized, so a
// read is one TLS address computation and a load. The SharedPtr that keeps the context
// alive lives in a separate thread_local in Core.cpp, written only by the assignments
// below (eglMakeCurrent and the release edge, MG_State::Init(), tests); its destructor
// clears this pointer before it drops the reference, so a thread's exit leaves no
// dangling pointer behind.
//
// Assignment, reset() and the SharedPtr conversions touch the owner and belong to the
// CALLING THREAD only: never assign through a reference to another thread's slot.
class CurrentGLContextSlot {
public:
constexpr CurrentGLContextSlot() = default;
CurrentGLContextSlot(const CurrentGLContextSlot&) = delete;
CurrentGLContextSlot& operator=(const CurrentGLContextSlot&) = delete;
GLState::GLContext* get() const { return m_context; }
GLState::GLContext* operator->() const { return m_context; }
GLState::GLContext& operator*() const { return *m_context; }
explicit operator bool() const { return m_context != nullptr; }
// The owning reference (cold: a second TLS read).
const SharedPtr<GLState::GLContext>& Shared() const;
operator const SharedPtr<GLState::GLContext>&() const& { return Shared(); }
// A move out of the slot is spelled Take(): std::move of the slot does not compile.
operator const SharedPtr<GLState::GLContext>&() const&& = delete;
SharedPtr<GLState::GLContext> Take();
CurrentGLContextSlot& operator=(SharedPtr<GLState::GLContext> context);
void reset() { *this = nullptr; }
friend Bool operator==(const CurrentGLContextSlot& slot, std::nullptr_t) {
return slot.m_context == nullptr;
}
friend Bool operator==(const CurrentGLContextSlot& slot, const SharedPtr<GLState::GLContext>& other) {
return slot.m_context == other.get();
}
private:
friend struct CurrentGLContextOwner;
GLState::GLContext* m_context = nullptr;
};
extern constinit thread_local CurrentGLContextSlot pGLContext;
#if MOBILEGL_BUILD_DISAGGREGATED
// True on a thread whose last EGL release let go of a context lost with an ended server
// session, until it makes a context current again. The release hands such a thread the
@@ -790,7 +836,7 @@ namespace MobileGL {
// TLS that state's last owner), so this is what keeps the thread answering as lost: an
// application that releases its context before asking glGetGraphicsResetStatus - Qt's
// render threads do - still learns that the context is gone and rebuilds it.
extern thread_local Bool tThreadReleasedLostContext;
extern constinit thread_local Bool tThreadReleasedLostContext;
#endif
// The fallback pGLContext starts at and an EGL release edge restores to: the process's
@@ -147,17 +147,17 @@ TEST(ContextEpochTest, SameXfbNameInTwoServedContextsDoesNotAliasThePausedSpan)
TEST(ContextEpochTest, ServerLivenessDoesNotFollowTheClientContextOrAVerbStamp) {
#if MOBILEGL_BUILD_DISAGGREGATED
DriverScope scope;
auto context = Move(MG_State::pGLContext);
auto context = MG_State::pGLContext.Take();
const Bool previous = MG_Pipe::MGPipeServerContextIsLive();
MG_Pipe::MGPipeServerSetContextLive(false);
MG_State::pGLContext = MakeUnique<MG_State::GLState::GLContext>();
EXPECT_FALSE(MG_Pipe::gPipeInputs.IsLive());
EXPECT_FALSE(MG_Pipe::gPipeInputs->IsLive());
MG_Pipe::MGPipeServerSetContextLive(true);
MG_State::pGLContext.reset();
EXPECT_TRUE(MG_Pipe::gPipeInputs.IsLive());
EXPECT_TRUE(MG_Pipe::gPipeInputs->IsLive());
MG_Pipe::MGPipeServerSetContextLive(false);
MG_Pipe::MGPipeServerStampVerbBoundary(MG_Pipe::MGPipeVerb::DrawArrays);
EXPECT_FALSE(MG_Pipe::gPipeInputs.IsLive()) << "a stale command resurrected a destroyed server context";
EXPECT_FALSE(MG_Pipe::gPipeInputs->IsLive()) << "a stale command resurrected a destroyed server context";
MG_Pipe::MGPipeServerClearVerbBoundary();
MG_State::pGLContext = Move(context);
MG_Pipe::MGPipeServerSetContextLive(previous);
+1 -1
View File
@@ -87,7 +87,7 @@ namespace {
m_blockKb = MG_Config::RecordArm.PersistentBlockKb;
m_adoptTier = MG_Config::Ipc.AdoptTier;
m_pipeStats = MG_Config::Features.PipeStats;
m_context = Move(MG_State::pGLContext);
m_context = MG_State::pGLContext.Take();
MG_State::pGLContext = MakeUnique<MG_State::GLState::GLContext>();
MG_Config::Transport = MG_Config::TransportMode::InProcess;
MG_Config::RecordArm.PersistentBlockKb = 64;
+76 -76
View File
@@ -87,7 +87,7 @@ namespace {
class FieldOwnershipTest : public ::testing::Test {
protected:
void SetUp() override {
m_previous = Move(MG_State::pGLContext);
m_previous = MG_State::pGLContext.Take();
MG_State::pGLContext = MakeUnique<GLContext>();
#if MOBILEGL_BUILD_DISAGGREGATED
MG_Config::Ipc.StrictErrors = false;
@@ -486,14 +486,14 @@ TEST_F(FieldOwnershipTest, TheResidualFillsSuppliedMemoReKeysOnEveryInputThatMov
TEST_F(FieldOwnershipTest, AServerStampMakesRecordSuppliedFieldsFreshAndWithdrawsTheRest) {
MGPipeServerStampVerbBoundary(MGPipeVerb::Clear);
EXPECT_TRUE(gPipeInputs.ServerStampedVerb());
EXPECT_EQ(gPipeInputs.CurrentVerb(), MGPipeVerb::Clear);
EXPECT_TRUE(gPipeInputs->ServerStampedVerb());
EXPECT_EQ(gPipeInputs->CurrentVerb(), MGPipeVerb::Clear);
const MGPipeFieldMask& mask = kMGPipeClassFieldMask[static_cast<SizeT>(
kMGPipeVerbClass[static_cast<SizeT>(MGPipeVerb::Clear)])];
SizeT stamped = 0;
for (SizeT i = 0; i < kMGPipeInputFieldCount; ++i) {
const auto field = static_cast<MGPipeInputField>(i);
const Bool fresh = MGPipeInputFieldIsFresh(gPipeInputs.FilledState(), field);
const Bool fresh = MGPipeInputFieldIsFresh(gPipeInputs->FilledState(), field);
// The stamp respects the verb's own may-read table for the client fill's reason: a
// field outside the class was never copied for this verb, so answering it would hand
// the server the previous verb's value.
@@ -512,7 +512,7 @@ TEST_F(FieldOwnershipTest, AServerStampMakesRecordSuppliedFieldsFreshAndWithdraw
// `answerable` out of kMGPipeFieldOwnership, so it can only catch a stamp that disagrees
// with the table - never a table that is wrong. These four say what the stamp must do for
// four fields whose class is an argument of this package rather than a lookup.
const MGPipeFilledState& filled = gPipeInputs.FilledState();
const MGPipeFilledState& filled = gPipeInputs->FilledState();
EXPECT_TRUE(MGPipeInputFieldIsFresh(filled, MGPipeInputField::GetClearColor)); // supplied
EXPECT_TRUE(MGPipeInputFieldIsFresh(filled, MGPipeInputField::GetRenderStateParameters));
EXPECT_FALSE(MGPipeInputFieldIsFresh(filled, MGPipeInputField::GetFramebufferBindingSlot)); // pulled
@@ -525,14 +525,14 @@ TEST_F(FieldOwnershipTest, AServerStampMakesRecordSuppliedFieldsFreshAndWithdraw
// changing. Before this, the seven most dangerous fields were exempt by construction.
TEST_F(FieldOwnershipTest, TheStickyExemptionIsCancelledByTheServerStamp) {
MGPipeValidateForVerb(MGPipeVerb::Clear); // the CLIENT fills and stamps all 63, sticky included
EXPECT_TRUE(MGPipeInputFieldIsFresh(gPipeInputs.FilledState(), MGPipeInputField::RecordError));
EXPECT_TRUE(MGPipeInputFieldIsFresh(gPipeInputs->FilledState(), MGPipeInputField::RecordError));
MGPipeServerStampVerbBoundary(MGPipeVerb::Clear);
for (SizeT i = 0; i < kMGPipeFieldOwnershipForwardCount; ++i) {
const MGPipeInputField field = kMGPipeFieldOwnershipForwardField[i];
// P5f fe retired the scalar forwards to server-owned answers. Their freshness
// comes from that ownership; the remaining client forwards lose the exemption.
const Bool serverOwned = MGPipeFieldOwnershipOf(field) == MGPipeFieldOwnership::kApplierDerived;
EXPECT_EQ(MGPipeInputFieldIsFresh(gPipeInputs.FilledState(), field), serverOwned)
EXPECT_EQ(MGPipeInputFieldIsFresh(gPipeInputs->FilledState(), field), serverOwned)
<< kMGPipeInputFieldNames[Index(field)] << " has the wrong server-stamp freshness";
}
}
@@ -543,21 +543,21 @@ TEST_F(FieldOwnershipTest, ARecordSuppliedFieldIsReadableAfterAServerStamp) {
ASSERT_EQ(MGPipeFieldOwnershipOf(MGPipeInputField::GetClearColor),
MGPipeFieldOwnership::kRecordSupplied);
MGPipeServerStampVerbBoundary(MGPipeVerb::Clear);
(void)gPipeInputs.GetClearColor();
(void)gPipeInputs.GetRenderStateParameters();
(void)gPipeInputs->GetClearColor();
(void)gPipeInputs->GetRenderStateParameters();
EXPECT_EQ(MGPipeResidualPullCount(), Uint64{0});
// The pixel store is in kReadback's class, not kClear's, so its readable half is exercised
// under the verb that actually reads it. And P5c rv's record-supplied rows join it there:
// GetActiveTextureUnit is exactly the read that used to count into `rsp`.
MGPipeServerStampVerbBoundary(MGPipeVerb::ReadPixels);
(void)gPipeInputs.GetPixelStoreParameters(false); // the half that has a carrier
(void)gPipeInputs.GetActiveTextureUnit(); // P5c rv: set_context_values carries it
(void)gPipeInputs.GetMaxTouchedTextureUnit();
(void)gPipeInputs->GetPixelStoreParameters(false); // the half that has a carrier
(void)gPipeInputs->GetActiveTextureUnit(); // P5c rv: set_context_values carries it
(void)gPipeInputs->GetMaxTouchedTextureUnit();
EXPECT_EQ(MGPipeResidualPullCount(), Uint64{0});
// And a shutter answers the applier's own Serial under a server stamp (APPLIER-DERIVED),
// not the client's residual-fill copy: still zero pulls, and the answer MOVES when the
// applier's texture state does.
(void)gPipeInputs.GetTextureBindGeneration();
(void)gPipeInputs->GetTextureBindGeneration();
EXPECT_EQ(MGPipeResidualPullCount(), Uint64{0});
}
@@ -578,16 +578,16 @@ TEST_F(FieldOwnershipTest, SetContextValuesLandsInPipeInputsAndTheShuttersAnswer
MGPipeApplySetContextValues(values);
MGPipeServerStampVerbBoundary(MGPipeVerb::ReadPixels);
EXPECT_EQ(gPipeInputs.GetActiveTextureUnit(), 5);
EXPECT_EQ(gPipeInputs.GetMaxTouchedTextureUnit(), 23);
EXPECT_EQ(gPipeInputs->GetActiveTextureUnit(), 5);
EXPECT_EQ(gPipeInputs->GetMaxTouchedTextureUnit(), 23);
MGPipeServerStampVerbBoundary(MGPipeVerb::DrawArrays);
EXPECT_EQ(gPipeInputs.GetTouchedBufferBindingPointCount(BufferTarget::Uniform), SizeT{7});
EXPECT_EQ(gPipeInputs.GetTouchedBufferBindingPointCount(BufferTarget::Vertex), SizeT{0});
EXPECT_TRUE(gPipeInputs.IsTransformFeedbackActive());
EXPECT_FALSE(gPipeInputs.IsTransformFeedbackPaused());
EXPECT_EQ(gPipeInputs.GetTransformFeedbackGeneration(), 0x1112131415161718ull);
EXPECT_EQ(gPipeInputs.GetBoundTransformFeedbackLifetimeId(), 0x2122232425262728ull);
EXPECT_EQ(gPipeInputs.GetTransformFeedbackCapturedVertices(), 0x3132333435363738ull);
EXPECT_EQ(gPipeInputs->GetTouchedBufferBindingPointCount(BufferTarget::Uniform), SizeT{7});
EXPECT_EQ(gPipeInputs->GetTouchedBufferBindingPointCount(BufferTarget::Vertex), SizeT{0});
EXPECT_TRUE(gPipeInputs->IsTransformFeedbackActive());
EXPECT_FALSE(gPipeInputs->IsTransformFeedbackPaused());
EXPECT_EQ(gPipeInputs->GetTransformFeedbackGeneration(), 0x1112131415161718ull);
EXPECT_EQ(gPipeInputs->GetBoundTransformFeedbackLifetimeId(), 0x2122232425262728ull);
EXPECT_EQ(gPipeInputs->GetTransformFeedbackCapturedVertices(), 0x3132333435363738ull);
// Eight record-supplied reads and not one residual pull.
EXPECT_EQ(MGPipeResidualPullCount(), Uint64{0});
@@ -595,12 +595,12 @@ TEST_F(FieldOwnershipTest, SetContextValuesLandsInPipeInputsAndTheShuttersAnswer
// SHUTTERS - the value matters only in that it MOVES when the server's texture state does
// and never walks backwards - so the pin is the identity with the applier's counters, not
// any particular number.
EXPECT_EQ(gPipeInputs.GetTextureBindGeneration(), MGPipeApplierTextureShutterSerial());
EXPECT_EQ(gPipeInputs.GetSamplingResolutionGeneration(), MGPipeApplierTextureShutterSerial());
EXPECT_EQ(gPipeInputs.GetTextureContextId(), MGPipeApplierContextSerial());
EXPECT_EQ(gPipeInputs->GetTextureBindGeneration(), MGPipeApplierTextureShutterSerial());
EXPECT_EQ(gPipeInputs->GetSamplingResolutionGeneration(), MGPipeApplierTextureShutterSerial());
EXPECT_EQ(gPipeInputs->GetTextureContextId(), MGPipeApplierContextSerial());
const Uint64 before = MGPipeApplierTextureShutterSerial();
MGPipeApplierNoteTextureStateMoved();
EXPECT_EQ(gPipeInputs.GetTextureBindGeneration(), before + 1)
EXPECT_EQ(gPipeInputs->GetTextureBindGeneration(), before + 1)
<< "a texture-state apply moved the serial but the shutter did not answer with it";
EXPECT_EQ(MGPipeResidualPullCount(), Uint64{0});
}
@@ -612,7 +612,7 @@ TEST_F(FieldOwnershipTest, ABarrierPulledReadAfterAServerStampIsCountedNotFatal)
const ChildResult r = RunInChild([] {
MGPipeServerStampVerbBoundary(MGPipeVerb::DrawArrays);
(void)gPipeInputs.GetBoundVertexArray();
(void)gPipeInputs->GetBoundVertexArray();
});
ASSERT_TRUE(DiedOfAbort(r)) << DescribeStatus(r) << "\n" << r.Log;
EXPECT_NE(r.Log.find("Fatal{UnmigratedPipeInput, \"GetBoundVertexArray@DrawArrays\"}"), std::string::npos) << r.Log;
@@ -629,7 +629,7 @@ TEST_F(FieldOwnershipTest, AStickyForwardIsCountedAsAResidualPull) {
const ChildResult r = RunInChild([] {
MGPipeServerStampVerbBoundary(MGPipeVerb::Clear);
(void)gPipeInputs.ValidateProgramName(1u);
(void)gPipeInputs->ValidateProgramName(1u);
});
ASSERT_TRUE(DiedOfAbort(r)) << DescribeStatus(r) << "\n" << r.Log;
EXPECT_NE(r.Log.find("Fatal{UnmigratedPipeInput, \"ValidateProgramName@Clear\"}"), std::string::npos) << r.Log;
@@ -644,11 +644,11 @@ TEST_F(FieldOwnershipTest, AStickyForwardIsCountedAsAResidualPull) {
// for - and what keeps build-split's monolith lanes behaving as a verify build's do.
TEST_F(FieldOwnershipTest, NothingIsCountedOutsideAServerStampedVerb) {
MGPipeValidateForVerb(MGPipeVerb::ReadPixels);
(void)gPipeInputs.ValidateProgramName(1u);
gPipeInputs.InvalidateCompileEnv();
(void)gPipeInputs.GetActiveTextureUnit();
(void)gPipeInputs->ValidateProgramName(1u);
gPipeInputs->InvalidateCompileEnv();
(void)gPipeInputs->GetActiveTextureUnit();
EXPECT_EQ(MGPipeResidualPullCount(), Uint64{0});
EXPECT_FALSE(gPipeInputs.ServerStampedVerb());
EXPECT_FALSE(gPipeInputs->ServerStampedVerb());
}
// THE APPLIER'S OWN CLEAR, reached directly rather than through the client's fill. In a spawned
@@ -658,13 +658,13 @@ TEST_F(FieldOwnershipTest, NothingIsCountedOutsideAServerStampedVerb) {
// InvalidateCompileEnv is reached from backend initialisation under - is gone for good.
TEST_F(FieldOwnershipTest, TheAppliersOwnClearDisarmsTheStampWithoutTheClientsFill) {
MGPipeServerStampVerbBoundary(MGPipeVerb::ReadPixels);
ASSERT_TRUE(gPipeInputs.ServerStampedVerb());
(void)gPipeInputs.GetActiveTextureUnit();
ASSERT_TRUE(gPipeInputs->ServerStampedVerb());
(void)gPipeInputs->GetActiveTextureUnit();
ASSERT_EQ(MGPipeResidualPullCount(), Uint64{0});
MGPipeServerClearVerbBoundary();
EXPECT_FALSE(gPipeInputs.ServerStampedVerb());
(void)gPipeInputs.ValidateProgramName(1u);
gPipeInputs.InvalidateCompileEnv();
EXPECT_FALSE(gPipeInputs->ServerStampedVerb());
(void)gPipeInputs->ValidateProgramName(1u);
gPipeInputs->InvalidateCompileEnv();
EXPECT_EQ(MGPipeResidualPullCount(), Uint64{0});
}
@@ -677,7 +677,7 @@ TEST_F(FieldOwnershipTest, ABarrierPulledFieldOutsideTheVerbsClassIsStillFatal)
#if MGTEST_HAVE_FORK
const ChildResult r = RunInChild([] {
MGPipeServerStampVerbBoundary(MGPipeVerb::Clear);
(void)gPipeInputs.GetProgramForDraw(); // retired accessor, outside kClear too
(void)gPipeInputs->GetProgramForDraw(); // retired accessor, outside kClear too
});
ASSERT_TRUE(DiedOfAbort(r)) << DescribeStatus(r) << "\n" << r.Log;
EXPECT_NE(r.Log.find("Fatal{UnmigratedPipeInput, \"GetProgramForDraw@Clear\"}"), std::string::npos)
@@ -693,7 +693,7 @@ TEST_F(FieldOwnershipTest, ResidualPullsReachThePublishedPerFrameCounter) {
PS::SetEnabledForTesting(true);
PS::ResetForTesting();
MGPipeServerStampVerbBoundary(MGPipeVerb::DrawArrays);
(void)gPipeInputs.GetCurrentVertexAttribute(0);
(void)gPipeInputs->GetCurrentVertexAttribute(0);
EXPECT_EQ(PS::FrameCalls(PS::CallClass::ResidualPulls), Uint64{0});
EXPECT_NE(PS::FormatWindowLine().find(" rsp=0"), std::string::npos) << PS::FormatWindowLine();
// Prove the reporting channel did not merely become a constant zero after retirement.
@@ -742,9 +742,9 @@ TEST_F(FieldOwnershipTest, SplitBindingPointCapacityNeverConsultsTheFrontend) {
RoleSplitArm guard;
guard.Arm(true);
MGPipeServerStampVerbBoundary(MGPipeVerb::DrawArrays);
EXPECT_EQ(gPipeInputs.GetBufferBindingPointCount(BufferTarget::Uniform), 84u);
EXPECT_EQ(gPipeInputs.GetBufferBindingPointCount(BufferTarget::TransformFeedback), 84u);
EXPECT_EQ(gPipeInputs.GetBufferBindingPointCount(BufferTarget::PixelPack), 0u);
EXPECT_EQ(gPipeInputs->GetBufferBindingPointCount(BufferTarget::Uniform), 84u);
EXPECT_EQ(gPipeInputs->GetBufferBindingPointCount(BufferTarget::TransformFeedback), 84u);
EXPECT_EQ(gPipeInputs->GetBufferBindingPointCount(BufferTarget::PixelPack), 0u);
EXPECT_EQ(MGPipeFieldOwnershipOf(MGPipeInputField::GetBufferBindingPointCount),
MGPipeFieldOwnership::kApplierDerived);
MGPipeServerClearVerbBoundary();
@@ -756,16 +756,16 @@ TEST_F(FieldOwnershipTest, SplitOpenSpansAreOwnedByTheApplierAndSurviveOtherBind
auto& state = MGPipeApplier();
state.StreamOutputSpans.clear();
MGPipeServerStampVerbBoundary(MGPipeVerb::DrawArrays);
EXPECT_FALSE(gPipeInputs.HasOpenTransformFeedbackSpan(0));
EXPECT_FALSE(gPipeInputs.HasOpenTransformFeedbackSpan(11));
EXPECT_FALSE(gPipeInputs->HasOpenTransformFeedbackSpan(0));
EXPECT_FALSE(gPipeInputs->HasOpenTransformFeedbackSpan(11));
state.StreamOutputSpans[11] = {};
state.StreamOutputSpans[12] = {};
state.BoundStreamOutputLifetimeId = 12;
EXPECT_TRUE(gPipeInputs.HasOpenTransformFeedbackSpan(11));
EXPECT_TRUE(gPipeInputs.HasOpenTransformFeedbackSpan(12));
EXPECT_TRUE(gPipeInputs->HasOpenTransformFeedbackSpan(11));
EXPECT_TRUE(gPipeInputs->HasOpenTransformFeedbackSpan(12));
state.StreamOutputSpans.erase(12);
EXPECT_TRUE(gPipeInputs.HasOpenTransformFeedbackSpan(11));
EXPECT_FALSE(gPipeInputs.HasOpenTransformFeedbackSpan(12));
EXPECT_TRUE(gPipeInputs->HasOpenTransformFeedbackSpan(11));
EXPECT_FALSE(gPipeInputs->HasOpenTransformFeedbackSpan(12));
EXPECT_EQ(MGPipeFieldOwnershipOf(MGPipeInputField::HasOpenTransformFeedbackSpan),
MGPipeFieldOwnership::kApplierDerived);
state.StreamOutputSpans.clear();
@@ -793,7 +793,7 @@ TEST_F(FieldOwnershipTest, SplitCaptureSnapshotSurvivesMakeCurrentUntilObjectRel
returning.IsTransformFeedbackActive = 1;
MGPipeApplySetContextValues(returning);
EXPECT_EQ(state.BoundStreamOutputLifetimeId, begin.LifetimeId);
EXPECT_TRUE(gPipeInputs.HasOpenTransformFeedbackSpan(begin.LifetimeId));
EXPECT_TRUE(gPipeInputs->HasOpenTransformFeedbackSpan(begin.LifetimeId));
const auto found = state.StreamOutputSpans.find(begin.LifetimeId);
EXPECT_NE(found, state.StreamOutputSpans.end());
if (found != state.StreamOutputSpans.end()) {
@@ -803,13 +803,13 @@ TEST_F(FieldOwnershipTest, SplitCaptureSnapshotSurvivesMakeCurrentUntilObjectRel
EXPECT_EQ(found->second.Targets[3].Size, 64u);
}
MGPipeApplierReleaseObjectRecords();
EXPECT_FALSE(gPipeInputs.HasOpenTransformFeedbackSpan(begin.LifetimeId));
EXPECT_FALSE(gPipeInputs->HasOpenTransformFeedbackSpan(begin.LifetimeId));
EXPECT_EQ(state.BoundStreamOutputLifetimeId, 0u);
}
TEST_F(FieldOwnershipTest, RoleSplitOffFoldsTheFillSideOntoTheSharedBlock) {
EXPECT_FALSE(MGPipeRoleSplitRehearsalActive());
EXPECT_EQ(&MGPipeClientInputs(), &gPipeInputs);
EXPECT_EQ(&MGPipeClientInputs(), &*gPipeInputs);
}
// The knob alone is not the arm: under monolith transport the two roles are one thread and
@@ -820,7 +820,7 @@ TEST_F(FieldOwnershipTest, RoleSplitUnderMonolithTransportIsStillOneBlock) {
MG_Config::Ipc.RoleSplitState = true;
MG_Config::Transport = MG_Config::TransportMode::Monolith;
EXPECT_FALSE(MGPipeRoleSplitRehearsalActive());
EXPECT_EQ(&MGPipeClientInputs(), &gPipeInputs);
EXPECT_EQ(&MGPipeClientInputs(), &*gPipeInputs);
}
// D1c/D10 (CONTRACT-P6 3.2, 3.4). THE PREDICATE THAT IS TRUE WITHOUT THE KNOB, and the reason
@@ -850,38 +850,38 @@ TEST_F(FieldOwnershipTest, MonolithKeepsOneBlockAndTheWiderPredicateSaysSo) {
MG_Config::Ipc.RoleSplitState = false;
MG_Config::Transport = MG_Config::TransportMode::Monolith;
EXPECT_FALSE(MGPipeBlocksAreDistinct());
EXPECT_EQ(&MGPipeClientInputs(), &gPipeInputs);
EXPECT_EQ(&MGPipeClientInputs(), &*gPipeInputs);
}
TEST_F(FieldOwnershipTest, RoleSplitGivesTheFillSideADistinctBlockTheStampNeverTouches) {
RoleSplitArm guard;
guard.Arm(true);
ASSERT_TRUE(MGPipeRoleSplitRehearsalActive());
ASSERT_NE(&MGPipeClientInputs(), &gPipeInputs);
ASSERT_NE(&MGPipeClientInputs(), &*gPipeInputs);
const Uint64 serverSerialBefore = gPipeInputs.FilledState().CurrentVerbSerial;
const Uint64 serverSerialBefore = gPipeInputs->FilledState().CurrentVerbSerial;
const Uint64 clientSerialBefore = MGPipeClientInputs().FilledState().CurrentVerbSerial;
MGPipeServerStampVerbBoundary(MGPipeVerb::DrawArrays);
EXPECT_EQ(gPipeInputs.FilledState().CurrentVerbSerial, serverSerialBefore + 1);
EXPECT_EQ(gPipeInputs->FilledState().CurrentVerbSerial, serverSerialBefore + 1);
EXPECT_EQ(MGPipeClientInputs().FilledState().CurrentVerbSerial, clientSerialBefore)
<< "the server's verb stamp reached into the client block";
EXPECT_TRUE(gPipeInputs.ServerStampedVerb());
EXPECT_TRUE(gPipeInputs->ServerStampedVerb());
EXPECT_FALSE(MGPipeClientInputs().ServerStampedVerb());
// The fill side's clear is the client block's own: it must not disarm the server's stamp,
// which is CONTRACT-P5E §3.2's "the server's stamp is server-private" as a fact rather
// than as a comment.
MGPipeClientClearVerbBoundary();
EXPECT_TRUE(gPipeInputs.ServerStampedVerb())
EXPECT_TRUE(gPipeInputs->ServerStampedVerb())
<< "the client-role clear withdrew the SERVER's stamp";
MGPipeServerClearVerbBoundary();
EXPECT_FALSE(gPipeInputs.ServerStampedVerb());
EXPECT_FALSE(gPipeInputs->ServerStampedVerb());
// And the server block's identity is server-owned under the rehearsal (§3.2's other half):
// the stamp set it, and it is non-null - a null identity reads as a hit against
// DirectGLES' zero-initialised fb-slot memo cache, which is the unnamed crash this line
// exists to preclude.
EXPECT_NE(gPipeInputs.ContextIdentity(), nullptr);
EXPECT_NE(gPipeInputs->ContextIdentity(), nullptr);
}
// The fill side's writers land in the client block alone. MGPipeLeaveVerb rather than
@@ -891,12 +891,12 @@ TEST_F(FieldOwnershipTest, RoleSplitGivesTheFillSideADistinctBlockTheStampNeverT
TEST_F(FieldOwnershipTest, TheClientVerbLeaveWritesTheClientBlockAlone) {
RoleSplitArm guard;
guard.Arm(true);
ASSERT_NE(&MGPipeClientInputs(), &gPipeInputs);
const Uint64 serverSerialBefore = gPipeInputs.FilledState().CurrentVerbSerial;
ASSERT_NE(&MGPipeClientInputs(), &*gPipeInputs);
const Uint64 serverSerialBefore = gPipeInputs->FilledState().CurrentVerbSerial;
const Uint64 clientSerialBefore = MGPipeClientInputs().FilledState().CurrentVerbSerial;
MGPipeLeaveVerb();
EXPECT_EQ(MGPipeClientInputs().FilledState().CurrentVerbSerial, clientSerialBefore + 1);
EXPECT_EQ(gPipeInputs.FilledState().CurrentVerbSerial, serverSerialBefore)
EXPECT_EQ(gPipeInputs->FilledState().CurrentVerbSerial, serverSerialBefore)
<< "a fill-side write reached the server block";
EXPECT_EQ(MGPipeClientInputs().CurrentVerb(), MGPipeVerb::kVerbCount);
}
@@ -909,9 +909,9 @@ TEST_F(FieldOwnershipTest, AClientFreshStampCannotReviveARetiredServerGetter) {
MGPipeServerStampVerbBoundary(MGPipeVerb::DrawArrays);
// Simulate a regressed writer stamping the old mirror as fresh. FATAL is a
// representation verdict, not a freshness verdict, and must still reject it.
auto& filled = const_cast<MGPipeFilledState&>(gPipeInputs.FilledState());
auto& filled = const_cast<MGPipeFilledState&>(gPipeInputs->FilledState());
filled.FilledGen[Index(MGPipeInputField::GetBoundVertexArray)] = filled.CurrentVerbSerial;
(void)gPipeInputs.GetBoundVertexArray();
(void)gPipeInputs->GetBoundVertexArray();
});
ASSERT_TRUE(DiedOfAbort(r)) << DescribeStatus(r) << "\n" << r.Log;
EXPECT_NE(r.Log.find("Fatal{UnmigratedPipeInput, \"GetBoundVertexArray@DrawArrays\"}"),
@@ -923,7 +923,7 @@ TEST_F(FieldOwnershipTest, StrictErrorsTurnsABarrierPulledReadIntoANamedAbort) {
const ChildResult r = RunInChild([] {
MG_Config::Ipc.StrictErrors = true;
MGPipeServerStampVerbBoundary(MGPipeVerb::DrawArrays);
(void)gPipeInputs.GetBoundVertexArray();
(void)gPipeInputs->GetBoundVertexArray();
});
ASSERT_TRUE(DiedOfAbort(r)) << DescribeStatus(r) << "\n" << r.Log;
EXPECT_NE(r.Log.find("Fatal{UnmigratedPipeInput, \"GetBoundVertexArray@DrawArrays\"}"), std::string::npos) << r.Log;
@@ -936,7 +936,7 @@ TEST_F(FieldOwnershipTest, TheSameReadWithoutStrictErrorsSurvivesAndIsCounted) {
const ChildResult r = RunInChild([] {
MG_Config::Ipc.StrictErrors = false;
MGPipeServerStampVerbBoundary(MGPipeVerb::DrawArrays);
(void)gPipeInputs.GetBoundVertexArray();
(void)gPipeInputs->GetBoundVertexArray();
});
ASSERT_TRUE(DiedOfAbort(r)) << DescribeStatus(r) << "\n" << r.Log;
EXPECT_NE(r.Log.find("Fatal{UnmigratedPipeInput, \"GetBoundVertexArray@DrawArrays\"}"), std::string::npos) << r.Log;
@@ -950,7 +950,7 @@ TEST_F(FieldOwnershipTest, AnEscalatedRecordsPullIsAdmittedAndSaysWhy) {
MG_Config::Ipc.StrictErrors = true;
MGPipeApplierSetCurrentRecordBarrieredByEscalation(true);
MGPipeServerStampVerbBoundary(MGPipeVerb::DrawArrays);
(void)gPipeInputs.GetBoundVertexArray();
(void)gPipeInputs->GetBoundVertexArray();
});
ASSERT_TRUE(DiedOfAbort(r)) << DescribeStatus(r) << "\n" << r.Log;
EXPECT_NE(r.Log.find("Fatal{UnmigratedPipeInput, \"GetBoundVertexArray@DrawArrays\"}"), std::string::npos) << r.Log;
@@ -963,7 +963,7 @@ TEST_F(FieldOwnershipTest, StrictErrorsReachTheStickyForwardsAndNameThemByField)
const ChildResult r = RunInChild([] {
MG_Config::Ipc.StrictErrors = true;
MGPipeServerStampVerbBoundary(MGPipeVerb::DrawArrays);
(void)gPipeInputs.ValidateProgramName(1u);
(void)gPipeInputs->ValidateProgramName(1u);
});
ASSERT_TRUE(DiedOfAbort(r)) << DescribeStatus(r) << "\n" << r.Log;
EXPECT_NE(r.Log.find("Fatal{UnmigratedPipeInput, \"ValidateProgramName@DrawArrays\"}"), std::string::npos) << r.Log;
@@ -976,7 +976,7 @@ TEST_F(FieldOwnershipTest, AnAdmittedBarrierPullIsLoudOnceAndNotFatal) {
const ChildResult r = RunInChild([] {
MG_Config::Ipc.StrictErrors = true;
MGPipeServerStampVerbBoundary(MGPipeVerb::ReadPixels);
(void)gPipeInputs.ValidateProgramName(1u);
(void)gPipeInputs->ValidateProgramName(1u);
});
ASSERT_TRUE(DiedOfAbort(r)) << DescribeStatus(r) << "\n" << r.Log;
EXPECT_NE(r.Log.find("Fatal{UnmigratedPipeInput, \"ValidateProgramName@ReadPixels\"}"), std::string::npos) << r.Log;
@@ -987,7 +987,7 @@ TEST_F(FieldOwnershipTest, AnAdmittedBarrierPullIsLoudOnceAndNotFatal) {
TEST_F(FieldOwnershipTest, AFatalClassReadAbortsEvenWithoutStrictErrors) {
const ChildResult r = RunInChild([] {
MGPipeServerStampVerbBoundary(MGPipeVerb::DrawArrays);
(void)gPipeInputs.GetTransformFeedbackPausedPrimitiveCounter();
(void)gPipeInputs->GetTransformFeedbackPausedPrimitiveCounter();
});
ASSERT_TRUE(DiedOfAbort(r)) << DescribeStatus(r) << "\n" << r.Log;
EXPECT_NE(r.Log.find("Fatal{UnmigratedPipeInput, "
@@ -1002,7 +1002,7 @@ TEST_F(FieldOwnershipTest, TheComputeProgramIsServedUnderADispatchStampFromP5bOn
const ChildResult r = RunInChild([] {
MGPipeServerStampVerbBoundary(MGPipeVerb::DispatchCompute);
(void)gPipeInputs.GetProgramForDispatch();
(void)gPipeInputs->GetProgramForDispatch();
});
ASSERT_TRUE(DiedOfAbort(r)) << DescribeStatus(r) << "\n" << r.Log;
EXPECT_NE(r.Log.find("Fatal{UnmigratedPipeInput, \"GetProgramForDispatch@DispatchCompute\"}"), std::string::npos) << r.Log;
@@ -1016,7 +1016,7 @@ TEST_F(FieldOwnershipTest, TheComputeProgramIsServedUnderADispatchStampFromP5bOn
TEST_F(FieldOwnershipTest, TheUnpackHalfOfThePixelStoreAbortsWhileThePackHalfDoesNot) {
const ChildResult fatal = RunInChild([] {
MGPipeServerStampVerbBoundary(MGPipeVerb::ReadPixels);
(void)gPipeInputs.GetPixelStoreParameters(true);
(void)gPipeInputs->GetPixelStoreParameters(true);
});
ASSERT_TRUE(DiedOfAbort(fatal)) << DescribeStatus(fatal) << "\n" << fatal.Log;
EXPECT_NE(fatal.Log.find("Fatal{UnmigratedPipeInput, \"GetPixelStoreParameters@ReadPixels\"}"),
@@ -1032,7 +1032,7 @@ TEST_F(FieldOwnershipTest, TheUnpackHalfOfThePixelStoreAbortsWhileThePackHalfDoe
const ChildResult ok = RunInChild([] {
MGPipeServerStampVerbBoundary(MGPipeVerb::ReadPixels);
(void)gPipeInputs.GetPixelStoreParameters(false);
(void)gPipeInputs->GetPixelStoreParameters(false);
if (MGPipeResidualPullCount() != 0) ::_exit(7);
});
ASSERT_TRUE(ExitedWith(ok, 0)) << DescribeStatus(ok) << "\n" << ok.Log;
@@ -1067,7 +1067,7 @@ TEST_F(FieldOwnershipTest, DualBlockMakesABarrierPulledReadANamedAbortWithoutStr
MG_Config::Ipc.RoleSplitState = true;
MG_Config::Ipc.StrictErrors = false;
MGPipeServerStampVerbBoundary(MGPipeVerb::DrawArrays);
(void)gPipeInputs.GetBoundVertexArray();
(void)gPipeInputs->GetBoundVertexArray();
});
ASSERT_TRUE(DiedOfAbort(r)) << DescribeStatus(r) << "\n" << r.Log;
EXPECT_NE(r.Log.find("Fatal{UnmigratedPipeInput, \"GetBoundVertexArray@DrawArrays\"}"), std::string::npos) << r.Log;
@@ -1081,7 +1081,7 @@ TEST_F(FieldOwnershipTest, DualBlockDoesNotArmUnderMonolithTransport) {
MG_Config::Ipc.RoleSplitState = true;
// A real monolith fill, not a manufactured server stamp on a monolith process.
MGPipeValidateForVerb(MGPipeVerb::DrawArrays);
(void)gPipeInputs.GetBoundVertexArray();
(void)gPipeInputs->GetBoundVertexArray();
if (MGPipeRoleSplitRehearsalActive() || MGPipeResidualPullCount() != 0) ::_exit(7);
});
ASSERT_TRUE(ExitedWith(r, 0)) << DescribeStatus(r) << "\n" << r.Log;
@@ -246,7 +246,7 @@ namespace {
kMGPipeSubsystemFramebuffer |
kMGPipeSubsystemTextureResources |
kMGPipeSubsystemSamplers;
m_previousContext = Move(MG_State::pGLContext);
m_previousContext = MG_State::pGLContext.Take();
MG_State::pGLContext = MakeUnique<GLContext>();
MGPipeFramebufferEmitterInstance().ResetForTest();
MGPipeTextureEmitterInstance().ResetForTest();
+35 -35
View File
@@ -73,7 +73,7 @@ namespace {
class PipeInputsTest : public ::testing::Test {
protected:
void SetUp() override {
m_previous = Move(MG_State::pGLContext);
m_previous = MG_State::pGLContext.Take();
MG_State::pGLContext = MakeUnique<GLContext>();
MGPipeSetPoisonOmission(nullptr, nullptr);
}
@@ -85,7 +85,7 @@ namespace {
};
#if MOBILEGL_PIPE_POISON
Bool Fresh(MGPipeInputField field) { return MGPipeInputFieldIsFresh(gPipeInputs.FilledState(), field); }
Bool Fresh(MGPipeInputField field) { return MGPipeInputFieldIsFresh(gPipeInputs->FilledState(), field); }
#endif
[[maybe_unused]] constexpr const char* kOmittedFatal ="Fatal{UnmigratedPipeInput, \"GetActiveTextureUnit@GenerateMipmap\"}";
@@ -147,7 +147,7 @@ TEST_F(PipeInputsTest, OmittingOneFieldForOneVerbLeavesExactlyThatFieldStale) {
EXPECT_TRUE(Fresh(MGPipeInputField::GetTextureUnitObject));
EXPECT_FALSE(Fresh(MGPipeInputField::GetActiveTextureUnit));
// The value was still copied: only the stamp is withheld.
EXPECT_EQ(gPipeInputs.CurrentVerb(), MGPipeVerb::GenerateMipmap);
EXPECT_EQ(gPipeInputs->CurrentVerb(), MGPipeVerb::GenerateMipmap);
// Exactly that field: a field is fresh iff its bit is in kTextureOp's mask and it is not
// the omitted one.
{
@@ -174,7 +174,7 @@ TEST_F(PipeInputsTest, OmittingOneFieldForOneVerbLeavesExactlyThatFieldStale) {
}
// Negative control B, layer 2 (G5): the read itself. The child fills GenerateMipmap with the
// omission and reads gPipeInputs.GetActiveTextureUnit(); the parent expects SIGABRT and the
// omission and reads gPipeInputs->GetActiveTextureUnit(); the parent expects SIGABRT and the
// exact Fatal line, and that nothing else was fatal.
TEST_F(PipeInputsTest, ReadingAnOmittedFieldAbortsNamingTheVerb) {
#if !MOBILEGL_PIPE_POISON
@@ -186,10 +186,10 @@ TEST_F(PipeInputsTest, ReadingAnOmittedFieldAbortsNamingTheVerb) {
const ChildResult r = RunInChild([] {
MGPipeSetPoisonOmission("GenerateMipmap", "GetActiveTextureUnit");
MGPipeValidateForVerb(MGPipeVerb::DrawArrays);
(void)gPipeInputs.GetRenderStateParameters(); // a filled field of the preceding draw: must not abort
(void)gPipeInputs->GetRenderStateParameters(); // a filled field of the preceding draw: must not abort
MGPipeValidateForVerb(MGPipeVerb::GenerateMipmap);
(void)gPipeInputs.GetTextureUnitObject(0); // the sibling field: filled, must not abort
(void)gPipeInputs.GetActiveTextureUnit(); // the omitted field: Fatal
(void)gPipeInputs->GetTextureUnitObject(0); // the sibling field: filled, must not abort
(void)gPipeInputs->GetActiveTextureUnit(); // the omitted field: Fatal
::_exit(3); // reached only if the poison failed
});
ASSERT_TRUE(DiedOfAbort(r)) << DescribeStatus(r) << "\n" << r.Log;
@@ -207,10 +207,10 @@ TEST_F(PipeInputsTest, ReadingAFilledFieldCompletes) {
ASSERT_FALSE(g_logPath.empty()) << "main() did not set MOBILEGL_LOG_FILE_PATH";
const ChildResult r = RunInChild([] {
MGPipeValidateForVerb(MGPipeVerb::DrawArrays);
(void)gPipeInputs.GetRenderStateParameters();
(void)gPipeInputs->GetRenderStateParameters();
MGPipeValidateForVerb(MGPipeVerb::GenerateMipmap);
(void)gPipeInputs.GetTextureUnitObject(0);
(void)gPipeInputs.GetActiveTextureUnit();
(void)gPipeInputs->GetTextureUnitObject(0);
(void)gPipeInputs->GetActiveTextureUnit();
});
ASSERT_TRUE(ExitedWith(r, 0)) << DescribeStatus(r) << "\n" << r.Log;
EXPECT_EQ(r.Log.find("Fatal{"), std::string::npos) << r.Log;
@@ -229,7 +229,7 @@ TEST_F(PipeInputsTest, ReadingBeforeAnyFillAbortsNamingNoVerb) {
GTEST_SKIP() << "no fork() on this platform";
#else
ASSERT_FALSE(g_logPath.empty()) << "main() did not set MOBILEGL_LOG_FILE_PATH";
if (gPipeInputs.FilledState().CurrentVerbSerial != 0) {
if (gPipeInputs->FilledState().CurrentVerbSerial != 0) {
GTEST_SKIP() << "another case already filled in this process; gtest_discover_tests runs each case alone";
}
MG_State::pGLContext->SetLineWidth(7.0f); // a live value the default storage (0) does not hold
@@ -237,7 +237,7 @@ TEST_F(PipeInputsTest, ReadingBeforeAnyFillAbortsNamingNoVerb) {
#if MOBILEGL_PIPE_VERIFY
MG_Config::Features.PipeVerify = true;
#endif
(void)gPipeInputs.GetLineWidth(); // Fatal{UnmigratedPipeInput, "GetLineWidth@<none>"}
(void)gPipeInputs->GetLineWidth(); // Fatal{UnmigratedPipeInput, "GetLineWidth@<none>"}
::_exit(3); // reached only if the pre-fill window read as fresh
});
ASSERT_TRUE(DiedOfAbort(r)) << DescribeStatus(r) << "\n" << r.Log;
@@ -261,8 +261,8 @@ TEST_F(PipeInputsTest, PoisonOmitKnobArmsTheOmission) {
const ChildResult r = RunInChild([] {
MG_Config::Features.PipePoisonOmit = kOmissionKnob;
MGPipeValidateForVerb(MGPipeVerb::GenerateMipmap);
(void)gPipeInputs.GetTextureUnitObject(0); // the sibling field: filled, must not abort
(void)gPipeInputs.GetActiveTextureUnit(); // the omitted field: Fatal
(void)gPipeInputs->GetTextureUnitObject(0); // the sibling field: filled, must not abort
(void)gPipeInputs->GetActiveTextureUnit(); // the omitted field: Fatal
::_exit(3);
});
ASSERT_TRUE(DiedOfAbort(r)) << DescribeStatus(r) << "\n" << r.Log;
@@ -301,9 +301,9 @@ TEST_F(PipeInputsTest, CorruptedSnapshotFieldIsNamedWithItsSerial) {
#if !MOBILEGL_PIPE_VERIFY
GTEST_SKIP() << "verify not compiled in (MOBILEGL_PIPE_VERIFY=OFF)";
#else
const Uint64 serialBefore = gPipeInputs.FilledState().CurrentVerbSerial;
const Uint64 serialBefore = gPipeInputs->FilledState().CurrentVerbSerial;
MGPipeValidateForVerb(MGPipeVerb::DrawArrays);
const Uint64 serial = gPipeInputs.FilledState().CurrentVerbSerial;
const Uint64 serial = gPipeInputs->FilledState().CurrentVerbSerial;
EXPECT_EQ(serial, serialBefore + 1);
const MGPipeFieldMask& mask = kMGPipeClassFieldMask[static_cast<SizeT>(MGPipeVerbClass::kDraw)];
@@ -318,7 +318,7 @@ TEST_F(PipeInputsTest, CorruptedSnapshotFieldIsNamedWithItsSerial) {
EXPECT_EQ(field, MGPipeInputField::GetRenderStateParameters);
EXPECT_STREQ(kMGPipeInputFieldNames[static_cast<SizeT>(field)], "GetRenderStateParameters");
// The serial the report would print is the fill's, and it stamped that field.
EXPECT_EQ(gPipeInputs.FilledState().FilledGen[static_cast<SizeT>(field)], serial);
EXPECT_EQ(gPipeInputs->FilledState().FilledGen[static_cast<SizeT>(field)], serial);
// A forwarded field has nothing to corrupt, and the corruption of a field outside the
// mask is not seen by a compare over that mask.
@@ -341,15 +341,15 @@ TEST_F(PipeInputsTest, MutatedFieldIsNamedAtRead) {
#else
ASSERT_FALSE(g_logPath.empty()) << "main() did not set MOBILEGL_LOG_FILE_PATH";
MGPipeValidateForVerb(MGPipeVerb::Clear); // the parent armed nothing: Features.PipeVerify is false here
const Uint64 serial = gPipeInputs.FilledState().CurrentVerbSerial + 1; // the child's DrawArrays fill
const Uint64 serial = gPipeInputs->FilledState().CurrentVerbSerial + 1; // the child's DrawArrays fill
const ChildResult r = RunInChild([] {
MG_Config::Features.PipeVerify = true;
MGPipeValidateForVerb(MGPipeVerb::DrawArrays);
const Float boundary = gPipeInputs.GetLineWidth(); // boundary == live: completes
(void)gPipeInputs.GetRenderStateParameters();
const Float boundary = gPipeInputs->GetLineWidth(); // boundary == live: completes
(void)gPipeInputs->GetRenderStateParameters();
MG_State::pGLContext->SetLineWidth(boundary + 1.0f);
if (MG_State::pGLContext->GetLineWidth() == boundary) ::_exit(7); // the mutation did not take
(void)gPipeInputs.GetLineWidth(); // the stored value is stale against the live one: Fatal
(void)gPipeInputs->GetLineWidth(); // the stored value is stale against the live one: Fatal
::_exit(3);
});
ASSERT_TRUE(DiedOfAbort(r)) << DescribeStatus(r) << "\n" << r.Log;
@@ -375,7 +375,7 @@ TEST_F(PipeInputsTest, VerifyCorruptKnobNamesTheFieldAtEntry) {
#else
ASSERT_FALSE(g_logPath.empty()) << "main() did not set MOBILEGL_LOG_FILE_PATH";
MGPipeValidateForVerb(MGPipeVerb::Clear);
const Uint64 serial = gPipeInputs.FilledState().CurrentVerbSerial + 1;
const Uint64 serial = gPipeInputs->FilledState().CurrentVerbSerial + 1;
const ChildResult r = RunInChild([] {
MG_Config::Features.PipeVerify = true;
MG_Config::Features.PipeVerifyCorrupt = "GetRenderStateParameters";
@@ -428,7 +428,7 @@ TEST_F(PipeInputsTest, VerifyFatalOffLogsTheDivergenceAndContinues) {
#else
ASSERT_FALSE(g_logPath.empty()) << "main() did not set MOBILEGL_LOG_FILE_PATH";
MGPipeValidateForVerb(MGPipeVerb::Clear);
const Uint64 serial = gPipeInputs.FilledState().CurrentVerbSerial + 1;
const Uint64 serial = gPipeInputs->FilledState().CurrentVerbSerial + 1;
const ChildResult r = RunInChild([] {
MG_Config::Features.PipeVerify = true;
MG_Config::Features.PipeVerifyFatal = false;
@@ -467,8 +467,8 @@ TEST_F(PipeInputsTest, EveryVerbFillsItsClassAndNothingElse) {
<< kMGPipeInputFieldNames[f] << " after " << kMGPipeVerbNames[v];
}
}
EXPECT_EQ(gPipeInputs.ContextIdentity(), static_cast<const void*>(MG_State::pGLContext.get()));
EXPECT_TRUE(gPipeInputs.IsLive());
EXPECT_EQ(gPipeInputs->ContextIdentity(), static_cast<const void*>(MG_State::pGLContext.get()));
EXPECT_TRUE(gPipeInputs->IsLive());
#endif
}
@@ -481,9 +481,9 @@ TEST_F(PipeInputsTest, EveryVerbFillsItsClassAndNothingElse) {
TEST_F(PipeInputsTest, AFrontendMutationInsideAVerbRefreshesThePushedField) {
auto& ctx = *MG_State::pGLContext;
MGPipeValidateForVerb(MGPipeVerb::DrawArrays);
ASSERT_EQ(gPipeInputs.GetSamplingResolutionGeneration(), ctx.GetSamplingResolutionGeneration());
ASSERT_EQ(gPipeInputs.GetTextureBindGeneration(), ctx.GetTextureBindGeneration());
ASSERT_EQ(gPipeInputs.GetMaxTouchedTextureUnit(), ctx.GetMaxTouchedTextureUnit());
ASSERT_EQ(gPipeInputs->GetSamplingResolutionGeneration(), ctx.GetSamplingResolutionGeneration());
ASSERT_EQ(gPipeInputs->GetTextureBindGeneration(), ctx.GetTextureBindGeneration());
ASSERT_EQ(gPipeInputs->GetMaxTouchedTextureUnit(), ctx.GetMaxTouchedTextureUnit());
// What UniformManager's fallback path does mid-draw: change a sampler object's filter,
// which bumps the context-wide sampling-resolution generation (SamplerObject.cpp).
@@ -492,7 +492,7 @@ TEST_F(PipeInputsTest, AFrontendMutationInsideAVerbRefreshesThePushedField) {
sampler->SetMinFilter(sampler->GetMinFilter() == SamplerFilterMode::Nearest ? SamplerFilterMode::Linear
: SamplerFilterMode::Nearest);
ASSERT_NE(ctx.GetSamplingResolutionGeneration(), samplingBefore) << "the mutation did not move the counter";
EXPECT_EQ(gPipeInputs.GetSamplingResolutionGeneration(), ctx.GetSamplingResolutionGeneration());
EXPECT_EQ(gPipeInputs->GetSamplingResolutionGeneration(), ctx.GetSamplingResolutionGeneration());
// And a bind reached from inside a verb moves both the bind generation and the
// high-water mark of touched units (TextureState::NoteUnitTouched).
@@ -501,8 +501,8 @@ TEST_F(PipeInputsTest, AFrontendMutationInsideAVerbRefreshesThePushedField) {
ctx.NoteTextureUnitTouched(unit);
ASSERT_NE(ctx.GetTextureBindGeneration(), bindBefore) << "the bind did not move the counter";
ASSERT_EQ(ctx.GetMaxTouchedTextureUnit(), unit);
EXPECT_EQ(gPipeInputs.GetTextureBindGeneration(), ctx.GetTextureBindGeneration());
EXPECT_EQ(gPipeInputs.GetMaxTouchedTextureUnit(), ctx.GetMaxTouchedTextureUnit());
EXPECT_EQ(gPipeInputs->GetTextureBindGeneration(), ctx.GetTextureBindGeneration());
EXPECT_EQ(gPipeInputs->GetMaxTouchedTextureUnit(), ctx.GetMaxTouchedTextureUnit());
}
// The notice refreshes the VALUE and never the stamp: a field this verb withheld the stamp
@@ -547,7 +547,7 @@ TEST_F(PipeInputsTest, AFrontendMutationInsideAVerbDoesNotDivergeAtRead) {
const ChildResult r = RunInChild([] {
MG_Config::Features.PipeVerify = true;
MGPipeValidateForVerb(MGPipeVerb::DrawArrays);
(void)gPipeInputs.GetSamplingResolutionGeneration(); // boundary == live: completes
(void)gPipeInputs->GetSamplingResolutionGeneration(); // boundary == live: completes
auto& ctx = *MG_State::pGLContext;
const Uint64 before = ctx.GetSamplingResolutionGeneration();
auto sampler = MakeShared<MG_State::GLState::SamplerObject>(0u);
@@ -556,10 +556,10 @@ TEST_F(PipeInputsTest, AFrontendMutationInsideAVerbDoesNotDivergeAtRead) {
if (ctx.GetSamplingResolutionGeneration() == before) {
::_exit(7); // the mutation did not take: the case would pass for the wrong reason
}
(void)gPipeInputs.GetSamplingResolutionGeneration(); // Fatal{PipeVerifyDiffer} without the notice
(void)gPipeInputs->GetSamplingResolutionGeneration(); // Fatal{PipeVerifyDiffer} without the notice
ctx.NoteTextureUnitTouched(ctx.GetMaxTouchedTextureUnit() + 1);
(void)gPipeInputs.GetTextureBindGeneration();
(void)gPipeInputs.GetMaxTouchedTextureUnit();
(void)gPipeInputs->GetTextureBindGeneration();
(void)gPipeInputs->GetMaxTouchedTextureUnit();
});
ASSERT_TRUE(ExitedWith(r, 0)) << DescribeStatus(r) << "\n" << r.Log;
EXPECT_NE(r.Log.find("MGPipe: verify armed"), std::string::npos) << r.Log;
+1 -1
View File
@@ -375,7 +375,7 @@ namespace {
m_savedResourceOps = MGPipeGetResourceOps();
static const MGPipeResourceOps kEmpty{};
MGPipeSetResourceOps(&kEmpty);
m_savedContext = Move(MG_State::pGLContext);
m_savedContext = MG_State::pGLContext.Take();
MG_State::pGLContext = MakeUnique<MG_State::GLState::GLContext>();
m_savedTable = MG_Backend::gBackendFunctionsTable;
+45 -45
View File
@@ -146,60 +146,60 @@ namespace {
// ---------------------------------------------------------------------------------
void ExpectDerivedDrawFieldsMatch(GLContext& ctx, const char* tag) {
SCOPED_TRACE(tag);
EXPECT_EQ(gPipeInputs.GetBlendColor(), ctx.GetBlendColor());
EXPECT_EQ(gPipeInputs->GetBlendColor(), ctx.GetBlendColor());
for (Uint i = 0; i < kMGMaxDrawBuffers; ++i) {
BlendEquation gotColor{}, gotAlpha{}, wantColor{}, wantAlpha{};
gPipeInputs.GetBlendEquationIndexed(i, gotColor, gotAlpha);
gPipeInputs->GetBlendEquationIndexed(i, gotColor, gotAlpha);
ctx.GetBlendEquationIndexed(i, wantColor, wantAlpha);
EXPECT_EQ(gotColor, wantColor) << "blend equation " << i;
EXPECT_EQ(gotAlpha, wantAlpha) << "blend equation " << i;
BlendFactor gotSrcRGB{}, gotDstRGB{}, gotSrcA{}, gotDstA{};
BlendFactor wantSrcRGB{}, wantDstRGB{}, wantSrcA{}, wantDstA{};
gPipeInputs.GetBlendFuncIndexed(i, gotSrcRGB, gotDstRGB, gotSrcA, gotDstA);
gPipeInputs->GetBlendFuncIndexed(i, gotSrcRGB, gotDstRGB, gotSrcA, gotDstA);
ctx.GetBlendFuncIndexed(i, wantSrcRGB, wantDstRGB, wantSrcA, wantDstA);
EXPECT_EQ(gotSrcRGB, wantSrcRGB) << "blend func " << i;
EXPECT_EQ(gotDstRGB, wantDstRGB) << "blend func " << i;
EXPECT_EQ(gotSrcA, wantSrcA) << "blend func " << i;
EXPECT_EQ(gotDstA, wantDstA) << "blend func " << i;
EXPECT_EQ(gPipeInputs.GetColorMaskIndexed(i), ctx.GetColorMaskIndexed(i)) << "colour mask " << i;
EXPECT_EQ(gPipeInputs.IsCapabilityEnabledIndexed(CapabilityInput::Blend, i),
EXPECT_EQ(gPipeInputs->GetColorMaskIndexed(i), ctx.GetColorMaskIndexed(i)) << "colour mask " << i;
EXPECT_EQ(gPipeInputs->IsCapabilityEnabledIndexed(CapabilityInput::Blend, i),
ctx.IsCapabilityEnabledIndexed(CapabilityInput::Blend, i))
<< "indexed blend enable " << i;
}
EXPECT_EQ(gPipeInputs.GetCullFaceMode(), ctx.GetCullFaceMode());
EXPECT_EQ(gPipeInputs.GetDepthFunc(), ctx.GetDepthFunc());
EXPECT_EQ(gPipeInputs.GetDepthMask(), ctx.GetDepthMask());
EXPECT_EQ(gPipeInputs->GetCullFaceMode(), ctx.GetCullFaceMode());
EXPECT_EQ(gPipeInputs->GetDepthFunc(), ctx.GetDepthFunc());
EXPECT_EQ(gPipeInputs->GetDepthMask(), ctx.GetDepthMask());
for (Uint i = 0; i < RenderStateParameters::MAX_VIEWPORTS; ++i) {
EXPECT_EQ(gPipeInputs.GetDepthRangeIndexed(i), ctx.GetDepthRangeIndexed(i)) << "depth range " << i;
EXPECT_EQ(gPipeInputs.GetViewportIndexed(i), ctx.GetViewportIndexed(i)) << "viewport " << i;
EXPECT_EQ(gPipeInputs.IsCapabilityEnabledIndexed(CapabilityInput::ScissorTest, i),
EXPECT_EQ(gPipeInputs->GetDepthRangeIndexed(i), ctx.GetDepthRangeIndexed(i)) << "depth range " << i;
EXPECT_EQ(gPipeInputs->GetViewportIndexed(i), ctx.GetViewportIndexed(i)) << "viewport " << i;
EXPECT_EQ(gPipeInputs->IsCapabilityEnabledIndexed(CapabilityInput::ScissorTest, i),
ctx.IsCapabilityEnabledIndexed(CapabilityInput::ScissorTest, i))
<< "indexed scissor enable " << i;
}
EXPECT_EQ(gPipeInputs.GetLineWidth(), ctx.GetLineWidth());
EXPECT_EQ(gPipeInputs.GetLogicOp(), ctx.GetLogicOp());
EXPECT_EQ(gPipeInputs.GetMinSampleShadingValue(), ctx.GetMinSampleShadingValue());
EXPECT_EQ(gPipeInputs.GetPatchDefaultInnerLevel(), ctx.GetPatchDefaultInnerLevel());
EXPECT_EQ(gPipeInputs.GetPatchDefaultOuterLevel(), ctx.GetPatchDefaultOuterLevel());
EXPECT_EQ(gPipeInputs.GetPatchVertices(), ctx.GetPatchVertices());
EXPECT_EQ(gPipeInputs.GetPolygonModeFront(), ctx.GetPolygonModeFront());
EXPECT_EQ(gPipeInputs.GetPolygonOffsetFactor(), ctx.GetPolygonOffsetFactor());
EXPECT_EQ(gPipeInputs.GetPolygonOffsetUnits(), ctx.GetPolygonOffsetUnits());
EXPECT_EQ(gPipeInputs.GetPrimitiveRestartIndex(), ctx.GetPrimitiveRestartIndex());
EXPECT_EQ(gPipeInputs.GetProvokingVertexMode(), ctx.GetProvokingVertexMode());
EXPECT_EQ(gPipeInputs.GetScissorBox(), ctx.GetScissorBox());
EXPECT_EQ(gPipeInputs.GetViewport(), ctx.GetViewport());
EXPECT_EQ(gPipeInputs->GetLineWidth(), ctx.GetLineWidth());
EXPECT_EQ(gPipeInputs->GetLogicOp(), ctx.GetLogicOp());
EXPECT_EQ(gPipeInputs->GetMinSampleShadingValue(), ctx.GetMinSampleShadingValue());
EXPECT_EQ(gPipeInputs->GetPatchDefaultInnerLevel(), ctx.GetPatchDefaultInnerLevel());
EXPECT_EQ(gPipeInputs->GetPatchDefaultOuterLevel(), ctx.GetPatchDefaultOuterLevel());
EXPECT_EQ(gPipeInputs->GetPatchVertices(), ctx.GetPatchVertices());
EXPECT_EQ(gPipeInputs->GetPolygonModeFront(), ctx.GetPolygonModeFront());
EXPECT_EQ(gPipeInputs->GetPolygonOffsetFactor(), ctx.GetPolygonOffsetFactor());
EXPECT_EQ(gPipeInputs->GetPolygonOffsetUnits(), ctx.GetPolygonOffsetUnits());
EXPECT_EQ(gPipeInputs->GetPrimitiveRestartIndex(), ctx.GetPrimitiveRestartIndex());
EXPECT_EQ(gPipeInputs->GetProvokingVertexMode(), ctx.GetProvokingVertexMode());
EXPECT_EQ(gPipeInputs->GetScissorBox(), ctx.GetScissorBox());
EXPECT_EQ(gPipeInputs->GetViewport(), ctx.GetViewport());
for (const StencilFace face : {StencilFace::Front, StencilFace::Back}) {
const StencilFaceState& got = gPipeInputs.GetStencilState(face);
const StencilFaceState& got = gPipeInputs->GetStencilState(face);
const StencilFaceState& want = ctx.GetStencilState(face);
EXPECT_EQ(std::memcmp(&got, &want, sizeof(StencilFaceState)), 0)
<< "stencil face " << static_cast<int>(face);
}
for (SizeT i = 0; i < static_cast<SizeT>(CapabilityInput::CapabilityInputCount); ++i) {
const CapabilityInput cap = static_cast<CapabilityInput>(i);
EXPECT_EQ(gPipeInputs.IsCapabilityEnabled(cap), ctx.IsCapabilityEnabled(cap)) << "capability " << i;
EXPECT_EQ(gPipeInputs->IsCapabilityEnabled(cap), ctx.IsCapabilityEnabled(cap)) << "capability " << i;
}
}
@@ -214,7 +214,7 @@ namespace {
// the only thing in the suite that covers them.
void ExpectAssembledBlockIsTheLiveBlock(GLContext& ctx, const char* tag) {
SCOPED_TRACE(tag);
EXPECT_EQ(std::memcmp(&gPipeInputs.GetRenderStateParameters(), &ctx.GetRenderStateParameters(),
EXPECT_EQ(std::memcmp(&gPipeInputs->GetRenderStateParameters(), &ctx.GetRenderStateParameters(),
sizeof(RenderStateParameters)),
0)
<< "the assembled working block is not byte-identical to the live one - a chunk of "
@@ -554,7 +554,7 @@ namespace {
// restored on the way out so the case stays independent (SanityTest's idiom).
struct ContextGuard {
SharedPtr<GLContext> Previous;
ContextGuard() : Previous(Move(MG_State::pGLContext)) {
ContextGuard() : Previous(MG_State::pGLContext.Take()) {
MG_State::pGLContext = MakeUnique<GLContext>();
MGPipeApplierReset();
}
@@ -653,7 +653,7 @@ namespace {
// The vacuity guard: the block still holds what the fill copied out of the DEFAULT
// context, so it must currently DISAGREE with the live one. If this ever passes, the
// comparisons below would be checking the filler against itself.
ASSERT_NE(gPipeInputs.GetLineWidth(), ctx.GetLineWidth());
ASSERT_NE(gPipeInputs->GetLineWidth(), ctx.GetLineWidth());
applyWholeBlock();
@@ -667,7 +667,7 @@ namespace {
// would hand the backends a 63-wide rectangle where 64 was asked for. The literal
// is std::lround's answer - round half AWAY FROM ZERO, so 1.5 -> 2 and 2.5 -> 3,
// not the banker's rounding a nearbyint() transcription would give.
EXPECT_EQ(gPipeInputs.GetViewport(), IntVec4(2, 3, 64, 32));
EXPECT_EQ(gPipeInputs->GetViewport(), IntVec4(2, 3, 64, 32));
} // phase 1, kDraw
// Phase 2, kClear: the three clear values. The verb's own fill runs FIRST and copies
@@ -678,13 +678,13 @@ namespace {
ctx.SetClearColor(FloatVec4(0.9f, 0.8f, 0.7f, 0.6f));
ctx.SetClearDepth(0.125f);
ctx.SetClearStencil(21);
ASSERT_NE(gPipeInputs.GetClearDepth(), ctx.GetClearDepth());
ASSERT_NE(gPipeInputs->GetClearDepth(), ctx.GetClearDepth());
applyWholeBlock();
EXPECT_EQ(gPipeInputs.GetClearColor(), ctx.GetClearColor());
EXPECT_EQ(gPipeInputs.GetClearDepth(), ctx.GetClearDepth());
EXPECT_EQ(gPipeInputs.GetClearStencil(), ctx.GetClearStencil());
EXPECT_EQ(gPipeInputs->GetClearColor(), ctx.GetClearColor());
EXPECT_EQ(gPipeInputs->GetClearDepth(), ctx.GetClearDepth());
EXPECT_EQ(gPipeInputs->GetClearStencil(), ctx.GetClearStencil());
}
// Phase 3, kReadback: GetClampReadColor, the one derived field no draw and no clear
@@ -692,12 +692,12 @@ namespace {
{
MG_Test::ScopedPipeVerb verb(MGPipeVerb::ReadPixels);
ctx.SetClampReadColor(GL_FALSE);
ASSERT_NE(gPipeInputs.GetClampReadColor(), ctx.GetClampReadColor());
ASSERT_NE(gPipeInputs->GetClampReadColor(), ctx.GetClampReadColor());
applyWholeBlock();
EXPECT_EQ(gPipeInputs.GetClampReadColor(), ctx.GetClampReadColor());
EXPECT_EQ(gPipeInputs.GetClampReadColor(), static_cast<GLenum>(GL_FALSE));
EXPECT_EQ(gPipeInputs->GetClampReadColor(), ctx.GetClampReadColor());
EXPECT_EQ(gPipeInputs->GetClampReadColor(), static_cast<GLenum>(GL_FALSE));
}
}
@@ -764,7 +764,7 @@ namespace {
TEST(RenderStateSpans, IncrementalChunksKeepEveryDerivedFieldInStep) {
struct ContextGuard {
SharedPtr<GLContext> Previous;
ContextGuard() : Previous(Move(MG_State::pGLContext)) {
ContextGuard() : Previous(MG_State::pGLContext.Take()) {
MG_State::pGLContext = MakeUnique<GLContext>();
MGPipeApplierReset();
}
@@ -926,7 +926,7 @@ namespace {
// idiom, and the same guard cases 3 and 5 declare inline).
struct ApplierContextGuard {
SharedPtr<GLContext> Previous;
ApplierContextGuard() : Previous(Move(MG_State::pGLContext)) {
ApplierContextGuard() : Previous(MG_State::pGLContext.Take()) {
MG_State::pGLContext = MakeUnique<GLContext>();
MGPipeApplierReset();
}
@@ -1122,7 +1122,7 @@ namespace {
pack.Pack.SkipRows = 5;
pack.Pack.SwapBytes = true;
MGPipeApplySetPixelPackState(pack);
const PixelStoreParameters got = gPipeInputs.GetPixelStoreParameters(false);
const PixelStoreParameters got = gPipeInputs->GetPixelStoreParameters(false);
EXPECT_EQ(got.Alignment, 8);
EXPECT_EQ(got.RowLength, 37);
EXPECT_EQ(got.SkipRows, 5);
@@ -1155,13 +1155,13 @@ namespace {
std::memcpy(tail[1].IntView, secondInt, sizeof(secondInt));
MGPipeApplySetVertexAttribDefaults(hdr, tail);
EXPECT_EQ(gPipeInputs.GetCurrentVertexAttribute(2).floatValue[0], 1.5f);
EXPECT_EQ(gPipeInputs.GetCurrentVertexAttribute(2).floatValue[3], 4.5f);
EXPECT_EQ(gPipeInputs.GetCurrentVertexAttribute(9).floatValue[2], 0.5f);
EXPECT_EQ(gPipeInputs->GetCurrentVertexAttribute(2).floatValue[0], 1.5f);
EXPECT_EQ(gPipeInputs->GetCurrentVertexAttribute(2).floatValue[3], 4.5f);
EXPECT_EQ(gPipeInputs->GetCurrentVertexAttribute(9).floatValue[2], 0.5f);
// ... and the CONVERTED views are the record's, not a memcpy of the float bits:
// 1.5f's bits are 0x3FC00000, and the frontend's int view of it is 1.
EXPECT_EQ(gPipeInputs.GetCurrentVertexAttribute(2).intValue[0], 1);
EXPECT_EQ(gPipeInputs.GetCurrentVertexAttribute(9).intValue[0], -1);
EXPECT_EQ(gPipeInputs->GetCurrentVertexAttribute(2).intValue[0], 1);
EXPECT_EQ(gPipeInputs->GetCurrentVertexAttribute(9).intValue[0], -1);
}
// delete_render_state: the record stops being live and a bound handle stops being
+1 -1
View File
@@ -1807,7 +1807,7 @@ namespace {
m_previousPush = MG_Config::Features.PipePush;
MG_Config::Features.PipePush |= kMGPipeSubsystemResources;
MGPipeSetResourceOps(&m_ops);
m_previousContext = Move(MG_State::pGLContext);
m_previousContext = MG_State::pGLContext.Take();
MG_State::pGLContext = MakeUnique<GLContext>();
}
~PushArm() {
+1 -1
View File
@@ -231,7 +231,7 @@ namespace {
MG_Config::Features.PipePush |= kMGPipeSubsystemResources |
kMGPipeSubsystemTextureResources |
kMGPipeSubsystemSamplers;
m_previousContext = Move(MG_State::pGLContext);
m_previousContext = MG_State::pGLContext.Take();
MG_State::pGLContext = MakeUnique<GLContext>();
MGPipeTextureEmitterInstance().ResetForTest();
// AND THE APPLIER IS A PROCESS SINGLETON, so its object records outlive a case. With
+8 -8
View File
@@ -58,7 +58,7 @@ namespace {
class TrackerAggregates : public ::testing::Test {
protected:
void SetUp() override {
m_previous = Move(MG_State::pGLContext);
m_previous = MG_State::pGLContext.Take();
MG_State::pGLContext = MakeUnique<GLContext>();
}
void TearDown() override { MG_State::pGLContext = Move(m_previous); }
@@ -174,7 +174,7 @@ namespace {
}
TEST_F(TrackerAggregates, ANoteWithoutALiveContextIsANoOp) {
SharedPtr<GLContext> held = Move(MG_State::pGLContext);
SharedPtr<GLContext> held = MG_State::pGLContext.Take();
MGP_NOTE_AGGREGATE(BufferChange); // must not dereference a null context
MG_State::pGLContext = Move(held);
SUCCEED();
@@ -246,7 +246,7 @@ namespace {
class TrackerWalk : public ::testing::Test {
protected:
void SetUp() override {
m_previous = Move(MG_State::pGLContext);
m_previous = MG_State::pGLContext.Take();
MG_State::pGLContext = MakeUnique<GLContext>();
m_savedPush = MG_Config::Features.PipePush;
ResetTheServerSideSingletons();
@@ -904,7 +904,7 @@ namespace {
class TrackerAttribPayload : public ::testing::Test {
protected:
void SetUp() override {
m_previous = Move(MG_State::pGLContext);
m_previous = MG_State::pGLContext.Take();
MG_State::pGLContext = MakeUnique<GLContext>();
}
void TearDown() override { MG_State::pGLContext = Move(m_previous); }
@@ -993,7 +993,7 @@ namespace {
class TrackerShippedEmitter : public ::testing::Test {
protected:
void SetUp() override {
m_previous = Move(MG_State::pGLContext);
m_previous = MG_State::pGLContext.Take();
MG_State::pGLContext = MakeUnique<GLContext>();
m_savedPush = MG_Config::Features.PipePush;
MG_Config::Features.PipePush = kMGPipeSubsystemsMigratedAtP2;
@@ -1166,7 +1166,7 @@ namespace {
// that the capability itself reaches the applier's assembled block on the next draw.
TEST_F(TrackerShippedEmitter, AClipDistanceEnableReArmsTheResidualBlock) {
Draw();
ASSERT_FALSE(MobileGL::MG_Pipe::gPipeInputs.IsCapabilityEnabled(CapabilityInput::ClipDistance0))
ASSERT_FALSE(MobileGL::MG_Pipe::gPipeInputs->IsCapabilityEnabled(CapabilityInput::ClipDistance0))
<< "the premise is gone: a fresh context already has clip distance 0 enabled";
Ctx().SetCapability(CapabilityInput::ClipDistance0, true);
@@ -1177,7 +1177,7 @@ namespace {
<< "the premise is gone: a clip-distance enable now moves the pipeline version";
EXPECT_EQ(static_cast<Uint16>(Ctx().GetPipelineStateVersion()), pipelineBefore);
EXPECT_TRUE(MobileGL::MG_Pipe::gPipeInputs.IsCapabilityEnabled(CapabilityInput::ClipDistance0))
EXPECT_TRUE(MobileGL::MG_Pipe::gPipeInputs->IsCapabilityEnabled(CapabilityInput::ClipDistance0))
<< "a capability change that moves only m_version never reached the applier's block";
}
@@ -1295,7 +1295,7 @@ namespace {
// stand into the next verb. A draw with no context is a no-op; its argument must not
// outlive it.
MGPipeSetPendingBaseInstance(11);
SharedPtr<GLContext> parked = Move(MG_State::pGLContext);
SharedPtr<GLContext> parked = MG_State::pGLContext.Take();
Draw();
EXPECT_EQ(MGPipePendingBaseInstance(), 0u)
<< "the no-live-context exit left the draw's base instance standing for the next verb";
@@ -132,7 +132,7 @@ namespace {
// case out of the gate's reach.
struct EmitterScope {
EmitterScope() {
m_previousContext = Move(MG_State::pGLContext);
m_previousContext = MG_State::pGLContext.Take();
MG_State::pGLContext = MakeUnique<GLContext>();
MGPipeVertexInputEmitterInstance().Reset();
MGPipeVertexInputEmitterInstance().ResetCounters();
+11 -11
View File
@@ -894,7 +894,7 @@ TEST(DirectVulkanSanity, AdvertisesSubgroupOnlyWhenVulkanReportsUsableSupport) {
TEST(DirectVulkanSanity, CapabilityRefreshInvalidatesTheCachedCompileEnvironment) {
using namespace MobileGL;
auto previousContext = Move(MG_State::pGLContext);
auto previousContext = MG_State::pGLContext.Take();
auto previousBackend = Move(MG_Backend::pActiveBackendObject);
MG_State::pGLContext = MakeUnique<MG_State::GLState::GLContext>();
@@ -1006,7 +1006,7 @@ TEST(GetterSanity, ClampsMaxVertexAttribsToCurrentValueStorageCapacity) {
TEST(GetterSanity, ReportsFragmentInterpolationLimitsForFloatAndIntegerQueries) {
using namespace MobileGL;
auto previousContext = Move(MG_State::pGLContext);
auto previousContext = MG_State::pGLContext.Take();
auto previousBackend = Move(MG_Backend::pActiveBackendObject);
MG_State::pGLContext = MakeUnique<MG_State::GLState::GLContext>();
@@ -1056,7 +1056,7 @@ TEST(GetterSanity, ReportsFragmentInterpolationLimitsForFloatAndIntegerQueries)
TEST(GetterSanity, StorageAndUniformBufferOffsetAlignmentsAreSeparateLimits) {
using namespace MobileGL;
auto previousContext = Move(MG_State::pGLContext);
auto previousContext = MG_State::pGLContext.Take();
auto previousBackend = Move(MG_Backend::pActiveBackendObject);
MG_State::pGLContext = MakeUnique<MG_State::GLState::GLContext>();
@@ -1145,7 +1145,7 @@ TEST(GetterSanity, AtomicCounterQueriesMatchShaderCompilerLimits) {
using namespace MobileGL;
namespace Transpiler = MG_Util::ShaderTranspiler;
auto previousContext = Move(MG_State::pGLContext);
auto previousContext = MG_State::pGLContext.Take();
auto previousBackend = Move(MG_Backend::pActiveBackendObject);
MG_State::pGLContext = MakeUnique<MG_State::GLState::GLContext>();
MG_Backend::pActiveBackendObject = MakeUnique<DynamicParameterBackend>(MG_Backend::DynamicBackendParameters{});
@@ -1224,7 +1224,7 @@ void main() {
TEST(GetterSanity, ComputeWorkGroupQueriesMatchShaderCompilerLimits) {
using namespace MobileGL;
auto previousContext = Move(MG_State::pGLContext);
auto previousContext = MG_State::pGLContext.Take();
auto previousBackend = Move(MG_Backend::pActiveBackendObject);
MG_State::pGLContext = MakeUnique<MG_State::GLState::GLContext>();
MG_Backend::pActiveBackendObject = MakeUnique<DynamicParameterBackend>(MG_Backend::DynamicBackendParameters{});
@@ -1305,7 +1305,7 @@ void main() {
TEST(GetterSanity, EveryLimitWithABuiltinAgreesWithItsQuery) {
using namespace MobileGL;
auto previousContext = Move(MG_State::pGLContext);
auto previousContext = MG_State::pGLContext.Take();
auto previousBackend = Move(MG_Backend::pActiveBackendObject);
MG_State::pGLContext = MakeUnique<MG_State::GLState::GLContext>();
MG_Backend::pActiveBackendObject =
@@ -1404,7 +1404,7 @@ TEST(GetterSanity, EveryLimitWithABuiltinAgreesWithItsQuery) {
TEST(GetterSanity, MaxElementIndexIsTheFull32BitIndexCeiling) {
using namespace MobileGL;
auto previousContext = Move(MG_State::pGLContext);
auto previousContext = MG_State::pGLContext.Take();
auto previousBackend = Move(MG_Backend::pActiveBackendObject);
MG_State::pGLContext = MakeUnique<MG_State::GLState::GLContext>();
MG_Backend::pActiveBackendObject = MakeUnique<DynamicParameterBackend>(MG_Backend::DynamicBackendParameters{});
@@ -1431,7 +1431,7 @@ TEST(GetterSanity, MaxElementIndexIsTheFull32BitIndexCeiling) {
TEST(GetterSanity, PerCategoryMultisampleCeilingsAreProbedRatherThanFlooredAtFour) {
using namespace MobileGL;
auto previousContext = Move(MG_State::pGLContext);
auto previousContext = MG_State::pGLContext.Take();
auto previousBackend = Move(MG_Backend::pActiveBackendObject);
MG_State::pGLContext = MakeUnique<MG_State::GLState::GLContext>();
@@ -1473,7 +1473,7 @@ TEST(GetterSanity, PerCategoryMultisampleCeilingsAreProbedRatherThanFlooredAtFou
TEST(ShaderCompilerSanity, ArbCullDistanceIsUsableBelow450) {
using namespace MobileGL;
auto previousContext = Move(MG_State::pGLContext);
auto previousContext = MG_State::pGLContext.Take();
auto previousBackend = Move(MG_Backend::pActiveBackendObject);
MG_State::pGLContext = MakeUnique<MG_State::GLState::GLContext>();
MG_Backend::pActiveBackendObject = MakeUnique<DynamicParameterBackend>(MG_Backend::DynamicBackendParameters{});
@@ -4579,7 +4579,7 @@ TEST(DirectGLESVertexInputDraw, TheAttributeWalkTakesItsBuffersFromTheRecordNotT
// this case exists to catch - gPipeInputs->GetBoundVertexArray()->GetAllAttributes() - has
// anything to read at all; a free-standing object would make the revert crash instead of
// disagree, which is a red for the wrong reason.
SharedPtr<GLContext> previousContext = Move(MG_State::pGLContext);
SharedPtr<GLContext> previousContext = MG_State::pGLContext.Take();
MG_State::pGLContext = MakeUnique<GLContext>();
MG_State::pGLContext->CreateVertexArrayObject(1);
MG_State::pGLContext->BindVertexArray(1);
@@ -4650,7 +4650,7 @@ TEST(DirectGLESVertexInputDraw, TheIndexArmTakesItsBufferAndItsSerialFromTheReco
// The frontend's element slot has moved to another buffer with nothing emitted, in a real
// context for the reason the case above gives.
SharedPtr<GLContext> previousContext = Move(MG_State::pGLContext);
SharedPtr<GLContext> previousContext = MG_State::pGLContext.Take();
MG_State::pGLContext = MakeUnique<GLContext>();
MG_State::pGLContext->CreateVertexArrayObject(1);
MG_State::pGLContext->BindVertexArray(1);
@@ -222,7 +222,7 @@ TEST_F(ContextStateOwnershipTest, DestroyingOneContextLeavesTheOtherAlone) {
TEST_F(ContextStateOwnershipTest, ReleasingAnEglContextRestoresTheThreadsPreviousGLState) {
const auto contextA = NewContext(EGL_NO_CONTEXT);
const auto before = MG_State::pGLContext;
const auto before = MG_State::pGLContext.Shared();
ASSERT_NE(before, nullptr);
MakeCurrent(contextA);
@@ -234,7 +234,7 @@ TEST_F(ContextStateOwnershipTest, ReleasingAnEglContextRestoresTheThreadsPreviou
}
TEST_F(ContextStateOwnershipTest, AThreadWithoutACurrentContextUsesTheProcessDefaultContext) {
const auto processDefault = MG_State::pGLContext;
const auto processDefault = MG_State::pGLContext.Shared();
ASSERT_NE(processDefault, nullptr);
EXPECT_EQ(processDefault, MG_State::ProcessDefaultGLContext());
+8 -8
View File
@@ -1469,19 +1469,19 @@ TEST_F(PipeWireCodecTest, SetContextValuesRoundTripsIntoPipeInputs) {
// kReadback's class carries the two texture-unit counters, kDraw's the rest
// (FillPoints.def) - the stamp is what publishes a RECORD-SUPPLIED field for the read.
MGPipeServerStampVerbBoundary(MGPipeVerb::ReadPixels);
EXPECT_EQ(gPipeInputs.GetActiveTextureUnit(), 9);
EXPECT_EQ(gPipeInputs.GetMaxTouchedTextureUnit(), 41);
EXPECT_EQ(gPipeInputs->GetActiveTextureUnit(), 9);
EXPECT_EQ(gPipeInputs->GetMaxTouchedTextureUnit(), 41);
MGPipeServerStampVerbBoundary(MGPipeVerb::DrawArrays);
for (Uint32 t = 0; t < 15; ++t) {
EXPECT_EQ(gPipeInputs.GetTouchedBufferBindingPointCount(static_cast<BufferTarget>(t)),
EXPECT_EQ(gPipeInputs->GetTouchedBufferBindingPointCount(static_cast<BufferTarget>(t)),
SizeT{100 + t})
<< "target " << t;
}
EXPECT_TRUE(gPipeInputs.IsTransformFeedbackActive());
EXPECT_TRUE(gPipeInputs.IsTransformFeedbackPaused());
EXPECT_EQ(gPipeInputs.GetTransformFeedbackGeneration(), 0xA1A2A3A4A5A6A7A8ull);
EXPECT_EQ(gPipeInputs.GetBoundTransformFeedbackLifetimeId(), 0xB1B2B3B4B5B6B7B8ull);
EXPECT_EQ(gPipeInputs.GetTransformFeedbackCapturedVertices(), 0xC1C2C3C4C5C6C7C8ull);
EXPECT_TRUE(gPipeInputs->IsTransformFeedbackActive());
EXPECT_TRUE(gPipeInputs->IsTransformFeedbackPaused());
EXPECT_EQ(gPipeInputs->GetTransformFeedbackGeneration(), 0xA1A2A3A4A5A6A7A8ull);
EXPECT_EQ(gPipeInputs->GetBoundTransformFeedbackLifetimeId(), 0xB1B2B3B4B5B6B7B8ull);
EXPECT_EQ(gPipeInputs->GetTransformFeedbackCapturedVertices(), 0xC1C2C3C4C5C6C7C8ull);
MGPipeServerClearVerbBoundary();
}
+12 -12
View File
@@ -816,21 +816,21 @@ TEST(ServerLoopTest, AClearRecordCrossesAndIsStampedAsAVerbBoundary) {
ASSERT_TRUE(fixture.StartLoop());
// The pre-state is the honest one: nothing has stamped yet in this process.
ASSERT_NE(MG_Pipe::gPipeInputs.CurrentVerb(), MG_Pipe::MGPipeVerb::Clear);
ASSERT_NE(MG_Pipe::gPipeInputs->CurrentVerb(), MG_Pipe::MGPipeVerb::Clear);
const MG_Pipe::MGPClear clear = WholeFramebufferClear();
ASSERT_TRUE(fixture.EmitAndWait(MG_Pipe::MGPWireOp::Clear, &clear, sizeof(clear)));
Server::ServerLoop& loop = Server::ServerLoopInstance();
EXPECT_EQ(loop.DrainedRecords(), 1u);
EXPECT_EQ(MG_Pipe::gPipeInputs.CurrentVerb(), MG_Pipe::MGPipeVerb::Clear)
EXPECT_EQ(MG_Pipe::gPipeInputs->CurrentVerb(), MG_Pipe::MGPipeVerb::Clear)
<< "PipeApplier::StampVerbBoundary did not run, so every server-side PipeInputs read "
"would abort on the first field inside SyncRenderState";
// MANDATORY on leaving the applier (p1's M-5): without it a SPAWNED server latches the flag
// for its whole life, every later read is judged against the last verb's mask, and the
// sticky forwards start aborting under strict on the very case their exemption exists for.
EXPECT_FALSE(MG_Pipe::gPipeInputs.ServerStampedVerb())
EXPECT_FALSE(MG_Pipe::gPipeInputs->ServerStampedVerb())
<< "MGPipeServerClearVerbBoundary was not called when the apply thread left the applier";
// No backend object in this process, so the five class-B verbs DECLINE. Asserted rather
@@ -1629,7 +1629,7 @@ namespace {
// Empty on success, else the step that failed - the harness's SkipReason shape.
std::string BringUp() {
PinHeadlessEglEnvironment();
savedContext = Move(MG_State::pGLContext);
savedContext = MG_State::pGLContext.Take();
MG_State::pGLContext = MakeUnique<MG_State::GLState::GLContext>();
Server::ServerLoop& loop = Server::ServerLoopInstance();
// InitSplitRoles step 1: the server's private backend, no GL and no EGL yet; its
@@ -1944,13 +1944,13 @@ TEST(ServerLoopEglTest, ServerLivenessFollowsControlFramesAcrossReleaseAndRecrea
MGL_EGL_BRING_UP_OR_BAIL(fixture);
const auto live = [] {
Bool answer = false;
EXPECT_EQ(OnApply([&] { answer = MG_Pipe::gPipeInputs.IsLive(); }), MOBILEGL_OK);
EXPECT_EQ(OnApply([&] { answer = MG_Pipe::gPipeInputs->IsLive(); }), MOBILEGL_OK);
return answer;
};
EXPECT_FALSE(live()) << "surface creation alone is not a served current context";
ASSERT_TRUE(fixture.MakeCurrent());
EXPECT_TRUE(live());
auto client = Move(MG_State::pGLContext);
auto client = MG_State::pGLContext.Take();
EXPECT_TRUE(live()) << "server liveness must not consult the client GLContext";
MG_State::pGLContext = Move(client);
ASSERT_TRUE(fixture.ReleaseCurrent());
@@ -2571,7 +2571,7 @@ TEST(ServerLoopTest, AnInstancedBaseVertexDrawRecordReachesTheSinkWithItsFieldsI
EXPECT_EQ(seen.FirstRange.IndexBias, 5);
EXPECT_FALSE(seen.HadUserIndices);
EXPECT_FALSE(seen.HadIndirect);
EXPECT_FALSE(MG_Pipe::gPipeInputs.ServerStampedVerb());
EXPECT_FALSE(MG_Pipe::gPipeInputs->ServerStampedVerb());
fixture.Stop();
}
@@ -2714,7 +2714,7 @@ TEST(P5fReverseChannel, GlErrorsUseTheOwnedCallbackWithoutAResidualPull) {
g_fvErrorCalls = 0;
MG_Pipe::MGPipeResetResidualPullCountForTesting();
MG_Pipe::MGPipeServerStampVerbBoundary(MG_Pipe::MGPipeVerb::Clear);
MG_Pipe::gPipeInputs.RecordError(ErrorCode::InvalidOperation,
MG_Pipe::gPipeInputs->RecordError(ErrorCode::InvalidOperation,
MakeUnique<GenericErrorInfo>("fv", "draw", "driver message"));
EXPECT_EQ(g_fvErrorCalls, 1u);
EXPECT_EQ(g_fvErrorCode, static_cast<Uint32>(ErrorCode::InvalidOperation));
@@ -2780,7 +2780,7 @@ TEST(P5fReverseChannel, RecordErrorWithoutCallbackIsNamedFatal) {
MG_Config::Ipc.StrictErrors = true;
MG_Config::Ipc.RoleSplitState = true;
MG_Pipe::gMGPipeCallbacks.OnGlError = nullptr;
MG_Pipe::gPipeInputs.RecordError(ErrorCode::InvalidOperation, nullptr);
MG_Pipe::gPipeInputs->RecordError(ErrorCode::InvalidOperation, nullptr);
std::_Exit(9);
}, ::testing::KilledBySignal(SIGABRT), "");
EXPECT_NE(ReadLog().find("OnGlError.callback-missing"), std::string::npos);
@@ -2815,7 +2815,7 @@ TEST(P5fReverseChannel, GlErrorMessagesShareFifoWithOtherReverseEvents) {
ASSERT_EQ(Server::ServerLoopInstance().RunProbeOnApplyThreadForTesting(
+[](void*) -> MobileGLResult {
MG_Pipe::MGPipeServerStampVerbBoundary(MG_Pipe::MGPipeVerb::Clear);
MG_Pipe::gPipeInputs.RecordError(ErrorCode::InvalidOperation,
MG_Pipe::gPipeInputs->RecordError(ErrorCode::InvalidOperation,
MakeUnique<GenericErrorInfo>("fv", "driver error"));
const MG_Pipe::MGPRange whole{0, MG_Pipe::kMGPipeWholeBuffer};
MG_Pipe::gMGPipeCallbacks.OnGpuWritten({17, 3}, 1, &whole);
@@ -3165,7 +3165,7 @@ namespace {
const MG_Pipe::MGPDrawRange range{0, 3, 0};
if (!fixture.EmitAndWaitWithTail(MG_Pipe::MGPWireOp::DrawVbo, &info, sizeof(info), &range, sizeof(range)))
::_exit(65);
if (MG_Pipe::gPipeInputs.CurrentVerb() == MG_Pipe::MGPipeVerb::Clear) ::_exit(66);
if (MG_Pipe::gPipeInputs->CurrentVerb() == MG_Pipe::MGPipeVerb::Clear) ::_exit(66);
// A Clear encoded legally and then SHORTENED in the ring before it is published - the
// raw-record peer driver's move, in process: its header says 8 bytes, a header and no
// MGPClear at all.
@@ -3185,7 +3185,7 @@ namespace {
Server::ServerLoop& loop = Server::ServerLoopInstance();
(void)PollUntil([&] { return !loop.Running(); }, 3000);
int failed = 0;
if (MG_Pipe::gPipeInputs.CurrentVerb() == MG_Pipe::MGPipeVerb::Clear) failed |= kShortStamped;
if (MG_Pipe::gPipeInputs->CurrentVerb() == MG_Pipe::MGPipeVerb::Clear) failed |= kShortStamped;
if (!Remote::SessionLatched() || std::strstr(Remote::SessionLatchedLine(), "\"record.Minimum\"") == nullptr)
failed |= kShortWrongFault;
if (loop.DrainedRecords() != 2) failed |= kShortDrained;