[Docs, Chore] (docs, tools, MobileGL, scripts, CONTRIBUTING.md, .gitignore): translate the documentation and use US spelling

This commit is contained in:
BZLZHH
2026-10-31 19:58:19 +08:00
parent 47892afddc
commit 4f50bedde0
512 changed files with 2955 additions and 2945 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
################################################################################ ################################################################################
# 此 .gitignore 文件已由 Microsoft(R) Visual Studio 自动创建。 # This .gitignore file was automatically created by Microsoft(R) Visual Studio.
################################################################################ ################################################################################
/build /build
+7 -7
View File
@@ -16,10 +16,10 @@ Commands below use `python3`. On Windows, run `python` instead.
- **Subject only by default.** Add `- ` detail lines only when a detail really needs to be in the commit message. Leave one blank line after the subject. A detail that wraps continues on at most two lines indented by two spaces. No paragraphs and no trailers. - **Subject only by default.** Add `- ` detail lines only when a detail really needs to be in the commit message. Leave one blank line after the subject. A detail that wraps continues on at most two lines indented by two spaces. No paragraphs and no trailers.
- **Content** after `): ` is at most 72 characters of plain English in the imperative mood ("flush once per frame", not "flushes" or "flushed"). - **Content** after `): ` is at most 72 characters of plain English in the imperative mood ("flush once per frame", not "flushes" or "flushed").
- **Type** is one or more of these, comma-separated (`[Fix, Test]`): - **Type** is one or more of these, comma-separated (`[Fix, Test]`):
- `Feat`: new behaviour. - `Feat`: new behavior.
- `Fix`: corrects behaviour. - `Fix`: corrects behavior.
- `Perf`: faster or lighter, same behaviour. - `Perf`: faster or lighter, same behavior.
- `Refactor`: restructures code, same behaviour. - `Refactor`: restructures code, same behavior.
- `Test`: tests only. - `Test`: tests only.
- `Docs`: documentation and comments only. - `Docs`: documentation and comments only.
- `Build`: CMake, Gradle, toolchains. - `Build`: CMake, Gradle, toolchains.
@@ -43,12 +43,12 @@ Example:
## Comments, strings and documentation ## Comments, strings and documentation
- Code, comments, log strings and commit messages are in English. Documentation may be in Chinese or English. - Code, comments, log strings and commit messages are in English, and documentation may be in English. American spelling is preferred in all of them: documentation, comments, strings and identifiers (color, behavior, center, cancel, initialize, normalize, organize, analyze, license, defense, artifact, modeling, labeled, gray, honor, neighbor, catalog).
- A comment states what the code does now and why, in a few lines. Rationale longer than about 15 lines belongs in a design document under `docs/`, with a one-line pointer in the code. - A comment states what the code does now and why, in a few lines. Rationale longer than about 15 lines belongs in a design document under `docs/`, with a one-line pointer in the code.
- In code and comments, cite a symbol, file or section, never a line number: `ARCHITECTURE.md §1.3`, `VulkanRenderer::BeginFrame()`. Documents may keep `file:line` citations; the documentation citation check resolves them. - In code and comments, cite a symbol, file or section, never a line number: `ARCHITECTURE.md §1.3`, `VulkanRenderer::BeginFrame()`. Documents may keep `file:line` citations; the documentation citation check resolves them.
- State the rule, not its history. No "previously" or "after review", and no planning ids in comments, strings, identifiers or file names. - State the rule, not its history. No "previously" or "after review", and no planning ids in comments, strings, identifiers or file names.
- Plain wording, without ALL-CAPS emphasis. - Plain wording, without ALL-CAPS emphasis.
- In code and comments (C, C++, GLSL, CMake, shell, Python, YAML), do not name external projects such as Mesa, Zink or gallium; describe the technique instead. Naming the driver or hardware where a behaviour was observed (llvmpipe, ANGLE, Adreno, Mali) is fine, and so are file, package and extension names that contain these words (`50_mesa.json`, `GL_MESA_*`). - In code and comments (C, C++, GLSL, CMake, shell, Python, YAML), do not name external projects such as Mesa, Zink or gallium; describe the technique instead. Naming the driver or hardware where a behavior was observed (llvmpipe, ANGLE, Adreno, Mali) is fine, and so are file, package and extension names that contain these words (`50_mesa.json`, `GL_MESA_*`).
## Code ## Code
@@ -69,7 +69,7 @@ Example:
5. `MG_Test`, `MG_IntegrationTest` and `MG_Benchmark`. 5. `MG_Test`, `MG_IntegrationTest` and `MG_Benchmark`.
- Each backend is isolated. Files under `MG_Backend/DirectGLES/` or `MG_Backend/DirectVulkan/` are included only by the same backend, by the registry files directly in `MG_Backend/`, by the root `.cpp` files and by tests. - Each backend is isolated. Files under `MG_Backend/DirectGLES/` or `MG_Backend/DirectVulkan/` are included only by the same backend, by the registry files directly in `MG_Backend/`, by the root `.cpp` files and by tests.
- Platform-specific APIs stay behind an abstraction, never raw in logic code. - Platform-specific APIs stay behind an abstraction, never raw in logic code.
- A fix that restores intended behaviour ships unconditionally. Add a switch only for a real trade-off. - A fix that restores intended behavior ships unconditionally. Add a switch only for a real trade-off.
- A cache keyed on object identity uses a monotonic lifetime id, never a GL name or a pointer, because both are recycled. - A cache keyed on object identity uses a monotonic lifetime id, never a GL name or a pointer, because both are recycled.
## What goes in the repository ## What goes in the repository
+6 -6
View File
@@ -173,7 +173,7 @@ namespace MobileGL {
// vertex, tessellation control, tessellation evaluation and geometry at 0, and only // vertex, tessellation control, tessellation evaluation and geometry at 0, and only
// fragment (8 in GL, 4 in ES) and compute are guaranteed to have any. Every real ARM // fragment (8 in GL, 4 in ES) and compute are guaranteed to have any. Every real ARM
// GLES driver takes that allowance - a Mali-G925 reports 0 for all four - so a // GLES driver takes that allowance - a Mali-G925 reports 0 for all four - so a
// backend that cannot honour a graphics-stage storage block MUST report 0 here // backend that cannot honor a graphics-stage storage block MUST report 0 here
// rather than a hopeful number. Advertising a non-zero count the driver will refuse // rather than a hopeful number. Advertising a non-zero count the driver will refuse
// does not make the block work; it only moves the failure from an honest // does not make the block work; it only moves the failure from an honest
// "unsupported" at query time to a backend link error the frontend never surfaces, // "unsupported" at query time to a backend link error the frontend never surfaces,
@@ -242,7 +242,7 @@ namespace MobileGL {
// GL_LAYER_PROVOKING_VERTEX / GL_VIEWPORT_INDEX_PROVOKING_VERTEX: which vertex of a // GL_LAYER_PROVOKING_VERTEX / GL_VIEWPORT_INDEX_PROVOKING_VERTEX: which vertex of a
// primitive supplies gl_Layer and gl_ViewportIndex. GL 4.6 table 23.65 makes // primitive supplies gl_Layer and gl_ViewportIndex. GL 4.6 table 23.65 makes
// GL_UNDEFINED_VERTEX a legal answer for both, and it is the honest default - naming // GL_UNDEFINED_VERTEX a legal answer for both, and it is the honest default - naming
// a convention is a statement about behaviour, so a backend that does not pin one // a convention is a statement about behavior, so a backend that does not pin one
// must not claim it does. DirectGLES fills the layer one from the ES 3.2 query and // must not claim it does. DirectGLES fills the layer one from the ES 3.2 query and
// the viewport one from GL_OES_viewport_array, and leaves UNDEFINED where the // the viewport one from GL_OES_viewport_array, and leaves UNDEFINED where the
// capability is absent: without the viewport array extension only viewport 0 is ever // capability is absent: without the viewport array extension only viewport 0 is ever
@@ -271,7 +271,7 @@ namespace MobileGL {
// same image and lets an implementation answer GL_FRAMEBUFFER_UNSUPPORTED // same image and lets an implementation answer GL_FRAMEBUFFER_UNSUPPORTED
// otherwise, which is what DirectVulkan (one combined attachment) and the // otherwise, which is what DirectVulkan (one combined attachment) and the
// real ES drivers behind DirectGLES both do. Defaults to true so a backend // real ES drivers behind DirectGLES both do. Defaults to true so a backend
// that never sets it keeps the permissive behaviour. // that never sets it keeps the permissive behavior.
Bool SupportsDistinctDepthStencilAttachments = true; Bool SupportsDistinctDepthStencilAttachments = true;
// Whether attaching a single layer of a 3D or array texture to a framebuffer actually // Whether attaching a single layer of a 3D or array texture to a framebuffer actually
// renders to that layer. DirectGLES hands the layer straight to // renders to that layer. DirectGLES hands the layer straight to
@@ -312,9 +312,9 @@ namespace MobileGL {
// profile") and the demotion there is mathematically mandatory, always. // profile") and the demotion there is mathematically mandatory, always.
// Defaults to false so a backend that never sets it - and the no-backend case, which // Defaults to false so a backend that never sets it - and the no-backend case, which
// is what standalone shader compiles and the unit tests run under - keeps the // is what standalone shader compiles and the unit tests run under - keeps the
// demotion, which is the behaviour that works everywhere. // demotion, which is the behavior that works everywhere.
Bool SupportsShaderFloat64 = false; Bool SupportsShaderFloat64 = false;
// Whether glVertexAttribLFormat / glVertexArrayAttribLFormat can be honoured, i.e. // Whether glVertexAttribLFormat / glVertexArrayAttribLFormat can be honored, i.e.
// whether a 64-bit vertex attribute can actually reach a shader unconverted. Detected, // whether a 64-bit vertex attribute can actually reach a shader unconverted. Detected,
// never assumed: DirectVulkan needs VkPhysicalDeviceFeatures::shaderFloat64 (the // never assumed: DirectVulkan needs VkPhysicalDeviceFeatures::shaderFloat64 (the
// attribute travels as its 32-bit word pair, so no VK_FORMAT_R64* is required, but the // attribute travels as its 32-bit word pair, so no VK_FORMAT_R64* is required, but the
@@ -382,7 +382,7 @@ namespace MobileGL {
Bool FragmentShadingRateAttachmentWithDefaultFramebuffer = false; Bool FragmentShadingRateAttachmentWithDefaultFramebuffer = false;
// What glGetFragmentShadingRatesEXT answers, one mask per sample count 1, 2, 4 .. 64 // What glGetFragmentShadingRatesEXT answers, one mask per sample count 1, 2, 4 .. 64
// (index = log2(samples)): bit (log2(w) << 2 | log2(h)) is set when that rate is // (index = log2(samples)): bit (log2(w) << 2 | log2(h)) is set when that rate is
// honoured exactly at that sample count, i.e. gl_ShadingRateEXT reads it back // honored exactly at that sample count, i.e. gl_ShadingRateEXT reads it back
// unclamped. Zero masks mean no rate at all, which is what a sample count the device // unclamped. Zero masks mean no rate at all, which is what a sample count the device
// cannot shade coarsely answers. // cannot shade coarsely answers.
Uint16 FragmentShadingRateMasks[7] = {}; Uint16 FragmentShadingRateMasks[7] = {};
+9 -9
View File
@@ -164,7 +164,7 @@ namespace MobileGL::MG_Config {
// surface's minimum (the frames-in-flight count still raises the request when it is // surface's minimum (the frames-in-flight count still raises the request when it is
// larger), clamped to the surface's maximum. With more images, the one the display has // larger), clamped to the surface's maximum. With more images, the one the display has
// just released is reused later, by when its release has usually signaled, so fewer // just released is reused later, by when its release has usually signaled, so fewer
// frames start their window pass behind it. Each image costs a full-size colour buffer // frames start their window pass behind it. Each image costs a full-size color buffer
// plus the default framebuffer's depth/stencil buffer of the same size. // plus the default framebuffer's depth/stencil buffer of the same size.
Uint32 MagmaExtraSwapchainImages = 0; Uint32 MagmaExtraSwapchainImages = 0;
// MOBILEGL_MAGMA_MAX_DRAWS_PER_COMMAND_BUFFER: GL draws and dispatches Magma records into // MOBILEGL_MAGMA_MAX_DRAWS_PER_COMMAND_BUFFER: GL draws and dispatches Magma records into
@@ -316,10 +316,10 @@ namespace MobileGL::MG_Config {
// keeps the built-in default (MG_Util::Async::kAsyncShaderCompileDefault); falsy // keeps the built-in default (MG_Util::Async::kAsyncShaderCompileDefault); falsy
// forces every glCompileShader/glLinkProgram to run synchronously on the calling // forces every glCompileShader/glLinkProgram to run synchronously on the calling
// thread AND withdraws GL_KHR_parallel_shader_compile, so the single switch reverts // thread AND withdraws GL_KHR_parallel_shader_compile, so the single switch reverts
// both the threading and the application-visible behaviour change. // both the threading and the application-visible behavior change.
QuirkOverride AsyncShaderCompile = QuirkOverride::Auto; QuirkOverride AsyncShaderCompile = QuirkOverride::Auto;
// MOBILEGL_ASYNC_SHADER_COMPILE_THREADS: shader-compile worker count. 0 (unset) means // MOBILEGL_ASYNC_SHADER_COMPILE_THREADS: shader-compile worker count. 0 (unset) means
// auto, which is min(4, big cores); an explicit value is honoured as given. // auto, which is min(4, big cores); an explicit value is honored as given.
Uint32 AsyncShaderCompileThreads = 0; Uint32 AsyncShaderCompileThreads = 0;
// MOBILEGL_DISABLE_SHADER_PREWARM: skip the glslang built-in table prewarm in // MOBILEGL_DISABLE_SHADER_PREWARM: skip the glslang built-in table prewarm in
// Initialize(), which moves ~20-100 ms of table building from the first shader compiles // Initialize(), which moves ~20-100 ms of table building from the first shader compiles
@@ -356,7 +356,7 @@ namespace MobileGL::MG_Config {
// extension only ever gave the SHADER a compilable name: MobileGL has never // extension only ever gave the SHADER a compilable name: MobileGL has never
// programmed a driver's INDEXED viewport state (SyncRenderState pushes index 0 // programmed a driver's INDEXED viewport state (SyncRenderState pushes index 0
// and nothing else), so on an extension-capable driver every index rasterized as // and nothing else), so on an extension-capable driver every index rasterized as
// index 0 exactly as it did without one. ForceOff returns to that behaviour - // index 0 exactly as it did without one. ForceOff returns to that behavior -
// the pre-emulation path, extension passthrough where it exists and // the pre-emulation path, extension passthrough where it exists and
// LowerViewportIndexPass' demote-to-a-plain-global where it does not - and is // LowerViewportIndexPass' demote-to-a-plain-global where it does not - and is
// the negative control the emulation is measured against. // the negative control the emulation is measured against.
@@ -420,7 +420,7 @@ namespace MobileGL::MG_Config {
// MOBILEGL_PIPE_PUSH: per-subsystem bitmask selecting which state the frontend // MOBILEGL_PIPE_PUSH: per-subsystem bitmask selecting which state the frontend
// PUSHES over MGPipe instead of leaving the backend to pull it out of GLContext. // PUSHES over MGPipe instead of leaving the backend to pull it out of GLContext.
// 0 - the only shipped value until the migration lands - is "pull everything", // 0 - the only shipped value until the migration lands - is "pull everything",
// i.e. exactly today's behaviour, and is the default of a PULL build, where the // i.e. exactly today's behavior, and is the default of a PULL build, where the
// knob is meaningless anyway. A PUSH build defaults to every subsystem migrated so // knob is meaningless anyway. A PUSH build defaults to every subsystem migrated so
// far (MG_Pipe::kMGPipeSubsystemsMigratedAtP5e = 0x3fff), so MOBILEGL_PIPE_PUSH=0 in // far (MG_Pipe::kMGPipeSubsystemsMigratedAtP5e = 0x3fff), so MOBILEGL_PIPE_PUSH=0 in
// the environment is the all-pull control and 0x1fff (kMGPipeSubsystemsMigratedAtP4a) // the environment is the all-pull control and 0x1fff (kMGPipeSubsystemsMigratedAtP4a)
@@ -447,7 +447,7 @@ namespace MobileGL::MG_Config {
// (every MGPBufferRange::Res names a Buffer handle; P5e) // (every MGPBufferRange::Res names a Buffer handle; P5e)
// A dependency that is not met is REFUSED with one ERROR naming both bits and the // A dependency that is not met is REFUSED with one ERROR naming both bits and the
// family runs its legacy arm; it is never half-run. // family runs its legacy arm; it is never half-run.
// 1<<63 NOT a subsystem, a BEHAVIOUR: turn OFF client-side content addressing of // 1<<63 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 // CSOs, so every pipeline-version change mints a fresh CSO and the map is
// never probed. The negative control the CSO design is measured against. // never probed. The negative control the CSO design is measured against.
// P13 W3b (ID-P13-3): bits 0-13 are FIXED ON. Their pre-handle arms are gone, so a mask // P13 W3b (ID-P13-3): bits 0-13 are FIXED ON. Their pre-handle arms are gone, so a mask
@@ -489,7 +489,7 @@ namespace MobileGL::MG_Config {
extern FeaturesTable Features; extern FeaturesTable Features;
// --------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------
// P5: the transport selector and the MOBILEGL_IPC_* family (ARCHITECTURE.md 16, 附 A) // P5: the transport selector and the MOBILEGL_IPC_* family (ARCHITECTURE.md 16, appendix A)
// --------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------
// //
// MOBILEGL_TRANSPORT = monolith | inproc | spawn | unix:<path> | pipe:<name>. // MOBILEGL_TRANSPORT = monolith | inproc | spawn | unix:<path> | pipe:<name>.
@@ -573,7 +573,7 @@ namespace MobileGL::MG_Config {
// The MOBILEGL_IPC_* family. A separate table rather than more FeaturesTable members, // The MOBILEGL_IPC_* family. A separate table rather than more FeaturesTable members,
// for the G1 reason above and because every field here is meaningless without the // for the G1 reason above and because every field here is meaningless without the
// transport: a build that cannot reach the MG_Remote code cannot honour one of them. // transport: a build that cannot reach the MG_Remote code cannot honor one of them.
// //
// P5 lands exactly the knobs P5's own packages read. A later phase's knob is added HERE, // P5 lands exactly the knobs P5's own packages read. A later phase's knob is added HERE,
// through the integrator, and not invented at its call site - ARCHITECTURE.md appendix A holds // through the integrator, and not invented at its call site - ARCHITECTURE.md appendix A holds
@@ -753,7 +753,7 @@ namespace MobileGL::MG_Config {
// own) and no SetWindowHandle, and take the surface's real geometry back from the server. // own) and no SetWindowHandle, and take the surface's real geometry back from the server.
// Meaningful only when this client talks to a remote server (MOBILEGL_TRANSPORT=spawn, the // Meaningful only when this client talks to a remote server (MOBILEGL_TRANSPORT=spawn, the
// fork or tcp:// control); under monolith / inproc it is parsed, logged as ignored, and // fork or tcp:// control); under monolith / inproc it is parsed, logged as ignored, and
// changes nothing. `offscreen` (the default) is today's behaviour byte for byte. // changes nothing. `offscreen` (the default) is today's behavior byte for byte.
// Pbuffers stay pbuffers in both modes (D4: one mode per session, decided by the first). // Pbuffers stay pbuffers in both modes (D4: one mode per session, decided by the first).
IpcSurface Surface = IpcSurface::Offscreen; IpcSurface Surface = IpcSurface::Offscreen;
}; };
+4 -4
View File
@@ -526,7 +526,7 @@ namespace MobileGL::MG_ConfigLoader {
"ring to an apply thread"); "ring to an apply thread");
return; return;
} }
// The three P6 forms. Recognised precisely, so the diagnostic can say "rejected" // The three P6 forms. Recognized precisely, so the diagnostic can say "rejected"
// rather than "unknown", which are different bugs on the operator's side. // rather than "unknown", which are different bugs on the operator's side.
// P6 `sm`/`cp`: spawn is IMPLEMENTED. It launches a server process on a // P6 `sm`/`cp`: spawn is IMPLEMENTED. It launches a server process on a
// rendezvous of its own and connects to it - the two processes are // rendezvous of its own and connects to it - the two processes are
@@ -575,7 +575,7 @@ namespace MobileGL::MG_ConfigLoader {
QueryEnvVariable("MOBILEGL_IPC_CONTROL", ipc.Control, "fork"); QueryEnvVariable("MOBILEGL_IPC_CONTROL", ipc.Control, "fork");
QueryEnvVariable("MOBILEGL_IPC_DATA", ipc.Data, "auto"); QueryEnvVariable("MOBILEGL_IPC_DATA", ipc.Data, "auto");
// Both ring floors are 1 MiB, not 0: a ring caps ONE record at half its size, and // Both ring floors are 1 MiB, not 0: a ring caps ONE record at half its size, and
// the catalogue's largest fixed payload (MGPFramebufferState, 304 bytes) plus a // the catalog's largest fixed payload (MGPFramebufferState, 304 bytes) plus a
// create_shader_state archive already needs far more than a toy ring. The ceilings // create_shader_state archive already needs far more than a toy ring. The ceilings
// are sanity, not policy. // are sanity, not policy.
ipc.RingMb = QueryEnvUint32("MOBILEGL_IPC_RING_MB", 8, 1, 1024); ipc.RingMb = QueryEnvUint32("MOBILEGL_IPC_RING_MB", 8, 1, 1024);
@@ -703,7 +703,7 @@ namespace MobileGL::MG_ConfigLoader {
const Bool serverProcess = role != nullptr && std::strcmp(role, "server") == 0; const Bool serverProcess = role != nullptr && std::strcmp(role, "server") == 0;
if (ipc.Surface == MG_Config::IpcSurface::Server && !serverProcess && if (ipc.Surface == MG_Config::IpcSurface::Server && !serverProcess &&
MG_Config::Transport != MG_Config::TransportMode::Spawn) { MG_Config::Transport != MG_Config::TransportMode::Spawn) {
// Said, not silently honoured: there is no remote server here to own a window. // Said, not silently honored: there is no remote server here to own a window.
MGLOG_W("Config: MOBILEGL_IPC_SURFACE=server is IGNORED - it asks a remote server " MGLOG_W("Config: MOBILEGL_IPC_SURFACE=server is IGNORED - it asks a remote server "
"(MOBILEGL_TRANSPORT=spawn, a fork or tcp:// control) to own the window surface, " "(MOBILEGL_TRANSPORT=spawn, a fork or tcp:// control) to own the window surface, "
"and this run has none. Window surfaces stay the client's own"); "and this run has none. Window surfaces stay the client's own");
@@ -711,7 +711,7 @@ namespace MobileGL::MG_ConfigLoader {
} }
if (MG_Config::Transport == MG_Config::TransportMode::Monolith) return; if (MG_Config::Transport == MG_Config::TransportMode::Monolith) return;
// One line, on the arm where these numbers decide behaviour, because every one of // One line, on the arm where these numbers decide behavior, because every one of
// them is a number a bug report has to quote. // them is a number a bug report has to quote.
// CREATE-WINDOW IS IN THIS LINE BECAUSE A RUN HAS TO SAY WHICH WINDOW IT USED. It was // CREATE-WINDOW IS IN THIS LINE BECAUSE A RUN HAS TO SAY WHICH WINDOW IT USED. It was
// missing while the value was a number nobody varied, and the moment it became one that // missing while the value was a number nobody varied, and the moment it became one that
+1 -1
View File
@@ -218,7 +218,7 @@ namespace MobileGL::MG_Backend {
void (*BeginTransformFeedback)(GLenum primitiveMode); void (*BeginTransformFeedback)(GLenum primitiveMode);
void (*EndTransformFeedback)(); void (*EndTransformFeedback)();
// ARB_transform_feedback2. A backend that leaves these null keeps the single // ARB_transform_feedback2. A backend that leaves these null keeps the single
// implicit capture span the frontend has always modelled; the frontend state // implicit capture span the frontend has always modeled; the frontend state
// (paused flag, per-object bindings) is tracked either way. // (paused flag, per-object bindings) is tracked either way.
void (*PauseTransformFeedback)(); void (*PauseTransformFeedback)();
void (*ResumeTransformFeedback)(); void (*ResumeTransformFeedback)();
+1 -1
View File
@@ -146,7 +146,7 @@ namespace MobileGL::MG_Backend {
return false; return false;
} }
// The same for a YUV fourcc, whose planes and hints `layout` gives. A YUV buffer the // The same for a YUV fourcc, whose planes and hints `layout` gives. A YUV buffer the
// server did not export is taken too (its labelled CPU-copy fallback). // server did not export is taken too (its labeled CPU-copy fallback).
virtual Bool ImportSharedImagePlanes(int fd, Uint32 width, Uint32 height, Uint32 fourcc, virtual Bool ImportSharedImagePlanes(int fd, Uint32 width, Uint32 height, Uint32 fourcc,
const SharedImageImportLayout& layout, Uint64* outId) { const SharedImageImportLayout& layout, Uint64* outId) {
(void)fd, (void)width, (void)height, (void)fourcc, (void)layout, (void)outId; (void)fd, (void)width, (void)height, (void)fourcc, (void)layout, (void)outId;
@@ -266,7 +266,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
reasons.push_back("GL_DEPTH_COMPONENT32 native probe failed on OpenGL ES"); reasons.push_back("GL_DEPTH_COMPONENT32 native probe failed on OpenGL ES");
} }
if (options & PixelFormatNormalizeOptionBit::NoThreeChannelRenderTarget) { if (options & PixelFormatNormalizeOptionBit::NoThreeChannelRenderTarget) {
reasons.push_back("no colour-renderable three-channel format on OpenGL ES"); reasons.push_back("no color-renderable three-channel format on OpenGL ES");
} }
// A format is either 8- or 16-bit signed normalized, so at most one of the two ever // A format is either 8- or 16-bit signed normalized, so at most one of the two ever
// survives GetApplicablePixelFormatNormalizeOptions and the reason is not duplicated. // survives GetApplicablePixelFormatNormalizeOptions and the reason is not duplicated.
@@ -369,7 +369,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// did - is not merely untidy: the two texture pnames are ES 3.1 state, so on an ES 3.0 // did - is not merely untidy: the two texture pnames are ES 3.1 state, so on an ES 3.0
// context the loader's rejected-probe clamp leaves them at 1 (see the multisample clamps // context the loader's rejected-probe clamp leaves them at 1 (see the multisample clamps
// in the GLES loader) and the walk below would never run past one sample, recording {1} // in the GLES loader) and the walk below would never run past one sample, recording {1}
// for EVERY colour format while GL_MAX_SAMPLES - ES 3.0 core, so genuinely answered - // for EVERY color format while GL_MAX_SAMPLES - ES 3.0 core, so genuinely answered -
// reports 4. Once the frontend validates against this list, that would reject every // reports 4. Once the frontend validates against this list, that would reject every
// multisample renderbuffer on such a context. // multisample renderbuffer on such a context.
Int GetGLESRenderbufferFormatMaxSamples(const MG_External::GLESCapabilities& capabilities, GLenum imageFormat) { Int GetGLESRenderbufferFormatMaxSamples(const MG_External::GLESCapabilities& capabilities, GLenum imageFormat) {
@@ -707,7 +707,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
for (SizeT targetIndex = 0; targetIndex < kFormatCapabilityTextureTargetCount; ++targetIndex) { for (SizeT targetIndex = 0; targetIndex < kFormatCapabilityTextureTargetCount; ++targetIndex) {
const auto target = static_cast<TextureTarget>(targetIndex); const auto target = static_cast<TextureTarget>(targetIndex);
// Colour-attachable targets need a colour-renderable fallback; the ordinary // Color-attachable targets need a color-renderable fallback; the ordinary
// fallback for a three-channel format is another three-channel one, which ES // fallback for a three-channel format is another three-channel one, which ES
// accepts as a texture but never as an attachment. Recompute the fallback per // accepts as a texture but never as an attachment. Recompute the fallback per
// target so those formats get widened where the target demands it. // target so those formats get widened where the target demands it.
@@ -814,7 +814,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
const SizeT renderbufferTargetIndex = GetRenderbufferFormatCapabilityTargetIndex(); const SizeT renderbufferTargetIndex = GetRenderbufferFormatCapabilityTargetIndex();
// A renderbuffer exists only to be attached, so it needs the same three-channel // A renderbuffer exists only to be attached, so it needs the same three-channel
// widening the colour-attachable texture targets get - and on the same terms: the // widening the color-attachable texture targets get - and on the same terms: the
// native storage is probed first, so a driver that renders to it keeps it. // native storage is probed first, so a driver that renders to it keeps it.
const Flags<PixelFormatNormalizeOptionBit> renderbufferOptions = const Flags<PixelFormatNormalizeOptionBit> renderbufferOptions =
TextureImpl::GetRenderTargetNormalizeOptions(capabilities, renderbufferTargetIndex); TextureImpl::GetRenderTargetNormalizeOptions(capabilities, renderbufferTargetIndex);
@@ -1053,7 +1053,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// built). // built).
UpdateAdvertisedCapabilityExtensions(m_GLESCapabilities); UpdateAdvertisedCapabilityExtensions(m_GLESCapabilities);
UpdateDynamicBackendParameters(); UpdateDynamicBackendParameters();
// The colour-attachment cap the framebuffer family checks is known from here on. // The color-attachment cap the framebuffer family checks is known from here on.
DirectGLES::ResolveRecordArmFamilies(); DirectGLES::ResolveRecordArmFamilies();
PopulateFormatCapabilities(m_GLESFunctions, m_GLESCapabilities, MutableFormatCapabilities()); PopulateFormatCapabilities(m_GLESFunctions, m_GLESCapabilities, MutableFormatCapabilities());
PrintFormatCapabilities(GetFormatCapabilities()); PrintFormatCapabilities(GetFormatCapabilities());
@@ -1540,7 +1540,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
E_GL_ARB_texture_rg, E_GL_ARB_texture_rg,
// GL_DEPTH_COMPONENT32F and GL_DEPTH32F_STENCIL8. // GL_DEPTH_COMPONENT32F and GL_DEPTH32F_STENCIL8.
E_GL_ARB_depth_buffer_float, E_GL_ARB_depth_buffer_float,
// The floating-point colour formats. Unlike the rest of this block this string DOES // The floating-point color formats. Unlike the rest of this block this string DOES
// gate CTS cases - KHR-GL4*.internalformat.texture2d.*{16f,32f} is keyed on it with no // gate CTS cases - KHR-GL4*.internalformat.texture2d.*{16f,32f} is keyed on it with no
// core-version fallback, so eight cases per version list were NotSupported on formats // core-version fallback, so eight cases per version list were NotSupported on formats
// the backend has always had. // the backend has always had.
@@ -1557,7 +1557,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// - multiple capture buffers per stream, gl_SkipComponents / gl_NextBuffer, the // - multiple capture buffers per stream, gl_SkipComponents / gl_NextBuffer, the
// indexed queries and glDrawTransformFeedbackStream (4.0); // indexed queries and glDrawTransformFeedbackStream (4.0);
E_GL_ARB_transform_feedback3, E_GL_ARB_transform_feedback3,
// - GL_RGB10_A2UI (3.3), GL_RGB9_E5 (3.0) and the integer colour formats (3.0); // - GL_RGB10_A2UI (3.3), GL_RGB9_E5 (3.0) and the integer color formats (3.0);
E_GL_ARB_texture_rgb10_a2ui, E_GL_EXT_texture_shared_exponent, E_GL_EXT_texture_integer, E_GL_ARB_texture_rgb10_a2ui, E_GL_EXT_texture_shared_exponent, E_GL_EXT_texture_integer,
// - the eleven pipeline-statistics query targets (4.6). Accepted by glBeginQuery and // - the eleven pipeline-statistics query targets (4.6). Accepted by glBeginQuery and
// answered with GL_QUERY_COUNTER_BITS = 0, the spec's own "this counter is not // answered with GL_QUERY_COUNTER_BITS = 0, the spec's own "this counter is not
@@ -1609,7 +1609,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// recorded trace can never cover: Iris and Sodium change their SUBMISSION SCHEDULE // recorded trace can never cover: Iris and Sodium change their SUBMISSION SCHEDULE
// the moment they see it - they enqueue whole pipeline batches and poll // the moment they see it - they enqueue whole pipeline batches and poll
// GL_COMPLETION_STATUS_KHR instead of compiling one program at a time - so // GL_COMPLETION_STATUS_KHR instead of compiling one program at a time - so
// MOBILEGL_ASYNC_SHADER_COMPILE=0 has to withdraw the application-visible behaviour // MOBILEGL_ASYNC_SHADER_COMPILE=0 has to withdraw the application-visible behavior
// change as well as the threading, or the kill switch would only be half a switch. // change as well as the threading, or the kill switch would only be half a switch.
if (MG_Util::Async::AsyncShaderCompileEnabled()) { if (MG_Util::Async::AsyncShaderCompileEnabled()) {
extensions.push_back(E_GL_KHR_parallel_shader_compile); extensions.push_back(E_GL_KHR_parallel_shader_compile);
@@ -1619,7 +1619,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// reaches this backend - so an application that simply uses doubles needs nothing // reaches this backend - so an application that simply uses doubles needs nothing
// advertised. What the extension additionally promises is 64-bit PRECISION, which no // advertised. What the extension additionally promises is 64-bit PRECISION, which no
// mobile GPU has and the narrowing cannot fake, so advertising it by default would // mobile GPU has and the narrowing cannot fake, so advertising it by default would
// make an application that checks the string take a path MobileGL cannot honour. // make an application that checks the string take a path MobileGL cannot honor.
if (MG_Config::Features.AdvertiseFp64) { if (MG_Config::Features.AdvertiseFp64) {
extensions.push_back(E_GL_ARB_gpu_shader_fp64); extensions.push_back(E_GL_ARB_gpu_shader_fp64);
} }
@@ -1650,7 +1650,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// DEPTH_STENCIL_TEXTURE_MODE = STENCIL_INDEX and through a view with DEPTH_COMPONENT, in // DEPTH_STENCIL_TEXTURE_MODE = STENCIL_INDEX and through a view with DEPTH_COMPONENT, in
// a single shading pass). A copy-based fallback satisfies neither half, and fails // a single shading pass). A copy-based fallback satisfies neither half, and fails
// silently; withholding the string and answering glTextureView with INVALID_OPERATION is // silently; withholding the string and answering glTextureView with INVALID_OPERATION is
// the only behaviour that cannot be mistaken for success. // the only behavior that cannot be mistaken for success.
// //
// The host extension is necessary and NOT sufficient, which is why this second gate // The host extension is necessary and NOT sufficient, which is why this second gate
// exists. Adreno 830 has EXT_texture_view, and on it the whole functional half of // exists. Adreno 830 has EXT_texture_view, and on it the whole functional half of
@@ -1891,7 +1891,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
} }
// (MaxShaderStorageBufferBindings is assigned above, before the per-stage clamp reads it.) // (MaxShaderStorageBufferBindings is assigned above, before the per-stage clamp reads it.)
// This is the number glGetIntegerv(GL_MAX_TEXTURE_BUFFER_SIZE) hands the application, and // This is the number glGetIntegerv(GL_MAX_TEXTURE_BUFFER_SIZE) hands the application, and
// on a host without buffer textures it is knowingly a floor MobileGL cannot honour rather // on a host without buffer textures it is knowingly a floor MobileGL cannot honor rather
// than a driver answer (m_GLESCapabilities.MaxTextureBufferSizeIsDriverReported says // than a driver answer (m_GLESCapabilities.MaxTextureBufferSizeIsDriverReported says
// which). Reporting 0 instead was considered and rejected: MobileGL advertises an OpenGL // which). Reporting 0 instead was considered and rejected: MobileGL advertises an OpenGL
// 4.x context, where buffer textures are core and the limit has a spec minimum of 65536, // 4.x context, where buffer textures are core and the limit has a spec minimum of 65536,
@@ -2007,7 +2007,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// for gl_ViewportIndex on every driver without GL_OES_viewport_array - which is both test // for gl_ViewportIndex on every driver without GL_OES_viewport_array - which is both test
// devices. That is not a shortfall being hidden: without the extension only viewport 0 is // devices. That is not a shortfall being hidden: without the extension only viewport 0 is
// ever rasterized, so no vertex "selects" a viewport index and naming a convention would // ever rasterized, so no vertex "selects" a viewport index and naming a convention would
// describe behaviour this backend does not implement. // describe behavior this backend does not implement.
m_dynamicParameters.LayerProvokingVertex = m_GLESCapabilities.LayerProvokingVertex; m_dynamicParameters.LayerProvokingVertex = m_GLESCapabilities.LayerProvokingVertex;
m_dynamicParameters.ViewportIndexProvokingVertex = m_GLESCapabilities.ViewportIndexProvokingVertex; m_dynamicParameters.ViewportIndexProvokingVertex = m_GLESCapabilities.ViewportIndexProvokingVertex;
// GL_EXT_fragment_shading_rate is a straight pass-through: the pipeline rate and the // GL_EXT_fragment_shading_rate is a straight pass-through: the pipeline rate and the
@@ -20,7 +20,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// deliberate and it is not this phase's to change: ARCHITECTURE.md assigns the two // deliberate and it is not this phase's to change: ARCHITECTURE.md assigns the two
// backend-specific post-processings of the resolved sampler set - this one and Magma's // backend-specific post-processings of the resolved sampler set - this one and Magma's
// feedback-loop detection - to the server, acting ON the already-resolved set, and the // feedback-loop detection - to the server, acting ON the already-resolved set, and the
// ROADMAP puts "raw-depth-fetch sampler 原生化" in P3b/P4b beside Magma's placeholder // ROADMAP puts "native raw-depth-fetch sampler" in P3b/P4b beside Magma's placeholder
// textures. What P4a changes is only what the substitution READS: the resolved view's // textures. What P4a changes is only what the substitution READS: the resolved view's
// format answers IsDepthFormatInternalFormat and the sampler CSO record's parameters // format answers IsDepthFormatInternalFormat and the sampler CSO record's parameters
// answer compareMode / minFilter / mipmapMode / magFilter. // answer compareMode / minFilter / mipmapMode / magFilter.
+5 -5
View File
@@ -187,7 +187,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// glTexBuffer / glTexBufferRange through whichever spelling this driver's buffer-texture // glTexBuffer / glTexBufferRange through whichever spelling this driver's buffer-texture
// support actually ships: the unsuffixed names are ES 3.2 core, while an EXT/OES driver // support actually ships: the unsuffixed names are ES 3.2 core, while an EXT/OES driver
// exports glTexBuffer{,Range}EXT / OES. Callers must have checked // exports glTexBuffer{,Range}EXT / OES. Callers must have checked
// AreBufferTexturesSupported() first. CallTexBufferRange reports whether it could honour // AreBufferTexturesSupported() first. CallTexBufferRange reports whether it could honor
// the range - no tier is required to expose the range form, and the whole-buffer form is // the range - no tier is required to expose the range form, and the whole-buffer form is
// the documented fallback. // the documented fallback.
void CallTexBuffer(GLenum target, GLenum internalFormat, GLuint buffer); void CallTexBuffer(GLenum target, GLenum internalFormat, GLuint buffer);
@@ -448,7 +448,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// hold" query and no way to ask a context for its share group, so a case that must tell "the // hold" query and no way to ask a context for its share group, so a case that must tell "the
// same native context still serves" from "a fresh one was built", or a sharing pair from two // same native context still serves" from "a fresh one was built", or a sharing pair from two
// unrelated contexts, reads it here. These are pure reads plus one key override; neither is on // unrelated contexts, reads it here. These are pure reads plus one key override; neither is on
// a production path and neither changes behaviour. // a production path and neither changes behavior.
EGLContext NativeContextHandleFor(Uint64 sessionKey, Uint64 contextToken); EGLContext NativeContextHandleFor(Uint64 sessionKey, Uint64 contextToken);
// Overrides the key the calling thread resolves to, for the life of the call. The backend's // Overrides the key the calling thread resolves to, for the life of the call. The backend's
// ordinary answer comes from the server layer's probe; this is how a case drives exactly the // ordinary answer comes from the server layer's probe; this is how a case drives exactly the
@@ -484,8 +484,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
// Pushes the frontend's render-state block to the ES driver, diffed against what was // Pushes the frontend's render-state block to the ES driver, diffed against what was
// last pushed. // last pushed.
// //
// `forColorClear` names the CALLER, and the only thing it changes is the colour write // `forColorClear` names the CALLER, and the only thing it changes is the color write
// mask handed to the driver. A draw into a colour attachment the backend widened from // mask handed to the driver. A draw into a color attachment the backend widened from
// three channels to four gets that buffer's alpha channel masked OFF, so nothing can // three channels to four gets that buffer's alpha channel masked OFF, so nothing can
// move the stored alpha away from the 1.0 the application's three-channel format // move the stored alpha away from the 1.0 the application's three-channel format
// implies (see FramebufferImpl::g_alphaWidenedDrawBufferMask). A CLEAR is how that 1.0 // implies (see FramebufferImpl::g_alphaWidenedDrawBufferMask). A CLEAR is how that 1.0
@@ -494,7 +494,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// followed by a draw re-pushes the mask instead of early-outing on an unchanged // followed by a draw re-pushes the mask instead of early-outing on an unchanged
// frontend version. // frontend version.
// //
// The application's own colour mask is never modified: glGet(GL_COLOR_WRITEMASK) // The application's own color mask is never modified: glGet(GL_COLOR_WRITEMASK)
// answers from the frontend state, which this function only reads. // answers from the frontend state, which this function only reads.
void SyncRenderState(Bool forColorClear = false); void SyncRenderState(Bool forColorClear = false);
void InvalidateSyncedRenderState(); void InvalidateSyncedRenderState();
@@ -636,7 +636,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// BOUNDS-CHECKED, exactly as ProgramObject::GetAttribType is not: the frontend's // BOUNDS-CHECKED, exactly as ProgramObject::GetAttribType is not: the frontend's
// form indexes attribTypes raw because the mask it walked came off the same // form indexes attribTypes raw because the mask it walked came off the same
// artefacts instance, and here the two vectors arrived over a wire. A short tail // artifacts instance, and here the two vectors arrived over a wire. A short tail
// is an archive whose halves disagree; 0 classifies as Unsupported and says so // is an archive whose halves disagree; 0 classifies as Unsupported and says so
// below rather than reading past the end. // below rather than reading past the end.
const GLenum attribType = location < link.attribTypes.size() ? link.attribTypes[location] : 0; const GLenum attribType = location < link.attribTypes.size() ? link.attribTypes[location] : 0;
@@ -686,7 +686,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// declined to run it. // declined to run it.
// //
// P4a e3 (D-G4) leaves BOTH properties of this gate exactly as they are, and says so // P4a e3 (D-G4) leaves BOTH properties of this gate exactly as they are, and says so
// because they are the kind of thing a later optimisation deletes: // because they are the kind of thing a later optimization deletes:
// //
// 1. the g_imageUnitHighWaterMark == 0 early-out is what makes every Minecraft draw // 1. the g_imageUnitHighWaterMark == 0 early-out is what makes every Minecraft draw
// pay one integer test, and it is asked BEFORE anything reads a record; // pay one integer test, and it is asked BEFORE anything reads a record;
@@ -1710,7 +1710,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// The last sample a usampler2DMS fetch may name, from the texture // The last sample a usampler2DMS fetch may name, from the texture
// actually bound: a stencil texture's real count is not an integer // actually bound: a stencil texture's real count is not an integer
// colour texture's. Keyed like the bias above - a texture's storage or // color texture's. Keyed like the bias above - a texture's storage or
// a unit's binding moving is what invalidates this pass's memo. // a unit's binding moving is what invalidates this pass's memo.
if (samplerBinding.msSampleMaxLocation >= 0) { if (samplerBinding.msSampleMaxLocation >= 0) {
const Int sampleMax = const Int sampleMax =
@@ -107,12 +107,12 @@ namespace MobileGL::MG_Backend::DirectGLES {
FramebufferImpl::InvalidateSyncedFramebufferSerial(target); FramebufferImpl::InvalidateSyncedFramebufferSerial(target);
} }
// Sized internal format of the currently bound READ framebuffer's read colour // Sized internal format of the currently bound READ framebuffer's read color
// attachment, 0 when it cannot be determined. // attachment, 0 when it cannot be determined.
static GLenum QueryReadColorAttachmentInternalFormat() { static GLenum QueryReadColorAttachmentInternalFormat() {
GLint attachmentType = 0; GLint attachmentType = 0;
GLint attachmentName = 0; GLint attachmentName = 0;
// Ask the point the backend read buffer actually names, not COLOR_ATTACHMENT0. The colour map // Ask the point the backend read buffer actually names, not COLOR_ATTACHMENT0. The color map
// in BackendFramebufferObject is a permutation, so the read attachment's image only sits at // in BackendFramebufferObject is a permutation, so the read attachment's image only sits at
// CA0 when that map is identity; querying CA0 unconditionally would size the resolve // CA0 when that map is identity; querying CA0 unconditionally would size the resolve
// renderbuffer from a different attachment's format and either convert wrongly or fail the // renderbuffer from a different attachment's format and either convert wrongly or fail the
@@ -561,7 +561,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// The guard already left the pipeline neutral (nothing culled, tested, blended or // The guard already left the pipeline neutral (nothing culled, tested, blended or
// coverage-masked) on the borrowed texture unit; what is left is this pass's own // coverage-masked) on the borrowed texture unit; what is left is this pass's own
// choices - the destination rectangle, and colour writes off because it writes only // choices - the destination rectangle, and color writes off because it writes only
// depth and stencil. // depth and stencil.
VertexArrayImpl::BindBackendVAOId(s_vertexArray); VertexArrayImpl::BindBackendVAOId(s_vertexArray);
g_GLESFuncs.glBindTexture(GL_TEXTURE_2D, s_texture); g_GLESFuncs.glBindTexture(GL_TEXTURE_2D, s_texture);
@@ -626,7 +626,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// into a scratch framebuffer of the source's own native format, then run the caller's blit // into a scratch framebuffer of the source's own native format, then run the caller's blit
// - same mask, same filter, same orientation - from the scratch into the real destination. // - same mask, same filter, same orientation - from the scratch into the real destination.
// Overlapping rectangles stay undefined, as GL leaves them. A LINEAR blit that scales reads // Overlapping rectangles stay undefined, as GL leaves them. A LINEAR blit that scales reads
// clamped texels at the rectangle's edge where GL would read the neighbouring ones. // clamped texels at the rectangle's edge where GL would read the neighboring ones.
// What a framebuffer attachment point resolves to on the DRIVER side: the native object and, // What a framebuffer attachment point resolves to on the DRIVER side: the native object and,
// for a texture, the level, layer and cube face. A null name means the point is empty. // for a texture, the level, layer and cube face. A null name means the point is empty.
@@ -661,7 +661,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
a.Face == b.Face; a.Face == b.Face;
} }
// The colour attachment point the bound READ framebuffer reads from, GL_NONE when it reads // The color attachment point the bound READ framebuffer reads from, GL_NONE when it reads
// nothing. // nothing.
static GLenum DriverReadColorAttachment() { static GLenum DriverReadColorAttachment() {
GLint readBuffer = GL_NONE; GLint readBuffer = GL_NONE;
@@ -878,7 +878,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
g_GLESFuncs.glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &previousDraw); g_GLESFuncs.glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &previousDraw);
g_GLESFuncs.glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &previousRead); g_GLESFuncs.glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &previousRead);
// Every point is (re)attached on every call: the scratch is shared between colour and // Every point is (re)attached on every call: the scratch is shared between color and
// depth/stencil shapes, and a stale attachment would make it incomplete or blit an // depth/stencil shapes, and a stale attachment would make it incomplete or blit an
// aspect the source does not have. // aspect the source does not have.
g_GLESFuncs.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, s_scratchFramebuffer); g_GLESFuncs.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, s_scratchFramebuffer);
@@ -934,7 +934,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
return; return;
} }
// Two ES restrictions desktop GL does not have are worth a second attempt: a // Two ES restrictions desktop GL does not have are worth a second attempt: a
// multisample resolve that also converts colour format, and any blit into a // multisample resolve that also converts color format, and any blit into a
// multisample draw framebuffer. Both need to know how the two sides are sampled. // multisample draw framebuffer. Both need to know how the two sides are sampled.
GLint readSamples = 0; GLint readSamples = 0;
GLint drawSamples = 0; GLint drawSamples = 0;
@@ -968,21 +968,21 @@ namespace MobileGL::MG_Backend::DirectGLES {
if (ReplicateBlitIntoMultisampleDraw(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask)) { if (ReplicateBlitIntoMultisampleDraw(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask)) {
if ((mask & GL_COLOR_BUFFER_BIT) != 0) { if ((mask & GL_COLOR_BUFFER_BIT) != 0) {
MGLOG_E_ONCE( MGLOG_E_ONCE(
"BlitFramebuffer: colour replicate into a multisample draw framebuffer is not emulated"); "BlitFramebuffer: color replicate into a multisample draw framebuffer is not emulated");
} }
} }
return; return;
} }
// The combined call raised an error, so by GL 4.6 2.3.1 it wrote nothing at all: BOTH // The combined call raised an error, so by GL 4.6 2.3.1 it wrote nothing at all: BOTH
// aspect groups still owe their copy, and each has to be retried on its own. Re-issuing // aspect groups still owe their copy, and each has to be retried on its own. Re-issuing
// the depth/stencil half only as a rider on a SUCCESSFUL colour resolve dropped it // the depth/stencil half only as a rider on a SUCCESSFUL color resolve dropped it
// silently whenever the colour half could not be emulated - and on a framebuffer whose // silently whenever the color half could not be emulated - and on a framebuffer whose
// only attachment is depth it never can, because the colour emulation has no attachment // 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 // 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). // 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 colourBit = mask & static_cast<GLbitfield>(GL_COLOR_BUFFER_BIT);
const GLbitfield dsBits = mask & static_cast<GLbitfield>(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); const GLbitfield dsBits = mask & static_cast<GLbitfield>(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
// The colour group's one emulation is the multisample resolve that also converts format, // 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 // 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. // function, which is what cost a depth-only mask its single-aspect retry.
const Bool multisampleResolve = readSamples > 0 && drawSamples <= 0; const Bool multisampleResolve = readSamples > 0 && drawSamples <= 0;
@@ -993,7 +993,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
const Bool emulated = multisampleResolve && const Bool emulated = multisampleResolve &&
ResolveThenBlit(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, filter); ResolveThenBlit(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, filter);
if (!emulated) { if (!emulated) {
MGLOG_E_ONCE("BlitFramebuffer: the colour aspect was dropped - the driver rejected it on its " MGLOG_E_ONCE("BlitFramebuffer: the color aspect was dropped - the driver rejected it on its "
"own and no emulation applies"); "own and no emulation applies");
} }
} }
@@ -1002,7 +1002,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
DrainBlitErrors(); DrainBlitErrors();
g_GLESFuncs.glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, dsBits, filter); g_GLESFuncs.glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, dsBits, filter);
if (g_GLESFuncs.glGetError() != GL_NO_ERROR) { if (g_GLESFuncs.glGetError() != GL_NO_ERROR) {
// Nothing to fall back on yet: ResolveThenBlit is colour-only and the replicate // Nothing to fall back on yet: ResolveThenBlit is color-only and the replicate
// pass runs in the opposite direction, so a driver that declines a multisample // pass runs in the opposite direction, so a driver that declines a multisample
// depth/stencil resolve leaves the destination holding its clear value. The log // depth/stencil resolve leaves the destination holding its clear value. The log
// is the whole diagnostic - the frontend performs no validation of its own, so // is the whole diagnostic - the frontend performs no validation of its own, so
@@ -1283,7 +1283,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
}); });
MGLOG_D("ES %s(%d, %d, %d, %d, %d, %d, %d, %d, 0x%x, %s)", __func__, srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, MGLOG_D("ES %s(%d, %d, %d, %d, %d, %d, %d, %d, 0x%x, %s)", __func__, srcX0, srcY0, srcX1, srcY1, dstX0, dstY0,
dstX1, dstY1, mask, MG_Util::ConvertGLEnumToString(filter).c_str()); dstX1, dstY1, mask, MG_Util::ConvertGLEnumToString(filter).c_str());
// A no-op on every driver that honours a non-zero destination array layer, which is all // A no-op on every driver that honors a non-zero destination array layer, which is all
// of them but the probed one. Whatever it performs itself is taken out of the mask. // of them but the probed one. Whatever it performs itself is taken out of the mask.
// P5e (fb, §5.4): the bound arm's two binding-slot reads were the last GetBoundObject // P5e (fb, §5.4): the bound arm's two binding-slot reads were the last GetBoundObject
// pair on this path; under a transport the two bound RECORDS answer instead // pair on this path; under a transport the two bound RECORDS answer instead
@@ -378,7 +378,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// CountPrimitivesForDraw does not know and for the instanced/indirect entry // CountPrimitivesForDraw does not know and for the instanced/indirect entry
// points that never call it. // points that never call it.
MGLOG_E_ONCE("EndTransformFeedback: scattered capture discarded - packedStride=%zu, " MGLOG_E_ONCE("EndTransformFeedback: scattered capture discarded - packedStride=%zu, "
"CPU-modelled captured vertices=%zu, scratch capacity=%zu. The capture buffers keep " "CPU-modeled captured vertices=%zu, scratch capacity=%zu. The capture buffers keep "
"their pre-draw contents.", "their pre-draw contents.",
packedStride, modelledVertices, xfb.scatterCapacityVertices); packedStride, modelledVertices, xfb.scatterCapacityVertices);
return; return;
@@ -431,7 +431,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// (glBufferData(size, NULL) with no resource_subdata since), which // (glBufferData(size, NULL) with no resource_subdata since), which
// M-3 declares legal: every byte the application has not staged is // M-3 declares legal: every byte the application has not staged is
// UNDEFINED by its own declaration, and `staged` is already // UNDEFINED by its own declaration, and `staged` is already
// value-initialised to zero - which is byte for byte what the monolith // value-initialized to zero - which is byte for byte what the monolith
// arm scatters over when it reads a fresh MappedData(). Skipping the // arm scatters over when it reads a fresh MappedData(). Skipping the
// Memcpy is therefore the WHOLE of the difference; skipping the TARGET // Memcpy is therefore the WHOLE of the difference; skipping the TARGET
// threw the varyings away and left the application's buffer holding its // threw the varyings away and left the application's buffer holding its
@@ -669,7 +669,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// The frontend VAO is taken ONLY when the applier's own vertex-buffer window says a // The frontend VAO is taken ONLY when the applier's own vertex-buffer window says a
// client-sourced attribute exists, which is what retires the per-draw // client-sourced attribute exists, which is what retires the per-draw
// GetBoundVertexArray row of the ordinary (VBO-backed) DrawArrays - the strict lane's // GetBoundVertexArray row of the ordinary (VBO-backed) DrawArrays - the strict lane's
// `GetBoundVertexArray@DrawArrays` marker - without touching this path's behaviour. // `GetBoundVertexArray@DrawArrays` marker - without touching this path's behavior.
// `static` on purpose (G1): the pull build must not gain an exported name for a // `static` on purpose (G1): the pull build must not gain an exported name for a
// function that only re-homes two identical inline blocks the two DrawArrays entry // function that only re-homes two identical inline blocks the two DrawArrays entry
// points used to carry. The same spelling governs the three below, and for a second // points used to carry. The same spelling governs the three below, and for a second
@@ -343,14 +343,14 @@ namespace MobileGL::MG_Backend::DirectGLES {
// default framebuffer is answered out of that guess, and being wrong is not cosmetic: GL // default framebuffer is answered out of that guess, and being wrong is not cosmetic: GL
// blits depth/stencil only between IDENTICAL formats, so a caller that reads // blits depth/stencil only between IDENTICAL formats, so a caller that reads
// GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE, allocates the buffer it was told about and blits // GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE, allocates the buffer it was told about and blits
// gets GL_INVALID_OPERATION and a silently dropped blit - colour bits included, because a // gets GL_INVALID_OPERATION and a silently dropped blit - color bits included, because a
// rejected glBlitFramebuffer transfers nothing at all. DirectVulkan already publishes its // rejected glBlitFramebuffer transfers nothing at all. DirectVulkan already publishes its
// real format when it creates the swapchain (SwapchainObject::Create); this is the // real format when it creates the swapchain (SwapchainObject::Create); this is the
// DirectGLES half, and here the answer can simply be asked of the ES default framebuffer. // DirectGLES half, and here the answer can simply be asked of the ES default framebuffer.
// //
// Only the format is published. The placeholder's 512x512 extent is left alone: all three // Only the format is published. The placeholder's 512x512 extent is left alone: all three
// attachments share it, and FramebufferObject::CheckCompleteness requires them to agree, so // attachments share it, and FramebufferObject::CheckCompleteness requires them to agree, so
// resizing depth/stencil without colour would report the default framebuffer incomplete. // resizing depth/stencil without color would report the default framebuffer incomplete.
static void PublishDefaultFramebufferDepthStencilFormat() { static void PublishDefaultFramebufferDepthStencilFormat() {
auto& defaultFBOInfo = MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo; auto& defaultFBOInfo = MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo;
// P8-SE: THE PLACEHOLDER IS ONLY THE MONOLITH ARM'S TARGET. With a transport the answer is // P8-SE: THE PLACEHOLDER IS ONLY THE MONOLITH ARM'S TARGET. With a transport the answer is
@@ -1550,7 +1550,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
return false; return false;
} }
// Clearing the global owner works from ANY thread (a release request can // Clearing the global owner works from ANY thread (a release request can
// legally arrive on a thread other than the current owner); erring towards // legally arrive on a thread other than the current owner); erring toward
// "not current" only defers buffer ops, which is always safe. // "not current" only defers buffer ops, which is always safe.
InvalidateEglVerifiedStamp(); InvalidateEglVerifiedStamp();
t_native.boundContext = EGL_NO_CONTEXT; t_native.boundContext = EGL_NO_CONTEXT;
@@ -982,7 +982,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// P8-B2: RGB16F / RGB32F MIP GENERATION ON THE SERVER, FROM THE SERVER'S OWN LEVEL. // P8-B2: RGB16F / RGB32F MIP GENERATION ON THE SERVER, FROM THE SERVER'S OWN LEVEL.
// //
// ES generates a chain only for a colour-renderable, filterable format, and whether the // ES generates a chain only for a color-renderable, filterable format, and whether the
// driver's storage for these two is one depends on the driver: Espryt stores them natively // driver's storage for these two is one depends on the driver: Espryt stores them natively
// where the driver renders the three-channel form and widens them to RGBA16F / RGBA32F where // where the driver renders the three-channel form and widens them to RGBA16F / RGBA32F where
// it does not (TextureImpl::BackendTextureFormatAddsAlpha), and a widened RGBA32F still needs // it does not (TextureImpl::BackendTextureFormatAddsAlpha), and a widened RGBA32F still needs
@@ -1249,7 +1249,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
#if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG && MOBILEGL_ENABLE_SCOPE_MARKER #if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG && MOBILEGL_ENABLE_SCOPE_MARKER
DebugImpl::OpenGLScopeMarker marker(__func__); DebugImpl::OpenGLScopeMarker marker(__func__);
#endif #endif
// P5e (tx2), CONTRACT-P5E §5.2 / ruling 14 (ID-92's neighbour): GENERATE_MIPMAP RESOLVES // P5e (tx2), CONTRACT-P5E §5.2 / ruling 14 (ID-92's neighbor): GENERATE_MIPMAP RESOLVES
// ITS TEXTURE FROM VerbMipRes, NEVER FROM THE ACTIVE UNIT. // ITS TEXTURE FROM VerbMipRes, NEVER FROM THE ACTIVE UNIT.
// //
// The verb's own record names the texture (MGPMipPlan::Res, stashed as VerbMipRes) and // The verb's own record names the texture (MGPMipPlan::Res, stashed as VerbMipRes) and
@@ -1634,7 +1634,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// The frontend has already recorded the rebinding on the program object // The frontend has already recorded the rebinding on the program object
// (SetShaderStorageBlockBinding) - that record is what GL_BUFFER_BINDING reports and what // (SetShaderStorageBlockBinding) - that record is what GL_BUFFER_BINDING reports and what
// BackendProgramObjectImpl::SyncToBackend replays onto every driver program it builds. So // BackendProgramObjectImpl::SyncToBackend replays onto every driver program it builds. So
// the only work left here is an optimisation: push the change straight onto a driver // the only work left here is an optimization: push the change straight onto a driver
// program that is ALREADY built and already current with this link, so the next draw does // program that is ALREADY built and already current with this link, so the next draw does
// not have to be preceded by a rebuild. // not have to be preceded by a rebuild.
// //
@@ -1667,7 +1667,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
} }
namespace { namespace {
// A colour attachment the backend widened from three channels to four has to end up // A color attachment the backend widened from three channels to four has to end up
// holding alpha 1.0 - the value GL reports for a channel the application's format does // holding alpha 1.0 - the value GL reports for a channel the application's format does
// not have - so an explicit per-buffer clear of it writes 1.0 rather than whatever the // not have - so an explicit per-buffer clear of it writes 1.0 rather than whatever the
// application passed (SubstituteWidenedClearAlpha, in Managers.h). Draws can never move // application passed (SubstituteWidenedClearAlpha, in Managers.h). Draws can never move
@@ -59,7 +59,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// P5e (mv): THE ARM SELECTOR of this family - VertexInputReadsRecords() - moved to // P5e (mv): THE ARM SELECTOR of this family - VertexInputReadsRecords() - moved to
// Managers.h, unchanged. MultiDraw.cpp is a second translation unit on the same draw // Managers.h, unchanged. MultiDraw.cpp is a second translation unit on the same draw
// path and has to select the same arm; a copy of the conjunction there would be two // path and has to select the same arm; a copy of the conjunction there would be two
// selectors for one family. Its rationale travelled with it. // selectors for one family. Its rationale traveled with it.
// P5e (vi): "does this draw fetch any attribute out of the application's own memory", // P5e (vi): "does this draw fetch any attribute out of the application's own memory",
// answered FROM THE RECORD instead of from the frontend VAO. The emitter publishes // answered FROM THE RECORD instead of from the frontend VAO. The emitter publishes
@@ -238,7 +238,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// feedback records per emitted primitive, so a replayed draw would write its vertices N // feedback records per emitted primitive, so a replayed draw would write its vertices N
// times; rasterizer discard means there are no fragments to gate at all, so replaying // times; rasterizer discard means there are no fragments to gate at all, so replaying
// would be pure cost with nothing to show for it. Both fall back to a single pass with an // would be pure cost with nothing to show for it. Both fall back to a single pass with an
// open gate, i.e. to the pre-emulation behaviour, rather than to wrong data. // open gate, i.e. to the pre-emulation behavior, rather than to wrong data.
if (MG_Pipe::gPipeInputs->IsTransformFeedbackActive() || if (MG_Pipe::gPipeInputs->IsTransformFeedbackActive() ||
MG_Pipe::gPipeInputs->IsCapabilityEnabled(CapabilityInput::RasterizerDiscard)) { MG_Pipe::gPipeInputs->IsCapabilityEnabled(CapabilityInput::RasterizerDiscard)) {
return 1; return 1;
@@ -521,7 +521,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
#endif #endif
TextureImpl::SyncNeccessaryTextures(); TextureImpl::SyncNeccessaryTextures();
FramebufferImpl::SyncCurrentFBO(); FramebufferImpl::SyncCurrentFBO();
// A colour clear is exactly the operation that is allowed to write a widened // A color clear is exactly the operation that is allowed to write a widened
// attachment's alpha - it is what puts the 1.0 there that every later draw is masked // attachment's alpha - it is what puts the 1.0 there that every later draw is masked
// away from. SyncCurrentFBO ran first, so g_alphaWidenedDrawBufferMask already describes // away from. SyncCurrentFBO ran first, so g_alphaWidenedDrawBufferMask already describes
// the framebuffer this clear will land on. // the framebuffer this clear will land on.
@@ -565,17 +565,17 @@ namespace MobileGL::MG_Backend::DirectGLES {
const Bool outOfRange = cc.x() < 0.f || cc.x() > 1.f || cc.y() < 0.f || cc.y() > 1.f || cc.z() < 0.f || const Bool outOfRange = cc.x() < 0.f || cc.x() > 1.f || cc.y() < 0.f || cc.y() > 1.f || cc.z() < 0.f ||
cc.z() > 1.f || cc.w() < 0.f || cc.w() > 1.f; cc.z() > 1.f || cc.w() < 0.f || cc.w() > 1.f;
// A widened attachment's stored alpha has to end up 1.0, and glClear applies ONE // A widened attachment's stored alpha has to end up 1.0, and glClear applies ONE
// clear colour to every draw buffer - so a framebuffer that mixes a widened // clear color to every draw buffer - so a framebuffer that mixes a widened
// attachment with a native one cannot be served by doctoring glClearColor. Take the // attachment with a native one cannot be served by doctoring glClearColor. Take the
// same per-draw-buffer glClearBufferfv route the out-of-range case already uses and // same per-draw-buffer glClearBufferfv route the out-of-range case already uses and
// substitute the alpha only where it belongs. Scissor and the colour write mask apply // substitute the alpha only where it belongs. Scissor and the color write mask apply
// to glClearBufferfv exactly as they do to glClear, so a scissored clear stays // to glClearBufferfv exactly as they do to glClear, so a scissored clear stays
// scissored and an application that masked alpha off still gets its way (the storage // scissored and an application that masked alpha off still gets its way (the storage
// then keeps the 1.0 an earlier clear left, which is the same answer). // then keeps the 1.0 an earlier clear left, which is the same answer).
// //
// glClearBufferfv on an INTEGER colour buffer is GL_INVALID_OPERATION, so a // glClearBufferfv on an INTEGER color buffer is GL_INVALID_OPERATION, so a
// framebuffer with one of those as a draw buffer keeps plain glClear - which ES // framebuffer with one of those as a draw buffer keeps plain glClear - which ES
// leaves undefined for integer colour buffers anyway, and which an application that // leaves undefined for integer color buffers anyway, and which an application that
// wants a defined answer must replace with glClearBufferuiv/iv (those DO substitute // wants a defined answer must replace with glClearBufferuiv/iv (those DO substitute
// the widened alpha). The out-of-range trigger is left exactly as it was. // the widened alpha). The out-of-range trigger is left exactly as it was.
const Uint32 widenedDrawBuffers = FramebufferImpl::g_alphaWidenedDrawBufferMask; const Uint32 widenedDrawBuffers = FramebufferImpl::g_alphaWidenedDrawBufferMask;
@@ -611,9 +611,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
SamplerImpl::UnbindSampler(TextureImpl::TempTextureUnit); SamplerImpl::UnbindSampler(TextureImpl::TempTextureUnit);
// The neutral baseline every emulation pass wants: nothing culled, nothing // The neutral baseline every emulation pass wants: nothing culled, nothing
// clipped, nothing tested, no coverage games, and colour writes open. Callers // clipped, nothing tested, no coverage games, and color writes open. Callers
// turn back on only what they need (the replicate pass wants the depth and // turn back on only what they need (the replicate pass wants the depth and
// stencil tests, and masks colour off because it writes neither). // stencil tests, and masks color off because it writes neither).
for (Uint i = 0; i < m_capabilityCount; ++i) { for (Uint i = 0; i < m_capabilityCount; ++i) {
g_GLESFuncs.glDisable(m_capabilities[i].cap); g_GLESFuncs.glDisable(m_capabilities[i].cap);
} }
@@ -652,7 +652,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
g_GLESFuncs.glDisable(m_capabilities[i].cap); g_GLESFuncs.glDisable(m_capabilities[i].cap);
} }
} }
// The per-draw-buffer colour masks are not covered by the non-indexed // The per-draw-buffer color masks are not covered by the non-indexed
// glColorMask above. Restore what the SYNC actually pushed, not the raw // glColorMask above. Restore what the SYNC actually pushed, not the raw
// application masks: a widened attachment's alpha write is forced off by // application masks: a widened attachment's alpha write is forced off by
// SyncRenderState and memoized in g_syncedColorMaskAlphaWidenMask, and the // SyncRenderState and memoized in g_syncedColorMaskAlphaWidenMask, and the
@@ -886,7 +886,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
} }
// DirectGLESReadback.cpp defines it: E's extent-scoped stand-in for a // DirectGLESReadback.cpp defines it: E's extent-scoped stand-in for a
// driver that refuses to read a colour level back, asked here too so a host reaches the store // driver that refuses to read a color level back, asked here too so a host reaches the store
// route below (P8-SE). // route below (P8-SE).
Bool WireTextureReadbackRefusedForTesting(const IntVec3& extent); Bool WireTextureReadbackRefusedForTesting(const IntVec3& extent);
using FramebufferImpl::SubstituteWidenedClearAlpha; using FramebufferImpl::SubstituteWidenedClearAlpha;
@@ -111,10 +111,10 @@ namespace MobileGL::MG_Backend::DirectGLES {
// it on the way). ES rejects a depth/stencil blit between differing formats, so // it on the way). ES rejects a depth/stencil blit between differing formats, so
// the scratch has to match the source exactly; see DescribeReadDepthStencilSource. // the scratch has to match the source exactly; see DescribeReadDepthStencilSource.
// 2. Convert. Draw a full-screen triangle that samples the staged texture into a // 2. Convert. Draw a full-screen triangle that samples the staged texture into a
// scratch R32UI colour target: depth as floatBitsToUint (bit-exact for every depth // scratch R32UI color target: depth as floatBitsToUint (bit-exact for every depth
// format, and an integer colour target needs no float-renderable extension), // format, and an integer color target needs no float-renderable extension),
// stencil through GL_DEPTH_STENCIL_TEXTURE_MODE = GL_STENCIL_INDEX. // stencil through GL_DEPTH_STENCIL_TEXTURE_MODE = GL_STENCIL_INDEX.
// 3. Read. glReadPixels the colour target with GL_RGBA_INTEGER / GL_UNSIGNED_INT - // 3. Read. glReadPixels the color target with GL_RGBA_INTEGER / GL_UNSIGNED_INT -
// the pair ES guarantees for an unsigned-integer attachment - and hand the values // the pair ES guarantees for an unsigned-integer attachment - and hand the values
// to the existing re-encoders, which already own the client-side (format, type) // to the existing re-encoders, which already own the client-side (format, type)
// layout and the PACK pixel-store parameters. // layout and the PACK pixel-store parameters.
@@ -407,7 +407,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
if (s_colorTexture == 0) return false; if (s_colorTexture == 0) return false;
g_GLESFuncs.glBindTexture(GL_TEXTURE_2D, s_colorTexture); g_GLESFuncs.glBindTexture(GL_TEXTURE_2D, s_colorTexture);
ClearGLErrors(); ClearGLErrors();
// R32UI is colour-renderable in ES 3.0 core - no float-renderability extension // R32UI is color-renderable in ES 3.0 core - no float-renderability extension
// needed - and carries a depth bit pattern or a stencil index without loss. // needed - and carries a depth bit pattern or a stencil index without loss.
g_GLESFuncs.glTexStorage2D(GL_TEXTURE_2D, 1, GL_R32UI, s_colorWidth, s_colorHeight); g_GLESFuncs.glTexStorage2D(GL_TEXTURE_2D, 1, GL_R32UI, s_colorWidth, s_colorHeight);
const Bool ok = g_GLESFuncs.glGetError() == GL_NO_ERROR; const Bool ok = g_GLESFuncs.glGetError() == GL_NO_ERROR;
@@ -458,7 +458,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
return false; return false;
} }
// Runs one conversion pass over a staged slot and reads its colour target back. // Runs one conversion pass over a staged slot and reads its color target back.
// Requires the slot's texture bound on the borrowed unit and s_colorFramebuffer // Requires the slot's texture bound on the borrowed unit and s_colorFramebuffer
// bound as DRAW. // bound as DRAW.
static Bool ConvertAndRead(const StageSlot& slot, Bool stencilAspect, GLsizei width, GLsizei height, static Bool ConvertAndRead(const StageSlot& slot, Bool stencilAspect, GLsizei width, GLsizei height,
@@ -625,7 +625,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// Neither native spelling exists on this driver (which is the ordinary case: ES has // Neither native spelling exists on this driver (which is the ordinary case: ES has
// no depth readback in core and GL_NV_read_depth is rare), so sample the attachment // no depth readback in core and GL_NV_read_depth is rare), so sample the attachment
// instead. The scoped pack state above is irrelevant to that path - it reads its own // instead. The scoped pack state above is irrelevant to that path - it reads its own
// scratch colour target - but harmless, and leaving it in place keeps the restore in // scratch color target - but harmless, and leaving it in place keeps the restore in
// one place. // one place.
if (DepthStencilSamplingReadImpl::Read(x, y, width, height, &outDepth, /*outStencil=*/nullptr)) { if (DepthStencilSamplingReadImpl::Read(x, y, width, height, &outDepth, /*outStencil=*/nullptr)) {
return true; return true;
@@ -636,7 +636,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
return false; return false;
} }
// GL_DEPTH_COMPONENT readback into the client's layout, honouring the PACK pixel-store // GL_DEPTH_COMPONENT readback into the client's layout, honoring the PACK pixel-store
// parameters. GL 4.6 core 18.2.8 table 8.6: the normalized depth is written as-is for the // parameters. GL 4.6 core 18.2.8 table 8.6: the normalized depth is written as-is for the
// floating-point client types and scaled into the full range of whichever integer width the // floating-point client types and scaled into the full range of whichever integer width the
// client asked for otherwise - 2^n - 1 for an unsigned width, 2^(n-1) - 1 for a signed one. // client asked for otherwise - 2^n - 1 for an unsigned width, 2^(n-1) - 1 for a signed one.
@@ -764,7 +764,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
return false; return false;
} }
// GL_STENCIL_INDEX readback into the client's integer layout, honouring the PACK // GL_STENCIL_INDEX readback into the client's integer layout, honoring the PACK
// pixel-store parameters. Handles the widths desktop clients ask for; the stencil // pixel-store parameters. Handles the widths desktop clients ask for; the stencil
// values themselves are always 8 bits. // values themselves are always 8 bits.
static Bool ReadPixelsStencilViaNative(GLint x, GLint y, GLsizei width, GLsizei height, GLenum type, void* pixels) { static Bool ReadPixelsStencilViaNative(GLint x, GLint y, GLsizei width, GLsizei height, GLenum type, void* pixels) {
@@ -964,7 +964,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
} }
// Overwrites the alpha of a 4-channel wide read with the format's implied 1.0. Used for an // Overwrites the alpha of a 4-channel wide read with the format's implied 1.0. Used for an
// attachment the backend widened from three channels to keep it colour-renderable: the storage // attachment the backend widened from three channels to keep it color-renderable: the storage
// has a real alpha channel holding whatever the draw wrote, but the format the application // has a real alpha channel holding whatever the draw wrote, but the format the application
// asked for has none, and GL reads a missing channel back as one. // asked for has none, and GL reads a missing channel back as one.
static void ForceWideReadAlphaToOne(Vector<Uint8>& data, SizeT pixelCount, GLenum componentType) { static void ForceWideReadAlphaToOne(Vector<Uint8>& data, SizeT pixelCount, GLenum componentType) {
@@ -1033,7 +1033,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// (format, type) layout. Returns false when the combination is not convertible (the caller keeps its // (format, type) layout. Returns false when the combination is not convertible (the caller keeps its
// "not implemented" skip); returns true when the request was handled, even if it degraded to a logged no-op. // "not implemented" skip); returns true when the request was handled, even if it degraded to a logged no-op.
// `forceOpaqueAlpha`: the source image is a three-channel format the backend widened to four to // `forceOpaqueAlpha`: the source image is a three-channel format the backend widened to four to
// keep it colour-renderable, so its alpha channel holds whatever the draw wrote and has to be // keep it color-renderable, so its alpha channel holds whatever the draw wrote and has to be
// answered with the 1.0 the application's format implies. Passed in rather than derived here: // answered with the 1.0 the application's format implies. Passed in rather than derived here:
// glReadPixels reads the bound READ framebuffer, but glGetTexImage reads a texture through a // glReadPixels reads the bound READ framebuffer, but glGetTexImage reads a texture through a
// scratch framebuffer, so the frontend's READ binding describes a different image entirely - // scratch framebuffer, so the frontend's READ binding describes a different image entirely -
@@ -1230,7 +1230,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
ForceWideReadAlphaToOne(wide, static_cast<SizeT>(width) * static_cast<SizeT>(height), wideType); ForceWideReadAlphaToOne(wide, static_cast<SizeT>(width) * static_cast<SizeT>(height), wideType);
} }
// GL clamps a read from a fixed-point colour buffer to [0,1] (GL_CLAMP_READ_COLOR // GL clamps a read from a fixed-point color buffer to [0,1] (GL_CLAMP_READ_COLOR
// defaults to GL_FIXED_ONLY). Formats the backend substitutes with a floating-point // defaults to GL_FIXED_ONLY). Formats the backend substitutes with a floating-point
// one keep the out-of-range value the app stored, so apply the clamp here - a // one keep the out-of-range value the app stored, so apply the clamp here - a
// GL_R16_SNORM target holding -0.125 must still read back as 0. // GL_R16_SNORM target holding -0.125 must still read back as 0.
@@ -1283,13 +1283,13 @@ namespace MobileGL::MG_Backend::DirectGLES {
return true; return true;
} }
// ---- Bit-exact readback of a 32-bit packed colour level --------------------------------------- // ---- Bit-exact readback of a 32-bit packed color level ---------------------------------------
// //
// glGetTexImage of a packed format read with its OWN client type owes the application the words // glGetTexImage of a packed format read with its OWN client type owes the application the words
// the image HOLDS, and neither of the two routes above can promise that once anything other than // the image HOLDS, and neither of the two routes above can promise that once anything other than
// a glTexImage has written the level: // a glTexImage has written the level:
// //
// * the colour-attachment route reads GL_RGBA/GL_FLOAT and re-encodes, which canonicalizes an // * the color-attachment route reads GL_RGBA/GL_FLOAT and re-encodes, which canonicalizes an
// RGB9_E5 shared exponent (0xf8fc0000 -> 0xe7e00000, same value, different bits) and // RGB9_E5 shared exponent (0xf8fc0000 -> 0xe7e00000, same value, different bits) and
// collapses an R11F_G11F_B10F NaN to the canonical payload 1 // collapses an R11F_G11F_B10F NaN to the canonical payload 1
// (MG_Util::EncodeFloatToUnsignedSmallFloat) - and a copy-image from RGB9_E5 lands exactly // (MG_Util::EncodeFloatToUnsignedSmallFloat) - and a copy-image from RGB9_E5 lands exactly
@@ -1301,7 +1301,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// source (which has no shadow to mirror from at all), a cube or 1D-array endpoint, a // source (which has no shadow to mirror from at all), a cube or 1D-array endpoint, a
// self-copy - and the decline is silent, so the stale words are served as truth. // self-copy - and the decline is silent, so the stale words are served as truth.
// //
// glCopyImageSubData is a raw texel-block move and EXT_copy_image puts every 32-bit colour // glCopyImageSubData is a raw texel-block move and EXT_copy_image puts every 32-bit color
// format in one compatibility class, so copying the level into a scratch GL_R32UI image and // format in one compatibility class, so copying the level into a scratch GL_R32UI image and
// reading THAT back as unsigned integers hands over the stored words themselves, whoever wrote // reading THAT back as unsigned integers hands over the stored words themselves, whoever wrote
// them. This is what lets the shadow stop being the authority for these formats: it is tried // them. This is what lets the shadow stop being the authority for these formats: it is tried
@@ -1387,7 +1387,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
} }
// GL_RGBA_INTEGER/GL_UNSIGNED_INT is the one combination ES guarantees for an integer // GL_RGBA_INTEGER/GL_UNSIGNED_INT is the one combination ES guarantees for an integer
// colour buffer, so the read lands four words per texel and the red one is compacted out // color buffer, so the read lands four words per texel and the red one is compacted out
// here. The PACK scope is the tight default rather than the application's, so a row comes // here. The PACK scope is the tight default rather than the application's, so a row comes
// back packed at exactly `width * 4` words. One glGetError covers the whole loop: it // back packed at exactly `width * 4` words. One glGetError covers the whole loop: it
// accumulates, and a failure anywhere means the caller falls back rather than trusting a // accumulates, and a failure anywhere means the caller falls back rather than trusting a
@@ -1796,7 +1796,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
} }
// P8-E. MGITEST_ESPRYT_REFUSE_TEXTURE_READBACK_EXTENT=<w>x<h>, READ BY THE SERVER: // P8-E. MGITEST_ESPRYT_REFUSE_TEXTURE_READBACK_EXTENT=<w>x<h>, READ BY THE SERVER:
// a colour level of exactly that extent is treated as one the driver refused to attach - what an // a color level of exactly that extent is treated as one the driver refused to attach - what an
// Adreno does for RGB16 and the 16-bit SNORM formats, and what no host driver can be made to do. // Adreno does for RGB16 and the 16-bit SNORM formats, and what no host driver can be made to do.
// Scoped by the extent so the tcp lane's shared supervisor can carry it for the whole lane: only // Scoped by the extent so the tcp lane's shared supervisor can carry it for the whole lane: only
// the scenario that builds that extent (CopyImageStoreReadbackScenario) ever meets it. // the scenario that builds that extent (CopyImageStoreReadbackScenario) ever meets it.
@@ -1954,7 +1954,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
} }
// The (format, type) pairs the depth/stencil helpers below can service. They are not // The (format, type) pairs the depth/stencil helpers below can service. They are not
// covered by the colour tables above - GetReadbackChannelMapping has no entry for any // covered by the color tables above - GetReadbackChannelMapping has no entry for any
// depth or stencil format, so without this gate a read the helpers CAN serve (a // depth or stencil format, so without this gate a read the helpers CAN serve (a
// GL_UNSIGNED_SHORT depth, a GL_SHORT stencil) is turned away before it reaches them. // GL_UNSIGNED_SHORT depth, a GL_SHORT stencil) is turned away before it reaches them.
// The signed widths and GL_HALF_FLOAT belong in it for the same reason: the helpers scale a // The signed widths and GL_HALF_FLOAT belong in it for the same reason: the helpers scale a
@@ -2025,7 +2025,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// The read buffer is what glReadPixels reads, so the frontend's READ binding is exactly // The read buffer is what glReadPixels reads, so the frontend's READ binding is exactly
// the right thing to ask here. // the right thing to ask here.
const Bool forceOpaqueAlpha = FramebufferImpl::IsAlphaWidenedFallbackReadAttachment(); const Bool forceOpaqueAlpha = FramebufferImpl::IsAlphaWidenedFallbackReadAttachment();
// An attachment widened from three channels to stay colour-renderable also has to leave // An attachment widened from three channels to stay color-renderable also has to leave
// the fast pair: only the conversion path knows to answer its alpha with the 1.0 the // the fast pair: only the conversion path knows to answer its alpha with the 1.0 the
// application's format implies instead of whatever the draw wrote into the added channel. // application's format implies instead of whatever the draw wrote into the added channel.
const Bool nativeFastPair = !packSwapBytes && !forceOpaqueAlpha && const Bool nativeFastPair = !packSwapBytes && !forceOpaqueAlpha &&
@@ -56,7 +56,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
DupNativeFenceFdFn DupNativeFenceFd = nullptr; DupNativeFenceFdFn DupNativeFenceFd = nullptr;
// YUV images: sampled as external textures (GL_OES_EGL_image_external_essl3), and // YUV images: sampled as external textures (GL_OES_EGL_image_external_essl3), and
// through GL_EXT_YUV_target's raw Y'CbCr sampler where the driver has it, so the // through GL_EXT_YUV_target's raw Y'CbCr sampler where the driver has it, so the
// conversion is the import's own (its colour hints) rather than the driver's guess. // conversion is the import's own (its color hints) rather than the driver's guess.
Bool ExternalEssl3 = false; Bool ExternalEssl3 = false;
Bool YuvTarget = false; Bool YuvTarget = false;
}; };
@@ -573,7 +573,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
conversion.TargetOffset = g_GLESFuncs.glGetUniformLocation(program, "uOffset"); conversion.TargetOffset = g_GLESFuncs.glGetUniformLocation(program, "uOffset");
} }
MGLOG_I("Shared images: YUV images convert through %s", MGLOG_I("Shared images: YUV images convert through %s",
ownConversion ? "GL_EXT_YUV_target with the import's colour hints" ownConversion ? "GL_EXT_YUV_target with the import's color hints"
: "the driver's external-sampler conversion"); : "the driver's external-sampler conversion");
} }
if (conversion.Framebuffer == 0) g_GLESFuncs.glGenFramebuffers(1, &conversion.Framebuffer); if (conversion.Framebuffer == 0) g_GLESFuncs.glGenFramebuffers(1, &conversion.Framebuffer);
@@ -30,7 +30,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// //
// The cache stays on the LEGACY arm, gated on the same subsystem bit as the rest of this // The cache stays on the LEGACY arm, gated on the same subsystem bit as the rest of this
// slice, so that MOBILEGL_PIPE_PUSH=0 keeps being the faithful all-subsystems-pull control // slice, so that MOBILEGL_PIPE_PUSH=0 keeps being the faithful all-subsystems-pull control
// ConfigLoader.cpp documents - "reproduces P1's behaviour exactly" has to include this // ConfigLoader.cpp documents - "reproduces P1's behavior exactly" has to include this
// path, or the integrator's A/B measures e4 on both arms and attributes it to neither. In // path, or the integrator's A/B measures e4 on both arms and attributes it to neither. In
// the pull build gPipeInputs is the live GLContext, there is no poison to bypass and the // the pull build gPipeInputs is the live GLContext, there is no poison to bypass and the
// frontend getter still linear-scans, so the cache is exactly the code it was. // frontend getter still linear-scans, so the cache is exactly the code it was.
@@ -339,9 +339,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
// direct_state_access.renderbuffers_storage. One unconditional push settles the whole // direct_state_access.renderbuffers_storage. One unconditional push settles the whole
// block rather than the one cap that happened to be noticed. // block rather than the one cap that happened to be noticed.
static Bool g_forceFullRenderStateResync = true; static Bool g_forceFullRenderStateResync = true;
// Which draw buffers' alpha channel the colour mask last pushed to the driver had forced // Which draw buffers' alpha channel the color mask last pushed to the driver had forced
// OFF - i.e. the value of `appliedWidenMask` in the last SyncRenderState that reached the // OFF - i.e. the value of `appliedWidenMask` in the last SyncRenderState that reached the
// colour-mask block. NOT derivable from the frontend parameter block: it depends on the // color-mask block. NOT derivable from the frontend parameter block: it depends on the
// bound DRAW framebuffer's attachment formats and on whether the caller is a draw or a // bound DRAW framebuffer's attachment formats and on whether the caller is a draw or a
// clear, neither of which bumps the frontend render-state version. Without it a // clear, neither of which bumps the frontend render-state version. Without it a
// clear-then-draw pair on an unchanged parameter block early-outs and the draw inherits // clear-then-draw pair on an unchanged parameter block early-outs and the draw inherits
@@ -405,7 +405,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
Uint16 currentRenderStateVersion = MG_Pipe::gPipeInputs->GetRenderStateParametersVersion(); Uint16 currentRenderStateVersion = MG_Pipe::gPipeInputs->GetRenderStateParametersVersion();
const Bool forceFullPush = g_forceFullRenderStateResync; const Bool forceFullPush = g_forceFullRenderStateResync;
g_forceFullRenderStateResync = false; g_forceFullRenderStateResync = false;
// The alpha discipline for widened colour attachments (see the header comment on // The alpha discipline for widened color attachments (see the header comment on
// SyncRenderState): a DRAW must not be able to move the stored alpha off 1.0, a CLEAR // SyncRenderState): a DRAW must not be able to move the stored alpha off 1.0, a CLEAR
// is what puts it there. So the draw path masks alpha off on every widened draw // is what puts it there. So the draw path masks alpha off on every widened draw
// buffer and the clear path masks nothing. // buffer and the clear path masks nothing.
@@ -539,7 +539,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
{ // sRGB framebuffer writes. GLES core always encodes a write into an sRGB attachment, { // sRGB framebuffer writes. GLES core always encodes a write into an sRGB attachment,
// while GL_FRAMEBUFFER_SRGB is disabled by default in desktop GL and the frontend // while GL_FRAMEBUFFER_SRGB is disabled by default in desktop GL and the frontend
// never turns it on, so the driver has to be told to write raw. Without this a render // never turns it on, so the driver has to be told to write raw. Without this a render
// into an sRGB colour buffer comes back encoded once too often (the shader's own // into an sRGB color buffer comes back encoded once too often (the shader's own
// decode on the next fetch then leaves the value one conversion short). // decode on the next fetch then leaves the value one conversion short).
const Bool srgbWrites = MG_Pipe::gPipeInputs->IsCapabilityEnabled(CapabilityInput::FramebufferSrgb); const Bool srgbWrites = MG_Pipe::gPipeInputs->IsCapabilityEnabled(CapabilityInput::FramebufferSrgb);
if (g_GLESCapabilities.SupportsSrgbWriteControl && if (g_GLESCapabilities.SupportsSrgbWriteControl &&
@@ -853,7 +853,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// framebuffer, so a widened draw buffer 0 costs every other buffer its // framebuffer, so a widened draw buffer 0 costs every other buffer its
// alpha writes. ES 3.2 makes glColorMaski core and ES 3.1 has it as // alpha writes. ES 3.2 makes glColorMaski core and ES 3.1 has it as
// EXT/OES; the only devices that reach this line are ES 3.0-class, where // EXT/OES; the only devices that reach this line are ES 3.0-class, where
// MRT with a mixed widened/native colour attachment set is already rare. // MRT with a mixed widened/native color attachment set is already rare.
const BoolVec4& m = driverMask0; const BoolVec4& m = driverMask0;
g_GLESFuncs.glColorMask(ToGLBoolean(m.x()), ToGLBoolean(m.y()), ToGLBoolean(m.z()), g_GLESFuncs.glColorMask(ToGLBoolean(m.x()), ToGLBoolean(m.y()), ToGLBoolean(m.z()),
ToGLBoolean(m.w())); ToGLBoolean(m.w()));
@@ -601,7 +601,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// spelling the driver provides (Adreno exposes only eglSwapBuffersWithDamageKHR), so // spelling the driver provides (Adreno exposes only eglSwapBuffersWithDamageKHR), so
// the null check below is the whole chain: a driver with neither presents with plain // the null check below is the whole chain: a driver with neither presents with plain
// eglSwapBuffers. Passing n_rects == 0 is not a partial present - the extension // eglSwapBuffers. Passing n_rects == 0 is not a partial present - the extension
// defines it as "the entire surface is implicitly damaged and the behaviour is // defines it as "the entire surface is implicitly damaged and the behavior is
// equivalent to calling eglSwapBuffers" - and the wire protocol carries no damage // equivalent to calling eglSwapBuffers" - and the wire protocol carries no damage
// rectangles for the server to forward, so the full surface is what is meant anyway. // rectangles for the server to forward, so the full surface is what is meant anyway.
// //
@@ -35,7 +35,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// two never-synced textures as equal. The identity has to exist before the twin does. // two never-synced textures as equal. The identity has to exist before the twin does.
// //
// Split by the RUNTIME arm, not by the build, for exactly the reason g_fbSlotCache is: // Split by the RUNTIME arm, not by the build, for exactly the reason g_fbSlotCache is:
// MOBILEGL_PIPE_PUSH=0 has to reproduce P1's behaviour (ConfigLoader.cpp), and a // MOBILEGL_PIPE_PUSH=0 has to reproduce P1's behavior (ConfigLoader.cpp), and a
// legacy-arm run that debounced on lifetime ids would be running P2's mechanism while // legacy-arm run that debounced on lifetime ids would be running P2's mechanism while
// the A/B attributed the result to P1. The two answers are equivalent - OwnerEquals on // the A/B attributed the result to P1. The two answers are equivalent - OwnerEquals on
// two empty pointers is true and LifetimeIdOf(nullptr) == 0 == 0; a live-versus-expired // two empty pointers is true and LifetimeIdOf(nullptr) == 0 == 0; a live-versus-expired
@@ -454,7 +454,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// //
// A texture that is an attachment of the READ framebuffer ONLY reaches // A texture that is an attachment of the READ framebuffer ONLY reaches
// SyncAttachmentObject, which calls SyncMipmapsToBackend AND NOTHING ELSE - so its // SyncAttachmentObject, which calls SyncMipmapsToBackend AND NOTHING ELSE - so its
// swizzle, its GL_DEPTH_STENCIL_TEXTURE_MODE, its LOD clamp and its border colour // swizzle, its GL_DEPTH_STENCIL_TEXTURE_MODE, its LOD clamp and its border color
// never reach the driver. The list that pushes texture PARAMETERS for an attachment is // never reach the driver. The list that pushes texture PARAMETERS for an attachment is
// the one below, and it reads the DRAW binding slot alone. That is the gap // the one below, and it reads the DRAW binding slot alone. That is the gap
// ARCHITECTURE.md's D10 names, and the roadmap makes closing it a mandatory, // ARCHITECTURE.md's D10 names, and the roadmap makes closing it a mandatory,
@@ -464,7 +464,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// //
// DELIBERATELY NOT GATED ON THE FRAMEBUFFER SUBSYSTEM BIT. It is a deliverable of the // DELIBERATELY NOT GATED ON THE FRAMEBUFFER SUBSYSTEM BIT. It is a deliverable of the
// phase rather than an arm of it, so both arms of the object A/B carry it and the A/B // phase rather than an arm of it, so both arms of the object A/B carry it and the A/B
// keeps measuring the arm rather than the arm plus a behaviour change. It is inside // keeps measuring the arm rather than the arm plus a behavior change. It is inside
// MOBILEGL_PIPE_PUSH, so the pull build is untouched. // MOBILEGL_PIPE_PUSH, so the pull build is untouched.
// //
// Its own list and its own key rather than an extension of the draw list's, because // Its own list and its own key rather than an extension of the draw list's, because
@@ -562,7 +562,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// and the alias contest between two real textures on one native target was arbitrated // and the alias contest between two real textures on one native target was arbitrated
// by the program's own sampler types. So the window IS the work list. // by the program's own sampler types. So the window IS the work list.
// //
// NAMED BEHAVIOUR DELTA (P5e-5, contract §5.2): this syncs the PROGRAM-RESOLVED // NAMED BEHAVIOR DELTA (P5e-5, contract §5.2): this syncs the PROGRAM-RESOLVED
// texture per unit, where the frontend walk syncs every slot of every touched unit. // texture per unit, where the frontend walk syncs every slot of every touched unit.
// Coverage is unchanged - views UNION image units UNION the FBO attachment lists // Coverage is unchanged - views UNION image units UNION the FBO attachment lists
// UNION the waited texture ops still cover every texture anything reads - so it // UNION the waited texture ops still cover every texture anything reads - so it
+5 -5
View File
@@ -212,7 +212,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// InProcessTeardown() - touches neither the driver nor another TU's statics. // InProcessTeardown() - touches neither the driver nor another TU's statics.
// //
// P14 S6: "EVERY TABLE" IS NOW "EVERY BUCKET OF THIS SESSION". The tables are keyed by // P14 S6: "EVERY TABLE" IS NOW "EVERY BUCKET OF THIS SESSION". The tables are keyed by
// {session, share group}, so the same rebuild of the eight registries would take a NEIGHBOUR // {session, share group}, so the same rebuild of the eight registries would take a NEIGHBOR
// session's twins with it - and with S2's in-process supervisor serving several sessions at // session's twins with it - and with S2's in-process supervisor serving several sessions at
// once that is not a hypothetical: this runs from ~BackendObject_DirectGLES, on the apply // once that is not a hypothetical: this runs from ~BackendObject_DirectGLES, on the apply
// thread of the session that is ending. So the drop is scoped to the calling thread's // thread of the session that is ending. So the drop is scoped to the calling thread's
@@ -533,7 +533,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// Fragment stage. Returns false when the stage has no entry point to gate onto, which the // Fragment stage. Returns false when the stage has no entry point to gate onto, which the
// caller reports: the program still links and still renders, it just renders every index // caller reports: the program still links and still renders, it just renders every index
// with the first replay pass's state - i.e. it degrades to the pre-emulation behaviour // with the first replay pass's state - i.e. it degrades to the pre-emulation behavior
// rather than to a black screen. // rather than to a black screen.
Bool InjectViewportIndexPassGate(String& source) { Bool InjectViewportIndexPassGate(String& source) {
// Built beside the input and swapped in only on success, so a stage this pass declines // Built beside the input and swapped in only on success, so a stage this pass declines
@@ -879,7 +879,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
"MGPFramebufferState::Color[%u]'s wire width", "MGPFramebufferState::Color[%u]'s wire width",
cap, MG_Pipe::kMGPipeMaxColorAttachments); cap, MG_Pipe::kMGPipeMaxColorAttachments);
BufferImpl::StopOnArmlessPipeSubsystem( BufferImpl::StopOnArmlessPipeSubsystem(
"kMGPipeSubsystemFramebuffer (bit 9) cannot carry this driver's colour attachments, and " "kMGPipeSubsystemFramebuffer (bit 9) cannot carry this driver's color attachments, and "
"the record arm is the only arm since P13 W6"); "the record arm is the only arm since P13 W6");
} }
} }
@@ -914,7 +914,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
if (!TextureImpl::IsMultisampleTextureTarget(target)) return 0; if (!TextureImpl::IsMultisampleTextureTarget(target)) return 0;
// The same three inputs the storage allocation clamps with (SyncToBackend's multisample // The same three inputs the storage allocation clamps with (SyncToBackend's multisample
// arm): the record's format and requested count, and the format's ES transfer format, // arm): the record's format and requested count, and the format's ES transfer format,
// which only decides the integer-vs-colour fallback when the probe has no answer. // which only decides the integer-vs-color fallback when the probe has no answer.
const auto internalFormat = static_cast<TextureInternalFormat>(record->Desc.InternalFormat); const auto internalFormat = static_cast<TextureInternalFormat>(record->Desc.InternalFormat);
GLenum glInternalFormat = 0, glFormat = 0, glType = 0; GLenum glInternalFormat = 0, glFormat = 0, glType = 0;
TextureImpl::GenerateTextureFormatInfo(internalFormat, &glInternalFormat, &glFormat, &glType, target); TextureImpl::GenerateTextureFormatInfo(internalFormat, &glInternalFormat, &glFormat, &glType, target);
@@ -931,7 +931,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
} }
// N-7: D KEYS ON THE UPLOAD HALF; WIRE (AccumulatePendingUpload, PipeApply.cpp) KEYS ON // N-7: D KEYS ON THE UPLOAD HALF; WIRE (AccumulatePendingUpload, PipeApply.cpp) KEYS ON
// THE WHOLE PACKED FIELD. That asymmetry is resolved HERE, in D's favour and with the // THE WHOLE PACKED FIELD. That asymmetry is resolved HERE, in D's favor and with the
// reason, rather than by widening the two comparisons: D's question is per (uploadTarget, // reason, rather than by widening the two comparisons: D's question is per (uploadTarget,
// level) because that is the granularity its upload loop iterates - it is handed a // level) because that is the granularity its upload loop iterates - it is handed a
// TextureUploadTarget and a level and has no second key to offer - while wire's is per // TextureUploadTarget and a level and has no second key to offer - while wire's is per
+20 -20
View File
@@ -198,7 +198,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
GLenum IndexType() const { return m_indexType; } GLenum IndexType() const { return m_indexType; }
private: private:
// Declared before m_capOverride so it is initialised first (members initialise in // Declared before m_capOverride so it is initialized first (members initialize in
// declaration order): the whole decision is made once, and both the cap override and the // declaration order): the whole decision is made once, and both the cap override and the
// constructor body read the same answer. // constructor body read the same answer.
RestartSubstitutionKind m_kind = RestartSubstitutionKind::None; RestartSubstitutionKind m_kind = RestartSubstitutionKind::None;
@@ -248,7 +248,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// Indices whose whole state tuple (viewport rectangle, scissor rectangle, scissor-test enable, // Indices whose whole state tuple (viewport rectangle, scissor rectangle, scissor-test enable,
// depth range) is identical share ONE pass, so the overwhelmingly common case - every index // depth range) is identical share ONE pass, so the overwhelmingly common case - every index
// still holding what glViewport/glScissor/glDepthRange broadcast to all sixteen - collapses // still holding what glViewport/glScissor/glDepthRange broadcast to all sixteen - collapses
// to a single pass with an all-ones gate mask, i.e. one draw and no behaviour change at all. // to a single pass with an all-ones gate mask, i.e. one draw and no behavior change at all.
// //
// Whether emulation runs. Off only under MOBILEGL_ESPRYT_FORCE_VIEWPORT_ARRAY_EMULATION falsy, which // Whether emulation runs. Off only under MOBILEGL_ESPRYT_FORCE_VIEWPORT_ARRAY_EMULATION falsy, which
// restores the pre-emulation path as a negative control. // restores the pre-emulation path as a negative control.
@@ -527,7 +527,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
Bool ResolveProgramSubsystemArm(); Bool ResolveProgramSubsystemArm();
// P13 W6: every family runs the record arm - the frontend arms these resolvers used to choose // P13 W6: every family runs the record arm - the frontend arms these resolvers used to choose
// between are gone, so no draw-path site asks them any more (the integration peeks still read // between are gone, so no draw-path site asks them any more (the integration peeks still read
// the latches). The one refusal a running process can still meet - a driver whose colour- // the latches). The one refusal a running process can still meet - a driver whose color-
// attachment cap exceeds the wire's width - is checked once, at capability bring-up. // attachment cap exceeds the wire's width - is checked once, at capability bring-up.
void ResolveRecordArmFamilies(); void ResolveRecordArmFamilies();
@@ -843,7 +843,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// glBufferStorageExternalEXT, so `id` is immutable, persistentMapped and // glBufferStorageExternalEXT, so `id` is immutable, persistentMapped and
// persistentPtr is this server's own coherent map of the client's pages. The // persistentPtr is this server's own coherent map of the client's pages. The
// reference is this twin's own, released only after a fence that follows the // reference is this twin's own, released only after a fence that follows the
// store's last use has signalled (RetireT0Import). Survives an ES context loss: // store's last use has signaled (RetireT0Import). Survives an ES context loss:
// the next ensure re-imports it. Null for every store T0 did not adopt. // the next ensure re-imports it. Null for every store T0 did not adopt.
void* externalAhb = nullptr; void* externalAhb = nullptr;
// P8-C: A SHADER WROTE THIS STORE ON THIS SIDE since the R-11 staged copy (or, for // P8-C: A SHADER WROTE THIS STORE ON THIS SIDE since the R-11 staged copy (or, for
@@ -1632,7 +1632,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
} }
// Components per texel the frontend format's client data carries, for the three-channel // Components per texel the frontend format's client data carries, for the three-channel
// formats that can be widened to a four-channel colour-renderable target; 0 for everything // formats that can be widened to a four-channel color-renderable target; 0 for everything
// else. See PrepareChannelWidenedUpload. // else. See PrepareChannelWidenedUpload.
Uint GetWidenableClientComponentCount(TextureInternalFormat format); Uint GetWidenableClientComponentCount(TextureInternalFormat format);
@@ -1642,7 +1642,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
Bool IsIntegerWidenableFormat(TextureInternalFormat format); Bool IsIntegerWidenableFormat(TextureInternalFormat format);
// Repacks three-component client data as four components with an alpha of 1.0 in // Repacks three-component client data as four components with an alpha of 1.0 in
// `uploadType`, for a format the backend widened to keep a colour attachment renderable. // `uploadType`, for a format the backend widened to keep a color attachment renderable.
// Returns `data` untouched when no widening applies. Pure CPU and context-free so a unit // Returns `data` untouched when no widening applies. Pure CPU and context-free so a unit
// test can exercise the exact packing the driver is handed; `widenedData` is the caller's // test can exercise the exact packing the driver is handed; `widenedData` is the caller's
// scratch buffer and has to outlive the returned pointer. // scratch buffer and has to outlive the returned pointer.
@@ -1921,7 +1921,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
SamplerParameters m_cacheSamplerParameters; SamplerParameters m_cacheSamplerParameters;
UintVec2 m_cacheLodRange = {0, 1000}; UintVec2 m_cacheLodRange = {0, 1000};
// All three representations plus the form, because none of them alone identifies the // All three representations plus the form, because none of them alone identifies the
// border colour the driver texture is holding: two integer borders can share one float // border color the driver texture is holding: two integer borders can share one float
// (anything differing above 2^24), and a Float -> Int transition can leave every number // (anything differing above 2^24), and a Float -> Int transition can leave every number
// unchanged while still needing a different driver entry point. // unchanged while still needing a different driver entry point.
FloatVec4 m_cacheBorderColor = {0.0f, 0.0f, 0.0f, 0.0f}; FloatVec4 m_cacheBorderColor = {0.0f, 0.0f, 0.0f, 0.0f};
@@ -1975,7 +1975,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// Hands back the whole RECORD rather than its Params, because the parameter push // Hands back the whole RECORD rather than its Params, because the parameter push
// reads two things from beside them: Desc.InternalFormat, which decides the two // reads two things from beside them: Desc.InternalFormat, which decides the two
// channel-widening swizzle compositions, and Params.BuiltinSampler, which is where // channel-widening swizzle compositions, and Params.BuiltinSampler, which is where
// the border colour lives (it is sampler state, GL 4.6 table 23.18, and P4a does // the border color lives (it is sampler state, GL 4.6 table 23.18, and P4a does
// not duplicate it onto MGPTextureParams). // not duplicate it onto MGPTextureParams).
const MG_Pipe::MGPipeResourceRecord* ResolvePushedTextureParams( const MG_Pipe::MGPipeResourceRecord* ResolvePushedTextureParams(
const SharedPtr<MG_State::GLState::ITextureObject>& stateTextureObject); const SharedPtr<MG_State::GLState::ITextureObject>& stateTextureObject);
@@ -1994,7 +1994,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// m_syncedShapeParamsVersion) AND m_syncedContentVersion: the applier bumps it on // m_syncedShapeParamsVersion) AND m_syncedContentVersion: the applier bumps it on
// every respecify and every sub-data it applies to this resource, which is exactly // every respecify and every sub-data it applies to this resource, which is exactly
// the union those four covered, without the coarse "any texture's churn re-opens // the union those four covered, without the coarse "any texture's churn re-opens
// every gate" behaviour the sampling-resolution generation had. // every gate" behavior the sampling-resolution generation had.
Uint64 m_syncedResourceSerial = 0; Uint64 m_syncedResourceSerial = 0;
// The record's ParamsSerial at the last SyncTextureParamsToBackend. Replaces // The record's ParamsSerial at the last SyncTextureParamsToBackend. Replaces
// m_syncedTextureParamsVersion; MGPTextureParams::ForceResync replaces // m_syncedTextureParamsVersion; MGPTextureParams::ForceResync replaces
@@ -2186,7 +2186,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
framebuffer, as a GL_COLOR_ATTACHMENTm enum. ES only accepts glDrawBuffers bufs[s] == framebuffer, as a GL_COLOR_ATTACHMENTm enum. ES only accepts glDrawBuffers bufs[s] ==
GL_COLOR_ATTACHMENTs, so a GL draw-buffer slot s naming attachment a forces a's image GL_COLOR_ATTACHMENTs, so a GL draw-buffer slot s naming attachment a forces a's image
under backend slot s. This table is the single owner of that decision and is kept a under backend slot s. This table is the single owner of that decision and is kept a
PERMUTATION of the backend colour slots: every other attachment keeps its identity PERMUTATION of the backend color slots: every other attachment keeps its identity
slot when that slot survived, and is parked on the lowest free slot when it did not. slot when that slot survived, and is parked on the lowest free slot when it did not.
Deriving the point per-query from the draw-buffer array instead handed the identity Deriving the point per-query from the draw-buffer array instead handed the identity
point to any attachment that was not a draw buffer - i.e. exactly the point a point to any attachment that was not a draw buffer - i.e. exactly the point a
@@ -2248,7 +2248,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// false accounts of one physical state: a composite framebuffer synced as DRAW in a // false accounts of one physical state: a composite framebuffer synced as DRAW in a
// depth pre-pass detaches the points its other attachment state attaches, and the // depth pre-pass detaches the points its other attachment state attaches, and the
// end-of-frame sync of that same record on the READ side then found its own slot // end-of-frame sync of that same record on the READ side then found its own slot
// already stamped by an earlier identical sync, walked nothing, left the colour point // already stamped by an earlier identical sync, walked nothing, left the color point
// detached, and the driver refused the following glBlitFramebuffer with // detached, and the driver refused the following glBlitFramebuffer with
// INVALID_OPERATION. Keying on the object is what makes "this hash is applied" mean // INVALID_OPERATION. Keying on the object is what makes "this hash is applied" mean
// "these points are on the driver now". // "these points are on the driver now".
@@ -2305,7 +2305,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// describe that point at all (Color8..Color31, and the default framebuffer's FRONT/BACK // describe that point at all (Color8..Color31, and the default framebuffer's FRONT/BACK
// tokens). MGPFramebufferState carries Color[8] + Depth + Stencil, which is every point // tokens). MGPFramebufferState carries Color[8] + Depth + Stencil, which is every point
// a framebuffer can hold on the handle arm - D-C3 refuses bit 9 outright on a driver // a framebuffer can hold on the handle arm - D-C3 refuses bit 9 outright on a driver
// reporting more than 8 colour attachments. P5e (fb) exports it: the twin's attachment // reporting more than 8 color attachments. P5e (fb) exports it: the twin's attachment
// walk, the named blit's aspect plan and the detach walk all ask the same question of // walk, the named blit's aspect plan and the detach walk all ask the same question of
// the same record, and a second copy of the Color/Depth/Stencil dispatch beside each of // the same record, and a second copy of the Color/Depth/Stencil dispatch beside each of
// them is how one of them ends up describing a point differently from the others. // them is how one of them ends up describing a point differently from the others.
@@ -2313,13 +2313,13 @@ namespace MobileGL::MG_Backend::DirectGLES {
FramebufferAttachmentType point); FramebufferAttachmentType point);
// True when the read buffer names a fixed-point (norm/snorm) attachment that the // True when the read buffer names a fixed-point (norm/snorm) attachment that the
// backend actually stores in a floating-point format. GL clamps a read from a // backend actually stores in a floating-point format. GL clamps a read from a
// fixed-point colour buffer to [0,1] (GL_CLAMP_READ_COLOR defaults to // fixed-point color buffer to [0,1] (GL_CLAMP_READ_COLOR defaults to
// GL_FIXED_ONLY); the substituted float storage would not, so the readback path // GL_FIXED_ONLY); the substituted float storage would not, so the readback path
// has to apply the clamp itself. // has to apply the clamp itself.
Bool IsFixedPointFallbackReadAttachment(); Bool IsFixedPointFallbackReadAttachment();
// True when the read buffer names a three-channel attachment the backend actually stores // True when the read buffer names a three-channel attachment the backend actually stores
// in a four-channel format (the colour-renderable widening). A format without alpha reads // in a four-channel format (the color-renderable widening). A format without alpha reads
// back as 1.0, so the readback path has to overwrite the alpha the draw left behind - // back as 1.0, so the readback path has to overwrite the alpha the draw left behind -
// unconditionally, since this is the format's own semantics rather than the // unconditionally, since this is the format's own semantics rather than the
// GL_CLAMP_READ_COLOR rule the clamp above implements. // GL_CLAMP_READ_COLOR rule the clamp above implements.
@@ -2337,19 +2337,19 @@ namespace MobileGL::MG_Backend::DirectGLES {
// driver where no interception is possible. So the stored alpha is kept at 1.0 instead: // driver where no interception is possible. So the stored alpha is kept at 1.0 instead:
// a clear touching a widened buffer writes alpha 1.0, and every draw into it has its // a clear touching a widened buffer writes alpha 1.0, and every draw into it has its
// alpha write mask forced off, so nothing can ever move it again. The application's own // alpha write mask forced off, so nothing can ever move it again. The application's own
// colour mask is untouched - glGet(GL_COLOR_WRITEMASK) still reports what it set. // color mask is untouched - glGet(GL_COLOR_WRITEMASK) still reports what it set.
extern Uint32 g_alphaWidenedDrawBufferMask; extern Uint32 g_alphaWidenedDrawBufferMask;
// Bit i set = DRAW BUFFER i of the framebuffer bound as DRAW resolves to a colour // Bit i set = DRAW BUFFER i of the framebuffer bound as DRAW resolves to a color
// attachment with an INTEGER format. Recomputed beside the mask above and for its sake: // attachment with an INTEGER format. Recomputed beside the mask above and for its sake:
// glClearBufferfv on an integer colour buffer is GL_INVALID_OPERATION, so the // glClearBufferfv on an integer color buffer is GL_INVALID_OPERATION, so the
// per-draw-buffer clear route the widening needs has to stand down when one is present. // per-draw-buffer clear route the widening needs has to stand down when one is present.
// (glClear on an integer colour buffer is left undefined by ES in the first place, and // (glClear on an integer color buffer is left undefined by ES in the first place, and
// an application that wants a defined answer has to call glClearBufferuiv/iv - which does // an application that wants a defined answer has to call glClearBufferuiv/iv - which does
// carry the widened alpha substitution.) // carry the widened alpha substitution.)
extern Uint32 g_integerColorDrawBufferMask; extern Uint32 g_integerColorDrawBufferMask;
// The colour a clear has to hand the driver for one draw buffer: the application's value, // The color a clear has to hand the driver for one draw buffer: the application's value,
// except that a widened attachment's alpha is replaced by the 1.0 its three-channel // except that a widened attachment's alpha is replaced by the 1.0 its three-channel
// format implies. `one` is 1.0 encoded in the clear call's own component type - the // format implies. `one` is 1.0 encoded in the clear call's own component type - the
// integer clears carry the integer 1, the float clear carries 1.0f. // integer clears carry the integer 1, the float clear carries 1.0f.
@@ -2909,7 +2909,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// self-contained would produce a per-draw rebuild: the other eight inputs - the draw // self-contained would produce a per-draw rebuild: the other eight inputs - the draw
// FBO's snorm/unorm clamp masks, the fragColor broadcast count, the storage-block // FBO's snorm/unorm clamp masks, the fragColor broadcast count, the storage-block
// binding signature, the atomic-counter set, the live image formats and the patch // binding signature, the atomic-counter set, the live image formats and the patch
// parameters - are all still specialised at the verb, from state this backend holds. // parameters - are all still specialized at the verb, from state this backend holds.
// 0 means "never stamped", which is a guaranteed miss (applier serials start at 1). // 0 means "never stamped", which is a guaranteed miss (applier serials start at 1).
Uint64 GetSyncedShaderCsoSerial() const { return m_syncedShaderCsoSerial; } Uint64 GetSyncedShaderCsoSerial() const { return m_syncedShaderCsoSerial; }
// P5e (pg): the SECOND server-owned key, and it replaces GetImageUnitVersion() on // P5e (pg): the SECOND server-owned key, and it replaces GetImageUnitVersion() on
@@ -321,7 +321,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// (Ops_H_ImportExternal below). The twin holds its own reference, and every path that // (Ops_H_ImportExternal below). The twin holds its own reference, and every path that
// ends the store - a respecify, a destroy, a re-import - deletes the GL id and then // ends the store - a respecify, a destroy, a re-import - deletes the GL id and then
// RETIRES the reference here: a fence is inserted after the delete, and the reference is // RETIRES the reference here: a fence is inserted after the delete, and the reference is
// released only once that fence has signalled, i.e. once every command that could still // released only once that fence has signaled, i.e. once every command that could still
// read or write the pages has completed on this context's GPU. The client's own // read or write the pages has completed on this context's GPU. The client's own
// reference ends independently (it releases on respecify/destroy); the pages live until // reference ends independently (it releases on respecify/destroy); the pages live until
// the last of the two. No link watermark is involved (F-L2): the fence is this server's. // the last of the two. No link watermark is involved (F-L2): the fence is this server's.
@@ -1046,8 +1046,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
// sees the reconciled shadow on return, exactly as before. // sees the reconciled shadow on return, exactly as before.
// //
// THE ORDER FROM HERE IS A CORRECTNESS RULE, NOT A PREFERENCE // THE ORDER FROM HERE IS A CORRECTNESS RULE, NOT A PREFERENCE
// (ARCHITECTURE.md 8.2: "写回的 epoch bump 必须在任何后续读该 handle 的命令之前被 // (ARCHITECTURE.md 8.2: "the writeback's epoch bump must be applied by the server before any
// server 应用;反向通道需要与正向通道相同的有序保证"): // later command that reads that handle; the reverse channel needs the same ordering guarantees as the
// forward channel"):
// 1. writeback - the client's shadow takes the bytes, // 1. writeback - the client's shadow takes the bytes,
// 2. unmap - the read mapping goes away, // 2. unmap - the read mapping goes away,
// 3. serial stamp- syncedChangeSerial catches up with the record, so the next // 3. serial stamp- syncedChangeSerial catches up with the record, so the next
@@ -1900,7 +1901,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
} }
// The seventh slot table. A process-lifetime global, like the other six, so it links // The seventh slot table. A process-lifetime global, like the other six, so it links
// itself into its own holder list from its own constructor with no initialisation // itself into its own holder list from its own constructor with no initialization
// order question to answer (SlotTables.h). // order question to answer (SlotTables.h).
BackendBufferResourceTable g_backendBufferResources; BackendBufferResourceTable g_backendBufferResources;
@@ -2112,7 +2113,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
#if MOBILEGL_BUILD_DISAGGREGATED #if MOBILEGL_BUILD_DISAGGREGATED
// P11 B2: the T0 half of the deferred-release drain, run right after it at both sync // P11 B2: the T0 half of the deferred-release drain, run right after it at both sync
// points (ProcessDeferredBufferReleases itself moves verbatim, G5). Retired imports whose // points (ProcessDeferredBufferReleases itself moves verbatim, G5). Retired imports whose
// fence signalled let go of their AHardwareBuffer; a deferred T0 destroy is retired once // fence signaled let go of their AHardwareBuffer; a deferred T0 destroy is retired once
// the drain deleted its id (the fence then follows the delete) or its context is gone. // the drain deleted its id (the fence then follows the delete) or its context is gone.
void ProcessDeferredT0Retires() { void ProcessDeferredT0Retires() {
if (!g_t0Retired.empty()) SweepT0Retired(false); if (!g_t0Retired.empty()) SweepT0Retired(false);
@@ -2309,7 +2310,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
const SizeT poolSize = ResourceWidthOf(res); const SizeT poolSize = ResourceWidthOf(res);
// The pool DECISION is the legacy one, unchanged: extent and residence only. // The pool DECISION is the legacy one, unchanged: extent and residence only.
// An earlier cut added "and a shadow base was recorded", which silently stopped // An earlier cut added "and a shadow base was recorded", which silently stopped
// a buffer recycling pool ids - a change to D-F's pool behaviour, not to "the // a buffer recycling pool ids - a change to D-F's pool behavior, not to "the
// source of bytes" the conversion is allowed to move, and one that moves // source of bytes" the conversion is allowed to move, and one that moves
// AcquireFromPool call counts StreamedArenaScenario observes. // AcquireFromPool call counts StreamedArenaScenario observes.
const Uint reused = (poolSize > 0 && !resource->persistentMapped) ? AcquireFromPool(poolSize) : 0; const Uint reused = (poolSize > 0 && !resource->persistentMapped) ? AcquireFromPool(poolSize) : 0;
@@ -146,7 +146,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// re-issued is invalidated too - the attachment versions below, and under // re-issued is invalidated too - the attachment versions below, and under
// MOBILEGL_PIPE_PUSH the record memos stamped at the end of this function - so the // MOBILEGL_PIPE_PUSH the record memos stamped at the end of this function - so the
// next sync re-attaches all non-empty attachments at their identity points AND // next sync re-attaches all non-empty attachments at their identity points AND
// (see SyncToBackend's attachment loop) detaches any colour point whose frontend owner // (see SyncToBackend's attachment loop) detaches any color point whose frontend owner
// is empty. Without that detach a stale image would survive under a point the table now // is empty. Without that detach a stale image would survive under a point the table now
// claims for a different, empty attachment. // claims for a different, empty attachment.
for (Uint i = 0; i < MAX_COLOR_ATTACHMENT_SLOTS; ++i) { for (Uint i = 0; i < MAX_COLOR_ATTACHMENT_SLOTS; ++i) {
@@ -250,7 +250,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// kMGPipeSurfaceNoTextureTarget on a texture point is a seam defect - refused loudly and // kMGPipeSurfaceNoTextureTarget on a texture point is a seam defect - refused loudly and
// answered false, never guessed. False is the safe direction for all four: the mask // answered false, never guessed. False is the safe direction for all four: the mask
// turns an emulation ON, and an emulation that does not run leaves the driver's own // turns an emulation ON, and an emulation that does not run leaves the driver's own
// (correct-for-the-real-format) behaviour, while one that runs on the wrong buffer // (correct-for-the-real-format) behavior, while one that runs on the wrong buffer
// clamps or overwrites texels the application wrote. // clamps or overwrites texels the application wrote.
static Bool PushedSurfaceTextureTarget(const MG_Pipe::MGPSurface& surface, TextureTarget* out) { static Bool PushedSurfaceTextureTarget(const MG_Pipe::MGPSurface& surface, TextureTarget* out) {
if (surface.TextureTarget == MG_Pipe::kMGPipeSurfaceNoTextureTarget) { if (surface.TextureTarget == MG_Pipe::kMGPipeSurfaceNoTextureTarget) {
@@ -314,7 +314,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// The record's surface for an attachment POINT, or null when this record does not // The record's surface for an attachment POINT, or null when this record does not
// describe that point at all. MGPFramebufferState carries Color[8] + Depth + Stencil // describe that point at all. MGPFramebufferState carries Color[8] + Depth + Stencil
// (D-C1), which is every point a framebuffer can hold on the handle arm: D-C3 refuses // (D-C1), which is every point a framebuffer can hold on the handle arm: D-C3 refuses
// bit 9 outright on a driver reporting more than 8 colour attachments, and the // bit 9 outright on a driver reporting more than 8 color attachments, and the
// FRONT/BACK points belong to the DEFAULT framebuffer, which has no twin at all. // FRONT/BACK points belong to the DEFAULT framebuffer, which has no twin at all.
const MG_Pipe::MGPSurface* PushedSurfaceForAttachment(const MG_Pipe::MGPFramebufferState& record, const MG_Pipe::MGPSurface* PushedSurfaceForAttachment(const MG_Pipe::MGPFramebufferState& record,
FramebufferAttachmentType point) { FramebufferAttachmentType point) {
@@ -465,7 +465,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// an object the apply thread was handed. // an object the apply thread was handed.
// //
// P5e (fix1, ID-81): arm-selected for the texture arm's reason, and here it is a // P5e (fix1, ID-81): arm-selected for the texture arm's reason, and here it is a
// BEHAVIOUR difference even though the by-handle body dereferences nothing: the // BEHAVIOR difference even though the by-handle body dereferences nothing: the
// object form reports a refused allocation to the application through the live // object form reports a refused allocation to the application through the live
// GLContext (RecordError) and the by-handle form deliberately does not, because // GLContext (RecordError) and the by-handle form deliberately does not, because
// under a transport there is no application on this side to report it to. On the // under a transport there is no application on this side to report it to. On the
@@ -483,7 +483,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
} }
// The read attachment's storage carries an alpha channel its frontend format does not // The read attachment's storage carries an alpha channel its frontend format does not
// (the three-channel colour-renderable widening). GL answers such a read with 1.0, but // (the three-channel color-renderable widening). GL answers such a read with 1.0, but
// the storage holds whatever the draw wrote there, so the readback has to overwrite it. // the storage holds whatever the draw wrote there, so the readback has to overwrite it.
Bool IsAlphaWidenedFallbackReadAttachment() { Bool IsAlphaWidenedFallbackReadAttachment() {
const auto* record = MG_Pipe::MGPipeApplier().ReadFramebuffer(); const auto* record = MG_Pipe::MGPipeApplier().ReadFramebuffer();
@@ -552,7 +552,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
void BackendFramebufferObject::ApplyReadBufferFromRecord(const MG_Pipe::MGPFramebufferState& record, void BackendFramebufferObject::ApplyReadBufferFromRecord(const MG_Pipe::MGPFramebufferState& record,
Uint glNameForDiag) { Uint glNameForDiag) {
// D-C1 makes the NULL Res the spelling of "no attachment" and c0c's // D-C1 makes the NULL Res the spelling of "no attachment" and c0c's
// kMGPipeSurfaceKindNone makes Kind agree with it on a zero-initialised record. // kMGPipeSurfaceKindNone makes Kind agree with it on a zero-initialized record.
// Res stays the test - it is the one D-C1 fixes - and Kind is CROSS-CHECKED // Res stays the test - it is the one D-C1 fixes - and Kind is CROSS-CHECKED
// rather than consulted: the two are one statement the emitter has to make // rather than consulted: the two are one statement the emitter has to make
// twice, and a record where they disagree is a seam defect, not an empty point. // twice, and a record where they disagree is a seam defect, not an empty point.
@@ -569,7 +569,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
FramebufferAttachmentType pushedReadBuf = FramebufferAttachmentType::None; FramebufferAttachmentType pushedReadBuf = FramebufferAttachmentType::None;
if (!readSurfaceEmpty) { if (!readSurfaceEmpty) {
// EVERY FIELD THAT IDENTIFIES THE IMAGE, not the three v1 compared (m-4). // EVERY FIELD THAT IDENTIFIES THE IMAGE, not the three v1 compared (m-4).
// The same image legally sits at two colour points - a layered attachment of // The same image legally sits at two color points - a layered attachment of
// one array and a single slice of it are different surfaces of one Res - so a // one array and a single slice of it are different surfaces of one Res - so a
// partial comparison resolves to the lower index and the refusal branch below // partial comparison resolves to the lower index and the refusal branch below
// ("refusing to guess") would then be doing part of the guessing itself. // ("refusing to guess") would then be doing part of the guessing itself.
@@ -590,7 +590,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
} }
if (!matched) { if (!matched) {
MGLOG_E_ONCE("MGPipe: framebuffer %u's resolved read surface {%u, %u} matches no " MGLOG_E_ONCE("MGPipe: framebuffer %u's resolved read surface {%u, %u} matches no "
"colour attachment of its own record - refusing to guess a read " "color attachment of its own record - refusing to guess a read "
"buffer", "buffer",
glNameForDiag, record.ReadSurface.Res.Slot, record.ReadSurface.Res.Gen); glNameForDiag, record.ReadSurface.Res.Slot, record.ReadSurface.Res.Gen);
return; return;
@@ -659,7 +659,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// fire the table would disagree with the glDrawBuffers the caller already issued, // fire the table would disagree with the glDrawBuffers the caller already issued,
// which is the exact non-injectivity this table exists to remove. // which is the exact non-injectivity this table exists to remove.
MOBILEGL_ASSERT(a < slotCount && !assigned[a], MOBILEGL_ASSERT(a < slotCount && !assigned[a],
"Draw buffer %u names colour attachment %u which is out of range or duplicated.", s, a); "Draw buffer %u names color attachment %u which is out of range or duplicated.", s, a);
if (a >= slotCount || assigned[a]) { if (a >= slotCount || assigned[a]) {
continue; continue;
} }
@@ -890,7 +890,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// 3. The attachments. Three gates, and the key is the object's rather than the // 3. The attachments. Three gates, and the key is the object's rather than the
// binding's: idGenerationMoved ("I re-minted a driver id, so a point may hold a // binding's: idGenerationMoved ("I re-minted a driver id, so a point may hold a
// dead one"), attachmentPointsMoved ("the colour permutation moved, so a point // dead one"), attachmentPointsMoved ("the color permutation moved, so a point
// whose record did not change still has to be re-issued") and the record hash. // whose record did not change still has to be re-issued") and the record hash.
// NOT m_syncedRecordHashes: the per-target array is the object arm's re-arm // NOT m_syncedRecordHashes: the per-target array is the object arm's re-arm
// trigger, and the points written below belong to the driver FBO rather than to // trigger, and the points written below belong to the driver FBO rather than to
@@ -910,7 +910,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
const Bool empty = const Bool empty =
surface.Kind == MG_Pipe::kMGPipeSurfaceKindNone || MG_Pipe::MGPipeHandleIsNull(surface.Res); surface.Kind == MG_Pipe::kMGPipeSurfaceKindNone || MG_Pipe::MGPipeHandleIsNull(surface.Res);
// The detach that keeps m_backendColorSlots a permutation of the PHYSICAL // The detach that keeps m_backendColorSlots a permutation of the PHYSICAL
// layout: a colour point whose record says empty must not keep the previous // layout: a color point whose record says empty must not keep the previous
// owner's image, or glReadBuffer returns it. Bounded by the driver's cap, // owner's image, or glReadBuffer returns it. Bounded by the driver's cap,
// because GL_COLOR_ATTACHMENTn above it is INVALID_ENUM. // because GL_COLOR_ATTACHMENTn above it is INVALID_ENUM.
if (isColorPoint && empty) { if (isColorPoint && empty) {
@@ -979,12 +979,12 @@ namespace MobileGL::MG_Backend::DirectGLES {
} }
GLenum BackendFramebufferObject::GetBackendAttachmentType(FramebufferAttachmentType frontendAtt) const { GLenum BackendFramebufferObject::GetBackendAttachmentType(FramebufferAttachmentType frontendAtt) const {
// Only colour attachments are ever relocated; depth/stencil, the default framebuffer's // Only color attachments are ever relocated; depth/stencil, the default framebuffer's
// FRONT/BACK names and None map straight through. // FRONT/BACK names and None map straight through.
if (frontendAtt < FramebufferAttachmentType::Color0 || frontendAtt > FramebufferAttachmentType::Color31) { if (frontendAtt < FramebufferAttachmentType::Color0 || frontendAtt > FramebufferAttachmentType::Color31) {
return MG_Util::ConvertFramebufferAttachmentTypeToGLEnum(frontendAtt); return MG_Util::ConvertFramebufferAttachmentTypeToGLEnum(frontendAtt);
} }
// The table is a permutation of the backend colour points, so this is the one point that // The table is a permutation of the backend color points, so this is the one point that
// owns this attachment. Searching the draw-buffer array instead returned the identity // owns this attachment. Searching the draw-buffer array instead returned the identity
// point for every attachment that was not a draw buffer - which is exactly the point a // point for every attachment that was not a draw buffer - which is exactly the point a
// relocated draw buffer had just taken over, so COLOR_ATTACHMENT0 read back the image of // relocated draw buffer had just taken over, so COLOR_ATTACHMENT0 read back the image of
@@ -237,7 +237,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// The four hostBytes sites read the same in both builds. The non-split expansion is the // The four hostBytes sites read the same in both builds. The non-split expansion is the
// ORIGINAL EXPRESSION, character for character - `raw - offset` - so a push or verify build // ORIGINAL EXPRESSION, character for character - `raw - offset` - so a push or verify build
// compiles exactly what it compiled before R-11 and the split arm is the only new behaviour. // compiles exactly what it compiled before R-11 and the split arm is the only new behavior.
#define MGL_SERVER_STAGED_ADOPT(res, width, bytes, offset, size) \ #define MGL_SERVER_STAGED_ADOPT(res, width, bytes, offset, size) \
ServerStaged().Adopt(&(res), (width), (bytes), (offset), (size)) ServerStaged().Adopt(&(res), (width), (bytes), (offset), (size))
#define MGL_SERVER_STAGED_REQUIRE_PENDING(res, base, frontendSize, site) \ #define MGL_SERVER_STAGED_REQUIRE_PENDING(res, base, frontendSize, site) \
@@ -476,7 +476,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
namespace { namespace {
// One shape for all five, so a kind cannot grow a different liveness rule than its // One shape for all five, so a kind cannot grow a different liveness rule than its
// neighbours: index the applier's own dense table, refuse a slot it has not grown to, // neighbors: index the applier's own dense table, refuse a slot it has not grown to,
// refuse a record that is not Live, and refuse a generation that has moved on under // refuse a record that is not Live, and refuse a generation that has moved on under
// the caller. The last of the three is the one that matters: a stale handle resolving // the caller. The last of the three is the one that matters: a stale handle resolving
// to its successor's record is how a twin ends up describing another object's storage. // to its successor's record is how a twin ends up describing another object's storage.
@@ -417,7 +417,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// every draw of every program with an override would rebuild. Taking the client's number // every draw of every program with an override would rebuild. Taking the client's number
// verbatim removes that coupling: there is one authority, and the twin's stamp and the // verbatim removes that coupling: there is one authority, and the twin's stamp and the
// clean clause read the same field. The monolith arm's source carries the answer of the // clean clause read the same field. The monolith arm's source carries the answer of the
// function this replaces, computed from the frontend map, so its behaviour is unchanged. // function this replaces, computed from the frontend map, so its behavior is unchanged.
Uint64 ComputeShaderStorageBlockBindingSignature(const ProgramBuildSource& src) { Uint64 ComputeShaderStorageBlockBindingSignature(const ProgramBuildSource& src) {
return src.StorageOverrideSignature; return src.StorageOverrideSignature;
} }
@@ -1022,7 +1022,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// GL 4.6 core table 23.53 requires GL_MAX_SAMPLES >= 4, so every multisample // GL 4.6 core table 23.53 requires GL_MAX_SAMPLES >= 4, so every multisample
// ceiling MobileGL advertises is floored to 4 no matter what the ES driver // ceiling MobileGL advertises is floored to 4 no matter what the ES driver
// reports - but the realised allocation cannot be, and // reports - but the realized allocation cannot be, and
// ClampSamplesToBackendSupport quietly gives an integer or depth multisample // ClampSamplesToBackendSupport quietly gives an integer or depth multisample
// texture the ONE sample Adreno and Mali actually support for it. A shader // texture the ONE sample Adreno and Mali actually support for it. A shader
// written against the advertised ceiling then fetches a sample that storage does // written against the advertised ceiling then fetches a sample that storage does
@@ -1051,7 +1051,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
*effectiveSpirv, splitArrayInputSpirv, enableSpirvValidation) && *effectiveSpirv, splitArrayInputSpirv, enableSpirvValidation) &&
!splitArrayInputSpirv.empty() && splitArrayInputSpirv != *effectiveSpirv) { !splitArrayInputSpirv.empty() && splitArrayInputSpirv != *effectiveSpirv) {
// Only when the pass ACTUALLY split something: the optimizer hands back a // Only when the pass ACTUALLY split something: the optimizer hands back a
// re-serialised copy either way, and that round trip is not free - it cost the // re-serialized copy either way, and that round trip is not free - it cost the
// minecraft-1.21.1-neoforge-create-indirect-in-world-align1024 retrace 0.15 SSIM. // minecraft-1.21.1-neoforge-create-indirect-in-world-align1024 retrace 0.15 SSIM.
effectiveSpirv = &splitArrayInputSpirv; effectiveSpirv = &splitArrayInputSpirv;
} }
@@ -1095,11 +1095,11 @@ namespace MobileGL::MG_Backend::DirectGLES {
// Left outside, a second program sharing this stage's key would be served ESSL // Left outside, a second program sharing this stage's key would be served ESSL
// with the blocks un-renamed and the repair would silently stop working - the // with the blocks un-renamed and the repair would silently stop working - the
// same trap SetAtomicCounterBlockBindings sets one screen down. // same trap SetAtomicCounterBlockBindings sets one screen down.
// * AT THIS POSITION, because moving a SPIR-V pass in a chain is a behavioural // * AT THIS POSITION, because moving a SPIR-V pass in a chain is a behavioral
// change, and this one arrived device-verified on Mali. Hoisting it above the // change, and this one arrived device-verified on Mali. Hoisting it above the
// cache probe instead would have needed no key material at all (the rename would // cache probe instead would have needed no key material at all (the rename would
// already be in the module bytes the key hashes) and was rejected for that // already be in the module bytes the key hashes) and was rejected for that
// reason: it reorders the chain, and it would re-serialise the module on every // reason: it reorders the chain, and it would re-serialize the module on every
// build including the ones the memo is there to make free. // build including the ones the memo is there to make free.
// Its two rename maps are therefore KEY MATERIAL - see the caller. // Its two rename maps are therefore KEY MATERIAL - see the caller.
Vector<unsigned int> uniquifiedIoBlockSpirv; Vector<unsigned int> uniquifiedIoBlockSpirv;
@@ -1333,7 +1333,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// The caller arms the two directions; both are false unless the driver POST // The caller arms the two directions; both are false unless the driver POST
// measured the defect AND this program has a stage that can hit it. Adopted only // measured the defect AND this program has a stage that can hit it. Adopted only
// when this stage really had a located block, for the reason the array-input split // when this stage really had a located block, for the reason the array-input split
// documents: the optimizer hands back a re-serialised copy either way. // documents: the optimizer hands back a re-serialized copy either way.
// //
// LAST IN THE CHAIN, and that position is load-bearing. Vulkan SPIR-V REQUIRES a // LAST IN THE CHAIN, and that position is load-bearing. Vulkan SPIR-V REQUIRES a
// Location on every user-defined Input/Output variable // Location on every user-defined Input/Output variable
@@ -1385,7 +1385,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// ES fixes a storage block's binding at link from its layout(binding=) qualifier // ES fixes a storage block's binding at link from its layout(binding=) qualifier
// and has no glShaderStorageBlockBinding to move it afterwards, so a rebinding // and has no glShaderStorageBlockBinding to move it afterwards, so a rebinding
// can only be honoured by printing it INTO the qualifier. Rewriting the Binding // can only be honored by printing it INTO the qualifier. Rewriting the Binding
// decoration before SPIRV-Cross emits is what does that; RemoveLayoutBinding // decoration before SPIRV-Cross emits is what does that; RemoveLayoutBinding
// then deliberately preserves the qualifier for `buffer` declarations. // then deliberately preserves the qualifier for `buffer` declarations.
if (!storageBlockBindingOverrides.empty()) { // empty for almost every program if (!storageBlockBindingOverrides.empty()) { // empty for almost every program
@@ -1516,12 +1516,12 @@ namespace MobileGL::MG_Backend::DirectGLES {
return; return;
} }
// Mirrored from the neighbours rather than fixed: whether SPIRV-Cross redeclares // Mirrored from the neighbors rather than fixed: whether SPIRV-Cross redeclares
// gl_PerVertex, and with which members, depends on what the application's shaders // gl_PerVertex, and with which members, depends on what the application's shaders
// touched, and a synthesized stage that redeclares a DIFFERENT shape than the stage // touched, and a synthesized stage that redeclares a DIFFERENT shape than the stage
// it feeds is an ES link error against a program with no other problem. gl_in copies // it feeds is an ES link error against a program with no other problem. gl_in copies
// the vertex stage's OUT block (that is what arrives) and gl_out the evaluation // the vertex stage's OUT block (that is what arrives) and gl_out the evaluation
// stage's IN block (that is what is expected). A neighbour that redeclared nothing // stage's IN block (that is what is expected). A neighbor that redeclared nothing
// yields an empty list, which leaves the driver's own built-in declaration in place - // yields an empty list, which leaves the driver's own built-in declaration in place -
// which is exactly what matching it requires. // which is exactly what matching it requires.
const String inMembers = ExtractPerVertexBlockMembers(vertexStageEssl, /*input=*/false).value_or(String()); const String inMembers = ExtractPerVertexBlockMembers(vertexStageEssl, /*input=*/false).value_or(String());
@@ -1531,7 +1531,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
String source = String source =
BuildPassthroughTessControlEssl(ResolveBackendEsslVersion(), patchVertices, inMembers, outMembers, BuildPassthroughTessControlEssl(ResolveBackendEsslVersion(), patchVertices, inMembers, outMembers,
m_passthroughTessControlOuterLevel, m_passthroughTessControlInnerLevel); m_passthroughTessControlOuterLevel, m_passthroughTessControlInnerLevel);
// The mirrored member lists can carry gl_PointSize - the neighbour stage declared it, // The mirrored member lists can carry gl_PointSize - the neighbor stage declared it,
// so matching it is the whole point - and a redeclaration is exactly as illegal as a // so matching it is the whole point - and a redeclaration is exactly as illegal as a
// reference in ESSL without the extension. Same directive, same never-speculative // reference in ESSL without the extension. Same directive, same never-speculative
// rule as the per-stage loop; a driver with neither spelling gets nothing added and // rule as the per-stage loop; a driver with neither spelling gets nothing added and
@@ -1543,7 +1543,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
: nullptr; : nullptr;
source = RequestPointSizeExtension(Move(source), passthroughPointSizeExtension); source = RequestPointSizeExtension(Move(source), passthroughPointSizeExtension);
// Same for gl_ClipDistance/gl_CullDistance mirrored out of a vertex stage that // Same for gl_ClipDistance/gl_CullDistance mirrored out of a vertex stage that
// writes them: the neighbour could only compile with the extension, so it exists. // writes them: the neighbor could only compile with the extension, so it exists.
source = RequestClipCullDistanceExtension(Move(source)); source = RequestClipCullDistanceExtension(Move(source));
const GLuint backendShaderId = g_GLESFuncs.glCreateShader(GL_TESS_CONTROL_SHADER); const GLuint backendShaderId = g_GLESFuncs.glCreateShader(GL_TESS_CONTROL_SHADER);
@@ -1608,7 +1608,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
m_frontendProgramId = src.GetExternalIndex(); m_frontendProgramId = src.GetExternalIndex();
// GetSpirvStatus() as well as GetLinkStatus(): a program whose phase-B job was // GetSpirvStatus() as well as GetLinkStatus(): a program whose phase-B job was
// cancelled (teardown) or whose optimizer run failed is fully linked and fully // canceled (teardown) or whose optimizer run failed is fully linked and fully
// queryable, but has no SPIR-V to build a driver program out of. GL cannot retract // queryable, but has no SPIR-V to build a driver program out of. GL cannot retract
// a LINK_STATUS it already reported true, so "linked but not drawable" is the // a LINK_STATUS it already reported true, so "linked but not drawable" is the
// answer, and this is where the ES backend expresses it. // answer, and this is where the ES backend expresses it.
@@ -1773,7 +1773,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// //
// The repair is a rename keyed on the PRODUCING stage, planned here and applied // The repair is a rename keyed on the PRODUCING stage, planned here and applied
// per stage below so a producer and its consumer keep naming the same block. // per stage below so a producer and its consumer keep naming the same block.
// Gated twice over, because a re-serialised module is not free (it cost the // Gated twice over, because a re-serialized module is not free (it cost the
// create-indirect retrace 0.15 SSIM the first time the array-input split missed // create-indirect retrace 0.15 SSIM the first time the array-input split missed
// its gate; the case is now // its gate; the case is now
// minecraft-1.21.1-neoforge-create-indirect-in-world-align1024): only a tessellation // minecraft-1.21.1-neoforge-create-indirect-in-world-align1024): only a tessellation
@@ -1890,7 +1890,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// an output nothing can read. That is not merely useless: in a separable program // an output nothing can read. That is not merely useless: in a separable program
// pipeline the fragment stage lives in a DIFFERENT program, which never saw this // pipeline the fragment stage lives in a DIFFERENT program, which never saw this
// build and cannot be given a gate, so promoting there would hang an unmatched // build and cannot be given a gate, so promoting there would hang an unmatched
// varying off a program to buy nothing. Both cases keep the pre-emulation behaviour, // varying off a program to buy nothing. Both cases keep the pre-emulation behavior,
// which is what a program with no fragment stage had anyway. // which is what a program with no fragment stage had anyway.
const Bool programHasFragmentStage = const Bool programHasFragmentStage =
std::find(linkedStages.begin(), linkedStages.end(), ShaderStage::Fragment) != linkedStages.end(); std::find(linkedStages.begin(), linkedStages.end(), ShaderStage::Fragment) != linkedStages.end();
@@ -1974,7 +1974,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// KEYING ON THE RESOLVED MAPS rather than on what they were derived from // KEYING ON THE RESOLVED MAPS rather than on what they were derived from
// (collidingIoBlockNames, declaredIoBlockNames, stagePipelineIndices, this // (collidingIoBlockNames, declaredIoBlockNames, stagePipelineIndices, this
// stage's index) is deliberate: the maps ARE the pass's arguments, so they are // stage's index) is deliberate: the maps ARE the pass's arguments, so they are
// exactly as fine as the pass's behaviour and no finer. Two programs whose // exactly as fine as the pass's behavior and no finer. Two programs whose
// collision plans differ but whose maps for THIS stage come out identical really // collision plans differ but whose maps for THIS stage come out identical really
// do produce the same ESSL and should share the entry. // do produce the same ESSL and should share the entry.
// //
@@ -2225,7 +2225,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// * AFTER RebindImageUniformsToFrontendUnits, so the binding it copies onto // * AFTER RebindImageUniformsToFrontendUnits, so the binding it copies onto
// both halves of a split image is already the frontend texture unit (and so // both halves of a split image is already the frontend texture unit (and so
// that pass never has to reason about the alias it introduces); // that pass never has to reason about the alias it introduces);
// * BEFORE RemoveLayoutBinding, whose keepBindingRegex recognises an image // * BEFORE RemoveLayoutBinding, whose keepBindingRegex recognizes an image
// declaration and preserves its binding - an image unit cannot be set from // declaration and preserves its binding - an image unit cannot be set from
// the API in ES, so the qualifier is the only binding mechanism there is, // the API in ES, so the qualifier is the only binding mechanism there is,
// and both halves of the pair have to still be carrying theirs when it runs. // and both halves of the pair have to still be carrying theirs when it runs.
@@ -2639,7 +2639,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// arm - and building from an empty reflection would produce a program with no // arm - and building from an empty reflection would produce a program with no
// uniforms and no modules, i.e. a black screen with no marker anywhere. The // uniforms and no modules, i.e. a black screen with no marker anywhere. The
// create's own trip wire already refuses a record that declares no blobs AND // create's own trip wire already refuses a record that declares no blobs AND
// carries no artefacts (PipeApply.cpp); this is the same statement one level later, // carries no artifacts (PipeApply.cpp); this is the same statement one level later,
// where the twin can name the handle. // where the twin can name the handle.
if (!record->Archive) { if (!record->Archive) {
MGLOG_E_ONCE("MGPipe: shader CSO {%u, %u} carries no server-owned archive; its " MGLOG_E_ONCE("MGPipe: shader CSO {%u, %u} carries no server-owned archive; its "
@@ -2668,7 +2668,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// one location per ELEMENT, so a caller walking locations sees the same name // one location per ELEMENT, so a caller walking locations sees the same name
// repeatedly; this turns it back into "goku[k]". Anything that is not an array // repeatedly; this turns it back into "goku[k]". Anything that is not an array
// (or whose base location cannot be resolved) comes back unchanged, so the only // (or whose base location cannot be resolved) comes back unchanged, so the only
// behaviour that moves is the array case. // behavior that moves is the array case.
String SubscriptUniformNameForElement(const ProgramBuildSource& program, const String& name, String SubscriptUniformNameForElement(const ProgramBuildSource& program, const String& name,
Uint location) { Uint location) {
if (name.size() < 3 || name.compare(name.size() - 3, 3, "[0]") != 0) return name; if (name.size() < 3 || name.compare(name.size() - 3, 3, "[0]") != 0) return name;
@@ -2794,7 +2794,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
if (!m_backendProgramUsable) { if (!m_backendProgramUsable) {
// Every draw made with this program renders nothing and raises no GL error, so // Every draw made with this program renders nothing and raises no GL error, so
// without this line the only symptom is a framebuffer that kept its clear // without this line the only symptom is a framebuffer that kept its clear
// colour. Latched: the early return above only dedupes CONSECUTIVE binds, so an // color. Latched: the early return above only dedupes CONSECUTIVE binds, so an
// app alternating a healthy and a broken program would otherwise log every // app alternating a healthy and a broken program would otherwise log every
// single draw. Parked at MGLOG_I until the level ordering was fixed. // single draw. Parked at MGLOG_I until the level ordering was fixed.
MGLOG_E_ONCE("Backend program for GL program %u is unusable (a shader failed to transpile, " MGLOG_E_ONCE("Backend program for GL program %u is unusable (a shader failed to transpile, "
@@ -168,8 +168,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
m_cacheSamplerParameters.borderColorI != samplerParams.borderColorI || m_cacheSamplerParameters.borderColorI != samplerParams.borderColorI ||
m_cacheSamplerParameters.borderColorUI != samplerParams.borderColorUI || m_cacheSamplerParameters.borderColorUI != samplerParams.borderColorUI ||
m_cacheSamplerParameters.borderColorForm != samplerParams.borderColorForm) { m_cacheSamplerParameters.borderColorForm != samplerParams.borderColorForm) {
// Same gate as the texture-side border colour above, and the same reason for // Same gate as the texture-side border color above, and the same reason for
// branching on the form: an integer border colour must reach the driver through // branching on the form: an integer border color must reach the driver through
// glSamplerParameterIiv/Iuiv or an integer sampler reads the float's bit pattern // glSamplerParameterIiv/Iuiv or an integer sampler reads the float's bit pattern
// back instead of the value. // back instead of the value.
if (g_GLESCapabilities.SupportsTextureBorderClamp) { if (g_GLESCapabilities.SupportsTextureBorderClamp) {
@@ -212,7 +212,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
} }
} }
// A three-channel format widened to four to keep a colour attachment renderable (see // A three-channel format widened to four to keep a color attachment renderable (see
// NormalizePixelFormat) is described to the driver as a four-component transfer, so the // NormalizePixelFormat) is described to the driver as a four-component transfer, so the
// three-component client data has to be repacked with an alpha of 1.0 - otherwise the // three-component client data has to be repacked with an alpha of 1.0 - otherwise the
// driver walks three texels' worth of data per four-texel row and the image shears. // driver walks three texels' worth of data per four-texel row and the image shears.
@@ -467,7 +467,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// The transfer half of the image-format widening: an image-bindable texture whose ES // The transfer half of the image-format widening: an image-bindable texture whose ES
// storage was widened to a core carrier is described to the driver as a four-component // storage was widened to a core carrier is described to the driver as a four-component
// transfer, so its narrower client data has to be repacked the same way the three-channel // transfer, so its narrower client data has to be repacked the same way the three-channel
// colour-renderable widening repacks its own. // color-renderable widening repacks its own.
// //
// Three shapes, because the carriers come in three kinds. Most of them keep the frontend // Three shapes, because the carriers come in three kinds. Most of them keep the frontend
// format's component TYPE and only add channels, so padding the shadow out to four // format's component TYPE and only add channels, so padding the shadow out to four
@@ -836,7 +836,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// consume is a swap-and-pop - so every one of them is taken, used and dropped // consume is a swap-and-pop - so every one of them is taken, used and dropped
// inside one level's iteration, and never held across a consume. // inside one level's iteration, and never held across a consume.
if (pushedStorage != nullptr) { if (pushedStorage != nullptr) {
// THE METADATA RESPECIFY, HONOURED (ID-18 M4, review M-2). BindMask and // THE METADATA RESPECIFY, HONORED (ID-18 M4, review M-2). BindMask and
// ImageBindableHint are STICKY facts the client discovers AFTER allocation - a // ImageBindableHint are STICKY facts the client discovers AFTER allocation - a
// texture first bound as a shader image - and an IMMUTABLE texture never has a // texture first bound as a shader image - and an IMMUTABLE texture never has a
// later redefinition to carry them, so they arrive on a respecify that restates // later redefinition to carry them, so they arrive on a respecify that restates
@@ -868,7 +868,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// ONE compare replaces the whole cheap-gate trio AND the content version. The // ONE compare replaces the whole cheap-gate trio AND the content version. The
// applier bumps Serial on every respecify and every sub-data it applies to this // applier bumps Serial on every respecify and every sub-data it applies to this
// resource, which is exactly the union those four keys covered - without the // resource, which is exactly the union those four keys covered - without the
// coarse behaviour the sampling-resolution generation had, where any texture's // coarse behavior the sampling-resolution generation had, where any texture's
// shape churn re-opened every other texture's gate. // shape churn re-opened every other texture's gate.
// //
// The pending set is the second half and it is not redundant: a previous sync // The pending set is the second half and it is not redundant: a previous sync
@@ -2086,7 +2086,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// transport-gated - safe by a chain rather than by its own statement, which // transport-gated - safe by a chain rather than by its own statement, which
// is exactly the shape that cost the monolith arm 137 scenarios. Under a // is exactly the shape that cost the monolith arm 137 scenarios. Under a
// transport the conjunct is already implied by `pushedStorage != nullptr`, so // transport the conjunct is already implied by `pushedStorage != nullptr`, so
// this changes no behaviour and closes no door; it says the thing instead of // this changes no behavior and closes no door; it says the thing instead of
// depending on it. // depending on it.
const Bool texBufferByRecord = pushedStorage != nullptr && MGB_TEXTURE_HANDLE_ARM_OFF(*this) == false; const Bool texBufferByRecord = pushedStorage != nullptr && MGB_TEXTURE_HANDLE_ARM_OFF(*this) == false;
static const SharedPtr<MG_State::GLState::BufferObject> kNoFrontendBuffer{}; static const SharedPtr<MG_State::GLState::BufferObject> kNoFrontendBuffer{};
@@ -461,7 +461,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// otherwise still claim those values are in force and SyncTextureParamsToBackend // otherwise still claim those values are in force and SyncTextureParamsToBackend
// would skip the whole pass on the unchanged params version, leaving the driver // would skip the whole pass on the unchanged params version, leaving the driver
// texture at defaults for the rest of its life. Latent for swizzle, LOD range and // texture at defaults for the rest of its life. Latent for swizzle, LOD range and
// border colour long before GL_DEPTH_STENCIL_TEXTURE_MODE joined them; the mode // border color long before GL_DEPTH_STENCIL_TEXTURE_MODE joined them; the mode
// makes it visible because falling back to the default silently samples the wrong // makes it visible because falling back to the default silently samples the wrong
// aspect rather than merely mis-filtering. // aspect rather than merely mis-filtering.
m_cacheLodRange = {0, 1000}; m_cacheLodRange = {0, 1000};
@@ -835,7 +835,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// NO set_texture_params HAS BEEN APPLIED FOR THIS TEXTURE YET, which is not the same // NO set_texture_params HAS BEEN APPLIED FOR THIS TEXTURE YET, which is not the same
// thing as a malformed one and must not be read as one. ParamsSerial is the // thing as a malformed one and must not be read as one. ParamsSerial is the
// applier's own "have I ever stored a params record here" (it is bumped on every // applier's own "have I ever stored a params record here" (it is bumped on every
// set_texture_params and starts at 0), and Params is value-initialised beside it - // set_texture_params and starts at 0), and Params is value-initialized beside it -
// so on a texture the application has never given a parameter to, BuiltinSampler is // so on a texture the application has never given a parameter to, BuiltinSampler is
// null because the record was never written, not because a client wrote a null. // null because the record was never written, not because a client wrote a null.
// There is nothing to push in that state; SyncTextureParamsToBackend's own gate // There is nothing to push in that state; SyncTextureParamsToBackend's own gate
@@ -912,7 +912,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// P4a (D-E1): the fifteen sampler values are SAMPLER state (GL 4.6 table 23.18) and // P4a (D-E1): the fifteen sampler values are SAMPLER state (GL 4.6 table 23.18) and
// Espryt pushes them with glTexParameter* onto the TEXTURE rather than with // Espryt pushes them with glTexParameter* onto the TEXTURE rather than with
// glBindSampler onto the unit - behaviour P4a preserves exactly. What changes is // glBindSampler onto the unit - behavior P4a preserves exactly. What changes is
// where the values and the "did they move" answer come from. // where the values and the "did they move" answer come from.
// //
// The whole arm choice is a preprocessor #if/#else rather than a runtime branch // The whole arm choice is a preprocessor #if/#else rather than a runtime branch
@@ -1055,7 +1055,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// independent of any binding - that is what closes D-E3's gap, where a texture that is // independent of any binding - that is what closes D-E3's gap, where a texture that is
// only a READ-framebuffer attachment reaches SyncMipmapsToBackend and nothing else. The // only a READ-framebuffer attachment reaches SyncMipmapsToBackend and nothing else. The
// FORMAT comes from the descriptor beside them, and the BORDER comes from the built-in // FORMAT comes from the descriptor beside them, and the BORDER comes from the built-in
// sampler's CSO (D-F4), because a border colour is sampler state. // sampler's CSO (D-F4), because a border color is sampler state.
#define MGB_TEXPARAM_LEVEL_RANGE \ #define MGB_TEXPARAM_LEVEL_RANGE \
(pushedRecord != nullptr ? UintVec2{static_cast<Uint>(pushedRecord->Params.BaseLevel), \ (pushedRecord != nullptr ? UintVec2{static_cast<Uint>(pushedRecord->Params.BaseLevel), \
static_cast<Uint>(pushedRecord->Params.MaxLevel)} \ static_cast<Uint>(pushedRecord->Params.MaxLevel)} \
@@ -1117,7 +1117,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
Bool borderColorReadable = true; Bool borderColorReadable = true;
pushedRecord = ResolvePushedTextureParams(stateTextureObject); pushedRecord = ResolvePushedTextureParams(stateTextureObject);
if (pushedRecord == nullptr) return; if (pushedRecord == nullptr) return;
// D-F4: the border colour is SAMPLER state (GL 4.6 table 23.18) and lives on the // D-F4: the border color is SAMPLER state (GL 4.6 table 23.18) and lives on the
// built-in sampler's CSO, not on MGPTextureParams - P4a deliberately does not // built-in sampler's CSO, not on MGPTextureParams - P4a deliberately does not
// put SamplerParameters on the wire twice. All FOUR comparands (float, int, // put SamplerParameters on the wire twice. All FOUR comparands (float, int,
// uint, form) still cross and are still all compared below, because two integer // uint, form) still cross and are still all compared below, because two integer
@@ -1129,7 +1129,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
} else { } else {
borderColorReadable = false; borderColorReadable = false;
MGLOG_E_ONCE("MGPipe: texture %u's built-in sampler CSO {%u, %u} has no applier " MGLOG_E_ONCE("MGPipe: texture %u's built-in sampler CSO {%u, %u} has no applier "
"record, so its border colour cannot be pushed", "record, so its border color cannot be pushed",
TextureDiagName(stateTextureObject, pushedRecord), TextureDiagName(stateTextureObject, pushedRecord),
pushedRecord->Params.BuiltinSampler.Slot, pushedRecord->Params.BuiltinSampler.Gen); pushedRecord->Params.BuiltinSampler.Slot, pushedRecord->Params.BuiltinSampler.Gen);
} }
@@ -1185,7 +1185,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
MGLOG_D("%s(%s:%d) ES error %s", func, file, line, MG_Util::ConvertGLEnumToString(err).c_str()); MGLOG_D("%s(%s:%d) ES error %s", func, file, line, MG_Util::ConvertGLEnumToString(err).c_str());
}); });
// A three-channel format widened to four to keep the image colour-renderable (see // A three-channel format widened to four to keep the image color-renderable (see
// NormalizePixelFormat) gains an alpha channel the frontend format does not have, and // NormalizePixelFormat) gains an alpha channel the frontend format does not have, and
// whatever the draw that filled it wrote there is not what GL would report: a format // whatever the draw that filled it wrote there is not what GL would report: a format
// without alpha reads back as 1.0. Answer the ALPHA swizzle source with ONE so the // without alpha reads back as 1.0. Answer the ALPHA swizzle source with ONE so the
@@ -1246,7 +1246,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// GL_TEXTURE_BORDER_COLOR needs ES 3.2 or EXT/OES_texture_border_clamp; on a driver // GL_TEXTURE_BORDER_COLOR needs ES 3.2 or EXT/OES_texture_border_clamp; on a driver
// without it every such call is INVALID_ENUM, so the parameter is simply not synced. // without it every such call is INVALID_ENUM, so the parameter is simply not synced.
// //
// The FORM has to be forwarded along with the value. A border colour set through // The FORM has to be forwarded along with the value. A border color set through
// glTexParameterIiv/Iuiv is an integer one, and an isampler2D/usampler2D fetch of the // glTexParameterIiv/Iuiv is an integer one, and an isampler2D/usampler2D fetch of the
// border returns whatever the driver's integer border register holds - so pushing it // border returns whatever the driver's integer border register holds - so pushing it
// through glTexParameterfv handed the driver float 255.0 and the shader read back // through glTexParameterfv handed the driver float 255.0 and the shader read back
@@ -1259,7 +1259,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// leave the driver holding the first value forever. // leave the driver holding the first value forever.
// m-3: GATED, because on the handle arm an unreadable border means the CSO record is // m-3: GATED, because on the handle arm an unreadable border means the CSO record is
// missing and the block below is skipped with the refusal already named - so v1's // missing and the block below is skipped with the refusal already named - so v1's
// unconditional initialiser was a live frontend read on a switched-over path whose // unconditional initializer was a live frontend read on a switched-over path whose
// value nothing then used. The value a skipped block sees is the cache's own, which // value nothing then used. The value a skipped block sees is the cache's own, which
// cannot make the condition true. // cannot make the condition true.
const auto borderColorForm = borderColorReadable ? MGB_TEXPARAM_BORDER_FORM : m_cacheBorderColorForm; const auto borderColorForm = borderColorReadable ? MGB_TEXPARAM_BORDER_FORM : m_cacheBorderColorForm;
@@ -597,7 +597,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// already returned. A buffers-only walk leaves the flags as they are (above). // already returned. A buffers-only walk leaves the flags as they are (above).
if (!buffersOnly) HostSetEnabled(attribIndex, attrib.Enabled != 0); if (!buffersOnly) HostSetEnabled(attribIndex, attrib.Enabled != 0);
// The fp64 narrowing, verbatim in behaviour including the Adreno workaround: // The fp64 narrowing, verbatim in behavior including the Adreno workaround:
// when no float32 stream can be built the array is DISABLED rather than left // when no float32 stream can be built the array is DISABLED rather than left
// enabled with no pointer, which is what the driver turns into a SIGSEGV inside // enabled with no pointer, which is what the driver turns into a SIGSEGV inside
// the next draw (KHR-GL43.vertex_attrib_binding.basic-input-case4). IsLong is // the next draw (KHR-GL43.vertex_attrib_binding.basic-input-case4). IsLong is
@@ -981,7 +981,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// object - that absence is the design, not a gap in it (ARCHITECTURE.md 4.2). The // object - that absence is the design, not a gap in it (ARCHITECTURE.md 4.2). The
// two ways to keep the site here would each break something D-N or D-J protects: a // two ways to keep the site here would each break something D-N or D-J protects: a
// handle -> object map is the very thing the split removes, and pulling the bytes // handle -> object map is the very thing the split removes, and pulling the bytes
// eagerly on the client at every draw is new behaviour and new cost. // eagerly on the client at every draw is new behavior and new cost.
// //
// SO THE DEVIATION IS DECLARED, WITH ITS BLAST RADIUS. Under the default mask a // SO THE DEVIATION IS DECLARED, WITH ITS BLAST RADIUS. Under the default mask a
// 64-bit vertex array whose SOURCE buffer was written by a shader and not yet pulled // 64-bit vertex array whose SOURCE buffer was written by a shader and not yet pulled
@@ -1104,7 +1104,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// source type and `normalized` (GL accepts BGRA only normalized, and a packed type's // source type and `normalized` (GL accepts BGRA only normalized, and a packed type's
// signedness is the type's own). The narrowed stream is float32, and `normalized` is // signedness is the type's own). The narrowed stream is float32, and `normalized` is
// deliberately GL_FALSE there rather than attrib.Normalized: GL ignores it for // deliberately GL_FALSE there rather than attrib.Normalized: GL ignores it for
// floating-point array types, and honouring it would scale the fetched values. // floating-point array types, and honoring it would scale the fetched values.
const GLint convertedSize = bgra ? 4 : static_cast<GLint>(attrib.Size); const GLint convertedSize = bgra ? 4 : static_cast<GLint>(attrib.Size);
const GLenum convertedType = bgra ? MG_Util::ConvertDataTypeToGLEnum(sourceType) : GL_FLOAT; const GLenum convertedType = bgra ? MG_Util::ConvertDataTypeToGLEnum(sourceType) : GL_FLOAT;
const GLboolean convertedNormalized = (bgra && attrib.Normalized) ? GL_TRUE : GL_FALSE; const GLboolean convertedNormalized = (bgra && attrib.Normalized) ? GL_TRUE : GL_FALSE;
+5 -5
View File
@@ -87,7 +87,7 @@ namespace MobileGL::MG_Backend::DirectGLES::MultiDrawImpl {
// The flattening dispatch is one invocation per output index. ES 3.1 only // The flattening dispatch is one invocation per output index. ES 3.1 only
// guarantees 65535 work groups per dimension, and exceeding it makes // guarantees 65535 work groups per dimension, and exceeding it makes
// glDispatchCompute an INVALID_VALUE no-op - which would leave the draw reading an // glDispatchCompute an INVALID_VALUE no-op - which would leave the draw reading an
// uninitialised index buffer rather than failing visibly. Cap the tier there // uninitialized index buffer rather than failing visibly. Cap the tier there
// instead of querying: 4.19M indices is far past any real multi-draw batch, and // instead of querying: 4.19M indices is far past any real multi-draw batch, and
// beyond it the per-sub-draw tiers are the better answer anyway. // beyond it the per-sub-draw tiers are the better answer anyway.
constexpr SizeT kComputeWorkGroupSize = 64; constexpr SizeT kComputeWorkGroupSize = 64;
@@ -239,7 +239,7 @@ namespace MobileGL::MG_Backend::DirectGLES::MultiDrawImpl {
// ---- P5e (ra2), ID-136: THE SEAT THIS FILE'S OWN RULE HAD MISSED ------------------- // ---- P5e (ra2), ID-136: THE SEAT THIS FILE'S OWN RULE HAD MISSED -------------------
// //
// This was the LAST unguarded frontend read on the multi-draw apply path, and it sat // This was the LAST unguarded frontend read on the multi-draw apply path, and it sat
// twenty lines above the function that retired its neighbour. It asked // twenty lines above the function that retired its neighbor. It asked
// `gPipeInputs->GetBufferBindingSlot(BufferTarget::DrawIndirect)` and then // `gPipeInputs->GetBufferBindingSlot(BufferTarget::DrawIndirect)` and then
// `EnsureBufferResource(<frontend object>)` - a registry lookup keyed by the client's // `EnsureBufferResource(<frontend object>)` - a registry lookup keyed by the client's
// identity, which is §4.4's rule and not only the allocator's. Under run-ahead that is // identity, which is §4.4's rule and not only the allocator's. Under run-ahead that is
@@ -524,7 +524,7 @@ namespace MobileGL::MG_Backend::DirectGLES::MultiDrawImpl {
std::memcpy(&value, source + i * 4, sizeof(value)); std::memcpy(&value, source + i * 4, sizeof(value));
break; break;
} }
// Unsigned wraparound is the defined behaviour for a negative base vertex. // Unsigned wraparound is the defined behavior for a negative base vertex.
out[i] = (restartActive && value == restartSentinel) ? 0xFFFFFFFFu : value + baseVertexBits; out[i] = (restartActive && value == restartSentinel) ? 0xFFFFFFFFu : value + baseVertexBits;
} }
} }
@@ -789,7 +789,7 @@ void main() {
} }
uint localIndex = outIndex - (low == 0u ? 0u : drawInfo[(low - 1u) * 3u + 2u]); uint localIndex = outIndex - (low == 0u ? 0u : drawInfo[(low - 1u) * 3u + 2u]);
// Unsigned wraparound is the defined behaviour for a negative base vertex. No // Unsigned wraparound is the defined behavior for a negative base vertex. No
// restart sentinel handling: the tier declines outright while restart is enabled. // restart sentinel handling: the tier declines outright while restart is enabled.
flatIndices[outIndex] = ReadSourceIndex(localIndex + drawInfo[low * 3u]) + drawInfo[low * 3u + 1u]; flatIndices[outIndex] = ReadSourceIndex(localIndex + drawInfo[low * 3u]) + drawInfo[low * 3u + 1u];
} }
@@ -1072,7 +1072,7 @@ void main() {
GLsizei drawcount, const GLint* basevertex) { GLsizei drawcount, const GLint* basevertex) {
if (drawcount <= 0 || !count || !indices) return; if (drawcount <= 0 || !count || !indices) return;
// Read before any GL work, because it decides the tier below: a desktop restart index // Read before any GL work, because it decides the tier below: a desktop restart index
// the driver does not know about can only be honoured by the tier that rewrites the // the driver does not know about can only be honored by the tier that rewrites the
// index stream (see ResolveTierForBatch). A restart index this index type cannot hold // index stream (see ResolveTierForBatch). A restart index this index type cannot hold
// needs no rewrite at all - nothing can match it - but it does need the driver's own // needs no rewrite at all - nothing can match it - but it does need the driver's own
// fixed-index restart held off for the batch, which is what the scope below does. // fixed-index restart held off for the batch, which is what the scope below does.
@@ -169,7 +169,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
String PromoteNarrowIntegerWidthGlobals(String source) { String PromoteNarrowIntegerWidthGlobals(String source) {
for (const char* name : {kNarrowIntegerWidths8GlobalName, kNarrowIntegerWidths16GlobalName}) { for (const char* name : {kNarrowIntegerWidths8GlobalName, kNarrowIntegerWidths16GlobalName}) {
// The zero-initialised Private global is printed with or without a precision qualifier; // The zero-initialized Private global is printed with or without a precision qualifier;
// only a declaration starting its own line is the global itself. // only a declaration starting its own line is the global itself.
for (const char* prefix : {"highp uint ", "mediump uint ", "lowp uint ", "uint "}) { for (const char* prefix : {"highp uint ", "mediump uint ", "lowp uint ", "uint "}) {
Bool promoted = false; Bool promoted = false;
+4 -4
View File
@@ -302,7 +302,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// THE CALLING THREAD'S BUCKET, not every bucket, and that is the semantic answer // THE CALLING THREAD'S BUCKET, not every bucket, and that is the semantic answer
// rather than a shortcut: a caller walking twins is asking "which of the objects I // rather than a shortcut: a caller walking twins is asking "which of the objects I
// can see need this", and what it can see is its own share group. Every bucket in // can see need this", and what it can see is its own share group. Every bucket in
// one process would walk a neighbour session's objects. // one process would walk a neighbor session's objects.
ForCallingThread().ForEachLive(fn); ForCallingThread().ForEachLive(fn);
} }
// Every bucket, in one pass. The caller receives the key with the handle so it can tell // Every bucket, in one pass. The caller receives the key with the handle so it can tell
@@ -479,7 +479,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// THE TWO DIRECTIONS ARE NOT SYMMETRIC. The handle arrived in a payload, so // THE TWO DIRECTIONS ARE NOT SYMMETRIC. The handle arrived in a payload, so
// `handle.Gen < entry.Gen` is a reachable input, and adopting it would destroy the INCUMBENT LIVE twin - a // `handle.Gen < entry.Gen` is a reachable input, and adopting it would destroy the INCUMBENT LIVE twin - a
// driver buffer id, a persistent map, a pooled store, released by a defaulted destructor that issues no // driver buffer id, a persistent map, a pooled store, released by a defaulted destructor that issues no
// glDeleteBuffers and no pool enrolment - and then stamp the slot back to the dead resource's generation, // glDeleteBuffers and no pool enrollment - and then stamp the slot back to the dead resource's generation,
// after which the incumbent's own FindByHandle refuses it and it is silently handed a fresh, empty twin. // after which the incumbent's own FindByHandle refuses it and it is silently handed a fresh, empty twin.
// That is a leak AND a resource that loses its storage with no diagnostic, i.e. the shape commit d7655247 // That is a leak AND a resource that loses its storage with no diagnostic, i.e. the shape commit d7655247
// fixed and the thing MGPipeHandle::Gen exists to prevent. So: forward is a recycle and resets the twin, // fixed and the thing MGPipeHandle::Gen exists to prevent. So: forward is a recycle and resets the twin,
@@ -725,9 +725,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
} }
// The holder list: intrusive and doubly linked, so registering and unregistering are // The holder list: intrusive and doubly linked, so registering and unregistering are
// two pointer writes with no allocation, and its head is a constant-initialised // two pointer writes with no allocation, and its head is a constant-initialized
// static - which is what lets the process-lifetime registry globals in Managers.cpp // static - which is what lets the process-lifetime registry globals in Managers.cpp
// link themselves in from their own constructors with no initialisation-order // link themselves in from their own constructors with no initialization-order
// question to answer. Single-threaded, like every table it links (the tables live and // question to answer. Single-threaded, like every table it links (the tables live and
// die on the context thread, as the notice they answer does). // die on the context thread, as the notice they answer does).
void LinkHolder() { void LinkHolder() {
+7 -7
View File
@@ -163,7 +163,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
} // namespace } // namespace
namespace TextureImpl { namespace TextureImpl {
// Every image that can back a colour attachment needs a colour-renderable storage format, // Every image that can back a color attachment needs a color-renderable storage format,
// and ES has no renderable three-channel format at all: a three-channel float fallback is // and ES has no renderable three-channel format at all: a three-channel float fallback is
// a legal ES texture but neither legal multisample storage nor a legal attachment, so // a legal ES texture but neither legal multisample storage nor a legal attachment, so
// GL_RGB8_SNORM / GL_RGB16F / ... have to be widened to four channels for any of them. // GL_RGB8_SNORM / GL_RGB16F / ... have to be widened to four channels for any of them.
@@ -178,7 +178,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// allocating it byte for byte. // allocating it byte for byte.
// //
// Do NOT read that as "nothing changes off-device". Measured on llvmpipe 26.1.6 // Do NOT read that as "nothing changes off-device". Measured on llvmpipe 26.1.6
// (the headless CI driver), an ES 3.2 GL_TEXTURE_2D colour attachment is COMPLETE for // (the headless CI driver), an ES 3.2 GL_TEXTURE_2D color attachment is COMPLETE for
// GL_RGB8 and GL_RGB16F but INCOMPLETE_ATTACHMENT for GL_RGB8_SNORM, GL_SRGB8 and every // GL_RGB8 and GL_RGB16F but INCOMPLETE_ATTACHMENT for GL_RGB8_SNORM, GL_SRGB8 and every
// RGB integer format, and UNSUPPORTED for GL_RGB32F. Those eight formats therefore DO // RGB integer format, and UNSUPPORTED for GL_RGB32F. Those eight formats therefore DO
// take the widened path on llvmpipe, which is where the retrace fixtures and the glcts // take the widened path on llvmpipe, which is where the retrace fixtures and the glcts
@@ -798,7 +798,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// geometry STAGES core but leaves gl_PointSize out of their gl_PerVertex entirely, // geometry STAGES core but leaves gl_PointSize out of their gl_PerVertex entirely,
// and SPIRV-Cross - which only ever sees a SPIR-V BuiltIn PointSize decoration - // and SPIRV-Cross - which only ever sees a SPIR-V BuiltIn PointSize decoration -
// prints the identifier with no directive behind it. Same hard rule as the two // prints the identifier with no directive behind it. Same hard rule as the two
// neighbours: never emitted speculatively, because `#extension` on a name the driver // neighbors: never emitted speculatively, because `#extension` on a name the driver
// does not advertise is a compile error of its own. // does not advertise is a compile error of its own.
if (extensionName == nullptr || glslCode.find(extensionName) != String::npos) { if (extensionName == nullptr || glslCode.find(extensionName) != String::npos) {
return glslCode; return glslCode;
@@ -831,7 +831,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
static const std::regex imageDeclRegex( static const std::regex imageDeclRegex(
R"((layout\s*\(([^)]*)\)\s*)?uniform\s+(?:(?:readonly|writeonly|coherent|volatile|restrict|highp|mediump|lowp)\s+)*[iu]?image[A-Za-z0-9]+\s+([A-Za-z_][A-Za-z0-9_]*)\s*(\[[^\]]*\])?\s*;)"); R"((layout\s*\(([^)]*)\)\s*)?uniform\s+(?:(?:readonly|writeonly|coherent|volatile|restrict|highp|mediump|lowp)\s+)*[iu]?image[A-Za-z0-9]+\s+([A-Za-z_][A-Za-z0-9_]*)\s*(\[[^\]]*\])?\s*;)");
// Every image format spelling GLSL has, so a declaration that already carries one is // Every image format spelling GLSL has, so a declaration that already carries one is
// recognised whatever it says - the caller's map is consulted only for declarations // recognized whatever it says - the caller's map is consulted only for declarations
// with NO format, never to override a written one. // with NO format, never to override a written one.
static const std::regex existingFormatRegex( static const std::regex existingFormatRegex(
R"(\b(rgba32f|rgba16f|rg32f|rg16f|r11f_g11f_b10f|r32f|r16f|rgba16|rgb10_a2|rg16|rg8|r16|r8|rgba16_snorm|rgba8_snorm|rg16_snorm|rg8_snorm|r16_snorm|r8_snorm|rgba32i|rgba16i|rgba8i|rg32i|rg16i|rg8i|r32i|r16i|r8i|rgba32ui|rgba16ui|rgba8ui|rgb10_a2ui|rg32ui|rg16ui|rg8ui|r32ui|r16ui|r8ui)\b)"); R"(\b(rgba32f|rgba16f|rg32f|rg16f|r11f_g11f_b10f|r32f|r16f|rgba16|rgb10_a2|rg16|rg8|r16|r8|rgba16_snorm|rgba8_snorm|rg16_snorm|rg8_snorm|r16_snorm|r8_snorm|rgba32i|rgba16i|rgba8i|rg32i|rg16i|rg8i|r32i|r16i|r8i|rgba32ui|rgba16ui|rgba8ui|rgb10_a2ui|rg32ui|rg16ui|rg8ui|r32ui|r16ui|r8ui)\b)");
@@ -1124,7 +1124,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
source += "precision highp float;\n"; source += "precision highp float;\n";
source += "precision highp int;\n"; source += "precision highp int;\n";
source += "layout(vertices = " + std::to_string(patchVertices) + ") out;\n"; source += "layout(vertices = " + std::to_string(patchVertices) + ") out;\n";
// Mirrored, never invented. An empty member list means the neighbouring stage did not // Mirrored, never invented. An empty member list means the neighboring stage did not
// redeclare the block either, and the driver's own built-in declaration is then what // redeclare the block either, and the driver's own built-in declaration is then what
// both sides agree on - redeclaring here would be the thing that broke the match. // both sides agree on - redeclaring here would be the thing that broke the match.
if (!inPerVertexMembers.empty()) { if (!inPerVertexMembers.empty()) {
@@ -1783,7 +1783,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// //
// Nothing downstream reads these names: the two passes that key on the GL uniform // Nothing downstream reads these names: the two passes that key on the GL uniform
// name (RebindImageUniformsToFrontendUnits, BakeImageFormatQualifiers) both run // name (RebindImageUniformsToFrontendUnits, BakeImageFormatQualifiers) both run
// BEFORE this one, RemoveLayoutBinding recognises an image declaration by its TYPE // BEFORE this one, RemoveLayoutBinding recognizes an image declaration by its TYPE
// token, and CacheResourceLocations skips image uniforms outright because ES image // token, and CacheResourceLocations skips image uniforms outright because ES image
// units come only from layout(binding=N). The declarations this pass LEAVES ALONE - // units come only from layout(binding=N). The declarations this pass LEAVES ALONE -
// already readonly/writeonly in the source, or r32f/r32i/r32ui, which need no // already readonly/writeonly in the source, or r32f/r32i/r32ui, which need no
@@ -1825,7 +1825,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// Both halves carry `coherent`; see BuildImageDeclaration. The // Both halves carry `coherent`; see BuildImageDeclaration. The
// single-declaration cases below stay as they were - nothing aliases them, so // single-declaration cases below stay as they were - nothing aliases them, so
// there is no visibility to restore and no reason to pay for the cache // there is no visibility to restore and no reason to pay for the cache
// behaviour. // behavior.
edits.push_back({decl.declStart, decl.declLength, edits.push_back({decl.declStart, decl.declLength,
BuildImageDeclaration(decl, "readonly", decl.aliasName, BuildImageDeclaration(decl, "readonly", decl.aliasName,
/*forceCoherent=*/true) + /*forceCoherent=*/true) +
+11 -11
View File
@@ -47,8 +47,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
} // namespace VertexArrayImpl } // namespace VertexArrayImpl
namespace TextureImpl { namespace TextureImpl {
// Whether images on this format-capability target can back a colour attachment, and so // Whether images on this format-capability target can back a color attachment, and so
// need a colour-renderable storage format even when the frontend asked for a // need a color-renderable storage format even when the frontend asked for a
// three-channel one ES never renders to. Shared by the capability probe (which passes the // three-channel one ES never renders to. Shared by the capability probe (which passes the
// capabilities it has just queried, before the globals are published) and by the // capabilities it has just queried, before the globals are published) and by the
// allocation path (which reads the active backend's), so the format the cache was probed // allocation path (which reads the active backend's), so the format the cache was probed
@@ -74,7 +74,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
Bool ShouldUseCaveatTextureFormat(TextureInternalFormat internalFormat, TextureTarget target); Bool ShouldUseCaveatTextureFormat(TextureInternalFormat internalFormat, TextureTarget target);
// True when the format the image is actually created with has an alpha channel the // True when the format the image is actually created with has an alpha channel the
// frontend format does not (the three-channel colour-renderable widening). GL reads such // frontend format does not (the three-channel color-renderable widening). GL reads such
// a channel back as 1.0, so any swizzle source of ALPHA has to be answered with ONE and // a channel back as 1.0, so any swizzle source of ALPHA has to be answered with ONE and
// any readback of the image has to overwrite the alpha the draw happened to leave there. // any readback of the image has to overwrite the alpha the draw happened to leave there.
Bool BackendTextureFormatAddsAlpha(TextureInternalFormat internalFormat, TextureTarget target); Bool BackendTextureFormatAddsAlpha(TextureInternalFormat internalFormat, TextureTarget target);
@@ -105,7 +105,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// //
// The widened triple REPLACES what GenerateTextureFormatInfo chose, including any // The widened triple REPLACES what GenerateTextureFormatInfo chose, including any
// renderability substitution: an image that cannot be image-bound is useless whatever its // renderability substitution: an image that cannot be image-bound is useless whatever its
// attachment behaviour, so the image constraint wins. In practice that only bites // attachment behavior, so the image constraint wins. In practice that only bites
// RG8_SNORM/R8_SNORM on a driver without EXT_render_snorm, where the storage stays // RG8_SNORM/R8_SNORM on a driver without EXT_render_snorm, where the storage stays
// signed-normalized instead of becoming the half float that fallback would have picked - // signed-normalized instead of becoming the half float that fallback would have picked -
// so an image-bound texture in one of those two formats is no longer attachable, and // so an image-bound texture in one of those two formats is no longer attachable, and
@@ -115,11 +115,11 @@ namespace MobileGL::MG_Backend::DirectGLES {
// compiled at all, so nothing that works today is being given up. // compiled at all, so nothing that works today is being given up.
// //
// KNOWN GAP, for the same "all three layers move together" reason: a widened texture that // KNOWN GAP, for the same "all three layers move together" reason: a widened texture that
// is ALSO an FBO colour attachment gains one to three writable channels, and a draw into // is ALSO an FBO color attachment gains one to three writable channels, and a draw into
// it can leave values in channels GL says are 0 and 1. Sampling and imageLoad are covered // it can leave values in channels GL says are 0 and 1. Sampling and imageLoad are covered
// (the swizzle composition in SyncTextureParamsToBackend and the shader-side mask), but a // (the swizzle composition in SyncTextureParamsToBackend and the shader-side mask), but a
// glReadPixels/glGetTexImage that asks for more channels than the frontend format has // glReadPixels/glGetTexImage that asks for more channels than the frontend format has
// would see them. Closing it needs the per-draw-buffer colour mask the three-channel // would see them. Closing it needs the per-draw-buffer color mask the three-channel
// widening already carries (FramebufferImpl::g_alphaWidenedDrawBufferMask) generalized // widening already carries (FramebufferImpl::g_alphaWidenedDrawBufferMask) generalized
// from "alpha" to a channel count, which is its own change. // from "alpha" to a channel count, which is its own change.
// How the FRONTEND's CPU shadow for a widened format is laid out relative to the carrier's // How the FRONTEND's CPU shadow for a widened format is laid out relative to the carrier's
@@ -319,7 +319,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// Exists so BuildPassthroughTessControlEssl can MIRROR the stages it has to sit between // Exists so BuildPassthroughTessControlEssl can MIRROR the stages it has to sit between
// rather than guess at them. Whether SPIRV-Cross redeclares the built-in block, and with // rather than guess at them. Whether SPIRV-Cross redeclares the built-in block, and with
// which members, depends on what the application's shader touched; a synthesized stage // which members, depends on what the application's shader touched; a synthesized stage
// that redeclares a different shape than its neighbours is an ES link error against a // that redeclares a different shape than its neighbors is an ES link error against a
// program that has no other problem. // program that has no other problem.
std::optional<String> ExtractPerVertexBlockMembers(const String& essl, Bool input); std::optional<String> ExtractPerVertexBlockMembers(const String& essl, Bool input);
// Moves the clip/cull distance OUTPUTS of a vertex, tessellation evaluation or geometry // Moves the clip/cull distance OUTPUTS of a vertex, tessellation evaluation or geometry
@@ -365,15 +365,15 @@ namespace MobileGL::MG_Backend::DirectGLES {
// bound in its place, and every draw silently renders nothing. // bound in its place, and every draw silently renders nothing.
// //
// `inPerVertexMembers` / `outPerVertexMembers` are the member lists to redeclare gl_in // `inPerVertexMembers` / `outPerVertexMembers` are the member lists to redeclare gl_in
// and gl_out with - normally taken from the neighbouring stages' own emitted ESSL via // and gl_out with - normally taken from the neighboring stages' own emitted ESSL via
// ExtractPerVertexBlockMembers, and empty to leave the driver's built-in declaration // ExtractPerVertexBlockMembers, and empty to leave the driver's built-in declaration
// alone, which is what matching a neighbour that did not redeclare requires. // alone, which is what matching a neighbor that did not redeclare requires.
// //
// All four outer levels and both inner levels are written unconditionally: writing a // All four outer levels and both inner levels are written unconditionally: writing a
// level the evaluation stage's domain does not use is legal and ignored, and it saves // level the evaluation stage's domain does not use is legal and ignored, and it saves
// this from having to know the domain. They are the GL_PATCH_DEFAULT_OUTER_LEVEL / // this from having to know the domain. They are the GL_PATCH_DEFAULT_OUTER_LEVEL /
// GL_PATCH_DEFAULT_INNER_LEVEL state, baked in as literals - ES has no such state and no // GL_PATCH_DEFAULT_INNER_LEVEL state, baked in as literals - ES has no such state and no
// glPatchParameterfv to forward to, so compiling them in is the only way to honour them. // glPatchParameterfv to forward to, so compiling them in is the only way to honor them.
// That makes them part of what a built program is stale against, exactly as PATCH_VERTICES // That makes them part of what a built program is stale against, exactly as PATCH_VERTICES
// is: see the staleness clause in DirectGLES.cpp's SyncCurrentProgram, which compares both. // is: see the staleness clause in DirectGLES.cpp's SyncCurrentProgram, which compares both.
// //
@@ -524,7 +524,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// suffix is the sampler's own name. // suffix is the sampler's own name.
constexpr const char* MS_SAMPLE_MAX_UNIFORM_PREFIX = "mg_msSampleMax_"; constexpr const char* MS_SAMPLE_MAX_UNIFORM_PREFIX = "mg_msSampleMax_";
// The runtime half of ClampMultisampleFetchPass's deferDirectUnsignedFetches. A // The runtime half of ClampMultisampleFetchPass's deferDirectUnsignedFetches. A
// usampler2DMS[Array] reads integer colour textures, which the backend may allocate with // usampler2DMS[Array] reads integer color textures, which the backend may allocate with
// fewer samples than MobileGL advertises, AND the stencil aspect of multisample // fewer samples than MobileGL advertises, AND the stencil aspect of multisample
// depth/stencil textures, which it allocates under the (larger) depth ceiling - so no // depth/stencil textures, which it allocates under the (larger) depth ceiling - so no
// single compile-time bound is right for both. Declares one // single compile-time bound is right for both. Declares one
@@ -878,7 +878,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
E_GL_ARB_texture_rg, E_GL_ARB_texture_rg,
// GL_DEPTH_COMPONENT32F and GL_DEPTH32F_STENCIL8. // GL_DEPTH_COMPONENT32F and GL_DEPTH32F_STENCIL8.
E_GL_ARB_depth_buffer_float, E_GL_ARB_depth_buffer_float,
// The floating-point colour formats. Unlike the rest of this block this string DOES // The floating-point color formats. Unlike the rest of this block this string DOES
// gate CTS cases - KHR-GL4*.internalformat.texture2d.*{16f,32f} is keyed on it with no // gate CTS cases - KHR-GL4*.internalformat.texture2d.*{16f,32f} is keyed on it with no
// core-version fallback, so eight cases per version list were NotSupported on formats // core-version fallback, so eight cases per version list were NotSupported on formats
// the backend has always had. // the backend has always had.
@@ -895,7 +895,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// - multiple capture buffers per stream, gl_SkipComponents / gl_NextBuffer, the // - multiple capture buffers per stream, gl_SkipComponents / gl_NextBuffer, the
// indexed queries and glDrawTransformFeedbackStream (4.0); // indexed queries and glDrawTransformFeedbackStream (4.0);
E_GL_ARB_transform_feedback3, E_GL_ARB_transform_feedback3,
// - GL_RGB10_A2UI (3.3), GL_RGB9_E5 (3.0) and the integer colour formats (3.0); // - GL_RGB10_A2UI (3.3), GL_RGB9_E5 (3.0) and the integer color formats (3.0);
E_GL_ARB_texture_rgb10_a2ui, E_GL_EXT_texture_shared_exponent, E_GL_EXT_texture_integer, E_GL_ARB_texture_rgb10_a2ui, E_GL_EXT_texture_shared_exponent, E_GL_EXT_texture_integer,
// - the eleven pipeline-statistics query targets (4.6). Accepted by glBeginQuery and // - the eleven pipeline-statistics query targets (4.6). Accepted by glBeginQuery and
// answered with GL_QUERY_COUNTER_BITS = 0, the spec's own "this counter is not // answered with GL_QUERY_COUNTER_BITS = 0, the spec's own "this counter is not
@@ -941,7 +941,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// recorded trace can never cover: Iris and Sodium change their SUBMISSION SCHEDULE // recorded trace can never cover: Iris and Sodium change their SUBMISSION SCHEDULE
// the moment they see it - they enqueue whole pipeline batches and poll // the moment they see it - they enqueue whole pipeline batches and poll
// GL_COMPLETION_STATUS_KHR instead of compiling one program at a time - so // GL_COMPLETION_STATUS_KHR instead of compiling one program at a time - so
// MOBILEGL_ASYNC_SHADER_COMPILE=0 has to withdraw the application-visible behaviour // MOBILEGL_ASYNC_SHADER_COMPILE=0 has to withdraw the application-visible behavior
// change as well as the threading, or the kill switch would only be half a switch. // change as well as the threading, or the kill switch would only be half a switch.
if (MG_Util::Async::AsyncShaderCompileEnabled()) { if (MG_Util::Async::AsyncShaderCompileEnabled()) {
extensions.push_back(E_GL_KHR_parallel_shader_compile); extensions.push_back(E_GL_KHR_parallel_shader_compile);
@@ -238,7 +238,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// GetProgramResourceCache does it - over a multi-stage program with an arrayed SSBO and an // GetProgramResourceCache does it - over a multi-stage program with an arrayed SSBO and an
// atomic-counter block, and asserts element-for-element equality with what the archive // atomic-counter block, and asserts element-for-element equality with what the archive
// published. That equivalence is what makes an #if here a build-time SELECTION rather than // published. That equivalence is what makes an #if here a build-time SELECTION rather than
// a behavioural fork, and it is the reason the case exists. // a behavioral fork, and it is the reason the case exists.
// //
// WHY AN #if AT ALL, AND WHAT IT COSTS. LinkArtifacts::storageBlocks is // WHY AN #if AT ALL, AND WHAT IT COSTS. LinkArtifacts::storageBlocks is
// MOBILEGL_BUILD_DISAGGREGATED-only, because one more Vector member changes the struct's // MOBILEGL_BUILD_DISAGGREGATED-only, because one more Vector member changes the struct's
@@ -263,7 +263,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
}; };
auto it = find(name); auto it = find(name);
if (it == blocks.end()) { if (it == blocks.end()) {
// Archive names are normalised, so an arrayed block that GL enumerates per element // Archive names are normalized, so an arrayed block that GL enumerates per element
// - "B[0]", "B[1]" - is one entry here, spelled "B". Same second chance the cache // - "B[0]", "B[1]" - is one entry here, spelled "B". Same second chance the cache
// gave, for the same callers. // gave, for the same callers.
const auto bracket = name.rfind('['); const auto bracket = name.rfind('[');
@@ -14,7 +14,7 @@
// //
// SessionLocal<T> is that copy. It resolves through the CALLING THREAD's bound MagmaSession, and to // SessionLocal<T> is that copy. It resolves through the CALLING THREAD's bound MagmaSession, and to
// the process-wide default session on every thread nothing bound - which is the monolith, the client // the process-wide default session on every thread nothing bound - which is the monolith, the client
// role and every unit case, i.e. exactly the old behaviour, on whatever thread the app renders from. // role and every unit case, i.e. exactly the old behavior, on whatever thread the app renders from.
// A served session's backend owns its MagmaSession and binds it on its apply thread (the only thread // A served session's backend owns its MagmaSession and binds it on its apply thread (the only thread
// that runs that session's backend), so the two never mix. // that runs that session's backend), so the two never mix.
@@ -203,7 +203,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
} }
// The barrier belongs in the frame's own recording. Bailing out because // The barrier belongs in the frame's own recording. Bailing out because
// something was already recorded (the previous behaviour) dropped the // something was already recorded (the previous behavior) dropped the
// transition entirely for every frame that never ran a default-framebuffer // transition entirely for every frame that never ran a default-framebuffer
// render pass - the only other thing that carries the image to // render pass - the only other thing that carries the image to
// PRESENT_SRC_KHR, via that pass's finalLayout - so the swapchain image was // PRESENT_SRC_KHR, via that pass's finalLayout - so the swapchain image was
@@ -165,7 +165,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// wire draw path entirely - this header no longer includes spirv_reflect.h at all. // wire draw path entirely - this header no longer includes spirv_reflect.h at all.
Uint GetShaderStorageBlockIndex(const String& name) const { Uint GetShaderStorageBlockIndex(const String& name) const {
const auto& blocks = Link().storageBlocks; const auto& blocks = Link().storageBlocks;
// The archive carries NORMALISED names (the array subscript stripped), because // The archive carries NORMALIZED names (the array subscript stripped), because
// that is the spelling SPIRV-Reflect's type_name gives and the spelling // that is the spelling SPIRV-Reflect's type_name gives and the spelling
// ProgramFactory::ReflectLayout looks up. A caller that still holds a GL-style // ProgramFactory::ReflectLayout looks up. A caller that still holds a GL-style
// "Foo[2]" gets the same second chance it always had. // "Foo[2]" gets the same second chance it always had.
@@ -452,7 +452,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// call was DEBUG-or-env gated and only logged, so an invalid module produced by a backend // call was DEBUG-or-env gated and only logged, so an invalid module produced by a backend
// transform went straight to vkCreateShaderModule. That is not a survivable outcome on // transform went straight to vkCreateShaderModule. That is not a survivable outcome on
// this hardware - Mali r54 SIGSEGVs building the pipeline instead of returning an error, // this hardware - Mali r54 SIGSEGVs building the pipeline instead of returning an error,
// the same "not a validating entry point" behaviour PipelineFactory already documents for // the same "not a validating entry point" behavior PipelineFactory already documents for
// vkCreateGraphicsPipelines - so the callers that feed the driver now act on it. // vkCreateGraphicsPipelines - so the callers that feed the driver now act on it.
// //
// This function does NOT log the failure at E any more. It used to, unlatched, on the // This function does NOT log the failure at E any more. It used to, unlatched, on the
@@ -972,7 +972,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// //
// Deliberately conservative - it answers "assume yes" for every shape it cannot read // Deliberately conservative - it answers "assume yes" for every shape it cannot read
// exactly, because a false "no" would silently drop the clip-space fixup from a shader // exactly, because a false "no" would silently drop the clip-space fixup from a shader
// that does write gl_Position, while a false "yes" only reinstates the behaviour this // that does write gl_Position, while a false "yes" only reinstates the behavior this
// pass has always had. Scans every function rather than just the entry point's: a shader // pass has always had. Scans every function rather than just the entry point's: a shader
// that assigns gl_Position inside a helper is still a shader that writes it, and passing // that assigns gl_Position inside a helper is still a shader that writes it, and passing
// the pointer to a call is a write as far as this can tell. // the pointer to a call is a write as far as this can tell.
@@ -1089,7 +1089,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
if (!FindPositionTarget(context(), &target)) return Status::SuccessWithoutChange; if (!FindPositionTarget(context(), &target)) return Status::SuccessWithoutChange;
// Nothing to remap in a Position the shader never writes. Declining is not just // Nothing to remap in a Position the shader never writes. Declining is not just
// an optimisation: the fixup is load-modify-store, so on an unwritten Position it // an optimization: the fixup is load-modify-store, so on an unwritten Position it
// converts "undefined, never written" into "written with whatever the load // converts "undefined, never written" into "written with whatever the load
// returned", and the store is a reference to a variable the link-time sanitize // returned", and the store is a reference to a variable the link-time sanitize
// chain has already delisted from the entry-point interface. glslang emits the // chain has already delisted from the entry-point interface. glslang emits the
@@ -1165,7 +1165,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// ivec2(gl_FragCoord.xy)) into an image exactly the size of that viewport, so every // ivec2(gl_FragCoord.xy)) into an image exactly the size of that viewport, so every
// store fell outside the image and the test read back zeroes. // store fell outside the image and the test read back zeroes.
// //
// The rewrite redirects every read of the builtin to a Private copy initialised once at // The rewrite redirects every read of the builtin to a Private copy initialized once at
// entry, which is exact for all access forms (whole-vector loads, `.y` access chains, // entry, which is exact for all access forms (whole-vector loads, `.y` access chains,
// OpCopyMemory) and leaves the builtin itself - and its decorations - untouched. // OpCopyMemory) and leaves the builtin itself - and its decorations - untouched.
class GlFragCoordYFlipPass final : public spvtools::opt::Pass { class GlFragCoordYFlipPass final : public spvtools::opt::Pass {
@@ -1233,7 +1233,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
{SPV_OPERAND_TYPE_STORAGE_CLASS, {static_cast<Uint32>(spv::StorageClass::Private)}}}); {SPV_OPERAND_TYPE_STORAGE_CLASS, {static_cast<Uint32>(spv::StorageClass::Private)}}});
context()->AddGlobalValue(std::move(copyVar)); context()->AddGlobalValue(std::move(copyVar));
// Redirect the reads BEFORE emitting the initialiser, so the initialiser's own // Redirect the reads BEFORE emitting the initializer, so the initializer's own
// load of the builtin is not rewritten into a load of the (still empty) copy. // load of the builtin is not rewritten into a load of the (still empty) copy.
if (!RedirectReads(builtinVarId, copyVarId)) return Status::SuccessWithoutChange; if (!RedirectReads(builtinVarId, copyVarId)) return Status::SuccessWithoutChange;
@@ -1688,7 +1688,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
}; };
// Adreno 650 (driver 512.502) faults the GPU on an implicit-LOD sample of a full-screen // Adreno 650 (driver 512.502) faults the GPU on an implicit-LOD sample of a full-screen
// colour render target: the texture unit's derivative path reads outside the image's // color render target: the texture unit's derivative path reads outside the image's
// allocation even though the sampler clamps LOD to 0 and the mapping is 1:1. MobileGL's // allocation even though the sampler clamps LOD to 0 and the mapping is 1:1. MobileGL's
// own default-framebuffer blit shader works around it with textureLod, but an // own default-framebuffer blit shader works around it with textureLod, but an
// application's shader (Minecraft's blit.fsh is `texture(InSampler, texCoord)`) cannot be // application's shader (Minecraft's blit.fsh is `texture(InSampler, texCoord)`) cannot be
@@ -2146,7 +2146,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// GetOrCreateProgram's MOBILEGL_ASSERT(remapOk) as the only complaint, and // GetOrCreateProgram's MOBILEGL_ASSERT(remapOk) as the only complaint, and
// that assert compiles out above DEBUG - so a release build SILENTLY kept // that assert compiles out above DEBUG - so a release build SILENTLY kept
// glslang's per-stage auto-mapped binding numbers, skipping the cross-stage // glslang's per-stage auto-mapped binding numbers, skipping the cross-stage
// unification and the set->0 normalisation this function exists to do. A // unification and the set->0 normalization this function exists to do. A
// program with an image array plus any second descriptor got aliased // program with an image array plus any second descriptor got aliased
// bindings out of that, and a DEBUG build trapped on the same program. // bindings out of that, and a DEBUG build trapped on the same program.
// The refusal below is MGLOG_E and per-program-compile, so it reports every // The refusal below is MGLOG_E and per-program-compile, so it reports every
@@ -2989,7 +2989,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
MGLOG_D("ProgramFactory::ReflectLayout: %s array '%s' at binding %u spans %u descriptors but the " MGLOG_D("ProgramFactory::ReflectLayout: %s array '%s' at binding %u spans %u descriptors but the "
"reflection reserved fewer uniform locations for it (base=%d) - a multi-dimensional array " "reflection reserved fewer uniform locations for it (base=%d) - a multi-dimensional array "
"is the usual cause, and MobileGL declines it rather than resolve elements onto a " "is the usual cause, and MobileGL declines it rather than resolve elements onto a "
"neighbouring uniform", "neighboring uniform",
kindLabel, uniformName.c_str(), binding, count, baseLocation); kindLabel, uniformName.c_str(), binding, count, baseLocation);
outDeclined = true; outDeclined = true;
} }
@@ -3530,7 +3530,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
"ProgramFactory::ReflectLayout, vkCreatePipelineLayout"); "ProgramFactory::ReflectLayout, vkCreatePipelineLayout");
// Built here rather than where bindingKinds is sized: at that point the vector is only // Built here rather than where bindingKinds is sized: at that point the vector is only
// zero-initialised and the kinds are assigned further down, so a list built there would be // zero-initialized and the kinds are assigned further down, so a list built there would be
// empty. Ascending by construction because the index walks upward. // empty. Ascending by construction because the index walks upward.
entry.activeBindings.clear(); entry.activeBindings.clear();
for (Uint32 binding = 0; binding < static_cast<Uint32>(entry.bindingKinds.size()); ++binding) { for (Uint32 binding = 0; binding < static_cast<Uint32>(entry.bindingKinds.size()); ++binding) {
@@ -4128,7 +4128,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
ReflectPassthroughTessControlNeed(stages, moduleSpirvs, entry); ReflectPassthroughTessControlNeed(stages, moduleSpirvs, entry);
ReflectLayout(program, moduleSpirvs, entry); ReflectLayout(program, moduleSpirvs, entry);
// A failed remap means the modules kept glslang's per-stage auto-mapped binding numbers - // A failed remap means the modules kept glslang's per-stage auto-mapped binding numbers -
// no cross-stage unification, no set->0 normalisation - so the bindings this layout // no cross-stage unification, no set->0 normalization - so the bindings this layout
// describes are not the bindings the shader reads. That has to stop the program from // describes are not the bindings the shader reads. That has to stop the program from
// drawing, and until now nothing did: the MOBILEGL_ASSERT above compiles out of every // drawing, and until now nothing did: the MOBILEGL_ASSERT above compiles out of every
// build past DEBUG, and RemapDescriptorBindingsForVulkan's own refusal message said so at // build past DEBUG, and RemapDescriptorBindingsForVulkan's own refusal message said so at
@@ -4328,7 +4328,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// PATCH_DEFAULT_OUTER_LEVEL / PATCH_DEFAULT_INNER_LEVEL state. // PATCH_DEFAULT_OUTER_LEVEL / PATCH_DEFAULT_INNER_LEVEL state.
// //
// Those two levels are baked in as literals - Vulkan has no equivalent dynamic state, so // Those two levels are baked in as literals - Vulkan has no equivalent dynamic state, so
// compiling them in is the only way to honour glPatchParameterfv. That makes them part of // compiling them in is the only way to honor glPatchParameterfv. That makes them part of
// this module's identity: GetOrCreatePassthroughTessControlStage keys its cache on them, // this module's identity: GetOrCreatePassthroughTessControlStage keys its cache on them,
// and PipelineFactory hashes them into the pipeline key. The three must move together. // and PipelineFactory hashes them into the pipeline key. The three must move together.
// //
@@ -4344,7 +4344,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// this from having to know the domain. // this from having to know the domain.
String source = "#version 450 core\n"; String source = "#version 450 core\n";
source += "layout(vertices = " + std::to_string(patchVertices) + ") out;\n"; source += "layout(vertices = " + std::to_string(patchVertices) + ") out;\n";
// gl_in and gl_out are redeclared to the exact gl_PerVertex the NEIGHBOURING EVALUATION // gl_in and gl_out are redeclared to the exact gl_PerVertex the NEIGHBORING EVALUATION
// STAGE carries, because Vulkan matches built-in interface blocks by their whole shape, // STAGE carries, because Vulkan matches built-in interface blocks by their whole shape,
// and the two obvious spellings are both wrong: // and the two obvious spellings are both wrong:
// * narrowing the block to gl_Position alone makes the evaluation stage read a patch of // * narrowing the block to gl_Position alone makes the evaluation stage read a patch of
@@ -92,7 +92,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// The gl_PerVertex members a pass-through tessellation control stage may have to carry, // The gl_PerVertex members a pass-through tessellation control stage may have to carry,
// in the order glslang declares them - which is the order a redeclaration must use. // in the order glslang declares them - which is the order a redeclaration must use.
// Which of them exist is a function of the neighbouring stage's GLSL VERSION // Which of them exist is a function of the neighboring stage's GLSL VERSION
// (gl_CullDistance joins the block at #version 450), so the mask is read off that // (gl_CullDistance joins the block at #version 450), so the mask is read off that
// stage's SPIR-V rather than assumed. See ReflectPerVertexInputMembers. // stage's SPIR-V rather than assumed. See ReflectPerVertexInputMembers.
enum class PerVertexMemberBit : Uint32 { enum class PerVertexMemberBit : Uint32 {
@@ -647,7 +647,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// member set, because all three decide what the generator emits. The size comes from // member set, because all three decide what the generator emits. The size comes from
// PATCH_VERTICES and the levels from PATCH_DEFAULT_OUTER_LEVEL / PATCH_DEFAULT_INNER_LEVEL // PATCH_VERTICES and the levels from PATCH_DEFAULT_OUTER_LEVEL / PATCH_DEFAULT_INNER_LEVEL
// - draw state rather than link state, and the CTS case that motivated this links at the // - draw state rather than link state, and the CTS case that motivated this links at the
// default 3 and draws at 4. The member set comes from the neighbouring evaluation stage's // default 3 and draws at 4. The member set comes from the neighboring evaluation stage's
// own SPIR-V, so two programs at different GLSL versions need different modules. The // own SPIR-V, so two programs at different GLSL versions need different modules. The
// pipeline cache re-keys on the same inputs, so the module a pipeline was built with is // pipeline cache re-keys on the same inputs, so the module a pipeline was built with is
// part of that pipeline's identity. Compiling is bounded by the number of distinct // part of that pipeline's identity. Compiling is bounded by the number of distinct
@@ -66,9 +66,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// EGL content-validity tracking for the default framebuffer. A color // EGL content-validity tracking for the default framebuffer. A color
// buffer's content is undefined once its image has been presented // buffer's content is undefined once its image has been presented
// (EGL_BUFFER_DESTROYED swap behaviour, the implementation default), // (EGL_BUFFER_DESTROYED swap behavior, the implementation default),
// and every ancillary (depth/stencil) buffer's content is undefined // and every ancillary (depth/stencil) buffer's content is undefined
// after ANY swap regardless of swap behaviour (EGL 1.5 §3.10.1). The // after ANY swap regardless of swap behavior (EGL 1.5 §3.10.1). The
// render-pass manager turns an undefined attachment's tile load into // render-pass manager turns an undefined attachment's tile load into
// LOAD_OP_DONT_CARE. Flags start false (a fresh swapchain image holds // LOAD_OP_DONT_CARE. Flags start false (a fresh swapchain image holds
// garbage) and a render pass storing into an attachment sets it back // garbage) and a render pass storing into an attachment sets it back
@@ -430,7 +430,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// submission still carries, and run_trace_case.cmake reds a split retrace on any // submission still carries, and run_trace_case.cmake reds a split retrace on any
// "MGWIRE-FLOOR unsound-serial-complete" line, which is that fix's lane. // "MGWIRE-FLOOR unsound-serial-complete" line, which is that fix's lane.
// //
// THE SUBMISSION TERM IS A PROBE AND A DEFENCE, NOT A SECOND GUARANTEE. It is not sound // THE SUBMISSION TERM IS A PROBE AND A DEFENSE, NOT A SECOND GUARANTEE. It is not sound
// on its own: lastUseSubmitIndex is stamped at AcquireWireSlice with the next submission // on its own: lastUseSubmitIndex is stamped at AcquireWireSlice with the next submission
// index, but the draw that acquired the slice can still be pushed into a LATER // index, but the draw that acquired the slice can still be pushed into a LATER
// submission - BindProgramUniformBuffers -> SyncWireTextureShape's preserve path -> // submission - BindProgramUniformBuffers -> SyncWireTextureShape's preserve path ->
@@ -458,7 +458,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
} else if (busyBySerial || busyBySubmission) { } else if (busyBySerial || busyBySubmission) {
// Vulkan buffer copies require four-byte aligned ranges. An odd GL // Vulkan buffer copies require four-byte aligned ranges. An odd GL
// byte update waits, then writes only the exact range; rounding it // byte update waits, then writes only the exact range; rounding it
// from a CPU shadow could overwrite neighbouring GPU-written bytes. // from a CPU shadow could overwrite neighboring GPU-written bytes.
if (((offset | size) & 3u) == 0) { if (((offset | size) & 3u) == 0) {
uploaded = StagedWireRangeCopy(*resource, bytes, static_cast<SizeT>(offset), static_cast<SizeT>(size)); uploaded = StagedWireRangeCopy(*resource, bytes, static_cast<SizeT>(offset), static_cast<SizeT>(size));
} }
@@ -589,7 +589,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
} }
// B3: the draw this slice is being acquired for is normally recorded into the NEXT // B3: the draw this slice is being acquired for is normally recorded into the NEXT
// submission - normally, not always: a mid-draw flush (SyncWireTextureShape's preserve // submission - normally, not always: a mid-draw flush (SyncWireTextureShape's preserve
// path) can submit this index without the draw. This stamp is the defence term's input // path) can submit this index without the draw. This stamp is the defense term's input
// (see WriteWireBuffer); the floor behind lastUseSerial is the guarantee. Read off the // (see WriteWireBuffer); the floor behind lastUseSerial is the guarantee. Read off the
// owning renderer's counter (its GetWireNextSubmitIndex()) when it was handed one. // owning renderer's counter (its GetWireNextSubmitIndex()) when it was handed one.
if (m_submitCounter != nullptr) if (m_submitCounter != nullptr)
@@ -192,7 +192,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
Uint64 size = 0; Uint64 size = 0;
Uint64 lastUseSerial = 0; Uint64 lastUseSerial = 0;
// P7 wave 2 package B3: the submission expected to carry the most recent GPU use // P7 wave 2 package B3: the submission expected to carry the most recent GPU use
// of this buffer - the busy predicate's DEFENCE term, not its guarantee (see // of this buffer - the busy predicate's DEFENSE term, not its guarantee (see
// WriteWireBuffer). IsSubmitIndexComplete polls the real fence and reports an // WriteWireBuffer). IsSubmitIndexComplete polls the real fence and reports an
// unsubmitted index as incomplete, but the stamp is taken before the draw is // unsubmitted index as incomplete, but the stamp is taken before the draw is
// recorded and a mid-draw flush can submit it without the draw. // recorded and a mid-draw flush can submit it without the draw.
@@ -57,7 +57,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
if (MG_Pipe::gPipeInputs->IsCapabilityEnabled(MobileGL::CapabilityInput::FramebufferSrgb)) return; if (MG_Pipe::gPipeInputs->IsCapabilityEnabled(MobileGL::CapabilityInput::FramebufferSrgb)) return;
if (ResolveSrgbAttachmentWriteFormat(destinationFormat, false) == destinationFormat) return; if (ResolveSrgbAttachmentWriteFormat(destinationFormat, false) == destinationFormat) return;
// sRGB -> linear (GL 4.6 core 8.24), applied to the colour channels only: alpha is stored // sRGB -> linear (GL 4.6 core 8.24), applied to the color channels only: alpha is stored
// linearly in an sRGB format and must pass through untouched. // linearly in an sRGB format and must pass through untouched.
const auto toLinear = [](Float encoded) { const auto toLinear = [](Float encoded) {
const Float value = std::clamp(encoded, 0.0f, 1.0f); const Float value = std::clamp(encoded, 0.0f, 1.0f);
@@ -24,7 +24,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
Uint32 stencil{}; Uint32 stencil{};
}; };
// A colour clear reaches us from one of glClear/ClearBufferfv, ClearBufferiv or // A color clear reaches us from one of glClear/ClearBufferfv, ClearBufferiv or
// ClearBufferuiv, and Vulkan reads VkClearColorValue's union according to the destination // ClearBufferuiv, and Vulkan reads VkClearColorValue's union according to the destination
// image's format rather than converting between the members - a float written where an // image's format rather than converting between the members - a float written where an
// integer format is expected is reinterpreted bit for bit, not rounded. Remember which entry // integer format is expected is reinterpreted bit for bit, not rounded. Remember which entry
@@ -53,7 +53,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// vkCmdClearColorImage names the image, so the driver applies the destination format's transfer // vkCmdClearColorImage names the image, so the driver applies the destination format's transfer
// function to whatever value it is handed. Every other write path in this backend goes through // function to whatever value it is handed. Every other write path in this backend goes through
// the UNORM twin view while GL_FRAMEBUFFER_SRGB is off (ResolveSrgbAttachmentWriteFormat) and // the UNORM twin view while GL_FRAMEBUFFER_SRGB is off (ResolveSrgbAttachmentWriteFormat) and
// therefore stores the raw value GL asked for. Rewrites `payload` to the linear colour whose // therefore stores the raw value GL asked for. Rewrites `payload` to the linear color whose
// encoding is that raw value, so a direct image clear of an sRGB destination agrees with them. // encoding is that raw value, so a direct image clear of an sRGB destination agrees with them.
// A no-op for every other format, for integer clear encodings, and when GL is doing the // A no-op for every other format, for integer clear encodings, and when GL is doing the
// encoding itself. // encoding itself.
@@ -28,7 +28,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// ResolveAttachmentLayerCount lives in VkTextureManager.h, beside ToVulkanLevelExtent, because // ResolveAttachmentLayerCount lives in VkTextureManager.h, beside ToVulkanLevelExtent, because
// VkClearManager needs the SAME answer: its pending-clear key's layerCount becomes a real // VkClearManager needs the SAME answer: its pending-clear key's layerCount becomes a real
// VkImageSubresourceRange when a clear is materialised outside a render pass. See the header. // VkImageSubresourceRange when a clear is materialized outside a render pass. See the header.
DepthStencilAttachmentLoadInfo ResolveDepthStencilAttachmentLoadInfo(VkImageLayout trackedLayout, Bool clearDepth, DepthStencilAttachmentLoadInfo ResolveDepthStencilAttachmentLoadInfo(VkImageLayout trackedLayout, Bool clearDepth,
Bool clearStencil) { Bool clearStencil) {
@@ -81,7 +81,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// which std::swap(pair&, pair&) requires. That was invisible while UnorderedMap // which std::swap(pair&, pair&) requires. That was invisible while UnorderedMap
// only ever move-CONSTRUCTED an element into a fresh slot. ska::flat_hash_map // only ever move-CONSTRUCTED an element into a fresh slot. ska::flat_hash_map
// probes robin-hood: inserting swaps the entry being placed against the one // probes robin-hood: inserting swaps the entry being placed against the one
// already sitting in the slot whenever it has travelled further from its desired // already sitting in the slot whenever it has traveled further from its desired
// position, so the mapped type has to be swappable or the table fails to // position, so the mapped type has to be swappable or the table fails to
// instantiate at all. // instantiate at all.
// //
@@ -139,7 +139,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// Unsigned packed floats: no sign bit at all. 65024 is the largest 11-bit float; the // Unsigned packed floats: no sign bit at all. 65024 is the largest 11-bit float; the
// 10-bit blue channel tops out lower (64512) and RGB9E5 higher (65408), but the bound // 10-bit blue channel tops out lower (64512) and RGB9E5 higher (65408), but the bound
// that matters for correctness is the lower one, and a single conservative upper bound // that matters for correctness is the lower one, and a single conservative upper bound
// costs nothing a real border colour will ever notice. // costs nothing a real border color will ever notice.
case TextureInternalFormat::R11FG11FB10F: case TextureInternalFormat::R11FG11FB10F:
return {true, 0.0f, 64512.0f}; return {true, 0.0f, 64512.0f};
case TextureInternalFormat::RGB9E5: case TextureInternalFormat::RGB9E5:
@@ -152,7 +152,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// Per-component representable range of an integer texture format, as Int64 so that the whole // Per-component representable range of an integer texture format, as Int64 so that the whole
// signed and unsigned 32-bit ranges are expressible in one type and the clamp can be written // signed and unsigned 32-bit ranges are expressible in one type and the clamp can be written
// once for both domains. Alpha is carried separately because RGB10_A2UI is the one format // once for both domains. Alpha is carried separately because RGB10_A2UI is the one format
// whose alpha is narrower than its colour channels. // whose alpha is narrower than its color channels.
struct IntegerBorderRange { struct IntegerBorderRange {
Int64 rgbMin = 0; Int64 rgbMin = 0;
Int64 rgbMax = 0; Int64 rgbMax = 0;
@@ -360,7 +360,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
const auto compareFunc = sampler.GetSamplerCompareFunc(); const auto compareFunc = sampler.GetSamplerCompareFunc();
XXHASH_VERIFY(XXH64_update(hashState, &compareFunc, sizeof(compareFunc))); XXHASH_VERIFY(XXH64_update(hashState, &compareFunc, sizeof(compareFunc)));
// The resolved enum AND, when it is one of the *_CUSTOM_EXT values, the sixteen bytes of the // The resolved enum AND, when it is one of the *_CUSTOM_EXT values, the sixteen bytes of the
// colour itself: two samplers that differ only in a custom border colour carry the same enum // color itself: two samplers that differ only in a custom border color carry the same enum
// and would otherwise collide onto whichever one was created first. // and would otherwise collide onto whichever one was created first.
XXHASH_VERIFY(XXH64_update(hashState, &borderColor.color, sizeof(borderColor.color))); XXHASH_VERIFY(XXH64_update(hashState, &borderColor.color, sizeof(borderColor.color)));
if (borderColor.isCustom) { if (borderColor.isCustom) {
@@ -512,14 +512,14 @@ namespace MobileGL::MG_Backend::DirectVulkan {
return resolved; // FLOAT_TRANSPARENT_BLACK, never sampled return resolved; // FLOAT_TRANSPARENT_BLACK, never sampled
} }
// Border colour is sampler state: a bound sampler object supplies its own, and a texture // Border color is sampler state: a bound sampler object supplies its own, and a texture
// with none reaches the very same value through the sampler object it owns. // with none reaches the very same value through the sampler object it owns.
const auto domain = ResolveBorderColorDomain(format); const auto domain = ResolveBorderColorDomain(format);
const Bool canUseCustom = m_customBorderColorSupported && m_maxCustomBorderColorSamplers > 0 && const Bool canUseCustom = m_customBorderColorSupported && m_maxCustomBorderColorSamplers > 0 &&
m_customBorderColorSamplerCount < m_maxCustomBorderColorSamplers; m_customBorderColorSamplerCount < m_maxCustomBorderColorSamplers;
if (domain != BorderColorDomain::Float) { if (domain != BorderColorDomain::Float) {
// An integer image view REQUIRES an integer border colour, whatever the value is - even // An integer image view REQUIRES an integer border color, whatever the value is - even
// (0,0,0,1). The value itself is whichever integer form the application wrote; a float // (0,0,0,1). The value itself is whichever integer form the application wrote; a float
// border on an integer texture is nonsense GL leaves undefined, so the derived integer // border on an integer texture is nonsense GL leaves undefined, so the derived integer
// representation (a plain cast) is as good an answer as any. // representation (a plain cast) is as good an answer as any.
@@ -528,7 +528,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// through Int64 so the whole signed and unsigned 32-bit ranges are expressible at once. // through Int64 so the whole signed and unsigned 32-bit ranges are expressible at once.
// //
// Which representation to start from is the TEXTURE's domain, not the entry-point form // Which representation to start from is the TEXTURE's domain, not the entry-point form
// the application used. GL 4.6 core 8.10 stores an "I"-form border colour unmodified with // the application used. GL 4.6 core 8.10 stores an "I"-form border color unmodified with
// an integer internal data type and does not define a sign conversion between the two // an integer internal data type and does not define a sign conversion between the two
// integer forms, so the stored bits are reinterpreted in the sampled format's own // integer forms, so the stored bits are reinterpreted in the sampled format's own
// signedness. Measured, not assumed: a border of -1 written with glTexParameterIiv // signedness. Measured, not assumed: a border of -1 written with glTexParameterIiv
@@ -578,7 +578,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
} }
return resolved; return resolved;
} }
// No custom colour available: pick the nearest of the three integer palette entries // No custom color available: pick the nearest of the three integer palette entries
// rather than always answering transparent black, which is what turned an integer border // rather than always answering transparent black, which is what turned an integer border
// of (-1,-1,-1,-1) into 0 and broke the CTS's clamped-texel detection outright. // of (-1,-1,-1,-1) into 0 and broke the CTS's clamped-texel detection outright.
const Bool opaque = clamped[3] != 0; const Bool opaque = clamped[3] != 0;
@@ -588,7 +588,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
return resolved; return resolved;
} }
// Float domain. GL 4.6 core 8.14.2/8.23: the border colour is interpreted in the texture's // Float domain. GL 4.6 core 8.14.2/8.23: the border color is interpreted in the texture's
// format, so it is clamped to that format's representable range first. Without the clamp the // format, so it is clamped to that format's representable range first. Without the clamp the
// CTS's border of (255,255,255,255) on a GL_RGBA8 texture matched none of the palette entries // CTS's border of (255,255,255,255) on a GL_RGBA8 texture matched none of the palette entries
// and fell through to transparent black - every border texel sampled 0 where the test wanted // and fell through to transparent black - every border texel sampled 0 where the test wanted
@@ -32,7 +32,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// customBorderColorWithoutFormat; see VulkanRenderer::m_customBorderColorFeatureEnabled. // customBorderColorWithoutFormat; see VulkanRenderer::m_customBorderColorFeatureEnabled.
Bool customBorderColorSupported = false; Bool customBorderColorSupported = false;
// VkPhysicalDeviceCustomBorderColorPropertiesEXT::maxCustomBorderColorSamplers. A hard device // VkPhysicalDeviceCustomBorderColorPropertiesEXT::maxCustomBorderColorSamplers. A hard device
// limit on how many LIVE samplers may carry a custom border colour, so the cache counts them // limit on how many LIVE samplers may carry a custom border color, so the cache counts them
// and falls back to the snapped predefined value once it is reached. // and falls back to the snapped predefined value once it is reached.
Uint32 maxCustomBorderColorSamplers = 0; Uint32 maxCustomBorderColorSamplers = 0;
}; };
@@ -70,7 +70,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// Resolved ONCE per GetOrCreateSampler call and threaded into both the cache key and the // Resolved ONCE per GetOrCreateSampler call and threaded into both the cache key and the
// create-info, so the two cannot disagree - the same discipline the resolved anisotropy needs, // create-info, so the two cannot disagree - the same discipline the resolved anisotropy needs,
// and here it also makes the maxCustomBorderColorSamplers fallback deterministic: whether a // and here it also makes the maxCustomBorderColorSamplers fallback deterministic: whether a
// custom colour was affordable is decided before the key is built, not twice with a budget // custom color was affordable is decided before the key is built, not twice with a budget
// change in between. // change in between.
struct ResolvedBorderColor { struct ResolvedBorderColor {
VkBorderColor color = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK; VkBorderColor color = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;
@@ -115,7 +115,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
Float m_maxSamplerAnisotropy = 1.0f; Float m_maxSamplerAnisotropy = 1.0f;
Bool m_customBorderColorSupported = false; Bool m_customBorderColorSupported = false;
Uint32 m_maxCustomBorderColorSamplers = 0; Uint32 m_maxCustomBorderColorSamplers = 0;
// Live cache entries carrying a custom border colour. Kept in step with the entries themselves // Live cache entries carrying a custom border color. Kept in step with the entries themselves
// in exactly the three places one can appear or disappear: creation, the OnFrameBoundary sweep, // in exactly the three places one can appear or disappear: creation, the OnFrameBoundary sweep,
// and Shutdown. // and Shutdown.
Uint32 m_customBorderColorSamplerCount = 0; Uint32 m_customBorderColorSamplerCount = 0;
@@ -732,7 +732,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// TryResolveTextureShapeInfo's answers derived from the wire descriptor instead of the // TryResolveTextureShapeInfo's answers derived from the wire descriptor instead of the
// frontend object. The descriptor's Width/Height/Depth are the BASE level's GL extent and // frontend object. The descriptor's Width/Height/Depth are the BASE level's GL extent and
// ArrayLayers the layer count the emitter already normalised (MGPipeTextureExtentOf: a 1D // ArrayLayers the layer count the emitter already normalized (MGPipeTextureExtentOf: a 1D
// array's layers are already out of its height, a cube map reports 6). // array's layers are already out of its height, a cube map reports 6).
static Bool ResolveWireTextureShapeInfo(const MG_Pipe::MGPResourceDesc& desc, TextureShapeInfo& outShape) { static Bool ResolveWireTextureShapeInfo(const MG_Pipe::MGPResourceDesc& desc, TextureShapeInfo& outShape) {
using MG_Pipe::MGPipeResourceTarget; using MG_Pipe::MGPipeResourceTarget;
@@ -768,7 +768,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
outShape.arrayLayers = 6; outShape.arrayLayers = 6;
return desc.Width > 0 && desc.Width == desc.Height; return desc.Width > 0 && desc.Width == desc.Height;
case MGPipeResourceTarget::TexCubeArray: case MGPipeResourceTarget::TexCubeArray:
// glTexStorage3D hands 6n through as the depth; the emitter normalised it into // glTexStorage3D hands 6n through as the depth; the emitter normalized it into
// ArrayLayers. A non-whole cube count has no Vulkan shape - declined like every // ArrayLayers. A non-whole cube count has no Vulkan shape - declined like every
// other unrepresentable target. // other unrepresentable target.
outShape.viewType = VK_IMAGE_VIEW_TYPE_CUBE_ARRAY; outShape.viewType = VK_IMAGE_VIEW_TYPE_CUBE_ARRAY;
@@ -1706,7 +1706,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
return false; return false;
} }
// The RGBA8/RGBX8 buffers the registry allocates have a Vulkan format; an external-only // The RGBA8/RGBX8 buffers the registry allocates have a Vulkan format; an external-only
// format would need a YCbCr conversion and is not a colour buffer this path can write. // format would need a YCbCr conversion and is not a color buffer this path can write.
if (formatProperties.format == VK_FORMAT_UNDEFINED) { if (formatProperties.format == VK_FORMAT_UNDEFINED) {
why = "the buffer has no Vulkan format (external format only)"; why = "the buffer has no Vulkan format (external format only)";
return false; return false;
@@ -84,9 +84,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// again in VkClearManager.cpp - and the two are not independent: the render pass builds the // again in VkClearManager.cpp - and the two are not independent: the render pass builds the
// attachment view and VkFramebufferCreateInfo::layers from one, while the CLEAR key built from the // attachment view and VkFramebufferCreateInfo::layers from one, while the CLEAR key built from the
// other is written verbatim into VkImageSubresourceRange::layerCount when a queued glClear is // other is written verbatim into VkImageSubresourceRange::layerCount when a queued glClear is
// materialised outside a render pass (MaterializePendingClearForTexture). They are two consumers // materialized outside a render pass (MaterializePendingClearForTexture). They are two consumers
// of the same GL clear, so any disagreement means the same glClear produces two different pictures // of the same GL clear, so any disagreement means the same glClear produces two different pictures
// depending only on which path happens to consume it first - and the materialise path then POPS // depending only on which path happens to consume it first - and the materialize path then POPS
// the entry, so the other one never runs. Fixing one copy and leaving the other is exactly how // the entry, so the other one never runs. Fixing one copy and leaving the other is exactly how
// that split gets introduced; keep them the same function. // that split gets introduced; keep them the same function.
// //
@@ -586,7 +586,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
Uint32 layerCount = 1); Uint32 layerCount = 1);
// SHARED IMAGES: a server shared image's // SHARED IMAGES: a server shared image's
// AHardwareBuffer bound as a single-level 2D colour image on THIS session's device, on dedicated // AHardwareBuffer bound as a single-level 2D color image on THIS session's device, on dedicated
// memory imported from the buffer. Android only (false with `why` elsewhere, or when the device // memory imported from the buffer. Android only (false with `why` elsewhere, or when the device
// did not take the AHardwareBuffer extension). The image is born UNDEFINED; whoever uses it // did not take the AHardwareBuffer extension). The image is born UNDEFINED; whoever uses it
// acquires it from the foreign queue family first, and a writer releases it back after. // acquires it from the foreign queue family first, and a writer releases it back after.
@@ -296,7 +296,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// `defaultFramebufferOrientation` is set only when the source is the swapchain's // `defaultFramebufferOrientation` is set only when the source is the swapchain's
// depth/stencil image, which this renderer stores display-side-up: the copy rect then // depth/stencil image, which this renderer stores display-side-up: the copy rect then
// has to be mapped out of GL's bottom-origin space and the copied rows re-oriented on // has to be mapped out of GL's bottom-origin space and the copied rows re-oriented on
// the way back, exactly as the colour ReadPixels path does. // the way back, exactly as the color ReadPixels path does.
// `sourceLayerCount` above 1 says the `height` rows the client is owed are stored as that // `sourceLayerCount` above 1 says the `height` rows the client is owed are stored as that
// many ARRAY LAYERS of a one-row image rather than as rows of one layer - the shape a GL // many ARRAY LAYERS of a one-row image rather than as rows of one layer - the shape a GL
// 1D array has in Vulkan. The two produce byte-identical tightly-packed readbacks, so // 1D array has in Vulkan. The two produce byte-identical tightly-packed readbacks, so
@@ -651,7 +651,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// and the default draw framebuffer alike. The CAPABILITY decline inside // and the default draw framebuffer alike. The CAPABILITY decline inside
// ResolveWireDepthStencil (a stencil-only resolve without VK_EXT_shader_stencil_export // ResolveWireDepthStencil (a stencil-only resolve without VK_EXT_shader_stencil_export
// on a device without VK_KHR_depth_stencil_resolve) is still decided per aspect, after // on a device without VK_KHR_depth_stencil_resolve) is still decided per aspect, after
// colour ran; rule I (a) accepts that (§2.10). // color ran; rule I (a) accepts that (§2.10).
Bool DeclineWireDepthStencilResolveShape(const WireImage& source, const WireImage& destination, GLint sx0, Bool DeclineWireDepthStencilResolveShape(const WireImage& source, const WireImage& destination, GLint sx0,
GLint sy0, GLint sx1, GLint sy1, GLint dx0, GLint dy0, GLint dx1, GLint sy0, GLint sx1, GLint sy1, GLint dx0, GLint dy0, GLint dx1,
GLint dy1); GLint dy1);
@@ -682,7 +682,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
Bool m_wireAhbImport = false; Bool m_wireAhbImport = false;
void* m_wireGetAhbProperties = nullptr; void* m_wireGetAhbProperties = nullptr;
// YUV shared images (WireYuvImage.cpp): the device enabled samplerYcbcrConversion, and the // YUV shared images (WireYuvImage.cpp): the device enabled samplerYcbcrConversion, and the
// conversion pipelines made so far (one per buffer format and colour conversion). // conversion pipelines made so far (one per buffer format and color conversion).
Bool m_samplerYcbcrConversion = false; Bool m_samplerYcbcrConversion = false;
struct WireYuvResources; struct WireYuvResources;
WireYuvResources* m_wireYuvResources = nullptr; WireYuvResources* m_wireYuvResources = nullptr;
@@ -704,7 +704,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
UnorderedMap<Uint64, SharedImagePresentTarget> m_sharedImagePresentTargets; UnorderedMap<Uint64, SharedImagePresentTarget> m_sharedImagePresentTargets;
void DestroySharedImagePresentTargets(Bool onlyDead); void DestroySharedImagePresentTargets(Bool onlyDead);
// SHARED-IMAGE SYNCHRONISATION (WireSharedImage.cpp). The sync_file fences other sessions // SHARED-IMAGE SYNCHRONIZATION (WireSharedImage.cpp). The sync_file fences other sessions
// publish become binary semaphores: a temporary SYNC_FD import is a WAIT, an exportable // publish become binary semaphores: a temporary SYNC_FD import is a WAIT, an exportable
// semaphore a SIGNAL, of the next frame submission - whichever of SubmitPendingCommandBuffer // semaphore a SIGNAL, of the next frame submission - whichever of SubmitPendingCommandBuffer
// and Present's submit comes first, which is the one carrying what was recorded since. // and Present's submit comes first, which is the one carrying what was recorded since.
@@ -1185,9 +1185,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// VK_EXT_custom_border_color. Vulkan's four predefined VkBorderColor values cover only // VK_EXT_custom_border_color. Vulkan's four predefined VkBorderColor values cover only
// transparent/opaque black and opaque white; GL_TEXTURE_BORDER_COLOR is an arbitrary vec4 (or // transparent/opaque black and opaque white; GL_TEXTURE_BORDER_COLOR is an arbitrary vec4 (or
// an arbitrary ivec4/uvec4 through the "I" entry points). Without this extension a border // an arbitrary ivec4/uvec4 through the "I" entry points). Without this extension a border
// colour outside the palette has to be snapped to the nearest predefined one. Both features // color outside the palette has to be snapped to the nearest predefined one. Both features
// are required together: customBorderColorWithoutFormat is what lets a sampler carry a custom // are required together: customBorderColorWithoutFormat is what lets a sampler carry a custom
// colour without naming the image format it will be paired with, which GL's sampler objects // color without naming the image format it will be paired with, which GL's sampler objects
// cannot know. maxCustomBorderColorSamplers is a real device limit, so the sampler cache has // cannot know. maxCustomBorderColorSamplers is a real device limit, so the sampler cache has
// to be able to fall back to the snapped value once it is reached. // to be able to fall back to the snapped value once it is reached.
Bool m_customBorderColorFeatureEnabled = false; Bool m_customBorderColorFeatureEnabled = false;
@@ -1249,7 +1249,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// pipeline from asking for LAST on a device that cannot preserve a fan's provoking vertex. // pipeline from asking for LAST on a device that cannot preserve a fan's provoking vertex.
Bool m_provokingVertexXfbPreserveEnabled = false; Bool m_provokingVertexXfbPreserveEnabled = false;
// provokingVertexModePerPipeline: when VK_FALSE every pipeline in one render pass instance // provokingVertexModePerPipeline: when VK_FALSE every pipeline in one render pass instance
// must agree on the mode, so glProvokingVertex(GL_FIRST_VERTEX_CONVENTION) cannot be honoured // must agree on the mode, so glProvokingVertex(GL_FIRST_VERTEX_CONVENTION) cannot be honored
// per draw and every pipeline takes GL's default (LAST) instead. // per draw and every pipeline takes GL's default (LAST) instead.
Bool m_provokingVertexModePerPipeline = false; Bool m_provokingVertexModePerPipeline = false;
// transformFeedbackPreservesTriangleFanProvokingVertex. // transformFeedbackPreservesTriangleFanProvokingVertex.
@@ -726,11 +726,11 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// Nothing else on this path asks: the three checks around it cover the mip range, the region // Nothing else on this path asks: the three checks around it cover the mip range, the region
// bounds and the slice range, and none of them ever looked at a format. // bounds and the slice range, and none of them ever looked at a format.
// //
// A decline rather than a MOBILEGL_ASSERT, for the reason the neighbouring guards spell out: // A decline rather than a MOBILEGL_ASSERT, for the reason the neighboring guards spell out:
// assertions compile out of the release build that the CTS and shipping both run, which is // assertions compile out of the release build that the CTS and shipping both run, which is
// exactly where the corruption was observed. // exactly where the corruption was observed.
if (srcImage.format != dstImage.format) { if (srcImage.format != dstImage.format) {
// Size-compatibility is the COLOUR rule. Vulkan makes each depth/stencil format compatible // Size-compatibility is the COLOR rule. Vulkan makes each depth/stencil format compatible
// only with ITSELF, and the texel block sizes cannot tell them apart: X8_D24_UNORM_PACK32, // only with ITSELF, and the texel block sizes cannot tell them apart: X8_D24_UNORM_PACK32,
// D32_SFLOAT and D24_UNORM_S8_UINT are all 4 bytes and all in different compatibility // D32_SFLOAT and D24_UNORM_S8_UINT are all 4 bytes and all in different compatibility
// classes, so a raw block-size test waves through exactly the pairs Vulkan forbids. The // classes, so a raw block-size test waves through exactly the pairs Vulkan forbids. The
@@ -1419,9 +1419,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VK_PIPELINE_STAGE_TRANSFER_BIT, srcAccessMask, VK_ACCESS_TRANSFER_READ_BIT, imageAspect, mipLevel, 1); VK_PIPELINE_STAGE_TRANSFER_BIT, srcAccessMask, VK_ACCESS_TRANSFER_READ_BIT, imageAspect, mipLevel, 1);
MOBILEGL_ASSERT(ok, "%s: failed to transition depth-stencil source image", __func__); MOBILEGL_ASSERT(ok, "%s: failed to transition depth-stencil source image", __func__);
// The swapchain's depth/stencil image is stored display-side-up like its colour twin, so // The swapchain's depth/stencil image is stored display-side-up like its color twin, so
// the GL rect has to be mapped into that space before the copy and the copied rows // the GL rect has to be mapped into that space before the copy and the copied rows
// re-oriented afterwards - the same two halves the colour ReadPixels path applies. // re-oriented afterwards - the same two halves the color ReadPixels path applies.
VkOffset2D copyOffset{x, y}; VkOffset2D copyOffset{x, y};
VkExtent2D copyExtent{static_cast<Uint32>(width), static_cast<Uint32>(height)}; VkExtent2D copyExtent{static_cast<Uint32>(width), static_cast<Uint32>(height)};
if (defaultFramebufferOrientation) { if (defaultFramebufferOrientation) {
@@ -755,7 +755,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// P11 B2 (T0): a client's AHardwareBuffer imported as a wire buffer store. Wire arms, on // P11 B2 (T0): a client's AHardwareBuffer imported as a wire buffer store. Wire arms, on
// Android, whenever the device advertises the extension - not per session: the device is // Android, whenever the device advertises the extension - not per session: the device is
// created once per server and a later session asking T0 must find it able. Enabling it // created once per server and a later session asking T0 must find it able. Enabling it
// changes no T2 behaviour; only an import uses it. The 1.0-era dependencies are core at the // changes no T2 behavior; only an import uses it. The 1.0-era dependencies are core at the
// 1.1 this device is created at, and are enabled too when advertised (the spike's set). // 1.1 this device is created at, and are enabled too when advertised (the spike's set).
m_wireAhbImport = false; m_wireAhbImport = false;
m_wireGetAhbProperties = nullptr; m_wireGetAhbProperties = nullptr;
@@ -943,7 +943,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// an integer border of -1 come back as 0. // an integer border of -1 come back as 0.
// //
// customBorderColorWithoutFormat is required alongside customBorderColors, not merely // customBorderColorWithoutFormat is required alongside customBorderColors, not merely
// preferred: a GL sampler object carries a border colour with no idea which texture it will // preferred: a GL sampler object carries a border color with no idea which texture it will
// be paired with, so the VkSamplerCustomBorderColorCreateInfoEXT this backend builds has to // be paired with, so the VkSamplerCustomBorderColorCreateInfoEXT this backend builds has to
// leave `format` VK_FORMAT_UNDEFINED. // leave `format` VK_FORMAT_UNDEFINED.
m_customBorderColorFeatureEnabled = false; m_customBorderColorFeatureEnabled = false;
@@ -987,7 +987,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// Native subgroup topology, and VK_EXT_subgroup_size_control's // Native subgroup topology, and VK_EXT_subgroup_size_control's
// computeFullSubgroups feature. REQUIRE_FULL_SUBGROUPS on a compute stage is what // computeFullSubgroups feature. REQUIRE_FULL_SUBGROUPS on a compute stage is what
// turns the derived gl_NumSubgroups (DeriveNumSubgroupsPass) from // turns the derived gl_NumSubgroups (DeriveNumSubgroupsPass) from
// encouraged-but-unspecified driver behaviour into a spec guarantee: with the bit // encouraged-but-unspecified driver behavior into a spec guarantee: with the bit
// set and local_size_x a multiple of the subgroup size, every subgroup launches // set and local_size_x a multiple of the subgroup size, every subgroup launches
// full, so the subgroup count is exactly invocations / size ("Full Subgroups", // full, so the subgroup count is exactly invocations / size ("Full Subgroups",
// VUID-VkPipelineShaderStageCreateInfo-flags-02759/-02785). // VUID-VkPipelineShaderStageCreateInfo-flags-02759/-02785).
@@ -90,11 +90,11 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// end up with a module carrying no Xfb mode - the clip/XFB validation backstop rewinding // end up with a module carrying no Xfb mode - the clip/XFB validation backstop rewinding
// past the decoration, or XfbCaptureDecoratePass resolving none of the requested varyings // past the decoration, or XfbCaptureDecoratePass resolving none of the requested varyings
// and changing nothing. Declining the span leaves the capture buffers untouched, which is // and changing nothing. Declining the span leaves the capture buffers untouched, which is
// the same nothing the driver would have written, without the undefined behaviour. // the same nothing the driver would have written, without the undefined behavior.
if (m_currentDrawXfbCaptureDeclined) { if (m_currentDrawXfbCaptureDeclined) {
MGLOG_E_ONCE("BeginXfbCaptureForDraw: declining the capture span - the bound program's last " MGLOG_E_ONCE("BeginXfbCaptureForDraw: declining the capture span - the bound program's last "
"pre-rasterization stage carries no Xfb execution mode, so recording one would be " "pre-rasterization stage carries no Xfb execution mode, so recording one would be "
"undefined behaviour rather than a capture"); "undefined behavior rather than a capture");
return false; return false;
} }
return BeginWireXfbCaptureForDraw(frame); return BeginWireXfbCaptureForDraw(frame);
@@ -74,7 +74,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// //
// Rectangles were never converted. The viewport, the scissor and the ReadPixels copy offset // Rectangles were never converted. The viewport, the scissor and the ReadPixels copy offset
// all used the GL bottom-origin Y verbatim as a Vulkan top-origin Y, which is correct only // all used the GL bottom-origin Y verbatim as a Vulkan top-origin Y, which is correct only
// when y == H - y - h (full height, or vertically centred) - and full height is the only case // when y == H - y - h (full height, or vertically centered) - and full height is the only case
// any test ever exercised. In the conformance suite the errors CANCEL in placement (the draw // any test ever exercised. In the conformance suite the errors CANCEL in placement (the draw
// lands in Vulkan rows [y, y+h) and the readback copies the same rows back) and compose into // lands in Vulkan rows [y, y+h) and the readback copies the same rows back) and compose into
// an exact vertical flip: 1,759 of Magma's 1,793 non-passing cases, 861 vertical flips and // an exact vertical flip: 1,759 of Magma's 1,793 non-passing cases, 861 vertical flips and
@@ -155,7 +155,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// The GL viewport rect, expressed against the default framebuffer's stored orientation. // The GL viewport rect, expressed against the default framebuffer's stored orientation.
// A full-height viewport is unchanged by this, which is why every existing scenario keeps // A full-height viewport is unchanged by this, which is why every existing scenario keeps
// its exact behaviour. // its exact behavior.
if (isDefaultFramebuffer) { if (isDefaultFramebuffer) {
const DefaultFramebufferRectMapping mapping = GetDefaultFramebufferRectMapping(preTransform); const DefaultFramebufferRectMapping mapping = GetDefaultFramebufferRectMapping(preTransform);
viewportX = MapDefaultFramebufferRectAxis(viewportX, viewportWidth, framebufferExtent.x(), mapping.mirrorX); viewportX = MapDefaultFramebufferRectAxis(viewportX, viewportWidth, framebufferExtent.x(), mapping.mirrorX);
@@ -338,7 +338,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
static_cast<Uint32>(std::max<Int>(0, rawY1 - rawY0)), static_cast<Uint32>(std::max<Int>(0, rawY1 - rawY0)),
}; };
// A quarter turn maps to {false, false}, so this is a no-op today; it is here so the // A quarter turn maps to {false, false}, so this is a no-op today; it is here so the
// branch cannot drift away from the identity/180 one when quarter turns are modelled. // branch cannot drift away from the identity/180 one when quarter turns are modeled.
return MapScissorRectToDefaultFramebuffer(scissor, framebufferExtent, preTransform); return MapScissorRectToDefaultFramebuffer(scissor, framebufferExtent, preTransform);
} }
@@ -670,8 +670,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// - patch vertices, polygon mode, cull face mode, depth func, logic op, // - patch vertices, polygon mode, cull face mode, depth func, logic op,
// min sample shading, the glSampleMaski word // min sample shading, the glSampleMaski word
// - front/back stencil ops + compare funcs (ref/mask are dynamic state) // - front/back stencil ops + compare funcs (ref/mask are dynamic state)
// - per draw buffer up to the render pass's colour span: indexed blend enable, // - per draw buffer up to the render pass's color span: indexed blend enable,
// blend factors/equations, indexed colour write mask (broadcast from index 0 // blend factors/equations, indexed color write mask (broadcast from index 0
// when the device lacks independentBlend - the same read the payload does) // when the device lacks independentBlend - the same read the payload does)
// FBO-derived payload inputs (attachment presence/formats/draw-buffer gating) are // FBO-derived payload inputs (attachment presence/formats/draw-buffer gating) are
// pinned by the render-pass hash key, exactly as the version-keyed memo relied on. // pinned by the render-pass hash key, exactly as the version-keyed memo relied on.
@@ -1078,7 +1078,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// is an OVER-count, which breaks the lower-bound contract every other tally keeps. // is an OVER-count, which breaks the lower-bound contract every other tally keeps.
// //
// The 15 are: the six capability reads (cull face, depth test, polygon offset // The 15 are: the six capability reads (cull face, depth test, polygon offset
// fill, rasterizer discard, colour logic op, stencil test), the draw-FBO slot // fill, rasterizer discard, color logic op, stencil test), the draw-FBO slot
// read that gates depth/stencil, the two stencil face states, the polygon mode, // read that gates depth/stencil, the two stencil face states, the polygon mode,
// the min sample shading value, the patch vertex count, the depth mask, the depth // the min sample shading value, the patch vertex count, the depth mask, the depth
// func, and the second draw-FBO slot read below. The sample-shading CAPABILITY // func, and the second draw-FBO slot read below. The sample-shading CAPABILITY
@@ -1103,7 +1103,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
.sampleShadingEnable = m_sampleRateShadingFeatureEnabled && .sampleShadingEnable = m_sampleRateShadingFeatureEnabled &&
MG_Pipe::gPipeInputs->IsCapabilityEnabled(CapabilityInput::SampleShading), MG_Pipe::gPipeInputs->IsCapabilityEnabled(CapabilityInput::SampleShading),
.minSampleShading = MG_Pipe::gPipeInputs->GetMinSampleShadingValue(), .minSampleShading = MG_Pipe::gPipeInputs->GetMinSampleShadingValue(),
// Word 1 keeps its all-ones initialiser: GL has no state for samples 32..63. // Word 1 keeps its all-ones initializer: GL has no state for samples 32..63.
.sampleMask = {ResolveEffectiveSampleMask(renderPassEntry.sampleCount), 0xffffffffu}, .sampleMask = {ResolveEffectiveSampleMask(renderPassEntry.sampleCount), 0xffffffffu},
.subpass = 0, .subpass = 0,
.topology = vkTopology, .topology = vkTopology,
@@ -1121,7 +1121,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// over the same rows), and a Y-negated position - the default framebuffer's flip or // over the same rows), and a Y-negated position - the default framebuffer's flip or
// an upper-left clip origin - reverses the winding once more. Expressed as the front // an upper-left clip origin - reverses the winding once more. Expressed as the front
// face rather than as swapped cull bits so gl_FrontFacing agrees with culling, and // face rather than as swapped cull bits so gl_FrontFacing agrees with culling, and
// so glFrontFace(GL_CW) is honoured. // so glFrontFace(GL_CW) is honored.
.frontFace = ((MG_Pipe::gPipeInputs->GetRenderStateParameters().FrontFaceModeSetting == .frontFace = ((MG_Pipe::gPipeInputs->GetRenderStateParameters().FrontFaceModeSetting ==
FrontFaceMode::Clockwise) != invertClockwise) FrontFaceMode::Clockwise) != invertClockwise)
? VK_FRONT_FACE_COUNTER_CLOCKWISE ? VK_FRONT_FACE_COUNTER_CLOCKWISE
@@ -1164,7 +1164,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// attached and CreatePipeline refuses the pipeline, which skips the draw. // attached and CreatePipeline refuses the pipeline, which skips the draw.
// //
// Gated on the PATCH topology as well, and that gate is load-bearing rather than an // Gated on the PATCH topology as well, and that gate is load-bearing rather than an
// optimisation: patchControlPoints is only meaningful for a patch draw, and a pipeline // optimization: patchControlPoints is only meaningful for a patch draw, and a pipeline
// that carries tessellation stages while its topology is anything else violates // that carries tessellation stages while its topology is anything else violates
// VUID-VkGraphicsPipelineCreateInfo-topology-00737 - the same class of invalid input as // VUID-VkGraphicsPipelineCreateInfo-topology-00737 - the same class of invalid input as
// the missing control stage, on the same driver. Such a draw is illegal in GL too (a // the missing control stage, on the same driver. Such a draw is illegal in GL too (a
@@ -1217,7 +1217,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
payload.backStencilCompareOp = VK_COMPARE_OP_ALWAYS; payload.backStencilCompareOp = VK_COMPARE_OP_ALWAYS;
} }
const Uint32 fragmentOutputMask = programObj.activeFragmentOutputLocationMask; const Uint32 fragmentOutputMask = programObj.activeFragmentOutputLocationMask;
// Outputs at locations past the render pass's trimmed colour span are // Outputs at locations past the render pass's trimmed color span are
// simply discarded - GL's semantic for a fragment output whose draw // simply discarded - GL's semantic for a fragment output whose draw
// buffer is GL_NONE (the trailing UNUSED slots no longer occupy // buffer is GL_NONE (the trailing UNUSED slots no longer occupy
// references, see GetOrCreateRenderPass). // references, see GetOrCreateRenderPass).
@@ -1340,7 +1340,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// VUID-VkPipelineColorBlendAttachmentState-srcColorBlendFactor-00608 and its three // VUID-VkPipelineColorBlendAttachmentState-srcColorBlendFactor-00608 and its three
// siblings forbid a VK_BLEND_FACTOR_SRC1_* in the struct without the feature whatever // siblings forbid a VK_BLEND_FACTOR_SRC1_* in the struct without the feature whatever
// blendEnable says, so clearing only the enable would still be invalid pipeline state. // blendEnable says, so clearing only the enable would still be invalid pipeline state.
// The previous behaviour, throwing, took the whole process down over one unsupported // The previous behavior, throwing, took the whole process down over one unsupported
// blend factor; this is defined, survivable and visible in the log, and it matches what // blend factor; this is defined, survivable and visible in the log, and it matches what
// the non-blendable-format arm above already does. // the non-blendable-format arm above already does.
if (!m_dualSrcBlendFeatureEnabled && if (!m_dualSrcBlendFeatureEnabled &&
@@ -190,7 +190,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VK_VERIFY(result, "Present, vkQueuePresentKHR"); VK_VERIFY(result, "Present, vkQueuePresentKHR");
// EGL swap semantics: the presented color buffer's content is undefined the // EGL swap semantics: the presented color buffer's content is undefined the
// next time this image is acquired (EGL_BUFFER_DESTROYED, the default swap // next time this image is acquired (EGL_BUFFER_DESTROYED, the default swap
// behaviour), and EVERY ancillary depth/stencil buffer's content is // behavior), and EVERY ancillary depth/stencil buffer's content is
// undefined after any swap. The render-pass manager turns the undefined // undefined after any swap. The render-pass manager turns the undefined
// attachments' next tile loads into LOAD_OP_DONT_CARE. // attachments' next tile loads into LOAD_OP_DONT_CARE.
// //
@@ -212,7 +212,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// The floor now advances only to a serial NO remaining in-flight submission still // The floor now advances only to a serial NO remaining in-flight submission still
// carries. NotifyFrameSerialComplete is monotone and already refuses the still- // carries. NotifyFrameSerialComplete is monotone and already refuses the still-
// recording serial, so this can only ever advance the floor later, never further. // recording serial, so this can only ever advance the floor later, never further.
// On the disaggregated build's MONOLITH arm this is behaviour-identical (one // On the disaggregated build's MONOLITH arm this is behavior-identical (one
// submission per serial). The #else branch below is the pull build's statement, // submission per serial). The #else branch below is the pull build's statement,
// unchanged, which is what keeps G1's .text byte-identical. // unchanged, which is what keeps G1's .text byte-identical.
Bool retiredAny = false; Bool retiredAny = false;
@@ -707,7 +707,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
} }
// Only provokingVertexModePerPipeline lets modes differ inside one render pass instance; // 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 // elsewhere every pipeline takes GL's default so the render pass stays self-consistent, and
// glProvokingVertex(GL_FIRST_VERTEX_CONVENTION) goes unhonoured. Honouring it there would // glProvokingVertex(GL_FIRST_VERTEX_CONVENTION) goes unhonoured. Honoring it there would
// mean ending the render pass on every glProvokingVertex change; not worth it until a target // mean ending the render pass on every glProvokingVertex change; not worth it until a target
// device actually lacks the property. // device actually lacks the property.
if (!m_provokingVertexModePerPipeline) { if (!m_provokingVertexModePerPipeline) {
@@ -139,7 +139,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// P7 wave 2-B (CONTRACT-P7 §3.2, `default-color-blit-shape`). The shader blit below can only // P7 wave 2-B (CONTRACT-P7 §3.2, `default-color-blit-shape`). The shader blit below can only
// SAMPLE its source: sampler2D, a float result, and one of two 2D view types. A framebuffer // SAMPLE its source: sampler2D, a float result, and one of two 2D view types. A framebuffer
// whose colour attachment is RGBA8UI, or a layer of a 3D texture, is none of those, and on a // whose color attachment is RGBA8UI, or a layer of a 3D texture, is none of those, and on a
// rotated Android surface there is no other arm - an axis-aligned vkCmdBlitImage cannot // rotated Android surface there is no other arm - an axis-aligned vkCmdBlitImage cannot
// express a quarter turn. So the source region becomes an owned 2D image first, through // express a quarter turn. So the source region becomes an owned 2D image first, through
// vkCmdCopyImage, which cares about the texel BLOCK SIZE and not about how the block is // vkCmdCopyImage, which cares about the texel BLOCK SIZE and not about how the block is
@@ -163,7 +163,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
static_cast<Uint64>(std::max(sx0, sx1)) > source.extent.width || static_cast<Uint64>(std::max(sx0, sx1)) > source.extent.width ||
static_cast<Uint64>(std::max(sy0, sy1)) > source.extent.height) static_cast<Uint64>(std::max(sy0, sy1)) > source.extent.height)
return false; return false;
// A compressed source cannot be a colour attachment, so it cannot be a framebuffer blit's // A compressed source cannot be a color attachment, so it cannot be a framebuffer blit's
// source either - but the block arithmetic below would silently produce nonsense for one, // source either - but the block arithmetic below would silently produce nonsense for one,
// and "cannot happen" is the reason the guards this package retires were written as // and "cannot happen" is the reason the guards this package retires were written as
// asserts. The size-compatibility classes are Vulkan's own (VkFormat texel block size); // asserts. The size-compatibility classes are Vulkan's own (VkFormat texel block size);
@@ -10,28 +10,28 @@
#include <Includes.h> #include <Includes.h>
#include "../VkIncludes.h" #include "../VkIncludes.h"
// P7 g3-blit (g3det, gate 3): WHICH SAMPLER THE WIRE ARM'S SHADER COLOUR BLIT READS ITS SOURCE // P7 g3-blit (g3det, gate 3): WHICH SAMPLER THE WIRE ARM'S SHADER COLOR BLIT READS ITS SOURCE
// THROUGH. // THROUGH.
// //
// GL applies a blit's `filter` only where the image is STRETCHED (GL 4.6 core 18.3.1). A blit whose // GL applies a blit's `filter` only where the image is STRETCHED (GL 4.6 core 18.3.1). A blit whose
// source and destination rectangles are the same size is a copy, whatever `filter` says, and so is // source and destination rectangles are the same size is a copy, whatever `filter` says, and so is
// a mirrored one. The wire arm's shader blit (WireColorBlit.cpp, BlitWireColorImage) draws a quad // a mirrored one. The wire arm's shader blit (WireColorBlit.cpp, BlitWireColorImage) draws a quad
// and samples `textureLod(sourceImage, texCoord, 0)` at interpolated coordinates, and it used the // and samples `textureLod(sourceImage, texCoord, 0)` at interpolated coordinates, and it used the
// LINEAR sampler for every GL_LINEAR blit. At 1:1 every fragment centre maps to a texel centre, so // LINEAR sampler for every GL_LINEAR blit. At 1:1 every fragment center maps to a texel center, so
// in exact arithmetic linear filtering reads the same single texel. Adreno 830 quantises the // in exact arithmetic linear filtering reads the same single texel. Adreno 830 quantises the
// coordinate to sub-texel precision: a fragment that lands a fraction off the centre blends in a // coordinate to sub-texel precision: a fragment that lands a fraction off the center blends in a
// small weight of a neighbour. minecraft-1.21.1-neoforge-create-instancing-in-world ends with a // small weight of a neighbor. minecraft-1.21.1-neoforge-create-instancing-in-world ends with a
// 1:1 GL_LINEAR glBlitFramebuffer into FB 0, and on the wire arm 712 high-contrast pixels moved by // 1:1 GL_LINEAR glBlitFramebuffer into FB 0, and on the wire arm 712 high-contrast pixels moved by
// 1 LSB toward a neighbour. The monolith's vkCmdBlitImage copied them exactly // 1 LSB toward a neighbor. The monolith's vkCmdBlitImage copied them exactly
// (~/w7/logs/g3det/VERDICT.md section 3). NEAREST within half a texel of the centre is exact, so an // (~/w7/logs/g3det/VERDICT.md section 3). NEAREST within half a texel of the center is exact, so an
// unscaled blit takes NEAREST. Lavapipe samples texel centres exactly, so no host picture can see // unscaled blit takes NEAREST. Lavapipe samples texel centers exactly, so no host picture can see
// the difference; this pure half is what the unit test pins // the difference; this pure half is what the unit test pins
// (MG_Test/Pipeline/PipelineQuirkTest.cpp, WireColorBlitSamples*). // (MG_Test/Pipeline/PipelineQuirkTest.cpp, WireColorBlitSamples*).
// //
// THE SURFACE TRANSFORM DOES NOT TURN A COPY INTO A STRETCH. BlitWireColorImage applies the // THE SURFACE TRANSFORM DOES NOT TURN A COPY INTO A STRETCH. BlitWireColorImage applies the
// quarter and half turns in NDC, over a viewport of the native image, and for a quarter turn it // quarter and half turns in NDC, over a viewport of the native image, and for a quarter turn it
// takes the logical extent as the native extent swapped. A native pixel then has exactly the pitch // takes the logical extent as the native extent swapped. A native pixel then has exactly the pitch
// of a logical one, and the rotation maps pixel centres onto pixel centres. So the scale test is on // of a logical one, and the rotation maps pixel centers onto pixel centers. So the scale test is on
// GL's own (logical) rectangles for identity and all three rotations. BlitWireColorImage declines // GL's own (logical) rectangles for identity and all three rotations. BlitWireColorImage declines
// any other transform before it chooses a sampler; for one, this answers with the filter's own // any other transform before it chooses a sampler; for one, this answers with the filter's own
// mapping, as before this package. // mapping, as before this package.
@@ -129,7 +129,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VkPhysicalDeviceMemoryProperties memoryProperties{}; VkPhysicalDeviceMemoryProperties memoryProperties{};
vkGetPhysicalDeviceMemoryProperties(context.physicalDevice, &memoryProperties); vkGetPhysicalDeviceMemoryProperties(context.physicalDevice, &memoryProperties);
// The wire's images (VkTextureManager): MUTABLE_FORMAT (a storage-compatible colour class), // The wire's images (VkTextureManager): MUTABLE_FORMAT (a storage-compatible color class),
// SAMPLED | TRANSFER_SRC | TRANSFER_DST | COLOR_ATTACHMENT, a 3D one 2D_ARRAY_COMPATIBLE. // SAMPLED | TRANSFER_SRC | TRANSFER_DST | COLOR_ATTACHMENT, a 3D one 2D_ARRAY_COMPATIBLE.
const auto makeImage = [&](Bool volume, VkFormat format, VkImage& out) -> Bool { const auto makeImage = [&](Bool volume, VkFormat format, VkImage& out) -> Bool {
VkFormatProperties formatProperties{}; VkFormatProperties formatProperties{};
@@ -218,7 +218,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
depthStencil.depthCompareOp = VK_COMPARE_OP_ALWAYS; depthStencil.depthCompareOp = VK_COMPARE_OP_ALWAYS;
depthStencil.minDepthBounds = 0.0f; depthStencil.minDepthBounds = 0.0f;
depthStencil.maxDepthBounds = 1.0f; depthStencil.maxDepthBounds = 1.0f;
// No colour attachment in this render pass, so no attachment states either. // No color attachment in this render pass, so no attachment states either.
VkPipelineColorBlendStateCreateInfo blend{VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO}; VkPipelineColorBlendStateCreateInfo blend{VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO};
const VkDynamicState dynamicStates[]{VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR}; const VkDynamicState dynamicStates[]{VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR};
VkPipelineDynamicStateCreateInfo dynamic{VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO}; VkPipelineDynamicStateCreateInfo dynamic{VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO};
@@ -269,7 +269,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
framebufferInfo.layers = 1; framebufferInfo.layers = 1;
VkFramebuffer framebuffer = VK_NULL_HANDLE; VkFramebuffer framebuffer = VK_NULL_HANDLE;
VK_VERIFY(vkCreateFramebuffer(m_device, &framebufferInfo, nullptr, &framebuffer)); VK_VERIFY(vkCreateFramebuffer(m_device, &framebufferInfo, nullptr, &framebuffer));
// Each in-flight pass owns its descriptor set, for the colour blit's reason: updating // Each in-flight pass owns its descriptor set, for the color blit's reason: updating
// a cached set would rewrite the source view of an older queued pass. // a cached set would rewrite the source view of an older queued pass.
VkDescriptorPool descriptorPool = VK_NULL_HANDLE; VkDescriptorPool descriptorPool = VK_NULL_HANDLE;
VkDescriptorSet descriptorSet = VK_NULL_HANDLE; VkDescriptorSet descriptorSet = VK_NULL_HANDLE;
@@ -95,7 +95,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
if (result != VK_SUCCESS) return result; if (result != VK_SUCCESS) return result;
// THE STENCIL MODULE IS ONLY CREATED WHERE IT CAN RUN: its SPIR-V declares the // THE STENCIL MODULE IS ONLY CREATED WHERE IT CAN RUN: its SPIR-V declares the
// StencilExportEXT capability, and handing a module with an unsupported capability to // StencilExportEXT capability, and handing a module with an unsupported capability to
// vkCreateShaderModule is undefined behaviour - not an error this could catch. // vkCreateShaderModule is undefined behavior - not an error this could catch.
if (shaderStencilExport) { if (shaderStencilExport) {
result = shader(kWireMultisampleStencilResolveFragmentSpirv, result = shader(kWireMultisampleStencilResolveFragmentSpirv,
sizeof(kWireMultisampleStencilResolveFragmentSpirv), kit.stencilFragment); sizeof(kWireMultisampleStencilResolveFragmentSpirv), kit.stencilFragment);
@@ -206,7 +206,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
op.reference = 0; op.reference = 0;
depthStencilState.front = depthStencilState.back = op; depthStencilState.front = depthStencilState.back = op;
} }
// No colour attachment in this render pass, so no attachment states either. // No color attachment in this render pass, so no attachment states either.
VkPipelineColorBlendStateCreateInfo blend{VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO}; VkPipelineColorBlendStateCreateInfo blend{VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO};
const VkDynamicState dynamicStates[]{VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR}; const VkDynamicState dynamicStates[]{VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR};
VkPipelineDynamicStateCreateInfo dynamic{VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO}; VkPipelineDynamicStateCreateInfo dynamic{VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO};
@@ -1175,7 +1175,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// Every write goes through the pass's attachment views (the UNORM twin of an sRGB image while // Every write goes through the pass's attachment views (the UNORM twin of an sRGB image while
// GL_FRAMEBUFFER_SRGB is off, exactly GL's raw clear - no pre-compensation), and wire attachments // GL_FRAMEBUFFER_SRGB is off, exactly GL's raw clear - no pre-compensation), and wire attachments
// live in GENERAL. What none of this expresses goes back to the standalone path, per GL buffer // live in GENERAL. What none of this expresses goes back to the standalone path, per GL buffer
// bit: a partial colour or stencil write mask (that path warns and skips, as before), an // bit: a partial color or stencil write mask (that path warns and skips, as before), an
// attachment whose extent or layer count differs from the pass's (the pass covers the common area // attachment whose extent or layer count differs from the pass's (the pass covers the common area
// only), and a stencil clear the pass's depth attachment does not carry. // only), and a stencil clear the pass's depth attachment does not carry.
GLbitfield VulkanRenderer::ClearWireFramebufferInPass(const MG_Pipe::MGPFramebufferState& fbo, GLbitfield VulkanRenderer::ClearWireFramebufferInPass(const MG_Pipe::MGPFramebufferState& fbo,
@@ -1185,7 +1185,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// Nested inside a draw's preparation the scratch and the pass belong to that draw. // Nested inside a draw's preparation the scratch and the pass belong to that draw.
if (m_wirePreparationDepth != 0) return remaining; if (m_wirePreparationDepth != 0) return remaining;
// NOT THE DEFAULT FRAMEBUFFER (measured, not understood): on Adreno 750 the openra retrace // NOT THE DEFAULT FRAMEBUFFER (measured, not understood): on Adreno 750 the openra retrace
// loses its palette-sampled sprites (SSIM 0.956) when a colour+depth clear of the default // loses its palette-sampled sprites (SSIM 0.956) when a color+depth clear of the default
// framebuffer becomes part of its draw pass - with LOAD_OP_CLEAR as well as with // framebuffer becomes part of its draw pass - with LOAD_OP_CLEAR as well as with
// vkCmdClearAttachments - while the same clear split across the two paths, or left // vkCmdClearAttachments - while the same clear split across the two paths, or left
// standalone, renders correctly. Default-framebuffer clears keep the standalone path until // standalone, renders correctly. Default-framebuffer clears keep the standalone path until
@@ -1354,7 +1354,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
} }
// The draw pass `begun` with LOAD_OP_CLEAR for the attachments `variantKey` names (bit 2a: // The draw pass `begun` with LOAD_OP_CLEAR for the attachments `variantKey` names (bit 2a:
// attachment a's colour or depth, bit 2a+1: its stencil). Built once per key and kept on the // attachment a's color or depth, bit 2a+1: its stencil). Built once per key and kept on the
// pass, like the pass itself. // pass, like the pass itself.
VkRenderPass VulkanRenderer::WireClearVariant(RenderPassEntry& begun, Uint64 variantKey) { VkRenderPass VulkanRenderer::WireClearVariant(RenderPassEntry& begun, Uint64 variantKey) {
for (const auto& entry : begun.clearVariants) for (const auto& entry : begun.clearVariants)
@@ -25,7 +25,7 @@
// level return every non-canonical word canonicalized, while a 16x16 or 64x64 level reads back verbatim. // level return every non-canonical word canonicalized, while a 16x16 or 64x64 level reads back verbatim.
// The stored words are intact - a shader reading the same image through an R32_UINT view sees // The stored words are intact - a shader reading the same image through an R32_UINT view sees
// every one of them - and so are transfer WRITES into it. Measured with format lists, without // every one of them - and so are transfer WRITES into it. Measured with format lists, without
// the colour-attachment usage and with storage usage: none of them changes the answer, and the // the color-attachment usage and with storage usage: none of them changes the answer, and the
// device has no VK_EXT_image_compression_control to ask with. // device has no VK_EXT_image_compression_control to ask with.
// //
// The route therefore reads through a shader: a compute pass fetches the source through an // The route therefore reads through a shader: a compute pass fetches the source through an
@@ -109,7 +109,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// comparison or the route itself is what failed, and nothing is concluded. // comparison or the route itself is what failed, and nothing is concluded.
Uint32 controlMismatches = 0; Uint32 controlMismatches = 0;
// Reported, not judged: the same words in a 16x16 level read back by plain transfer. Clean // Reported, not judged: the same words in a 16x16 level read back by plain transfer. Clean
// on the device this was characterised on (power-of-two extents read back verbatim, 28, 30 // on the device this was characterized on (power-of-two extents read back verbatim, 28, 30
// and 31 do not), which is why the subject is 30x30. // and 31 do not), which is why the subject is 30x30.
Uint32 smallTexels = 0; Uint32 smallTexels = 0;
Uint32 smallMismatches = 0; Uint32 smallMismatches = 0;
@@ -615,7 +615,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
} }
// THE HOST GATE CANNOT REACH THE SHADER MIP ARM ON ITS OWN. lavapipe reports // THE HOST GATE CANNOT REACH THE SHADER MIP ARM ON ITS OWN. lavapipe reports
// BLIT_SRC|BLIT_DST for every colour format this lane can create, so `nativeBlit` is // BLIT_SRC|BLIT_DST for every color format this lane can create, so `nativeBlit` is
// always true there and both shader shapes below would only ever run on the phone - // always true there and both shader shapes below would only ever run on the phone -
// which is how the Adreno refusal reached a release in the first place. The knob makes // which is how the Adreno refusal reached a release in the first place. The knob makes
// them reachable from a host lane and is read exactly once: // them reachable from a host lane and is read exactly once:
@@ -789,7 +789,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// ---- P15: WIRE DEFERRED CLEARS ------------------------------------------------------------- // ---- P15: WIRE DEFERRED CLEARS -------------------------------------------------------------
// //
// Minecraft (1.21.5) clears its targets through framebuffers of their own - a depth-only one // Minecraft (1.21.5) clears its targets through framebuffers of their own - a depth-only one
// for the shared depth texture, a colour+depth one per target - and then draws through the // for the shared depth texture, a color+depth one per target - and then draws through the
// main framebuffer. A clear that begins a LOAD_OP_CLEAR pass of its own made every such clear a // main framebuffer. A clear that begins a LOAD_OP_CLEAR pass of its own made every such clear a
// pass that did nothing but clear, closed by the very next draw: 11.5 passes a frame against // pass that did nothing but clear, closed by the very next draw: 11.5 passes a frame against
// dev's 4.1 (whose clear manager folded the same clears into the next pass's load op), each // dev's 4.1 (whose clear manager folded the same clears into the next pass's load op), each
@@ -805,7 +805,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// ClearWireFramebufferInPass), and it is presented, not sampled. // ClearWireFramebufferInPass), and it is presented, not sampled.
// Whether `image` can carry a deferred clear: a plain single-sample 2D(-array) image of this // Whether `image` can carry a deferred clear: a plain single-sample 2D(-array) image of this
// session with a float colour or a depth/stencil format, clearable by a transfer command. // session with a float color or a depth/stencil format, clearable by a transfer command.
Bool VulkanRenderer::WireImageCanDeferClear(const WireImage& image) { Bool VulkanRenderer::WireImageCanDeferClear(const WireImage& image) {
const auto* resource = image.resource; const auto* resource = image.resource;
if (resource == nullptr || image.isDefault || image.is3D || image.samples != VK_SAMPLE_COUNT_1_BIT) if (resource == nullptr || image.isDefault || image.is3D || image.samples != VK_SAMPLE_COUNT_1_BIT)
@@ -1328,7 +1328,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
!m_swapchainObject.IsImageContentDefined(source.swapchainImageIndex)) { !m_swapchainObject.IsImageContentDefined(source.swapchainImageIndex)) {
// Named rather than silent: the readback hands back whatever the image holds. The // Named rather than silent: the readback hands back whatever the image holds. The
// ordinary cause is a readback of the default framebuffer applied after the Present // ordinary cause is a readback of the default framebuffer applied after the Present
// that consumed it, where the app's own colour is still what is expected (see // that consumed it, where the app's own color is still what is expected (see
// m_defaultFramebufferImageIndex). // m_defaultFramebufferImageIndex).
MGLOG_W_ONCE("DirectVulkan::ReadPixels: no write is recorded for swapchain image %u of the " MGLOG_W_ONCE("DirectVulkan::ReadPixels: no write is recorded for swapchain image %u of the "
"default framebuffer; the readback returns its raw pixels", "default framebuffer; the readback returns its raw pixels",
@@ -1416,7 +1416,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
const Int64 dstHeight = dstSpanY < 0 ? -dstSpanY : dstSpanY; const Int64 dstHeight = dstSpanY < 0 ? -dstSpanY : dstSpanY;
// A SCALE has no arm here and is not supposed to: 18.3.1 makes a multisample blit whose // A SCALE has no arm here and is not supposed to: 18.3.1 makes a multisample blit whose
// rectangles differ in size INVALID_OPERATION. Rule I (a), the same three lines the // rectangles differ in size INVALID_OPERATION. Rule I (a), the same three lines the
// colour arm's multisample-destination decline uses. A ZERO span on one side only is a // color arm's multisample-destination decline uses. A ZERO span on one side only is a
// size mismatch too (review round 2): it used to return silently with the other side // size mismatch too (review round 2): it used to return silently with the other side
// non-empty, where GL raises the error. Only when BOTH sides are empty on an axis is // non-empty, where GL raises the error. Only when BOTH sides are empty on an axis is
// there nothing to do and nothing to say - that is the caller's early return. // there nothing to do and nothing to say - that is the caller's early return.
@@ -1602,7 +1602,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// P7 wave 2-B2 (review round): A REVERSED RECTANGLE IS NOT A MALFORMED ONE. This guard // P7 wave 2-B2 (review round): A REVERSED RECTANGLE IS NOT A MALFORMED ONE. This guard
// used to require `sx1 > sx0` and `sy1 > sy0` and take the session with a named Fatal // used to require `sx1 > sx0` and `sy1 > sy0` and take the session with a named Fatal
// otherwise - so `glBlitFramebuffer(0, h, w, 0, 0, 0, w, h, COLOR|DEPTH)` out of a // otherwise - so `glBlitFramebuffer(0, h, w, 0, 0, 0, w, h, COLOR|DEPTH)` out of a
// multisample framebuffer blitted its COLOUR correctly (the colour arm has handled a // multisample framebuffer blitted its COLOR correctly (the color arm has handled a
// flip since P5f and now handles a scale too) and then died on its DEPTH. GL 4.6 core // flip since P5f and now handles a scale too) and then died on its DEPTH. GL 4.6 core
// 18.3.1 asks for identical DIMENSIONS, not identical corners - a conforming check compares absolute // 18.3.1 asks for identical DIMENSIONS, not identical corners - a conforming check compares absolute
// spans - so a flip is a legal blit that has to produce a mirrored picture. // spans - so a flip is a legal blit that has to produce a mirrored picture.
@@ -1610,10 +1610,10 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// Spans are therefore signed and everything downstream works off the min corner. // Spans are therefore signed and everything downstream works off the min corner.
// //
// THE SHAPE DECLINES ARE DECIDED ONCE PER CALL (review round 2). BlitWireFramebuffers // THE SHAPE DECLINES ARE DECIDED ONCE PER CALL (review round 2). BlitWireFramebuffers
// asks DeclineWireDepthStencilResolveShape before ANY aspect runs, because the colour // asks DeclineWireDepthStencilResolveShape before ANY aspect runs, because the color
// lambda runs first: asked only here, a scaled COLOR|DEPTH blit scaled and wrote its // lambda runs first: asked only here, a scaled COLOR|DEPTH blit scaled and wrote its
// colour and THEN recorded INVALID_OPERATION on its depth - a partial effect GL 18.3.1 // color and THEN recorded INVALID_OPERATION on its depth - a partial effect GL 18.3.1
// forbids (the whole call errors) - and an X-mirrored one mirrored its colour and left // forbids (the whole call errors) - and an X-mirrored one mirrored its color and left
// its depth alone. The call below is the same question for a caller that did not ask; // its depth alone. The call below is the same question for a caller that did not ask;
// on the BlitWireFramebuffers path it has already answered false. // on the BlitWireFramebuffers path it has already answered false.
if (DeclineWireDepthStencilResolveShape(source, destination, sx0, sy0, sx1, sy1, dx0, dy0, dx1, dy1)) return; if (DeclineWireDepthStencilResolveShape(source, destination, sx0, sy0, sx1, sy1, dx0, dy0, dx1, dy1)) return;
@@ -1874,8 +1874,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
if (!read || !draw) MagmaWireFatal("blit-framebuffer-record"); if (!read || !draw) MagmaWireFatal("blit-framebuffer-record");
state.VerbBlitNamedConsumed = named; state.VerbBlitNamedConsumed = named;
// P7 wave 2-B2 (review round 2): ONE ANSWER FOR THE WHOLE CALL. The aspects below run // P7 wave 2-B2 (review round 2): ONE ANSWER FOR THE WHOLE CALL. The aspects below run
// colour first, so a multisample depth/stencil shape that declines (a scale, an X // color first, so a multisample depth/stencil shape that declines (a scale, an X
// mirror, a Y mirror across two formats) used to be discovered only after the colour // mirror, a Y mirror across two formats) used to be discovered only after the color
// attachment had already been scaled or mirrored and written - the call then recorded // attachment had already been scaled or mirrored and written - the call then recorded
// INVALID_OPERATION with half its effect in place, which GL 18.3.1 does not allow (an // INVALID_OPERATION with half its effect in place, which GL 18.3.1 does not allow (an
// erroring blit writes nothing). The shape is a fact about the rectangles and the two // erroring blit writes nothing). The shape is a fact about the rectangles and the two
@@ -1887,7 +1887,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// on the belief that a default draw behind a multisample read was ResolveWireDepthStencil's // on the belief that a default draw behind a multisample read was ResolveWireDepthStencil's
// region Fatal whatever the shape, but that arm asks DeclineWireDepthStencilResolveShape // region Fatal whatever the shape, but that arm asks DeclineWireDepthStencilResolveShape
// BEFORE its region check - so a scaled (or X-mirrored) COLOR|DEPTH blit out of a // BEFORE its region check - so a scaled (or X-mirrored) COLOR|DEPTH blit out of a
// multisample framebuffer onto the window scaled and wrote its colour through // multisample framebuffer onto the window scaled and wrote its color through
// BlitWireColorToDefault and then recorded INVALID_OPERATION on its depth, the very // BlitWireColorToDefault and then recorded INVALID_OPERATION on its depth, the very
// partial effect this block exists to close. Both sides are resolved as READS // partial effect this block exists to close. Both sides are resolved as READS
// (isWriteTarget=false): only their format and samples are consulted here, and a write // (isWriteTarget=false): only their format and samples are consulted here, and a write
@@ -2005,7 +2005,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// The filter only started MATTERING with the scale above: at 1:1 LINEAR and // The filter only started MATTERING with the scale above: at 1:1 LINEAR and
// NEAREST read the same texel, so nothing here ever asked whether the format // NEAREST read the same texel, so nothing here ever asked whether the format
// could be filtered. VK_FILTER_LINEAR on a source whose optimalTilingFeatures // could be filtered. VK_FILTER_LINEAR on a source whose optimalTilingFeatures
// lack SAMPLED_IMAGE_FILTER_LINEAR is undefined behaviour, and GL says filter // lack SAMPLED_IMAGE_FILTER_LINEAR is undefined behavior, and GL says filter
// is ignored for a multisample resolve in the first place - so a format that // is ignored for a multisample resolve in the first place - so a format that
// cannot be filtered takes NEAREST rather than the command being illegal. // cannot be filtered takes NEAREST rather than the command being illegal.
VkFormatProperties scratchProperties{}; VkFormatProperties scratchProperties{};
@@ -2077,7 +2077,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// express a quarter turn. It is still TAKEN FIRST on every transform it can // express a quarter turn. It is still TAKEN FIRST on every transform it can
// express, identity included. That has been the routing since 38919d453: every // express, identity included. That has been the routing since 38919d453: every
// host lane (all identity) and the Redmi pbuffer run their default-framebuffer // host lane (all identity) and the Redmi pbuffer run their default-framebuffer
// colour blits through it, F1WireScenario.ColorBlitToDefaultHonorsScissorAndReversedRect // color blits through it, F1WireScenario.ColorBlitToDefaultHonorsScissorAndReversedRect
// among them. The ordinary vkCmdBlitImage arm below is reached on identity only // among them. The ordinary vkCmdBlitImage arm below is reached on identity only
// when this pass declines the source. Because identity blits run here, the pass // when this pass declines the source. Because identity blits run here, the pass
// must copy exactly wherever GL says the blit is a copy: an unscaled blit samples // must copy exactly wherever GL says the blit is a copy: an unscaled blit samples
@@ -2179,7 +2179,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// P7 wave 2-B2, CONTRACT-P7 §3.2: WHAT IS LEFT HERE IS A MULTISAMPLE DESTINATION. // P7 wave 2-B2, CONTRACT-P7 §3.2: WHAT IS LEFT HERE IS A MULTISAMPLE DESTINATION.
// Every blit that resolves a multisample SOURCE into a single-sample destination // Every blit that resolves a multisample SOURCE into a single-sample destination
// returned above - the depth/stencil arm at the top of this lambda through // returned above - the depth/stencil arm at the top of this lambda through
// ResolveWireDepthStencil, the colour arm below it through its resolve scratch - // ResolveWireDepthStencil, the color arm below it through its resolve scratch -
// and both of those now carry their own scale and flip. So the samples of the two // and both of those now carry their own scale and flip. So the samples of the two
// sides can only differ here by the DESTINATION being multisampled, which is the // sides can only differ here by the DESTINATION being multisampled, which is the
// shape this arm declines rather than the one it performs. // shape this arm declines rather than the one it performs.
@@ -2423,7 +2423,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT; VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT;
const Bool depthArmTakesShape = const Bool depthArmTakesShape =
planar && layersInRange && (props.optimalTilingFeatures & depthPassFeatures) == depthPassFeatures; planar && layersInRange && (props.optimalTilingFeatures & depthPassFeatures) == depthPassFeatures;
// The colour arm's shape (the two shader shapes are explained at the colour arm below). // 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 colour = resource->aspect == VK_IMAGE_ASPECT_COLOR_BIT;
// The shader arm samples through a sampler2D and writes a vec4, which is what // 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 // BlitWireColorImage's own float check enforces; naming it here rather than letting the
@@ -2459,7 +2459,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// P7 wave 2-B, CONTRACT-P7 §5.1 (A): THE DEPTH ARM, and it is first because a // P7 wave 2-B, CONTRACT-P7 §5.1 (A): THE DEPTH ARM, and it is first because a
// depth format without BLIT_DST is the ordinary case, not the exotic one - // depth format without BLIT_DST is the ordinary case, not the exotic one -
// VK_FORMAT_FEATURE_BLIT_DST is optional for depth and plenty of devices decline // VK_FORMAT_FEATURE_BLIT_DST is optional for depth and plenty of devices decline
// it. Before this package the whole branch below was colour-only, so such a // it. Before this package the whole branch below was color-only, so such a
// texture reached `mipmap-shader-format-or-shape` on its very first disjunction // texture reached `mipmap-shader-format-or-shape` on its very first disjunction
// while the monolith arm quietly generated the chain with a shader. // while the monolith arm quietly generated the chain with a shader.
// //
@@ -2511,7 +2511,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// TWO SHADER SHAPES, not one. The arm that was here rendered STRAIGHT INTO the // TWO SHADER SHAPES, not one. The arm that was here rendered STRAIGHT INTO the
// next level, which needs the image to have been created with // next level, which needs the image to have been created with
// COLOR_ATTACHMENT usage - and a texture whose format the device refuses as a // COLOR_ATTACHMENT usage - and a texture whose format the device refuses as a
// colour attachment never gets that bit (VkTextureManager.cpp:2156-2160), so // color attachment never gets that bit (VkTextureManager.cpp:2156-2160), so
// every sampled-only texture without BLIT support fell into the refusal rather // every sampled-only texture without BLIT support fell into the refusal rather
// than into a fallback. When the FORMAT can be rendered and only the IMAGE // than into a fallback. When the FORMAT can be rendered and only the IMAGE
// cannot, an owned scratch attachment of the destination level's size renders // cannot, an owned scratch attachment of the destination level's size renders
@@ -13,7 +13,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// GL shader compiler. It is WireDepthMipmap.cpp's pass with the box filter taken out and a // GL shader compiler. It is WireDepthMipmap.cpp's pass with the box filter taken out and a
// sample index put in. // sample index put in.
// //
// WHY THIS FILE EXISTS. vkCmdResolveImage is COLOUR ONLY. The one way Vulkan resolves a depth // WHY THIS FILE EXISTS. vkCmdResolveImage is COLOR ONLY. The one way Vulkan resolves a depth
// or stencil aspect without a shader is a render pass with a depth/stencil resolve attachment, // or stencil aspect without a shader is a render pass with a depth/stencil resolve attachment,
// which needs VK_KHR_create_renderpass2 and VK_KHR_depth_stencil_resolve - and until this // which needs VK_KHR_create_renderpass2 and VK_KHR_depth_stencil_resolve - and until this
// package a device without either killed the session at // package a device without either killed the session at
@@ -138,7 +138,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VkFramebuffer framebuffer = VK_NULL_HANDLE; VkFramebuffer framebuffer = VK_NULL_HANDLE;
VK_VERIFY(vkCreateFramebuffer(m_device, &framebufferInfo, nullptr, &framebuffer)); VK_VERIFY(vkCreateFramebuffer(m_device, &framebufferInfo, nullptr, &framebuffer));
// Each in-flight pass owns its descriptor set, for the colour blit's reason: updating a // Each in-flight pass owns its descriptor set, for the color blit's reason: updating a
// cached set would rewrite the source view of an older queued pass. // cached set would rewrite the source view of an older queued pass.
VkDescriptorPool descriptorPool = VK_NULL_HANDLE; VkDescriptorPool descriptorPool = VK_NULL_HANDLE;
VkDescriptorSet descriptorSet = VK_NULL_HANDLE; VkDescriptorSet descriptorSet = VK_NULL_HANDLE;
@@ -37,7 +37,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// Every private placeholder is its own vkCreateImage + vkAllocateMemory, freed only at Shutdown. // Every private placeholder is its own vkCreateImage + vkAllocateMemory, freed only at Shutdown.
// (shape, unit) bounds them by shapes x kMGPipeMaxImageUnits, and a peer rotating formats and // (shape, unit) bounds them by shapes x kMGPipeMaxImageUnits, and a peer rotating formats and
// targets across the units could still walk that towards maxMemoryAllocationCount (4096 is the // targets across the units could still walk that toward maxMemoryAllocationCount (4096 is the
// spec minimum). So past this many, a NEW (shape, unit) takes its shape's SHARED placeholder - the // spec minimum). So past this many, a NEW (shape, unit) takes its shape's SHARED placeholder - the
// pre-fix aliasing, logged once by the caller - instead of another allocation; a unit that already // pre-fix aliasing, logged once by the caller - instead of another allocation; a unit that already
// has its private placeholder keeps it. Two per unit is far past any real program. // has its private placeholder keeps it. Two per unit is far past any real program.
@@ -15,9 +15,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// texture's image: the texture keeps its own RGBA8 level 0 (VkTextureManager::SyncWireTextureShape) // texture's image: the texture keeps its own RGBA8 level 0 (VkTextureManager::SyncWireTextureShape)
// and, at its first use in each frame, the image is CONVERTED into it here - the image's // and, at its first use in each frame, the image is CONVERTED into it here - the image's
// AHardwareBuffer imported with its external format, sampled through an immutable sampler that // AHardwareBuffer imported with its external format, sampled through an immutable sampler that
// carries a VkSamplerYcbcrConversion built from the import's colour hints, one triangle over the // carries a VkSamplerYcbcrConversion built from the import's color hints, one triangle over the
// target. Zero CPU copies for an image this server allocated; a foreign one (a video decoder's // target. Zero CPU copies for an image this server allocated; a foreign one (a video decoder's
// buffer) is first brought up to date by the registry's labelled CPU copy (RefreshForeign). // buffer) is first brought up to date by the registry's labeled CPU copy (RefreshForeign).
// //
// Why converted rather than sampled in place: a Y'CbCr conversion is an immutable sampler, which // Why converted rather than sampled in place: a Y'CbCr conversion is an immutable sampler, which
// lives in the descriptor set layout and therefore in every application pipeline that samples the // lives in the descriptor set layout and therefore in every application pipeline that samples the
@@ -154,7 +154,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// ---- the texture's import of the buffer, and the pipeline its conversion needs ---- // ---- the texture's import of the buffer, and the pipeline its conversion needs ----
auto* source = static_cast<WireYuvSource*>(resource.yuvSource.get()); auto* source = static_cast<WireYuvSource*>(resource.yuvSource.get());
if (source != nullptr && !(source->hints == hints)) { if (source != nullptr && !(source->hints == hints)) {
// Re-imported with other colour hints: the conversion (and so the view) changes. // Re-imported with other color hints: the conversion (and so the view) changes.
auto retired = MakeUnique<VkTextureManager::TextureResource>(); auto retired = MakeUnique<VkTextureManager::TextureResource>();
retired->yuvSource = std::move(resource.yuvSource); retired->yuvSource = std::move(resource.yuvSource);
WireRetiredObjects objects{}; WireRetiredObjects objects{};
+1 -1
View File
@@ -240,7 +240,7 @@ namespace MobileGL::MG_Backend {
// a bit derived from `ActiveBackendType == ...` would keep claiming the capability // a bit derived from `ActiveBackendType == ...` would keep claiming the capability
// for a build, a flavour or a future backend whose table does not carry the arm - // for a build, a flavour or a future backend whose table does not carry the arm -
// which is exactly how 66 uploads were lost to a consumer bit that nobody could // which is exactly how 66 uploads were lost to a consumer bit that nobody could
// have honoured. `g_resourceOpsAtStep2` is the pointer this function just pinned, // have honored. `g_resourceOpsAtStep2` is the pointer this function just pinned,
// and step 5 already refuses a run where that pointer was swapped underneath us, so // and step 5 already refuses a run where that pointer was swapped underneath us, so
// reading the arm off it means the published bit and the table that has to answer // reading the arm off it means the published bit and the table that has to answer
// for it cannot come apart. // for it cannot come apart.
+3 -3
View File
@@ -353,11 +353,11 @@ namespace MobileGL::MG_Pipe {
void MGPipeServerBlockNoteIdentity() { void MGPipeServerBlockNoteIdentity() {
// D10: THE WIDER PREDICATE, and the narrow one was a latent crash rather than a missing // D10: THE WIDER PREDICATE, and the narrow one was a latent crash rather than a missing
// optimisation. Gated on the REHEARSAL, this early-returned for the whole life of a // optimization. Gated on the REHEARSAL, this early-returned for the whole life of a
// spawn server - because that knob is off by default and nothing in the spawn shape // spawn server - because that knob is off by default and nothing in the spawn shape
// turns it on - so the server's gPipeInputs->ContextIdentity() stayed nullptr forever. // turns it on - so the server's gPipeInputs->ContextIdentity() stayed nullptr forever.
// DirectGLES' fb-slot memo compares that identity FIRST and with no generation, so a // DirectGLES' fb-slot memo compares that identity FIRST and with no generation, so a
// nullptr against its own nullptr initialiser reads as a CACHE HIT and hands out a slot // nullptr against its own nullptr initializer reads as a CACHE HIT and hands out a slot
// that was never filled. `st` lands the named Fatal at that site; this is the gate that // that was never filled. `st` lands the named Fatal at that site; this is the gate that
// stops it being reachable in the first place. // stops it being reachable in the first place.
if (!MGPipeBlocksAreDistinct()) return; if (!MGPipeBlocksAreDistinct()) return;
@@ -366,7 +366,7 @@ namespace MobileGL::MG_Pipe {
// served-context clock: MGPipeApplierContextSerial moves exactly when the served // served-context clock: MGPipeApplierContextSerial moves exactly when the served
// context does (MGPipeApplierReset), which is all the backends' per-context memo keys // context does (MGPipeApplierReset), which is all the backends' per-context memo keys
// ask of an identity. The odd-encoding keeps the token non-null at serial 0: a null // ask of an identity. The odd-encoding keeps the token non-null at serial 0: a null
// identity reads as a hit against DirectGLES' zero-initialised fb-slot memo cache and // identity reads as a hit against DirectGLES' zero-initialized fb-slot memo cache and
// hands out a null slot - an unnamed crash where the rehearsal exists to produce a // hands out a null slot - an unnamed crash where the rehearsal exists to produce a
// named one. // named one.
const Uint64 serial = MGPipeApplierContextSerial(); const Uint64 serial = MGPipeApplierContextSerial();
+4 -4
View File
@@ -227,7 +227,7 @@ namespace MobileGL::MG_Pipe {
// such a verb is a BARRIER_PULLED read counted rather than Fatal, and only there is a // such a verb is a BARRIER_PULLED read counted rather than Fatal, and only there is a
// sticky forward a residual pull. PipeApplier::LeaveApplier must clear it after every // sticky forward a residual pull. PipeApplier::LeaveApplier must clear it after every
// record: a server process has no client half to do it, so the flag would stay set and // record: a server process has no client half to do it, so the flag would stay set and
// later reads (InvalidateCompileEnv during another context's backend initialisation) // later reads (InvalidateCompileEnv during another context's backend initialization)
// would be judged against the last verb's mask. // would be judged against the last verb's mask.
Bool ServerStampedVerb() const { return m_serverStampedVerb; } Bool ServerStampedVerb() const { return m_serverStampedVerb; }
@@ -622,7 +622,7 @@ namespace MobileGL::MG_Pipe {
// them with handle tables and callbacks. // them with handle tables and callbacks.
// They carry no MGP_INPUT_CHECK / MGP_INPUT_VERIFY_READ (the declared exception to // They carry no MGP_INPUT_CHECK / MGP_INPUT_VERIFY_READ (the declared exception to
// P1 brief D4's "every accessor body"): a forward is a live call, not a stored value, // P1 brief D4's "every accessor body"): a forward is a live call, not a stored value,
// and InvalidateCompileEnv is reached from backend initialisation before any verb has // and InvalidateCompileEnv is reached from backend initialization before any verb has
// filled, where a check would be Fatal{...@<none>} on every start. Their sticky stamp // filled, where a check would be Fatal{...@<none>} on every start. Their sticky stamp
// is therefore consulted by no accessor; the tests pin it through // is therefore consulted by no accessor; the tests pin it through
// MGPipeInputFieldIsFresh directly. // MGPipeInputFieldIsFresh directly.
@@ -689,7 +689,7 @@ namespace MobileGL::MG_Pipe {
// MGPipeServerSetContextLive's flag. In the block, not in a process global, because the // MGPipeServerSetContextLive's flag. In the block, not in a process global, because the
// in-process display server runs several sessions at once and each has its own served // in-process display server runs several sessions at once and each has its own served
// context: one session's teardown or eglMakeCurrent(NO_CONTEXT) must not mark a // context: one session's teardown or eglMakeCurrent(NO_CONTEXT) must not mark a
// neighbour's context dead mid-frame (P14; the per-thread block resolution below is // neighbor's context dead mid-frame (P14; the per-thread block resolution below is
// what makes this the calling session's). // what makes this the calling session's).
Bool m_serverContextLive = false; Bool m_serverContextLive = false;
MGPipeVerb m_currentVerb = MGPipeVerb::kVerbCount; MGPipeVerb m_currentVerb = MGPipeVerb::kVerbCount;
@@ -896,7 +896,7 @@ namespace MobileGL::MG_Pipe {
// from MGPipeApplierContextSerial() - the server's own served-context clock, which is what // from MGPipeApplierContextSerial() - the server's own served-context clock, which is what
// moves when the served context does. Without it the server block's ContextIdentity() would // moves when the served context does. Without it the server block's ContextIdentity() would
// stay nullptr, and DirectGLES' fb-slot memo cache compares identity FIRST (a nullptr // stay nullptr, and DirectGLES' fb-slot memo cache compares identity FIRST (a nullptr
// against its own nullptr initialiser reads as a hit and hands out a null slot): an // against its own nullptr initializer reads as a hit and hands out a null slot): an
// unnamed crash where the rehearsal exists to produce a named one. A no-op with the // unnamed crash where the rehearsal exists to produce a named one. A no-op with the
// rehearsal off, so the client's per-verb SetIdentity keeps owning the shared block there. // rehearsal off, so the client's per-verb SetIdentity keeps owning the shared block there.
void MGPipeServerBlockNoteIdentity(); void MGPipeServerBlockNoteIdentity();
@@ -98,9 +98,9 @@ namespace MobileGL::MG_Record {
break; break;
default: default:
// A value class outside the three is a wire fault, not a fallback: all three // A value class outside the three is a wire fault, not a fallback: all three
// representations of a clear colour are numerically populated by the frontend // representations of a clear color are numerically populated by the frontend
// and only this field says which one the backend must use, so guessing renders // and only this field says which one the backend must use, so guessing renders
// a plausible wrong colour. // a plausible wrong color.
MG_Pipe::MGPipeProtocolFatalAt("MGPClear::ValueClass", clear.ValueClass, 3); MG_Pipe::MGPipeProtocolFatalAt("MGPClear::ValueClass", clear.ValueClass, 3);
} }
break; break;
@@ -227,7 +227,7 @@ namespace MobileGL::MG_Record {
// scatters it per its own GL_PACK_* state, so a DstSize that disagrees with the tight // scatters it per its own GL_PACK_* state, so a DstSize that disagrees with the tight
// extent (a client that sized its destination with pack padding) is logged and the tight // extent (a client that sized its destination with pack padding) is logged and the tight
// extent this side owns is read and posted - never the client's number, so a wrong // extent this side owns is read and posted - never the client's number, so a wrong
// DstSize cannot make this a short read into uninitialised scratch. F2's first PH-3 draft // DstSize cannot make this a short read into uninitialized scratch. F2's first PH-3 draft
// refused the mismatch instead, which broke the ID-49 control // refused the mismatch instead, which broke the ID-49 control
// (ServerLoopEglTest.AReadPixelsReplyIsTheTightExtentWhateverDstSizeTheClientSent); the // (ServerLoopEglTest.AReadPixelsReplyIsTheTightExtentWhateverDstSizeTheClientSent); the
// bound above is what PH-3 needs and it does not depend on DstSize. // bound above is what PH-3 needs and it does not depend on DstSize.
+2 -2
View File
@@ -22,7 +22,7 @@
// INVALIDATE_RANGE clobbered GPU-written data - an SSBO counter beside the app's SubData - with // INVALIDATE_RANGE clobbered GPU-written data - an SSBO counter beside the app's SubData - with
// stale shadow bytes (FlushPendingRangesFrom, ManagersBuffers.cpp). Tier 1's INVALIDATE_RANGE is an ASSERTION that // stale shadow bytes (FlushPendingRangesFrom, ManagersBuffers.cpp). Tier 1's INVALIDATE_RANGE is an ASSERTION that
// the old bytes are dead, so declaring bytes covered that nothing staged is not a missing // the old bytes are dead, so declaring bytes covered that nothing staged is not a missing
// optimisation, it is a silent data loss. The coverage set below is therefore exact, and a // optimization, it is a silent data loss. The coverage set below is therefore exact, and a
// drain that reaches outside it is Fatal rather than a re-read of whatever happens to be there. // drain that reaches outside it is Fatal rather than a re-read of whatever happens to be there.
// //
// WHY IT IS A HEADER AND NOT A BLOCK INSIDE Managers.cpp. Two reasons, and the second is the // WHY IT IS A HEADER AND NOT A BLOCK INSIDE Managers.cpp. Two reasons, and the second is the
@@ -55,7 +55,7 @@ namespace MobileGL::MG_Record {
// P14: WHOSE STAGED BYTES ARE THESE? The two server stores below are one per process, but the // P14: WHOSE STAGED BYTES ARE THESE? The two server stores below are one per process, but the
// in-process display server serves several sessions in that process at once, and a session's // in-process display server serves several sessions in that process at once, and a session's
// end (DestroyEGLContext's OnBackendContextDestroyed) used to DropAll() them - every live // end (DestroyEGLContext's OnBackendContextDestroyed) used to DropAll() them - every live
// neighbour's declared texture levels and buffer shadows with it. The neighbour's next upload // neighbor's declared texture levels and buffer shadows with it. The neighbor's next upload
// into a texture it had declared long ago then read as "resource_subdata arrived before the // into a texture it had declared long ago then read as "resource_subdata arrived before the
// server declared texture level", a Fatal that took the whole display process (and so every // server declared texture level", a Fatal that took the whole display process (and so every
// session in it) down. The texture store is also keyed by the client's {slot, gen}, which two // session in it) down. The texture store is also keyed by the client's {slot, gen}, which two
@@ -61,7 +61,7 @@
// that lets the sync read - the set is complete exactly when every piece has landed, and a level // that lets the sync read - the set is complete exactly when every piece has landed, and a level
// with a hole in it is Fatal{StageSnapshotTooNarrow}: the same words as the buffer half, for the // with a hole in it is Fatal{StageSnapshotTooNarrow}: the same words as the buffer half, for the
// same reason, because those bytes have never existed on this side and inventing them is silent // same reason, because those bytes have never existed on this side and inventing them is silent
// data loss rather than a missing optimisation. THE QUESTION IS "IS THE RANGE THE READER ASKS // data loss rather than a missing optimization. THE QUESTION IS "IS THE RANGE THE READER ASKS
// FOR COVERED", not "is the level the size the client meant": no format crosses the wire, so a // FOR COVERED", not "is the level the size the client meant": no format crosses the wire, so a
// leading run that already reaches the image's high-water mark reads as complete when later // leading run that already reaches the image's high-water mark reads as complete when later
// pieces are still missing - unreachable, because nothing reads between the pieces of one verb, // pieces are still missing - unreachable, because nothing reads between the pieces of one verb,
@@ -502,7 +502,7 @@ namespace MobileGL::MG_Record {
// The calling thread's SESSION's levels (every group of it - the callers are a session's // The calling thread's SESSION's levels (every group of it - the callers are a session's
// context teardown and its applier release), or every level on a thread that is no // context teardown and its applier release), or every level on a thread that is no
// session's. A live neighbour session's declared levels are not this caller's to drop: // session's. A live neighbor session's declared levels are not this caller's to drop:
// that is the P14 Fatal StagedShadow.h's StagedBucket describes. // that is the P14 Fatal StagedShadow.h's StagedBucket describes.
void DropAll() { void DropAll() {
if (!m_any.load(std::memory_order_acquire)) return; if (!m_any.load(std::memory_order_acquire)) return;
+2 -2
View File
@@ -10,7 +10,7 @@
#include <Includes.h> #include <Includes.h>
namespace MobileGL::MG_Backend { namespace MobileGL::MG_Backend {
// SHARED IMAGES: server-allocated colour // SHARED IMAGES: server-allocated color
// buffers exported to clients as dma-buf descriptors. // buffers exported to clients as dma-buf descriptors.
// //
// The server-side view of one image, as a backend sees it: `NativeBuffer` is the image's // The server-side view of one image, as a backend sees it: `NativeBuffer` is the image's
@@ -42,7 +42,7 @@ namespace MobileGL::MG_Backend {
}; };
// An import of a two-plane (YUV) dma-buf: where its planes are in the one descriptor, and // An import of a two-plane (YUV) dma-buf: where its planes are in the one descriptor, and
// EGL_EXT_image_dma_buf_import's colour hints (0 = not given). // EGL_EXT_image_dma_buf_import's color hints (0 = not given).
struct SharedImageImportLayout { struct SharedImageImportLayout {
Uint32 PlaneCount = 1; Uint32 PlaneCount = 1;
Uint32 Offset[2] = {0, 0}; Uint32 Offset[2] = {0, 0};
+1 -1
View File
@@ -245,7 +245,7 @@ static void bench_multi_draw_elements_base_vertex(GLenum mode, const GLsizei* co
* glFinish and glFlush as no-ops (MG_Impl/GLImpl/Exporting/Definitions.cpp), * glFinish and glFlush as no-ops (MG_Impl/GLImpl/Exporting/Definitions.cpp),
* so a glFinish-paced loop would time only the CPU-side submit on a MobileGL * so a glFinish-paced loop would time only the CPU-side submit on a MobileGL
* backend while timing submit-plus-GPU on the native driver - the two numbers * backend while timing submit-plus-GPU on the native driver - the two numbers
* would not describe the same work. A sync object is honoured by every stack * would not describe the same work. A sync object is honored by every stack
* measured here. * measured here.
*/ */
typedef void (*case_fn)(int frame, long a, long b); typedef void (*case_fn)(int frame, long a, long b);
@@ -605,7 +605,7 @@ static void case_mc_use_program(int frame, long a, long b) {
/* ---- the case table both harnesses iterate -------------------------------- /* ---- the case table both harnesses iterate --------------------------------
* a/b are the case's own knobs; opsPerFrame is what one bench frame is * a/b are the case's own knobs; opsPerFrame is what one bench frame is
* normalised by, so ns_per_op compares across renderers. The mc_* rates are * normalized by, so ns_per_op compares across renderers. The mc_* rates are
* the per-frame call counts measured from the captured traces. * the per-frame call counts measured from the captured traces.
*/ */
typedef void (*bench_case_fn)(int frame, long a, long b); typedef void (*bench_case_fn)(int frame, long a, long b);
@@ -19,7 +19,7 @@
// at glCompileShader - a different entry point from the one L1 // at glCompileShader - a different entry point from the one L1
// memoizes - and fresh shader objects meant ShaderCompileAdoptionMap // memoizes - and fresh shader objects meant ShaderCompileAdoptionMap
// could not hand the earlier parse over either. L1c is what closed // could not hand the earlier parse over either. L1c is what closed
// that: the compile half of the memo recognises each stage's source // that: the compile half of the memo recognizes each stage's source
// and publishes its verdict without parsing, so on a hit this case now // and publishes its verdict without parsing, so on a hit this case now
// constructs no glslang object at all. // constructs no glslang object at all.
// //
@@ -405,7 +405,7 @@ BENCHMARK(BM_EsslTranspile_CacheOn)->Arg(0)->Arg(120)->Unit(benchmark::kMicrosec
// shows up here and nowhere else. // shows up here and nowhere else.
// //
// The distinct fragment sources also churn both front-end levels through their FIFO caps, // The distinct fragment sources also churn both front-end levels through their FIFO caps,
// which is the eviction behaviour a real shaderpack load produces; over a long run the // which is the eviction behavior a real shaderpack load produces; over a long run the
// constant vertex entry is occasionally evicted by that churn and re-inserted, so the L1c // constant vertex entry is occasionally evicted by that churn and re-inserted, so the L1c
// hit rate reported below is high but not exactly 1.0 per iteration. // hit rate reported below is high but not exactly 1.0 per iteration.
namespace { namespace {
@@ -19,7 +19,7 @@
// zero-pass Optimizer::Run and a bare BuildModule on the same binary, so the FIXED // zero-pass Optimizer::Run and a bare BuildModule on the same binary, so the FIXED
// parse/serialize overhead can be separated from the work the passes actually do. It also // parse/serialize overhead can be separated from the work the passes actually do. It also
// runs the same pass set merged onto ONE Optimizer, which is the saving a merged chain // runs the same pass set merged onto ONE Optimizer, which is the saving a merged chain
// would realise. // would realize.
// //
// Measurement only: it links MobileGL_s and drives the public ShaderCompiler API, so it // Measurement only: it links MobileGL_s and drives the public ShaderCompiler API, so it
// needs no GL context and no driver. The driver's own glCompileShader/glLinkProgram is // needs no GL context and no driver. The driver's own glCompileShader/glLinkProgram is
@@ -746,7 +746,7 @@ void main() {
Emit(name, "program", "glslang.GlslangToSpv (both stages)", "frontend", 0, inner); Emit(name, "program", "glslang.GlslangToSpv (both stages)", "frontend", 0, inner);
} }
// ---- build the artefacts every later measurement runs against ---- // ---- build the artifacts every later measurement runs against ----
BuiltProgram built; BuiltProgram built;
{ {
auto shaders = ParseBoth(vsPre, fsPre, env); auto shaders = ParseBoth(vsPre, fsPre, env);
+2 -2
View File
@@ -7,7 +7,7 @@
// End of Source File Header // End of Source File Header
// mobilegl_gbm.so: a GBM backend (the loader's backend ABI, gbm_backend_abi.h) whose buffer // mobilegl_gbm.so: a GBM backend (the loader's backend ABI, gbm_backend_abi.h) whose buffer
// objects are MobileGL shared images - colour buffers the MobileGL server allocates and exports // objects are MobileGL shared images - color buffers the MobileGL server allocates and exports
// as dma-bufs. Selected with // as dma-bufs. Selected with
// GBM_BACKEND=mobilegl; the loader then finds <dir>/mobilegl_gbm.so on its backend path. // GBM_BACKEND=mobilegl; the loader then finds <dir>/mobilegl_gbm.so on its backend path.
// //
@@ -54,7 +54,7 @@ namespace {
constexpr uint64_t kModifierLinear = 0; constexpr uint64_t kModifierLinear = 0;
constexpr uint64_t kModifierInvalid = 0x00ffffffffffffffull; constexpr uint64_t kModifierInvalid = 0x00ffffffffffffffull;
// Usages no shared image can honour: a CPU-written cursor (bo_write), a guaranteed-linear // Usages no shared image can honor: a CPU-written cursor (bo_write), a guaranteed-linear
// layout, protected content. Scanout/rendering/texturing are accepted - every caller of // layout, protected content. Scanout/rendering/texturing are accepted - every caller of
// gbm_bo_create passes them, and a shared image serves all three. // gbm_bo_create passes them, and a shared image serves all three.
constexpr uint32_t kRefusedUsage = GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE | GBM_BO_USE_LINEAR | GBM_BO_USE_PROTECTED; constexpr uint32_t kRefusedUsage = GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE | GBM_BO_USE_LINEAR | GBM_BO_USE_PROTECTED;
@@ -36,7 +36,7 @@ namespace MobileGL::MG_Impl::EGLImpl {
"EGL_MESA_platform_surfaceless" MOBILEGL_PLATFORM_WAYLAND_EXTENSIONS; "EGL_MESA_platform_surfaceless" MOBILEGL_PLATFORM_WAYLAND_EXTENSIONS;
// The display list again, with the dma-buf import a backend with shared images has (a split // The display list again, with the dma-buf import a backend with shared images has (a split
// client: the server allocates the images and names the descriptors that come back). A // client: the server allocates the images and names the descriptors that come back). A
// monolith has no allocator to recognise a dma-buf with, so it never advertises these. The // monolith has no allocator to recognize a dma-buf with, so it never advertises these. The
// same server answers the buffer age of its surfaces and takes swap damage to its presents. // same server answers the buffer age of its surfaces and takes swap damage to its presents.
inline constexpr const char* kDisplayExtensionStringWithSharedImages = inline constexpr const char* kDisplayExtensionStringWithSharedImages =
"EGL_KHR_create_context EGL_KHR_surfaceless_context EGL_KHR_no_config_context EGL_EXT_platform_base " "EGL_KHR_create_context EGL_KHR_surfaceless_context EGL_KHR_no_config_context EGL_EXT_platform_base "
+1 -1
View File
@@ -50,7 +50,7 @@ namespace MobileGL::MG_Impl::EGLImpl::Wayland {
inline constexpr EGLenum kPlatformWayland = 0x31D8; inline constexpr EGLenum kPlatformWayland = 0x31D8;
// Whether a display is a Wayland one: asked for as EGL_PLATFORM_WAYLAND, or (eglGetDisplay with // Whether a display is a Wayland one: asked for as EGL_PLATFORM_WAYLAND, or (eglGetDisplay with
// no platform) a native display that is a wl_display - recognised the way EGL loaders usually do, by its // no platform) a native display that is a wl_display - recognized the way EGL loaders usually do, by its
// first word being libwayland-client's wl_display_interface (or, for an application with its // first word being libwayland-client's wl_display_interface (or, for an application with its
// own copy of libwayland, an interface named "wl_display"). // own copy of libwayland, an interface named "wl_display").
Bool IsWaylandDisplay(Uint64 nativeDisplay, EGLenum platform); Bool IsWaylandDisplay(Uint64 nativeDisplay, EGLenum platform);
+1 -1
View File
@@ -412,7 +412,7 @@ namespace MobileGL::MG_Impl::GLImpl {
break; break;
case GL_BUFFER_ACCESS_FLAGS: case GL_BUFFER_ACCESS_FLAGS:
// The MapBufferRange flags verbatim; glMapBuffer's access enum has already been // The MapBufferRange flags verbatim; glMapBuffer's access enum has already been
// normalised into the same bits. Zero while the buffer is not mapped. // normalized into the same bits. Zero while the buffer is not mapped.
*params = bufferObject->IsMapped() ? static_cast<GLint>(MG_Util::ConvertBufferMappingAccessToGLEnum( *params = bufferObject->IsMapped() ? static_cast<GLint>(MG_Util::ConvertBufferMappingAccessToGLEnum(
bufferObject->GetMappingAccess())) bufferObject->GetMappingAccess()))
: 0; : 0;
+1 -1
View File
@@ -90,7 +90,7 @@ namespace MobileGL::MG_Impl::GLImpl {
} }
// Whether `name` currently names an object of `identifier`'s type. GL 4.6 core 20.5 makes // Whether `name` currently names an object of `identifier`'s type. GL 4.6 core 20.5 makes
// labelling something that does not exist INVALID_VALUE, and every type KHR_debug lists // labeling something that does not exist INVALID_VALUE, and every type KHR_debug lists
// has a frontend name check - so this is answered exactly rather than waved through. // 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, // 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. // which MobileGL does not expose, so it falls to the INVALID_ENUM path below.
+1 -1
View File
@@ -19,7 +19,7 @@ namespace MobileGL::MG_Impl::GLImpl {
// * glObjectLabel names a FRONTEND object. MobileGL's texture 5 is not the ES driver's // * glObjectLabel names a FRONTEND object. MobileGL's texture 5 is not the ES driver's
// texture 5 (and under DirectVulkan it is not a driver object at all), so forwarding the // texture 5 (and under DirectVulkan it is not a driver object at all), so forwarding the
// pair verbatim would label an unrelated object or a nonexistent one - worse than not // pair verbatim would label an unrelated object or a nonexistent one - worse than not
// labelling. // labeling.
// * A debug GROUP is only meaningful if it brackets the commands the application issued // * A debug GROUP is only meaningful if it brackets the commands the application issued
// inside it. Neither backend emits its work at the moment the GL call arrives: DirectGLES // inside it. Neither backend emits its work at the moment the GL call arrives: DirectGLES
// defers and reorders state sync and uploads around draws, and DirectVulkan is usually not // defers and reorders state sync and uploads around draws, and DirectVulkan is usually not
@@ -204,7 +204,7 @@ namespace MobileGL::MG_Impl::GLImpl {
// Draws without a geometry stage write exactly the primitives they assemble, // Draws without a geometry stage write exactly the primitives they assemble,
// clamped by the capture buffers' remaining capacity (a full buffer stops // clamped by the capture buffers' remaining capacity (a full buffer stops
// recording whole primitives, which is what PRIMITIVES_WRITTEN reports). // recording whole primitives, which is what PRIMITIVES_WRITTEN reports).
// Geometry amplification is not modelled here. // Geometry amplification is not modeled here.
static void AccountTransformFeedbackPrimitives(GLenum mode, GLsizei count) { static void AccountTransformFeedbackPrimitives(GLenum mode, GLsizei count) {
if (!MG_State::pGLContext->IsTransformFeedbackActive()) return; if (!MG_State::pGLContext->IsTransformFeedbackActive()) return;
// A paused span captures nothing, so a draw made while paused contributes to // A paused span captures nothing, so a draw made while paused contributes to
@@ -128,7 +128,7 @@ namespace MobileGL::MG_Impl::GLImpl {
// because a capability recorded for one of them says nothing about the others: DirectGLES // because a capability recorded for one of them says nothing about the others: DirectGLES
// decides each target's substitution against that target's own probe, and a buffer texture // decides each target's substitution against that target's own probe, and a buffer texture
// never gets one at all. This is also where the three-channel widening becomes visible to // never gets one at all. This is also where the three-channel widening becomes visible to
// the application - a GL_RGB8_SNORM colour attachment on a driver with no renderable // the application - a GL_RGB8_SNORM color attachment on a driver with no renderable
// three-channel format answers COMPLETE because the backend stores it as GL_RGBA16F and // three-channel format answers COMPLETE because the backend stores it as GL_RGBA16F and
// recorded FramebufferRenderable in CaveatCaps. // recorded FramebufferRenderable in CaveatCaps.
Bool IsColorInternalFormatRenderable(TextureInternalFormat format, SizeT capabilityTargetIndex) { Bool IsColorInternalFormatRenderable(TextureInternalFormat format, SizeT capabilityTargetIndex) {
@@ -502,9 +502,9 @@ namespace MobileGL::MG_Impl::GLImpl {
if (!FramebufferImpl::ValidateColorAttachmentInRange(attachmentType, functionName)) return false; if (!FramebufferImpl::ValidateColorAttachmentInRange(attachmentType, functionName)) return false;
// "An INVALID_OPERATION error is generated if zero is bound to target." MobileGL keeps // "An INVALID_OPERATION error is generated if zero is bound to target." MobileGL keeps
// a real FramebufferObject for framebuffer 0, so a null test can never see this - the // 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 recognised by identity instead, // 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 // the same comparison DrawBuffers_State makes. Without this an attach onto the default
// framebuffer silently REPLACED its colour attachment, permanently desynchronising it // framebuffer silently REPLACED its color attachment, permanently desynchronising it
// from what the swapchain keeps publishing. // from what the swapchain keeps publishing.
const auto& defaultFramebufferInfo = FramebufferImpl::pDefaultFramebufferInfo; const auto& defaultFramebufferInfo = FramebufferImpl::pDefaultFramebufferInfo;
if (!framebufferObject || if (!framebufferObject ||
@@ -568,12 +568,12 @@ namespace MobileGL::MG_Impl::GLImpl {
// shorthand for attaching the same image to both halves (GL 4.6 core 9.2.6), so // shorthand for attaching the same image to both halves (GL 4.6 core 9.2.6), so
// whether glFramebufferTexture made it LAYERED is a property of the call, not of // whether glFramebufferTexture made it LAYERED is a property of the call, not of
// which half is being recorded - and dropping it here (the parameter defaults to // which half is being recorded - and dropping it here (the parameter defaults to
// false) recorded a non-layered depth/stencil attachment beside a layered colour // false) recorded a non-layered depth/stencil attachment beside a layered color
// one for every layered target. That is an inconsistent framebuffer by 9.4.1's // one for every layered target. That is an inconsistent framebuffer by 9.4.1's
// own rule, and downstream it means the depth/stencil attachment covers layer 0 // own rule, and downstream it means the depth/stencil attachment covers layer 0
// alone: DirectVulkan built its view with layerCount 1 under a framebuffer // alone: DirectVulkan built its view with layerCount 1 under a framebuffer
// declaring N layers (VUID-VkFramebufferCreateInfo-flags-04535), and DirectGLES // declaring N layers (VUID-VkFramebufferCreateInfo-flags-04535), and DirectGLES
// attached one layer of it beside a layered colour target, which the driver // attached one layer of it beside a layered color target, which the driver
// answers with GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS - every draw silently // answers with GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS - every draw silently
// produced nothing. This is the shape // produced nothing. This is the shape
// texture_cube_map_array.stencil_attachments_*_layered and // texture_cube_map_array.stencil_attachments_*_layered and
@@ -1654,7 +1654,7 @@ namespace MobileGL::MG_Impl::GLImpl {
} }
// Everything above is the spec's error set and is answered for every target and every layer // Everything above is the spec's error set and is answered for every target and every layer
// on every backend. Whether the attachment can actually be honoured is a backend question: // on every backend. Whether the attachment can actually be honored is a backend question:
// DirectGLES hands the layer to glFramebufferTextureLayer and renders to it, while // DirectGLES hands the layer to glFramebufferTextureLayer and renders to it, while
// DirectVulkan maps a GL layer onto a Vulkan array layer with no notion of a 3D depth slice, // DirectVulkan maps a GL layer onto a Vulkan array layer with no notion of a 3D depth slice,
// so a slice lands outside the image and the renderer asserts on the clear. Letting it // so a slice lands outside the image and the renderer asserts on the clear. Letting it
@@ -1904,7 +1904,7 @@ namespace MobileGL::MG_Impl::GLImpl {
MG_State::pGLContext->RecordError( MG_State::pGLContext->RecordError(
isMultiBufferName ? ErrorCode::InvalidOperation : ErrorCode::InvalidEnum, isMultiBufferName ? ErrorCode::InvalidOperation : ErrorCode::InvalidEnum,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", caller, MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", caller,
std::format("`src` = {} does not name a single readable colour buffer.", std::format("`src` = {} does not name a single readable color buffer.",
MG_Util::ConvertGLEnumToString(src)))); MG_Util::ConvertGLEnumToString(src))));
return; return;
} }
@@ -1918,7 +1918,7 @@ namespace MobileGL::MG_Impl::GLImpl {
// Naming a buffer the other kind of framebuffer has is INVALID_OPERATION rather than // Naming a buffer the other kind of framebuffer has is INVALID_OPERATION rather than
// INVALID_ENUM (GL 4.6 core 17.4.1) - the name is accepted, this framebuffer just has no // INVALID_ENUM (GL 4.6 core 17.4.1) - the name is accepted, this framebuffer just has no
// such colour buffer. // such color buffer.
if (isDefaultFBO && attType != FramebufferAttachmentType::None && if (isDefaultFBO && attType != FramebufferAttachmentType::None &&
(attType < FramebufferAttachmentType::FrontLeft || attType > FramebufferAttachmentType::BackRight)) { (attType < FramebufferAttachmentType::FrontLeft || attType > FramebufferAttachmentType::BackRight)) {
MG_State::pGLContext->RecordError( MG_State::pGLContext->RecordError(
@@ -2060,7 +2060,7 @@ namespace MobileGL::MG_Impl::GLImpl {
// Which buffers the integer clears accept is narrower than the float one, and differs between // Which buffers the integer clears accept is narrower than the float one, and differs between
// them: signed values can clear COLOR or STENCIL, unsigned only COLOR (GL 4.6 core 17.4.3.1). // them: signed values can clear COLOR or STENCIL, unsigned only COLOR (GL 4.6 core 17.4.3.1).
// Only the colour buffer is indexed; a stencil clear names the single stencil buffer, so any // Only the color buffer is indexed; a stencil clear names the single stencil buffer, so any
// drawbuffer other than 0 is out of range rather than merely unused. // drawbuffer other than 0 is out of range rather than merely unused.
Bool ValidateNamedClearIntegerv_State(GLenum buffer, GLint drawbuffer, const void* value, Bool allowStencil, Bool ValidateNamedClearIntegerv_State(GLenum buffer, GLint drawbuffer, const void* value, Bool allowStencil,
const char* caller) { const char* caller) {
@@ -2121,7 +2121,7 @@ namespace MobileGL::MG_Impl::GLImpl {
// glInvalidateFramebuffer and its three siblings only grant the implementation permission to // glInvalidateFramebuffer and its three siblings only grant the implementation permission to
// throw the named attachments' contents away - "become undefined" is satisfied by keeping them // throw the named attachments' contents away - "become undefined" is satisfied by keeping them
// - so MobileGL validates the call and leaves the contents alone. Discarding is a bandwidth // - so MobileGL validates the call and leaves the contents alone. Discarding is a bandwidth
// optimisation that would need a backend dependency; it can be added later without changing // optimization that would need a backend dependency; it can be added later without changing
// what any of these entry points promise. The conformance tests exercise the validation, which // what any of these entry points promise. The conformance tests exercise the validation, which
// is the part that was missing. // is the part that was missing.
Bool ValidateInvalidateAttachments_State(const SharedPtr<MG_State::GLState::FramebufferObject>& framebuffer, Bool ValidateInvalidateAttachments_State(const SharedPtr<MG_State::GLState::FramebufferObject>& framebuffer,
@@ -2144,7 +2144,7 @@ namespace MobileGL::MG_Impl::GLImpl {
if (isDefaultFramebuffer) { if (isDefaultFramebuffer) {
switch (attachment) { switch (attachment) {
// The by-name forms name the default framebuffer's buffers the way // The by-name forms name the default framebuffer's buffers the way
// glClearNamedFramebuffer does, with COLOR standing for the colour buffer, while // glClearNamedFramebuffer does, with COLOR standing for the color buffer, while
// the target forms use the individual FRONT_LEFT/BACK_RIGHT tokens. Both spellings // the target forms use the individual FRONT_LEFT/BACK_RIGHT tokens. Both spellings
// reach here, so both are accepted (GL 4.6 core 17.4.4). // reach here, so both are accepted (GL 4.6 core 17.4.4).
case GL_COLOR: case GL_COLOR:
@@ -3341,7 +3341,7 @@ namespace MobileGL::MG_Impl::GLImpl {
return false; return false;
} }
// Depth and stencil have no meaningful interpolation, so GL_LINEAR is rejected outright // Depth and stencil have no meaningful interpolation, so GL_LINEAR is rejected outright
// rather than downgraded - even when the mask also carries the colour bit. // rather than downgraded - even when the mask also carries the color bit.
if (filter == GL_LINEAR && (mask & (GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)) != 0) { if (filter == GL_LINEAR && (mask & (GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)) != 0) {
MG_State::pGLContext->RecordError( MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation, ErrorCode::InvalidOperation,
@@ -75,7 +75,7 @@ namespace MobileGL::MG_Impl::GLImpl::FramebufferImpl {
ErrorCode::InvalidOperation, ErrorCode::InvalidOperation,
MakeUnique<GenericErrorInfo>( MakeUnique<GenericErrorInfo>(
"MG_Impl/GLImpl/FramebufferImpl", caller, "MG_Impl/GLImpl/FramebufferImpl", caller,
std::format("Colour attachment {} is beyond GL_MAX_COLOR_ATTACHMENTS ({}).", colorIndex, limit))); std::format("Color attachment {} is beyond GL_MAX_COLOR_ATTACHMENTS ({}).", colorIndex, limit)));
return false; return false;
} }
return true; return true;
@@ -16,7 +16,7 @@ namespace MobileGL::MG_Impl::GLImpl::FramebufferImpl {
Bool ValidateFramebufferAttachmentType(FramebufferAttachmentType attachment); Bool ValidateFramebufferAttachmentType(FramebufferAttachmentType attachment);
// GL_COLOR_ATTACHMENTn is a token per n up to 31, but only the first GL_MAX_COLOR_ATTACHMENTS of // GL_COLOR_ATTACHMENTn is a token per n up to 31, but only the first GL_MAX_COLOR_ATTACHMENTS of
// them name an attachment point of a framebuffer object; the rest are INVALID_OPERATION for the // them name an attachment point of a framebuffer object; the rest are INVALID_OPERATION for the
// attaching entry points (GL 4.6 core 9.2.7). Non-colour attachments pass through unchanged. // attaching entry points (GL 4.6 core 9.2.7). Non-color attachments pass through unchanged.
Bool ValidateColorAttachmentInRange(FramebufferAttachmentType attachment, const char* caller); Bool ValidateColorAttachmentInRange(FramebufferAttachmentType attachment, const char* caller);
Bool ValidateRenderbufferTarget(RenderbufferTarget target); Bool ValidateRenderbufferTarget(RenderbufferTarget target);
Bool ValidateRenderbufferName(Uint index, Bool allowZero = true); Bool ValidateRenderbufferName(Uint index, Bool allowZero = true);
+3 -3
View File
@@ -152,7 +152,7 @@ namespace MobileGL::MG_Impl::GLImpl {
// glGetSubroutineIndex / glUniformSubroutinesuiv, only the program-interface enum // glGetSubroutineIndex / glUniformSubroutinesuiv, only the program-interface enum
// plumbing. These are answered - with the GL 4.5 core minimums - because the conformance // plumbing. These are answered - with the GL 4.5 core minimums - because the conformance
// suite queries them before it checks for the feature and an INVALID_ENUM both leaves the // suite queries them before it checks for the feature and an INVALID_ENUM both leaves the
// caller reading its own uninitialised stack slot and strands an error for the next // caller reading its own uninitialized stack slot and strands an error for the next
// unrelated call to trip over. The extension is deliberately NOT advertised, so the // unrelated call to trip over. The extension is deliberately NOT advertised, so the
// numbers are a table entry, not a capability claim. // numbers are a table entry, not a capability claim.
constexpr GLint kFrontendMaxSubroutines = 256; constexpr GLint kFrontendMaxSubroutines = 256;
@@ -1542,7 +1542,7 @@ namespace MobileGL::MG_Impl::GLImpl {
// //
// The POINTER-valued macro, so the init-statement below is unchanged in a pull // The POINTER-valued macro, so the init-statement below is unchanged in a pull
// build and G1 cannot see this edit: the Bool-valued spelling moved this // build and G1 cannot see this edit: the Bool-valued spelling moved this
// function by -150 bytes for no behavioural reason at all. // function by -150 bytes for no behavioral reason at all.
if (const auto getGpuTimestampNs = if (const auto getGpuTimestampNs =
MGL_BACKEND_SLOT_PTR_CAP(GetGpuTimestampNs, MG_Pipe::kCapTimerQuery)) { MGL_BACKEND_SLOT_PTR_CAP(GetGpuTimestampNs, MG_Pipe::kCapTimerQuery)) {
MGP_FILL(GetGpuTimestampNs); MGP_FILL(GetGpuTimestampNs);
@@ -2584,7 +2584,7 @@ namespace MobileGL::MG_Impl::GLImpl {
// //
// The POINTER-valued macro, so the init-statement below is unchanged in a pull // The POINTER-valued macro, so the init-statement below is unchanged in a pull
// build and G1 cannot see this edit: the Bool-valued spelling moved this // build and G1 cannot see this edit: the Bool-valued spelling moved this
// function by -150 bytes for no behavioural reason at all. // function by -150 bytes for no behavioral reason at all.
if (const auto getGpuTimestampNs = if (const auto getGpuTimestampNs =
MGL_BACKEND_SLOT_PTR_CAP(GetGpuTimestampNs, MG_Pipe::kCapTimerQuery)) { MGL_BACKEND_SLOT_PTR_CAP(GetGpuTimestampNs, MG_Pipe::kCapTimerQuery)) {
MGP_FILL(GetGpuTimestampNs); MGP_FILL(GetGpuTimestampNs);
@@ -573,7 +573,7 @@ namespace MobileGL::MG_Impl::GLImpl {
} }
GLuint CreateShader_State(GLenum type) { GLuint CreateShader_State(GLenum type) {
// GL 4.6 core 7.1: shaderType is an enum, so an unrecognised one is INVALID_ENUM (it // GL 4.6 core 7.1: shaderType is an enum, so an unrecognized one is INVALID_ENUM (it
// used to be documented as INVALID_VALUE). The check has to happen HERE: the state // used to be documented as INVALID_VALUE). The check has to happen HERE: the state
// layer hands out a name for ShaderStage::Unknown just as happily as for a real // layer hands out a name for ShaderStage::Unknown just as happily as for a real
// stage, so the old "shaderId == 0 means bad type" test could never fire and an // stage, so the old "shaderId == 0 means bad type" test could never fire and an
@@ -3324,7 +3324,7 @@ namespace MobileGL::MG_Impl::GLImpl {
void GetProgramResourceiv(GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, void GetProgramResourceiv(GLuint program, GLenum programInterface, GLuint index, GLsizei propCount,
const GLenum* props, GLsizei bufSize, GLsizei* length, GLint* params) { const GLenum* props, GLsizei bufSize, GLsizei* length, GLint* params) {
// Every early-out below reports "nothing was written", and it has to say so before it can // Every early-out below reports "nothing was written", and it has to say so before it can
// take one: callers legitimately leave *length uninitialised and then loop to it. The CTS // take one: callers legitimately leave *length uninitialized and then loop to it. The CTS
// does exactly that (gl4cProgramInterfaceQueryTests.cpp:2172 declares `GLsizei length;` and // does exactly that (gl4cProgramInterfaceQueryTests.cpp:2172 declares `GLsizei length;` and
// walks `for (i = 0; i < length; ++i)` over a 1000-entry stack array), so an untouched // walks `for (i = 0; i < length; ++i)` over a 1000-entry stack array), so an untouched
// *length turned every error path here into a stack overrun inside the caller - // *length turned every error path here into a stack overrun inside the caller -
@@ -3414,7 +3414,7 @@ namespace MobileGL::MG_Impl::GLImpl {
// already carries, so this is a rename of glGetProgramResourceiv's props onto the older // already carries, so this is a rename of glGetProgramResourceiv's props onto the older
// entry point's - and the two are required to agree, which is only true while both read the // entry point's - and the two are required to agree, which is only true while both read the
// same model. It was a silent stub: it wrote nothing, raised nothing, and left every probe // same model. It was a silent stub: it wrote nothing, raised nothing, and left every probe
// reading its own uninitialised output. // reading its own uninitialized output.
static Bool TryMapActiveAtomicCounterBufferProp(GLenum pname, GLenum& outProp) { static Bool TryMapActiveAtomicCounterBufferProp(GLenum pname, GLenum& outProp) {
switch (pname) { switch (pname) {
case GL_ATOMIC_COUNTER_BUFFER_BINDING: case GL_ATOMIC_COUNTER_BUFFER_BINDING:
@@ -3547,7 +3547,7 @@ namespace MobileGL::MG_Impl::GLImpl {
// raise is one of theirs, raised at the point they would raise it. // raise is one of theirs, raised at the point they would raise it.
GLuint CreateShaderProgramv(GLenum type, GLsizei count, const GLchar* const* strings) { GLuint CreateShaderProgramv(GLenum type, GLsizei count, const GLchar* const* strings) {
// GL 4.6 core 7.3: a negative count is INVALID_VALUE and is checked before anything // GL 4.6 core 7.3: a negative count is INVALID_VALUE and is checked before anything
// is created, so a bad count never leaks a shader name. An unrecognised type is // is created, so a bad count never leaks a shader name. An unrecognized type is
// INVALID_ENUM, which CreateShader_State raises below. // INVALID_ENUM, which CreateShader_State raises below.
if (count < 0) { if (count < 0) {
MG_State::pGLContext->RecordError( MG_State::pGLContext->RecordError(
@@ -372,7 +372,7 @@ namespace MobileGL::MG_Impl::GLImpl {
} }
// GL_EXT_fragment_shading_rate 14.X.1: the only error is a rate outside table X.1, and the // GL_EXT_fragment_shading_rate 14.X.1: the only error is a rate outside table X.1, and the
// state is left untouched by it. Whether the device can honour the rate is not an error - // state is left untouched by it. Whether the device can honor the rate is not an error -
// an unsupported rate is clamped at rasterization, which the backends leave to the driver. // an unsupported rate is clamped at rasterization, which the backends leave to the driver.
void ShadingRateEXT_State(GLenum rate) { void ShadingRateEXT_State(GLenum rate) {
const Uint8 packed = MGPackShadingRate(rate); const Uint8 packed = MGPackShadingRate(rate);
@@ -57,7 +57,7 @@ namespace MobileGL::MG_Impl::GLImpl {
return; return;
} }
// Via GLint in both integer spellings: a direct float -> GLuint cast of a negative value // Via GLint in both integer spellings: a direct float -> GLuint cast of a negative value
// (GL_TEXTURE_MIN_LOD defaults to -1000) is undefined behaviour, while the two-step // (GL_TEXTURE_MIN_LOD defaults to -1000) is undefined behavior, while the two-step
// conversion is the well-defined modular one, and it is what the texture-side // conversion is the well-defined modular one, and it is what the texture-side
// GetTexParameterIuiv fallback does. // GetTexParameterIuiv fallback does.
const GLint asInt = static_cast<GLint>(value); const GLint asInt = static_cast<GLint>(value);
@@ -86,7 +86,7 @@ namespace MobileGL::MG_Impl::GLImpl {
case GL_TEXTURE_MAX_LOD: case GL_TEXTURE_MAX_LOD:
case GL_TEXTURE_LOD_BIAS: case GL_TEXTURE_LOD_BIAS:
return true; return true;
// Four components, and GL puts no range on them - a border colour outside [0,1] is // Four components, and GL puts no range on them - a border color outside [0,1] is
// clamped when a fixed-point format is sampled, not rejected here. The scalar readers // clamped when a fixed-point format is sampled, not rejected here. The scalar readers
// below would look at one component and invent an error. // below would look at one component and invent an error.
case GL_TEXTURE_BORDER_COLOR: case GL_TEXTURE_BORDER_COLOR:
@@ -182,7 +182,7 @@ namespace MobileGL::MG_Impl::GLImpl {
const auto* values = (const GLfloat*)param; const auto* values = (const GLfloat*)param;
samplerObj->SetBorderColor(FloatVec4(values[0], values[1], values[2], values[3])); samplerObj->SetBorderColor(FloatVec4(values[0], values[1], values[2], values[3]));
} else if (!isIntegerCommand) { } else if (!isIntegerCommand) {
// glSamplerParameteriv: GL 4.6 core equation 2.2 into the FLOAT border colour. // glSamplerParameteriv: GL 4.6 core equation 2.2 into the FLOAT border color.
const auto* values = (const GLint*)param; const auto* values = (const GLint*)param;
samplerObj->SetBorderColor(FloatVec4(MG_Util::SignedNormalizedInt32ToFloat(values[0]), samplerObj->SetBorderColor(FloatVec4(MG_Util::SignedNormalizedInt32ToFloat(values[0]),
MG_Util::SignedNormalizedInt32ToFloat(values[1]), MG_Util::SignedNormalizedInt32ToFloat(values[1]),
+1 -1
View File
@@ -167,7 +167,7 @@ namespace MobileGL::MG_Impl::GLImpl {
void GetSynciv(GLsync sync, GLenum pname, GLsizei bufSize, GLsizei* length, GLint* values) { void GetSynciv(GLsync sync, GLenum pname, GLsizei bufSize, GLsizei* length, GLint* values) {
// GL 4.6 core 4.1: a negative bufSize is INVALID_VALUE, an unnamed sync is INVALID_VALUE // GL 4.6 core 4.1: a negative bufSize is INVALID_VALUE, an unnamed sync is INVALID_VALUE
// and an unrecognised pname is INVALID_ENUM. All three used to leave glGetError() clean // and an unrecognized pname is INVALID_ENUM. All three used to leave glGetError() clean
// and write a plausible-looking zero, which is the one failure mode a caller cannot tell // and write a plausible-looking zero, which is the one failure mode a caller cannot tell
// apart from a real answer - GL_SYNC_STATUS legitimately answers GL_UNSIGNALED (0x9118), // apart from a real answer - GL_SYNC_STATUS legitimately answers GL_UNSIGNALED (0x9118),
// but a mistyped pname answered a bare 0 that no query ever returns. // but a mistyped pname answered a bare 0 that no query ever returns.
+16 -16
View File
@@ -67,7 +67,7 @@ namespace MobileGL::MG_Impl::GLImpl {
} }
// glTexParameteriv(GL_TEXTURE_BORDER_COLOR): GL 4.6 core 8.10 sends the components through // glTexParameteriv(GL_TEXTURE_BORDER_COLOR): GL 4.6 core 8.10 sends the components through
// equation 2.2 into the floating-point border colour. glGetTexParameteriv reverses it with // equation 2.2 into the floating-point border color. glGetTexParameteriv reverses it with
// equation 2.3; the two live in one header so they cannot drift apart. // equation 2.3; the two live in one header so they cannot drift apart.
void SetTextureBorderColorFromInts(const SharedPtr<MG_State::GLState::ITextureObject>& textureObject, void SetTextureBorderColorFromInts(const SharedPtr<MG_State::GLState::ITextureObject>& textureObject,
const GLint* params) { const GLint* params) {
@@ -582,7 +582,7 @@ namespace MobileGL::MG_Impl::GLImpl {
// AT the category limit (BackendObject_DirectGLES's ProbeTextureSampleCounts) so its // AT the category limit (BackendObject_DirectGLES's ProbeTextureSampleCounts) so its
// head can never exceed it, and max() therefore always collapsed to the category // head can never exceed it, and max() therefore always collapsed to the category
// value. A format whose 4- and 2-sample probes fail inside a 4-sample category - a // value. A format whose 4- and 2-sample probes fail inside a 4-sample category - a
// float colour format under EXT_color_buffer_float is the natural instance - was // float color format under EXT_color_buffer_float is the natural instance - was
// still accepted at 4, silently squeezed to 1 by ClampSamplesToBackendSupport, and // still accepted at 4, silently squeezed to 1 by ClampSamplesToBackendSupport, and
// then reported as 4 by GL_TEXTURE_SAMPLES while glGetInternalformativ said 1. // then reported as 4 by GL_TEXTURE_SAMPLES while glGetInternalformativ said 1.
const Int probedMaxSamples = GetProbedMaxTextureSamples(textureTarget, textureInternalFormat); const Int probedMaxSamples = GetProbedMaxTextureSamples(textureTarget, textureInternalFormat);
@@ -672,7 +672,7 @@ namespace MobileGL::MG_Impl::GLImpl {
// Redefining level 0 of a texture that already had a base image drops the rest of the chain, // Redefining level 0 of a texture that already had a base image drops the rest of the chain,
// which is exactly what AllocateLevel used to do implicitly for every level. Keeping that // which is exactly what AllocateLevel used to do implicitly for every level. Keeping that
// behaviour for level 0 - and only for level 0 - is what makes the grow-only change safe: // behavior for level 0 - and only for level 0 - is what makes the grow-only change safe:
// any level-0 respecification leaves the chain in precisely the state it would have had // any level-0 respecification leaves the chain in precisely the state it would have had
// before, while an upload to level N no longer destroys the levels beneath it. // before, while an upload to level N no longer destroys the levels beneath it.
// //
@@ -1002,7 +1002,7 @@ namespace MobileGL::MG_Impl::GLImpl {
return true; return true;
} }
// GL 4.6 core 8.6: CopyTexSubImage* is not affected by pixel-store state or by a bound // GL 4.6 core 8.6: CopyTexSubImage* is not affected by pixel-store state or by a bound
// pack buffer, but the backend readback this borrows honours both. Neutralise them for the // pack buffer, but the backend readback this borrows honors both. Neutralise them for the
// duration of the read and put them back afterwards. // duration of the read and put them back afterwards.
class ScopedNeutralPackState { class ScopedNeutralPackState {
public: public:
@@ -1203,7 +1203,7 @@ namespace MobileGL::MG_Impl::GLImpl {
} }
// The six pnames a texture object shares with a sampler object carry an enum VALUE, and an // The six pnames a texture object shares with a sampler object carry an enum VALUE, and an
// unrecognised one is INVALID_ENUM. The texture path used to hand the value straight to // unrecognized one is INVALID_ENUM. The texture path used to hand the value straight to
// ConvertGLEnumToSamplerWrapMode / ...FilterMode and throw the Unknown away, so // ConvertGLEnumToSamplerWrapMode / ...FilterMode and throw the Unknown away, so
// glTexParameteri(GL_TEXTURE_WRAP_S, GL_RED) was silently accepted. Sampler objects have had // glTexParameteri(GL_TEXTURE_WRAP_S, GL_RED) was silently accepted. Sampler objects have had
// exactly this validator all along; calling it here rather than writing a second one is also // exactly this validator all along; calling it here rather than writing a second one is also
@@ -1348,7 +1348,7 @@ namespace MobileGL::MG_Impl::GLImpl {
// every setter reaches" - and MGPTextureParams takes MinLod, MaxLod and LodBias off that // every setter reaches" - and MGPTextureParams takes MinLod, MaxLod and LodBias off that
// object, so every glTexParameter that writes GL_TEXTURE_MIN_LOD / MAX_LOD / LOD_BIAS // object, so every glTexParameter that writes GL_TEXTURE_MIN_LOD / MAX_LOD / LOD_BIAS
// landed on state nothing watched and the applier's record kept saying MinLod = 0. // landed on state nothing watched and the applier's record kept saying MinLod = 0.
// Wrong pixels, not a lost optimisation. // Wrong pixels, not a lost optimization.
// //
// THE HOOK IS HERE RATHER THAN ON BumpVersion because MG_State/GLState/SamplerState is // THE HOOK IS HERE RATHER THAN ON BumpVersion because MG_State/GLState/SamplerState is
// package C's after the tag; C.7 grants this file for exactly this class of path ("the // package C's after the tag; C.7 grants this file for exactly this class of path ("the
@@ -1462,7 +1462,7 @@ namespace MobileGL::MG_Impl::GLImpl {
// every setter reaches" - and MGPTextureParams takes MinLod, MaxLod and LodBias off that // every setter reaches" - and MGPTextureParams takes MinLod, MaxLod and LodBias off that
// object, so every glTexParameter that writes GL_TEXTURE_MIN_LOD / MAX_LOD / LOD_BIAS // object, so every glTexParameter that writes GL_TEXTURE_MIN_LOD / MAX_LOD / LOD_BIAS
// landed on state nothing watched and the applier's record kept saying MinLod = 0. // landed on state nothing watched and the applier's record kept saying MinLod = 0.
// Wrong pixels, not a lost optimisation. // Wrong pixels, not a lost optimization.
// //
// THE HOOK IS HERE RATHER THAN ON BumpVersion because MG_State/GLState/SamplerState is // THE HOOK IS HERE RATHER THAN ON BumpVersion because MG_State/GLState/SamplerState is
// package C's after the tag; C.7 grants this file for exactly this class of path ("the // package C's after the tag; C.7 grants this file for exactly this class of path ("the
@@ -2700,9 +2700,9 @@ namespace MobileGL::MG_Impl::GLImpl {
return MG_State::pGLContext->ValidateTextureObject(texture) ? GL_TRUE : GL_FALSE; return MG_State::pGLContext->ValidateTextureObject(texture) ? GL_TRUE : GL_FALSE;
} }
// Every texture parameter but the border colour, for one resolved texture: up to four values, // Every texture parameter but the border color, for one resolved texture: up to four values,
// as doubles because a double holds each enum, level and float this state stores exactly. False // as doubles because a double holds each enum, level and float this state stores exactly. False
// after recording an error. The border colour converts differently for each query type (GL 4.6 // after recording an error. The border color converts differently for each query type (GL 4.6
// core 8.11.2), so each form answers it itself. // core 8.11.2), so each form answers it itself.
static Bool QueryTextureParameter(const SharedPtr<MG_State::GLState::ITextureObject>& textureObject, GLenum pname, static Bool QueryTextureParameter(const SharedPtr<MG_State::GLState::ITextureObject>& textureObject, GLenum pname,
Double (&values)[4], Uint& count, const char* caller) { Double (&values)[4], Uint& count, const char* caller) {
@@ -2819,7 +2819,7 @@ namespace MobileGL::MG_Impl::GLImpl {
GLenum pname, GLint* params, const char* caller) { GLenum pname, GLint* params, const char* caller) {
if (pname == GL_TEXTURE_BORDER_COLOR) { if (pname == GL_TEXTURE_BORDER_COLOR) {
// The inverse of glTexParameteriv: GL 4.6 core equation 2.3 against equation 2.2 on the // The inverse of glTexParameteriv: GL 4.6 core equation 2.3 against equation 2.2 on the
// write side (SetTextureBorderColorFromInts). The raw integer colour is the Iiv query's. // write side (SetTextureBorderColorFromInts). The raw integer color is the Iiv query's.
const auto& borderColor = textureObject->GetBorderColor(); const auto& borderColor = textureObject->GetBorderColor();
for (int i = 0; i < 4; ++i) for (int i = 0; i < 4; ++i)
params[i] = MG_Util::FloatToSignedNormalizedInt32(borderColor[i]); params[i] = MG_Util::FloatToSignedNormalizedInt32(borderColor[i]);
@@ -3302,7 +3302,7 @@ namespace MobileGL::MG_Impl::GLImpl {
namespace { namespace {
// The eleven targets GL 4.6 core 18.3.2 accepts. GL_TEXTURE_BUFFER, the six cube FACE // The eleven targets GL 4.6 core 18.3.2 accepts. GL_TEXTURE_BUFFER, the six cube FACE
// enums and every PROXY enum all convert to a TextureTarget this frontend recognises, // enums and every PROXY enum all convert to a TextureTarget this frontend recognizes,
// so ValidateTextureTarget lets them through; here they are INVALID_ENUM. // so ValidateTextureTarget lets them through; here they are INVALID_ENUM.
Bool ValidateCopyImageTarget(GLenum target, const char* endpointName) { Bool ValidateCopyImageTarget(GLenum target, const char* endpointName) {
switch (target) { switch (target) {
@@ -3786,7 +3786,7 @@ namespace MobileGL::MG_Impl::GLImpl {
const Int z = cubeFaces ? 0 : offset.z(); const Int z = cubeFaces ? 0 : offset.z();
const Int depth = cubeFaces ? 1 : size.z(); const Int depth = cubeFaces ? 1 : size.z();
// Subtractions rather than sums: offset + extent are application-supplied GLints, and a // Subtractions rather than sums: offset + extent are application-supplied GLints, and a
// signed overflow is undefined behaviour a compiler may resolve by assuming the check passes. // signed overflow is undefined behavior a compiler may resolve by assuming the check passes.
if (offset.x() < 0 || offset.y() < 0 || z < 0 || size.x() > levelSize.x() - offset.x() || if (offset.x() < 0 || offset.y() < 0 || z < 0 || size.x() > levelSize.x() - offset.x() ||
size.y() > levelSize.y() - offset.y() || depth > levelDepth - z) { size.y() > levelSize.y() - offset.y() || depth > levelDepth - z) {
MG_State::pGLContext->RecordError( MG_State::pGLContext->RecordError(
@@ -4421,7 +4421,7 @@ namespace MobileGL::MG_Impl::GLImpl {
// //
// ProcessTexturePixelsDataPack performs NO format or type conversion: it sizes every texel with // ProcessTexturePixelsDataPack performs NO format or type conversion: it sizes every texel with
// GetInternalBytesPerPixel(the TEXTURE's internal format) and memcpys the shadow rows verbatim, // GetInternalBytesPerPixel(the TEXTURE's internal format) and memcpys the shadow rows verbatim,
// and it carries a standing TODO for the pixel-store parameters, so it honours only SwapBytes and // and it carries a standing TODO for the pixel-store parameters, so it honors only SwapBytes and
// the bitmap LSBFirst path. Both facts are invisible from the outside, and both are dangerous: // the bitmap LSBFirst path. Both facts are invisible from the outside, and both are dangerous:
// //
// * a (format, type) narrower than the shadow's own texel makes the copy write MORE bytes than // * a (format, type) narrower than the shadow's own texel makes the copy write MORE bytes than
@@ -4432,7 +4432,7 @@ namespace MobileGL::MG_Impl::GLImpl {
// overflow but is still wrong data. // overflow but is still wrong data.
// * a pack state that puts padding, a row-length override or a skip offset between rows is // * a pack state that puts padding, a row-length override or a skip offset between rows is
// ignored outright, so the rows land at the wrong destination strides - while the GPU // ignored outright, so the rows land at the wrong destination strides - while the GPU
// readback path (DirectGLES StoreClientRows, and DirectVulkan through it) honours all of it. // readback path (DirectGLES StoreClientRows, and DirectVulkan through it) honors all of it.
// Same glGetTexImage call, two different destination layouts, decided by whether the texture // Same glGetTexImage call, two different destination layouts, decided by whether the texture
// happens to have a GPU image. // happens to have a GPU image.
// //
@@ -4475,7 +4475,7 @@ namespace MobileGL::MG_Impl::GLImpl {
ErrorCode::InvalidOperation, ErrorCode::InvalidOperation,
MakeUnique<GenericErrorInfo>( MakeUnique<GenericErrorInfo>(
"MG_Impl/GLImpl", caller, "MG_Impl/GLImpl", caller,
"The CPU-shadow readback path packs rows tightly and cannot honour a pixel-store state that " "The CPU-shadow readback path packs rows tightly and cannot honor a pixel-store state that "
"adds row padding, a row-length override or a skip offset.")); "adds row padding, a row-length override or a skip offset."));
return false; return false;
} }
@@ -6100,7 +6100,7 @@ namespace MobileGL::MG_Impl::GLImpl {
// The backend publishes where its device really places the samples of each count (a // The backend publishes where its device really places the samples of each count (a
// shader's gl_SamplePosition reads the same places). A count it could not determine keeps // shader's gl_SamplePosition reads the same places). A count it could not determine keeps
// answering the pixel centre. // answering the pixel center.
Float position[2] = {0.5f, 0.5f}; Float position[2] = {0.5f, 0.5f};
if (MG_Backend::pActiveBackendObject != nullptr) { if (MG_Backend::pActiveBackendObject != nullptr) {
MG_Backend::pActiveBackendObject->GetDynamicParameters().GetSamplePosition(samples, static_cast<Int>(index), MG_Backend::pActiveBackendObject->GetDynamicParameters().GetSamplePosition(samples, static_cast<Int>(index),

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