mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-10 21:28:32 +09:00
[Perf] (MG_Backend/DirectVulkan): Batch layout transitions and Reuse staging buffers.
This commit is contained in:
@@ -17,6 +17,7 @@
|
|||||||
namespace MobileGL::MG_Backend::DirectVulkanTMP::TmpImpl {
|
namespace MobileGL::MG_Backend::DirectVulkanTMP::TmpImpl {
|
||||||
class PendingClearInfo;
|
class PendingClearInfo;
|
||||||
void BeginRenderPass(GLbitfield clearMask, const PendingClearInfo* clearInfo);
|
void BeginRenderPass(GLbitfield clearMask, const PendingClearInfo* clearInfo);
|
||||||
|
void BeginCommandBuffer();
|
||||||
|
|
||||||
VulkanState g;
|
VulkanState g;
|
||||||
|
|
||||||
@@ -77,6 +78,33 @@ namespace MobileGL::MG_Backend::DirectVulkanTMP::TmpImpl {
|
|||||||
"CreateBuffer: buffer or memory is null");
|
"CreateBuffer: buffer or memory is null");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BufferResource AcquireStagingBuffer(VkDeviceSize size) {
|
||||||
|
for (auto it = g.stagingPool.begin(); it != g.stagingPool.end(); ++it) {
|
||||||
|
if (it->size >= size) {
|
||||||
|
BufferResource res = *it;
|
||||||
|
g.stagingPool.erase(it);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BufferResource res{};
|
||||||
|
CreateBuffer(size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
|
||||||
|
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReleaseStagingBuffer(BufferResource& res) {
|
||||||
|
if (res.buffer == VK_NULL_HANDLE && res.memory == VK_NULL_HANDLE) return;
|
||||||
|
if (g.frames.empty()) {
|
||||||
|
DestroyBuffer(res);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (g.stagingPending.empty()) {
|
||||||
|
g.stagingPending.resize(g.frames.size());
|
||||||
|
}
|
||||||
|
g.stagingPending[g.currentFrame].push_back(res);
|
||||||
|
res = BufferResource{};
|
||||||
|
}
|
||||||
|
|
||||||
void UpdateBufferFromObject(BufferResource& res, BufferObject* obj) {
|
void UpdateBufferFromObject(BufferResource& res, BufferObject* obj) {
|
||||||
auto dataPtr = obj->GetDataReadOnly();
|
auto dataPtr = obj->GetDataReadOnly();
|
||||||
if (!dataPtr || dataPtr->empty()) {
|
if (!dataPtr || dataPtr->empty()) {
|
||||||
@@ -792,11 +820,42 @@ namespace MobileGL::MG_Backend::DirectVulkanTMP::TmpImpl {
|
|||||||
vkFreeCommandBuffers(g.ctx->GetDevice(), g.commandPool, 1, &cmd);
|
vkFreeCommandBuffers(g.ctx->GetDevice(), g.commandPool, 1, &cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool RecordTransferCommands(const std::function<void(VkCommandBuffer)>& record, bool restartIfRecording) {
|
||||||
|
if (g.frames.empty()) {
|
||||||
|
SubmitImmediate(record);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
BeginCommandBuffer();
|
||||||
|
auto& frame = *g.frames[g.currentFrame];
|
||||||
|
Bool wasRecording = g.recording;
|
||||||
|
if (g.recording) {
|
||||||
|
vkCmdEndRenderPass(frame.CommandBuffer);
|
||||||
|
g.recording = false;
|
||||||
|
g.recordingRenderPass = VK_NULL_HANDLE;
|
||||||
|
g.recordingFramebuffer = VK_NULL_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
record(frame.CommandBuffer);
|
||||||
|
|
||||||
|
if (wasRecording && restartIfRecording) {
|
||||||
|
if (g.activeIsDefault) {
|
||||||
|
g.activeRenderPass = GetOrCreateDefaultRenderPass(0);
|
||||||
|
} else if (g.activeBackendFBO) {
|
||||||
|
g.activeRenderPass = GetOrCreateBackendRenderPass(*g.activeBackendFBO, 0);
|
||||||
|
}
|
||||||
|
if (g.activeRenderPass != VK_NULL_HANDLE && g.activeFramebuffer != VK_NULL_HANDLE) {
|
||||||
|
BeginRenderPass(0, nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void EnsureImageLayout(VkImage image, VkImageLayout& currentLayout, VkImageLayout desiredLayout,
|
void EnsureImageLayout(VkImage image, VkImageLayout& currentLayout, VkImageLayout desiredLayout,
|
||||||
VkImageAspectFlags aspect) {
|
VkImageAspectFlags aspect) {
|
||||||
if (currentLayout == desiredLayout) return;
|
if (currentLayout == desiredLayout) return;
|
||||||
SubmitImmediate(
|
RecordTransferCommands(
|
||||||
[&](VkCommandBuffer cmd) { CmdTransitionImageLayout(cmd, image, currentLayout, desiredLayout, aspect); });
|
[&](VkCommandBuffer cmd) { CmdTransitionImageLayout(cmd, image, currentLayout, desiredLayout, aspect); },
|
||||||
|
false);
|
||||||
currentLayout = desiredLayout;
|
currentLayout = desiredLayout;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -850,33 +909,34 @@ namespace MobileGL::MG_Backend::DirectVulkanTMP::TmpImpl {
|
|||||||
void* data = mip->MapMipmapData(target, 0);
|
void* data = mip->MapMipmapData(target, 0);
|
||||||
if (!data) return;
|
if (!data) return;
|
||||||
|
|
||||||
BufferResource staging{};
|
BufferResource staging = AcquireStagingBuffer(size);
|
||||||
CreateBuffer(size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
|
|
||||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, staging);
|
|
||||||
Memcpy(staging.mapped, data, size);
|
Memcpy(staging.mapped, data, size);
|
||||||
|
|
||||||
VkExtent3D extent = res.extent;
|
VkExtent3D extent = res.extent;
|
||||||
VkImageAspectFlags aspect = AspectForFormat(res.format);
|
VkImageAspectFlags aspect = AspectForFormat(res.format);
|
||||||
VkImageLayout sampledLayout = SampledLayoutForFormat(res.format);
|
VkImageLayout sampledLayout = SampledLayoutForFormat(res.format);
|
||||||
|
|
||||||
SubmitImmediate([&](VkCommandBuffer cmd) {
|
RecordTransferCommands(
|
||||||
CmdTransitionImageLayout(cmd, res.image, res.layout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, aspect);
|
[&](VkCommandBuffer cmd) {
|
||||||
|
CmdTransitionImageLayout(cmd, res.image, res.layout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, aspect);
|
||||||
|
|
||||||
VkBufferImageCopy region{};
|
VkBufferImageCopy region{};
|
||||||
region.imageSubresource.aspectMask = aspect;
|
region.imageSubresource.aspectMask = aspect;
|
||||||
region.imageSubresource.mipLevel = 0;
|
region.imageSubresource.mipLevel = 0;
|
||||||
region.imageSubresource.baseArrayLayer = 0;
|
region.imageSubresource.baseArrayLayer = 0;
|
||||||
region.imageSubresource.layerCount = 1;
|
region.imageSubresource.layerCount = 1;
|
||||||
region.imageExtent = extent;
|
region.imageExtent = extent;
|
||||||
vkCmdCopyBufferToImage(cmd, staging.buffer, res.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion);
|
vkCmdCopyBufferToImage(cmd, staging.buffer, res.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1,
|
||||||
|
®ion);
|
||||||
|
|
||||||
CmdTransitionImageLayout(cmd, res.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, sampledLayout, aspect);
|
CmdTransitionImageLayout(cmd, res.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, sampledLayout, aspect);
|
||||||
});
|
},
|
||||||
|
true);
|
||||||
|
|
||||||
res.layout = sampledLayout;
|
res.layout = sampledLayout;
|
||||||
mip->MarkStorageDirty(target, 0, false);
|
mip->MarkStorageDirty(target, 0, false);
|
||||||
|
|
||||||
DestroyBuffer(staging);
|
ReleaseStagingBuffer(staging);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CopyImageMipLevels(const TextureResource& src, TextureResource& dst) {
|
void CopyImageMipLevels(const TextureResource& src, TextureResource& dst) {
|
||||||
@@ -884,25 +944,28 @@ namespace MobileGL::MG_Backend::DirectVulkanTMP::TmpImpl {
|
|||||||
VkImageLayout finalLayout =
|
VkImageLayout finalLayout =
|
||||||
(src.layout != VK_IMAGE_LAYOUT_UNDEFINED) ? src.layout : SampledLayoutForFormat(src.format);
|
(src.layout != VK_IMAGE_LAYOUT_UNDEFINED) ? src.layout : SampledLayoutForFormat(src.format);
|
||||||
|
|
||||||
SubmitImmediate([&](VkCommandBuffer cmd) {
|
RecordTransferCommands(
|
||||||
CmdTransitionImageLayout(cmd, src.image, src.layout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, aspect);
|
[&](VkCommandBuffer cmd) {
|
||||||
CmdTransitionImageLayout(cmd, dst.image, dst.layout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, aspect);
|
CmdTransitionImageLayout(cmd, src.image, src.layout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, aspect);
|
||||||
|
CmdTransitionImageLayout(cmd, dst.image, dst.layout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, aspect);
|
||||||
|
|
||||||
Uint32 copyLevels = std::min(src.mipLevels, dst.mipLevels);
|
Uint32 copyLevels = std::min(src.mipLevels, dst.mipLevels);
|
||||||
for (Uint32 level = 0; level < copyLevels; ++level) {
|
for (Uint32 level = 0; level < copyLevels; ++level) {
|
||||||
VkImageCopy region{};
|
VkImageCopy region{};
|
||||||
region.srcSubresource.aspectMask = aspect;
|
region.srcSubresource.aspectMask = aspect;
|
||||||
region.srcSubresource.mipLevel = level;
|
region.srcSubresource.mipLevel = level;
|
||||||
region.srcSubresource.baseArrayLayer = 0;
|
region.srcSubresource.baseArrayLayer = 0;
|
||||||
region.srcSubresource.layerCount = 1;
|
region.srcSubresource.layerCount = 1;
|
||||||
region.dstSubresource = region.srcSubresource;
|
region.dstSubresource = region.srcSubresource;
|
||||||
region.extent = {std::max(1u, dst.extent.width >> level), std::max(1u, dst.extent.height >> level), 1};
|
region.extent = {std::max(1u, dst.extent.width >> level), std::max(1u, dst.extent.height >> level),
|
||||||
vkCmdCopyImage(cmd, src.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dst.image,
|
1};
|
||||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion);
|
vkCmdCopyImage(cmd, src.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dst.image,
|
||||||
}
|
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion);
|
||||||
|
}
|
||||||
|
|
||||||
CmdTransitionImageLayout(cmd, dst.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, finalLayout, aspect);
|
CmdTransitionImageLayout(cmd, dst.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, finalLayout, aspect);
|
||||||
});
|
},
|
||||||
|
true);
|
||||||
|
|
||||||
dst.layout = finalLayout;
|
dst.layout = finalLayout;
|
||||||
}
|
}
|
||||||
@@ -2365,30 +2428,30 @@ namespace MobileGL::MG_Backend::DirectVulkanTMP::TmpImpl {
|
|||||||
g.defaultTexture.valid = true;
|
g.defaultTexture.valid = true;
|
||||||
|
|
||||||
Uint32 pixel = 0xFFFFFFFFu;
|
Uint32 pixel = 0xFFFFFFFFu;
|
||||||
BufferResource staging{};
|
BufferResource staging = AcquireStagingBuffer(sizeof(pixel));
|
||||||
CreateBuffer(sizeof(pixel), VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
|
|
||||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, staging);
|
|
||||||
Memcpy(staging.mapped, &pixel, sizeof(pixel));
|
Memcpy(staging.mapped, &pixel, sizeof(pixel));
|
||||||
|
|
||||||
SubmitImmediate([&](VkCommandBuffer cmd) {
|
RecordTransferCommands(
|
||||||
CmdTransitionImageLayout(cmd, g.defaultTexture.image, g.defaultTexture.layout,
|
[&](VkCommandBuffer cmd) {
|
||||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_ASPECT_COLOR_BIT);
|
CmdTransitionImageLayout(cmd, g.defaultTexture.image, g.defaultTexture.layout,
|
||||||
|
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_ASPECT_COLOR_BIT);
|
||||||
|
|
||||||
VkBufferImageCopy region{};
|
VkBufferImageCopy region{};
|
||||||
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||||
region.imageSubresource.mipLevel = 0;
|
region.imageSubresource.mipLevel = 0;
|
||||||
region.imageSubresource.baseArrayLayer = 0;
|
region.imageSubresource.baseArrayLayer = 0;
|
||||||
region.imageSubresource.layerCount = 1;
|
region.imageSubresource.layerCount = 1;
|
||||||
region.imageExtent = {1, 1, 1};
|
region.imageExtent = {1, 1, 1};
|
||||||
vkCmdCopyBufferToImage(cmd, staging.buffer, g.defaultTexture.image,
|
vkCmdCopyBufferToImage(cmd, staging.buffer, g.defaultTexture.image,
|
||||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion);
|
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion);
|
||||||
|
|
||||||
CmdTransitionImageLayout(cmd, g.defaultTexture.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
CmdTransitionImageLayout(cmd, g.defaultTexture.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_ASPECT_COLOR_BIT);
|
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_ASPECT_COLOR_BIT);
|
||||||
});
|
},
|
||||||
|
false);
|
||||||
|
|
||||||
g.defaultTexture.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
g.defaultTexture.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||||
DestroyBuffer(staging);
|
ReleaseStagingBuffer(staging);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2431,10 +2494,13 @@ namespace MobileGL::MG_Backend::DirectVulkanTMP::TmpImpl {
|
|||||||
g.depthMemory);
|
g.depthMemory);
|
||||||
g.depthView = CreateImageView(g.depthImage, g.depthFormat, AspectForFormat(g.depthFormat));
|
g.depthView = CreateImageView(g.depthImage, g.depthFormat, AspectForFormat(g.depthFormat));
|
||||||
|
|
||||||
SubmitImmediate([&](VkCommandBuffer cmd) {
|
RecordTransferCommands(
|
||||||
CmdTransitionImageLayout(cmd, g.depthImage, VK_IMAGE_LAYOUT_UNDEFINED,
|
[&](VkCommandBuffer cmd) {
|
||||||
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, AspectForFormat(g.depthFormat));
|
CmdTransitionImageLayout(cmd, g.depthImage, VK_IMAGE_LAYOUT_UNDEFINED,
|
||||||
});
|
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
|
||||||
|
AspectForFormat(g.depthFormat));
|
||||||
|
},
|
||||||
|
false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DestroyRenderPass() {
|
void DestroyRenderPass() {
|
||||||
@@ -2480,6 +2546,12 @@ namespace MobileGL::MG_Backend::DirectVulkanTMP::TmpImpl {
|
|||||||
|
|
||||||
void DestroyFrameResources() {
|
void DestroyFrameResources() {
|
||||||
DestroyDescriptorPools();
|
DestroyDescriptorPools();
|
||||||
|
for (auto& pending : g.stagingPending) {
|
||||||
|
for (auto& buf : pending) {
|
||||||
|
g.stagingPool.push_back(buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g.stagingPending.clear();
|
||||||
for (auto& f : g.frames) {
|
for (auto& f : g.frames) {
|
||||||
if (f) f->Cleanup(*g.ctx);
|
if (f) f->Cleanup(*g.ctx);
|
||||||
}
|
}
|
||||||
@@ -2498,6 +2570,8 @@ namespace MobileGL::MG_Backend::DirectVulkanTMP::TmpImpl {
|
|||||||
}
|
}
|
||||||
g.frameDescriptorPools.clear();
|
g.frameDescriptorPools.clear();
|
||||||
g.frameDescriptorPools.resize(frames);
|
g.frameDescriptorPools.resize(frames);
|
||||||
|
g.stagingPending.clear();
|
||||||
|
g.stagingPending.resize(frames);
|
||||||
g.currentFrame = 0;
|
g.currentFrame = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2514,6 +2588,13 @@ namespace MobileGL::MG_Backend::DirectVulkanTMP::TmpImpl {
|
|||||||
auto& frame = *g.frames[g.currentFrame];
|
auto& frame = *g.frames[g.currentFrame];
|
||||||
VK_VERIFY(vkWaitForFences(g.ctx->GetDevice(), 1, &frame.InFlightFence, VK_TRUE, UINT64_MAX), "vkWaitForFences");
|
VK_VERIFY(vkWaitForFences(g.ctx->GetDevice(), 1, &frame.InFlightFence, VK_TRUE, UINT64_MAX), "vkWaitForFences");
|
||||||
VK_VERIFY(vkResetFences(g.ctx->GetDevice(), 1, &frame.InFlightFence), "vkResetFences");
|
VK_VERIFY(vkResetFences(g.ctx->GetDevice(), 1, &frame.InFlightFence), "vkResetFences");
|
||||||
|
if (!g.stagingPending.empty()) {
|
||||||
|
auto& pending = g.stagingPending[g.currentFrame];
|
||||||
|
for (auto& buf : pending) {
|
||||||
|
g.stagingPool.push_back(buf);
|
||||||
|
}
|
||||||
|
pending.clear();
|
||||||
|
}
|
||||||
ClearFrameTrashBuffers();
|
ClearFrameTrashBuffers();
|
||||||
ClearFrameTrashImages();
|
ClearFrameTrashImages();
|
||||||
VK_VERIFY(vkResetCommandBuffer(frame.CommandBuffer, 0), "vkResetCommandBuffer");
|
VK_VERIFY(vkResetCommandBuffer(frame.CommandBuffer, 0), "vkResetCommandBuffer");
|
||||||
|
|||||||
@@ -179,6 +179,8 @@ namespace MobileGL::MG_Backend::DirectVulkanTMP::TmpImpl {
|
|||||||
VkCommandPool commandPool = VK_NULL_HANDLE;
|
VkCommandPool commandPool = VK_NULL_HANDLE;
|
||||||
Vector<UniquePtr<DV::FrameContext>> frames;
|
Vector<UniquePtr<DV::FrameContext>> frames;
|
||||||
Vector<FrameDescriptorPools> frameDescriptorPools;
|
Vector<FrameDescriptorPools> frameDescriptorPools;
|
||||||
|
Vector<BufferResource> stagingPool;
|
||||||
|
Vector<Vector<BufferResource>> stagingPending;
|
||||||
Uint32 currentFrame = 0;
|
Uint32 currentFrame = 0;
|
||||||
Uint32 maxFramesInFlight = 2;
|
Uint32 maxFramesInFlight = 2;
|
||||||
Bool initialized = false;
|
Bool initialized = false;
|
||||||
|
|||||||
Reference in New Issue
Block a user