diff --git a/MobileGL/ConfigLoader.cpp b/MobileGL/ConfigLoader.cpp index 44f2b659..5cecdff5 100644 --- a/MobileGL/ConfigLoader.cpp +++ b/MobileGL/ConfigLoader.cpp @@ -58,7 +58,7 @@ namespace MobileGL::MG_ConfigLoader { inline void InitBackendType() { String backendTypeStr; - QueryEnvVariable("MOBILEGL_BACKEND_TYPE", backendTypeStr, "DirectGLES"); + QueryEnvVariable("MOBILEGL_BACKEND_TYPE", backendTypeStr, "DirectVulkan"); #define ENTRY(backendType) \ if (backendTypeStr == #backendType) { \ MG_Config::ActiveBackendType = BackendType::backendType; \ diff --git a/MobileGL/MG_Backend/DirectVulkan/DirectVulkan.cpp b/MobileGL/MG_Backend/DirectVulkan/DirectVulkan.cpp index e669faac..ea5bd755 100644 --- a/MobileGL/MG_Backend/DirectVulkan/DirectVulkan.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/DirectVulkan.cpp @@ -17,165 +17,11 @@ namespace MobileGL::MG_Backend::DirectVulkan { void ClearBufferfv(GLenum buffer, GLint drawbuffer, const GLfloat* value) {} 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) { - MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::DrawElementsBaseVertex called with null VulkanRenderer"); - MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::DrawElementsBaseVertex called with null GL context"); - - if (count < 0) { - MGLOG_W("DrawElementsBaseVertex skipped: count (%d) must be non-negative", count); - return; - } - if (count == 0) { - return; - } - if (mode != GL_TRIANGLES) { - MGLOG_W("DrawElementsBaseVertex 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("DrawElementsBaseVertex skipped: index type %u is not supported yet", type); - return; - } - - const auto vao = MG_State::pGLContext->GetBoundVertexArray(); - if (!vao) { - MGLOG_W("DrawElementsBaseVertex skipped: no bound VAO"); - return; - } - - const auto indexBuffer = vao->GetIndexBufferBindingSlot().GetBoundObject(); - if (!indexBuffer) { - MGLOG_W("DrawElementsBaseVertex skipped: no bound ELEMENT_ARRAY_BUFFER"); - return; - } - - const auto indexData = indexBuffer->GetDataReadOnly(); - if (!indexData || indexData->empty()) { - MGLOG_W("DrawElementsBaseVertex skipped: ELEMENT_ARRAY_BUFFER has no data"); - return; - } - - const SizeT byteOffset = reinterpret_cast(indices); - const SizeT requiredBytes = static_cast(count) * indexSize; - if (byteOffset + requiredBytes > indexBuffer->GetSize()) { - MGLOG_W("DrawElementsBaseVertex skipped: index range out of bounds (offset=%zu, size=%zu, buffer=%zu)", - byteOffset, requiredBytes, indexBuffer->GetSize()); - return; - } - - DrawElementPayload payload{}; - payload.drawArray.mode = mode; - payload.drawArray.first = 0; - payload.drawArray.count = count; - const auto currentProgram = MG_State::pGLContext->GetCurrentProgram(); - payload.drawArray.program = currentProgram ? currentProgram.get() : nullptr; - payload.drawArray.vertexArray = vao.get(); - payload.indexType = type; - payload.indexByteOffset = byteOffset; - payload.baseVertex = basevertex; - - pVulkanRenderer->DrawElements(payload); - } + void DrawElementsBaseVertex(GLenum mode, GLsizei count, GLenum type, const GLvoid* indices, GLint basevertex) {} void MultiDrawElementsBaseVertex(GLenum mode, const GLsizei* count, GLenum type, const GLvoid* const* indices, - GLsizei drawcount, const GLint* basevertex) { - MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::MultiDrawElementsBaseVertex called with null VulkanRenderer"); - MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::MultiDrawElementsBaseVertex called with null GL context"); + GLsizei drawcount, const GLint* basevertex) {} - if (drawcount < 0) { - MGLOG_W("MultiDrawElementsBaseVertex skipped: drawcount (%d) must be non-negative", drawcount); - return; - } - if (drawcount == 0) { - return; - } - if (!count || !indices || !basevertex) { - MGLOG_W("MultiDrawElementsBaseVertex skipped: count/indices/basevertex pointer is null"); - return; - } - if (mode != GL_TRIANGLES) { - MGLOG_W("MultiDrawElementsBaseVertex 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("MultiDrawElementsBaseVertex skipped: index type %u is not supported yet", type); - return; - } - - const auto vao = MG_State::pGLContext->GetBoundVertexArray(); - if (!vao) { - MGLOG_W("MultiDrawElementsBaseVertex skipped: no bound VAO"); - return; - } - - const auto indexBuffer = vao->GetIndexBufferBindingSlot().GetBoundObject(); - if (!indexBuffer) { - MGLOG_W("MultiDrawElementsBaseVertex skipped: no bound ELEMENT_ARRAY_BUFFER"); - return; - } - - const auto indexData = indexBuffer->GetDataReadOnly(); - if (!indexData || indexData->empty()) { - MGLOG_W("MultiDrawElementsBaseVertex 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("MultiDrawElementsBaseVertex skipped: count[%d] (%d) must be non-negative", i, count[i]); - return; - } - if (count[i] == 0) { - continue; - } - - const SizeT byteOffset = reinterpret_cast(indices[i]); - const SizeT requiredBytes = static_cast(count[i]) * indexSize; - if (byteOffset + requiredBytes > indexBuffer->GetSize()) { - MGLOG_W("MultiDrawElementsBaseVertex 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; - payload.baseVertex = basevertex[i]; - payloads.push_back(payload); - } - - if (payloads.empty()) { - return; - } - pVulkanRenderer->MultiDrawElements(payloads); - } void MultiDrawElementsIndirect(GLenum mode, GLenum type, const void* indirect, GLsizei drawcount, GLsizei stride) {} void MultiDrawArraysIndirect(GLenum mode, const void* indirect, GLsizei drawcount, GLsizei stride) {} void DrawRangeElementsBaseVertex(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, @@ -206,181 +52,44 @@ namespace MobileGL::MG_Backend::DirectVulkan { 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)); + Vector cmds; + cmds.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(); + DrawElementCmd payload{}; + payload.mode = mode; + payload.first = 0; + payload.count = count[i]; payload.indexType = type; - payload.indexByteOffset = byteOffset; - payloads.push_back(payload); + payload.indexByteOffset = reinterpret_cast(indices[i]); + cmds.push_back(payload); } - if (payloads.empty()) { + if (cmds.empty()) { return; } - pVulkanRenderer->MultiDrawElements(payloads); + pVulkanRenderer->MultiDrawElements(cmds); } 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"); - - const auto& drawFboSlot = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw); - const auto drawFbo = drawFboSlot.GetBoundObject(); - const auto defaultFboInfo = MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo; - const auto defaultFbo = defaultFboInfo ? defaultFboInfo->defaultFBO : nullptr; - const Bool isDefaultFboTarget = (drawFbo == defaultFbo) || (drawFbo == nullptr && defaultFbo != nullptr); - Uint drawFboExternalIndex = 0; - if (drawFbo) { - drawFboExternalIndex = drawFbo->GetExternalIndex(); - } else if (defaultFbo) { - drawFboExternalIndex = defaultFbo->GetExternalIndex(); - } - - const auto& clearColor = MG_State::pGLContext->GetClearColor(); - const auto clearDepth = MG_State::pGLContext->GetClearDepth(); - const auto clearStencil = static_cast(MG_State::pGLContext->GetClearStencil()); - pVulkanRenderer->QueueClearRequest(mask, clearColor, clearDepth, clearStencil, - drawFboExternalIndex, - isDefaultFboTarget); + pVulkanRenderer->Clear(mask); } void DrawElements(GLenum mode, GLsizei count, GLenum type, const void* indices) { MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::DrawElements called with null VulkanRenderer"); MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::DrawElements called with null GL context"); - if (count < 0) { - MGLOG_W("DrawElements skipped: count (%d) must be non-negative", count); - return; - } - - if (count == 0) { - return; - } - - if (mode != GL_TRIANGLES) { - MGLOG_W("DrawElements 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("DrawElements skipped: index type %u is not supported yet", type); - return; - } - - const auto vao = MG_State::pGLContext->GetBoundVertexArray(); - if (!vao) { - MGLOG_W("DrawElements skipped: no bound VAO"); - return; - } - - const auto indexBuffer = vao->GetIndexBufferBindingSlot().GetBoundObject(); - if (!indexBuffer) { - MGLOG_W("DrawElements skipped: no bound ELEMENT_ARRAY_BUFFER"); - return; - } - - const auto indexData = indexBuffer->GetDataReadOnly(); - if (!indexData || indexData->empty()) { - MGLOG_W("DrawElements skipped: ELEMENT_ARRAY_BUFFER has no data"); - return; - } - - const auto byteOffset = reinterpret_cast(indices); - const SizeT requiredBytes = static_cast(count) * indexSize; - if (byteOffset + requiredBytes > indexBuffer->GetSize()) { - MGLOG_W("DrawElements skipped: index range out of bounds (offset=%zu, size=%zu, buffer=%zu)", byteOffset, - requiredBytes, indexBuffer->GetSize()); - return; - } - - DrawElementPayload payload{}; - payload.drawArray.mode = mode; - payload.drawArray.first = 0; - payload.drawArray.count = count; - const auto currentProgram = MG_State::pGLContext->GetCurrentProgram(); - payload.drawArray.program = currentProgram ? currentProgram.get() : nullptr; - payload.drawArray.vertexArray = vao.get(); + DrawElementCmd payload{}; + payload.mode = mode; + payload.first = 0; + payload.count = count; payload.indexType = type; - payload.indexByteOffset = byteOffset; + payload.indexByteOffset = reinterpret_cast(indices); pVulkanRenderer->DrawElements(payload); } @@ -389,34 +98,10 @@ namespace MobileGL::MG_Backend::DirectVulkan { MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::DrawArrays called with null VulkanRenderer"); MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::DrawArrays called with null GL context"); - if (first < 0) { - MGLOG_W("DrawArrays skipped: first (%d) must be non-negative", first); - return; - } - - if (count < 0) { - MGLOG_W("DrawArrays skipped: count (%d) must be non-negative", count); - return; - } - - if (count == 0) { - return; - } - - if (mode != GL_TRIANGLES) { - MGLOG_W("DrawArrays skipped: primitive mode %u is not supported yet", mode); - return; - } - - DrawArrayPayload payload{}; + DrawArrayCmd payload{}; payload.mode = mode; payload.first = first; payload.count = count; - const auto currentProgram = MG_State::pGLContext->GetCurrentProgram(); - payload.program = currentProgram ? currentProgram.get() : nullptr; - - const auto vao = MG_State::pGLContext->GetBoundVertexArray(); - payload.vertexArray = vao ? vao.get() : nullptr; pVulkanRenderer->DrawArrays(payload); } diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformDescriptorBinder.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformDescriptorBinder.cpp index d0fd6aa8..609c0777 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformDescriptorBinder.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformDescriptorBinder.cpp @@ -475,11 +475,15 @@ namespace MobileGL::MG_Backend::DirectVulkan { if (!samplerToUse) { return false; } - - if (!m_textureManager->SyncTextureAndGetDescriptor(*texture, outImageInfo)) { + VkTextureManager::TextureResource resource; + if (!m_textureManager->SyncTextureAndGetDescriptor(*texture, resource)) { return false; } - outImageInfo.sampler = m_samplerManager->GetOrCreateSampler(*samplerToUse); + outImageInfo = { + .sampler = m_samplerManager->GetOrCreateSampler(*samplerToUse), + .imageView = resource.view, + .imageLayout = resource.layout, + }; return outImageInfo.sampler != VK_NULL_HANDLE; } @@ -678,27 +682,10 @@ namespace MobileGL::MG_Backend::DirectVulkan { return true; } - Bool UniformDescriptorBinder::BindProgramUniformBuffers(VkCommandBuffer commandBuffer, VkPipelineLayout pipelineLayout, + Bool UniformDescriptorBinder::BindProgramUniformBuffers(VkCommandBuffer commandBuffer, const MG_State::GLState::ProgramObject& program, Uint32 frameIndex) { - if (m_frames.empty()) { - MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: binder is not initialized"); - return false; - } - if (frameIndex >= m_frames.size()) { - MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: invalid frame index %u", frameIndex); - return false; - } - ProgramLayout* layout = GetOrCreateProgramLayout(program); - if (!layout || layout->pipelineLayout == VK_NULL_HANDLE || layout->descriptorSetLayout == VK_NULL_HANDLE) { - MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: cannot get program layout"); - return false; - } - if (layout->pipelineLayout != pipelineLayout) { - MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: pipelineLayout mismatch"); - return false; - } auto& frame = m_frames[frameIndex]; if (frame.descriptorPools.empty()) { @@ -837,7 +824,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { vkUpdateDescriptorSets(m_device, static_cast(writes.size()), writes.data(), 0, nullptr); } - vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, + vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, layout->pipelineLayout, 0, 1, &descriptorSet, static_cast(dynamicOffsets.size()), dynamicOffsets.data()); return true; } diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformDescriptorBinder.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformDescriptorBinder.h index 49eb7e1f..2fbf297c 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformDescriptorBinder.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformDescriptorBinder.h @@ -36,7 +36,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { void BeginFrame(Uint32 frameIndex); VkPipelineLayout GetOrCreatePipelineLayout(const MG_State::GLState::ProgramObject& program); - Bool BindProgramUniformBuffers(VkCommandBuffer commandBuffer, VkPipelineLayout pipelineLayout, + Bool BindProgramUniformBuffers(VkCommandBuffer commandBuffer, const MG_State::GLState::ProgramObject& program, Uint32 frameIndex); private: diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkClearManager.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkClearManager.cpp index 2ff99e66..392ba807 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkClearManager.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkClearManager.cpp @@ -81,6 +81,11 @@ namespace MobileGL::MG_Backend::DirectVulkan { } SizeT VkClearManager::CollectGarbage() { + m_gcCounter++; + if (m_gcCounter != 0) { + return 0; + } + SizeT count = 0; for (const auto& [raw, weak]: m_aliveObjects) { if (weak.expired()) { diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkClearManager.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkClearManager.h index 9bfcf2ff..a46506c8 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkClearManager.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkClearManager.h @@ -45,6 +45,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { void PopPendingClear(MG_State::GLState::ITextureObject* texture); SizeT CollectGarbage(); private: + Uint8 m_gcCounter = 0; UnorderedMap m_pendingClears; UnorderedMap> m_aliveObjects; }; diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkRenderPassManager.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkRenderPassManager.cpp index c15fce75..122d32cf 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkRenderPassManager.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkRenderPassManager.cpp @@ -94,9 +94,9 @@ namespace MobileGL::MG_Backend::DirectVulkan { Int width = 0; Int height = 0; - Vector colorAttachmentDescriptions(validDrawBufCount); + Vector attachmentDescriptions(validDrawBufCount); Vector colorAttachmentRefs(validDrawBufCount); - Vector descriptorImageInfo(validDrawBufCount); + Vector textureResources(validDrawBufCount); // This should automatically work on default & offscreen FBO // assuming default FBO has the right param for (Int i = 0; i < validDrawBufCount; ++i) { @@ -110,7 +110,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { const auto textureTarget = texture->GetTarget(); // Color attachment description - VkAttachmentDescription& desc = colorAttachmentDescriptions[i]; + VkAttachmentDescription& desc = attachmentDescriptions[i]; switch (textureTarget) { case TextureTarget::Texture2D: { auto* texture2d = @@ -135,8 +135,8 @@ namespace MobileGL::MG_Backend::DirectVulkan { if (height == 0) height = texture2d->GetBaseSize().y(); - Bool ok = m_textureManager.SyncTextureAndGetDescriptor(*texture, descriptorImageInfo[i]); - MOBILEGL_ASSERT(ok, "GetOrCreateRenderPass: SyncTextureAndGetDescriptor failed"); + Bool ok = m_textureManager.SyncTextureAndGetDescriptor(*texture, textureResources[i]); + MOBILEGL_ASSERT(ok, "GetOrCreateRenderPass: SyncTextureAndGetDescriptor failed at color attachment %d", i); break; } @@ -153,9 +153,11 @@ namespace MobileGL::MG_Backend::DirectVulkan { // Depth attachment description auto& depthAtt = fbo.GetAttachment(FramebufferAttachmentType::Depth); VkAttachmentDescription depthAttachmentDescription; - if (depthAtt.IsComplete()) { + VkTextureManager::TextureResource depthTextureResource; + if (depthAtt.IsComplete() && depthAtt.IsTexture()) { + auto& texture = *depthAtt.GetTexture(); depthAttachmentDescription.format = - MG_Util::ConvertTextureInternalFormatToVkEnum(depthAtt.GetTexture()->GetFormat()); + MG_Util::ConvertTextureInternalFormatToVkEnum(texture.GetFormat()); depthAttachmentDescription.samples = VK_SAMPLE_COUNT_1_BIT; depthAttachmentDescription.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; depthAttachmentDescription.storeOp = VK_ATTACHMENT_STORE_OP_STORE; @@ -165,6 +167,9 @@ namespace MobileGL::MG_Backend::DirectVulkan { depthAttachmentDescription.finalLayout = isDefaultFbo ? VK_IMAGE_LAYOUT_PRESENT_SRC_KHR : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + Bool ok = m_textureManager.SyncTextureAndGetDescriptor(texture, depthTextureResource); + MOBILEGL_ASSERT(ok, "GetOrCreateRenderPass: SyncTextureAndGetDescriptor failed at depth attachment"); + attachmentDescriptions.emplace_back(depthAttachmentDescription); } // Depth attachment ref @@ -190,8 +195,8 @@ namespace MobileGL::MG_Backend::DirectVulkan { renderPassCreateInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; renderPassCreateInfo.pNext = VK_NULL_HANDLE; renderPassCreateInfo.flags = 0; - renderPassCreateInfo.attachmentCount = colorAttachmentDescriptions.size(); - renderPassCreateInfo.pAttachments = colorAttachmentDescriptions.data(); + renderPassCreateInfo.attachmentCount = attachmentDescriptions.size(); + renderPassCreateInfo.pAttachments = attachmentDescriptions.data(); renderPassCreateInfo.subpassCount = 1; renderPassCreateInfo.pSubpasses = &subpassDesc; renderPassCreateInfo.dependencyCount = 0; @@ -200,9 +205,9 @@ namespace MobileGL::MG_Backend::DirectVulkan { VkRenderPass renderPass = VK_NULL_HANDLE; VK_VERIFY(vkCreateRenderPass(m_device, &renderPassCreateInfo, nullptr, &renderPass)); - Vector attachmentViews(descriptorImageInfo.size(), VK_NULL_HANDLE); - for (Int i = 0; i < descriptorImageInfo.size(); i++) { - attachmentViews[i] = descriptorImageInfo[i].imageView; + Vector attachmentViews(textureResources.size(), VK_NULL_HANDLE); + for (Int i = 0; i < textureResources.size(); i++) { + attachmentViews[i] = textureResources[i].view; } // Framebuffer @@ -219,23 +224,22 @@ namespace MobileGL::MG_Backend::DirectVulkan { VkFramebuffer framebuffer = VK_NULL_HANDLE; VK_VERIFY(vkCreateFramebuffer(m_device, &framebufferCreateInfo, nullptr, &framebuffer)); IntVec2 extent = {width, height}; - m_renderPasses[hash] = { renderPass, framebuffer, Move(descriptorImageInfo), extent }; + m_renderPasses[hash] = { renderPass, framebuffer, Move(textureResources), extent, 1 }; return m_renderPasses[hash]; } - Bool VkRenderPassManager::StartRenderPass(VkCommandBuffer commandBuffer, RenderPassEntry& renderPassEntry) { - if (m_activeRenderPass != nullptr) - return false; + Bool VkRenderPassManager::BeginRenderPass(VkCommandBuffer commandBuffer, RenderPassEntry& renderPassEntry) { + // TODO: Transition all the attachments into proper layout before starting the render pass + - m_activeRenderPass = &renderPassEntry; VkRenderPassBeginInfo renderPassBeginInfo; renderPassBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; renderPassBeginInfo.pNext = nullptr; - renderPassBeginInfo.renderPass = m_activeRenderPass->renderPass; - renderPassBeginInfo.framebuffer = m_activeRenderPass->framebuffer; + renderPassBeginInfo.renderPass = renderPassEntry.renderPass; + renderPassBeginInfo.framebuffer = renderPassEntry.framebuffer; renderPassBeginInfo.renderArea.offset = { 0, 0 }; renderPassBeginInfo.renderArea.extent = { - (Uint32)m_activeRenderPass->extent.x(), (Uint32)m_activeRenderPass->extent.y() }; + (Uint32)renderPassEntry.extent.x(), (Uint32)renderPassEntry.extent.y() }; // TODO: should query proper clear color VkClearValue clearValue; @@ -251,8 +255,6 @@ namespace MobileGL::MG_Backend::DirectVulkan { } Bool VkRenderPassManager::EndRenderPass(VkCommandBuffer commandBuffer) { - if (m_activeRenderPass == nullptr) - return false; vkCmdEndRenderPass(commandBuffer); return true; } diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkRenderPassManager.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkRenderPassManager.h index f1fceb23..2302e524 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkRenderPassManager.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkRenderPassManager.h @@ -21,8 +21,9 @@ namespace MobileGL::MG_Backend::DirectVulkan { static inline VkDevice s_device; VkRenderPass renderPass = VK_NULL_HANDLE; VkFramebuffer framebuffer = VK_NULL_HANDLE; - Vector descriptorImageInfo; + Vector textureResources; IntVec2 extent = {0, 0}; + Uint32 subpass = 0; ~RenderPassEntry() { if (renderPass != VK_NULL_HANDLE) { @@ -46,16 +47,14 @@ namespace MobileGL::MG_Backend::DirectVulkan { HashType ComputeHash(const MG_State::GLState::FramebufferObject& fbo) const; RenderPassEntry& GetOrCreateRenderPass(const MG_State::GLState::FramebufferObject& fbo); - RenderPassEntry* GetActiveRenderPass() const { return m_activeRenderPass; } - Bool StartRenderPass(VkCommandBuffer commandBuffer, RenderPassEntry& renderPassEntry); - Bool EndRenderPass(VkCommandBuffer commandBuffer); + static Bool BeginRenderPass(VkCommandBuffer commandBuffer, RenderPassEntry& renderPassEntry); + static Bool EndRenderPass(VkCommandBuffer commandBuffer); private: VkDevice m_device = VK_NULL_HANDLE; const VulkanRendererConfig& m_config; VkClearManager& m_clearManager; VkTextureManager& m_textureManager; UnorderedMap m_renderPasses; - RenderPassEntry* m_activeRenderPass = nullptr; static inline XXH64_state_t* m_hashState = XXH64_createState(); }; } // namespace MobileGL::MG_Backend::DirectVulkan diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.cpp index c008ebfb..f74147e7 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.cpp @@ -41,7 +41,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { } Bool VkTextureManager::SyncTextureAndGetDescriptor(MG_State::GLState::ITextureObject& texture, - VkDescriptorImageInfo& outImageInfo) { + TextureResource& outTextureResource) { MOBILEGL_ASSERT(m_device != VK_NULL_HANDLE, "SyncTextureAndGetDescriptor: m_device == VK_NULL_HANDLE"); auto it = m_textureResources.find(texture.GetExternalIndex()); @@ -56,15 +56,8 @@ namespace MobileGL::MG_Backend::DirectVulkan { return false; } - if (it->second.view == VK_NULL_HANDLE) { - return false; - } + outTextureResource = it->second; - outImageInfo.sampler = VK_NULL_HANDLE; - outImageInfo.imageView = it->second.view; - // Will be VK_IMAGE_LAYOUT_UNDEFINED or VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL - // Shouldn't hardcode VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL here - // outImageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; return true; } Bool VkTextureManager::TransitionImageLayout(VkCommandBuffer commandBuffer, VkImage image, diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.h index 98298f17..640920b8 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.h @@ -28,17 +28,6 @@ public: VkQueue graphicsQueue = VK_NULL_HANDLE; }; - Bool Initialize(const InitInfo& initInfo); - void Shutdown(); - - Bool SyncTextureAndGetDescriptor(MG_State::GLState::ITextureObject& texture, VkDescriptorImageInfo& outImageInfo); - - - static Bool TransitionImageLayout(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout& trackedLayout, - VkImageLayout newLayout, VkPipelineStageFlags srcStageMask, - VkPipelineStageFlags dstStageMask, VkAccessFlags srcAccessMask, - VkAccessFlags dstAccessMask, VkImageAspectFlags aspectMask); -private: struct TextureResource { VkImage image = VK_NULL_HANDLE; VmaAllocation allocation = nullptr; @@ -50,6 +39,19 @@ private: Uint textureExternalIndex = 0; }; + Bool Initialize(const InitInfo& initInfo); + void Shutdown(); + + Bool SyncTextureAndGetDescriptor( + MG_State::GLState::ITextureObject& texture, TextureResource& outTextureResource); + + + static Bool TransitionImageLayout(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout& trackedLayout, + VkImageLayout newLayout, VkPipelineStageFlags srcStageMask, + VkPipelineStageFlags dstStageMask, VkAccessFlags srcAccessMask, + VkAccessFlags dstAccessMask, VkImageAspectFlags aspectMask); +private: + Bool SyncTexture(MG_State::GLState::ITextureObject &texture, TextureResource &outResource); Bool SyncTextureResource(const MG_State::GLState::ITextureObject &texture, diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp index 7ad6173b..e97708f5 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp @@ -85,109 +85,6 @@ namespace MobileGL::MG_Backend::DirectVulkan { return flags; } - VkPipeline VulkanRenderer::GetOrCreatePipeline(const MG_State::GLState::ProgramObject& program, - VkPipelineLayout pipelineLayout, Uint64 vertexInputHash, - const VkPipelineVertexInputStateCreateInfo& vertexInputState) { - MOBILEGL_ASSERT(m_pipelineFactory != nullptr, "PipelineFactory is not initialized"); - MOBILEGL_ASSERT(m_programFactory != nullptr, "ProgramFactory is not initialized"); - ProgramFactory::CompileOptionFlags transformFlags = GetShaderTransformFlags(m_swapchainObject.GetPreTransform()); - auto& stages = m_programFactory->GetOrCreatePipelineShaderStages(program, transformFlags); - if (stages.empty()) { - MGLOG_D("GetOrCreatePipeline skipped: program has no shader stages"); - return VK_NULL_HANDLE; - } - const Uint64 programHash = m_programFactory->ComputeHash(program, transformFlags); - auto toVkCompareOp = [](DepthTestFunc func) -> VkCompareOp { - switch (func) { - case DepthTestFunc::Never: - return VK_COMPARE_OP_NEVER; - case DepthTestFunc::Less: - return VK_COMPARE_OP_LESS; - case DepthTestFunc::Equal: - return VK_COMPARE_OP_EQUAL; - case DepthTestFunc::LessEqual: - return VK_COMPARE_OP_LESS_OR_EQUAL; - case DepthTestFunc::Greater: - return VK_COMPARE_OP_GREATER; - case DepthTestFunc::NotEqual: - return VK_COMPARE_OP_NOT_EQUAL; - case DepthTestFunc::GreaterEqual: - return VK_COMPARE_OP_GREATER_OR_EQUAL; - case DepthTestFunc::Always: - default: - return VK_COMPARE_OP_ALWAYS; - } - }; - auto toVkBlendFactor = [](BlendFactor factor) -> VkBlendFactor { - switch (factor) { - case BlendFactor::Zero: - return VK_BLEND_FACTOR_ZERO; - case BlendFactor::One: - return VK_BLEND_FACTOR_ONE; - case BlendFactor::SrcColor: - return VK_BLEND_FACTOR_SRC_COLOR; - case BlendFactor::OneMinusSrcColor: - return VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR; - case BlendFactor::DstColor: - return VK_BLEND_FACTOR_DST_COLOR; - case BlendFactor::OneMinusDstColor: - return VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR; - case BlendFactor::SrcAlpha: - return VK_BLEND_FACTOR_SRC_ALPHA; - case BlendFactor::OneMinusSrcAlpha: - return VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; - case BlendFactor::DstAlpha: - return VK_BLEND_FACTOR_DST_ALPHA; - case BlendFactor::OneMinusDstAlpha: - return VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA; - case BlendFactor::ConstantColor: - return VK_BLEND_FACTOR_CONSTANT_COLOR; - case BlendFactor::OneMinusConstantColor: - return VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR; - case BlendFactor::ConstantAlpha: - return VK_BLEND_FACTOR_CONSTANT_ALPHA; - case BlendFactor::OneMinusConstantAlpha: - return VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA; - default: - return VK_BLEND_FACTOR_ONE; - } - }; - - PipelineFactory::PipelineCreatePayload payload{}; - payload.programHash = programHash; - payload.vertexInputHash = vertexInputHash; - payload.pipelineLayout = pipelineLayout; - payload.renderPass = m_activeRenderPass; - payload.subpass = 0; - payload.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; - const Bool depthTestEnabled = MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::DepthTest); - payload.depthTestEnable = depthTestEnabled; - payload.depthWriteEnable = depthTestEnabled && MG_State::pGLContext->GetDepthMask(); - payload.depthCompareOp = toVkCompareOp(MG_State::pGLContext->GetDepthFunc()); - - payload.blendEnable = MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::Blend); - BlendFactor srcRGB = BlendFactor::One; - BlendFactor dstRGB = BlendFactor::Zero; - BlendFactor srcAlpha = BlendFactor::One; - BlendFactor dstAlpha = BlendFactor::Zero; - MG_State::pGLContext->GetBlendFunc(srcRGB, dstRGB, srcAlpha, dstAlpha); - payload.srcColorBlendFactor = toVkBlendFactor(srcRGB); - payload.dstColorBlendFactor = toVkBlendFactor(dstRGB); - payload.srcAlphaBlendFactor = toVkBlendFactor(srcAlpha); - payload.dstAlphaBlendFactor = toVkBlendFactor(dstAlpha); - - payload.colorWriteMask = 0; - const BoolVec4 colorMask = MG_State::pGLContext->GetColorMask(); - if (colorMask.x()) payload.colorWriteMask |= VK_COLOR_COMPONENT_R_BIT; - if (colorMask.y()) payload.colorWriteMask |= VK_COLOR_COMPONENT_G_BIT; - if (colorMask.z()) payload.colorWriteMask |= VK_COLOR_COMPONENT_B_BIT; - if (colorMask.w()) payload.colorWriteMask |= VK_COLOR_COMPONENT_A_BIT; - - payload.stages = &stages; - payload.vertexInputState = &vertexInputState; - return m_pipelineFactory->GetOrCreatePipeline(payload); - } - void VulkanRenderer::CreateFrameContexts() { VK_VERIFY(m_frameContext.Initialize(m_device, m_commandPool, m_config.MaxFramesInFlight), "CreateFrameContexts"); @@ -326,117 +223,6 @@ namespace MobileGL::MG_Backend::DirectVulkan { MGLOG_I("VulkanRenderer shut down completed"); } - void VulkanRenderer::QueueClearRequest(GLbitfield mask, const FloatVec4& color, Float depth, Uint32 stencil, - Uint drawFboExternalIndex, Bool isDefaultFramebufferTarget) { - const GLbitfield supportedMask = GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT; - const GLbitfield requestMask = (mask & supportedMask); - if (requestMask == 0) { - return; - } - - const Uint64 pendingKey = BuildPendingClearKey(drawFboExternalIndex, isDefaultFramebufferTarget); - auto& pending = m_pendingClears[pendingKey]; - - if ((requestMask & GL_COLOR_BUFFER_BIT) != 0) { - pending.color.float32[0] = color.x(); - pending.color.float32[1] = color.y(); - pending.color.float32[2] = color.z(); - pending.color.float32[3] = color.w(); - } - if ((requestMask & GL_DEPTH_BUFFER_BIT) != 0) { - pending.depth = depth; - } - if ((requestMask & GL_STENCIL_BUFFER_BIT) != 0) { - pending.stencil = stencil; - } - pending.drawFboExternalIndex = drawFboExternalIndex; - pending.targetsDefaultFramebuffer = isDefaultFramebufferTarget; - pending.mask |= requestMask; - - if (m_clearManager == nullptr) { - return; - } - const auto* defaultFboInfo = MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo; - const auto* defaultFbo = defaultFboInfo ? defaultFboInfo->defaultFBO.get() : nullptr; - const MG_State::GLState::FramebufferObject* drawFbo = nullptr; - if (isDefaultFramebufferTarget) { - drawFbo = defaultFbo; - } else { - drawFbo = MG_State::pGLContext->GetFramebufferObject(drawFboExternalIndex).get(); - } - if (drawFbo == nullptr) { - return; - } - m_clearManager->QueueClear( - requestMask, - { - .color = color, - .depth = depth, - .stencil = stencil, - }, - *drawFbo); - } - - Bool VulkanRenderer::ConsumePendingColorClear(VkClearColorValue& outClearColor) { - for (auto it = m_pendingClears.begin(); it != m_pendingClears.end(); ++it) { - if (!it->second.targetsDefaultFramebuffer || (it->second.mask & GL_COLOR_BUFFER_BIT) == 0) { - continue; - } - outClearColor = it->second.color; - it->second.mask &= ~GL_COLOR_BUFFER_BIT; - if (it->second.mask == 0) { - m_pendingClears.erase(it); - } - return true; - } - return false; - } - - void VulkanRenderer::ApplyPendingClearsForActiveTarget(VkCommandBuffer commandBuffer, Uint64 drawTargetKey) { - if (m_activeRenderExtent.width == 0 || m_activeRenderExtent.height == 0) { - return; - } - auto pendingIt = m_pendingClears.find(drawTargetKey); - if (pendingIt == m_pendingClears.end()) { - return; - } - - VkClearRect clearRect{}; - clearRect.rect.offset = {0, 0}; - clearRect.rect.extent = m_activeRenderExtent; - clearRect.baseArrayLayer = 0; - clearRect.layerCount = 1; - - if ((pendingIt->second.mask & GL_COLOR_BUFFER_BIT) != 0) { - VkClearAttachment colorAttachment{}; - colorAttachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - colorAttachment.colorAttachment = 0; - colorAttachment.clearValue.color = pendingIt->second.color; - vkCmdClearAttachments(commandBuffer, 1, &colorAttachment, 1, &clearRect); - pendingIt->second.mask &= ~GL_COLOR_BUFFER_BIT; - } - - if ((pendingIt->second.mask & (GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)) != 0) { - VkClearAttachment depthStencilAttachment{}; - depthStencilAttachment.aspectMask = 0; - if ((pendingIt->second.mask & GL_DEPTH_BUFFER_BIT) != 0) { - depthStencilAttachment.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; - } - if ((pendingIt->second.mask & GL_STENCIL_BUFFER_BIT) != 0) { - depthStencilAttachment.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; - } - depthStencilAttachment.colorAttachment = 0; - depthStencilAttachment.clearValue.depthStencil.depth = pendingIt->second.depth; - depthStencilAttachment.clearValue.depthStencil.stencil = pendingIt->second.stencil; - vkCmdClearAttachments(commandBuffer, 1, &depthStencilAttachment, 1, &clearRect); - pendingIt->second.mask &= ~(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); - } - - if (pendingIt->second.mask == 0) { - m_pendingClears.erase(pendingIt); - } - } - void VulkanRenderer::TransitionSwapchainImageToColorAttachment(VkCommandBuffer commandBuffer, Uint32 imageIndex) { auto layout = m_swapchainObject.GetImageLayout(imageIndex); Bool ok = VkTextureManager::TransitionImageLayout(commandBuffer, m_swapchainObject.GetImage(imageIndex), @@ -470,129 +256,12 @@ namespace MobileGL::MG_Backend::DirectVulkan { m_swapchainObject.SetDepthStencilImageLayout(imageIndex, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); } - void VulkanRenderer::EnsureFrameRecordingStarted() { - auto& frame = m_frameContext.GetCurrent(); - if (frame.hasCommandBufferRecorded) { - MGLOG_D("EnsureFrameRecordingStarted skipped: current frame command buffer is already finalized"); - return; - } - - const auto drawFbo = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject(); - const auto defaultFboInfo = MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo; - const auto defaultFbo = defaultFboInfo ? defaultFboInfo->defaultFBO : nullptr; - const Bool drawTargetsDefault = (drawFbo == defaultFbo) || (drawFbo == nullptr && defaultFbo != nullptr); - const Uint drawFboExternalIndex = - drawFbo ? drawFbo->GetExternalIndex() : (defaultFbo ? defaultFbo->GetExternalIndex() : 0U); - const Uint64 drawTargetKey = BuildPendingClearKey(drawFboExternalIndex, drawTargetsDefault); - - if (frame.isCommandRecording && m_isMainRenderPassActive) { - const Bool activeTargetMismatch = - (drawTargetsDefault != m_activeRenderTargetIsDefault) || - (!drawTargetsDefault && m_activeDrawFboExternalIndex != drawFboExternalIndex); - if (activeTargetMismatch) { - MOBILEGL_ASSERT(m_renderPassManager != nullptr, "EnsureFrameRecordingStarted: manager is null"); - m_renderPassManager->EndRenderPass(frame.commandBuffer); - if (m_activeRenderTargetIsDefault) { - m_swapchainObject.SetImageLayout(m_imageIndexAcquired, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); - } - m_isMainRenderPassActive = false; - m_activeRenderPass = VK_NULL_HANDLE; - m_activeRenderExtent = {0, 0}; - m_activeDepthStencilFormat = VK_FORMAT_UNDEFINED; - m_activeRenderTargetIsDefault = true; - m_activeDrawFboExternalIndex = 0; - } else { - return; - } - } - - VkCommandBuffer* commandBufferPtr = nullptr; - if (frame.isCommandRecording) { - commandBufferPtr = &frame.commandBuffer; - } else { - commandBufferPtr = &m_frameContext.BeginCommandRecording(); - MOBILEGL_ASSERT(m_uniformDescriptorBinder != nullptr, "EnsureFrameRecordingStarted: binder is null"); - m_uniformDescriptorBinder->BeginFrame(m_frameContext.GetCurrentFrameIndex()); - } - VkCommandBuffer& commandBuffer = *commandBufferPtr; - - if (!drawTargetsDefault) { - if (!drawFbo) { - MGLOG_D("EnsureFrameRecordingStarted skipped: offscreen draw target is unavailable"); - return; - } - - // This frame touched only offscreen resources. Present still requires - // the acquired swapchain image to be in PRESENT layout. - auto swapchainOldLayout = m_swapchainObject.GetImageLayout(m_imageIndexAcquired); - if (swapchainOldLayout != VK_IMAGE_LAYOUT_PRESENT_SRC_KHR && - swapchainOldLayout != VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR) { - Bool ok = VkTextureManager::TransitionImageLayout(commandBuffer, m_swapchainObject.GetImage(m_imageIndexAcquired), - swapchainOldLayout, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, - VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, - 0, 0, - VK_IMAGE_ASPECT_COLOR_BIT); - MOBILEGL_ASSERT(ok, "Transition swapchain image to VK_IMAGE_LAYOUT_PRESENT_SRC_KHR failed"); - m_swapchainObject.SetImageLayout(m_imageIndexAcquired, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); - } - - MOBILEGL_ASSERT(m_renderPassManager != nullptr, "EnsureFrameRecordingStarted: manager is null"); - auto& renderPassEntry = m_renderPassManager->GetOrCreateRenderPass(*drawFbo); - Bool started = m_renderPassManager->StartRenderPass(commandBuffer, renderPassEntry); - MOBILEGL_ASSERT(started, "EnsureFrameRecordingStarted: StartRenderPass for offscreen target failed"); - m_isMainRenderPassActive = true; - m_activeRenderPass = renderPassEntry.renderPass; - m_activeRenderExtent = { - static_cast(renderPassEntry.extent.x() > 0 ? renderPassEntry.extent.x() : 0), - static_cast(renderPassEntry.extent.y() > 0 ? renderPassEntry.extent.y() : 0)}; - m_activeDepthStencilFormat = VK_FORMAT_UNDEFINED; - m_activeRenderTargetIsDefault = false; - m_activeDrawFboExternalIndex = drawFboExternalIndex; - ApplyPendingClearsForActiveTarget(commandBuffer, drawTargetKey); - return; - } - - TransitionSwapchainImageToColorAttachment(commandBuffer, m_imageIndexAcquired); - TransitionDepthStencilImageToAttachment(commandBuffer, m_imageIndexAcquired); - - if (!defaultFbo) { - MGLOG_D("EnsureFrameRecordingStarted skipped: default render target unavailable"); - return; - } - MOBILEGL_ASSERT(m_renderPassManager != nullptr, "EnsureFrameRecordingStarted: manager is null"); - auto& renderPassEntry = m_renderPassManager->GetOrCreateRenderPass(*defaultFbo); - Bool started = m_renderPassManager->StartRenderPass(commandBuffer, renderPassEntry); - MOBILEGL_ASSERT(started, "EnsureFrameRecordingStarted: StartRenderPass for default target failed"); - m_isMainRenderPassActive = true; - m_activeRenderPass = renderPassEntry.renderPass; - m_activeRenderExtent = { - static_cast(renderPassEntry.extent.x() > 0 ? renderPassEntry.extent.x() : 0), - static_cast(renderPassEntry.extent.y() > 0 ? renderPassEntry.extent.y() : 0)}; - m_activeDepthStencilFormat = m_swapchainObject.GetDepthStencilFormat(); - m_activeRenderTargetIsDefault = true; - m_activeDrawFboExternalIndex = drawFboExternalIndex; - ApplyPendingClearsForActiveTarget(commandBuffer, drawTargetKey); - } - void VulkanRenderer::EndFrameRecordingIfNeeded() { auto& frame = m_frameContext.GetCurrent(); if (!frame.isCommandRecording) { return; } - if (m_isMainRenderPassActive) { - MOBILEGL_ASSERT(m_renderPassManager != nullptr, "EndFrameRecordingIfNeeded: manager is null"); - m_renderPassManager->EndRenderPass(frame.commandBuffer); - if (m_activeRenderTargetIsDefault) { - m_swapchainObject.SetImageLayout(m_imageIndexAcquired, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); - } - m_isMainRenderPassActive = false; - m_activeRenderPass = VK_NULL_HANDLE; - m_activeRenderExtent = {0, 0}; - m_activeDepthStencilFormat = VK_FORMAT_UNDEFINED; - m_activeRenderTargetIsDefault = true; - m_activeDrawFboExternalIndex = 0; - } m_frameContext.EndCommandRecording(); } @@ -649,27 +318,17 @@ namespace MobileGL::MG_Backend::DirectVulkan { } Bool VulkanRenderer::UploadAndBindVertexStreams( - const VertexInputStateFactory::BackendVertexInputState& vertexInputState, const DrawArrayPayload& payload, - VkCommandBuffer commandBuffer) { - if (vertexInputState.bindings.empty()) { - return true; - } + VkCommandBuffer commandBuffer, const MG_State::GLState::VertexArrayObject& vao) { + auto& vertexInputState = m_vertexInputStateFactory->GetOrCreateVertexInputState(vao); const auto bindingCount = vertexInputState.bindings.size(); - if (vertexInputState.bindingBufferKeys.size() != bindingCount) { - MGLOG_E("UploadAndBindVertexStreams failed: binding metadata mismatch"); - return false; - } Vector vkBuffers(bindingCount, VK_NULL_HANDLE); Vector vkOffsets(bindingCount, 0); const Uint32 frameIndex = m_frameContext.GetCurrentFrameIndex(); auto findBufferByKey = [&](SizeT bufferKey) -> const MG_State::GLState::BufferObject* { - if (!payload.vertexArray) { - return nullptr; - } - const auto& attrs = payload.vertexArray->GetAllAttributes(); + const auto& attrs = vao.GetAllAttributes(); for (Uint32 location = 0; location < MG_State::GLState::VertexArrayObject::MAX_VERTEX_ATTRIBS; ++location) { const auto& attr = attrs[location]; if (!attr.Buffer) { @@ -687,16 +346,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { const SizeT bufferKey = vertexInputState.bindingBufferKeys[binding]; const MG_State::GLState::BufferObject* sourceBuffer = findBufferByKey(bufferKey); - if (!sourceBuffer) { - MGLOG_D("UploadAndBindVertexStreams skipped: no source buffer for binding %zu", binding); - return false; - } - const auto sourceData = sourceBuffer->GetDataReadOnly(); - if (!sourceData || sourceData->empty()) { - MGLOG_D("UploadAndBindVertexStreams skipped: source buffer has no data for binding %zu", binding); - return false; - } const SizeT sourceSize = sourceBuffer->GetSize(); VkDeviceSize& frameHead = m_frameVertexUploadHeads[frameIndex]; @@ -721,98 +371,213 @@ namespace MobileGL::MG_Backend::DirectVulkan { return true; } - void VulkanRenderer::DrawArrays(const DrawArrayPayload& payload) { - if (payload.mode != GL_TRIANGLES) { - MGLOG_D("DrawArrays skipped: primitive mode %u is not supported yet", payload.mode); - return; - } - - MOBILEGL_ASSERT(m_vertexInputStateFactory != nullptr, "DrawArrays: vertex input state factory is null"); - const VertexInputStateFactory::BackendVertexInputState* vertexInputState = nullptr; - if (payload.vertexArray) { - vertexInputState = &m_vertexInputStateFactory->GetOrCreateVertexInputState(*payload.vertexArray); - } - - EnsureFrameRecordingStarted(); - auto& frame = m_frameContext.GetCurrent(); - if (!frame.isCommandRecording || !m_isMainRenderPassActive) { - MGLOG_D("DrawArrays skipped: frame recording was not started"); - return; - } - - VkCommandBuffer& commandBuffer = frame.commandBuffer; - const auto activeExtent = m_activeRenderExtent; - - if (payload.program == nullptr) { - MGLOG_D("DrawArrays skipped: no current program is bound"); - return; - } - - const Uint64 vertexInputHash = vertexInputState ? vertexInputState->hash : 0; - const VkPipelineVertexInputStateCreateInfo* vertexInputInfo = - vertexInputState ? &vertexInputState->state : nullptr; - if (!vertexInputInfo) { - VertexInputStateBuilder emptyVertexInputBuilder; - vertexInputInfo = &emptyVertexInputBuilder.Build(); - } - - MOBILEGL_ASSERT(m_uniformDescriptorBinder != nullptr, "DrawArrays: binder is null"); - VkPipelineLayout pipelineLayoutToUse = m_uniformDescriptorBinder->GetOrCreatePipelineLayout(*payload.program); - if (pipelineLayoutToUse == VK_NULL_HANDLE) { - MGLOG_D("DrawArrays skipped: failed to get pipeline layout for program"); - return; - } - - VkPipeline pipelineToBind = - GetOrCreatePipeline(*payload.program, pipelineLayoutToUse, vertexInputHash, *vertexInputInfo); - if (pipelineToBind == VK_NULL_HANDLE) { - MGLOG_D("DrawArrays skipped: failed to create/get pipeline"); - return; - } - - vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineToBind); - if (!m_uniformDescriptorBinder->BindProgramUniformBuffers(commandBuffer, pipelineLayoutToUse, *payload.program, - m_frameContext.GetCurrentFrameIndex())) { - MGLOG_D("DrawArrays skipped: failed to bind uniform descriptors"); - return; - } - - if (vertexInputState && !vertexInputState->bindings.empty()) { - if (!payload.vertexArray) { - MGLOG_D("DrawArrays skipped: vertex input requires VAO"); - return; + VkPipeline VulkanRenderer::GetOrCreatePipeline( + GLenum mode, + const MG_State::GLState::ProgramObject& program, + const MG_State::GLState::VertexArrayObject& vao, + const MG_State::GLState::FramebufferObject& drawFbo) { + auto toVkTopology = [](GLenum mode) -> VkPrimitiveTopology { + switch (mode) { + case GL_POINTS: + return VK_PRIMITIVE_TOPOLOGY_POINT_LIST; + case GL_LINES: + return VK_PRIMITIVE_TOPOLOGY_LINE_LIST; + case GL_LINE_STRIP: + return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP; + case GL_TRIANGLES: + return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + case GL_TRIANGLE_STRIP: + return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP; + case GL_TRIANGLE_FAN: + return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN; + case GL_LINE_LOOP: + default: + MGLOG_W("Unrecognized primitive topology"); + return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; } - if (!UploadAndBindVertexStreams(*vertexInputState, payload, commandBuffer)) { - return; + }; + auto toVkCompareOp = [](DepthTestFunc func) -> VkCompareOp { + switch (func) { + case DepthTestFunc::Never: + return VK_COMPARE_OP_NEVER; + case DepthTestFunc::Less: + return VK_COMPARE_OP_LESS; + case DepthTestFunc::Equal: + return VK_COMPARE_OP_EQUAL; + case DepthTestFunc::LessEqual: + return VK_COMPARE_OP_LESS_OR_EQUAL; + case DepthTestFunc::Greater: + return VK_COMPARE_OP_GREATER; + case DepthTestFunc::NotEqual: + return VK_COMPARE_OP_NOT_EQUAL; + case DepthTestFunc::GreaterEqual: + return VK_COMPARE_OP_GREATER_OR_EQUAL; + case DepthTestFunc::Always: + default: + return VK_COMPARE_OP_ALWAYS; } + }; + auto toVkBlendFactor = [](BlendFactor factor) -> VkBlendFactor { + switch (factor) { + case BlendFactor::Zero: + return VK_BLEND_FACTOR_ZERO; + case BlendFactor::One: + return VK_BLEND_FACTOR_ONE; + case BlendFactor::SrcColor: + return VK_BLEND_FACTOR_SRC_COLOR; + case BlendFactor::OneMinusSrcColor: + return VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR; + case BlendFactor::DstColor: + return VK_BLEND_FACTOR_DST_COLOR; + case BlendFactor::OneMinusDstColor: + return VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR; + case BlendFactor::SrcAlpha: + return VK_BLEND_FACTOR_SRC_ALPHA; + case BlendFactor::OneMinusSrcAlpha: + return VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + case BlendFactor::DstAlpha: + return VK_BLEND_FACTOR_DST_ALPHA; + case BlendFactor::OneMinusDstAlpha: + return VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA; + case BlendFactor::ConstantColor: + return VK_BLEND_FACTOR_CONSTANT_COLOR; + case BlendFactor::OneMinusConstantColor: + return VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR; + case BlendFactor::ConstantAlpha: + return VK_BLEND_FACTOR_CONSTANT_ALPHA; + case BlendFactor::OneMinusConstantAlpha: + return VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA; + default: + return VK_BLEND_FACTOR_ONE; + } + }; + + ProgramFactory::CompileOptionFlags transformFlags = GetShaderTransformFlags(m_swapchainObject.GetPreTransform()); + auto& stages = m_programFactory->GetOrCreatePipelineShaderStages(program, transformFlags); + if (stages.empty()) { + MGLOG_D("GetOrCreatePipeline skipped: program has no shader stages"); + return VK_NULL_HANDLE; } + const Uint64 programHash = m_programFactory->ComputeHash(program, transformFlags); + + auto vertexInputHash = m_vertexInputStateFactory->ComputeHash(vao); + auto& vis = m_vertexInputStateFactory->GetOrCreateVertexInputState(vao); + auto pipelineLayout = m_uniformDescriptorBinder->GetOrCreatePipelineLayout(program); + auto renderPassEntry = m_renderPassManager->GetOrCreateRenderPass(drawFbo); + auto depthTestEnabled = MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::DepthTest); + BlendFactor srcRGB = BlendFactor::One; + BlendFactor dstRGB = BlendFactor::Zero; + BlendFactor srcAlpha = BlendFactor::One; + BlendFactor dstAlpha = BlendFactor::Zero; + MG_State::pGLContext->GetBlendFunc(srcRGB, dstRGB, srcAlpha, dstAlpha); + auto mask = MG_State::pGLContext->GetColorMask(); + + PipelineFactory::PipelineCreatePayload payload { + .programHash = programHash, + .vertexInputHash = vertexInputHash, + .pipelineLayout = pipelineLayout, + .renderPass = renderPassEntry.renderPass, + .subpass = renderPassEntry.subpass, + .topology = toVkTopology(mode), + .depthTestEnable = depthTestEnabled, + .depthWriteEnable = depthTestEnabled && MG_State::pGLContext->GetDepthMask(), + .depthCompareOp = toVkCompareOp(MG_State::pGLContext->GetDepthFunc()), + .blendEnable = MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::Blend), + .srcColorBlendFactor = toVkBlendFactor(srcRGB), + .dstColorBlendFactor = toVkBlendFactor(dstRGB), + .srcAlphaBlendFactor = toVkBlendFactor(srcAlpha), + .dstAlphaBlendFactor = toVkBlendFactor(dstAlpha), + .colorWriteMask = ( + (mask.r() ? VK_COLOR_COMPONENT_R_BIT : 0u) | + (mask.g() ? VK_COLOR_COMPONENT_G_BIT : 0u) | + (mask.b() ? VK_COLOR_COMPONENT_B_BIT : 0u) | + (mask.a() ? VK_COLOR_COMPONENT_A_BIT : 0u) ), + .stages = &stages, + .vertexInputState = &vis.state + }; + return m_pipelineFactory->GetOrCreatePipeline(payload); + } + + void VulkanRenderer::SetupDraw(FrameContext::FrameData& frame, GLenum mode, Flags aspects) { + // Begin command recording if not yet + if (!frame.isCommandRecording) { + m_frameContext.BeginCommandRecording(); + m_uniformDescriptorBinder->BeginFrame(m_frameContext.GetCurrentFrameIndex()); + } + + const auto& drawFbo = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject(); + + // Begin render pass + // TODO: properly deal with clear + auto& renderPassEntry = m_renderPassManager->GetOrCreateRenderPass(*drawFbo); + if (m_activeRenderPass != &renderPassEntry) { + if (m_activeRenderPass) { + VkRenderPassManager::EndRenderPass(frame.commandBuffer); + } + m_activeRenderPass = &renderPassEntry; + Bool ok = VkRenderPassManager::BeginRenderPass(frame.commandBuffer, renderPassEntry); + MOBILEGL_ASSERT(ok, "%s: BeginRenderPass failed", __func__); + } else { + // We probably already have one compatible render pass running. Keep going. + } + + const auto& vao = *MG_State::pGLContext->GetBoundVertexArray(); + const auto& program = *MG_State::pGLContext->GetCurrentProgram(); + auto pipeline = GetOrCreatePipeline(mode, program, vao, *drawFbo); + vkCmdBindPipeline(frame.commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + + m_uniformDescriptorBinder->BindProgramUniformBuffers(frame.commandBuffer, program, + m_frameContext.GetCurrentFrameIndex()); + + UploadAndBindVertexStreams(frame.commandBuffer, vao); VkViewport viewport{}; viewport.x = 0.0f; viewport.y = 0.0f; - viewport.width = static_cast(activeExtent.width); - viewport.height = static_cast(activeExtent.height); + viewport.width = static_cast(renderPassEntry.extent.x()); + viewport.height = static_cast(renderPassEntry.extent.y()); viewport.minDepth = 0.0f; viewport.maxDepth = 1.0f; - vkCmdSetViewport(commandBuffer, 0, 1, &viewport); + vkCmdSetViewport(frame.commandBuffer, 0, 1, &viewport); VkRect2D scissor{}; scissor.offset = {0, 0}; - scissor.extent = activeExtent; - vkCmdSetScissor(commandBuffer, 0, 1, &scissor); + scissor.extent = { (Uint)renderPassEntry.extent.x(), (Uint)renderPassEntry.extent.y() }; + vkCmdSetScissor(frame.commandBuffer, 0, 1, &scissor); + } + + void VulkanRenderer::Clear(GLbitfield mask) { + m_clearManager->CollectGarbage(); + auto* fbo = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject().get(); + if (!fbo) { + MGLOG_D("VulkanRenderer::Clear: draw framebuffer not found"); + } + ClearFramebufferPayload payload { + .color = MG_State::pGLContext->GetClearColor(), + .depth = MG_State::pGLContext->GetClearDepth(), + .stencil = MG_State::pGLContext->GetClearStencil() + }; + m_clearManager->QueueClear(mask, payload, *fbo); + } + + void VulkanRenderer::DrawArrays(const DrawArrayCmd& payload) { + auto& frame = m_frameContext.GetCurrent(); + + SetupDraw(frame, payload.mode, 0); + + MOBILEGL_ASSERT(frame.isCommandRecording, "%s: frame recording was not started", __func__); + + VkCommandBuffer& commandBuffer = frame.commandBuffer; vkCmdDraw(commandBuffer, static_cast(payload.count), 1, static_cast(payload.first), 0); } - void VulkanRenderer::DrawElements(const DrawElementPayload& payload) { - if (payload.drawArray.mode != GL_TRIANGLES) { - MGLOG_D("DrawElements skipped: primitive mode %u is not supported yet", payload.drawArray.mode); - return; - } - - EnsureFrameRecordingStarted(); + void VulkanRenderer::DrawElements(const DrawElementCmd& payload) { auto& frame = m_frameContext.GetCurrent(); - if (!frame.isCommandRecording || !m_isMainRenderPassActive) { + + SetupDraw(frame, payload.mode, 0); + + if (!frame.isCommandRecording) { MGLOG_D("DrawElements skipped: frame recording was not started"); return; } @@ -830,60 +595,15 @@ namespace MobileGL::MG_Backend::DirectVulkan { return; } - if (payload.drawArray.vertexArray == nullptr) { - MGLOG_D("DrawElements skipped: no VAO provided"); - return; - } - - // VertexArrayObject currently exposes index-buffer binding through non-const accessor. - auto* vao = const_cast(payload.drawArray.vertexArray); - const auto indexBuffer = vao->GetIndexBufferBindingSlot().GetBoundObject(); - if (!indexBuffer) { - MGLOG_D("DrawElements skipped: VAO has no bound ELEMENT_ARRAY_BUFFER"); - return; - } - + auto* vao = MG_State::pGLContext->GetBoundVertexArray().get(); + const auto* indexBuffer = vao->GetIndexBufferBindingSlot().GetBoundObject().get(); const auto indexData = indexBuffer->GetDataReadOnly(); MOBILEGL_ASSERT(indexData != nullptr && !indexData->empty(), "DrawElements requires non-empty EBO data"); const SizeT indexSize = (payload.indexType == GL_UNSIGNED_SHORT) ? sizeof(Uint16) : sizeof(Uint32); - const SizeT indexDataSizeBytes = static_cast(payload.drawArray.count) * indexSize; + const SizeT indexDataSizeBytes = static_cast(payload.count) * indexSize; MOBILEGL_ASSERT(payload.indexByteOffset + indexDataSizeBytes <= indexBuffer->GetSize(), "DrawElements index range out of bounds"); - MOBILEGL_ASSERT(m_vertexInputStateFactory != nullptr, "DrawElements: vertex input state factory is null"); - const VertexInputStateFactory::BackendVertexInputState* vertexInputState = nullptr; - if (payload.drawArray.vertexArray) { - vertexInputState = &m_vertexInputStateFactory->GetOrCreateVertexInputState(*payload.drawArray.vertexArray); - } - - if (payload.drawArray.program == nullptr) { - MGLOG_D("DrawElements skipped: no current program is bound"); - return; - } - - const Uint64 vertexInputHash = vertexInputState ? vertexInputState->hash : 0; - const VkPipelineVertexInputStateCreateInfo* vertexInputInfo = - vertexInputState ? &vertexInputState->state : nullptr; - if (!vertexInputInfo) { - VertexInputStateBuilder emptyVertexInputBuilder; - vertexInputInfo = &emptyVertexInputBuilder.Build(); - } - - MOBILEGL_ASSERT(m_uniformDescriptorBinder != nullptr, "DrawElements: binder is null"); - VkPipelineLayout pipelineLayoutToUse = - m_uniformDescriptorBinder->GetOrCreatePipelineLayout(*payload.drawArray.program); - if (pipelineLayoutToUse == VK_NULL_HANDLE) { - MGLOG_D("DrawElements skipped: failed to get pipeline layout for program"); - return; - } - - VkPipeline pipelineToBind = - GetOrCreatePipeline(*payload.drawArray.program, pipelineLayoutToUse, vertexInputHash, *vertexInputInfo); - if (pipelineToBind == VK_NULL_HANDLE) { - MGLOG_D("DrawElements skipped: failed to create/get pipeline"); - return; - } - const Uint32 frameIndex = m_frameContext.GetCurrentFrameIndex(); VkDeviceSize& frameIndexHead = m_frameIndexUploadHeads[frameIndex]; const VkDeviceSize alignment = static_cast(indexSize); @@ -903,414 +623,20 @@ namespace MobileGL::MG_Backend::DirectVulkan { frameIndexHead = writeEnd; VkCommandBuffer& commandBuffer = frame.commandBuffer; - const auto activeExtent = m_activeRenderExtent; - - vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineToBind); - if (!m_uniformDescriptorBinder->BindProgramUniformBuffers(commandBuffer, pipelineLayoutToUse, - *payload.drawArray.program, - m_frameContext.GetCurrentFrameIndex())) { - MGLOG_D("DrawElements skipped: failed to bind uniform descriptors"); - return; - } - - if (vertexInputState && !vertexInputState->bindings.empty()) { - if (!payload.drawArray.vertexArray) { - MGLOG_D("DrawElements skipped: vertex input requires VAO"); - return; - } - if (!UploadAndBindVertexStreams(*vertexInputState, payload.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); - vkCmdDrawIndexed(commandBuffer, static_cast(payload.drawArray.count), 1, 0, + vkCmdDrawIndexed(commandBuffer, static_cast(payload.count), 1, 0, static_cast(payload.baseVertex), 0); } - void VulkanRenderer::MultiDrawElements(const Vector& payloads) { - if (payloads.empty()) { - return; - } + void VulkanRenderer::MultiDrawElements(const Vector& payloads) { - 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; - Int32 vertexOffset = 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; - draw.vertexOffset = static_cast(payload.baseVertex); - preparedDraws.push_back(draw); - - firstIndex += draw.indexCount; - totalIndexBytes += drawByteCount; - } - - if (preparedDraws.empty()) { - return; - } - - MOBILEGL_ASSERT(m_vertexInputStateFactory != nullptr, "MultiDrawElements: vertex input state factory is null"); - const VertexInputStateFactory::BackendVertexInputState* vertexInputState = nullptr; - if (firstPayload.drawArray.vertexArray) { - 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(); - } - - MOBILEGL_ASSERT(m_uniformDescriptorBinder != nullptr, "MultiDrawElements: binder is null"); - VkPipelineLayout 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->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 = preparedDraws[i].vertexOffset; - 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, draw.vertexOffset, 0); - } } void VulkanRenderer::Present() { MOBILEGL_ASSERT(m_imageIndexAcquired < m_swapchainObject.GetImageCount(), "Present, acquired image index out of range"); auto& frame = m_frameContext.GetCurrent(); - if (!m_pendingClears.empty() && frame.hasCommandBufferRecorded) { - MGLOG_D("Dropping pending clears for current frame because command buffer is already finalized"); - m_pendingClears.clear(); - } else if (!m_pendingClears.empty()) { - auto activateTarget = [&](Bool targetIsDefault, Uint targetFboExternalIndex) -> Bool { - const Bool alreadyMatched = frame.isCommandRecording && m_isMainRenderPassActive && - (targetIsDefault == m_activeRenderTargetIsDefault) && - (targetIsDefault || targetFboExternalIndex == m_activeDrawFboExternalIndex); - if (alreadyMatched) { - return true; - } - if (frame.hasCommandBufferRecorded) { - return false; - } - MOBILEGL_ASSERT(m_renderPassManager != nullptr, "Present: render pass manager is null"); - if (!frame.isCommandRecording) { - m_frameContext.BeginCommandRecording(); - MOBILEGL_ASSERT(m_uniformDescriptorBinder != nullptr, "Present: binder is null"); - m_uniformDescriptorBinder->BeginFrame(m_frameContext.GetCurrentFrameIndex()); - } - VkCommandBuffer commandBuffer = frame.commandBuffer; - - if (m_isMainRenderPassActive) { - MOBILEGL_ASSERT(m_renderPassManager != nullptr, "Present: render pass manager is null"); - m_renderPassManager->EndRenderPass(commandBuffer); - if (m_activeRenderTargetIsDefault) { - m_swapchainObject.SetImageLayout(m_imageIndexAcquired, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); - } - m_isMainRenderPassActive = false; - m_activeRenderPass = VK_NULL_HANDLE; - m_activeRenderExtent = {0, 0}; - m_activeDepthStencilFormat = VK_FORMAT_UNDEFINED; - m_activeRenderTargetIsDefault = true; - m_activeDrawFboExternalIndex = 0; - } - - if (targetIsDefault) { - TransitionSwapchainImageToColorAttachment(commandBuffer, m_imageIndexAcquired); - TransitionDepthStencilImageToAttachment(commandBuffer, m_imageIndexAcquired); - - const auto* defaultFboInfo = MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo; - const auto* defaultFbo = defaultFboInfo ? defaultFboInfo->defaultFBO.get() : nullptr; - if (defaultFbo == nullptr) { - return false; - } - auto& renderPassEntry = m_renderPassManager->GetOrCreateRenderPass(*defaultFbo); - const Bool started = m_renderPassManager->StartRenderPass(commandBuffer, renderPassEntry); - if (!started) { - return false; - } - m_isMainRenderPassActive = true; - m_activeRenderPass = renderPassEntry.renderPass; - m_activeRenderExtent = { - static_cast(renderPassEntry.extent.x() > 0 ? renderPassEntry.extent.x() : 0), - static_cast(renderPassEntry.extent.y() > 0 ? renderPassEntry.extent.y() : 0)}; - m_activeDepthStencilFormat = m_swapchainObject.GetDepthStencilFormat(); - m_activeRenderTargetIsDefault = true; - m_activeDrawFboExternalIndex = 0; - return true; - } - - const auto pendingFbo = MG_State::pGLContext->GetFramebufferObject(targetFboExternalIndex); - if (!pendingFbo) { - return false; - } - - const auto swapchainOldLayout = m_swapchainObject.GetImageLayout(m_imageIndexAcquired); - if (swapchainOldLayout != VK_IMAGE_LAYOUT_PRESENT_SRC_KHR && - swapchainOldLayout != VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR) { - VkImageMemoryBarrier presentBarrier{}; - presentBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - presentBarrier.srcAccessMask = 0; - presentBarrier.dstAccessMask = 0; - presentBarrier.oldLayout = swapchainOldLayout; - presentBarrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - presentBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - presentBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - presentBarrier.image = m_swapchainObject.GetImage(m_imageIndexAcquired); - presentBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - presentBarrier.subresourceRange.baseMipLevel = 0; - presentBarrier.subresourceRange.levelCount = 1; - presentBarrier.subresourceRange.baseArrayLayer = 0; - presentBarrier.subresourceRange.layerCount = 1; - vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, - VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, 1, - &presentBarrier); - m_swapchainObject.SetImageLayout(m_imageIndexAcquired, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); - } - - auto& renderPassEntry = m_renderPassManager->GetOrCreateRenderPass(*pendingFbo); - const Bool started = m_renderPassManager->StartRenderPass(commandBuffer, renderPassEntry); - if (!started) { - return false; - } - m_isMainRenderPassActive = true; - m_activeRenderPass = renderPassEntry.renderPass; - m_activeRenderExtent = { - static_cast(renderPassEntry.extent.x() > 0 ? renderPassEntry.extent.x() : 0), - static_cast(renderPassEntry.extent.y() > 0 ? renderPassEntry.extent.y() : 0)}; - m_activeDepthStencilFormat = VK_FORMAT_UNDEFINED; - m_activeRenderTargetIsDefault = false; - m_activeDrawFboExternalIndex = targetFboExternalIndex; - return true; - }; - - while (!m_pendingClears.empty()) { - auto pendingIt = m_pendingClears.end(); - if (m_isMainRenderPassActive) { - const Uint64 activeKey = - BuildPendingClearKey(m_activeDrawFboExternalIndex, m_activeRenderTargetIsDefault); - pendingIt = m_pendingClears.find(activeKey); - } - if (pendingIt == m_pendingClears.end()) { - pendingIt = m_pendingClears.begin(); - } - - const auto pendingTarget = pendingIt->second; - if (!activateTarget(pendingTarget.targetsDefaultFramebuffer, pendingTarget.drawFboExternalIndex)) { - MGLOG_D("Present: dropping pending clear because target activation failed (FBO %u, default=%d)", - pendingTarget.drawFboExternalIndex, pendingTarget.targetsDefaultFramebuffer); - m_pendingClears.erase(pendingIt); - continue; - } - - const Uint64 activeKey = - BuildPendingClearKey(m_activeDrawFboExternalIndex, m_activeRenderTargetIsDefault); - auto activePendingIt = m_pendingClears.find(activeKey); - if (activePendingIt == m_pendingClears.end()) { - continue; - } - - ApplyPendingClearsForActiveTarget(frame.commandBuffer, activeKey); - } - } EndFrameRecordingIfNeeded(); const auto acquiredImageLayout = m_swapchainObject.GetImageLayout(m_imageIndexAcquired); @@ -1902,13 +1228,6 @@ namespace MobileGL::MG_Backend::DirectVulkan { m_frameVertexUploadHeads.assign(m_frameContext.GetFrameCount(), 0); m_frameIndexUploadBuffers.resize(m_frameContext.GetFrameCount()); m_frameIndexUploadHeads.assign(m_frameContext.GetFrameCount(), 0); - m_isMainRenderPassActive = false; - m_activeRenderPass = VK_NULL_HANDLE; - m_activeRenderExtent = {0, 0}; - m_activeDepthStencilFormat = VK_FORMAT_UNDEFINED; - m_activeRenderTargetIsDefault = true; - m_activeDrawFboExternalIndex = 0; - m_pendingClears.clear(); } const PhysicalDevice& VulkanRenderer::GetPhysicalDevice() const { diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h index ee0da8bd..1b2672ff 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h @@ -32,16 +32,23 @@ namespace MobileGL::MG_State::GLState { } // namespace MobileGL::MG_State::GLState namespace MobileGL::MG_Backend::DirectVulkan { - struct DrawArrayPayload { + enum class DrawSetupAspect: Uint8 { + FramebufferObject = 1 << 0, + VertexArrayObject = 1 << 1, + UniformBuffer = 1 << 2, + VertexBuffer = 1 << 3, + IndexBuffer = 1 << 4, + Viewport = 1 << 5, + Scissor = 1 << 6, + }; + + struct DrawArrayCmd { GLenum mode = GL_TRIANGLES; GLint first = 0; GLsizei count = 0; - const MG_State::GLState::ProgramObject* program = nullptr; - const MG_State::GLState::VertexArrayObject* vertexArray = nullptr; }; - struct DrawElementPayload { - DrawArrayPayload drawArray; + struct DrawElementCmd: public DrawArrayCmd { GLenum indexType = GL_UNSIGNED_SHORT; SizeT indexByteOffset = 0; GLint baseVertex = 0; @@ -70,13 +77,12 @@ namespace MobileGL::MG_Backend::DirectVulkan { void Initialize(); void Shutdown(); - void QueueClearRequest(GLbitfield mask, const FloatVec4& color, Float depth, Uint32 stencil, - Uint drawFboExternalIndex, Bool isDefaultFramebufferTarget); - Bool ConsumePendingColorClear(VkClearColorValue& outClearColor); - void EnsureFrameRecordingStarted(); - void DrawArrays(const DrawArrayPayload& payload); - void DrawElements(const DrawElementPayload& payload); - void MultiDrawElements(const Vector& payloads); + void SetupDraw(FrameContext::FrameData& frame, GLenum mode, Flags aspects); + + void Clear(GLbitfield mask); + void DrawArrays(const DrawArrayCmd& payload); + void DrawElements(const DrawElementCmd& payload); + void MultiDrawElements(const Vector& payloads); void Present(); const PhysicalDevice& GetPhysicalDevice() const; @@ -85,15 +91,6 @@ namespace MobileGL::MG_Backend::DirectVulkan { void RecreateSwapchain(); private: - struct PendingClearState { - GLbitfield mask = 0; - VkClearColorValue color = {{0.0f, 0.0f, 0.0f, 1.0f}}; - Float depth = 1.0f; - Uint32 stencil = 0; - Uint drawFboExternalIndex = 0; - Bool targetsDefaultFramebuffer = true; - }; - NativeWindowType m_window = 0; VulkanRendererConfig m_config; @@ -123,6 +120,8 @@ namespace MobileGL::MG_Backend::DirectVulkan { VkCommandPool m_commandPool = VK_NULL_HANDLE; + RenderPassEntry* m_activeRenderPass = nullptr; + Vector m_frameVertexUploadBuffers; Vector m_frameVertexUploadHeads; Vector m_frameIndexUploadBuffers; @@ -131,13 +130,6 @@ namespace MobileGL::MG_Backend::DirectVulkan { Uint m_imageIndexAcquired = 0; FrameContext m_frameContext; - UnorderedMap m_pendingClears; - Bool m_isMainRenderPassActive = false; - VkRenderPass m_activeRenderPass = VK_NULL_HANDLE; - VkExtent2D m_activeRenderExtent = {0, 0}; - VkFormat m_activeDepthStencilFormat = VK_FORMAT_UNDEFINED; - Bool m_activeRenderTargetIsDefault = true; - Uint m_activeDrawFboExternalIndex = 0; UniquePtr m_pipelineFactory; UniquePtr m_programFactory; @@ -160,9 +152,13 @@ namespace MobileGL::MG_Backend::DirectVulkan { void CreateSwapchain(); void CreateCommandPool(); void CreateFrameContexts(); - VkPipeline GetOrCreatePipeline(const MG_State::GLState::ProgramObject& program, VkPipelineLayout pipelineLayout, - Uint64 vertexInputHash, - const VkPipelineVertexInputStateCreateInfo& vertexInputState); + + VkPipeline GetOrCreatePipeline( + GLenum mode, + const MG_State::GLState::ProgramObject& program, + const MG_State::GLState::VertexArrayObject& vao, + const MG_State::GLState::FramebufferObject& drawFbo); + void TransitionSwapchainImageToColorAttachment(VkCommandBuffer commandBuffer, Uint32 imageIndex); void TransitionDepthStencilImageToAttachment(VkCommandBuffer commandBuffer, Uint32 imageIndex); void EndFrameRecordingIfNeeded(); @@ -170,9 +166,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { void CollectDeferredBufferReleases(Uint32 frameIndex); Bool EnsureFrameUploadBufferCapacity(Uint32 frameIndex, Bool isIndexBuffer, VkDeviceSize requiredEndOffset, VkDeviceSize minCapacity, VkBufferUsageFlags usage); - Bool UploadAndBindVertexStreams(const VertexInputStateFactory::BackendVertexInputState& vertexInputState, - const DrawArrayPayload& payload, VkCommandBuffer commandBuffer); - void ApplyPendingClearsForActiveTarget(VkCommandBuffer commandBuffer, Uint64 drawTargetKey); + Bool UploadAndBindVertexStreams(VkCommandBuffer commandBuffer, const MG_State::GLState::VertexArrayObject& vao); void ShutdownSwapchain();