[Feat] (Diligent, MG_State): bind named application uniform blocks from frontend buffers

- Resolve named UBOs through SPIRV-Reflect type names when block names are empty
- Read the bound GL buffer range at the frontend uniform-block binding point and upload it as a Diligent uniform buffer
- Add DrawsNamedUniformBlockFromMobileGLState test; 12 Diligent tests pass
This commit is contained in:
BZLZHH
2026-08-23 09:35:32 +08:00
parent 7e765e1535
commit e829e70d8b
4 changed files with 159 additions and 10 deletions
+4 -3
View File
@@ -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).
@@ -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<SizeT>(
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);
}
}
}
@@ -117,6 +117,7 @@ namespace MobileGL::MG_Backend::DiligentBackend {
UnorderedMap<Uint64, TextureResource> m_textureCache;
UnorderedMap<Uint64, SamplerResource> m_samplerCache;
UnorderedMap<Uint32, TextureResource> m_renderbufferCache;
UnorderedMap<Uint64, ::Diligent::RefCntAutoPtr<::Diligent::IBuffer>> m_namedUboCache;
Uint32 m_width = 256;
Uint32 m_height = 256;
Uint32 m_lastDrawVertexCount = 0;
@@ -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";
}