diff --git a/MobileGL/MG_Backend/DirectVulkan/DirectVulkan.cpp b/MobileGL/MG_Backend/DirectVulkan/DirectVulkan.cpp index a6e6305a..6c722b25 100644 --- a/MobileGL/MG_Backend/DirectVulkan/DirectVulkan.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/DirectVulkan.cpp @@ -18,8 +18,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { void ClearBufferuiv(GLenum buffer, GLint drawbuffer, const GLuint* value) {} void ClearBufferiv(GLenum buffer, GLint drawbuffer, const GLint* value) {} void DrawElementsBaseVertex(GLenum mode, GLsizei count, GLenum type, const GLvoid* indices, GLint basevertex) {} - void MultiDrawElements(GLenum mode, const GLsizei* count, GLenum type, const GLvoid* const* indices, - GLsizei drawcount) {} + void MultiDrawElementsBaseVertex(GLenum mode, const GLsizei* count, GLenum type, const GLvoid* const* indices, GLsizei drawcount, const GLint* basevertex) {} void MultiDrawElementsIndirect(GLenum mode, GLenum type, const void* indirect, GLsizei drawcount, GLsizei stride) {} @@ -47,6 +46,96 @@ namespace MobileGL::MG_Backend::DirectVulkan { void ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* pixels) {} void GetTexImage(GLenum target, GLint level, GLenum format, GLenum type, GLvoid* pixels) {} + void MultiDrawElements(GLenum mode, const GLsizei* count, GLenum type, const GLvoid* const* indices, + GLsizei drawcount) { + MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::MultiDrawElements called with null VulkanRenderer"); + MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::MultiDrawElements called with null GL context"); + + if (drawcount < 0) { + MGLOG_W("MultiDrawElements skipped: drawcount (%d) must be non-negative", drawcount); + return; + } + if (drawcount == 0) { + return; + } + if (!count || !indices) { + MGLOG_W("MultiDrawElements skipped: count/indices pointer is null"); + return; + } + if (mode != GL_TRIANGLES) { + MGLOG_W("MultiDrawElements skipped: primitive mode %u is not supported yet", mode); + return; + } + + SizeT indexSize = 0; + switch (type) { + case GL_UNSIGNED_SHORT: + indexSize = sizeof(Uint16); + break; + case GL_UNSIGNED_INT: + indexSize = sizeof(Uint32); + break; + default: + MGLOG_W("MultiDrawElements skipped: index type %u is not supported yet", type); + return; + } + + const auto vao = MG_State::pGLContext->GetBoundVertexArray(); + if (!vao) { + MGLOG_W("MultiDrawElements skipped: no bound VAO"); + return; + } + + const auto indexBuffer = vao->GetIndexBufferBindingSlot().GetBoundObject(); + if (!indexBuffer) { + MGLOG_W("MultiDrawElements skipped: no bound ELEMENT_ARRAY_BUFFER"); + return; + } + + const auto indexData = indexBuffer->GetDataReadOnly(); + if (!indexData || indexData->empty()) { + MGLOG_W("MultiDrawElements skipped: ELEMENT_ARRAY_BUFFER has no data"); + return; + } + + const auto currentProgram = MG_State::pGLContext->GetCurrentProgram(); + Vector payloads; + payloads.reserve(static_cast(drawcount)); + for (GLsizei i = 0; i < drawcount; ++i) { + if (count[i] < 0) { + MGLOG_W("MultiDrawElements skipped: count[%d] (%d) must be non-negative", i, count[i]); + return; + } + if (count[i] == 0) { + continue; + } + + const auto byteOffset = reinterpret_cast(indices[i]); + const SizeT requiredBytes = static_cast(count[i]) * indexSize; + if (byteOffset + requiredBytes > indexBuffer->GetSize()) { + MGLOG_W("MultiDrawElements skipped: draw[%d] index range out of bounds (offset=%zu, size=%zu, " + "buffer=%zu)", + i, byteOffset, requiredBytes, indexBuffer->GetSize()); + return; + } + + DrawElementPayload payload{}; + payload.drawArray.mode = mode; + payload.drawArray.first = 0; + payload.drawArray.count = count[i]; + payload.drawArray.program = currentProgram ? currentProgram.get() : nullptr; + payload.drawArray.vertexArray = vao.get(); + payload.indexType = type; + payload.indexByteOffset = byteOffset; + payloads.push_back(payload); + } + + if (payloads.empty()) { + return; + } + pVulkanRenderer->MultiDrawElements(payloads); + } + void Clear(GLbitfield mask) { MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::Clear called with null VulkanRenderer"); MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::Clear called with null GL context"); diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp index 29a09b8c..69abf670 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp @@ -13,6 +13,7 @@ #include "MG_State/GLState/Core.h" #include "MG_State/GLState/ProgramState/ProgramObject.h" #include "MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.h" +#include namespace MobileGL::MG_Backend::DirectVulkan { VkBool32 VulkanRenderer::DebugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, @@ -292,6 +293,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { vkDestroyDevice(m_device, nullptr); m_device = VK_NULL_HANDLE; } + m_cmdDrawIndexedIndirectCount = nullptr; if (m_surface != VK_NULL_HANDLE) { vkDestroySurfaceKHR(m_instance, m_surface, nullptr); @@ -999,6 +1001,235 @@ namespace MobileGL::MG_Backend::DirectVulkan { vkCmdDrawIndexed(commandBuffer, static_cast(payload.drawArray.count), 1, 0, 0, 0); } + void VulkanRenderer::MultiDrawElements(const Vector& payloads) { + if (payloads.empty()) { + return; + } + + const DrawElementPayload& firstPayload = payloads.front(); + if (firstPayload.drawArray.mode != GL_TRIANGLES) { + MGLOG_D("MultiDrawElements skipped: primitive mode %u is not supported yet", firstPayload.drawArray.mode); + return; + } + + EnsureFrameRecordingStarted(); + auto& frame = m_frameContext.GetCurrent(); + if (!frame.isCommandRecording || !m_isMainRenderPassActive) { + MGLOG_D("MultiDrawElements skipped: frame recording was not started"); + return; + } + + VkIndexType vkIndexType = VK_INDEX_TYPE_MAX_ENUM; + SizeT indexSize = 0; + switch (firstPayload.indexType) { + case GL_UNSIGNED_SHORT: + vkIndexType = VK_INDEX_TYPE_UINT16; + indexSize = sizeof(Uint16); + break; + case GL_UNSIGNED_INT: + vkIndexType = VK_INDEX_TYPE_UINT32; + indexSize = sizeof(Uint32); + break; + default: + MGLOG_D("MultiDrawElements skipped: index type %u is not supported yet", firstPayload.indexType); + return; + } + + if (firstPayload.drawArray.vertexArray == nullptr) { + MGLOG_D("MultiDrawElements skipped: no VAO provided"); + return; + } + if (firstPayload.drawArray.program == nullptr) { + MGLOG_D("MultiDrawElements skipped: no current program is bound"); + return; + } + + // VertexArrayObject currently exposes index-buffer binding through non-const accessor. + auto* vao = const_cast(firstPayload.drawArray.vertexArray); + const auto indexBuffer = vao->GetIndexBufferBindingSlot().GetBoundObject(); + if (!indexBuffer) { + MGLOG_D("MultiDrawElements skipped: VAO has no bound ELEMENT_ARRAY_BUFFER"); + return; + } + + const auto indexData = indexBuffer->GetDataReadOnly(); + MOBILEGL_ASSERT(indexData != nullptr && !indexData->empty(), "MultiDrawElements requires non-empty EBO data"); + + struct PreparedDraw { + Uint32 indexCount = 0; + SizeT sourceOffset = 0; + SizeT sourceByteCount = 0; + Uint32 firstIndex = 0; + }; + Vector preparedDraws; + preparedDraws.reserve(payloads.size()); + + SizeT totalIndexBytes = 0; + Uint32 firstIndex = 0; + for (const auto& payload : payloads) { + if (payload.drawArray.count <= 0) { + continue; + } + if (payload.drawArray.mode != firstPayload.drawArray.mode || payload.indexType != firstPayload.indexType || + payload.drawArray.vertexArray != firstPayload.drawArray.vertexArray || + payload.drawArray.program != firstPayload.drawArray.program) { + MGLOG_D("MultiDrawElements skipped: mixed draw state in one multi-draw call is not supported"); + return; + } + + const SizeT drawByteCount = static_cast(payload.drawArray.count) * indexSize; + MOBILEGL_ASSERT(payload.indexByteOffset + drawByteCount <= indexBuffer->GetSize(), + "MultiDrawElements index range out of bounds"); + + PreparedDraw draw{}; + draw.indexCount = static_cast(payload.drawArray.count); + draw.sourceOffset = payload.indexByteOffset; + draw.sourceByteCount = drawByteCount; + draw.firstIndex = firstIndex; + preparedDraws.push_back(draw); + + firstIndex += draw.indexCount; + totalIndexBytes += drawByteCount; + } + + if (preparedDraws.empty()) { + return; + } + + const VertexInputStateFactory::BackendVertexInputState* vertexInputState = nullptr; + if (firstPayload.drawArray.vertexArray && m_vertexInputStateFactory) { + vertexInputState = + &m_vertexInputStateFactory->GetOrCreateVertexInputState(*firstPayload.drawArray.vertexArray); + } + + const Uint64 vertexInputHash = vertexInputState ? vertexInputState->hash : 0; + const VkPipelineVertexInputStateCreateInfo* vertexInputInfo = + vertexInputState ? &vertexInputState->state : nullptr; + if (!vertexInputInfo) { + VertexInputStateBuilder emptyVertexInputBuilder; + vertexInputInfo = &emptyVertexInputBuilder.Build(); + } + + VkPipelineLayout pipelineLayoutToUse = m_pipelineLayout; + if (m_uniformDescriptorBinder) { + pipelineLayoutToUse = m_uniformDescriptorBinder->GetOrCreatePipelineLayout(*firstPayload.drawArray.program); + if (pipelineLayoutToUse == VK_NULL_HANDLE) { + MGLOG_D("MultiDrawElements skipped: failed to get pipeline layout for program"); + return; + } + } + + VkPipeline pipelineToBind = GetOrCreatePipeline(*firstPayload.drawArray.program, pipelineLayoutToUse, + vertexInputHash, *vertexInputInfo); + if (pipelineToBind == VK_NULL_HANDLE) { + MGLOG_D("MultiDrawElements skipped: failed to create/get pipeline"); + return; + } + + const Uint32 frameIndex = m_frameContext.GetCurrentFrameIndex(); + VkDeviceSize& frameIndexHead = m_frameIndexUploadHeads[frameIndex]; + const auto alignment = static_cast(indexSize); + const VkDeviceSize writeOffset = (frameIndexHead + alignment - 1) & ~(alignment - 1); + const VkDeviceSize writeEnd = writeOffset + static_cast(totalIndexBytes); + if (!EnsureFrameUploadBufferCapacity(frameIndex, true, writeEnd, 1 * 1024 * 1024, + VK_BUFFER_USAGE_INDEX_BUFFER_BIT)) { + MGLOG_E("MultiDrawElements skipped: failed to prepare index upload buffer"); + return; + } + auto& frameIndexUploadBuffer = m_frameIndexUploadBuffers[frameIndex]; + + VkDeviceSize writeCursor = writeOffset; + for (const auto& draw : preparedDraws) { + if (!frameIndexUploadBuffer.Upload(indexData->data() + draw.sourceOffset, + static_cast(draw.sourceByteCount), writeCursor)) { + MGLOG_E("MultiDrawElements skipped: failed to upload index data"); + return; + } + writeCursor += static_cast(draw.sourceByteCount); + } + frameIndexHead = writeEnd; + + VkCommandBuffer& commandBuffer = frame.commandBuffer; + const auto activeExtent = m_activeRenderExtent; + vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineToBind); + if (m_uniformDescriptorBinder && !m_uniformDescriptorBinder->BindProgramUniformBuffers( + commandBuffer, pipelineLayoutToUse, *firstPayload.drawArray.program, + m_frameContext.GetCurrentFrameIndex())) { + MGLOG_D("MultiDrawElements skipped: failed to bind uniform descriptors"); + return; + } + + if (vertexInputState && !vertexInputState->bindings.empty()) { + if (!UploadAndBindVertexStreams(*vertexInputState, firstPayload.drawArray, commandBuffer)) { + return; + } + } + + VkViewport viewport{}; + viewport.x = 0.0f; + viewport.y = 0.0f; + viewport.width = static_cast(activeExtent.width); + viewport.height = static_cast(activeExtent.height); + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + vkCmdSetViewport(commandBuffer, 0, 1, &viewport); + + VkRect2D scissor{}; + scissor.offset = {0, 0}; + scissor.extent = activeExtent; + vkCmdSetScissor(commandBuffer, 0, 1, &scissor); + + vkCmdBindIndexBuffer(commandBuffer, frameIndexUploadBuffer.GetHandle(), writeOffset, vkIndexType); + + const Bool canUseIndirectCount = + m_drawIndirectCountExtensionEnabled && m_cmdDrawIndexedIndirectCount != nullptr; + if (canUseIndirectCount) { + Vector indirectCommands(preparedDraws.size()); + for (SizeT i = 0; i < preparedDraws.size(); ++i) { + indirectCommands[i].indexCount = preparedDraws[i].indexCount; + indirectCommands[i].instanceCount = 1; + indirectCommands[i].firstIndex = preparedDraws[i].firstIndex; + indirectCommands[i].vertexOffset = 0; + indirectCommands[i].firstInstance = 0; + } + + const auto commandBytes = + static_cast(indirectCommands.size() * sizeof(VkDrawIndexedIndirectCommand)); + const VkDeviceSize countOffset = (commandBytes + 3) & ~VkDeviceSize(3); + const VkDeviceSize totalBytes = countOffset + sizeof(Uint32); + + VkBufferObject indirectUploadBuffer; + if (!indirectUploadBuffer.Create( + m_allocator, totalBytes, VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT, VMA_MEMORY_USAGE_AUTO_PREFER_HOST, + VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT)) { + MGLOG_W("MultiDrawElements: failed to allocate indirect buffer, fallback to vkCmdDrawIndexed loop"); + } else { + Vector indirectBlob(static_cast(totalBytes), 0); + memcpy(indirectBlob.data(), indirectCommands.data(), static_cast(commandBytes)); + const auto indirectDrawCount = static_cast(indirectCommands.size()); + memcpy(indirectBlob.data() + static_cast(countOffset), &indirectDrawCount, + sizeof(indirectDrawCount)); + + if (!indirectUploadBuffer.Upload(indirectBlob.data(), totalBytes, 0)) { + MGLOG_W("MultiDrawElements: failed to upload indirect commands, fallback to vkCmdDrawIndexed loop"); + } else { + m_cmdDrawIndexedIndirectCount(commandBuffer, indirectUploadBuffer.GetHandle(), 0, + indirectUploadBuffer.GetHandle(), countOffset, + static_cast(indirectCommands.size()), + static_cast(sizeof(VkDrawIndexedIndirectCommand))); + DeferDestroyBuffer(indirectUploadBuffer); + return; + } + DeferDestroyBuffer(indirectUploadBuffer); + } + } + + // Fallback + for (const auto& draw : preparedDraws) { + vkCmdDrawIndexed(commandBuffer, draw.indexCount, 1, draw.firstIndex, 0, 0); + } + } + Bool VulkanRenderer::BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter, Uint readFboExternalIndex, Uint drawFboExternalIndex, @@ -1584,30 +1815,19 @@ namespace MobileGL::MG_Backend::DirectVulkan { } Bool VulkanRenderer::IsNecessaryDeviceExtensionSupported(VkPhysicalDevice device) { - Uint32 extensionCount = 0; - vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr); + const Vector availableExtensions = EnumerateDeviceExtensions(device); - std::vector availableExtensions(extensionCount); - vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, availableExtensions.data()); - - MGLOG_I("Got %u Vulkan device extensions: ", extensionCount); + MGLOG_I("Got %u Vulkan device extensions: ", static_cast(availableExtensions.size())); for (auto& extension : availableExtensions) { MGLOG_I(" %s (r.%u)", extension.extensionName, extension.specVersion); } for (SizeT i = 0; i < std::size(s_deviceExtensionNames); ++i) { - Bool found = false; - for (const auto& extension : availableExtensions) { - if (strcmp(extension.extensionName, s_deviceExtensionNames[i]) == 0) { - MGLOG_I("Required extension found: %s", s_deviceExtensionNames[i]); - found = true; - break; - } - } - if (!found) { + if (!IsExtensionSupported(availableExtensions, s_deviceExtensionNames[i])) { MGLOG_I("Required extension not found: %s", s_deviceExtensionNames[i]); return false; } + MGLOG_I("Required extension found: %s", s_deviceExtensionNames[i]); } return true; @@ -1649,30 +1869,28 @@ namespace MobileGL::MG_Backend::DirectVulkan { } Vector enabledDeviceExtensions; - enabledDeviceExtensions.reserve(std::size(s_deviceExtensionNames) + 1); + enabledDeviceExtensions.reserve(std::size(s_deviceExtensionNames) + 2); for (const char* extensionName : s_deviceExtensionNames) { enabledDeviceExtensions.push_back(extensionName); } -#ifdef VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME - Uint32 extensionCount = 0; - VK_VERIFY(vkEnumerateDeviceExtensionProperties(m_physicalDevice.handle, nullptr, &extensionCount, nullptr)); - Vector availableExtensions(extensionCount); - VK_VERIFY(vkEnumerateDeviceExtensionProperties(m_physicalDevice.handle, nullptr, &extensionCount, - availableExtensions.data())); - - for (const auto& extension : availableExtensions) { - if (strcmp(extension.extensionName, VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME) == 0) { - enabledDeviceExtensions.push_back(VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME); - MGLOG_I("Enabled optional device extension: %s", VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME); - break; - } - } -#endif + const Vector availableExtensions = EnumerateDeviceExtensions(m_physicalDevice.handle); + ResolveOptionalDeviceExtensions(availableExtensions, enabledDeviceExtensions); + MGLOG_I("VK_KHR_draw_indirect_count enabled: %s", m_drawIndirectCountExtensionEnabled ? "true" : "false"); deviceCreateInfo.enabledExtensionCount = static_cast(enabledDeviceExtensions.size()); deviceCreateInfo.ppEnabledExtensionNames = enabledDeviceExtensions.data(); VK_VERIFY(vkCreateDevice(m_physicalDevice.handle, &deviceCreateInfo, nullptr, &m_device), "vkCreateDevice"); + + m_cmdDrawIndexedIndirectCount = reinterpret_cast( + vkGetDeviceProcAddr(m_device, "vkCmdDrawIndexedIndirectCountKHR")); + if (m_cmdDrawIndexedIndirectCount == nullptr) { + m_cmdDrawIndexedIndirectCount = reinterpret_cast( + vkGetDeviceProcAddr(m_device, "vkCmdDrawIndexedIndirectCount")); + } + if (m_drawIndirectCountExtensionEnabled && m_cmdDrawIndexedIndirectCount == nullptr) { + MGLOG_W("VK_KHR_draw_indirect_count enabled but vkCmdDrawIndexedIndirectCount entry point is missing"); + } MGLOG_I("Logical device created."); // Queues @@ -1989,6 +2207,59 @@ namespace MobileGL::MG_Backend::DirectVulkan { return extensions; } + Vector VulkanRenderer::EnumerateDeviceExtensions(VkPhysicalDevice device) { + Uint32 extensionCount = 0; + VK_VERIFY(vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr)); + Vector extensions(extensionCount); + VK_VERIFY(vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, extensions.data())); + return extensions; + } + + Bool VulkanRenderer::IsExtensionSupported(const Vector& availableExtensions, + const char* extensionName) { + for (const auto& extension : availableExtensions) { + if (strcmp(extension.extensionName, extensionName) == 0) { + return true; + } + } + return false; + } + + Bool VulkanRenderer::IsExtensionAlreadyEnabled(const Vector& enabledExtensions, + const char* extensionName) { + for (const char* enabledExtensionName : enabledExtensions) { + if (strcmp(enabledExtensionName, extensionName) == 0) { + return true; + } + } + return false; + } + + Bool VulkanRenderer::EnableOptionalDeviceExtension(const Vector& availableExtensions, + Vector& inOutEnabledExtensions, + const char* extensionName) { + if (!IsExtensionSupported(availableExtensions, extensionName)) { + MGLOG_I("Optional device extension not supported: %s", extensionName); + return false; + } + + if (!IsExtensionAlreadyEnabled(inOutEnabledExtensions, extensionName)) { + inOutEnabledExtensions.push_back(extensionName); + } + MGLOG_I("Enabled optional device extension: %s", extensionName); + return true; + } + + void VulkanRenderer::ResolveOptionalDeviceExtensions(const Vector& availableExtensions, + Vector& inOutEnabledExtensions) { + m_drawIndirectCountExtensionEnabled = EnableOptionalDeviceExtension(availableExtensions, inOutEnabledExtensions, + VK_KHR_DRAW_INDIRECT_COUNT_EXTENSION_NAME); +#ifdef VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME + EnableOptionalDeviceExtension(availableExtensions, inOutEnabledExtensions, + VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME); +#endif + } + Bool VulkanRenderer::CheckValidationLayerSupport() { Uint32 layerCount = 0; VK_VERIFY(vkEnumerateInstanceLayerProperties(&layerCount, nullptr)); @@ -2072,4 +2343,8 @@ namespace MobileGL::MG_Backend::DirectVulkan { const PhysicalDevice& VulkanRenderer::GetPhysicalDevice() const { return m_physicalDevice; } + + Bool VulkanRenderer::IsDrawIndirectCountExtensionEnabled() const { + return m_drawIndirectCountExtensionEnabled; + } } // namespace MobileGL::MG_Backend::DirectVulkan diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h index d970ced0..d6724b84 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h @@ -72,6 +72,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { void EnsureFrameRecordingStarted(); void DrawArrays(const DrawArrayPayload& payload); void DrawElements(const DrawElementPayload& payload); + void MultiDrawElements(const Vector& payloads); Bool BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter, Uint readFboExternalIndex, Uint drawFboExternalIndex, Bool readIsDefaultFramebuffer, Bool drawIsDefaultFramebuffer); @@ -79,6 +80,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { void Present(); const PhysicalDevice& GetPhysicalDevice() const; + Bool IsDrawIndirectCountExtensionEnabled() const; void RecreateSwapchain(); @@ -112,6 +114,12 @@ namespace MobileGL::MG_Backend::DirectVulkan { VkQueue m_graphicsQueue = VK_NULL_HANDLE; VkQueue m_presentQueue = VK_NULL_HANDLE; + Bool m_drawIndirectCountExtensionEnabled = false; + using PFNDrawIndexedIndirectCountFunc = void(VKAPI_PTR*)(VkCommandBuffer commandBuffer, VkBuffer buffer, + VkDeviceSize offset, VkBuffer countBuffer, + VkDeviceSize countBufferOffset, Uint32 maxDrawCount, + Uint32 stride); + PFNDrawIndexedIndirectCountFunc m_cmdDrawIndexedIndirectCount = nullptr; VkCommandPool m_commandPool = VK_NULL_HANDLE; @@ -189,6 +197,15 @@ namespace MobileGL::MG_Backend::DirectVulkan { static Vector GetQueueFamilyFromPhysicalDevice(VkPhysicalDevice device); static Int GetQueueFamilyIndex(const Vector& queueFamilies, VkQueueFlagBits flag); static Vector EnumerateInstanceExtensions(); + static Vector EnumerateDeviceExtensions(VkPhysicalDevice device); + static Bool IsExtensionSupported(const Vector& availableExtensions, + const char* extensionName); + static Bool IsExtensionAlreadyEnabled(const Vector& enabledExtensions, const char* extensionName); + static Bool EnableOptionalDeviceExtension(const Vector& availableExtensions, + Vector& inOutEnabledExtensions, + const char* extensionName); + void ResolveOptionalDeviceExtensions(const Vector& availableExtensions, + Vector& inOutEnabledExtensions); static Bool IsNecessaryDeviceExtensionSupported(VkPhysicalDevice device); static Bool GetMoreCapablePhysicalDevice(VkPhysicalDevice newVkDevice, VkSurfaceKHR surface, const PhysicalDevice& compareWithDevice,