diff --git a/MobileGL/MG_Backend/DirectGLES/Utils.cpp b/MobileGL/MG_Backend/DirectGLES/Utils.cpp index 5ad96c6c..fbc91690 100644 --- a/MobileGL/MG_Backend/DirectGLES/Utils.cpp +++ b/MobileGL/MG_Backend/DirectGLES/Utils.cpp @@ -37,6 +37,15 @@ #include namespace MobileGL::MG_Backend::DirectGLES { +#if MOBILEGL_BUILD_DISAGGREGATED + // 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& SplitReadbackPackBuffer() { + static const SharedPtr none; + return none; + } +#endif + #if MOBILEGL_BUILD_DISAGGREGATED const FormatCapabilityCache* ActiveBackendFormatCaps() { // Under a live split session the SERVER's private backend is what owns the context on the @@ -2350,7 +2359,11 @@ 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 = - MGB_CTX->GetBufferBindingSlot(BufferTarget::PixelPack).GetBoundObject(); +#if MOBILEGL_BUILD_DISAGGREGATED + MG_Config::Transport != MG_Config::TransportMode::Monolith + ? SplitReadbackPackBuffer() : +#endif + MGB_CTX->GetBufferBindingSlot(BufferTarget::PixelPack).GetBoundObject(); // Destination layout is computed from the client-side PACK parameters; only the actual pixel // rows are written so skip regions of the destination stay untouched. diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp index 0bd42cd4..b32438f8 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp @@ -3154,9 +3154,15 @@ void main() { inline ProgramFactory::CompileOptionFlags GetShaderTransformFlags(VkSurfaceTransformFlagBitsKHR preTransform) { ProgramFactory::CompileOptionFlags flags = ProgramFactory::CompileOptionBit::PositionZRemap; - const auto& currentDrawFBO = - MGB_CTX->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject(); - if (currentDrawFBO != nullptr && currentDrawFBO->IsDefaultFramebuffer()) { +#if MOBILEGL_BUILD_DISAGGREGATED + const auto* wireFbo = MG_Config::Transport != MG_Config::TransportMode::Monolith ? MG_Pipe::MGPipeApplier().DrawFramebuffer() : nullptr; + const Bool isDefault = wireFbo ? wireFbo->IsDefault : + MGB_CTX->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject()->IsDefaultFramebuffer(); +#else + const auto& currentDrawFBO = MGB_CTX->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject(); + const Bool isDefault = currentDrawFBO != nullptr && currentDrawFBO->IsDefaultFramebuffer(); +#endif + if (isDefault) { flags |= ProgramFactory::CompileOptionBit::PositionYFlip; // gl_FragCoord follows the same rule the default-framebuffer RECTANGLES follow // (GetDefaultFramebufferRectMapping): flipped for identity/180, left alone under a @@ -3420,6 +3426,9 @@ void main() { if (m_device != VK_NULL_HANDLE) { VK_VERIFY(vkDeviceWaitIdle(m_device)); } +#if MOBILEGL_BUILD_DISAGGREGATED + DestroyWireDrawPass(); +#endif OnSubmitsCompletedUpTo(m_submitCounter); DestroySubmitFencePool(); @@ -5210,7 +5219,7 @@ void main() { // A program that runs a geometry shader AND captures transform feedback. Both halves are // link-time properties, so this is safe to fold into a pipeline keyed on the program hash. - static Bool ProgramCapturesXfbFromGeometryStage(const MG_State::GLState::ProgramObject& program) { + static Bool ProgramCapturesXfbFromGeometryStage(const MagmaProgramSource& program) { if (program.GetTransformFeedbackVaryingCount() == 0) return false; // Both halves are link-time properties, so both are asked of the LAST LINK. Reading the // live attach list would let a glAttachShader that has not been linked in yet - which GL @@ -5249,12 +5258,22 @@ void main() { VkPipeline VulkanRenderer::GetOrCreatePipeline( GLenum mode, - const MG_State::GLState::ProgramObject& program, + const MagmaProgramSource& program, const ProgramFactory::VkProgramObject& programObj, ProgramFactory::CompileOptionFlags transformFlags, const MG_State::GLState::VertexArrayObject& vao, const RenderPassEntry& renderPassEntry, Bool primitiveRestartEnable) { +#if MOBILEGL_BUILD_DISAGGREGATED + return GetOrCreatePipelineWithInput(mode, program, programObj, transformFlags, + m_vertexInputStateFactory->GetOrCreateVertexInputState(vao), renderPassEntry, primitiveRestartEnable); + } + + VkPipeline VulkanRenderer::GetOrCreatePipelineWithInput(GLenum mode, const MagmaProgramSource& program, + const ProgramFactory::VkProgramObject& programObj, ProgramFactory::CompileOptionFlags transformFlags, + const VertexInputStateFactory::BackendVertexInputState& vis, const RenderPassEntry& renderPassEntry, + Bool primitiveRestartEnable) { +#endif Bool invertClockwise = transformFlags & ProgramFactory::CompileOptionBit::PositionYFlip; if (programObj.stages.empty()) { MGLOG_D("GetOrCreatePipeline skipped: program has no shader stages"); @@ -5272,7 +5291,9 @@ void main() { // payload key on the resolved LAYOUT hash instead, so draws over identical // layouts share one pipeline. // The one-arg fetch rides the VAO's state-pointer memo (no hash, no map). +#if !MOBILEGL_BUILD_DISAGGREGATED auto& vis = m_vertexInputStateFactory->GetOrCreateVertexInputState(vao); +#endif const Uint64 vertexLayoutHash = vis.layoutHash; const Uint64 renderPassHash = renderPassEntry.hash; // The pipeline-relevant subset only: glViewport / glScissor / glBlendColor / glStencilMask @@ -5506,6 +5527,16 @@ void main() { // (stencil) test always passes and nothing is written - even when the bound // image is a packed depth-stencil texture attached through only one half. { +#if MOBILEGL_BUILD_DISAGGREGATED + if (MG_Config::Transport != MG_Config::TransportMode::Monolith) { + const auto* fbo = MG_Pipe::MGPipeApplier().DrawFramebuffer(); + if (fbo && !fbo->IsDefault) { + if (fbo->Depth.Kind == MG_Pipe::kMGPipeSurfaceKindNone) depthTestEnabled = false; + if (fbo->Stencil.Kind == MG_Pipe::kMGPipeSurfaceKindNone) stencilTestEnabled = false; + } + } else +#endif + { const auto& gatingFbo = MGB_CTX->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject(); if (gatingFbo != nullptr && !gatingFbo->IsDefaultFramebuffer()) { @@ -5519,6 +5550,7 @@ void main() { } } } + } const StencilFaceState& frontStencil = MGB_CTX->GetStencilState(StencilFace::Front); const StencilFaceState& backStencil = MGB_CTX->GetStencilState(StencilFace::Back); const VkPolygonMode requestedPolygonMode = @@ -5726,12 +5758,31 @@ void main() { MOBILEGL_ASSERT(payload.colorAttachmentCount <= PipelineFactory::PipelineCreatePayload::kMaxColorAttachments, "GetOrCreatePipeline: colorAttachmentCount=%u exceeds payload capacity", payload.colorAttachmentCount); +#if MOBILEGL_BUILD_DISAGGREGATED + const auto* wireFbo = MG_Config::Transport != MG_Config::TransportMode::Monolith ? + MG_Pipe::MGPipeApplier().DrawFramebuffer() : nullptr; + const auto drawFboBinding = wireFbo ? SharedPtr{} : + MGB_CTX->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject(); +#else const auto& drawFboBinding = MGB_CTX->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject(); +#endif MOBILEGL_ASSERT(drawFboBinding != nullptr, "GetOrCreatePipeline: draw framebuffer is null"); +#if MOBILEGL_BUILD_DISAGGREGATED + const Bool isDefaultDrawFbo = wireFbo ? wireFbo->IsDefault : drawFboBinding->IsDefaultFramebuffer(); + Array wireDrawBuffers{}; + if (wireFbo) for (SizeT i = 0; i < wireDrawBuffers.size(); ++i) + wireDrawBuffers[i] = wireFbo->DrawBuffers[i] < 0 ? FramebufferAttachmentType::None : + static_cast(static_cast(FramebufferAttachmentType::Color0) + wireFbo->DrawBuffers[i]); + const auto& drawBuffers = wireFbo ? wireDrawBuffers : drawFboBinding->GetDrawBuffers(); +#else const Bool isDefaultDrawFbo = drawFboBinding->IsDefaultFramebuffer(); const auto& drawBuffers = drawFboBinding->GetDrawBuffers(); +#endif auto resolveCompleteColorAttachmentTexture = [&](Uint32 drawBufferIndex) -> MG_State::GLState::ITextureObject* { +#if MOBILEGL_BUILD_DISAGGREGATED + if (wireFbo) return nullptr; +#endif if (isDefaultDrawFbo || drawBufferIndex >= drawBuffers.size()) { return nullptr; } @@ -5782,6 +5833,15 @@ void main() { attachmentColorWriteMask = 0; effectiveBlendEnabled = false; } +#if MOBILEGL_BUILD_DISAGGREGATED + if (wireFbo && !isDefaultDrawFbo) { + const Int32 slot = wireFbo->DrawBuffers[i]; + if (slot < 0 || wireFbo->Color[slot].Kind == MG_Pipe::kMGPipeSurfaceKindNone) { + attachmentColorWriteMask = 0; + effectiveBlendEnabled = false; + } + } else +#endif if (!isDefaultDrawFbo && i < drawBuffers.size()) { const auto drawBuffer = drawBuffers[i]; colorAttachmentTexture = resolveCompleteColorAttachmentTexture(i); @@ -5877,6 +5937,12 @@ void main() { VkFormat colorAttachmentFormat = VK_FORMAT_UNDEFINED; Int textureExternalIndex = -1; +#if MOBILEGL_BUILD_DISAGGREGATED + if (wireFbo && !isDefaultDrawFbo) { + const Int32 slot = wireFbo->DrawBuffers[i]; + colorAttachmentFormat = ResolveWireImage(*wireFbo, wireFbo->Color[slot], VK_IMAGE_ASPECT_COLOR_BIT).format; + } else +#endif if (isDefaultDrawFbo) { colorAttachmentFormat = m_swapchainObject.GetSurfaceFormat().format; } else if (colorAttachmentRenderbuffer != nullptr) { @@ -6004,7 +6070,7 @@ void main() { Bool VulkanRenderer::PrepareStorageImageTextures( FrameContext::FrameData& frame, - const MG_State::GLState::ProgramObject& program, + const MagmaProgramSource& program, const ProgramFactory::VkProgramObject& programObj) { if (!programObj.hasStorageImages) { return true; @@ -6100,7 +6166,7 @@ void main() { } Bool VulkanRenderer::PrepareSamplerImageFeedbackSnapshots( FrameContext::FrameData& frame, - const MG_State::GLState::ProgramObject& program, + const MagmaProgramSource& program, const ProgramFactory::VkProgramObject& programObj, VkPipelineStageFlags consumerShaderStageMask) { auto& feedbackBindings = m_samplerImageFeedbackScratch; @@ -6801,6 +6867,10 @@ void main() { Bool VulkanRenderer::SetupDraw(FrameContext::FrameData& frame, GLenum mode, Flags aspects, const DrawCmdParam& drawParams, const IndexBufferView* pIndexBufferView) { +#if MOBILEGL_BUILD_DISAGGREGATED + if (MG_Config::Transport != MG_Config::TransportMode::Monolith) + return SetupWireDraw(frame, mode, aspects, drawParams); +#endif // Sync each sampled texture at most once across this whole draw: the layout // probe loop, the post-transition loop, and ResolveSamplerDescriptor would // otherwise each re-run the full SyncTexture path on the same textures. @@ -7417,6 +7487,12 @@ void main() { } void VulkanRenderer::DispatchCompute(GLuint numGroupsX, GLuint numGroupsY, GLuint numGroupsZ) { +#if MOBILEGL_BUILD_DISAGGREGATED + if (MG_Config::Transport != MG_Config::TransportMode::Monolith) { + DispatchWireCompute(numGroupsX, numGroupsY, numGroupsZ); + return; + } +#endif m_textureManager->CollectGarbage(); auto& frame = m_frameContext.GetCurrent(); // The DISPATCH accessor: with a pipeline bound this is its compute stage program @@ -7637,6 +7713,7 @@ void main() { } #include "WireFramebuffer.inc" + #include "WireDraw.inc" void VulkanRenderer::Clear(GLbitfield mask) { #if MOBILEGL_BUILD_DISAGGREGATED diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h index 01db1540..08a61c60 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h @@ -457,6 +457,17 @@ namespace MobileGL::MG_Backend::DirectVulkan { void ReadWirePixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* pixels); void GenerateWireMipmap(); + Bool SetupWireDraw(FrameContext::FrameData& frame, GLenum mode, Flags aspects, + const DrawCmdParam& drawParams); + void DestroyWireDrawPass(); + void DispatchWireCompute(GLuint x, GLuint y, GLuint z); + UniquePtr m_wireDrawPass; + Vector m_wireDrawViews; + VkPipeline GetOrCreatePipelineWithInput(GLenum mode, const MagmaProgramSource& program, + const ProgramFactory::VkProgramObject& programObj, ProgramFactory::CompileOptionFlags transformFlags, + const VertexInputStateFactory::BackendVertexInputState& vis, const RenderPassEntry& renderPassEntry, + Bool primitiveRestartEnable); + void CopyWireFramebufferToTexture(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); #endif @@ -1535,7 +1546,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { VkPipeline GetOrCreatePipeline( GLenum mode, - const MG_State::GLState::ProgramObject& program, + const MagmaProgramSource& program, const ProgramFactory::VkProgramObject& programObj, ProgramFactory::CompileOptionFlags transformFlags, const MG_State::GLState::VertexArrayObject& vao, @@ -1547,14 +1558,14 @@ namespace MobileGL::MG_Backend::DirectVulkan { // flush the pending recording (see the body), which retires the current command buffer. Bool PrepareStorageImageTextures( FrameContext::FrameData& frame, - const MG_State::GLState::ProgramObject& program, + const MagmaProgramSource& program, const ProgramFactory::VkProgramObject& programObj); // Vulkan forbids a sampled descriptor and writable storage descriptor from naming the // same image subresource in one shader operation. Snapshot only the sampler side; the // storage descriptor continues to name the application texture. Bool PrepareSamplerImageFeedbackSnapshots( FrameContext::FrameData& frame, - const MG_State::GLState::ProgramObject& program, + const MagmaProgramSource& program, const ProgramFactory::VkProgramObject& programObj, VkPipelineStageFlags consumerShaderStageMask); diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/WireDraw.inc b/MobileGL/MG_Backend/DirectVulkan/Renderer/WireDraw.inc new file mode 100644 index 00000000..dd612878 --- /dev/null +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/WireDraw.inc @@ -0,0 +1,179 @@ +// Server-owned draw/dispatch setup. The legacy setup remains the monolith implementation. +#if MOBILEGL_BUILD_DISAGGREGATED + static MagmaProgramSource WireProgramSource(MG_Pipe::MGPipeHandle handle) { + auto& state = MG_Pipe::MGPipeApplier(); + const Bool composite = handle.Slot >= MG_Pipe::kMGPipeShaderCsoCompositeSlotBase; + const auto& records = composite ? state.CompositeShaderCsos : state.ShaderCsos; + const SizeT index = composite ? handle.Slot - MG_Pipe::kMGPipeShaderCsoCompositeSlotBase : handle.Slot; + if (MG_Pipe::MGPipeHandleIsNull(handle) || index >= records.size() || + !records[index].Live || records[index].Gen != handle.Gen || !records[index].Archive) + MagmaWireFatal("program-record"); + return MagmaProgramSource(handle, records[index]); + } + + [[noreturn]] static void WireBufferLegacyFatal() { + MGLOG_F("MGPipe: Fatal{RoleViolation, \"buffer-legacy-arm\"} (Magma P7 buffer consumer)"); + std::abort(); + } + + void VulkanRenderer::DestroyWireDrawPass() { + m_wireDrawPass.reset(); + for (const auto view : m_wireDrawViews) vkDestroyImageView(m_device, view, nullptr); + m_wireDrawViews.clear(); + } + + Bool VulkanRenderer::SetupWireDraw(FrameContext::FrameData& frame, GLenum mode, + Flags aspects, const DrawCmdParam& drawParams) { + auto& state = MG_Pipe::MGPipeApplier(); + const auto program = WireProgramSource(state.BoundShaderCso); + if (!program.GetLinkStatus() || !program.GetSpirvStatus()) return false; + if (aspects & DrawSetupAspect::IndexBuffer || aspects & DrawSetupAspect::IndirectDrawBuffer || + MGB_CTX->IsTransformFeedbackActive()) WireBufferLegacyFatal(); + const auto* fbo = state.DrawFramebuffer(); + if (!fbo) MagmaWireFatal("draw-framebuffer-record"); + if (HasPendingRecordedWork() && !FlushPendingCommands()) MagmaWireFatal("draw-flush"); + DestroyWireDrawPass(); + + ProgramFactory::CompileOptionFlags flags = GetShaderTransformFlags(m_swapchainObject.GetPreTransform()); + const auto* programObject = &m_programFactory->GetOrCreateProgram(program, flags); + if (programObject->readsBaseVertexBuiltin) { + flags |= ProgramFactory::CompileOptionBit::ZeroBaseVertex; + programObject = &m_programFactory->GetOrCreateProgram(program, flags); + } + if (programObject->pointSizeCapabilityUnsupported) return false; + const auto& programObj = *programObject; + const auto vaoHandle = state.BoundVertexElements; + if (!MG_Pipe::MGPipeHandleIsNull(vaoHandle)) { + if (vaoHandle.Slot >= state.VertexElementsCsos.size()) MagmaWireFatal("vertex-elements-record"); + const auto& vao = state.VertexElementsCsos[vaoHandle.Slot]; + if (!vao.Live || vao.Gen != vaoHandle.Gen) MagmaWireFatal("vertex-elements-generation"); + for (Uint32 i = 0; i < vao.AttributeCount; ++i) + if (vao.Attributes[i].Enabled && (programObj.activeVertexInputLocationMask & (1u << i))) + WireBufferLegacyFatal(); + } + + Vector attachments; + Vector views; + Vector colors; + VkAttachmentReference depth{VK_ATTACHMENT_UNUSED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL}; + VkExtent2D extent{~0u, ~0u}; + Uint32 layers = ~0u; + VkSampleCountFlagBits samples = VK_SAMPLE_COUNT_1_BIT; + Uint64 passHash = 0; + const auto add = [&](const MG_Pipe::MGPSurface& surface, VkImageAspectFlags aspect) -> Uint32 { + WireImage image = ResolveWireImage(*fbo, surface, aspect); + if (!image.image) return VK_ATTACHMENT_UNUSED; + m_textureManager->FlushPendingUploads(); + const VkImageLayout layout = aspect == VK_IMAGE_ASPECT_COLOR_BIT ? + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL : VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + TransitionWireImage(image, layout); + VkImageViewCreateInfo info{VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO}; + info.image = image.image; + info.viewType = image.layers > 1 ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D; + info.format = image.format; + info.subresourceRange = {aspect, image.level, 1, image.layer, image.layers}; + if (image.format == VK_FORMAT_D24_UNORM_S8_UINT || image.format == VK_FORMAT_D32_SFLOAT_S8_UINT) + info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; + VkImageView view = VK_NULL_HANDLE; + VK_VERIFY(vkCreateImageView(m_device, &info, nullptr, &view)); + m_wireDrawViews.push_back(view); + views.push_back(view); + VkAttachmentDescription description{}; + description.format = image.format; + description.samples = image.samples; + description.loadOp = description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + description.storeOp = description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE; + description.initialLayout = description.finalLayout = layout; + attachments.push_back(description); + extent.width = std::min(extent.width, image.extent.width); + extent.height = std::min(extent.height, image.extent.height); + layers = std::min(layers, image.layers); + samples = image.samples; + passHash = XXH64(&description, sizeof(description), passHash); + if (!image.isDefault && surface.Kind == MG_Pipe::kMGPipeSurfaceKindTexture) + m_textureManager->MarkWireTextureGpuWritten(image.storage, image.level, image.layer, image.layers); + if (image.isDefault) { + if (aspect == VK_IMAGE_ASPECT_COLOR_BIT) m_swapchainObject.SetImageContentDefined(m_imageIndexAcquired, true); + else m_swapchainObject.SetDepthStencilContentDefined(m_imageIndexAcquired, true); + } + return static_cast(attachments.size() - 1); + }; + for (Uint32 i = 0; i < MG_Pipe::kMGPipeMaxColorAttachments; ++i) { + const Int32 slot = fbo->DrawBuffers[i]; + const Uint32 index = slot < 0 ? VK_ATTACHMENT_UNUSED : add(fbo->Color[slot], VK_IMAGE_ASPECT_COLOR_BIT); + colors.push_back({index, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}); + } + while (!colors.empty() && colors.back().attachment == VK_ATTACHMENT_UNUSED) colors.pop_back(); + if (fbo->IsDefault || fbo->Depth.Kind != MG_Pipe::kMGPipeSurfaceKindNone) + depth.attachment = add(fbo->Depth, VK_IMAGE_ASPECT_DEPTH_BIT); + else if (fbo->Stencil.Kind != MG_Pipe::kMGPipeSurfaceKindNone) + depth.attachment = add(fbo->Stencil, VK_IMAGE_ASPECT_STENCIL_BIT); + if (attachments.empty()) return false; + VkSubpassDescription subpass{}; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.colorAttachmentCount = static_cast(colors.size()); + subpass.pColorAttachments = colors.data(); + subpass.pDepthStencilAttachment = depth.attachment != VK_ATTACHMENT_UNUSED ? &depth : nullptr; + VkRenderPassCreateInfo passInfo{VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO}; + passInfo.attachmentCount = static_cast(attachments.size()); + passInfo.pAttachments = attachments.data(); + passInfo.subpassCount = 1; + passInfo.pSubpasses = &subpass; + m_wireDrawPass = MakeUnique(); + auto& pass = *m_wireDrawPass; + VK_VERIFY(vkCreateRenderPass(m_device, &passInfo, nullptr, &pass.renderPass)); + VkFramebufferCreateInfo fbInfo{VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO}; + fbInfo.renderPass = pass.renderPass; + fbInfo.attachmentCount = static_cast(views.size()); + fbInfo.pAttachments = views.data(); + fbInfo.width = extent.width; fbInfo.height = extent.height; fbInfo.layers = layers; + VK_VERIFY(vkCreateFramebuffer(m_device, &fbInfo, nullptr, &pass.framebuffer)); + pass.attachmentCount = static_cast(attachments.size()); + pass.colorAttachmentCount = static_cast(colors.size()); + pass.hasDepthStencilAttachment = depth.attachment != VK_ATTACHMENT_UNUSED; + pass.sampleCount = samples; + pass.extent = {static_cast(extent.width), static_cast(extent.height)}; + pass.layers = layers; + pass.hash = pass.compatibilityHash = XXH64(colors.data(), colors.size() * sizeof(colors[0]), passHash); + VertexInputStateFactory::BackendVertexInputState input{}; + const auto pipeline = GetOrCreatePipelineWithInput(mode, program, programObj, flags, input, pass, false); + if (pipeline == VK_NULL_HANDLE) return false; + if (!frame.isCommandRecording) m_frameContext.BeginCommandRecording(); + if (!m_uniformManager->BindProgramUniformBuffers(frame.commandBuffer, program, programObj, + m_frameContext.GetCurrentFrameIndex(), VK_PIPELINE_BIND_POINT_GRAPHICS)) return false; + Uint32 binding = 0; + for (Uint32 location = 0; location < kMaxVertexAttribs; ++location) { + if (!(programObj.activeVertexInputLocationMask & (1u << location))) continue; + VkFormat format = VK_FORMAT_UNDEFINED; + const void* data = nullptr; + VkDeviceSize size = 0; + if (!TryGetCurrentVertexAttributeUploadPayload(MGB_CTX->GetCurrentVertexAttribute(location), + programObj.vertexInputTypes[location], format, data, size)) return false; + BufferSlice slice{}; + if (!m_bufferManager.UploadTransient(BufferKind::Vertex, m_frameContext.GetCurrentFrameIndex(), + data, size, 16, slice)) return false; + vkCmdBindVertexBuffers(frame.commandBuffer, binding++, 1, &slice.buffer, &slice.offset); + } + if (!VkRenderPassManager::BeginRenderPass(frame.commandBuffer, pass)) return false; + vkCmdBindPipeline(frame.commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + ApplyDynamicDrawStateTail(frame, pass.extent, fbo->IsDefault, + ResolveDrawViewportCount(programObj.writesViewportIndexBuiltin)); + (void)drawParams; + return true; + } + + void VulkanRenderer::DispatchWireCompute(GLuint x, GLuint y, GLuint z) { + if (HasPendingRecordedWork() && !FlushPendingCommands()) MagmaWireFatal("dispatch-flush"); + auto& frame = m_frameContext.GetCurrent(); + const auto program = WireProgramSource(MG_Pipe::MGPipeApplier().DispatchProgram); + if (!program.GetLinkStatus() || !program.GetSpirvStatus()) return; + const auto& programObj = m_programFactory->GetOrCreateProgram(program, {}); + const auto pipeline = GetOrCreateComputePipeline(programObj); + if (!pipeline) return; + if (!frame.isCommandRecording) m_frameContext.BeginCommandRecording(); + if (!m_uniformManager->BindProgramUniformBuffers(frame.commandBuffer, program, programObj, + m_frameContext.GetCurrentFrameIndex(), VK_PIPELINE_BIND_POINT_COMPUTE)) return; + vkCmdBindPipeline(frame.commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline); + vkCmdDispatch(frame.commandBuffer, x, y, z); + } +#endif diff --git a/MobileGL/MG_Backend/Init.cpp b/MobileGL/MG_Backend/Init.cpp index 6d947032..e19b7fea 100644 --- a/MobileGL/MG_Backend/Init.cpp +++ b/MobileGL/MG_Backend/Init.cpp @@ -113,7 +113,8 @@ namespace MobileGL::MG_Backend { switch (type) { case BackendType::DirectGLES: return MG_Pipe::kMGPipeSubsystemsMigratedAtP5e; case BackendType::DirectVulkan: - return MG_Pipe::kMGPipeSubsystemsMigratedAtP4a & ~MG_Pipe::kMGPipeSubsystemResources; + return (MG_Pipe::kMGPipeSubsystemsMigratedAtP4a | MG_Pipe::kMGPipeSubsystemBufferBindings) & + ~MG_Pipe::kMGPipeSubsystemResources; default: return 0; } } diff --git a/MobileGL/MG_IntegrationTest/CMakeLists.txt b/MobileGL/MG_IntegrationTest/CMakeLists.txt index 5be75719..44c603f3 100644 --- a/MobileGL/MG_IntegrationTest/CMakeLists.txt +++ b/MobileGL/MG_IntegrationTest/CMakeLists.txt @@ -2383,7 +2383,7 @@ if (MOBILEGL_BUILD_DISAGGREGATED) "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) + foreach(fmSlot PartialTextureUploadPreservesGpuClear TextureViewClearTargetsItsRootMipAndLayer 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"