mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-11-13 01:29:41 +01:00
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:
4 files changed
+46
-15
No files matched your search
@@ -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, ©_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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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, ©Batch, 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, ©Batch, 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, ©Batch, 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, ©Batch);
|
||||
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;
|
||||
|
||||
|
||||
Reference in new issue
Block a user