Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 44e2f58

Browse files
authored
[Impeller] Change texture upload pipeline in Vulkan (#37623)
Upload to staging buffer and then copy the buffer to texture, this ensure that tiling is respected and the image is shown as intended, without this change the image would be shown as random chunks.
1 parent a76ec91 commit 44e2f58

File tree

8 files changed

+159
-64
lines changed

8 files changed

+159
-64
lines changed

impeller/renderer/backend/vulkan/allocator_vk.cc

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,8 @@ std::shared_ptr<Texture> AllocatorVK::OnCreateTexture(
115115
image_create_info.tiling = vk::ImageTiling::eOptimal;
116116
image_create_info.initialLayout = vk::ImageLayout::eUndefined;
117117
image_create_info.usage = vk::ImageUsageFlagBits::eSampled |
118-
vk::ImageUsageFlagBits::eColorAttachment;
118+
vk::ImageUsageFlagBits::eColorAttachment |
119+
vk::ImageUsageFlagBits::eTransferDst;
119120

120121
VmaAllocationCreateInfo alloc_create_info = {};
121122
alloc_create_info.usage = VMA_MEMORY_USAGE_AUTO;
@@ -164,14 +165,20 @@ std::shared_ptr<Texture> AllocatorVK::OnCreateTexture(
164165
}
165166

166167
auto image_view = static_cast<vk::ImageView::NativeType>(img_view_res.value);
168+
auto staging_buffer =
169+
CreateHostVisibleDeviceAllocation(desc.GetByteSizeOfBaseMipLevel());
167170

168171
auto texture_info = std::make_unique<TextureInfoVK>(TextureInfoVK{
169172
.backing_type = TextureBackingTypeVK::kAllocatedTexture,
170173
.allocated_texture =
171174
{
172-
.allocator = &allocator_,
173-
.allocation = allocation,
174-
.allocation_info = allocation_info,
175+
.staging_buffer = staging_buffer,
176+
.backing_allocation =
177+
{
178+
.allocator = &allocator_,
179+
.allocation = allocation,
180+
.allocation_info = allocation_info,
181+
},
175182
.image = img,
176183
.image_view = image_view,
177184
},
@@ -184,14 +191,22 @@ std::shared_ptr<DeviceBuffer> AllocatorVK::OnCreateBuffer(
184191
const DeviceBufferDescriptor& desc) {
185192
// TODO (kaushikiska): consider optimizing the usage flags based on
186193
// StorageMode.
194+
auto device_allocation = std::make_unique<DeviceBufferAllocationVK>(
195+
CreateHostVisibleDeviceAllocation(desc.size));
196+
return std::make_shared<DeviceBufferVK>(desc, context_,
197+
std::move(device_allocation));
198+
}
199+
200+
DeviceBufferAllocationVK AllocatorVK::CreateHostVisibleDeviceAllocation(
201+
size_t size) {
187202
auto buffer_create_info = static_cast<vk::BufferCreateInfo::NativeType>(
188203
vk::BufferCreateInfo()
189204
.setUsage(vk::BufferUsageFlagBits::eVertexBuffer |
190205
vk::BufferUsageFlagBits::eIndexBuffer |
191206
vk::BufferUsageFlagBits::eUniformBuffer |
192207
vk::BufferUsageFlagBits::eTransferSrc |
193208
vk::BufferUsageFlagBits::eTransferDst)
194-
.setSize(desc.size)
209+
.setSize(size)
195210
.setSharingMode(vk::SharingMode::eExclusive));
196211

197212
VmaAllocationCreateInfo allocCreateInfo = {};
@@ -207,15 +222,27 @@ std::shared_ptr<DeviceBuffer> AllocatorVK::OnCreateBuffer(
207222
&buffer, &buffer_allocation, &buffer_allocation_info)};
208223

209224
if (result != vk::Result::eSuccess) {
210-
VALIDATION_LOG << "Unable to allocate a device buffer";
211-
return nullptr;
225+
VALIDATION_LOG << "Unable to allocate a device buffer: "
226+
<< vk::to_string(result);
227+
return {};
212228
}
213229

214-
auto device_allocation = std::make_unique<DeviceBufferAllocationVK>(
215-
allocator_, buffer, buffer_allocation, buffer_allocation_info);
230+
VkMemoryPropertyFlags memory_props;
231+
vmaGetAllocationMemoryProperties(allocator_, buffer_allocation,
232+
&memory_props);
233+
if (!(memory_props & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)) {
234+
VALIDATION_LOG << "Unable to create host visible device buffer.";
235+
}
216236

217-
return std::make_shared<DeviceBufferVK>(desc, context_,
218-
std::move(device_allocation));
237+
return DeviceBufferAllocationVK{
238+
.buffer = vk::Buffer{buffer},
239+
.backing_allocation =
240+
{
241+
.allocator = &allocator_,
242+
.allocation = buffer_allocation,
243+
.allocation_info = buffer_allocation_info,
244+
},
245+
};
219246
}
220247

221248
// |Allocator|
@@ -225,4 +252,5 @@ ISize AllocatorVK::GetMaxTextureSizeSupported() const {
225252
// https://registry.khronos.org/vulkan/specs/1.2-extensions/html/vkspec.html#limits-minmax
226253
return {4096, 4096};
227254
}
255+
228256
} // namespace impeller

impeller/renderer/backend/vulkan/allocator_vk.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "flutter/vulkan/procs/vulkan_proc_table.h"
1010
#include "impeller/renderer/allocator.h"
1111
#include "impeller/renderer/backend/vulkan/context_vk.h"
12+
#include "impeller/renderer/backend/vulkan/device_buffer_vk.h"
1213
#include "impeller/renderer/backend/vulkan/vk.h"
1314

1415
#include <memory>
@@ -51,6 +52,8 @@ class AllocatorVK final : public Allocator {
5152
// |Allocator|
5253
ISize GetMaxTextureSizeSupported() const override;
5354

55+
DeviceBufferAllocationVK CreateHostVisibleDeviceAllocation(size_t size);
56+
5457
FML_DISALLOW_COPY_AND_ASSIGN(AllocatorVK);
5558
};
5659

impeller/renderer/backend/vulkan/device_buffer_vk.cc

Lines changed: 3 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,30 +9,12 @@
99

1010
namespace impeller {
1111

12-
DeviceBufferAllocationVK::DeviceBufferAllocationVK(
13-
const VmaAllocator& allocator,
14-
VkBuffer buffer,
15-
VmaAllocation allocation,
16-
VmaAllocationInfo allocation_info)
17-
: allocator_(allocator),
18-
buffer_(buffer),
19-
allocation_(allocation),
20-
allocation_info_(allocation_info) {}
21-
22-
DeviceBufferAllocationVK::~DeviceBufferAllocationVK() {
23-
if (buffer_) {
24-
// https://github.com/flutter/flutter/issues/112387
25-
// This buffer can be freed once the command buffer is disposed.
26-
// vmaDestroyBuffer(allocator_, buffer_, allocation_);
27-
}
12+
void* DeviceBufferAllocationVK::GetMapping() const {
13+
return backing_allocation.allocation_info.pMappedData;
2814
}
2915

3016
vk::Buffer DeviceBufferAllocationVK::GetBufferHandle() const {
31-
return buffer_;
32-
}
33-
34-
void* DeviceBufferAllocationVK::GetMapping() const {
35-
return allocation_info_.pMappedData;
17+
return buffer;
3618
}
3719

3820
DeviceBufferVK::DeviceBufferVK(

impeller/renderer/backend/vulkan/device_buffer_vk.h

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,26 +13,22 @@
1313

1414
namespace impeller {
1515

16-
class DeviceBufferAllocationVK {
17-
public:
18-
DeviceBufferAllocationVK(const VmaAllocator& allocator,
19-
VkBuffer buffer,
20-
VmaAllocation allocation,
21-
VmaAllocationInfo allocation_info);
22-
23-
~DeviceBufferAllocationVK();
16+
// https://github.com/flutter/flutter/issues/112387
17+
// This buffer can be freed once the command buffer is disposed.
18+
// vmaDestroyBuffer(allocator_, buffer_, allocation_);
19+
struct BackingAllocationVK {
20+
VmaAllocator* allocator = nullptr;
21+
VmaAllocation allocation = nullptr;
22+
VmaAllocationInfo allocation_info = {};
23+
};
2424

25-
vk::Buffer GetBufferHandle() const;
25+
struct DeviceBufferAllocationVK {
26+
vk::Buffer buffer = VK_NULL_HANDLE;
27+
BackingAllocationVK backing_allocation = {};
2628

2729
void* GetMapping() const;
2830

29-
private:
30-
const VmaAllocator& allocator_;
31-
vk::Buffer buffer_;
32-
VmaAllocation allocation_;
33-
VmaAllocationInfo allocation_info_;
34-
35-
FML_DISALLOW_COPY_AND_ASSIGN(DeviceBufferAllocationVK);
31+
vk::Buffer GetBufferHandle() const;
3632
};
3733

3834
class DeviceBufferVK final : public DeviceBuffer,

impeller/renderer/backend/vulkan/render_pass_vk.cc

Lines changed: 79 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,12 @@ bool RenderPassVK::OnEncodeCommands(const Context& context) const {
125125
}
126126
}
127127

128+
if (!TransitionImageLayout(frame_num, tex_info.swapchain_image->GetImage(),
129+
vk::ImageLayout::eUndefined,
130+
vk::ImageLayout::eColorAttachmentOptimal)) {
131+
return false;
132+
}
133+
128134
command_buffer_->endRenderPass();
129135

130136
return const_cast<RenderPassVK*>(this)->EndCommandBuffer(frame_num);
@@ -312,12 +318,20 @@ bool RenderPassVK::UpdateDescriptorSets(uint32_t frame_num,
312318

313319
if (!TransitionImageLayout(frame_num, texture_vk.GetImage(),
314320
vk::ImageLayout::eUndefined,
315-
vk::ImageLayout::eGeneral)) {
321+
vk::ImageLayout::eTransferDstOptimal)) {
322+
return false;
323+
}
324+
325+
CopyBufferToImage(frame_num, texture_vk);
326+
327+
if (!TransitionImageLayout(frame_num, texture_vk.GetImage(),
328+
vk::ImageLayout::eTransferDstOptimal,
329+
vk::ImageLayout::eShaderReadOnlyOptimal)) {
316330
return false;
317331
}
318332

319333
vk::DescriptorImageInfo desc_image_info;
320-
desc_image_info.setImageLayout(vk::ImageLayout::eGeneral);
334+
desc_image_info.setImageLayout(vk::ImageLayout::eShaderReadOnlyOptimal);
321335
desc_image_info.setSampler(sampler_vk.GetSamplerVK());
322336
desc_image_info.setImageView(texture_vk.GetImageView());
323337
image_infos.push_back(desc_image_info);
@@ -404,8 +418,10 @@ bool RenderPassVK::TransitionImageLayout(uint32_t frame_num,
404418

405419
vk::ImageMemoryBarrier barrier =
406420
vk::ImageMemoryBarrier()
407-
.setSrcAccessMask(vk::AccessFlagBits::eColorAttachmentRead)
408-
.setDstAccessMask(vk::AccessFlagBits::eColorAttachmentWrite)
421+
.setSrcAccessMask(vk::AccessFlagBits::eColorAttachmentWrite |
422+
vk::AccessFlagBits::eTransferWrite)
423+
.setDstAccessMask(vk::AccessFlagBits::eColorAttachmentRead |
424+
vk::AccessFlagBits::eShaderRead)
409425
.setOldLayout(layout_old)
410426
.setNewLayout(layout_new)
411427
.setSrcQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED)
@@ -418,10 +434,9 @@ bool RenderPassVK::TransitionImageLayout(uint32_t frame_num,
418434
.setLevelCount(1)
419435
.setBaseArrayLayer(0)
420436
.setLayerCount(1));
421-
transition_cmd->pipelineBarrier(
422-
vk::PipelineStageFlagBits::eColorAttachmentOutput,
423-
vk::PipelineStageFlagBits::eColorAttachmentOutput, {}, nullptr, nullptr,
424-
barrier);
437+
transition_cmd->pipelineBarrier(vk::PipelineStageFlagBits::eAllGraphics,
438+
vk::PipelineStageFlagBits::eAllGraphics, {},
439+
nullptr, nullptr, barrier);
425440

426441
res = transition_cmd->end();
427442
if (res != vk::Result::eSuccess) {
@@ -433,4 +448,60 @@ bool RenderPassVK::TransitionImageLayout(uint32_t frame_num,
433448
return true;
434449
}
435450

451+
bool RenderPassVK::CopyBufferToImage(uint32_t frame_num,
452+
const TextureVK& texture_vk) const {
453+
auto pool = command_buffer_.getPool();
454+
vk::CommandBufferAllocateInfo alloc_info =
455+
vk::CommandBufferAllocateInfo()
456+
.setCommandPool(pool)
457+
.setLevel(vk::CommandBufferLevel::ePrimary)
458+
.setCommandBufferCount(1);
459+
auto cmd_buf_res = device_.allocateCommandBuffersUnique(alloc_info);
460+
if (cmd_buf_res.result != vk::Result::eSuccess) {
461+
VALIDATION_LOG << "Failed to allocate command buffer: "
462+
<< vk::to_string(cmd_buf_res.result);
463+
return false;
464+
}
465+
466+
auto copy_cmd = std::move(cmd_buf_res.value[0]);
467+
468+
const auto& size = texture_vk.GetTextureDescriptor().size;
469+
470+
// actual copy happens here
471+
vk::BufferImageCopy region =
472+
vk::BufferImageCopy()
473+
.setBufferOffset(0)
474+
.setBufferRowLength(0)
475+
.setBufferImageHeight(0)
476+
.setImageSubresource(
477+
vk::ImageSubresourceLayers()
478+
.setAspectMask(vk::ImageAspectFlagBits::eColor)
479+
.setMipLevel(0)
480+
.setBaseArrayLayer(0)
481+
.setLayerCount(1))
482+
.setImageOffset(vk::Offset3D(0, 0, 0))
483+
.setImageExtent(vk::Extent3D(size.width, size.height, 1));
484+
485+
vk::CommandBufferBeginInfo begin_info;
486+
auto res = copy_cmd->begin(begin_info);
487+
488+
if (res != vk::Result::eSuccess) {
489+
VALIDATION_LOG << "Failed to begin command buffer: " << vk::to_string(res);
490+
return false;
491+
}
492+
493+
copy_cmd->copyBufferToImage(texture_vk.GetStagingBuffer(),
494+
texture_vk.GetImage(),
495+
vk::ImageLayout::eTransferDstOptimal, region);
496+
497+
res = copy_cmd->end();
498+
if (res != vk::Result::eSuccess) {
499+
VALIDATION_LOG << "Failed to end command buffer: " << vk::to_string(res);
500+
return false;
501+
}
502+
503+
surface_producer_->QueueCommandBuffer(frame_num, std::move(copy_cmd));
504+
return true;
505+
}
506+
436507
} // namespace impeller

impeller/renderer/backend/vulkan/render_pass_vk.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,13 @@
44

55
#pragma once
66

7-
#include <vector>
87
#include "flutter/fml/macros.h"
98
#include "impeller/renderer/backend/vulkan/surface_producer_vk.h"
109
#include "impeller/renderer/backend/vulkan/texture_vk.h"
1110
#include "impeller/renderer/backend/vulkan/vk.h"
1211
#include "impeller/renderer/command.h"
1312
#include "impeller/renderer/render_pass.h"
1413
#include "impeller/renderer/render_target.h"
15-
#include "vulkan/vulkan_enums.hpp"
16-
#include "vulkan/vulkan_structs.hpp"
1714

1815
namespace impeller {
1916

@@ -77,6 +74,8 @@ class RenderPassVK final : public RenderPass {
7774
vk::ImageLayout layout_old,
7875
vk::ImageLayout layout_new) const;
7976

77+
bool CopyBufferToImage(uint32_t frame_num, const TextureVK& texture_vk) const;
78+
8079
FML_DISALLOW_COPY_AND_ASSIGN(RenderPassVK);
8180
};
8281

impeller/renderer/backend/vulkan/texture_vk.cc

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ TextureVK::TextureVK(TextureDescriptor desc,
1616
TextureVK::~TextureVK() {
1717
if (!IsWrapped() && IsValid()) {
1818
const auto& texture = texture_info_->allocated_texture;
19-
vmaDestroyImage(*texture.allocator, texture.image, texture.allocation);
19+
vmaDestroyImage(*texture.backing_allocation.allocator, texture.image,
20+
texture.backing_allocation.allocation);
2021
}
2122
}
2223

@@ -45,7 +46,7 @@ bool TextureVK::OnSetContents(const uint8_t* contents,
4546
}
4647

4748
// currently we are only supporting 2d textures, no cube textures etc.
48-
auto mapping = texture_info_->allocated_texture.allocation_info.pMappedData;
49+
auto mapping = texture_info_->allocated_texture.staging_buffer.GetMapping();
4950

5051
if (mapping) {
5152
memcpy(mapping, contents, length);
@@ -107,4 +108,17 @@ vk::Image TextureVK::GetImage() const {
107108
}
108109
}
109110

111+
vk::Buffer TextureVK::GetStagingBuffer() const {
112+
switch (texture_info_->backing_type) {
113+
case TextureBackingTypeVK::kUnknownType:
114+
FML_CHECK(false) << "Unknown texture backing type";
115+
return nullptr;
116+
case TextureBackingTypeVK::kAllocatedTexture:
117+
return texture_info_->allocated_texture.staging_buffer.GetBufferHandle();
118+
case TextureBackingTypeVK::kWrappedTexture:
119+
FML_CHECK(false) << "Wrapped textures do not have staging buffers";
120+
return nullptr;
121+
}
122+
}
123+
110124
} // namespace impeller

0 commit comments

Comments
 (0)