Guard the AImageReader window path behind __ANDROID__ and add a
mobilegl-desktop DEQP target so glcts can run against libMobileGL.so on a
Linux host via pbuffer surfaces (VK_EXT_headless_surface).
- every render pass declared colorAttachmentCount=8 (the full GL draw-buffer
slot span) with trailing VK_ATTACHMENT_UNUSED references, and Adreno
configures its per-pixel render-backend/export path from the DECLARED
count - so every fragment of every pass paid an 8-render-target export
cost; this was the bulk of the 1.5x per-pixel gap against
MobileGlues+ANGLE on the same Qualcomm driver (their subpasses declare
exactly the used span)
- measured on Adreno 650 / MC 26.2 / 1440x3044: total GPU frame time
11.9 -> 7.5 ms (-37%, now below ANGLE's 7.87 ms), the single-quad
swapchain blit pass alone 1.26 -> 0.40 ms, steady in-world FPS 82.8 -> 123
under the standard cooled-start protocol, matching the
MobileGlues+ANGLE+system-Vulkan benchmark of 123.8
- trailing UNUSED references are popped before the subpass is built (the
entry's colorAttachmentCount and every pipeline's colour-blend span follow
it); interior GL_NONE holes keep their slots so fragment-output locations
still line up
- the pipeline-side fragmentOutputMask check downgrades from assert to a
debug log: an output at a location past the trimmed span is discarded,
which is GL's defined behaviour for a draw buffer set to GL_NONE
- viewport, scissor, blend constants, depth bias, line width and the six
stencil parameters were re-emitted unconditionally for EVERY draw (~1500
vkCmdSet* per frame in MC 26.2, where ANGLE emits a handful), costing CPU
record time and GPU command-processor work for values that almost never
change between draws
- a recording-scoped shadow now drops any vkCmdSet* whose values match what
the command buffer already holds; valid because every PipelineFactory
pipeline declares the same eight dynamic states, so set values persist
across those binds
- the shadow resets at every command-buffer (re)begin (dynamic state does
not survive the boundary) and after binding the blit or depth-mipmap
pipelines, whose narrower dynamic sets make the untouched states undefined
and whose raw viewport/scissor writes bypass the shadow
- every non-MSAA texture was allocated with a full mip chain regardless of
how many levels the GL texture actually defines, so MC's 3044x1440 main
colour and depth render targets each carried 12 levels where ANGLE
allocates one; a level-0-only texture now gets a single-level backing and
upgrades to the full chain exactly once when a second level is first
defined, through the existing preserve-copy recreation path
- saves a third of the memory of every mip-less texture and keeps
single-level render targets off the multi-mip image layout entirely, which
also removes the surface the Adreno 650 implicit-LOD overread workaround
(ForceExplicitLod0SamplePass) exists to defend
- measured perf-neutral on Adreno 650 / MC 26.2 (the driver keeps full UBWC
on multi-mip render targets), so this is a memory/robustness fix, not a
speed one
- a draw whose sampled texture needs out-of-pass work (deferred clear
materialization or a sampled-layout transition) used to end the active
render pass - a full-target store+reload on a tiler - even when the only
ordering the work needs is 'before this draw'; MC 26.2 clears an overlay
texture every frame and samples it mid-pass, splitting the main scene pass
once per frame for nothing
- every frame slot now carries a second primary command buffer, submitted
strictly AHEAD of the frame command buffer in the same vkQueueSubmit; when
the open recording has not referenced the image yet (tracked via a
recording-generation stamp on the texture resource, advanced on every
frame-command-buffer begin and stamped at every recorded reference:
attachments at BeginRenderPass/attachment-write, sampled reads per draw,
layout transitions), the clear/transition is recorded there and the active
pass stays open - ANGLE's outside-render-pass command stream, restricted
to the provably reorderable case
- mid-frame flushes and readback submits close and carry the pre stream with
the frame buffer (it must never be submitted later than the recording it
was paired with), retiring both under the same submit index; dropped
recordings (present suspension, swapchain recreation) abandon it
- MaterializePendingClearForTexture's no-active-render-pass assert now
applies only to the frame command buffer, since the pre stream records
while a pass is open on the frame buffer by design
- EGL swap semantics make the presented colour buffer's content undefined at
its next acquire (EGL_BUFFER_DESTROYED, the implementation default) and
every ancillary depth/stencil buffer's content undefined after ANY swap,
yet the default-FBO render pass reloaded both with LOAD_OP_LOAD every
frame; SwapchainObject now tracks per-image content validity (defined when
a pass stores into the attachment, invalidated at present) and the
render-pass manager turns an undefined attachment's tile load into
LOAD_OP_DONT_CARE with initialLayout=UNDEFINED, keyed into both hashes so
the cached LOAD variants cannot be hit by mistake
- the default framebuffer's depth attachment is now attached ON DEMAND: a
draw with depth test and stencil test both disabled (GL: a disabled test
neither reads nor writes its buffer), and no pending depth/stencil clear,
resolves to a depth-less pass flavour, dropping the D24S8 tile load AND
store outright - MC 26.2 renders its GUI into its own FBO and only ever
blits colour to the default framebuffer, so its swapchain pass carried a
full-screen depth round-trip for nothing
- the flavour only escalates: an active depth-full pass absorbs depth-less
draws unchanged, while a depth-using draw against a depth-less pass
resolves to an incompatible entry and splits, its depth loading DONT_CARE
(the content was undefined all along); the depth-less flavour is folded
into ComputeHash and the per-draw fast-path memo so the two flavours can
never alias
- Every storage-capable colour texture was created MUTABLE_FORMAT, and Adreno
gives up bandwidth compression on an image that may be viewed as any format in
its compatibility class. MC's main render target therefore ran uncompressed;
in a fill-bound scene that is the whole frame budget. Measured on Adreno 650,
MC 26.2, same scene and camera, device cooled to 38-40C before each run:
65.3 -> 80.9 fps (+23.9%), GPU busy ~93% in both.
- VK_KHR_image_format_list (enabled when present) fixes it without giving up
mutability: VkImageFormatListCreateInfo names the exact formats a view may
use, so the driver can keep the image compressed. The set must be exhaustive
or the result is undefined - for sampled views it is exactly what
ResolveSampledImageViewFormat can return over the three numeric domains.
- glBindImageTexture may name any compatible format, which cannot be enumerated
ahead of time, so a texture bound to an image unit gets no format list. That
is what VK_IMAGE_USAGE_STORAGE_BIT becoming on-demand is for: it makes
"unmarked" mean "will never receive an arbitrary-format storage view", which
is what makes the list sound. Removing STORAGE is worth nothing on its own
(65.4 fps, measured) - only the mutability bound pays.
- MarkStorageImageTexture runs over every collected image-unit texture before
the probe loop in PrepareStorageImageTextures, because that loop stops at the
first texture needing work and would leave the rest unmarked. The mark makes
NeedsStorageImagePreparation report true, which is what ends the render pass,
so the recreate lands outside it.
- storageUsageResolved separates "not upgraded yet" from "this format can never
carry STORAGE", so a format whose optimalTilingFeatures lack STORAGE_IMAGE
cannot ask for a recreate that will never happen. SyncTexture's cross-draw
early-out also has to break on a pending upgrade or the recreate never runs.
- An upgrade recreates the image and carries its contents forward through
PreserveTextureContentsOnRecreate, which submits its own command buffer and
waits. Whatever the frame already recorded into the old image is still
unsubmitted, so that copy would read pre-frame content and this frame's
rendering into the texture would be lost - exactly the render-target-then-
image-unit case. PrepareStorageImageTextures now flushes first; it takes the
FrameData rather than a command buffer because the flush retires the current
one, and drops the sampled-descriptor-set memo that described it.
- SyncCurrentFBO skips the READ-target pass when the same GL FBO is bound as
both draw and read (the common GL_FRAMEBUFFER case), but the read buffer
(glReadBuffer) is only applied inside SyncToBackend's READ path — so the skip
silently dropped every glReadBuffer change, leaving the backend read buffer
stuck at COLOR_ATTACHMENT0.
- Extract the read-buffer application into BackendFramebufferObject::
SyncReadBufferToBackend and invoke it from the skip branch (target == Read)
as well as from SyncToBackend, so reads always target the right attachment.
- Bind the backend FBO as READ inside the helper before glReadBuffer, since the
skip path only bound it as DRAW.
- Fixes KHR-GL3x.draw_buffers.draw_buffers_1 (reading COLOR_ATTACHMENT1 while
the FBO stays GL_FRAMEBUFFER-bound returned attachment 0's value); the render
was already correct, only the readback resolved the wrong attachment.
- BlankBlockComments replaced comment chars with spaces but preserved interior newlines, so a block comment spanning a newline inside a #define truncated the macro body (VALUE became empty)
- glslang has a conformant preprocessor and collapses a block comment to one space across newlines, so the delivered source now keeps comments intact and lets glslang handle them
- Fixes KHR-GL3x.shaders.preprocessor multiline_comment_define / redefine_object_multiline_comment / function_redefinition_3 (6 cases, both devices)
- FilterUnsupportedGpuShaderInt64 relied on the blanking to skip commented-out #extension lines; it now masks comments locally (MaskCommentsAndQuotedText) like the sibling passes, collecting edits and applying them back-to-front
- conditional_inclusion.basic_2 (defined() via macro expansion) stays failing by design: glslang rejects it as UB and working around it would mean re-running preprocessing MobileGL defers to glslang