diff --git a/HANDOFF_DILIGENT.md b/HANDOFF_DILIGENT.md index 66986384..7beb2b92 100644 --- a/HANDOFF_DILIGENT.md +++ b/HANDOFF_DILIGENT.md @@ -104,6 +104,7 @@ Working tree is clean. - `DrawsWithScissorFromMobileGLState` - `DrawsWithBlendFromMobileGLState` - `DrawsWithDepthTestFromMobileGLState` + - `DrawsNamedUniformBlockFromMobileGLState` --- @@ -155,7 +156,7 @@ Verified locally on Turnip Adreno 750: - Local test result: ``` -[ PASSED ] 11 tests +[ PASSED ] 12 tests ``` --- @@ -204,7 +205,7 @@ Notes: - User framebuffers now support texture color attachments and depth/stencil texture or renderbuffer attachments; renderbuffer readback and 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*`) now uploads and binds; named application UBO blocks and SSBOs are not fed from frontend buffer bindings yet. +- 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. - No transform feedback / queries / sync / readback of non-color resources. - Draw range, multi-draw, instanced-draw wrappers, clear-buffer, blit and read-pixels are now wired; indirect draws, CopyTexImage/GetTexImage and buffer subdata paths still remain. @@ -228,7 +229,7 @@ Notes: 3. **Uniform / UBO support** - [x] Create Diligent buffer for `ProgramObject::GetUBOData()` / `GetUBOSize()`. - [x] Bind the global UBO as a dynamic shader resource. - - [ ] Handle per-program uniform block bindings / named UBO blocks. + - [x] Handle per-program uniform block bindings / named UBO blocks. 4. **PSO / resource caching** - [~] Cache PSOs by program + VAO config + render state + topology (single last-PSO fast path). diff --git a/MobileGL/MG_Backend/Diligent/Renderer/DiligentRenderer.cpp b/MobileGL/MG_Backend/Diligent/Renderer/DiligentRenderer.cpp index 6f5a20cd..accde107 100644 --- a/MobileGL/MG_Backend/Diligent/Renderer/DiligentRenderer.cpp +++ b/MobileGL/MG_Backend/Diligent/Renderer/DiligentRenderer.cpp @@ -1064,19 +1064,86 @@ void main() variable->Set(srv); } } else if (binding->descriptor_type == SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_BUFFER) { - // MobileGL routes all default-block uniforms into one synthesized - // MGL_GLOBAL_UBO. SPIRV-Reflect may report an empty block name for the - // synthesized block, so always resolve the Diligent variable by the - // frontend's fixed global-UBO name. - if (!UploadUBOFromState(program) || !m_pUBO) { + const char* blockName = binding->block.name != nullptr && binding->block.name[0] != '\0' + ? binding->block.name + : (binding->name != nullptr && binding->name[0] != '\0' + ? binding->name + : (binding->type_description != nullptr && + binding->type_description->type_name != nullptr + ? binding->type_description->type_name + : "")); + const Bool isGlobal = blockName == nullptr || blockName[0] == '\0' || + std::strstr(blockName, "MGL_GLOBAL_UBO") != nullptr; + if (isGlobal) { + // SPIRV-Reflect may report an empty block name for the synthesized + // global UBO; resolve it by the frontend's fixed name. + if (!UploadUBOFromState(program) || !m_pUBO) { + continue; + } + auto* variable = m_pStateSRB->GetVariableByName(stage, "MGL_GLOBAL_UBO"); + if (variable == nullptr && binding->name != nullptr && binding->name[0] != '\0') { + variable = m_pStateSRB->GetVariableByName(stage, binding->name); + } + if (variable != nullptr) { + variable->Set(m_pUBO); + } continue; } - auto* variable = m_pStateSRB->GetVariableByName(stage, "MGL_GLOBAL_UBO"); + + // Named application uniform block: resolve the GL block index, read + // the frontend buffer currently bound at the block's binding point, + // and upload its mapped range to a Diligent uniform buffer. + const Uint blockIndex = program.GetUniformBlockIndex(blockName); + if (blockIndex == 0xFFFFFFFFu) { + continue; + } + const Uint frontendBinding = program.GetUniformBlockBinding(blockIndex); + if (MG_State::pGLContext == nullptr || + frontendBinding >= MG_State::pGLContext->GetBufferBindingPointCount(BufferTarget::Uniform)) { + continue; + } + auto& bindingPoint = MG_State::pGLContext->GetBufferBindingPoint(BufferTarget::Uniform, + frontendBinding); + const auto& bufferObject = bindingPoint.GetBoundObject(); + if (!bufferObject) { + continue; + } + bufferObject->SyncPersistentMappedRange(); + const Uint8* data = bufferObject->MappedData(); + if (data == nullptr) { + continue; + } + const auto range = bindingPoint.GetRange(); + if (range.end <= range.start) { + continue; + } + const SizeT rangeSize = range.end - range.start; + auto& cached = m_namedUboCache[bufferObject->GetLifetimeId()]; + if (!cached) { + ::Diligent::BufferDesc desc; + desc.Name = "MobileGL state named UBO"; + desc.BindFlags = ::Diligent::BIND_UNIFORM_BUFFER; + desc.Size = rangeSize; + desc.Usage = ::Diligent::USAGE_DEFAULT; + ::Diligent::BufferData initialData; + initialData.pData = data + range.start; + initialData.DataSize = static_cast<::Diligent::Uint32>(rangeSize); + m_pDevice->CreateBuffer(desc, &initialData, &cached); + if (!cached) { + continue; + } + } else { + const SizeT bufferSize = static_cast( + std::min<::Diligent::Uint64>(cached->GetDesc().Size, static_cast<::Diligent::Uint64>(rangeSize))); + m_pContext->UpdateBuffer(cached, 0, bufferSize, data + range.start, + ::Diligent::RESOURCE_STATE_TRANSITION_MODE_TRANSITION); + } + auto* variable = m_pStateSRB->GetVariableByName(stage, blockName); if (variable == nullptr && binding->name != nullptr && binding->name[0] != '\0') { variable = m_pStateSRB->GetVariableByName(stage, binding->name); } if (variable != nullptr) { - variable->Set(m_pUBO); + variable->Set(cached); } } } diff --git a/MobileGL/MG_Backend/Diligent/Renderer/DiligentRenderer.h b/MobileGL/MG_Backend/Diligent/Renderer/DiligentRenderer.h index a82527e9..4847ca02 100644 --- a/MobileGL/MG_Backend/Diligent/Renderer/DiligentRenderer.h +++ b/MobileGL/MG_Backend/Diligent/Renderer/DiligentRenderer.h @@ -117,6 +117,7 @@ namespace MobileGL::MG_Backend::DiligentBackend { UnorderedMap m_textureCache; UnorderedMap m_samplerCache; UnorderedMap m_renderbufferCache; + UnorderedMap> m_namedUboCache; Uint32 m_width = 256; Uint32 m_height = 256; Uint32 m_lastDrawVertexCount = 0; diff --git a/MobileGL/MG_Test/Backend/Diligent/SanityTest.cpp b/MobileGL/MG_Test/Backend/Diligent/SanityTest.cpp index 5c36e787..1703945b 100644 --- a/MobileGL/MG_Test/Backend/Diligent/SanityTest.cpp +++ b/MobileGL/MG_Test/Backend/Diligent/SanityTest.cpp @@ -718,3 +718,83 @@ void main() { Color = vec4(0.0, 0.0, 1.0, 1.0); } EXPECT_GT(center[0], 200) << "center should stay red from the nearer depth draw"; EXPECT_LT(center[2], 50) << "center should not become blue from the farther draw"; } + + +TEST(DiligentVulkanBackend, DrawsNamedUniformBlockFromMobileGLState) { + 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(std140) uniform TestBlock { + vec4 u_color; +}; +out vec4 Color; +void main() { Color = u_color; } +)"; + + 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); + GLint linkStatus = 0; + GetProgramiv(program, GL_LINK_STATUS, &linkStatus); + ASSERT_EQ(linkStatus, GL_TRUE); + const GLuint blockIndex = GetUniformBlockIndex(program, "TestBlock"); + ASSERT_NE(blockIndex, GL_INVALID_INDEX); + UniformBlockBinding(program, blockIndex, 0); + UseProgram(program); + Viewport(0, 0, 256, 256); + BindFramebuffer(GL_FRAMEBUFFER, 0); + 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); + + const float blockColor[4] = {0.0f, 0.0f, 1.0f, 1.0f}; + GLuint ubo = 0; + GenBuffers(1, &ubo); + BindBuffer(GL_UNIFORM_BUFFER, ubo); + BufferData(GL_UNIFORM_BUFFER, sizeof(blockColor), blockColor, GL_STATIC_DRAW); + BindBufferBase(GL_UNIFORM_BUFFER, 0, ubo); + + DiligentBackend::BackendObject_Diligent backend; + backend.Initialize(); + auto* renderer = backend.GetRenderer(); + if (renderer == nullptr) { + GTEST_SKIP() << "No Vulkan adapter available; skipping named UBO state 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_LT(center[0], 50) << "center should not be red"; + EXPECT_LT(center[1], 50) << "center should not be green"; + EXPECT_GT(center[2], 200) << "center should be blue from the named uniform block"; +}