[Fix] (MG_Backend/DirectVulkan): refactor VkRenderPassManager and hook it up (this commit only compiles and does not work)

This commit is contained in:
2026-03-01 18:17:34 +08:00
parent e234d471fe
commit f61f068e28
12 changed files with 305 additions and 1318 deletions
+1 -1
View File
@@ -58,7 +58,7 @@ namespace MobileGL::MG_ConfigLoader {
inline void InitBackendType() { inline void InitBackendType() {
String backendTypeStr; String backendTypeStr;
QueryEnvVariable("MOBILEGL_BACKEND_TYPE", backendTypeStr, "DirectGLES"); QueryEnvVariable("MOBILEGL_BACKEND_TYPE", backendTypeStr, "DirectVulkan");
#define ENTRY(backendType) \ #define ENTRY(backendType) \
if (backendTypeStr == #backendType) { \ if (backendTypeStr == #backendType) { \
MG_Config::ActiveBackendType = BackendType::backendType; \ MG_Config::ActiveBackendType = BackendType::backendType; \
+19 -334
View File
@@ -17,165 +17,11 @@ namespace MobileGL::MG_Backend::DirectVulkan {
void ClearBufferfv(GLenum buffer, GLint drawbuffer, const GLfloat* value) {} void ClearBufferfv(GLenum buffer, GLint drawbuffer, const GLfloat* value) {}
void ClearBufferuiv(GLenum buffer, GLint drawbuffer, const GLuint* value) {} void ClearBufferuiv(GLenum buffer, GLint drawbuffer, const GLuint* value) {}
void ClearBufferiv(GLenum buffer, GLint drawbuffer, const GLint* value) {} void ClearBufferiv(GLenum buffer, GLint drawbuffer, const GLint* value) {}
void DrawElementsBaseVertex(GLenum mode, GLsizei count, GLenum type, const GLvoid* indices, GLint basevertex) { 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<SizeT>(indices);
const SizeT requiredBytes = static_cast<SizeT>(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 MultiDrawElementsBaseVertex(GLenum mode, const GLsizei* count, GLenum type, const GLvoid* const* indices, void MultiDrawElementsBaseVertex(GLenum mode, const GLsizei* count, GLenum type, const GLvoid* const* indices,
GLsizei drawcount, const GLint* basevertex) { 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");
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<DrawElementPayload> payloads;
payloads.reserve(static_cast<SizeT>(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<SizeT>(indices[i]);
const SizeT requiredBytes = static_cast<SizeT>(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 MultiDrawElementsIndirect(GLenum mode, GLenum type, const void* indirect, GLsizei drawcount, GLsizei stride) {}
void MultiDrawArraysIndirect(GLenum mode, 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, 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(pVulkanRenderer, "DirectVulkan::MultiDrawElements called with null VulkanRenderer");
MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::MultiDrawElements called with null GL context"); MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::MultiDrawElements called with null GL context");
if (drawcount < 0) { Vector<DrawElementCmd> cmds;
MGLOG_W("MultiDrawElements skipped: drawcount (%d) must be non-negative", drawcount); cmds.reserve(static_cast<SizeT>(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) { 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) { if (count[i] == 0) {
continue; continue;
} }
const auto byteOffset = reinterpret_cast<SizeT>(indices[i]); DrawElementCmd payload{};
const SizeT requiredBytes = static_cast<SizeT>(count[i]) * indexSize; payload.mode = mode;
if (byteOffset + requiredBytes > indexBuffer->GetSize()) { payload.first = 0;
MGLOG_W("MultiDrawElements skipped: draw[%d] index range out of bounds (offset=%zu, size=%zu, " payload.count = count[i];
"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.indexType = type;
payload.indexByteOffset = byteOffset; payload.indexByteOffset = reinterpret_cast<SizeT>(indices[i]);
payloads.push_back(payload); cmds.push_back(payload);
} }
if (payloads.empty()) { if (cmds.empty()) {
return; return;
} }
pVulkanRenderer->MultiDrawElements(payloads); pVulkanRenderer->MultiDrawElements(cmds);
} }
void Clear(GLbitfield mask) { void Clear(GLbitfield mask) {
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::Clear called with null VulkanRenderer"); MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::Clear called with null VulkanRenderer");
MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::Clear called with null GL context"); MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::Clear called with null GL context");
pVulkanRenderer->Clear(mask);
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<Uint32>(MG_State::pGLContext->GetClearStencil());
pVulkanRenderer->QueueClearRequest(mask, clearColor, clearDepth, clearStencil,
drawFboExternalIndex,
isDefaultFboTarget);
} }
void DrawElements(GLenum mode, GLsizei count, GLenum type, const void* indices) { void DrawElements(GLenum mode, GLsizei count, GLenum type, const void* indices) {
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::DrawElements called with null VulkanRenderer"); MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::DrawElements called with null VulkanRenderer");
MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::DrawElements called with null GL context"); MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::DrawElements called with null GL context");
if (count < 0) { DrawElementCmd payload{};
MGLOG_W("DrawElements skipped: count (%d) must be non-negative", count); payload.mode = mode;
return; payload.first = 0;
} payload.count = count;
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<SizeT>(indices);
const SizeT requiredBytes = static_cast<SizeT>(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();
payload.indexType = type; payload.indexType = type;
payload.indexByteOffset = byteOffset; payload.indexByteOffset = reinterpret_cast<SizeT>(indices);
pVulkanRenderer->DrawElements(payload); pVulkanRenderer->DrawElements(payload);
} }
@@ -389,34 +98,10 @@ namespace MobileGL::MG_Backend::DirectVulkan {
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::DrawArrays called with null VulkanRenderer"); MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::DrawArrays called with null VulkanRenderer");
MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::DrawArrays called with null GL context"); MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::DrawArrays called with null GL context");
if (first < 0) { DrawArrayCmd payload{};
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{};
payload.mode = mode; payload.mode = mode;
payload.first = first; payload.first = first;
payload.count = count; 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); pVulkanRenderer->DrawArrays(payload);
} }
@@ -475,11 +475,15 @@ namespace MobileGL::MG_Backend::DirectVulkan {
if (!samplerToUse) { if (!samplerToUse) {
return false; return false;
} }
VkTextureManager::TextureResource resource;
if (!m_textureManager->SyncTextureAndGetDescriptor(*texture, outImageInfo)) { if (!m_textureManager->SyncTextureAndGetDescriptor(*texture, resource)) {
return false; 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; return outImageInfo.sampler != VK_NULL_HANDLE;
} }
@@ -678,27 +682,10 @@ namespace MobileGL::MG_Backend::DirectVulkan {
return true; return true;
} }
Bool UniformDescriptorBinder::BindProgramUniformBuffers(VkCommandBuffer commandBuffer, VkPipelineLayout pipelineLayout, Bool UniformDescriptorBinder::BindProgramUniformBuffers(VkCommandBuffer commandBuffer,
const MG_State::GLState::ProgramObject& program, const MG_State::GLState::ProgramObject& program,
Uint32 frameIndex) { 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); 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]; auto& frame = m_frames[frameIndex];
if (frame.descriptorPools.empty()) { if (frame.descriptorPools.empty()) {
@@ -837,7 +824,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
vkUpdateDescriptorSets(m_device, static_cast<Uint32>(writes.size()), writes.data(), 0, nullptr); vkUpdateDescriptorSets(m_device, static_cast<Uint32>(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<Uint32>(dynamicOffsets.size()), dynamicOffsets.data()); static_cast<Uint32>(dynamicOffsets.size()), dynamicOffsets.data());
return true; return true;
} }
@@ -36,7 +36,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
void BeginFrame(Uint32 frameIndex); void BeginFrame(Uint32 frameIndex);
VkPipelineLayout GetOrCreatePipelineLayout(const MG_State::GLState::ProgramObject& program); 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); const MG_State::GLState::ProgramObject& program, Uint32 frameIndex);
private: private:
@@ -81,6 +81,11 @@ namespace MobileGL::MG_Backend::DirectVulkan {
} }
SizeT VkClearManager::CollectGarbage() { SizeT VkClearManager::CollectGarbage() {
m_gcCounter++;
if (m_gcCounter != 0) {
return 0;
}
SizeT count = 0; SizeT count = 0;
for (const auto& [raw, weak]: m_aliveObjects) { for (const auto& [raw, weak]: m_aliveObjects) {
if (weak.expired()) { if (weak.expired()) {
@@ -45,6 +45,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
void PopPendingClear(MG_State::GLState::ITextureObject* texture); void PopPendingClear(MG_State::GLState::ITextureObject* texture);
SizeT CollectGarbage(); SizeT CollectGarbage();
private: private:
Uint8 m_gcCounter = 0;
UnorderedMap<MG_State::GLState::ITextureObject*, ClearAttachmentPayload> m_pendingClears; UnorderedMap<MG_State::GLState::ITextureObject*, ClearAttachmentPayload> m_pendingClears;
UnorderedMap<MG_State::GLState::ITextureObject*, WeakPtr<MG_State::GLState::ITextureObject>> m_aliveObjects; UnorderedMap<MG_State::GLState::ITextureObject*, WeakPtr<MG_State::GLState::ITextureObject>> m_aliveObjects;
}; };
@@ -94,9 +94,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
Int width = 0; Int width = 0;
Int height = 0; Int height = 0;
Vector<VkAttachmentDescription> colorAttachmentDescriptions(validDrawBufCount); Vector<VkAttachmentDescription> attachmentDescriptions(validDrawBufCount);
Vector<VkAttachmentReference> colorAttachmentRefs(validDrawBufCount); Vector<VkAttachmentReference> colorAttachmentRefs(validDrawBufCount);
Vector<VkDescriptorImageInfo> descriptorImageInfo(validDrawBufCount); Vector<VkTextureManager::TextureResource> textureResources(validDrawBufCount);
// This should automatically work on default & offscreen FBO // This should automatically work on default & offscreen FBO
// assuming default FBO has the right param // assuming default FBO has the right param
for (Int i = 0; i < validDrawBufCount; ++i) { for (Int i = 0; i < validDrawBufCount; ++i) {
@@ -110,7 +110,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
const auto textureTarget = texture->GetTarget(); const auto textureTarget = texture->GetTarget();
// Color attachment description // Color attachment description
VkAttachmentDescription& desc = colorAttachmentDescriptions[i]; VkAttachmentDescription& desc = attachmentDescriptions[i];
switch (textureTarget) { switch (textureTarget) {
case TextureTarget::Texture2D: { case TextureTarget::Texture2D: {
auto* texture2d = auto* texture2d =
@@ -135,8 +135,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
if (height == 0) if (height == 0)
height = texture2d->GetBaseSize().y(); height = texture2d->GetBaseSize().y();
Bool ok = m_textureManager.SyncTextureAndGetDescriptor(*texture, descriptorImageInfo[i]); Bool ok = m_textureManager.SyncTextureAndGetDescriptor(*texture, textureResources[i]);
MOBILEGL_ASSERT(ok, "GetOrCreateRenderPass: SyncTextureAndGetDescriptor failed"); MOBILEGL_ASSERT(ok, "GetOrCreateRenderPass: SyncTextureAndGetDescriptor failed at color attachment %d", i);
break; break;
} }
@@ -153,9 +153,11 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// Depth attachment description // Depth attachment description
auto& depthAtt = fbo.GetAttachment(FramebufferAttachmentType::Depth); auto& depthAtt = fbo.GetAttachment(FramebufferAttachmentType::Depth);
VkAttachmentDescription depthAttachmentDescription; VkAttachmentDescription depthAttachmentDescription;
if (depthAtt.IsComplete()) { VkTextureManager::TextureResource depthTextureResource;
if (depthAtt.IsComplete() && depthAtt.IsTexture()) {
auto& texture = *depthAtt.GetTexture();
depthAttachmentDescription.format = depthAttachmentDescription.format =
MG_Util::ConvertTextureInternalFormatToVkEnum(depthAtt.GetTexture()->GetFormat()); MG_Util::ConvertTextureInternalFormatToVkEnum(texture.GetFormat());
depthAttachmentDescription.samples = VK_SAMPLE_COUNT_1_BIT; depthAttachmentDescription.samples = VK_SAMPLE_COUNT_1_BIT;
depthAttachmentDescription.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; depthAttachmentDescription.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
depthAttachmentDescription.storeOp = VK_ATTACHMENT_STORE_OP_STORE; depthAttachmentDescription.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
@@ -165,6 +167,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
depthAttachmentDescription.finalLayout = isDefaultFbo ? depthAttachmentDescription.finalLayout = isDefaultFbo ?
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR : VK_IMAGE_LAYOUT_PRESENT_SRC_KHR :
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; 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 // Depth attachment ref
@@ -190,8 +195,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
renderPassCreateInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; renderPassCreateInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
renderPassCreateInfo.pNext = VK_NULL_HANDLE; renderPassCreateInfo.pNext = VK_NULL_HANDLE;
renderPassCreateInfo.flags = 0; renderPassCreateInfo.flags = 0;
renderPassCreateInfo.attachmentCount = colorAttachmentDescriptions.size(); renderPassCreateInfo.attachmentCount = attachmentDescriptions.size();
renderPassCreateInfo.pAttachments = colorAttachmentDescriptions.data(); renderPassCreateInfo.pAttachments = attachmentDescriptions.data();
renderPassCreateInfo.subpassCount = 1; renderPassCreateInfo.subpassCount = 1;
renderPassCreateInfo.pSubpasses = &subpassDesc; renderPassCreateInfo.pSubpasses = &subpassDesc;
renderPassCreateInfo.dependencyCount = 0; renderPassCreateInfo.dependencyCount = 0;
@@ -200,9 +205,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VkRenderPass renderPass = VK_NULL_HANDLE; VkRenderPass renderPass = VK_NULL_HANDLE;
VK_VERIFY(vkCreateRenderPass(m_device, &renderPassCreateInfo, nullptr, &renderPass)); VK_VERIFY(vkCreateRenderPass(m_device, &renderPassCreateInfo, nullptr, &renderPass));
Vector<VkImageView> attachmentViews(descriptorImageInfo.size(), VK_NULL_HANDLE); Vector<VkImageView> attachmentViews(textureResources.size(), VK_NULL_HANDLE);
for (Int i = 0; i < descriptorImageInfo.size(); i++) { for (Int i = 0; i < textureResources.size(); i++) {
attachmentViews[i] = descriptorImageInfo[i].imageView; attachmentViews[i] = textureResources[i].view;
} }
// Framebuffer // Framebuffer
@@ -219,23 +224,22 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VkFramebuffer framebuffer = VK_NULL_HANDLE; VkFramebuffer framebuffer = VK_NULL_HANDLE;
VK_VERIFY(vkCreateFramebuffer(m_device, &framebufferCreateInfo, nullptr, &framebuffer)); VK_VERIFY(vkCreateFramebuffer(m_device, &framebufferCreateInfo, nullptr, &framebuffer));
IntVec2 extent = {width, height}; 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]; return m_renderPasses[hash];
} }
Bool VkRenderPassManager::StartRenderPass(VkCommandBuffer commandBuffer, RenderPassEntry& renderPassEntry) { Bool VkRenderPassManager::BeginRenderPass(VkCommandBuffer commandBuffer, RenderPassEntry& renderPassEntry) {
if (m_activeRenderPass != nullptr) // TODO: Transition all the attachments into proper layout before starting the render pass
return false;
m_activeRenderPass = &renderPassEntry;
VkRenderPassBeginInfo renderPassBeginInfo; VkRenderPassBeginInfo renderPassBeginInfo;
renderPassBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; renderPassBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
renderPassBeginInfo.pNext = nullptr; renderPassBeginInfo.pNext = nullptr;
renderPassBeginInfo.renderPass = m_activeRenderPass->renderPass; renderPassBeginInfo.renderPass = renderPassEntry.renderPass;
renderPassBeginInfo.framebuffer = m_activeRenderPass->framebuffer; renderPassBeginInfo.framebuffer = renderPassEntry.framebuffer;
renderPassBeginInfo.renderArea.offset = { 0, 0 }; renderPassBeginInfo.renderArea.offset = { 0, 0 };
renderPassBeginInfo.renderArea.extent = { 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 // TODO: should query proper clear color
VkClearValue clearValue; VkClearValue clearValue;
@@ -251,8 +255,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
} }
Bool VkRenderPassManager::EndRenderPass(VkCommandBuffer commandBuffer) { Bool VkRenderPassManager::EndRenderPass(VkCommandBuffer commandBuffer) {
if (m_activeRenderPass == nullptr)
return false;
vkCmdEndRenderPass(commandBuffer); vkCmdEndRenderPass(commandBuffer);
return true; return true;
} }
@@ -21,8 +21,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
static inline VkDevice s_device; static inline VkDevice s_device;
VkRenderPass renderPass = VK_NULL_HANDLE; VkRenderPass renderPass = VK_NULL_HANDLE;
VkFramebuffer framebuffer = VK_NULL_HANDLE; VkFramebuffer framebuffer = VK_NULL_HANDLE;
Vector<VkDescriptorImageInfo> descriptorImageInfo; Vector<VkTextureManager::TextureResource> textureResources;
IntVec2 extent = {0, 0}; IntVec2 extent = {0, 0};
Uint32 subpass = 0;
~RenderPassEntry() { ~RenderPassEntry() {
if (renderPass != VK_NULL_HANDLE) { if (renderPass != VK_NULL_HANDLE) {
@@ -46,16 +47,14 @@ namespace MobileGL::MG_Backend::DirectVulkan {
HashType ComputeHash(const MG_State::GLState::FramebufferObject& fbo) const; HashType ComputeHash(const MG_State::GLState::FramebufferObject& fbo) const;
RenderPassEntry& GetOrCreateRenderPass(const MG_State::GLState::FramebufferObject& fbo); RenderPassEntry& GetOrCreateRenderPass(const MG_State::GLState::FramebufferObject& fbo);
RenderPassEntry* GetActiveRenderPass() const { return m_activeRenderPass; } static Bool BeginRenderPass(VkCommandBuffer commandBuffer, RenderPassEntry& renderPassEntry);
Bool StartRenderPass(VkCommandBuffer commandBuffer, RenderPassEntry& renderPassEntry); static Bool EndRenderPass(VkCommandBuffer commandBuffer);
Bool EndRenderPass(VkCommandBuffer commandBuffer);
private: private:
VkDevice m_device = VK_NULL_HANDLE; VkDevice m_device = VK_NULL_HANDLE;
const VulkanRendererConfig& m_config; const VulkanRendererConfig& m_config;
VkClearManager& m_clearManager; VkClearManager& m_clearManager;
VkTextureManager& m_textureManager; VkTextureManager& m_textureManager;
UnorderedMap<Uint64, RenderPassEntry> m_renderPasses; UnorderedMap<Uint64, RenderPassEntry> m_renderPasses;
RenderPassEntry* m_activeRenderPass = nullptr;
static inline XXH64_state_t* m_hashState = XXH64_createState(); static inline XXH64_state_t* m_hashState = XXH64_createState();
}; };
} // namespace MobileGL::MG_Backend::DirectVulkan } // namespace MobileGL::MG_Backend::DirectVulkan
@@ -41,7 +41,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
} }
Bool VkTextureManager::SyncTextureAndGetDescriptor(MG_State::GLState::ITextureObject& texture, 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"); MOBILEGL_ASSERT(m_device != VK_NULL_HANDLE, "SyncTextureAndGetDescriptor: m_device == VK_NULL_HANDLE");
auto it = m_textureResources.find(texture.GetExternalIndex()); auto it = m_textureResources.find(texture.GetExternalIndex());
@@ -56,15 +56,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
return false; return false;
} }
if (it->second.view == VK_NULL_HANDLE) { outTextureResource = it->second;
return false;
}
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; return true;
} }
Bool VkTextureManager::TransitionImageLayout(VkCommandBuffer commandBuffer, VkImage image, Bool VkTextureManager::TransitionImageLayout(VkCommandBuffer commandBuffer, VkImage image,
@@ -28,17 +28,6 @@ public:
VkQueue graphicsQueue = VK_NULL_HANDLE; 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 { struct TextureResource {
VkImage image = VK_NULL_HANDLE; VkImage image = VK_NULL_HANDLE;
VmaAllocation allocation = nullptr; VmaAllocation allocation = nullptr;
@@ -50,6 +39,19 @@ private:
Uint textureExternalIndex = 0; 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, Bool SyncTexture(MG_State::GLState::ITextureObject &texture,
TextureResource &outResource); TextureResource &outResource);
Bool SyncTextureResource(const MG_State::GLState::ITextureObject &texture, Bool SyncTextureResource(const MG_State::GLState::ITextureObject &texture,
File diff suppressed because it is too large Load Diff
@@ -32,16 +32,23 @@ namespace MobileGL::MG_State::GLState {
} // namespace MobileGL::MG_State::GLState } // namespace MobileGL::MG_State::GLState
namespace MobileGL::MG_Backend::DirectVulkan { 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; GLenum mode = GL_TRIANGLES;
GLint first = 0; GLint first = 0;
GLsizei count = 0; GLsizei count = 0;
const MG_State::GLState::ProgramObject* program = nullptr;
const MG_State::GLState::VertexArrayObject* vertexArray = nullptr;
}; };
struct DrawElementPayload { struct DrawElementCmd: public DrawArrayCmd {
DrawArrayPayload drawArray;
GLenum indexType = GL_UNSIGNED_SHORT; GLenum indexType = GL_UNSIGNED_SHORT;
SizeT indexByteOffset = 0; SizeT indexByteOffset = 0;
GLint baseVertex = 0; GLint baseVertex = 0;
@@ -70,13 +77,12 @@ namespace MobileGL::MG_Backend::DirectVulkan {
void Initialize(); void Initialize();
void Shutdown(); void Shutdown();
void QueueClearRequest(GLbitfield mask, const FloatVec4& color, Float depth, Uint32 stencil, void SetupDraw(FrameContext::FrameData& frame, GLenum mode, Flags<DrawSetupAspect> aspects);
Uint drawFboExternalIndex, Bool isDefaultFramebufferTarget);
Bool ConsumePendingColorClear(VkClearColorValue& outClearColor); void Clear(GLbitfield mask);
void EnsureFrameRecordingStarted(); void DrawArrays(const DrawArrayCmd& payload);
void DrawArrays(const DrawArrayPayload& payload); void DrawElements(const DrawElementCmd& payload);
void DrawElements(const DrawElementPayload& payload); void MultiDrawElements(const Vector<DrawElementCmd>& payloads);
void MultiDrawElements(const Vector<DrawElementPayload>& payloads);
void Present(); void Present();
const PhysicalDevice& GetPhysicalDevice() const; const PhysicalDevice& GetPhysicalDevice() const;
@@ -85,15 +91,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
void RecreateSwapchain(); void RecreateSwapchain();
private: 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; NativeWindowType m_window = 0;
VulkanRendererConfig m_config; VulkanRendererConfig m_config;
@@ -123,6 +120,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VkCommandPool m_commandPool = VK_NULL_HANDLE; VkCommandPool m_commandPool = VK_NULL_HANDLE;
RenderPassEntry* m_activeRenderPass = nullptr;
Vector<VkBufferObject> m_frameVertexUploadBuffers; Vector<VkBufferObject> m_frameVertexUploadBuffers;
Vector<VkDeviceSize> m_frameVertexUploadHeads; Vector<VkDeviceSize> m_frameVertexUploadHeads;
Vector<VkBufferObject> m_frameIndexUploadBuffers; Vector<VkBufferObject> m_frameIndexUploadBuffers;
@@ -131,13 +130,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
Uint m_imageIndexAcquired = 0; Uint m_imageIndexAcquired = 0;
FrameContext m_frameContext; FrameContext m_frameContext;
UnorderedMap<Uint64, PendingClearState> 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<PipelineFactory> m_pipelineFactory; UniquePtr<PipelineFactory> m_pipelineFactory;
UniquePtr<ProgramFactory> m_programFactory; UniquePtr<ProgramFactory> m_programFactory;
@@ -160,9 +152,13 @@ namespace MobileGL::MG_Backend::DirectVulkan {
void CreateSwapchain(); void CreateSwapchain();
void CreateCommandPool(); void CreateCommandPool();
void CreateFrameContexts(); void CreateFrameContexts();
VkPipeline GetOrCreatePipeline(const MG_State::GLState::ProgramObject& program, VkPipelineLayout pipelineLayout,
Uint64 vertexInputHash, VkPipeline GetOrCreatePipeline(
const VkPipelineVertexInputStateCreateInfo& vertexInputState); 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 TransitionSwapchainImageToColorAttachment(VkCommandBuffer commandBuffer, Uint32 imageIndex);
void TransitionDepthStencilImageToAttachment(VkCommandBuffer commandBuffer, Uint32 imageIndex); void TransitionDepthStencilImageToAttachment(VkCommandBuffer commandBuffer, Uint32 imageIndex);
void EndFrameRecordingIfNeeded(); void EndFrameRecordingIfNeeded();
@@ -170,9 +166,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
void CollectDeferredBufferReleases(Uint32 frameIndex); void CollectDeferredBufferReleases(Uint32 frameIndex);
Bool EnsureFrameUploadBufferCapacity(Uint32 frameIndex, Bool isIndexBuffer, VkDeviceSize requiredEndOffset, Bool EnsureFrameUploadBufferCapacity(Uint32 frameIndex, Bool isIndexBuffer, VkDeviceSize requiredEndOffset,
VkDeviceSize minCapacity, VkBufferUsageFlags usage); VkDeviceSize minCapacity, VkBufferUsageFlags usage);
Bool UploadAndBindVertexStreams(const VertexInputStateFactory::BackendVertexInputState& vertexInputState, Bool UploadAndBindVertexStreams(VkCommandBuffer commandBuffer, const MG_State::GLState::VertexArrayObject& vao);
const DrawArrayPayload& payload, VkCommandBuffer commandBuffer);
void ApplyPendingClearsForActiveTarget(VkCommandBuffer commandBuffer, Uint64 drawTargetKey);
void ShutdownSwapchain(); void ShutdownSwapchain();