From 20bbf9647eaef3e9b348c3324c2b677ed377064a Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Sat, 19 Sep 2026 22:11:38 -0400 Subject: [PATCH] [MG_Backend, MG_IntegrationTest] (Disaggregated): P5f fm - carry framebuffer copies and exercise Magma wire verbs --- .../DirectVulkan/Renderer/VulkanRenderer.cpp | 6 +++ .../DirectVulkan/Renderer/VulkanRenderer.h | 2 + .../DirectVulkan/Renderer/WireFramebuffer.inc | 39 +++++++++++++++++++ MobileGL/MG_IntegrationTest/CMakeLists.txt | 15 +++++++ 4 files changed, 62 insertions(+) diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp index a3a3459b..dfde03b1 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp @@ -9612,6 +9612,12 @@ 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) { + CopyWireFramebufferToTexture(target,level,xoffset,yoffset,x,y,width,height); + return; + } +#endif if (width <= 0 || height <= 0) { return; } diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h index da3801e5..ff1c8301 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h @@ -455,6 +455,8 @@ namespace MobileGL::MG_Backend::DirectVulkan { void ReadWirePixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* pixels); void GenerateWireMipmap(); + 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, diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/WireFramebuffer.inc b/MobileGL/MG_Backend/DirectVulkan/Renderer/WireFramebuffer.inc index f513ef20..d6ff6460 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/WireFramebuffer.inc +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/WireFramebuffer.inc @@ -282,6 +282,45 @@ if (mask & GL_DEPTH_BUFFER_BIT) blit(read->Depth,draw->Depth,VK_IMAGE_ASPECT_DEPTH_BIT); if (mask & GL_STENCIL_BUFFER_BIT) blit(read->Stencil,draw->Stencil,VK_IMAGE_ASPECT_STENCIL_BIT); } + void VulkanRenderer::CopyWireFramebufferToTexture(GLenum target, GLint level, GLint xoffset, GLint yoffset, + GLint x, GLint y, GLsizei width, GLsizei height) { + if (width <= 0 || height <= 0) return; + if (target != GL_TEXTURE_2D) { + RecordTextureCopyError(__func__,ErrorCode::InvalidOperation, + "CopyTexSubImage2D currently only supports GL_TEXTURE_2D destinations."); + return; + } + auto& state = MG_Pipe::MGPipeApplier(); + const auto* read = state.ReadFramebuffer(); + if (!read) MagmaWireFatal("copy-framebuffer-record"); + const auto* texture = m_textureManager->SyncTextureResourceByHandle(state.VerbCopyTexDst); + if (!texture) MagmaWireFatal("copy-destination-record"); + const VkImageAspectFlags aspect = texture->aspect; + MG_Pipe::MGPFramebufferState fake{}; + MG_Pipe::MGPSurface surface{}; + surface.Kind = MG_Pipe::kMGPipeSurfaceKindTexture; + surface.Res = state.VerbCopyTexDst; + surface.Level = static_cast(level); + WireImage source = ResolveWireImage(*read,aspect & VK_IMAGE_ASPECT_DEPTH_BIT ? read->Depth : + aspect & VK_IMAGE_ASPECT_STENCIL_BIT ? read->Stencil : read->ReadSurface,aspect); + WireImage destination = ResolveWireImage(fake,surface,aspect); + if (!source.image || !destination.image) return; + m_textureManager->FlushPendingUploads(); + TransitionWireImage(source,VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); + TransitionWireImage(destination,VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + VkImageBlit region{}; + region.srcSubresource = {aspect,source.level,source.layer,1}; + region.dstSubresource = {aspect,destination.level,destination.layer,1}; + region.srcOffsets[0] = {x,y,0}; region.srcOffsets[1] = {x+width,y+height,1}; + region.dstOffsets[0] = {xoffset,yoffset,0}; region.dstOffsets[1] = {xoffset+width,yoffset+height,1}; + if (source.isDefault) { + region.srcOffsets[0].y = static_cast(source.extent.height)-y; + region.srcOffsets[1].y = static_cast(source.extent.height)-y-height; + } + vkCmdBlitImage(m_frameContext.GetCurrent().commandBuffer,source.image,VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + destination.image,VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,1,®ion,VK_FILTER_NEAREST); + } + void VulkanRenderer::GenerateWireMipmap() { auto& state = MG_Pipe::MGPipeApplier(); const auto handle = state.VerbMipRes; diff --git a/MobileGL/MG_IntegrationTest/CMakeLists.txt b/MobileGL/MG_IntegrationTest/CMakeLists.txt index 1f2a80d4..5be75719 100644 --- a/MobileGL/MG_IntegrationTest/CMakeLists.txt +++ b/MobileGL/MG_IntegrationTest/CMakeLists.txt @@ -2378,6 +2378,21 @@ if (MOBILEGL_BUILD_DISAGGREGATED) ENVIRONMENT "${MGL_ITEST_GLES_SPLIT_ENVIRONMENT}\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/p5b-f1-${f1Slot}.log" ) endforeach() + # P5f fm: the framebuffer/copy/mipmap verbs now consume server records on Magma too. + mgl_itest_join_environment(MGL_ITEST_MAGMA_WIRE_ENVIRONMENT + "MOBILEGL_BACKEND_TYPE=DirectVulkan" "MOBILEGL_TRANSPORT=inproc" + "MOBILEGL_ITEST_REQUIRE_GPU=1" "MGITEST_SPLIT_LANE=1" + ${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_COMMON_ENV}) + foreach(fmSlot ClearBufferfi ClearBufferfv ClearBufferiv ClearBufferuiv ClearNamedFramebufferfi ClearNamedFramebufferfv ClearNamedFramebufferiv ClearNamedFramebufferuiv CopyTexImage2D CopyTexSubImage2D GenerateMipmap GenerateMipmapPackedFloat GenerateMipmapDepth) + gtest_discover_tests(MobileGLIntegrationTest + TEST_PREFIX "DirectVulkan.Split.Fm." + TEST_FILTER "F1WireScenario.${fmSlot}Pixels" + DISCOVERY_TIMEOUT 30 + PROPERTIES + LABELS "integration-magma-split" + TIMEOUT ${MGL_ITEST_TIMEOUT} + ENVIRONMENT "${MGL_ITEST_MAGMA_WIRE_ENVIRONMENT}\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/p5f-fm-${fmSlot}.log") + endforeach() endif() # P5c ct (MG_Remote/CONTRACT-P5C.md ยง5): the two control records, split-only source for the