Merge pull request #17114 from hrydgard/vulkan-batch-mip-uploads

Vulkan: During texture upload, batch the buffer->image copies to do all the mips at once.
This commit is contained in:
Henrik Rydgård authored and GitHub committed 2023-03-14 10:36:47 +01:00
commit ae3c8136f1
4 files changed
+46 -15

No files matched your search

+16 -3
View File
@@ -142,8 +142,7 @@ bool VulkanTexture::CreateDirect(VkCommandBuffer cmd, int w, int h, int depth, i
return true;
}
// TODO: Batch these.
void VulkanTexture::UploadMip(VkCommandBuffer cmd, int mip, int mipWidth, int mipHeight, int depthLayer, VkBuffer buffer, uint32_t offset, size_t rowLength) {
void VulkanTexture::CopyBufferToMipLevel(VkCommandBuffer cmd, TextureCopyBatch *copyBatch, int mip, int mipWidth, int mipHeight, int depthLayer, VkBuffer buffer, uint32_t offset, size_t rowLength) {
VkBufferImageCopy copy_region{};
copy_region.bufferOffset = offset;
copy_region.bufferRowLength = (uint32_t)rowLength;
@@ -157,7 +156,21 @@ void VulkanTexture::UploadMip(VkCommandBuffer cmd, int mip, int mipWidth, int mi
copy_region.imageSubresource.baseArrayLayer = 0;
copy_region.imageSubresource.layerCount = 1;
vkCmdCopyBufferToImage(cmd, buffer, image_, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &copy_region);
if (!copyBatch->buffer) {
copyBatch->buffer = buffer;
} else if (copyBatch->buffer != buffer) {
// Need to flush the batch if this image isn't from the same buffer as the previous ones.
FinishCopyBatch(cmd, copyBatch);
copyBatch->buffer = buffer;
}
copyBatch->copies.push_back(copy_region);
}
void VulkanTexture::FinishCopyBatch(VkCommandBuffer cmd, TextureCopyBatch *copyBatch) {
if (!copyBatch->copies.empty()) {
vkCmdCopyBufferToImage(cmd, copyBatch->buffer, image_, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, (uint32_t)copyBatch->copies.size(), copyBatch->copies.data());
copyBatch->copies.clear();
}
}
void VulkanTexture::ClearMip(VkCommandBuffer cmd, int mip, uint32_t value) {
+10 -1
View File
@@ -7,6 +7,13 @@ class VulkanDeviceAllocator;
VK_DEFINE_HANDLE(VmaAllocation);
struct TextureCopyBatch {
std::vector<VkBufferImageCopy> copies;
VkBuffer buffer = VK_NULL_HANDLE;
void reserve(size_t mips) { copies.reserve(mips); }
bool empty() const { return copies.empty(); }
};
// Wrapper around what you need to use a texture.
// ALWAYS use an allocator when calling CreateDirect.
class VulkanTexture {
@@ -23,7 +30,9 @@ public:
void ClearMip(VkCommandBuffer cmd, int mip, uint32_t value);
// Can also be used to copy individual levels of a 3D texture.
void UploadMip(VkCommandBuffer cmd, int mip, int mipWidth, int mipHeight, int depthLayer, VkBuffer buffer, uint32_t offset, size_t rowLength); // rowLength is in pixels
// If possible, will just add to the batch instead of submitting a copy.
void CopyBufferToMipLevel(VkCommandBuffer cmd, TextureCopyBatch *copyBatch, int mip, int mipWidth, int mipHeight, int depthLayer, VkBuffer buffer, uint32_t offset, size_t rowLength); // rowLength is in pixels
void FinishCopyBatch(VkCommandBuffer cmd, TextureCopyBatch *copyBatch);
void GenerateMips(VkCommandBuffer cmd, int firstMipToGenerate, bool fromCompute);
void EndCreate(VkCommandBuffer cmd, bool vertexTexture, VkPipelineStageFlags prevStage, VkImageLayout layout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
+6 -2
View File
@@ -682,7 +682,9 @@ VulkanTexture *VKContext::GetNullTexture() {
data[y*w + x] = 0; // black
}
}
nullTexture_->UploadMip(cmdInit, 0, w, h, 0, bindBuf, bindOffset, w);
TextureCopyBatch batch;
nullTexture_->CopyBufferToMipLevel(cmdInit, &batch, 0, w, h, 0, bindBuf, bindOffset, w);
nullTexture_->FinishCopyBatch(cmdInit, &batch);
nullTexture_->EndCreate(cmdInit, false, VK_PIPELINE_STAGE_TRANSFER_BIT);
}
return nullTexture_;
@@ -784,7 +786,9 @@ bool VKTexture::Create(VkCommandBuffer cmd, VulkanPushBuffer *push, const Textur
} else {
offset = push->PushAligned((const void *)desc.initData[i], size, 16, &buf);
}
vkTex_->UploadMip(cmd, i, w, h, 0, buf, offset, w);
TextureCopyBatch batch;
vkTex_->CopyBufferToMipLevel(cmd, &batch, i, w, h, 0, buf, offset, w);
vkTex_->FinishCopyBatch(cmd, &batch);
w = (w + 1) / 2;
h = (h + 1) / 2;
d = (d + 1) / 2;
+14 -9
View File
@@ -545,6 +545,10 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) {
VulkanPushBuffer *pushBuffer = drawEngine_->GetPushBufferForTextureData();
// Batch the copies.
TextureCopyBatch copyBatch;
copyBatch.reserve(levels);
for (int i = 0; i < levels; i++) {
int mipUnscaledWidth = gstate.getTextureWidth(i);
int mipUnscaledHeight = gstate.getTextureHeight(i);
@@ -595,15 +599,12 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) {
// TODO: Fill with some pattern?
}
replacementTimeThisFrame_ += time_now_d() - replaceStart;
VK_PROFILE_BEGIN(vulkan, cmdInit, VK_PIPELINE_STAGE_TRANSFER_BIT,
"Copy Upload (replaced): %dx%d", mipWidth, mipHeight);
entry->vkTex->UploadMip(cmdInit, i, mipWidth, mipHeight, 0, texBuf, bufferOffset, rowLength);
VK_PROFILE_END(vulkan, cmdInit, VK_PIPELINE_STAGE_TRANSFER_BIT);
entry->vkTex->CopyBufferToMipLevel(cmdInit, &copyBatch, i, mipWidth, mipHeight, 0, texBuf, bufferOffset, rowLength);
} else {
if (plan.depth != 1) {
// 3D texturing.
loadLevel(uploadSize, i, byteStride, plan.scaleFactor);
entry->vkTex->UploadMip(cmdInit, 0, mipWidth, mipHeight, i, texBuf, bufferOffset, pixelStride);
entry->vkTex->CopyBufferToMipLevel(cmdInit, &copyBatch, 0, mipWidth, mipHeight, i, texBuf, bufferOffset, pixelStride);
} else if (computeUpload) {
int srcBpp = VkFormatBytesPerPixel(dstFmt);
int srcStride = mipUnscaledWidth * srcBpp;
@@ -625,10 +626,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) {
vulkan->Delete().QueueDeleteImageView(view);
} else {
loadLevel(uploadSize, i == 0 ? plan.baseLevelSrc : i, byteStride, plan.scaleFactor);
VK_PROFILE_BEGIN(vulkan, cmdInit, VK_PIPELINE_STAGE_TRANSFER_BIT,
"Copy Upload: %dx%d", mipWidth, mipHeight);
entry->vkTex->UploadMip(cmdInit, i, mipWidth, mipHeight, 0, texBuf, bufferOffset, pixelStride);
VK_PROFILE_END(vulkan, cmdInit, VK_PIPELINE_STAGE_TRANSFER_BIT);
entry->vkTex->CopyBufferToMipLevel(cmdInit, &copyBatch, i, mipWidth, mipHeight, 0, texBuf, bufferOffset, pixelStride);
}
// Format might be wrong in lowMemoryMode_, so don't save.
if (plan.saveTexture && !lowMemoryMode_) {
@@ -650,6 +648,13 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) {
}
}
if (!copyBatch.empty()) {
VK_PROFILE_BEGIN(vulkan, cmdInit, VK_PIPELINE_STAGE_TRANSFER_BIT, "Copy Upload");
// Submit the whole batch of mip uploads.
entry->vkTex->FinishCopyBatch(cmdInit, &copyBatch);
VK_PROFILE_END(vulkan, cmdInit, VK_PIPELINE_STAGE_TRANSFER_BIT);
}
VkImageLayout layout = computeUpload ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
VkPipelineStageFlags prevStage = computeUpload ? VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT : VK_PIPELINE_STAGE_TRANSFER_BIT;