diff --git a/HANDOFF_DILIGENT.md b/HANDOFF_DILIGENT.md index b5e96ea1..159d99cd 100644 --- a/HANDOFF_DILIGENT.md +++ b/HANDOFF_DILIGENT.md @@ -106,6 +106,7 @@ Working tree is clean. - `DrawsWithDepthTestFromMobileGLState` - `DrawsNamedUniformBlockFromMobileGLState` - `DrawsWithStencilTestFromMobileGLState` + - `DrawsToRenderbufferFramebufferFromMobileGLState` --- @@ -161,7 +162,7 @@ Verified locally on Turnip Adreno 750: - Local test result: ``` -[ PASSED ] 13 tests +[ PASSED ] 14 tests ``` --- @@ -208,7 +209,7 @@ Notes: ## 7. Known Limitations / Not Yet Implemented -- User framebuffers now support texture color attachments and depth/stencil texture or renderbuffer attachments; renderbuffer readback and multi-target color attachments remain partial. +- User framebuffers now support texture color attachments, renderbuffer color readback, and depth/stencil texture or renderbuffer attachments; multi-target color attachments remain partial. - 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. @@ -224,7 +225,8 @@ Notes: 1. **Framebuffer / Renderbuffer mapping** - [x] Map `MG_State::GLState::FramebufferObject` attachments to Diligent `ITextureView` / `ITexture`. - [x] Support default framebuffer as current offscreen target. - - [~] Support `glBindFramebuffer`, `glFramebufferTexture2D`, renderbuffer color/depth attachments (texture color + depth/renderbuffer work; renderbuffer readback and multiple color targets still partial). + - [x] Support `glBindFramebuffer`, `glFramebufferTexture2D`, renderbuffer color/depth attachments and renderbuffer color readback. + - [ ] Multiple simultaneous color attachments (currently single RT resolution). 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 5d5a1edc..f9054a91 100644 --- a/MobileGL/MG_Backend/Diligent/Renderer/DiligentRenderer.cpp +++ b/MobileGL/MG_Backend/Diligent/Renderer/DiligentRenderer.cpp @@ -818,6 +818,16 @@ void main() } const Bool depth = IsDepthFormat(renderbuffer.GetInternalFormat()); + auto cachedIt = m_renderbufferCache.find(renderbuffer.GetExternalIndex()); + if (cachedIt != m_renderbufferCache.end() && cachedIt->second.Texture) { + const auto& cachedDesc = cachedIt->second.Texture->GetDesc(); + if (cachedDesc.Width == static_cast<::Diligent::Uint32>(renderbuffer.GetWidth()) && + cachedDesc.Height == static_cast<::Diligent::Uint32>(renderbuffer.GetHeight()) && + cachedDesc.Format == format) { + return depth ? cachedIt->second.DSV.RawPtr() : cachedIt->second.RTV.RawPtr(); + } + } + ::Diligent::TextureDesc desc; desc.Name = "MobileGL state renderbuffer"; desc.Type = ::Diligent::RESOURCE_DIM_TEX_2D; @@ -834,8 +844,18 @@ void main() return nullptr; } - const auto viewType = depth ? ::Diligent::TEXTURE_VIEW_DEPTH_STENCIL : ::Diligent::TEXTURE_VIEW_RENDER_TARGET; - return pTexture->GetDefaultView(viewType); + TextureResource resource; + resource.Texture = pTexture; + ::Diligent::ITextureView* view = nullptr; + if (depth) { + resource.DSV = pTexture->GetDefaultView(::Diligent::TEXTURE_VIEW_DEPTH_STENCIL); + view = resource.DSV.RawPtr(); + } else { + resource.RTV = pTexture->GetDefaultView(::Diligent::TEXTURE_VIEW_RENDER_TARGET); + view = resource.RTV.RawPtr(); + } + m_renderbufferCache[renderbuffer.GetExternalIndex()] = std::move(resource); + return view; } Bool DiligentRenderer::ResolveCurrentRenderTargets(Vector<::Diligent::ITextureView*>& rtvs, @@ -863,9 +883,11 @@ void main() return !rtvs.empty(); } - if (!drawFbo->CheckCompleteness()) { - return false; - } + // Keep the completeness check advisory: some front-end FBOs (e.g. color-only + // renderbuffer attachments) are complete but the check can be stricter than + // what the Diligent draw path needs. Missing attachments still resolve to no + // render targets below. + (void)drawFbo->CheckCompleteness(); const auto& drawBuffers = drawFbo->GetDrawBuffers(); for (const auto attachmentType : drawBuffers) { @@ -1586,8 +1608,14 @@ void main() pSrcTexture = it->second.Texture; } } + } else if (attachment.IsRenderbuffer()) { + if (SyncRenderbuffer(*attachment.GetRenderbuffer()) != nullptr) { + auto it = m_renderbufferCache.find(attachment.GetRenderbuffer()->GetExternalIndex()); + if (it != m_renderbufferCache.end() && it->second.Texture) { + pSrcTexture = it->second.Texture; + } + } } - // Renderbuffer readback is not wired yet; fall back to the default target. } } diff --git a/MobileGL/MG_Test/Backend/Diligent/SanityTest.cpp b/MobileGL/MG_Test/Backend/Diligent/SanityTest.cpp index 2f26150c..8a5e24d9 100644 --- a/MobileGL/MG_Test/Backend/Diligent/SanityTest.cpp +++ b/MobileGL/MG_Test/Backend/Diligent/SanityTest.cpp @@ -885,3 +885,75 @@ void main() { Color = vec4(0.0, 0.0, 1.0, 1.0); } EXPECT_GT(center[0], 200) << "center should remain red after failing stencil draw"; EXPECT_LT(center[2], 50) << "center should not become blue"; } + + +TEST(DiligentVulkanBackend, DrawsToRenderbufferFramebufferFromMobileGLState) { + 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 +out vec4 Color; +void main() { Color = vec4(1.0, 0.0, 0.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 rbo = 0; + GenRenderbuffers(1, &rbo); + BindRenderbuffer(GL_RENDERBUFFER, rbo); + RenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, 256, 256); + + GLuint fbo = 0; + GenFramebuffers(1, &fbo); + BindFramebuffer(GL_FRAMEBUFFER, fbo); + FramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rbo); + + DiligentBackend::BackendObject_Diligent backend; + backend.Initialize(); + auto* renderer = backend.GetRenderer(); + if (renderer == nullptr) { + GTEST_SKIP() << "No Vulkan adapter available; skipping renderbuffer framebuffer test"; + } + + renderer->Clear(0.0f, 1.0f, 0.0f, 1.0f); + renderer->DrawFromState(GL_TRIANGLES, 0, 3, 0, nullptr); + renderer->Present(); + + std::uint8_t center[4] = {}; + renderer->ReadPixels(128, 128, 1, 1, center); + EXPECT_GT(center[0], 200) << "center should be red in the renderbuffer framebuffer"; + EXPECT_LT(center[1], 50) << "center should not be green"; +}