diff --git a/MobileGL/ConfigLoader.cpp b/MobileGL/ConfigLoader.cpp index 9af9c376..93fc8aac 100644 --- a/MobileGL/ConfigLoader.cpp +++ b/MobileGL/ConfigLoader.cpp @@ -465,10 +465,10 @@ namespace MobileGL::MG_ConfigLoader { String lowered = value; std::transform(lowered.begin(), lowered.end(), lowered.begin(), [](unsigned char c) { return static_cast(std::tolower(c)); }); - // P13 W4: the backends switch one at a time. Espryt's record arm runs in monolith from W4a; - // Magma's verb entry points pick one arm for every family at once, so it joins at its - // single flip, after Espryt's last family. - const Bool backendReady = MG_Config::ActiveBackendType == BackendType::DirectGLES; + // P13 W4: both backends' monolith runs the record arm - Espryt's from W4a, family by family; + // Magma's at its single flip (its verb entry points pick one arm for every family at once). + const Bool backendReady = MG_Config::ActiveBackendType == BackendType::DirectGLES || + MG_Config::ActiveBackendType == BackendType::DirectVulkan; MG_Config::MonolithTakesRecordArm = backendReady; if (lowered.empty() || lowered == "record") return; if (lowered == "frontend") { diff --git a/MobileGL/MG_Backend/DirectVulkan/DirectVulkan.cpp b/MobileGL/MG_Backend/DirectVulkan/DirectVulkan.cpp index 11775400..443a96c4 100644 --- a/MobileGL/MG_Backend/DirectVulkan/DirectVulkan.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/DirectVulkan.cpp @@ -40,7 +40,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { namespace { #if MOBILEGL_BUILD_DISAGGREGATED void RejectWireLegacyBuffer() { - if (MG_Config::Transport != MG_Config::TransportMode::Monolith) { + if (MG_Config::DataArmIsRecord()) { MGLOG_F("MGPipe: Fatal{RoleViolation, \"buffer-legacy-arm\"} (Magma P7 buffer consumer)"); std::abort(); } @@ -647,7 +647,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { void MultiDrawElementsIndirect(GLenum mode, GLenum type, const void* indirect, GLsizei drawcount, GLsizei stride) { #if MOBILEGL_BUILD_DISAGGREGATED - if (MG_Config::Transport != MG_Config::TransportMode::Monolith) { + if (MG_Config::DataArmIsRecord()) { DrawWireIndirect(mode, type, indirect, drawcount, stride, true); return; } @@ -658,7 +658,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { } void MultiDrawArraysIndirect(GLenum mode, const void* indirect, GLsizei drawcount, GLsizei stride) { #if MOBILEGL_BUILD_DISAGGREGATED - if (MG_Config::Transport != MG_Config::TransportMode::Monolith) { + if (MG_Config::DataArmIsRecord()) { DrawWireIndirect(mode, 0, indirect, drawcount, stride, false); return; } @@ -715,7 +715,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { void MultiDrawElementsIndirectCount(GLenum mode, GLenum type, const void* indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride) { #if MOBILEGL_BUILD_DISAGGREGATED - if (MG_Config::Transport != MG_Config::TransportMode::Monolith) { + if (MG_Config::DataArmIsRecord()) { DrawWireIndirect(mode, type, indirect, maxdrawcount, stride, true, true, drawcount); return; } @@ -727,7 +727,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { void MultiDrawArraysIndirectCount(GLenum mode, const void* indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride) { #if MOBILEGL_BUILD_DISAGGREGATED - if (MG_Config::Transport != MG_Config::TransportMode::Monolith) { + if (MG_Config::DataArmIsRecord()) { DrawWireIndirect(mode, 0, indirect, maxdrawcount, stride, false, true, drawcount); return; } @@ -805,7 +805,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { } void DrawElementsIndirect(GLenum mode, GLenum type, const void* indirect) { #if MOBILEGL_BUILD_DISAGGREGATED - if (MG_Config::Transport != MG_Config::TransportMode::Monolith) { + if (MG_Config::DataArmIsRecord()) { DrawWireIndirect(mode, type, indirect, 1, 0, true); return; } @@ -870,7 +870,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { } void DrawArraysIndirect(GLenum mode, const void* indirect) { #if MOBILEGL_BUILD_DISAGGREGATED - if (MG_Config::Transport != MG_Config::TransportMode::Monolith) { + if (MG_Config::DataArmIsRecord()) { DrawWireIndirect(mode, 0, indirect, 1, 0, false); return; } @@ -948,7 +948,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { // DispatchComputeIndirect takes its transport branch into DispatchWireComputeIndirect // (WireDraw.inc), which issues vkCmdDispatchIndirect from the verb's store. The CPU read // that was here (ReadWireBuffer, a whole-GPU wait once a shader had written the store) is gone. - if (MG_Config::Transport != MG_Config::TransportMode::Monolith) { + if (MG_Config::DataArmIsRecord()) { pVulkanRenderer->DispatchComputeIndirect(indirect); return; } @@ -1015,7 +1015,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { void ShaderStorageBlockBinding(GLuint program, const GLchar* storageBlockName, GLuint storageBlockBinding) { #if MOBILEGL_BUILD_DISAGGREGATED - if (MG_Config::Transport != MG_Config::TransportMode::Monolith) { + if (MG_Config::DataArmIsRecord()) { auto& state = MG_Pipe::MGPipeApplier(); const auto handle = state.VerbStorageBlockProgram; if (handle.Slot >= state.ShaderCsos.size() || !state.ShaderCsos[handle.Slot].Live || @@ -1141,7 +1141,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { static Bool BuildClosedLineLoopIndices(GLsizei count, GLenum type, const void* indices, Vector& outIndices) { #if MOBILEGL_BUILD_DISAGGREGATED - if (MG_Config::Transport != MG_Config::TransportMode::Monolith) { + if (MG_Config::DataArmIsRecord()) { const SizeT width = MG_Util::GetGLTypeSize(type); if ((width != 1 && width != 2 && width != 4) || count < 2) return false; const auto& bound = MG_Pipe::MGPipeApplier().IndexBuffer; @@ -1249,7 +1249,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { // batch that command buffer belongs to - the handle SetupDraw is holding goes stale, and // the next vkCmd* records into a command buffer that is no longer the frame's. Reconciling // HERE, before SetupDraw starts recording, keeps the wait out of the recording. - if (MG_Config::Transport == MG_Config::TransportMode::Monolith) { + if (!MG_Config::DataArmIsRecord()) { const auto& currentVAO = MG_Pipe::gPipeInputs.GetBoundVertexArray(); Bool clientArray = false; if (currentVAO) { @@ -1319,7 +1319,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { // offset is a hardcoded 0, so UploadAndBindIndexBuffer saw a null client pointer, // declined the whole batch and painted nothing.) #if MOBILEGL_BUILD_DISAGGREGATED - const Bool noIndexBuffer = MG_Config::Transport != MG_Config::TransportMode::Monolith + const Bool noIndexBuffer = MG_Config::DataArmIsRecord() ? MG_Pipe::MGPipeHandleIsNull(MG_Pipe::MGPipeApplier().IndexBuffer.Res) : MG_Pipe::gPipeInputs.GetBoundVertexArray()->GetIndexBufferBindingSlot().GetBoundObject() == nullptr; if (noIndexBuffer) { @@ -1451,6 +1451,8 @@ namespace MobileGL::MG_Backend::DirectVulkan { // old renderer's GPU work is long complete, and the index must // not be compared against the new renderer's restarted counter. Uint64 rendererGeneration = 0; + // P13 W4: status polls answered "not yet" while the batch was still unsubmitted. + mutable Uint32 unsubmittedPolls = 0; }; } // namespace @@ -1509,7 +1511,23 @@ namespace MobileGL::MG_Backend::DirectVulkan { return true; } // Pure status read (glGetSynciv must not flush). - return pVulkanRenderer->IsSubmitIndexComplete(sync->submitIndex); + if (pVulkanRenderer->IsSubmitIndexComplete(sync->submitIndex)) return true; +#if MOBILEGL_BUILD_DISAGGREGATED + // 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 + // ever hand the GPU. Under split the apply thread submits when it goes idle (the server's + // fence report, one ClientWaitSync(FLUSH, 0)); monolith has no idle moment, so a poller that + // keeps asking the same unsubmitted fence is that moment. The threshold keeps an ordinary + // once-a-frame status read from splitting the frame it is polled in. + constexpr Uint32 kPollsBeforeIdleFlush = 64; + if (MG_Config::RecordArmAliasesFrontend() && ++sync->unsubmittedPolls >= kPollsBeforeIdleFlush) { + sync->unsubmittedPolls = 0; + pVulkanRenderer->FlushForSyncPoint(sync->submitIndex); + return pVulkanRenderer->IsSubmitIndexComplete(sync->submitIndex); + } +#endif + return false; } namespace { diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/BufferArena.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/BufferArena.cpp index b7f9eede..e353cf90 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/BufferArena.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/BufferArena.cpp @@ -132,7 +132,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { // 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::Transport != MG_Config::TransportMode::Monolith && m_desc.persistentlyMapped) { + if (MG_Config::DataArmIsRecord() && m_desc.persistentlyMapped) { bufferDesc.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; } #endif diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/FrameContext.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/FrameContext.cpp index b225af0a..f9143ec9 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/FrameContext.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/FrameContext.cpp @@ -228,7 +228,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { // 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::Transport != MG_Config::TransportMode::Monolith) + if (MG_Config::DataArmIsRecord()) presentBarrier.srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT; #endif presentBarrier.dstAccessMask = 0; @@ -244,7 +244,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { presentBarrier.subresourceRange.layerCount = 1; vkCmdPipelineBarrier(commandBuffer, #if MOBILEGL_BUILD_DISAGGREGATED - MG_Config::Transport != MG_Config::TransportMode::Monolith + MG_Config::DataArmIsRecord() ? VK_PIPELINE_STAGE_ALL_COMMANDS_BIT : VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, #else VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.cpp index 2222bf94..00af613f 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.cpp @@ -591,7 +591,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { m_programFactory = programFactory; m_minDynamicOffsetAlignment = std::max(1, minUniformBufferOffsetAlignment); #if MOBILEGL_BUILD_DISAGGREGATED - if (MG_Config::Transport != MG_Config::TransportMode::Monolith) { + if (MG_Config::DataArmIsRecord()) { VkPhysicalDeviceProperties properties{}; vkGetPhysicalDeviceProperties(m_physicalDevice, &properties); m_wireStorageOffsetAlignment = std::max(1, properties.limits.minStorageBufferOffsetAlignment); diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VertexInputStateFactory.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VertexInputStateFactory.cpp index 472a4ebd..e46a37f8 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VertexInputStateFactory.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VertexInputStateFactory.cpp @@ -802,7 +802,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { return false; } #if MOBILEGL_BUILD_DISAGGREGATED - if (MG_Config::Transport != MG_Config::TransportMode::Monolith) { + if (MG_Config::DataArmIsRecord()) { const auto found = m_wireVertexFormatSupport.find(format); if (found != m_wireVertexFormatSupport.end()) return found->second; VkFormatProperties properties{}; diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferManager.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferManager.cpp index 40d89fa1..a9550aea 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferManager.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferManager.cpp @@ -216,8 +216,14 @@ namespace MobileGL::MG_Backend::DirectVulkan { }, .Destroy = [](auto res) { if (auto* m = WireManager("destroy")) m->DestroyWireBuffer(res); }, // Split mapping remains T2: the client owns its map and pushes exact - // modified ranges. A server pointer is never donated across the wire. - .MapPersistent = [](MG_Pipe::MGPipeHandle, Uint64, const void*) -> void* { return nullptr; }, + // modified ranges. A server pointer is never donated across the wire. Monolith's record + // arm (P13 W4) is one process: there the store IS handed over, as the legacy arm's + // AcquirePersistentMap always did. + .MapPersistent = [](MG_Pipe::MGPipeHandle res, Uint64 size, const void* seed) -> void* { + if (!MG_Config::RecordArmAliasesFrontend()) return nullptr; + auto* m = WireManager("map_persistent"); + return m != nullptr ? m->DonateWireBuffer(res, size, seed) : nullptr; + }, .UnmapPersistent = [](MG_Pipe::MGPipeHandle) {}, #if defined(__ANDROID__) // P11 B2 (T0): the one door a server pointer is still never donated through stays shut; @@ -236,7 +242,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { #if MOBILEGL_BUILD_DISAGGREGATED void VkBufferManager::RegisterWireResourceOps() { - if (MG_Config::Transport != MG_Config::TransportMode::Monolith) { + if (MG_Config::DataArmIsRecord()) { MG_Pipe::MGPipeSetResourceOps(&g_vulkanWireResourceOps); } } @@ -771,6 +777,29 @@ namespace MobileGL::MG_Backend::DirectVulkan { } // namespace #endif + // P13 W4: THE IN-PROCESS DONATION. Every wire store is already host-visible, coherent and + // persistently mapped (RespecifyWireBuffer), so in one process the client can write straight + // into it, exactly as the legacy arm's persistent maps did. The store is then the client's + // pages - T0's shape without an AHardwareBuffer - so it takes T0's flag: a readback is "done, no + // bytes" (the client reads its own pointer once this side's GPU is through with it) and a + // respecify retires the store behind the serial. It is seeded from the client's shadow, after + // any GPU use of the store has drained, because the shadow is what the application sees. + void* VkBufferManager::DonateWireBuffer(MG_Pipe::MGPipeHandle res, Uint64 size, const void* seedBytes) { + auto* resource = FindWireBuffer(res); + if (resource == nullptr || size == 0 || size != resource->size) return nullptr; + void* mapped = resource->buffer.GetMappedData(); + if (mapped == nullptr) return nullptr; + if (resource->imported) return mapped; // already donated: the same pointer + if (!WaitForWireBufferHostAccess(*resource)) return nullptr; + if (seedBytes != nullptr) std::memcpy(mapped, seedBytes, static_cast(size)); + resource->imported = true; + resource->gpuWritesPending = false; + resource->stagedCoverage.clear(); + MG_Remote::Server::StagedShadowStore::CoverageAdd(resource->stagedCoverage, 0, static_cast(size)); + ++m_sliceEpochCounter; + return mapped; + } + Bool VkBufferManager::ImportWireBuffer(MG_Pipe::MGPipeHandle res, void* ahb, Uint64 size) { #if defined(__ANDROID__) auto* resource = FindWireBuffer(res); @@ -1371,7 +1400,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { .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_DISAGGREGATED - (MG_Config::Transport != MG_Config::TransportMode::Monolith ? VkBufferUsageFlags{VK_BUFFER_USAGE_TRANSFER_DST_BIT} : 0u) | + (MG_Config::DataArmIsRecord() ? VkBufferUsageFlags{VK_BUFFER_USAGE_TRANSFER_DST_BIT} : 0u) | #endif VK_BUFFER_USAGE_TRANSFER_SRC_BIT, .memoryUsage = m_initInfo.transientMemoryUsage, diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferManager.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferManager.h index eb0cc02a..5a027919 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferManager.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferManager.h @@ -153,6 +153,9 @@ namespace MobileGL::MG_Backend::DirectVulkan { // applies unchanged. The T2 store it replaces goes through DeferWireRelease. False = not // imported (the T2 store is untouched and the client is DECLINED). Bool ImportWireBuffer(MG_Pipe::MGPipeHandle res, void* ahb, Uint64 size); + // P13 W4: monolith's record arm donates the wire store itself as the client's persistent + // map (see the definition). Null when the store cannot be handed over. + 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 diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.cpp index b611c3a0..ecbcab44 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.cpp @@ -2487,9 +2487,10 @@ namespace MobileGL::MG_Backend::DirectVulkan { return true; } auto& store = MG_Remote::Server::ServerStagedTexture(); - // This arm is only ever entered under an active transport, where the store copies. - MOBILEGL_ASSERT(store.CopiesIntoServerStorage(), - "UploadPendingWireLevels under a non-copying staged store: the wire arm is transport-only"); + // This arm runs under a transport (the store copies) or on monolith's record arm (it + // aliases the frontend's level shadows); a store that holds neither has nothing to upload. + MOBILEGL_ASSERT(store.Holds(), + "UploadPendingWireLevels under a staged store that holds no levels: the wire arm needs one"); const Uint64 key = MG_Remote::Server::StagedTextureStore::KeyForHandle(handle); const TextureInternalFormat internalFormat = static_cast(record.Desc.InternalFormat); const TextureFormatInfo formatInfo = ResolveTextureFormatInfo(internalFormat); diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp index f8ca412c..736cd462 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp @@ -3220,7 +3220,7 @@ void main() { inline ProgramFactory::CompileOptionFlags GetShaderTransformFlags(VkSurfaceTransformFlagBitsKHR preTransform) { ProgramFactory::CompileOptionFlags flags = ProgramFactory::CompileOptionBit::PositionZRemap; #if MOBILEGL_BUILD_DISAGGREGATED - const Bool wire = MG_Config::Transport != MG_Config::TransportMode::Monolith; + const Bool wire = MG_Config::DataArmIsRecord(); const auto* wireFbo = wire ? MG_Pipe::MGPipeApplier().DrawFramebuffer() : nullptr; const auto currentDrawFBO = wire ? SharedPtr{} : MG_Pipe::gPipeInputs.GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject(); @@ -5632,7 +5632,7 @@ void main() { // image is a packed depth-stencil texture attached through only one half. { #if MOBILEGL_BUILD_DISAGGREGATED - if (MG_Config::Transport != MG_Config::TransportMode::Monolith) { + if (MG_Config::DataArmIsRecord()) { const auto* fbo = MG_Pipe::MGPipeApplier().DrawFramebuffer(); if (fbo && !fbo->IsDefault) { if (fbo->Depth.Kind == MG_Pipe::kMGPipeSurfaceKindNone) depthTestEnabled = false; @@ -5866,7 +5866,7 @@ void main() { "GetOrCreatePipeline: colorAttachmentCount=%u exceeds payload capacity", payload.colorAttachmentCount); #if MOBILEGL_BUILD_DISAGGREGATED - const auto* wireFbo = MG_Config::Transport != MG_Config::TransportMode::Monolith ? + const auto* wireFbo = MG_Config::DataArmIsRecord() ? MG_Pipe::MGPipeApplier().DrawFramebuffer() : nullptr; const auto drawFboBinding = wireFbo ? SharedPtr{} : MG_Pipe::gPipeInputs.GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject(); @@ -6551,7 +6551,7 @@ void main() { MOBILEGL_ASSERT( [&] { #if MOBILEGL_BUILD_DISAGGREGATED - if (MG_Config::Transport != MG_Config::TransportMode::Monolith) { + if (MG_Config::DataArmIsRecord()) { const auto* fbo = MG_Pipe::MGPipeApplier().DrawFramebuffer(); return isDefaultFbo == (fbo != nullptr && fbo->IsDefault); } @@ -6984,7 +6984,7 @@ void main() { // the same loading frame grows it the same way there. SplitOversizedRecording(); #if MOBILEGL_BUILD_DISAGGREGATED - if (MG_Config::Transport != MG_Config::TransportMode::Monolith) { + if (MG_Config::DataArmIsRecord()) { if (!RewindWireDescriptorSetsIfDue()) return false; // The wire route returns before the monolith branch's draw-gated // sweep. Dead wire texture/renderbuffer records otherwise live @@ -7619,7 +7619,7 @@ void main() { // Before the wire branch, for SetupDraw's reason. SplitOversizedRecording(); #if MOBILEGL_BUILD_DISAGGREGATED - if (MG_Config::Transport != MG_Config::TransportMode::Monolith) { + if (MG_Config::DataArmIsRecord()) { if (!RewindWireDescriptorSetsIfDue()) return; m_textureManager->CollectGarbage(); DispatchWireCompute(numGroupsX, numGroupsY, numGroupsZ); @@ -7685,7 +7685,7 @@ void main() { SplitOversizedRecording(); #if MOBILEGL_BUILD_DISAGGREGATED // P8-SV: the wire arm's own indirect dispatch, DispatchCompute's shape one call over. - if (MG_Config::Transport != MG_Config::TransportMode::Monolith) { + if (MG_Config::DataArmIsRecord()) { if (!RewindWireDescriptorSetsIfDue()) return; m_textureManager->CollectGarbage(); DispatchWireComputeIndirect(indirect); @@ -7797,7 +7797,7 @@ void main() { #if MOBILEGL_BUILD_DISAGGREGATED // ALL_COMMANDS does not include HOST. The barrier's // HOST_WRITE access must have a matching source stage. - MG_Config::Transport != MG_Config::TransportMode::Monolith + MG_Config::DataArmIsRecord() ? VK_PIPELINE_STAGE_ALL_COMMANDS_BIT | VK_PIPELINE_STAGE_HOST_BIT : VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, #else @@ -7963,7 +7963,7 @@ void main() { void VulkanRenderer::Clear(GLbitfield mask) { #if MOBILEGL_BUILD_DISAGGREGATED - if (MG_Config::Transport != MG_Config::TransportMode::Monolith) { + if (MG_Config::DataArmIsRecord()) { const auto* fbo = MG_Pipe::MGPipeApplier().DrawFramebuffer(); if (!fbo) MagmaWireFatal("clear-framebuffer-record"); ClearAttachmentPayload payload{}; @@ -8367,7 +8367,7 @@ void main() { void VulkanRenderer::QueueClearBufferPayload(GLenum buffer, GLint drawbuffer, const ClearAttachmentPayload& clearPayload) { #if MOBILEGL_BUILD_DISAGGREGATED - if (MG_Config::Transport != MG_Config::TransportMode::Monolith) { + if (MG_Config::DataArmIsRecord()) { const auto* fbo = MG_Pipe::MGPipeApplier().DrawFramebuffer(); if (!fbo) MagmaWireFatal("clear-buffer-framebuffer-record"); ClearWireFramebuffer(*fbo,clearPayload,buffer == GL_COLOR ? drawbuffer : -1); @@ -9506,7 +9506,7 @@ void main() { GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) { #if MOBILEGL_BUILD_DISAGGREGATED - if (MG_Config::Transport != MG_Config::TransportMode::Monolith) { + if (MG_Config::DataArmIsRecord()) { BlitWireFramebuffers(srcX0,srcY0,srcX1,srcY1,dstX0,dstY0,dstX1,dstY1,mask,filter); return; } @@ -10148,7 +10148,7 @@ void main() { void VulkanRenderer::CopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height) { #if MOBILEGL_BUILD_DISAGGREGATED - if (MG_Config::Transport != MG_Config::TransportMode::Monolith) { + if (MG_Config::DataArmIsRecord()) { CopyWireFramebufferToTexture(target,level,xoffset,yoffset,x,y,width,height); return; } @@ -10467,7 +10467,7 @@ void main() { Uint CopyImageEndpointName(const CopyImageEndpoint& endpoint) { #if MOBILEGL_BUILD_DISAGGREGATED - if (MG_Config::Transport != MG_Config::TransportMode::Monolith) + if (MG_Config::DataArmIsRecord()) return endpoint.IsRenderbuffer() ? endpoint.RenderbufferHandle.Slot : endpoint.TextureHandle.Slot; #endif if (endpoint.IsRenderbuffer()) return endpoint.Renderbuffer->GetExternalIndex(); @@ -10481,7 +10481,7 @@ void main() { GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth) { #if MOBILEGL_BUILD_DISAGGREGATED - const Bool wire = MG_Config::Transport != MG_Config::TransportMode::Monolith; + const Bool wire = MG_Config::DataArmIsRecord(); MG_Pipe::MGPipeHandle dstStorageHandle = dstEndpoint.TextureHandle; if (wire && HasPendingRecordedWork() && !FlushPendingCommands()) { if (LatchWireDeviceLoss("copy-image-flush")) return; @@ -10529,7 +10529,7 @@ void main() { // render-pass manager, which is where a renderbuffer's VkImage lives. const auto resolveImage = [this](const CopyImageEndpoint& endpoint, CopyImageVkImage& out) { #if MOBILEGL_BUILD_DISAGGREGATED - if (MG_Config::Transport != MG_Config::TransportMode::Monolith) { + if (MG_Config::DataArmIsRecord()) { const Bool renderbuffer = endpoint.IsRenderbuffer(); // The same handle-keyed allocation used by wire FBO clear/draw/read. // The legacy render-pass manager owns a different, frontend-keyed map. @@ -10787,7 +10787,7 @@ void main() { const auto materializeClear = [this, &frame](const CopyImageEndpoint& endpoint) { #if MOBILEGL_BUILD_DISAGGREGATED - if (MG_Config::Transport != MG_Config::TransportMode::Monolith) return true; + if (MG_Config::DataArmIsRecord()) return true; #endif if (endpoint.IsRenderbuffer()) { return MaterializePendingClearForRenderbuffer(frame.commandBuffer, endpoint.Renderbuffer); @@ -11067,7 +11067,7 @@ void main() { void VulkanRenderer::ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* pixels) { #if MOBILEGL_BUILD_DISAGGREGATED - if (MG_Config::Transport != MG_Config::TransportMode::Monolith) { + if (MG_Config::DataArmIsRecord()) { ReadWirePixels(x,y,width,height,format,type,pixels); return; } @@ -11769,7 +11769,7 @@ void main() { #endif const auto& pixelPackBufferObject = #if MOBILEGL_BUILD_DISAGGREGATED - MG_Config::Transport != MG_Config::TransportMode::Monolith ? wireReplyHasNoPackBuffer : + MG_Config::DataArmIsRecord() ? wireReplyHasNoPackBuffer : #endif MG_Pipe::gPipeInputs.GetBufferBindingSlot(BufferTarget::PixelPack).GetBoundObject(); const auto packParams = MG_Pipe::gPipeInputs.GetPixelStoreParameters(false); @@ -12047,7 +12047,7 @@ void main() { void VulkanRenderer::GenerateMipmap(GLenum target) { #if MOBILEGL_BUILD_DISAGGREGATED - if (MG_Config::Transport != MG_Config::TransportMode::Monolith) { + if (MG_Config::DataArmIsRecord()) { GenerateWireMipmap(); return; } @@ -12156,7 +12156,7 @@ void main() { // server's staged shadow (keyed by the synced TextureResource above) and the client's // level storage is never written; in monolith the client-object path runs unchanged. const Bool allocatedMipmapStorage = - MG_Config::Transport != MG_Config::TransportMode::Monolith + MG_Config::DataArmIsRecord() ? EnsureGenerateMipmapShadowAllocated(*resource, baseMipLevel, uploadTargets) : EnsureGenerateMipmapStorageAllocated(*mipmapTexture, baseMipLevel, endMipLevel); #else @@ -12452,7 +12452,7 @@ void main() { return false; } #if MOBILEGL_BUILD_DISAGGREGATED - if (MG_Config::Transport != MG_Config::TransportMode::Monolith) + if (MG_Config::DataArmIsRecord()) return BeginWireXfbCaptureForDraw(frame); #endif const auto& program = MG_Pipe::gPipeInputs.GetTransformFeedbackProgram(); @@ -13474,7 +13474,7 @@ void main() { vertexRange.instanceCount = 1; // MONOLITH ONLY: with a transport the frontend VAO is not this side's to read at all // (the client owns those bytes and stages them itself), and this runs on the apply thread. - if (MG_Config::Transport == MG_Config::TransportMode::Monolith) { + if (!MG_Config::DataArmIsRecord()) { const auto& currentVAO = MG_Pipe::gPipeInputs.GetBoundVertexArray(); Bool clientArray = false; if (currentVAO) { @@ -13865,7 +13865,7 @@ void main() { } #if MOBILEGL_BUILD_DISAGGREGATED - if (MG_Config::Transport != MG_Config::TransportMode::Monolith) { + if (MG_Config::DataArmIsRecord()) { // Texture uploads submit on this queue with their own fences. The // renderer watermark alone cannot prove images/views are idle. if (m_textureManager && !m_textureManager->WireUploadsAreIdle()) return false; @@ -15344,7 +15344,7 @@ void main() { // CreateInstance requests Vulkan 1.1 on these platforms. A 1.2+ GPU // does not promote descriptor indexing into that application's core // API; the wire path must enable VK_EXT_descriptor_indexing instead. - (MG_Config::Transport == MG_Config::TransportMode::Monolith) && + (!MG_Config::DataArmIsRecord()) && #endif m_physicalDevice.properties.apiVersion >= VK_API_VERSION_1_2; const Bool descriptorIndexingExtension = @@ -15367,7 +15367,7 @@ void main() { m_physicalDevice.properties.apiVersion >= VK_API_VERSION_1_1 && IsExtensionSupported(availableExtensions, VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME) && IsExtensionSupported(availableExtensions, VK_KHR_DEPTH_STENCIL_RESOLVE_EXTENSION_NAME); - const Bool wireDepthResolveEnabled = MG_Config::Transport != MG_Config::TransportMode::Monolith && + const Bool wireDepthResolveEnabled = MG_Config::DataArmIsRecord() && getPhysicalDeviceProperties2 && (wireDepthResolveCore || wireDepthResolveExtensions); m_wireCreateRenderPass2 = nullptr; m_wireDepthResolveModes = m_wireStencilResolveModes = 0; @@ -15391,7 +15391,7 @@ void main() { // write the stencil aspect without it. Enabled on the same terms as the resolve // extensions above - wire arms only - and the arm declines stencil where it is absent // rather than producing an undefined aspect. - m_wireShaderStencilExport = MG_Config::Transport != MG_Config::TransportMode::Monolith && + m_wireShaderStencilExport = MG_Config::DataArmIsRecord() && IsExtensionSupported(availableExtensions, VK_EXT_SHADER_STENCIL_EXPORT_EXTENSION_NAME); if (m_wireShaderStencilExport) EnableOptionalDeviceExtension(availableExtensions, enabledDeviceExtensions, @@ -15408,7 +15408,7 @@ void main() { m_wireGetAhbProperties = nullptr; m_sharedImageSyncFd = false; #if defined(__ANDROID__) - if (MG_Config::Transport != MG_Config::TransportMode::Monolith && + if (MG_Config::DataArmIsRecord() && m_physicalDevice.properties.apiVersion >= VK_API_VERSION_1_1 && IsExtensionSupported(availableExtensions, VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_EXTENSION_NAME) && IsExtensionSupported(availableExtensions, VK_EXT_QUEUE_FAMILY_FOREIGN_EXTENSION_NAME)) { @@ -15963,7 +15963,7 @@ void main() { IsExtensionSupported(availableExtensions, VK_EXT_ROBUSTNESS_2_EXTENSION_NAME) ? VK_EXT_ROBUSTNESS_2_EXTENSION_NAME : IsExtensionSupported(availableExtensions, VK_KHR_ROBUSTNESS_2_EXTENSION_NAME) ? VK_KHR_ROBUSTNESS_2_EXTENSION_NAME : nullptr; - if (MG_Config::Transport != MG_Config::TransportMode::Monolith && robustness2Name != nullptr && + if (MG_Config::DataArmIsRecord() && robustness2Name != nullptr && getPhysicalDeviceFeatures2 != nullptr) { VkPhysicalDeviceFeatures2 featureQuery{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2}; featureQuery.pNext = &robustness2Features; @@ -15977,7 +15977,7 @@ void main() { m_wireNullDescriptor = true; } } - if (MG_Config::Transport != MG_Config::TransportMode::Monolith) { + if (MG_Config::DataArmIsRecord()) { MGLOG_I("Magma wire: robustness2 nullDescriptor %s (%s)", m_wireNullDescriptor ? "enabled" : "unavailable", robustness2Name != nullptr ? robustness2Name : "no robustness2 extension"); } @@ -16216,7 +16216,7 @@ void main() { // red when the real recording, readback or tally stops detecting it too. void VulkanRenderer::ArmWireDepthResolveOrder() { m_wirePreferShaderDepthResolve = false; - if (MG_Config::Transport == MG_Config::TransportMode::Monolith) return; + if (!MG_Config::DataArmIsRecord()) return; // ResolveWireDepthStencil's own test for the render-pass arm, less its per-format half // (the probe asks that per format): without it the shader pass is the only arm. const Bool renderPassArmAvailable = !MagmaWireForcedShaderDepthResolve() && m_wireCreateRenderPass2 && diff --git a/MobileGL/MG_IntegrationTest/CMakeLists.txt b/MobileGL/MG_IntegrationTest/CMakeLists.txt index 2c1c854c..41baa023 100644 --- a/MobileGL/MG_IntegrationTest/CMakeLists.txt +++ b/MobileGL/MG_IntegrationTest/CMakeLists.txt @@ -1251,8 +1251,12 @@ mgl_itest_join_environment(MGL_ITEST_VULKAN_HANDLE_HANDLES_ENVIRONMENT "MOBILEGL_TRANSPORT=monolith" "MOBILEGL_BACKEND_TYPE=DirectVulkan" "MGITEST_HANDLE_ARM=handles" ${MGL_ITEST_HANDLES_ARM_KNOBS} ${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_VULKAN_ENV}) +# P13 W4: THE ABA CONTROL'S SUBJECT IS THE FRONTEND ARM. The knob defeats the identity half of +# Magma's frontend-arm keys (MagmaPipeIdentityTables); monolith's record arm keys every object on +# the applier's {slot, gen}, which the knob does not - and must not - reach. So the lane pins the +# frontend arm (MOBILEGL_PIPE_DATA_ARM, a W4-W6 knob) and goes with that arm in W6. mgl_itest_join_environment(MGL_ITEST_VULKAN_HANDLE_ABA_HANDLES_ENVIRONMENT - "MOBILEGL_TRANSPORT=monolith" + "MOBILEGL_TRANSPORT=monolith" "MOBILEGL_PIPE_DATA_ARM=frontend" "MOBILEGL_BACKEND_TYPE=DirectVulkan" "MGITEST_HANDLE_ARM=aba" ${MGL_ITEST_HANDLES_ARM_KNOBS} ${MGL_ITEST_ABA_ARM_KNOBS} ${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_VULKAN_ENV}) diff --git a/MobileGL/MG_IntegrationTest/Scenarios/DescriptorPoolGrowthScenario.cpp b/MobileGL/MG_IntegrationTest/Scenarios/DescriptorPoolGrowthScenario.cpp index 7ba1b5bc..3dddbe5a 100644 --- a/MobileGL/MG_IntegrationTest/Scenarios/DescriptorPoolGrowthScenario.cpp +++ b/MobileGL/MG_IntegrationTest/Scenarios/DescriptorPoolGrowthScenario.cpp @@ -109,7 +109,8 @@ void main() { o_color = texture(uTex, vec2(0.5)); } constexpr std::uint64_t kWireDescriptorSetBudget = 2048; std::uint64_t BurstSetsHeldAtOnce() { const std::uint64_t draws = static_cast(kDraws); - return PeekSplitRuntime().transportResolved ? std::min(draws, kWireDescriptorSetBudget) : draws; + // The wire budget binds the RECORD arm, which monolith takes too from P13 W4. + return PeekSplitRuntime().dataArmIsRecord ? std::min(draws, kWireDescriptorSetBudget) : draws; } // Pools a geometric policy adds for n sets from an empty flavour: 64, 128, ... up to the cap, diff --git a/MobileGL/MG_IntegrationTest/Scenarios/WireIndirectDispatchScenario.cpp b/MobileGL/MG_IntegrationTest/Scenarios/WireIndirectDispatchScenario.cpp index c5c2f656..03ecfff0 100644 --- a/MobileGL/MG_IntegrationTest/Scenarios/WireIndirectDispatchScenario.cpp +++ b/MobileGL/MG_IntegrationTest/Scenarios/WireIndirectDispatchScenario.cpp @@ -73,6 +73,7 @@ void main() { words[gl_GlobalInvocationID.x] = source[gl_GlobalInvocationID.x]; if (!Ready()) return; m_magma = Gl().BackendName() == "DirectVulkan"; m_transport = PeekSplitRuntime().transportName; + m_recordArm = PeekSplitRuntime().dataArmIsRecord; m_mark = CompileCompute(kMarkSource); ASSERT_NE(m_mark, 0u); m_copy = CompileCompute(kCopySource); @@ -192,7 +193,10 @@ void main() { words[gl_GlobalInvocationID.x] = source[gl_GlobalInvocationID.x]; // Magma's server in THIS process: the inproc arm. Spawn and tcp servers are other // processes, and the monolith arm never takes the wire path at all. - bool CountersReadable() const { return m_magma && m_transport == "inproc"; } + // The counters are this process's: inproc, and (P13 W4) monolith on the record arm. + bool CountersReadable() const { + return m_magma && (m_transport == "inproc" || (m_transport == "monolith" && m_recordArm)); + } WireIndirectCounters Delta() const { WireIndirectCounters now{}; @@ -209,6 +213,7 @@ void main() { words[gl_GlobalInvocationID.x] = source[gl_GlobalInvocationID.x]; bool m_magma = false; std::string m_transport; + bool m_recordArm = false; WireIndirectCounters m_before{}; GLuint m_mark = 0, m_copy = 0, m_groupCounts = 0, m_source = 0; std::vector m_marks; @@ -266,7 +271,7 @@ void main() { words[gl_GlobalInvocationID.x] = source[gl_GlobalInvocationID.x]; // dependency here (P8-D recorded that half for dev), and Espryt's is its driver's. TEST_F(WireIndirectDispatchScenario, ShaderWrittenGroupCountsWithoutAnApplicationBarrierAreOrdered) { if (!Ready() || IsSkipped()) return; - if (!m_magma || m_transport == "monolith") { + if (!m_magma || !m_recordArm) { GTEST_SKIP() << "the implicit INDIRECT_COMMAND_READ barrier is the Magma wire arm's (P8-D, P8-SV)"; } Parameters({1, 1, 1}); diff --git a/MobileGL/MG_IntegrationTest/Scenarios/WireIndirectDrawScenario.cpp b/MobileGL/MG_IntegrationTest/Scenarios/WireIndirectDrawScenario.cpp index 26f0e0cf..d5b725ba 100644 --- a/MobileGL/MG_IntegrationTest/Scenarios/WireIndirectDrawScenario.cpp +++ b/MobileGL/MG_IntegrationTest/Scenarios/WireIndirectDrawScenario.cpp @@ -90,6 +90,7 @@ void main() { words[gl_GlobalInvocationID.x] = source[gl_GlobalInvocationID.x]; if (!Ready()) return; m_magma = Gl().BackendName() == "DirectVulkan"; m_transport = PeekSplitRuntime().transportName; + m_recordArm = PeekSplitRuntime().dataArmIsRecord; std::string error; m_program = CompileProgram(kVertexSource, kFragmentSource, &error); ASSERT_NE(m_program, 0u) << error; @@ -203,7 +204,10 @@ void main() { words[gl_GlobalInvocationID.x] = source[gl_GlobalInvocationID.x]; // Magma's server in THIS process: the inproc arm. Spawn and tcp servers are other // processes, and the monolith arm never takes the wire path at all. - bool CountersReadable() const { return m_magma && m_transport == "inproc"; } + // The counters are this process's: inproc, and (P13 W4) monolith on the record arm. + bool CountersReadable() const { + return m_magma && (m_transport == "inproc" || (m_transport == "monolith" && m_recordArm)); + } WireIndirectCounters Delta() const { WireIndirectCounters now{}; @@ -215,6 +219,7 @@ void main() { words[gl_GlobalInvocationID.x] = source[gl_GlobalInvocationID.x]; bool m_magma = false; std::string m_transport; + bool m_recordArm = false; WireIndirectCounters m_before{}; GLuint m_program = 0, m_copy = 0, m_vao = 0, m_vbo = 0, m_ebo = 0; GLuint m_indirect = 0, m_parameter = 0, m_source = 0; @@ -304,7 +309,7 @@ void main() { words[gl_GlobalInvocationID.x] = source[gl_GlobalInvocationID.x]; // monolith defects), left to P13 (ID-P8-13). TEST_F(WireIndirectDrawScenario, ComputeWrittenCountWordIsTheOneTheGpuReads) { if (!Ready() || IsSkipped()) return; - if (!m_magma && m_transport == "monolith" && !PeekSplitRuntime().dataArmIsRecord) { + if (!m_magma && !m_recordArm) { GTEST_SKIP() << "Espryt's monolith arm reads the count word from the frontend shadow without " "a sync (OQ15; recorded for P13, ID-P8-13)"; } @@ -316,7 +321,7 @@ void main() { words[gl_GlobalInvocationID.x] = source[gl_GlobalInvocationID.x]; glMultiDrawElementsIndirectCount(GL_TRIANGLES, GL_UNSIGNED_INT, nullptr, 0, 3, 0); }); ExpectStrips(image, {true, false, false, false}, "glMultiDrawElementsIndirectCount, shader-written count 1"); - if (m_transport == "monolith") { + if (!m_recordArm) { // DirectVulkan.cpp's monolith MultiDrawArraysIndirectCount reads this word from the // frontend shadow without SyncGpuWrites: recorded for dev (ID-P8-3), not this lane's. return; @@ -344,7 +349,7 @@ void main() { words[gl_GlobalInvocationID.x] = source[gl_GlobalInvocationID.x]; // arm records no dependency here (recorded for dev), and Espryt's is its driver's. TEST_F(WireIndirectDrawScenario, ShaderWrittenCommandsWithoutAnApplicationBarrierAreOrdered) { if (!Ready() || IsSkipped()) return; - if (!m_magma || m_transport == "monolith") { + if (!m_magma || !m_recordArm) { GTEST_SKIP() << "the implicit INDIRECT_COMMAND_READ barrier is the Magma wire arm's (P8-D)"; } Fill(m_indirect, GL_DRAW_INDIRECT_BUFFER, std::vector{Arrays(3), Arrays(2)}); diff --git a/MobileGL/MG_Pipe/PipeApply.cpp b/MobileGL/MG_Pipe/PipeApply.cpp index 71fbf0fa..a200bd03 100644 --- a/MobileGL/MG_Pipe/PipeApply.cpp +++ b/MobileGL/MG_Pipe/PipeApply.cpp @@ -1435,7 +1435,7 @@ namespace MobileGL::MG_Pipe { // existing backend hooks authoritative when present, and use this fallback otherwise. void AdoptTextureWithoutBackendHook(const MGPipeResourceRecord& stored, const MGPSubData& upload, const void* bytes, const MGPSubRegion* regions) { - if (MG_Config::Transport == MG_Config::TransportMode::Monolith || !bytes || upload.Blob.Size == 0) return; + if (!MG_Config::DataArmIsRecord() || !bytes || upload.Blob.Size == 0) return; auto& store = MG_Remote::Server::ServerStagedTexture(); const auto& desc = stored.Desc; const Uint64 key = MG_Remote::Server::StagedTextureStore::KeyForHandle(upload.Res); @@ -1445,18 +1445,20 @@ namespace MobileGL::MG_Pipe { // is adopted with the spelling that replaces the level; every other record names a RUN // of the level by its own box (fix A2: a level too large to stage whole crosses as // slabs, one record each, and the first of them is at the level's first byte). + // A client record's run: on monolith's record arm it is the frontend level shadow, which + // an aliasing store keeps by address (P13 W4). if (upload.RegionCount == 0) { store.Adopt(key, target, upload.Level, extent, bytes, - static_cast(upload.Blob.Size)); + static_cast(upload.Blob.Size), /*frontendShadow=*/true); return; } store.AdoptRun(key, target, upload.Level, extent, MG_Remote::Server::StagedTextureRunImageOffset(upload, regions), bytes, - static_cast(upload.Blob.Size)); + static_cast(upload.Blob.Size), /*frontendShadow=*/true); } void DefineTextureWithoutBackendHook(const MGPResourceDesc& desc, const MGPRespecifiedLevel* level) { - if (MG_Config::Transport == MG_Config::TransportMode::Monolith) return; + if (!MG_Config::DataArmIsRecord()) return; auto& store = MG_Remote::Server::ServerStagedTexture(); const Uint64 key = MG_Remote::Server::StagedTextureStore::KeyForHandle(desc.Resource); const auto define = [&](Uint16 target, Uint16 mip, const IntVec3& extent) { @@ -1910,7 +1912,7 @@ namespace MobileGL::MG_Pipe { void MGPipeApplierReleaseObjectRecords() { #if MOBILEGL_BUILD_DISAGGREGATED - if (MG_Config::Transport != MG_Config::TransportMode::Monolith && + if (MG_Config::DataArmIsRecord() && (g_resourceOps == nullptr || g_resourceOps->TextureDestroy == nullptr)) { MG_Remote::Server::ServerStagedTexture().DropAll(); } @@ -2686,7 +2688,7 @@ namespace MobileGL::MG_Pipe { if (static_cast(handle.Kind) == MGPipeKind::Texture) { if (g_resourceOps != nullptr && g_resourceOps->TextureDestroy != nullptr) g_resourceOps->TextureDestroy(handle.Handle); - else if (MG_Config::Transport != MG_Config::TransportMode::Monolith) + else if (MG_Config::DataArmIsRecord()) MG_Remote::Server::ServerStagedTexture().Drop( MG_Remote::Server::StagedTextureStore::KeyForHandle(handle.Handle)); }