mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 01:53:24 +09:00
[Refactor] (P13 W5 S8): MOBILEGL_BUILD_RECORD_ARM is folded away - the record arm is unconditional - and retired_switches keeps it from coming back.
This commit is contained in:
+18
-29
@@ -553,12 +553,9 @@ if (MOBILEGL_BUILD_DISAGGREGATED AND
|
||||
set(MOBILEGL_BUILD_DISAGGREGATED_INPROC OFF)
|
||||
endif()
|
||||
|
||||
# P13 W5 (notes/p13/W5-DESIGN.md): THE RECORD ARM'S OWN GUARD. A plain variable, not an option and
|
||||
# not a cache entry, so no build tree can keep a stale value (W3a's lesson). It equalled
|
||||
# MOBILEGL_BUILD_DISAGGREGATED while the record arm's guards were reclassified one by one; since S7
|
||||
# it is on for every build, so the library without MG_Remote (the FCL-embedded one) runs the record
|
||||
# arm too. S8 removes the variable and the guards it still names. Always defined, to 0 or 1.
|
||||
set(MOBILEGL_BUILD_RECORD_ARM ON)
|
||||
# P13 W5 (notes/p13/W5-DESIGN.md): the record arm is part of every library - the one without
|
||||
# MG_Remote (FCL's) runs it too. Its temporary guard, MOBILEGL_BUILD_RECORD_ARM, was folded away at
|
||||
# S8 (scripts/ci/retired_switches.py keeps it from coming back).
|
||||
|
||||
message(STATUS "MobileGL: PipeInputs push ON, appending the MGPipe fill sources")
|
||||
list(APPEND SOURCE_FILES
|
||||
@@ -684,23 +681,21 @@ if (MOBILEGL_BUILD_DISAGGREGATED)
|
||||
)
|
||||
endif()
|
||||
|
||||
# P13 W5: the record arm's own sources, compiled wherever the record arm is (MG_Remote or not).
|
||||
if (MOBILEGL_BUILD_RECORD_ARM)
|
||||
list(APPEND SOURCE_FILES
|
||||
# P7 gate 5 (g5-msprobe): the Magma wire arm's two multisample depth/stencil resolve
|
||||
# arms and the probe that measures the first against the second, shared by the
|
||||
# renderer's bring-up and the Vulkan POST.
|
||||
MobileGL/MG_Backend/DirectVulkan/Renderer/WireDepthResolveProbe.cpp
|
||||
# P5 b1: the block-granularity persistent-map push the record arm makes mandatory (T2).
|
||||
MobileGL/MG_State/GLState/BufferState/PersistentMapTracker.cpp
|
||||
# P13 W5: the verb emitters and the monolith verb port, and the client GPU-write set
|
||||
# they mark before a draw or a dispatch (MG_Record; EmitTables.cpp resolves the session).
|
||||
MobileGL/MG_Impl/Pipe/Verb/VerbPort.cpp
|
||||
MobileGL/MG_Impl/Pipe/Verb/GpuWriteSet.cpp
|
||||
# The record verbs' consumer (ServerVerbSink under a transport is this plus the session's).
|
||||
MobileGL/MG_Backend/Record/RecordVerbSink.cpp
|
||||
)
|
||||
endif()
|
||||
# P13 W5: the record arm's own sources, compiled into every library (MG_Remote or not).
|
||||
list(APPEND SOURCE_FILES
|
||||
# P7 gate 5 (g5-msprobe): the Magma wire arm's two multisample depth/stencil resolve
|
||||
# arms and the probe that measures the first against the second, shared by the
|
||||
# renderer's bring-up and the Vulkan POST.
|
||||
MobileGL/MG_Backend/DirectVulkan/Renderer/WireDepthResolveProbe.cpp
|
||||
# P5 b1: the block-granularity persistent-map push the record arm makes mandatory (T2).
|
||||
MobileGL/MG_State/GLState/BufferState/PersistentMapTracker.cpp
|
||||
# P13 W5: the verb emitters and the monolith verb port, and the client GPU-write set
|
||||
# they mark before a draw or a dispatch (MG_Record; EmitTables.cpp resolves the session).
|
||||
MobileGL/MG_Impl/Pipe/Verb/VerbPort.cpp
|
||||
MobileGL/MG_Impl/Pipe/Verb/GpuWriteSet.cpp
|
||||
# The record verbs' consumer (ServerVerbSink under a transport is this plus the session's).
|
||||
MobileGL/MG_Backend/Record/RecordVerbSink.cpp
|
||||
)
|
||||
|
||||
if (APPLE AND NOT MOBILEGL_IOS)
|
||||
list(APPEND SOURCE_FILES
|
||||
@@ -764,12 +759,6 @@ if (MOBILEGL_BUILD_DISAGGREGATED)
|
||||
list(APPEND MOBILEGL_COMPILE_DEF -DMOBILEGL_BUILD_DISAGGREGATED=1)
|
||||
endif()
|
||||
|
||||
if (MOBILEGL_BUILD_RECORD_ARM)
|
||||
list(APPEND MOBILEGL_COMPILE_DEF -DMOBILEGL_BUILD_RECORD_ARM=1)
|
||||
else()
|
||||
list(APPEND MOBILEGL_COMPILE_DEF -DMOBILEGL_BUILD_RECORD_ARM=0)
|
||||
endif()
|
||||
|
||||
if (MOBILEGL_PIPE_VERIFY)
|
||||
list(APPEND MOBILEGL_COMPILE_DEF -DMOBILEGL_PIPE_VERIFY=1)
|
||||
endif()
|
||||
|
||||
@@ -462,7 +462,6 @@ namespace MobileGL::MG_Config {
|
||||
NamedPipe = 4, // P6: Windows named pipe (Endpoint = <name>)
|
||||
};
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P13 W5: THE RECORD ARM'S OWN KNOBS. They kept their MOBILEGL_IPC_* names (logs, CI lanes and
|
||||
// notes spell them so), but they tune the record arm - the persistent-map push and Magma's wire
|
||||
// stores - which runs in a library without a transport too, so they are not IpcTable's.
|
||||
@@ -498,7 +497,6 @@ namespace MobileGL::MG_Config {
|
||||
Uint32 PersistentMprotect = 1;
|
||||
};
|
||||
extern RecordArmTable RecordArm;
|
||||
#endif
|
||||
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
// Parsed once by MG_ConfigLoader::Init(). Defaults to Monolith even here: building the
|
||||
@@ -759,7 +757,6 @@ namespace MobileGL::MG_Config {
|
||||
inline constexpr TransportMode Transport = TransportMode::Monolith;
|
||||
#endif
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P13 W4: THE DATA-ARM QUESTION - do the backends read RECORDS (the applier's twins, the staged
|
||||
// stores, the verb's own handles) rather than frontend objects? Under a wire the answer has
|
||||
// always been yes; P13 makes it yes for monolith too ("monolith is the push variant"), one
|
||||
@@ -779,10 +776,4 @@ namespace MobileGL::MG_Config {
|
||||
inline Bool RecordArmAliasesFrontend() {
|
||||
return Transport == TransportMode::Monolith && MonolithTakesRecordArm;
|
||||
}
|
||||
#else
|
||||
// A build without the record arm reads frontend objects everywhere (the negative control a
|
||||
// library built with MOBILEGL_BUILD_RECORD_ARM=0 keeps until W6 deletes the frontend arms).
|
||||
inline constexpr Bool DataArmIsRecord() { return false; }
|
||||
inline constexpr Bool RecordArmAliasesFrontend() { return false; }
|
||||
#endif
|
||||
} // namespace MobileGL::MG_Config
|
||||
|
||||
@@ -39,10 +39,8 @@ namespace MobileGL::MG_Config {
|
||||
Bool SplitTransportRequestedByConfig = false;
|
||||
IpcTable Ipc;
|
||||
#endif
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
Bool MonolithTakesRecordArm = false;
|
||||
RecordArmTable RecordArm;
|
||||
#endif
|
||||
} // namespace MobileGL::MG_Config
|
||||
|
||||
namespace MobileGL::MG_ConfigLoader {
|
||||
@@ -678,7 +676,6 @@ namespace MobileGL::MG_ConfigLoader {
|
||||
}
|
||||
#endif
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P13 W4 (Config.h DataArmIsRecord). Read after InitTransport: `frontend` names monolith's
|
||||
// arm only - a wire has no frontend objects on its server half to read, so there it is
|
||||
// ignored by name rather than half-honoured.
|
||||
@@ -737,7 +734,6 @@ namespace MobileGL::MG_ConfigLoader {
|
||||
"the persistent-map push is OFF and a coherent-map scenario must go red");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void Init() {
|
||||
MGLOG_D("Loading configuration from environment variables...");
|
||||
@@ -745,17 +741,13 @@ namespace MobileGL::MG_ConfigLoader {
|
||||
|
||||
InitBackendType();
|
||||
InitFeatures();
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
InitRecordArm();
|
||||
#endif
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
// After InitFeatures, so the one line InitIpc logs is the last word on this run's
|
||||
// configuration, and before the accepted-env map is destroyed just below.
|
||||
InitTransport();
|
||||
#endif
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
InitDataArm();
|
||||
#endif
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
InitIpc();
|
||||
#endif
|
||||
|
||||
@@ -24,9 +24,7 @@
|
||||
#if MOBILEGL_PIPE_VERIFY
|
||||
#include <MG_Backend/MGPipe/PipeInputs.h> // MGPipeVerifyFlushSummary, before Debug::Close in DestroyImpl
|
||||
#endif
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
#include <MG_Impl/Pipe/Verb/VerbPort.h>
|
||||
#endif
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
#include <MG_Remote/Client/ServerProbe.h>
|
||||
#endif
|
||||
@@ -185,7 +183,6 @@ namespace MobileGL {
|
||||
#endif
|
||||
{
|
||||
MG_Backend::Init();
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P13 W4: monolith's record arm reads the verb's own state, which only a verb sink
|
||||
// writes; the port routes the ported verbs through one (MG_Impl/Pipe/Verb/VerbPort.cpp).
|
||||
// Installed HERE, by the library's role-neutral init, rather than by MG_Backend's: the
|
||||
@@ -193,7 +190,6 @@ namespace MobileGL {
|
||||
if (MG_Config::RecordArmAliasesFrontend() && MG_Backend::pActiveBackendObject) {
|
||||
MG_Record::InstallMonolithVerbPort(MG_Backend::gBackendFunctionsTable);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
MGLOG_D("MG_Backend initialized");
|
||||
MG_Impl::Init();
|
||||
|
||||
@@ -10,9 +10,7 @@
|
||||
#include <Includes.h>
|
||||
#include "MG_State/GLState/TextureState/TextureEnum.h"
|
||||
#include <MG_Util/Damage/Damage.h>
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
#include <MG_Pipe/MGPipeHandles.h>
|
||||
#endif
|
||||
|
||||
namespace MobileGL {
|
||||
namespace MG_State::GLState {
|
||||
@@ -37,22 +35,16 @@ namespace MobileGL {
|
||||
struct CopyImageEndpoint {
|
||||
SharedPtr<MG_State::GLState::ITextureObject> Texture;
|
||||
SharedPtr<MG_State::GLState::RenderbufferObject> Renderbuffer;
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
MG_Pipe::MGPipeHandle TextureHandle = MG_Pipe::kMGPipeNullHandle;
|
||||
MG_Pipe::MGPipeHandle RenderbufferHandle = MG_Pipe::kMGPipeNullHandle;
|
||||
#endif
|
||||
|
||||
Bool IsRenderbuffer() const {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (!MG_Pipe::MGPipeHandleIsNull(RenderbufferHandle)) return true;
|
||||
#endif
|
||||
return Renderbuffer != nullptr;
|
||||
}
|
||||
Bool Exists() const {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (!MG_Pipe::MGPipeHandleIsNull(TextureHandle)) return true;
|
||||
if (!MG_Pipe::MGPipeHandleIsNull(RenderbufferHandle)) return true;
|
||||
#endif
|
||||
return Texture != nullptr || Renderbuffer != nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -32,7 +32,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
return draw == EGL_NO_SURFACE && read == EGL_NO_SURFACE && ctx == EGL_NO_CONTEXT;
|
||||
}
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// ID-54 / ID-67 (v1, under ID-52/ID-59's grant for this file). Which VIRTUAL (dpy, draw,
|
||||
// read, ctx) the process's one native ES context is currently bound FOR, on the apply
|
||||
// thread. DirectGLES has one native context and one native surface (g_Context, g_Surface)
|
||||
@@ -78,7 +77,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
void ClearGLErrors(const MG_External::GLESFunctionsTable& gl) {
|
||||
if (!gl.glGetError) return;
|
||||
@@ -860,7 +858,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
|
||||
Int maxSamples = 0;
|
||||
const SizeT formatIndex = static_cast<SizeT>(logicalFormat);
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// C6 / ID-52: read the ROLE's own cache. Under split that is the server's private backend
|
||||
// (ActiveBackendFormatCaps), not pActiveBackendObject, which holds the client's mirror.
|
||||
const FormatCapabilityCache* activeCaps = ActiveBackendFormatCaps();
|
||||
@@ -872,17 +869,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
maxSamples = probedCounts.front();
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (pActiveBackendObject && targetIndex < kFormatCapabilityTargetCount &&
|
||||
formatIndex < kFormatCapabilityFormatCount) {
|
||||
// Descending, so the head is the largest count this device actually allocated.
|
||||
const Vector<Int>& probedCounts =
|
||||
pActiveBackendObject->GetFormatCapabilities().SampleCounts[targetIndex][formatIndex];
|
||||
if (!probedCounts.empty()) {
|
||||
maxSamples = probedCounts.front();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (maxSamples <= 0) {
|
||||
maxSamples = GetGLESFormatMaxSamples(g_GLESCapabilities, logicalFormat, imageFormat);
|
||||
}
|
||||
@@ -1085,7 +1071,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
ResetEGLRuntimeState();
|
||||
}
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// ID-54 / ID-67: as for the window surface - InitPbufferSurface bound natively. The pull
|
||||
// arm below is the original statement, byte for byte (G1).
|
||||
const Bool created = BackendObject::CreateEGLPbufferSurface(surface, width, height);
|
||||
@@ -1098,9 +1083,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
NoteNativeContextGone();
|
||||
}
|
||||
return created;
|
||||
#else
|
||||
return BackendObject::CreateEGLPbufferSurface(surface, width, height);
|
||||
#endif
|
||||
}
|
||||
|
||||
Bool BackendObject_DirectGLES::ResizeEGLWindowSurface(EGLSurface surface, Uint32 width, Uint32 height) {
|
||||
@@ -1139,9 +1121,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
if (!DirectGLES::ReleaseCurrent()) {
|
||||
return false;
|
||||
}
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
NoteNativeContextGone();
|
||||
#endif
|
||||
return BackendObject::MakeEGLCurrent(dpy, draw, read, ctx);
|
||||
}
|
||||
|
||||
@@ -1212,9 +1192,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
|
||||
if (!BackendObject::MakeEGLCurrent(dpy, draw, read, ctx)) {
|
||||
(void)DirectGLES::ReleaseCurrent();
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
NoteNativeContextGone();
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -1267,9 +1245,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
void BackendObject_DirectGLES::ReleaseEGLResources() {
|
||||
const std::lock_guard<std::recursive_mutex> lock(m_eglStateMutex);
|
||||
DestroyEGLContext();
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
NoteNativeContextGone();
|
||||
#endif
|
||||
m_suspendedWindowSurfaces.clear();
|
||||
BackendObject::ReleaseEGLResources();
|
||||
}
|
||||
@@ -1295,10 +1271,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
}
|
||||
m_suspendedWindowSurfaces.push_back(client);
|
||||
}
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// The native binding moved onto the placeholder: the next make-current binds for real.
|
||||
NoteNativeContextGone();
|
||||
#endif
|
||||
MGLOG_I("DirectGLES: %zu window surface(s) on server window %p now draw into placeholder pbuffers; the "
|
||||
"contexts and their objects stay",
|
||||
onWindow.size(), window);
|
||||
@@ -1327,9 +1301,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
if (surface == EGL_NO_SURFACE) return false;
|
||||
native->second = surface;
|
||||
}
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
NoteNativeContextGone();
|
||||
#endif
|
||||
MGLOG_I("DirectGLES: %zu suspended window surface(s) rebuilt on server window %p (%ux%u)", suspended.size(),
|
||||
window, width, height);
|
||||
return true;
|
||||
@@ -1366,9 +1338,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// every object the client had built - which a multi-window client hit on its first closed
|
||||
// popup.
|
||||
(void)surface;
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
NoteNativeContextGone();
|
||||
#endif
|
||||
}
|
||||
|
||||
void BackendObject_DirectGLES::OnEGLSurfaceForgotten(EGLSurface surface) {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -30,7 +30,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
Bool ReadTextureLevelTight(GLuint texture, TextureTarget target, TextureUploadTarget uploadTarget,
|
||||
TextureInternalFormat logicalFormat, GLint level, const IntVec3& logicalExtent,
|
||||
Bool sourceUsesImageCarrier, GLenum format, GLenum type, Vector<Uint8>& bytes);
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
Bool ReadTextureImageWire(const MG_Pipe::MGPReadbackInfo& info, Vector<Uint8>& bytes);
|
||||
// Whether the NATIVE texture currently holds `level` of `uploadTarget` at `logicalExtent`
|
||||
// (glGetTexLevelParameteriv). False for a level the driver never allocated - one defined after
|
||||
@@ -38,7 +37,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// refuses answers true, keeping the caller on its readback path.
|
||||
Bool NativeTextureLevelHasExtent(GLuint texture, TextureTarget target, TextureUploadTarget uploadTarget,
|
||||
GLint level, const IntVec3& logicalExtent);
|
||||
#endif
|
||||
void ClearBufferfi(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil);
|
||||
void ClearBufferfv(GLenum buffer, GLint drawbuffer, const GLfloat* value);
|
||||
void ClearBufferuiv(GLenum buffer, GLint drawbuffer, const GLuint* value);
|
||||
@@ -432,12 +430,10 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// native context already current on this thread IS that one - so the single-context world pays
|
||||
// nothing for it. Called from the bind_context path on the apply thread.
|
||||
Bool MakeNativeContextCurrentForBoundToken();
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P14: the server's backend turn moved to the calling apply thread (ServerLoop.cpp's
|
||||
// BackendTurnLock). Takes the backend's GL ownership for this thread and drops every
|
||||
// binding shadow, which still describes the previous holder's native context.
|
||||
void OnBackendTurnHandoff();
|
||||
#endif
|
||||
// How many native contexts this process holds, and how many of them belong to one session. The
|
||||
// test observables for "a second context did not tear the first one down".
|
||||
Uint64 NativeContextCount();
|
||||
|
||||
@@ -18,12 +18,10 @@
|
||||
// needs only the name - the deleter is captured where the archive is constructed - but the
|
||||
// program build reads through it, so the one translation unit that does needs the whole type.
|
||||
#include <MG_State/GLState/ProgramState/ProgramArtifactsCodec.h>
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// R-11's server-owned staging copy. Header-only and package v1's; see its own header block for
|
||||
// why GLESBufferResource does not simply gain a member.
|
||||
#include <MG_Backend/Record/StagedShadow.h>
|
||||
#include <MG_Backend/Record/StagedTextureStore.h>
|
||||
#endif
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
#include <MG_Remote/Server/ServerLoop.h>
|
||||
// P5c (ct): object_death's producer (CONTRACT-P5C.md §5.2) - the death notice's split arm
|
||||
@@ -1256,7 +1254,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
return StorageMatchesSize(resource, bufferObject.GetSize());
|
||||
}
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// -------------------------------------------------------------------------------
|
||||
// R-11 - THE SERVER'S OWN COPY OF THE STAGED BYTES. Package v1.
|
||||
//
|
||||
@@ -1304,7 +1301,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
ServerStaged().RequireCoverage(&resource, hostBase, start, end, site);
|
||||
}
|
||||
}
|
||||
#endif // MOBILEGL_BUILD_RECORD_ARM
|
||||
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
// -------------------------------------------------------------------------------
|
||||
@@ -1385,7 +1381,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// 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
|
||||
// compiles exactly what it compiled before R-11 and the split arm is the only new behaviour.
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
#define MGL_SERVER_STAGED_ADOPT(res, width, bytes, offset, size) \
|
||||
ServerStaged().Adopt(&(res), (width), (bytes), (offset), (size))
|
||||
#define MGL_SERVER_STAGED_DROP(res) ServerStaged().Drop(&(res))
|
||||
@@ -1394,14 +1389,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
ServerStaged().RequireCoverage(&(res), (base), (start), (end), (site))
|
||||
#define MGL_SERVER_STAGED_REQUIRE_PENDING(res, base, frontendSize, site) \
|
||||
RequireStagedCoverageForPendingRanges((res), (base), (frontendSize), (site))
|
||||
#else
|
||||
#define MGL_SERVER_STAGED_ADOPT(res, width, bytes, offset, size) \
|
||||
(static_cast<const Uint8*>(bytes) - (offset))
|
||||
#define MGL_SERVER_STAGED_DROP(res) ((void)0)
|
||||
#define MGL_SERVER_STAGED_DROP_ALL() ((void)0)
|
||||
#define MGL_SERVER_STAGED_REQUIRE(res, base, start, end, site) ((void)0)
|
||||
#define MGL_SERVER_STAGED_REQUIRE_PENDING(res, base, frontendSize, site) ((void)0)
|
||||
#endif
|
||||
|
||||
// The host bytes a range upload/flush reads. On the legacy arm the frontend object's
|
||||
// shadow; on the handle arm the base the last content-carrying call handed over.
|
||||
@@ -1410,9 +1397,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
|
||||
#endif
|
||||
if (start >= end || hostBase == nullptr) return;
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
MGL_SERVER_STAGED_REQUIRE(resource, hostBase, start, end, "upload_range");
|
||||
#endif
|
||||
BindBufferId(TempBufferTarget, resource.id);
|
||||
g_GLESFuncs.glBufferSubData(TempBufferTarget, (GLintptr)start, (GLsizeiptr)(end - start),
|
||||
hostBase + start);
|
||||
@@ -1987,9 +1972,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
auto* glesResource = static_cast<GLESBufferResource*>(resource.get());
|
||||
if (glesResource->contextGeneration != g_bufferContextGeneration) {
|
||||
glesResource->id = 0; // id belonged to a destroyed context
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
RetireT0Import(*glesResource); // P11 B2: its GL store died with that context
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
if (CanTouchGLNow()) {
|
||||
@@ -2005,10 +1988,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
g_GLESFuncs.glDeleteBuffers(1, &glesResource->id);
|
||||
glesResource->id = 0;
|
||||
}
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P11 B2: a T0 store's AHardwareBuffer goes only after a fence behind the delete.
|
||||
RetireT0Import(*glesResource);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
const std::lock_guard<std::mutex> lock(g_deferredBufferReleasesMutex);
|
||||
@@ -2112,7 +2093,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
void Ops_H_Respecify(MG_Pipe::MGPipeHandle res, const MG_Pipe::MGPResourceDesc& desc,
|
||||
const void* initialBytes) {
|
||||
auto* resource = FindBufferResourceForHandle(res);
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P13 W4a (§1.4): ON MONOLITH'S RECORD ARM THE TWIN IS MINTED HERE, not lazily at
|
||||
// the first draw. The record arm's readers have no frontend object to re-read a
|
||||
// base from, so the base this call carries - the client's shadow, for an orphaning
|
||||
@@ -2122,9 +2102,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
if (resource == nullptr && aliasesFrontend && initialBytes != nullptr) {
|
||||
resource = GetOrCreateBufferResourceForHandle(res);
|
||||
}
|
||||
#else
|
||||
constexpr Bool aliasesFrontend = false;
|
||||
#endif
|
||||
if (resource != nullptr) {
|
||||
// A respecify's companion pointer IS the shadow base (offset 0) - and an
|
||||
// ORPHANING one (glBufferData with NULL) carries HasDefinedContent clear and
|
||||
@@ -2155,11 +2132,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
MGL_SERVER_STAGED_DROP(*resource);
|
||||
resource->hostBytes = nullptr;
|
||||
}
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P8-C: whatever a shader wrote belonged to the store this call replaces.
|
||||
resource->serverGpuWritten = false;
|
||||
resource->contentSinceRespecify = false;
|
||||
#endif
|
||||
}
|
||||
if (!resource) return; // lazy: the ensure path full-uploads on creation
|
||||
if (resource->immutableStorage) {
|
||||
@@ -2178,13 +2153,11 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
resource->id = 0;
|
||||
resource->immutableStorage = false;
|
||||
}
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P11 B2: a respecified T0 store ends here on the server's side too. Its
|
||||
// AHardwareBuffer is released only after a fence behind the delete (or at once
|
||||
// when the store's context is already gone) - never under a frame in flight,
|
||||
// however the client's own release (at this respecify) raced it.
|
||||
RetireT0Import(*resource);
|
||||
#endif
|
||||
resource->storageInitialized = false;
|
||||
resource->storageSize = 0;
|
||||
resource->pendingRespecify = true;
|
||||
@@ -2219,7 +2192,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
const SizeT offset = static_cast<SizeT>(MG_Pipe::MGPipeSubDataBufferOffset(record));
|
||||
const SizeT size = static_cast<SizeT>(MG_Pipe::MGPipeSubDataBufferSize(record));
|
||||
auto* resource = FindBufferResourceForHandle(res);
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// R-11 / ID-52 item 3: under an ACTIVE TRANSPORT this record's bytes are the ONLY
|
||||
// delivery of the buffer's content - the client sends no companion pointer and the
|
||||
// server has no MappedData to fall back on. But the twin is created LAZILY at the
|
||||
@@ -2233,9 +2205,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
MG_Config::DataArmIsRecord()) {
|
||||
resource = GetOrCreateBufferResourceForHandle(res);
|
||||
}
|
||||
#endif
|
||||
if (!resource) return;
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P11 B2: A T0 STORE TAKES A SERVER-ORIGINATED WRITE GPU-ORDERED. Its pages are the
|
||||
// client's AHardwareBuffer, so there is no staged copy to refresh and the adopted
|
||||
// early return below ("the coherent map already has the bytes") is false for bytes
|
||||
@@ -2294,7 +2264,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
if (resource != nullptr) resource->serverGpuWritten = true;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
// M-2: UNDER pendingMutex, because this line runs BEFORE the CanTouchGLNow()
|
||||
// test below - i.e. on the arm D-A2 deliberately keeps reachable off the render
|
||||
// thread - while every reader of hostBytes (Ops_H_Readback's drain, the
|
||||
@@ -2317,9 +2286,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// ("these bytes, at this base") and the drain takes this mutex to lift them.
|
||||
resource->hostBytes =
|
||||
MGL_SERVER_STAGED_ADOPT(*resource, ResourceWidthOf(res), bytes, offset, size);
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (size != 0) resource->contentSinceRespecify = true;
|
||||
#endif
|
||||
}
|
||||
if (resource->pendingRespecify) return; // full re-upload pending anyway
|
||||
if (!CanTouchGLNow() || resource->id == 0 ||
|
||||
@@ -2380,9 +2347,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
const std::lock_guard<std::mutex> lock(resource->pendingMutex);
|
||||
resource->hostBytes = MGL_SERVER_STAGED_ADOPT(*resource, ResourceWidthOf(res), bytes,
|
||||
start, end - start);
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (end > start) resource->contentSinceRespecify = true;
|
||||
#endif
|
||||
}
|
||||
if (resource->pendingRespecify) return;
|
||||
if (!CanTouchGLNow() || resource->id == 0 ||
|
||||
@@ -2413,14 +2378,12 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
#ifdef TRACY_ENABLE
|
||||
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
|
||||
#endif
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// The kill-switch arm's own Memcpy, and the one that passes
|
||||
// GL_MAP_INVALIDATE_RANGE_BIT - i.e. the one that tells the driver the old
|
||||
// bytes are dead. Bytes outside the staged coverage are exactly the ones
|
||||
// that claim is false for.
|
||||
MGL_SERVER_STAGED_REQUIRE(*resource, resource->hostBytes, start, end,
|
||||
"flush_range_invalidate_map");
|
||||
#endif
|
||||
BindBufferId(TempBufferTarget, resource->id);
|
||||
void* mappedData = g_GLESFuncs.glMapBufferRange(
|
||||
TempBufferTarget, (GLintptr)start, (GLsizeiptr)(end - start),
|
||||
@@ -2440,15 +2403,11 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
}
|
||||
|
||||
void Ops_H_Readback(MG_Pipe::MGPipeHandle res, const MG_Pipe::MGPReadback& record) {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// An empty capture may conservatively request a writeback before
|
||||
// any draw materialized this buffer. Resolve its staged resource
|
||||
// exactly as a GPU consumer would, then return the real bytes.
|
||||
auto* resource = MG_Config::DataArmIsRecord()
|
||||
? EnsureBufferResourceForHandle(nullptr, res) : FindBufferResourceForHandle(res);
|
||||
#else
|
||||
auto* resource = FindBufferResourceForHandle(res);
|
||||
#endif
|
||||
if (!resource || resource->id == 0 || !resource->storageInitialized) return;
|
||||
if (!CanTouchGLNow() || resource->contextGeneration != g_bufferContextGeneration) return;
|
||||
if (resource->persistentMapped) {
|
||||
@@ -2517,7 +2476,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// The shadow now matches the backend byte for byte; without this the next draw
|
||||
// would see a newer serial and re-upload the readback over it.
|
||||
resource->syncedChangeSerial = ResourceSerialOf(res);
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P13 W4a: on monolith's record arm the base this twin reads IS the shadow the
|
||||
// writeback just filled, so a whole-store readback is also the refresh
|
||||
// SplitHostBytesForCpuRead would owe. (A copying store's copy is not the
|
||||
@@ -2525,7 +2483,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
if (MG_Config::RecordArmAliasesFrontend() && readOffset == 0 && size >= ResourceWidthOf(res)) {
|
||||
resource->serverGpuWritten = false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Ops_H_Destroy(MG_Pipe::MGPipeHandle res) {
|
||||
@@ -3052,7 +3009,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
}
|
||||
#endif // MOBILEGL_BUILD_DISAGGREGATED && __ANDROID__
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// ---- P5c (tx): the TEXTURE half of the resource family, staged server-side --------
|
||||
//
|
||||
// The buffer half's R-11 pattern (ServerStaged() above), for texture levels: the
|
||||
@@ -3110,7 +3066,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// disaggregation did not compile. The narrower fix (guarding the macro block too) is wrong:
|
||||
// that block also carries TextureDiagName and MGB_TEXTURE_RECORD_ARM_SELECTED, which the push
|
||||
// flavour does need. Caught by the `flavours` gate step; see ID-124.
|
||||
#endif // MOBILEGL_BUILD_RECORD_ARM
|
||||
// The inverse of MG_Pipe::MGPipeResourceTargetForTextureTarget, for the sync's
|
||||
// target reads (ConvertTextureTargetToBackendGLEnum and MapToBackendTextureTarget
|
||||
// both want the frontend enum).
|
||||
@@ -3130,7 +3085,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
default: return TextureTarget::Unknown;
|
||||
}
|
||||
}
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
|
||||
void Ops_H_TextureSubData(MG_Pipe::MGPipeHandle res, const MG_Pipe::MGPSubData& record,
|
||||
const void* bytes, const MG_Pipe::MGPSubRegion* regions) {
|
||||
@@ -3232,7 +3186,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
MG_Record::ServerStagedTexture().Drop(
|
||||
MG_Record::StagedTextureStore::KeyForHandle(res));
|
||||
}
|
||||
#endif // MOBILEGL_BUILD_RECORD_ARM
|
||||
|
||||
const MG_Pipe::MGPipeResourceOps g_glesResourceOps = {
|
||||
.Create = Ops_H_Create,
|
||||
@@ -3244,7 +3197,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
.Destroy = Ops_H_DestroyTracked,
|
||||
.MapPersistent = Ops_H_MapPersistentTracked,
|
||||
.UnmapPersistent = Ops_H_UnmapPersistent,
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
.TextureSubData = Ops_H_TextureSubData,
|
||||
.TextureRespecify = Ops_H_TextureRespecify,
|
||||
.TextureDestroy = Ops_H_TextureDestroy,
|
||||
@@ -3252,7 +3204,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// P11 B2 (T0). Android only: nowhere else is there an AHardwareBuffer to import.
|
||||
.ImportExternal = Ops_H_ImportExternalTracked,
|
||||
.SelfTestExternal = Ops_H_SelfTestExternal,
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -3512,7 +3463,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
return twin ? twin->get() : nullptr;
|
||||
}
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
void RequireStagedCoverage(GLESBufferResource& resource, const Uint8* hostBase, SizeT start,
|
||||
SizeT end, const char* site) {
|
||||
ServerStaged().RequireCoverage(&resource, hostBase, start, end, site);
|
||||
@@ -3678,7 +3628,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
const std::lock_guard<std::mutex> lock(resource.pendingMutex);
|
||||
resource.hostBytes = MGL_SERVER_STAGED_ADOPT(resource, ResourceWidthOf(res), bytes, offset, size);
|
||||
}
|
||||
#endif
|
||||
|
||||
MG_Pipe::MGPipeHandle HandleOfBuffer(const MG_State::GLState::BufferObject* bufferObject) {
|
||||
if (bufferObject == nullptr) return MG_Pipe::kMGPipeNullHandle;
|
||||
@@ -3690,7 +3639,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// allocator probe on a miss - and remembers the answer, which the minting overload
|
||||
// was previously the only writer of. A null answer is deliberately never memoised
|
||||
// (see the comment at HandleOf).
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P5c merge coordination (CONTRACT-P5C §3.1's named exemption): every caller of
|
||||
// this function is a site whose handle-carrying records (set_shader_buffers /
|
||||
// set_stream_output_targets) the client does not emit until sb, so the probe
|
||||
@@ -3706,7 +3654,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// IsBufferDrawCleanByHandle / MarkBufferGpuWrittenByHandle, which is the last
|
||||
// caller set this function has.
|
||||
const MG_Pipe::MGPipeFrontendKeyedRegistryScope frontendKeyedRegistry;
|
||||
#endif
|
||||
return g_backendBufferResources.HandleOf(bufferObject);
|
||||
}
|
||||
|
||||
@@ -3816,7 +3763,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// generation, and a shadow that outlived its twin would be looked up by a RECYCLED
|
||||
// address on the next allocation - which is the quietest possible wrong answer.
|
||||
MGL_SERVER_STAGED_DROP_ALL();
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// tx's texture shadows die for the same reason, keyed by handle rather than address
|
||||
// but with the same recycled-identity failure mode: a new context's allocator may
|
||||
// hand out a {slot, gen} the old one's store still answers for.
|
||||
@@ -3827,7 +3773,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
ProcessDeferredT0Retires();
|
||||
SweepT0Retired(/*contextGone=*/true);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -3929,11 +3874,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
}
|
||||
const auto* record = ResourceRecordOf(res);
|
||||
if (record == nullptr) return false;
|
||||
#if MOBILEGL_PIPE_VERIFY && !MOBILEGL_BUILD_RECORD_ARM
|
||||
MOBILEGL_ASSERT(!record->HasLiveHostWrites,
|
||||
"MGPipeResourceRecord::HasLiveHostWrites is set, but this build has no producer "
|
||||
"for it - P5 b1's producer is MGPSubData::HasLiveHostWrites and is split-only");
|
||||
#endif
|
||||
if (record->HasLiveHostWrites) return false;
|
||||
// THE LAST FRONTEND READ IN THIS FUNCTION, AND P5 b1 RETIRES IT - under split
|
||||
// only, because that is the only build where it is both wrong and replaceable.
|
||||
@@ -4012,7 +3952,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// fallback for the drains that have NO object (the readback flush), and it is
|
||||
// nulled at both of those events.
|
||||
const auto liveHostBase = [&resource, &bufferObject]() -> const Uint8* {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// M-2 / codex 3 / ID-52 item 3: under an ACTIVE TRANSPORT the server's staged
|
||||
// copy (resource->hostBytes, filled by MGL_SERVER_STAGED_ADOPT in Ops_H_SubData /
|
||||
// Ops_H_FlushRange) is the ONLY authoritative base. Preferring the frontend
|
||||
@@ -4024,7 +3963,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
if (MG_Config::DataArmIsRecord()) {
|
||||
return resource->hostBytes;
|
||||
}
|
||||
#endif
|
||||
if (bufferObject) return bufferObject->MappedData();
|
||||
return resource->hostBytes;
|
||||
};
|
||||
@@ -4041,7 +3979,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
resource->persistentMapped = false;
|
||||
resource->persistentPtr = nullptr;
|
||||
resource->immutableStorage = false;
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// m-5 / codex 5: OnBackendContextDestroyed ran MGL_SERVER_STAGED_DROP_ALL(), which
|
||||
// frees every server shadow but does NOT null the hostBytes that name them - so a
|
||||
// twin that SURVIVES a context loss still carries a base into the freed allocation.
|
||||
@@ -4084,7 +4021,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
RetireT0Import(*resource);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -4129,12 +4065,10 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// application ORPHANED the gap is its own undefined content and the upload is
|
||||
// legal (ResourceContentIsDeclared, above). RequireCoverage is a no-op for the
|
||||
// legacy arm's MappedData() and for a non-copying store.
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (ResourceContentIsDeclared(res)) {
|
||||
MGL_SERVER_STAGED_REQUIRE(*resource, liveHostBase(), 0, poolSize,
|
||||
"pool_reuse_whole_store");
|
||||
}
|
||||
#endif
|
||||
g_GLESFuncs.glBufferSubData(TempBufferTarget, 0, (GLsizeiptr)poolSize, liveHostBase());
|
||||
if (MG_Util::PipeStats::Enabled()) {
|
||||
MG_Util::PipeStats::AddBytes(MG_Util::PipeStats::ByteClass::StageBuffer,
|
||||
@@ -4161,7 +4095,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
|
||||
// D-N keeps this call here for P3a. It can queue ranges and move the record, so
|
||||
// everything below is read AFTER it.
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P5c (hd, CONTRACT-P5C §3.8): under an active transport the frontend accessor is a
|
||||
// layer-1 surface ("buffer-legacy-arm") and the client's own pre-verb
|
||||
// persistent-map push is the only producer; the call is skipped outright. Under
|
||||
@@ -4169,9 +4102,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
if (bufferObject && !MG_Config::DataArmIsRecord()) {
|
||||
bufferObject->SyncPersistentMappedRange();
|
||||
}
|
||||
#else
|
||||
if (bufferObject) bufferObject->SyncPersistentMappedRange();
|
||||
#endif
|
||||
|
||||
const auto* record = ResourceRecordOf(res);
|
||||
const SizeT size = record != nullptr ? static_cast<SizeT>(record->Desc.Width) : 0;
|
||||
@@ -4214,13 +4144,11 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// the last orphan". With no coverage and a clear bit the store is undefined by the
|
||||
// application's own declaration and the upload stays a pure NULL reallocation.
|
||||
const Bool shadowHasContent =
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
MG_Config::DataArmIsRecord()
|
||||
? (record->Desc.HasDefinedContent != 0 ||
|
||||
(ServerStaged().CopiesIntoServerStorage() ? ServerStaged().CoveredRunCount(resource) != 0
|
||||
: resource->contentSinceRespecify))
|
||||
:
|
||||
#endif
|
||||
(bufferObject ? bufferObject->HasDefinedContent() : (record->Desc.HasDefinedContent != 0));
|
||||
const void* initialData = shadowHasContent ? hostBase : nullptr;
|
||||
// M-3 / codex 4: a RespecifyStorageWith(..., initialData != nullptr) is a WHOLE-STORE
|
||||
@@ -4235,14 +4163,10 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// the bytes it did not write are undefined by its own glBufferData(NULL) - the rule at
|
||||
// ResourceContentIsDeclared. Round 2 refused that idiom and aborted six joint entries.
|
||||
const auto requireWholeStoreCoverage = [&]() {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (initialData != nullptr && record->Desc.HasDefinedContent != 0) {
|
||||
MGL_SERVER_STAGED_REQUIRE(*resource, static_cast<const Uint8*>(initialData), 0,
|
||||
size, "respecify_whole_store");
|
||||
}
|
||||
#else
|
||||
(void)initialData;
|
||||
#endif
|
||||
};
|
||||
|
||||
if (resource->pendingRespecify || !resource->storageInitialized || resource->storageSize != size) {
|
||||
@@ -4942,7 +4866,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
return true;
|
||||
}
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// THE SAFETY ARGUMENT P4a MADE AND NEVER RAN (P3b/P4b espryt D1 slice 5).
|
||||
//
|
||||
// "Running the legacy arm" is a real answer in a monolith and a POSTPONED CRASH under a
|
||||
@@ -4974,7 +4897,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
bit, detail);
|
||||
BufferImpl::StopOnArmlessPipeSubsystem(what);
|
||||
}
|
||||
#endif
|
||||
|
||||
// The release-build VOICE for StateBackendObjectRegistry::GetOrCreateByHandle's two
|
||||
// silent refusals, shared by the kinds P4a re-keys so the wording cannot drift between
|
||||
@@ -5096,7 +5018,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
"MOBILEGL_PIPE_PUSH leaves kMGPipeSubsystemFramebuffer (bit 9) clear (or refuses "
|
||||
"it) and MOBILEGL_PIPE_LEGACY_MEMOS=0 disables the pre-handle g_fboSynced* arm");
|
||||
}
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (MG_Config::DataArmIsRecord() &&
|
||||
verdict != BufferImpl::PipeSubsystemArmVerdict::Handles) {
|
||||
StopOnTransportWithoutRecordArm(
|
||||
@@ -5107,7 +5028,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
"to reach it would raise Fatal{UnmigratedPipeInput, "
|
||||
"\"GetFramebufferBindingSlot@<verb>\"} mid-frame instead");
|
||||
}
|
||||
#endif
|
||||
const Bool enabled = verdict == BufferImpl::PipeSubsystemArmVerdict::Handles;
|
||||
MGLOG_D("MGPipe: Espryt framebuffer family runs the %s arm", enabled ? "handle" : "legacy");
|
||||
return enabled;
|
||||
@@ -5141,7 +5061,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
"refuses it) and MOBILEGL_PIPE_LEGACY_MEMOS=0 disables the pre-handle texture "
|
||||
"cheap-gate trio");
|
||||
}
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (MG_Config::DataArmIsRecord() &&
|
||||
verdict != BufferImpl::PipeSubsystemArmVerdict::Handles) {
|
||||
StopOnTransportWithoutRecordArm(
|
||||
@@ -5150,7 +5069,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
"objects and their binding slots, which under a transport are client memory (rule "
|
||||
"E) rather than anything this side owns");
|
||||
}
|
||||
#endif
|
||||
const Bool enabled = verdict == BufferImpl::PipeSubsystemArmVerdict::Handles;
|
||||
MGLOG_D("MGPipe: Espryt texture-resource family runs the %s arm", enabled ? "handle" : "legacy");
|
||||
return enabled;
|
||||
@@ -5177,7 +5095,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
"it) and MOBILEGL_PIPE_LEGACY_MEMOS=0 disables UnitSamplerLookupMemo's WeakPtr "
|
||||
"arm and SamplerPassMemo's raw-pointer rows");
|
||||
}
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (MG_Config::DataArmIsRecord() &&
|
||||
verdict != BufferImpl::PipeSubsystemArmVerdict::Handles) {
|
||||
StopOnTransportWithoutRecordArm(
|
||||
@@ -5187,7 +5104,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
"this side to key on - a WeakPtr that can never lock is not a slower arm, it is a "
|
||||
"unit that samples through whatever it last held");
|
||||
}
|
||||
#endif
|
||||
const Bool enabled = verdict == BufferImpl::PipeSubsystemArmVerdict::Handles;
|
||||
MGLOG_D("MGPipe: Espryt sampler family runs the %s arm", enabled ? "handle" : "legacy");
|
||||
return enabled;
|
||||
@@ -5206,7 +5122,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
"MOBILEGL_PIPE_PUSH leaves kMGPipeSubsystemPrograms (bit 12) clear and "
|
||||
"MOBILEGL_PIPE_LEGACY_MEMOS=0 disables g_programTwinLookupMemo");
|
||||
}
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (MG_Config::DataArmIsRecord() &&
|
||||
verdict != BufferImpl::PipeSubsystemArmVerdict::Handles) {
|
||||
StopOnTransportWithoutRecordArm(
|
||||
@@ -5216,7 +5131,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
"ShaderCso handle with its archive beside the record and there is no frontend "
|
||||
"program here at all");
|
||||
}
|
||||
#endif
|
||||
const Bool enabled = verdict == BufferImpl::PipeSubsystemArmVerdict::Handles;
|
||||
MGLOG_D("MGPipe: Espryt program family runs the %s arm", enabled ? "handle" : "legacy");
|
||||
return enabled;
|
||||
@@ -5487,11 +5401,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// MGPipeSlots().FindByLifetimeId, which is a hash lookup. HandleOf answers
|
||||
// identically - the same allocator probe on a miss - and remembers the answer; a
|
||||
// null answer is deliberately never memoised (SlotTables.h, at HandleOf).
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P5c (G6, CONTRACT-P5C §5.4): frontend-keyed twin resolution, named debt inside
|
||||
// the scope - P3b/P4b rekeys the sampler-view registry onto handles.
|
||||
const MG_Pipe::MGPipeFrontendKeyedRegistryScope frontendKeyedRegistry;
|
||||
#endif
|
||||
return g_backendSamplerViews.HandleOf(textureObject);
|
||||
}
|
||||
} // namespace SamplerViewImpl
|
||||
@@ -6366,15 +6278,11 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// recorded shadow base for such a resource read freed memory; hostBytes is nulled
|
||||
// at adoption for the same reason, so the fallback below is a null and a refusal
|
||||
// rather than a stale read.
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
const Bool sourceGpuWritten =
|
||||
MG_Config::DataArmIsRecord() && resource->serverGpuWritten;
|
||||
if (sourceGpuWritten) {
|
||||
BufferImpl::SplitHostBytesForCpuRead(*resource, binding.Res, "fp64_vertex_narrowing");
|
||||
}
|
||||
#else
|
||||
constexpr Bool sourceGpuWritten = false;
|
||||
#endif
|
||||
const Uint8* const sourceBase =
|
||||
(resource->persistentMapped && resource->persistentPtr != nullptr)
|
||||
? static_cast<const Uint8*>(resource->persistentPtr)
|
||||
@@ -6553,7 +6461,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
if (m_imageBindableStorageRequired) {
|
||||
return;
|
||||
}
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P13 W4b (defect h): monolith's record arm takes the body too; its old arm only
|
||||
// cleared m_isInitialized and replayed the frontend shadow at the next sync.
|
||||
if (MG_Config::DataArmIsRecord()) {
|
||||
@@ -6704,10 +6611,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
MG_Util::PipeStats::AddCalls(MG_Util::PipeStats::CallClass::TextureRemintPulls, 1);
|
||||
}
|
||||
}
|
||||
#else
|
||||
(void)res;
|
||||
(void)record;
|
||||
#endif
|
||||
// Every defined byte now belongs to the server and every required
|
||||
// replay is in its pending set. The existing upload path builds and
|
||||
// fills the immutable/widened carrier from those records.
|
||||
@@ -6948,11 +6851,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
const MG_Pipe::MGPipeHandle rearmRes =
|
||||
TextureResourceSubsystemEnabled()
|
||||
? [&]() {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P5c (G6, CONTRACT-P5C §5.4): frontend-keyed resolution, named
|
||||
// debt inside the scope - P3b/P4b rekeys the registry.
|
||||
const MG_Pipe::MGPipeFrontendKeyedRegistryScope frontendKeyedRegistry;
|
||||
#endif
|
||||
return g_backendTextureObjects.HandleOf(stateTextureObject.get());
|
||||
}()
|
||||
: MG_Pipe::kMGPipeNullHandle;
|
||||
@@ -7171,11 +7072,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
static inline GLint s_skipImages = 0;
|
||||
};
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
void ExerciseDefaultUnpackScopeForTesting() {
|
||||
const ScopedDefaultUnpackState scope;
|
||||
}
|
||||
#endif
|
||||
|
||||
// --- Unpack-ring staging (see BufferImpl::UnpackRingAvailable) ---------------
|
||||
// One rectangular (or whole-level) region of a level shadow, repacked TIGHTLY
|
||||
@@ -7859,12 +7758,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// read). Sites that were previously correct only because every writer of a handle-note happens
|
||||
// to be transport-gated now say so themselves, so a future by-handle noter cannot quietly
|
||||
// re-open ID-107's hole somewhere else.
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
#define MGB_TEXTURE_RECORD_ARM_SELECTED() \
|
||||
(MG_Config::DataArmIsRecord() && TextureResourceSubsystemEnabled())
|
||||
#else
|
||||
#define MGB_TEXTURE_RECORD_ARM_SELECTED() (false)
|
||||
#endif
|
||||
|
||||
// Returns true when the caller must decline (no handle noted: the caller's bug it always
|
||||
// was, named by the caller's own log line), false when the record arm can serve the call,
|
||||
@@ -8017,7 +7912,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
StampViewSyncKeys(stateTextureObject);
|
||||
}
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P5e (tx2): see the declaration for the split between what the record answers and what
|
||||
// the wire still cannot say about a view.
|
||||
void BackendTextureObject::SyncTextureViewToBackendByRecord(
|
||||
@@ -8102,7 +7996,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// The storage's, for the steady arm's reason above (P3b/P4b D3).
|
||||
m_syncedResourceSerial = storageSerial;
|
||||
}
|
||||
#endif
|
||||
|
||||
// P4a (D-D5), the dirty-ownership inversion, applied at the two places the upload loops
|
||||
// below actually ask: "does this (uploadTarget, level) owe an upload" and "it has been
|
||||
@@ -8126,7 +8019,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// pre-P4a expression exactly when MOBILEGL_PIPE_PUSH is off, so the pull build's
|
||||
// preprocessed text, and therefore its object code, is unchanged. Both are #undef'd
|
||||
// immediately after the function.
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P5c (tx): THE READS THE FOUR UPLOAD ARMS MAKE, re-sourced. With an active transport the
|
||||
// apply thread may not name the client's TextureObjectMipmap at all (rule E), so on the
|
||||
// handle arm:
|
||||
@@ -8172,15 +8064,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
MG_Record::StagedTextureStore::KeyForHandle(pushedRes), static_cast<Uint16>(tgt), \
|
||||
static_cast<Uint16>(lvl), site) \
|
||||
: (obj)->MapMipmapData(tgt, lvl))
|
||||
#else
|
||||
#define MGB_TEXTURE_TARGET(obj) ((obj)->GetTarget())
|
||||
#define MGB_UPLOAD_TARGETS(obj) ((obj)->GetUploadTargets())
|
||||
#define MGB_LEVEL_TEXEL_SIZE(obj, tgt, lvl) ((obj)->GetMipmapTexelSize(tgt, lvl))
|
||||
#define MGB_LEVEL_BYTE_SIZE(obj, tgt, lvl) ((obj)->GetMipmapByteSize(tgt, lvl))
|
||||
#define MGB_LEVEL_TEXELS(obj, tgt, lvl, site) ((obj)->MapMipmapData(tgt, lvl))
|
||||
#endif
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P5c (tx): the disaggregated pair adds ONE term and ONE clear to the P4a shapes - the
|
||||
// staged-texture store's GPU-dirty mark (T5: a level the GPU generated dirties the SERVER's
|
||||
// shadow, and the pending set cannot see it). The mark is never set on Espryt today - its
|
||||
@@ -8210,23 +8094,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
(obj)->MarkStorageDirty(tgt, lvl, false); \
|
||||
} \
|
||||
} while (0)
|
||||
#else
|
||||
#define MGB_LEVEL_NEEDS_UPLOAD(obj, tgt, lvl) \
|
||||
(pushedStorage != nullptr \
|
||||
? FindPipeTextureUpload(*pushedStorage, static_cast<Uint16>(tgt), static_cast<Uint16>(lvl)) != nullptr \
|
||||
: (obj)->IsStorageDirty(tgt, lvl))
|
||||
// Re-resolves the record itself, so it is safe after any amount of driver work - and it
|
||||
// invalidates any PendingUpload* taken earlier for THIS texture, which is why every such pointer
|
||||
// is used and dropped inside one level's iteration.
|
||||
#define MGB_LEVEL_UPLOAD_DONE(obj, tgt, lvl) \
|
||||
do { \
|
||||
if (pushedStorage != nullptr) { \
|
||||
ConsumePipeTextureUpload(pushedRes, static_cast<Uint16>(tgt), static_cast<Uint16>(lvl)); \
|
||||
} else { \
|
||||
(obj)->MarkStorageDirty(tgt, lvl, false); \
|
||||
} \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
// THE STORAGE SHAPE, and on the handle arm it is the DESCRIPTOR's (C.3's d2: "the
|
||||
// m_prevTextureInfo probe re-keyed onto the resource record's Serial AND Desc"; review
|
||||
@@ -8480,7 +8347,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
return;
|
||||
}
|
||||
}
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
// SHARED IMAGES: ahead of the view test and of the staged-store and serial gates below,
|
||||
// none of which knows the texels belong to an image - the staged store holds none of
|
||||
@@ -8515,7 +8381,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
if (MG_Config::DataArmIsRecord() && pushedStorage != nullptr) {
|
||||
// Not a view: fall through to the storage body with the frontend test skipped.
|
||||
} else
|
||||
#endif
|
||||
// A texture created by glTextureView owns no storage: the levels, the format and
|
||||
// every texel belong to the texture it views, and this name only has to be made to
|
||||
// ALIAS them. Everything below - storage allocation, respecification, per-level
|
||||
@@ -8563,14 +8428,12 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
pushedStorage->Desc.ImageBindableHint != 0 ||
|
||||
(pushedStorage->Desc.BindMask & MG_Pipe::kMGPipeBindShaderImage) != 0;
|
||||
if (pushedWantsImageBindableStorage && !m_imageBindableStorageRequired) {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P5e (tx2), ruling 1's arm: under a transport the re-dirty half is a NAMED
|
||||
// refusal at its entry (§5.2) instead of a walk of the client's level shadows;
|
||||
// the push-monolith build keeps the frontend transition exactly as it is.
|
||||
if (MG_Config::DataArmIsRecord()) {
|
||||
RequireImageBindableStorageByHandle(pushedRes, *pushedStorage);
|
||||
} else
|
||||
#endif
|
||||
RequireImageBindableStorage(stateTextureObject);
|
||||
}
|
||||
|
||||
@@ -8638,7 +8501,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// left the backend name with no levels whatsoever, so the level that WAS defined could
|
||||
// never be sampled or read back. Sync whenever some level holds an image; the per-level
|
||||
// loops below skip the degenerate ones individually.
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P5c (gt, CONTRACT-P5C §6 layer 1): on the staged arm the same question is answered
|
||||
// out of the SERVER's staged-texture store, never out of the frontend object - the
|
||||
// store's Defined-ness (fed by the respecify hook and the sub-data adoption) IS "some
|
||||
@@ -8679,7 +8541,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
return;
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
if (!stateTextureObject->IsComplete() && !HasAnyDefinedMipmapLevel(stateTextureObject.get())) {
|
||||
MGLOG_D("Texture object with ID: %u has no defined image level, skipping sync.",
|
||||
stateTextureObject->GetExternalIndex());
|
||||
@@ -9263,7 +9124,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
: nullptr;
|
||||
const auto dirtyRegion = [&]() -> MG_State::GLState::MipmapDirtyRegion {
|
||||
if (pendingUpload == nullptr) {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P5c (tx): a level this arm owes with NO pending upload behind it
|
||||
// was dirtied by the GPU (T5), and the dirty answer is the
|
||||
// server's own mark on the staged shadow - the whole level,
|
||||
@@ -9276,7 +9136,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
static_cast<Uint16>(uploadTarget), static_cast<Uint16>(level));
|
||||
return MG_State::GLState::MipmapDirtyRegion{IntVec3{0, 0, 0}, gpuExtent};
|
||||
}
|
||||
#endif
|
||||
return textureMipmapObject->GetStorageDirtyRegion(uploadTarget, level);
|
||||
}
|
||||
// MGPBox is {origin, extent}; MipmapDirtyRegion is {lo, hi}. The
|
||||
@@ -9469,13 +9328,11 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
region.Z + static_cast<Int32>(region.D)}};
|
||||
}
|
||||
} else
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// tx: with an active transport the rect list is the record's (the
|
||||
// pendingUpload arm above) or nothing - the server's GPU-dirty mark
|
||||
// is whole-level and has no scatter refinement to hand out, and the
|
||||
// frontend's rect model is not this side's to read.
|
||||
if (!MGB_STAGED_TEXTURE_LIVE)
|
||||
#endif
|
||||
dirtyRectCount = textureMipmapObject->GetStorageDirtyRects(
|
||||
uploadTarget, level, dirtyRects,
|
||||
MG_State::GLState::MipmapStorage::kMaxDirtyRects);
|
||||
@@ -9512,14 +9369,12 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
region.Z + static_cast<Int32>(region.D)}});
|
||||
}
|
||||
} else
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// With an active transport and no record behind the level, the
|
||||
// server's own whole-level GPU-dirty mark is the whole story (the
|
||||
// same reason the rect list above is not read here).
|
||||
if (MGB_STAGED_TEXTURE_LIVE) {
|
||||
dirtyFootprint.clear();
|
||||
} else
|
||||
#endif
|
||||
textureMipmapObject->GetStorageDirtyFootprint(uploadTarget, level, dirtyFootprint);
|
||||
// One box is the union box itself, which the branches below already
|
||||
// upload exactly (as the whole level when it is the whole level).
|
||||
@@ -10052,9 +9907,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
#undef MGB_LEVEL_TEXEL_SIZE
|
||||
#undef MGB_LEVEL_BYTE_SIZE
|
||||
#undef MGB_LEVEL_TEXELS
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
#undef MGB_STAGED_TEXTURE_LIVE
|
||||
#endif
|
||||
|
||||
// P5e (tx2), CONTRACT-P5E §5.2: THIS TWIN'S OWN RECORD. On the by-handle arm the handle
|
||||
// is already known - the caller resolved the record before it resolved this twin and
|
||||
@@ -10066,11 +9919,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
MG_Pipe::MGPipeHandle res = m_pushedSyncHandle;
|
||||
if (MG_Pipe::MGPipeHandleIsNull(res)) {
|
||||
if (!stateTextureObject) return nullptr;
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P5c (G6, CONTRACT-P5C §5.4): frontend-keyed resolution, named debt inside the
|
||||
// scope - the by-handle arm above is what retires it.
|
||||
const MG_Pipe::MGPipeFrontendKeyedRegistryScope frontendKeyedRegistry;
|
||||
#endif
|
||||
res = g_backendTextureObjects.HandleOf(stateTextureObject.get());
|
||||
}
|
||||
return PipeTextureRecordForHandle(res);
|
||||
@@ -10687,18 +10538,13 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
storage = record->Desc.ViewOf;
|
||||
}
|
||||
if (auto* twin = ResolveTextureTwin(storage)) twin->NoteDriverSideWrite();
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P8-E: the staged store stops standing for this texture's content (a no-op on a
|
||||
// monolith arm, where the store does not copy).
|
||||
if (!storeFollowsTheWrite && !MG_Pipe::MGPipeHandleIsNull(storage)) {
|
||||
MG_Record::ServerStagedTexture().MarkDriverWritten(
|
||||
MG_Record::StagedTextureStore::KeyForHandle(storage));
|
||||
}
|
||||
#else
|
||||
(void)storeFollowsTheWrite;
|
||||
#endif
|
||||
}
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
Bool FollowCopyImageInStagedStore(MG_Pipe::MGPipeHandle source, Uint32 sourceLevel, const IntVec3& sourceOrigin,
|
||||
MG_Pipe::MGPipeHandle destination, Uint32 destinationLevel,
|
||||
const IntVec3& destinationOrigin, const IntVec3& size) {
|
||||
@@ -10737,7 +10583,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
destinationTarget, static_cast<Uint16>(destinationLevel), destinationOrigin,
|
||||
size);
|
||||
}
|
||||
#endif
|
||||
} // namespace TextureImpl
|
||||
|
||||
namespace FramebufferImpl {
|
||||
@@ -10900,11 +10745,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
if (attachmentObject.IsTexture()) {
|
||||
const auto& textureObject = attachmentObject.GetTexture();
|
||||
SharedPtr<TextureImpl::BackendTextureObject> backendTextureObject;
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P5c (G6, CONTRACT-P5C §5.4): frontend-keyed twin resolution (and the mint on
|
||||
// a first attach), named debt inside the scope - P3b/P4b rekeys the registry.
|
||||
const MG_Pipe::MGPipeFrontendKeyedRegistryScope frontendKeyedRegistry;
|
||||
#endif
|
||||
if (auto* backendTextureSlot = TextureImpl::g_backendTextureObjects.Find(textureObject.get())) {
|
||||
backendTextureObject = *backendTextureSlot;
|
||||
} else {
|
||||
@@ -10957,11 +10800,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
}
|
||||
} else if (attachmentObject.IsRenderbuffer()) {
|
||||
const auto& renderbufferObject = attachmentObject.GetRenderbuffer();
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P5c (G6, CONTRACT-P5C §5.4): frontend-keyed twin resolution (and the mint on
|
||||
// a first attach), named debt inside the scope - P3b/P4b rekeys the registry.
|
||||
const MG_Pipe::MGPipeFrontendKeyedRegistryScope frontendKeyedRegistry;
|
||||
#endif
|
||||
SharedPtr<RenderbufferImpl::BackendRenderbufferObject> backendRenderbufferObject;
|
||||
if (auto* backendRenderbufferSlot =
|
||||
RenderbufferImpl::g_backendRenderbufferObjects.Find(renderbufferObject.get())) {
|
||||
@@ -11295,14 +11136,12 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// push-monolith arm keeps the frontend sync it had at f6cfcbd3, token for token,
|
||||
// because tx2's record arm inside SyncMipmapsToBackend is itself selected by
|
||||
// `Transport != Monolith` and cannot answer here.
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (MG_Config::DataArmIsRecord()) {
|
||||
backendTextureObject->SyncMipmapsToBackendByHandle(surface.Res);
|
||||
// From here on every draw, clear and blit into this framebuffer can write the
|
||||
// texture behind the staged shadow's back (the frontend arm's note, by handle).
|
||||
TextureImpl::NoteDriverSideTextureWriteByHandle(surface.Res);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
const SharedPtr<MG_State::GLState::ITextureObject> monolithTexture =
|
||||
(monolithAttachment != nullptr && monolithAttachment->IsTexture())
|
||||
@@ -11384,11 +11223,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// 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
|
||||
// push-monolith arm there is, so the monolith arm keeps the object form.
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (MG_Config::DataArmIsRecord()) {
|
||||
backendRenderbufferObject->SyncToBackendByHandle(surface.Res);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
const SharedPtr<MG_State::GLState::RenderbufferObject> monolithRenderbuffer =
|
||||
(monolithAttachment != nullptr && monolithAttachment->IsRenderbuffer())
|
||||
@@ -11436,12 +11273,10 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// (the three-channel colour-renderable widening). GL answers such a read with 1.0, but
|
||||
// the storage holds whatever the draw wrote there, so the readback has to overwrite it.
|
||||
Bool IsAlphaWidenedFallbackReadAttachment() {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (MG_Config::DataArmIsRecord()) {
|
||||
const auto* record = MG_Pipe::MGPipeApplier().ReadFramebuffer();
|
||||
return record && IsAlphaWidenedColorSurface(record->ReadSurface);
|
||||
}
|
||||
#endif
|
||||
const auto* attachmentObject = GetReadColorAttachment();
|
||||
if (attachmentObject == nullptr) {
|
||||
return false;
|
||||
@@ -11450,13 +11285,11 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
}
|
||||
|
||||
Bool IsFixedPointFallbackReadAttachment() {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (MG_Config::DataArmIsRecord()) {
|
||||
const auto* record = MG_Pipe::MGPipeApplier().ReadFramebuffer();
|
||||
return record && record->ReadSurface.Kind != MG_Pipe::kMGPipeSurfaceKindNone &&
|
||||
IsSnormFormat(static_cast<TextureInternalFormat>(record->ReadSurface.InternalFormat));
|
||||
}
|
||||
#endif
|
||||
const auto& readFBO =
|
||||
MG_Pipe::gPipeInputs.GetFramebufferBindingSlot(FramebufferTarget::Read).GetBoundObject();
|
||||
if (!readFBO) {
|
||||
@@ -11623,11 +11456,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// P5c (hd): with an active transport the handle is the caller's
|
||||
// (m_pushedSyncHandle), never the client allocator's - see SyncToBackend.
|
||||
const MG_Pipe::MGPipeHandle fbo =
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
MG_Config::DataArmIsRecord()
|
||||
? m_pushedSyncHandle
|
||||
:
|
||||
#endif
|
||||
g_backendFramebufferObjects.HandleOf(stateFBOObject.get());
|
||||
// ID-19: the OBJECT's record, not "the record of whatever is bound to READ". A
|
||||
// framebuffer whose read buffer is being pushed need not be the read binding at
|
||||
@@ -11801,11 +11632,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// is never probed (T2). A null there means a caller reached this arm without a
|
||||
// record, which the null-record refusal below names.
|
||||
const MG_Pipe::MGPipeHandle fbo =
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
MG_Config::DataArmIsRecord()
|
||||
? m_pushedSyncHandle
|
||||
:
|
||||
#endif
|
||||
g_backendFramebufferObjects.HandleOf(stateFBOObject.get());
|
||||
pushedRecord = PushedFramebufferRecord(fbo);
|
||||
if (pushedRecord == nullptr) {
|
||||
@@ -12083,11 +11912,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// Verify that the backend object's name and parameters match the frontend attachment state
|
||||
if (attachmentObject.IsTexture()) {
|
||||
const auto& textureObject = attachmentObject.GetTexture();
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P5c (G6, CONTRACT-P5C §5.4): frontend-keyed twin resolution, named
|
||||
// debt inside the scope - P3b/P4b rekeys the registry.
|
||||
const MG_Pipe::MGPipeFrontendKeyedRegistryScope frontendKeyedRegistry;
|
||||
#endif
|
||||
auto* backendTextureSlot = TextureImpl::g_backendTextureObjects.Find(textureObject.get());
|
||||
MOBILEGL_ASSERT(backendTextureSlot != nullptr && *backendTextureSlot != nullptr,
|
||||
"No backend texture found while framebuffer reports texture attachment.");
|
||||
@@ -12104,11 +11931,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
"Attachment texture level mismatch between GLES and state object.");
|
||||
} else if (attachmentObject.IsRenderbuffer()) {
|
||||
const auto& renderbufferObject = attachmentObject.GetRenderbuffer();
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P5c (G6, CONTRACT-P5C §5.4): frontend-keyed twin resolution, named
|
||||
// debt inside the scope - P3b/P4b rekeys the registry.
|
||||
const MG_Pipe::MGPipeFrontendKeyedRegistryScope frontendKeyedRegistry;
|
||||
#endif
|
||||
auto* backendRboSlot =
|
||||
RenderbufferImpl::g_backendRenderbufferObjects.Find(renderbufferObject.get());
|
||||
MOBILEGL_ASSERT(
|
||||
@@ -12253,11 +12078,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
fbo.Slot, fbo.Gen);
|
||||
return;
|
||||
}
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// Kept in step for the monolith-glue overloads that still resolve their record
|
||||
// through it (SyncReadBufferToBackend's push arm).
|
||||
m_pushedSyncHandle = fbo;
|
||||
#endif
|
||||
const GLenum glFBOTarget = MG_Util::ConvertFramebufferTargetToGLEnum(asTarget);
|
||||
Bind(asTarget);
|
||||
|
||||
@@ -13109,7 +12932,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// outside the frontend's array, which cannot be addressed at all.
|
||||
Uint BoundImageUnitFormat(Int unit) {
|
||||
if (unit < 0 || unit >= MG_State::GLState::TextureState::MAX_TEXTURE_IMAGE_UNITS) return 0;
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P5e (fb, §5.4): the format-less image bake asks what format the APPLICATION
|
||||
// named at this unit, and under a transport the record is the only statement of
|
||||
// it - MGPImageView::InternalFormat is that same GLenum, copied by the client
|
||||
@@ -13120,7 +12942,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
if (static_cast<SizeT>(unit) >= st.BoundShaderImages.size()) return 0;
|
||||
return static_cast<Uint>(st.BoundShaderImages[static_cast<SizeT>(unit)].InternalFormat);
|
||||
}
|
||||
#endif
|
||||
return static_cast<Uint>(MG_Pipe::gPipeInputs.GetImageTextureBinding(unit).Format);
|
||||
}
|
||||
|
||||
@@ -15241,14 +15062,12 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// nine-clause rebuild condition reads on the handle arm; the clause COUNT does not
|
||||
// shrink, its inputs move (D-H5).
|
||||
if (ProgramSubsystemEnabled()) {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P5c (G6, CONTRACT-P5C §5.4): MONOLITH GLUE ONLY. This is the frontend-keyed
|
||||
// lookup the phase exists to retire, and it survives here for the reason
|
||||
// ruling 1 (ID-81) gives: under Transport=monolith the push build keeps its
|
||||
// frontend arms token for token, and this arm is only ever reached from them.
|
||||
// The handle half below names no frontend identity at all.
|
||||
const MG_Pipe::MGPipeFrontendKeyedRegistryScope frontendKeyedRegistry;
|
||||
#endif
|
||||
// The reader hides the composite band, so a program-pipeline composite - which
|
||||
// the server must never learn is one - resolves through the same call.
|
||||
const MG_Pipe::MGPipeHandle cso = g_backendProgramObjects.HandleOf(stateProgramObject.get());
|
||||
@@ -15593,12 +15412,10 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// the values are taken from the object, which in monolith are the very
|
||||
// values the client content-addressed, so the picture stays right while
|
||||
// the gap is visible rather than silent.
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P5c (G6, CONTRACT-P5C §5.4): the registration probe below resolves the
|
||||
// sampler's handle by frontend identity - frontend-keyed twin resolution,
|
||||
// named debt inside the scope - P3b/P4b rekeys the registry onto handles.
|
||||
const MG_Pipe::MGPipeFrontendKeyedRegistryScope frontendKeyedRegistry;
|
||||
#endif
|
||||
if (!MG_Pipe::MGPipeHandleIsNull(
|
||||
g_backendSamplerObjects.HandleOf(stateSamplerObject.get()))) {
|
||||
MGLOG_E_ONCE("MGPipe: sampler %u was synced without its unit's SamplerCso handle, so "
|
||||
@@ -15849,12 +15666,10 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
|
||||
const MG_Pipe::MGPipeResourceRecord* pushedRecord = nullptr;
|
||||
if (TextureResourceSubsystemEnabled()) {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P5c (G6, CONTRACT-P5C §5.4): the renderbuffer's handle is resolved by
|
||||
// frontend identity below - frontend-keyed twin resolution, named debt inside
|
||||
// the scope - P3b/P4b rekeys the registry onto handles.
|
||||
const MG_Pipe::MGPipeFrontendKeyedRegistryScope frontendKeyedRegistry;
|
||||
#endif
|
||||
// MONOLITH GLUE, named as such: the Renderbuffer handle of an object this
|
||||
// backend still arrives holding. Under a real split it rides in the payload.
|
||||
const MG_Pipe::MGPipeHandle res = g_backendRenderbufferObjects.HandleOf(stateRBOObject.get());
|
||||
|
||||
@@ -478,27 +478,19 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
if (!EsprytSlotTablesEnabled()) return nullptr;
|
||||
if (MG_Pipe::MGPipeHandleIsNull(handle)) return nullptr;
|
||||
if (handle.Slot >= SlotTable::kMaxHandleSlot) {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
MG_Pipe::MGPipeRecordFail( // @Ph-declined (ID-P7-1): PH-2 stays Fatal, CONTRACT-P7 §12
|
||||
MG_Pipe::MGFatalFamily::ProtocolCorruption,
|
||||
"MGPipe: Fatal{ProtocolCorruption, \"BackendSlotTable.HandleSlot\"} - "
|
||||
"GetOrCreateByHandle named slot %u, past this table's %u bound",
|
||||
handle.Slot, SlotTable::kMaxHandleSlot);
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
const Uint32 liveGen = m_slotTables.LiveGenAt(handle.Slot);
|
||||
if (liveGen != 0 && liveGen > handle.Gen) {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
MG_Pipe::MGPipeRecordFail( // @Ph-declined (ID-P7-1): PH-2 stays Fatal, CONTRACT-P7 §12
|
||||
MG_Pipe::MGFatalFamily::ProtocolCorruption,
|
||||
"MGPipe: Fatal{ProtocolCorruption, \"BackendSlotTable.Generation\"} - "
|
||||
"GetOrCreateByHandle named generation %u at slot %u, behind live generation %u",
|
||||
handle.Gen, handle.Slot, liveGen);
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
return &m_slotTables.GetOrCreate(handle);
|
||||
}
|
||||
@@ -940,7 +932,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// AcquirePersistentMap or (FLUSH_EXPLICIT) publish only via FlushMappedRange.
|
||||
Uint64 CurrentBufferMutationEpoch();
|
||||
void BumpBufferMutationEpoch();
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P5e (vi), CONTRACT-P5E §5.1's first pin: how many bumps came from a thread other than
|
||||
// the apply thread WHILE an apply thread was running, under a live transport. The
|
||||
// record arm's clean gate stamps this counter and skips its probes while the stamp
|
||||
@@ -985,7 +976,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
Uint64 Serial = 0;
|
||||
};
|
||||
DrawIndexBufferRequest ResolveDrawIndexBufferFromRecord(const MG_Pipe::MGPipeApplierState& st);
|
||||
#endif
|
||||
|
||||
// The DirectGLES storage behind one frontend buffer. Owned (refcounted) by
|
||||
// the frontend BufferObject; immediate BufferBackendOps keep it current, so
|
||||
@@ -1073,7 +1063,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// the legacy arm did; this member exists for the drains that have no object, which
|
||||
// in P3a is the readback flush and the fp64 narrowing.
|
||||
const Uint8* hostBytes = nullptr;
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P11 B2 (T0): the client's AHardwareBuffer this store IS - imported with
|
||||
// glBufferStorageExternalEXT, so `id` is immutable, persistentMapped and
|
||||
// persistentPtr is this server's own coherent map of the client's pages. The
|
||||
@@ -1092,7 +1081,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// record arm) keeps no coverage, so the twin says it - the streaming idiom (orphan, then
|
||||
// glBufferSubData) defines content no descriptor declares.
|
||||
Bool contentSinceRespecify = false;
|
||||
#endif
|
||||
};
|
||||
|
||||
// P3a (D-A4): the SEVENTH Espryt slot table, and the first one keyed by a handle the
|
||||
@@ -1152,7 +1140,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
GLESBufferResource* GetOrCreateBufferResourceForHandle(MG_Pipe::MGPipeHandle res);
|
||||
GLESBufferResource* FindBufferResourceForHandle(MG_Pipe::MGPipeHandle res);
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P5c (hd): the staged-coverage assertion (StagedShadowStore::RequireCoverage) for a
|
||||
// read of the server shadow outside the upload ladders - the indirect command-byte
|
||||
// resolver. A no-op for a base that is not this resource's server shadow.
|
||||
@@ -1201,7 +1188,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// ladder and DirectGLES.cpp's XFB scatter are its two callers and they must not be able
|
||||
// to answer it differently.
|
||||
Bool ResourceContentIsDeclared(MG_Pipe::MGPipeHandle res);
|
||||
#endif
|
||||
|
||||
// MONOLITH GLUE, and named as such: the handle of a resource this backend is looking
|
||||
// at through a frontend object, resolved through the client allocator's lifetime-id
|
||||
@@ -1326,12 +1312,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// conjunction in MultiDraw.cpp would satisfy in letter while giving the family two
|
||||
// selectors that can drift apart. One definition, spelled at every site that reads it.
|
||||
inline Bool VertexInputReadsRecords() {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
return MG_Config::DataArmIsRecord() &&
|
||||
VertexInputSubsystemEnabled();
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Registered as the frontend's BufferBackendOps at backend init and on
|
||||
@@ -1830,10 +1812,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
} // namespace VertexArrayImpl
|
||||
|
||||
namespace TextureImpl {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// Drives the actual unpack save/restore helper without needing a texture upload.
|
||||
void ExerciseDefaultUnpackScopeForTesting();
|
||||
#endif
|
||||
inline Bool IsSupportedTextureTarget(TextureTarget target) {
|
||||
// Every desktop-only target is stored on an ES one; see MapToBackendTextureTarget.
|
||||
(void)target;
|
||||
@@ -1968,7 +1948,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// parameter and sampler halves are unchanged and run on this name as on any other.
|
||||
void SyncTextureViewToBackend(const SharedPtr<MG_State::GLState::ITextureObject>& stateTextureObject);
|
||||
void StampViewSyncKeys(const SharedPtr<MG_State::GLState::ITextureObject>& stateTextureObject);
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P5e (tx2). THE VIEW ARM UNDER A TRANSPORT, and what it can and cannot answer.
|
||||
//
|
||||
// `Desc.ViewOf` names the storage owner BY HANDLE, so the STEADY question - "is my ES
|
||||
@@ -1983,7 +1962,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
void SyncTextureViewToBackendByRecord(
|
||||
MG_Pipe::MGPipeHandle res, const MG_Pipe::MGPipeResourceRecord& record,
|
||||
const SharedPtr<MG_State::GLState::ITextureObject>& stateTextureObject);
|
||||
#endif
|
||||
// The storage half of the sync for a texture created by glTextureView. Instead of
|
||||
// allocating storage and replaying uploads, it makes this object's ES name BE a view
|
||||
// of the storage texture's ES name (EXT/OES_texture_view), which is what gives the
|
||||
@@ -2367,7 +2345,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// glCopyImageSubData passes: it tries FollowCopyImageInStagedStore first and marks the store
|
||||
// itself when that fails.
|
||||
void NoteDriverSideTextureWriteByHandle(MG_Pipe::MGPipeHandle texture, Bool storeFollowsTheWrite = false);
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P8-E (notes/p8/E.md): make glCopyImageSubData's texel-block move in the server's staged
|
||||
// store too (StagedTextureStore::FollowCopy), so the destination level's bytes stay its
|
||||
// content. Box in each image's own texel grid. False when the store cannot follow: a cube
|
||||
@@ -2377,7 +2354,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
Bool FollowCopyImageInStagedStore(MG_Pipe::MGPipeHandle source, Uint32 sourceLevel, const IntVec3& sourceOrigin,
|
||||
MG_Pipe::MGPipeHandle destination, Uint32 destinationLevel,
|
||||
const IntVec3& destinationOrigin, const IntVec3& size);
|
||||
#endif
|
||||
// Brings every texture the next draw reads - the touched units' bindings and the draw
|
||||
// FBO's texture attachments - onto the backend, through the two borrowed-pair memos
|
||||
// documented at their definitions. Declared here so tests can drive those memos directly.
|
||||
@@ -2424,14 +2400,12 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// "one object is bound to BOTH bindings" skip, where the DRAW pass already did the
|
||||
// attachment work and only glReadBuffer is READ-target-specific.
|
||||
void SyncReadBufferToBackendByHandle(MG_Pipe::MGPipeHandle fbo);
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P5c (hd, CONTRACT-P5C §3.2): the framebuffer handle the CURRENT sync is keyed on.
|
||||
// A caller applying a record sets it before SyncToBackend / SyncReadBufferToBackend,
|
||||
// which then read the applier's record for THAT handle instead of probing the
|
||||
// client's slot allocator for the frontend object's lifetime id (T2). Read only
|
||||
// with an active transport; monolith resolves through HandleOf as it always did.
|
||||
MG_Pipe::MGPipeHandle m_pushedSyncHandle = MG_Pipe::kMGPipeNullHandle;
|
||||
#endif
|
||||
void InvalidateSyncedState();
|
||||
Uint GetBackendFramebufferId() const { return m_backendFBOId; }
|
||||
void Bind(FramebufferTarget target) const;
|
||||
@@ -3124,11 +3098,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// Same for gl_BaseVertex: only a program that reads it pays for the per-draw
|
||||
// uniform write, and only such a program needs the reset after one.
|
||||
Bool ReadsBaseVertex() const { return m_baseVertexUniformLocation >= 0; }
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P8-C: same for the gl_BaseInstance uniform - which an indirect draw feeds from the
|
||||
// command on the CPU only when the mg_IndirectParams view is absent.
|
||||
Bool ReadsBaseInstance() const { return m_baseInstanceUniformLocation >= 0; }
|
||||
#endif
|
||||
// Which viewport indices the next draw's fragments may keep, one bit each. Written
|
||||
// once per replay pass; see ForEachViewportRoutingPass.
|
||||
void SetViewportPassMask(Uint32 indexMask) const;
|
||||
@@ -3672,7 +3644,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
g_backendRenderbufferObjects;
|
||||
} // namespace RenderbufferImpl
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P5c (ct), CONTRACT-P5C.md §5.2: object_death's per-kind release, one entry point for all
|
||||
// seven kinds for the same reason the notice switch is one - the answer is the same for
|
||||
// all of them: every holder of the kind's twin table lets go of the twin at this handle.
|
||||
@@ -3704,5 +3675,4 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// destructor calls into the driver (there is no current context, and the ids name nothing).
|
||||
// A no-op once exit() has begun (the statics may already be gone). Monolith never calls it.
|
||||
void DropEveryTwinForEndedServerSession();
|
||||
#endif
|
||||
} // namespace MobileGL::MG_Backend::DirectGLES
|
||||
|
||||
@@ -125,7 +125,6 @@ namespace MobileGL::MG_Backend::DirectGLES::MultiDrawImpl {
|
||||
const Uint8* HostBytes = nullptr; // HostBytes: null when there is no CPU copy
|
||||
};
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// A missing record on the handle arm is a NAMED refusal and never a quiet fall-back to
|
||||
// the frontend (TASK-mv requirement 2; the shape is Managers.cpp's
|
||||
// RefuseNullFrontendTextureOffTheHandleArm). The frontend element slot is not a second
|
||||
@@ -175,11 +174,9 @@ namespace MobileGL::MG_Backend::DirectGLES::MultiDrawImpl {
|
||||
if (!candidate.Live || candidate.Gen != res.Gen) return false;
|
||||
return candidate.Desc.HasDefinedContent != 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
BoundIndexBufferView ResolveBoundIndexBuffer(IndexBufferQuestion question, const char* entry) {
|
||||
BoundIndexBufferView view;
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (BufferImpl::VertexInputReadsRecords()) {
|
||||
const auto& st = MG_Pipe::MGPipeApplier();
|
||||
const MG_Pipe::MGPipeHandle res = BufferImpl::ResolveDrawIndexBufferFromRecord(st).Res;
|
||||
@@ -219,7 +216,6 @@ namespace MobileGL::MG_Backend::DirectGLES::MultiDrawImpl {
|
||||
}
|
||||
return view;
|
||||
}
|
||||
#endif
|
||||
// MONOLITH GLUE from here down, token for token what each call site did before.
|
||||
const auto& vao = MG_Pipe::gPipeInputs.GetBoundVertexArray();
|
||||
if (!vao) return view;
|
||||
@@ -293,7 +289,6 @@ namespace MobileGL::MG_Backend::DirectGLES::MultiDrawImpl {
|
||||
// an empty slot, and not the arm test (ID-110: the arm was decided by the transport
|
||||
// above, never inferred from a null).
|
||||
Uint BoundDrawIndirectBufferId() {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (MG_Config::DataArmIsRecord()) {
|
||||
const MG_Pipe::MGPipeHandle res = MG_Pipe::MGPipeApplier().VerbIndirectBuffer;
|
||||
if (MG_Pipe::MGPipeHandleIsNull(res)) return 0;
|
||||
@@ -303,7 +298,6 @@ namespace MobileGL::MG_Backend::DirectGLES::MultiDrawImpl {
|
||||
}
|
||||
return resource->id;
|
||||
}
|
||||
#endif
|
||||
// MONOLITH GLUE from here down, token for token what this function did before.
|
||||
const auto& indirect =
|
||||
MG_Pipe::gPipeInputs.GetBufferBindingSlot(BufferTarget::DrawIndirect).GetBoundObject();
|
||||
|
||||
@@ -37,16 +37,13 @@
|
||||
#include <regex>
|
||||
|
||||
namespace MobileGL::MG_Backend::DirectGLES {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// read_pixels writes server reply scratch. The client refuses PACK_BUFFER before
|
||||
// emission, so no frontend pack binding can affect this destination.
|
||||
static const SharedPtr<MG_State::GLState::BufferObject>& SplitReadbackPackBuffer() {
|
||||
static const SharedPtr<MG_State::GLState::BufferObject> none;
|
||||
return none;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
const FormatCapabilityCache* ActiveBackendFormatCaps() {
|
||||
// Under a live split session the SERVER's private backend is what owns the context on the
|
||||
// apply thread (and these reads all run there), so its cache is the authoritative one.
|
||||
@@ -70,7 +67,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
#endif
|
||||
return pActiveBackendObject ? &pActiveBackendObject->GetFormatCapabilities() : nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
Flags<PixelFormatNormalizeOptionBit> GetForcedPixelFormatNormalizeOptions() {
|
||||
@@ -110,26 +106,16 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
SizeT targetIndex,
|
||||
Bool caveat,
|
||||
FormatCapability capability) {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
const FormatCapabilityCache* activeCaps = ActiveBackendFormatCaps();
|
||||
if (activeCaps == nullptr || targetIndex >= kFormatCapabilityTargetCount) {
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
if (!pActiveBackendObject || targetIndex >= kFormatCapabilityTargetCount) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
const SizeT formatIndex = static_cast<SizeT>(internalFormat);
|
||||
if (formatIndex >= kFormatCapabilityFormatCount) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
const FormatCapabilityCache& cache = *activeCaps;
|
||||
#else
|
||||
const FormatCapabilityCache& cache = pActiveBackendObject->GetFormatCapabilities();
|
||||
#endif
|
||||
const FormatCapabilityFlags caps =
|
||||
caveat ? cache.CaveatCaps[targetIndex][formatIndex] : cache.FullCaps[targetIndex][formatIndex];
|
||||
return HasFormatCapability(caps, capability);
|
||||
@@ -173,11 +159,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
using namespace MobileGL::MG_Util::TextureFormatProcessor;
|
||||
const GLenum requestedInternalFormat = MG_Util::ConvertTextureInternalFormatToGLEnum(internalFormat);
|
||||
Flags<PixelFormatNormalizeOptionBit> options;
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (ActiveBackendFormatCaps() == nullptr || ShouldUseCaveatFormat(internalFormat, targetIndex)) {
|
||||
#else
|
||||
if (!pActiveBackendObject || ShouldUseCaveatFormat(internalFormat, targetIndex)) {
|
||||
#endif
|
||||
options = GetRuntimeFallbackNormalizeOptions(
|
||||
requestedInternalFormat,
|
||||
TextureImpl::GetRenderTargetNormalizeOptions(g_GLESCapabilities, targetIndex));
|
||||
@@ -271,15 +253,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// not be resolved yet - a probe run then would latch "cannot tell" as "clean"
|
||||
// forever. Once the backend exists, the first narrow-format image this process
|
||||
// creates runs the probe on a live context.
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (ActiveBackendFormatCaps() == nullptr) {
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
if (pActiveBackendObject == nullptr) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
return MG_Util::SelfTest::CopyImageMirrorsPacked16FieldOrder(g_GLESFuncs);
|
||||
}
|
||||
|
||||
@@ -317,15 +293,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
if (!TargetRequiresRenderableFormat(targetIndex)) {
|
||||
return false;
|
||||
}
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (ActiveBackendFormatCaps() != nullptr && !ShouldUseCaveatFormat(internalFormat, targetIndex)) {
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
if (pActiveBackendObject && !ShouldUseCaveatFormat(internalFormat, targetIndex)) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
const GLenum requestedInternalFormat = MG_Util::ConvertTextureInternalFormatToGLEnum(internalFormat);
|
||||
const Flags<PixelFormatNormalizeOptionBit> options = GetRuntimeFallbackNormalizeOptions(
|
||||
requestedInternalFormat, GetRenderTargetNormalizeOptions(g_GLESCapabilities, targetIndex));
|
||||
@@ -2364,10 +2334,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
static Bool StoreClientRows(SizeT dstPixelBytes, SizeT swapGroupSize, GLsizei width, GLsizei sliceHeight,
|
||||
GLsizei sliceCount, void* pixels, Bool applyPackImageParams, FillRow&& fillRow) {
|
||||
const auto& pixelPackBufferObject =
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
MG_Config::DataArmIsRecord()
|
||||
? SplitReadbackPackBuffer() :
|
||||
#endif
|
||||
MG_Pipe::gPipeInputs.GetBufferBindingSlot(BufferTarget::PixelPack).GetBoundObject();
|
||||
|
||||
// Destination layout is computed from the client-side PACK parameters; only the actual pixel
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
#include <MG_Util/Texture/TextureFormatProcessor.h>
|
||||
|
||||
namespace MobileGL::MG_Backend::DirectGLES {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// C6 / ID-52 / CONTRACT-P5 table 3's pActiveBackendObject row. The format-capability cache of
|
||||
// THIS ROLE's backend. Under an active transport the server's own private
|
||||
// BackendObject_DirectGLES owns the context on the apply thread, so a backend-internal format
|
||||
@@ -24,7 +23,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// (five in Utils.cpp, ClampSamplesToBackendSupport in BackendObject_DirectGLES.cpp) go through
|
||||
// this instead of dereferencing pActiveBackendObject directly.
|
||||
const FormatCapabilityCache* ActiveBackendFormatCaps();
|
||||
#endif
|
||||
|
||||
namespace DebugImpl {
|
||||
class ErrorLopper {
|
||||
|
||||
@@ -281,7 +281,6 @@
|
||||
return true;
|
||||
}
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P9 W2-a. The store's Defined-ness follows the client's respecifies; the driver's follows the
|
||||
// twin's last sync. A re-mint asks this before reading a level back, because a level defined
|
||||
// after that sync (null data, or null data plus a partial upload) is not on the driver yet and
|
||||
@@ -460,4 +459,3 @@
|
||||
bytes = Move(result);
|
||||
return true;
|
||||
}
|
||||
#endif // MOBILEGL_BUILD_DISAGGREGATED
|
||||
|
||||
@@ -423,11 +423,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
}
|
||||
|
||||
Bool BackendObject_DirectVulkan::LatchIfGpuFaulted() {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
return pVulkanRenderer != nullptr && pVulkanRenderer->LatchIfGpuHung("apply");
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void BackendObject_DirectVulkan::Initialize() {
|
||||
|
||||
@@ -38,16 +38,12 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
RendererSlot pVulkanRenderer;
|
||||
|
||||
namespace {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
void RejectWireLegacyBuffer() {
|
||||
if (MG_Config::DataArmIsRecord()) {
|
||||
MGLOG_F("MGPipe: Fatal{RoleViolation, \"buffer-legacy-arm\"} (Magma P7 buffer consumer)");
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
#else
|
||||
inline void RejectWireLegacyBuffer() {}
|
||||
#endif
|
||||
// Generation of the live VulkanRenderer instance, mirroring
|
||||
// DirectGLES's g_syncContextGeneration. BackendObject_DirectVulkan
|
||||
// bumps it (BumpRendererGeneration) wherever pVulkanRenderer is reset
|
||||
@@ -138,53 +134,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
}
|
||||
|
||||
namespace {
|
||||
#if !MOBILEGL_BUILD_RECORD_ARM
|
||||
// P7 wave 2 package C, OQ-8: THE PULL BUILD'S HALF, VERBATIM. A disaggregated build
|
||||
// answers GetShaderStorageBlock{Index,Binding} out of LinkArtifacts::storageBlocks
|
||||
// instead (see those functions below), which retires this whole cache - the map, the
|
||||
// SPIRV-Reflect rebuild, the teardown clear and the rehash hazard. It cannot be
|
||||
// retired HERE because the archive member it reads is itself disaggregated-only, for
|
||||
// G1: one more Vector in LinkArtifacts moves the pull build's .text, which is pinned
|
||||
// byte-for-byte at 0xa52203.
|
||||
//
|
||||
// NOTHING OUTSIDE THIS FILE READS ANY OF IT (measured: BufferVariableResource,
|
||||
// bufferVariables, activeVariables and StorageBlockResource have no reference anywhere
|
||||
// else in the tree, and `dataSize` is written and never read - GL_BUFFER_VARIABLE and
|
||||
// GL_ACTIVE_VARIABLES are answered by MG_Impl/GLImpl/Program/ProgramInterface.cpp out
|
||||
// of glslang's own reflection, which is a different index space). So the two arms
|
||||
// below differ in exactly the two functions their callers use, and in nothing else.
|
||||
struct BufferVariableResource {
|
||||
String name;
|
||||
GLuint blockIndex = 0;
|
||||
GLint offset = 0;
|
||||
GLint size = 0;
|
||||
};
|
||||
|
||||
struct StorageBlockResource {
|
||||
String name;
|
||||
GLuint binding = 0;
|
||||
GLint dataSize = 0;
|
||||
Vector<GLuint> activeVariables;
|
||||
};
|
||||
|
||||
struct ProgramResourceCache {
|
||||
// Lifetime id of the program the cached reflection belongs to. GL names are
|
||||
// recycled (IndexGenerator hands freed indices straight back), and a
|
||||
// recreated program's backendStateVersion restarts at the same small values,
|
||||
// so the version alone can collide; the never-reused lifetime id makes the
|
||||
// slot's ownership unambiguous.
|
||||
Uint64 programLifetimeId = 0;
|
||||
Uint32 backendStateVersion = 0;
|
||||
// glShaderStorageBlockBinding deliberately does NOT bump the backend state
|
||||
// version, and the pipeline composite is unnamed so the in-place patch in
|
||||
// DirectVulkan::ShaderStorageBlockBinding can never reach its slot - the
|
||||
// mirror replay bumps only the program's block-binding version. Without this
|
||||
// key the composite's slot kept serving the pre-rebind block.binding.
|
||||
Uint32 blockBindingVersion = 0;
|
||||
Vector<StorageBlockResource> storageBlocks;
|
||||
Vector<BufferVariableResource> bufferVariables;
|
||||
};
|
||||
#endif // !MOBILEGL_BUILD_RECORD_ARM
|
||||
|
||||
struct DrawElementsIndirectCommand {
|
||||
Uint32 count = 0;
|
||||
@@ -201,14 +150,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
Uint32 baseInstance = 0;
|
||||
};
|
||||
|
||||
#if !MOBILEGL_BUILD_RECORD_ARM
|
||||
// Keyed by GL program name so the freed-name reuse in IndexGenerator bounds the
|
||||
// map at the peak-simultaneous-program high-water mark; each slot's ownership is
|
||||
// checked against the program's lifetime id before it is served (see
|
||||
// GetProgramResourceCache). Cleared wholesale at EGL teardown via
|
||||
// ClearProgramResourceCaches.
|
||||
UnorderedMap<GLuint, ProgramResourceCache> g_programResourceCaches;
|
||||
#endif
|
||||
|
||||
void ClearReadPixelsOutput(GLsizei width, GLsizei height, GLenum format, GLenum type, void* pixels) {
|
||||
if (!pixels || width <= 0 || height <= 0) {
|
||||
@@ -223,160 +164,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
}
|
||||
}
|
||||
|
||||
#if !MOBILEGL_BUILD_RECORD_ARM
|
||||
String NormalizeDescriptorName(const SpvReflectDescriptorBinding& binding) {
|
||||
const char* rawName = binding.name;
|
||||
if (binding.type_description != nullptr && binding.type_description->type_name != nullptr) {
|
||||
rawName = binding.type_description->type_name;
|
||||
}
|
||||
if (rawName == nullptr) {
|
||||
return {};
|
||||
}
|
||||
String name = rawName;
|
||||
const auto arraySuffix = name.find("[0]");
|
||||
if (arraySuffix != String::npos) {
|
||||
name = name.substr(0, arraySuffix);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
void AddBufferVariablesRecursive(const SpvReflectBlockVariable& variable, const String& prefix,
|
||||
GLuint blockIndex, Vector<BufferVariableResource>& variables,
|
||||
Vector<GLuint>& activeVariables) {
|
||||
for (Uint32 memberIndex = 0; memberIndex < variable.member_count; ++memberIndex) {
|
||||
const auto& member = variable.members[memberIndex];
|
||||
String name = prefix;
|
||||
if (!name.empty()) {
|
||||
name += ".";
|
||||
}
|
||||
name += member.name ? member.name : "";
|
||||
|
||||
if (member.member_count > 0) {
|
||||
AddBufferVariablesRecursive(member, name, blockIndex, variables, activeVariables);
|
||||
continue;
|
||||
}
|
||||
|
||||
BufferVariableResource resource{};
|
||||
resource.name = name;
|
||||
resource.blockIndex = blockIndex;
|
||||
resource.offset = static_cast<GLint>(member.offset);
|
||||
resource.size = static_cast<GLint>(member.size);
|
||||
const GLuint variableIndex = static_cast<GLuint>(variables.size());
|
||||
variables.push_back(resource);
|
||||
activeVariables.push_back(variableIndex);
|
||||
}
|
||||
}
|
||||
|
||||
ProgramResourceCache& GetProgramResourceCache(const MG_State::GLState::ProgramObject& program) {
|
||||
auto& cache = g_programResourceCaches[program.GetExternalIndex()];
|
||||
const Uint64 programLifetimeId = program.GetLifetimeId();
|
||||
const Uint32 backendStateVersion = program.GetBackendStateVersion();
|
||||
const Uint32 blockBindingVersion = program.GetBlockBindingVersion();
|
||||
// The lifetime id must match too: a new program that reuses a deleted
|
||||
// program's name and happens to land on the same backendStateVersion (both
|
||||
// count from zero) would otherwise be served the dead program's reflection.
|
||||
if (cache.programLifetimeId == programLifetimeId &&
|
||||
cache.backendStateVersion == backendStateVersion &&
|
||||
(!cache.storageBlocks.empty() || !cache.bufferVariables.empty())) {
|
||||
if (cache.blockBindingVersion != blockBindingVersion) {
|
||||
// Only the block bindings moved (glShaderStorageBlockBinding, or the
|
||||
// pipeline composite's mirror replay - neither touches the backend
|
||||
// state version): the reflection itself is unchanged, so re-apply the
|
||||
// overrides by name instead of re-running spirv-reflect. Overrides
|
||||
// only ever accumulate, so a block without one still holds its
|
||||
// declared binding.
|
||||
for (auto& block : cache.storageBlocks) {
|
||||
const Int rebound = program.GetShaderStorageBlockBindingOverride(block.name);
|
||||
if (rebound >= 0) block.binding = static_cast<Uint32>(rebound);
|
||||
}
|
||||
cache.blockBindingVersion = blockBindingVersion;
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
||||
cache = {};
|
||||
cache.programLifetimeId = programLifetimeId;
|
||||
cache.backendStateVersion = backendStateVersion;
|
||||
cache.blockBindingVersion = blockBindingVersion;
|
||||
|
||||
Vector<SpvReflectShaderModule> modules;
|
||||
Vector<Bool> validModules;
|
||||
const auto& spirvs = program.GetGeneratedSpirv();
|
||||
for (const auto& spirv : spirvs) {
|
||||
if (spirv.empty()) {
|
||||
continue;
|
||||
}
|
||||
SpvReflectShaderModule module{};
|
||||
const SpvReflectResult result =
|
||||
spvReflectCreateShaderModule(spirv.size() * sizeof(Uint), spirv.data(), &module);
|
||||
if (result != SPV_REFLECT_RESULT_SUCCESS) {
|
||||
continue;
|
||||
}
|
||||
modules.push_back(module);
|
||||
validModules.push_back(true);
|
||||
}
|
||||
|
||||
for (auto& module : modules) {
|
||||
uint32_t bindingCount = 0;
|
||||
SpvReflectResult result = spvReflectEnumerateDescriptorBindings(&module, &bindingCount, nullptr);
|
||||
if (result != SPV_REFLECT_RESULT_SUCCESS || bindingCount == 0) {
|
||||
continue;
|
||||
}
|
||||
Vector<SpvReflectDescriptorBinding*> bindings(bindingCount);
|
||||
result = spvReflectEnumerateDescriptorBindings(&module, &bindingCount, bindings.data());
|
||||
if (result != SPV_REFLECT_RESULT_SUCCESS) {
|
||||
continue;
|
||||
}
|
||||
std::sort(bindings.begin(), bindings.end(), [](const auto* lhs, const auto* rhs) {
|
||||
const String lhsName = lhs ? NormalizeDescriptorName(*lhs) : String();
|
||||
const String rhsName = rhs ? NormalizeDescriptorName(*rhs) : String();
|
||||
if (lhsName != rhsName) return lhsName < rhsName;
|
||||
return lhs->binding < rhs->binding;
|
||||
});
|
||||
for (const auto* binding : bindings) {
|
||||
if (binding == nullptr ||
|
||||
binding->descriptor_type != SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_BUFFER) {
|
||||
continue;
|
||||
}
|
||||
const String blockName = NormalizeDescriptorName(*binding);
|
||||
if (blockName.empty()) {
|
||||
continue;
|
||||
}
|
||||
const auto existing = std::find_if(
|
||||
cache.storageBlocks.begin(), cache.storageBlocks.end(),
|
||||
[&](const StorageBlockResource& block) { return block.name == blockName; });
|
||||
if (existing != cache.storageBlocks.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
StorageBlockResource block{};
|
||||
block.name = blockName;
|
||||
block.binding = binding->binding;
|
||||
// glShaderStorageBlockBinding survives every rebuild of this cache: the
|
||||
// authoritative record of a rebound block lives on the program (it is what
|
||||
// GL_BUFFER_BINDING reports), and only the shader's declared binding is
|
||||
// recoverable from the SPIR-V. Without this, any unrelated state-version
|
||||
// bump would silently revert the block to its declared binding.
|
||||
const Int rebound = program.GetShaderStorageBlockBindingOverride(blockName);
|
||||
if (rebound >= 0) block.binding = static_cast<Uint32>(rebound);
|
||||
block.dataSize = static_cast<GLint>(binding->block.size);
|
||||
const GLuint blockIndex = static_cast<GLuint>(cache.storageBlocks.size());
|
||||
AddBufferVariablesRecursive(binding->block, blockName, blockIndex, cache.bufferVariables,
|
||||
block.activeVariables);
|
||||
cache.storageBlocks.push_back(block);
|
||||
}
|
||||
}
|
||||
|
||||
for (SizeT i = 0; i < modules.size(); ++i) {
|
||||
if (validModules[i]) {
|
||||
spvReflectDestroyShaderModule(&modules[i]);
|
||||
}
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
#endif // !MOBILEGL_BUILD_RECORD_ARM
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// The verb's handles identify server stores; never inspect a client binding.
|
||||
//
|
||||
// P8-D: THE GPU READS THE WORDS (VulkanRenderer::DrawWireIndirectNative, WireDraw.inc), as
|
||||
@@ -456,7 +244,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
pVulkanRenderer->MultiDrawArrays(payload);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
MG_State::GLState::ProgramObject* TryGetDirectVulkanProgram(GLuint program) {
|
||||
if (!MG_Pipe::gPipeInputs.ValidateProgramName(program)) {
|
||||
@@ -495,7 +282,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
|
||||
} // namespace
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P7 wave 2 package C, OQ-8 (CONTRACT-P7 §5.3): THE MONOLITH CONSUMER READS THE ARCHIVE
|
||||
// TOO, so one published list serves both of this backend's arms.
|
||||
//
|
||||
@@ -555,43 +341,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
if (rebound >= 0) return static_cast<GLuint>(rebound);
|
||||
return blocks[blockIndex].binding;
|
||||
}
|
||||
#else
|
||||
void ClearProgramResourceCaches() {
|
||||
// Called from EGL teardown while the backend's m_eglStateMutex is held; GL
|
||||
// calls are serialized in this codebase (contexts migrate threads but never
|
||||
// run concurrently), so no other thread can be inside the unsynchronized map.
|
||||
// Live programs in another context self-heal: their entry rebuilds from the
|
||||
// retained generated SPIR-V on the next resource query.
|
||||
g_programResourceCaches.clear();
|
||||
}
|
||||
|
||||
GLuint GetShaderStorageBlockIndex(const MG_State::GLState::ProgramObject& program, const String& name) {
|
||||
auto& cache = GetProgramResourceCache(program);
|
||||
auto find = [&cache](const String& key) {
|
||||
return std::find_if(cache.storageBlocks.begin(), cache.storageBlocks.end(),
|
||||
[&](const StorageBlockResource& block) { return block.name == key; });
|
||||
};
|
||||
auto it = find(name);
|
||||
if (it == cache.storageBlocks.end()) {
|
||||
// Cache names are normalized (NormalizeDescriptorName drops the array suffix), so
|
||||
// an arrayed block that GL enumerates per element - "B[0]", "B[1]" - is one entry
|
||||
// here, spelled "B". Retry against the bare name before giving up.
|
||||
const auto bracket = name.rfind('[');
|
||||
if (bracket == String::npos || name.empty() || name.back() != ']') return GL_INVALID_INDEX;
|
||||
it = find(name.substr(0, bracket));
|
||||
if (it == cache.storageBlocks.end()) return GL_INVALID_INDEX;
|
||||
}
|
||||
return static_cast<GLuint>(std::distance(cache.storageBlocks.begin(), it));
|
||||
}
|
||||
|
||||
GLuint GetShaderStorageBlockBinding(const MG_State::GLState::ProgramObject& program, GLuint blockIndex) {
|
||||
auto& cache = GetProgramResourceCache(program);
|
||||
if (blockIndex >= cache.storageBlocks.size()) {
|
||||
return 0;
|
||||
}
|
||||
return cache.storageBlocks[blockIndex].binding;
|
||||
}
|
||||
#endif
|
||||
|
||||
void ClearBufferfi(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil) {
|
||||
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::ClearBufferfi called with null VulkanRenderer");
|
||||
@@ -646,23 +395,19 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
}
|
||||
|
||||
void MultiDrawElementsIndirect(GLenum mode, GLenum type, const void* indirect, GLsizei drawcount, GLsizei stride) {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (MG_Config::DataArmIsRecord()) {
|
||||
DrawWireIndirect(mode, type, indirect, drawcount, stride, true);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::MultiDrawElementsIndirect called with null VulkanRenderer");
|
||||
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs.IsLive(), "DirectVulkan::MultiDrawElementsIndirect called with null GL context");
|
||||
pVulkanRenderer->MultiDrawElementsIndirect(mode, type, indirect, drawcount, stride);
|
||||
}
|
||||
void MultiDrawArraysIndirect(GLenum mode, const void* indirect, GLsizei drawcount, GLsizei stride) {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (MG_Config::DataArmIsRecord()) {
|
||||
DrawWireIndirect(mode, 0, indirect, drawcount, stride, false);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::MultiDrawArraysIndirect called with null VulkanRenderer");
|
||||
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs.IsLive(), "DirectVulkan::MultiDrawArraysIndirect called with null GL context");
|
||||
|
||||
@@ -714,24 +459,20 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
}
|
||||
void MultiDrawElementsIndirectCount(GLenum mode, GLenum type, const void* indirect, GLintptr drawcount,
|
||||
GLsizei maxdrawcount, GLsizei stride) {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (MG_Config::DataArmIsRecord()) {
|
||||
DrawWireIndirect(mode, type, indirect, maxdrawcount, stride, true, true, drawcount);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::MultiDrawElementsIndirectCount called with null VulkanRenderer");
|
||||
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs.IsLive(), "DirectVulkan::MultiDrawElementsIndirectCount called with null GL context");
|
||||
pVulkanRenderer->MultiDrawElementsIndirectCount(mode, type, indirect, drawcount, maxdrawcount, stride);
|
||||
}
|
||||
void MultiDrawArraysIndirectCount(GLenum mode, const void* indirect, GLintptr drawcount,
|
||||
GLsizei maxdrawcount, GLsizei stride) {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (MG_Config::DataArmIsRecord()) {
|
||||
DrawWireIndirect(mode, 0, indirect, maxdrawcount, stride, false, true, drawcount);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::MultiDrawArraysIndirectCount called with null VulkanRenderer");
|
||||
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs.IsLive(), "DirectVulkan::MultiDrawArraysIndirectCount called with null GL context");
|
||||
|
||||
@@ -804,12 +545,10 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
DrawElementsInstancedBaseVertexBaseInstance(mode, count, type, indices, instancecount, 0, 0);
|
||||
}
|
||||
void DrawElementsIndirect(GLenum mode, GLenum type, const void* indirect) {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (MG_Config::DataArmIsRecord()) {
|
||||
DrawWireIndirect(mode, type, indirect, 1, 0, true);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::DrawElementsIndirect called with null VulkanRenderer");
|
||||
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs.IsLive(), "DirectVulkan::DrawElementsIndirect called with null GL context");
|
||||
|
||||
@@ -869,12 +608,10 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
DrawArraysInstancedBaseInstance(mode, first, count, instancecount, 0);
|
||||
}
|
||||
void DrawArraysIndirect(GLenum mode, const void* indirect) {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (MG_Config::DataArmIsRecord()) {
|
||||
DrawWireIndirect(mode, 0, indirect, 1, 0, false);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::DrawArraysIndirect called with null VulkanRenderer");
|
||||
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs.IsLive(), "DirectVulkan::DrawArraysIndirect called with null GL context");
|
||||
|
||||
@@ -943,7 +680,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
}
|
||||
|
||||
void DispatchComputeIndirect(GLintptr indirect) {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P8-SV: THE GPU READS THE GROUP COUNTS on the wire arm too - VulkanRenderer's
|
||||
// DispatchComputeIndirect takes its transport branch into DispatchWireComputeIndirect
|
||||
// (WireDraw.inc), which issues vkCmdDispatchIndirect from the verb's store. The CPU read
|
||||
@@ -952,7 +688,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
pVulkanRenderer->DispatchComputeIndirect(indirect);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::DispatchComputeIndirect called with null VulkanRenderer");
|
||||
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs.IsLive(), "DirectVulkan::DispatchComputeIndirect called with null GL context");
|
||||
pVulkanRenderer->DispatchComputeIndirect(indirect);
|
||||
@@ -1014,7 +749,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
}
|
||||
|
||||
void ShaderStorageBlockBinding(GLuint program, const GLchar* storageBlockName, GLuint storageBlockBinding) {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (MG_Config::DataArmIsRecord()) {
|
||||
auto& state = MG_Pipe::MGPipeApplier();
|
||||
const auto handle = state.VerbStorageBlockProgram;
|
||||
@@ -1032,7 +766,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
++record.BindingsSerial;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
auto* programObject = TryGetDirectVulkanProgram(program);
|
||||
if (!programObject || storageBlockName == nullptr) return;
|
||||
const Int maxBindings =
|
||||
@@ -1058,7 +791,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
MakeUnique<GenericErrorInfo>("DirectVulkan", __func__, "Shader storage binding is out of range."));
|
||||
return;
|
||||
}
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P7 wave 2 package C, OQ-8: NOTHING LEFT TO PATCH, and the hazard goes with it.
|
||||
//
|
||||
// The frontend has already recorded the new binding on the program, and that record is
|
||||
@@ -1078,26 +810,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
// cache is the retirement. The pull build below keeps both.
|
||||
(void)storageBlockBinding;
|
||||
}
|
||||
#else
|
||||
// The frontend already validated that the name denotes an active block, and has
|
||||
// already recorded the new binding on the program - which is what reseeds this cache
|
||||
// whenever it is rebuilt. Writing the entry here as well keeps an ALREADY-BUILT cache
|
||||
// (the common case: the very next draw reads it) from having to be thrown away.
|
||||
//
|
||||
// Resolve the index BEFORE taking the reference, and bounds-check the way the
|
||||
// sibling getter does. GetShaderStorageBlockIndex re-enters GetProgramResourceCache,
|
||||
// which indexes g_programResourceCaches and can therefore insert - and that map is
|
||||
// open-addressed, so a rehash MOVES its entries and a reference taken before the
|
||||
// call is left dangling. Binding a program's storage block
|
||||
// while another program's entry was still absent from the cache was a reproducible
|
||||
// segfault (ProgramPipelineScenario's two storage-block cases, in one process).
|
||||
const GLuint blockIndex = GetShaderStorageBlockIndex(*programObject, storageBlockName);
|
||||
if (blockIndex == GL_INVALID_INDEX) return;
|
||||
auto& cache = GetProgramResourceCache(*programObject);
|
||||
if (blockIndex >= cache.storageBlocks.size()) return;
|
||||
cache.storageBlocks[blockIndex].binding = storageBlockBinding;
|
||||
}
|
||||
#endif
|
||||
void ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* pixels) {
|
||||
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::ReadPixels called with null VulkanRenderer");
|
||||
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs.IsLive(), "DirectVulkan::ReadPixels called with null GL context");
|
||||
@@ -1140,7 +852,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
// memory) into uint32 values with the loop-closing first index appended.
|
||||
static Bool BuildClosedLineLoopIndices(GLsizei count, GLenum type, const void* indices,
|
||||
Vector<Uint32>& outIndices) {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (MG_Config::DataArmIsRecord()) {
|
||||
const SizeT width = MG_Util::GetGLTypeSize(type);
|
||||
if ((width != 1 && width != 2 && width != 4) || count < 2) return false;
|
||||
@@ -1163,7 +874,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
outIndices[count] = outIndices[0];
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
const SizeT indexSize = MG_Util::GetGLTypeSize(type);
|
||||
if (indexSize == 0 || count < 2) {
|
||||
return false;
|
||||
@@ -1318,15 +1028,10 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
// MultiDrawIndexedCmd left the client-memory shape addressing a view whose byte
|
||||
// offset is a hardcoded 0, so UploadAndBindIndexBuffer saw a null client pointer,
|
||||
// declined the whole batch and painted nothing.)
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
const Bool noIndexBuffer = MG_Config::DataArmIsRecord()
|
||||
? MG_Pipe::MGPipeHandleIsNull(MG_Pipe::MGPipeApplier().IndexBuffer.Res)
|
||||
: MG_Pipe::gPipeInputs.GetBoundVertexArray()->GetIndexBufferBindingSlot().GetBoundObject() == nullptr;
|
||||
if (noIndexBuffer) {
|
||||
#else
|
||||
const auto& vao = *MG_Pipe::gPipeInputs.GetBoundVertexArray();
|
||||
if (vao.GetIndexBufferBindingSlot().GetBoundObject() == nullptr) {
|
||||
#endif
|
||||
for (GLsizei i = 0; i < drawcount; ++i) {
|
||||
if (count[i] <= 0) {
|
||||
continue;
|
||||
@@ -1512,7 +1217,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
}
|
||||
// Pure status read (glGetSynciv must not flush).
|
||||
if (pVulkanRenderer->IsSubmitIndexComplete(sync->submitIndex)) return true;
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P13 W4: A STATUS POLL THAT CAN NEVER BE ANSWERED. On the record arm a frame's draws stay
|
||||
// in one command buffer (and one render pass) until something submits it, so a client that
|
||||
// only polls - glGetSynciv in a loop, no flush bit, no swap - waits for work nothing will
|
||||
@@ -1526,7 +1230,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
pVulkanRenderer->FlushForSyncPoint(sync->submitIndex);
|
||||
return pVulkanRenderer->IsSubmitIndexComplete(sync->submitIndex);
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
Uint64 GetRendererGeneration();
|
||||
void BumpRendererGeneration();
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P7 wave 2 package C (CONTRACT-P7 §5.5). Magma's StateObjectDeathOps table, installed
|
||||
// from BackendObject_DirectVulkan::Initialize() so that GetStateObjectDeathOps() is
|
||||
// non-null on every Magma role that owns a backend. See the definition for what the one
|
||||
@@ -55,7 +54,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
// True when the notice currently installed is this backend's. Read by MG_Test so the
|
||||
// install can be asserted without a death having to happen first.
|
||||
Bool StateObjectDeathOpsInstalled();
|
||||
#endif
|
||||
|
||||
// Drops every cached program-resource reflection entry (CPU-side strings/vectors
|
||||
// only, no Vulkan handles). Called at EGL teardown next to the renderer reset;
|
||||
|
||||
@@ -7,9 +7,7 @@
|
||||
// End of Source File Header
|
||||
|
||||
#include "BufferArena.h"
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
#include <Config.h>
|
||||
#endif
|
||||
|
||||
namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
Bool BufferArena::Initialize(const BufferArenaDesc& desc) {
|
||||
@@ -128,14 +126,12 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
bufferDesc.usage = m_desc.usage;
|
||||
bufferDesc.memoryUsage = m_desc.memoryUsage;
|
||||
bufferDesc.allocationFlags = m_desc.allocationFlags;
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// Split staging may also be the destination of a GPU UBO range copy.
|
||||
// Persistent host writes must be coherent: Upload deliberately writes
|
||||
// the mapped pointer without a per-range VMA flush.
|
||||
if (MG_Config::DataArmIsRecord() && m_desc.persistentlyMapped) {
|
||||
bufferDesc.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
|
||||
}
|
||||
#endif
|
||||
if (!buffer.Create(bufferDesc)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -7,9 +7,7 @@
|
||||
// End of Source File Header
|
||||
|
||||
#include "FrameContext.h"
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
#include <Config.h>
|
||||
#endif
|
||||
|
||||
namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
VkResult FrameContext::Initialize(VkDevice device, VkCommandPool commandPool, Uint32 frameCount) {
|
||||
@@ -224,13 +222,11 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
VkImageMemoryBarrier presentBarrier{};
|
||||
presentBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
||||
presentBarrier.srcAccessMask = 0;
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// A layout transition writes the image too. Order it after the wire
|
||||
// render pass's storeOp, clears and copies instead of relying on the old
|
||||
// per-draw queue-idle path. This applies even when contents are discarded.
|
||||
if (MG_Config::DataArmIsRecord())
|
||||
presentBarrier.srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT;
|
||||
#endif
|
||||
presentBarrier.dstAccessMask = 0;
|
||||
presentBarrier.oldLayout = oldLayout;
|
||||
presentBarrier.newLayout = presentLayout;
|
||||
@@ -243,12 +239,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
presentBarrier.subresourceRange.baseArrayLayer = 0;
|
||||
presentBarrier.subresourceRange.layerCount = 1;
|
||||
vkCmdPipelineBarrier(commandBuffer,
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
MG_Config::DataArmIsRecord()
|
||||
? VK_PIPELINE_STAGE_ALL_COMMANDS_BIT : VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
|
||||
#else
|
||||
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
|
||||
#endif
|
||||
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0,
|
||||
nullptr, 0, nullptr, 1, &presentBarrier);
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
#include <MG_State/GLState/ProgramState/ProgramObject.h>
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
#include <MG_Pipe/PipeApply.h>
|
||||
#include <MG_State/GLState/ProgramState/ProgramArtifactsCodec.h>
|
||||
#include "../DirectVulkanResourceState.h"
|
||||
@@ -12,10 +11,8 @@
|
||||
// the archive (LinkArtifacts::storageBlocks), so the SPIR-V reflector is off the Magma wire
|
||||
// draw path entirely. A future edit that re-adds the include is re-adding a per-draw reflect,
|
||||
// because a MagmaProgramSource is constructed once per draw and can memoise nothing.
|
||||
#endif
|
||||
|
||||
namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// A borrowed view, valid only while its frontend object or server record lives.
|
||||
// It never constructs a ProgramObject on the server. Post-link mutable values
|
||||
// come from the record tails, not from the archive's link-time defaults.
|
||||
@@ -218,7 +215,4 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
// also why they never helped: a view lives for one draw, so the latch was cold every
|
||||
// time it was read.
|
||||
};
|
||||
#else
|
||||
using MagmaProgramSource = MG_State::GLState::ProgramObject;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -198,14 +198,12 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
XXHASH_VERIFY(XXH64_update(m_hashState, &payload.programHash, sizeof(payload.programHash)));
|
||||
XXHASH_VERIFY(XXH64_update(m_hashState, &payload.vertexInputHash, sizeof(payload.vertexInputHash)));
|
||||
XXHASH_VERIFY(XXH64_update(m_hashState, &payload.pipelineLayout, sizeof(payload.pipelineLayout)));
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (payload.wireRenderPassCompatibilityId != 0) {
|
||||
constexpr Uint64 wireDomain = 0x5749524552504b59ull;
|
||||
XXHASH_VERIFY(XXH64_update(m_hashState, &wireDomain, sizeof(wireDomain)));
|
||||
XXHASH_VERIFY(XXH64_update(m_hashState, &payload.wireRenderPassCompatibilityId,
|
||||
sizeof(payload.wireRenderPassCompatibilityId)));
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
XXHASH_VERIFY(XXH64_update(m_hashState, &payload.renderPass, sizeof(payload.renderPass)));
|
||||
}
|
||||
@@ -286,12 +284,10 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
m_cache.emplace(hash, PipelineCacheEntry{pipeline, payload.programHash,
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// Wire pipelines outlive their creator pass and
|
||||
// may serve another compatible pass in flight.
|
||||
// Never evict them by a recycled native handle.
|
||||
payload.wireRenderPassCompatibilityId != 0 ? VK_NULL_HANDLE :
|
||||
#endif
|
||||
payload.renderPass,
|
||||
m_frameCounter});
|
||||
return pipeline;
|
||||
|
||||
@@ -35,11 +35,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
HashType vertexInputHash = 0;
|
||||
VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
|
||||
VkRenderPass renderPass = VK_NULL_HANDLE;
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// Nonzero is an exact, renderer-lifetime compatibility identity.
|
||||
// renderPass remains the valid creation handle, not the cache key.
|
||||
Uint64 wireRenderPassCompatibilityId = 0;
|
||||
#endif
|
||||
Uint32 colorAttachmentCount = 1;
|
||||
VkSampleCountFlagBits rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
|
||||
// glEnable(GL_SAMPLE_SHADING) + glMinSampleShading, which Vulkan bakes into the
|
||||
|
||||
@@ -3199,11 +3199,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
|
||||
if (descriptorKind == DescriptorBindingKind::StorageBuffer) {
|
||||
const GLuint blockIndex =
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
program.GetShaderStorageBlockIndex(uniformName);
|
||||
#else
|
||||
GetShaderStorageBlockIndex(program, uniformName);
|
||||
#endif
|
||||
if (blockIndex == GL_INVALID_INDEX) {
|
||||
MGLOG_D("ProgramFactory::ReflectLayout: skipping inactive SSBO '%s' at binding %u",
|
||||
uniformName.c_str(), binding);
|
||||
@@ -3495,13 +3491,11 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
updateAfterBindSampledImages + updateAfterBindStorageImages;
|
||||
const auto& uab = m_updateAfterBindLimits;
|
||||
entry.usesUpdateAfterBind =
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// Vulkan forbids ANY update-after-bind binding in a set layout that
|
||||
// contains a dynamic buffer descriptor (VUID 03001/03011). Wire draws
|
||||
// retain UniformManager's per-frame, content-versioned descriptor sets;
|
||||
// this selects the legal ordinary pool, not in-place mutation of a live set.
|
||||
(!program.IsWire() || entry.dynamicBindings.empty()) &&
|
||||
#endif
|
||||
uab.enabled && updateAfterBindSamplers <= uab.maxPerStageSamplers &&
|
||||
updateAfterBindUniformBuffers <= uab.maxPerStageUniformBuffers &&
|
||||
updateAfterBindStorageBuffers <= uab.maxPerStageStorageBuffers &&
|
||||
|
||||
@@ -28,13 +28,11 @@
|
||||
#include "MG_Util/Metrics/TextureMetrics.h"
|
||||
#include "MG_Util/ShaderTranspiler/Types.h"
|
||||
#include <Config.h>
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
#include <MG_Pipe/PipeApply.h>
|
||||
// P7 wave 0: the seam WireDescriptorFatal dies through. See MG_Pipe/PipeSessionFail.h.
|
||||
#include <MG_Pipe/PipeSessionFail.h>
|
||||
// P7 wave 2 package B3: rule I's tally for this file's silent wire-draw exits.
|
||||
#include "WireDeclineTally.h"
|
||||
#endif
|
||||
#include <vulkan/utility/vk_format_utils.h>
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
@@ -43,7 +41,6 @@
|
||||
#include <limits>
|
||||
|
||||
namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
SizeT UniformManager::WireImageViewKeyHash::operator()(const WireImageViewKey& key) const {
|
||||
SizeT hash = std::hash<VkImage>{}(key.image);
|
||||
const auto mix = [&hash](Uint64 value) {
|
||||
@@ -323,7 +320,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
namespace {
|
||||
constexpr Uint kFallbackTexture2DExternalIndex = 0xFFFFFF00u;
|
||||
// One id for every storage-image placeholder. They are never reachable through GL - no
|
||||
@@ -590,7 +586,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
m_bufferManager = bufferManager;
|
||||
m_programFactory = programFactory;
|
||||
m_minDynamicOffsetAlignment = std::max<VkDeviceSize>(1, minUniformBufferOffsetAlignment);
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (MG_Config::DataArmIsRecord()) {
|
||||
VkPhysicalDeviceProperties properties{};
|
||||
vkGetPhysicalDeviceProperties(m_physicalDevice, &properties);
|
||||
@@ -600,7 +595,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
m_wireMaxStorageRange = properties.limits.maxStorageBufferRange;
|
||||
m_wireMaxTexelElements = properties.limits.maxTexelBufferElements;
|
||||
}
|
||||
#endif
|
||||
m_frameCount = frameCount;
|
||||
m_maxBindings = maxBindings;
|
||||
m_samplerResolveMemo.assign(m_maxBindings, SamplerResolveMemo{});
|
||||
@@ -656,11 +650,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
m_unboundStorageImageTextures.clear();
|
||||
for (auto& frame : m_frames) {
|
||||
if (m_device != VK_NULL_HANDLE) {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
frame.wireImageViewCache.clear();
|
||||
for (const auto view : frame.wireImageViews) vkDestroyImageView(m_device, view, nullptr);
|
||||
frame.wireImageViews.clear();
|
||||
#endif
|
||||
for (auto& view : frame.texelBufferViews) {
|
||||
if (view != VK_NULL_HANDLE) {
|
||||
vkDestroyBufferView(m_device, view, nullptr);
|
||||
@@ -683,12 +675,10 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
frame.quietEpochs = 0;
|
||||
}
|
||||
m_frames.clear();
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
for (auto& entry : m_wirePlaceholderImages) DestroyWirePlaceholderImage(entry.second);
|
||||
m_wirePlaceholderImages.clear();
|
||||
m_wirePrivateStoragePlaceholders = 0;
|
||||
m_wireInvalidStorageImagesBindNull = false;
|
||||
#endif
|
||||
m_epochSlot = 0;
|
||||
m_epochOpen = false;
|
||||
|
||||
@@ -712,12 +702,10 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
void UniformManager::BeginFrame(Uint32 frameIndex) {
|
||||
MOBILEGL_ASSERT(frameIndex < m_frames.size(), "UniformDescriptorBinder::BeginFrame invalid frame index");
|
||||
auto& frame = m_frames[frameIndex];
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
m_wireFrameIndex = frameIndex;
|
||||
frame.wireImageViewCache.clear();
|
||||
for (const auto view : frame.wireImageViews) vkDestroyImageView(m_device, view, nullptr);
|
||||
frame.wireImageViews.clear();
|
||||
#endif
|
||||
for (auto& view : frame.texelBufferViews) {
|
||||
if (view != VK_NULL_HANDLE) {
|
||||
vkDestroyBufferView(m_device, view, nullptr);
|
||||
@@ -766,7 +754,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
}
|
||||
}
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
Bool UniformManager::WireDescriptorSetBudgetReached(Uint32 frameIndex) const {
|
||||
MOBILEGL_ASSERT(frameIndex < m_frames.size(), "WireDescriptorSetBudgetReached invalid frame index");
|
||||
return m_frames[frameIndex].allocatedSetsThisFrame >= kWireDescriptorSetBudget;
|
||||
@@ -799,7 +786,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
}
|
||||
return cachedSets;
|
||||
}
|
||||
#endif
|
||||
|
||||
void UniformManager::InvalidateDescriptorSetMemos() {
|
||||
// Every VkDescriptorSet handle held outside the per-layout caches - in this backend they all
|
||||
@@ -907,9 +893,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
Uint32 binding, Uint32 element,
|
||||
VkDescriptorImageInfo& outImageInfo,
|
||||
Bool trustUnchangedHint) const {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (program.IsWire()) return ResolveWireImageDescriptor(commandBuffer, program, programObj, binding, element, false, outImageInfo);
|
||||
#endif
|
||||
MOBILEGL_ASSERT(m_textureManager != nullptr, "ResolveSamplerDescriptor: texture manager is null");
|
||||
MOBILEGL_ASSERT(m_samplerManager != nullptr, "ResolveSamplerDescriptor: sampler manager is null");
|
||||
// The whole-descriptor memo below is keyed by binding alone, so it describes a binding
|
||||
@@ -1192,9 +1176,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
|
||||
Bool UniformManager::ProgramSamplesOnlySingleLevelTextures(
|
||||
const MagmaProgramSource& program, const ProgramFactory::VkProgramObject& programObj) {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (program.IsWire()) return false; // Conservative optimization gate; no frontend probe.
|
||||
#endif
|
||||
// A declined program never draws (see VkProgramObject::declinedDescriptors), and its
|
||||
// declined binding has no resolvable uniform location - so there is nothing to prove
|
||||
// about the textures it would have sampled.
|
||||
@@ -1305,7 +1287,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
return texture;
|
||||
}
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
Bool UniformManager::ResolveWireTexelBufferDescriptor(const MagmaProgramSource& program,
|
||||
const ProgramFactory::VkProgramObject& programObj, Uint32 binding, Uint32 frameIndex,
|
||||
Bool storage, VkBufferView& out) {
|
||||
@@ -1407,16 +1388,13 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
m_bufferManager->MarkWireBufferGpuWritten(buffer, start, size);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
Bool UniformManager::ResolveTexelBufferDescriptor(const MagmaProgramSource& program,
|
||||
const ProgramFactory::VkProgramObject& programObj,
|
||||
Uint32 binding, Uint32 frameIndex,
|
||||
VkBufferView& outBufferView) {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (program.IsWire())
|
||||
return ResolveWireTexelBufferDescriptor(program, programObj, binding, frameIndex, false, outBufferView);
|
||||
#endif
|
||||
outBufferView = VK_NULL_HANDLE;
|
||||
MOBILEGL_ASSERT(m_bufferManager != nullptr, "ResolveTexelBufferDescriptor: buffer manager is null");
|
||||
MOBILEGL_ASSERT(frameIndex < m_frames.size(), "ResolveTexelBufferDescriptor: frame index out of range");
|
||||
@@ -1541,10 +1519,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
const ProgramFactory::VkProgramObject& programObj,
|
||||
Uint32 binding, Uint32 frameIndex,
|
||||
VkBufferView& outBufferView) {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (program.IsWire())
|
||||
return ResolveWireTexelBufferDescriptor(program, programObj, binding, frameIndex, true, outBufferView);
|
||||
#endif
|
||||
outBufferView = VK_NULL_HANDLE;
|
||||
MOBILEGL_ASSERT(m_bufferManager != nullptr, "ResolveStorageTexelBufferDescriptor: buffer manager is null");
|
||||
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs.IsLive(), "ResolveStorageTexelBufferDescriptor: GL context is null");
|
||||
@@ -1712,18 +1688,14 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs.IsLive(), "ResolveStorageBufferDescriptor: GL context is null");
|
||||
MOBILEGL_ASSERT(binding < programObj.storageBlockIndexByBinding.size(),
|
||||
"ResolveStorageBufferDescriptor: binding %u out of range", binding);
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (program.IsWire() && (binding >= programObj.storageBlockIndexByBinding.size() ||
|
||||
binding >= programObj.storageBlockNameByBinding.size()))
|
||||
WireDescriptorFatal("storage-buffer-reflection-binding");
|
||||
#endif
|
||||
|
||||
const Int blockIndex = programObj.storageBlockIndexByBinding[binding];
|
||||
MOBILEGL_ASSERT(blockIndex >= 0, "ResolveStorageBufferDescriptor: no SSBO block mapped to binding %u",
|
||||
binding);
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (program.IsWire() && blockIndex < 0) WireDescriptorFatal("storage-buffer-reflection-block");
|
||||
#endif
|
||||
// An atomic counter is not an SSBO the application ever declared: glslang lowers every
|
||||
// atomic_uint onto a synthesized gl_AtomicCounterBlock_<N> storage block, where N is the
|
||||
// GL ATOMIC-COUNTER binding. That block arrives here auto-mapped to an arbitrary
|
||||
@@ -1753,13 +1725,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
isAtomicCounterBlock
|
||||
? static_cast<GLuint>(atomicCounterBinding)
|
||||
:
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
(program.IsWire() ? program.GetShaderStorageBlockBinding(static_cast<GLuint>(blockIndex)) :
|
||||
GetShaderStorageBlockBinding(*program.Frontend(), static_cast<GLuint>(blockIndex))) + element;
|
||||
#else
|
||||
GetShaderStorageBlockBinding(program, static_cast<GLuint>(blockIndex)) + element;
|
||||
#endif
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (program.IsWire()) {
|
||||
const auto& state = MG_Pipe::MGPipeApplier();
|
||||
const Uint32 cls = isAtomicCounterBlock ? MG_Pipe::kMGPipeShaderBufferClassAtomicCounter
|
||||
@@ -1835,7 +1802,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
m_bufferManager->MarkWireBufferGpuWritten(range.Res, start, size);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
const Uint32 bindingPointCount =
|
||||
static_cast<Uint32>(MG_Pipe::gPipeInputs.GetBufferBindingPointCount(bufferTarget));
|
||||
MOBILEGL_ASSERT(frontendBinding < bindingPointCount,
|
||||
@@ -1914,9 +1880,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
const ProgramFactory::VkProgramObject& programObj,
|
||||
Uint32 binding, Uint32 element,
|
||||
VkDescriptorImageInfo& outImageInfo) const {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (program.IsWire()) return ResolveWireImageDescriptor(commandBuffer, program, programObj, binding, element, true, outImageInfo);
|
||||
#endif
|
||||
outImageInfo = {};
|
||||
MOBILEGL_ASSERT(m_textureManager != nullptr, "ResolveStorageImageDescriptor: texture manager is null");
|
||||
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs.IsLive(), "ResolveStorageImageDescriptor: GL context is null");
|
||||
@@ -2369,9 +2333,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
const ProgramFactory::VkProgramObject& programObj,
|
||||
Vector<MG_State::GLState::ITextureObject*>& outTextures,
|
||||
Vector<SampledBindingRecord>* outBindingRecords) {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (program.IsWire()) { outTextures.clear(); if (outBindingRecords) outBindingRecords->clear(); return true; }
|
||||
#endif
|
||||
outTextures.clear();
|
||||
if (outBindingRecords != nullptr) {
|
||||
outBindingRecords->clear();
|
||||
@@ -2418,9 +2380,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
Bool UniformManager::SampledBindingsUnchanged(const MagmaProgramSource& program,
|
||||
const ProgramFactory::VkProgramObject& programObj,
|
||||
const Vector<SampledBindingRecord>& previousRecords) const {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (program.IsWire()) return false;
|
||||
#endif
|
||||
// A declined program takes the full path every time and is refused there.
|
||||
if (programObj.declinedDescriptors) {
|
||||
return false;
|
||||
@@ -2464,9 +2424,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
const MagmaProgramSource& program,
|
||||
const ProgramFactory::VkProgramObject& programObj,
|
||||
Vector<MG_State::GLState::ITextureObject*>& outTextures) const {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (program.IsWire()) { outTextures.clear(); return true; }
|
||||
#endif
|
||||
outTextures.clear();
|
||||
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs.IsLive(),
|
||||
"CollectStorageImageTextures: GL context is null");
|
||||
@@ -2549,9 +2507,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
const MagmaProgramSource& program,
|
||||
const ProgramFactory::VkProgramObject& programObj,
|
||||
Vector<SamplerImageFeedbackBinding>& outBindings) const {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (program.IsWire()) { outBindings.clear(); return true; }
|
||||
#endif
|
||||
outBindings.clear();
|
||||
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs.IsLive(),
|
||||
"CollectSamplerImageFeedback: GL context is null");
|
||||
@@ -2627,7 +2583,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
return true;
|
||||
}
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
Bool UniformManager::ResolveWireUniformBufferPayload(const MagmaProgramSource& program,
|
||||
Uint32 blockIndex, Uint32 bindingPoint, UboBindResult& out) const {
|
||||
if (bindingPoint >= MG_Pipe::kMGPipeMaxBufferBindingPoints)
|
||||
@@ -2722,7 +2677,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
out.dynamicOffset = padded.offset;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
Bool UniformManager::ResolveUniformBufferPayload(const MagmaProgramSource& program,
|
||||
const ProgramFactory::VkProgramObject& programObj, Uint32 binding,
|
||||
@@ -2752,10 +2706,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
|
||||
MOBILEGL_ASSERT(binding < programObj.uniformBlockIndexByBinding.size(),
|
||||
"ResolveUniformBufferPayload: UBO mapping binding %u out of range", binding);
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (program.IsWire() && binding >= programObj.uniformBlockIndexByBinding.size())
|
||||
WireDescriptorFatal("uniform-buffer-reflection-binding");
|
||||
#endif
|
||||
Int blockIndex = programObj.uniformBlockIndexByBinding[binding];
|
||||
if (arrayElement > 0) {
|
||||
const auto arrayIt = programObj.arrayedUniformBlockIndicesByBinding.find(binding);
|
||||
@@ -2776,16 +2728,12 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
MOBILEGL_ASSERT(static_cast<Uint32>(blockIndex) < activeUniformBlockCount,
|
||||
"ResolveUniformBufferPayload: uniform block index %d out of range (count=%u)", blockIndex,
|
||||
activeUniformBlockCount);
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (program.IsWire() && (blockIndex < 0 || static_cast<Uint32>(blockIndex) >= activeUniformBlockCount))
|
||||
WireDescriptorFatal("uniform-buffer-reflection-block");
|
||||
#endif
|
||||
|
||||
const Uint32 frontendBinding = program.GetUniformBlockBinding(static_cast<Uint32>(blockIndex));
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (program.IsWire())
|
||||
return ResolveWireUniformBufferPayload(program, static_cast<Uint32>(blockIndex), frontendBinding, out);
|
||||
#endif
|
||||
const Uint32 uniformBindingPointCount =
|
||||
static_cast<Uint32>(MG_Pipe::gPipeInputs.GetBufferBindingPointCount(BufferTarget::Uniform));
|
||||
MOBILEGL_ASSERT(frontendBinding < uniformBindingPointCount,
|
||||
@@ -3059,9 +3007,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
Uint32& outDynamicOffset) {
|
||||
UboBindResult ubo{};
|
||||
const Bool hasPayload = ResolveUniformBufferPayload(program, programObj, binding, arrayElement, ubo);
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (program.IsWire() && !hasPayload) return false;
|
||||
#endif
|
||||
MOBILEGL_ASSERT(hasPayload && (ubo.directBindable || (ubo.payload != nullptr && ubo.payloadSize > 0)),
|
||||
"UniformDescriptorBinder::ResolveDynamicUboDescriptor failed: missing UBO payload on binding %u element %u",
|
||||
binding, arrayElement);
|
||||
@@ -3150,7 +3096,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
}
|
||||
}
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
Bool UniformManager::PrepareWireTextureResources(const MagmaProgramSource& program,
|
||||
const ProgramFactory::VkProgramObject& programObj) {
|
||||
if (!program.IsWire()) return true;
|
||||
@@ -3204,7 +3149,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
m_textureManager->FlushPendingUploads();
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
Bool UniformManager::BindProgramUniformBuffers(VkCommandBuffer commandBuffer,
|
||||
const MagmaProgramSource& program,
|
||||
@@ -3244,12 +3188,10 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
(samplerBindingOverrides == nullptr || samplerBindingOverrides->empty());
|
||||
if (cacheable && samplerDescriptorsUnchangedHint && m_fastRebindMemo.valid &&
|
||||
m_fastRebindMemo.frameIndex == frameIndex &&
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// A wire store destroyed since the memo was taken may have handed its handle
|
||||
// value to a later mint (see FastRebindMemo): "same VkBuffer" then names a
|
||||
// different store, so the memo is refused and the full walk re-records it.
|
||||
m_fastRebindMemo.wireStoreDestroyEpoch == m_bufferManager->GetWireStoreDestroyEpoch() &&
|
||||
#endif
|
||||
m_fastRebindMemo.programLifetimeId == program.GetLifetimeId() &&
|
||||
m_fastRebindMemo.programHash == programObj.hash) {
|
||||
VkBuffer uboBuffer = VK_NULL_HANDLE;
|
||||
@@ -3546,7 +3488,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
}
|
||||
};
|
||||
mixWords(&programObj.descriptorSetLayout, sizeof(programObj.descriptorSetLayout));
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P7 M2 round 2 (ID-P7-43): the buffer infos below name WIRE stores by VkBuffer
|
||||
// handle, and a wire store can be destroyed mid-frame (VkBufferManager::
|
||||
// DeferredWireRelease) with its handle value re-minted before this frame's memo
|
||||
@@ -3555,7 +3496,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
// store. NOT the slice epoch: that moves on every glBufferSubData and would make
|
||||
// the memo miss on every draw.
|
||||
mix64(m_bufferManager->GetWireStoreDestroyEpoch());
|
||||
#endif
|
||||
for (const auto& write : writes) {
|
||||
mix64((static_cast<Uint64>(write.dstBinding) << 40) ^
|
||||
(static_cast<Uint64>(write.descriptorType) << 8) ^
|
||||
@@ -3611,9 +3551,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
/*valid=*/true, frameIndex, program.GetLifetimeId(), programObj.hash,
|
||||
fastRebindUboBinding, bufferInfos[0].buffer,
|
||||
bufferInfos[0].range, descriptorSet,
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
m_bufferManager->GetWireStoreDestroyEpoch(),
|
||||
#endif
|
||||
};
|
||||
} else {
|
||||
m_fastRebindMemo.valid = false;
|
||||
|
||||
@@ -56,7 +56,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
void Shutdown();
|
||||
|
||||
void BeginFrame(Uint32 frameIndex);
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// A present-less wire frame may run arbitrarily many draws. Before its
|
||||
// descriptor cursor reaches this budget, the renderer retires the
|
||||
// submission that last used the sets and rewinds every layout cursor.
|
||||
@@ -74,7 +73,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
// store inside one pass reading zero, nor privacy past kWirePrivateStoragePlaceholderCap
|
||||
// placeholders (CONTRACT-P7 §12). Called after Initialize.
|
||||
void SetWireInvalidStorageImageArm(Bool deviceNullDescriptor);
|
||||
#endif
|
||||
// A command buffer (re)began recording: descriptor bindings recorded into
|
||||
// the previous buffer do not carry over, so drop the bind-dedup shadow.
|
||||
void OnCommandBufferBoundary() { m_lastBindValid = false; }
|
||||
@@ -123,13 +121,11 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
Vector<SamplerImageFeedbackBinding>& outBindings) const;
|
||||
static Bool SamplerOverlapsWritableImageSubresource(Int samplerBaseLevel, Int samplerMaxLevel,
|
||||
GLint imageLevel, GLenum imageAccess);
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// Resolve lazy texture uploads/promotions before the caller captures the
|
||||
// command buffer passed to BindProgramUniformBuffers. Preparation may
|
||||
// submit older work, but descriptor recording must never rotate it.
|
||||
Bool PrepareWireTextureResources(const MagmaProgramSource& program,
|
||||
const ProgramFactory::VkProgramObject& programObj);
|
||||
#endif
|
||||
// samplerDescriptorsUnchangedHint: the caller (SetupDraw fast path) proved that
|
||||
// every input of every combined-image-sampler resolution is unchanged since the
|
||||
// previous draw's resolve - same (texture, sampler) per binding, texture params
|
||||
@@ -215,7 +211,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
Uint32 cursor = 0;
|
||||
};
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// ResolveWireImageDescriptor emits no pNext chain. Key every view-create
|
||||
// value explicitly: hash collisions must never alias different windows,
|
||||
// formats or swizzles. The root handle and allocation epoch also prevent
|
||||
@@ -273,7 +268,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
const ProgramFactory::VkProgramObject& programObj, Uint32 binding, Bool storage,
|
||||
VkDescriptorImageInfo& out, VkFormat boundStorageFormat = VK_FORMAT_UNDEFINED, Uint32 unit = ~0u) const;
|
||||
void DestroyWirePlaceholderImage(WirePlaceholderImage& image) const;
|
||||
#endif
|
||||
|
||||
// descriptorSetCacheByLayout is the per-frame set cache: BeginFrame rewinds every cursor, so
|
||||
// a slot re-writes the sets it allocated in earlier frames instead of allocating new ones, and
|
||||
@@ -283,12 +277,10 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
Vector<DescriptorPoolBucket> descriptorPools;
|
||||
UnorderedMap<VkDescriptorSetLayout, DescriptorSetCacheEntry> descriptorSetCacheByLayout;
|
||||
Vector<VkBufferView> texelBufferViews;
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
mutable Vector<VkImageView> wireImageViews;
|
||||
// Own views in the vector above until this slot's fence/idle proof.
|
||||
// Cache hits preserve the handle, enabling descriptor-content reuse.
|
||||
mutable UnorderedMap<WireImageViewKey, VkImageView, WireImageViewKeyHash> wireImageViewCache;
|
||||
#endif
|
||||
Uint32 activeDescriptorPoolIndex = 0;
|
||||
Uint32 allocatedSetsThisFrame = 0;
|
||||
Uint32 peakAllocatedSetsThisFrame = 0;
|
||||
@@ -366,7 +358,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
const ProgramFactory::VkProgramObject& programObj, Uint32 binding,
|
||||
Uint32 element, VkDescriptorImageInfo& outImageInfo,
|
||||
Bool trustUnchangedHint = false) const;
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
Bool ResolveWireImageDescriptor(VkCommandBuffer commandBuffer, const MagmaProgramSource& program,
|
||||
const ProgramFactory::VkProgramObject& programObj, Uint32 binding,
|
||||
Uint32 element, Bool storage, VkDescriptorImageInfo& out) const;
|
||||
@@ -379,7 +370,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
VkDeviceSize m_wireMaxUniformRange = ~VkDeviceSize{0};
|
||||
VkDeviceSize m_wireMaxStorageRange = ~VkDeviceSize{0};
|
||||
Uint32 m_wireMaxTexelElements = ~Uint32{0};
|
||||
#endif
|
||||
Bool ResolveSamplerDescriptorOverride(const SamplerBindingOverride& samplerBindingOverride,
|
||||
VkDescriptorImageInfo& outImageInfo) const;
|
||||
Bool ResolveTexelBufferDescriptor(const MagmaProgramSource& program,
|
||||
@@ -415,10 +405,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
const void* payload = nullptr; // fallback UploadTransient path
|
||||
VkDeviceSize payloadSize = 0;
|
||||
};
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
Bool ResolveWireUniformBufferPayload(const MagmaProgramSource& program, Uint32 blockIndex,
|
||||
Uint32 bindingPoint, UboBindResult& out) const;
|
||||
#endif
|
||||
Bool ResolveUniformBufferPayload(const MagmaProgramSource& program,
|
||||
const ProgramFactory::VkProgramObject& programObj, Uint32 binding,
|
||||
Uint32 arrayElement, UboBindResult& out) const;
|
||||
@@ -563,9 +551,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
VkBuffer uboBuffer = VK_NULL_HANDLE;
|
||||
VkDeviceSize uboRange = 0;
|
||||
VkDescriptorSet set = VK_NULL_HANDLE;
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
Uint64 wireStoreDestroyEpoch = 0;
|
||||
#endif
|
||||
};
|
||||
FastRebindMemo m_fastRebindMemo;
|
||||
|
||||
|
||||
@@ -394,7 +394,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
return entry;
|
||||
}
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
Bool VertexInputStateFactory::BuildWireVertexInput(const MG_Pipe::MGPipeVertexElementsRecord& elements,
|
||||
const MG_Pipe::MGPipeApplierState& state, Uint32 activeMask, BackendVertexInputState& out) const {
|
||||
out = BackendVertexInputState{};
|
||||
@@ -532,7 +531,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
void VertexInputStateFactory::OnFrameBoundary() {
|
||||
++m_frameBoundaryCounter;
|
||||
@@ -802,7 +800,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
if (m_physicalDevice == VK_NULL_HANDLE || format == VK_FORMAT_UNDEFINED) {
|
||||
return false;
|
||||
}
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (MG_Config::DataArmIsRecord()) {
|
||||
const auto found = m_wireVertexFormatSupport.find(format);
|
||||
if (found != m_wireVertexFormatSupport.end()) return found->second;
|
||||
@@ -812,7 +809,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
m_wireVertexFormatSupport.emplace(format, supported);
|
||||
return supported;
|
||||
}
|
||||
#endif
|
||||
VkFormatProperties properties{};
|
||||
vkGetPhysicalDeviceFormatProperties(m_physicalDevice, format, &properties);
|
||||
return (properties.bufferFeatures & VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT) != 0;
|
||||
|
||||
@@ -10,9 +10,7 @@
|
||||
// MG_Pipe::MGPipeHandle for the P2 D12.5 memo table below. A header of constexpr constants,
|
||||
// so the pull build gains nothing from it.
|
||||
#include <MG_Pipe/MGPipeHandles.h>
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
#include <MG_Pipe/PipeApply.h>
|
||||
#endif
|
||||
|
||||
#include "Config.h"
|
||||
#include "MagmaPipeArms.h"
|
||||
@@ -122,13 +120,11 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
// normal types, and 4 (one packed word) for the 2_10_10_10 types and GL_BGRA. Returns 0 for
|
||||
// an unknown/unsupported type.
|
||||
static SizeT GetAttributeByteSize(DataType type, Int size, Bool isBgra);
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// A wire VAO is a layout record plus a separate per-attribute buffer window.
|
||||
// No frontend VAO identity, address or memo participates in this layout.
|
||||
Bool BuildWireVertexInput(const MG_Pipe::MGPipeVertexElementsRecord& elements,
|
||||
const MG_Pipe::MGPipeApplierState& state, Uint32 activeMask,
|
||||
BackendVertexInputState& out) const;
|
||||
#endif
|
||||
|
||||
private:
|
||||
static VkFormat ToVkVertexFormat(DataType type, Int size, Bool normalized, Bool isInteger, Bool isBgra = false,
|
||||
@@ -175,11 +171,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
|
||||
const VulkanRendererConfig& m_config;
|
||||
VkPhysicalDevice m_physicalDevice = VK_NULL_HANDLE;
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// Physical-device format capabilities never change during this factory's
|
||||
// lifetime. The wire path rebuilds layouts without the legacy VAO memo.
|
||||
mutable UnorderedMap<VkFormat, Bool> m_wireVertexFormatSupport;
|
||||
#endif
|
||||
// Values are heap-allocated: UnorderedMap is open-addressing, so INSERT
|
||||
// invalidates references to stored values - and so does ERASE, which shifts
|
||||
// the rest of the probe cluster into the hole and therefore moves entries
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#include "VulkanRenderer.h"
|
||||
|
||||
#include "MG_Util/Metrics/PipeStats.h"
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
#include "MG_Pipe/MGPipeCallbacks.h"
|
||||
#include "MG_Pipe/PipeApply.h"
|
||||
#include <MG_Backend/Record/StagedShadow.h>
|
||||
@@ -25,11 +24,9 @@
|
||||
#include <Config.h>
|
||||
#include <chrono>
|
||||
#include <cstdlib>
|
||||
#endif
|
||||
|
||||
namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
namespace {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P7 wave 2 package B3, INVESTIGATION PROBE (split-only, read once, not in
|
||||
// ConfigLoader's accepted-env table - package B's MGITEST_MAGMA_FORCE_SHADER_MIPMAP
|
||||
// shape). MOBILEGL_MAGMA_WIREBUF_PROBE=1 prints one line per streamed-buffer event so
|
||||
@@ -74,7 +71,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
// Counts only the writes the busy predicate wanted to ORDER, so the selector indexes
|
||||
// the streamed subdata -> draw pairs and nothing else.
|
||||
Uint64 g_orderedWireWriteCounter = 0;
|
||||
#endif
|
||||
constexpr VmaAllocationCreateFlags kResidentBufferAllocationFlags =
|
||||
VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT;
|
||||
constexpr SizeT kLiveResourcePruneThreshold = 256;
|
||||
@@ -181,7 +177,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
.ReadbackFromGpu = Ops_ReadbackFromGpu,
|
||||
};
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// The session's manager, or null once its renderer is gone - a client's records can still
|
||||
// arrive after its eglTerminate released it (or, surfaceless, before its first surface built
|
||||
// one). Such a record is declined with one line rather than taking the whole server down.
|
||||
@@ -237,10 +232,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
},
|
||||
#endif
|
||||
};
|
||||
#endif
|
||||
} // namespace
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
void VkBufferManager::RegisterWireResourceOps() {
|
||||
if (MG_Config::DataArmIsRecord()) {
|
||||
MG_Pipe::MGPipeSetResourceOps(&g_vulkanWireResourceOps);
|
||||
@@ -1208,7 +1201,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
m_deferredWireReleases.clear();
|
||||
m_deferredWireBytes = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
Bool VkBufferManager::Initialize(const VkBufferManagerInitInfo& initInfo) {
|
||||
Shutdown();
|
||||
@@ -1227,16 +1219,13 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
}
|
||||
g_activeBufferManager.Get() = this;
|
||||
MG_State::GLState::SetBufferBackendOps(&g_vulkanBufferBackendOps);
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
RegisterWireResourceOps();
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
void VkBufferManager::Shutdown() {
|
||||
if (g_activeBufferManager.Get() == this) {
|
||||
g_activeBufferManager.Get() = nullptr;
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// Under a transport the table is the PROCESS's and serves every session: it dispatches
|
||||
// to the calling session's own manager (WireManager), so one session's renderer going
|
||||
// away must not take it from the others - their buffer creates would be dropped from
|
||||
@@ -1245,7 +1234,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
MG_Pipe::MGPipeGetResourceOps() == &g_vulkanWireResourceOps) {
|
||||
MG_Pipe::MGPipeSetResourceOps(nullptr);
|
||||
}
|
||||
#endif
|
||||
if (MG_State::GLState::GetBufferBackendOps() == &g_vulkanBufferBackendOps) {
|
||||
MG_State::GLState::SetBufferBackendOps(nullptr);
|
||||
}
|
||||
@@ -1255,7 +1243,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
m_unboundTexelBuffer.Destroy();
|
||||
DestroyAllDeferredReleases();
|
||||
ReleaseAllLiveResources();
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// DestroyAllDeferredReleases above emptied the parked list; this destroys the stores the
|
||||
// records still hold, so nothing this arm minted outlives the count.
|
||||
if (!m_wireBuffers.empty()) ++m_wireStoreDestroyEpoch;
|
||||
@@ -1264,7 +1251,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
m_wireStoreCountPeak = 0;
|
||||
m_deferredWireBytesPeak = 0;
|
||||
m_wireDeferredSyncs = 0;
|
||||
#endif
|
||||
m_copyProvider = nullptr;
|
||||
m_initInfo = {};
|
||||
m_currentFrameIndex = 0;
|
||||
@@ -1293,13 +1279,11 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
"VkBufferManager::BeginFrame frame index out of range");
|
||||
m_currentFrameIndex = frameIndex;
|
||||
++m_frameSerial;
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (WireBufProbeEnabled()) {
|
||||
MGLOG_I("WBUF BEGINFRAME idx=%u newSerial=%llu floor=%llu (arena slot rewound)", frameIndex,
|
||||
static_cast<unsigned long long>(m_frameSerial),
|
||||
static_cast<unsigned long long>(m_completedSerialFloor));
|
||||
}
|
||||
#endif
|
||||
CollectDeferredReleases(frameIndex);
|
||||
m_transientUploadArena.BeginFrame(frameIndex);
|
||||
}
|
||||
@@ -1399,9 +1383,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
.frameCount = m_initInfo.frameCount,
|
||||
.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT |
|
||||
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT |
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
(MG_Config::DataArmIsRecord() ? VkBufferUsageFlags{VK_BUFFER_USAGE_TRANSFER_DST_BIT} : 0u) |
|
||||
#endif
|
||||
VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
|
||||
.memoryUsage = m_initInfo.transientMemoryUsage,
|
||||
.allocationFlags = m_initInfo.transientAllocationFlags,
|
||||
@@ -1557,7 +1539,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
return true;
|
||||
}
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
Bool VkBufferManager::StagedWireRangeCopy(WireBufferResource& resource, const void* data,
|
||||
SizeT offset, SizeT size) {
|
||||
if (!m_copyProvider) {
|
||||
@@ -1610,7 +1591,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void VkBufferManager::OnRespecify(MG_State::GLState::BufferObject& bufferObject) {
|
||||
auto* resource = ResourceOf(bufferObject);
|
||||
@@ -1987,12 +1967,10 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
"VkBufferManager::CollectDeferredReleases frame index out of range");
|
||||
m_deferredBufferReleases[frameIndex].clear();
|
||||
m_deferredResourceReleases[frameIndex].clear();
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// The wire list is not per-slot and does not wait for a frame boundary; a boundary is
|
||||
// just one more point to sweep at (BeginFrame after its slot fence, and every slot of
|
||||
// the idle drain, where the renderer-idle rule empties the list).
|
||||
SweepDeferredWireReleases();
|
||||
#endif
|
||||
}
|
||||
|
||||
BufferSlice VkBufferManager::AcquireUnboundStorageDescriptor() {
|
||||
@@ -2088,10 +2066,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
}
|
||||
|
||||
void VkBufferManager::DestroyAllDeferredReleases() {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// Both callers (Shutdown, RecreateTransientArenas) have proven the device idle.
|
||||
DestroyAllDeferredWireReleases();
|
||||
#endif
|
||||
for (auto& releases : m_deferredBufferReleases) {
|
||||
for (auto& buffer : releases) {
|
||||
buffer.Destroy();
|
||||
|
||||
@@ -13,10 +13,8 @@
|
||||
#include "../VkIncludes.h"
|
||||
#include <Includes.h>
|
||||
#include <vk_mem_alloc.h>
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
#include "MG_Pipe/MGPipeTypes.h"
|
||||
#include <unordered_map>
|
||||
#endif
|
||||
|
||||
namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
enum class BufferKind : Uint8 {
|
||||
@@ -103,7 +101,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
Bool Initialize(const VkBufferManagerInitInfo& initInfo);
|
||||
void Shutdown();
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// Registered before caps publication; the initialized renderer owns the storage.
|
||||
static void RegisterWireResourceOps();
|
||||
// Transport resources are owned by their complete wire handle, never by a
|
||||
@@ -158,7 +155,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
void* DonateWireBuffer(MG_Pipe::MGPipeHandle res, Uint64 size, const void* seedBytes);
|
||||
// The POST self-test of the sustained-lock pattern on this device (see the definition).
|
||||
static Bool SelfTestWireImport(char* why, Uint64 whyBytes);
|
||||
#endif
|
||||
|
||||
// Recreate all per-frame transient arenas
|
||||
Bool RecreateTransientArenas(Uint32 frameCount);
|
||||
@@ -232,13 +228,11 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
// back, and none was persistently mapped, in between - so a memo of resolved
|
||||
// slices needs no per-buffer re-check. See AcquirePersistentMap for the mapping half.
|
||||
Uint64 GetSliceEpochCounter() const { return m_sliceEpochCounter; }
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// Bumped every time this manager destroys a WIRE store's VkBuffer (see
|
||||
// m_wireStoreDestroyEpoch). Unchanged since a memo was taken means no VkBuffer handle
|
||||
// that memo names can have been freed and re-minted in between, which is the one
|
||||
// fact a handle-keyed memo of wire descriptors needs and cannot read off the handle.
|
||||
Uint64 GetWireStoreDestroyEpoch() const { return m_wireStoreDestroyEpoch; }
|
||||
#endif
|
||||
// Highest frame serial whose GPU work is known complete; serials at or
|
||||
// below it may be considered signaled. Drives IsResourceBusy and the
|
||||
// backend GL fence objects.
|
||||
@@ -254,7 +248,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
void DeferRelease(VkBufferObject&& buffer);
|
||||
|
||||
private:
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
struct WireBufferResource {
|
||||
VkBufferObject buffer;
|
||||
Uint64 size = 0;
|
||||
@@ -374,7 +367,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
// the stats channel is on; MagmaWireReclaimScenario reads them off the server's line.
|
||||
void NoteWireStorePeaks();
|
||||
void PublishWireReclaimGauges();
|
||||
#endif
|
||||
Bool InitializeTransientArenas();
|
||||
static VkBufferUsageFlags GetVkBufferUsage(BufferKind kind);
|
||||
VkBufferResource* GetOrCreateResource(const SharedPtr<MG_State::GLState::BufferObject>& bufferObject);
|
||||
@@ -387,9 +379,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
// in-flight and already-recorded GPU work.
|
||||
Bool StagedRangeCopy(VkBufferResource& resource, const void* data,
|
||||
SizeT offset, SizeT size);
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
Bool StagedWireRangeCopy(WireBufferResource& resource, const void* data, SizeT offset, SizeT size);
|
||||
#endif
|
||||
void CollectDeferredReleases(Uint32 frameIndex);
|
||||
void DestroyAllDeferredReleases();
|
||||
void TrackLiveResource(const SharedPtr<VkBufferResource>& resource);
|
||||
@@ -408,7 +398,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
Vector<Vector<VkBufferObject>> m_deferredBufferReleases;
|
||||
Vector<Vector<SharedPtr<VkBufferResource>>> m_deferredResourceReleases;
|
||||
Vector<WeakPtr<VkBufferResource>> m_liveResources;
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
std::unordered_map<Uint64, WireBufferResource> m_wireBuffers;
|
||||
// See DeferredWireRelease. ONE FLAT LIST rather than the per-frame-slot buckets above:
|
||||
// the whole point is that these entries do not wait for a frame slot to come round.
|
||||
@@ -427,7 +416,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
// for m_sliceEpochCounter's reason: a memo taken before a re-initialize must not match
|
||||
// a handle minted after it.
|
||||
Uint64 m_wireStoreDestroyEpoch = 0;
|
||||
#endif
|
||||
// Size m_liveResources had just after the last sweep; the next sweep waits for it to double.
|
||||
SizeT m_liveResourcesLastPruned = 0;
|
||||
Uint32 m_currentFrameIndex = 0;
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
#endif
|
||||
|
||||
namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
void VkBufferObject::MoveExternalFrom(VkBufferObject& other) {
|
||||
m_device = other.m_device;
|
||||
m_externalMemory = other.m_externalMemory;
|
||||
@@ -36,7 +35,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
m_externalCoherent = coherent;
|
||||
m_externalAhb = ahb;
|
||||
}
|
||||
#endif
|
||||
|
||||
VkBufferObject::VkBufferObject(VkBufferObject&& other) noexcept {
|
||||
m_allocator = other.m_allocator;
|
||||
@@ -44,9 +42,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
m_allocation = other.m_allocation;
|
||||
m_mappedData = other.m_mappedData;
|
||||
m_size = other.m_size;
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
MoveExternalFrom(other);
|
||||
#endif
|
||||
|
||||
other.m_allocator = nullptr;
|
||||
other.m_buffer = VK_NULL_HANDLE;
|
||||
@@ -67,9 +63,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
m_allocation = other.m_allocation;
|
||||
m_mappedData = other.m_mappedData;
|
||||
m_size = other.m_size;
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
MoveExternalFrom(other);
|
||||
#endif
|
||||
|
||||
other.m_allocator = nullptr;
|
||||
other.m_buffer = VK_NULL_HANDLE;
|
||||
@@ -170,10 +164,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
}
|
||||
|
||||
void VkBufferObject::Unmap() {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// An imported store stays mapped for its life (Destroy unmaps it).
|
||||
if (m_externalMemory != VK_NULL_HANDLE) return;
|
||||
#endif
|
||||
if (!IsValid() || m_mappedData == nullptr) {
|
||||
m_mappedData = nullptr;
|
||||
return;
|
||||
@@ -200,7 +192,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
}
|
||||
|
||||
Memcpy(static_cast<Uint8*>(mapped) + offset, data, static_cast<SizeT>(size));
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (m_externalMemory != VK_NULL_HANDLE) {
|
||||
if (m_externalCoherent) return true;
|
||||
VkMappedMemoryRange range{VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE};
|
||||
@@ -209,7 +200,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
range.size = VK_WHOLE_SIZE;
|
||||
return vkFlushMappedMemoryRanges(m_device, 1, &range) == VK_SUCCESS;
|
||||
}
|
||||
#endif
|
||||
const VkResult flushResult = vmaFlushAllocation(m_allocator, m_allocation, offset, size);
|
||||
if (flushResult != VK_SUCCESS) {
|
||||
MGLOG_E_ONCE("VkBufferObject::Upload failed: vmaFlushAllocation returned %d", flushResult);
|
||||
@@ -234,7 +224,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
if (resolvedSize == 0) {
|
||||
return true;
|
||||
}
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (m_externalMemory != VK_NULL_HANDLE) {
|
||||
if (m_externalCoherent) return true;
|
||||
VkMappedMemoryRange range{VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE};
|
||||
@@ -243,7 +232,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
range.size = VK_WHOLE_SIZE;
|
||||
return vkInvalidateMappedMemoryRanges(m_device, 1, &range) == VK_SUCCESS;
|
||||
}
|
||||
#endif
|
||||
|
||||
const VkResult result = vmaInvalidateAllocation(m_allocator, m_allocation, offset, resolvedSize);
|
||||
if (result != VK_SUCCESS) {
|
||||
|
||||
@@ -65,13 +65,10 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
void* GetMappedData() const { return m_mappedData; }
|
||||
Bool IsMapped() const { return m_mappedData != nullptr; }
|
||||
Bool IsValid() const {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (m_externalMemory != VK_NULL_HANDLE) return m_buffer != VK_NULL_HANDLE;
|
||||
#endif
|
||||
return m_allocator != nullptr && m_buffer != VK_NULL_HANDLE && m_allocation != nullptr;
|
||||
}
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P11 B2 (T0): a store whose memory is NOT this allocator's - a client's AHardwareBuffer
|
||||
// imported with VK_ANDROID_external_memory_android_hardware_buffer into `memory` (a
|
||||
// dedicated allocation bound to `buffer`, mapped persistently at `mapped`). It behaves
|
||||
@@ -82,7 +79,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
void AdoptExternal(VkDevice device, VkBuffer buffer, VkDeviceMemory memory, VkDeviceSize size,
|
||||
void* mapped, Bool coherent, void* ahb);
|
||||
Bool IsExternal() const { return m_externalMemory != VK_NULL_HANDLE; }
|
||||
#endif
|
||||
|
||||
private:
|
||||
VmaAllocator m_allocator = nullptr;
|
||||
@@ -90,12 +86,10 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
VmaAllocation m_allocation = nullptr;
|
||||
void* m_mappedData = nullptr;
|
||||
VkDeviceSize m_size = 0;
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
VkDevice m_device = VK_NULL_HANDLE;
|
||||
VkDeviceMemory m_externalMemory = VK_NULL_HANDLE;
|
||||
void* m_externalAhb = nullptr;
|
||||
Bool m_externalCoherent = true;
|
||||
void MoveExternalFrom(VkBufferObject& other);
|
||||
#endif
|
||||
};
|
||||
} // namespace MobileGL::MG_Backend::DirectVulkan
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
|
||||
namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
namespace {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// An accessor view over the wire's POD values, not a GL object with constructor,
|
||||
// lifetime ID, allocator hooks or mutation callbacks. Both sources below use
|
||||
// one sampler policy, including format-sensitive border-color conversion.
|
||||
@@ -43,9 +42,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
|
||||
template <class SamplerSource>
|
||||
Bool UsesBorderColor(const SamplerSource& sampler) {
|
||||
#else
|
||||
Bool UsesBorderColor(const MG_State::GLState::SamplerObject& sampler) {
|
||||
#endif
|
||||
return sampler.GetWrapS() == SamplerWrapMode::ClampToBorder ||
|
||||
sampler.GetWrapT() == SamplerWrapMode::ClampToBorder ||
|
||||
sampler.GetWrapR() == SamplerWrapMode::ClampToBorder;
|
||||
@@ -221,24 +217,16 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
return std::fabs(lhs - rhs) <= 1e-6f;
|
||||
}
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
template <class SamplerSource>
|
||||
Float ResolveEffectiveMaxLod(const SamplerSource& sampler) {
|
||||
#else
|
||||
Float ResolveEffectiveMaxLod(const MG_State::GLState::SamplerObject& sampler) {
|
||||
#endif
|
||||
if (sampler.GetMipmapMode() == SamplerMipmapMode::None) {
|
||||
return 0.0f;
|
||||
}
|
||||
return sampler.GetMaxLod();
|
||||
}
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
template <class SamplerSource>
|
||||
Float ResolveEffectiveMinLod(const SamplerSource& sampler, Float effectiveMaxLod) {
|
||||
#else
|
||||
Float ResolveEffectiveMinLod(const MG_State::GLState::SamplerObject& sampler, Float effectiveMaxLod) {
|
||||
#endif
|
||||
return std::min(sampler.GetMinLod(), effectiveMaxLod);
|
||||
}
|
||||
|
||||
@@ -249,12 +237,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
// GL's non-mipmapped minification - large enough for lambda to stay positive, small enough
|
||||
// that a NEAREST mip mode still rounds down to level 0. Clamped rather than assigned, so a
|
||||
// texture whose GL_TEXTURE_MAX_LOD really is 0 keeps magnifying as GL says it must.
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
template <class SamplerSource>
|
||||
Float ResolveSingleLevelMaxLod(const SamplerSource& sampler, Bool singleLevelView) {
|
||||
#else
|
||||
Float ResolveSingleLevelMaxLod(const MG_State::GLState::SamplerObject& sampler, Bool singleLevelView) {
|
||||
#endif
|
||||
const Float maxLod = ResolveEffectiveMaxLod(sampler);
|
||||
return singleLevelView ? std::min(maxLod, 0.25f) : maxLod;
|
||||
}
|
||||
@@ -275,12 +259,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
return true;
|
||||
}
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
template <class SamplerSource>
|
||||
Float VkSamplerManager::ResolveEffectiveMaxAnisotropy(const SamplerSource& sampler,
|
||||
#else
|
||||
Float VkSamplerManager::ResolveEffectiveMaxAnisotropy(const MG_State::GLState::SamplerObject& sampler,
|
||||
#endif
|
||||
Bool forceNearestFiltering) const {
|
||||
if (!m_samplerAnisotropySupported) return 1.0f;
|
||||
if (forceNearestFiltering) return 1.0f;
|
||||
@@ -339,12 +319,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
}
|
||||
}
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
template <class SamplerSource>
|
||||
Uint64 VkSamplerManager::BuildSamplerKey(const SamplerSource& sampler,
|
||||
#else
|
||||
Uint64 VkSamplerManager::BuildSamplerKey(const MG_State::GLState::SamplerObject& sampler,
|
||||
#endif
|
||||
Bool forceNearestFiltering, Bool singleLevelView,
|
||||
const ResolvedBorderColor& borderColor) const {
|
||||
MOBILEGL_ASSERT(m_config != nullptr, "VkSamplerManager::BuildSamplerKey: m_config is null");
|
||||
@@ -390,14 +366,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
return XXH64_digest(m_hashState);
|
||||
}
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
template <class SamplerSource>
|
||||
VkSampler VkSamplerManager::GetOrCreateSamplerImpl(const SamplerSource& sampler,
|
||||
TextureInternalFormat format,
|
||||
#else
|
||||
VkSampler VkSamplerManager::GetOrCreateSampler(const MG_State::GLState::SamplerObject& sampler,
|
||||
const MG_State::GLState::ITextureObject& texture,
|
||||
#endif
|
||||
Bool forceNearestFiltering, Uint32 viewLevelCount) {
|
||||
// A view that exposes a single mip level has no second level to blend with, so GL's
|
||||
// *_MIPMAP_* minification filters degenerate to plain filtering on the base level -
|
||||
@@ -409,11 +380,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
// the default-framebuffer blit shader had to work around with an explicit-LOD sample.
|
||||
const Bool singleLevelView = viewLevelCount == 1;
|
||||
// Resolved once and used for both the key and the create-info; see ResolvedBorderColor.
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
const ResolvedBorderColor borderColor = ResolveBorderColor(sampler, format);
|
||||
#else
|
||||
const ResolvedBorderColor borderColor = ResolveBorderColor(sampler, texture);
|
||||
#endif
|
||||
const Uint64 key = BuildSamplerKey(sampler, forceNearestFiltering, singleLevelView, borderColor);
|
||||
auto it = m_samplers.find(key);
|
||||
if (it != m_samplers.end()) {
|
||||
@@ -473,7 +440,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
return vkSampler;
|
||||
}
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
VkSampler VkSamplerManager::GetOrCreateSampler(const MG_State::GLState::SamplerObject& sampler,
|
||||
const MG_State::GLState::ITextureObject& texture,
|
||||
Bool forceNearestFiltering, Uint32 viewLevelCount) {
|
||||
@@ -485,7 +451,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
return GetOrCreateSamplerImpl(SamplerParametersSource{parameters}, format,
|
||||
forceNearestFiltering, viewLevelCount);
|
||||
}
|
||||
#endif
|
||||
|
||||
VkFilter VkSamplerManager::ToVkFilter(SamplerFilterMode mode) {
|
||||
return mode == SamplerFilterMode::Nearest ? VK_FILTER_NEAREST : VK_FILTER_LINEAR;
|
||||
@@ -542,14 +507,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
}
|
||||
}
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
template <class SamplerSource>
|
||||
VkSamplerManager::ResolvedBorderColor VkSamplerManager::ResolveBorderColor(
|
||||
const SamplerSource& sampler, TextureInternalFormat format) const {
|
||||
#else
|
||||
VkSamplerManager::ResolvedBorderColor VkSamplerManager::ResolveBorderColor(
|
||||
const MG_State::GLState::SamplerObject& sampler, const MG_State::GLState::ITextureObject& texture) const {
|
||||
#endif
|
||||
ResolvedBorderColor resolved{};
|
||||
if (!UsesBorderColor(sampler)) {
|
||||
return resolved; // FLOAT_TRANSPARENT_BLACK, never sampled
|
||||
@@ -557,9 +517,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
|
||||
// Border colour 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.
|
||||
#if !MOBILEGL_BUILD_RECORD_ARM
|
||||
const auto format = texture.GetFormat();
|
||||
#endif
|
||||
const auto domain = ResolveBorderColorDomain(format);
|
||||
const Bool canUseCustom = m_customBorderColorSupported && m_maxCustomBorderColorSamplers > 0 &&
|
||||
m_customBorderColorSamplerCount < m_maxCustomBorderColorSamplers;
|
||||
|
||||
@@ -46,12 +46,10 @@ public:
|
||||
const MG_State::GLState::ITextureObject& texture,
|
||||
Bool forceNearestFiltering = false,
|
||||
Uint32 viewLevelCount = 0);
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// The applier owns these values: effective bound/builtin sampler parameters and
|
||||
// the sampled texture view's format/range. No frontend object is constructed.
|
||||
VkSampler GetOrCreateSamplerFromParameters(const SamplerParameters& parameters,
|
||||
TextureInternalFormat format, Bool forceNearestFiltering = false, Uint32 viewLevelCount = 0);
|
||||
#endif
|
||||
// Frame boundary hook: ages the sampler cache and destroys samplers not used
|
||||
// for many frames. The key hashes continuous float state (lodBias, LOD clamps,
|
||||
// anisotropy), so an app animating those would otherwise mint an unbounded
|
||||
@@ -92,37 +90,24 @@ private:
|
||||
Bool usesCustomBorderColor = false;
|
||||
};
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
template <class SamplerSource>
|
||||
VkSampler GetOrCreateSamplerImpl(const SamplerSource& sampler, TextureInternalFormat format,
|
||||
Bool forceNearestFiltering, Uint32 viewLevelCount);
|
||||
template <class SamplerSource>
|
||||
Uint64 BuildSamplerKey(const SamplerSource& sampler, Bool forceNearestFiltering,
|
||||
#else
|
||||
Uint64 BuildSamplerKey(const MG_State::GLState::SamplerObject& sampler, Bool forceNearestFiltering,
|
||||
#endif
|
||||
Bool singleLevelView, const ResolvedBorderColor& borderColor) const;
|
||||
static VkFilter ToVkFilter(SamplerFilterMode mode);
|
||||
static VkSamplerMipmapMode ToVkMipmapMode(SamplerMipmapMode mode);
|
||||
static VkSamplerAddressMode ToVkAddressMode(SamplerWrapMode mode);
|
||||
static VkCompareOp ToVkCompareOp(SamplerCompareFunc func);
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
template <class SamplerSource>
|
||||
ResolvedBorderColor ResolveBorderColor(const SamplerSource& sampler, TextureInternalFormat format) const;
|
||||
#else
|
||||
ResolvedBorderColor ResolveBorderColor(const MG_State::GLState::SamplerObject& sampler,
|
||||
const MG_State::GLState::ITextureObject& texture) const;
|
||||
#endif
|
||||
// The anisotropy Vulkan will actually apply: 1.0 (i.e. disabled) unless the feature is on and
|
||||
// the sampler filters linearly both ways, otherwise the GL request clamped to the device limit.
|
||||
// GL happily carries GL_TEXTURE_MAX_ANISOTROPY on a NEAREST sampler (Blaze3D's blocks do exactly
|
||||
// that) while Vulkan forbids anisotropyEnable there, so the GL value must never be forwarded raw.
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
template <class SamplerSource>
|
||||
Float ResolveEffectiveMaxAnisotropy(const SamplerSource& sampler,
|
||||
#else
|
||||
Float ResolveEffectiveMaxAnisotropy(const MG_State::GLState::SamplerObject& sampler,
|
||||
#endif
|
||||
Bool forceNearestFiltering) const;
|
||||
|
||||
VkDevice m_device = VK_NULL_HANDLE;
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
#include "MG_Util/Metrics/PipeStats.h"
|
||||
|
||||
#include <Config.h>
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P5f (fm): the handle-keyed texture arm's two sources - the applier's resource records
|
||||
// (shape) and the server's staged-texture store (texels).
|
||||
#include <MG_Pipe/PipeApply.h>
|
||||
@@ -26,7 +25,6 @@
|
||||
#include "../DirectVulkan.h"
|
||||
// P7 wave 2 package B3: rule I's tally for the silent exits on this file's wire arm.
|
||||
#include "WireDeclineTally.h"
|
||||
#endif
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
// Shared images: a texture whose level 0 is a server-allocated AHardwareBuffer.
|
||||
#include <MG_Remote/Server/SharedImageRegistry.h>
|
||||
@@ -319,9 +317,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
VkQueue graphicsQueue,
|
||||
const VkTextureManager::TextureResource& oldResource,
|
||||
VkTextureManager::TextureResource& newResource
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
, Bool splitAspects = false
|
||||
#endif
|
||||
) {
|
||||
MOBILEGL_ASSERT(device != VK_NULL_HANDLE, "PreserveTextureContentsOnRecreate: device is null");
|
||||
MOBILEGL_ASSERT(commandPool != VK_NULL_HANDLE, "PreserveTextureContentsOnRecreate: commandPool is null");
|
||||
@@ -380,7 +376,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
copy.extent.width = std::max(oldResource.extent.width >> level, 1u);
|
||||
copy.extent.height = std::max(oldResource.extent.height >> level, 1u);
|
||||
copy.extent.depth = std::max(oldResource.depth >> level, 1u);
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (splitAspects) {
|
||||
for (const VkImageAspectFlags aspect : {VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
VK_IMAGE_ASPECT_DEPTH_BIT,
|
||||
@@ -391,7 +386,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
copyRegions.push_back(copy);
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
copyRegions.push_back(copy);
|
||||
}
|
||||
|
||||
@@ -723,12 +717,10 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
DestroyDeferredReleases();
|
||||
++m_resourceEraseEpoch; // every memoized resource pointer dies with the map
|
||||
m_textureResources.clear();
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// Destroy images while the device/allocator still exist, including after context death.
|
||||
m_wireTextureResources.clear();
|
||||
m_wireRenderbufferResources.clear();
|
||||
m_sharedImageUses.clear();
|
||||
#endif
|
||||
m_aliveObjects.clear();
|
||||
m_storageImageTextures.clear();
|
||||
|
||||
@@ -1846,7 +1838,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
EraseTrackedTexture(identity);
|
||||
}
|
||||
prunedCount += orphanIdentities.size();
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
const auto pruneWire = [&](auto& resources, const auto& records) {
|
||||
for (auto it = resources.begin(); it != resources.end();) {
|
||||
const Uint32 slot = static_cast<Uint32>(it->first >> 32) & 0x7fffffffu;
|
||||
@@ -1864,11 +1855,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
const auto& applier = MG_Pipe::MGPipeApplier();
|
||||
pruneWire(m_wireTextureResources, applier.TextureResources);
|
||||
pruneWire(m_wireRenderbufferResources, applier.RenderbufferResources);
|
||||
#endif
|
||||
return prunedCount;
|
||||
}
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// ---------------------------------------------------------------------------------
|
||||
// P5f (fm): THE HANDLE-KEYED TEXTURE ARM
|
||||
// ---------------------------------------------------------------------------------
|
||||
@@ -3275,7 +3264,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
std::construct_at(&resource, Move(grown));
|
||||
return true;
|
||||
}
|
||||
#endif // MOBILEGL_BUILD_RECORD_ARM
|
||||
|
||||
Bool VkTextureManager::SyncTexture(MG_State::GLState::ITextureObject &texture,
|
||||
TextureResource &outResource) {
|
||||
@@ -3832,9 +3820,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
resource.perMipViews.empty() && resource.perMipSampledViews.empty() &&
|
||||
resource.attachmentViews.empty() && resource.alternateSampledViews.empty() &&
|
||||
resource.storageImageViews.empty()
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
&& resource.yuvSource == nullptr
|
||||
#endif
|
||||
) {
|
||||
return;
|
||||
}
|
||||
@@ -4006,7 +3992,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
return current->mapped + alignedCursor;
|
||||
}
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
Bool VkTextureManager::WireUploadsAreIdle() {
|
||||
// An open batch still owns references even before it has a fence. Poll
|
||||
// submitted batches without waiting; reclamation is deferred if any lives.
|
||||
@@ -4014,7 +3999,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
ReclaimCompletedUploads();
|
||||
return m_pendingUploadReclaims.empty();
|
||||
}
|
||||
#endif
|
||||
|
||||
void VkTextureManager::FlushPendingUploads() {
|
||||
if (!m_uploadBatchOpen) {
|
||||
|
||||
@@ -19,20 +19,16 @@
|
||||
#include <unordered_set>
|
||||
|
||||
#include <Config.h>
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// The handle-keyed arm's key type. Header-only POD, disagg-only, exactly the shape
|
||||
// MagmaPipeArms.h already gives this directory.
|
||||
#include <MG_Pipe/MGPipeHandles.h>
|
||||
#endif
|
||||
|
||||
namespace MobileGL::MG_State::GLState {
|
||||
class ITextureObject;
|
||||
}
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
namespace MobileGL::MG_Pipe {
|
||||
struct MGPipeResourceRecord;
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
enum class SamplerNumericDomain : Uint8;
|
||||
@@ -355,7 +351,6 @@ public:
|
||||
// Serial at the last successful sync. 0 also means "this resource has only ever been
|
||||
// synced by the frontend arm" - a wire record's Serial starts at 0 on a create and moves
|
||||
// on every mutation, so the first handle sync of a fresh image always re-checks.
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
Uint64 syncedWireSerial = 0;
|
||||
// SHARED IMAGES: level 0 is a server shared image (an imported AHardwareBuffer), not a
|
||||
// VMA allocation. The resource owns the image and its dedicated imported memory, and
|
||||
@@ -381,7 +376,6 @@ public:
|
||||
SharedPtr<const void> yuvOwner;
|
||||
SharedPtr<void> yuvSource;
|
||||
Uint64 yuvConvertedFrame = 0;
|
||||
#endif
|
||||
|
||||
TextureResource() = default;
|
||||
TextureResource(const TextureResource&) = delete;
|
||||
@@ -415,7 +409,6 @@ public:
|
||||
std::swap(this->syncedContentVersion, that.syncedContentVersion);
|
||||
std::swap(this->syncedMipLevelCount, that.syncedMipLevelCount);
|
||||
std::swap(this->syncedShapeVersion, that.syncedShapeVersion);
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
std::swap(this->syncedWireSerial, that.syncedWireSerial);
|
||||
std::swap(this->importedMemory, that.importedMemory);
|
||||
std::swap(this->sharedImageId, that.sharedImageId);
|
||||
@@ -428,7 +421,6 @@ public:
|
||||
std::swap(this->yuvOwner, that.yuvOwner);
|
||||
std::swap(this->yuvSource, that.yuvSource);
|
||||
std::swap(this->yuvConvertedFrame, that.yuvConvertedFrame);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Reset() {
|
||||
@@ -466,7 +458,6 @@ public:
|
||||
if (image != VK_NULL_HANDLE && allocation != nullptr) {
|
||||
vmaDestroyImage(s_allocator.Get(), image, allocation);
|
||||
}
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (importedMemory != VK_NULL_HANDLE) {
|
||||
if (image != VK_NULL_HANDLE) vkDestroyImage(s_device.Get(), image, nullptr);
|
||||
vkFreeMemory(s_device.Get(), importedMemory, nullptr);
|
||||
@@ -482,7 +473,6 @@ public:
|
||||
yuvOwner.reset();
|
||||
yuvSource.reset();
|
||||
yuvConvertedFrame = 0;
|
||||
#endif
|
||||
fullView = VK_NULL_HANDLE;
|
||||
sampledView = VK_NULL_HANDLE;
|
||||
perMipViews.clear();
|
||||
@@ -510,9 +500,7 @@ public:
|
||||
syncedContentVersion = 0;
|
||||
syncedMipLevelCount = 0;
|
||||
syncedShapeVersion = 0;
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
syncedWireSerial = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
~TextureResource() {
|
||||
@@ -539,11 +527,9 @@ public:
|
||||
// preserve-on-recreate copy are the existing callers. No-op when the
|
||||
// batch is empty.
|
||||
void FlushPendingUploads();
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// Non-blocking idle proof for wire-object retirement. Independent texture
|
||||
// submissions are not represented by VulkanRenderer's submit counter.
|
||||
Bool WireUploadsAreIdle();
|
||||
#endif
|
||||
|
||||
// Drains every frame slot's deferred image/view releases. Only valid when
|
||||
// the caller has proven every queue submission complete; used by the
|
||||
@@ -604,7 +590,6 @@ public:
|
||||
|
||||
TextureResource* SyncTextureAndGetDescriptor(
|
||||
MG_State::GLState::ITextureObject& texture);
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P5f (fm): THE HANDLE-KEYED ARM of SyncTextureAndGetDescriptor. Under an active transport
|
||||
// the apply thread may not name the client's ITextureObject (rule E), so the clear / blit /
|
||||
// readback / mipmap verbs resolve their attachment textures from the wire handle the
|
||||
@@ -679,7 +664,6 @@ public:
|
||||
const auto it = m_wireTextureResources.find(key);
|
||||
return it != m_wireTextureResources.end() && it->second.image != VK_NULL_HANDLE ? &it->second : nullptr;
|
||||
}
|
||||
#endif
|
||||
VkImageView GetOrCreateViewAtMipLevel(MG_State::GLState::ITextureObject& texture, Uint32 mipLevel);
|
||||
VkImageView GetOrCreateAttachmentViewAtMipLevel(MG_State::GLState::ITextureObject& texture, Uint32 mipLevel,
|
||||
Uint32 baseArrayLayer, Uint32 layerCount,
|
||||
@@ -856,7 +840,6 @@ private:
|
||||
void EraseTrackedTexture(const TextureIdentity& identity);
|
||||
void PruneStaleTextureAliases(MG_State::GLState::ITextureObject* texture);
|
||||
SizeT PruneDeadTextures();
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// The two halves of SyncTextureResourceByHandle: shape from the resource record's
|
||||
// descriptor (create / recreate / compatibility), then the pending-upload walk against the
|
||||
// server's staged store. UploadPendingWireLevels mutates the applier's record (consumed
|
||||
@@ -879,7 +862,6 @@ private:
|
||||
Vector<Uint64> m_sharedImageUses;
|
||||
// Handles are per-kind; a renderbuffer and texture may have identical slot/gen.
|
||||
std::unordered_map<Uint64, TextureResource> m_wireRenderbufferResources;
|
||||
#endif
|
||||
|
||||
VkDevice m_device = VK_NULL_HANDLE;
|
||||
VkPhysicalDevice m_physicalDevice = VK_NULL_HANDLE;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -22,13 +22,11 @@
|
||||
#include "VkSamplerManager.h"
|
||||
#include "VkTextureManager.h"
|
||||
#include "VkTimerQueryManager.h"
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
#include "GpuProgressMarkers.h"
|
||||
#include "WireRenderPassCompatibility.h"
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
#include <MG_Remote/Server/SharedImageRegistry.h>
|
||||
#endif
|
||||
#endif
|
||||
#include "MG_Util/Math/VectorTypes.h"
|
||||
#include <Includes.h>
|
||||
#include <MG_Backend/BackendObject.h>
|
||||
@@ -150,7 +148,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
// of the swapchain image acquired for it (SwapchainObject::BufferAgeOf). Asking makes the
|
||||
// target keep its presented images' content from then on.
|
||||
Int32 CurrentDrawBufferAge();
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
VkBufferManager& GetWireBufferManager() { return m_bufferManager; }
|
||||
Bool FlushWirePendingCommandsForTextureUpdate() {
|
||||
// A new texture upload must not race graphics work that samples
|
||||
@@ -222,7 +219,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
// P13 W5: no transport, no shared image (only a server allocates one), so a texture's
|
||||
// sharedImageId is never set and there is nothing to acquire.
|
||||
void AcquireSharedImage(VkTextureManager::TextureResource&) {}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// FrameContext::IRecordingObserver: prepares the frame's timer-query
|
||||
@@ -296,11 +292,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth);
|
||||
void GenerateMipmap(GLenum target);
|
||||
void ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* pixels);
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// Server resource handle in, tightly packed owned bytes out. No frontend
|
||||
// texture, pixel-pack state or PBO is consulted by this readback.
|
||||
Bool ReadTextureImageWire(const MG_Pipe::MGPReadbackInfo& info, Vector<Uint8>& ownedBytes);
|
||||
#endif
|
||||
// GL_DEPTH_COMPONENT / GL_DEPTH_STENCIL / GL_STENCIL_INDEX readback from the
|
||||
// read framebuffer's depth/stencil attachment (per-aspect buffer copies with
|
||||
// CPU repacking into the requested client layout).
|
||||
@@ -410,7 +404,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
// loss here is this session's alone.
|
||||
void NoteDeviceLoss(VkResult result, const char* where);
|
||||
Bool IsDeviceLost();
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// For a wire site whose submit or wait just failed: true when the failure is a lost
|
||||
// device, in which case this session has been latched (MGPipeSessionLatch - it ends, the
|
||||
// display server's other sessions do not) and the caller must return from its verb
|
||||
@@ -422,7 +415,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
// is latched, and the caller returns from its verb without submitting anything more. Asked
|
||||
// before every frame submission and by the apply loop before it applies another record.
|
||||
Bool LatchIfGpuHung(const char* site);
|
||||
#endif
|
||||
// Flush gated on usefulness: only flushes when `submitIndex` is still
|
||||
// unsubmitted, so poll loops on already-submitted fences do not split
|
||||
// the frame's render pass (a full tile load/store on TBDR GPUs).
|
||||
@@ -581,26 +573,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
// The structs stay, empty, so ShutdownBlitResources and the two `= {}` assignments read
|
||||
// the same on both builds.
|
||||
struct BlitResources {
|
||||
#if !MOBILEGL_BUILD_RECORD_ARM
|
||||
SharedPtr<MG_State::GLState::ProgramObject> program;
|
||||
SharedPtr<MG_State::GLState::SamplerObject> nearestSampler;
|
||||
SharedPtr<MG_State::GLState::SamplerObject> linearSampler;
|
||||
Int srcRectLocation = -1;
|
||||
Int dstRectLocation = -1;
|
||||
Int surfaceTransformLocation = -1;
|
||||
Uint32 samplerBinding = 0;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct DepthMipmapResources {
|
||||
#if !MOBILEGL_BUILD_RECORD_ARM
|
||||
SharedPtr<MG_State::GLState::ProgramObject> program;
|
||||
Int srcRectLocation = -1;
|
||||
Int dstRectLocation = -1;
|
||||
Int surfaceTransformLocation = -1;
|
||||
Int srcTexelSizeLocation = -1;
|
||||
Uint32 samplerBinding = 0;
|
||||
#endif
|
||||
};
|
||||
|
||||
// A single-sample staging image for multisample-resolve blits that also have to change
|
||||
@@ -639,7 +614,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
// See NoteDeviceLoss. Set once, never cleared: a lost VkDevice stays lost.
|
||||
Bool m_deviceLost = false;
|
||||
Bool m_deviceLossLatched = false;
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// Brackets this device's frame submissions for the GPU hang watch (LatchIfGpuHung).
|
||||
GpuProgressMarkers m_progressMarkers;
|
||||
struct WireImage {
|
||||
@@ -925,7 +899,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
|
||||
void CopyWireFramebufferToTexture(GLenum target, GLint level, GLint xoffset, GLint yoffset,
|
||||
GLint x, GLint y, GLsizei width, GLsizei height);
|
||||
#endif
|
||||
void QueueClearBufferPayloadForFramebuffer(const MG_State::GLState::FramebufferObject& framebuffer,
|
||||
GLenum buffer, GLint drawbuffer,
|
||||
const ClearAttachmentPayload& clearPayload);
|
||||
@@ -972,11 +945,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
// was waited or the device was idled); drops their records and
|
||||
// recycles pooled fences.
|
||||
void OnSubmitsCompletedUpTo(Uint64 submitIndex);
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// A later fence alone cannot retire earlier submissions. Wait for the
|
||||
// whole still-live prefix before advancing the completed submit floor.
|
||||
Bool WaitForSubmitsUpTo(Uint64 submitIndex, Uint64 timeoutNs);
|
||||
#endif
|
||||
VkFence AcquirePooledSubmitFence();
|
||||
void DestroySubmitFencePool();
|
||||
Bool HasPendingRecordedWork() const;
|
||||
@@ -1160,12 +1131,10 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
VkDeviceSize countBufferOffset, Uint32 maxDrawCount,
|
||||
Uint32 stride);
|
||||
PFNDrawIndexedIndirectCountFunc s_vkCmdDrawIndexedIndirectCount = nullptr;
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P8-D: vkCmdDrawIndirectCount (same signature, same extension), for the wire arm's
|
||||
// glMultiDrawArraysIndirectCount. The monolith arm never loads it: it reads that count on
|
||||
// the CPU (DirectVulkan.cpp's MultiDrawArraysIndirectCount).
|
||||
PFNDrawIndexedIndirectCountFunc s_vkCmdWireDrawIndirectCount = nullptr;
|
||||
#endif
|
||||
// VK_EXT_multi_draw entry points, loaded at device creation when the extension
|
||||
// (and its multiDraw feature) is enabled; null otherwise.
|
||||
PFN_vkCmdDrawMultiEXT s_vkCmdDrawMultiEXT = nullptr;
|
||||
@@ -1233,9 +1202,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
Bool BeginXfbCaptureWithBuffers(FrameContext::FrameData& frame, Uint32 count,
|
||||
const VkBuffer* buffers, const VkDeviceSize* offsets,
|
||||
const VkDeviceSize* sizes);
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
Bool BeginWireXfbCaptureForDraw(FrameContext::FrameData& frame);
|
||||
#endif
|
||||
Uint32 m_currentDrawXfbBufferCount = 0;
|
||||
Uint32 m_currentDrawXfbBufferMask = 0;
|
||||
void EndXfbCaptureForDraw(FrameContext::FrameData& frame, Bool began);
|
||||
@@ -1383,9 +1350,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
Uint64 programHash = 0;
|
||||
Uint64 vertexInputHash = 0;
|
||||
Uint64 renderPassHash = 0;
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
Uint64 wireRenderPassCompatibilityId = 0;
|
||||
#endif
|
||||
// The PRE-HANDLE arm's key component (P2 brief D12.1), and 0 in every entry the
|
||||
// handle arm mints. VALUE hash of the pipeline-relevant fixed-function state (see
|
||||
// ComputePipelineStateHash), not the monotonic pipeline-state version: the version
|
||||
@@ -2158,12 +2123,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
Bool MaterializePendingDepthStencilClearForDefaultFramebuffer(
|
||||
VkCommandBuffer commandBuffer, const MG_State::GLState::FramebufferAttachmentObject& attachment,
|
||||
const ClearAttachmentPayload& payload);
|
||||
#if !MOBILEGL_BUILD_RECORD_ARM
|
||||
// P7 wave 2-B2 (CONTRACT-P7 §5.2, (B')): the hidden blit program's pipeline. A
|
||||
// disaggregated build has no hidden program on either arm, so this has neither a body
|
||||
// nor a caller there.
|
||||
VkPipeline GetOrCreateBlitPipeline(const RenderPassEntry& renderPassEntry);
|
||||
#endif
|
||||
Bool GenerateDepthMipmapWithShader(FrameContext::FrameData& frame,
|
||||
MG_State::GLState::ITextureObject& texture,
|
||||
VkTextureManager::TextureResource& resource,
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// Included by VulkanRenderer.cpp inside the backend namespace, after WireFramebuffer.inc.
|
||||
// The native blit owns no frontend object and never invokes the GL shader compiler.
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
#include "WireColorBlitSpirv.h"
|
||||
|
||||
struct VulkanRenderer::WireColorBlitResources {
|
||||
@@ -405,4 +404,3 @@
|
||||
if (destination.isDefault) m_swapchainObject.SetImageContentDefined(destination.swapchainImageIndex, true);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -35,7 +35,6 @@
|
||||
// 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
|
||||
// mapping, as before this package.
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
// A glBlitFramebuffer rectangle as GL names it: two corners, either order.
|
||||
struct WireBlitRect {
|
||||
@@ -63,4 +62,3 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
return VK_FILTER_LINEAR;
|
||||
}
|
||||
} // namespace MobileGL::MG_Backend::DirectVulkan
|
||||
#endif
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <Config.h>
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
|
||||
#include <Includes.h>
|
||||
#include <MG_Util/Debug/Log.h>
|
||||
@@ -169,4 +168,3 @@ private:
|
||||
|
||||
} // namespace MobileGL::MG_Backend::DirectVulkan
|
||||
|
||||
#endif // MOBILEGL_BUILD_RECORD_ARM
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
// frontend objects and a GL compiler on the server's apply thread. The baked program below is
|
||||
// the same shader with the same arithmetic, driven by a push constant and its own descriptor
|
||||
// set, which is what lets the server generate a depth mip while owning nothing of the client.
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
#include "WireDepthMipmapSpirv.h"
|
||||
|
||||
struct VulkanRenderer::WireDepthMipmapResources {
|
||||
@@ -326,4 +325,3 @@
|
||||
RetireWireObjects(Move(retired));
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
#include <cstring>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
#include "WireMultisampleResolveSpirv.h"
|
||||
|
||||
@@ -993,4 +992,3 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
return measurement;
|
||||
}
|
||||
} // namespace MobileGL::MG_Backend::DirectVulkan
|
||||
#endif
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
//
|
||||
// Disaggregated builds only: the monolith never records either arm, and the pull build's image
|
||||
// must not change (P7 gate G1).
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
// Every aspect a wire depth/stencil format has.
|
||||
VkImageAspectFlags WireDepthStencilFormatAspects(VkFormat format);
|
||||
@@ -118,4 +117,3 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
Uint32 graphicsQueueFamilyIndex, const Vector<VkExtensionProperties>& deviceExtensions,
|
||||
Bool& renderPassArmAvailable, Bool& shaderStencilExport);
|
||||
} // namespace MobileGL::MG_Backend::DirectVulkan
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// Server-owned draw/dispatch setup. The legacy setup remains the monolith implementation.
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
static MagmaProgramSource WireProgramSource(MG_Pipe::MGPipeHandle handle) {
|
||||
auto& state = MG_Pipe::MGPipeApplier();
|
||||
const Bool composite = handle.Slot >= MG_Pipe::kMGPipeShaderCsoCompositeSlotBase;
|
||||
@@ -909,4 +908,3 @@
|
||||
if (MG_Util::PipeStats::Enabled())
|
||||
MG_Util::PipeStats::AddCalls(MG_Util::PipeStats::CallClass::WireIndirectNativeDispatches, 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// P5f fm: included by VulkanRenderer.cpp inside the backend namespace. All inputs here
|
||||
// belong to the applier; no frontend object, lifetime-id lookup or allocator is used.
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P7 wave 0. THE MESSAGE IS UNCHANGED; where it goes is not. This funnel is how eleven of
|
||||
// the fifteen P7-marked refusals die (the other four are WireDescriptorFatal's), and until now
|
||||
// every one of them raised its own abort: no SessionFault frame for the peer, no
|
||||
@@ -1730,4 +1729,3 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
// shader arm fetches sample 0 - not an average. Two arms that answer differently on two devices
|
||||
// would make "the wire arm is observably the monolith's" untestable, and GL 4.6 core 18.3.1
|
||||
// requires a SELECTED sample for stencil anyway.
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// The pass's modules, layouts, render passes and pipeline, and the draw it records, are
|
||||
// WireDepthResolveProbe.cpp's (g5-msprobe): the multisample depth/stencil resolve probe runs
|
||||
// this exact arm as its CONTROL, so it is defined once for both.
|
||||
@@ -164,4 +163,3 @@
|
||||
RetireWireObjects(Move(retired));
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// UniformManager-owned native descriptors for wire bindings with no image.
|
||||
// No GL object is constructed or inserted into the client's resource namespace.
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
void UniformManager::DestroyWirePlaceholderImage(WirePlaceholderImage& image) const {
|
||||
if (m_device != VK_NULL_HANDLE) {
|
||||
if (image.clearFramebuffer) vkDestroyFramebuffer(m_device, image.clearFramebuffer, nullptr);
|
||||
@@ -245,4 +244,3 @@
|
||||
out = {image.sampler, image.view, VK_IMAGE_LAYOUT_GENERAL};
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
// The pure half of UniformManager's wire placeholder cache (WirePlaceholderImages.inc): WHICH
|
||||
// placeholder a binding with no usable image takes. A unit test pins it
|
||||
// (MG_Test/Pipeline/PipelineQuirkTest.cpp); the Vulkan half that makes the image stays in the .inc.
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
#include <MG_Pipe/MGPipeTypes.h>
|
||||
|
||||
namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
@@ -59,4 +58,3 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
return key;
|
||||
}
|
||||
} // namespace MobileGL::MG_Backend::DirectVulkan
|
||||
#endif
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
#include <Includes.h>
|
||||
#include "../VkIncludes.h"
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
// The wire draw pass has one graphics subpass, no input attachments,
|
||||
// multiview or extensions. Its Vulkan pipeline compatibility is formats,
|
||||
@@ -46,4 +45,3 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
Uint64 m_nextId = 1; // zero selects the native-handle pipeline domain
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// Server-owned texture readback. Included after WireFramebuffer.inc so image
|
||||
// layout tracking and frame retirement are the same as every other wire verb.
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
static Bool PackWireColorTight(const Uint8* source, SizeT sourceBytes, VkFormat format,
|
||||
TextureInternalFormat logicalFormat, const MG_Pipe::MGPReadbackInfo& info,
|
||||
Vector<Uint8>& result) {
|
||||
@@ -211,4 +210,3 @@
|
||||
ownedBytes = Move(result);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -16,10 +16,8 @@
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
#include <Config.h>
|
||||
#include <MG_Util/Metrics/PipeStats.h>
|
||||
#endif
|
||||
|
||||
namespace MobileGL::MG_Pipe {
|
||||
const char* MGPipeVerbName(MGPipeVerb verb) {
|
||||
@@ -49,7 +47,7 @@ namespace MobileGL::MG_Pipe {
|
||||
|
||||
// The stamp is the poison's (m_filled, the generations): a build without MOBILEGL_PIPE_POISON
|
||||
// compiles no read check that could ask any of this. Every transport build has the poison.
|
||||
#if MOBILEGL_BUILD_RECORD_ARM && MOBILEGL_PIPE_POISON
|
||||
#if MOBILEGL_PIPE_POISON
|
||||
// ================================================================================
|
||||
// P5: the server's verb stamp, the residual-pull counter, and the four-way read verdict
|
||||
// ================================================================================
|
||||
|
||||
@@ -13,14 +13,12 @@
|
||||
// the live context happens on the client side, in MG_Impl/Pipe/PipeFill.cpp.
|
||||
#include <MG_State/GLState/Core.h>
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P5c (rv, CONTRACT-P5C.md §5.3): the three texture shutters' server-side answer lives in the
|
||||
// applier - MGPipeApplierTextureShutterSerial() / MGPipeApplierContextSerial(), declared here
|
||||
// so the accessors below can answer with them under a server-stamped verb. MG_Pipe is below
|
||||
// MG_Backend, so this direction is the layering's, and PipeApply.h forward-declares
|
||||
// PipeInputs rather than including this header, so there is no cycle.
|
||||
#include <MG_Pipe/PipeApply.h>
|
||||
#endif
|
||||
|
||||
// MOBILEGL_PIPE_POISON: the per-verb generation stamps and the read-side
|
||||
// Fatal{UnmigratedPipeInput} check. Derived here, once. The repository's debug gate is
|
||||
@@ -53,7 +51,6 @@ namespace MobileGL::MG_Pipe {
|
||||
Optional<MGPipeInputField> MGPipeFindInputField(const char* name);
|
||||
Optional<MGPipeVerb> MGPipeFindVerb(const char* name);
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// ---- P5: the split arm of the read check (R-7.2, R-7.3) ------------------------------
|
||||
//
|
||||
// A stale read stops being one answer and becomes FOUR, keyed on the field's table-2 class
|
||||
@@ -87,13 +84,11 @@ namespace MobileGL::MG_Pipe {
|
||||
// from either counting the same read twice or - worse, because the field's own class would
|
||||
// not be BARRIER-PULLED - aborting a read the argument row had just declared legal.
|
||||
Bool MGPipeInputArgumentRead(MGPipeInputField field, Uint32 arg0, MGPipeVerb verb, Bool serverStamped);
|
||||
#endif
|
||||
|
||||
// The read-side poison check, on every non-forwarded accessor. Under MOBILEGL_PIPE_POISON
|
||||
// a read of a field whose stamp is older than the current verb serial is
|
||||
// Fatal{UnmigratedPipeInput, "Field@Verb"}; otherwise the accessor is a plain load.
|
||||
#if MOBILEGL_PIPE_POISON
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
#define MGP_INPUT_CHECK(Field) \
|
||||
do { \
|
||||
if ((m_serverStampedVerb && ::MobileGL::MG_Pipe::MGPipeFieldOwnershipOf(Field) == \
|
||||
@@ -110,15 +105,6 @@ namespace MobileGL::MG_Pipe {
|
||||
} \
|
||||
} while (0)
|
||||
#else
|
||||
#define MGP_INPUT_CHECK(Field) \
|
||||
do { \
|
||||
if (!::MobileGL::MG_Pipe::MGPipeInputFieldIsFresh(m_filled, (Field))) { \
|
||||
::MobileGL::MG_Pipe::MGPipeInputPoisonFatalForVerb((Field), m_currentVerb); \
|
||||
} \
|
||||
} while (0)
|
||||
#define MGP_INPUT_CHECK_ARG(Field, Arg0) MGP_INPUT_CHECK(Field)
|
||||
#endif
|
||||
#else
|
||||
#define MGP_INPUT_CHECK(Field) ((void)0)
|
||||
#define MGP_INPUT_CHECK_ARG(Field, Arg0) ((void)0)
|
||||
#endif
|
||||
@@ -266,7 +252,6 @@ namespace MobileGL::MG_Pipe {
|
||||
#if MOBILEGL_PIPE_POISON
|
||||
const MGPipeFilledState& FilledState() const { return m_filled; }
|
||||
#endif
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// TRUE between the server's verb-boundary stamp and whoever clears it. It is the
|
||||
// arming condition of the whole split read path: only inside a server-stamped verb is
|
||||
// a BARRIER-PULLED read counted rather than Fatal, and only there is a sticky forward
|
||||
@@ -285,7 +270,6 @@ namespace MobileGL::MG_Pipe {
|
||||
// case the sticky exemption was written for, would be counted and, under strict, would
|
||||
// abort. So: PipeApplier clears on leaving the applier. Not optional.
|
||||
Bool ServerStampedVerb() const { return m_serverStampedVerb; }
|
||||
#endif
|
||||
|
||||
// ---- V: values ----
|
||||
Int GetActiveTextureUnit() const {
|
||||
@@ -491,9 +475,7 @@ namespace MobileGL::MG_Pipe {
|
||||
Uint64 GetSamplingResolutionGeneration() const {
|
||||
MGP_INPUT_CHECK(MGPipeInputField::GetSamplingResolutionGeneration);
|
||||
MGP_INPUT_VERIFY_READ(MGPipeInputField::GetSamplingResolutionGeneration, 0, 0);
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (m_serverStampedVerb) return MGPipeApplierTextureShutterSerial();
|
||||
#endif
|
||||
return m_samplingResolutionGeneration;
|
||||
}
|
||||
const IntVec4& GetScissorBox() const {
|
||||
@@ -509,21 +491,17 @@ namespace MobileGL::MG_Pipe {
|
||||
Uint64 GetTextureBindGeneration() const {
|
||||
MGP_INPUT_CHECK(MGPipeInputField::GetTextureBindGeneration);
|
||||
MGP_INPUT_VERIFY_READ(MGPipeInputField::GetTextureBindGeneration, 0, 0);
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// See GetSamplingResolutionGeneration: the server answers from its own Serial.
|
||||
if (m_serverStampedVerb) return MGPipeApplierTextureShutterSerial();
|
||||
#endif
|
||||
return m_textureBindGeneration;
|
||||
}
|
||||
Uint64 GetTextureContextId() const {
|
||||
MGP_INPUT_CHECK(MGPipeInputField::GetTextureContextId);
|
||||
MGP_INPUT_VERIFY_READ(MGPipeInputField::GetTextureContextId, 0, 0);
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// A context IDENTITY rather than a generation: stable within the served context,
|
||||
// moved by every MGPipeApplierReset - which is all the backends' per-context memo
|
||||
// keys ask of it.
|
||||
if (m_serverStampedVerb) return MGPipeApplierContextSerial();
|
||||
#endif
|
||||
return m_textureContextId;
|
||||
}
|
||||
Uint64 GetTransformFeedbackCapturedVertices() const {
|
||||
@@ -757,9 +735,7 @@ namespace MobileGL::MG_Pipe {
|
||||
#if MOBILEGL_PIPE_POISON
|
||||
MGPipeFilledState m_filled{};
|
||||
#endif
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
Bool m_serverStampedVerb = false;
|
||||
#endif
|
||||
|
||||
// ---- V ----
|
||||
Int m_activeTextureUnit = 0;
|
||||
@@ -1019,7 +995,6 @@ namespace MobileGL::MG_Pipe {
|
||||
// The docs budget ~20 KB; the block is a few KB.
|
||||
static_assert(sizeof(PipeInputs) < 20 * 1024, "PipeInputs outgrew its budget");
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// ============================================================================
|
||||
// P5: the server-side verb stamp, and the counter that sizes what it leaves behind
|
||||
// ============================================================================
|
||||
@@ -1094,7 +1069,6 @@ namespace MobileGL::MG_Pipe {
|
||||
// object or write into the frontend. This is what puts them in `rsp` and, under
|
||||
// MOBILEGL_IPC_STRICT_ERRORS=1, makes them Fatal like any other BARRIER-PULLED row.
|
||||
void MGPipeStickyForwardPull(MGPipeInputField field);
|
||||
#endif
|
||||
|
||||
#if MOBILEGL_PIPE_VERIFY
|
||||
// PipeInputs.cpp. Per-field equality for the entry compare (P1 brief D8): V by value
|
||||
|
||||
@@ -130,7 +130,6 @@ namespace MobileGL::MG_Record {
|
||||
const MG_Backend::GlobalBackendFunctionsTable* table = Table("blit");
|
||||
if (table == nullptr) return false;
|
||||
if (table->GL.BlitFramebuffer == nullptr) return false;
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P5c (hd, CONTRACT-P5C §3.3): the record's handles cross to the backend as the verb's
|
||||
// own state. The bound form carries two nulls and nothing changes; the named form's
|
||||
// pair is what the backend's named-blit arm resolves - the sink no longer relies on
|
||||
@@ -143,12 +142,10 @@ namespace MobileGL::MG_Record {
|
||||
applierState.VerbBlitDrawFbo = blit.DrawFbo;
|
||||
const Bool named = !MG_Pipe::MGPipeHandleIsNull(blit.ReadFbo) ||
|
||||
!MG_Pipe::MGPipeHandleIsNull(blit.DrawFbo);
|
||||
#endif
|
||||
table->GL.BlitFramebuffer(blit.SrcX0, blit.SrcY0, blit.SrcX1, blit.SrcY1, blit.DstX0,
|
||||
blit.DstY0, blit.DstX1, blit.DstY1,
|
||||
static_cast<GLbitfield>(blit.Mask),
|
||||
static_cast<GLenum>(blit.Filter));
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (named && !applierState.VerbBlitNamedConsumed) {
|
||||
MGLOG_E_ONCE("MGPipe: a named blit (read {%u, %u}, draw {%u, %u}) reached a backend "
|
||||
"with no named-blit arm; the verb is DECLINED rather than applied to "
|
||||
@@ -158,7 +155,6 @@ namespace MobileGL::MG_Record {
|
||||
applierState.VerbBlitDrawFbo = MG_Pipe::kMGPipeNullHandle;
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
++m_blits;
|
||||
return true;
|
||||
}
|
||||
@@ -303,13 +299,11 @@ namespace MobileGL::MG_Record {
|
||||
}
|
||||
|
||||
Bool RecordVerbSink::ReadTextureImageTight(const MG_Pipe::MGPReadbackInfo& image, Vector<Uint8>& bytes) {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (m_backend && m_backend->GetBackendType() == BackendType::DirectGLES)
|
||||
return MG_Backend::DirectGLES::ReadTextureImageWire(image, bytes);
|
||||
if (m_backend && m_backend->GetBackendType() == BackendType::DirectVulkan &&
|
||||
MG_Backend::DirectVulkan::pVulkanRenderer)
|
||||
return MG_Backend::DirectVulkan::pVulkanRenderer->ReadTextureImageWire(image, bytes);
|
||||
#endif
|
||||
(void)image;
|
||||
(void)bytes;
|
||||
return false;
|
||||
@@ -535,7 +529,6 @@ namespace MobileGL::MG_Record {
|
||||
// with a span (our client stages client indices into an owned element buffer and never sets
|
||||
// kDrawHasUserIndices) and a multi-draw that claims instancing (no GL entry point produces
|
||||
// one; the client never sends it). Only a foreign peer reaches either.
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
namespace {
|
||||
// P5e (tx2), CONTRACT-P5E §5.3 / ruling 19 (ID-95, A8 closed). THE TWO UNIT WINDOWS MUST
|
||||
// COVER [0, MaxTouchedTextureUnit], AND THIS IS WHERE THAT PROMISE IS CHECKED.
|
||||
@@ -595,7 +588,6 @@ namespace MobileGL::MG_Record {
|
||||
return true;
|
||||
}
|
||||
} // namespace
|
||||
#endif
|
||||
|
||||
Bool RecordVerbSink::OnDrawVbo(const MG_Pipe::MGPDrawInfo& info,
|
||||
const MG_Pipe::MGPDrawRange* ranges,
|
||||
@@ -619,11 +611,9 @@ namespace MobileGL::MG_Record {
|
||||
}
|
||||
++m_drawRecords;
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// Ruling 19 / ID-95: the window promise, checked at every draw, before the backend is
|
||||
// asked to resolve anything out of the windows.
|
||||
if (!CheckUnitWindows(MG_Pipe::MGPipeApplier(), "draw_vbo")) return false;
|
||||
#endif
|
||||
const MG_Backend::GlobalBackendFunctionsTable* table = Table("draw_vbo");
|
||||
if (table == nullptr) return false;
|
||||
const MG_Backend::GLFunctionsTable& gl = table->GL;
|
||||
@@ -645,7 +635,6 @@ namespace MobileGL::MG_Record {
|
||||
if (indirect != nullptr) {
|
||||
// The layout already refused a record that sets both flags or declares ranges
|
||||
// beside the block, so NumDraws is 0 and there is no span here.
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P5c (hd, CONTRACT-P5C §3.5): the command/parameter buffer handles cross as the
|
||||
// verb's own state; the backend's indirect arm resolves the buffer twins from
|
||||
// them instead of reading the client's GL_DRAW_INDIRECT_BUFFER binding slot.
|
||||
@@ -653,7 +642,6 @@ namespace MobileGL::MG_Record {
|
||||
applierState.ClearVerbHandles();
|
||||
applierState.VerbIndirectBuffer = indirect->Buffer;
|
||||
applierState.VerbIndirectParameterBuffer = indirect->ParameterBuffer;
|
||||
#endif
|
||||
const auto offset = reinterpret_cast<const void*>(static_cast<std::uintptr_t>(indirect->Offset));
|
||||
const auto drawCount = static_cast<GLsizei>(indirect->DrawCount);
|
||||
const auto stride = static_cast<GLsizei>(indirect->Stride);
|
||||
@@ -843,10 +831,8 @@ namespace MobileGL::MG_Record {
|
||||
// identical - and it is also the honest statement of the debt, which `rsp` counts.
|
||||
|
||||
Bool RecordVerbSink::OnLaunchGrid(const MG_Pipe::MGPGridInfo& grid) {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// Ruling 19 / ID-95: a dispatch samples through the same unit windows a draw does.
|
||||
if (!CheckUnitWindows(MG_Pipe::MGPipeApplier(), "launch_grid")) return false;
|
||||
#endif
|
||||
const MG_Backend::GlobalBackendFunctionsTable* table = Table("launch_grid");
|
||||
if (table == nullptr) return false;
|
||||
const MG_Backend::GLFunctionsTable& gl = table->GL;
|
||||
@@ -868,14 +854,12 @@ namespace MobileGL::MG_Record {
|
||||
// IndirectBuffer travels for P7's sake; the BINDING is server state, put there by
|
||||
// the set_buffer_bindings record that preceded this one, exactly as OnClear's Fbo
|
||||
// is not re-resolved here. glDispatchComputeIndirect takes only the offset.
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P5c (hd, CONTRACT-P5C §3.5): the buffer handle itself is now also the verb's own
|
||||
// state, so the backend's dispatch-indirect arm resolves the twin from the record
|
||||
// rather than from the client's GL_DISPATCH_INDIRECT_BUFFER binding slot.
|
||||
auto& applierState = MG_Pipe::MGPipeApplier();
|
||||
applierState.ClearVerbHandles();
|
||||
applierState.VerbDispatchIndirectBuffer = grid.IndirectBuffer;
|
||||
#endif
|
||||
gl.DispatchComputeIndirect(static_cast<GLintptr>(grid.IndirectOffset));
|
||||
} else {
|
||||
if (gl.DispatchCompute == nullptr) return false;
|
||||
@@ -1116,7 +1100,6 @@ namespace MobileGL::MG_Record {
|
||||
Bool RecordVerbSink::OnGenerateMipmap(const MG_Pipe::MGPMipPlan& plan) {
|
||||
const auto* table = Table("GenerateMipmap");
|
||||
if (table == nullptr || table->GL.GenerateMipmap == nullptr) return false;
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P5c (hd, CONTRACT-P5C §3.2): the texture the client resolved at Target on the active
|
||||
// unit crosses as the verb's own state; the backend's mip-descriptor check resolves
|
||||
// the record from it instead of probing the client allocator for the bound object's
|
||||
@@ -1126,7 +1109,6 @@ namespace MobileGL::MG_Record {
|
||||
applierState.VerbMipRes = plan.Res;
|
||||
applierState.VerbMipBaseLevel = plan.BaseLevel;
|
||||
applierState.VerbMipLevelCount = plan.LevelCount;
|
||||
#endif
|
||||
table->GL.GenerateMipmap(plan.Target);
|
||||
return true;
|
||||
}
|
||||
@@ -1134,14 +1116,12 @@ namespace MobileGL::MG_Record {
|
||||
Bool RecordVerbSink::OnCopyFramebufferToTexture(const MG_Pipe::MGPCopyFromFramebuffer& copy) {
|
||||
const auto* table = Table(copy.SubImage ? "CopyTexSubImage2D" : "CopyTexImage2D");
|
||||
if (table == nullptr) return false;
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P5c (hd, CONTRACT-P5C §3.4): the destination texture the client resolved at the
|
||||
// active unit crosses as the verb's own state; the backend resolves its twin from the
|
||||
// handle instead of reading the client's texture-unit binding slot (T4).
|
||||
auto& applierState = MG_Pipe::MGPipeApplier();
|
||||
applierState.ClearVerbHandles();
|
||||
applierState.VerbCopyTexDst = copy.Dst;
|
||||
#endif
|
||||
if (copy.SubImage) {
|
||||
if (table->GL.CopyTexSubImage2D == nullptr) return false;
|
||||
table->GL.CopyTexSubImage2D(copy.Target, copy.Level, copy.XOffset, copy.YOffset,
|
||||
|
||||
@@ -114,9 +114,7 @@
|
||||
#include <mutex>
|
||||
#include <limits>
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
#include <Config.h>
|
||||
#endif
|
||||
|
||||
namespace MobileGL::MG_Record {
|
||||
|
||||
@@ -355,7 +353,6 @@ namespace MobileGL::MG_Record {
|
||||
const std::lock_guard<std::mutex> lock(m_mutex);
|
||||
LevelShadow& shadow = m_shadows[bucketed].Levels[PackLevel(uploadTarget, level)];
|
||||
RequireDeclaredLevel(shadow, key, uploadTarget, level, extent);
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// A run can be the first piece of a redefined level. Old coverage
|
||||
// belongs to the previous coordinate system even when the new run
|
||||
// happens to touch its old range; retaining it invents bytes that
|
||||
@@ -369,7 +366,6 @@ namespace MobileGL::MG_Record {
|
||||
shadow.Alias = nullptr;
|
||||
shadow.Covered.clear();
|
||||
}
|
||||
#endif
|
||||
if (shadow.HoldsFollowedCopy) {
|
||||
// P8-E: a run landed whole over a followed copy (AdoptClientBoxes is the spelling
|
||||
// that keeps it), so the bytes no longer hold what the driver does.
|
||||
@@ -1030,7 +1026,6 @@ namespace MobileGL::MG_Record {
|
||||
std::atomic<Bool> m_any{false};
|
||||
};
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// ONE PER PROCESS, and its copying arm is decided ONCE at first use - StagedShadow's
|
||||
// ServerStaged() ruling verbatim: the two arms hold the authoritative bytes in DIFFERENT
|
||||
// places, so an answer that changed mid-run would strand every level already staged.
|
||||
@@ -1043,6 +1038,5 @@ namespace MobileGL::MG_Record {
|
||||
*new StagedTextureStore(MG_Config::Transport != MG_Config::TransportMode::Monolith);
|
||||
return store;
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace MobileGL::MG_Record
|
||||
|
||||
@@ -12,9 +12,7 @@
|
||||
#include <MG_State/EGLState/Core.h>
|
||||
#include <MG_Backend/BackendObjects.h>
|
||||
#include <MG_Impl/Pipe/PipeFill.h>
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
#include <MG_Impl/Pipe/Verb/GpuWriteSet.h>
|
||||
#endif
|
||||
// CONTRACT-P5.md §7 / ID-14: a null check on a GLFunctionsTable slot may not survive into the
|
||||
// client under split - it becomes a caps-mirror read. SlotCaps.h carries the rule and the test
|
||||
// that decides which of its two spellings a site takes; in a pull build both expand to exactly
|
||||
@@ -1332,7 +1330,6 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
static_cast<Uint>(bufferIndex));
|
||||
const auto& buffer = bindingPoint.GetBoundObject();
|
||||
if (buffer == nullptr) continue;
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// This fixup READS the captured bytes back through the shadow, so it is the one
|
||||
// consumer that cannot simply inherit the deferral EndTransformFeedback's dropped
|
||||
// fence introduces. Under split it pays the reconciliation itself, which is the
|
||||
@@ -1345,7 +1342,6 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
// caller still runs, so the readback it emits is pure new work on the monolith
|
||||
// path and integration-gpu cannot see it.
|
||||
if (MG_Config::DataArmIsRecord()) buffer->SyncGpuWrites();
|
||||
#endif
|
||||
const Range1D range = bindingPoint.GetRange();
|
||||
const Uint8* mapped = buffer->MappedData();
|
||||
if (mapped == nullptr) continue;
|
||||
@@ -1386,12 +1382,10 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
MGP_FILL(EndTransformFeedback);
|
||||
endXfb();
|
||||
}
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P5 (b1), the second producer the client-side GPU-write set ADDS, and it has to be
|
||||
// taken HERE - before GLContext::EndTransformFeedback clears the live bindings, since
|
||||
// a mark taken after it marks nothing.
|
||||
MG_Record::MarkEndTransformFeedbackCaptureTargets();
|
||||
#endif
|
||||
MG_State::pGLContext->EndTransformFeedback();
|
||||
// Captured results must be visible to MapBuffer/GetBufferSubData after
|
||||
// End; the capture targets are host-coherent GPU memory, so completing
|
||||
|
||||
@@ -49,11 +49,7 @@
|
||||
// SyncGpuWrites, exactly as glMapBufferRange and glGetBufferSubData already do. Monolith keeps its
|
||||
// read as it was: its ReadPixels maps the driver PBO back into the shadow inside the call, and the
|
||||
// pull build compiles this to nothing (G1).
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
#define MGL_SYNC_PIXEL_UNPACK_SOURCE(buffer) do { if (MG_Config::DataArmIsRecord()) (buffer)->SyncGpuWrites(); } while (0)
|
||||
#else
|
||||
#define MGL_SYNC_PIXEL_UNPACK_SOURCE(buffer) ((void)0)
|
||||
#endif
|
||||
|
||||
namespace MobileGL::MG_Impl::GLImpl {
|
||||
static SharedPtr<MG_State::GLState::ITextureObject> nullTextureObject;
|
||||
@@ -6121,10 +6117,8 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
viewObject->SetInternalFormat(viewInternalFormat);
|
||||
viewObject->SetSamples(storageOwner->GetSamples());
|
||||
viewObject->SetFixedSampleLocations(storageOwner->HasFixedSampleLocations());
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (MG_Config::DataArmIsRecord())
|
||||
MG_Pipe::MGPipeEmitSamplerViewCreate(*viewObject);
|
||||
#endif
|
||||
}
|
||||
|
||||
void TexStorage1D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width) {
|
||||
@@ -6586,14 +6580,12 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
static void GetTextureImageForUploadTarget(const SharedPtr<MG_State::GLState::ITextureObject>& textureObject,
|
||||
TextureUploadTarget uploadTarget, GLint level, GLenum format,
|
||||
GLenum type, GLsizei bufSize, void* pixels, const char* caller) {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (MG_Config::DataArmIsRecord()) {
|
||||
MGP_FILL(GetTextureImage);
|
||||
MG_Backend::gBackendFunctionsTable.GL.GetTextureImage(textureObject, uploadTarget, level, format, type,
|
||||
bufSize, pixels);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (MG_Backend::pActiveBackendObject != nullptr &&
|
||||
MG_Backend::pActiveBackendObject->GetBackendType() == BackendType::DirectVulkan &&
|
||||
MGL_BACKEND_SLOT_LOCAL(GetTextureImage)) {
|
||||
@@ -6614,12 +6606,10 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
return;
|
||||
}
|
||||
auto uploadTarget = GetPrimaryUploadTarget(textureObject);
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// Unlike glGetTexImage's face target, this DSA call reads all six faces.
|
||||
if (MG_Config::DataArmIsRecord() &&
|
||||
textureObject->GetTarget() == TextureTarget::TextureCubeMap)
|
||||
uploadTarget = TextureUploadTarget::Unknown;
|
||||
#endif
|
||||
GetTextureImageForUploadTarget(textureObject, uploadTarget, level, format, type,
|
||||
bufSize, pixels, __func__);
|
||||
}
|
||||
@@ -6880,12 +6870,10 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
|
||||
void GetTexImage(GLenum target, GLint level, GLenum format, GLenum type, GLvoid* pixels) {
|
||||
if (!GetTexImage_State(target, level, format, type, pixels)) return;
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (MG_Config::DataArmIsRecord()) {
|
||||
GetTexImage_Backend(target, level, format, type, pixels);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (MGL_BACKEND_SLOT_LOCAL(GetTexImage)) {
|
||||
GetTexImage_Backend(target, level, format, type, pixels);
|
||||
return;
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
// Client memory becomes ordinary owned buffer resources before a draw.
|
||||
#pragma once
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
#include <MG_Impl/Pipe/ClientFetchPlan.h>
|
||||
#include <MG_Impl/Pipe/VertexInputEmit.h>
|
||||
#include <MG_State/GLState/BufferState/BufferObject.h>
|
||||
@@ -207,4 +206,3 @@ namespace MobileGL::MG_Pipe {
|
||||
Bool m_indexBindingChanged = false;
|
||||
};
|
||||
} // namespace MobileGL::MG_Pipe
|
||||
#endif
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
#include <MG_Pipe/PipeRoute.h>
|
||||
#include <MG_Pipe/PipeMutation.h>
|
||||
#include <Config.h>
|
||||
#if MOBILEGL_BUILD_RECORD_ARM && MOBILEGL_PIPE_VERIFY
|
||||
#if MOBILEGL_PIPE_VERIFY
|
||||
#include <MG_Impl/Pipe/Verb/VerbPort.h>
|
||||
#endif
|
||||
|
||||
@@ -96,7 +96,6 @@ namespace MobileGL::MG_Pipe {
|
||||
return inputs.m_currentVertexAttribute;
|
||||
}
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P5e (ra, CONTRACT-P5E §2.3). THE FOUR O-CLASS ROWS, AND ONLY THEY: these are the only
|
||||
// members of this block that own a frontend object rather than point into one, so they
|
||||
// are the only ones through which the apply thread can become a last owner. The raw
|
||||
@@ -108,7 +107,6 @@ namespace MobileGL::MG_Pipe {
|
||||
inputs.m_programForDraw.reset();
|
||||
inputs.m_transformFeedbackProgram.reset();
|
||||
}
|
||||
#endif
|
||||
|
||||
static void CopyField(PipeInputs& dst, GLContext& ctx, MGPipeInputField field) {
|
||||
using F = MGPipeInputField;
|
||||
@@ -691,7 +689,6 @@ namespace MobileGL::MG_Pipe {
|
||||
if (&self != &gPipeInputs || !g_verify.Enabled) return;
|
||||
const auto index = static_cast<SizeT>(field);
|
||||
if (kMGPipeInputFieldSticky[index]) return;
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P13 W5: the monolith verb port applies records without a server stamp, and its readback
|
||||
// forces the neutral pack around the backend's read exactly as the server's does.
|
||||
if ((self.ServerStampedVerb() || MG_Record::MonolithPortApplyingForVerify()) &&
|
||||
@@ -711,7 +708,6 @@ namespace MobileGL::MG_Pipe {
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (g_verify.InHook) return;
|
||||
auto* ctx = LiveContext();
|
||||
if (ctx == nullptr) return;
|
||||
@@ -1106,7 +1102,6 @@ namespace MobileGL::MG_Pipe {
|
||||
// one was emitted, so this cannot be false - but a zeroed record (null
|
||||
// handle, size 0) is not the answer if that pre-pass is ever relaxed.
|
||||
if (!MGPipeBuildSubDataRecord(handle, at, length, record, /*verbatimShadow=*/true)) return;
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P5 (b1): the live-host-writes bit rides the content record, because
|
||||
// "someone may be writing these bytes without telling you" is a fact about the
|
||||
// CONTENT and not about the storage. It is set from the object's PUBLISHED
|
||||
@@ -1114,7 +1109,6 @@ namespace MobileGL::MG_Pipe {
|
||||
// edge that announced it can never disagree. MGPipeBuildSubDataRecord does not
|
||||
// take the object, which is why it is set here and not in the builder.
|
||||
record.HasLiveHostWrites = buffer.HasLiveHostWritesForWire() ? 1 : 0;
|
||||
#endif
|
||||
// `length` is this chunk's byte count, which the record also declares
|
||||
// (MGPipeBuildSubDataRecord writes it into the destination range) - passed
|
||||
// rather than re-read so the staged run and the record's own claim come from
|
||||
@@ -1140,7 +1134,6 @@ namespace MobileGL::MG_Pipe {
|
||||
// store, or the pattern FillSubData expanded locally, and neither is this
|
||||
// client's untransformed shadow of the level.
|
||||
if (!MGPipeBuildSubDataRecord(handle, at, length, record, /*verbatimShadow=*/false)) return;
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P5 (b1): THE SECOND CONTENT EMITTER, and it has to speak for the same reason
|
||||
// the first does. ApplyBufferWrite ASSIGNS the bit - a content record emitted
|
||||
// while nothing maps the buffer is how the state goes back to false - so a
|
||||
@@ -1148,7 +1141,6 @@ namespace MobileGL::MG_Pipe {
|
||||
// map. `glBufferSubData` against a persistently mapped arena is legal and is
|
||||
// the ordinary Flywheel/Create shape, so that is not a corner.
|
||||
record.HasLiveHostWrites = buffer.HasLiveHostWritesForWire() ? 1 : 0;
|
||||
#endif
|
||||
// The application's STAGING store, valid for the duration of the call only.
|
||||
MGPipeRouteBufferSubDataResident(record, base + (at - offset), length);
|
||||
// P7 wave 2 package C, OQ-10: `rsd=`, counted HERE rather than at the call
|
||||
@@ -2270,11 +2262,9 @@ namespace MobileGL::MG_Pipe {
|
||||
}
|
||||
|
||||
Bool PipeInputs::HasOpenTransformFeedbackSpan(Uint64 lifetimeId) const {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (MG_Config::DataArmIsRecord()) {
|
||||
return lifetimeId != 0 && MGPipeApplier().StreamOutputSpans.count(lifetimeId) != 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
MGP_STICKY_FORWARD_PULL(HasOpenTransformFeedbackSpan);
|
||||
const auto* ctx = LiveContext();
|
||||
@@ -2369,9 +2359,7 @@ namespace MobileGL::MG_Pipe {
|
||||
// stamped, so every stamp this verb made falls behind the serial.
|
||||
++MGPipeFillAccess::Filled(inputs).CurrentVerbSerial;
|
||||
#endif
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
MGPipeClientClearVerbBoundary();
|
||||
#endif
|
||||
MGPipeFillAccess::SetVerb(inputs, MGPipeVerb::kVerbCount);
|
||||
// The pending base instance belongs to the verb that was about to run, so leaving
|
||||
// one drops it.
|
||||
@@ -2917,9 +2905,7 @@ namespace MobileGL::MG_Pipe {
|
||||
Bool ApplierDerives = false;
|
||||
Bool ContextValuesWireLive = false;
|
||||
Bool P4aConsumer = false;
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
Uint64 CapsGeneration = 0;
|
||||
#endif
|
||||
// One bit per FIELD - not per verb class. The class mask is applied at the walk
|
||||
// exactly as it always was, so "does this verb read this field" stays the walk's
|
||||
// business and this stays a statement about EMISSION alone.
|
||||
@@ -3125,9 +3111,7 @@ namespace MobileGL::MG_Pipe {
|
||||
// `g_attribDefaultLastHeader`, which is what it just published; there is nothing
|
||||
// the mirror could add that the wire does not already carry. A build that ever
|
||||
// needs the repair arm again has to earn it with a barriered row.
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (ClientRunsAhead()) return sizeof(MGPVertexAttribDefaults) + header.Count * sizeof(MGPAttribValue);
|
||||
#endif
|
||||
const auto* mirror = MGPipeFillAccess::VertexAttribDefaultsOf(MGPipeClientInputs());
|
||||
Bool reproduced = true;
|
||||
for (SizeT i = 0; i < kAttribs && reproduced; ++i) {
|
||||
@@ -3144,7 +3128,6 @@ namespace MobileGL::MG_Pipe {
|
||||
return sizeof(MGPVertexAttribDefaults) + header.Count * sizeof(MGPAttribValue);
|
||||
}
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// set_context_values (P5c rv, CONTRACT-P5C.md §5.3): the residual-value record. One
|
||||
// POD carrying every value-class field no other set_* supplies - the two texture-unit
|
||||
// counters, the 15 per-target touched-buffer-binding counts and the five XFB values -
|
||||
@@ -3201,7 +3184,6 @@ namespace MobileGL::MG_Pipe {
|
||||
MGPipeFieldMaskHas(mask, MGPipeInputField::GetBoundTransformFeedbackLifetimeId) ||
|
||||
MGPipeFieldMaskHas(mask, MGPipeInputField::GetTransformFeedbackCapturedVertices);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
@@ -3403,7 +3385,6 @@ namespace MobileGL::MG_Pipe {
|
||||
// is the CLIENT-role block and gPipeInputs is the server's alone; off, or under
|
||||
// monolith transport, it folds back onto gPipeInputs and nothing below changes.
|
||||
PipeInputs& inputs = MGPipeClientInputs();
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// ---- P5e (ra), CONTRACT-P5E §3: WHO OWNS gPipeInputs FOR THIS VERB ----------------
|
||||
//
|
||||
// Under run-ahead the block is SERVER-ROLE MEMORY for an unbarriered record: the
|
||||
@@ -3418,7 +3399,6 @@ namespace MobileGL::MG_Pipe {
|
||||
// every verb) and the guard below then fires Fatal{RoleViolation, "gPipeInputs"} on
|
||||
// the first unbarriered verb, by name.
|
||||
const Bool runAhead = ClientRunsAhead();
|
||||
#endif
|
||||
ParsePoisonOmissionKnob();
|
||||
#if MOBILEGL_PIPE_VERIFY
|
||||
ArmVerify();
|
||||
@@ -3479,7 +3459,6 @@ namespace MobileGL::MG_Pipe {
|
||||
// Fatal{UnmigratedPipeInput, "<Field>@<none>"} rather than default storage.
|
||||
++MGPipeFillAccess::Filled(inputs).CurrentVerbSerial;
|
||||
#endif
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// The client is filling, so whatever the server stamped at its last verb boundary
|
||||
// is withdrawn: the stamps below are the CLIENT's again and a stale read is a
|
||||
// defect, not a residual pull. Disarming here rather than at the end of the
|
||||
@@ -3499,7 +3478,6 @@ namespace MobileGL::MG_Pipe {
|
||||
// (PipeApplier::LeaveApplier) - which is CONTRACT-P5E §3.2's per-role stamp
|
||||
// ownership, landed as the dual block rather than as moved fields.
|
||||
MGPipeClientClearVerbBoundary();
|
||||
#endif
|
||||
MGPipeFillAccess::SetVerb(inputs, verb);
|
||||
}
|
||||
#if MOBILEGL_PIPE_POISON
|
||||
@@ -3742,7 +3720,6 @@ namespace MobileGL::MG_Pipe {
|
||||
payloadBytes += EmitVertexAttribDefaults(*ctx, tracker.FreshlyPrimed());
|
||||
}
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P5c rv (CONTRACT-P5C.md §5.3): the residual-value record, emitted when any covered
|
||||
// value moved. THE GATE IS THE SUBSYSTEM BIT PLUS THE WIRE BEING LIVE - the family has
|
||||
// no dirty bit (NoDirtyBitOwnsTheResidualSubsystem) and no P4a consumer predicate (it
|
||||
@@ -3753,7 +3730,6 @@ namespace MobileGL::MG_Pipe {
|
||||
VerbMaskReadsContextValues(mask)) {
|
||||
payloadBytes += EmitContextValues(*ctx);
|
||||
}
|
||||
#endif
|
||||
|
||||
// P3a's vertex segment, in the order the design fixes: vertex elements, then the
|
||||
// vertex buffers that fill them, then the index binding. All three are LIVE now (m1):
|
||||
|
||||
@@ -37,9 +37,7 @@
|
||||
// detection. Neither is reproduced here and neither may be.
|
||||
//
|
||||
// HEADER-ONLY, for the ownership reason Tracker.h and ResourceTracker.h both state.
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
#include <MG_Impl/EGLImpl/EGLImpl.h>
|
||||
#endif
|
||||
#include <MG_Impl/Pipe/SetHashSuppressor.h>
|
||||
#include <MG_Impl/Pipe/SlotAllocator.h>
|
||||
#include <MG_Impl/Pipe/Tracker.h>
|
||||
|
||||
@@ -899,7 +899,6 @@ namespace MobileGL::MG_Pipe {
|
||||
MGPRespecifiedLevel key{};
|
||||
key.UploadTarget = packedTarget;
|
||||
key.Level = static_cast<Uint16>(firstLevel + i);
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
const auto* mipmap = MG_State::GLState::AsMipmapTexture(&texture);
|
||||
if (mipmap == nullptr) {
|
||||
MGLOG_E_ONCE("MGPipe: a per-level texture respecify had no mipmap storage; it was not emitted");
|
||||
@@ -910,7 +909,6 @@ namespace MobileGL::MG_Pipe {
|
||||
key.Width = static_cast<Uint32>(exactLevelExtent.x());
|
||||
key.Height = static_cast<Uint32>(exactLevelExtent.y());
|
||||
key.Depth = static_cast<Uint32>(exactLevelExtent.z());
|
||||
#endif
|
||||
accepted = RespecifyOnce(texture, handle, entry, desc, &key, viewOf, bufferHandle, bufOffset,
|
||||
bufSize);
|
||||
if (!accepted) break;
|
||||
|
||||
@@ -382,19 +382,15 @@ namespace MobileGL::MG_Pipe {
|
||||
for (SizeT i = 0; i < kAttribs; ++i) {
|
||||
m_attributes[i] = MGPipeBuildVertexAttribWire(vao.GetAttribute(static_cast<Uint>(i)),
|
||||
vao.GetAttributeBindingIndex(static_cast<Uint>(i)));
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// Client addresses never cross the transport. Draw emission snapshots
|
||||
// their referenced elements into owned buffers with a zero byte origin.
|
||||
if (MG_Config::DataArmIsRecord() &&
|
||||
!vao.GetAttribute(static_cast<Uint>(i)).Buffer) m_attributes[i].Offset = 0;
|
||||
#endif
|
||||
}
|
||||
for (SizeT i = 0; i < kBindings; ++i) {
|
||||
m_bindingPoints[i] = MGPipeBuildVertexBindingPointWire(vao.GetBindingPoint(static_cast<Uint>(i)));
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (MG_Config::DataArmIsRecord() &&
|
||||
!vao.GetBindingPoint(static_cast<Uint>(i)).Buffer) m_bindingPoints[i].Offset = 0;
|
||||
#endif
|
||||
}
|
||||
// Attributes first, then binding points, both ascending and contiguous.
|
||||
constexpr SizeT kAttribBytes = kAttribs * sizeof(MGPVertexAttribWire);
|
||||
|
||||
@@ -1939,9 +1939,7 @@ endif()
|
||||
# not true. All this file has to decide now is whether that peek can be COMPILED, which is exactly
|
||||
# "did this build compile MG_Remote", which is exactly the build option.
|
||||
# P13 W5: the record arm's half of the peek (dataArmIsRecord) needs no transport.
|
||||
if (MOBILEGL_BUILD_RECORD_ARM)
|
||||
target_compile_definitions(MobileGLIntegrationTest PRIVATE -DMGITEST_RECORD_ARM_PEEK=1)
|
||||
endif()
|
||||
target_compile_definitions(MobileGLIntegrationTest PRIVATE -DMGITEST_RECORD_ARM_PEEK=1)
|
||||
if (MOBILEGL_BUILD_DISAGGREGATED)
|
||||
target_compile_definitions(MobileGLIntegrationTest PRIVATE -DMGITEST_SPLIT_RUNTIME_PEEK=1)
|
||||
message(STATUS "Integration tests: the DirectGLES.Split. lanes arm from the RUNNING PROCESS - "
|
||||
@@ -1965,25 +1963,19 @@ if (MOBILEGL_BUILD_DISAGGREGATED AND DEFINED MOBILEGL_LOG_ACTIVE_LEVEL
|
||||
endif()
|
||||
|
||||
# Package b1's client-side persistent-map tracker, which is what Harness/PersistentMapPeek.cpp
|
||||
# asks the membership question of (b1-v1.md 4.1 item 2). TWO halves, and both are needed:
|
||||
# MOBILEGL_BUILD_RECORD_ARM because a build that never compiled the record arm cannot LINK the call
|
||||
# (P13 W5: the tracker is record core, MG_Record, and no longer needs MG_Remote), and the content
|
||||
# probe because b1 may not have landed it yet. __has_include in the peek would answer yes in a
|
||||
# build without it - the header is in the source tree of every build - and turn a healthy build
|
||||
# into a link error, which is why this decision is made here.
|
||||
if (MOBILEGL_BUILD_RECORD_ARM)
|
||||
mgl_itest_probe_for_symbol(MGL_ITEST_PERSISTENT_MAP_TRACKER
|
||||
"${MGL_ITEST_ROOT}/MobileGL/MG_State/GLState/BufferState" "IsLivePersistentMap")
|
||||
if (MGL_ITEST_PERSISTENT_MAP_TRACKER)
|
||||
message(STATUS "Integration tests: package b1's persistent-map tracker is present "
|
||||
"(${MGL_ITEST_PERSISTENT_MAP_TRACKER}) - PersistentCoherentMapScenario's "
|
||||
"membership assertion is live")
|
||||
target_compile_definitions(MobileGLIntegrationTest PRIVATE -DMGITEST_PERSISTENT_MAP_TRACKER=1)
|
||||
else()
|
||||
message(STATUS "Integration tests: no source under MobileGL/MG_State/GLState/BufferState names "
|
||||
"IsLivePersistentMap - package b1's tracker has not landed, so "
|
||||
"PersistentCoherentMapScenario's membership assertion SKIPS")
|
||||
endif()
|
||||
# asks the membership question of (b1-v1.md 4.1 item 2). Every library builds it since P13 W5 (the
|
||||
# tracker is record core, MG_Record); the content probe stays so a tree without it skips by name.
|
||||
mgl_itest_probe_for_symbol(MGL_ITEST_PERSISTENT_MAP_TRACKER
|
||||
"${MGL_ITEST_ROOT}/MobileGL/MG_State/GLState/BufferState" "IsLivePersistentMap")
|
||||
if (MGL_ITEST_PERSISTENT_MAP_TRACKER)
|
||||
message(STATUS "Integration tests: package b1's persistent-map tracker is present "
|
||||
"(${MGL_ITEST_PERSISTENT_MAP_TRACKER}) - PersistentCoherentMapScenario's "
|
||||
"membership assertion is live")
|
||||
target_compile_definitions(MobileGLIntegrationTest PRIVATE -DMGITEST_PERSISTENT_MAP_TRACKER=1)
|
||||
else()
|
||||
message(STATUS "Integration tests: no source under MobileGL/MG_State/GLState/BufferState names "
|
||||
"IsLivePersistentMap - package b1's tracker has not landed, so "
|
||||
"PersistentCoherentMapScenario's membership assertion SKIPS")
|
||||
endif()
|
||||
|
||||
# ARCHITECTURE.md:543 - every new ctest ENVIRONMENT and add_trace_replay_test's SPLIT branch
|
||||
|
||||
@@ -76,7 +76,6 @@ namespace MobileGL::MG_Pipe {
|
||||
// Null-initialized: a backend that installs nothing sends nothing.
|
||||
inline MGPipeCallbacks gMGPipeCallbacks{};
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// HOW MUCH OF ITSELF ONE REVERSE-CHANNEL RECORD MAY BE, and it is not a tuning knob: a
|
||||
// producer that ignores it kills the server process.
|
||||
//
|
||||
@@ -113,5 +112,4 @@ namespace MobileGL::MG_Pipe {
|
||||
const Uint64 quarter = capacity / 4;
|
||||
return quarter < 4096 ? 4096 : quarter;
|
||||
}
|
||||
#endif
|
||||
} // namespace MobileGL::MG_Pipe
|
||||
|
||||
@@ -463,25 +463,17 @@ namespace MobileGL::MG_Pipe {
|
||||
desc.HasRespecifiedLevel = 1;
|
||||
desc.RespecifiedUploadTarget = uploadTarget;
|
||||
desc.RespecifiedLevel = level;
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// Non-buffer texture targets leave this descriptor range unused. A named-level
|
||||
// respecify repurposes it as exact W/H/D; TexBuffer cannot legally name a mip level.
|
||||
desc.BufOffset = (static_cast<Uint64>(width) << 32) | static_cast<Uint64>(height);
|
||||
desc.BufSize = depth;
|
||||
#else
|
||||
(void)width;
|
||||
(void)height;
|
||||
(void)depth;
|
||||
#endif
|
||||
}
|
||||
inline constexpr void MGPipeClearRespecifiedLevel(MGPResourceDesc& desc) {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (desc.HasRespecifiedLevel != 0 &&
|
||||
desc.Target != static_cast<Uint8>(MGPipeResourceTarget::TexBuffer)) {
|
||||
desc.BufOffset = 0;
|
||||
desc.BufSize = 0;
|
||||
}
|
||||
#endif
|
||||
desc.HasRespecifiedLevel = 0;
|
||||
desc.RespecifiedUploadTarget = 0;
|
||||
desc.RespecifiedLevel = 0;
|
||||
|
||||
@@ -16,11 +16,9 @@
|
||||
|
||||
#include <MG_Backend/MGPipe/PipeInputs.h>
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// MGPipeUnmigratedEmulation's split arm reads MG_Config::Transport, which is what tells an
|
||||
// emulation site whether it is running inside a server or in the monolith it was written for.
|
||||
#include <Config.h>
|
||||
#endif
|
||||
|
||||
#if MOBILEGL_PIPE_VERIFY
|
||||
// THE ONE PLACE THE PROGRAM ARCHIVE'S CODEC IS CALLED, and it is compiled into the VERIFY
|
||||
@@ -53,9 +51,7 @@
|
||||
#include <MG_Remote/Server/ServerSession.h>
|
||||
#include <MG_Remote/CapsCodec.h>
|
||||
#endif
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
#include <MG_Backend/Record/StagedTextureStore.h>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
@@ -209,7 +205,6 @@ namespace MobileGL::MG_Pipe {
|
||||
return g_clientLossConfirmer != nullptr && g_clientLossConfirmer(waitMs);
|
||||
}
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// ----------------------------------------------------------------------------
|
||||
// THE BACKEND-SIDE SESSION-FAIL SEAM (P7 wave 0). PipeSessionFail.h holds the argument;
|
||||
// this is the whole implementation.
|
||||
@@ -250,9 +245,7 @@ namespace MobileGL::MG_Pipe {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
#endif // MOBILEGL_BUILD_RECORD_ARM
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
namespace {
|
||||
MGPipeSessionLatchHook g_sessionLatchHook = nullptr;
|
||||
MGPipeSessionLatchArmedHook g_sessionLatchArmedHook = nullptr;
|
||||
@@ -267,7 +260,6 @@ namespace MobileGL::MG_Pipe {
|
||||
|
||||
bool MGPipeSessionLatchArmed() { return g_sessionLatchArmedHook != nullptr && g_sessionLatchArmedHook(); }
|
||||
|
||||
#endif // MOBILEGL_BUILD_RECORD_ARM
|
||||
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
bool MGPipeDebugDeviceLossDue() {
|
||||
@@ -349,7 +341,6 @@ namespace MobileGL::MG_Pipe {
|
||||
|
||||
#endif // MOBILEGL_BUILD_DISAGGREGATED
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
bool MGPipeSessionLatch(MGPipeFatalFamily family, const char* fmt, ...) {
|
||||
char line[512];
|
||||
va_list args;
|
||||
@@ -364,7 +355,6 @@ namespace MobileGL::MG_Pipe {
|
||||
// NO HOOK: no session to latch, so the death the fail seam would have given.
|
||||
MGPipeSessionFail(family, "%s", line);
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
// ----------------------------------------------------------------------------
|
||||
@@ -1343,28 +1333,7 @@ namespace MobileGL::MG_Pipe {
|
||||
return true;
|
||||
}
|
||||
|
||||
#if MOBILEGL_PIPE_VERIFY && !MOBILEGL_BUILD_RECORD_ARM
|
||||
// D-A4's pin, AND P5 (b1) IS THE PHASE IT WAS WAITING FOR. It said "HasLiveHostWrites
|
||||
// is always false in this phase and is written by nobody: it exists so the phase that
|
||||
// pushes persistent-mapped host writes can set it with no new record kind", and a
|
||||
// verify build refused to let such a producer arrive unannounced.
|
||||
//
|
||||
// The producer is announced: MGPSubData::HasLiveHostWrites, set by
|
||||
// MGPipeEmitResourceSubData from BufferObject::HasLiveHostWritesForWire and read by
|
||||
// ApplyBufferWrite below. So the pin is LIFTED FOR A SPLIT BUILD ONLY, and left
|
||||
// standing everywhere else - in a monolith build nothing sets the bit and the wire is
|
||||
// still the thing that would say so if something started to. That is the whole value
|
||||
// of the pin and it survives the phase it was written for.
|
||||
void PinNoLiveHostWrites(const MGPipeResourceRecord& record, MGPipeHandle res, const char* call) {
|
||||
if (!record.HasLiveHostWrites) return;
|
||||
MGP_TRIP_WIRE_REPORT("MGPipe: " MGP_TRIP_WIRE_TAG("PipeLiveHostWrites")
|
||||
" %s {slot=%u, gen=%u}: the resource record says host writes are live, and "
|
||||
"no path in this phase may set that",
|
||||
call, res.Slot, res.Gen);
|
||||
}
|
||||
#else
|
||||
void PinNoLiveHostWrites(const MGPipeResourceRecord&, MGPipeHandle, const char*) {}
|
||||
#endif
|
||||
|
||||
#if MOBILEGL_PIPE_VERIFY
|
||||
// P5's pin, RELAXED BY P7 WAVE 3 (CONTRACT-P7 §6) - and relaxed rather than deleted,
|
||||
@@ -1492,7 +1461,6 @@ namespace MobileGL::MG_Pipe {
|
||||
return false;
|
||||
}
|
||||
PinNoLiveHostWrites(*stored, record.Res, call);
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P5 (b1): THE PRODUCER. The record's own statement about the resource, applied
|
||||
// before the serial moves so that a probe re-entered from inside the backend hook
|
||||
// below already sees it. It is an assignment and not an OR: the bit is a STATE,
|
||||
@@ -1501,7 +1469,6 @@ namespace MobileGL::MG_Pipe {
|
||||
// (BufferObject::NotePersistentMapStateChanged) rather than relying on the next
|
||||
// ordinary write to arrive.
|
||||
stored->HasLiveHostWrites = record.HasLiveHostWrites != 0;
|
||||
#endif
|
||||
|
||||
// THE SERIAL MOVES BEFORE THE BACKEND IS TOLD, and that order is load-bearing:
|
||||
// the backend stamps its own synced serial from this record inside the hook, so a
|
||||
@@ -1528,7 +1495,6 @@ namespace MobileGL::MG_Pipe {
|
||||
return true;
|
||||
}
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// Texture records can have a server consumer without the buffer ops table (Magma).
|
||||
// Their staged bytes still belong to the server before SEG_STAGE retires. Keep the
|
||||
// existing backend hooks authoritative when present, and use this fallback otherwise.
|
||||
@@ -1596,7 +1562,6 @@ namespace MobileGL::MG_Pipe {
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// The texture half of resource_subdata, and it DISPATCHES TO NOBODY at GL-call time:
|
||||
// a texture write marks a level dirty and Espryt uploads it at its own sync point, out
|
||||
@@ -1646,7 +1611,6 @@ namespace MobileGL::MG_Pipe {
|
||||
// record was accumulated, so the texels are the server's now, and that is the true
|
||||
// this returns.
|
||||
++stored->Serial;
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P5c (tx): THE STAGED BYTES ARE ADOPTED HERE, at the last instant `bytes` is known
|
||||
// alive (rule C: SEG_STAGE retires when this record does, and PendingUpload
|
||||
// deliberately holds no byte pointer). The hook copies the run into the server's
|
||||
@@ -1660,7 +1624,6 @@ namespace MobileGL::MG_Pipe {
|
||||
} else {
|
||||
AdoptTextureWithoutBackendHook(*stored, record, bytes, regions);
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1985,12 +1948,10 @@ namespace MobileGL::MG_Pipe {
|
||||
MGPipeApplier().DrawProgram = kMGPipeNullHandle;
|
||||
MGPipeApplier().DispatchProgram = kMGPipeNullHandle;
|
||||
MGPipeApplier().BoundShaderCso = kMGPipeNullHandle;
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
MGPipeApplier().BoundStreamOutputLifetimeId = 0;
|
||||
// Begin is emitted once per span, not on make-current. Its immutable object
|
||||
// snapshot survives with the resource/program records until End or release;
|
||||
// set_context_values restores the returning context's bound lifetime id.
|
||||
#endif
|
||||
// P5e (sb, CONTRACT-P5E.md §5.6): the three binding-point windows are per-context
|
||||
// WORKING state and go with the rest of it - a returning context has its own
|
||||
// glBindBufferBase history and may not inherit the one this applier was left holding.
|
||||
@@ -2010,12 +1971,10 @@ namespace MobileGL::MG_Pipe {
|
||||
}
|
||||
|
||||
void MGPipeApplierReleaseObjectRecords() {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (MG_Config::DataArmIsRecord() &&
|
||||
(g_resourceOps == nullptr || g_resourceOps->TextureDestroy == nullptr)) {
|
||||
MG_Record::ServerStagedTexture().DropAll();
|
||||
}
|
||||
#endif
|
||||
// The served context is going away and this applier with it. Under split that is one
|
||||
// applier per served context; in the monolith there is one applier behind every
|
||||
// context, so nothing wires this - see PipeApply.h. The two serials advance here for
|
||||
@@ -2049,10 +2008,8 @@ namespace MobileGL::MG_Pipe {
|
||||
MGPipeApplier().DrawProgram = kMGPipeNullHandle;
|
||||
MGPipeApplier().DispatchProgram = kMGPipeNullHandle;
|
||||
MGPipeApplier().BoundShaderCso = kMGPipeNullHandle;
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
MGPipeApplier().BoundStreamOutputLifetimeId = 0;
|
||||
MGPipeApplier().StreamOutputSpans.clear();
|
||||
#endif
|
||||
// AND "THE WORKING HANDLES THEY COULD NAME" IS ALL OF THEM, NOT JUST THE THREE ABOVE.
|
||||
// Every framebuffer record holds eleven MGPSurface::Res naming texture and renderbuffer
|
||||
// records this function has just dropped - which is why the table itself goes above -
|
||||
@@ -2215,9 +2172,7 @@ namespace MobileGL::MG_Pipe {
|
||||
// memory the rule exists to stop it reading. One store, inert until the caps bit is
|
||||
// published, and it is what makes the two roles' third clause the same clause.
|
||||
MGPipeApplier().IsTransformFeedbackActive = values.IsTransformFeedbackActive != 0;
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
MGPipeApplier().BoundStreamOutputLifetimeId = values.BoundTransformFeedbackLifetimeId;
|
||||
#endif
|
||||
}
|
||||
|
||||
void MGPipeApplySetPatchState(const MGPPatchState& patch) {
|
||||
@@ -2352,7 +2307,6 @@ namespace MobileGL::MG_Pipe {
|
||||
// ================================================================================
|
||||
|
||||
Bool MGPipeApplyResourceCreate(const MGPResourceDesc& desc) {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (!MGPipeRespecifyIsWholeResource(desc)) {
|
||||
MGP_TRIP_WIRE_REPORT("MGPipe: Fatal{ProtocolCorruption, \"ResourceCreate.RespecifyScope\"} - "
|
||||
"resource_create cannot carry a per-level respecify extent");
|
||||
@@ -2362,7 +2316,6 @@ namespace MobileGL::MG_Pipe {
|
||||
MGP_TRIP_WIRE_REPORT("MGPipe: Fatal{ProtocolCorruption, \"ResourceCreate.BufferRange\"} - "
|
||||
"only a texture-buffer resource may carry BufOffset/BufSize");
|
||||
}
|
||||
#endif
|
||||
MOBILEGL_ASSERT(desc.Resource.Slot >= kMGPipeFirstAllocatableSlot,
|
||||
"resource_create named the reserved slot 0");
|
||||
if (desc.Resource.Slot < kMGPipeFirstAllocatableSlot) return false;
|
||||
@@ -2453,7 +2406,6 @@ namespace MobileGL::MG_Pipe {
|
||||
if (record == nullptr) return false;
|
||||
PinNoLiveHostWrites(*record, desc.Resource, "resource_respecify");
|
||||
PinRespecifyScopeCoversItsDeclaration(desc, level, desc.Resource, "resource_respecify");
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (level != nullptr &&
|
||||
(desc.Target == kMGPipeResourceTargetBuffer ||
|
||||
desc.Target == static_cast<Uint8>(MGPipeResourceTarget::Renderbuffer) ||
|
||||
@@ -2471,7 +2423,6 @@ namespace MobileGL::MG_Pipe {
|
||||
MGP_TRIP_WIRE_REPORT("MGPipe: Fatal{ProtocolCorruption, \"ResourceRespecify.BufferRange\"} - "
|
||||
"a whole non-buffer resource may not carry BufOffset/BufSize");
|
||||
}
|
||||
#endif
|
||||
|
||||
// IS THIS A REDEFINITION AT ALL? Asked BEFORE the descriptor is replaced, because the
|
||||
// stored one is the only thing there is to compare against (ID-18 M4). See
|
||||
@@ -2493,14 +2444,12 @@ namespace MobileGL::MG_Pipe {
|
||||
level != nullptr ? "one level" : "whole resource");
|
||||
record->SharedImageId = 0;
|
||||
}
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (level != nullptr && desc.Target != static_cast<Uint8>(MGPipeResourceTarget::TexBuffer)) {
|
||||
// Scope and BufOffset/BufSize are transient respecify carriers, not resource state.
|
||||
MGPipeClearRespecifiedLevel(record->Desc);
|
||||
record->Desc.BufOffset = 0;
|
||||
record->Desc.BufSize = 0;
|
||||
}
|
||||
#endif
|
||||
// THE SERIAL MOVES EITHER WAY, and for a metadata update it is the entire publication:
|
||||
// the twin re-derives its storage flags from the new mask at its next sync and decides
|
||||
// for itself whether the backend needs a recreate.
|
||||
@@ -2561,7 +2510,6 @@ namespace MobileGL::MG_Pipe {
|
||||
record->PendingUploads.clear();
|
||||
}
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P5c (tx): the staged-texture store's defined-ness and drop bookkeeping rides THE SAME
|
||||
// scope rules as the pending-set drops above - a named level redefines that one
|
||||
// (uploadTarget, level), a whole-resource respecify drops every level, and a metadata
|
||||
@@ -2578,7 +2526,6 @@ namespace MobileGL::MG_Pipe {
|
||||
else
|
||||
DefineTextureWithoutBackendHook(desc, level);
|
||||
}
|
||||
#endif
|
||||
|
||||
// resource_respecify is the catalogue's only kNeedsAck call, and the per-record half
|
||||
// of that flag is MGPipeResourceRespecifyNeedsAck(desc): glBufferStorage is a real
|
||||
@@ -2780,7 +2727,6 @@ namespace MobileGL::MG_Pipe {
|
||||
// AND ONLY A BUFFER IS HANDED ON, for resource_create's reason: the op table is the
|
||||
// buffer family's, its Destroy takes a handle whose kind that backend registered for,
|
||||
// and a texture's death is read out of the record at the sync that would have used it.
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P5c (tx): with ONE exception - the staged-texture store is keyed by the handle, so
|
||||
// the death must reach it or a recycled slot's stale levels would answer for the
|
||||
// successor. This does not hand the texture to the buffer family's Destroy.
|
||||
@@ -2791,7 +2737,6 @@ namespace MobileGL::MG_Pipe {
|
||||
MG_Record::ServerStagedTexture().Drop(
|
||||
MG_Record::StagedTextureStore::KeyForHandle(handle.Handle));
|
||||
}
|
||||
#endif
|
||||
if (static_cast<MGPipeKind>(handle.Kind) != MGPipeKind::Buffer) return;
|
||||
if (g_resourceOps != nullptr && g_resourceOps->Destroy != nullptr) {
|
||||
g_resourceOps->Destroy(handle.Handle);
|
||||
|
||||
@@ -108,7 +108,6 @@ namespace MobileGL::MG_Pipe {
|
||||
void (*Destroy)(MGPipeHandle res);
|
||||
void* (*MapPersistent)(MGPipeHandle res, Uint64 size, const void* seedBytes);
|
||||
void (*UnmapPersistent)(MGPipeHandle res);
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P5c (tx): the TEXTURE half of the resource family, appended so every positional
|
||||
// initialiser of the nine P3a members keeps its meaning. TextureSubData is called
|
||||
// from ApplyTextureUpload AFTER the gate, the accumulation and the serial, while
|
||||
@@ -143,7 +142,6 @@ namespace MobileGL::MG_Pipe {
|
||||
// pattern of its own, and read that back through the held pointer (and through the
|
||||
// backend's own host map of the import). False with the reason in `why`.
|
||||
Bool (*SelfTestExternal)(char* why, Uint64 whyBytes);
|
||||
#endif
|
||||
};
|
||||
|
||||
// Install / read the table. A null argument uninstalls, which is what a backend does at
|
||||
@@ -873,7 +871,6 @@ namespace MobileGL::MG_Pipe {
|
||||
Uint64 TextureShutterSerial = 0;
|
||||
Uint64 ContextSerial = 0;
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// ---- P5c (hd, CONTRACT-P5C §3.2): THE CURRENT VERB'S OWN HANDLES. ----------------
|
||||
//
|
||||
// Server state, written by ServerVerbSink at the top of a verb's dispatch and read by
|
||||
@@ -932,7 +929,6 @@ namespace MobileGL::MG_Pipe {
|
||||
VerbIndirectParameterBuffer = kMGPipeNullHandle;
|
||||
VerbDispatchIndirectBuffer = kMGPipeNullHandle;
|
||||
}
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
@@ -1309,13 +1305,11 @@ namespace MobileGL::MG_Pipe {
|
||||
struct MGPRespecifiedLevel {
|
||||
Uint16 UploadTarget = 0;
|
||||
Uint16 Level = 0;
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P7 PH-4: mutable mip dimensions do not follow from the base descriptor. These fields
|
||||
// are carried by the split resource_respecify record; the pull helper remains 4 bytes.
|
||||
Uint32 Width = 0;
|
||||
Uint32 Height = 0;
|
||||
Uint32 Depth = 0;
|
||||
#endif
|
||||
};
|
||||
|
||||
inline MGPRespecifiedLevel MGPipeMakeRespecifiedLevel(Uint16 uploadTarget, Uint16 level,
|
||||
@@ -1324,15 +1318,9 @@ namespace MobileGL::MG_Pipe {
|
||||
MGPRespecifiedLevel value{};
|
||||
value.UploadTarget = uploadTarget;
|
||||
value.Level = level;
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
value.Width = width;
|
||||
value.Height = height;
|
||||
value.Depth = depth;
|
||||
#else
|
||||
(void)width;
|
||||
(void)height;
|
||||
(void)depth;
|
||||
#endif
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -1483,14 +1471,12 @@ namespace MobileGL::MG_Pipe {
|
||||
// unmap_persistent: the donation ends. Never emitted by P3a's own paths; the call exists
|
||||
// so the pair is complete and the transport has both halves.
|
||||
void MGPipeApplyUnmapPersistent(const MGPHandleOnly& handle);
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P11 B2 (T0): the split server's map_persistent for a session that runs T0. The client's
|
||||
// AHardwareBuffer `ahb` (`size` bytes) becomes buffer `handle`'s resident store through the
|
||||
// backend's ImportExternal. A handle that is not a live buffer of that width, or a backend
|
||||
// without the member, declines (false); the codec answers DECLINED and the store runs T2.
|
||||
// MapPersistentRoundtrips is not moved: under split the server never counted map_persistent.
|
||||
Bool MGPipeApplyAdoptExternal(const MGPHandleOnly& handle, void* ahb, Uint64 size);
|
||||
#endif
|
||||
|
||||
// ---------------------------------------------------------------------------------
|
||||
// P3a: the five vertex-input entry points (D-G, D-H, D-I)
|
||||
|
||||
@@ -310,7 +310,6 @@ namespace MobileGL::MG_Pipe {
|
||||
// the monolith twin reads GetLinkedShaderStages() off the frontend object it is
|
||||
// handed. Named and discarded rather than left out of the signature, so the two
|
||||
// arms stay one row.
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P13 W4c: MONOLITH'S RECORD ARM READS THE RECORD'S ARCHIVE (ProgramHandleArm), so it
|
||||
// gets one: the link's snapshot, taken here, once per link - the same thing the wire
|
||||
// decodes into the record, without the codec. A copy rather than a borrow, because the
|
||||
@@ -324,7 +323,6 @@ namespace MobileGL::MG_Pipe {
|
||||
MGPipeApplyCreateShaderState(*desc, link, spirv, std::move(archive));
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
(void)linkedStages;
|
||||
(void)linkedStageCount;
|
||||
// P5e (pg): NO ARCHIVE ON THIS ARM, and the null is the arm selection rather than a
|
||||
|
||||
@@ -294,15 +294,10 @@ namespace MobileGL::MG_Pipe {
|
||||
// latch clear, so every later row for that object keeps taking the real answer. The cost of
|
||||
// that case is throughput, never silence.
|
||||
inline Bool MGPipeResourceRespecifyWantsItsReply(const MGPResourceDesc& desc) {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (MGPipeInstalledArm() != MGPipeRouteArm::kClientWire) return true;
|
||||
if (static_cast<MGPipeResourceTarget>(desc.Target) == MGPipeResourceTarget::Buffer)
|
||||
return false;
|
||||
return !MGPipeHandleIsPublished(MGPipeKind::Texture, desc.Resource);
|
||||
#else
|
||||
(void)desc;
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
inline Bool MGPipeRouteResourceRespecify(const MGPResourceDesc& desc, const void* initialBytes,
|
||||
const MGPRespecifiedLevel* level = nullptr) {
|
||||
@@ -372,12 +367,10 @@ namespace MobileGL::MG_Pipe {
|
||||
// invariant MGPipeTypes.h asserts beside the packer and which the applier's own
|
||||
// SubDataNamesABuffer already reads.
|
||||
inline Bool MGPipeSubDataWantsItsReply(const MGPSubData& record) {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// THE WIRE ARM ONLY. Everything above is an argument about a client talking to a server
|
||||
// over SEG_CMD/SEG_STAGE; a monolith run inside a split build (every unit and
|
||||
// integration-gpu lane is one) keeps the answer it always gave.
|
||||
if (MGPipeInstalledArm() == MGPipeRouteArm::kClientWire) return false;
|
||||
#endif
|
||||
// THE PULL BUILD PREPROCESSES TO EXACTLY THIS LINE AND NOTHING ELSE, which is what G1
|
||||
// compares: the arm test above sits inside the build switch, so the pull arm emits the
|
||||
// expression this function has always had.
|
||||
@@ -396,10 +389,8 @@ namespace MobileGL::MG_Pipe {
|
||||
MGPReplySlot reply = MGPipeMintReplySlot();
|
||||
MGP_ResourceSubData(&record, bytes, byteCount, regions, record.RegionCount, &reply);
|
||||
const Bool accepted = MGPipeTakeReplyBool(reply, "resource_subdata");
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// A cancelled wire upload is declined even when no server reply was requested.
|
||||
if (MGPipeInstalledArm() == MGPipeRouteArm::kClientWire) return accepted;
|
||||
#endif
|
||||
return MGPipeSubDataWantsItsReply(record) ? accepted : true;
|
||||
}
|
||||
inline void MGPipeRouteBufferSubDataResident(const MGPSubData& record, const void* bytes,
|
||||
|
||||
@@ -58,13 +58,11 @@ namespace MobileGL::MG_Pipe {
|
||||
UnmigratedVerb,
|
||||
// A call that reached an arm this build did not compile, or ran on the wrong role.
|
||||
RoleViolation,
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// A malformed peer record rejected by the server-side applier.
|
||||
ProtocolCorruption,
|
||||
// The GPU device a session's backend renders with is lost (a GPU fault or hang the driver
|
||||
// reset). Raised only through MGPipeSessionLatch below: it ends THAT session.
|
||||
DeviceLost,
|
||||
#endif
|
||||
};
|
||||
|
||||
// What the layer above installs. `line` is the FULLY FORMATTED message, so the hook forwards
|
||||
@@ -131,7 +129,6 @@ namespace MobileGL::MG_Pipe {
|
||||
[[noreturn]] void MGPipeProtocolFatal(const char* what, const char* detail);
|
||||
[[noreturn]] void MGPipeProtocolFatalAt(const char* what, ::std::uint64_t got, ::std::uint64_t expected);
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// ---- THE LATCH TWIN: A LOST GPU DEVICE ENDS ITS SESSION, NOT THE PROCESS ----------------
|
||||
//
|
||||
// MGPipeSessionFail is for a verb the backend cannot honour, and it does not return. A device
|
||||
@@ -161,7 +158,6 @@ namespace MobileGL::MG_Pipe {
|
||||
__attribute__((format(printf, 2, 3)))
|
||||
#endif
|
||||
MGPipeSessionLatch(MGPipeFatalFamily family, const char* fmt, ...);
|
||||
#endif
|
||||
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
|
||||
|
||||
@@ -17,14 +17,12 @@
|
||||
// role IS MG_State plus MG_Impl - and the edge exists only in a build that has the transport at all.
|
||||
#include <MG_Remote/Client/GpuWritePending.h>
|
||||
#endif
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// The client role's persistent-map tracker (P13 W5: the record arm's, so beside this file).
|
||||
#include <MG_State/GLState/BufferState/PersistentMapTracker.h>
|
||||
#include <MG_Util/Debug/Log.h>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <MG_Util/Metrics/PipeStats.h>
|
||||
#endif
|
||||
|
||||
namespace MobileGL::MG_State::GLState {
|
||||
namespace {
|
||||
@@ -32,7 +30,6 @@ namespace MobileGL::MG_State::GLState {
|
||||
// Starts at 1 so a zero-initialized cache slot can never carry a live buffer's id.
|
||||
std::atomic<Uint64> g_nextBufferLifetimeId{1};
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P5c (hd, CONTRACT-P5C §3.8 / §6 layer 1): the frontend BufferObject's legacy
|
||||
// accessors are a layer-1 surface. With an active transport, the pre-handle buffer
|
||||
// arm that reads them (Managers.cpp's RespecifyStorageNow / UploadRangeNow /
|
||||
@@ -66,10 +63,6 @@ namespace MobileGL::MG_State::GLState {
|
||||
if (!MG_Record::PersistentMapTracker::OnServerRole()) return;
|
||||
FatalLegacyBufferArmFromApplyThread(accessor);
|
||||
}
|
||||
#else
|
||||
// No record arm, no apply thread to refuse.
|
||||
inline void RefuseLegacyBufferArmFromApplyThread(const char*) {}
|
||||
#endif
|
||||
}
|
||||
|
||||
Uint64 BufferObject::AllocateLifetimeId() {
|
||||
@@ -97,13 +90,11 @@ namespace MobileGL::MG_State::GLState {
|
||||
}
|
||||
|
||||
BufferObject::~BufferObject() {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// Unconditional, not behind PushIsArmed(): the transport mode cannot change, but the
|
||||
// tracker is a leaked singleton whose entries are raw pointers, and an entry that
|
||||
// outlives its object is the one failure this set must not have. Forget is a no-op
|
||||
// for a buffer that was never a member.
|
||||
MG_Record::PersistentMapTracker::Instance().Forget(*this);
|
||||
#endif
|
||||
// P3a D-L: the buffer's death crosses as resource_destroy, which is the catalogue
|
||||
// call for it - no seventh NotifyStateObjectDestroyed raiser is added, because that
|
||||
// header exists for kinds that have no such call. The emit-then-free ORDER is fixed
|
||||
@@ -215,12 +206,10 @@ namespace MobileGL::MG_State::GLState {
|
||||
}
|
||||
m_size = size;
|
||||
m_resource.ResizeShadow(size);
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// The store this buffer's membership was about no longer exists, and ResizeShadow is
|
||||
// reserve+resize - a grow past the reserve reallocates - so a pushed block's source
|
||||
// base has moved too. Both are the same event to the tracker: re-read the predicate.
|
||||
NotePersistentMapStateChanged();
|
||||
#endif
|
||||
}
|
||||
|
||||
void BufferObject::Respecify(SizeT size, const void* data) {
|
||||
@@ -364,12 +353,10 @@ namespace MobileGL::MG_State::GLState {
|
||||
m_mappedRange = {0, 0};
|
||||
m_stagingBias = 0;
|
||||
m_ownsStagingData = false;
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// AFTER the reset, so the predicate reads the post-unmap state, and after the landing
|
||||
// above, so the last bytes of a write map are already on the wire when the record
|
||||
// that says "no live writer" goes out behind them.
|
||||
NotePersistentMapStateChanged();
|
||||
#endif
|
||||
}
|
||||
|
||||
void BufferObject::FlushMemoryRange(SizeT offset, SizeT length) {
|
||||
@@ -406,7 +393,6 @@ namespace MobileGL::MG_State::GLState {
|
||||
}
|
||||
|
||||
void BufferObject::SyncPersistentMappedRange() {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P5c (hd): the named refusal comes FIRST - a silent return here used to let a
|
||||
// server-side caller slip through with one MGLOG_D's worth of evidence (B3).
|
||||
//
|
||||
@@ -431,7 +417,6 @@ namespace MobileGL::MG_State::GLState {
|
||||
MG_Record::PersistentMapTracker::Instance().PushBlocksFor(*this);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (!m_isMapped) return;
|
||||
// GPU-resident: the app already wrote directly into coherent GPU memory. This is
|
||||
// the whole point of the persistent-map path - the per-draw whole-buffer re-upload
|
||||
@@ -445,7 +430,6 @@ namespace MobileGL::MG_State::GLState {
|
||||
NotifySubData(m_mappedRange.start, m_mappedRange.end - m_mappedRange.start);
|
||||
}
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
void BufferObject::PushMappedSpanBlock(SizeT offset, SizeT size) {
|
||||
// NotifySubData and not MGPipeEmitResourceSubData directly: the serial bump, the
|
||||
// defined-content promotion and the legacy-ops fallback are what the monolith span
|
||||
@@ -532,7 +516,6 @@ namespace MobileGL::MG_State::GLState {
|
||||
Bool BufferObject::HasLiveHostWritesForWire() const {
|
||||
return m_publishedLiveHostWrites;
|
||||
}
|
||||
#endif
|
||||
|
||||
void BufferObject::WritebackFromBackend(DataPtr data, SizeT atOffset) {
|
||||
MOBILEGL_ASSERT(atOffset + data.size <= m_size,
|
||||
@@ -541,7 +524,6 @@ namespace MobileGL::MG_State::GLState {
|
||||
Memcpy(m_resource.Bytes() + atOffset, data.data, data.size);
|
||||
++m_changeSerial;
|
||||
MGP_NOTE_AGGREGATE(BufferChange);
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// THE THIRD STATE'S ONLY EXIT. In a split build SyncGpuWrites does NOT clear the
|
||||
// flag before emitting, because between the emission and the answer the shadow is
|
||||
// stale and the object has no way to say so; the answer landing here is what makes it
|
||||
@@ -556,7 +538,6 @@ namespace MobileGL::MG_State::GLState {
|
||||
if (MG_Config::DataArmIsRecord() && atOffset == 0 && data.size >= m_size) {
|
||||
m_gpuWritePending = false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void BufferObject::MarkGpuWritten() {
|
||||
@@ -824,12 +805,10 @@ namespace MobileGL::MG_State::GLState {
|
||||
m_mappingAccess = (read ? BufferMappingAccessBit::Read : BufferMappingAccessBit::Null) |
|
||||
(write ? BufferMappingAccessBit::Write : BufferMappingAccessBit::Null);
|
||||
m_mappedRange = {0, m_size};
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// glMapBuffer never takes the Persistent bit, so this buffer can never join the
|
||||
// push set - but a WRITE map still mutates the shadow with no call, which is the
|
||||
// half of the live-host-writes bit that is not about the push at all.
|
||||
NotePersistentMapStateChanged();
|
||||
#endif
|
||||
|
||||
if (m_mappingAccess & BufferMappingAccessBit::Write) {
|
||||
// glMapBuffer maps from offset 0, so no bias: the allocation's own
|
||||
@@ -909,7 +888,6 @@ namespace MobileGL::MG_State::GLState {
|
||||
m_isMapped = true;
|
||||
m_mappingAccess = access;
|
||||
m_mappedRange = range;
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// BEFORE the adoption attempt below, deliberately. Under R-6 the acquisition always
|
||||
// declines, so the predicate this publishes is already final; if a later phase ever
|
||||
// mints one, the adoption path notes the change itself (it does now - the call is
|
||||
@@ -917,7 +895,6 @@ namespace MobileGL::MG_State::GLState {
|
||||
// having been made, which is the order that keeps the set conservative under both
|
||||
// answers.
|
||||
NotePersistentMapStateChanged();
|
||||
#endif
|
||||
|
||||
if (access & BufferMappingAccessBit::Persistent) {
|
||||
m_ownsStagingData = false;
|
||||
@@ -931,14 +908,11 @@ namespace MobileGL::MG_State::GLState {
|
||||
!(access & BufferMappingAccessBit::FlushExplicit) &&
|
||||
MG_Pipe::MGPipeResourceSubsystemEnabled()) {
|
||||
if (void* pushedBase = MG_Pipe::MGPipeEmitMapPersistent(*this)) {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P11 B2 made this reachable (a T0 adoption), so the tracker lets go of the
|
||||
// shadow range WHILE THE SHADOW IS STILL OURS: AdoptPersistentMap frees it, and
|
||||
// an untrack after that would mprotect pages the buffer no longer owns.
|
||||
MG_Record::PersistentMapTracker::Instance().Forget(*this);
|
||||
#endif
|
||||
m_resource.AdoptPersistentMap(pushedBase);
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// An adoption takes the buffer OUT of the push set and out of the
|
||||
// live-host-writes state: the application now writes coherent GPU memory
|
||||
// and there is nothing to ship. Under R-6 this is unreachable; it is here
|
||||
@@ -946,7 +920,6 @@ namespace MobileGL::MG_State::GLState {
|
||||
// entry and nothing did, which would have left the published bit true
|
||||
// across an adoption until unmap (latent for P11).
|
||||
NotePersistentMapStateChanged();
|
||||
#endif
|
||||
}
|
||||
return m_resource.Bytes() + range.start;
|
||||
}
|
||||
@@ -994,7 +967,6 @@ namespace MobileGL::MG_State::GLState {
|
||||
return m_resource.Bytes();
|
||||
}
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
SizeT BufferObject::ShadowAllocationBytes() const {
|
||||
// The extent of the very pointer MappedData() hands out, read by the same caller
|
||||
// (the tracker's registration) on the same thread: the same rule-E surface, the
|
||||
@@ -1002,7 +974,6 @@ namespace MobileGL::MG_State::GLState {
|
||||
RefuseLegacyBufferArmFromApplyThread("ShadowAllocationBytes");
|
||||
return m_resource.ShadowAllocationBytes();
|
||||
}
|
||||
#endif
|
||||
|
||||
Bool BufferObject::IsBackendPersistentMapped() const {
|
||||
return m_resource.IsGpuResident();
|
||||
|
||||
@@ -187,7 +187,6 @@ namespace MobileGL {
|
||||
// from every path that reads the shadow on the app's behalf.
|
||||
void SyncGpuWrites();
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// ---- P5 b1: the two things a split build has to do that a monolith does not ---
|
||||
//
|
||||
// Everything here is behind the build option AND behind
|
||||
@@ -238,7 +237,6 @@ namespace MobileGL {
|
||||
// layer-1 read of client memory like MappedData (CONTRACT-P5C rule E) and carries
|
||||
// the same apply-thread refusal.
|
||||
SizeT ShadowAllocationBytes() const;
|
||||
#endif
|
||||
|
||||
Bool IsMapped() const;
|
||||
Bool IsImmutableStorage() const;
|
||||
@@ -321,12 +319,10 @@ namespace MobileGL {
|
||||
// and, in a split build, cleared by WritebackFromBackend instead, because there
|
||||
// the answer arrives later than the request. See SyncGpuWrites' definition.
|
||||
Bool m_gpuWritePending = false;
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// The last value of MGPResourceDesc::HasLiveHostWrites this object published.
|
||||
// Behind the option so the pull build's layout - and therefore every inlined
|
||||
// constructor and accessor in it - does not move (G1).
|
||||
Bool m_publishedLiveHostWrites = false;
|
||||
#endif
|
||||
Range1D m_mappedRange;
|
||||
// The write-map staging store. MapAlignedData because the application is handed a
|
||||
// pointer into it, and biased by m_stagingBias because ARB_map_buffer_alignment
|
||||
|
||||
@@ -53,11 +53,9 @@ namespace MobileGL::MG_State::GLState {
|
||||
// never exceed what was allocated. The pull build keeps the exact count and does not
|
||||
// see this function at all: it has no tracker to spend the rounding on, and G1 holds
|
||||
// its symbol set and .text byte-identical only if allocate()'s own text does not move.
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
inline constexpr SizeT ShadowAllocationBytesFor(SizeT count) {
|
||||
return (count + SHADOW_ALLOCATION_ALIGNMENT - 1) & ~(SHADOW_ALLOCATION_ALIGNMENT - 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Allocator that gives every allocation SHADOW_ALLOCATION_ALIGNMENT (and, in a split
|
||||
// build, a page-granular size - see ShadowAllocationBytesFor). Deliberately minimal: the
|
||||
@@ -73,13 +71,8 @@ namespace MobileGL::MG_State::GLState {
|
||||
|
||||
T* allocate(SizeT count) {
|
||||
if (count == 0) return nullptr;
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
return static_cast<T*>(::operator new(ShadowAllocationBytesFor(count * sizeof(T)),
|
||||
std::align_val_t{SHADOW_ALLOCATION_ALIGNMENT}));
|
||||
#else
|
||||
return static_cast<T*>(
|
||||
::operator new(count * sizeof(T), std::align_val_t{SHADOW_ALLOCATION_ALIGNMENT}));
|
||||
#endif
|
||||
}
|
||||
void deallocate(T* pointer, SizeT) noexcept {
|
||||
::operator delete(pointer, std::align_val_t{SHADOW_ALLOCATION_ALIGNMENT});
|
||||
@@ -141,7 +134,6 @@ namespace MobileGL::MG_State::GLState {
|
||||
const SizeT reserved = std::bit_ceil(size == 0 ? SizeT{1} : size);
|
||||
m_shadow->reserve(reserved);
|
||||
m_shadow->resize(size);
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// RECORDED HERE, NOT INFERRED FROM capacity(). `reserved` is the count this
|
||||
// call asked the allocator for, so ShadowAllocationBytesFor(reserved) is the
|
||||
// block the allocator handed back whenever this reserve reallocated - and when
|
||||
@@ -155,14 +147,12 @@ namespace MobileGL::MG_State::GLState {
|
||||
// under-report costs nothing: the tracker still needs rangeEnd <= extent, and
|
||||
// size <= reserved always.
|
||||
m_shadowExtent = ShadowAllocationBytesFor(reserved);
|
||||
#endif
|
||||
}
|
||||
// Direct shadow access, used only by the backend's upload-from-shadow path,
|
||||
// which never runs for a GPU-resident (persistent) buffer.
|
||||
MapAlignedData& Shadow() { return *m_shadow; }
|
||||
const MapAlignedData& Shadow() const { return *m_shadow; }
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// The extent of the shadow's heap allocation in bytes, as ResizeShadow asked for
|
||||
// it: every byte of [Bytes(), Bytes() + this) belongs to this shadow and to
|
||||
// nothing else, and the extent is a whole number of SHADOW_ALLOCATION_ALIGNMENT
|
||||
@@ -172,7 +162,6 @@ namespace MobileGL::MG_State::GLState {
|
||||
// kill). Zero for an adopted (GPU-resident) store, whose shadow was released, and
|
||||
// for a shadow that was never sized; the tracker excludes both.
|
||||
SizeT ShadowAllocationBytes() const { return m_gpuMapped != nullptr ? 0 : m_shadowExtent; }
|
||||
#endif
|
||||
|
||||
// Transition to persistent GPU residency: adopt the backend's coherent
|
||||
// mapped base as the source of truth and drop the CPU shadow. The caller
|
||||
@@ -182,10 +171,8 @@ namespace MobileGL::MG_State::GLState {
|
||||
m_gpuMapped = mappedBase;
|
||||
m_shadow->clear();
|
||||
m_shadow->shrink_to_fit();
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// The block is gone with the shrink; the next ResizeShadow records the next one.
|
||||
m_shadowExtent = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Give the adoption back: the bytes resolve against the shadow again (which
|
||||
@@ -209,9 +196,7 @@ namespace MobileGL::MG_State::GLState {
|
||||
SharedPtr<MapAlignedData> m_shadow = MakeShared<MapAlignedData>();
|
||||
void* m_gpuMapped = nullptr;
|
||||
SharedPtr<BackendBufferResource> m_backend;
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// See ShadowAllocationBytes. Split-only so the pull build's layout does not move (G1).
|
||||
SizeT m_shadowExtent = 0;
|
||||
#endif
|
||||
};
|
||||
} // namespace MobileGL::MG_State::GLState
|
||||
|
||||
@@ -1556,12 +1556,10 @@ namespace MobileGL::MG_State {
|
||||
return it != m_transformFeedbackObjects.end() && it->second.everBound;
|
||||
}
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
Uint64 GLContext::GetTransformFeedbackLifetimeId(Uint index) const {
|
||||
const auto found = m_transformFeedbackObjects.find(index);
|
||||
return found == m_transformFeedbackObjects.end() ? 0 : found->second.lifetimeId;
|
||||
}
|
||||
#endif
|
||||
|
||||
void GLContext::MarkTransformFeedbackObjectForDeletion(Uint index) {
|
||||
if (index == 0 || !m_transformFeedbackNames.IsValid(index)) return;
|
||||
|
||||
@@ -571,9 +571,7 @@ namespace MobileGL {
|
||||
// Never returns 0 - the counter starts at 1 so a zero-initialised memo slot cannot
|
||||
// be mistaken for a live object.
|
||||
Uint64 GetBoundTransformFeedbackLifetimeId() const { return m_boundTransformFeedbackLifetimeId; }
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
Uint64 GetTransformFeedbackLifetimeId(Uint index) const;
|
||||
#endif
|
||||
// Whether the object carrying this identity still has an OPEN capture span - one
|
||||
// that glBeginTransformFeedback started and glEndTransformFeedback has not closed,
|
||||
// paused or not. A backend that hands out a bounded set of per-object slots must
|
||||
|
||||
@@ -87,7 +87,6 @@ namespace MobileGL::MG_State::GLState {
|
||||
return m_framebufferObjects.find(index) != m_framebufferObjects.end();
|
||||
}
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
SharedPtr<FramebufferObject> FramebufferState::FindFramebufferObjectByLifetimeId(Uint64 lifetimeId) const {
|
||||
for (const auto& [index, framebufferObject] : m_framebufferObjects) {
|
||||
if (framebufferObject && framebufferObject->GetLifetimeId() == lifetimeId) {
|
||||
@@ -96,5 +95,4 @@ namespace MobileGL::MG_State::GLState {
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
} // namespace MobileGL::MG_State::GLState
|
||||
|
||||
@@ -28,7 +28,6 @@ namespace MobileGL::MG_State::GLState {
|
||||
Bool ValidateName(Uint index) const;
|
||||
Bool ValidateFramebufferObject(Uint index) const;
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P5c (G6, CONTRACT-P5C §3.3/§5.4): the reverse of HandleFor() for a server that holds
|
||||
// only the handle - the named-blit consumer on a backend with no FBO twin registry
|
||||
// (Magma) resolves the verb's ReadFbo/DrawFbo to the frontend object by the lifetime
|
||||
@@ -36,7 +35,6 @@ namespace MobileGL::MG_State::GLState {
|
||||
// MGPipeFrontendKeyedRegistryScope; P3b/P4b retire it by carrying the object identity
|
||||
// in the record. nullptr when no live framebuffer owns the id.
|
||||
SharedPtr<FramebufferObject> FindFramebufferObjectByLifetimeId(Uint64 lifetimeId) const;
|
||||
#endif
|
||||
|
||||
// P2 brief D4: "did the attachment set or the default geometry of ANY framebuffer
|
||||
// move". It does NOT cover a BIND - a bind writes a BindingSlot, not the object - so
|
||||
|
||||
@@ -90,7 +90,6 @@ namespace MobileGL::MG_State::GLState {
|
||||
using PipeInputReflection = ResourceReflection;
|
||||
using PipeOutputReflection = ResourceReflection;
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P7 wave 2 package C, OQ-8 (CONTRACT-P7 §5.3): THE SHADER STORAGE BLOCKS AS DIRECTVULKAN
|
||||
// INDEXES THEM, published once at link time instead of re-derived per draw.
|
||||
//
|
||||
@@ -129,7 +128,6 @@ namespace MobileGL::MG_State::GLState {
|
||||
// it; the wire consumer does not read it today.
|
||||
Int32 dataSize = 0;
|
||||
};
|
||||
#endif
|
||||
|
||||
// Transform feedback (GL 3.0 core: glTransformFeedbackVaryings applies on
|
||||
// the NEXT link; the linked snapshot below is what draws and queries see).
|
||||
@@ -208,12 +206,10 @@ namespace MobileGL::MG_State::GLState {
|
||||
// The owned reflection snapshot. Indexed by TProgram index; see the structs above.
|
||||
Vector<UniformReflection> uniformReflection;
|
||||
Vector<BlockReflection> blockReflection;
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P7 OQ-8: DirectVulkan's storage-block index space, filled beside blockReflection in
|
||||
// SnapshotGlslangReflection. See StorageBlockReflection above for what the order is
|
||||
// and why it is not blockReflection's.
|
||||
Vector<StorageBlockReflection> storageBlocks;
|
||||
#endif
|
||||
Vector<PipeInputReflection> pipeInputReflection;
|
||||
Vector<PipeOutputReflection> pipeOutputReflection;
|
||||
// Program-level scalars glslang answers off the linked intermediates.
|
||||
@@ -526,7 +522,6 @@ namespace MobileGL::MG_State::GLState {
|
||||
v("blockMemberElement", a.blockMemberElement);
|
||||
} // 11 fields
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P7 OQ-8. Three plain fields, so the generic codec arms carry it with no new arm of their
|
||||
// own - which is the whole reason it is a table rather than hand-written bytes.
|
||||
template <class Self, class V>
|
||||
@@ -536,7 +531,6 @@ namespace MobileGL::MG_State::GLState {
|
||||
v("binding", a.binding);
|
||||
v("dataSize", a.dataSize);
|
||||
} // 3 fields
|
||||
#endif
|
||||
|
||||
// Every member EXCEPT `program`: it is null for every archived instance by construction
|
||||
// (ProgramTranslationCache.h asserts that at insert) and must never be serialized - it is
|
||||
@@ -547,11 +541,9 @@ namespace MobileGL::MG_State::GLState {
|
||||
void VisitFields(Self& a, V&& v) {
|
||||
v("uniformReflection", a.uniformReflection);
|
||||
v("blockReflection", a.blockReflection);
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// Beside blockReflection because that is where it is FILLED, and a visitor that walks
|
||||
// the table in declaration order then reads the two together.
|
||||
v("storageBlocks", a.storageBlocks);
|
||||
#endif
|
||||
v("pipeInputReflection", a.pipeInputReflection);
|
||||
v("pipeOutputReflection", a.pipeOutputReflection);
|
||||
v("lastStageIsFragment", a.lastStageIsFragment);
|
||||
@@ -634,13 +626,9 @@ namespace MobileGL::MG_State::GLState {
|
||||
#if defined(__GLIBCXX__) && !defined(_GLIBCXX_DEBUG) && (SIZE_MAX == UINT64_MAX)
|
||||
#define MGL_RESOURCEREFLECTION_SIZE 128
|
||||
#define MGL_XFBVARYING_SIZE 128
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P7 OQ-8 added storageBlocks, one more Vector, in the disaggregated build ONLY. The pull
|
||||
// number below is the one G1 measures and it has not moved.
|
||||
#define MGL_LINKARTIFACTS_SIZE 1080
|
||||
#else
|
||||
#define MGL_LINKARTIFACTS_SIZE 1056
|
||||
#endif
|
||||
#define MGL_SPIRVARTIFACTS_SIZE 88
|
||||
#elif defined(_LIBCPP_VERSION) && (SIZE_MAX == UINT64_MAX) && defined(MGL_ARTIFACT_SIZES_LIBCXX_PINNED)
|
||||
// The integrator pins these from the NDK build (brief C.4); until then this branch is inert.
|
||||
|
||||
@@ -85,7 +85,6 @@ namespace MobileGL::MG_State::GLState {
|
||||
visit("floatValues", value.floatValues);
|
||||
}
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
void SchemaWord(Uint64& hash, Uint64 word) {
|
||||
for (unsigned i = 0; i < 8; ++i) {
|
||||
hash ^= (word >> (i * 8)) & 255u;
|
||||
@@ -151,7 +150,6 @@ namespace MobileGL::MG_State::GLState {
|
||||
SchemaName(hash, "end-record");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// ---- the writer ----
|
||||
|
||||
@@ -200,20 +198,9 @@ namespace MobileGL::MG_State::GLState {
|
||||
} else if constexpr (ArchiveVector<T>) {
|
||||
WriteSequence(out, value);
|
||||
} else if constexpr (ArchiveUniformInitializer<T>) {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
VisitUniformInitializer(value, [&out](const char*, const auto& member) {
|
||||
WriteValue(out, member);
|
||||
});
|
||||
#else
|
||||
WriteValue(out, value.name);
|
||||
WriteValue(out, value.basicType);
|
||||
WriteValue(out, value.vectorSize);
|
||||
WriteValue(out, value.matrixCols);
|
||||
WriteValue(out, value.matrixRows);
|
||||
WriteValue(out, value.arraySize);
|
||||
WriteValue(out, value.intValues);
|
||||
WriteValue(out, value.floatValues);
|
||||
#endif
|
||||
} else {
|
||||
// The archive's own structs: TypeFacts, ResourceReflection, XfbVarying. ONE
|
||||
// table serves both directions, so a member added to any of them is carried by
|
||||
@@ -366,20 +353,9 @@ namespace MobileGL::MG_State::GLState {
|
||||
if (!in.Ok) return;
|
||||
}
|
||||
} else if constexpr (ArchiveUniformInitializer<T>) {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
VisitUniformInitializer(value, [&in](const char*, auto& member) {
|
||||
if (in.Ok) ReadValue(in, member);
|
||||
});
|
||||
#else
|
||||
ReadValue(in, value.name);
|
||||
ReadValue(in, value.basicType);
|
||||
ReadValue(in, value.vectorSize);
|
||||
ReadValue(in, value.matrixCols);
|
||||
ReadValue(in, value.matrixRows);
|
||||
ReadValue(in, value.arraySize);
|
||||
ReadValue(in, value.intValues);
|
||||
ReadValue(in, value.floatValues);
|
||||
#endif
|
||||
} else {
|
||||
VisitFields(value, [&in](const char*, auto& field) {
|
||||
if (in.Ok) ReadValue(in, field);
|
||||
@@ -389,16 +365,8 @@ namespace MobileGL::MG_State::GLState {
|
||||
|
||||
// v1 is retained for local monolith verification. Its native size echo is not a
|
||||
// wire compatibility fact: libstdc++ writes 1056 while unpinned libc++ writes zero.
|
||||
#if !MOBILEGL_BUILD_RECORD_ARM
|
||||
#ifdef MGL_LINKARTIFACTS_SIZE
|
||||
inline constexpr Uint64 kLinkArtifactsSizeEcho = MGL_LINKARTIFACTS_SIZE;
|
||||
#else
|
||||
inline constexpr Uint64 kLinkArtifactsSizeEcho = 0;
|
||||
#endif
|
||||
#endif
|
||||
} // namespace
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
Uint64 ProgramArtifactsSchemaFingerprint() {
|
||||
static const Uint64 fingerprint = [] {
|
||||
Uint64 hash = 1469598103934665603ull;
|
||||
@@ -412,16 +380,11 @@ namespace MobileGL::MG_State::GLState {
|
||||
}();
|
||||
return fingerprint;
|
||||
}
|
||||
#endif
|
||||
|
||||
void EncodeProgramArtifacts(const LinkArtifacts& link, const SpirvArtifacts& spirv,
|
||||
Vector<Uint8>& out) {
|
||||
PutRaw(out, kProgramArtifactsCodecVersion);
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
PutRaw(out, ProgramArtifactsSchemaFingerprint());
|
||||
#else
|
||||
PutRaw(out, kLinkArtifactsSizeEcho);
|
||||
#endif
|
||||
// `link` is walked through its own VisitFields table, which omits the live
|
||||
// SharedPtr<glslang::TProgram>: 57 of the 58 members. There is no arm here for it and
|
||||
// there must not be one - it points into a glslang arena that no archived instance
|
||||
@@ -446,11 +409,7 @@ namespace MobileGL::MG_State::GLState {
|
||||
// Refuse the declared wire shape before reading any field. In v2 the second word
|
||||
// follows serialization types/order; C++ container object sizes are irrelevant.
|
||||
if (version != kProgramArtifactsCodecVersion) return false;
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
if (schema != ProgramArtifactsSchemaFingerprint()) return false;
|
||||
#else
|
||||
if (schema != kLinkArtifactsSizeEcho) return false;
|
||||
#endif
|
||||
|
||||
ReadValue(in, link);
|
||||
ReadValue(in, spirv);
|
||||
|
||||
@@ -60,7 +60,6 @@ namespace MobileGL::MG_State::GLState {
|
||||
|
||||
// Bumped whenever the byte format changes in a way a previous reader would misread. A
|
||||
// reader that sees a different word REFUSES; it never tries to guess a layout.
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// 3 since P7 wave 2 package C (OQ-8): LinkArtifacts gained `storageBlocks`, so a v2 reader
|
||||
// would run out of bytes in the middle of the stream rather than notice. The schema
|
||||
// fingerprint beside the version would catch it on its own - it is derived from the
|
||||
@@ -72,9 +71,6 @@ namespace MobileGL::MG_State::GLState {
|
||||
// Derived from the actual VisitFields order/names, container element schemas and scalar
|
||||
// representations. No native container size/offset is included. Also checked in Hello.
|
||||
Uint64 ProgramArtifactsSchemaFingerprint();
|
||||
#else
|
||||
inline constexpr Uint32 kProgramArtifactsCodecVersion = 1;
|
||||
#endif
|
||||
|
||||
// Appends the archive to `out` (which is not cleared, so a caller may frame it). Never
|
||||
// fails: everything it walks is owned plain data.
|
||||
|
||||
@@ -106,7 +106,6 @@ namespace {
|
||||
return bracket == MobileGL::String::npos ? name : name.substr(0, bracket);
|
||||
}
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P7 OQ-8. ShaderStage -> EShLanguage, so a reflection record's `stages` mask (which is an
|
||||
// EShLanguageMask) can be tested against the stage of a snapshotted shader. Written here
|
||||
// rather than lifted into a shared header because this is the only caller and
|
||||
@@ -127,7 +126,6 @@ namespace {
|
||||
}
|
||||
return EShLangCount;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Element index of an arrayed interface-block instance: "GOKU[3]" -> 3, "GOKU" -> 0.
|
||||
// Reflection spells arrayed instances exactly this way (glslang expands the instance
|
||||
@@ -1758,17 +1756,14 @@ namespace MobileGL::MG_State::GLState {
|
||||
}
|
||||
|
||||
SnapshotGlslangReflection();
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P7 OQ-8. Guarded at the CALL as well as at the definition, which is not belt and
|
||||
// braces: LinkArtifacts::storageBlocks does not exist in a pull build, so neither can
|
||||
// the function that fills it, and G1 requires this translation unit to be statement
|
||||
// for statement what it was there.
|
||||
SnapshotStorageBlockIndexSpace();
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P7 wave 2 package C, OQ-8 (CONTRACT-P7 §5.3): DIRECTVULKAN'S STORAGE-BLOCK INDEX SPACE,
|
||||
// BUILT ONCE HERE INSTEAD OF PER DRAW.
|
||||
//
|
||||
@@ -1847,7 +1842,6 @@ namespace MobileGL::MG_State::GLState {
|
||||
MGLOG_D("ProgramObject %u: Reflection - %zu storage block(s) in the backend index space",
|
||||
in.externalIndex, artifacts.storageBlocks.size());
|
||||
}
|
||||
#endif
|
||||
|
||||
// The last thing DoReflection does, and the thing that lets everything after it stop
|
||||
// caring that a glslang::TProgram ever existed: copy every reflection record the GL query
|
||||
|
||||
@@ -207,14 +207,12 @@ namespace MobileGL::MG_State::GLState {
|
||||
// one and overwritten the qualifier. See the definition for why the invented binding is
|
||||
// deliberately left in place for the backends' own use.
|
||||
void SeedDefaultStorageBlockBindings();
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P7 wave 2 package C, OQ-8: builds LinkArtifacts::storageBlocks - DirectVulkan's own
|
||||
// storage-block index space - out of the reflection snapshot the call above produced,
|
||||
// so the backends stop re-deriving it with SPIRV-Reflect on every draw. Runs after
|
||||
// SnapshotGlslangReflection because it reads blockReflection. See the definition for
|
||||
// which order it has to reproduce and why each clause of it is derivable here.
|
||||
void SnapshotStorageBlockIndexSpace();
|
||||
#endif
|
||||
Bool ValidateFragmentOutputLocations();
|
||||
Bool ResolveTransformFeedbackVaryings();
|
||||
void ResolveGsTriangleStripCapture(const glslang::TIntermediate* captureIntermediate);
|
||||
|
||||
@@ -48,10 +48,8 @@ namespace MobileGL::MG_State::GLState {
|
||||
using BlockReflection = ResourceReflection;
|
||||
using PipeInputReflection = ResourceReflection;
|
||||
using PipeOutputReflection = ResourceReflection;
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P7 OQ-8: DirectVulkan's storage-block index space, carried in LinkArtifacts.
|
||||
using StorageBlockReflection = MobileGL::MG_State::GLState::StorageBlockReflection;
|
||||
#endif
|
||||
using XfbVarying = MobileGL::MG_State::GLState::XfbVarying;
|
||||
using LinkArtifacts = MobileGL::MG_State::GLState::LinkArtifacts;
|
||||
using SpirvArtifacts = MobileGL::MG_State::GLState::SpirvArtifacts;
|
||||
|
||||
@@ -1205,65 +1205,11 @@ namespace {
|
||||
// always true) and the production-path probe pair in MG_Test/SanityTest.cpp and
|
||||
// MG_Test/Buffer/SplitBufferTest.cpp, both of which go red when the producer is
|
||||
// deleted. This skip is what the build-verify-split lane exists to make visible.
|
||||
#elif MOBILEGL_BUILD_RECORD_ARM
|
||||
#else
|
||||
// P13 W5: the producer is the record arm's (BufferObject::NotePersistentMapStateChanged),
|
||||
// so the wire is retired wherever that arm is built (PipeApply.cpp's pin follows it).
|
||||
GTEST_SKIP() << "P5 gave HasLiveHostWrites a producer, so the always-false wire is retired "
|
||||
"wherever the record arm is built; PinLiveHostWritesNamesABuffer replaces it";
|
||||
#elif !MGTEST_HAVE_FORK
|
||||
GTEST_SKIP() << "no fork on this platform; the wire's verdict is std::abort()";
|
||||
#else
|
||||
ApplierGuard guard;
|
||||
const MGPipeHandle res{8, 4};
|
||||
const Uint8 bytes[64] = {};
|
||||
MGPipeApplyResourceCreate(BufferDesc(res, 0, 55));
|
||||
MGPipeApplyResourceRespecify(BufferDesc(res, 256, 55), nullptr);
|
||||
|
||||
// The negative control: with the flag clear the same call is silent and answers
|
||||
// normally, so what follows is the flag firing and not the call.
|
||||
EXPECT_EQ(MGPipeApplyMapPersistent(BufferHandle(res), 256, bytes), nullptr);
|
||||
EXPECT_EQ(ReadLog().find("PipeLiveHostWrites"), std::string::npos);
|
||||
|
||||
struct Drive {
|
||||
MGPipeHandle Res;
|
||||
const char* Call;
|
||||
};
|
||||
const Drive drives[] = {
|
||||
{res, "map_persistent"}, {res, "resource_respecify"}, {res, "resource_subdata"},
|
||||
{res, "resource_flush_range"}, {res, "resource_readback"},
|
||||
};
|
||||
for (const Drive& drive : drives) {
|
||||
const ChildResult child = RunInChild([&drive, &bytes]() {
|
||||
// Set in the CHILD: the parent's applier must stay honest for the next drive.
|
||||
MGPipeApplier().Resources[drive.Res.Slot].HasLiveHostWrites = true;
|
||||
const String call = drive.Call;
|
||||
if (call == "map_persistent") {
|
||||
MGPipeApplyMapPersistent(MGPHandleOnly{drive.Res, static_cast<Uint32>(MGPipeKind::Buffer), 0},
|
||||
256, bytes);
|
||||
} else if (call == "resource_respecify") {
|
||||
MGPResourceDesc desc{};
|
||||
desc.Resource = drive.Res;
|
||||
desc.Width = 256;
|
||||
desc.GlNameForDiag = 55;
|
||||
MGPipeApplyResourceRespecify(desc, nullptr);
|
||||
} else if (call == "resource_subdata") {
|
||||
MGPSubData record{};
|
||||
record.Res = drive.Res;
|
||||
MGPipeSetSubDataBufferRange(record, 0, 64);
|
||||
MGPipeApplyResourceSubData(record, bytes);
|
||||
} else if (call == "resource_flush_range") {
|
||||
MGPipeApplyResourceFlushRange(MGPFlushRange{drive.Res, 0, 64, 0, 0}, bytes);
|
||||
} else {
|
||||
MGPipeApplyResourceReadback(MGPReadback{drive.Res, 0, 256});
|
||||
}
|
||||
});
|
||||
EXPECT_TRUE(DiedOfAbort(child))
|
||||
<< drive.Call << ": " << DescribeStatus(child) << "; log: " << child.Log;
|
||||
const std::string wanted =
|
||||
std::string("Fatal{PipeLiveHostWrites} ") + drive.Call + " {slot=8, gen=4}";
|
||||
EXPECT_NE(child.Log.find(wanted), std::string::npos)
|
||||
<< "wanted \"" << wanted << "\"; log: " << child.Log;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -529,14 +529,10 @@ TEST(ProgramArtifactsCodec, AVersionMismatchIsRefused) {
|
||||
// TProgram, which is null for every archived instance by construction and points into an arena
|
||||
// no archive owns. A codec arm for it would be a use-after-free waiting for a cache hit.
|
||||
TEST(ProgramArtifactsCodec, TheTablesVisitEveryMemberExceptTheLiveProgram) {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P7 OQ-8: 59 members, 58 visited. The 59th is still the live TProgram; the 58th is
|
||||
// storageBlocks, which follows the record arm's guard (ProgramArtifacts.h).
|
||||
EXPECT_EQ(ProgramArtifactsVisitedFieldCount<LinkArtifacts>(), 58u);
|
||||
EXPECT_EQ(ProgramArtifactsVisitedFieldCount<StorageBlockReflection>(), 3u);
|
||||
#else
|
||||
EXPECT_EQ(ProgramArtifactsVisitedFieldCount<LinkArtifacts>(), 57u);
|
||||
#endif
|
||||
EXPECT_EQ(ProgramArtifactsVisitedFieldCount<SpirvArtifacts>(), 8u);
|
||||
EXPECT_EQ(ProgramArtifactsVisitedFieldCount<ResourceReflection>(), 14u);
|
||||
EXPECT_EQ(ProgramArtifactsVisitedFieldCount<XfbVarying>(), 11u);
|
||||
|
||||
@@ -81,14 +81,10 @@ namespace {
|
||||
EXPECT_EQ(CountFields<TypeFacts>(), 20u);
|
||||
EXPECT_EQ(CountFields<ResourceReflection>(), 14u);
|
||||
EXPECT_EQ(CountFields<XfbVarying>(), 11u);
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P7 OQ-8 added LinkArtifacts::storageBlocks and its own three-field table; since P13 W5
|
||||
// the member follows the record arm (ProgramArtifacts.h), which every library builds.
|
||||
EXPECT_EQ(CountFields<StorageBlockReflection>(), 3u);
|
||||
EXPECT_EQ(CountFields<LinkArtifacts>(), 58u);
|
||||
#else
|
||||
EXPECT_EQ(CountFields<LinkArtifacts>(), 57u);
|
||||
#endif
|
||||
EXPECT_EQ(CountFields<SpirvArtifacts>(), 8u);
|
||||
}
|
||||
|
||||
|
||||
@@ -24,18 +24,14 @@ namespace MobileGL::MG_Test {
|
||||
class ScopedMonolithFrontendArm {
|
||||
public:
|
||||
ScopedMonolithFrontendArm() : m_savedTable(MG_Backend::gBackendFunctionsTable) {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
m_saved = MG_Config::MonolithTakesRecordArm;
|
||||
MG_Config::MonolithTakesRecordArm = false;
|
||||
if (MG_Config::Transport == MG_Config::TransportMode::Monolith && MG_Backend::pActiveBackendObject) {
|
||||
MG_Backend::gBackendFunctionsTable = MG_Backend::pActiveBackendObject->GetBackendFunctions();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
~ScopedMonolithFrontendArm() {
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
MG_Config::MonolithTakesRecordArm = m_saved;
|
||||
#endif
|
||||
MG_Backend::gBackendFunctionsTable = m_savedTable;
|
||||
}
|
||||
ScopedMonolithFrontendArm(const ScopedMonolithFrontendArm&) = delete;
|
||||
|
||||
@@ -11,9 +11,7 @@
|
||||
|
||||
#include <Config.h>
|
||||
#include <MG_Util/Debug/Log.h>
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
#include <MG_Backend/DirectVulkan/Renderer/WireDepthResolveProbe.h>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
@@ -2479,7 +2477,6 @@ namespace MobileGL::MG_Util::SelfTest {
|
||||
return findings;
|
||||
}
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
Optional<DriverBugFinding> DescribeDepthStencilResolvePassBug(
|
||||
const MG_Backend::DirectVulkan::WireDepthResolveProbeMeasurement& measurement, Bool shaderStencilExport) {
|
||||
using namespace MG_Backend::DirectVulkan;
|
||||
@@ -2546,5 +2543,4 @@ namespace MobileGL::MG_Util::SelfTest {
|
||||
}
|
||||
return findings;
|
||||
}
|
||||
#endif
|
||||
} // namespace MobileGL::MG_Util::SelfTest
|
||||
|
||||
@@ -9,9 +9,7 @@
|
||||
#pragma once
|
||||
#include <Includes.h>
|
||||
#include <MG_Util/BackendLoaders/OpenGL/Loader.h>
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
#include <MG_Backend/DirectVulkan/Renderer/WireDepthResolveArm.h>
|
||||
#endif
|
||||
|
||||
namespace MobileGL::MG_Util::SelfTest {
|
||||
// ===================== KNOWN DRIVER BUGS =====================
|
||||
@@ -347,7 +345,6 @@ namespace MobileGL::MG_Util::SelfTest {
|
||||
// so an unaffected device renders an empty section rather than a wall of "not affected".
|
||||
Vector<DriverBugFinding> CollectGlesKnownDriverBugs(const MG_External::GLESFunctionsTable& gl);
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// ===================== KNOWN VULKAN DRIVER BUGS =====================
|
||||
//
|
||||
// The DirectVulkan (Magma) backend's table, under the rules above: a control per probe, an
|
||||
@@ -376,5 +373,4 @@ namespace MobileGL::MG_Util::SelfTest {
|
||||
|
||||
// Every known driver bug this Vulkan driver actually has.
|
||||
Vector<DriverBugFinding> CollectVulkanKnownDriverBugs(const VulkanDriverBugProbeContext& context);
|
||||
#endif
|
||||
} // namespace MobileGL::MG_Util::SelfTest
|
||||
|
||||
@@ -3028,7 +3028,6 @@ namespace MobileGL::MG_Util::SelfTest {
|
||||
graphicsQueueFamilyIndex, deviceExtensions, features,
|
||||
vkGetPhysicalDeviceFeatures2Fn, vkGetPhysicalDeviceProperties2Fn);
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// The "Known Driver Bugs" section's Vulkan table (DriverBugProbes.h): each probe makes its
|
||||
// own throwaway device on the physical device picked above.
|
||||
{
|
||||
@@ -3040,7 +3039,6 @@ namespace MobileGL::MG_Util::SelfTest {
|
||||
bugContext.deviceExtensions = &deviceExtensions;
|
||||
builder.report.knownDriverBugs = CollectVulkanKnownDriverBugs(bugContext);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (HasVkExtension(deviceExtensions, VK_KHR_DRAW_INDIRECT_COUNT_EXTENSION_NAME)) {
|
||||
builder.Pass("VK_KHR_draw_indirect_count",
|
||||
|
||||
@@ -23,6 +23,9 @@ RULES = (
|
||||
'MOBILEGL_PIPE_LEGACY_MEMOS)'),
|
||||
(re.compile(r'\bMGB_CTX(?:_LIVE|_IDENTITY)?\b'),
|
||||
'MGB_CTX is back; backends read MG_Pipe::gPipeInputs directly since P13 W3b'),
|
||||
(re.compile(r'^\s*#\s*(?:if|ifdef|ifndef|elif)\b.*\bMOBILEGL_BUILD_RECORD_ARM\b'),
|
||||
'a preprocessor conditional names MOBILEGL_BUILD_RECORD_ARM, folded away at P13 W5 S8 - the '
|
||||
'record arm is in every library; a transport-only block takes MOBILEGL_BUILD_DISAGGREGATED'),
|
||||
)
|
||||
|
||||
|
||||
@@ -49,12 +52,15 @@ def self_test():
|
||||
planted = [('planted.cpp', '#if MOBILEGL_PIPE_PUSH\nx();\n#endif\n'),
|
||||
('planted.h', ' # elif !MOBILEGL_PIPE_LEGACY_MEMOS && FOO\n'),
|
||||
('planted.inc', 'auto v = MGB_CTX->GetX();\n'),
|
||||
('planted2.cpp', 'if (MGB_CTX_LIVE) {}\n')]
|
||||
('planted2.cpp', 'if (MGB_CTX_LIVE) {}\n'),
|
||||
('planted3.cpp', '#if MOBILEGL_BUILD_RECORD_ARM && MOBILEGL_PIPE_POISON\n'),
|
||||
('planted4.h', '#elif !MOBILEGL_BUILD_RECORD_ARM\n')]
|
||||
for item in planted:
|
||||
assert scan([item]), f'the gate missed a planted line: {item}'
|
||||
clean = [('clean.cpp', 'features.PipePush = QueryEnvUint64("MOBILEGL_PIPE_PUSH", 0x3fff);\n'),
|
||||
('clean.h', '// MOBILEGL_PIPE_PUSH=0x7f was the P2 mask\n'),
|
||||
('clean.inc', 'MG_Pipe::gPipeInputs.GetX();\n')]
|
||||
('clean.inc', 'MG_Pipe::gPipeInputs.GetX();\n'),
|
||||
('clean2.cpp', '// MOBILEGL_BUILD_RECORD_ARM was W5\'s temporary guard\n')]
|
||||
for item in clean:
|
||||
assert not scan([item]), f'the gate fired on a legitimate line: {item}'
|
||||
print(f'retired switches self-test: {len(planted)} planted lines caught, {len(clean)} clean lines left alone')
|
||||
@@ -68,7 +74,8 @@ def main():
|
||||
print(f'::error::{line}')
|
||||
if findings:
|
||||
return 1
|
||||
print('retired switches: none of MOBILEGL_PIPE_PUSH / MOBILEGL_PIPE_LEGACY_MEMOS (as #if) or MGB_CTX in source')
|
||||
print('retired switches: none of MOBILEGL_PIPE_PUSH / MOBILEGL_PIPE_LEGACY_MEMOS / MOBILEGL_BUILD_RECORD_ARM '
|
||||
'(as #if) or MGB_CTX in source')
|
||||
return 0
|
||||
|
||||
|
||||
|
||||
@@ -82,9 +82,7 @@ FLOW_STOP = re.compile(r"^\s*(?:case\b|default\s*:|break\s*;|return\b|continue\s
|
||||
PP_OPEN = re.compile(r"^\s*#\s*(?:if|ifdef|ifndef)\b")
|
||||
PP_CLOSE = re.compile(r"^\s*#\s*endif\b")
|
||||
PP_STOP = re.compile(r"^\s*#\s*(?:else|elif)\b")
|
||||
# P13 W5: the record arm's own guard, MOBILEGL_BUILD_RECORD_ARM, is the same build-selecting kind of
|
||||
# opener (it equals the disaggregated build until W5's flip and is unifdef'd away after it).
|
||||
PP_DISAGGREGATED = re.compile(r"^\s*#\s*(?:if|ifdef)\s+MOBILEGL_BUILD_(?:DISAGGREGATED|RECORD_ARM)\s*$")
|
||||
PP_DISAGGREGATED = re.compile(r"^\s*#\s*(?:if|ifdef)\s+MOBILEGL_BUILD_DISAGGREGATED\s*$")
|
||||
|
||||
|
||||
def strip_code(text: str) -> str:
|
||||
@@ -361,24 +359,6 @@ SELF_TEST_FIXTURES = (
|
||||
"#endif\n"
|
||||
" WireDeclineTally::Count(WireDeclineSite::GhostRow);\n"
|
||||
"}\n", 1, NO_LOG),
|
||||
("log only on the other side of a RECORD_ARM #else", _DEF_GHOST,
|
||||
"void F(int x) {\n"
|
||||
"#if MOBILEGL_BUILD_RECORD_ARM\n"
|
||||
" MGLOG_W(\"x\");\n"
|
||||
"#else\n"
|
||||
" MGLOG_D(\"compiled away\");\n"
|
||||
"#endif\n"
|
||||
" WireDeclineTally::Count(WireDeclineSite::GhostRow);\n"
|
||||
"}\n", 1, NO_LOG),
|
||||
("count under its log across a column-0 RECORD_ARM #if/#endif", _DEF_GHOST,
|
||||
"void F(int x) {\n"
|
||||
" if (x) {\n"
|
||||
" MGLOG_W(\"x\");\n"
|
||||
"#if MOBILEGL_BUILD_RECORD_ARM\n"
|
||||
" WireDeclineTally::Count(WireDeclineSite::GhostRow);\n"
|
||||
"#endif\n"
|
||||
" }\n"
|
||||
"}\n", 0, "0 unlogged, 0 unknown"),
|
||||
("log only under a #if 0 above the Count", _DEF_GHOST,
|
||||
"void F(int x) {\n"
|
||||
" if (x) {\n"
|
||||
|
||||
Reference in New Issue
Block a user