mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-07 19:58:32 +09:00
[Feat] (MG_Backend/DirectVulkan): Add optional device extension. Impl MultiDrawElements.
This commit is contained in:
@@ -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<DrawElementPayload> payloads;
|
||||
payloads.reserve(static_cast<SizeT>(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<SizeT>(indices[i]);
|
||||
const SizeT requiredBytes = static_cast<SizeT>(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");
|
||||
|
||||
@@ -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 <vulkan/vulkan_core.h>
|
||||
|
||||
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<Uint32>(payload.drawArray.count), 1, 0, 0, 0);
|
||||
}
|
||||
|
||||
void VulkanRenderer::MultiDrawElements(const Vector<DrawElementPayload>& 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<MG_State::GLState::VertexArrayObject*>(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<PreparedDraw> 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<SizeT>(payload.drawArray.count) * indexSize;
|
||||
MOBILEGL_ASSERT(payload.indexByteOffset + drawByteCount <= indexBuffer->GetSize(),
|
||||
"MultiDrawElements index range out of bounds");
|
||||
|
||||
PreparedDraw draw{};
|
||||
draw.indexCount = static_cast<Uint32>(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<VkDeviceSize>(indexSize);
|
||||
const VkDeviceSize writeOffset = (frameIndexHead + alignment - 1) & ~(alignment - 1);
|
||||
const VkDeviceSize writeEnd = writeOffset + static_cast<VkDeviceSize>(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<VkDeviceSize>(draw.sourceByteCount), writeCursor)) {
|
||||
MGLOG_E("MultiDrawElements skipped: failed to upload index data");
|
||||
return;
|
||||
}
|
||||
writeCursor += static_cast<VkDeviceSize>(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<float>(activeExtent.width);
|
||||
viewport.height = static_cast<float>(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<VkDrawIndexedIndirectCommand> 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<VkDeviceSize>(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<Uint8> indirectBlob(static_cast<SizeT>(totalBytes), 0);
|
||||
memcpy(indirectBlob.data(), indirectCommands.data(), static_cast<SizeT>(commandBytes));
|
||||
const auto indirectDrawCount = static_cast<Uint32>(indirectCommands.size());
|
||||
memcpy(indirectBlob.data() + static_cast<SizeT>(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<Uint32>(indirectCommands.size()),
|
||||
static_cast<Uint32>(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<VkExtensionProperties> availableExtensions = EnumerateDeviceExtensions(device);
|
||||
|
||||
std::vector<VkExtensionProperties> 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<Uint32>(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<const char*> 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<VkExtensionProperties> 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<VkExtensionProperties> 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<Uint32>(enabledDeviceExtensions.size());
|
||||
deviceCreateInfo.ppEnabledExtensionNames = enabledDeviceExtensions.data();
|
||||
VK_VERIFY(vkCreateDevice(m_physicalDevice.handle, &deviceCreateInfo, nullptr, &m_device), "vkCreateDevice");
|
||||
|
||||
m_cmdDrawIndexedIndirectCount = reinterpret_cast<PFNDrawIndexedIndirectCountFunc>(
|
||||
vkGetDeviceProcAddr(m_device, "vkCmdDrawIndexedIndirectCountKHR"));
|
||||
if (m_cmdDrawIndexedIndirectCount == nullptr) {
|
||||
m_cmdDrawIndexedIndirectCount = reinterpret_cast<PFNDrawIndexedIndirectCountFunc>(
|
||||
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<VkExtensionProperties> VulkanRenderer::EnumerateDeviceExtensions(VkPhysicalDevice device) {
|
||||
Uint32 extensionCount = 0;
|
||||
VK_VERIFY(vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr));
|
||||
Vector<VkExtensionProperties> extensions(extensionCount);
|
||||
VK_VERIFY(vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, extensions.data()));
|
||||
return extensions;
|
||||
}
|
||||
|
||||
Bool VulkanRenderer::IsExtensionSupported(const Vector<VkExtensionProperties>& availableExtensions,
|
||||
const char* extensionName) {
|
||||
for (const auto& extension : availableExtensions) {
|
||||
if (strcmp(extension.extensionName, extensionName) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Bool VulkanRenderer::IsExtensionAlreadyEnabled(const Vector<const char*>& enabledExtensions,
|
||||
const char* extensionName) {
|
||||
for (const char* enabledExtensionName : enabledExtensions) {
|
||||
if (strcmp(enabledExtensionName, extensionName) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Bool VulkanRenderer::EnableOptionalDeviceExtension(const Vector<VkExtensionProperties>& availableExtensions,
|
||||
Vector<const char*>& 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<VkExtensionProperties>& availableExtensions,
|
||||
Vector<const char*>& 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
|
||||
|
||||
@@ -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<DrawElementPayload>& 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<VkQueueFamilyProperties> GetQueueFamilyFromPhysicalDevice(VkPhysicalDevice device);
|
||||
static Int GetQueueFamilyIndex(const Vector<VkQueueFamilyProperties>& queueFamilies, VkQueueFlagBits flag);
|
||||
static Vector<VkExtensionProperties> EnumerateInstanceExtensions();
|
||||
static Vector<VkExtensionProperties> EnumerateDeviceExtensions(VkPhysicalDevice device);
|
||||
static Bool IsExtensionSupported(const Vector<VkExtensionProperties>& availableExtensions,
|
||||
const char* extensionName);
|
||||
static Bool IsExtensionAlreadyEnabled(const Vector<const char*>& enabledExtensions, const char* extensionName);
|
||||
static Bool EnableOptionalDeviceExtension(const Vector<VkExtensionProperties>& availableExtensions,
|
||||
Vector<const char*>& inOutEnabledExtensions,
|
||||
const char* extensionName);
|
||||
void ResolveOptionalDeviceExtensions(const Vector<VkExtensionProperties>& availableExtensions,
|
||||
Vector<const char*>& inOutEnabledExtensions);
|
||||
static Bool IsNecessaryDeviceExtensionSupported(VkPhysicalDevice device);
|
||||
static Bool GetMoreCapablePhysicalDevice(VkPhysicalDevice newVkDevice, VkSurfaceKHR surface,
|
||||
const PhysicalDevice& compareWithDevice,
|
||||
|
||||
Reference in New Issue
Block a user