Merge commit '19a4400a' into codex/p5f-wave2

This commit is contained in:
2026-09-19 22:13:37 -04:00
17 changed files with 401 additions and 107 deletions
+6
View File
@@ -9,6 +9,9 @@
#pragma once
#include <Includes.h>
#include "MG_State/GLState/TextureState/TextureEnum.h"
#if MOBILEGL_BUILD_DISAGGREGATED
#include <MG_Pipe/MGPipeHandles.h>
#endif
namespace MobileGL {
namespace MG_State::GLState {
@@ -33,6 +36,9 @@ namespace MobileGL {
struct CopyImageEndpoint {
SharedPtr<MG_State::GLState::ITextureObject> Texture;
SharedPtr<MG_State::GLState::RenderbufferObject> Renderbuffer;
#if MOBILEGL_BUILD_DISAGGREGATED
MG_Pipe::MGPipeHandle TextureHandle = MG_Pipe::kMGPipeNullHandle;
#endif
Bool IsRenderbuffer() const { return Renderbuffer != nullptr; }
Bool Exists() const { return Texture != nullptr || Renderbuffer != nullptr; }
+153 -2
View File
@@ -64,6 +64,14 @@
#endif
namespace MobileGL::MG_Backend::DirectGLES {
#if MOBILEGL_BUILD_DISAGGREGATED
// read_pixels writes the server reply scratch; client-side scatter owns any PBO.
static const SharedPtr<MG_State::GLState::BufferObject>& SplitReadbackPackBuffer() {
static const SharedPtr<MG_State::GLState::BufferObject> none;
return none;
}
#endif
MG_External::EGLFunctionsTable g_EGLFuncs;
MG_External::GLESFunctionsTable g_GLESFuncs;
MG_External::GLESCapabilities g_GLESCapabilities;
@@ -1520,8 +1528,39 @@ namespace MobileGL::MG_Backend::DirectGLES {
// gl_NextBuffer is the shape that makes them differ: buffer 0 has stride 0
// and nothing bound, so target 0 describes buffer 1.
SizeT bufferIndex = 0;
#if MOBILEGL_BUILD_DISAGGREGATED
MG_Pipe::MGPipeHandle handle = MG_Pipe::kMGPipeNullHandle;
#endif
};
#if MOBILEGL_BUILD_DISAGGREGATED
struct XfbProgramSource {
SharedPtr<MG_State::GLState::ProgramObject> Frontend;
SharedPtr<const MG_State::GLState::ProgramArchive> Archive;
explicit operator Bool() const { return Frontend || Archive; }
const XfbProgramSource* operator->() const { return this; }
SizeT GetTransformFeedbackBufferCount() const {
return Archive ? Archive->Link.xfbStrides.size() : Frontend->GetTransformFeedbackBufferCount();
}
Uint32 GetTransformFeedbackStride(Uint32 index) const {
return Archive ? (index < Archive->Link.xfbStrides.size() ? Archive->Link.xfbStrides[index] : 0)
: Frontend->GetTransformFeedbackStride(index);
}
Bool NeedsScatteredTransformFeedbackCapture() const {
return Archive ? Archive->Link.xfbNeedsScatteredCapture : Frontend->NeedsScatteredTransformFeedbackCapture();
}
Uint32 GetTransformFeedbackPackedStride() const {
return Archive ? Archive->Link.xfbPackedStride : Frontend->GetTransformFeedbackPackedStride();
}
GLenum GetTransformFeedbackBufferMode() const {
return Archive ? Archive->Link.xfbBufferMode : Frontend->GetTransformFeedbackBufferMode();
}
const auto& GetTransformFeedbackVaryings() const {
return Archive ? Archive->Link.xfbVaryings : Frontend->GetTransformFeedbackVaryings();
}
};
#endif
// Per frontend transform feedback object. The default object (name 0) maps to
// the driver's default object (id 0) and is always present.
struct XfbObjectState {
@@ -1536,6 +1575,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
// End scatters them into `targets`.
Bool scattered = false;
SharedPtr<MG_State::GLState::ProgramObject> scatterProgram;
#if MOBILEGL_BUILD_DISAGGREGATED
SharedPtr<const MG_State::GLState::ProgramArchive> scatterArchive;
#endif
SizeT scatterCapacityVertices = 0;
};
@@ -1612,6 +1654,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
}
if (g_GLESFuncs.glMapBufferRange != nullptr && g_GLESFuncs.glUnmapBuffer != nullptr) {
for (const auto& target : targets) {
#if MOBILEGL_BUILD_DISAGGREGATED
if (MG_Config::Transport == MG_Config::TransportMode::Monolith)
#endif
if (!target.buffer) continue;
#if MOBILEGL_BUILD_DISAGGREGATED
// P5c: under an active transport the frontend object is client
@@ -1619,7 +1664,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// resource's and the captured bytes go back as a writeback EVENT -
// WritebackFromBackend from the apply thread is the R1/R2 shape.
if (MG_Config::Transport != MG_Config::TransportMode::Monolith) {
const MG_Pipe::MGPipeHandle res = BufferImpl::HandleOfBuffer(target.buffer.get());
const MG_Pipe::MGPipeHandle res = target.handle;
auto* resource = BufferImpl::FindBufferResourceForHandle(res);
if (resource == nullptr || resource->persistentMapped) continue;
const SizeT size = target.end - target.start;
@@ -1724,7 +1769,11 @@ namespace MobileGL::MG_Backend::DirectGLES {
// occupies are written, so the holes gl_SkipComponents asks for keep whatever the
// application had put there - which is the whole point of the feature.
void ScatterCapturedRecords(XfbObjectState& xfb) {
#if MOBILEGL_BUILD_DISAGGREGATED
const XfbProgramSource program{xfb.scatterProgram, xfb.scatterArchive};
#else
const auto& program = xfb.scatterProgram;
#endif
if (!program || xfb.targets.empty()) return;
if (g_GLESFuncs.glMapBufferRange == nullptr || g_GLESFuncs.glUnmapBuffer == nullptr) return;
@@ -1761,6 +1810,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
// whole range down once.
for (SizeT targetIndex = 0; targetIndex < xfb.targets.size(); ++targetIndex) {
const auto& target = xfb.targets[targetIndex];
#if MOBILEGL_BUILD_DISAGGREGATED
if (MG_Config::Transport == MG_Config::TransportMode::Monolith)
#endif
if (!target.buffer) continue;
// By BUFFER index, not by position in the compacted list - see XfbCaptureTarget.
const SizeT stride = program->GetTransformFeedbackStride(static_cast<Uint32>(target.bufferIndex));
@@ -1776,7 +1828,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
MG_Pipe::MGPipeHandle splitRes = MG_Pipe::kMGPipeNullHandle;
BufferImpl::GLESBufferResource* splitResource = nullptr;
if (MG_Config::Transport != MG_Config::TransportMode::Monolith) {
splitRes = BufferImpl::HandleOfBuffer(target.buffer.get());
splitRes = target.handle;
splitResource = BufferImpl::FindBufferResourceForHandle(splitRes);
const Uint8* hostBytes = splitResource != nullptr ? splitResource->hostBytes : nullptr;
if (splitResource == nullptr || hostBytes == nullptr) {
@@ -1868,7 +1920,23 @@ namespace MobileGL::MG_Backend::DirectGLES {
// not captured, and opening the span would also subject it to the capture
// primitive-mode rule the paused draw is exempt from.
if (!xfb.pending || xfb.paused) return;
#if MOBILEGL_BUILD_DISAGGREGATED
XfbProgramSource program;
const MG_Pipe::MGPStreamOutputBegin* span = nullptr;
if (MG_Config::Transport != MG_Config::TransportMode::Monolith) {
const auto& state = MG_Pipe::MGPipeApplier();
const auto found = state.StreamOutputSpans.find(state.BoundStreamOutputLifetimeId);
if (found != state.StreamOutputSpans.end()) {
span = &found->second;
const auto* record = PipeShaderCsoRecordForHandle(span->CaptureProgram);
if (record) program.Archive = record->Archive;
}
} else {
program.Frontend = MGB_CTX->GetTransformFeedbackProgram();
}
#else
const auto& program = MGB_CTX->GetTransformFeedbackProgram();
#endif
if (!program) {
// The pending flag is deliberately NOT consumed here. It used to be cleared
// before this check, so a single draw that could not see the capture program
@@ -1888,6 +1956,20 @@ namespace MobileGL::MG_Backend::DirectGLES {
// recording it here keeps End independent of the frontend capture state.
const SizeT bufferCount = program->GetTransformFeedbackBufferCount();
for (SizeT i = 0; i < bufferCount; ++i) {
#if MOBILEGL_BUILD_DISAGGREGATED
if (MG_Config::Transport != MG_Config::TransportMode::Monolith) {
if (!span || i >= 4) continue;
const auto& range = span->Targets[i];
if (MG_Pipe::MGPipeHandleIsNull(range.Res) || range.Size == 0) continue;
auto* resource = BufferImpl::EnsureBufferResourceForHandle(nullptr, range.Res);
if (!resource || resource->id == 0) continue;
xfb.targets.push_back({nullptr, resource->id, static_cast<SizeT>(range.Offset),
static_cast<SizeT>(range.Offset + range.Size), i, range.Res});
BufferImpl::BindBufferRangeCached(GL_TRANSFORM_FEEDBACK_BUFFER, static_cast<GLuint>(i),
resource->id, static_cast<GLintptr>(range.Offset), static_cast<GLsizeiptr>(range.Size));
continue;
}
#endif
auto& point = MGB_CTX->GetBufferBindingPoint(BufferTarget::TransformFeedback,
static_cast<Uint>(i));
const auto& bufferObject = point.GetBoundObject();
@@ -1901,12 +1983,18 @@ namespace MobileGL::MG_Backend::DirectGLES {
xfb.targets.push_back({bufferObject, backendResource->id, start, end, i});
}
#if MOBILEGL_BUILD_DISAGGREGATED
if (MG_Config::Transport == MG_Config::TransportMode::Monolith)
#endif
BufferImpl::SyncTransformFeedbackBindingPoints(bufferCount);
// A layout with holes or several interleaved buffers is not expressible on ES:
// capture gap-free into scratch storage and place the records at End instead.
xfb.scattered = false;
xfb.scatterProgram.reset();
#if MOBILEGL_BUILD_DISAGGREGATED
xfb.scatterArchive.reset();
#endif
xfb.scatterCapacityVertices = 0;
if (program->NeedsScatteredTransformFeedbackCapture()) {
SizeT capacityVertices = ~SizeT(0);
@@ -1922,7 +2010,12 @@ namespace MobileGL::MG_Backend::DirectGLES {
if (capacityVertices == ~SizeT(0)) capacityVertices = 0;
if (BindScatterCaptureBuffer(program->GetTransformFeedbackPackedStride(), capacityVertices)) {
xfb.scattered = true;
#if MOBILEGL_BUILD_DISAGGREGATED
xfb.scatterProgram = program.Frontend;
xfb.scatterArchive = program.Archive;
#else
xfb.scatterProgram = program;
#endif
xfb.scatterCapacityVertices = capacityVertices;
} else {
// NO SPAN RATHER THAN A SPAN THAT WRITES SOMEWHERE ELSE. The ES program for a
@@ -2007,6 +2100,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
ScatterCapturedRecords(xfb);
xfb.scattered = false;
xfb.scatterProgram.reset();
#if MOBILEGL_BUILD_DISAGGREGATED
xfb.scatterArchive.reset();
#endif
} else {
ReadbackCapturedRanges(xfb.targets);
}
@@ -10757,7 +10853,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
ZoneScopedNC(__func__, TRACY_ZONECOLOR_BACKEND);
#endif
auto unit = MGB_CTX->GetActiveTextureUnit();
#if !MOBILEGL_BUILD_DISAGGREGATED
auto& textureUnit = MGB_CTX->GetTextureUnitObject(unit);
#endif
auto textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
if (!TextureImpl::IsSupportedTextureTarget(textureTarget)) {
@@ -10789,6 +10887,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
}
#endif
#if MOBILEGL_BUILD_DISAGGREGATED
auto& textureUnit = MGB_CTX->GetTextureUnitObject(unit);
#endif
const auto& bindingSlot = textureUnit.GetBindingSlot(textureTarget);
{
const auto& textureObject = bindingSlot.GetBoundObject();
@@ -12297,8 +12398,15 @@ namespace MobileGL::MG_Backend::DirectGLES {
// An endpoint that named nothing is the frontend validator's INVALID_VALUE and never
// reaches here - but the assertion that says so is compiled out of a release build, and
// SyncTextureObjectToBackend would register a null state object.
#if MOBILEGL_BUILD_DISAGGREGATED
if (MG_Config::Transport != MG_Config::TransportMode::Monolith) {
out.texture = TextureImpl::SyncTextureToBackendByHandle(endpoint.TextureHandle);
} else
#endif
{
if (!endpoint.Texture) return false;
out.texture = TextureImpl::SyncTextureObjectToBackend(endpoint.Texture);
}
if (!out.texture) return false;
const TextureTarget stateTarget = MG_Util::ConvertGLEnumToTextureTarget(appTarget);
out.target = TextureImpl::ConvertTextureTargetToBackendGLEnum(stateTarget);
@@ -12316,6 +12424,13 @@ namespace MobileGL::MG_Backend::DirectGLES {
}
static TextureInternalFormat GetCopyImageEndpointFormat(const CopyImageEndpoint& endpoint) {
#if MOBILEGL_BUILD_DISAGGREGATED
if (MG_Config::Transport != MG_Config::TransportMode::Monolith) {
const auto* record = PipeTextureRecordForHandle(endpoint.TextureHandle);
return record ? static_cast<TextureInternalFormat>(record->Desc.InternalFormat)
: TextureInternalFormat::Unknown;
}
#endif
if (endpoint.IsRenderbuffer()) return endpoint.Renderbuffer->GetInternalFormat();
return endpoint.Texture ? endpoint.Texture->GetFormat() : TextureInternalFormat::Unknown;
}
@@ -12596,6 +12711,17 @@ namespace MobileGL::MG_Backend::DirectGLES {
// effect by the block's next use.
void ShaderStorageBlockBinding(GLuint program, const GLchar* storageBlockName, GLuint storageBlockBinding) {
if (!storageBlockName) return;
#if MOBILEGL_BUILD_DISAGGREGATED
if (MG_Config::Transport != MG_Config::TransportMode::Monolith) {
const auto handle = MG_Pipe::MGPipeApplier().VerbStorageBlockProgram;
auto* slot = PrgramImpl::g_backendProgramObjects.FindByHandle(handle);
if (slot && *slot && (*slot)->GetBackendProgramId()) {
PrgramImpl::ApplyShaderStorageBlockBinding((*slot)->GetBackendProgramId(),
storageBlockName, storageBlockBinding);
}
return;
}
#endif
if (!MGB_CTX->ValidateProgramName(program)) return;
auto& programObject = MGB_CTX->GetProgramObject(program);
if (!programObject) return;
@@ -12806,6 +12932,11 @@ namespace MobileGL::MG_Backend::DirectGLES {
const SizeT rowBytes = static_cast<SizeT>(width) * dstPixelBytes;
const SizeT packedSize = dstOffset + static_cast<SizeT>(height - 1) * dstRowStride + rowBytes;
const auto& pixelPackBufferObject =
#if MOBILEGL_BUILD_DISAGGREGATED
MG_Config::Transport != MG_Config::TransportMode::Monolith
? SplitReadbackPackBuffer() :
#endif
MGB_CTX->GetBufferBindingSlot(BufferTarget::PixelPack).GetBoundObject();
const SizeT pboOffset = reinterpret_cast<SizeT>(pixels);
if (pixelPackBufferObject && pboOffset + packedSize > pixelPackBufferObject->GetSize()) {
@@ -13788,6 +13919,11 @@ namespace MobileGL::MG_Backend::DirectGLES {
return true;
}
const auto& pixelPackBufferObject =
#if MOBILEGL_BUILD_DISAGGREGATED
MG_Config::Transport != MG_Config::TransportMode::Monolith
? SplitReadbackPackBuffer() :
#endif
MGB_CTX->GetBufferBindingSlot(BufferTarget::PixelPack).GetBoundObject();
if (!pixelPackBufferObject && pixels == nullptr) {
return true;
@@ -14025,6 +14161,11 @@ namespace MobileGL::MG_Backend::DirectGLES {
MG_Pipe::MGPipeUnmigratedEmulation("get-tex-image-shadow");
#endif
const auto& pixelPackBufferObject =
#if MOBILEGL_BUILD_DISAGGREGATED
MG_Config::Transport != MG_Config::TransportMode::Monolith
? SplitReadbackPackBuffer() :
#endif
MGB_CTX->GetBufferBindingSlot(BufferTarget::PixelPack).GetBoundObject();
if (!pixelPackBufferObject && pixels == nullptr) {
return true;
@@ -14335,6 +14476,11 @@ namespace MobileGL::MG_Backend::DirectGLES {
// (the driver-level binding used to stay on the user PBO after this call,
// capturing subsequent client-memory readbacks into it).
auto& pixelPackBufferObject =
#if MOBILEGL_BUILD_DISAGGREGATED
MG_Config::Transport != MG_Config::TransportMode::Monolith
? SplitReadbackPackBuffer() :
#endif
MGB_CTX->GetBufferBindingSlot(BufferTarget::PixelPack).GetBoundObject();
Bool usePBO = false;
GLuint packBufferId = 0;
@@ -14766,6 +14912,11 @@ namespace MobileGL::MG_Backend::DirectGLES {
// Handle PBO. The pack binding is scoped: it returns to the resting 0 state
// on every exit path, so a later readback can never land in a stale PBO.
auto& pixelPackBufferObject =
#if MOBILEGL_BUILD_DISAGGREGATED
MG_Config::Transport != MG_Config::TransportMode::Monolith
? SplitReadbackPackBuffer() :
#endif
MGB_CTX->GetBufferBindingSlot(BufferTarget::PixelPack).GetBoundObject();
Bool usePBO = false;
GLuint packBufferId = 0;
@@ -10287,6 +10287,12 @@ namespace MobileGL::MG_Backend::DirectGLES {
// (the three-channel colour-renderable widening). GL answers such a read with 1.0, but
// the storage holds whatever the draw wrote there, so the readback has to overwrite it.
Bool IsAlphaWidenedFallbackReadAttachment() {
#if MOBILEGL_BUILD_DISAGGREGATED
if (MG_Config::Transport != MG_Config::TransportMode::Monolith) {
const auto* record = MG_Pipe::MGPipeApplier().ReadFramebuffer();
return record && IsAlphaWidenedColorSurface(record->ReadSurface);
}
#endif
const auto* attachmentObject = GetReadColorAttachment();
if (attachmentObject == nullptr) {
return false;
@@ -10295,6 +10301,13 @@ namespace MobileGL::MG_Backend::DirectGLES {
}
Bool IsFixedPointFallbackReadAttachment() {
#if MOBILEGL_BUILD_DISAGGREGATED
if (MG_Config::Transport != MG_Config::TransportMode::Monolith) {
const auto* record = MG_Pipe::MGPipeApplier().ReadFramebuffer();
return record && record->ReadSurface.Kind != MG_Pipe::kMGPipeSurfaceKindNone &&
IsSnormFormat(static_cast<TextureInternalFormat>(record->ReadSurface.InternalFormat));
}
#endif
const auto& readFBO =
MGB_CTX->GetFramebufferBindingSlot(FramebufferTarget::Read).GetBoundObject();
if (!readFBO) {
+14 -1
View File
@@ -37,6 +37,14 @@
#include <regex>
namespace MobileGL::MG_Backend::DirectGLES {
#if MOBILEGL_BUILD_DISAGGREGATED
// read_pixels writes the server reply scratch; client-side scatter owns any PBO.
static const SharedPtr<MG_State::GLState::BufferObject>& SplitReadbackPackBuffer() {
static const SharedPtr<MG_State::GLState::BufferObject> none;
return none;
}
#endif
#if MOBILEGL_BUILD_DISAGGREGATED
const FormatCapabilityCache* ActiveBackendFormatCaps() {
// Under a live split session the SERVER's private backend is what owns the context on the
@@ -2350,7 +2358,12 @@ namespace MobileGL::MG_Backend::DirectGLES {
static Bool StoreClientRows(SizeT dstPixelBytes, SizeT swapGroupSize, GLsizei width, GLsizei sliceHeight,
GLsizei sliceCount, void* pixels, Bool applyPackImageParams, FillRow&& fillRow) {
const auto& pixelPackBufferObject =
MGB_CTX->GetBufferBindingSlot(BufferTarget::PixelPack).GetBoundObject();
#if MOBILEGL_BUILD_DISAGGREGATED
MG_Config::Transport != MG_Config::TransportMode::Monolith
? SplitReadbackPackBuffer() :
#endif
MGB_CTX->GetBufferBindingSlot(BufferTarget::PixelPack).GetBoundObject();
// 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.
+18
View File
@@ -2117,6 +2117,18 @@ namespace MobileGL::MG_Pipe {
#endif
SizeT PipeInputs::GetBufferBindingPointCount(BufferTarget target) const {
#if MOBILEGL_BUILD_DISAGGREGATED
if (MG_Config::Transport != MG_Config::TransportMode::Monolith) {
switch (target) {
case BufferTarget::Uniform:
case BufferTarget::ShaderStorage:
case BufferTarget::AtomicCounter:
case BufferTarget::TransformFeedback:
return kMGPipeMaxBufferBindingPoints;
default: return 0;
}
}
#endif
MGP_STICKY_FORWARD_PULL(GetBufferBindingPointCount);
const auto* ctx = LiveContext();
return ctx != nullptr ? ctx->GetBufferBindingPointCount(target) : 0;
@@ -2135,6 +2147,12 @@ namespace MobileGL::MG_Pipe {
}
Bool PipeInputs::HasOpenTransformFeedbackSpan(Uint64 lifetimeId) const {
#if MOBILEGL_BUILD_DISAGGREGATED
if (MG_Config::Transport != MG_Config::TransportMode::Monolith) {
return lifetimeId != 0 && MGPipeApplier().StreamOutputSpans.count(lifetimeId) != 0;
}
#endif
MGP_STICKY_FORWARD_PULL(HasOpenTransformFeedbackSpan);
const auto* ctx = LiveContext();
return ctx != nullptr && ctx->HasOpenTransformFeedbackSpan(lifetimeId);
@@ -34,23 +34,10 @@
# a row advances those entries to their next one. See docs/Disaggregated/notes/p5f/f1-report.md.
# ---------------------------------------------------------------------------------------------
#
# Espryt's readback path reads the framebuffer binding slot out of the block (the field's
# Espryt half retired at P5e for the UNBARRIERED path only; the barriered readback pull is the
# residual). The largest block of the census by far.
# 132 entries
GetFramebufferBindingSlot@ReadPixels
# The XFB program check at draw setup (FieldOwnership.def: "P3b/P4b (Espryt), P7 (Magma)").
# 11 entries (the TessellationXfbCapture / XfbCaptureBufferReuse family)
GetTransformFeedbackProgram@DrawArrays
# CopyImageSubData resolves both textures through the sticky forward (def: "P7").
# 6 entries
GetTextureObject@CopyImageSubData
# Magma's Clear path (the two DirectVulkan.Split.NamedBlit entries) - fm's first rows.
# 2 entries
# P5f fe measured at 881e050e: 181 entries, 175 green, 4 skipped, 2 red.
# Espryt retired all five previous first-blocker pairs, and their downstream reads:
# READ FBO surface classification + reply-only PixelPack; copy destination arm ordering;
# copy-image endpoint handles; capture archive/ranges; storage-block program handle.
# The complete lane reached no new first-blocker pair (see fe-report.md).
# Magma's Clear path remains fm's independently owned pair (two NamedBlit entries).
GetFramebufferBindingSlot@Clear
# CopyTexImage2D's texture-unit read (def: "P5e (Espryt unbarriered), P7 (Magma)").
# 2 entries
GetTextureUnitObject@CopyTexImage2D
# The program-name validation sticky forward at ShaderStorageBlockBinding (def: "P9").
# 2 entries
ValidateProgramName@ShaderStorageBlockBinding
@@ -19,34 +19,10 @@
# <(python3 scripts/gen_pipe_field_ownership.py --print-admitted) this-file
# and put the census it prints here.
#
# ---------------------------------------------------------------------------------------------
# RE-MEASURED ON THE WAVE-3 INTEGRATION TREE (pa + mv + gl merged), 181 lane entries, 181 green,
# ZERO Fatal pairs. The note this block used to carry - that it was measured at gl's base and the
# merge would retire three rows - was discharged exactly as written: the ratchet named
# GetProgramForDraw@DrawArrays as an expected pair that no longer appears, and named
# GetBufferBindingSlot@DrawArrays as one the lane had not seen before. Both are reconciled below.
# GetProgramForDispatch@DispatchCompute never reached this file (pa retired it before it was
# written) and the ordinary GetBoundVertexArray@DrawArrays is gone; the two that remain under that
# name are the client-vertex-array entries, which are escalation-barriered and P8's.
# ---------------------------------------------------------------------------------------------
#
# Admitted by the generated table (disjuncts 1 and 2 of ID-116 / ID-125):
GetBufferBindingSlot@ReadPixels
# NEW IN WAVE 3, and it was named before it could fire: mv's report called
# BoundDrawIndirectBufferId's GetBufferBindingSlot(DrawIndirect) a latent row that "would surface
# as GetBufferBindingSlot@DrawArrays if an indirect tier were ever forced under strict", and then
# gated the five multi-draw tiers, which forced it. Retires in P8 (FieldOwnership.def:78).
GetBufferBindingSlot@DrawArrays
GetFramebufferBindingSlot@ReadPixels
GetProgramObject@ShaderStorageBlockBinding
GetTextureObject@CopyImageSubData
GetTextureUnitObject@CopyTexImage2D
GetTransformFeedbackProgram@DrawArrays
ValidateProgramName@ShaderStorageBlockBinding
#
# Admitted by ESCALATION only (disjunct 3, ID-128): the record was barriered because its payload
# said so - an open transform-feedback span, or a draw carrying client vertex arrays - which no
# table indexed by (field, verb) can see. All three are pulls on the draw verb by entries that
# are NOT the ordinary draw path.
GetBoundVertexArray@DrawArrays
GetBufferBindingPoint@DrawArrays
# P5f fe census: 179 integration-split entries pass, with zero Fatal, ordinary Admitted,
# or ESCALATED pairs. The expected set is therefore empty; either new marker kind is red.
# Eight live pairs retired through server framebuffer/texture/program records, reply scratch,
# and the Begin XFB snapshot. The two already-stale rows GetBoundVertexArray@DrawArrays and
# GetBufferBindingSlot@DrawArrays were independently identified at the f1 baseline (f1-report
# section 8.1). fe-report records every removed pair and its source. P8 client-array tests
# retain their separate lane; an empty census here does not assert that P8 is implemented.
+9 -9
View File
@@ -83,11 +83,11 @@
/* (:2995) is SELF-DECLARED monolith glue (DirectGLES.cpp:2961-2965) while BindCurrentFBO */ \
/* (:4303-4353) is already split-clean - the bind is migrated, the sync is not. */ \
X(GetFramebufferBindingSlot, BARRIER_PULLED, "P5e (Espryt unbarriered), P7 (Magma)", \
"frontend BindingSlot<FramebufferObject> pointer") \
"P5f fe: Espryt reads framebuffer records; Magma retains frontend pointer debt") \
X(GetImageTextureBinding, BARRIER_PULLED, "P5e (Espryt unbarriered), P7 (Magma)", \
"frontend ImageTextureBinding base pointer") \
X(GetTextureUnitObject, BARRIER_PULLED, "P5e (Espryt unbarriered), P7 (Magma)", \
"frontend TextureUnit base pointer; 13 Espryt + 8 Magma sites") \
"P5f fe: Espryt transport readers retired; Magma retains texture-unit debt") \
/* DirectGLES.cpp:4497, PrepareForDraw, the second unconditional pointer read of every draw. */ \
X(GetProgramForDraw, BARRIER_PULLED, "P5e (Espryt unbarriered), P7 (Magma)", \
"frontend SharedPtr<ProgramObject>; the record carries a handle") \
@@ -104,7 +104,7 @@
/* case a field census taken from "what the scenario does" rather than from the mask would */ \
/* miss. The family's five VALUE rows rode set_context_values out of this list at P5c rv. */ \
X(GetTransformFeedbackProgram, BARRIER_PULLED, "P3b/P4b (Espryt), P7 (Magma)", \
"frontend SharedPtr<ProgramObject>") \
"P5f fe: Espryt capture uses the Begin snapshot; Magma retains program debt") \
\
/* ---- APPLIER_DERIVED ---- */ \
/* THE FIRST ROW, and it is the one the contract asked P5 to split into two field ids. It is */ \
@@ -144,14 +144,14 @@
/* pair that disagrees. Their poison exemption (generated/PipeFilled.inc:422's sticky arm) */ \
/* is cancelled under split by the server stamp, which zeroes their FilledGen - and 0 loses */ \
/* to nothing, because :419's "never filled" test runs first. */ \
X(GetBufferBindingPointCount, BARRIER_PULLED, "P7/P13", \
"an argument-keyed lookup into the frontend's binding-point table") \
X(GetBufferBindingPointCount, APPLIER_DERIVED, "-", \
"P5f fe: server constant binding-point capacity, no frontend lookup") \
X(GetProgramObject, BARRIER_PULLED, "P9", \
"hands the backend a frontend SharedPtr<ProgramObject> keyed by GL name") \
X(GetTextureObject, BARRIER_PULLED, "P7", \
"hands the backend a frontend SharedPtr<ITextureObject> keyed by GL name") \
X(HasOpenTransformFeedbackSpan, BARRIER_PULLED, "P7/P9", \
"a lookup into frontend XFB span state keyed by lifetime id") \
X(HasOpenTransformFeedbackSpan, APPLIER_DERIVED, "-", \
"P5f fe: begin/end_stream_output maintain server spans by lifetime id") \
X(ValidateProgramName, BARRIER_PULLED, "P9", \
"a frontend name-table probe; Coverage.def calls it kClientResolved") \
/* P5c ev DELETED the forward under an active transport (CONTRACT-P5C section 4.2): */ \
@@ -176,13 +176,13 @@
// Mechanism is what replaces the forward, which is NOT the same question as which phase
// retires the pull.
#define MGP_FIELD_OWNERSHIP_FORWARD_LIST(X) \
X(GetBufferBindingPointCount, BARRIER_PULLED, "P7/P13", \
X(GetBufferBindingPointCount, APPLIER_DERIVED, "-", \
"a server-side binding-point table") \
X(GetProgramObject, BARRIER_PULLED, "P9", \
"a client-resolved program handle table (ARCHITECTURE 3.2 explicitly-not-ported)") \
X(GetTextureObject, BARRIER_PULLED, "P7", \
"a server-side texture handle table") \
X(HasOpenTransformFeedbackSpan, BARRIER_PULLED, "P7/P9", \
X(HasOpenTransformFeedbackSpan, APPLIER_DERIVED, "-", \
"server-side XFB span state") \
X(ValidateProgramName, BARRIER_PULLED, "P9", \
"a client-resolved program name probe") \
+5 -1
View File
@@ -1669,8 +1669,12 @@ namespace MobileGL::MG_Pipe {
struct MGPStreamOutputBegin {
Uint32 PrimitiveMode;
Uint32 Pad0;
// P5f: immutable span snapshot; no frontend pointers survive Begin.
MGPipeHandle CaptureProgram;
Uint64 LifetimeId;
MGPBufferRange Targets[4];
};
MGP_ASSERT_POD(MGPStreamOutputBegin, 8);
MGP_ASSERT_POD(MGPStreamOutputBegin, 120);
// end_stream_output carries the accounting the client owns; the scatter itself is a
// read-modify-write of the client's shadow and lives there (section 7.2.1).
+13
View File
@@ -1379,6 +1379,12 @@ namespace MobileGL::MG_Pipe {
g_applier.DrawProgram = kMGPipeNullHandle;
g_applier.DispatchProgram = kMGPipeNullHandle;
g_applier.BoundShaderCso = kMGPipeNullHandle;
#if MOBILEGL_BUILD_DISAGGREGATED
g_applier.BoundStreamOutputLifetimeId = 0;
// Begin is emitted once per span, not on make-current. Its immutable object
// snapshot survives with the resource/program records until End or release;
// set_context_values restores the returning context's bound lifetime id.
#endif
// P5e (sb, CONTRACT-P5E.md §5.6): the three binding-point windows are per-context
// WORKING state and go with the rest of it - a returning context has its own
// glBindBufferBase history and may not inherit the one this applier was left holding.
@@ -1431,6 +1437,10 @@ namespace MobileGL::MG_Pipe {
g_applier.DrawProgram = kMGPipeNullHandle;
g_applier.DispatchProgram = kMGPipeNullHandle;
g_applier.BoundShaderCso = kMGPipeNullHandle;
#if MOBILEGL_BUILD_DISAGGREGATED
g_applier.BoundStreamOutputLifetimeId = 0;
g_applier.StreamOutputSpans.clear();
#endif
// AND "THE WORKING HANDLES THEY COULD NAME" IS ALL OF THEM, NOT JUST THE THREE ABOVE.
// Every framebuffer record holds eleven MGPSurface::Res naming texture and renderbuffer
// records this function has just dropped - which is why the table itself goes above -
@@ -1578,6 +1588,9 @@ namespace MobileGL::MG_Pipe {
// memory the rule exists to stop it reading. One store, inert until the caps bit is
// published, and it is what makes the two roles' third clause the same clause.
g_applier.IsTransformFeedbackActive = values.IsTransformFeedbackActive != 0;
#if MOBILEGL_BUILD_DISAGGREGATED
g_applier.BoundStreamOutputLifetimeId = values.BoundTransformFeedbackLifetimeId;
#endif
}
void MGPipeApplySetPatchState(const MGPPatchState& patch) {
+4
View File
@@ -846,6 +846,9 @@ namespace MobileGL::MG_Pipe {
Bool VerbBlitNamedConsumed = false;
// copy_framebuffer_to_texture's destination texture (MGPCopyFromFramebuffer::Dst).
MGPipeHandle VerbCopyTexDst = kMGPipeNullHandle;
MGPipeHandle VerbStorageBlockProgram = kMGPipeNullHandle;
Uint64 BoundStreamOutputLifetimeId = 0;
UnorderedMap<Uint64, MGPStreamOutputBegin> StreamOutputSpans;
// generate_mipmap's texture (MGPMipPlan::Res).
MGPipeHandle VerbMipRes = kMGPipeNullHandle;
// The current indirect draw's command buffer and (for the *Count forms) parameter
@@ -870,6 +873,7 @@ namespace MobileGL::MG_Pipe {
VerbBlitDrawFbo = kMGPipeNullHandle;
VerbBlitNamedConsumed = false;
VerbCopyTexDst = kMGPipeNullHandle;
VerbStorageBlockProgram = kMGPipeNullHandle;
VerbMipRes = kMGPipeNullHandle;
VerbIndirectBuffer = kMGPipeNullHandle;
VerbIndirectParameterBuffer = kMGPipeNullHandle;
+1 -1
View File
@@ -236,7 +236,7 @@
F(Bits) F(ByRegion)
#define MGP_FIELDS_MGPStreamOutputBegin(F) \
F(PrimitiveMode)
F(PrimitiveMode) F(CaptureProgram) F(LifetimeId) F(Targets)
#define MGP_FIELDS_MGPXfbAccounting(F) \
F(CapturedVertices) F(PrimitivesWritten) F(PrimitiveMode)
@@ -42,7 +42,7 @@ inline constexpr MGPipeFieldOwnership kMGPipeFieldOwnership[kMGPipeInputFieldCou
MGPipeFieldOwnership::kBarrierPulled, // GetBoundVertexArray
MGPipeFieldOwnership::kBarrierPulled, // GetBufferBindingSlot
MGPipeFieldOwnership::kBarrierPulled, // GetBufferBindingPoint
MGPipeFieldOwnership::kBarrierPulled, // GetBufferBindingPointCount
MGPipeFieldOwnership::kApplierDerived, // GetBufferBindingPointCount
MGPipeFieldOwnership::kRecordSupplied, // GetTouchedBufferBindingPointCount
MGPipeFieldOwnership::kRecordSupplied, // GetClampReadColor
MGPipeFieldOwnership::kRecordSupplied, // GetClearColor
@@ -96,7 +96,7 @@ inline constexpr MGPipeFieldOwnership kMGPipeFieldOwnership[kMGPipeInputFieldCou
MGPipeFieldOwnership::kBarrierPulled, // ValidateProgramName
MGPipeFieldOwnership::kBarrierPulled, // RecordError
MGPipeFieldOwnership::kRecordSupplied, // GetBoundTransformFeedbackLifetimeId
MGPipeFieldOwnership::kBarrierPulled, // HasOpenTransformFeedbackSpan
MGPipeFieldOwnership::kApplierDerived, // HasOpenTransformFeedbackSpan
};
// The ROADMAP phase whose row retires the pull. "-" for every class but BARRIER-PULLED.
@@ -109,7 +109,7 @@ inline constexpr const char* kMGPipeFieldRetiringPhase[kMGPipeInputFieldCount] =
"P5e (Espryt unbarriered), P7 (Magma)", // GetBoundVertexArray
"P8 (indirect), P9 (readback), P13 (transfer)", // GetBufferBindingSlot
"P5e (Espryt unbarriered), P7 (Magma)", // GetBufferBindingPoint
"P7/P13", // GetBufferBindingPointCount
"-", // GetBufferBindingPointCount
"-", // GetTouchedBufferBindingPointCount
"-", // GetClampReadColor
"-", // GetClearColor
@@ -163,7 +163,7 @@ inline constexpr const char* kMGPipeFieldRetiringPhase[kMGPipeInputFieldCount] =
"P9", // ValidateProgramName
"P9", // RecordError
"-", // GetBoundTransformFeedbackLifetimeId
"P7/P9", // HasOpenTransformFeedbackSpan
"-", // HasOpenTransformFeedbackSpan
};
// The seven sticky forwards, which are among the 63 above and need a row of their own:
@@ -182,19 +182,19 @@ inline constexpr MGPipeInputField kMGPipeFieldOwnershipForwardField[kMGPipeField
MGPipeInputField::RecordError,
};
inline constexpr MGPipeFieldOwnership kMGPipeFieldOwnershipForward[kMGPipeFieldOwnershipForwardCount] = {
MGPipeFieldOwnership::kBarrierPulled, // GetBufferBindingPointCount
MGPipeFieldOwnership::kApplierDerived, // GetBufferBindingPointCount
MGPipeFieldOwnership::kBarrierPulled, // GetProgramObject
MGPipeFieldOwnership::kBarrierPulled, // GetTextureObject
MGPipeFieldOwnership::kBarrierPulled, // HasOpenTransformFeedbackSpan
MGPipeFieldOwnership::kApplierDerived, // HasOpenTransformFeedbackSpan
MGPipeFieldOwnership::kBarrierPulled, // ValidateProgramName
MGPipeFieldOwnership::kFatal, // InvalidateCompileEnv
MGPipeFieldOwnership::kBarrierPulled, // RecordError
};
inline constexpr const char* kMGPipeFieldOwnershipForwardMechanism[kMGPipeFieldOwnershipForwardCount] = {
"a server-side binding-point table", // GetBufferBindingPointCount, retires in P7/P13
"a server-side binding-point table", // GetBufferBindingPointCount, retires in -
"a client-resolved program handle table (ARCHITECTURE 3.2 explicitly-not-ported)", // GetProgramObject, retires in P9
"a server-side texture handle table", // GetTextureObject, retires in P7
"server-side XFB span state", // HasOpenTransformFeedbackSpan, retires in P7/P9
"server-side XFB span state", // HasOpenTransformFeedbackSpan, retires in -
"a client-resolved program name probe", // ValidateProgramName, retires in P9
"DELETED under split by P5c ev; the re-arriving caps snapshot IS the invalidation (R-12)", // InvalidateCompileEnv, retires in -
"OnGlError, the ordered reverse-channel error post (R-12); its ordering is P9's", // RecordError, retires in P9
@@ -323,7 +323,7 @@ inline constexpr SizeT kMGPipeVerbBoundaryExemptCount = 3;
// wait. A verb with no stamp row admits nothing - the server stamps only MGP_VERB_OP_LIST's
// ops, so no other verb can be the one a residual pull is reported against.
inline constexpr MGPipeFieldMask kMGPipeAdmittedPullMask[kMGPipeVerbCount] = {
{{0x5808402000000140ull, 0x0000000000000000ull}}, // DrawArrays
{{0x1808402000000040ull, 0x0000000000000000ull}}, // DrawArrays
{{0x0000000000000000ull, 0x0000000000000000ull}}, // DrawElements
{{0x0000000000000000ull, 0x0000000000000000ull}}, // DrawElementsBaseVertex
{{0x0000000000000000ull, 0x0000000000000000ull}}, // MultiDrawArrays
@@ -343,7 +343,7 @@ inline constexpr MGPipeFieldMask kMGPipeAdmittedPullMask[kMGPipeVerbCount] = {
{{0x0000000000000000ull, 0x0000000000000000ull}}, // DrawArraysInstanced
{{0x0000000000000000ull, 0x0000000000000000ull}}, // DrawElementsIndirect
{{0x0000000000000000ull, 0x0000000000000000ull}}, // DrawArraysIndirect
{{0x5800402000000100ull, 0x0000000000000000ull}}, // Clear
{{0x1800402000000000ull, 0x0000000000000000ull}}, // Clear
{{0x0000000000000000ull, 0x0000000000000000ull}}, // ClearBufferfi
{{0x0000000000000000ull, 0x0000000000000000ull}}, // ClearBufferfv
{{0x0000000000000000ull, 0x0000000000000000ull}}, // ClearBufferuiv
@@ -352,27 +352,27 @@ inline constexpr MGPipeFieldMask kMGPipeAdmittedPullMask[kMGPipeVerbCount] = {
{{0x0000000000000000ull, 0x0000000000000000ull}}, // ClearNamedFramebufferfi
{{0x0000000000000000ull, 0x0000000000000000ull}}, // ClearNamedFramebufferiv
{{0x0000000000000000ull, 0x0000000000000000ull}}, // ClearNamedFramebufferuiv
{{0x5800402000000100ull, 0x0000000000000000ull}}, // BlitFramebuffer
{{0x1800402000000000ull, 0x0000000000000000ull}}, // BlitFramebuffer
{{0x0000000000000000ull, 0x0000000000000000ull}}, // BlitNamedFramebuffer
{{0x5800c02000300180ull, 0x0000000000000000ull}}, // CopyTexImage2D
{{0x1800c02000300080ull, 0x0000000000000000ull}}, // CopyTexImage2D
{{0x0000000000000000ull, 0x0000000000000000ull}}, // CopyTexSubImage2D
{{0x5800c02000300180ull, 0x0000000000000000ull}}, // CopyImageSubData
{{0x5800c02000300180ull, 0x0000000000000000ull}}, // GenerateMipmap
{{0x5800c02000300140ull, 0x0000000000000000ull}}, // ReadPixels
{{0x1800c02000300080ull, 0x0000000000000000ull}}, // CopyImageSubData
{{0x1800c02000300080ull, 0x0000000000000000ull}}, // GenerateMipmap
{{0x1800c02000300040ull, 0x0000000000000000ull}}, // ReadPixels
{{0x0000000000000000ull, 0x0000000000000000ull}}, // GetTexImage
{{0x5800c02000300140ull, 0x0000000000000000ull}}, // GetTextureImage
{{0x5800402000000140ull, 0x0000000000000000ull}}, // DispatchCompute
{{0x1800c02000300040ull, 0x0000000000000000ull}}, // GetTextureImage
{{0x1800402000000040ull, 0x0000000000000000ull}}, // DispatchCompute
{{0x0000000000000000ull, 0x0000000000000000ull}}, // DispatchComputeIndirect
{{0x5800402000000100ull, 0x0000000000000000ull}}, // MemoryBarrier
{{0x1800402000000000ull, 0x0000000000000000ull}}, // MemoryBarrier
{{0x0000000000000000ull, 0x0000000000000000ull}}, // MemoryBarrierByRegion
{{0x5800402000000100ull, 0x0000000000000000ull}}, // BindImageTexture
{{0x1800402000000000ull, 0x0000000000000000ull}}, // BindImageTexture
{{0x0000000000000000ull, 0x0000000000000000ull}}, // GetIntegeri_v
{{0x5800c02000300100ull, 0x0000000000000000ull}}, // ShaderStorageBlockBinding
{{0x5800402000000100ull, 0x0000000000000000ull}}, // FenceSync
{{0x5800402000000100ull, 0x0000000000000000ull}}, // ClientWaitSync
{{0x5800402000000100ull, 0x0000000000000000ull}}, // WaitSync
{{0x5800402000000100ull, 0x0000000000000000ull}}, // DeleteSync
{{0x5800402000000100ull, 0x0000000000000000ull}}, // GetSyncStatus
{{0x1800c02000300000ull, 0x0000000000000000ull}}, // ShaderStorageBlockBinding
{{0x1800402000000000ull, 0x0000000000000000ull}}, // FenceSync
{{0x1800402000000000ull, 0x0000000000000000ull}}, // ClientWaitSync
{{0x1800402000000000ull, 0x0000000000000000ull}}, // WaitSync
{{0x1800402000000000ull, 0x0000000000000000ull}}, // DeleteSync
{{0x1800402000000000ull, 0x0000000000000000ull}}, // GetSyncStatus
{{0x0000000000000000ull, 0x0000000000000000ull}}, // IsTimerQuerySupported
{{0x0000000000000000ull, 0x0000000000000000ull}}, // BeginTimeElapsedQuery
{{0x0000000000000000ull, 0x0000000000000000ull}}, // EndTimeElapsedQuery
@@ -384,17 +384,17 @@ inline constexpr MGPipeFieldMask kMGPipeAdmittedPullMask[kMGPipeVerbCount] = {
{{0x0000000000000000ull, 0x0000000000000000ull}}, // EndOcclusionQuery
{{0x0000000000000000ull, 0x0000000000000000ull}}, // BeginXfbPrimitivesQuery
{{0x0000000000000000ull, 0x0000000000000000ull}}, // EndXfbPrimitivesQuery
{{0x5800402000000100ull, 0x0000000000000000ull}}, // PatchParameteri
{{0x5808402000000180ull, 0x0000000000000000ull}}, // BeginTransformFeedback
{{0x5808402000000180ull, 0x0000000000000000ull}}, // EndTransformFeedback
{{0x5808402000000180ull, 0x0000000000000000ull}}, // PauseTransformFeedback
{{0x5808402000000180ull, 0x0000000000000000ull}}, // ResumeTransformFeedback
{{0x5808402000000180ull, 0x0000000000000000ull}}, // BindTransformFeedback
{{0x1800402000000000ull, 0x0000000000000000ull}}, // PatchParameteri
{{0x1808402000000080ull, 0x0000000000000000ull}}, // BeginTransformFeedback
{{0x1808402000000080ull, 0x0000000000000000ull}}, // EndTransformFeedback
{{0x1808402000000080ull, 0x0000000000000000ull}}, // PauseTransformFeedback
{{0x1808402000000080ull, 0x0000000000000000ull}}, // ResumeTransformFeedback
{{0x1808402000000080ull, 0x0000000000000000ull}}, // BindTransformFeedback
{{0x0000000000000000ull, 0x0000000000000000ull}}, // DeleteTransformFeedback
{{0x0000000000000000ull, 0x0000000000000000ull}}, // GetGpuTimestampNs
};
inline constexpr SizeT kMGPipeAdmittedPullPairCount = 174;
inline constexpr SizeT kMGPipeAdmittedPullPairCount = 128;
// THE PREDICATE THE STRICT KNOB ASKS (ID-117). A 69 x 128-bit table, so this costs a shift and
// a test on the apply thread - the same shape MGPipeFieldMaskHas already has.
@@ -428,7 +428,7 @@ static_assert(MGPipeEveryAdmittedPullIsADebtOfItsVerbsClass(),
// The class sizes, as constants a test can pin without recounting the table.
inline constexpr SizeT kMGPipeRecordSuppliedFieldCount = 41;
inline constexpr SizeT kMGPipeApplierDerivedFieldCount = 4;
inline constexpr SizeT kMGPipeBarrierPulledFieldCount = 15;
inline constexpr SizeT kMGPipeApplierDerivedFieldCount = 6;
inline constexpr SizeT kMGPipeBarrierPulledFieldCount = 13;
inline constexpr SizeT kMGPipeFatalFieldCount = 3;
static_assert(kMGPipeRecordSuppliedFieldCount + kMGPipeApplierDerivedFieldCount + kMGPipeBarrierPulledFieldCount + kMGPipeFatalFieldCount == kMGPipeInputFieldCount, "the four class sizes do not partition the field set");
+19
View File
@@ -62,6 +62,7 @@
#include <MG_Impl/Pipe/FramebufferEmit.h>
#include <MG_Impl/Pipe/PipeFill.h>
#include <MG_Impl/Pipe/TextureEmit.h>
#include <MG_Impl/Pipe/ProgramEmit.h>
#include <MG_Pipe/PipeMutation.h>
namespace MobileGL::MG_Remote::Client {
@@ -1412,6 +1413,24 @@ namespace MobileGL::MG_Remote::Client {
// The GL token verbatim (contract table 0's "GL enums on the wire"): the sink hands
// it to the backend slot that takes it, and nothing between here and there reads it.
record.PrimitiveMode = static_cast<Uint32>(primitiveMode);
auto& context = *MG_State::pGLContext;
record.LifetimeId = context.GetBoundTransformFeedbackLifetimeId();
const auto& program = context.GetTransformFeedbackProgram();
if (program) {
Uint64 bytes = 0;
record.CaptureProgram = MG_Pipe::MGPipeProgramEmitterInstance().AcquireShaderCso(*program, bytes);
}
static_assert(MG_State::GLState::GLContext::MAX_TRANSFORM_FEEDBACK_BUFFERS == 4);
for (Uint i = 0; i < 4; ++i) {
const auto& point = context.GetBufferBindingPoint(BufferTarget::TransformFeedback, i);
const auto& buffer = point.GetBoundObject();
if (!buffer) continue;
const auto range = point.GetRange();
const SizeT start = std::min(range.start, buffer->GetSize());
const SizeT end = std::min(range.end, buffer->GetSize());
record.Targets[i] = {MG_Pipe::MGPipeResourceTrackerInstance().Find(*buffer), start,
end > start ? end - start : 0};
}
session.EmitAndWait(MG_Pipe::MGPWireOp::BeginStreamOutput, &record, sizeof(record),
nullptr, 0, nullptr, 0, nullptr);
}
+16 -4
View File
@@ -855,9 +855,9 @@ namespace MobileGL::MG_Remote::Server {
// retires them by making the backend take the handles that travel beside the names.
MG_Backend::CopyImageEndpoint src{};
MG_Backend::CopyImageEndpoint dst{};
src.Texture = MG_Pipe::gPipeInputs.GetTextureObject(static_cast<Uint>(copy.SrcGlName));
dst.Texture = MG_Pipe::gPipeInputs.GetTextureObject(static_cast<Uint>(copy.DstGlName));
if (!src.Exists() || !dst.Exists()) {
src.TextureHandle = copy.Src;
dst.TextureHandle = copy.Dst;
if (MG_Pipe::MGPipeHandleIsNull(copy.Src) || MG_Pipe::MGPipeHandleIsNull(copy.Dst)) {
// The monolith's own answer to this, in its own words (DirectGLES.cpp:9067
// "source or destination image failed to sync; declining the copy"): the frontend
// validator is what keeps it unreachable and what reports the INVALID_VALUE the
@@ -921,6 +921,8 @@ namespace MobileGL::MG_Remote::Server {
// Both backends resolve the PROGRAM through the barrier-pulled GetProgramObject(GlName)
// / TryGetDirectVulkanProgram - `rsp` again, retired by P9. ShaderCso travels beside the
// name for the phase that dispatches on it.
MG_Pipe::MGPipeApplier().ClearVerbHandles();
MG_Pipe::MGPipeApplier().VerbStorageBlockProgram = binding.ShaderCso;
table->GL.ShaderStorageBlockBinding(static_cast<GLuint>(binding.GlName), name,
static_cast<GLuint>(binding.Binding));
++m_storageBlockBindings;
@@ -944,6 +946,9 @@ namespace MobileGL::MG_Remote::Server {
// crash or as a silent success. Contract §2 t2 says so for PatchParameteri by name.
Bool ServerVerbSink::OnBeginStreamOutput(const MG_Pipe::MGPStreamOutputBegin& begin) {
auto& state = MG_Pipe::MGPipeApplier();
state.BoundStreamOutputLifetimeId = begin.LifetimeId;
state.StreamOutputSpans[begin.LifetimeId] = begin;
const MG_Backend::GlobalBackendFunctionsTable* table = Table("begin_stream_output");
if (table == nullptr) return false;
if (table->GL.BeginTransformFeedback == nullptr) return false;
@@ -961,7 +966,11 @@ namespace MobileGL::MG_Remote::Server {
Bool ServerVerbSink::OnEndStreamOutput(const MG_Pipe::MGPXfbAccounting& accounting) {
const MG_Backend::GlobalBackendFunctionsTable* table = Table("end_stream_output");
if (table == nullptr) return false;
if (table->GL.EndTransformFeedback == nullptr) return false;
if (table->GL.EndTransformFeedback == nullptr) {
auto& state = MG_Pipe::MGPipeApplier();
state.StreamOutputSpans.erase(state.BoundStreamOutputLifetimeId);
return false;
}
// THE THREE ACCOUNTING FIELDS ARE NOT READ, AND THAT IS THE RULING RATHER THAN AN
// OMISSION. glEndTransformFeedback takes no arguments; the numbers are the CLIENT's own
// per-span accounting (contract §2 t2's companions row) and the client is where they are
@@ -972,6 +981,8 @@ namespace MobileGL::MG_Remote::Server {
// server-side scatter is what will need them.
(void)accounting;
table->GL.EndTransformFeedback();
auto& state = MG_Pipe::MGPipeApplier();
state.StreamOutputSpans.erase(state.BoundStreamOutputLifetimeId);
++m_streamOutputSpans;
return true;
}
@@ -1008,6 +1019,7 @@ namespace MobileGL::MG_Remote::Server {
// lifetime of its own - it has no reader on this side today, and pretending otherwise
// by folding it into the key is exactly the "a GL name is never an identity" confusion
// the contract's GlName row is written against.
MG_Pipe::MGPipeApplier().BoundStreamOutputLifetimeId = bind.LifetimeId;
table->GL.BindTransformFeedback(static_cast<GLuint>(bind.GlName));
++m_streamOutputBinds;
return true;
+75 -5
View File
@@ -263,10 +263,8 @@ TEST_F(FieldOwnershipTest, TheReducedPathsUnmigratedFieldsAreAllAccountedFor) {
// (CONTRACT-P5C.md §7 table 2) - and the only other BARRIER-PULLED rows are six of the
// seven sticky forwards (the seventh, InvalidateCompileEnv, is FATAL since P5c ev).
const MGPipeInputField pulledSticky[] = {
MGPipeInputField::GetBufferBindingPointCount,
MGPipeInputField::GetProgramObject,
MGPipeInputField::GetTextureObject,
MGPipeInputField::HasOpenTransformFeedbackSpan,
MGPipeInputField::ValidateProgramName,
MGPipeInputField::RecordError,
};
@@ -281,7 +279,7 @@ TEST_F(FieldOwnershipTest, TheReducedPathsUnmigratedFieldsAreAllAccountedFor) {
EXPECT_TRUE(named) << kMGPipeInputFieldNames[i]
<< " is BARRIER-PULLED and not in the pinned object-class list";
}
EXPECT_EQ(pulledCount, 15u) << "9 non-sticky object rows + 6 sticky forwards";
EXPECT_EQ(pulledCount, 13u) << "9 non-sticky object rows + 4 sticky forwards";
EXPECT_EQ(pulledCount, kMGPipeBarrierPulledFieldCount);
EXPECT_EQ(MGPipeFieldOwnershipOf(MGPipeInputField::GetPixelStoreParameters),
MGPipeFieldOwnership::kApplierDerived);
@@ -705,8 +703,11 @@ TEST_F(FieldOwnershipTest, TheStickyExemptionIsCancelledByTheServerStamp) {
MGPipeServerStampVerbBoundary(MGPipeVerb::Clear);
for (SizeT i = 0; i < kMGPipeFieldOwnershipForwardCount; ++i) {
const MGPipeInputField field = kMGPipeFieldOwnershipForwardField[i];
EXPECT_FALSE(MGPipeInputFieldIsFresh(gPipeInputs.FilledState(), field))
<< kMGPipeInputFieldNames[Index(field)] << " is still exempt under split";
// 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)
<< kMGPipeInputFieldNames[Index(field)] << " has the wrong server-stamp freshness";
}
}
@@ -899,6 +900,75 @@ namespace {
};
} // namespace
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(MGPipeFieldOwnershipOf(MGPipeInputField::GetBufferBindingPointCount),
MGPipeFieldOwnership::kApplierDerived);
MGPipeServerClearVerbBoundary();
}
TEST_F(FieldOwnershipTest, SplitOpenSpansAreOwnedByTheApplierAndSurviveOtherBindings) {
RoleSplitArm guard;
guard.Arm(true);
auto& state = MGPipeApplier();
state.StreamOutputSpans.clear();
MGPipeServerStampVerbBoundary(MGPipeVerb::DrawArrays);
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));
state.StreamOutputSpans.erase(12);
EXPECT_TRUE(gPipeInputs.HasOpenTransformFeedbackSpan(11));
EXPECT_FALSE(gPipeInputs.HasOpenTransformFeedbackSpan(12));
EXPECT_EQ(MGPipeFieldOwnershipOf(MGPipeInputField::HasOpenTransformFeedbackSpan),
MGPipeFieldOwnership::kApplierDerived);
state.StreamOutputSpans.clear();
state.BoundStreamOutputLifetimeId = 0;
MGPipeServerClearVerbBoundary();
}
TEST_F(FieldOwnershipTest, SplitCaptureSnapshotSurvivesMakeCurrentUntilObjectRelease) {
RoleSplitArm guard;
guard.Arm(true);
auto& state = MGPipeApplier();
state.StreamOutputSpans.clear();
MGPStreamOutputBegin begin{};
begin.LifetimeId = 0x100000002ull;
begin.CaptureProgram = {7, 2};
begin.Targets[3] = {{9, 3}, 16, 64};
state.StreamOutputSpans[begin.LifetimeId] = begin;
state.BoundStreamOutputLifetimeId = begin.LifetimeId;
// Make-current resets binding state, not a live object's open capture. The
// returning context emits context values again, but never repeats Begin.
MGPipeApplierReset();
MGPContextValues returning{};
returning.BoundTransformFeedbackLifetimeId = begin.LifetimeId;
returning.IsTransformFeedbackActive = 1;
MGPipeApplySetContextValues(returning);
EXPECT_EQ(state.BoundStreamOutputLifetimeId, 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()) {
EXPECT_EQ(found->second.CaptureProgram, begin.CaptureProgram);
EXPECT_EQ(found->second.Targets[3].Res, begin.Targets[3].Res);
EXPECT_EQ(found->second.Targets[3].Offset, 16u);
EXPECT_EQ(found->second.Targets[3].Size, 64u);
}
MGPipeApplierReleaseObjectRecords();
EXPECT_FALSE(gPipeInputs.HasOpenTransformFeedbackSpan(begin.LifetimeId));
EXPECT_EQ(state.BoundStreamOutputLifetimeId, 0u);
}
TEST_F(FieldOwnershipTest, RoleSplitOffFoldsTheFillSideOntoTheSharedBlock) {
EXPECT_FALSE(MGPipeRoleSplitActive());
EXPECT_EQ(&MGPipeClientInputs(), &gPipeInputs);
@@ -1194,6 +1194,9 @@ TEST_F(PipeWireCodecTest, TheFourStreamOutputSpanRowsReachTheSink) {
Wire2 wire;
MGPStreamOutputBegin begin{};
begin.PrimitiveMode = 4;
begin.CaptureProgram = MakeHandle(95);
begin.LifetimeId = 0x100000002ull;
begin.Targets[3] = {MakeHandle(96), 0x100000010ull, 64};
MGPStreamOutputControl control{};
MGPXfbAccounting end{};
end.CapturedVertices = 300;
@@ -1210,6 +1213,11 @@ TEST_F(PipeWireCodecTest, TheFourStreamOutputSpanRowsReachTheSink) {
}
ASSERT_EQ(wire.Sink().Begins.size(), 1u);
EXPECT_EQ(wire.Sink().Begins[0].PrimitiveMode, 4u);
EXPECT_EQ(wire.Sink().Begins[0].CaptureProgram, MakeHandle(95));
EXPECT_EQ(wire.Sink().Begins[0].LifetimeId, 0x100000002ull);
EXPECT_EQ(wire.Sink().Begins[0].Targets[3].Res, MakeHandle(96));
EXPECT_EQ(wire.Sink().Begins[0].Targets[3].Offset, 0x100000010ull);
EXPECT_EQ(wire.Sink().Begins[0].Targets[3].Size, 64u);
EXPECT_EQ(wire.Sink().Pauses, 1u);
EXPECT_EQ(wire.Sink().Resumes, 1u);
ASSERT_EQ(wire.Sink().Ends.size(), 1u);