mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-12 06:08:30 +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`
|
- `DrawsWithScissorFromMobileGLState`
|
||||||
- `DrawsWithBlendFromMobileGLState`
|
- `DrawsWithBlendFromMobileGLState`
|
||||||
- `DrawsWithDepthTestFromMobileGLState`
|
- `DrawsWithDepthTestFromMobileGLState`
|
||||||
|
- `DrawsNamedUniformBlockFromMobileGLState`
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -155,7 +156,7 @@ Verified locally on Turnip Adreno 750:
|
|||||||
- Local test result:
|
- 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.
|
- 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.
|
- 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 swapchain / EGL window surface presentation yet; `Present()` only flushes.
|
||||||
- No transform feedback / queries / sync / readback of non-color resources.
|
- 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.
|
- 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**
|
3. **Uniform / UBO support**
|
||||||
- [x] Create Diligent buffer for `ProgramObject::GetUBOData()` / `GetUBOSize()`.
|
- [x] Create Diligent buffer for `ProgramObject::GetUBOData()` / `GetUBOSize()`.
|
||||||
- [x] Bind the global UBO as a dynamic shader resource.
|
- [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**
|
4. **PSO / resource caching**
|
||||||
- [~] Cache PSOs by program + VAO config + render state + topology (single last-PSO fast path).
|
- [~] Cache PSOs by program + VAO config + render state + topology (single last-PSO fast path).
|
||||||
|
|||||||
@@ -1064,19 +1064,86 @@ void main()
|
|||||||
variable->Set(srv);
|
variable->Set(srv);
|
||||||
}
|
}
|
||||||
} else if (binding->descriptor_type == SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_BUFFER) {
|
} else if (binding->descriptor_type == SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_BUFFER) {
|
||||||
// MobileGL routes all default-block uniforms into one synthesized
|
const char* blockName = binding->block.name != nullptr && binding->block.name[0] != '\0'
|
||||||
// MGL_GLOBAL_UBO. SPIRV-Reflect may report an empty block name for the
|
? binding->block.name
|
||||||
// synthesized block, so always resolve the Diligent variable by the
|
: (binding->name != nullptr && binding->name[0] != '\0'
|
||||||
// frontend's fixed global-UBO name.
|
? binding->name
|
||||||
if (!UploadUBOFromState(program) || !m_pUBO) {
|
: (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;
|
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') {
|
if (variable == nullptr && binding->name != nullptr && binding->name[0] != '\0') {
|
||||||
variable = m_pStateSRB->GetVariableByName(stage, binding->name);
|
variable = m_pStateSRB->GetVariableByName(stage, binding->name);
|
||||||
}
|
}
|
||||||
if (variable != nullptr) {
|
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, TextureResource> m_textureCache;
|
||||||
UnorderedMap<Uint64, SamplerResource> m_samplerCache;
|
UnorderedMap<Uint64, SamplerResource> m_samplerCache;
|
||||||
UnorderedMap<Uint32, TextureResource> m_renderbufferCache;
|
UnorderedMap<Uint32, TextureResource> m_renderbufferCache;
|
||||||
|
UnorderedMap<Uint64, ::Diligent::RefCntAutoPtr<::Diligent::IBuffer>> m_namedUboCache;
|
||||||
Uint32 m_width = 256;
|
Uint32 m_width = 256;
|
||||||
Uint32 m_height = 256;
|
Uint32 m_height = 256;
|
||||||
Uint32 m_lastDrawVertexCount = 0;
|
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_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";
|
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