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

Commit a12501b

Browse files
committed
[Impeller] Change texture upload pipeline in Vulkan
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 b4fd07f commit a12501b

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
@@ -114,7 +114,8 @@ std::shared_ptr<Texture> AllocatorVK::OnCreateTexture(
114114
image_create_info.tiling = vk::ImageTiling::eOptimal;
115115
image_create_info.initialLayout = vk::ImageLayout::eUndefined;
116116
image_create_info.usage = vk::ImageUsageFlagBits::eSampled |
117-
vk::ImageUsageFlagBits::eColorAttachment;
117+
vk::ImageUsageFlagBits::eColorAttachment |
118+
vk::ImageUsageFlagBits::eTransferDst;
118119

119120
VmaAllocationCreateInfo alloc_create_info = {};
120121
alloc_create_info.usage = VMA_MEMORY_USAGE_AUTO;
@@ -154,14 +155,20 @@ std::shared_ptr<Texture> AllocatorVK::OnCreateTexture(
154155
}
155156

156157
auto image_view = static_cast<vk::ImageView::NativeType>(img_view_res.value);
158+
auto staging_buffer =
159+
CreateHostVisibleDeviceAllocation(desc.GetByteSizeOfBaseMipLevel());
157160

158161
auto texture_info = std::make_unique<TextureInfoVK>(TextureInfoVK{
159162
.backing_type = TextureBackingTypeVK::kAllocatedTexture,
160163
.allocated_texture =
161164
{
162-
.allocator = &allocator_,
163-
.allocation = allocation,
164-
.allocation_info = allocation_info,
165+
.staging_buffer = staging_buffer,
166+
.backing_allocation =
167+
{
168+
.allocator = &allocator_,
169+
.allocation = allocation,
170+
.allocation_info = allocation_info,
171+
},
165172
.image = img,
166173
.image_view = image_view,
167174
},
@@ -174,14 +181,22 @@ std::shared_ptr<DeviceBuffer> AllocatorVK::OnCreateBuffer(
174181
const DeviceBufferDescriptor& desc) {
175182
// TODO (kaushikiska): consider optimizing the usage flags based on
176183
// StorageMode.
184+
auto device_allocation = std::make_unique<DeviceBufferAllocationVK>(
185+
CreateHostVisibleDeviceAllocation(desc.size));
186+
return std::make_shared<DeviceBufferVK>(desc, context_,
187+
std::move(device_allocation));
188+
}
189+
190+
DeviceBufferAllocationVK AllocatorVK::CreateHostVisibleDeviceAllocation(
191+
size_t size) {
177192
auto buffer_create_info = static_cast<vk::BufferCreateInfo::NativeType>(
178193
vk::BufferCreateInfo()
179194
.setUsage(vk::BufferUsageFlagBits::eVertexBuffer |
180195
vk::BufferUsageFlagBits::eIndexBuffer |
181196
vk::BufferUsageFlagBits::eUniformBuffer |
182197
vk::BufferUsageFlagBits::eTransferSrc |
183198
vk::BufferUsageFlagBits::eTransferDst)
184-
.setSize(desc.size)
199+
.setSize(size)
185200
.setSharingMode(vk::SharingMode::eExclusive));
186201

187202
VmaAllocationCreateInfo allocCreateInfo = {};
@@ -197,15 +212,27 @@ std::shared_ptr<DeviceBuffer> AllocatorVK::OnCreateBuffer(
197212
&buffer, &buffer_allocation, &buffer_allocation_info)};
198213

199214
if (result != vk::Result::eSuccess) {
200-
VALIDATION_LOG << "Unable to allocate a device buffer";
201-
return nullptr;
215+
VALIDATION_LOG << "Unable to allocate a device buffer: "
216+
<< vk::to_string(result);
217+
return {};
202218
}
203219

204-
auto device_allocation = std::make_unique<DeviceBufferAllocationVK>(
205-
allocator_, buffer, buffer_allocation, buffer_allocation_info);
220+
VkMemoryPropertyFlags memory_props;
221+
vmaGetAllocationMemoryProperties(allocator_, buffer_allocation,
222+
&memory_props);
223+
if (!(memory_props & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)) {
224+
VALIDATION_LOG << "Unable to create host visible device buffer.";
225+
}
206226

207-
return std::make_shared<DeviceBufferVK>(desc, context_,
208-
std::move(device_allocation));
227+
return DeviceBufferAllocationVK{
228+
.buffer = vk::Buffer{buffer},
229+
.backing_allocation =
230+
{
231+
.allocator = &allocator_,
232+
.allocation = buffer_allocation,
233+
.allocation_info = buffer_allocation_info,
234+
},
235+
};
209236
}
210237

211238
// |Allocator|
@@ -215,4 +242,5 @@ ISize AllocatorVK::GetMaxTextureSizeSupported() const {
215242
// https://registry.khronos.org/vulkan/specs/1.2-extensions/html/vkspec.html#limits-minmax
216243
return {4096, 4096};
217244
}
245+
218246
} // 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)