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 "flutter/lib/ui/painting/image_decoder_impeller.h" |
6 | |
7 | #include <memory> |
8 | |
9 | #include "flutter/fml/closure.h" |
10 | #include "flutter/fml/make_copyable.h" |
11 | #include "flutter/fml/trace_event.h" |
12 | #include "flutter/impeller/core/allocator.h" |
13 | #include "flutter/impeller/core/texture.h" |
14 | #include "flutter/impeller/display_list/dl_image_impeller.h" |
15 | #include "flutter/impeller/renderer/command_buffer.h" |
16 | #include "flutter/impeller/renderer/context.h" |
17 | #include "flutter/lib/ui/painting/image_decoder_skia.h" |
18 | #include "impeller/base/strings.h" |
19 | #include "impeller/display_list/skia_conversions.h" |
20 | #include "impeller/geometry/size.h" |
21 | #include "third_party/skia/include/core/SkAlphaType.h" |
22 | #include "third_party/skia/include/core/SkBitmap.h" |
23 | #include "third_party/skia/include/core/SkColorSpace.h" |
24 | #include "third_party/skia/include/core/SkColorType.h" |
25 | #include "third_party/skia/include/core/SkImageInfo.h" |
26 | #include "third_party/skia/include/core/SkMallocPixelRef.h" |
27 | #include "third_party/skia/include/core/SkPixelRef.h" |
28 | #include "third_party/skia/include/core/SkPixmap.h" |
29 | #include "third_party/skia/include/core/SkPoint.h" |
30 | #include "third_party/skia/include/core/SkSamplingOptions.h" |
31 | #include "third_party/skia/include/core/SkSize.h" |
32 | |
33 | namespace flutter { |
34 | |
35 | class MallocDeviceBuffer : public impeller::DeviceBuffer { |
36 | public: |
37 | explicit MallocDeviceBuffer(impeller::DeviceBufferDescriptor desc) |
38 | : impeller::DeviceBuffer(desc) { |
39 | data_ = static_cast<uint8_t*>(malloc(size: desc.size)); |
40 | } |
41 | |
42 | ~MallocDeviceBuffer() override { free(ptr: data_); } |
43 | |
44 | bool SetLabel(const std::string& label) override { return true; } |
45 | |
46 | bool SetLabel(const std::string& label, impeller::Range range) override { |
47 | return true; |
48 | } |
49 | |
50 | uint8_t* OnGetContents() const override { return data_; } |
51 | |
52 | bool OnCopyHostBuffer(const uint8_t* source, |
53 | impeller::Range source_range, |
54 | size_t offset) override { |
55 | memcpy(dest: data_ + offset, src: source + source_range.offset, n: source_range.length); |
56 | return true; |
57 | } |
58 | |
59 | private: |
60 | uint8_t* data_; |
61 | |
62 | FML_DISALLOW_COPY_AND_ASSIGN(MallocDeviceBuffer); |
63 | }; |
64 | |
65 | #ifdef FML_OS_ANDROID |
66 | static constexpr bool kShouldUseMallocDeviceBuffer = true; |
67 | #else |
68 | static constexpr bool kShouldUseMallocDeviceBuffer = false; |
69 | #endif // FML_OS_ANDROID |
70 | |
71 | namespace { |
72 | /** |
73 | * Loads the gamut as a set of three points (triangle). |
74 | */ |
75 | void LoadGamut(SkPoint abc[3], const skcms_Matrix3x3& xyz) { |
76 | // rx = rX / (rX + rY + rZ) |
77 | // ry = rY / (rX + rY + rZ) |
78 | // gx, gy, bx, and gy are calculated similarly. |
79 | for (int index = 0; index < 3; index++) { |
80 | float sum = xyz.vals[index][0] + xyz.vals[index][1] + xyz.vals[index][2]; |
81 | abc[index].fX = xyz.vals[index][0] / sum; |
82 | abc[index].fY = xyz.vals[index][1] / sum; |
83 | } |
84 | } |
85 | |
86 | /** |
87 | * Calculates the area of the triangular gamut. |
88 | */ |
89 | float CalculateArea(SkPoint abc[3]) { |
90 | const SkPoint& a = abc[0]; |
91 | const SkPoint& b = abc[1]; |
92 | const SkPoint& c = abc[2]; |
93 | return 0.5f * fabsf(x: a.fX * b.fY + b.fX * c.fY - a.fX * c.fY - c.fX * b.fY - |
94 | b.fX * a.fY); |
95 | } |
96 | |
97 | // Note: This was calculated from SkColorSpace::MakeSRGB(). |
98 | static constexpr float kSrgbGamutArea = 0.0982f; |
99 | |
100 | // Source: |
101 | // https://source.chromium.org/chromium/_/skia/skia.git/+/393fb1ec80f41d8ad7d104921b6920e69749fda1:src/codec/SkAndroidCodec.cpp;l=67;drc=46572b4d445f41943059d0e377afc6d6748cd5ca;bpv=1;bpt=0 |
102 | bool IsWideGamut(const SkColorSpace* color_space) { |
103 | if (!color_space) { |
104 | return false; |
105 | } |
106 | skcms_Matrix3x3 xyzd50; |
107 | color_space->toXYZD50(toXYZD50: &xyzd50); |
108 | SkPoint rgb[3]; |
109 | LoadGamut(abc: rgb, xyz: xyzd50); |
110 | float area = CalculateArea(abc: rgb); |
111 | return area > kSrgbGamutArea; |
112 | } |
113 | } // namespace |
114 | |
115 | ImageDecoderImpeller::ImageDecoderImpeller( |
116 | const TaskRunners& runners, |
117 | std::shared_ptr<fml::ConcurrentTaskRunner> concurrent_task_runner, |
118 | const fml::WeakPtr<IOManager>& io_manager, |
119 | bool supports_wide_gamut, |
120 | const std::shared_ptr<fml::SyncSwitch>& gpu_disabled_switch) |
121 | : ImageDecoder(runners, std::move(concurrent_task_runner), io_manager), |
122 | supports_wide_gamut_(supports_wide_gamut), |
123 | gpu_disabled_switch_(gpu_disabled_switch) { |
124 | std::promise<std::shared_ptr<impeller::Context>> context_promise; |
125 | context_ = context_promise.get_future(); |
126 | runners_.GetIOTaskRunner()->PostTask(task: fml::MakeCopyable( |
127 | lambda: [promise = std::move(context_promise), io_manager]() mutable { |
128 | promise.set_value(io_manager ? io_manager->GetImpellerContext() |
129 | : nullptr); |
130 | })); |
131 | } |
132 | |
133 | ImageDecoderImpeller::~ImageDecoderImpeller() = default; |
134 | |
135 | static SkColorType ChooseCompatibleColorType(SkColorType type) { |
136 | switch (type) { |
137 | case kRGBA_F32_SkColorType: |
138 | return kRGBA_F16_SkColorType; |
139 | default: |
140 | return kRGBA_8888_SkColorType; |
141 | } |
142 | } |
143 | |
144 | static SkAlphaType ChooseCompatibleAlphaType(SkAlphaType type) { |
145 | return type; |
146 | } |
147 | |
148 | DecompressResult ImageDecoderImpeller::DecompressTexture( |
149 | ImageDescriptor* descriptor, |
150 | SkISize target_size, |
151 | impeller::ISize max_texture_size, |
152 | bool supports_wide_gamut, |
153 | const std::shared_ptr<impeller::Allocator>& allocator) { |
154 | TRACE_EVENT0("impeller", __FUNCTION__); |
155 | if (!descriptor) { |
156 | std::string decode_error("Invalid descriptor (should never happen)"); |
157 | FML_DLOG(ERROR) << decode_error; |
158 | return DecompressResult{.decode_error = decode_error}; |
159 | } |
160 | |
161 | target_size.set(w: std::min(a: static_cast<int32_t>(max_texture_size.width), |
162 | b: target_size.width()), |
163 | h: std::min(a: static_cast<int32_t>(max_texture_size.height), |
164 | b: target_size.height())); |
165 | |
166 | const SkISize source_size = descriptor->image_info().dimensions(); |
167 | auto decode_size = source_size; |
168 | if (descriptor->is_compressed()) { |
169 | decode_size = descriptor->get_scaled_dimensions(scale: std::max( |
170 | a: static_cast<float>(target_size.width()) / source_size.width(), |
171 | b: static_cast<float>(target_size.height()) / source_size.height())); |
172 | } |
173 | |
174 | //---------------------------------------------------------------------------- |
175 | /// 1. Decode the image. |
176 | /// |
177 | |
178 | const auto base_image_info = descriptor->image_info(); |
179 | const bool is_wide_gamut = |
180 | supports_wide_gamut ? IsWideGamut(color_space: base_image_info.colorSpace()) : false; |
181 | SkAlphaType alpha_type = |
182 | ChooseCompatibleAlphaType(type: base_image_info.alphaType()); |
183 | SkImageInfo image_info; |
184 | if (is_wide_gamut) { |
185 | SkColorType color_type = alpha_type == SkAlphaType::kOpaque_SkAlphaType |
186 | ? kBGR_101010x_XR_SkColorType |
187 | : kRGBA_F16_SkColorType; |
188 | image_info = |
189 | base_image_info.makeWH(newWidth: decode_size.width(), newHeight: decode_size.height()) |
190 | .makeColorType(newColorType: color_type) |
191 | .makeAlphaType(newAlphaType: alpha_type) |
192 | .makeColorSpace(cs: SkColorSpace::MakeSRGB()); |
193 | } else { |
194 | image_info = |
195 | base_image_info.makeWH(newWidth: decode_size.width(), newHeight: decode_size.height()) |
196 | .makeColorType( |
197 | newColorType: ChooseCompatibleColorType(type: base_image_info.colorType())) |
198 | .makeAlphaType(newAlphaType: alpha_type); |
199 | } |
200 | |
201 | const auto pixel_format = |
202 | impeller::skia_conversions::ToPixelFormat(type: image_info.colorType()); |
203 | if (!pixel_format.has_value()) { |
204 | std::string decode_error(impeller::SPrintF( |
205 | format: "Codec pixel format is not supported (SkColorType=%d)", |
206 | image_info.colorType())); |
207 | FML_DLOG(ERROR) << decode_error; |
208 | return DecompressResult{.decode_error = decode_error}; |
209 | } |
210 | |
211 | auto bitmap = std::make_shared<SkBitmap>(); |
212 | bitmap->setInfo(imageInfo: image_info); |
213 | auto bitmap_allocator = std::make_shared<ImpellerAllocator>(args: allocator); |
214 | |
215 | if (descriptor->is_compressed()) { |
216 | if (!bitmap->tryAllocPixels(allocator: bitmap_allocator.get())) { |
217 | std::string decode_error( |
218 | "Could not allocate intermediate for image decompression."); |
219 | FML_DLOG(ERROR) << decode_error; |
220 | return DecompressResult{.decode_error = decode_error}; |
221 | } |
222 | // Decode the image into the image generator's closest supported size. |
223 | if (!descriptor->get_pixels(pixmap: bitmap->pixmap())) { |
224 | std::string decode_error("Could not decompress image."); |
225 | FML_DLOG(ERROR) << decode_error; |
226 | return DecompressResult{.decode_error = decode_error}; |
227 | } |
228 | } else { |
229 | auto temp_bitmap = std::make_shared<SkBitmap>(); |
230 | temp_bitmap->setInfo(imageInfo: base_image_info); |
231 | auto pixel_ref = SkMallocPixelRef::MakeWithData( |
232 | base_image_info, rowBytes: descriptor->row_bytes(), data: descriptor->data()); |
233 | temp_bitmap->setPixelRef(pixelRef: pixel_ref, dx: 0, dy: 0); |
234 | |
235 | if (!bitmap->tryAllocPixels(allocator: bitmap_allocator.get())) { |
236 | std::string decode_error( |
237 | "Could not allocate intermediate for pixel conversion."); |
238 | FML_DLOG(ERROR) << decode_error; |
239 | return DecompressResult{.decode_error = decode_error}; |
240 | } |
241 | temp_bitmap->readPixels(dst: bitmap->pixmap()); |
242 | bitmap->setImmutable(); |
243 | } |
244 | |
245 | if (bitmap->dimensions() == target_size) { |
246 | auto buffer = bitmap_allocator->GetDeviceBuffer(); |
247 | if (!buffer) { |
248 | return DecompressResult{.decode_error = "Unable to get device buffer"}; |
249 | } |
250 | return DecompressResult{.device_buffer = buffer, |
251 | .sk_bitmap = bitmap, |
252 | .image_info = bitmap->info()}; |
253 | } |
254 | |
255 | //---------------------------------------------------------------------------- |
256 | /// 2. If the decoded image isn't the requested target size, resize it. |
257 | /// |
258 | |
259 | TRACE_EVENT0("impeller", "DecodeScale"); |
260 | const auto scaled_image_info = image_info.makeDimensions(newSize: target_size); |
261 | |
262 | auto scaled_bitmap = std::make_shared<SkBitmap>(); |
263 | auto scaled_allocator = std::make_shared<ImpellerAllocator>(args: allocator); |
264 | scaled_bitmap->setInfo(imageInfo: scaled_image_info); |
265 | if (!scaled_bitmap->tryAllocPixels(allocator: scaled_allocator.get())) { |
266 | std::string decode_error( |
267 | "Could not allocate scaled bitmap for image decompression."); |
268 | FML_DLOG(ERROR) << decode_error; |
269 | return DecompressResult{.decode_error = decode_error}; |
270 | } |
271 | if (!bitmap->pixmap().scalePixels( |
272 | dst: scaled_bitmap->pixmap(), |
273 | SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNone))) { |
274 | FML_LOG(ERROR) << "Could not scale decoded bitmap data."; |
275 | } |
276 | scaled_bitmap->setImmutable(); |
277 | |
278 | auto buffer = scaled_allocator->GetDeviceBuffer(); |
279 | if (!buffer) { |
280 | return DecompressResult{.decode_error = "Unable to get device buffer"}; |
281 | } |
282 | return DecompressResult{.device_buffer = buffer, |
283 | .sk_bitmap = scaled_bitmap, |
284 | .image_info = scaled_bitmap->info()}; |
285 | } |
286 | |
287 | /// Only call this method if the GPU is available. |
288 | static std::pair<sk_sp<DlImage>, std::string> UnsafeUploadTextureToPrivate( |
289 | const std::shared_ptr<impeller::Context>& context, |
290 | const std::shared_ptr<impeller::DeviceBuffer>& buffer, |
291 | const SkImageInfo& image_info) { |
292 | const auto pixel_format = |
293 | impeller::skia_conversions::ToPixelFormat(type: image_info.colorType()); |
294 | if (!pixel_format) { |
295 | std::string decode_error(impeller::SPrintF( |
296 | format: "Unsupported pixel format (SkColorType=%d)", image_info.colorType())); |
297 | FML_DLOG(ERROR) << decode_error; |
298 | return std::make_pair(t1: nullptr, t2&: decode_error); |
299 | } |
300 | |
301 | impeller::TextureDescriptor texture_descriptor; |
302 | texture_descriptor.storage_mode = impeller::StorageMode::kDevicePrivate; |
303 | texture_descriptor.format = pixel_format.value(); |
304 | texture_descriptor.size = {image_info.width(), image_info.height()}; |
305 | texture_descriptor.mip_count = texture_descriptor.size.MipCount(); |
306 | texture_descriptor.compression_type = impeller::CompressionType::kLossy; |
307 | |
308 | auto dest_texture = |
309 | context->GetResourceAllocator()->CreateTexture(desc: texture_descriptor); |
310 | if (!dest_texture) { |
311 | std::string decode_error("Could not create Impeller texture."); |
312 | FML_DLOG(ERROR) << decode_error; |
313 | return std::make_pair(t1: nullptr, t2&: decode_error); |
314 | } |
315 | |
316 | dest_texture->SetLabel( |
317 | impeller::SPrintF(format: "ui.Image(%p)", dest_texture.get()).c_str()); |
318 | |
319 | auto command_buffer = context->CreateCommandBuffer(); |
320 | if (!command_buffer) { |
321 | std::string decode_error( |
322 | "Could not create command buffer for mipmap generation."); |
323 | FML_DLOG(ERROR) << decode_error; |
324 | return std::make_pair(t1: nullptr, t2&: decode_error); |
325 | } |
326 | command_buffer->SetLabel("Mipmap Command Buffer"); |
327 | |
328 | auto blit_pass = command_buffer->CreateBlitPass(); |
329 | if (!blit_pass) { |
330 | std::string decode_error( |
331 | "Could not create blit pass for mipmap generation."); |
332 | FML_DLOG(ERROR) << decode_error; |
333 | return std::make_pair(t1: nullptr, t2&: decode_error); |
334 | } |
335 | blit_pass->SetLabel("Mipmap Blit Pass"); |
336 | blit_pass->AddCopy(source: buffer->AsBufferView(), destination: dest_texture); |
337 | if (texture_descriptor.size.MipCount() > 1) { |
338 | blit_pass->GenerateMipmap(texture: dest_texture); |
339 | } |
340 | |
341 | blit_pass->EncodeCommands(transients_allocator: context->GetResourceAllocator()); |
342 | if (!command_buffer->SubmitCommands()) { |
343 | std::string decode_error("Failed to submit blit pass command buffer."); |
344 | FML_DLOG(ERROR) << decode_error; |
345 | return std::make_pair(t1: nullptr, t2&: decode_error); |
346 | } |
347 | |
348 | return std::make_pair( |
349 | t1: impeller::DlImageImpeller::Make(texture: std::move(dest_texture)), t2: std::string()); |
350 | } |
351 | |
352 | std::pair<sk_sp<DlImage>, std::string> |
353 | ImageDecoderImpeller::UploadTextureToPrivate( |
354 | const std::shared_ptr<impeller::Context>& context, |
355 | const std::shared_ptr<impeller::DeviceBuffer>& buffer, |
356 | const SkImageInfo& image_info, |
357 | const std::shared_ptr<SkBitmap>& bitmap, |
358 | const std::shared_ptr<fml::SyncSwitch>& gpu_disabled_switch) { |
359 | TRACE_EVENT0("impeller", __FUNCTION__); |
360 | if (!context) { |
361 | return std::make_pair(t1: nullptr, t2: "No Impeller context is available"); |
362 | } |
363 | if (!buffer) { |
364 | return std::make_pair(t1: nullptr, t2: "No Impeller device buffer is available"); |
365 | } |
366 | |
367 | std::pair<sk_sp<DlImage>, std::string> result; |
368 | gpu_disabled_switch->Execute( |
369 | handlers: fml::SyncSwitch::Handlers() |
370 | .SetIfFalse([&result, context, buffer, image_info] { |
371 | result = UnsafeUploadTextureToPrivate(context, buffer, image_info); |
372 | }) |
373 | .SetIfTrue([&result, context, bitmap, gpu_disabled_switch] { |
374 | // create_mips is false because we already know the GPU is disabled. |
375 | result = |
376 | UploadTextureToStorage(context, bitmap, gpu_disabled_switch, |
377 | storage_mode: impeller::StorageMode::kHostVisible, |
378 | /*create_mips=*/false); |
379 | })); |
380 | return result; |
381 | } |
382 | |
383 | std::pair<sk_sp<DlImage>, std::string> |
384 | ImageDecoderImpeller::UploadTextureToStorage( |
385 | const std::shared_ptr<impeller::Context>& context, |
386 | std::shared_ptr<SkBitmap> bitmap, |
387 | const std::shared_ptr<fml::SyncSwitch>& gpu_disabled_switch, |
388 | impeller::StorageMode storage_mode, |
389 | bool create_mips) { |
390 | TRACE_EVENT0("impeller", __FUNCTION__); |
391 | if (!context) { |
392 | return std::make_pair(t1: nullptr, t2: "No Impeller context is available"); |
393 | } |
394 | if (!bitmap) { |
395 | return std::make_pair(t1: nullptr, t2: "No texture bitmap is available"); |
396 | } |
397 | const auto image_info = bitmap->info(); |
398 | const auto pixel_format = |
399 | impeller::skia_conversions::ToPixelFormat(type: image_info.colorType()); |
400 | if (!pixel_format) { |
401 | std::string decode_error(impeller::SPrintF( |
402 | format: "Unsupported pixel format (SkColorType=%d)", image_info.colorType())); |
403 | FML_DLOG(ERROR) << decode_error; |
404 | return std::make_pair(t1: nullptr, t2&: decode_error); |
405 | } |
406 | |
407 | impeller::TextureDescriptor texture_descriptor; |
408 | texture_descriptor.storage_mode = storage_mode; |
409 | texture_descriptor.format = pixel_format.value(); |
410 | texture_descriptor.size = {image_info.width(), image_info.height()}; |
411 | texture_descriptor.mip_count = |
412 | create_mips ? texture_descriptor.size.MipCount() : 1; |
413 | |
414 | auto texture = |
415 | context->GetResourceAllocator()->CreateTexture(desc: texture_descriptor); |
416 | if (!texture) { |
417 | std::string decode_error("Could not create Impeller texture."); |
418 | FML_DLOG(ERROR) << decode_error; |
419 | return std::make_pair(t1: nullptr, t2&: decode_error); |
420 | } |
421 | |
422 | auto mapping = std::make_shared<fml::NonOwnedMapping>( |
423 | args: reinterpret_cast<const uint8_t*>(bitmap->getAddr(x: 0, y: 0)), // data |
424 | args: texture_descriptor.GetByteSizeOfBaseMipLevel(), // size |
425 | args: [bitmap](auto, auto) mutable { bitmap.reset(); } // proc |
426 | ); |
427 | |
428 | if (!texture->SetContents(mapping)) { |
429 | std::string decode_error("Could not copy contents into Impeller texture."); |
430 | FML_DLOG(ERROR) << decode_error; |
431 | return std::make_pair(t1: nullptr, t2&: decode_error); |
432 | } |
433 | |
434 | texture->SetLabel(impeller::SPrintF(format: "ui.Image(%p)", texture.get()).c_str()); |
435 | |
436 | if (texture_descriptor.mip_count > 1u && create_mips) { |
437 | std::optional<std::string> decode_error; |
438 | |
439 | // The only platform that needs mipmapping unconditionally is GL. |
440 | // GL based platforms never disable GPU access. |
441 | // This is only really needed for iOS. |
442 | gpu_disabled_switch->Execute(handlers: fml::SyncSwitch::Handlers().SetIfFalse( |
443 | [context, &texture, &decode_error] { |
444 | auto command_buffer = context->CreateCommandBuffer(); |
445 | if (!command_buffer) { |
446 | decode_error = |
447 | "Could not create command buffer for mipmap generation."; |
448 | return; |
449 | } |
450 | command_buffer->SetLabel("Mipmap Command Buffer"); |
451 | |
452 | auto blit_pass = command_buffer->CreateBlitPass(); |
453 | if (!blit_pass) { |
454 | decode_error = "Could not create blit pass for mipmap generation."; |
455 | return; |
456 | } |
457 | blit_pass->SetLabel("Mipmap Blit Pass"); |
458 | blit_pass->GenerateMipmap(texture); |
459 | |
460 | blit_pass->EncodeCommands(transients_allocator: context->GetResourceAllocator()); |
461 | if (!command_buffer->SubmitCommands()) { |
462 | decode_error = "Failed to submit blit pass command buffer."; |
463 | return; |
464 | } |
465 | command_buffer->WaitUntilScheduled(); |
466 | })); |
467 | if (decode_error.has_value()) { |
468 | FML_DLOG(ERROR) << decode_error.value(); |
469 | return std::make_pair(t1: nullptr, t2&: decode_error.value()); |
470 | } |
471 | } |
472 | |
473 | return std::make_pair(t1: impeller::DlImageImpeller::Make(texture: std::move(texture)), |
474 | t2: std::string()); |
475 | } |
476 | |
477 | // |ImageDecoder| |
478 | void ImageDecoderImpeller::Decode(fml::RefPtr<ImageDescriptor> descriptor, |
479 | uint32_t target_width, |
480 | uint32_t target_height, |
481 | const ImageResult& p_result) { |
482 | FML_DCHECK(descriptor); |
483 | FML_DCHECK(p_result); |
484 | |
485 | // Wrap the result callback so that it can be invoked from any thread. |
486 | auto raw_descriptor = descriptor.get(); |
487 | raw_descriptor->AddRef(); |
488 | ImageResult result = [p_result, // |
489 | raw_descriptor, // |
490 | ui_runner = runners_.GetUITaskRunner() // |
491 | ](auto image, auto decode_error) { |
492 | ui_runner->PostTask(task: [raw_descriptor, p_result, image, decode_error]() { |
493 | raw_descriptor->Release(); |
494 | p_result(std::move(image), decode_error); |
495 | }); |
496 | }; |
497 | |
498 | concurrent_task_runner_->PostTask( |
499 | task: [raw_descriptor, // |
500 | context = context_.get(), // |
501 | target_size = SkISize::Make(w: target_width, h: target_height), // |
502 | io_runner = runners_.GetIOTaskRunner(), // |
503 | result, |
504 | supports_wide_gamut = supports_wide_gamut_, // |
505 | gpu_disabled_switch = gpu_disabled_switch_]() { |
506 | if (!context) { |
507 | result(nullptr, "No Impeller context is available"); |
508 | return; |
509 | } |
510 | auto max_size_supported = |
511 | context->GetResourceAllocator()->GetMaxTextureSizeSupported(); |
512 | |
513 | // Always decompress on the concurrent runner. |
514 | auto bitmap_result = DecompressTexture( |
515 | descriptor: raw_descriptor, target_size, max_texture_size: max_size_supported, |
516 | supports_wide_gamut, allocator: context->GetResourceAllocator()); |
517 | if (!bitmap_result.device_buffer) { |
518 | result(nullptr, bitmap_result.decode_error); |
519 | return; |
520 | } |
521 | auto upload_texture_and_invoke_result = [result, context, bitmap_result, |
522 | gpu_disabled_switch]() { |
523 | sk_sp<DlImage> image; |
524 | std::string decode_error; |
525 | if (!kShouldUseMallocDeviceBuffer && |
526 | context->GetCapabilities()->SupportsBufferToTextureBlits()) { |
527 | std::tie(t&: image, t&: decode_error) = UploadTextureToPrivate( |
528 | context, buffer: bitmap_result.device_buffer, image_info: bitmap_result.image_info, |
529 | bitmap: bitmap_result.sk_bitmap, gpu_disabled_switch); |
530 | result(image, decode_error); |
531 | } else { |
532 | std::tie(t&: image, t&: decode_error) = UploadTextureToStorage( |
533 | context, bitmap: bitmap_result.sk_bitmap, gpu_disabled_switch, |
534 | storage_mode: impeller::StorageMode::kDevicePrivate, |
535 | /*create_mips=*/true); |
536 | result(image, decode_error); |
537 | } |
538 | }; |
539 | // TODO(jonahwilliams): |
540 | // https://github.com/flutter/flutter/issues/123058 Technically we |
541 | // don't need to post tasks to the io runner, but without this |
542 | // forced serialization we can end up overloading the GPU and/or |
543 | // competing with raster workloads. |
544 | io_runner->PostTask(task: upload_texture_and_invoke_result); |
545 | }); |
546 | } |
547 | |
548 | ImpellerAllocator::ImpellerAllocator( |
549 | std::shared_ptr<impeller::Allocator> allocator) |
550 | : allocator_(std::move(allocator)) {} |
551 | |
552 | std::shared_ptr<impeller::DeviceBuffer> ImpellerAllocator::GetDeviceBuffer() |
553 | const { |
554 | return buffer_; |
555 | } |
556 | |
557 | bool ImpellerAllocator::allocPixelRef(SkBitmap* bitmap) { |
558 | const SkImageInfo& info = bitmap->info(); |
559 | if (kUnknown_SkColorType == info.colorType() || info.width() < 0 || |
560 | info.height() < 0 || !info.validRowBytes(rowBytes: bitmap->rowBytes())) { |
561 | return false; |
562 | } |
563 | |
564 | impeller::DeviceBufferDescriptor descriptor; |
565 | descriptor.storage_mode = impeller::StorageMode::kHostVisible; |
566 | descriptor.size = ((bitmap->height() - 1) * bitmap->rowBytes()) + |
567 | (bitmap->width() * bitmap->bytesPerPixel()); |
568 | |
569 | std::shared_ptr<impeller::DeviceBuffer> device_buffer = |
570 | kShouldUseMallocDeviceBuffer |
571 | ? std::make_shared<MallocDeviceBuffer>(args&: descriptor) |
572 | : allocator_->CreateBuffer(desc: descriptor); |
573 | |
574 | struct ImpellerPixelRef final : public SkPixelRef { |
575 | ImpellerPixelRef(int w, int h, void* s, size_t r) |
576 | : SkPixelRef(w, h, s, r) {} |
577 | |
578 | ~ImpellerPixelRef() override {} |
579 | }; |
580 | |
581 | auto pixel_ref = sk_sp<SkPixelRef>( |
582 | new ImpellerPixelRef(info.width(), info.height(), |
583 | device_buffer->OnGetContents(), bitmap->rowBytes())); |
584 | |
585 | bitmap->setPixelRef(pixelRef: std::move(pixel_ref), dx: 0, dy: 0); |
586 | buffer_ = std::move(device_buffer); |
587 | return true; |
588 | } |
589 | |
590 | } // namespace flutter |
591 |
Definitions
- MallocDeviceBuffer
- MallocDeviceBuffer
- ~MallocDeviceBuffer
- SetLabel
- SetLabel
- OnGetContents
- OnCopyHostBuffer
- MallocDeviceBuffer
- kShouldUseMallocDeviceBuffer
- LoadGamut
- CalculateArea
- kSrgbGamutArea
- IsWideGamut
- ImageDecoderImpeller
- ~ImageDecoderImpeller
- ChooseCompatibleColorType
- ChooseCompatibleAlphaType
- DecompressTexture
- UnsafeUploadTextureToPrivate
- UploadTextureToPrivate
- UploadTextureToStorage
- Decode
- ImpellerAllocator
- GetDeviceBuffer
Learn more about Flutter for embedded and desktop on industrialflutter.com