Skip to content

Commit 1d38f15

Browse files
committed
GPU Vulkan Backend: Write UBOs and map/unmap/recreate buffers only on demand
1 parent 1ba4cb4 commit 1d38f15

2 files changed

Lines changed: 66 additions & 56 deletions

File tree

‎GPU/GPUTracking/display/backend/GPUDisplayBackendVulkan.cxx‎

Lines changed: 61 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -639,16 +639,14 @@ void GPUDisplayBackendVulkan::createUniformLayoutsAndBuffers()
639639
{
640640
for (int32_t j = 0; j < 3; j++) {
641641
mUniformBuffersMat[j].resize(mImageCount);
642-
mUniformBuffersCol[j].resize(mImageCount);
643642
for (uint32_t i = 0; i < mImageCount; i++) {
644643
mUniformBuffersMat[j][i] = createBuffer(sizeof(hmm_mat4), nullptr, vk::BufferUsageFlagBits::eUniformBuffer, mDisplay->cfg().vulkan.uniformBuffersInDeviceMemory ? 2 : 0);
645-
mUniformBuffersCol[j][i] = createBuffer(sizeof(float) * 4, nullptr, vk::BufferUsageFlagBits::eUniformBuffer, mDisplay->cfg().vulkan.uniformBuffersInDeviceMemory ? 2 : 0);
646644
}
647645
}
648646

649647
std::array<vk::DescriptorPoolSize, 2> poolSizes{};
650648
poolSizes[0].type = vk::DescriptorType::eUniformBuffer;
651-
poolSizes[0].descriptorCount = (uint32_t)mImageCount * (2 * 3);
649+
poolSizes[0].descriptorCount = (uint32_t)mImageCount * 3;
652650
poolSizes[1].type = vk::DescriptorType::eCombinedImageSampler;
653651
poolSizes[1].descriptorCount = (uint32_t)mImageCount * 2;
654652
vk::DescriptorPoolCreateInfo poolInfo{};
@@ -662,21 +660,18 @@ void GPUDisplayBackendVulkan::createUniformLayoutsAndBuffers()
662660
uboLayoutBindingMat.descriptorType = vk::DescriptorType::eUniformBuffer;
663661
uboLayoutBindingMat.descriptorCount = 1;
664662
uboLayoutBindingMat.stageFlags = vk::ShaderStageFlagBits::eVertex;
665-
vk::DescriptorSetLayoutBinding uboLayoutBindingCol = uboLayoutBindingMat;
666-
uboLayoutBindingCol.binding = 1;
667-
uboLayoutBindingCol.stageFlags = vk::ShaderStageFlagBits::eFragment;
668663
vk::DescriptorSetLayoutBinding samplerLayoutBinding{};
669664
samplerLayoutBinding.binding = 2;
670665
samplerLayoutBinding.descriptorCount = 1;
671666
samplerLayoutBinding.descriptorType = vk::DescriptorType::eCombinedImageSampler;
672667
samplerLayoutBinding.stageFlags = vk::ShaderStageFlagBits::eFragment;
673-
vk::DescriptorSetLayoutBinding bindings[3] = {uboLayoutBindingMat, uboLayoutBindingCol, samplerLayoutBinding};
668+
vk::DescriptorSetLayoutBinding bindings[2] = {uboLayoutBindingMat, samplerLayoutBinding};
674669

675670
vk::DescriptorSetLayoutCreateInfo layoutInfo{};
676-
layoutInfo.bindingCount = 2;
671+
layoutInfo.bindingCount = 1;
677672
layoutInfo.pBindings = bindings;
678673
mUniformDescriptor = mDevice.createDescriptorSetLayout(layoutInfo, nullptr);
679-
layoutInfo.bindingCount = 3;
674+
layoutInfo.bindingCount = 2;
680675
mUniformDescriptorTexture = mDevice.createDescriptorSetLayout(layoutInfo, nullptr);
681676

682677
vk::DescriptorSetAllocateInfo allocInfo{};
@@ -687,27 +682,25 @@ void GPUDisplayBackendVulkan::createUniformLayoutsAndBuffers()
687682
allocInfo.pSetLayouts = layouts.data();
688683
mDescriptorSets[j] = mDevice.allocateDescriptorSets(allocInfo);
689684

690-
for (int32_t k = 0; k < 2; k++) {
691-
auto& mUniformBuffers = k ? mUniformBuffersCol[j] : mUniformBuffersMat[j];
692-
for (uint32_t i = 0; i < mImageCount; i++) {
693-
vk::DescriptorBufferInfo bufferInfo{};
694-
bufferInfo.buffer = mUniformBuffers[i].buffer;
695-
bufferInfo.offset = 0;
696-
bufferInfo.range = mUniformBuffers[i].size;
697-
698-
vk::WriteDescriptorSet descriptorWrite{};
699-
descriptorWrite.dstSet = mDescriptorSets[j][i];
700-
descriptorWrite.dstBinding = k;
701-
descriptorWrite.dstArrayElement = 0;
702-
descriptorWrite.descriptorType = vk::DescriptorType::eUniformBuffer;
703-
descriptorWrite.descriptorCount = 1;
704-
descriptorWrite.pBufferInfo = &bufferInfo;
705-
descriptorWrite.pImageInfo = nullptr;
706-
descriptorWrite.pTexelBufferView = nullptr;
707-
mDevice.updateDescriptorSets(1, &descriptorWrite, 0, nullptr);
708-
}
685+
for (uint32_t i = 0; i < mImageCount; i++) {
686+
vk::DescriptorBufferInfo bufferInfo{};
687+
bufferInfo.buffer = mUniformBuffersMat[j][i].buffer;
688+
bufferInfo.offset = 0;
689+
bufferInfo.range = mUniformBuffersMat[j][i].size;
690+
691+
vk::WriteDescriptorSet descriptorWrite{};
692+
descriptorWrite.dstSet = mDescriptorSets[j][i];
693+
descriptorWrite.dstBinding = 0;
694+
descriptorWrite.dstArrayElement = 0;
695+
descriptorWrite.descriptorType = vk::DescriptorType::eUniformBuffer;
696+
descriptorWrite.descriptorCount = 1;
697+
descriptorWrite.pBufferInfo = &bufferInfo;
698+
descriptorWrite.pImageInfo = nullptr;
699+
descriptorWrite.pTexelBufferView = nullptr;
700+
mDevice.updateDescriptorSets(1, &descriptorWrite, 0, nullptr);
709701
}
710702
}
703+
mUpdateUBOdrawMatrix = mUpdateUBOtextMatrix = mUpdateUBOmixMatrix = -1;
711704

712705
if (mFontImage.sizex && mFontImage.sizey) {
713706
updateFontTextureDescriptor();
@@ -721,7 +714,6 @@ void GPUDisplayBackendVulkan::clearUniformLayoutsAndBuffers()
721714
mDevice.destroyDescriptorPool(mDescriptorPool, nullptr);
722715
for (int32_t j = 0; j < 3; j++) {
723716
clearVector(mUniformBuffersMat[j], [&](auto& x) { clearBuffer(x); });
724-
clearVector(mUniformBuffersCol[j], [&](auto& x) { clearBuffer(x); });
725717
}
726718
}
727719

@@ -821,6 +813,9 @@ void GPUDisplayBackendVulkan::createSwapChain(bool forScreenshot, bool forMixing
821813
uint32_t oldFramesInFlight = mFramesInFlight, oldImageCount = mImageCount;
822814
mFramesInFlight = mDisplay->cfg().vulkan.nFramesInFlight;
823815
mImageCount = mSwapChainImages.size();
816+
if (mImageCount > sizeof(mUpdateUBOdrawMatrix) * 8) {
817+
throw std::runtime_error("Too many swapchain images");
818+
}
824819

825820
if (mFramesInFlight != oldFramesInFlight || mImageCount != oldImageCount || !mCommandInfrastructureCreated) {
826821
if (mCommandInfrastructureCreated) {
@@ -868,6 +863,7 @@ void GPUDisplayBackendVulkan::recreateRendering(bool forScreenshot, bool forMixi
868863
}
869864
createPipeline();
870865
needRecordCommandBuffers();
866+
mUpdateUBOtextMatrix = mUpdateUBOmixMatrix = -1; // Screen / render size may have changed
871867
}
872868

873869
// ---------------------------- VULKAN OFFSCREEN BUFFERS ----------------------------
@@ -945,6 +941,7 @@ void GPUDisplayBackendVulkan::clearOffscreenBuffers()
945941
clearVector(mDownsampleImages, [&](auto& x) { clearImage(x); });
946942
clearVector(mZImages, [&](auto& x) { clearImage(x); });
947943
clearVector(mMixImages, [&](auto& x) { clearImage(x); });
944+
clearVector(mFontVertexBuffer, [&](auto& x) { if (x.size) { clearBuffer(x); } });
948945
if (mMixingSupported) {
949946
clearBuffer(mMixingTextureVertexArray);
950947
}
@@ -1214,11 +1211,8 @@ void GPUDisplayBackendVulkan::clearShaders()
12141211

12151212
void GPUDisplayBackendVulkan::writeToBuffer(VulkanBuffer& buffer, size_t size, const void* srcData)
12161213
{
1217-
if (buffer.deviceMemory != 1) {
1218-
void* dstData;
1219-
CHKERR(mDevice.mapMemory(buffer.memory, 0, buffer.size, {}, &dstData));
1220-
memcpy(dstData, srcData, size);
1221-
mDevice.unmapMemory(buffer.memory);
1214+
if (buffer.mapped) {
1215+
memcpy(buffer.mapped, srcData, size);
12221216
} else {
12231217
auto tmp = createBuffer(size, srcData, vk::BufferUsageFlagBits::eTransferSrc, 0);
12241218

@@ -1263,6 +1257,9 @@ GPUDisplayBackendVulkan::VulkanBuffer GPUDisplayBackendVulkan::createBuffer(size
12631257

12641258
buffer.size = size;
12651259
buffer.deviceMemory = deviceMemory;
1260+
if (deviceMemory != 1) {
1261+
CHKERR(mDevice.mapMemory(buffer.memory, 0, VK_WHOLE_SIZE, {}, &buffer.mapped));
1262+
}
12661263

12671264
if (srcData != nullptr) {
12681265
writeToBuffer(buffer, size, srcData);
@@ -1274,7 +1271,7 @@ GPUDisplayBackendVulkan::VulkanBuffer GPUDisplayBackendVulkan::createBuffer(size
12741271
void GPUDisplayBackendVulkan::clearBuffer(VulkanBuffer& buffer)
12751272
{
12761273
mDevice.destroyBuffer(buffer.buffer, nullptr);
1277-
mDevice.freeMemory(buffer.memory, nullptr);
1274+
mDevice.freeMemory(buffer.memory, nullptr); // Implicitly unmaps
12781275
}
12791276

12801277
void GPUDisplayBackendVulkan::clearVertexBuffers()
@@ -1287,12 +1284,6 @@ void GPUDisplayBackendVulkan::clearVertexBuffers()
12871284
clearBuffer(mIndirectCommandBuffer);
12881285
mIndirectCommandBuffer.size = 0;
12891286
}
1290-
for (auto& buf : mFontVertexBuffer) {
1291-
if (buf.size) {
1292-
clearBuffer(buf);
1293-
}
1294-
buf.size = 0;
1295-
}
12961287
}
12971288

12981289
// ---------------------------- VULKAN TEXTURES ----------------------------
@@ -1468,7 +1459,15 @@ void GPUDisplayBackendVulkan::prepareDraw(const hmm_mat4& proj, const hmm_mat4&
14681459
mHasDrawnText = false;
14691460
mFrameCommandBuffers.clear();
14701461
const hmm_mat4 modelViewProj = proj * view;
1471-
writeToBuffer(mUniformBuffersMat[0][mCurrentImageIndex], sizeof(modelViewProj), &modelViewProj);
1462+
static_assert(sizeof(modelViewProj) == sizeof(mLastMVP));
1463+
if (memcmp(&modelViewProj, mLastMVP, sizeof(mLastMVP))) {
1464+
memcpy(mLastMVP, &modelViewProj, sizeof(mLastMVP));
1465+
mUpdateUBOdrawMatrix = -1;
1466+
}
1467+
if (mUpdateUBOdrawMatrix & (1u << mCurrentImageIndex)) {
1468+
mUpdateUBOdrawMatrix &= ~(1u << mCurrentImageIndex);
1469+
writeToBuffer(mUniformBuffersMat[0][mCurrentImageIndex], sizeof(mLastMVP), mLastMVP);
1470+
}
14721471
}
14731472

14741473
mCurrentCommandBuffer = toMixBuffer ? mCommandBuffersMix[mCurrentImageIndex] : mCommandBuffers[mCurrentImageIndex];
@@ -1577,9 +1576,11 @@ void GPUDisplayBackendVulkan::downsampleToFramebuffer(vk::CommandBuffer& command
15771576

15781577
void GPUDisplayBackendVulkan::prepareText()
15791578
{
1580-
hmm_mat4 proj = HMM_Orthographic(0.f, mScreenWidth, 0.f, mScreenHeight, -1, 1);
1581-
writeToBuffer(mUniformBuffersMat[1][mCurrentImageIndex], sizeof(proj), &proj);
1582-
1579+
if (mUpdateUBOtextMatrix & (1u << mCurrentImageIndex)) {
1580+
mUpdateUBOtextMatrix &= ~(1u << mCurrentImageIndex);
1581+
hmm_mat4 proj = HMM_Orthographic(0.f, mScreenWidth, 0.f, mScreenHeight, -1, 1);
1582+
writeToBuffer(mUniformBuffersMat[1][mCurrentImageIndex], sizeof(proj), &proj);
1583+
}
15831584
mFontVertexBufferHost.clear();
15841585
mTextDrawCommands.clear();
15851586
}
@@ -1598,15 +1599,20 @@ void GPUDisplayBackendVulkan::finishText()
15981599

15991600
cmdBeginRenderingLoad(mCommandBuffersText[mCurrentImageIndex], mSwapChainImages[mCurrentImageIndex], mSwapChainImageViews[mCurrentImageIndex], mScreenWidth, mScreenHeight);
16001601

1601-
if (mFontVertexBuffer[mCurrentImageIndex].size) {
1602-
clearBuffer(mFontVertexBuffer[mCurrentImageIndex]);
1602+
VulkanBuffer& fontBuffer = mFontVertexBuffer[mCurrentImageIndex];
1603+
const size_t fontBufferSize = mFontVertexBufferHost.size() * sizeof(mFontVertexBufferHost[0]);
1604+
if (fontBuffer.size < fontBufferSize) { // Persistent per image, only grows
1605+
if (fontBuffer.size) {
1606+
clearBuffer(fontBuffer);
1607+
}
1608+
fontBuffer = createBuffer(std::max(fontBufferSize, 2 * fontBuffer.size), nullptr, vk::BufferUsageFlagBits::eVertexBuffer, 0);
16031609
}
1604-
mFontVertexBuffer[mCurrentImageIndex] = createBuffer(mFontVertexBufferHost.size() * sizeof(float), mFontVertexBufferHost.data(), vk::BufferUsageFlagBits::eVertexBuffer, 0);
1610+
writeToBuffer(fontBuffer, fontBufferSize, mFontVertexBufferHost.data());
16051611

16061612
mCommandBuffersText[mCurrentImageIndex].bindPipeline(vk::PipelineBindPoint::eGraphics, mPipelines[3]);
16071613
mCommandBuffersText[mCurrentImageIndex].bindDescriptorSets(vk::PipelineBindPoint::eGraphics, mPipelineLayoutTexture, 0, 1, &mDescriptorSets[1][mCurrentImageIndex], 0, nullptr);
16081614
vk::DeviceSize offsets[] = {0};
1609-
mCommandBuffersText[mCurrentImageIndex].bindVertexBuffers(0, 1, &mFontVertexBuffer[mCurrentImageIndex].buffer, offsets);
1615+
mCommandBuffersText[mCurrentImageIndex].bindVertexBuffers(0, 1, &fontBuffer.buffer, offsets);
16101616

16111617
for (const auto& cmd : mTextDrawCommands) {
16121618
mCommandBuffersText[mCurrentImageIndex].pushConstants(mPipelineLayoutTexture, vk::ShaderStageFlagBits::eFragment, 0, sizeof(cmd.color), cmd.color);
@@ -1621,9 +1627,11 @@ void GPUDisplayBackendVulkan::finishText()
16211627

16221628
void GPUDisplayBackendVulkan::mixImages(vk::CommandBuffer commandBuffer, float mixSlaveImage)
16231629
{
1624-
hmm_mat4 proj = HMM_Orthographic(0.f, mRenderWidth, 0.f, mRenderHeight, -1, 1);
1625-
writeToBuffer(mUniformBuffersMat[2][mCurrentImageIndex], sizeof(proj), &proj);
1626-
1630+
if (mUpdateUBOmixMatrix & (1u << mCurrentImageIndex)) {
1631+
mUpdateUBOmixMatrix &= ~(1u << mCurrentImageIndex);
1632+
hmm_mat4 proj = HMM_Orthographic(0.f, mRenderWidth, 0.f, mRenderHeight, -1, 1);
1633+
writeToBuffer(mUniformBuffersMat[2][mCurrentImageIndex], sizeof(proj), &proj);
1634+
}
16271635
commandBuffer.reset({});
16281636
vk::CommandBufferBeginInfo beginInfo{};
16291637
beginInfo.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit;
@@ -1874,8 +1882,7 @@ void GPUDisplayBackendVulkan::readImageToPixels(vk::Image image, vk::ImageLayout
18741882
submitSingleTimeCommandBuffer(cmdBuffer);
18751883

18761884
const bool swapRB = format == vk::Format::eR8G8B8A8Unorm || format == vk::Format::eR8G8B8A8Srgb; // Raw copy in swapchain format, the BMP needs BGRA
1877-
const char* data;
1878-
CHKERR(mDevice.mapMemory(readBuffer.memory, 0, VK_WHOLE_SIZE, {}, (void**)&data));
1885+
const char* data = (const char*)readBuffer.mapped;
18791886
for (uint32_t i = 0; i < height; i++) { // BMP rows are bottom-up
18801887
char* dst = pixels.data() + i * width * bytesPerPixel;
18811888
memcpy(dst, data + (height - i - 1) * width * bytesPerPixel, width * bytesPerPixel);
@@ -1885,7 +1892,6 @@ void GPUDisplayBackendVulkan::readImageToPixels(vk::Image image, vk::ImageLayout
18851892
}
18861893
}
18871894
}
1888-
mDevice.unmapMemory(readBuffer.memory);
18891895
clearBuffer(readBuffer);
18901896
if (mDisplay->cfgR().screenshotScaleFactor != 1) {
18911897
mDevice.freeMemory(dstImageMemory, nullptr);

‎GPU/GPUTracking/display/backend/GPUDisplayBackendVulkan.h‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ class GPUDisplayBackendVulkan : public GPUDisplayBackend
4545
vk::DeviceMemory memory;
4646
size_t size = 0;
4747
int32_t deviceMemory;
48+
void* mapped = nullptr; // Persistent mapping of host-visible buffers (deviceMemory 0 / 2)
4849
};
4950
struct VulkanImage {
5051
vk::Image image;
@@ -186,7 +187,10 @@ class GPUDisplayBackendVulkan : public GPUDisplayBackend
186187
std::vector<uint64_t> mImageTimelineValue; // Per swapchain image: value of the last submit using its resources
187188
std::mutex mQueueMutex; // Queue submissions must be externally synchronized
188189
std::vector<VulkanBuffer> mUniformBuffersMat[3];
189-
std::vector<VulkanBuffer> mUniformBuffersCol[3];
190+
uint32_t mUpdateUBOdrawMatrix = -1; // Bitmask per swapchain image: UBO must be rewritten when the image is drawn next, set to -1 to update all
191+
uint32_t mUpdateUBOtextMatrix = -1;
192+
uint32_t mUpdateUBOmixMatrix = -1;
193+
float mLastMVP[16] = {};
190194
std::vector<vk::DescriptorSet> mDescriptorSets[3];
191195
vk::DescriptorSetLayout mUniformDescriptor;
192196
vk::DescriptorSetLayout mUniformDescriptorTexture;

0 commit comments

Comments
 (0)