[Feat] (MG_Backend/DirectVulkan): add draw cmd MultiDrawIndexedCmd

This commit is contained in:
2026-03-18 16:08:00 +08:00
parent bc018c9513
commit e4455aed9a
3 changed files with 120 additions and 68 deletions
@@ -55,8 +55,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
DrawCmd payload{}; DrawCmd payload{};
payload.mode = mode; payload.mode = mode;
payload.firstVertex = first; payload.params.firstVertex = first;
payload.vertexCount = count; payload.params.vertexCount = count;
pVulkanRenderer->DrawArrays(payload); pVulkanRenderer->DrawArrays(payload);
} }
@@ -69,8 +69,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
payload.mode = mode; payload.mode = mode;
payload.indexBufferView.indexType = type; payload.indexBufferView.indexType = type;
payload.indexBufferView.indexByteOffset = reinterpret_cast<SizeT>(indices); payload.indexBufferView.indexByteOffset = reinterpret_cast<SizeT>(indices);
payload.indexCount = count; payload.indexBufferView.indexByteSize = count * MG_Util::GetGLTypeSize(type);
payload.instanceCount = 1; payload.params.indexCount = count;
payload.params.instanceCount = 1;
pVulkanRenderer->DrawElements(payload); pVulkanRenderer->DrawElements(payload);
} }
@@ -109,11 +110,12 @@ namespace MobileGL::MG_Backend::DirectVulkan {
payload.mode = mode; payload.mode = mode;
payload.indexBufferView.indexType = type; payload.indexBufferView.indexType = type;
payload.indexBufferView.indexByteOffset = reinterpret_cast<SizeT>(indices); payload.indexBufferView.indexByteOffset = reinterpret_cast<SizeT>(indices);
payload.indexCount = count; payload.indexBufferView.indexByteSize = count * MG_Util::GetGLTypeSize(type);
payload.instanceCount = 1; payload.params.indexCount = count;
payload.firstIndex = 0; payload.params.instanceCount = 1;
payload.vertexOffset = basevertex; payload.params.firstIndex = 0;
payload.firstInstance = 0; payload.params.vertexOffset = basevertex;
payload.params.firstInstance = 0;
pVulkanRenderer->DrawElements(payload); pVulkanRenderer->DrawElements(payload);
} }
@@ -121,13 +123,38 @@ namespace MobileGL::MG_Backend::DirectVulkan {
GLsizei drawcount, const GLint* basevertex) { GLsizei drawcount, const GLint* basevertex) {
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::MultiDrawElements called with null VulkanRenderer"); MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::MultiDrawElements called with null VulkanRenderer");
MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::MultiDrawElements called with null GL context"); MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::MultiDrawElements called with null GL context");
// TODO: properly batch the draw calls MultiDrawIndexedCmd payload{};
payload.mode = mode;
payload.indexBufferView.indexType = type;
// TODO: allocate draw cmd buf elsewhere
static Vector<DrawIndexedCmdParam> params;
params.clear();
params.resize(drawcount);
for (GLsizei i = 0; i < drawcount; ++i) { for (GLsizei i = 0; i < drawcount; ++i) {
if (count[i] == 0) { if (count[i] == 0) {
continue; continue;
} }
DrawElementsBaseVertex(mode, count[i], type, indices[i], basevertex[i]);
// TODO: this index view needs a redesign, now there's a lotta redundant uploads
payload.indexBufferView.indexByteOffset = 0;
payload.indexBufferView.indexByteSize =
std::max(reinterpret_cast<SizeT>(indices[i]) + count[i] * MG_Util::GetGLTypeSize(type),
payload.indexBufferView.indexByteSize);
auto& param = params[i];
param.indexCount = count[i];
param.instanceCount = 1;
param.firstIndex = reinterpret_cast<SizeT>(indices[i]) / MG_Util::GetGLTypeSize(type);
param.vertexOffset = basevertex[i];
param.firstInstance = 0;
} }
payload.drawCount = drawcount;
payload.pParams = params.data();
pVulkanRenderer->MultiDrawElements(payload);
} }
void BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, void BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1,
@@ -399,7 +399,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
vkDestroyDevice(m_device, nullptr); vkDestroyDevice(m_device, nullptr);
m_device = VK_NULL_HANDLE; m_device = VK_NULL_HANDLE;
} }
m_cmdDrawIndexedIndirectCount = nullptr; s_vkCmdDrawIndexedIndirectCount = nullptr;
if (m_surface != VK_NULL_HANDLE) { if (m_surface != VK_NULL_HANDLE) {
vkDestroySurfaceKHR(m_instance, m_surface, nullptr); vkDestroySurfaceKHR(m_instance, m_surface, nullptr);
@@ -526,11 +526,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
Bool VulkanRenderer::UploadAndBindIndexBuffer(FrameContext::FrameData& frame, Bool VulkanRenderer::UploadAndBindIndexBuffer(FrameContext::FrameData& frame,
const MG_State::GLState::VertexArrayObject& vao, const MG_State::GLState::VertexArrayObject& vao,
GLenum indexType, const IndexBufferView* pIndexBufferView) {
SizeT indexByteOffset,
Uint32 indexCount) {
VkIndexType vkIndexType = VK_INDEX_TYPE_MAX_ENUM; VkIndexType vkIndexType = VK_INDEX_TYPE_MAX_ENUM;
switch (indexType) { switch (pIndexBufferView->indexType) {
case GL_UNSIGNED_SHORT: case GL_UNSIGNED_SHORT:
vkIndexType = VK_INDEX_TYPE_UINT16; vkIndexType = VK_INDEX_TYPE_UINT16;
break; break;
@@ -538,7 +536,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
vkIndexType = VK_INDEX_TYPE_UINT32; vkIndexType = VK_INDEX_TYPE_UINT32;
break; break;
default: default:
MGLOG_D("DrawElements skipped: index type %u is not supported yet", indexType); MGLOG_D("DrawElements skipped: index type %u is not supported yet", pIndexBufferView->indexType);
return false; return false;
} }
@@ -547,14 +545,14 @@ namespace MobileGL::MG_Backend::DirectVulkan {
const auto indexData = indexBuffer->GetDataReadOnly(); const auto indexData = indexBuffer->GetDataReadOnly();
MOBILEGL_ASSERT(indexData != nullptr && !indexData->empty(), "DrawElements requires non-empty EBO data"); MOBILEGL_ASSERT(indexData != nullptr && !indexData->empty(), "DrawElements requires non-empty EBO data");
const SizeT indexSize = (indexType == GL_UNSIGNED_SHORT) ? sizeof(Uint16) : sizeof(Uint32); const SizeT indexSize = MG_Util::GetGLTypeSize(pIndexBufferView->indexType);
const SizeT indexDataSizeBytes = static_cast<SizeT>(indexCount) * indexSize; const SizeT indexDataSizeBytes = pIndexBufferView->indexByteSize;
MOBILEGL_ASSERT(indexByteOffset + indexDataSizeBytes <= indexBuffer->GetSize(), MOBILEGL_ASSERT(pIndexBufferView->indexByteOffset + indexDataSizeBytes <= indexBuffer->GetSize(),
"DrawElements index range out of bounds"); "DrawElements index range out of bounds");
const Uint32 frameIndex = m_frameContext.GetCurrentFrameIndex(); const Uint32 frameIndex = m_frameContext.GetCurrentFrameIndex();
VkDeviceSize& frameIndexHead = m_frameIndexUploadHeads[frameIndex]; VkDeviceSize& frameIndexHead = m_frameIndexUploadHeads[frameIndex];
const VkDeviceSize alignment = static_cast<VkDeviceSize>(indexSize); const VkDeviceSize alignment = indexSize;
const VkDeviceSize writeOffset = (frameIndexHead + alignment - 1) & ~(alignment - 1); const VkDeviceSize writeOffset = (frameIndexHead + alignment - 1) & ~(alignment - 1);
const VkDeviceSize writeEnd = writeOffset + static_cast<VkDeviceSize>(indexDataSizeBytes); const VkDeviceSize writeEnd = writeOffset + static_cast<VkDeviceSize>(indexDataSizeBytes);
if (!EnsureFrameUploadBufferCapacity(frameIndex, true, writeEnd, 1 * 1024 * 1024, if (!EnsureFrameUploadBufferCapacity(frameIndex, true, writeEnd, 1 * 1024 * 1024,
@@ -564,7 +562,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
} }
auto& frameIndexUploadBuffer = m_frameIndexUploadBuffers[frameIndex]; auto& frameIndexUploadBuffer = m_frameIndexUploadBuffers[frameIndex];
if (!frameIndexUploadBuffer.Upload(indexData->data() + indexByteOffset, if (!frameIndexUploadBuffer.Upload(indexData->data() + pIndexBufferView->indexByteOffset,
static_cast<VkDeviceSize>(indexDataSizeBytes), writeOffset)) { static_cast<VkDeviceSize>(indexDataSizeBytes), writeOffset)) {
MGLOG_E("DrawElements skipped: failed to upload index data"); MGLOG_E("DrawElements skipped: failed to upload index data");
return false; return false;
@@ -771,7 +769,7 @@ void main() {
} }
void VulkanRenderer::SetupDraw(FrameContext::FrameData& frame, GLenum mode, Flags<DrawSetupAspect> aspects, void VulkanRenderer::SetupDraw(FrameContext::FrameData& frame, GLenum mode, Flags<DrawSetupAspect> aspects,
GLenum indexType, SizeT indexByteOffset, Uint32 indexCount) { const IndexBufferView* pIndexBufferView) {
m_textureManager->CollectGarbage(); m_textureManager->CollectGarbage();
const auto& drawFbo = const auto& drawFbo =
MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject(); MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject();
@@ -884,7 +882,7 @@ void main() {
MOBILEGL_ASSERT(vtxUploadOk, "SetupDraw skipped: failed to upload vertex streams"); MOBILEGL_ASSERT(vtxUploadOk, "SetupDraw skipped: failed to upload vertex streams");
if (aspects & DrawSetupAspect::IndexBuffer) { if (aspects & DrawSetupAspect::IndexBuffer) {
auto idxUploadOk = UploadAndBindIndexBuffer(frame, vao, indexType, indexByteOffset, indexCount); auto idxUploadOk = UploadAndBindIndexBuffer(frame, vao, pIndexBufferView);
MOBILEGL_ASSERT(idxUploadOk, "SetupDraw skipped: failed to upload index buffer"); MOBILEGL_ASSERT(idxUploadOk, "SetupDraw skipped: failed to upload index buffer");
} }
@@ -1259,32 +1257,48 @@ void main() {
VkCommandBuffer& commandBuffer = frame.commandBuffer; VkCommandBuffer& commandBuffer = frame.commandBuffer;
vkCmdDraw(commandBuffer, vkCmdDraw(commandBuffer,
payload.vertexCount, payload.params.vertexCount,
payload.instanceCount, payload.params.instanceCount,
payload.firstVertex, payload.params.firstVertex,
payload.firstInstance); payload.params.firstInstance);
} }
void VulkanRenderer::DrawElements(const DrawIndexedCmd& payload) { void VulkanRenderer::DrawElements(const DrawIndexedCmd& payload) {
auto& frame = m_frameContext.GetCurrent(); auto& frame = m_frameContext.GetCurrent();
SetupDraw(frame, payload.mode, DrawSetupAspect::IndexBuffer, SetupDraw(frame, payload.mode, DrawSetupAspect::IndexBuffer,
payload.indexBufferView.indexType, payload.indexBufferView.indexByteOffset, payload.indexCount); &payload.indexBufferView);
MOBILEGL_ASSERT(frame.isCommandRecording, "%s: frame recording was not started", __func__); MOBILEGL_ASSERT(frame.isCommandRecording, "%s: frame recording was not started", __func__);
VkCommandBuffer& commandBuffer = frame.commandBuffer; VkCommandBuffer& commandBuffer = frame.commandBuffer;
vkCmdDrawIndexed(commandBuffer, vkCmdDrawIndexed(commandBuffer,
payload.indexCount, payload.params.indexCount,
payload.instanceCount, payload.params.instanceCount,
payload.firstIndex, payload.params.firstIndex,
payload.vertexOffset, payload.params.vertexOffset,
payload.firstInstance); payload.params.firstInstance);
} }
void VulkanRenderer::MultiDrawElements(const Vector<DrawIndexedCmd>& payloads) { void VulkanRenderer::MultiDrawElements(const MultiDrawIndexedCmd& payload) {
auto& frame = m_frameContext.GetCurrent();
SetupDraw(frame, payload.mode, DrawSetupAspect::IndexBuffer,
&payload.indexBufferView);
MOBILEGL_ASSERT(frame.isCommandRecording, "%s: frame recording was not started", __func__);
VkCommandBuffer& commandBuffer = frame.commandBuffer;
for (Uint32 idraw = 0; idraw < payload.drawCount; ++idraw) {
vkCmdDrawIndexed(commandBuffer,
payload.pParams[idraw].indexCount,
payload.pParams[idraw].instanceCount,
payload.pParams[idraw].firstIndex,
payload.pParams[idraw].vertexOffset,
payload.pParams[idraw].firstInstance);
}
} }
void VulkanRenderer::Present() { void VulkanRenderer::Present() {
@@ -1639,14 +1653,15 @@ void main() {
deviceCreateInfo.ppEnabledExtensionNames = enabledDeviceExtensions.data(); deviceCreateInfo.ppEnabledExtensionNames = enabledDeviceExtensions.data();
VK_VERIFY(vkCreateDevice(m_physicalDevice.handle, &deviceCreateInfo, nullptr, &m_device), "vkCreateDevice"); VK_VERIFY(vkCreateDevice(m_physicalDevice.handle, &deviceCreateInfo, nullptr, &m_device), "vkCreateDevice");
m_cmdDrawIndexedIndirectCount = reinterpret_cast<PFNDrawIndexedIndirectCountFunc>( s_vkCmdDrawIndexedIndirectCount = reinterpret_cast<PFNDrawIndexedIndirectCountFunc>(
vkGetDeviceProcAddr(m_device, "vkCmdDrawIndexedIndirectCountKHR")); vkGetDeviceProcAddr(m_device, "vkCmdDrawIndexedIndirectCountKHR"));
if (m_cmdDrawIndexedIndirectCount == nullptr) { if (s_vkCmdDrawIndexedIndirectCount == nullptr) {
m_cmdDrawIndexedIndirectCount = reinterpret_cast<PFNDrawIndexedIndirectCountFunc>( s_vkCmdDrawIndexedIndirectCount = reinterpret_cast<PFNDrawIndexedIndirectCountFunc>(
vkGetDeviceProcAddr(m_device, "vkCmdDrawIndexedIndirectCount")); vkGetDeviceProcAddr(m_device, "vkCmdDrawIndexedIndirectCount"));
} }
if (m_drawIndirectCountExtensionEnabled && m_cmdDrawIndexedIndirectCount == nullptr) { if (m_drawIndirectCountExtensionEnabled && s_vkCmdDrawIndexedIndirectCount == nullptr) {
MGLOG_W("VK_KHR_draw_indirect_count enabled but vkCmdDrawIndexedIndirectCount entry point is missing"); MGLOG_W("VK_KHR_draw_indirect_count enabled but vkCmdDrawIndexedIndirectCount entry point is missing, will continue as if VK_KHR_draw_indirect_count is not supported!");
m_drawIndirectCountExtensionEnabled = false;
} }
MGLOG_I("Logical device created."); MGLOG_I("Logical device created.");
@@ -39,29 +39,19 @@ namespace MobileGL::MG_Backend::DirectVulkan {
UniformBuffer = 1 << 2, UniformBuffer = 1 << 2,
VertexBuffer = 1 << 3, VertexBuffer = 1 << 3,
IndexBuffer = 1 << 4, IndexBuffer = 1 << 4,
Viewport = 1 << 5, IndirectDrawBuffer = 1 << 5,
Scissor = 1 << 6, Viewport = 1 << 6,
Scissor = 1 << 7,
}; };
struct DrawBaseCmd { struct DrawCmdParam {
GLenum mode = GL_TRIANGLES;
};
struct DrawCmd: public DrawBaseCmd {
Uint32 vertexCount = 0; Uint32 vertexCount = 0;
Uint32 instanceCount = 1; Uint32 instanceCount = 1;
Uint32 firstVertex = 0; Uint32 firstVertex = 0;
Uint32 firstInstance = 0; Uint32 firstInstance = 0;
}; };
struct IndexBufferView { struct DrawIndexedCmdParam {
GLenum indexType = GL_UNSIGNED_SHORT;
SizeT indexByteOffset = 0;
};
struct DrawIndexedCmd: public DrawBaseCmd {
IndexBufferView indexBufferView;
Uint32 indexCount = 0; Uint32 indexCount = 0;
Uint32 instanceCount = 1; Uint32 instanceCount = 1;
Uint32 firstIndex = 0; Uint32 firstIndex = 0;
@@ -69,8 +59,30 @@ namespace MobileGL::MG_Backend::DirectVulkan {
Int32 firstInstance = 0; Int32 firstInstance = 0;
}; };
struct MultiDrawElementsCmd { struct DrawCmd {
GLenum mode = GL_TRIANGLES;
DrawCmdParam params;
};
struct IndexBufferView {
GLenum indexType = GL_UNSIGNED_SHORT;
SizeT indexByteOffset = 0;
SizeT indexByteSize = 0;
};
struct DrawIndexedCmd {
GLenum mode = GL_TRIANGLES;
IndexBufferView indexBufferView;
DrawIndexedCmdParam params;
};
struct MultiDrawIndexedCmd {
GLenum mode = GL_TRIANGLES;
IndexBufferView indexBufferView;
Uint32 drawCount = 0;
DrawIndexedCmdParam* pParams = nullptr;
}; };
struct QueueFamilyIndices { struct QueueFamilyIndices {
@@ -97,7 +109,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
void Shutdown(); void Shutdown();
void SetupDraw(FrameContext::FrameData& frame, GLenum mode, Flags<DrawSetupAspect> aspects, void SetupDraw(FrameContext::FrameData& frame, GLenum mode, Flags<DrawSetupAspect> aspects,
GLenum indexType = 0, SizeT indexByteOffset = 0, Uint32 indexCount = 0); const IndexBufferView* pIndexBufferView = nullptr);
void ClearAttachmentsOnActiveRenderPass(VkCommandBuffer commandBuffer, void ClearAttachmentsOnActiveRenderPass(VkCommandBuffer commandBuffer,
const RenderPassEntry& compatibleRenderPassEntry); const RenderPassEntry& compatibleRenderPassEntry);
@@ -107,7 +119,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
GLbitfield mask, GLenum filter); GLbitfield mask, GLenum filter);
void DrawArrays(const DrawCmd& payload); void DrawArrays(const DrawCmd& payload);
void DrawElements(const DrawIndexedCmd& payload); void DrawElements(const DrawIndexedCmd& payload);
void MultiDrawElements(const Vector<DrawIndexedCmd>& payloads); void MultiDrawElements(const MultiDrawIndexedCmd& payloads);
void Present(); void Present();
const PhysicalDevice& GetPhysicalDevice() const; const PhysicalDevice& GetPhysicalDevice() const;
@@ -154,7 +166,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize offset, VkBuffer countBuffer,
VkDeviceSize countBufferOffset, Uint32 maxDrawCount, VkDeviceSize countBufferOffset, Uint32 maxDrawCount,
Uint32 stride); Uint32 stride);
PFNDrawIndexedIndirectCountFunc m_cmdDrawIndexedIndirectCount = nullptr; static inline PFNDrawIndexedIndirectCountFunc s_vkCmdDrawIndexedIndirectCount = nullptr;
VkCommandPool m_commandPool = VK_NULL_HANDLE; VkCommandPool m_commandPool = VK_NULL_HANDLE;
@@ -203,9 +215,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
Bool UploadAndBindVertexStreams(VkCommandBuffer commandBuffer, const MG_State::GLState::VertexArrayObject& vao); Bool UploadAndBindVertexStreams(VkCommandBuffer commandBuffer, const MG_State::GLState::VertexArrayObject& vao);
Bool UploadAndBindIndexBuffer(FrameContext::FrameData& frame, Bool UploadAndBindIndexBuffer(FrameContext::FrameData& frame,
const MG_State::GLState::VertexArrayObject& vao, const MG_State::GLState::VertexArrayObject& vao,
GLenum indexType, const IndexBufferView* pIndexBufferView = nullptr);
SizeT indexByteOffset,
Uint32 indexCount);
Bool InitializeBlitResources(); Bool InitializeBlitResources();
void ShutdownBlitResources(); void ShutdownBlitResources();
Bool TryBlitToDefaultFramebufferWithShader(FrameContext::FrameData& frame, Bool TryBlitToDefaultFramebufferWithShader(FrameContext::FrameData& frame,