mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-09 20:58:31 +09:00
[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:
+4
-3
@@ -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";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user