23#include "third_party/skia/include/core/SkAlphaType.h"
24#include "third_party/skia/include/core/SkBitmap.h"
25#include "third_party/skia/include/core/SkColorSpace.h"
26#include "third_party/skia/include/core/SkColorType.h"
27#include "third_party/skia/include/core/SkImageInfo.h"
28#include "third_party/skia/include/core/SkMallocPixelRef.h"
29#include "third_party/skia/include/core/SkPixelRef.h"
30#include "third_party/skia/include/core/SkPixmap.h"
31#include "third_party/skia/include/core/SkPoint.h"
32#include "third_party/skia/include/core/SkSize.h"
40void LoadGamut(SkPoint abc[3],
const skcms_Matrix3x3& xyz) {
44 for (
int index = 0; index < 3; index++) {
45 float sum = xyz.vals[index][0] + xyz.vals[index][1] + xyz.vals[index][2];
46 abc[index].fX = xyz.vals[index][0] / sum;
47 abc[index].fY = xyz.vals[index][1] / sum;
54float CalculateArea(SkPoint abc[3]) {
55 const SkPoint& a = abc[0];
56 const SkPoint&
b = abc[1];
57 const SkPoint&
c = abc[2];
58 return 0.5f * fabsf(a.fX *
b.fY +
b.fX *
c.fY - a.fX *
c.fY -
c.fX *
b.fY -
63static constexpr float kSrgbGamutArea = 0.0982f;
67bool IsWideGamut(
const SkColorSpace* color_space) {
71 skcms_Matrix3x3 xyzd50;
72 color_space->toXYZD50(&xyzd50);
74 LoadGamut(rgb, xyzd50);
75 float area = CalculateArea(rgb);
76 return area > kSrgbGamutArea;
79absl::StatusOr<ImageDecoderImpeller::ImageInfo> ToImageInfo(
80 const SkImageInfo& sk_image_format) {
81 const std::optional<impeller::PixelFormat> pixel_format =
83 if (!pixel_format.has_value()) {
84 std::string decode_error(
85 std::format(
"Codec pixel format is not supported (SkColorType={})",
86 static_cast<int>(sk_image_format.colorType())));
87 return absl::InvalidArgumentError(decode_error);
92 .format = pixel_format.value(),
96SkColorType ChooseCompatibleColorType(SkColorType
type) {
98 case kRGBA_F32_SkColorType:
99 return kRGBA_F16_SkColorType;
101 return kRGBA_8888_SkColorType;
105SkAlphaType ChooseCompatibleAlphaType(SkAlphaType
type) {
109absl::StatusOr<SkColorType> ChooseCompatibleColorType(
113 return kRGBA_F32_SkColorType;
115 return absl::InvalidArgumentError(
"unsupported target pixel format");
119absl::StatusOr<SkImageInfo> CreateImageInfo(
120 const SkImageInfo& base_image_info,
121 const SkISize& decode_size,
122 bool supports_wide_gamut,
124 const bool is_wide_gamut =
125 supports_wide_gamut ? IsWideGamut(base_image_info.colorSpace()) : false;
127 ChooseCompatibleAlphaType(base_image_info.alphaType());
129 absl::StatusOr<SkColorType> target_skia_color_type =
130 ChooseCompatibleColorType(target_format);
131 if (!target_skia_color_type.ok()) {
132 return target_skia_color_type.status();
134 return base_image_info.makeWH(decode_size.width(), decode_size.height())
135 .makeColorType(target_skia_color_type.value())
137 .makeColorSpace(SkColorSpace::MakeSRGB());
138 }
else if (is_wide_gamut) {
142 ? kBGR_101010x_XR_SkColorType
143 : kRGBA_F16_SkColorType;
144 return base_image_info.makeWH(decode_size.width(), decode_size.height())
147 .makeColorSpace(SkColorSpace::MakeSRGB());
149 return base_image_info.makeWH(decode_size.width(), decode_size.height())
150 .makeColorType(ChooseCompatibleColorType(base_image_info.colorType()))
152 .makeColorSpace(SkColorSpace::MakeSRGB());
156struct DecodedBitmap {
161absl::StatusOr<DecodedBitmap> DecodeToBitmap(
162 ImageDescriptor* descriptor,
163 const SkImageInfo& image_info,
164 const SkImageInfo& base_image_info,
165 const std::shared_ptr<impeller::Allocator>&
allocator) {
166 std::shared_ptr<SkBitmap>
bitmap = std::make_shared<SkBitmap>();
167 bitmap->setInfo(image_info);
168 std::shared_ptr<ImpellerAllocator> bitmap_allocator =
169 std::make_shared<ImpellerAllocator>(
allocator);
171 if (descriptor->is_compressed()) {
172 if (!
bitmap->tryAllocPixels(bitmap_allocator.get())) {
174 "Could not allocate intermediate for image decompression.";
176 return absl::InvalidArgumentError(
error);
178 if (!descriptor->get_pixels(
bitmap->pixmap())) {
179 std::string
error =
"Could not decompress image.";
181 return absl::InvalidArgumentError(
error);
184 std::shared_ptr<SkBitmap> temp_bitmap = std::make_shared<SkBitmap>();
185 temp_bitmap->setInfo(base_image_info);
186 sk_sp<SkPixelRef> pixel_ref = SkMallocPixelRef::MakeWithData(
187 base_image_info, descriptor->row_bytes(), descriptor->data());
188 temp_bitmap->setPixelRef(pixel_ref, 0, 0);
190 if (!
bitmap->tryAllocPixels(bitmap_allocator.get())) {
192 "Could not allocate intermediate for pixel conversion.";
194 return absl::ResourceExhaustedError(
error);
196 temp_bitmap->readPixels(
bitmap->pixmap());
199 return DecodedBitmap{.bitmap =
bitmap, .allocator = bitmap_allocator};
202absl::StatusOr<DecodedBitmap> HandlePremultiplication(
203 const std::shared_ptr<SkBitmap>&
bitmap,
204 std::shared_ptr<ImpellerAllocator> bitmap_allocator,
205 const std::shared_ptr<impeller::Allocator>&
allocator) {
206 if (
bitmap->alphaType() != SkAlphaType::kUnpremul_SkAlphaType) {
207 return DecodedBitmap{.bitmap =
bitmap,
208 .allocator = std::move(bitmap_allocator)};
211 std::shared_ptr<ImpellerAllocator> premul_allocator =
212 std::make_shared<ImpellerAllocator>(
allocator);
213 std::shared_ptr<SkBitmap> premul_bitmap = std::make_shared<SkBitmap>();
214 premul_bitmap->setInfo(
bitmap->info().makeAlphaType(kPremul_SkAlphaType));
215 if (!premul_bitmap->tryAllocPixels(premul_allocator.get())) {
217 "Could not allocate intermediate for premultiplication conversion.";
219 return absl::ResourceExhaustedError(
error);
221 bitmap->readPixels(premul_bitmap->pixmap());
222 premul_bitmap->setImmutable();
223 return DecodedBitmap{.bitmap = premul_bitmap, .allocator = premul_allocator};
226absl::StatusOr<ImageDecoderImpeller::DecompressResult> ResizeOnCpu(
227 const std::shared_ptr<SkBitmap>&
bitmap,
228 const SkISize& target_size,
229 const std::shared_ptr<impeller::Allocator>&
allocator) {
231 const SkImageInfo scaled_image_info =
232 bitmap->info().makeDimensions(target_size);
234 std::shared_ptr<SkBitmap> scaled_bitmap = std::make_shared<SkBitmap>();
235 std::shared_ptr<ImpellerAllocator> scaled_allocator =
236 std::make_shared<ImpellerAllocator>(
allocator);
237 scaled_bitmap->setInfo(scaled_image_info);
238 if (!scaled_bitmap->tryAllocPixels(scaled_allocator.get())) {
240 "Could not allocate scaled bitmap for image decompression.";
242 return absl::ResourceExhaustedError(
error);
244 if (!
bitmap->pixmap().scalePixels(
245 scaled_bitmap->pixmap(),
246 SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNone))) {
247 FML_LOG(ERROR) <<
"Could not scale decoded bitmap data.";
249 scaled_bitmap->setImmutable();
251 std::shared_ptr<impeller::DeviceBuffer>
buffer =
252 scaled_allocator->GetDeviceBuffer();
254 return absl::InternalError(
"Unable to get device buffer");
258 absl::StatusOr<ImageDecoderImpeller::ImageInfo> decoded_image_info =
259 ToImageInfo(scaled_bitmap->info());
260 if (!decoded_image_info.ok()) {
261 return decoded_image_info.status();
263 return ImageDecoderImpeller::DecompressResult{
264 .device_buffer = std::move(buffer),
265 .image_info = decoded_image_info.value()};
284 FML_DCHECK(
false) <<
"Unsupported pixel format";
293 case kRGBA_8888_SkColorType:
295 case kBGRA_8888_SkColorType:
297 case kRGBA_F16_SkColorType:
299 case kBGR_101010x_XR_SkColorType:
301 case kRGBA_F32_SkColorType:
311 std::shared_ptr<fml::ConcurrentTaskRunner> concurrent_task_runner,
313 bool wide_gamut_enabled,
314 const std::shared_ptr<fml::SyncSwitch>& gpu_disabled_switch)
315 :
ImageDecoder(runners,
std::move(concurrent_task_runner), io_manager),
316 wide_gamut_enabled_(wide_gamut_enabled),
317 gpu_disabled_switch_(gpu_disabled_switch) {
318 std::promise<std::shared_ptr<impeller::Context>> context_promise;
319 context_ = context_promise.get_future();
321 [promise = std::move(context_promise), io_manager]()
mutable {
323 promise.set_value(io_manager->GetImpellerContext());
325 promise.set_value(
nullptr);
332absl::StatusOr<ImageDecoderImpeller::DecompressResult>
337 bool supports_wide_gamut,
338 const std::shared_ptr<const impeller::Capabilities>& capabilities,
339 const std::shared_ptr<impeller::Allocator>&
allocator) {
342 std::string decode_error(
"Invalid descriptor (should never happen)");
344 return absl::InvalidArgumentError(decode_error);
347 const SkISize source_size = SkISize::Make(descriptor->
image_info().width,
349 const SkISize target_size =
350 SkISize::Make(std::min(max_texture_size.
width,
352 std::min(max_texture_size.
height,
356 if (!descriptor->
is_compressed() && source_size == target_size &&
361 desc.
size = descriptor->
data()->size();
362 std::shared_ptr<impeller::DeviceBuffer>
buffer =
365 return absl::ResourceExhaustedError(
"Could not create buffer for image.");
367 sk_sp<SkData>
data = descriptor->
data();
382 SkISize decode_size = source_size;
385 static_cast<float>(target_size.width()) / source_size.width(),
386 static_cast<float>(target_size.height()) / source_size.height()));
389 const SkImageInfo base_image_info =
391 const absl::StatusOr<SkImageInfo> image_info = CreateImageInfo(
392 base_image_info, decode_size, supports_wide_gamut, options.
target_format);
394 if (!image_info.ok()) {
395 return image_info.status();
398 if (!
ToPixelFormat(image_info.value().colorType()).has_value()) {
399 std::string decode_error =
400 std::format(
"Codec pixel format is not supported (SkColorType={})",
401 static_cast<int>(image_info.value().colorType()));
403 return absl::InvalidArgumentError(decode_error);
406 absl::StatusOr<DecodedBitmap> decoded = DecodeToBitmap(
407 descriptor, image_info.value(), base_image_info,
allocator);
409 return decoded.status();
412 absl::StatusOr<DecodedBitmap> premultiplied =
413 HandlePremultiplication(decoded->bitmap, decoded->allocator,
allocator);
414 if (!premultiplied.ok()) {
415 return premultiplied.status();
417 std::shared_ptr<SkBitmap>
bitmap = premultiplied->bitmap;
418 std::shared_ptr<ImpellerAllocator> bitmap_allocator =
419 premultiplied->allocator;
421 if (source_size.width() > max_texture_size.
width ||
422 source_size.height() > max_texture_size.
height ||
423 !capabilities->SupportsTextureToTextureBlits()) {
427 std::shared_ptr<impeller::DeviceBuffer>
buffer =
428 bitmap_allocator->GetDeviceBuffer();
430 return absl::InternalError(
"Unable to get device buffer");
434 std::optional<SkImageInfo> resize_info =
435 bitmap->dimensions() == target_size
437 : std::optional<SkImageInfo>(
438 image_info.value().makeDimensions(target_size));
440 absl::StatusOr<ImageDecoderImpeller::ImageInfo> decoded_image_info =
441 ToImageInfo(
bitmap->info());
442 if (!decoded_image_info.ok()) {
443 return decoded_image_info.status();
448 .image_info = decoded_image_info.value(),
449 .resize_info = resize_info};
453std::pair<sk_sp<DlImage>, std::string>
454ImageDecoderImpeller::UnsafeUploadTextureToPrivate(
455 const std::shared_ptr<impeller::Context>& context,
456 const std::shared_ptr<impeller::DeviceBuffer>& buffer,
458 const std::optional<SkImageInfo>& resize_info) {
465 resize_info.has_value()) {
472 context->GetResourceAllocator()->CreateTexture(texture_descriptor);
474 std::string decode_error(
"Could not create Impeller texture.");
476 return std::make_pair(
nullptr, decode_error);
479 dest_texture->SetLabel(
480 std::format(
"ui.Image({})",
static_cast<const void*
>(dest_texture.get()))
483 auto command_buffer = context->CreateCommandBuffer();
484 if (!command_buffer) {
485 std::string decode_error(
486 "Could not create command buffer for mipmap generation.");
488 return std::make_pair(
nullptr, decode_error);
490 command_buffer->SetLabel(
"Mipmap Command Buffer");
492 auto blit_pass = command_buffer->CreateBlitPass();
494 std::string decode_error(
495 "Could not create blit pass for mipmap generation.");
497 return std::make_pair(
nullptr, decode_error);
499 blit_pass->SetLabel(
"Mipmap Blit Pass");
503 blit_pass->GenerateMipmap(dest_texture);
506 std::shared_ptr<impeller::Texture> result_texture = dest_texture;
507 if (resize_info.has_value()) {
511 resize_desc.
size = {resize_info->
width(), resize_info->height()};
519 auto resize_texture =
520 context->GetResourceAllocator()->CreateTexture(resize_desc);
521 if (!resize_texture) {
522 std::string decode_error(
"Could not create resized Impeller texture.");
524 return std::make_pair(
nullptr, decode_error);
527 blit_pass->ResizeTexture(dest_texture,
530 blit_pass->GenerateMipmap(resize_texture);
533 result_texture = std::move(resize_texture);
535 blit_pass->EncodeCommands();
536 if (!context->GetCommandQueue()
540 if (status == impeller::CommandBuffer::Status::kError) {
542 <<
"GPU Error submitting image decoding command buffer.";
547 std::string decode_error(
"Failed to submit image decoding command buffer.");
549 return std::make_pair(
nullptr, decode_error);
554 if (context->AddTrackingFence(result_texture)) {
555 command_buffer->WaitUntilScheduled();
557 command_buffer->WaitUntilCompleted();
560 context->DisposeThreadLocalCachedResources();
562 return std::make_pair(
567void ImageDecoderImpeller::UploadTextureToPrivate(
569 const std::shared_ptr<impeller::Context>& context,
570 const std::shared_ptr<impeller::DeviceBuffer>& buffer,
572 const std::optional<SkImageInfo>& resize_info,
573 const std::shared_ptr<const fml::SyncSwitch>& gpu_disabled_switch) {
576 result(
nullptr,
"No Impeller context is available");
580 result(
nullptr,
"No Impeller device buffer is available");
584 gpu_disabled_switch->Execute(
586 .SetIfFalse([&result, context,
buffer, image_info, resize_info] {
587 sk_sp<DlImage>
image;
588 std::string decode_error;
589 std::tie(
image, decode_error) = std::tie(
image, decode_error) =
590 UnsafeUploadTextureToPrivate(context,
buffer, image_info,
592 result(
image, decode_error);
594 .SetIfTrue([&result, context,
buffer, image_info, resize_info] {
595 auto result_ptr = std::make_shared<ImageResult>(std::move(result));
596 context->StoreTaskForGPU(
597 [result_ptr, context,
buffer, image_info, resize_info]() {
598 sk_sp<DlImage>
image;
599 std::string decode_error;
600 std::tie(
image, decode_error) = UnsafeUploadTextureToPrivate(
601 context,
buffer, image_info, resize_info);
602 (*result_ptr)(
image, decode_error);
607 "Image upload failed due to loss of GPU access.");
612std::pair<sk_sp<DlImage>, std::string>
613ImageDecoderImpeller::UploadTextureToStorage(
614 const std::shared_ptr<impeller::Context>& context,
615 std::shared_ptr<SkBitmap>
bitmap) {
618 return std::make_pair(
nullptr,
"No Impeller context is available");
621 return std::make_pair(
nullptr,
"No texture bitmap is available");
623 const auto image_info =
bitmap->info();
624 const auto pixel_format =
ToPixelFormat(image_info.colorType());
626 std::string decode_error(
627 std::format(
"Unsupported pixel format (SkColorType={})",
628 static_cast<int>(image_info.colorType())));
630 return std::make_pair(
nullptr, decode_error);
635 texture_descriptor.
format = pixel_format.value();
636 texture_descriptor.
size = {image_info.width(), image_info.height()};
640 context->GetResourceAllocator()->CreateTexture(texture_descriptor);
642 std::string decode_error(
"Could not create Impeller texture.");
644 return std::make_pair(
nullptr, decode_error);
647 auto mapping = std::make_shared<fml::NonOwnedMapping>(
648 reinterpret_cast<const uint8_t*
>(
bitmap->getAddr(0, 0)),
650 [
bitmap](
auto,
auto)
mutable { bitmap.reset(); }
653 if (!
texture->SetContents(mapping)) {
654 std::string decode_error(
"Could not copy contents into Impeller texture.");
656 return std::make_pair(
nullptr, decode_error);
660 std::format(
"ui.Image({})",
static_cast<const void*
>(
texture.get()))
663 context->DisposeThreadLocalCachedResources();
677 auto raw_descriptor = descriptor.
get();
678 raw_descriptor->AddRef();
681 ui_runner = runners_.GetUITaskRunner()
682 ](
const auto&
image,
const auto& decode_error) {
683 ui_runner->PostTask([raw_descriptor, p_result,
image, decode_error]() {
684 raw_descriptor->Release();
685 p_result(std::move(
image), decode_error);
689 concurrent_task_runner_->PostTask(
691 context = context_.get(),
693 io_runner = runners_.GetIOTaskRunner(),
695 wide_gamut_enabled = wide_gamut_enabled_,
696 gpu_disabled_switch = gpu_disabled_switch_]() {
697#if FML_OS_IOS_SIMULATOR
705 result(
nullptr,
"No Impeller context is available");
708 auto max_size_supported =
709 context->GetResourceAllocator()->GetMaxTextureSizeSupported();
712 auto bitmap_result = DecompressTexture(
713 raw_descriptor, options, max_size_supported,
714 wide_gamut_enabled &&
715 context->GetCapabilities()->SupportsExtendedRangeFormats(),
716 context->GetCapabilities(), context->GetResourceAllocator());
717 if (!bitmap_result.ok()) {
718 result(
nullptr, std::string(bitmap_result.status().message()));
722 auto upload_texture_and_invoke_result = [result, context, bitmap_result,
723 gpu_disabled_switch]() {
724 UploadTextureToPrivate(result, context,
725 bitmap_result->device_buffer,
726 bitmap_result->image_info,
727 bitmap_result->resize_info,
732 if (context->GetBackendType() ==
734 io_runner->PostTask(upload_texture_and_invoke_result);
736 upload_texture_and_invoke_result();
741ImpellerAllocator::ImpellerAllocator(
742 std::shared_ptr<impeller::Allocator>
allocator)
754 const SkImageInfo& info =
bitmap->info();
755 if (kUnknown_SkColorType == info.colorType() || info.width() < 0 ||
756 info.height() < 0 || !info.validRowBytes(
bitmap->rowBytes())) {
765 std::shared_ptr<impeller::DeviceBuffer> device_buffer =
766 allocator_->CreateBuffer(descriptor);
767 if (!device_buffer) {
771 struct ImpellerPixelRef final :
public SkPixelRef {
772 ImpellerPixelRef(
int w,
int h,
void* s,
size_t r)
773 : SkPixelRef(w,
h, s, r) {}
775 ~ImpellerPixelRef()
override {}
778 auto pixel_ref = sk_sp<SkPixelRef>(
779 new ImpellerPixelRef(info.width(), info.height(),
780 device_buffer->OnGetContents(),
bitmap->rowBytes()));
782 bitmap->setPixelRef(std::move(pixel_ref), 0, 0);
783 buffer_ = std::move(device_buffer);
std::function< void(sk_sp< DlImage >, std::string)> ImageResult
@ kDontCare
Explicitly declare the target pixel is left for the engine to decide.
ImageDecoderImpeller(const TaskRunners &runners, std::shared_ptr< fml::ConcurrentTaskRunner > concurrent_task_runner, const fml::WeakPtr< IOManager > &io_manager, bool supports_wide_gamut, const std::shared_ptr< fml::SyncSwitch > &gpu_disabled_switch)
static absl::StatusOr< DecompressResult > DecompressTexture(ImageDescriptor *descriptor, const ImageDecoder::Options &options, impeller::ISize max_texture_size, bool supports_wide_gamut, const std::shared_ptr< const impeller::Capabilities > &capabilities, const std::shared_ptr< impeller::Allocator > &allocator)
static std::optional< impeller::PixelFormat > ToPixelFormat(SkColorType type)
~ImageDecoderImpeller() override
Creates an image descriptor for encoded or decoded image data, describing the width,...
SkISize get_scaled_dimensions(float scale)
Gets the scaled dimensions of this image, if backed by an ImageGenerator that can perform efficient s...
static SkImageInfo ToSkImageInfo(const ImageInfo &image_info)
bool is_compressed() const
Whether this descriptor represents compressed (encoded) data or not.
const ImageInfo & image_info() const
The orientation corrected image info for this image.
sk_sp< SkData > data() const
The underlying buffer for this image.
bool allocPixelRef(SkBitmap *bitmap) override
std::shared_ptr< impeller::DeviceBuffer > GetDeviceBuffer() const
fml::RefPtr< fml::TaskRunner > GetIOTaskRunner() const
virtual void PostTask(const fml::closure &task) override
static BufferView AsBufferView(std::shared_ptr< DeviceBuffer > buffer)
Create a buffer view of this entire buffer.
static sk_sp< DlImageImpeller > Make(std::shared_ptr< Texture > texture, OwningContext owning_context=OwningContext::kIO)
FlutterVulkanImage * image
const uint8_t uint32_t uint32_t GError ** error
#define FML_DLOG(severity)
#define FML_LOG(severity)
#define FML_DCHECK(condition)
std::shared_ptr< SkBitmap > bitmap
std::shared_ptr< ImpellerAllocator > allocator
constexpr impeller::PixelFormat ToImpellerPixelFormat(FlutterGPUPixelFormat value)
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot data
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder h
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set profile Make the profiler discard new samples once the profiler sample buffer is full When this flag is not the profiler sample buffer is used as a ring buffer
internal::CopyableLambda< T > MakeCopyable(T lambda)
constexpr PixelFormat ToPixelFormat(vk::Format format)
PixelFormat
The Pixel formats supported by Impeller. The naming convention denotes the usage of the component,...
TargetPixelFormat target_format
std::shared_ptr< impeller::DeviceBuffer > device_buffer
impeller::PixelFormat format
Represents the 2 code paths available when calling |SyncSwitchExecute|.
constexpr size_t MipCount() const
Return the mip count of the texture.
A lightweight object that describes the attributes of a texture that can then used an allocator to cr...
constexpr size_t GetByteSizeOfBaseMipLevel() const
CompressionType compression_type
#define TRACE_EVENT0(category_group, name)