| Flutter Impeller |

blit_pass_vk.cc
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "impeller/renderer/backend/vulkan/blit_pass_vk.h"
6
7 #include "impeller/renderer/backend/vulkan/barrier_vk.h"
8 #include "impeller/renderer/backend/vulkan/command_buffer_vk.h"
9 #include "impeller/renderer/backend/vulkan/command_encoder_vk.h"
10 #include "impeller/renderer/backend/vulkan/texture_vk.h"
11 #include "vulkan/vulkan_core.h"
12 #include "vulkan/vulkan_enums.hpp"
13 #include "vulkan/vulkan_structs.hpp"
14
15 namespace impeller {
16
17 static void InsertImageMemoryBarrier(const vk::CommandBuffer& cmd,
18 const vk::Image& image,
19 vk::AccessFlags src_access_mask,
20 vk::AccessFlags dst_access_mask,
21 vk::ImageLayout old_layout,
22 vk::ImageLayout new_layout,
23 vk::PipelineStageFlags src_stage,
24 vk::PipelineStageFlags dst_stage,
25 uint32_t base_mip_level,
26 uint32_t mip_level_count = 1u) {
27 if (old_layout == new_layout) {
28 return;
29 }
30
31 vk::ImageMemoryBarrier barrier;
32 barrier.srcAccessMask = src_access_mask;
33 barrier.dstAccessMask = dst_access_mask;
34 barrier.oldLayout = old_layout;
35 barrier.newLayout = new_layout;
36 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
37 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
38 barrier.image = image;
39 barrier.subresourceRange.aspectMask = vk::ImageAspectFlagBits::eColor;
40 barrier.subresourceRange.baseMipLevel = base_mip_level;
41 barrier.subresourceRange.levelCount = mip_level_count;
42 barrier.subresourceRange.baseArrayLayer = 0u;
43 barrier.subresourceRange.layerCount = 1u;
44
45 cmd.pipelineBarrier(src_stage, dst_stage, {}, nullptr, nullptr, barrier);
46 }
47
48 BlitPassVK::BlitPassVK(std::shared_ptr<CommandBufferVK> command_buffer)
49 : command_buffer_(std::move(command_buffer)) {}
50
51 BlitPassVK::~BlitPassVK() = default;
52
53 void BlitPassVK::OnSetLabel(std::string label) {
54 if (label.empty()) {
55 return;
56 }
57 label_ = std::move(label);
58 }
59
60 // |BlitPass|
61 bool BlitPassVK::IsValid() const {
62 return true;
63 }
64
65 // |BlitPass|
66 bool BlitPassVK::EncodeCommands(
67 const std::shared_ptr<Allocator>& transients_allocator) const {
68 return true;
69 }
70
71 // |BlitPass|
72 bool BlitPassVK::OnCopyTextureToTextureCommand(
73 std::shared_ptr<Texture> source,
74 std::shared_ptr<Texture> destination,
75 IRect source_region,
76 IPoint destination_origin,
77 std::string label) {
78 auto& encoder = *command_buffer_->GetEncoder();
79 const auto& cmd_buffer = encoder.GetCommandBuffer();
80
81 const auto& src = TextureVK::Cast(*source);
82 const auto& dst = TextureVK::Cast(*destination);
83
84 if (!encoder.Track(source) || !encoder.Track(destination)) {
85 return false;
86 }
87
88 BarrierVK src_barrier;
89 src_barrier.cmd_buffer = cmd_buffer;
90 src_barrier.new_layout = vk::ImageLayout::eTransferSrcOptimal;
91 src_barrier.src_access = vk::AccessFlagBits::eTransferWrite |
92 vk::AccessFlagBits::eShaderWrite |
93 vk::AccessFlagBits::eColorAttachmentWrite;
94 src_barrier.src_stage = vk::PipelineStageFlagBits::eTransfer |
95 vk::PipelineStageFlagBits::eFragmentShader |
96 vk::PipelineStageFlagBits::eColorAttachmentOutput;
97 src_barrier.dst_access = vk::AccessFlagBits::eTransferRead;
98 src_barrier.dst_stage = vk::PipelineStageFlagBits::eTransfer;
99
100 BarrierVK dst_barrier;
101 dst_barrier.cmd_buffer = cmd_buffer;
102 dst_barrier.new_layout = vk::ImageLayout::eTransferDstOptimal;
103 dst_barrier.src_access = {};
104 dst_barrier.src_stage = vk::PipelineStageFlagBits::eTopOfPipe;
105 dst_barrier.dst_access =
106 vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eTransferWrite;
107 dst_barrier.dst_stage = vk::PipelineStageFlagBits::eFragmentShader |
108 vk::PipelineStageFlagBits::eTransfer;
109
110 if (!src.SetLayout(src_barrier) || !dst.SetLayout(dst_barrier)) {
111 VALIDATION_LOG << "Could not complete layout transitions.";
112 return false;
113 }
114
115 vk::ImageCopy image_copy;
116
117 image_copy.setSrcSubresource(
118 vk::ImageSubresourceLayers(vk::ImageAspectFlagBits::eColor, 0, 0, 1));
119 image_copy.setDstSubresource(
120 vk::ImageSubresourceLayers(vk::ImageAspectFlagBits::eColor, 0, 0, 1));
121
122 image_copy.srcOffset =
123 vk::Offset3D(source_region.GetX(), source_region.GetY(), 0);
124 image_copy.dstOffset =
125 vk::Offset3D(destination_origin.x, destination_origin.y, 0);
126 image_copy.extent =
127 vk::Extent3D(source_region.GetWidth(), source_region.GetHeight(), 1);
128
129 // Issue the copy command now that the images are already in the right
130 // layouts.
131 cmd_buffer.copyImage(src.GetImage(), //
132 src_barrier.new_layout, //
133 dst.GetImage(), //
134 dst_barrier.new_layout, //
135 image_copy //
136 );
137
138 // If this is an onscreen texture, do not transition the layout
139 // back to shader read.
140 if (dst.IsSwapchainImage()) {
141 return true;
142 }
143
144 BarrierVK barrier;
145 barrier.cmd_buffer = cmd_buffer;
146 barrier.new_layout = vk::ImageLayout::eShaderReadOnlyOptimal;
147 barrier.src_access = {};
148 barrier.src_stage = vk::PipelineStageFlagBits::eTopOfPipe;
149 barrier.dst_access = vk::AccessFlagBits::eShaderRead;
150 barrier.dst_stage = vk::PipelineStageFlagBits::eFragmentShader;
151
152 return dst.SetLayout(barrier);
153 }
154
155 // |BlitPass|
156 bool BlitPassVK::OnCopyTextureToBufferCommand(
157 std::shared_ptr<Texture> source,
158 std::shared_ptr<DeviceBuffer> destination,
159 IRect source_region,
160 size_t destination_offset,
161 std::string label) {
162 auto& encoder = *command_buffer_->GetEncoder();
163 const auto& cmd_buffer = encoder.GetCommandBuffer();
164
165 // cast source and destination to TextureVK
166 const auto& src = TextureVK::Cast(*source);
167
168 if (!encoder.Track(source) || !encoder.Track(destination)) {
169 return false;
170 }
171
172 BarrierVK barrier;
173 barrier.cmd_buffer = cmd_buffer;
174 barrier.new_layout = vk::ImageLayout::eTransferSrcOptimal;
175 barrier.src_access = vk::AccessFlagBits::eShaderWrite |
176 vk::AccessFlagBits::eTransferWrite |
177 vk::AccessFlagBits::eColorAttachmentWrite;
178 barrier.src_stage = vk::PipelineStageFlagBits::eFragmentShader |
179 vk::PipelineStageFlagBits::eTransfer |
180 vk::PipelineStageFlagBits::eColorAttachmentOutput;
181 barrier.dst_access = vk::AccessFlagBits::eShaderRead;
182 barrier.dst_stage = vk::PipelineStageFlagBits::eVertexShader |
183 vk::PipelineStageFlagBits::eFragmentShader;
184
185 const auto& dst = DeviceBufferVK::Cast(*destination);
186
187 vk::BufferImageCopy image_copy;
188 image_copy.setBufferOffset(destination_offset);
189 image_copy.setBufferRowLength(0);
190 image_copy.setBufferImageHeight(0);
191 image_copy.setImageSubresource(
192 vk::ImageSubresourceLayers(vk::ImageAspectFlagBits::eColor, 0, 0, 1));
193 image_copy.setImageOffset(
194 vk::Offset3D(source_region.GetX(), source_region.GetY(), 0));
195 image_copy.setImageExtent(
196 vk::Extent3D(source_region.GetWidth(), source_region.GetHeight(), 1));
197
198 if (!src.SetLayout(barrier)) {
199 VALIDATION_LOG << "Could not encode layout transition.";
200 return false;
201 }
202
203 cmd_buffer.copyImageToBuffer(src.GetImage(), //
204 barrier.new_layout, //
205 dst.GetBuffer(), //
206 image_copy //
207 );
208
209 // If the buffer is used for readback, then apply a transfer -> host memory
210 // barrier.
211 if (destination->GetDeviceBufferDescriptor().readback) {
212 vk::MemoryBarrier barrier;
213 barrier.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
214 barrier.dstAccessMask = vk::AccessFlagBits::eHostRead;
215
216 cmd_buffer.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
217 vk::PipelineStageFlagBits::eHost, {}, 1,
218 &barrier, 0, {}, 0, {});
219 }
220
221 return true;
222 }
223
224 bool BlitPassVK::ConvertTextureToShaderRead(
225 const std::shared_ptr<Texture>& texture) {
226 auto& encoder = *command_buffer_->GetEncoder();
227 const auto& cmd_buffer = encoder.GetCommandBuffer();
228
229 BarrierVK barrier;
230 barrier.cmd_buffer = cmd_buffer;
231 barrier.src_access = vk::AccessFlagBits::eTransferWrite;
232 barrier.src_stage = vk::PipelineStageFlagBits::eTransfer;
233 barrier.dst_access = vk::AccessFlagBits::eShaderRead;
234 barrier.dst_stage = vk::PipelineStageFlagBits::eFragmentShader;
235
236 barrier.new_layout = vk::ImageLayout::eShaderReadOnlyOptimal;
237
238 const auto& texture_vk = TextureVK::Cast(*texture);
239
240 if (!encoder.Track(texture)) {
241 return false;
242 }
243
244 return texture_vk.SetLayout(barrier);
245 }
246
247 // |BlitPass|
248 bool BlitPassVK::OnCopyBufferToTextureCommand(
249 BufferView source,
250 std::shared_ptr<Texture> destination,
251 IRect destination_region,
252 std::string label,
253 uint32_t slice,
254 bool convert_to_read) {
255 auto& encoder = *command_buffer_->GetEncoder();
256 const auto& cmd_buffer = encoder.GetCommandBuffer();
257
258 // cast destination to TextureVK
259 const auto& dst = TextureVK::Cast(*destination);
260 const auto& src = DeviceBufferVK::Cast(*source.buffer);
261
262 if (!encoder.Track(source.buffer) || !encoder.Track(destination)) {
263 return false;
264 }
265
266 BarrierVK dst_barrier;
267 dst_barrier.cmd_buffer = cmd_buffer;
268 dst_barrier.new_layout = vk::ImageLayout::eTransferDstOptimal;
269 dst_barrier.src_access = {};
270 dst_barrier.src_stage = vk::PipelineStageFlagBits::eTopOfPipe;
271 dst_barrier.dst_access =
272 vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eTransferWrite;
273 dst_barrier.dst_stage = vk::PipelineStageFlagBits::eFragmentShader |
274 vk::PipelineStageFlagBits::eTransfer;
275
276 vk::BufferImageCopy image_copy;
277 image_copy.setBufferOffset(source.range.offset);
278 image_copy.setBufferRowLength(0);
279 image_copy.setBufferImageHeight(0);
280 image_copy.setImageSubresource(
281 vk::ImageSubresourceLayers(vk::ImageAspectFlagBits::eColor, 0, 0, 1));
282 image_copy.imageOffset.x = destination_region.GetX();
283 image_copy.imageOffset.y = destination_region.GetY();
284 image_copy.imageOffset.z = 0u;
285 image_copy.imageExtent.width = destination_region.GetWidth();
286 image_copy.imageExtent.height = destination_region.GetHeight();
287 image_copy.imageExtent.depth = 1u;
288
289 // Note: this barrier should do nothing if we're already in the transfer dst
290 // optimal state. This is important for performance of repeated blit pass
291 // encoding.
292 if (!dst.SetLayout(dst_barrier)) {
293 VALIDATION_LOG << "Could not encode layout transition.";
294 return false;
295 }
296
297 cmd_buffer.copyBufferToImage(src.GetBuffer(), //
298 dst.GetImage(), //
299 dst_barrier.new_layout, //
300 image_copy //
301 );
302
303 // Transition to shader-read.
304 if (convert_to_read) {
305 BarrierVK barrier;
306 barrier.cmd_buffer = cmd_buffer;
307 barrier.src_access = vk::AccessFlagBits::eTransferWrite;
308 barrier.src_stage = vk::PipelineStageFlagBits::eTransfer;
309 barrier.dst_access = vk::AccessFlagBits::eShaderRead;
310 barrier.dst_stage = vk::PipelineStageFlagBits::eFragmentShader;
311
312 barrier.new_layout = vk::ImageLayout::eShaderReadOnlyOptimal;
313
314 if (!dst.SetLayout(barrier)) {
315 return false;
316 }
317 }
318
319 return true;
320 }
321
322 // |BlitPass|
323 bool BlitPassVK::OnGenerateMipmapCommand(std::shared_ptr<Texture> texture,
324 std::string label) {
325 auto& encoder = *command_buffer_->GetEncoder();
326 auto& src = TextureVK::Cast(*texture);
327
328 const auto size = src.GetTextureDescriptor().size;
329 uint32_t mip_count = src.GetTextureDescriptor().mip_count;
330
331 if (mip_count < 2u) {
332 return true;
333 }
334
335 const auto& image = src.GetImage();
336 const auto& cmd = encoder.GetCommandBuffer();
337
338 if (!encoder.Track(texture)) {
339 return false;
340 }
341
342 // Initialize all mip levels to be in TransferDst mode. Later, in a loop,
343 // after writing to that mip level, we'll first switch its layout to
344 // TransferSrc to prepare the mip level after it, use the image as the source
345 // of the blit, before finally switching it to ShaderReadOnly so its available
346 // for sampling in a shader.
347 InsertImageMemoryBarrier(
348 /*cmd=*/cmd,
349 /*image=*/image,
350 /*src_access_mask=*/vk::AccessFlagBits::eTransferWrite |
351 vk::AccessFlagBits::eColorAttachmentWrite,
352 /*dst_access_mask=*/vk::AccessFlagBits::eTransferRead,
353 /*old_layout=*/src.GetLayout(),
354 /*new_layout=*/vk::ImageLayout::eTransferDstOptimal,
355 /*src_stage=*/vk::PipelineStageFlagBits::eTransfer |
356 vk::PipelineStageFlagBits::eColorAttachmentOutput,
357 /*dst_stage=*/vk::PipelineStageFlagBits::eTransfer,
358 /*base_mip_level=*/0u,
359 /*mip_level_count=*/mip_count);
360
361 vk::ImageMemoryBarrier barrier;
362 barrier.image = image;
363 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
364 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
365 barrier.subresourceRange.aspectMask = vk::ImageAspectFlagBits::eColor;
366 barrier.subresourceRange.baseArrayLayer = 0;
367 barrier.subresourceRange.layerCount = 1;
368 barrier.subresourceRange.levelCount = 1;
369
370 // Blit from the mip level N - 1 to mip level N.
371 size_t width = size.width;
372 size_t height = size.height;
373 for (size_t mip_level = 1u; mip_level < mip_count; mip_level++) {
374 barrier.subresourceRange.baseMipLevel = mip_level - 1;
375 barrier.oldLayout = vk::ImageLayout::eTransferDstOptimal;
376 barrier.newLayout = vk::ImageLayout::eTransferSrcOptimal;
377 barrier.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
378 barrier.dstAccessMask = vk::AccessFlagBits::eTransferRead;
379
380 // We just finished writing to the previous (N-1) mip level or it was the
381 // base mip level. These were initialized to TransferDst earler. We are now
382 // going to read from it to write to the current level (N) . So it must be
383 // converted to TransferSrc.
384 cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
385 vk::PipelineStageFlagBits::eTransfer, {}, {}, {},
386 {barrier});
387
388 vk::ImageBlit blit;
389 blit.srcSubresource.aspectMask = vk::ImageAspectFlagBits::eColor;
390 blit.srcSubresource.baseArrayLayer = 0u;
391 blit.srcSubresource.layerCount = 1u;
392 blit.srcSubresource.mipLevel = mip_level - 1;
393
394 blit.dstSubresource.aspectMask = vk::ImageAspectFlagBits::eColor;
395 blit.dstSubresource.baseArrayLayer = 0u;
396 blit.dstSubresource.layerCount = 1u;
397 blit.dstSubresource.mipLevel = mip_level;
398
399 // offsets[0] is origin.
400 blit.srcOffsets[1].x = std::max<int32_t>(width, 1u);
401 blit.srcOffsets[1].y = std::max<int32_t>(height, 1u);
402 blit.srcOffsets[1].z = 1u;
403
404 width = width / 2;
405 height = height / 2;
406
407 // offsets[0] is origin.
408 blit.dstOffsets[1].x = std::max<int32_t>(width, 1u);
409 blit.dstOffsets[1].y = std::max<int32_t>(height, 1u);
410 blit.dstOffsets[1].z = 1u;
411
412 cmd.blitImage(image, // src image
413 vk::ImageLayout::eTransferSrcOptimal, // src layout
414 image, // dst image
415 vk::ImageLayout::eTransferDstOptimal, // dst layout
416 1u, // region count
417 &blit, // regions
418 vk::Filter::eLinear // filter
419 );
420
421 barrier.oldLayout = vk::ImageLayout::eTransferSrcOptimal;
422 barrier.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
423 barrier.srcAccessMask = vk::AccessFlagBits::eTransferRead;
424 barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead;
425
426 // Now that the blit is done, the image at the previous level (N-1)
427 // is done reading from (TransferSrc)/ Now we must prepare it to be read
428 // from a shader (ShaderReadOnly).
429 cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
430 vk::PipelineStageFlagBits::eFragmentShader, {}, {}, {},
431 {barrier});
432 }
433
434 barrier.subresourceRange.baseMipLevel = mip_count - 1;
435 barrier.oldLayout = vk::ImageLayout::eTransferDstOptimal;
436 barrier.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
437 barrier.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
438 barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead;
439
440 cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
441 vk::PipelineStageFlagBits::eFragmentShader, {}, {}, {},
442 {barrier});
443
444 // We modified the layouts of this image from underneath it. Tell it its new
445 // state so it doesn't try to perform redundant transitions under the hood.
446 src.SetLayoutWithoutEncoding(vk::ImageLayout::eShaderReadOnlyOptimal);
447 src.SetMipMapGenerated();
448
449 return true;
450 }
451
452 } // namespace impeller
barrier_vk.h
command_encoder_vk.h
command_buffer_vk.h
impeller::InsertImageMemoryBarrier
static void InsertImageMemoryBarrier(const vk::CommandBuffer &cmd, const vk::Image &image, vk::AccessFlags src_access_mask, vk::AccessFlags dst_access_mask, vk::ImageLayout old_layout, vk::ImageLayout new_layout, vk::PipelineStageFlags src_stage, vk::PipelineStageFlags dst_stage, uint32_t base_mip_level, uint32_t mip_level_count=1u)
Definition: blit_pass_vk.cc:17
blit_pass_vk.h
texture_vk.h
VALIDATION_LOG
#define VALIDATION_LOG
Definition: validation.h:73
std
Definition: comparable.h:95
impeller::IPoint
TPoint< int64_t > IPoint
Definition: point.h:323
impeller::IRect
IRect64 IRect
Definition: rect.h:772
impeller
Definition: aiks_blend_unittests.cc:18