[Feat] (Diligent, MG_Impl): honor DrawElementsBaseVertex baseVertex in CPU vertex packing

- Add baseVertex parameter through DrawFromState/UploadVertexDataFromState
- Apply baseVertex when resolving indexed vertex indices
- Pass baseVertex through DrawElementsBaseVertex, instanced, and indirect indexed draws
- Add DrawsIndexedBaseVertexFromMobileGLState; 16 Diligent tests pass
This commit is contained in:
BZLZHH
2026-08-23 10:02:44 +08:00
parent eb8ef893be
commit 5dca617f01
5 changed files with 121 additions and 20 deletions
+3 -1
View File
@@ -108,6 +108,7 @@ Working tree is clean.
- `DrawsWithStencilTestFromMobileGLState` - `DrawsWithStencilTestFromMobileGLState`
- `DrawsToRenderbufferFramebufferFromMobileGLState` - `DrawsToRenderbufferFramebufferFromMobileGLState`
- `DrawsToMultipleColorAttachmentsFromMobileGLState` - `DrawsToMultipleColorAttachmentsFromMobileGLState`
- `DrawsIndexedBaseVertexFromMobileGLState`
--- ---
@@ -149,6 +150,7 @@ Verified locally on Turnip Adreno 750:
- `ReadPixels` can read back from a user FBO color attachment - `ReadPixels` can read back from a user FBO color attachment
- More GL entry points wired: - More GL entry points wired:
- `DrawRangeElements` / `DrawRangeElementsBaseVertex` - `DrawRangeElements` / `DrawRangeElementsBaseVertex`
- `DrawElementsBaseVertex` with real baseVertex selection
- `MultiDrawArrays` / `MultiDrawElements` / `MultiDrawElementsBaseVertex` - `MultiDrawArrays` / `MultiDrawElements` / `MultiDrawElementsBaseVertex`
- `DrawArraysInstanced` / `DrawElementsInstanced` family - `DrawArraysInstanced` / `DrawElementsInstanced` family
- Indirect draw CPU fallback: `DrawArraysIndirect`, `DrawElementsIndirect`, `MultiDraw*Indirect`, `*IndirectCount` - Indirect draw CPU fallback: `DrawArraysIndirect`, `DrawElementsIndirect`, `MultiDraw*Indirect`, `*IndirectCount`
@@ -163,7 +165,7 @@ Verified locally on Turnip Adreno 750:
- Local test result: - Local test result:
``` ```
[ PASSED ] 15 tests [ PASSED ] 16 tests
``` ```
--- ---
@@ -113,8 +113,10 @@ namespace MobileGL::MG_Backend::DiligentBackend {
const void* indices, GLint basevertex) { const void* indices, GLint basevertex) {
(void)start; (void)start;
(void)end; (void)end;
(void)basevertex; auto* renderer = GetActiveRenderer();
DrawElements(mode, count, type, indices); if (renderer != nullptr) {
renderer->DrawFromState(mode, 0, count, type, indices, basevertex);
}
} }
void MultiDrawArrays(GLenum mode, const GLint* first, const GLsizei* count, GLsizei drawcount) { void MultiDrawArrays(GLenum mode, const GLint* first, const GLsizei* count, GLsizei drawcount) {
@@ -144,11 +146,10 @@ namespace MobileGL::MG_Backend::DiligentBackend {
void DrawElementsBaseVertex(GLenum mode, GLsizei count, GLenum type, const void* indices, void DrawElementsBaseVertex(GLenum mode, GLsizei count, GLenum type, const void* indices,
GLint basevertex) { GLint basevertex) {
// The CPU vertex-upload path currently treats the element indices as absolute auto* renderer = GetActiveRenderer();
// vertex indices; basevertex is accepted for compatibility and applied by the if (renderer != nullptr) {
// UploadVertexDataFromState path when it is extended. renderer->DrawFromState(mode, 0, count, type, indices, basevertex);
(void)basevertex; }
DrawElements(mode, count, type, indices);
} }
void MultiDrawElementsBaseVertex(GLenum mode, const GLsizei* count, GLenum type, void MultiDrawElementsBaseVertex(GLenum mode, const GLsizei* count, GLenum type,
@@ -204,7 +205,8 @@ namespace MobileGL::MG_Backend::DiligentBackend {
} }
const void* indices = reinterpret_cast<const void*>(static_cast<SizeT>(cmd.FirstIndex) * indexSize); const void* indices = reinterpret_cast<const void*>(static_cast<SizeT>(cmd.FirstIndex) * indexSize);
for (Uint32 i = 0; i < cmd.InstanceCount; ++i) { for (Uint32 i = 0; i < cmd.InstanceCount; ++i) {
renderer->DrawFromState(mode, 0, static_cast<GLsizei>(cmd.Count), type, indices); renderer->DrawFromState(mode, 0, static_cast<GLsizei>(cmd.Count), type, indices,
static_cast<GLint>(cmd.BaseVertex));
} }
} }
@@ -258,7 +260,8 @@ namespace MobileGL::MG_Backend::DiligentBackend {
} }
const void* indices = reinterpret_cast<const void*>(static_cast<SizeT>(cmd.FirstIndex) * indexSize); const void* indices = reinterpret_cast<const void*>(static_cast<SizeT>(cmd.FirstIndex) * indexSize);
for (Uint32 instance = 0; instance < cmd.InstanceCount; ++instance) { for (Uint32 instance = 0; instance < cmd.InstanceCount; ++instance) {
renderer->DrawFromState(mode, 0, static_cast<GLsizei>(cmd.Count), type, indices); renderer->DrawFromState(mode, 0, static_cast<GLsizei>(cmd.Count), type, indices,
static_cast<GLint>(cmd.BaseVertex));
} }
} }
} }
@@ -332,8 +335,13 @@ namespace MobileGL::MG_Backend::DiligentBackend {
void DrawElementsInstancedBaseVertex(GLenum mode, GLsizei count, GLenum type, const void* indices, void DrawElementsInstancedBaseVertex(GLenum mode, GLsizei count, GLenum type, const void* indices,
GLsizei instancecount, GLint basevertex) { GLsizei instancecount, GLint basevertex) {
(void)basevertex; auto* renderer = GetActiveRenderer();
DrawElementsInstanced(mode, count, type, indices, instancecount); if (renderer == nullptr || instancecount <= 0) {
return;
}
for (GLsizei i = 0; i < instancecount; ++i) {
renderer->DrawFromState(mode, 0, count, type, indices, basevertex);
}
} }
void DrawElementsInstancedBaseInstance(GLenum mode, GLsizei count, GLenum type, const void* indices, void DrawElementsInstancedBaseInstance(GLenum mode, GLsizei count, GLenum type, const void* indices,
@@ -345,9 +353,14 @@ namespace MobileGL::MG_Backend::DiligentBackend {
void DrawElementsInstancedBaseVertexBaseInstance(GLenum mode, GLsizei count, GLenum type, void DrawElementsInstancedBaseVertexBaseInstance(GLenum mode, GLsizei count, GLenum type,
const void* indices, GLsizei instancecount, const void* indices, GLsizei instancecount,
GLint basevertex, GLuint baseinstance) { GLint basevertex, GLuint baseinstance) {
(void)basevertex;
(void)baseinstance; (void)baseinstance;
DrawElementsInstanced(mode, count, type, indices, instancecount); auto* renderer = GetActiveRenderer();
if (renderer == nullptr || instancecount <= 0) {
return;
}
for (GLsizei i = 0; i < instancecount; ++i) {
renderer->DrawFromState(mode, 0, count, type, indices, basevertex);
}
} }
void ClearBufferfv(GLenum buffer, GLint drawbuffer, const GLfloat* value) { void ClearBufferfv(GLenum buffer, GLint drawbuffer, const GLfloat* value) {
@@ -1190,14 +1190,15 @@ void main()
return true; return true;
} }
void DiligentRenderer::DrawFromState(GLenum mode, GLint first, GLsizei count, GLenum type, const void* indices) { void DiligentRenderer::DrawFromState(GLenum mode, GLint first, GLsizei count, GLenum type, const void* indices,
GLint baseVertex) {
if (!m_initialized || !m_pContext || MG_State::pGLContext == nullptr) { if (!m_initialized || !m_pContext || MG_State::pGLContext == nullptr) {
return; return;
} }
if (!CreatePipelineFromState(mode)) { if (!CreatePipelineFromState(mode)) {
return; return;
} }
if (!UploadVertexDataFromState(mode, first, count, type, indices)) { if (!UploadVertexDataFromState(mode, first, count, type, indices, baseVertex)) {
return; return;
} }
@@ -1496,7 +1497,7 @@ void main()
} }
Bool DiligentRenderer::UploadVertexDataFromState(GLenum mode, GLint first, GLsizei count, GLenum type, Bool DiligentRenderer::UploadVertexDataFromState(GLenum mode, GLint first, GLsizei count, GLenum type,
const void* indices) { const void* indices, GLint baseVertex) {
if (MG_State::pGLContext == nullptr) { if (MG_State::pGLContext == nullptr) {
return false; return false;
} }
@@ -1581,7 +1582,16 @@ void main()
Vector<Uint8> vertexData(static_cast<SizeT>(drawVertexCount) * vertexStride); Vector<Uint8> vertexData(static_cast<SizeT>(drawVertexCount) * vertexStride);
for (Uint32 vi = 0; vi < drawVertexCount; ++vi) { for (Uint32 vi = 0; vi < drawVertexCount; ++vi) {
const Uint32 srcIndex = indicesData.empty() ? (static_cast<Uint32>(first) + vi) : indicesData[vi]; Uint32 srcIndex = 0;
if (indicesData.empty()) {
srcIndex = static_cast<Uint32>(first) + vi;
} else {
const Int64 resolvedIndex = static_cast<Int64>(indicesData[vi]) + baseVertex;
if (resolvedIndex < 0) {
return false;
}
srcIndex = static_cast<Uint32>(resolvedIndex);
}
Uint8* dst = vertexData.data() + static_cast<SizeT>(vi) * vertexStride; Uint8* dst = vertexData.data() + static_cast<SizeT>(vi) * vertexStride;
for (Uint32 attrIndex : activeAttribs) { for (Uint32 attrIndex : activeAttribs) {
const auto& attr = attributes[attrIndex]; const auto& attr = attributes[attrIndex];
@@ -59,7 +59,8 @@ namespace MobileGL::MG_Backend::DiligentBackend {
Bool CreateTestTexture(const void* data, Uint32 width, Uint32 height); Bool CreateTestTexture(const void* data, Uint32 width, Uint32 height);
// Draws using the live MG_State GL context: current program, VAO and // Draws using the live MG_State GL context: current program, VAO and
// bound buffers. This is the front-end emulation entry point. // bound buffers. This is the front-end emulation entry point.
void DrawFromState(GLenum mode, GLint first, GLsizei count, GLenum type, const void* indices); void DrawFromState(GLenum mode, GLint first, GLsizei count, GLenum type, const void* indices,
GLint baseVertex = 0);
void ReadPixels(Uint32 x, Uint32 y, Uint32 width, Uint32 height, void* pixels); void ReadPixels(Uint32 x, Uint32 y, Uint32 width, Uint32 height, void* pixels);
void BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, void BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
@@ -91,7 +92,8 @@ namespace MobileGL::MG_Backend::DiligentBackend {
Bool CreatePipeline(); Bool CreatePipeline();
Bool CreateVertexBuffer(); Bool CreateVertexBuffer();
Bool CreatePipelineFromState(GLenum mode); Bool CreatePipelineFromState(GLenum mode);
Bool UploadVertexDataFromState(GLenum mode, GLint first, GLsizei count, GLenum type, const void* indices); Bool UploadVertexDataFromState(GLenum mode, GLint first, GLsizei count, GLenum type, const void* indices,
GLint baseVertex = 0);
::Diligent::ITextureView* SyncTexture(MG_State::GLState::ITextureObject& texture); ::Diligent::ITextureView* SyncTexture(MG_State::GLState::ITextureObject& texture);
::Diligent::ITextureView* SyncTextureForAttachment(MG_State::GLState::ITextureObject& texture, Bool depth); ::Diligent::ITextureView* SyncTextureForAttachment(MG_State::GLState::ITextureObject& texture, Bool depth);
::Diligent::ITextureView* SyncRenderbuffer(MG_State::GLState::RenderbufferObject& renderbuffer); ::Diligent::ITextureView* SyncRenderbuffer(MG_State::GLState::RenderbufferObject& renderbuffer);
@@ -1046,3 +1046,77 @@ void main() {
EXPECT_LT(center1[0], 50) << "attachment 1 should not be red"; EXPECT_LT(center1[0], 50) << "attachment 1 should not be red";
EXPECT_GT(center1[2], 200) << "attachment 1 should be blue"; EXPECT_GT(center1[2], 200) << "attachment 1 should be blue";
} }
TEST(DiligentVulkanBackend, DrawsIndexedBaseVertexFromMobileGLState) {
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
out vec4 Color;
void main() { Color = vec4(1.0, 0.0, 0.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);
BindFramebuffer(GL_FRAMEBUFFER, 0);
ReadBuffer(GL_BACK);
Disable(GL_DEPTH_TEST);
Disable(GL_BLEND);
Disable(GL_SCISSOR_TEST);
Disable(GL_STENCIL_TEST);
// First triangle is far offscreen; baseVertex=3 makes indices {0,1,2} select
// the second, visible triangle.
const float vertices[] = {
-10.0f, -10.0f, -9.0f, -10.0f, -9.0f, -9.0f,
-0.5f, -0.5f, 0.5f, -0.5f, 0.0f, 0.5f,
};
const GLuint indices[] = {0, 1, 2};
GLuint vbo = 0;
GenBuffers(1, &vbo);
BindBuffer(GL_ARRAY_BUFFER, vbo);
BufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
GLuint ebo = 0;
GenBuffers(1, &ebo);
BindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
BufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
GLuint vao = 0;
GenVertexArrays(1, &vao);
BindVertexArray(vao);
EnableVertexAttribArray(0);
VertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
BindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
DiligentBackend::BackendObject_Diligent backend;
backend.Initialize();
auto* renderer = backend.GetRenderer();
if (renderer == nullptr) {
GTEST_SKIP() << "No Vulkan adapter available; skipping base vertex test";
}
renderer->Clear(0.0f, 1.0f, 0.0f, 1.0f);
renderer->DrawFromState(GL_TRIANGLES, 0, 3, GL_UNSIGNED_INT, nullptr, 3);
renderer->Present();
std::uint8_t center[4] = {};
renderer->ReadPixels(128, 128, 1, 1, center);
EXPECT_GT(center[0], 200) << "center should be red from baseVertex-selected triangle";
EXPECT_LT(center[1], 50) << "center should not be green";
}