From eb8ef893bed03c683c0598b6dfe5d18e4e0212a0 Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Sun, 23 Aug 2026 09:56:58 +0800 Subject: [PATCH] [Feat] (Diligent): support multiple simultaneous color attachments - PSO RTV count/formats now derive from the bound draw FBO color attachments - Include RT layout in the last-PSO cache key - Add DrawsToMultipleColorAttachmentsFromMobileGLState; 15 Diligent tests pass --- HANDOFF_DILIGENT.md | 7 +- .../Diligent/Renderer/DiligentRenderer.cpp | 41 ++++++++- .../MG_Test/Backend/Diligent/SanityTest.cpp | 89 +++++++++++++++++++ 3 files changed, 132 insertions(+), 5 deletions(-) diff --git a/HANDOFF_DILIGENT.md b/HANDOFF_DILIGENT.md index 159d99cd..dbb29883 100644 --- a/HANDOFF_DILIGENT.md +++ b/HANDOFF_DILIGENT.md @@ -107,6 +107,7 @@ Working tree is clean. - `DrawsNamedUniformBlockFromMobileGLState` - `DrawsWithStencilTestFromMobileGLState` - `DrawsToRenderbufferFramebufferFromMobileGLState` + - `DrawsToMultipleColorAttachmentsFromMobileGLState` --- @@ -162,7 +163,7 @@ Verified locally on Turnip Adreno 750: - Local test result: ``` -[ PASSED ] 14 tests +[ PASSED ] 15 tests ``` --- @@ -209,7 +210,7 @@ Notes: ## 7. Known Limitations / Not Yet Implemented -- User framebuffers now support texture color attachments, renderbuffer color readback, and depth/stencil texture or renderbuffer attachments; multi-target color attachments remain partial. +- User framebuffers now support texture color attachments, renderbuffer color readback, multiple simultaneous color targets, and depth/stencil texture or renderbuffer attachments. - Textures auto-sync `ITextureObject` → Diligent resources, including mip levels and sampler state; compressed textures and integer/3-channel formats that Diligent lacks are still skipped. - Global UBO (default-block `glUniform*`) and named application UBO blocks (through `glBindBufferBase`/`glUniformBlockBinding`) now upload and bind; SSBOs are still not fed from frontend buffer bindings. - No swapchain / EGL window surface presentation yet; `Present()` only flushes. @@ -226,7 +227,7 @@ Notes: - [x] Map `MG_State::GLState::FramebufferObject` attachments to Diligent `ITextureView` / `ITexture`. - [x] Support default framebuffer as current offscreen target. - [x] Support `glBindFramebuffer`, `glFramebufferTexture2D`, renderbuffer color/depth attachments and renderbuffer color readback. - - [ ] Multiple simultaneous color attachments (currently single RT resolution). + - [x] Multiple simultaneous color attachments. 2. **Texture / Sampler full integration** - [x] Translate MobileGL `ITextureObject` to Diligent `ITexture` and cache by `GetLifetimeId()`. diff --git a/MobileGL/MG_Backend/Diligent/Renderer/DiligentRenderer.cpp b/MobileGL/MG_Backend/Diligent/Renderer/DiligentRenderer.cpp index f9054a91..b7534e39 100644 --- a/MobileGL/MG_Backend/Diligent/Renderer/DiligentRenderer.cpp +++ b/MobileGL/MG_Backend/Diligent/Renderer/DiligentRenderer.cpp @@ -1268,6 +1268,37 @@ void main() return false; } + // Resolve the current draw-FBO color attachment set so the PSO is created with + // matching RTV formats/count. + Uint32 rtCount = 1; + ::Diligent::TEXTURE_FORMAT rtFormats[8] = {}; + rtFormats[0] = ::Diligent::TEX_FORMAT_RGBA8_UNORM; + if (MG_State::pGLContext != nullptr) { + auto drawFbo = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject(); + if (drawFbo && !drawFbo->IsDefaultFramebuffer()) { + rtCount = 0; + for (const auto attachmentType : drawFbo->GetDrawBuffers()) { + if (attachmentType == FramebufferAttachmentType::None) { + continue; + } + const auto& attachment = drawFbo->GetAttachment(attachmentType); + ::Diligent::TEXTURE_FORMAT fmt = ::Diligent::TEX_FORMAT_UNKNOWN; + if (attachment.IsTexture()) { + fmt = ConvertInternalFormatToDiligent(attachment.GetTexture()->GetFormat()); + } else if (attachment.IsRenderbuffer()) { + fmt = ConvertInternalFormatToDiligent(attachment.GetRenderbuffer()->GetInternalFormat()); + } + if (fmt != ::Diligent::TEX_FORMAT_UNKNOWN && rtCount < 8) { + rtFormats[rtCount++] = fmt; + } + } + if (rtCount == 0) { + rtCount = 1; + rtFormats[0] = ::Diligent::TEX_FORMAT_RGBA8_UNORM; + } + } + } + // Cheap last-PSO cache: the pipeline depends only on the program, the // render-state subset baked into the PSO, the primitive topology and the // enabled vertex-attribute layout. Textures/UBOs are bound dynamically, so @@ -1277,6 +1308,10 @@ void main() Uint64 psoKey = program->GetLifetimeId(); psoKey = psoKey * 1099511628211ull + MG_State::pGLContext->GetPipelineStateVersion(); psoKey = psoKey * 1099511628211ull + static_cast(mode); + psoKey = psoKey * 1099511628211ull + rtCount; + for (Uint32 i = 0; i < rtCount; ++i) { + psoKey = psoKey * 1099511628211ull + static_cast(rtFormats[i]); + } for (Uint32 i = 0; i < cachedVao.MAX_VERTEX_ATTRIBS; ++i) { const auto& attr = cachedAttributes[i]; if (!attr.Enabled) { @@ -1350,8 +1385,10 @@ void main() psoDesc.PipelineType = ::Diligent::PIPELINE_TYPE_GRAPHICS; psoDesc.Name = "MobileGL Diligent state PSO"; psoDesc.ResourceLayout.DefaultVariableType = ::Diligent::SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC; - graphicsPipeline.NumRenderTargets = 1; - graphicsPipeline.RTVFormats[0] = ::Diligent::TEX_FORMAT_RGBA8_UNORM; + graphicsPipeline.NumRenderTargets = static_cast<::Diligent::Uint8>(rtCount); + for (Uint32 i = 0; i < rtCount; ++i) { + graphicsPipeline.RTVFormats[i] = rtFormats[i]; + } graphicsPipeline.DSVFormat = ::Diligent::TEX_FORMAT_D24_UNORM_S8_UINT; switch (mode) { case GL_POINTS: diff --git a/MobileGL/MG_Test/Backend/Diligent/SanityTest.cpp b/MobileGL/MG_Test/Backend/Diligent/SanityTest.cpp index 8a5e24d9..38910a9b 100644 --- a/MobileGL/MG_Test/Backend/Diligent/SanityTest.cpp +++ b/MobileGL/MG_Test/Backend/Diligent/SanityTest.cpp @@ -957,3 +957,92 @@ void main() { Color = vec4(1.0, 0.0, 0.0, 1.0); } EXPECT_GT(center[0], 200) << "center should be red in the renderbuffer framebuffer"; EXPECT_LT(center[1], 50) << "center should not be green"; } + + +TEST(DiligentVulkanBackend, DrawsToMultipleColorAttachmentsFromMobileGLState) { + MobileGL::Initialize(); + + const char* vsSrc = R"(#version 330 core +layout(location = 0) in vec2 Position; +void main() { gl_Position = vec4(Position, 0.0, 1.0); } +)"; + const char* fsSrc = R"(#version 330 core +layout(location = 0) out vec4 Color0; +layout(location = 1) out vec4 Color1; +void main() { + Color0 = vec4(1.0, 0.0, 0.0, 1.0); + Color1 = vec4(0.0, 0.0, 1.0, 1.0); +} +)"; + + const GLuint vs = CreateShader(GL_VERTEX_SHADER); + ShaderSource(vs, 1, &vsSrc, nullptr); + CompileShader(vs); + const GLuint fs = CreateShader(GL_FRAGMENT_SHADER); + ShaderSource(fs, 1, &fsSrc, nullptr); + CompileShader(fs); + const GLuint program = CreateProgram(); + AttachShader(program, vs); + AttachShader(program, fs); + LinkProgram(program); + UseProgram(program); + Viewport(0, 0, 256, 256); + Disable(GL_DEPTH_TEST); + Disable(GL_BLEND); + Disable(GL_SCISSOR_TEST); + + const float vertices[] = { + -0.5f, -0.5f, + 0.5f, -0.5f, + 0.0f, 0.5f, + }; + GLuint vbo = 0; + GenBuffers(1, &vbo); + BindBuffer(GL_ARRAY_BUFFER, vbo); + BufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + + GLuint vao = 0; + GenVertexArrays(1, &vao); + BindVertexArray(vao); + EnableVertexAttribArray(0); + VertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, nullptr); + + GLuint tex0 = 0, tex1 = 0; + GenTextures(1, &tex0); + BindTexture(GL_TEXTURE_2D, tex0); + TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + GenTextures(1, &tex1); + BindTexture(GL_TEXTURE_2D, tex1); + TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + + GLuint fbo = 0; + GenFramebuffers(1, &fbo); + BindFramebuffer(GL_FRAMEBUFFER, fbo); + FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex0, 0); + FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, tex1, 0); + const GLenum bufs[] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1}; + DrawBuffers(2, bufs); + + DiligentBackend::BackendObject_Diligent backend; + backend.Initialize(); + auto* renderer = backend.GetRenderer(); + if (renderer == nullptr) { + GTEST_SKIP() << "No Vulkan adapter available; skipping multi-color attachment test"; + } + + renderer->Clear(0.0f, 1.0f, 0.0f, 1.0f); + renderer->DrawFromState(GL_TRIANGLES, 0, 3, 0, nullptr); + renderer->Present(); + + ReadBuffer(GL_COLOR_ATTACHMENT0); + std::uint8_t center0[4] = {}; + renderer->ReadPixels(128, 128, 1, 1, center0); + EXPECT_GT(center0[0], 200) << "attachment 0 should be red"; + EXPECT_LT(center0[2], 50) << "attachment 0 should not be blue"; + + ReadBuffer(GL_COLOR_ATTACHMENT1); + std::uint8_t center1[4] = {}; + renderer->ReadPixels(128, 128, 1, 1, center1); + EXPECT_LT(center1[0], 50) << "attachment 1 should not be red"; + EXPECT_GT(center1[2], 200) << "attachment 1 should be blue"; +}