[Chore] (docs, MobileGL, scripts, tools, .github): use US spelling in identifiers and file names

This commit is contained in:
BZLZHH
2026-10-31 20:06:22 +08:00
parent 4f50bedde0
commit 6d3b00e820
180 changed files with 1070 additions and 1073 deletions
+1 -1
View File
@@ -45,7 +45,7 @@ runs:
sudo tee /etc/apt/apt.conf.d/80-mobilegl-ci-network > /dev/null
fi
# Cores go through zstd: a unit run's intentional aborts (death tests) left hundreds
# of raw cores in /tmp and filled the disk. The handler keeps honouring each
# of raw cores in /tmp and filled the disk. The handler keeps honoring each
# process's RLIMIT_CORE (%c), so only steps that raise it with ulimit -c get cores;
# read one with 'zstd -d core.<exe>.<pid>.zst' first.
if [ ! -f /.dockerenv ] && command -v sudo > /dev/null && command -v zstd > /dev/null; then
+3 -3
View File
@@ -23,7 +23,7 @@ env:
# Software-rendered CI submissions may exceed the interactive GPU hang budget.
MOBILEGL_GPU_HANG_BUDGET_MS: "0"
# ONE LIVE RUN PER BRANCH, as in test.yml: a superseded push is cancelled rather than left queued
# ONE LIVE RUN PER BRANCH, as in test.yml: a superseded push is canceled rather than left queued
# behind its successor; a manual dispatch gets a group of its own; dev queues rather than cancels.
concurrency:
group: ${{ github.workflow }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.ref }}
@@ -438,7 +438,7 @@ jobs:
strategy:
fail-fast: false
# The account runs at most 20 jobs at once, shared with Test. These ~230 emulator jobs are
# the longer of the two runs, so they may take the whole pool: what is minimised is when the
# the longer of the two runs, so they may take the whole pool: what is minimized is when the
# later of APK and Test finishes, not Test alone.
max-parallel: 20
matrix: ${{ fromJSON(needs.trace-cases.outputs.android) }}
@@ -829,7 +829,7 @@ jobs:
# Fail before deletion if either paginated inventory cannot be read.
# 50 rows per page: the jobs endpoint answers HTTP 502 for 90+ rows on a re-run attempt of a run this size.
gh api --paginate "repos/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}/jobs?per_page=50" \
--jq '.jobs[] | select(.name | startswith("retrace (")) | select(.conclusion == "failure" or .conclusion == "cancelled" or .conclusion == "timed_out" or .conclusion == "action_required") | .name' \
--jq '.jobs[] | select(.name | startswith("retrace (")) | select(.conclusion == "failure" or .conclusion == "canceled" or .conclusion == "timed_out" or .conclusion == "action_required") | .name' \
> "${RUNNER_TEMP}/cleanup-jobs.txt"
gh api --paginate "repos/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}/artifacts?per_page=100" \
--jq '.artifacts[] | select(.name | startswith("MobileGL-trace-fixture-") or startswith("MobileGL-android-retrace-result-") or startswith("trace-fixture-") or startswith("retrace-result-")) | [.id, .name] | @tsv' \
+4 -4
View File
@@ -1544,7 +1544,7 @@ jobs:
- name: Spawn/split lane parity (9.2)
run: python3 scripts/ci/spawn_lane_parity.py "${SPLIT_BUILD_DIR}"
# P8-A (ID-P8-2): every monolith case, tails normalised, is on a gated split arm or is named
# P8-A (ID-P8-2): every monolith case, tails normalized, is on a gated split arm or is named
# with a reason class in scripts/data/split_coverage_exemptions.txt. The same `ctest -N`
# cost as the parity step above, and the question it cannot answer: parity compares the
# split arms with EACH OTHER, so a case missing from all three passes it.
@@ -1553,7 +1553,7 @@ jobs:
python3 scripts/ci/split_coverage.py --self-test
python3 scripts/ci/split_coverage.py "${SPLIT_BUILD_DIR}"
# P8-SE: PipeCatalogueTest's unmigrated-emulation list equals the MGPipeUnmigratedEmulation
# P8-SE: PipeCatalogTest's unmigrated-emulation list equals the MGPipeUnmigratedEmulation
# call sites in the tree, one site per name. A source lint: no build needed.
- name: Unmigrated-emulation list against its call sites (P8-SE)
run: |
@@ -3626,7 +3626,7 @@ jobs:
# an unchecked process substitution.
# 50 rows per page: the jobs endpoint answers HTTP 502 for 90+ rows on a re-run attempt of a run this size.
gh api --paginate "repos/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}/jobs?per_page=50" \
--jq '.jobs[] | select((.name | startswith("retrace (")) or (.name | startswith("retrace verify (")) or (.name | startswith("retrace split (")) or (.name | startswith("retrace shipped library ("))) | select(.conclusion == "failure" or .conclusion == "cancelled" or .conclusion == "timed_out" or .conclusion == "action_required") | .name' \
--jq '.jobs[] | select((.name | startswith("retrace (")) or (.name | startswith("retrace verify (")) or (.name | startswith("retrace split (")) or (.name | startswith("retrace shipped library ("))) | select(.conclusion == "failure" or .conclusion == "canceled" or .conclusion == "timed_out" or .conclusion == "action_required") | .name' \
> "${RUNNER_TEMP}/cleanup-jobs.txt"
gh api --paginate "repos/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}/artifacts?per_page=100" \
--jq '.artifacts[] | select(.name | startswith("trace-fixture-") or startswith("retrace-result-") or startswith("retrace-split-result-") or startswith("retrace-shipped-result-")) | [.id, .name] | @tsv' \
@@ -3688,7 +3688,7 @@ jobs:
# The seven generators all read MG_Pipe/*.def, so regenerating and diffing is what
# keeps the two interface tables, the wire records, the verify comparators, the
# PipeInputs field ids, the read-inventory coverage and the render-state member list
# from drifting apart from the catalogue. The generated files are committed
# from drifting apart from the catalog. The generated files are committed
# deliberately: the build must not depend on python.
- name: Configure runner environment
uses: ./.github/actions/setup-test-runner
+5 -5
View File
@@ -34,7 +34,7 @@ option(MOBILEGL_EXTMEM_PROBE_AS_SERVER "Spike only (P11 B2 step 1): also package
# The PipeInputs strangler (ARCHITECTURE.md 9.2) is finished: since P13 every build pushes, and
# there is neither an option nor a macro for it (W3a removed the option, W3b the `#if` guards and
# the definition). A -DMOBILEGL_PIPE_PUSH=OFF (or the OFF that every pre-P13 build tree holds in its
# cache, the old default) is named and dropped rather than honoured: a warning, not an error, so an
# cache, the old default) is named and dropped rather than honored: a warning, not an error, so an
# existing tree reconfigures. The RUNTIME MOBILEGL_PIPE_PUSH environment bitmask is unrelated.
if (DEFINED CACHE{MOBILEGL_PIPE_PUSH} AND NOT "$CACHE{MOBILEGL_PIPE_PUSH}")
message(WARNING "MobileGL: MOBILEGL_PIPE_PUSH=OFF asks for the pull build, which P13 retired; "
@@ -78,7 +78,7 @@ if (ANDROID)
# Read the level back from the compiler target triple first. Its trailing
# number (aarch64-none-linux-android26) is precisely what clang turns into
# __ANDROID_API__, so it cannot disagree with the compile itself, and it is
# already past every NDK normalisation step - codename aliases, "latest",
# already past every NDK normalization step - codename aliases, "latest",
# and per-ABI minimum pull-ups. ANDROID_PLATFORM_LEVEL is the fallback for
# generators/languages where the triple variable is not populated.
#
@@ -257,7 +257,7 @@ string(SUBSTRING "${GIT_COMMIT_HASH_FULL}" 0 7 GIT_COMMIT_HASH_SHORT)
#
# GIT_COMMIT_HASH_SHORT MAY NOT MOVE. It is baked into glGetString(GL_VERSION)
# (MG_Impl/GLImpl/Getter/GL_Getter.cpp), so redefining it changes an APPLICATION-VISIBLE GL
# string and the pull build's .rodata - a behaviour leak and a G1 break at once. The handshake
# string and the pull build's .rodata - a behavior leak and a G1 break at once. The handshake
# gets its own pair instead, consumed only by the ABI fingerprint, which no pull build compiles.
#
# PRESENT IS A FIRST-CLASS INPUT, not the emptiness of VALUE. "this build has no stamp" and
@@ -633,7 +633,7 @@ if (MOBILEGL_BUILD_DISAGGREGATED AND
# A NORMAL variable, deliberately not `CACHE BOOL ... FORCE`: forcing OFF into the cache
# made the plain re-configure after `git submodule update` stay OFF with no message at
# all. Shadowing the cache entry for this configure only keeps the operator's ON where it
# was, so the next configure - with the submodule there - honours it.
# was, so the next configure - with the submodule there - honors it.
set(MOBILEGL_BUILD_DISAGGREGATED OFF)
# And with it the shim option, or the `-DMOBILEGL_BUILD_DISAGGREGATED_INPROC=1` below
# would still be defined over a build with no MG_Remote in it at all.
@@ -659,7 +659,7 @@ list(APPEND SOURCE_FILES
# VisitFields tables. Push-only for the same G1 reason as the three above - in
# monolith the archive never crosses (create_shader_state hands the two structs over
# by pointer beside the record), so the codec is live code only in the VERIFY lane,
# where the applier serialises, deserialises and field-compares before storing.
# where the applier serializes, deserializes and field-compares before storing.
MobileGL/MG_State/GLState/ProgramState/ProgramArtifactsCodec.cpp
# P5's contract (integrator ruling R-17, package c1): the MONOLITH arm of the
# client->wire routing - the thirty-seven adapters that install gMGPipeScreen /
+1 -1
View File
@@ -145,7 +145,7 @@ namespace MobileGL {
// ORDER is the fix - not a wait, and not a friendlier default mask.
//
// MG_State::Init() constructs GLContext, and GLContext's TextureState constructor
// materialises one default texture object per target (GL 3.3 core 3.8,
// materializes one default texture object per target (GL 3.3 core 3.8,
// TextureState.cpp:62-71). TextureObjectBase's constructor emits resource_create for
// every one of them (TextureState/TextureObject.cpp:168-169), and that emission is
// gated, through PipeFill's FamilyIsLive, on
@@ -980,15 +980,15 @@ namespace MobileGL::MG_Backend::DirectGLES {
// only attachment is depth it never can, because the color emulation has no attachment
// to take a format from (KHR-GL33.framebuffer_blit's depth config test blits
// COLOR|DEPTH|STENCIL across depth-only framebuffers and kept reading the clear value).
const GLbitfield colourBit = mask & static_cast<GLbitfield>(GL_COLOR_BUFFER_BIT);
const GLbitfield colorBit = mask & static_cast<GLbitfield>(GL_COLOR_BUFFER_BIT);
const GLbitfield dsBits = mask & static_cast<GLbitfield>(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
// The color group's one emulation is the multisample resolve that also converts format,
// which is the shape this names. It used to double as an early-out for the whole
// function, which is what cost a depth-only mask its single-aspect retry.
const Bool multisampleResolve = readSamples > 0 && drawSamples <= 0;
if (colourBit != 0) {
if (colorBit != 0) {
DrainBlitErrors();
g_GLESFuncs.glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, colourBit, filter);
g_GLESFuncs.glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, colorBit, filter);
if (g_GLESFuncs.glGetError() != GL_NO_ERROR) {
const Bool emulated = multisampleResolve &&
ResolveThenBlit(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, filter);
@@ -367,9 +367,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
if (g_GLESFuncs.glMapBufferRange == nullptr || g_GLESFuncs.glUnmapBuffer == nullptr) return;
const SizeT packedStride = program->GetTransformFeedbackPackedStride();
const SizeT modelledVertices =
const SizeT modeledVertices =
static_cast<SizeT>(MG_Pipe::gPipeInputs->GetTransformFeedbackCapturedVertices());
const SizeT vertices = std::min<SizeT>(modelledVertices, xfb.scatterCapacityVertices);
const SizeT vertices = std::min<SizeT>(modeledVertices, xfb.scatterCapacityVertices);
if (packedStride == 0 || vertices == 0) {
// The scatter path redirected the DRIVER's capture into the scratch buffer,
// so bailing here leaves the application's buffers holding their pre-draw
@@ -380,7 +380,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
MGLOG_E_ONCE("EndTransformFeedback: scattered capture discarded - packedStride=%zu, "
"CPU-modeled captured vertices=%zu, scratch capacity=%zu. The capture buffers keep "
"their pre-draw contents.",
packedStride, modelledVertices, xfb.scatterCapacityVertices);
packedStride, modeledVertices, xfb.scatterCapacityVertices);
return;
}
@@ -1146,7 +1146,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
//
// The ORDERING that made this read come from the frontend is preserved and is what
// makes reading a record legal here: the client emits every set_* BEFORE the verb and
// the server specialises AT the verb, so by the time this runs the record already
// the server specializes AT the verb, so by the time this runs the record already
// describes the framebuffer this draw will use - which is exactly the property the
// frontend read was buying (a program compiled against a stale count would otherwise
// not be relinked until the draw after the one that needed it).
+1 -1
View File
@@ -845,7 +845,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// Bit 12 depends on NOTHING, and that is said out loud rather than left as an absence:
// a ShaderCso handle names no texture and no buffer, the archive rides beside the
// record as a companion pointer, and the eight extra inputs the server still
// specialises on (D-H5) are read from state this backend already holds.
// specializes on (D-H5) are read from state this backend already holds.
const Bool bitSet = (MG_Config::Features.PipePush & MG_Pipe::kMGPipeSubsystemPrograms) != 0;
const BufferImpl::PipeSubsystemArmVerdict verdict = BufferImpl::ClassifyPipeSubsystemArm(
bitSet, /*legacyMemosEnabled=*/true, /*legacyArmSurvivesLegacyMemos=*/false);
+1 -1
View File
@@ -1934,7 +1934,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// texture that never asks for the stencil aspect never emits the call. The
// depth/stencil readback and replicate-blit emulations also write this parameter
// raw, but only ever on their own scratch textures (never on an application
// texture), so they cannot desynchronise this cache.
// texture), so they cannot desynchronize this cache.
GLenum m_cacheDepthStencilTextureMode = GL_DEPTH_COMPONENT;
Uint16 m_syncedSamplerVersion = 0;
Uint16 m_syncedTextureParamsVersion = 0;
@@ -963,7 +963,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
return;
}
// The kill-switch arm, and it reads the application's REAL flags per call -
// which is why MGPFlushRange carries them unnormalised.
// which is why MGPFlushRange carries them unnormalized.
const Flags<BufferMappingAccessBit> appAccess{
static_cast<std::underlying_type_t<BufferMappingAccessBit>>(record.AccessFlags)};
const Bool invalidate = (appAccess & BufferMappingAccessBit::InvalidateRange) ||
@@ -416,7 +416,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
//
// A streamed `glBufferSubData -> draw` pair is only correct if the write is ordered
// against every draw that still reads the bytes it overwrites; a write this predicate
// answers "idle" is applied as an unsynchronised host memcpy, and a draw still reading
// answers "idle" is applied as an unsynchronized host memcpy, and a draw still reading
// the range then renders bytes that are not its own - silently, with no log line, no GL
// error and no Fatal. That was the OpenRA device divergence (magma-b3.md §2).
//
@@ -1657,7 +1657,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
MakeWireDepthResolveDeviceIdentity(m_physicalDevice.properties, renderPassArmAvailable, knob);
static WireDepthResolveArmCache s_choices;
const WireDepthResolveArmChoice resolved = s_choices.Resolve(identity, [&]() {
if (knob == WireDepthResolveProbeKnob::Unrecognised)
if (knob == WireDepthResolveProbeKnob::Unrecognized)
MGLOG_W("DirectVulkan: MGITEST_MAGMA_DEPTH_RESOLVE_PROBE=%s is not `bug`, `clean` or "
"`elide-subject`; the resolve probe runs as if it were unset",
knobValue);
@@ -707,7 +707,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
}
// Only provokingVertexModePerPipeline lets modes differ inside one render pass instance;
// elsewhere every pipeline takes GL's default so the render pass stays self-consistent, and
// glProvokingVertex(GL_FIRST_VERTEX_CONVENTION) goes unhonoured. Honoring it there would
// glProvokingVertex(GL_FIRST_VERTEX_CONVENTION) goes unhonored. Honoring it there would
// mean ending the render pass on every glProvokingVertex change; not worth it until a target
// device actually lacks the property.
if (!m_provokingVertexModePerPipeline) {
@@ -10,9 +10,9 @@
// Native Vulkan depth mip generation: no GL program, registry entry, or runtime transpilation.
//
// The geometry is WireColorBlit.vert's, unchanged - the same quad, the same push-constant
// rect, the same GL-bottom-left to native-image mapping - so a depth mip and a colour blit
// rect, the same GL-bottom-left to native-image mapping - so a depth mip and a color blit
// place their fragments identically and only the fragment stage differs. The block carries
// one member more than the colour blit's, and the fragment stage is the member's only
// one member more than the color blit's, and the fragment stage is the member's only
// reader: a push-constant block must be declared identically in every stage of one pipeline,
// so it is declared here too rather than split into a second range.
layout(push_constant) uniform DepthMipmapRect {
@@ -163,7 +163,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
ForceBug,
ForceClean,
ElideSubject,
Unrecognised
Unrecognized
};
inline WireDepthResolveProbeKnob ParseWireDepthResolveProbeKnob(const char* value) {
@@ -171,7 +171,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
if (std::strcmp(value, "bug") == 0) return WireDepthResolveProbeKnob::ForceBug;
if (std::strcmp(value, "clean") == 0) return WireDepthResolveProbeKnob::ForceClean;
if (std::strcmp(value, "elide-subject") == 0) return WireDepthResolveProbeKnob::ElideSubject;
return WireDepthResolveProbeKnob::Unrecognised;
return WireDepthResolveProbeKnob::Unrecognized;
}
// One packed format's worth of readings: the control resolved both aspects; the render pass
@@ -2424,7 +2424,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
const Bool depthArmTakesShape =
planar && layersInRange && (props.optimalTilingFeatures & depthPassFeatures) == depthPassFeatures;
// The color arm's shape (the two shader shapes are explained at the color arm below).
const Bool colour = resource->aspect == VK_IMAGE_ASPECT_COLOR_BIT;
const Bool color = resource->aspect == VK_IMAGE_ASPECT_COLOR_BIT;
// The shader arm samples through a sampler2D and writes a vec4, which is what
// BlitWireColorImage's own float check enforces; naming it here rather than letting the
// blit return false is what puts the reason in the decline line.
@@ -2434,15 +2434,14 @@ namespace MobileGL::MG_Backend::DirectVulkan {
forcedShaderTier < 2 && (resource->usageFlags & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) != 0;
const Bool viaScratch =
!attachable && renderableFormat && (resource->usageFlags & VK_IMAGE_USAGE_TRANSFER_DST_BIT) != 0;
const Bool colourArmTakesShape =
colour && planar && layersInRange && sampledFloat && (attachable || viaScratch);
const Bool colorArmTakesShape = color && planar && layersInRange && sampledFloat && (attachable || viaScratch);
// P8-F: `mipmap-shader-format-or-shape` IS A NAMED DECLINE, NOT A FATAL (rule I (a)).
// The monolith arm has always answered this chain with a decline (VulkanRenderer.cpp's
// GenerateMipmap: a format without blit support is skipped and the chain left unwritten),
// and a shape no shader arm takes is a coverage gap in this backend, not a broken record.
// The leading word stays, so every device-log grep keyed on it still finds the line.
const Bool shaderArmTakesShape =
resource->aspect == VK_IMAGE_ASPECT_DEPTH_BIT ? depthArmTakesShape : colourArmTakesShape;
resource->aspect == VK_IMAGE_ASPECT_DEPTH_BIT ? depthArmTakesShape : colorArmTakesShape;
if (!nativeBlit && !shaderArmTakesShape) {
const String detail =
MagmaWireShapeDetail("mipmap-shader-format-or-shape", format, resource->format, props, resource->aspect,
@@ -2519,7 +2518,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// the texels still come from GPU contents rather than from a staged shadow.
//
// Which of the two (`attachable` or `viaScratch`), and whether either takes the
// shape at all, was decided before the growth (colourArmTakesShape, P8-F).
// shape at all, was decided before the growth (colorArmTakesShape, P8-F).
for (Uint32 level = base + 1; level < base + count; ++level) {
for (Uint32 layer = baseLayer; layer < baseLayer + layerCount; ++layer) {
WireImage source{};
+4 -4
View File
@@ -94,7 +94,7 @@ namespace MobileGL::MG_Impl::GLImpl {
// has a frontend name check - so this is answered exactly rather than waved through.
// GL_DISPLAY_LIST is deliberately absent: it exists only in the compatibility profile,
// which MobileGL does not expose, so it falls to the INVALID_ENUM path below.
Bool ValidateLabelledObject(GLenum identifier, GLuint name, Bool& outIdentifierKnown) {
Bool ValidateLabeledObject(GLenum identifier, GLuint name, Bool& outIdentifierKnown) {
outIdentifierKnown = true;
auto* context = MG_State::pGLContext.get();
switch (identifier) {
@@ -206,7 +206,7 @@ namespace MobileGL::MG_Impl::GLImpl {
void ObjectLabel(GLenum identifier, GLuint name, GLsizei length, const GLchar* label) {
Bool identifierKnown = false;
const Bool objectExists = ValidateLabelledObject(identifier, name, identifierKnown);
const Bool objectExists = ValidateLabeledObject(identifier, name, identifierKnown);
if (!identifierKnown) {
RecordDebugError(ErrorCode::InvalidEnum, __func__,
std::format("identifier {} is not a labellable object type.",
@@ -237,7 +237,7 @@ namespace MobileGL::MG_Impl::GLImpl {
return;
}
Bool identifierKnown = false;
const Bool objectExists = ValidateLabelledObject(identifier, name, identifierKnown);
const Bool objectExists = ValidateLabeledObject(identifier, name, identifierKnown);
if (!identifierKnown) {
RecordDebugError(ErrorCode::InvalidEnum, __func__,
std::format("identifier {} is not a labellable object type.",
@@ -254,7 +254,7 @@ namespace MobileGL::MG_Impl::GLImpl {
const auto& labels = State().objectLabels;
const auto it = labels.find(MakeObjectLabelKey(identifier, name));
const String& text = it != labels.end() ? it->second : String{};
// GL 4.6 core 20.5: the returned length excludes the NUL, and an unlabelled object hands
// GL 4.6 core 20.5: the returned length excludes the NUL, and an unlabeled object hands
// back an empty string with length 0 rather than an error.
SizeT copied = 0;
if (label != nullptr && bufSize > 0) {
@@ -504,7 +504,7 @@ namespace MobileGL::MG_Impl::GLImpl {
// a real FramebufferObject for framebuffer 0, so a null test can never see this - the
// object is always there, and framebuffer 0 has to be recognized by identity instead,
// the same comparison DrawBuffers_State makes. Without this an attach onto the default
// framebuffer silently REPLACED its color attachment, permanently desynchronising it
// framebuffer silently REPLACED its color attachment, permanently desynchronizing it
// from what the swapchain keeps publishing.
const auto& defaultFramebufferInfo = FramebufferImpl::pDefaultFramebufferInfo;
if (!framebufferObject ||
+2 -2
View File
@@ -14,7 +14,7 @@
// 396 pipeline bytes, probes, and confirms a hit with a memcmp: a bare 64-bit match could alias
// two render states onto one CSO, which is silently wrong pixels. A miss mints a slot and emits
// create_render_state with every pipeline chunk. 64 entries, LRU, about 26 KB per context; the
// counters give the hit rate. kMGPipeBehaviourNoCsoContentAddressing (bit 63 of
// counters give the hit rate. kMGPipeBehaviorNoCsoContentAddressing (bit 63 of
// MOBILEGL_PIPE_PUSH) disables reuse as a negative control, which CsoContentAddressingScenario
// keeps exercised.
#include <Config.h>
@@ -55,7 +55,7 @@ namespace MobileGL::MG_Pipe {
MGPipeGatherPipelineBytes(params, bytes.data());
++m_counters.Binds;
const Bool contentAddressed = (MG_Config::Features.PipePush & kMGPipeBehaviourNoCsoContentAddressing) == 0;
const Bool contentAddressed = (MG_Config::Features.PipePush & kMGPipeBehaviorNoCsoContentAddressing) == 0;
if (contentAddressed) {
const Uint64 hash =
s_hashForTest != nullptr ? s_hashForTest(bytes.data()) : MGPipeHashPipelineBytes(bytes.data());
+6 -6
View File
@@ -523,7 +523,7 @@ namespace MobileGL::MG_Pipe {
Bool InHook = false; // a re-read that re-enters an accessor is not re-verified
Optional<MGPipeInputField> Corrupt;
std::atomic<Uint64> Divergences{0};
Uint64 Summarised = 0; // how many of them the summary line has reported so far
Uint64 Summarized = 0; // how many of them the summary line has reported so far
// THE SUMMARY IS WRITTEN FROM MobileGL::Destroy, NOT FROM HERE (V1 fix round 2). This
// is a namespace-scope static, so this destructor runs from __run_exit_handlers, AFTER
// DestroyImpl has called MG_Util::Debug::Close(). Close nulls the role's sink; Log.cpp's
@@ -538,8 +538,8 @@ namespace MobileGL::MG_Pipe {
~VerifyState() { FlushSummary(); }
void FlushSummary() {
const Uint64 count = Divergences.load(std::memory_order_relaxed);
if (count == 0 || count == Summarised) return;
Summarised = count;
if (count == 0 || count == Summarized) return;
Summarized = count;
MGLOG_E("MGPipe: verify summary - %llu divergence(s) survived MOBILEGL_PIPE_VERIFY_FATAL=0",
static_cast<unsigned long long>(count));
}
@@ -1742,7 +1742,7 @@ namespace MobileGL::MG_Pipe {
// not consulted, and under spawn a silent no-op that leaks every one of those objects.
// The parameter type is the route's, which is byte-identical to the applier's
// (const MGPHandleOnly&, void return), so the fix is `&MGPipeRoute<Name>` at the five call
// sites; PipeCatalogue.FrontendNeverTakesAnApplierAddress checks MG_Impl in every unit lane.
// sites; PipeCatalog.FrontendNeverTakesAnApplierAddress checks MG_Impl in every unit lane.
Bool EmitDeleteIfPublished(MGPipeKind kind, MGPipeHandle handle, void (*route)(const MGPHandleOnly&)) {
if (!MGPipeHandleIsPublished(kind, handle)) return false;
route(HandleOnly(kind, handle));
@@ -2908,7 +2908,7 @@ namespace MobileGL::MG_Pipe {
// textures/sampler/image/global constants - and that document is explicit that the
// order is code organization and NOT a contract: all of a verb's set_*/bind_* must
// complete before the verb, and apart from "a resource create precedes a bind to it"
// there is no ordering requirement between them. The server specialises the shader and
// there is no ordering requirement between them. The server specializes the shader and
// the pipeline lazily at the verb, from everything it holds at that moment, which is
// what makes deriving the fragColor broadcast count from the framebuffer record legal
// at the verb rather than at the FBO sync.
@@ -3198,7 +3198,7 @@ namespace MobileGL::MG_Pipe {
// program, then textures / sampler / image / global constants - which is why it stands
// before the render-state block rather than after it. That order is explicitly code
// organization and not a contract (all of a verb's set_*/bind_* complete before the
// verb, and the server specialises lazily AT the verb from everything it then holds),
// verb, and the server specializes lazily AT the verb from everything it then holds),
// so nothing about the P2 and P3a emissions changes by standing after it; what it buys
// is that the file reads in the order the design states.
//
+1 -1
View File
@@ -13,7 +13,7 @@
// set_dispatch_program and set_global_constants. create_shader_state is emitted from the
// GetProgramForDraw() / GetProgramForDispatch() join the verb makes anyway; the tracker's
// shutter reads GetCurrentProgram() so asking "did the shader move" never forces a compile.
// The record carries the artifacts only; the server specialises at the verb for the draw-FBO
// The record carries the artifacts only; the server specializes at the verb for the draw-FBO
// clamp masks, fragColor broadcast, storage-block bindings, atomic counters, image formats and
// patch parameters (ARCHITECTURE.md §7). In monolith the artifacts ride beside the record as
// companion pointers with Size 0 blob refs, so the codec runs only in the verify build.
+28 -28
View File
@@ -282,7 +282,7 @@ function(mgl_itest_find_driver_json outVar)
endif()
endforeach()
endforeach()
# Nothing recognised as hardware. Report the first non-software entry if there
# Nothing recognized as hardware. Report the first non-software entry if there
# is one; otherwise report nothing, so the warning below fires.
foreach(candidate IN LISTS candidates)
get_filename_component(leaf "${candidate}" NAME)
@@ -347,7 +347,7 @@ option(MOBILEGL_ITEST_REQUIRE_GPU
# configured before this change would keep injecting EGL_PLATFORM=x11 and go on
# binding to a window system - silently, and only on the machines that have one.
# Someone reproducing a platform-specific bug sets EGL_PLATFORM in their own
# environment, which the harness still honours.
# environment, which the harness still honors.
set(MGL_ITEST_COMMON_ENV "")
if (MOBILEGL_ITEST_EGL_VENDOR)
@@ -394,7 +394,7 @@ endfunction()
# One lane of the integration binary: the cases FILTER selects, registered as PREFIX<suite.case>
# with the environment ENV (a list joined by mgl_itest_join_environment). LABELS defaults to
# integration-gpu and TIMEOUT to the scenario timeout. LOCK serialises lanes that share a resource
# integration-gpu and TIMEOUT to the scenario timeout. LOCK serializes lanes that share a resource
# such as a log file; TEST_LIST names the ctest-time variable listing the lane's tests; FAIL_REGEX
# and FIXTURES set those properties; PROPERTIES passes further property pairs through.
function(mgl_itest_lane prefix filter)
@@ -518,7 +518,7 @@ endfunction()
# DirectGLES' Track H arm, probed by the subsystem bit it is gated on rather than by
# SlotTables.h existing: the bit is declared in the contract (kMGPipeSubsystemEsprytSlots, MG_Pipe/MGPipe.h) and the
# backend has to name it to honour MOBILEGL_PIPE_PUSH's default mask, whatever files package C
# backend has to name it to honor MOBILEGL_PIPE_PUSH's default mask, whatever files package C
# spreads the slot tables across.
mgl_itest_probe_for_symbol(MGL_ITEST_ESPRYT_SLOTS
"${MGL_ITEST_ROOT}/MobileGL/MG_Backend/DirectGLES" "kMGPipeSubsystemEsprytSlots")
@@ -603,7 +603,7 @@ endif()
# P4a's THIRD re-key question, per backend, and it is not either of the two above. Bits 5/6
# re-keyed each backend's VERTEX-INPUT memos and bit 7/8 the BUFFER's; P4a re-keys six OBJECT
# classes - texture, renderbuffer, framebuffer, sampler CSO, sampler view, shader CSO - behind
# four new subsystem bits, and a backend has to NAME one of those constants to honour
# four new subsystem bits, and a backend has to NAME one of those constants to honor
# MOBILEGL_PIPE_PUSH's default mask. A P4a case that read either older marker would arm its
# Handles arm on a tree where nothing about a texture is keyed on a handle: green for a re-key
# that does not exist. Probed by the constants rather than by a file, for the reason the block
@@ -938,7 +938,7 @@ mgl_itest_lane("DirectVulkan.OptimisticShaderStatus." "AsyncCompileScenario.Iris
# The negative control for the DirectGLES gl_ViewportIndex emulation, in a process that
# has it switched off. One case, because it is the only one the switch may touch: with
# the emulation off the three positive cases in the same fixture describe behaviour the
# the emulation off the three positive cases in the same fixture describe behavior the
# backend does not have, so a whole-scenario registration would be three guaranteed reds.
# DirectGLES only - the flag steers nothing on DirectVulkan, which routes natively.
mgl_itest_lane("DirectGLES.NoViewportArrayEmulation." "ViewportArrayScenario.WithoutTheEmulationEveryIndexCollapsesOntoViewportZero"
@@ -1029,7 +1029,7 @@ mgl_itest_lane("DirectVulkan.HandleRecycle.Handles." "HandleRecycleScenario.*"
LABELS "integration-gpu\;integration-monolith-control" ENV "${MGL_ITEST_VULKAN_HANDLE_HANDLES_ENVIRONMENT}")
# --- CSO content-addressing negative control ----------------------------------------------
# The On and Off masks differ only in bit 63 of MOBILEGL_PIPE_PUSH
# (kMGPipeBehaviourNoCsoContentAddressing); the rest of each mask is the default 0x3fff, because
# (kMGPipeBehaviorNoCsoContentAddressing); the rest of each mask is the default 0x3fff, because
# subsystem bits 0-13 are fixed on. The cases read the csom / csob counters from the library log,
# so each lane has a private log path (ARCHITECTURE.md §16.1). MOBILEGL_PIPE_STATS_PERIOD=1 writes
# one summary line per eglSwapBuffers, so two swaps bracket exactly the workload.
@@ -1144,7 +1144,7 @@ mgl_itest_lane("DirectGLES.MapPersistentRoundtrips." "StorageBufferRegrowScenari
mgl_itest_lane("DirectGLES.MapPersistentRoundtrips." "LargeArenaAdoptionScenario.AnAdoptionCostsExactlyOneMapPersistentRoundtrip"
LABELS "integration-gpu\;integration-monolith-control" ENV "${MGL_ITEST_GLES_MPR_ARENA_ENVIRONMENT}")
# LargeArenaAdoption's three behavioural cases under BOTH arms of the same A/B. This file is where
# LargeArenaAdoption's three behavioral cases under BOTH arms of the same A/B. This file is where
# an adopted store's whole life is exercised - the NULL-data definition that adopts it, an
# in-flight SubData, a readback and a GPU write - so if the handle path and the legacy
# BufferBackendOps path disagree about any of it, one of these two lanes goes red and names which.
@@ -1254,7 +1254,7 @@ mgl_itest_lane("DirectGLES.TextureUploadShape." "TextureUploadShapeScenario.TheE
LOCK texture-upload-shape-DirectGLES.log ENV "${MGL_ITEST_GLES_TEXTURE_UPLOAD_SHAPE_ENVIRONMENT}")
# --- TextureParamsWithoutASamplerViewScenario needs no lane of its own ---------------------
# Its observable is a sampled colour, not a counter or a log, and its claim is about the shipping
# Its observable is a sampled color, not a counter or a log, and its claim is about the shipping
# configuration, so the ambient DirectGLES. / DirectVulkan. registrations at the default mask are
# the arms it needs. The DirectVulkan entries skip in SetUp: the scenario covers Espryt's
# SyncAttachmentObject / SyncNeccessaryTextures pair.
@@ -1285,7 +1285,7 @@ if (MOBILEGL_PIPE_VERIFY)
# PipeVerifyArmingScenario.Armed reads the library's log, and the log is a per-LANE resource: it
# is opened fopen(path, "w"), so every process in a lane truncates it. In the ambient Verify.
# lane that is 400-odd processes on one path, run `-j 4` in CI, and a whole-file read there
# races a neighbour's bring-up. Every other log-reading scenario in this file (UnlocatedIoBlocks,
# races a neighbor's bring-up. Every other log-reading scenario in this file (UnlocatedIoBlocks,
# the primgen reroute, the point-size demotion) is registered exactly like this for the same
# reason. MGITEST_PIPE_ARMING_LANE is a harness marker - the library never reads it - and it is
# what makes the case skip in the ambient lane instead of racing there.
@@ -1326,7 +1326,7 @@ if (MOBILEGL_PIPE_VERIFY)
# The whole suite again, per backend, with the comparator armed. Same scenarios, same
# assertions, but every backend read of frontend state is now checked against a snapshot taken
# from the live context at the verb boundary - which is what "the 742 integration entries
# prove push equals pull" means. Labelled integration-gpu as well so a verify build's
# prove push equals pull" means. Labeled integration-gpu as well so a verify build's
# `ctest -L integration-gpu` still describes the whole registration set.
mgl_itest_lane("DirectGLES.Verify." "-${MGL_ITEST_GLES_VIEW_FILTER}:${MGL_ITEST_SPLIT_ARM_ONLY_FILTER}"
LABELS "integration-gpu\;integration-verify\;integration-monolith-control" TIMEOUT ${MGL_ITEST_VERIFY_TIMEOUT} ENV "${MGL_ITEST_GLES_VERIFY_ENVIRONMENT}")
@@ -2266,7 +2266,7 @@ if (MOBILEGL_BUILD_DISAGGREGATED)
# the whole-binary discovery.
mgl_itest_register_split_arms("CopyImageStoreReadbackScenario.*" "")
# The same cases with MGITEST_ESPRYT_REFUSE_TEXTURE_READBACK_EXTENT pinned: the Espryt server
# treats a colour level of exactly that extent as one the driver refused to attach (what an
# treats a color level of exactly that extent as one the driver refused to attach (what an
# Adreno does for RGB16 and 16-bit SNORM), so its staged store has to answer. READ BY THE
# SERVER, and it reaches all three arms: inproc sees this entry's environment, spawn copies it
# into the child, and the tcp supervisor carries it in its own fixture ENVIRONMENT (appended to
@@ -2346,7 +2346,7 @@ if (MOBILEGL_BUILD_DISAGGREGATED)
# MOBILEGL_IPC_STRICT_ERRORS=1 a barrier-pulled pipe-input read is Fatal{UnmigratedPipeInput},
# so these go red if GetProgramForDraw@DrawArrays or GetProgramForDispatch@DispatchCompute is
# pulled again. The MultiDrawScenario case prepares a draw and checks GL errors, not pixels;
# AtomicCounterScenario dispatches. Labelled integration-split only: on the monolith arm the
# AtomicCounterScenario dispatches. Labeled integration-split only: on the monolith arm the
# knob cannot trip, because the client fills every field at every verb.
mgl_itest_join_environment(MGL_ITEST_GLES_SPLIT_STRICT_ENVIRONMENT
"MOBILEGL_BACKEND_TYPE=DirectGLES" "MOBILEGL_TRANSPORT=inproc" "MOBILEGL_ITEST_REQUIRE_GPU=1"
@@ -2476,8 +2476,8 @@ if (MOBILEGL_BUILD_DISAGGREGATED)
ClipDistanceScenario.ADisabledClipDistanceRemovesNothing
ClipDistanceScenario.TheEnablesAreIndependentPerDistance
CopyImageLayeredScenario.ArrayToArrayCopiesEverySlice
CopyImageLayeredScenario.ArrayToArrayHonoursDifferentLayerOffsets
CopyImageLayeredScenario.VolumeToVolumeHonoursNonZeroZ
CopyImageLayeredScenario.ArrayToArrayHonorsDifferentLayerOffsets
CopyImageLayeredScenario.VolumeToVolumeHonorsNonZeroZ
CopyImageLayeredScenario.VolumeToVolumeAtNonZeroMipLevel
CopyImageLayeredScenario.ArrayToVolumeCopiesEverySlice
CopyImageLayeredScenario.VolumeToArrayCopiesEverySlice)
@@ -2494,7 +2494,7 @@ if (MOBILEGL_BUILD_DISAGGREGATED)
endforeach()
# ShaderMip<tier>: MGITEST_MAGMA_FORCE_SHADER_MIPMAP makes GenerateWireMipmap skip its
# native blit (tier 1) and also treat the level as not attachable (tier 2), the arms
# devices take. Lavapipe blits every colour format this binary creates, R11F_G11F_B10F
# devices take. Lavapipe blits every color format this binary creates, R11F_G11F_B10F
# included, so no format choice reaches them. Own entries: the knob retargets every mipmap
# in the process. A server-read knob set per entry never reaches the shared tcp fixture's
# server, so knob entries skip the Tcp arm (mglItestServerEnvKnobArm) and are listed in
@@ -2542,7 +2542,7 @@ if (MOBILEGL_BUILD_DISAGGREGATED)
#
# TWO ENTRIES OVER ONE CASE, for the reason the ShaderMip pair gives. The plain entry is
# a regression gate that runs on every driver. The forced entry is the red-once of the
# DEFENCE term: it makes the serial half of VkBufferManager's busy predicate answer
# DEFENSE term: it makes the serial half of VkBufferManager's busy predicate answer
# "idle" - the state an unsound completed-serial floor put the wire arm into on the
# device - so only the submission-fence half is left to order the copy. It does NOT
# cover the floor fix itself (forcing the serial term bypasses the floor entirely); the
@@ -2583,7 +2583,7 @@ if (MOBILEGL_BUILD_DISAGGREGATED)
# ---- P7 wave 2-B2: `multisample-blit-shape@P7`'s retirement and its decline ----------
#
# SPLIT-ONLY, like the in-place copy pair below and for the same reason: the retired arm
# does something the monolith arm does not. A scaled multisample colour blit takes the
# does something the monolith arm does not. A scaled multisample color blit takes the
# monolith's `vkCmdResolveImage` at 1:1 into the destination's offset - the scale is
# dropped - so there is no `DirectVulkan.` reading to register these beside, and an
# equality between the two arms would be an assertion that the wire arm is as wrong.
@@ -2734,7 +2734,7 @@ endif()
# Fatal{ReplyTooLarge, "ReadPixels 256x512 0x1908/0x1406 2097152 > 2097136"}. One entry per case
# for a private log each - two processes append to it on the spawn arm.
if (MOBILEGL_BUILD_DISAGGREGATED)
# P11: LargeArenaAdoptionScenario's cases that run on every Magma arm - the five behavioural
# P11: LargeArenaAdoptionScenario's cases that run on every Magma arm - the five behavioral
# cases and the tier assertion (the mpr and adopt-tier-refusal cases need lane markers of their
# own). The `Arena.` block below and the Buffers lane both walk it.
set(MGL_ITEST_ARENA_CASES
@@ -2764,7 +2764,7 @@ if (MOBILEGL_BUILD_DISAGGREGATED)
foreach(bandCase RenderbufferReadOfExactlyTwoMiBIsBandedNotRefused
TwoMiBPlusOneRowIsBanded
AReadLargerThanTheWholeReplySegmentIsBanded
BandedReadsHonourPackAlignmentRowLengthAndSkips
BandedReadsHonorPackAlignmentRowLengthAndSkips
BandedReadIntoAPixelPackBufferKeepsItsPadding
BandedDepthAndDepthStencilReads
GetTexImageLargerThanAReplySlotIsWindowed)
@@ -3265,7 +3265,7 @@ if (MOBILEGL_BUILD_DISAGGREGATED)
# THE LABEL IS `integration-magma-split` AND NOT `integration-split-magma`, and that is not
# a preference: `ctest -L` takes a REGEX, not an exact name, so `-L integration-split`
# MATCHES a label spelled `integration-split-magma` as a substring. The obvious name would
# have left both entries in the Espryt lane while every listing showed them relabelled - a
# have left both entries in the Espryt lane while every listing showed them relabeled - a
# split that looks done and is not. Measured: with that spelling
# `ctest -N -L integration-split` still listed both DirectVulkan entries.
if (namedBlitBackend STREQUAL "DirectVulkan")
@@ -3399,7 +3399,7 @@ if (MOBILEGL_BUILD_DISAGGREGATED)
# B2's CPU mip arm x P8-E's driver-written mark: the forced-CPU knob plus E's refused-readback
# knob at the base level's extent (16x16), both read by the SERVER - so inproc and spawn only,
# and their own label as B's `CpuMip.` entries have (the tcp arm's server is the shared
# TcpServer.Start fixture, and the refusal there would reach every 16x16 colour readback).
# TcpServer.Start fixture, and the refusal there would reach every 16x16 color readback).
foreach(mglItestArm IN ITEMS Split Spawn)
string(TOUPPER "${mglItestArm}" mglItestArmUpper)
string(TOLOWER "${mglItestArm}" mglItestArmLower)
@@ -3409,16 +3409,16 @@ if (MOBILEGL_BUILD_DISAGGREGATED)
LABELS "integration-gpu\\;integration-cpu-mip-${mglItestArmLower}" ENV "${MGL_ITEST_GLES_${mglItestArmUpper}_ENVIRONMENT}\;MGITEST_ESPRYT_FORCE_CPU_MIPMAP=1\;MGITEST_ESPRYT_REFUSE_TEXTURE_READBACK_EXTENT=16x16\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/p8se-cpu-mip-refused-${mglItestArmLower}-${cpuMipCase}.log")
endforeach()
endforeach()
# PipeCatalogueTest's unmigrated-emulation list against the call sites in the tree (a source
# PipeCatalogTest's unmigrated-emulation list against the call sites in the tree (a source
# lint, so the unit lane runs it wherever the split build's unit tests run; CI also runs it
# beside split_coverage.py).
add_test(NAME PipeCatalogue.UnmigratedEmulationSitesSelfTest
add_test(NAME PipeCatalog.UnmigratedEmulationSitesSelfTest
COMMAND "${Python3_EXECUTABLE}" "${CMAKE_SOURCE_DIR}/scripts/ci/unmigrated_emulation_sites.py" --self-test)
add_test(NAME PipeCatalogue.UnmigratedEmulationSitesMatchTheList
add_test(NAME PipeCatalog.UnmigratedEmulationSitesMatchTheList
COMMAND "${Python3_EXECUTABLE}" "${CMAKE_SOURCE_DIR}/scripts/ci/unmigrated_emulation_sites.py"
"${CMAKE_SOURCE_DIR}")
set_tests_properties(PipeCatalogue.UnmigratedEmulationSitesSelfTest
PipeCatalogue.UnmigratedEmulationSitesMatchTheList PROPERTIES LABELS unit TIMEOUT 60)
set_tests_properties(PipeCatalog.UnmigratedEmulationSitesSelfTest
PipeCatalog.UnmigratedEmulationSitesMatchTheList PROPERTIES LABELS unit TIMEOUT 60)
endif()
# P8-SV: Magma split residuals (docs/archive/notes/p8/SV.md) - the 3D native mip blit's layer
@@ -3615,7 +3615,7 @@ if (MOBILEGL_BUILD_DISAGGREGATED AND MOBILEGL_PIPE_VERIFY)
# The arming assertion on this arm, one entry per backend, with a log path nothing else
# writes to - the monolith VerifyArming. entries' shape and their reason (the case reads the
# whole file, and a path shared with 500 neighbours races them under `-j 4`). This is the
# whole file, and a path shared with 500 neighbors races them under `-j 4`). This is the
# entry that cannot be green against a library with no comparator compiled in, and unlike the
# monolith pair it also cannot be green against one whose comparator never survived the
# server's bring-up - which is precisely what the relaxed respecify pin unblocked.
@@ -175,7 +175,7 @@ DUALBLOCK_ALLOWED_SKIPS = {
"DirectGLES cannot forward a geometry-shader-less adjacency draw",
"DirectGLES.Split.ForcedDs.DepthStencilReadbackMatrixScenario.AFlippedMultisampleResolveMirrorsTheBandsAndAScaleDeclines":
"does not produce this picture yet: a flipped multisample depth resolve writes nothing",
"DirectGLES.Split.Glsl420DeclarationScenario.AnArrayOfSamplerArraysIsHonouredOrDeclinedCleanly":
"DirectGLES.Split.Glsl420DeclarationScenario.AnArrayOfSamplerArraysIsHonoredOrDeclinedCleanly":
"the frontend's binding-qualifier seeding does not walk an array of arrays",
# ...and driver capabilities llvmpipe does not have (the runner and the host both skip them).
"DirectGLES.Split.ForcedDs.DepthStencilReadbackMatrixScenario.SeparateDepthAndStencilAttachmentsAreBothReadable":
@@ -112,7 +112,7 @@ class ResultAccountingTest(unittest.TestCase):
# different sentence, and must not ride the same row. The accepted half is asserted
# FIRST: without it this case would pass on a helper that does not list the entry at all
# (CI's dual-block census went red on exactly that, run 35785685692 onward).
name = "DirectGLES.Split.Glsl420DeclarationScenario.AnArrayOfSamplerArraysIsHonouredOrDeclinedCleanly"
name = "DirectGLES.Split.Glsl420DeclarationScenario.AnArrayOfSamplerArraysIsHonoredOrDeclinedCleanly"
self.assertIn(name, helper.DUALBLOCK_ALLOWED_SKIPS)
for reason, accepted in [
("the frontend's binding-qualifier seeding does not walk an array of arrays, so "
@@ -128,7 +128,7 @@ DirectVulkan.VerifySplit.SampleMaskScopeScenario.TheAdvertisedSampleMaskWordCoun
# FRAMEBUFFER COMPLETENESS QUERIES - attach and ask, never render (2)
# glCheckFramebufferStatus on an attachment of that format. Only the DirectGLES half is
# here: on DirectVulkan both cases SKIP (the format is not renderable on lavapipe), and a skip is
# recognised without a name.
# recognized without a name.
# --------------------------------------------------------------------------------------------
DirectGLES.VerifySplit.SnormAttachmentScenario.SignedNormalizedColorAttachmentsReportComplete
DirectGLES.VerifySplit.ThreeChannelAttachmentScenario.ThreeChannelColorAttachmentsReportComplete
@@ -271,7 +271,7 @@ void main() {
// outside the range: offset 4 still holds the seed on the CPU side and the first dispatch's
// increments on the GPU. Renaming it would put the seed back and the second dispatch would
// count from there.
TEST_F(AtomicCounterScenario, APartialSubDataBetweenDispatchesKeepsTheShaderWrittenNeighbour) {
TEST_F(AtomicCounterScenario, APartialSubDataBetweenDispatchesKeepsTheShaderWrittenNeighbor) {
if (!Ready() || IsSkipped()) return;
const GLuint zero = MakeCounterBuffer(0, {0u, 0u});
@@ -221,7 +221,7 @@ namespace MGITest {
glReadBuffer(GL_COLOR_ATTACHMENT0);
ASSERT_EQ(glCheckFramebufferStatus(GL_FRAMEBUFFER), static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE));
auto stripeColour = [](int x, int y) {
auto stripeColor = [](int x, int y) {
const int stripe = y / 32;
const int half = x / 128;
return std::array<GLfloat, 4>{static_cast<GLfloat>(stripe) / 16.0f, static_cast<GLfloat>(half),
@@ -231,9 +231,9 @@ namespace MGITest {
glEnable(GL_SCISSOR_TEST);
for (int stripe = 0; stripe < kHeight / 32; ++stripe) {
for (int half = 0; half < 2; ++half) {
const auto colour = stripeColour(half * 128, stripe * 32);
const auto color = stripeColor(half * 128, stripe * 32);
glScissor(half * 128, stripe * 32, 128, 32);
glClearColor(colour[0], colour[1], colour[2], colour[3]);
glClearColor(color[0], color[1], color[2], color[3]);
glClear(GL_COLOR_BUFFER_BIT);
}
}
@@ -247,7 +247,7 @@ namespace MGITest {
std::string first;
for (int y = 0; y < kHeight; ++y) {
for (int x = 0; x < kWidth; ++x) {
const auto expected = stripeColour(x, y);
const auto expected = stripeColor(x, y);
const GLfloat* got = pixels.data() + (static_cast<std::size_t>(y) * kWidth + x) * 4;
if (std::memcmp(got, expected.data(), sizeof(expected)) != 0 && bad++ == 0) {
first = "(" + std::to_string(x) + "," + std::to_string(y) + ") = " + std::to_string(got[0]) + "," +
@@ -278,7 +278,7 @@ namespace MGITest {
// offset box so every band's own box origin is exercised as well. PACK_ALIGNMENT 8 with an odd
// ROW_LENGTH pads every row, SKIP_ROWS / SKIP_PIXELS move the first byte - and the bytes GL does
// NOT name must keep their sentinel, which is what a band scattered on the wrong stride breaks.
TEST_F(BandedReadbackScenario, BandedReadsHonourPackAlignmentRowLengthAndSkips) {
TEST_F(BandedReadbackScenario, BandedReadsHonorPackAlignmentRowLengthAndSkips) {
if (!Ready()) return;
struct PackCase {
PatternFormat format;
@@ -262,7 +262,7 @@ void main() { color = tint; })";
ExpectPixel(pair.main, 4, 4, green, "the draw after the clear passes the cleared depth");
}
TEST_F(ClearOrderScenario, AColourClearThroughItsOwnFramebufferReachesAnotherFramebufferHoldingTheTexture) {
TEST_F(ClearOrderScenario, AColorClearThroughItsOwnFramebufferReachesAnotherFramebufferHoldingTheTexture) {
if (!Ready() || IsSkipped()) return;
const GLuint texture = NewColor(kSize, kSize, black);
const GLuint own = NewFramebuffer(texture, 0);
@@ -98,10 +98,10 @@ void main() { o_color = vec4(0.1, 0.2, 0.3, 1.0); }
DrawFullViewportQuad(program);
{
const Image painted = ReadPixels(width, height);
const Rgba8 centre = painted.At(width / 2, height / 2);
ASSERT_NEAR(centre.r, 26, 2) << "the setup frame did not paint; the staleness test would be vacuous";
ASSERT_NEAR(centre.g, 51, 2);
ASSERT_NEAR(centre.b, 77, 2);
const Rgba8 center = painted.At(width / 2, height / 2);
ASSERT_NEAR(center.r, 26, 2) << "the setup frame did not paint; the staleness test would be vacuous";
ASSERT_NEAR(center.g, 51, 2);
ASSERT_NEAR(center.b, 77, 2);
}
gl.EndFrame();
@@ -220,13 +220,13 @@ void main() { o_color = vec4(0.1, 0.2, 0.3, 1.0); }
const Image blitted = ReadPixels(width, height);
EXPECT_EQ(FirstGLError(), 0u);
const Rgba8 centre = blitted.At(width / 2, height / 2);
EXPECT_NEAR(centre.r, 26, 2) << "the blit into the default framebuffer did not survive the clear that "
const Rgba8 center = blitted.At(width / 2, height / 2);
EXPECT_NEAR(center.r, 26, 2) << "the blit into the default framebuffer did not survive the clear that "
"preceded it; read back rgba("
<< static_cast<int>(centre.r) << ", " << static_cast<int>(centre.g) << ", "
<< static_cast<int>(centre.b) << ", " << static_cast<int>(centre.a) << ")";
EXPECT_NEAR(centre.g, 51, 2);
EXPECT_NEAR(centre.b, 77, 2);
<< static_cast<int>(center.r) << ", " << static_cast<int>(center.g) << ", "
<< static_cast<int>(center.b) << ", " << static_cast<int>(center.a) << ")";
EXPECT_NEAR(center.g, 51, 2);
EXPECT_NEAR(center.b, 77, 2);
DestroyColorFbo(source);
gl.EndFrame();
@@ -323,10 +323,10 @@ void main() { o_color = vec4(0.1, 0.2, 0.3, 1.0); }
EXPECT_EQ(FirstGLError(), 0u);
const Image painted = ReadPixels(width, height);
const Rgba8 centre = painted.At(width / 2, height / 2);
EXPECT_NEAR(centre.r, 26, 2) << "the draw did not survive the clear that preceded it";
EXPECT_NEAR(centre.g, 51, 2);
EXPECT_NEAR(centre.b, 77, 2);
const Rgba8 center = painted.At(width / 2, height / 2);
EXPECT_NEAR(center.r, 26, 2) << "the draw did not survive the clear that preceded it";
EXPECT_NEAR(center.g, 51, 2);
EXPECT_NEAR(center.b, 77, 2);
gl.EndFrame();
glDeleteProgram(program);
@@ -88,7 +88,7 @@ void main() { o_color = vec4(0.0, 1.0, 0.0, 1.0); }
// comparison - the claim is about the vertex SOURCE, not about rasterisation.
const float kQuad[] = {-1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f};
Rgba8 CentreAfter(int width, int height) {
Rgba8 CenterAfter(int width, int height) {
const Image image = ReadPixelsRect(0, 0, width, height);
return image.At(width / 2, height / 2);
}
@@ -145,12 +145,12 @@ void main() { o_color = vec4(0.0, 1.0, 0.0, 1.0); }
glUseProgram(program);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
const Rgba8 centre = CentreAfter(width, height);
EXPECT_GT(static_cast<int>(centre.g), 200)
<< "the client-memory vertex array never reached the draw: the center is " << centre
const Rgba8 center = CenterAfter(width, height);
EXPECT_GT(static_cast<int>(center.g), 200)
<< "the client-memory vertex array never reached the draw: the center is " << center
<< ", i.e. still the clear color. The draw did not consume its owned client-vertex snapshot";
EXPECT_LT(static_cast<int>(centre.b), 60)
<< "the quad drew, but the clear color is still showing through: " << centre;
EXPECT_LT(static_cast<int>(center.b), 60)
<< "the quad drew, but the clear color is still showing through: " << center;
quad.Release();
glUseProgram(0);
@@ -302,7 +302,7 @@ void main() {
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, 0);
glUseProgram(program);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr);
EXPECT_GT(CentreAfter(Gl().Width(), Gl().Height()).g, 200);
EXPECT_GT(CenterAfter(Gl().Width(), Gl().Height()).g, 200);
quad.Release();
glUseProgram(0);
glDeleteBuffers(1, &ebo);
@@ -351,7 +351,7 @@ void main() { gl_Position = vec4(aPos + aOffset, 0.0, 1.0); }
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, 0);
glUseProgram(program);
glDrawArraysIndirect(GL_TRIANGLE_STRIP, nullptr);
EXPECT_GT(CentreAfter(Gl().Width(), Gl().Height()).g, 200);
EXPECT_GT(CenterAfter(Gl().Width(), Gl().Height()).g, 200);
quad.Release();
glUseProgram(0);
glBindBuffer(GL_DRAW_INDIRECT_BUFFER, 0);
@@ -31,7 +31,7 @@
// clip by EVERY declared gl_ClipDistance regardless of the enables, so
// AnEnabledClipDistanceRemovesTheNegativeHalf goes green there against the broken tree as well,
// and the two cases that need real per-distance semantics skip (see
// DriverHonoursPerDistanceEnables). What actually pins the behavior is Adreno, through
// DriverHonorsPerDistanceEnables). What actually pins the behavior is Adreno, through
// KHR-GLxx.clip_distance.functional - whose "without dynamic redeclaration" variants declare all
// gl_MaxClipDistances slots and enable only the first N, i.e. exactly the subset semantics these
// skipped cases assert. Read a green CI run here as "the state survives the frontend", not as
@@ -183,7 +183,7 @@ void main() { fragColor = vec4(0.0, 1.0, 0.0, 1.0); }
// pipeline key - a feature, not a fix, and deliberately not attempted here. The
// cases that need the real semantics gate on this probe and say so when they skip,
// rather than being deleted or silently weakened.
bool DriverHonoursPerDistanceEnables(GLuint program, GLuint vao) const {
bool DriverHonorsPerDistanceEnables(GLuint program, GLuint vao) const {
for (int i = 0; i < 8; ++i) {
glDisable(static_cast<GLenum>(GL_CLIP_DISTANCE0 + i));
}
@@ -323,17 +323,17 @@ void main() { fragColor = vec4(0.0, 1.0, 0.0, 1.0); }
EXPECT_EQ(FirstGLError(), 0u);
EXPECT_TRUE(IsGreen(right)) << "with every clip distance disabled the whole triangle must survive";
const bool driverHonoursEnables = IsGreen(left);
const bool driverHonorsEnables = IsGreen(left);
glUseProgram(0);
glBindVertexArray(0);
glDeleteProgram(program);
glDeleteVertexArrays(1, &vao);
gl.EndFrame();
if (!driverHonoursEnables) {
if (!driverHonorsEnables) {
GTEST_SKIP() << "renderer " << gl.RendererString()
<< " clips by a DISABLED gl_ClipDistance - it does not implement per-distance enable state "
"(see DriverHonoursPerDistanceEnables). Emulating GL's semantics there needs shader-side "
"(see DriverHonorsPerDistanceEnables). Emulating GL's semantics there needs shader-side "
"masking keyed on the enable mask, which is a separate feature";
}
}
@@ -363,7 +363,7 @@ void main() { fragColor = vec4(0.0, 1.0, 0.0, 1.0); }
glDisable(GL_SCISSOR_TEST);
glDisable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
if (!DriverHonoursPerDistanceEnables(program, vao)) {
if (!DriverHonorsPerDistanceEnables(program, vao)) {
glUseProgram(0);
glBindVertexArray(0);
glDeleteProgram(program);
@@ -227,7 +227,7 @@ namespace MGITest {
// The same pair with the layer ranges offset differently on the two sides: the shape that
// separates "copies more than slice 0" from "copies the RIGHT slices". A backend that read
// the source range but wrote from layer 0 (or vice versa) passes the case above.
TEST_F(CopyImageLayeredScenario, ArrayToArrayHonoursDifferentLayerOffsets) {
TEST_F(CopyImageLayeredScenario, ArrayToArrayHonorsDifferentLayerOffsets) {
if (!Ready() || IsSkipped()) return;
const GLuint source = MakeTexture(GL_TEXTURE_2D_ARRAY, 1, SourceColor);
@@ -247,7 +247,7 @@ namespace MGITest {
// 3d -> 3d. Neither endpoint has array layers at all: the depth travels on extent.depth and
// the offsets on srcOffset.z/dstOffset.z, with both layer counts pinned to 1.
TEST_F(CopyImageLayeredScenario, VolumeToVolumeHonoursNonZeroZ) {
TEST_F(CopyImageLayeredScenario, VolumeToVolumeHonorsNonZeroZ) {
if (!Ready() || IsSkipped()) return;
const GLuint source = MakeTexture(GL_TEXTURE_3D, 1, SourceColor);
@@ -579,7 +579,7 @@ void main() {
const std::vector<Vertex> allRed = SceneVertices(/*leftQuadIsGreen=*/false);
const GLsizeiptr leftQuadBytes = GLsizeiptr(kLeftQuadVertexCount * sizeof(Vertex));
int centreSampleMissed = 0;
int centerSampleMissed = 0;
std::string missedVertices;
for (int staleVertex = 0; staleVertex < kLeftQuadVertexCount; ++staleVertex) {
// Every left-quad vertex turns green except this one.
@@ -608,21 +608,21 @@ void main() {
// What the old two-pixel form of ExpectHalves would have concluded.
if (std::strcmp(image.ColorName(w / 4, h / 2), "green") == 0) {
++centreSampleMissed;
++centerSampleMissed;
if (!missedVertices.empty()) missedVertices += ",";
missedVertices += std::to_string(staleVertex);
}
}
EXPECT_GT(centreSampleMissed, 0)
EXPECT_GT(centerSampleMissed, 0)
<< "no single-vertex staleness was invisible to a center sample, so this negative control "
<< "is no longer demonstrating anything - re-derive it before trusting it";
if (centreSampleMissed > 0) {
RecordProperty("centre_sample_blind_to_stale_vertices", missedVertices);
if (centerSampleMissed > 0) {
RecordProperty("center_sample_blind_to_stale_vertices", missedVertices);
std::fprintf(stderr,
"[itest] whole-region scan caught all %d single-stale-vertex cases; a center "
"sample alone was blind to %d of them (vertices %s)\n",
kLeftQuadVertexCount, centreSampleMissed, missedVertices.c_str());
kLeftQuadVertexCount, centerSampleMissed, missedVertices.c_str());
}
EXPECT_EQ(FirstGLError(), GLenum(GL_NO_ERROR));
}
@@ -10,7 +10,7 @@
//
// P2's render-state CSO is content-addressed: the client hashes the 396 pipeline bytes, probes a
// 64-entry cache, memcmps a hash hit and reuses the handle. The whole design is measured against
// a knob that turns that off - kMGPipeBehaviourNoCsoContentAddressing, bit 63 of the runtime
// a knob that turns that off - kMGPipeBehaviorNoCsoContentAddressing, bit 63 of the runtime
// MOBILEGL_PIPE_PUSH bitmask - so that "push is slower" can be told apart from "the CSO design is
// slower" (P2 brief D.4.5). A measurement knob has one characteristic failure mode: it stops
// steering anything and every later number is quietly taken against a switch that does nothing.
@@ -305,7 +305,7 @@ void main() { oColor = vec4(0.0, 1.0, 0.0, 1.0); }
<< "with content addressing on the cache must be answering binds it did not mint. " << window.line;
} else if (m_lane == kLaneNoContentAddressing) {
EXPECT_EQ(window.mints, window.binds)
<< "kMGPipeBehaviourNoCsoContentAddressing (bit 63 of MOBILEGL_PIPE_PUSH) must make every "
<< "kMGPipeBehaviorNoCsoContentAddressing (bit 63 of MOBILEGL_PIPE_PUSH) must make every "
"bind mint a fresh CSO - the map is never probed and no handle is ever reused. Equal "
"counters are the only reading that proves the bit STEERED anything: if it were "
"ignored, this arm would report the same bounded mint count as the other one. It "
@@ -636,9 +636,9 @@ namespace MGITest {
EXPECT_EQ(FirstGLError(), 0u);
ExpectAllDepth(top, 0.25f, "flipped resolve: the destination's top band");
// Color went the same way up, through the arm that could already do this.
std::array<GLubyte, 4> colour{};
glReadPixels(kWidth / 2, 1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, colour.data());
EXPECT_EQ(colour, (std::array<GLubyte, 4>{0, 255, 0, 255}))
std::array<GLubyte, 4> color{};
glReadPixels(kWidth / 2, 1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, color.data());
EXPECT_EQ(color, (std::array<GLubyte, 4>{0, 255, 0, 255}))
<< "flipped resolve: color and depth must agree about which way up the blit landed";
// THE STENCIL ASPECT, NARROW AND OFFSET ON BOTH SIDES. The copy-out moves one aspect
@@ -1280,7 +1280,7 @@ namespace MGITest {
// The PACK pixel-store parameters apply to a depth read exactly as they do to a color
// one, and the gap regions they create must be left alone.
TEST_F(DepthStencilReadbackMatrixScenario, DepthReadbackHonoursThePackPixelStoreParameters) {
TEST_F(DepthStencilReadbackMatrixScenario, DepthReadbackHonorsThePackPixelStoreParameters) {
if (!Ready()) return;
DepthSource source = MakeTextureSource(GL_DEPTH_COMPONENT24);
ASSERT_TRUE(SourceIsUsable());
@@ -112,11 +112,11 @@ namespace MGITest {
glClearDepth(0.25);
glClear(GL_DEPTH_BUFFER_BIT);
const float centre = ReadDepthAt(width / 2, height / 2);
const float center = ReadDepthAt(width / 2, height / 2);
EXPECT_EQ(FirstGLError(), 0u);
EXPECT_NEAR(centre, 0.25f, 1.0f / 4096.0f)
<< "glReadPixels(GL_DEPTH_COMPONENT) of the default framebuffer returned " << centre
<< (std::fabs(centre - kDepthPoison) < 1e-6f ? " - the destination was never written at all" : "");
EXPECT_NEAR(center, 0.25f, 1.0f / 4096.0f)
<< "glReadPixels(GL_DEPTH_COMPONENT) of the default framebuffer returned " << center
<< (std::fabs(center - kDepthPoison) < 1e-6f ? " - the destination was never written at all" : "");
gl.EndFrame();
}
@@ -139,10 +139,10 @@ namespace MGITest {
glClearStencil(3);
glClear(GL_STENCIL_BUFFER_BIT);
const int centre = ReadStencilAt(width / 2, height / 2);
const int center = ReadStencilAt(width / 2, height / 2);
EXPECT_EQ(FirstGLError(), 0u);
EXPECT_EQ(centre, 3) << "glReadPixels(GL_STENCIL_INDEX) of the default framebuffer returned " << centre
<< (centre == kStencilPoison ? " - the destination was never written at all" : "");
EXPECT_EQ(center, 3) << "glReadPixels(GL_STENCIL_INDEX) of the default framebuffer returned " << center
<< (center == kStencilPoison ? " - the destination was never written at all" : "");
gl.EndFrame();
}
@@ -198,11 +198,11 @@ void main()
const Image image = ReadPixels(kExtent, kExtent);
ASSERT_FALSE(image.Empty());
const Rgba8 centre = image.At(kExtent / 2, kExtent / 2);
const int red = static_cast<int>(centre.r);
const Rgba8 center = image.At(kExtent / 2, kExtent / 2);
const int red = static_cast<int>(center.r);
const bool blended = red > 100 && red < 160;
const bool declined = red > 245;
EXPECT_TRUE(blended || declined) << "got " << centre
EXPECT_TRUE(blended || declined) << "got " << center
<< ", which is neither the dual-source blend (~128) nor the declined "
<< "straight-through source (255) - the SRC1 factors were mistranslated";
Gl().EndFrame();
@@ -227,9 +227,9 @@ void main()
const Image image = ReadPixels(kExtent, kExtent);
ASSERT_FALSE(image.Empty());
const Rgba8 centre = image.At(kExtent / 2, kExtent / 2);
EXPECT_GT(static_cast<int>(centre.r), 245)
<< "got " << centre << ": blending is disabled, so the source has to be written straight through";
const Rgba8 center = image.At(kExtent / 2, kExtent / 2);
EXPECT_GT(static_cast<int>(center.r), 245)
<< "got " << center << ": blending is disabled, so the source has to be written straight through";
Gl().EndFrame();
}
@@ -1209,7 +1209,7 @@ void main() { color = value; }
if (Gl().BackendName() != "DirectVulkan") GTEST_SKIP() << "Magma short-UBO compatibility policy";
Attach(GL_RGBA8);
const char* fragment = R"(#version 430 core
layout(std140, binding=3) uniform ShortColour { vec4 value; vec4 tail; };
layout(std140, binding=3) uniform ShortColor { vec4 value; vec4 tail; };
layout(location=0) out vec4 color;
void main() { color = value + tail; }
)";
@@ -1265,7 +1265,7 @@ void main() { color = value + tail; }
if (Gl().BackendName() != "DirectVulkan") GTEST_SKIP() << "Magma short-UBO compatibility policy";
Attach(GL_RGBA8);
const char* fragment = R"(#version 430 core
layout(std140, binding=3) uniform ShortColour { vec4 value; vec4 tail; };
layout(std140, binding=3) uniform ShortColor { vec4 value; vec4 tail; };
layout(location=0) out vec4 color;
void main() { color = value + tail; }
)";
@@ -2136,17 +2136,16 @@ void main() { color = vec4(0.5, 0.5, 0.5, 1.0); }
for (int layer = 0; layer < 2; ++layer)
for (int y = 0; y < 8; ++y)
for (int x = 0; x < 8; ++x) {
const auto colour = layer == 0 ? InPlaceQuadrant(x, y) : kInPlaceMagenta;
const auto color = layer == 0 ? InPlaceQuadrant(x, y) : kInPlaceMagenta;
const size_t at = (static_cast<size_t>(layer) * 64 + y * 8 + x) * 4;
std::copy(colour.begin(), colour.end(), level0.begin() + at);
std::copy(color.begin(), color.end(), level0.begin() + at);
}
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, 0, 8, 8, 2, GL_RGBA, GL_UNSIGNED_BYTE, level0.data());
std::vector<GLubyte> level1(4 * 4 * 2 * 4);
for (int layer = 0; layer < 2; ++layer)
for (int at = 0; at < 16; ++at) {
const auto colour = layer == 0 ? kInPlaceCyan : kInPlaceWhite;
std::copy(colour.begin(), colour.end(),
level1.begin() + (static_cast<size_t>(layer) * 16 + at) * 4);
const auto color = layer == 0 ? kInPlaceCyan : kInPlaceWhite;
std::copy(color.begin(), color.end(), level1.begin() + (static_cast<size_t>(layer) * 16 + at) * 4);
}
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 1, 0, 0, 0, 4, 4, 2, GL_RGBA, GL_UNSIGNED_BYTE, level1.data());
return name;
@@ -139,14 +139,14 @@ void main() {
std::vector<unsigned char> bytes(static_cast<std::size_t>(kSize) * kSize * 4, 0);
glReadBuffer(GL_COLOR_ATTACHMENT0 + index);
glReadPixels(0, 0, kSize, kSize, GL_RGBA, GL_UNSIGNED_BYTE, bytes.data());
std::vector<float> centre(4, -1.0f);
std::vector<float> center(4, -1.0f);
// The middle pixel: the quad covers the whole target, so every pixel is the same,
// and the middle one cannot be a rasterization edge case.
const std::size_t offset = (static_cast<std::size_t>(kSize / 2) * kSize + kSize / 2) * 4;
for (int i = 0; i < 4; ++i) {
centre[static_cast<std::size_t>(i)] = static_cast<float>(bytes[offset + i]) / 255.0f;
center[static_cast<std::size_t>(i)] = static_cast<float>(bytes[offset + i]) / 255.0f;
}
return centre;
return center;
}
GLuint m_fbo = 0;
@@ -216,8 +216,8 @@ void main() { o_color = gl_FragCoord.y < u_split ? u_bottom : u_top; }
return texels;
}
static std::vector<float> Flat(const Format& format, int edge, const Rgb& colour, float depth) {
return Halves(format, edge, colour, colour, depth, depth);
static std::vector<float> Flat(const Format& format, int edge, const Rgb& color, float depth) {
return Halves(format, edge, color, color, depth, depth);
}
static void DefineLevel(const Format& format, int level, int edge, const std::vector<float>& texels) {
@@ -273,8 +273,8 @@ void main() { o_color = gl_FragCoord.y < u_split ? u_bottom : u_top; }
if (std::abs(got.r - want) > 2) return;
continue;
}
const Rgb& colour = low ? bottom : top;
const int want[3] = {ToByte(colour[0]), ToByte(colour[1]), ToByte(colour[2])};
const Rgb& color = low ? bottom : top;
const int want[3] = {ToByte(color[0]), ToByte(color[1]), ToByte(color[2])};
const int have[3] = {got.r, got.g, got.b};
for (int c = 0; c < 3; ++c) {
EXPECT_NEAR(have[c], want[c], 2)
@@ -285,9 +285,9 @@ void main() { o_color = gl_FragCoord.y < u_split ? u_bottom : u_top; }
}
}
static void ExpectFlat(const Image& image, const Format& format, const Rgb& colour, float depth,
static void ExpectFlat(const Image& image, const Format& format, const Rgb& color, float depth,
const std::string& what) {
ExpectHalves(image, format, colour, colour, depth, depth, what);
ExpectHalves(image, format, color, color, depth, depth, what);
}
void Generate() {
@@ -324,10 +324,10 @@ void main() { o_color = vec4(0.0, 1.0, 0.0, 1.0); }
}
glUseProgram(0);
const Rgba8 centre = DrawAndRead(program);
const Rgba8 center = DrawAndRead(program);
EXPECT_EQ(FirstGLError(), 0u);
EXPECT_EQ(centre.r, 0) << "sampler array elements that read the wrong texture: " << BadElements(centre.r);
EXPECT_EQ(centre.g, 255) << "the draw did not reach the fragment stage at all";
EXPECT_EQ(center.r, 0) << "sampler array elements that read the wrong texture: " << BadElements(center.r);
EXPECT_EQ(center.g, 255) << "the draw did not reach the fragment stage at all";
}
// An array of ARRAYS of samplers is the shape the two reflections count differently:
@@ -341,7 +341,7 @@ void main() { o_color = vec4(0.0, 1.0, 0.0, 1.0); }
// Deliberately weak on the pixels for that reason: what is asserted on every backend is that
// the program builds, the draw raises no GL error, and the process survives. Where the
// descriptors do resolve, the colors are checked too.
TEST_F(Glsl420DeclarationScenario, AnArrayOfSamplerArraysIsHonouredOrDeclinedCleanly) {
TEST_F(Glsl420DeclarationScenario, AnArrayOfSamplerArraysIsHonoredOrDeclinedCleanly) {
if (!Ready()) return;
static const std::uint8_t colors[kElements][4] = {
@@ -372,7 +372,7 @@ void main() { o_color = vec4(0.0, 1.0, 0.0, 1.0); }
}
glUseProgram(0);
const Rgba8 centre = DrawAndRead(program);
const Rgba8 center = DrawAndRead(program);
EXPECT_EQ(FirstGLError(), 0u) << "declining a descriptor array must not raise a GL error";
if (!MultiDimensionalSamplerArraysAreDeclined()) {
@@ -386,14 +386,14 @@ void main() { o_color = vec4(0.0, 1.0, 0.0, 1.0); }
// painted nothing, which is the documented Magma outcome. A non-zero red channel is the
// third: the draw DID reach the fragment stage and elements read the wrong textures,
// which is exactly the silent mismatch this decline exists to prevent.
if (centre.g == 255) {
EXPECT_EQ(centre.r, 0) << "elements of the array of arrays that read the wrong texture: "
<< BadElements(centre.r);
if (center.g == 255) {
EXPECT_EQ(center.r, 0) << "elements of the array of arrays that read the wrong texture: "
<< BadElements(center.r);
return;
}
EXPECT_EQ(centre.r, 0) << "the array of arrays was not resolved, but the draw still painted "
EXPECT_EQ(center.r, 0) << "the array of arrays was not resolved, but the draw still painted "
"a mismatch instead of being declined: "
<< BadElements(centre.r);
<< BadElements(center.r);
}
// Instance k of a uniform block array sits on buffer binding point N+k - again both as
@@ -420,10 +420,10 @@ void main() { o_color = vec4(0.0, 1.0, 0.0, 1.0); }
}
EXPECT_EQ(FirstGLError(), 0u) << "the block queries left a GL error behind";
const Rgba8 centre = DrawAndRead(program);
const Rgba8 center = DrawAndRead(program);
EXPECT_EQ(FirstGLError(), 0u);
EXPECT_EQ(centre.r, 0) << "block array instances that read the wrong buffer: " << BadElements(centre.r);
EXPECT_EQ(centre.g, 255) << "the draw did not reach the fragment stage at all";
EXPECT_EQ(center.r, 0) << "block array instances that read the wrong buffer: " << BadElements(center.r);
EXPECT_EQ(center.g, 255) << "the draw did not reach the fragment stage at all";
}
// 'invariant' written on a fragment input at #version 420. The same source compiles at
@@ -434,10 +434,10 @@ void main() { o_color = vec4(0.0, 1.0, 0.0, 1.0); }
const GLuint program = Build(kInvariantInVS, kInvariantInFS);
if (program == 0) return;
const Rgba8 centre = DrawAndRead(program);
const Rgba8 center = DrawAndRead(program);
EXPECT_EQ(FirstGLError(), 0u);
EXPECT_EQ(centre.g, 255) << "the invariant-qualified varying did not arrive";
EXPECT_EQ(centre.r, 0);
EXPECT_EQ(center.g, 255) << "the invariant-qualified varying did not arrive";
EXPECT_EQ(center.r, 0);
}
// A #version 420 shader may call atomicCounterIncrement() with no extension at all. The
@@ -477,9 +477,9 @@ void main() { o_color = vec4(0.0, 1.0, 0.0, 1.0); }
<< " yet; the compile above is what this case pins";
}
const Rgba8 centre = DrawAndRead(program);
const Rgba8 center = DrawAndRead(program);
EXPECT_EQ(FirstGLError(), 0u);
EXPECT_EQ(centre.g, 255) << "the atomic-counter shader linked but painted nothing";
EXPECT_EQ(center.g, 255) << "the atomic-counter shader linked but painted nothing";
}
// `layout(location = 0, index = 0)` is the GL default written out loud, and an application
@@ -495,11 +495,11 @@ void main() { o_color = vec4(0.0, 1.0, 0.0, 1.0); }
const GLuint program = Build(kQuadVS, kExplicitColorIndexFS);
if (program == 0) return;
const Rgba8 centre = DrawAndRead(program);
const Rgba8 center = DrawAndRead(program);
EXPECT_EQ(FirstGLError(), 0u);
EXPECT_EQ(centre.g, 255) << "a fragment output declared layout(location = 0, index = 0) painted "
EXPECT_EQ(center.g, 255) << "a fragment output declared layout(location = 0, index = 0) painted "
"nothing; its stage was almost certainly refused by the driver";
EXPECT_EQ(centre.r, 0u);
EXPECT_EQ(center.r, 0u);
}
} // namespace MGITest
@@ -165,7 +165,7 @@ namespace MGITest {
// MGITEST_HANDLE_ARM would otherwise downgrade that lane's Handles or AbaControl assertion
// to the Legacy one, which passes - a lane reporting green for an arm it never ran. Same
// shape as CsoContentAddressingScenario's FAIL() on an unknown MGITEST_CSO_LANE.
bool ArmNameIsRecognised() {
bool ArmNameIsRecognized() {
const char* name = std::getenv(kArmMarker);
return name == nullptr || std::strcmp(name, "handles") == 0 || std::strcmp(name, "legacy") == 0 ||
std::strcmp(name, "aba") == 0;
@@ -440,7 +440,7 @@ void main() { oColor = texture(uTex, vUv); }
void SetUp() override {
ScenarioTest::SetUp();
if (!Ready()) return;
if (!ArmNameIsRecognised()) {
if (!ArmNameIsRecognized()) {
const char* raw = std::getenv(kArmMarker);
FAIL() << "unknown " << kArmMarker << " value '" << (raw != nullptr ? raw : "")
<< "': the arms are handles / legacy / aba. Reading an unrecognized name "
@@ -137,7 +137,7 @@ void main()
// the whole span instead, which cost seven for those four elements and had to be
// declined on a stage that could not afford them - hence the budget gate that used
// to be here.) DirectVulkan needs no rewrite at all.
bool PerElementImageUnitsAreHonoured() const {
bool PerElementImageUnitsAreHonored() const {
if (Gl().BackendName() == "DirectVulkan") return true;
GLint maxFragmentImageUniforms = 0;
glGetIntegerv(GL_MAX_FRAGMENT_IMAGE_UNIFORMS, &maxFragmentImageUniforms);
@@ -172,7 +172,7 @@ void main()
TEST_F(ImageLoadStoreSsoScenario, PerElementImageUnitsReachAPipelineDraw) {
if (!Ready()) return;
if (!ImagesAreUsable()) GTEST_SKIP() << "fewer than 8 image units";
if (!PerElementImageUnitsAreHonoured()) {
if (!PerElementImageUnitsAreHonored()) {
GTEST_SKIP() << "fewer than 4 fragment image uniforms: the array under test does not fit";
}
HeadlessGL& gl = Gl();
@@ -152,7 +152,7 @@ void main()
}
// Draw once with `expected` pushed to the shader and report the center pixel.
void DrawAndReadCentre(int expectedWidth, int expectedHeight, unsigned char (&centre)[4]) {
void DrawAndReadCenter(int expectedWidth, int expectedHeight, unsigned char (&center)[4]) {
const GLint location = glGetUniformLocation(m_program, "g_expected_size");
ASSERT_NE(location, -1) << "the program has no g_expected_size uniform";
glUseProgram(m_program);
@@ -171,7 +171,7 @@ void main()
const std::size_t offset =
(static_cast<std::size_t>(kTargetSize / 2) * kTargetSize + kTargetSize / 2) * 4;
for (int i = 0; i < 4; ++i) {
centre[i] = pixels[offset + static_cast<std::size_t>(i)];
center[i] = pixels[offset + static_cast<std::size_t>(i)];
}
}
@@ -207,12 +207,12 @@ void main()
glBindImageTexture(0, m_image, 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA8);
ASSERT_EQ(FirstGLError(), 0u) << "binding the image texture errored";
unsigned char centre[4] = {0, 0, 0, 0};
DrawAndReadCentre(32, 32, centre);
EXPECT_EQ(static_cast<int>(centre[0]), 0)
<< "the FIRST draw already disagrees about imageSize(): got (" << static_cast<int>(centre[0]) << ", "
<< static_cast<int>(centre[1]) << ", " << static_cast<int>(centre[2]) << ")";
EXPECT_EQ(static_cast<int>(centre[1]), 255);
unsigned char center[4] = {0, 0, 0, 0};
DrawAndReadCenter(32, 32, center);
EXPECT_EQ(static_cast<int>(center[0]), 0)
<< "the FIRST draw already disagrees about imageSize(): got (" << static_cast<int>(center[0]) << ", "
<< static_cast<int>(center[1]) << ", " << static_cast<int>(center[2]) << ")";
EXPECT_EQ(static_cast<int>(center[1]), 255);
// The re-spec. The image unit is deliberately NOT re-bound: GL 4.6 core 8.26 says the
// unit references the texture object, so this alone has to be visible to the next draw.
@@ -220,12 +220,12 @@ void main()
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
ASSERT_EQ(FirstGLError(), 0u) << "re-specifying the image texture errored";
DrawAndReadCentre(16, 16, centre);
EXPECT_EQ(static_cast<int>(centre[0]), 0)
DrawAndReadCenter(16, 16, center);
EXPECT_EQ(static_cast<int>(center[0]), 0)
<< "after the re-spec the draw still sees the OLD image size; center pixel was ("
<< static_cast<int>(centre[0]) << ", " << static_cast<int>(centre[1]) << ", " << static_cast<int>(centre[2])
<< static_cast<int>(center[0]) << ", " << static_cast<int>(center[1]) << ", " << static_cast<int>(center[2])
<< ")";
EXPECT_EQ(static_cast<int>(centre[1]), 255);
EXPECT_EQ(static_cast<int>(center[1]), 255);
}
} // namespace MGITest
@@ -267,7 +267,7 @@ void main()
// The floor, and the control that keeps the two tests below from passing vacuously: an INSIDE
// sample has to fetch the texture's own texel. If this fails the sampler, the shader or the
// integer readback is broken and nothing about the border color has been measured.
TEST_F(IntegerBorderColorScenario, InsideTexelsAreUnaffectedByTheBorderColour) {
TEST_F(IntegerBorderColorScenario, InsideTexelsAreUnaffectedByTheBorderColor) {
if (!Ready()) GTEST_SKIP();
glBindTexture(GL_TEXTURE_2D, m_sourceTexture);
@@ -284,7 +284,7 @@ void main()
// The regression, texture-object spelling. glTexParameterIiv is the entry point the frontend
// already accepted and then flattened into the same FloatVec4 every other spelling wrote.
TEST_F(IntegerBorderColorScenario, TexParameterIivBorderColourSurvivesToAnIntegerSampler) {
TEST_F(IntegerBorderColorScenario, TexParameterIivBorderColorSurvivesToAnIntegerSampler) {
if (!Ready()) GTEST_SKIP();
glBindTexture(GL_TEXTURE_2D, m_sourceTexture);
@@ -299,7 +299,7 @@ void main()
// and a separate code path in both backends (BackendSamplerObject::Sync on DirectGLES, and the
// sampler cache key on DirectVulkan, where a border color that is not part of the key would
// alias two samplers that differ only in it).
TEST_F(IntegerBorderColorScenario, SamplerParameterIivBorderColourSurvivesToAnIntegerSampler) {
TEST_F(IntegerBorderColorScenario, SamplerParameterIivBorderColorSurvivesToAnIntegerSampler) {
if (!Ready()) GTEST_SKIP();
glGenSamplers(1, &m_sampler);
@@ -282,7 +282,7 @@ void main()
// Clears to BLUE, so "the draw painted nothing" is a color of its own rather
// than something that could be mistaken for a zeroed payload.
Rgba8 DrawAndReadCentre(GLuint program) const {
Rgba8 DrawAndReadCenter(GLuint program) const {
glViewport(0, 0, Gl().Width(), Gl().Height());
glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
@@ -344,9 +344,9 @@ void main()
<< Gl().BackendName() << ", so there is no block to carry through: " << BuildLog();
}
const Rgba8 centre = DrawAndReadCentre(program);
const Rgba8 center = DrawAndReadCenter(program);
EXPECT_EQ(FirstGLError(), 0u);
EXPECT_TRUE(IsGreen(centre)) << "the control pipeline did not deliver its payload: " << centre;
EXPECT_TRUE(IsGreen(center)) << "the control pipeline did not deliver its payload: " << center;
}
TEST_F(IoBlockNameCollisionScenario, OneBlockNameInBothDirectionsStillCarriesThePayload) {
@@ -368,12 +368,12 @@ void main()
"desktop GLSL, but the program did not build: "
<< BuildLog();
const Rgba8 centre = DrawAndReadCentre(program);
const Rgba8 center = DrawAndReadCenter(program);
EXPECT_EQ(FirstGLError(), 0u);
EXPECT_TRUE(IsGreen(centre))
EXPECT_TRUE(IsGreen(center))
<< "the payload did not survive the stage that names its input and output block "
"the same: "
<< centre
<< center
<< " (blue: nothing drew; red: the plain varying was lost too; black: "
"the block arrived empty)";
}
@@ -320,12 +320,12 @@ void main() { o_color = vec4(0.1, 0.2, 0.8, 1.0); }
glClearDepth(0.375);
glClear(GL_DEPTH_BUFFER_BIT);
const float centre = ReadDepthAt(kWidth / 2, kHeight / 2);
const float center = ReadDepthAt(kWidth / 2, kHeight / 2);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
EXPECT_EQ(FirstGLError(), 0u);
EXPECT_NEAR(centre, 0.375f, 1.0f / 4096.0f)
<< "glReadPixels(GL_DEPTH_COMPONENT) off layer " << kSubjectLayer << " returned " << centre
<< (std::fabs(centre - kDepthPoison) < 1e-6f ? " - the destination was never written at all" : "");
EXPECT_NEAR(center, 0.375f, 1.0f / 4096.0f)
<< "glReadPixels(GL_DEPTH_COMPONENT) off layer " << kSubjectLayer << " returned " << center
<< (std::fabs(center - kDepthPoison) < 1e-6f ? " - the destination was never written at all" : "");
}
// The depth leg of the blit path, both endpoints above layer 0. Verified by reading the
@@ -909,7 +909,7 @@ void main()
//
// The draw goes into a scratch 2D target, never into the layered attachment, so the
// materialize path is the only consumer that can fire.
TEST_F(LayeredAttachmentShapeScenario, LayeredCubeMapClearMaterialisedBySamplingReachesEveryFace) {
TEST_F(LayeredAttachmentShapeScenario, LayeredCubeMapClearMaterializedBySamplingReachesEveryFace) {
if (!Ready()) return;
const GLuint program = BuildProgram(nullptr, kCubeSampleFragmentSource);
@@ -974,7 +974,7 @@ void main()
// The 1D-array half of the same divergence. Pre-existing rather than introduced by this
// branch (the clear copy never had ToVulkanLevelExtent), and fixed by the same hoist.
TEST_F(LayeredAttachmentShapeScenario, LayeredOneDArrayClearMaterialisedBySamplingReachesEveryLayer) {
TEST_F(LayeredAttachmentShapeScenario, LayeredOneDArrayClearMaterializedBySamplingReachesEveryLayer) {
if (!Ready()) return;
const GLuint program = BuildProgram(nullptr, kOneDArraySampleFragmentSource);
@@ -56,7 +56,7 @@ namespace MGITest {
constexpr int kCubeLayerFaces = 12; // two cubes, which is what the conformance case uses
// A value no store writes, so "the store never landed" and "the store wrote the wrong
// thing" cannot be confused - and so a readback served from the stale CPU shadow is
// recognisable on sight.
// recognizable on sight.
constexpr GLuint kSeed = 0xFEEDBEEFu;
// Deliberately not 0: the unit has to travel through glUniform1i and be baked into the
// generated ESSL, so a defect there cannot hide behind the default.
@@ -443,8 +443,8 @@ void main() { fragColor = color[0]; })";
const PipeStatsWindow::LogMark mark = SetupFrame();
ClearTo(0.0f, 0.0f, 0.0f, 0.0f);
const Rgba8 colourA{200, 60, 30, 255};
const Rgba8 colourB{60, 200, 90, 255};
const Rgba8 colorA{200, 60, 30, 255};
const Rgba8 colorB{60, 200, 90, 255};
const auto blockBytes = [](std::size_t size, const Rgba8& c) {
// The block's vec4 at offset 0; every other slot holds a color no step draws, so a
// read off any other offset is as visible as a read off the dead store.
@@ -463,7 +463,7 @@ void main() { fragColor = color[0]; })";
glGenBuffers(1, &ubo);
glGenBuffers(1, &pin);
glBindBuffer(GL_UNIFORM_BUFFER, ubo);
const std::vector<std::uint8_t> bytesA = blockBytes(kAbaDeadStoreBytes, colourA);
const std::vector<std::uint8_t> bytesA = blockBytes(kAbaDeadStoreBytes, colorA);
glBufferData(GL_UNIFORM_BUFFER, GLsizeiptr(bytesA.size()), bytesA.data(), GL_DYNAMIC_DRAW);
glBindBufferBase(GL_UNIFORM_BUFFER, 0, ubo);
glBindBuffer(GL_COPY_WRITE_BUFFER, pin);
@@ -496,7 +496,7 @@ void main() { fragColor = color[0]; })";
glBufferData(GL_ARRAY_BUFFER, 0, nullptr, GL_STREAM_DRAW);
// Step 7: a new store through the freed handle value, holding B.
glBindBuffer(GL_UNIFORM_BUFFER, ubo);
const std::vector<std::uint8_t> bytesB = blockBytes(kAbaNewStoreBytes, colourB);
const std::vector<std::uint8_t> bytesB = blockBytes(kAbaNewStoreBytes, colorB);
glBufferData(GL_UNIFORM_BUFFER, GLsizeiptr(bytesB.size()), bytesB.data(), GL_DYNAMIC_DRAW);
glBindBufferBase(GL_UNIFORM_BUFFER, 0, ubo);
glBindBuffer(GL_ARRAY_BUFFER, m_buffer);
@@ -508,8 +508,8 @@ void main() { fragColor = color[0]; })";
const Image image = ReadPixels(kWidth, kHeight);
ASSERT_EQ(FirstGLError(), GLenum(GL_NO_ERROR));
const Rgba8 got = image.At(kWidth / 2, kHeight / 2);
EXPECT_TRUE(Near(got, colourB))
<< "read " << got << ", wanted " << colourB << " (the live store's bytes). " << colourA
EXPECT_TRUE(Near(got, colorB))
<< "read " << got << ", wanted " << colorB << " (the live store's bytes). " << colorA
<< " is the DEAD store's color: the draw through the re-minted VkBuffer handle reused a "
"descriptor set memoized under the first store's handle and read its freed memory "
"(ID-P7-43); anything else is that memory already reused";
@@ -189,10 +189,10 @@ void main() { o_color = vec4(0.25, 0.5, 0.75, 1.0); }
glBindVertexArray(0);
glDeleteBuffers(1, &vbo);
glDeleteVertexArrays(1, &vao);
m_centre = pixel;
m_center = pixel;
}
Rgba8 m_centre{};
Rgba8 m_center{};
};
// THE CASE THAT FAILS A LANE WHOSE LIBRARY NEVER ARMED.
@@ -311,7 +311,7 @@ namespace MGITest {
// not tell a missing round-up from a present one); SKIP_ROWS 1 and SKIP_PIXELS 2 move the
// first pixel to byte 24 + 8 = 32 of the image; and the read is issued at buffer offset 16,
// so the first pixel lands at 48 and a branch that dropped the offset would land at 32.
TEST_F(PixelStoreSweepScenario, ReadPixelsIntoAPackBufferHonoursPackStateAndLeavesTheGapsAlone) {
TEST_F(PixelStoreSweepScenario, ReadPixelsIntoAPackBufferHonorsPackStateAndLeavesTheGapsAlone) {
if (!Ready()) return;
ResetAllPixelStoreModes();
@@ -277,8 +277,8 @@ void main() { o_color = vec4(0.25, 0.5, 0.75, 1.0); }
// The verb the omission names (kOmittedPairs): a readback, which is a round trip,
// so the server has applied it - and read the pack state - before this returns.
unsigned char centre[4] = {};
glReadPixels(gl.Width() / 2, gl.Height() / 2, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, centre);
unsigned char center[4] = {};
glReadPixels(gl.Width() / 2, gl.Height() / 2, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, center);
std::fprintf(stderr, "[itest] poison worker: glReadPixels returned\n");
// The sequence is the WHOLE datum this child reports, so a GL error in it must be
@@ -275,7 +275,7 @@ void main()
// A second draw with a DIFFERENT restart index has to be rewritten again. The substitution
// stages through one scratch buffer, so a cached or half-restored element-array binding
// would show up here as the second draw reusing the first one's data.
TEST_F(PrimitiveRestartScenario, ChangingTheRestartIndexBetweenDrawsIsHonoured) {
TEST_F(PrimitiveRestartScenario, ChangingTheRestartIndexBetweenDrawsIsHonored) {
if (!Ready()) GTEST_SKIP();
glEnable(GL_PRIMITIVE_RESTART);
@@ -553,9 +553,9 @@ void main() { o_color = vec4(1.0); }
const int height = gl.Height();
static const char* kUniformBlockFS = R"(#version 430 core
layout(std140) uniform Color { vec4 u_colour; };
layout(std140) uniform Color { vec4 u_color; };
out vec4 o_color;
void main() { o_color = u_colour; }
void main() { o_color = u_color; }
)";
const GLuint vs = MakeSeparable(GL_VERTEX_SHADER, kSeparableVS);
const GLuint fs = MakeSeparable(GL_FRAGMENT_SHADER, kUniformBlockFS);
@@ -698,9 +698,9 @@ void main() { o_color = vec4(0.0, 1.0, 0.0, u_mvp[3][3]); }
const int height = gl.Height();
static const char* kUniformBlockFS = R"(#version 430 core
layout(std140) uniform Color { vec4 u_colour; };
layout(std140) uniform Color { vec4 u_color; };
out vec4 o_color;
void main() { o_color = u_colour; }
void main() { o_color = u_color; }
)";
const GLuint vs = MakeSeparable(GL_VERTEX_SHADER, kSeparableVS);
const GLuint fs = MakeSeparable(GL_FRAGMENT_SHADER, kUniformBlockFS);
@@ -788,14 +788,14 @@ void main() { o_color = texture(u_tex, vec2(0.5)); }
// One texture per unit, each a different solid color, so the pixels say which unit the
// draw actually sampled.
constexpr int kUnits = 4;
const GLubyte colours[kUnits][4] = {{255, 0, 0, 255}, {0, 255, 0, 255}, {0, 0, 255, 255}, {255, 255, 0, 255}};
const GLubyte colors[kUnits][4] = {{255, 0, 0, 255}, {0, 255, 0, 255}, {0, 0, 255, 255}, {255, 255, 0, 255}};
const char* names[kUnits] = {"red", "green", "blue", "yellow"};
GLuint textures[kUnits] = {};
glGenTextures(kUnits, textures);
for (int unit = 0; unit < kUnits; ++unit) {
glActiveTexture(GL_TEXTURE0 + unit);
glBindTexture(GL_TEXTURE_2D, textures[unit]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, colours[unit]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, colors[unit]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
}
@@ -144,12 +144,12 @@ void main()
void ExpectBlendedRatherThanOverwritten(const char* what) {
const Image image = ReadPixels(kExtent, kExtent);
ASSERT_FALSE(image.Empty()) << what;
const Rgba8 centre = image.At(kExtent / 2, kExtent / 2);
EXPECT_GT(int(centre.r), 40) << what << ": got " << centre
const Rgba8 center = image.At(kExtent / 2, kExtent / 2);
EXPECT_GT(int(center.r), 40) << what << ": got " << center
<< ", which is darker than a blend of "
"black and 50% white";
EXPECT_LT(int(centre.r), 215)
<< what << ": got " << centre << ", which is the source color - blending was disabled";
EXPECT_LT(int(center.r), 215)
<< what << ": got " << center << ", which is the source color - blending was disabled";
}
} // namespace
@@ -139,9 +139,9 @@ void main() { o_color = vec4(float(gl_NumSamples) * (16.0 / 255.0), 0.0, 0.0, 1.
{
const Image resolved = ReadPixels(width, height);
const Rgba8 centre = resolved.At(width / 2, height / 2);
EXPECT_NEAR(centre.r, 16 * realizedSamples, 2)
<< "gl_NumSamples read " << (centre.r / 16.0) << " into a " << realizedSamples
const Rgba8 center = resolved.At(width / 2, height / 2);
EXPECT_NEAR(center.r, 16 * realizedSamples, 2)
<< "gl_NumSamples read " << (center.r / 16.0) << " into a " << realizedSamples
<< "-sample framebuffer; 1 means the reserved uniform was never written, 0 means it was "
<< "written but never uploaded";
}
@@ -165,11 +165,11 @@ void main() { o_color = vec4(float(gl_NumSamples) * (16.0 / 255.0), 0.0, 0.0, 1.
{
const Image single = ReadPixels(width, height);
const Rgba8 centre = single.At(width / 2, height / 2);
const Rgba8 center = single.At(width / 2, height / 2);
// GL 4.6 core 15.2.2: gl_NumSamples is ONE for a non-multisample framebuffer, where
// glGetIntegerv(GL_SAMPLES) answers zero.
EXPECT_NEAR(centre.r, 16, 2)
<< "gl_NumSamples read " << (centre.r / 16.0)
EXPECT_NEAR(center.r, 16, 2)
<< "gl_NumSamples read " << (center.r / 16.0)
<< " into a single-sample framebuffer; the value is a property of the DRAW FRAMEBUFFER, "
<< "so re-using the program must re-write it";
}
@@ -255,10 +255,10 @@ void main() { o_color = vec4(float(gl_NumSamples) * (16.0 / 255.0), 0.0, 0.0, 1.
glBindFramebuffer(GL_FRAMEBUFFER, 0);
const Image resolved = ReadPixels(width, height);
const Rgba8 centre = resolved.At(width / 2, height / 2);
const Rgba8 center = resolved.At(width / 2, height / 2);
// The rate changes how OFTEN the shader runs, never what it computes - so the same
// gl_NumSamples reading has to come back.
EXPECT_NEAR(centre.r, 16 * realizedSamples, 2) << "the draw changed its result once sample shading was enabled";
EXPECT_NEAR(center.r, 16 * realizedSamples, 2) << "the draw changed its result once sample shading was enabled";
glMinSampleShading(0.0f);
glDisable(GL_SAMPLE_SHADING);
@@ -10,7 +10,7 @@
//
// DirectVulkan does not bind a texture GL calls incomplete: it substitutes a fallback so the
// sampler reads (0,0,0,1) instead of losing the draw. That decision is made twice per draw - once
// by CollectSampledTextures, which builds the list SetupDraw syncs, materialises pending clears
// by CollectSampledTextures, which builds the list SetupDraw syncs, materializes pending clears
// for and transitions to a sampled layout BEFORE the render pass opens, and once by the descriptor
// resolve inside the pass. Both ask SamplesAsIncompleteTexture.
//
@@ -138,7 +138,7 @@ void main() {
} // namespace
// The full sequence, ordered so the ONLY state change between the two draws is the filter.
TEST_F(SampledSetStalenessScenario, AQueuedClearIsMaterialisedWhenAFilterChangeCompletesTheTexture) {
TEST_F(SampledSetStalenessScenario, AQueuedClearIsMaterializedWhenAFilterChangeCompletesTheTexture) {
if (!Ready() || IsSkipped()) return;
// 1. Queue a clear on the texture through an FBO and take it straight back out, with no
@@ -186,7 +186,7 @@ void main() {
// The same flip driven from a SAMPLER OBJECT rather than the texture's own parameters. It is
// the other half of what feeds the completeness predicate, it moves the same generation, and
// it likewise binds nothing.
TEST_F(SampledSetStalenessScenario, AQueuedClearIsMaterialisedWhenASamplerObjectCompletesTheTexture) {
TEST_F(SampledSetStalenessScenario, AQueuedClearIsMaterializedWhenASamplerObjectCompletesTheTexture) {
if (!Ready() || IsSkipped()) return;
GLuint sampler = 0;
@@ -316,9 +316,9 @@ namespace MGITest {
EXPECT_EQ(FirstGLError(), 0u);
const Image painted = ReadPixels(width, height);
const Rgba8 centre = painted.At(width / 2, height / 2);
EXPECT_LT(centre.r, 32) << "the fragment module wrote the wrong channel; constant id 7 was not applied";
EXPECT_GT(centre.g, 224) << "the center of a 0.5-scaled quad must be painted";
const Rgba8 center = painted.At(width / 2, height / 2);
EXPECT_LT(center.r, 32) << "the fragment module wrote the wrong channel; constant id 7 was not applied";
EXPECT_GT(center.g, 224) << "the center of a 0.5-scaled quad must be painted";
// A pixel just inside the corner is OUTSIDE the 0.5-scaled quad and must still be the
// clear color - which is what proves constant id 3 reached the vertex module. At the
@@ -136,7 +136,7 @@ void main() {
return program;
}
// A buffer of `elements` ivec4s, filled with a recognisable pattern.
// A buffer of `elements` ivec4s, filled with a recognizable pattern.
GLuint MakeStorageBuffer(int elements) {
std::vector<int> contents(static_cast<std::size_t>(elements) * 4, 41);
GLuint buffer = 0;
@@ -47,7 +47,7 @@
namespace MGITest {
namespace {
// The conformance test's own source texel, one recognisable value per channel.
// The conformance test's own source texel, one recognizable value per channel.
constexpr std::uint32_t kSourceTexel[4] = {0x3FFFFFFFu, 0x7FFFFFFFu, 0xBFFFFFFFu, 0xFFFFFFFFu};
constexpr int kOutputWidth = 8;
@@ -130,7 +130,7 @@ void main() { o_color = texture(u_texture, vec2(0.5, 0.5)); }
glUniform1i(glGetUniformLocation(m_program, "u_texture"), 0);
}
std::array<GLubyte, 4> DrawAndReadCentre() {
std::array<GLubyte, 4> DrawAndReadCenter() {
glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);
glClearColor(0.5f, 0.5f, 0.5f, 0.5f);
glClear(GL_COLOR_BUFFER_BIT);
@@ -170,7 +170,7 @@ void main() { o_color = texture(u_texture, vec2(0.5, 0.5)); }
if (HasFatalFailure()) return;
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, m_source);
const auto texel = DrawAndReadCentre();
const auto texel = DrawAndReadCenter();
EXPECT_EQ(glGetError(), static_cast<GLenum>(GL_NO_ERROR));
EXPECT_EQ(static_cast<int>(texel[0]), 255) << "red: the multisample texture was not bound";
EXPECT_EQ(static_cast<int>(texel[1]), 0);
@@ -191,12 +191,12 @@ void main() { o_color = texture(u_texture, vec2(0.5, 0.5)); }
UseProgram(kSample2DSource);
if (HasFatalFailure()) return;
const auto bound = DrawAndReadCentre();
const auto bound = DrawAndReadCenter();
ASSERT_EQ(static_cast<int>(bound[0]), 255) << "the control: the bound texture is sampled";
ASSERT_EQ(static_cast<int>(bound[1]), 0);
glBindTexture(GL_TEXTURE_2D, 0);
const auto unbound = DrawAndReadCentre();
const auto unbound = DrawAndReadCenter();
EXPECT_EQ(glGetError(), static_cast<GLenum>(GL_NO_ERROR));
EXPECT_EQ(static_cast<int>(unbound[0]), 0)
<< "texture 0 is bound, yet the unit still answers with the texture bound before it";
@@ -224,19 +224,19 @@ void main() { oColor = texture(uTex, vec3(vUv, float(uLayer))); }
30, 255};
}
static std::vector<std::uint8_t> Fill(Rgba8 colour) {
static std::vector<std::uint8_t> Fill(Rgba8 color) {
std::vector<std::uint8_t> texels(static_cast<std::size_t>(kSize) * kSize * 4);
for (std::size_t i = 0; i < texels.size(); i += 4) {
texels[i + 0] = colour.r;
texels[i + 1] = colour.g;
texels[i + 2] = colour.b;
texels[i + 3] = colour.a;
texels[i + 0] = color.r;
texels[i + 1] = color.g;
texels[i + 2] = color.b;
texels[i + 3] = color.a;
}
return texels;
}
void UploadLayerThroughOwner(GLuint owner, int layer, Rgba8 colour) {
const std::vector<std::uint8_t> texels = Fill(colour);
void UploadLayerThroughOwner(GLuint owner, int layer, Rgba8 color) {
const std::vector<std::uint8_t> texels = Fill(color);
glBindTexture(GL_TEXTURE_2D_ARRAY, owner);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, layer, kSize, kSize, 1, GL_RGBA, GL_UNSIGNED_BYTE,
@@ -247,8 +247,8 @@ void main() { oColor = texture(uTex, vec3(vUv, float(uLayer))); }
// THE VIEW-SIDE WRITE. A single-layer GL_TEXTURE_2D view of the array: its own level 0
// is the owner's level <minLevel>, and its own layer 0 is the owner's layer
// kViewLayer. glTexSubImage2D here is what ToOwnerRegionOffset has to move.
void UploadThroughView(GLuint view, Rgba8 colour) {
const std::vector<std::uint8_t> texels = Fill(colour);
void UploadThroughView(GLuint view, Rgba8 color) {
const std::vector<std::uint8_t> texels = Fill(color);
glBindTexture(GL_TEXTURE_2D, view);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, kSize, kSize, GL_RGBA, GL_UNSIGNED_BYTE, texels.data());
@@ -719,13 +719,13 @@ void main() {
// depend on a CPU sub-image reaching a layer whose content the GPU wrote - which
// DirectVulkan does not currently do even for a plain array texture (no view
// involved), and which would make a failure here unattributable.
const auto uploadLayer = [&](GLuint texture, int layer, Rgba8 colour) {
const auto uploadLayer = [&](GLuint texture, int layer, Rgba8 color) {
std::vector<std::uint8_t> texels(static_cast<std::size_t>(kSize) * kSize * 4);
for (std::size_t i = 0; i < texels.size(); i += 4) {
texels[i + 0] = colour.r;
texels[i + 1] = colour.g;
texels[i + 2] = colour.b;
texels[i + 3] = colour.a;
texels[i + 0] = color.r;
texels[i + 1] = color.g;
texels[i + 2] = color.b;
texels[i + 3] = color.a;
}
glBindTexture(GL_TEXTURE_2D_ARRAY, texture);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
@@ -942,7 +942,7 @@ void main() {
constexpr int kViewNumLayers = 2;
// One color per level, so a level that was redefined from the view's base extent reads
// as another level's color rather than as a plausible one.
const Rgba8 levelColour[kLevels] = {
const Rgba8 levelColor[kLevels] = {
{255, 0, 0, 255}, {0, 0, 255, 255}, {255, 255, 0, 255}, {255, 0, 255, 255}, {0, 255, 255, 255}};
const GLuint storage = MakeTexture();
@@ -953,10 +953,10 @@ void main() {
const int side = kBase >> level;
std::vector<std::uint8_t> texels(static_cast<std::size_t>(side) * side * kLayers * 4);
for (std::size_t at = 0; at < texels.size(); at += 4) {
texels[at + 0] = levelColour[level].r;
texels[at + 1] = levelColour[level].g;
texels[at + 2] = levelColour[level].b;
texels[at + 3] = levelColour[level].a;
texels[at + 0] = levelColor[level].r;
texels[at + 1] = levelColor[level].g;
texels[at + 2] = levelColor[level].b;
texels[at + 3] = levelColor[level].a;
}
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, level, 0, 0, 0, side, side, kLayers, GL_RGBA, GL_UNSIGNED_BYTE,
texels.data());
@@ -1027,7 +1027,7 @@ void main() {
// 3 are outside the view's layer window.
const bool inWindow =
level >= 2 && level <= 3 && layer >= kViewMinLayer && layer < kViewMinLayer + kViewNumLayers;
const Rgba8 expected = inWindow ? Rgba8{0, 255, 0, 255} : levelColour[level];
const Rgba8 expected = inWindow ? Rgba8{0, 255, 0, 255} : levelColor[level];
const std::string what = "the owner's mip " + std::to_string(level) + ", layer " +
std::to_string(layer) +
(inWindow ? ", inside the view's window" : ", outside the view's window");
@@ -1062,7 +1062,7 @@ void main() {
constexpr std::uint8_t kCanaryByte = 0xAB;
// One color per storage layer, so a lost layer origin and a read past the window are
// both visible as another layer's color rather than as a plausible one.
const auto layerColour = [](int layer) {
const auto layerColor = [](int layer) {
return Rgba8{static_cast<std::uint8_t>(10 * (layer + 1)), 40, 7, 255};
};
const Rgba8 canary{kCanaryByte, kCanaryByte, kCanaryByte, kCanaryByte};
@@ -1077,11 +1077,11 @@ void main() {
for (int layer = 0; layer < kLayers; ++layer) {
for (int t = 0; t < side * side; ++t) {
std::uint8_t* p = texels.data() + (static_cast<std::size_t>(layer) * side * side + t) * 4;
const Rgba8 colour = layerColour(layer);
p[0] = colour.r;
p[1] = colour.g;
p[2] = colour.b;
p[3] = colour.a;
const Rgba8 color = layerColor(layer);
p[0] = color.r;
p[1] = color.g;
p[2] = color.b;
p[3] = color.a;
}
}
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, level, 0, 0, 0, side, side, kLayers, GL_RGBA, GL_UNSIGNED_BYTE,
@@ -1107,7 +1107,7 @@ void main() {
std::memcpy(image.Data(), dst.data() + static_cast<std::size_t>(slice) * side * side * 4,
static_cast<std::size_t>(side) * side * 4);
const bool inWindow = slice < kViewNumLayers;
const Rgba8 expected = inWindow ? layerColour(kViewMinLayer + slice) : canary;
const Rgba8 expected = inWindow ? layerColor(kViewMinLayer + slice) : canary;
const std::string what = inWindow ? "the view's slice " + std::to_string(slice) +
", the owner's layer " + std::to_string(kViewMinLayer + slice)
: "slice " + std::to_string(slice) +
@@ -151,7 +151,7 @@ void main() {
// color-mask discipline in SyncRenderState is for. GL_DST_ALPHA blending and
// glBlitFramebuffer read that stored alpha inside the driver, where no readback fixup can
// reach it, so "the storage really holds 1.0" is the only workable invariant.
TEST_F(ThreeChannelAttachmentScenario, WidenedAttachmentReadsBackOpaqueWhileItsNeighbourKeepsItsAlpha) {
TEST_F(ThreeChannelAttachmentScenario, WidenedAttachmentReadsBackOpaqueWhileItsNeighborKeepsItsAlpha) {
if (!Ready() || IsSkipped()) return;
std::string error;
@@ -330,7 +330,7 @@ void main()
// Clears to BLUE, so "the draw painted nothing" is a color of its own rather
// than something that could be mistaken for a zeroed payload.
Rgba8 DrawAndReadCentre(GLuint program) const {
Rgba8 DrawAndReadCenter(GLuint program) const {
glViewport(0, 0, Gl().Width(), Gl().Height());
glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
@@ -413,10 +413,10 @@ void main()
<< Gl().BackendName() << ", so there is no block to carry through: " << BuildLog();
}
const Rgba8 centre = DrawAndReadCentre(program);
const Rgba8 center = DrawAndReadCenter(program);
EXPECT_EQ(FirstGLError(), 0u);
EXPECT_TRUE(IsGreen(centre)) << "a four-boundary interface-block chain did not deliver its payload: "
<< centre
EXPECT_TRUE(IsGreen(center)) << "a four-boundary interface-block chain did not deliver its payload: "
<< center
<< " (blue: nothing drew; red: the plain varying was lost too; black: a block "
"member arrived wrong, i.e. the interface stopped matching)";
}
@@ -438,9 +438,9 @@ void main()
"desktop GLSL, but the program did not build: "
<< BuildLog();
const Rgba8 centre = DrawAndReadCentre(program);
const Rgba8 center = DrawAndReadCenter(program);
EXPECT_EQ(FirstGLError(), 0u);
EXPECT_TRUE(IsGreen(centre)) << "the renamed-and-unlocated interface chain lost its payload: " << centre;
EXPECT_TRUE(IsGreen(center)) << "the renamed-and-unlocated interface chain lost its payload: " << center;
}
// THE ONE CASE THAT CAN FAIL WHEN THE REPAIR SILENTLY STOPS BEING ARMED.
@@ -498,9 +498,9 @@ void main()
}
// Drawn as well as built, so a stack that defers its backend program to first use
// still reaches the transpile this is asserting about.
const Rgba8 centre = DrawAndReadCentre(program);
const Rgba8 center = DrawAndReadCenter(program);
EXPECT_EQ(FirstGLError(), 0u);
EXPECT_TRUE(IsGreen(centre)) << "the pinned-on lane did not even render correctly: " << centre;
EXPECT_TRUE(IsGreen(center)) << "the pinned-on lane did not even render correctly: " << center;
const std::string appended = LibraryLogSince(before);
EXPECT_NE(appended.find("WITHOUT their layout(location) qualifier"), std::string::npos)
@@ -301,7 +301,7 @@ void main() { fragColor = gl_FragCoord.z; }
// The center of grid cell (x, y), in the bottom-left-origin coordinates glReadPixels
// returns. Sampling the center rather than a corner keeps the assertion about WHICH
// viewport was selected rather than about edge rounding.
static GLint CellCentre(const std::vector<GLint>& pixels, int stride, int x, int y) {
static GLint CellCenter(const std::vector<GLint>& pixels, int stride, int x, int y) {
const int px = x * kCellSize + kCellSize / 2;
const int py = y * kCellSize + kCellSize / 2;
return pixels[static_cast<size_t>(py) * stride + px];
@@ -326,7 +326,7 @@ void main() { fragColor = gl_FragCoord.z; }
for (int y = 0; y < kGridSide; ++y) {
for (int x = 0; x < kGridSide; ++x) {
const GLint expected = y * kGridSide + x;
EXPECT_EQ(CellCentre(pixels, kSurfaceSide, x, y), expected)
EXPECT_EQ(CellCenter(pixels, kSurfaceSide, x, y), expected)
<< "cell (" << x << ", " << y << ") should hold viewport index " << expected
<< "; a single-viewport backend paints the whole image with 15 (the last invocation)";
}
@@ -484,12 +484,12 @@ void main() { fragColor = vec4(float(gsIndex) * 16.0 / 255.0, 0.0, 0.0, 1.0); }
ASSERT_EQ(glGetError(), GL_NO_ERROR);
{
const std::vector<GLint> pixels = ReadInts(kSurfaceSide, kSurfaceSide);
EXPECT_EQ(CellCentre(pixels, kSurfaceSide, kProbeX, kProbeY), kProbeIndex)
EXPECT_EQ(CellCenter(pixels, kSurfaceSide, kProbeX, kProbeY), kProbeIndex)
<< "the scissored index must still paint inside its own box";
for (int y = 0; y < kGridSide; ++y) {
for (int x = 0; x < kGridSide; ++x) {
if (x == kProbeX && y == kProbeY) continue;
EXPECT_EQ(CellCentre(pixels, kSurfaceSide, x, y), kUnwritten)
EXPECT_EQ(CellCenter(pixels, kSurfaceSide, x, y), kUnwritten)
<< "cell (" << x << ", " << y << ") is outside scissor rectangle " << kProbeIndex
<< " and must be untouched";
}
@@ -509,7 +509,7 @@ void main() { fragColor = vec4(float(gsIndex) * 16.0 / 255.0, 0.0, 0.0, 1.0); }
const std::vector<GLint> pixels = ReadInts(kSurfaceSide, kSurfaceSide);
for (int y = 0; y < kGridSide; ++y) {
for (int x = 0; x < kGridSide; ++x) {
EXPECT_EQ(CellCentre(pixels, kSurfaceSide, x, y), kProbeIndex)
EXPECT_EQ(CellCenter(pixels, kSurfaceSide, x, y), kProbeIndex)
<< "with the scissor test off for index " << kProbeIndex
<< ", its full-viewport quad must cover cell (" << x << ", " << y << ")";
}
@@ -576,14 +576,14 @@ void main() { fragColor = vec4(float(gsIndex) * 16.0 / 255.0, 0.0, 0.0, 1.0); }
const std::vector<GLint> pixels = ReadInts(kSurfaceSide, kSurfaceSide);
// Cell (0, 0) IS viewport 0's rectangle, so it is the one cell an unrouted draw paints
// with something. Everything it holds comes from the last geometry invocation.
EXPECT_EQ(CellCentre(pixels, kSurfaceSide, 0, 0), kViewportCount - 1)
EXPECT_EQ(CellCenter(pixels, kSurfaceSide, 0, 0), kViewportCount - 1)
<< "with the emulation off, viewport 0's rectangle must hold the LAST invocation's "
"index - if it holds 0 the routing is still happening and this control proves "
"nothing";
for (int y = 0; y < kGridSide; ++y) {
for (int x = 0; x < kGridSide; ++x) {
if (x == 0 && y == 0) continue;
EXPECT_EQ(CellCentre(pixels, kSurfaceSide, x, y), kUnwritten)
EXPECT_EQ(CellCenter(pixels, kSurfaceSide, x, y), kUnwritten)
<< "cell (" << x << ", " << y << ") is outside viewport 0's rectangle and an "
<< "unrouted draw cannot reach it";
}
@@ -428,7 +428,7 @@ void main (void)
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, captureBuffer);
glBindBuffer(GL_ARRAY_BUFFER, 0);
// The pre-fill that makes "nothing was captured" recognisable.
// The pre-fill that makes "nothing was captured" recognizable.
std::vector<float> prefill(storeFloats);
for (unsigned i = 0; i < storeFloats; ++i) {
prefill[i] = -1.0f - static_cast<float>(i);
@@ -287,7 +287,7 @@ void main() {
glGenBuffers(1, &xfbBuffer);
glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, xfbBuffer);
// Poisoned at creation - the storage is immutable, so this is the only chance to
// put a recognisable value there, and without it a span that captured nothing
// put a recognizable value there, and without it a span that captured nothing
// would be indistinguishable from one that captured the right thing whenever the
// untouched bytes happened to read back as the expected number.
const std::vector<float> poison(kCaptureFloats, kPoison);
+1 -1
View File
@@ -48,7 +48,7 @@
X(UnsizedReadback, ProtocolCorruption, "a readback arrived with no declared size") \
X(BlobMissing, ProtocolCorruption, "a record named a blob that was never staged") \
X(BlobNotStaged, ProtocolCorruption, "a blob was referenced before its staging completed") \
X(ArtefactsMissing, ProtocolCorruption, "a program create arrived with no artifacts") \
X(ArtifactsMissing, ProtocolCorruption, "a program create arrived with no artifacts") \
X(ArchiveEmpty, ProtocolCorruption, "a create_*_state archive carried no payload") \
X(BarrierViolation, ProtocolCorruption, "a field was pulled across a verb boundary the barrier forbids") \
X(UnimplementedAdoptTier, ProtocolCorruption, "MOBILEGL_IPC_ADOPT_TIER named a tier P5 does not implement") \
+3 -3
View File
@@ -160,8 +160,8 @@
X(kDispatch, GetSamplingResolutionGeneration) \
X(kDispatch, GetImageTextureBinding) \
X(kDispatch, GetFramebufferBindingSlot) \
/* Magma's PrepareStorageImageTextures materialises a queued clear for every */ \
/* storage image the dispatch writes, and the clear pre-compensates its colour */ \
/* Magma's PrepareStorageImageTextures materializes a queued clear for every */ \
/* storage image the dispatch writes, and the clear pre-compensates its color */ \
/* against GL_FRAMEBUFFER_SRGB (VkClearManager::PreCompensateSrgbClearColor). */ \
X(kDispatch, IsCapabilityEnabled) \
X(kDispatch, GetPatchVertices) \
@@ -224,7 +224,7 @@
X(kTextureOp, GetSamplingResolutionGeneration) \
X(kTextureOp, GetTextureBindGeneration) \
X(kTextureOp, GetMaxTouchedTextureUnit) \
/* Magma's GenerateMipmap materialises the texture's queued clear before it */ \
/* Magma's GenerateMipmap materializes the texture's queued clear before it */ \
/* blits (MaterializePendingClearForTexture -> PreCompensateSrgbClearColor, */ \
/* which reads GL_FRAMEBUFFER_SRGB), and a depth texture takes the shader path */ \
/* (GenerateDepthMipmapWithShader -> BindProgramUniformBuffers), whose sampler */ \
+1 -1
View File
@@ -123,7 +123,7 @@ namespace MobileGL::MG_Pipe {
// NOT a subsystem, a BEHAVIOR: turn OFF client-side content addressing of CSOs, so
// every pipeline-version change mints a fresh CSO and the map is never probed. This is
// the negative control the whole CSO design is measured against (ROADMAP.md P2).
inline constexpr Uint64 kMGPipeBehaviourNoCsoContentAddressing = 1ull << 63;
inline constexpr Uint64 kMGPipeBehaviorNoCsoContentAddressing = 1ull << 63;
// The default of a push build with the knob unset (ConfigLoader.cpp). Each phase's
// constant STAYS, because it is the A/B control for the phase after it: P3a's
// "everything P2 had and nothing of mine" arm is spelled MOBILEGL_PIPE_PUSH=0x7f.
+4 -4
View File
@@ -637,7 +637,7 @@ namespace MobileGL::MG_Pipe {
// there is no spare byte and a fifth one costs the alignment slack below. It is paid
// once, on a record that fires at most once per link, and it is confined to the push
// build (this payload is only ever instantiated under MOBILEGL_PIPE_PUSH), which is
// why G1 does not see it. PipeCatalogueTest pins the new size.
// why G1 does not see it. PipeCatalogTest pins the new size.
Uint8 LinkStatus;
Uint8 Pad0[7];
MGPBlobRef Spirv[6]; // per stage
@@ -1048,7 +1048,7 @@ namespace MobileGL::MG_Pipe {
// view the application wrote directly (the verify comparator covers it, and the
// emission-side suppressor compares the three views, not it). This is the catalog's
// second payload-size amendment after P5b's MGPCopyRegion 64 -> 72 (CONTRACT-P5C.md §7.6):
// 24 -> 56 bytes, and PipeCatalogueTest pins the new size.
// 24 -> 56 bytes, and PipeCatalogTest pins the new size.
struct MGPAttribValue {
Uint32 Location;
Uint8 ValueClass; // Float | Int | Uint | Double
@@ -1240,8 +1240,8 @@ namespace MobileGL::MG_Pipe {
// need an acknowledgment, and this decides. Only an immutable buffer (glBufferStorage) is a
// synchronous allocation; glBufferData is not. glTexStorage* also sets Immutable, but texture
// and renderbuffer storage is allocated lazily at sync time, so neither may ack
// (ARCHITECTURE.md §8.3). PipeCatalogue.ResourceRespecifyAcksOnlyImmutableStorage
// (PipeCatalogueTest.cpp) is the negative control against widening it.
// (ARCHITECTURE.md §8.3). PipeCatalog.ResourceRespecifyAcksOnlyImmutableStorage
// (PipeCatalogTest.cpp) is the negative control against widening it.
inline Bool MGPipeResourceRespecifyNeedsAck(const MGPResourceDesc& desc) {
return desc.Immutable != 0 && desc.Target == kMGPipeResourceTargetBuffer;
}
+6 -6
View File
@@ -25,7 +25,7 @@
// build only. In monolith the archive does not travel - MGPProgramDesc's seven blob refs are
// declared with Size 0 and the two structs ride beside the record through the entry point's
// companion pointers - so a push build pays nothing for it and the header keeps its forward
// declarations. The verify build serializes, deserialises and compares before storing, which
// declarations. The verify build serializes, deserializes and compares before storing, which
// makes the codec LIVE CODE WITH A GATE rather than dead code with a unit test.
#include <MG_State/GLState/ProgramState/ProgramArtifactsCodec.h>
#endif
@@ -2060,7 +2060,7 @@ namespace MobileGL::MG_Pipe {
// entry. PipeInputs::kMaxVertexAttribs is VertexArrayObject's 32 today, so the
// out-of-range arm is unreachable - the guard is what keeps that true if the two ever
// stop agreeing. None of the three consistency checks may be left to MOBILEGL_ASSERT,
// which is inert at INFO: a malformed tail would then desynchronise the attribute
// which is inert at INFO: a malformed tail would then desynchronize the attribute
// writes without a word in exactly the builds that ship.
Uint32 consumed = 0;
const char* fault = nullptr;
@@ -2303,7 +2303,7 @@ namespace MobileGL::MG_Pipe {
// resource_respecify is the only kNeedsAck call, and MGPipeResourceRespecifyNeedsAck(desc)
// narrows the ack to an immutable buffer, i.e. glBufferStorage (ARCHITECTURE.md §8.3). In
// process the ack is this function's return, so the predicate is not branched on;
// PipeCatalogue.ResourceRespecifyAcksOnlyImmutableStorage pins it. A metadata-only update
// PipeCatalog.ResourceRespecifyAcksOnlyImmutableStorage pins it. A metadata-only update
// never acks: the predicate is false for every non-buffer target, and
// RespecifyRedefinesNoStorage never classifies a buffer as metadata-only.
if (desc.Target != kMGPipeResourceTargetBuffer) return true;
@@ -3542,10 +3542,10 @@ namespace MobileGL::MG_Pipe {
record.Live = true;
// The DESCRIPTOR is what is stored, and the artifacts are not: in monolith the backend
// reads the frontend's own archive, and under split the descriptor is what the
// deserialised archive is attached to. Either way the applier owns identity, extent and
// deserialized archive is attached to. Either way the applier owns identity, extent and
// order, and never content.
record.Desc = desc;
// P5e (pg), gap G-A: "under split the descriptor is what the deserialised archive is
// P5e (pg), gap G-A: "under split the descriptor is what the deserialized archive is
// attached to" is now literally true, and this is the attachment. ADOPTED
// UNCONDITIONALLY, INCLUDING A NULL: a monolith create must CLEAR an archive a
// previous split-arm create left behind, because the record outlives a transport
@@ -3731,7 +3731,7 @@ namespace MobileGL::MG_Pipe {
// rather than degrade silently. It arms on the transport, not the build, because split
// builds also run in-process lanes where such code is correct. No log or counter: it may sit
// on hot paths. Product code has no call sites; scripts/ci/unmigrated_emulation_sites.py
// and PipeCatalogue.EveryUnmigratedEmulationIsNamedOnce keep the names and sites equal.
// and PipeCatalog.EveryUnmigratedEmulationIsNamedOnce keep the names and sites equal.
namespace {
thread_local Bool tl_serverOriginatedWrite = false;
} // namespace
+4 -4
View File
@@ -356,7 +356,7 @@ namespace MobileGL::MG_Pipe {
// A snapshot of this link's artifacts, so every reflection read by the program twin is
// of memory the record owns: ProgramObject::Link() replaces the frontend's artifacts in
// place, and a client running ahead may already have relinked. Under a transport the
// decoder deserialises it from SEG_STAGE; in one process PipeRoute copies it without the
// decoder deserializes it from SEG_STAGE; in one process PipeRoute copies it without the
// codec. A SharedPtr because a program-pipeline composite and its stage programs share
// one link's artifacts and the twin holds it across a build. Every create replaces it.
SharedPtr<const MG_State::GLState::ProgramArchive> Archive;
@@ -1281,7 +1281,7 @@ namespace MobileGL::MG_Pipe {
// silent no-op: a record that reached an unimplemented applier and returned quietly is a
// dropped binding set, which renders wrong rather than stopping - the exact failure mode
// ID-39's sixty-six lost uploads had. Nothing routes either opcode until its package lands
// (PipeCatalogueTest pins both table slots null), so the abort is unreachable today.
// (PipeCatalogTest pins both table slots null), so the abort is unreachable today.
// set_shader_buffers, per Class. `tail` is hdr.Count MGPBufferRanges starting at hdr.Start;
// Class >= kMGPipeShaderBufferClassCount and Start + Count above the capacity are
@@ -1304,7 +1304,7 @@ namespace MobileGL::MG_Pipe {
// create_shader_state. `link` and `spirv` are the program's artifacts and `archive` is the
// snapshot the record adopts (a null clears the previous one); desc's blob refs are not
// read. Under a transport the decoder passes its deserialised archive and pointers into it;
// read. Under a transport the decoder passes its deserialized archive and pointers into it;
// in one process PipeRoute copies the frontend's artifacts into a new archive without the
// codec. The verify build round-trips the artifacts through the codec before storing, and a
// mismatch is Fatal{PipeVerifyDiffer, "program-archive"}. `archive` has no default so that a
@@ -1335,7 +1335,7 @@ namespace MobileGL::MG_Pipe {
// abort loudly rather than degrade silently.
//
// Monolith body: (void)name;. The list of names is pinned by
// PipeCatalogueTest.EveryUnmigratedEmulationIsNamedOnce and the call count is grepped by
// PipeCatalogTest.EveryUnmigratedEmulationIsNamedOnce and the call count is grepped by
// the purity gate, so a site that quietly disappears is a red gate rather than a surprise
// at P8.
void MGPipeUnmigratedEmulation(const char* name);
+4 -4
View File
@@ -6,10 +6,10 @@
// SPDX-License-Identifier: LGPL-3.0-only
// End of Source File Header
// The MGPipe call catalogue (ARCHITECTURE.md §3), one X(Name, PayloadStruct, Class, Flags,
// The MGPipe call catalog (ARCHITECTURE.md §3), one X(Name, PayloadStruct, Class, Flags,
// WaitClass) per call, read by scripts/gen_pipe.py into MG_Pipe/generated/*.inc. The wire
// opcode is the 1-based row position: append new calls at the end, keep retired rows in place.
// MGP_CALL_LIST_DOCUMENTED_COUNT is the row count, pinned by MG_Test/Pipe/PipeCatalogueTest.cpp.
// MGP_CALL_LIST_DOCUMENTED_COUNT is the row count, pinned by MG_Test/Pipe/PipeCatalogTest.cpp.
// Class: kScreen rows go to MGPipeScreen, all others to MGPipeContext. kHasBlob means the
// payload owns an MGPBlobRef member; kNeedsAck means a per-record predicate may ask for an ack.
// WaitClass (MGPipeWaitClass) is what the client does after publishing under run-ahead;
@@ -141,7 +141,7 @@
X(Present, MGPPresent, kCtxVerb, kNone, kWaitPresent) \
X(SetSwapInterval, MGPSwapInterval, kCtxVerb, kOptional, kWaitNone) \
/* ---- APPENDED. Opcodes are positional, so a late arrival goes at the END, never into ---- */ \
/* ---- its group: three live GLFunctionsTable entries the catalogue had no carrier for. ---- */ \
/* ---- its group: three live GLFunctionsTable entries the catalog had no carrier for. ---- */ \
/* glGetInteger64v(GL_TIMESTAMP) - GetGpuTimestampNs, a synchronous server answer, which */ \
/* the reply slot carries. The query namespace is the context's (plan 4.3). */ \
X(QueryTimestamp, MGPTimestampRequest, kCtxQuery, kReplySlot, kWaitReply) \
@@ -243,7 +243,7 @@
X(SharedImage, MGPSharedImageOp, kScreen, kReplySlot, kWaitReply)
// clang-format on
// Not in the catalogue (ARCHITECTURE.md §3.2):
// Not in the catalog (ARCHITECTURE.md §3.2):
// - GetIntegeri_v / GetInteger64i_v: the only device-answered indexed pnames, the compute
// work-group count and size limits, travel in MGPCaps (DynamicBackendParameters); every
// other indexed pname is frontend state.
+3 -3
View File
@@ -10,7 +10,7 @@
// in MGPipeTypes.h, listing the fields that carry MEANING - padding is deliberately absent,
// because MOBILEGL_PIPE_VERIFY has to have ZERO false positives and a padding byte is
// exactly what makes a memcmp of RenderStateParameters false-DIFFER
// (DirectGLES.cpp documents that behaviour where it does the same comparison itself).
// (DirectGLES.cpp documents that behavior where it does the same comparison itself).
//
// Hand maintained alongside MGPipeTypes.h, MGPipeValueTypes.h, MGPipeHostSpan.h and
// MG_Backend/BackendFunctions.h. Adding a member to one of these structs without adding it here
@@ -174,7 +174,7 @@
F(Vertices) F(Outer) F(Inner)
// P2 ratcheted ResidualValueBlock from six rows to one and P13 W3c deleted it; what stays is
// the retired op 46's payload, which the record catalogue still has to lay out.
// the retired op 46's payload, which the record catalog still has to lay out.
#define MGP_FIELDS_MGPResidualValueState(F) \
F(Version) F(Blob)
@@ -191,7 +191,7 @@
// LevelPad IS PADDING BY ROLE AND NOT BY NAME, and that is the whole reason it has a row here:
// the cover check excuses only the members NAMED Pad<n>, so this one left out is a pipe-gates
// failure. The row costs no false positive, because every producer builds the record as
// `MGPSubData{}` (so the word is a deterministic zero, not an uninitialised byte) and the
// `MGPSubData{}` (so the word is a deterministic zero, not an uninitialized byte) and the
// payload rides the wire as one POD memcpy - MGP_ASSERT_POD(MGPSubData, 88) is the whole
// record, with no untransferred tail - so both sides of a comparison read the same zero.
#define MGP_FIELDS_MGPSubData(F) \
+1 -1
View File
@@ -12,7 +12,7 @@
// Frontend mutation notices. The verb fill (MG_Impl/Pipe/PipeFill.cpp) copies a verb's may-read
// set out of the live GLContext at the verb boundary, and the backend reads that copy for the
// whole verb. A frontend value written inside the verb (Magma synthesising a fallback texture,
// materialising a queued clear, overriding a sampler's filter) would leave the copy stale, so
// materializing a queued clear, overriding a sampler's filter) would leave the copy stale, so
// the mutator calls MGP_NOTE_MUTATION(Field) where it moves the value. The notice refreshes
// that one field when it belongs to the verb in flight. It never touches the poison stamp, so a
// stamp the fill withheld (negative control B) stays withheld.
+1 -1
View File
@@ -330,7 +330,7 @@ namespace MobileGL::MG_Pipe {
// P5e (pg): NO ARCHIVE ON THIS ARM, and the null is the arm selection rather than a
// hole. Under monolith the two companion pointers ARE the frontend's own archive,
// the backend twin reads it through its frontend overload (ruling 1 keeps that arm
// token for token), and serializing here to deserialise into the record would put
// token for token), and serializing here to deserialize into the record would put
// EncodeProgramArtifacts on the monolith path - which PipeRoute.h's escape note
// and PipeApply.h both promise it is not.
MGPipeApplyCreateShaderState(*desc, link, spirv, nullptr);
+1 -1
View File
@@ -243,7 +243,7 @@ namespace MobileGL::MG_Pipe {
// gates on it); under monolith the row is never produced and the fields keep coming
// through the residual fill (G1). The row IS an ordinary routed set_* otherwise - the
// monolith adapter exists so the wire emitter's server-role arm has something defined to
// forward to, and the catalog's null-row accounting (PipeCatalogueTest) counts it.
// forward to, and the catalog's null-row accounting (PipeCatalogTest) counts it.
inline void MGPipeRouteSetContextValues(const MGPContextValues& values) {
MGP_SetContextValues(&values);
}
+2 -2
View File
@@ -47,7 +47,7 @@ namespace MobileGL::MG_Pipe {
// bindings (PipeApply.h, MGPipeVertexElementsRecord), which a revision-1 peer would not.
inline constexpr Uint32 kMGPipeResourceRespecifyExtentCarrierRevision = 2;
constexpr Uint64 WireCatalogueDigest() {
constexpr Uint64 WireCatalogDigest() {
Uint64 hash = 1469598103934665603ull;
for (const auto& call : kMGPipeWireCallLayouts) {
WireMixName(hash, call.Name);
@@ -58,7 +58,7 @@ namespace MobileGL::MG_Pipe {
}
return hash;
}
inline constexpr Uint64 kMGPipeWireCatalogueDigest = WireCatalogueDigest();
inline constexpr Uint64 kMGPipeWireCatalogDigest = WireCatalogDigest();
constexpr Uint64 WireRenderStateDigest() {
Uint64 hash = 1469598103934665603ull;
WireMixNumber(hash, kMGPipeRenderStateChunkTableVersion);
+1 -1
View File
@@ -25,7 +25,7 @@
X(kMGPipeSubsystemFramebuffer, kMGPipeSubsystemTextureResources, "every MGPSurface::Res names a Texture or Renderbuffer handle and only bit 10 populates those two slot tables (ResolveFramebufferSubsystemArm)") \
X(kMGPipeSubsystemTextureResources, kMGPipeSubsystemResources | kMGPipeSubsystemSamplers, "a buffer texture's BufferForTexBuffer names a Buffer handle (bit 7); and MGPTextureParams::BuiltinSampler is a SamplerCso HANDLE only bit 11 mints, whose null the applier treats as Fatal{ProtocolCorruption} rather than a decline - D-K2's fourth row, ID-14/ID-15, which WITHDRAWS the brief's 'bit 10 without 11 is fine'") \
X(kMGPipeSubsystemSamplers, kMGPipeSubsystemTextureResources, "every MGPBoundView::Texture and MGPImageView::Res names a Texture handle and only bit 10 populates that slot table; with the row above this is SYMMETRIC - bits 10 and 11 are one arm with two switches (ResolveSamplerSubsystemArm)") \
X(kMGPipeSubsystemPrograms, 0, "a ShaderCso handle names no texture and no buffer, the archive rides beside the record as a companion pointer, and the server's extra specialisation inputs come from state the backend already holds (ResolveProgramSubsystemArm)") \
X(kMGPipeSubsystemPrograms, 0, "a ShaderCso handle names no texture and no buffer, the archive rides beside the record as a companion pointer, and the server's extra specialization inputs come from state the backend already holds (ResolveProgramSubsystemArm)") \
X(kMGPipeSubsystemBufferBindings, kMGPipeSubsystemResources, "every MGPBufferRange::Res names a Buffer handle and only bit 7 puts one in the slot table, so without it every server-side EnsureBufferResourceForHandle would mint a twin with no record behind it (P5e sb; ResolveBufferBindingSubsystemArm)")
// clang-format on
+1 -1
View File
@@ -446,7 +446,7 @@ namespace MobileGL::MG_Remote {
inputs.DynamicParamsSize = sizeof(MG_Backend::DynamicBackendParameters);
inputs.CapsSize = sizeof(MG_Pipe::MGPCaps);
inputs.MemberLayout = MG_Pipe::kMGPipeWireMemberLayoutDigest;
inputs.CatalogueLayout = MG_Pipe::kMGPipeWireCatalogueDigest;
inputs.CatalogLayout = MG_Pipe::kMGPipeWireCatalogDigest;
inputs.RenderStateLayout = MG_Pipe::WireRenderStateDigest();
inputs.FormatCapabilityTargets = MG_Backend::kFormatCapabilityTargetCount;
inputs.FormatCapabilityFormats = MG_Backend::kFormatCapabilityFormatCount;
+5 -5
View File
@@ -2157,11 +2157,11 @@ namespace MobileGL::MG_Remote::Client {
Wire::WireOpName(op));
}
const auto declineCancelled = [&] {
const auto declineCanceled = [&] {
if (statusOut) *statusOut = Wire::ReplySink::kStatusDeclined;
return Wire::kInvalidSeq;
};
if (DeviceLost() || m_encoder.Cancelled()) return declineCancelled();
if (DeviceLost() || m_encoder.Canceled()) return declineCanceled();
const Bool rowCarriesReplySlot =
(MG_Pipe::MGPipeCallFlagsFor(op) & static_cast<Uint32>(MG_Pipe::kReplySlot)) != 0;
const Bool ownsReplySlot = rowCarriesReplySlot && wantReply;
@@ -2186,7 +2186,7 @@ namespace MobileGL::MG_Remote::Client {
const Bool skipAnswer = rowCarriesReplySlot && !willReadReply;
Uint64 seq = m_encoder.EncodeRecord(op, payload, payloadBytes, tails, tailCount, skipAnswer);
if (seq == Wire::kInvalidSeq) {
if (DeviceLost() || m_encoder.Cancelled()) return declineCancelled();
if (DeviceLost() || m_encoder.Canceled()) return declineCanceled();
// EncodeRecord already refused individually oversized records. This one fits,
// but appliedSeq may be ahead of retiredTail: wait for actual reclaimable space,
// including the wrap pad. Prior EmitAndWait calls have published every record.
@@ -2199,7 +2199,7 @@ namespace MobileGL::MG_Remote::Client {
const auto wait = m_producer.WaitForCmdSpace(needed, kBarrierTimeoutMs);
if (wait == Transport::SessionWait::ShutDown) {
(void)m_encoder.CheckCancellation();
return declineCancelled();
return declineCanceled();
}
if (wait != Transport::SessionWait::Reached) {
SessionFail(MGFatalFamily::RetirementWaitFailed,
@@ -2216,7 +2216,7 @@ namespace MobileGL::MG_Remote::Client {
DrainEventRing(*m_events);
seq = m_encoder.EncodeRecord(op, payload, payloadBytes, tails, tailCount);
if (seq == Wire::kInvalidSeq) {
if (DeviceLost() || m_encoder.Cancelled()) return declineCancelled();
if (DeviceLost() || m_encoder.Canceled()) return declineCanceled();
SessionFail(MGFatalFamily::RingOverrun,
"MGPipe: Fatal{RingOverrun, \"SEG_CMD\"} - %s refused after sufficient "
"space retired",
+2 -2
View File
@@ -854,8 +854,8 @@ namespace MobileGL::MG_Remote::Client {
}
ClientSession& session = RequireSession("CreateShaderState");
if (link == nullptr || spirv == nullptr) {
SessionFail(MGFatalFamily::ArtefactsMissing,
"MGPipe: Fatal{ArtefactsMissing, \"create_shader_state\"} - the record's "
SessionFail(MGFatalFamily::ArtifactsMissing,
"MGPipe: Fatal{ArtifactsMissing, \"create_shader_state\"} - the record's "
"two typed companions are null. Under monolith the applier reads the "
"modules out of spirv->generatedSpirv; under split there is nothing to "
"serialize, and emitting the record anyway would create a CSO with no "
+2 -2
View File
@@ -17,7 +17,7 @@
// - the hot path -> FlatBuffers *structs* (fixed layout, no vtable, no
// offset indirection) written straight into the SEG_CMD ring. Those
// records are generated from MG_Pipe/PipeCalls.def and are deliberately
// NOT in this schema yet: the call catalogue is a separate P0 deliverable
// NOT in this schema yet: the call catalog is a separate P0 deliverable
// and record numbering must never churn.
//
// Regeneration: scripts/gen_protocol.py (flatc is NOT part of the default
@@ -181,7 +181,7 @@ table PairBind {
// tableSlotMask - GLFunctionsTable has SIXTY-NINE function-pointer slots
// (BackendObject.h:117-292) and a ulong is 64 bits, so the field could never
// address the table its own comment named; and ARCHITECTURE.md:114 already
// retired "is this table slot null" as the capability probe in favour of
// retired "is this table slot null" as the capability probe in favor of
// CallMask, so keeping it would re-introduce exactly what replaced it.
// prefersCpuXfbPrimitiveAccounting - answered by kCapCpuXfbPrimitiveAccounting
// in `callMask` below.
+11 -11
View File
@@ -9,7 +9,7 @@
// Where the apply thread runs and how long it spins before parking, chosen per session from the
// peer (the Hello's DialMode) and the data plane. With MOBILEGL_IPC_SERVER_AFFINITY=auto an
// in-process, forked or stream-plane peer gets every big core and the configured spin; a peer
// that dialled in over shared segments gets at most one core (ReservedApplyCore), never the only
// that dialed in over shared segments gets at most one core (ReservedApplyCore), never the only
// prime core, and kConnectedPeerSpinUs unless MOBILEGL_IPC_SPIN_US is set, because it writes the
// ring the apply thread spins on and its GL thread wants a prime core. An explicit mask or `off`
// wins for every peer. Pure functions, so the unit suite covers every row; the measurements are
@@ -28,11 +28,11 @@ namespace MobileGL::MG_Remote::Server {
enum class ApplyPeer : Uint8 {
InProcess = 0, // DialMode::No - inproc: the client is another thread of this process
Forked = 1, // DialMode::Fork - the client launched this server (same app, same build)
Connected = 2, // DialMode::Connect - the client dialled a standing server: another process,
Connected = 2, // DialMode::Connect - the client dialed a standing server: another process,
// possibly another app (tcp://, unix:, fd:)
};
// The apply thread's spin before it parks, for a dialled-in shared-segment peer when
// The apply thread's spin before it parks, for a dialed-in shared-segment peer when
// MOBILEGL_IPC_SPIN_US is not set. 50 us - the global default - by measurement: parking at once
// (0) lost in every cell measured (rd12 Espryt 120 -> 74 fps on the helper route, 27 -> 17 from the
// shell), so the short spin stays (CONTRACT-P11 B1).
@@ -41,7 +41,7 @@ namespace MobileGL::MG_Remote::Server {
struct ApplyThreadPolicy {
Uint64 requestedMask = 0; // 0 = make no affinity call
Uint32 spinUs = 0;
Bool recognised = true; // false: the affinity text was not `auto`, `off` or a number
Bool recognized = true; // false: the affinity text was not `auto`, `off` or a number
const char* rule = ""; // what the log line names
};
@@ -79,11 +79,11 @@ namespace MobileGL::MG_Remote::Server {
inline const char* ReservedCoreReasonText(ReservedCoreReason reason) {
switch (reason) {
case ReservedCoreReason::LowestOfSeveralPrimeCores:
return "auto: the lowest of the prime cores, the rest left to the dialled-in shared-segment client";
return "auto: the lowest of the prime cores, the rest left to the dialed-in shared-segment client";
case ReservedCoreReason::BigCoreBesideTheOnlyPrime:
return "auto: a big core beside the only prime core, which is left to the dialled-in shared-segment client";
return "auto: a big core beside the only prime core, which is left to the dialed-in shared-segment client";
case ReservedCoreReason::OnlyPrimeCoreLeftAlone:
return "auto: unpinned - the only prime core is left to the dialled-in shared-segment client and there "
return "auto: unpinned - the only prime core is left to the dialed-in shared-segment client and there "
"is no other big core";
case ReservedCoreReason::NoAsymmetry:
return "auto: unpinned - no asymmetric big/prime topology to split";
@@ -91,7 +91,7 @@ namespace MobileGL::MG_Remote::Server {
return "";
}
// The core `auto` reserves for a dialled-in shared-segment peer's apply thread, which spins there.
// The core `auto` reserves for a dialed-in shared-segment peer's apply thread, which spins there.
// `bigCoreMask` = cpus within 15% of the peak clock, `primeMask` = cpus AT the peak clock (both
// DetectCoreTopology's), `onlineMask` = one bit per cpu counted. The rule never hands the spinning
// apply thread the ONLY prime core - a foreground client's GL thread wants it:
@@ -141,7 +141,7 @@ namespace MobileGL::MG_Remote::Server {
char* end = nullptr;
const unsigned long long parsed = std::strtoull(affinity, &end, 0);
if (end == affinity || (end != nullptr && *end != '\0')) {
policy.recognised = false;
policy.recognized = false;
policy.rule = "unrecognized";
return policy;
}
@@ -158,7 +158,7 @@ namespace MobileGL::MG_Remote::Server {
// The stream plane: an io thread feeds the apply thread, which does not spin on the
// client's writes - B0 measured 7.9 ms/frame of apply CPU there against 26.8 on shm.
policy.requestedMask = bigCoreMask;
policy.rule = "auto: every big core (dialled-in peer on the stream plane)";
policy.rule = "auto: every big core (dialed-in peer on the stream plane)";
return policy;
}
policy.spinUs = spinExplicit ? configuredSpinUs : kConnectedPeerSpinUs;
@@ -169,7 +169,7 @@ namespace MobileGL::MG_Remote::Server {
}
// What the supervisor announces at start-up and the broker relays to the helper: the core this
// process's configuration reserves for a dialled-in shared-segment peer's apply thread. An operator's
// process's configuration reserves for a dialed-in shared-segment peer's apply thread. An operator's
// affinity (a mask or `off`) reserves nothing - the client is then not kept off any core.
inline Uint64 ReservedApplyCoreForConfig(const char* affinity, Uint64 bigCoreMask, Uint64 primeMask,
Uint64 onlineMask, ReservedCoreReason* outReason = nullptr) {
+2 -2
View File
@@ -38,7 +38,7 @@ namespace MobileGL::MG_Remote::Server {
return "NoDisplay";
case ServerWindowAcquire::NoWindow:
return "NoWindow";
case ServerWindowAcquire::Cancelled:
case ServerWindowAcquire::Canceled:
return "Canceled";
case ServerWindowAcquire::Interrupted:
return "Interrupted";
@@ -247,7 +247,7 @@ namespace MobileGL::MG_Remote::Server {
for (;;) {
if (!m_installed) return ServerWindowAcquire::NoDisplay;
if (m_interrupts != interrupts) return ServerWindowAcquire::Interrupted;
if (cancel != nullptr && cancel(cancelUser)) return ServerWindowAcquire::Cancelled;
if (cancel != nullptr && cancel(cancelUser)) return ServerWindowAcquire::Canceled;
// ANOTHER HOLDER: refused at once while its session goes on, waited for while it ends
// (its EndLease wakes this). The window is judged only once the lease is free.
const Bool heldElsewhere = m_leaseHolder != nullptr && m_leaseHolder != holder;
+1 -1
View File
@@ -60,7 +60,7 @@ namespace MobileGL::MG_Remote::Server {
Acquired,
NoDisplay, // this process owns no display (HasDisplay() is false)
NoWindow, // a display, but no window was attached within the wait
Cancelled, // the caller's cancel predicate said so
Canceled, // the caller's cancel predicate said so
Interrupted, // Interrupt() (the in-process server is stopping)
// P14 S2: NOT A BUG ANY MORE. Several sessions share one process and the display's window
// is one window, so a second ON-SCREEN session asking for it is refused by name here
+9 -9
View File
@@ -189,7 +189,7 @@ namespace MobileGL::MG_Remote::Server {
// the topology cannot be read - Windows, macOS, a container that hides the cpufreq
// tree, or a partially readable one - because with no asymmetry information the honest
// answer is "do not pin", not "pin to a guess".
// P11 B1: and the PRIME set beside it - the cpus AT the peak ceiling - which the dialled-in
// P11 B1: and the PRIME set beside it - the cpus AT the peak ceiling - which the dialed-in
// shared-segment rule needs so it never hands the spinning apply thread the only prime core.
void DetectCoreTopology(Uint64* outBig, Uint64* outPrime) {
*outBig = *outPrime = 0;
@@ -218,7 +218,7 @@ namespace MobileGL::MG_Remote::Server {
}
// Whether MOBILEGL_IPC_SPIN_US was set at all: ConfigLoader answers 50 for "unset" and
// for "50", and the dialled-in peer's default spin (ApplyThreadPolicy.h) applies only to
// for "50", and the dialed-in peer's default spin (ApplyThreadPolicy.h) applies only to
// the first.
Bool SpinSetExplicitly() {
const char* text = std::getenv("MOBILEGL_IPC_SPIN_US");
@@ -628,10 +628,10 @@ namespace MobileGL::MG_Remote::Server {
const ApplyThreadPolicy policy =
SelectApplyThreadPolicy(raw, SpinSetExplicitly(), SpinUsFromConfig(), session.Peer(),
session.PeerSharedSegments(), bigCores, primeCores, OnlineCpuMask());
const Bool recognised = policy.recognised;
const Bool recognized = policy.recognized;
const Uint64 requested = policy.requestedMask;
m_affinityMask = ApplyAffinity(requested);
if (!recognised) {
if (!recognized) {
MGLOG_E("MG_Remote server: MOBILEGL_IPC_SERVER_AFFINITY='%s' is not `auto`, `off` or "
"a number; NO affinity was applied. This is logged rather than defaulted "
"because an affinity that silently did nothing is indistinguishable from "
@@ -1220,7 +1220,7 @@ namespace MobileGL::MG_Remote::Server {
if (corrupt) {
// Ring.h's own rule: a header the producer could not have written is
// Fatal{ProtocolCorruption}, never a retry. Retrying re-reads the same bytes
// for ever; skipping desynchronises seq, and seq IS the reply-slot id. PH-1 (3):
// for ever; skipping desynchronizes seq, and seq IS the reply-slot id. PH-1 (3):
// the header is the peer's bytes, so an armed session child latches here and
// stops reading the ring - neither a retry nor a skip.
(void)SessionLatch(MGFatalFamily::ProtocolCorruption,
@@ -1973,7 +1973,7 @@ namespace MobileGL::MG_Remote::Server {
}
}
Bool ServerLoop::ServerWindowWaitCancelled(void* self) {
Bool ServerLoop::ServerWindowWaitCanceled(void* self) {
const auto* loop = static_cast<const ServerLoop*>(self);
// A window-lost request also ends the wait: this thread holds a lease on a window that is
// being destroyed, and the UI thread is blocked until it answers (PumpControlRequest).
@@ -1990,7 +1990,7 @@ namespace MobileGL::MG_Remote::Server {
{
const ScopedBackendTurnYield yield;
acquired = display.AcquireFor(width, height, timeoutMs, this, &ServerWindowLostThunk,
&ServerWindowWaitCancelled, this, lease, &ServerWindowResizedThunk);
&ServerWindowWaitCanceled, this, lease, &ServerWindowResizedThunk);
}
switch (acquired) {
case ServerWindowAcquire::Acquired:
@@ -2008,7 +2008,7 @@ namespace MobileGL::MG_Remote::Server {
width, height);
return MOBILEGL_ERR_UNSUPPORTED;
case ServerWindowAcquire::NoWindow:
case ServerWindowAcquire::Cancelled:
case ServerWindowAcquire::Canceled:
case ServerWindowAcquire::Interrupted:
case ServerWindowAcquire::LeasedElsewhere:
break;
@@ -2265,7 +2265,7 @@ namespace MobileGL::MG_Remote::Server {
const ScopedBackendTurnYield yield;
acquired = ServerDisplayInstance().AcquireFor(
m_serverWindowWantWidth, m_serverWindowWantHeight, ServerDisplay::kGeometryGraceMs, this,
&ServerWindowLostThunk, &ServerWindowWaitCancelled, this, &lease, &ServerWindowResizedThunk);
&ServerWindowLostThunk, &ServerWindowWaitCanceled, this, &lease, &ServerWindowResizedThunk);
}
if (acquired != ServerWindowAcquire::Acquired) {
// Still registered: the next new window wakes this again.
+2 -2
View File
@@ -366,7 +366,7 @@ namespace MobileGL::MG_Remote::Server {
// what this session last handed it.
void NoteServerWindowExtent(void* window, Uint32 width, Uint32 height);
// AcquireFor's cancel predicate: the loop is stopping, or the session latched.
static Bool ServerWindowWaitCancelled(void* self);
static Bool ServerWindowWaitCanceled(void* self);
// D4: latched at the session's first successful surface creation; reset by Start(). Per
// session, because the loop is (P14 S2).
SessionSurfaceMode m_surfaceMode = SessionSurfaceMode::None;
@@ -531,7 +531,7 @@ namespace MobileGL::MG_Remote::Server {
EGLContext curCtx, EGLDisplay dpy, EGLSurface draw, EGLSurface read,
EGLContext ctx);
// P11 B1 (CONTRACT-P11 B1): the cpu core this process's `auto` policy reserves for a dialled-in
// P11 B1 (CONTRACT-P11 B1): the cpu core this process's `auto` policy reserves for a dialed-in
// shared-segment client's apply thread (ApplyThreadPolicy.h ReservedApplyCoreForConfig over this
// machine's topology and MOBILEGL_IPC_SERVER_AFFINITY), 0 when none; `outRule` receives the reason.
// The supervisor announces it at start-up; the server app's broker relays it to the helper.
+3 -3
View File
@@ -1508,17 +1508,17 @@ extern "C" __attribute__((visibility("default"))) int mobilegl_server_main(int a
if (SocketTransport::Listen(endpoint, &listener) != MOBILEGL_OK) return 72;
WireLogError("MG_Remote server: pid=%d listening on %s", static_cast<int>(::getpid()), endpoint.c_str());
{
// P11 B1 (CONTRACT-P11 B1): the one announcement of the core a dialled-in shared-segment client's
// P11 B1 (CONTRACT-P11 B1): the one announcement of the core a dialed-in shared-segment client's
// apply thread will take, on stdout, which the server app reads line by line
// (MobileGLServerService) and its broker hands to the helper - so the helper keeps the client
// off the core THIS server picks rather than a Java re-derivation of the rule.
const char* rule = "";
const auto reserved = static_cast<unsigned long long>(Server::ReservedApplyCoreForThisProcess(&rule));
std::fprintf(stdout,
"MG_Remote server: apply core reserved for dialled-in shared-segment clients: 0x%llx (%s)\n",
"MG_Remote server: apply core reserved for dialed-in shared-segment clients: 0x%llx (%s)\n",
reserved, rule);
std::fflush(stdout);
WireLogError("MG_Remote server: apply core reserved for dialled-in shared-segment clients: 0x%llx (%s)",
WireLogError("MG_Remote server: apply core reserved for dialed-in shared-segment clients: 0x%llx (%s)",
reserved, rule);
}
unsigned long sessionsFaulted = 0;
+1 -1
View File
@@ -112,7 +112,7 @@ namespace MobileGL::MG_Remote::Server {
// ---- s1's additions beyond c0's signature block ---------------------------------
// The sizes to create the segments with. Must be called before Accept; after it, the
// geometry is on the wire in Welcome and changing it would desynchronise the peer.
// geometry is on the wire in Welcome and changing it would desynchronize the peer.
void SetSegmentSizes(const Transport::SessionSegmentSizes& sizes);
// The server role's private BackendObject (ServerLoop::Backend(), v1's). It is NOT
+1 -1
View File
@@ -82,7 +82,7 @@ namespace MobileGL::MG_Remote::Transport {
std::uint32_t CalibrateSpinItersPerUs() {
// The probe body is the spin loop's body with the caller's `ready()` replaced
// by an atomic load the compiler may not hoist and may not fold: an atomic
// the optimiser cannot prove constant is the only portable way to keep the
// the optimizer cannot prove constant is the only portable way to keep the
// loop from vanishing, and it is also the closest thing to what a real
// `ready()` costs.
std::atomic<std::uint32_t> probe{0};
+1 -1
View File
@@ -54,7 +54,7 @@ namespace MobileGL::MG_Remote::Transport::FdPassing {
namespace {
// Every datagram starts with this, so the sideband length is explicit
// and a stray datagram is recognisable.
// and a stray datagram is recognizable.
struct SidebandHeader {
std::uint32_t magic;
std::uint32_t sidebandSize;
+2 -2
View File
@@ -78,7 +78,7 @@ namespace MobileGL::MG_Remote::Transport {
return MOBILEGL_ERR_INVALID_ARGUMENT;
}
// Cursors stay monotonic across the drain - only the generation moves,
// so any offset either side cached is now recognisably stale.
// so any offset either side cached is now recognizably stale.
control.ringGeneration.fetch_add(1, std::memory_order_acq_rel);
return MOBILEGL_OK;
}
@@ -715,7 +715,7 @@ namespace MobileGL::MG_Remote::Transport {
mix(inputs.DynamicParamsSize);
mix(inputs.CapsSize);
mix(inputs.MemberLayout);
mix(inputs.CatalogueLayout);
mix(inputs.CatalogLayout);
mix(inputs.RenderStateLayout);
mix(inputs.FormatCapabilityTargets);
mix(inputs.FormatCapabilityFormats);
+1 -1
View File
@@ -501,7 +501,7 @@ namespace MobileGL::MG_Remote::Transport {
std::uint64_t DynamicParamsSize = 0;
std::uint64_t CapsSize = 0;
std::uint64_t MemberLayout = 0;
std::uint64_t CatalogueLayout = 0;
std::uint64_t CatalogLayout = 0;
std::uint64_t RenderStateLayout = 0;
std::uint64_t FormatCapabilityTargets = 0;
std::uint64_t FormatCapabilityFormats = 0;
+4 -4
View File
@@ -886,11 +886,11 @@ namespace MobileGL::MG_Remote::Wire {
}
Bool PipeWireEncoder::CheckCancellation() {
if (Cancelled() || (m_cancellationHook && m_cancellationHook(m_cancellationSelf)) ||
if (Canceled() || (m_cancellationHook && m_cancellationHook(m_cancellationSelf)) ||
(m_stageRetirementBell && m_stageRetirementBell->Dead())) {
m_cancelled = true;
m_canceled = true;
}
return m_cancelled;
return m_canceled;
}
Uint8* PipeWireEncoder::StageAllocate(Uint64 size) {
@@ -1059,7 +1059,7 @@ namespace MobileGL::MG_Remote::Wire {
Uint64 PipeWireEncoder::EncodeRecord(MGPWireOp op, const void* payload, Uint64 payloadBytes, const WireTail* tails,
Uint32 tailCount, Bool noReply) {
if (Cancelled()) return kInvalidSeq;
if (Canceled()) return kInvalidSeq;
if (!Valid()) {
WireProtocolFatal("PipeWireEncoder::EncodeRecord", "no SEG_CMD producer installed");
}
+3 -3
View File
@@ -305,8 +305,8 @@ namespace MobileGL::MG_Remote::Wire {
}
// The fast check is used on encode; the doorbell probe only runs while
// staging or at an unsuccessful wait boundary.
Bool Cancelled() const {
return m_cancelled || (m_sessionLost && m_sessionLost->load(std::memory_order_acquire));
Bool Canceled() const {
return m_canceled || (m_sessionLost && m_sessionLost->load(std::memory_order_acquire));
}
Bool CheckCancellation();
void SetStageWaitTimeoutMs(Uint32 timeoutMs) { m_stageWaitTimeoutMs = timeoutMs; }
@@ -373,7 +373,7 @@ namespace MobileGL::MG_Remote::Wire {
const std::atomic<bool>* m_sessionLost = nullptr;
CancellationHook m_cancellationHook = nullptr;
void* m_cancellationSelf = nullptr;
Bool m_cancelled = false;
Bool m_canceled = false;
Uint32 m_stageWaitTimeoutMs = 120000;
// P5e (ra): the session's event drain, called around the stage-bell park (§2.6).

Some files were not shown because too many files have changed in this diff Show More