[Feat] (Diligent): support multiple simultaneous color attachments

- PSO RTV count/formats now derive from the bound draw FBO color attachments
- Include RT layout in the last-PSO cache key
- Add DrawsToMultipleColorAttachmentsFromMobileGLState; 15 Diligent tests pass
This commit is contained in:
BZLZHH
2026-08-23 09:56:58 +08:00
parent 20567fba6d
commit eb8ef893be
3 changed files with 132 additions and 5 deletions
+4 -3
View File
@@ -107,6 +107,7 @@ Working tree is clean.
- `DrawsNamedUniformBlockFromMobileGLState`
- `DrawsWithStencilTestFromMobileGLState`
- `DrawsToRenderbufferFramebufferFromMobileGLState`
- `DrawsToMultipleColorAttachmentsFromMobileGLState`
---
@@ -162,7 +163,7 @@ Verified locally on Turnip Adreno 750:
- Local test result:
```
[ PASSED ] 14 tests
[ PASSED ] 15 tests
```
---
@@ -209,7 +210,7 @@ Notes:
## 7. Known Limitations / Not Yet Implemented
- User framebuffers now support texture color attachments, renderbuffer color readback, and depth/stencil texture or renderbuffer attachments; multi-target color attachments remain partial.
- User framebuffers now support texture color attachments, renderbuffer color readback, multiple simultaneous color targets, and depth/stencil texture or renderbuffer attachments.
- 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.
@@ -226,7 +227,7 @@ Notes:
- [x] Map `MG_State::GLState::FramebufferObject` attachments to Diligent `ITextureView` / `ITexture`.
- [x] Support default framebuffer as current offscreen target.
- [x] Support `glBindFramebuffer`, `glFramebufferTexture2D`, renderbuffer color/depth attachments and renderbuffer color readback.
- [ ] Multiple simultaneous color attachments (currently single RT resolution).
- [x] Multiple simultaneous color attachments.
2. **Texture / Sampler full integration**
- [x] Translate MobileGL `ITextureObject` to Diligent `ITexture` and cache by `GetLifetimeId()`.
@@ -1268,6 +1268,37 @@ void main()
return false;
}
// Resolve the current draw-FBO color attachment set so the PSO is created with
// matching RTV formats/count.
Uint32 rtCount = 1;
::Diligent::TEXTURE_FORMAT rtFormats[8] = {};
rtFormats[0] = ::Diligent::TEX_FORMAT_RGBA8_UNORM;
if (MG_State::pGLContext != nullptr) {
auto drawFbo = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject();
if (drawFbo && !drawFbo->IsDefaultFramebuffer()) {
rtCount = 0;
for (const auto attachmentType : drawFbo->GetDrawBuffers()) {
if (attachmentType == FramebufferAttachmentType::None) {
continue;
}
const auto& attachment = drawFbo->GetAttachment(attachmentType);
::Diligent::TEXTURE_FORMAT fmt = ::Diligent::TEX_FORMAT_UNKNOWN;
if (attachment.IsTexture()) {
fmt = ConvertInternalFormatToDiligent(attachment.GetTexture()->GetFormat());
} else if (attachment.IsRenderbuffer()) {
fmt = ConvertInternalFormatToDiligent(attachment.GetRenderbuffer()->GetInternalFormat());
}
if (fmt != ::Diligent::TEX_FORMAT_UNKNOWN && rtCount < 8) {
rtFormats[rtCount++] = fmt;
}
}
if (rtCount == 0) {
rtCount = 1;
rtFormats[0] = ::Diligent::TEX_FORMAT_RGBA8_UNORM;
}
}
}
// Cheap last-PSO cache: the pipeline depends only on the program, the
// render-state subset baked into the PSO, the primitive topology and the
// enabled vertex-attribute layout. Textures/UBOs are bound dynamically, so
@@ -1277,6 +1308,10 @@ void main()
Uint64 psoKey = program->GetLifetimeId();
psoKey = psoKey * 1099511628211ull + MG_State::pGLContext->GetPipelineStateVersion();
psoKey = psoKey * 1099511628211ull + static_cast<Uint64>(mode);
psoKey = psoKey * 1099511628211ull + rtCount;
for (Uint32 i = 0; i < rtCount; ++i) {
psoKey = psoKey * 1099511628211ull + static_cast<Uint64>(rtFormats[i]);
}
for (Uint32 i = 0; i < cachedVao.MAX_VERTEX_ATTRIBS; ++i) {
const auto& attr = cachedAttributes[i];
if (!attr.Enabled) {
@@ -1350,8 +1385,10 @@ void main()
psoDesc.PipelineType = ::Diligent::PIPELINE_TYPE_GRAPHICS;
psoDesc.Name = "MobileGL Diligent state PSO";
psoDesc.ResourceLayout.DefaultVariableType = ::Diligent::SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC;
graphicsPipeline.NumRenderTargets = 1;
graphicsPipeline.RTVFormats[0] = ::Diligent::TEX_FORMAT_RGBA8_UNORM;
graphicsPipeline.NumRenderTargets = static_cast<::Diligent::Uint8>(rtCount);
for (Uint32 i = 0; i < rtCount; ++i) {
graphicsPipeline.RTVFormats[i] = rtFormats[i];
}
graphicsPipeline.DSVFormat = ::Diligent::TEX_FORMAT_D24_UNORM_S8_UINT;
switch (mode) {
case GL_POINTS:
@@ -957,3 +957,92 @@ void main() { Color = vec4(1.0, 0.0, 0.0, 1.0); }
EXPECT_GT(center[0], 200) << "center should be red in the renderbuffer framebuffer";
EXPECT_LT(center[1], 50) << "center should not be green";
}
TEST(DiligentVulkanBackend, DrawsToMultipleColorAttachmentsFromMobileGLState) {
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(location = 0) out vec4 Color0;
layout(location = 1) out vec4 Color1;
void main() {
Color0 = vec4(1.0, 0.0, 0.0, 1.0);
Color1 = vec4(0.0, 0.0, 1.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 tex0 = 0, tex1 = 0;
GenTextures(1, &tex0);
BindTexture(GL_TEXTURE_2D, tex0);
TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
GenTextures(1, &tex1);
BindTexture(GL_TEXTURE_2D, tex1);
TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
GLuint fbo = 0;
GenFramebuffers(1, &fbo);
BindFramebuffer(GL_FRAMEBUFFER, fbo);
FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex0, 0);
FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, tex1, 0);
const GLenum bufs[] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1};
DrawBuffers(2, bufs);
DiligentBackend::BackendObject_Diligent backend;
backend.Initialize();
auto* renderer = backend.GetRenderer();
if (renderer == nullptr) {
GTEST_SKIP() << "No Vulkan adapter available; skipping multi-color attachment test";
}
renderer->Clear(0.0f, 1.0f, 0.0f, 1.0f);
renderer->DrawFromState(GL_TRIANGLES, 0, 3, 0, nullptr);
renderer->Present();
ReadBuffer(GL_COLOR_ATTACHMENT0);
std::uint8_t center0[4] = {};
renderer->ReadPixels(128, 128, 1, 1, center0);
EXPECT_GT(center0[0], 200) << "attachment 0 should be red";
EXPECT_LT(center0[2], 50) << "attachment 0 should not be blue";
ReadBuffer(GL_COLOR_ATTACHMENT1);
std::uint8_t center1[4] = {};
renderer->ReadPixels(128, 128, 1, 1, center1);
EXPECT_LT(center1[0], 50) << "attachment 1 should not be red";
EXPECT_GT(center1[2], 200) << "attachment 1 should be blue";
}