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) {
140 ? kBGR_101010x_XR_SkColorType
141 : kRGBA_F16_SkColorType;
142 return base_image_info.makeWH(decode_size.width(), decode_size.height())
145 .makeColorSpace(SkColorSpace::MakeSRGB());
147 return base_image_info.makeWH(decode_size.width(), decode_size.height())
148 .makeColorType(ChooseCompatibleColorType(base_image_info.colorType()))
150 .makeColorSpace(SkColorSpace::MakeSRGB());
154struct DecodedBitmap {
159absl::StatusOr<DecodedBitmap> DecodeToBitmap(
160 ImageDescriptor* descriptor,
161 const SkImageInfo& image_info,
162 const SkImageInfo& base_image_info,
163 const std::shared_ptr<impeller::Allocator>&
allocator) {
164 std::shared_ptr<SkBitmap>
bitmap = std::make_shared<SkBitmap>();
165 bitmap->setInfo(image_info);
166 std::shared_ptr<ImpellerAllocator> bitmap_allocator =
167 std::make_shared<ImpellerAllocator>(
allocator);
169 if (descriptor->is_compressed()) {
170 if (!
bitmap->tryAllocPixels(bitmap_allocator.get())) {
172 "Could not allocate intermediate for image decompression.";
174 return absl::InvalidArgumentError(
error);
176 if (!descriptor->get_pixels(
bitmap->pixmap())) {
177 std::string
error =
"Could not decompress image.";
179 return absl::InvalidArgumentError(
error);
182 std::shared_ptr<SkBitmap> temp_bitmap = std::make_shared<SkBitmap>();
183 temp_bitmap->setInfo(base_image_info);
184 sk_sp<SkPixelRef> pixel_ref = SkMallocPixelRef::MakeWithData(
185 base_image_info, descriptor->row_bytes(), descriptor->data());
186 temp_bitmap->setPixelRef(pixel_ref, 0, 0);
188 if (!
bitmap->tryAllocPixels(bitmap_allocator.get())) {
190 "Could not allocate intermediate for pixel conversion.";
192 return absl::ResourceExhaustedError(
error);
194 temp_bitmap->readPixels(
bitmap->pixmap());
197 return DecodedBitmap{.bitmap =
bitmap, .allocator = bitmap_allocator};
200absl::StatusOr<DecodedBitmap> HandlePremultiplication(
201 const std::shared_ptr<SkBitmap>&
bitmap,
202 std::shared_ptr<ImpellerAllocator> bitmap_allocator,
203 const std::shared_ptr<impeller::Allocator>&
allocator) {
204 if (
bitmap->alphaType() != SkAlphaType::kUnpremul_SkAlphaType) {
205 return DecodedBitmap{.bitmap =
bitmap,
206 .allocator = std::move(bitmap_allocator)};
209 std::shared_ptr<ImpellerAllocator> premul_allocator =
210 std::make_shared<ImpellerAllocator>(
allocator);
211 std::shared_ptr<SkBitmap> premul_bitmap = std::make_shared<SkBitmap>();
212 premul_bitmap->setInfo(
bitmap->info().makeAlphaType(kPremul_SkAlphaType));
213 if (!premul_bitmap->tryAllocPixels(premul_allocator.get())) {
215 "Could not allocate intermediate for premultiplication conversion.";
217 return absl::ResourceExhaustedError(
error);
219 bitmap->readPixels(premul_bitmap->pixmap());
220 premul_bitmap->setImmutable();
221 return DecodedBitmap{.bitmap = premul_bitmap, .allocator = premul_allocator};
224absl::StatusOr<ImageDecoderImpeller::DecompressResult> ResizeOnCpu(
225 const std::shared_ptr<SkBitmap>&
bitmap,
226 const SkISize& target_size,
227 const std::shared_ptr<impeller::Allocator>&
allocator) {
229 const SkImageInfo scaled_image_info =
230 bitmap->info().makeDimensions(target_size);
232 std::shared_ptr<SkBitmap> scaled_bitmap = std::make_shared<SkBitmap>();
233 std::shared_ptr<ImpellerAllocator> scaled_allocator =
234 std::make_shared<ImpellerAllocator>(
allocator);
235 scaled_bitmap->setInfo(scaled_image_info);
236 if (!scaled_bitmap->tryAllocPixels(scaled_allocator.get())) {
238 "Could not allocate scaled bitmap for image decompression.";
240 return absl::ResourceExhaustedError(
error);
242 if (!
bitmap->pixmap().scalePixels(
243 scaled_bitmap->pixmap(),
244 SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNone))) {
245 FML_LOG(ERROR) <<
"Could not scale decoded bitmap data.";
247 scaled_bitmap->setImmutable();
249 std::shared_ptr<impeller::DeviceBuffer>
buffer =
250 scaled_allocator->GetDeviceBuffer();
252 return absl::InternalError(
"Unable to get device buffer");
256 absl::StatusOr<ImageDecoderImpeller::ImageInfo> decoded_image_info =
257 ToImageInfo(scaled_bitmap->info());
258 if (!decoded_image_info.ok()) {
259 return decoded_image_info.status();
261 return ImageDecoderImpeller::DecompressResult{
262 .device_buffer = std::move(buffer),
263 .image_info = decoded_image_info.value()};
282 FML_DCHECK(
false) <<
"Unsupported pixel format";
291 case kRGBA_8888_SkColorType:
293 case kBGRA_8888_SkColorType:
295 case kRGBA_F16_SkColorType:
297 case kBGR_101010x_XR_SkColorType:
299 case kRGBA_F32_SkColorType:
309 std::shared_ptr<fml::ConcurrentTaskRunner> concurrent_task_runner,
311 bool wide_gamut_enabled,
312 const std::shared_ptr<fml::SyncSwitch>& gpu_disabled_switch)
313 :
ImageDecoder(runners,
std::move(concurrent_task_runner), io_manager),
314 wide_gamut_enabled_(wide_gamut_enabled),
315 gpu_disabled_switch_(gpu_disabled_switch) {
316 std::promise<std::shared_ptr<impeller::Context>> context_promise;
317 context_ = context_promise.get_future();
319 [promise = std::move(context_promise), io_manager]()
mutable {
321 promise.set_value(io_manager->GetImpellerContext());
323 promise.set_value(
nullptr);
330absl::StatusOr<ImageDecoderImpeller::DecompressResult>
335 bool supports_wide_gamut,
336 const std::shared_ptr<const impeller::Capabilities>& capabilities,
337 const std::shared_ptr<impeller::Allocator>&
allocator) {
340 std::string decode_error(
"Invalid descriptor (should never happen)");
342 return absl::InvalidArgumentError(decode_error);
345 const SkISize source_size = SkISize::Make(descriptor->
image_info().width,
347 const SkISize target_size =
348 SkISize::Make(std::min(max_texture_size.
width,
350 std::min(max_texture_size.
height,
354 if (!descriptor->
is_compressed() && source_size == target_size &&
359 desc.
size = descriptor->
data()->size();
360 std::shared_ptr<impeller::DeviceBuffer>
buffer =
363 return absl::ResourceExhaustedError(
"Could not create buffer for image.");
365 sk_sp<SkData>
data = descriptor->
data();
380 SkISize decode_size = source_size;
383 static_cast<float>(target_size.width()) / source_size.width(),
384 static_cast<float>(target_size.height()) / source_size.height()));
387 const SkImageInfo base_image_info =
389 const absl::StatusOr<SkImageInfo> image_info = CreateImageInfo(
390 base_image_info, decode_size, supports_wide_gamut, options.
target_format);
392 if (!image_info.ok()) {
393 return image_info.status();
396 if (!
ToPixelFormat(image_info.value().colorType()).has_value()) {
397 std::string decode_error =
398 std::format(
"Codec pixel format is not supported (SkColorType={})",
399 static_cast<int>(image_info.value().colorType()));
401 return absl::InvalidArgumentError(decode_error);
404 absl::StatusOr<DecodedBitmap> decoded = DecodeToBitmap(
405 descriptor, image_info.value(), base_image_info,
allocator);
407 return decoded.status();
410 absl::StatusOr<DecodedBitmap> premultiplied =
411 HandlePremultiplication(decoded->bitmap, decoded->allocator,
allocator);
412 if (!premultiplied.ok()) {
413 return premultiplied.status();
415 std::shared_ptr<SkBitmap>
bitmap = premultiplied->bitmap;
416 std::shared_ptr<ImpellerAllocator> bitmap_allocator =
417 premultiplied->allocator;
419 if (source_size.width() > max_texture_size.
width ||
420 source_size.height() > max_texture_size.
height ||
421 !capabilities->SupportsTextureToTextureBlits()) {
425 std::shared_ptr<impeller::DeviceBuffer>
buffer =
426 bitmap_allocator->GetDeviceBuffer();
428 return absl::InternalError(
"Unable to get device buffer");
432 std::optional<SkImageInfo> resize_info =
433 bitmap->dimensions() == target_size
435 : std::optional<SkImageInfo>(
436 image_info.value().makeDimensions(target_size));
438 absl::StatusOr<ImageDecoderImpeller::ImageInfo> decoded_image_info =
439 ToImageInfo(
bitmap->info());
440 if (!decoded_image_info.ok()) {
441 return decoded_image_info.status();
446 .image_info = decoded_image_info.value(),
447 .resize_info = resize_info};
451std::pair<sk_sp<DlImage>, std::string>
452ImageDecoderImpeller::UnsafeUploadTextureToPrivate(
453 const std::shared_ptr<impeller::Context>& context,
454 const std::shared_ptr<impeller::DeviceBuffer>& buffer,
456 const std::optional<SkImageInfo>& resize_info) {
463 resize_info.has_value()) {
470 context->GetResourceAllocator()->CreateTexture(texture_descriptor);
472 std::string decode_error(
"Could not create Impeller texture.");
474 return std::make_pair(
nullptr, decode_error);
477 dest_texture->SetLabel(
478 std::format(
"ui.Image({})",
static_cast<const void*
>(dest_texture.get()))
481 auto command_buffer = context->CreateCommandBuffer();
482 if (!command_buffer) {
483 std::string decode_error(
484 "Could not create command buffer for mipmap generation.");
486 return std::make_pair(
nullptr, decode_error);
488 command_buffer->SetLabel(
"Mipmap Command Buffer");
490 auto blit_pass = command_buffer->CreateBlitPass();
492 std::string decode_error(
493 "Could not create blit pass for mipmap generation.");
495 return std::make_pair(
nullptr, decode_error);
497 blit_pass->SetLabel(
"Mipmap Blit Pass");
501 blit_pass->GenerateMipmap(dest_texture);
504 std::shared_ptr<impeller::Texture> result_texture = dest_texture;
505 if (resize_info.has_value()) {
509 resize_desc.
size = {resize_info->
width(), resize_info->height()};
517 auto resize_texture =
518 context->GetResourceAllocator()->CreateTexture(resize_desc);
519 if (!resize_texture) {
520 std::string decode_error(
"Could not create resized Impeller texture.");
522 return std::make_pair(
nullptr, decode_error);
525 blit_pass->ResizeTexture(dest_texture,
528 blit_pass->GenerateMipmap(resize_texture);
531 result_texture = std::move(resize_texture);
533 blit_pass->EncodeCommands();
534 if (!context->GetCommandQueue()
538 if (status == impeller::CommandBuffer::Status::kError) {
540 <<
"GPU Error submitting image decoding command buffer.";
545 std::string decode_error(
"Failed to submit image decoding command buffer.");
547 return std::make_pair(
nullptr, decode_error);
552 if (context->AddTrackingFence(result_texture)) {
553 command_buffer->WaitUntilScheduled();
555 command_buffer->WaitUntilCompleted();
558 context->DisposeThreadLocalCachedResources();
560 return std::make_pair(
565void ImageDecoderImpeller::UploadTextureToPrivate(
567 const std::shared_ptr<impeller::Context>& context,
568 const std::shared_ptr<impeller::DeviceBuffer>& buffer,
570 const std::optional<SkImageInfo>& resize_info,
571 const std::shared_ptr<const fml::SyncSwitch>& gpu_disabled_switch) {
574 result(
nullptr,
"No Impeller context is available");
578 result(
nullptr,
"No Impeller device buffer is available");
582 gpu_disabled_switch->Execute(
584 .SetIfFalse([&result, context,
buffer, image_info, resize_info] {
585 sk_sp<DlImage>
image;
586 std::string decode_error;
587 std::tie(
image, decode_error) = std::tie(
image, decode_error) =
588 UnsafeUploadTextureToPrivate(context,
buffer, image_info,
590 result(
image, decode_error);
592 .SetIfTrue([&result, context,
buffer, image_info, resize_info] {
593 auto result_ptr = std::make_shared<ImageResult>(std::move(result));
594 context->StoreTaskForGPU(
595 [result_ptr, context,
buffer, image_info, resize_info]() {
596 sk_sp<DlImage>
image;
597 std::string decode_error;
598 std::tie(
image, decode_error) = UnsafeUploadTextureToPrivate(
599 context,
buffer, image_info, resize_info);
600 (*result_ptr)(
image, decode_error);
605 "Image upload failed due to loss of GPU access.");
610std::pair<sk_sp<DlImage>, std::string>
611ImageDecoderImpeller::UploadTextureToStorage(
612 const std::shared_ptr<impeller::Context>& context,
613 std::shared_ptr<SkBitmap>
bitmap) {
616 return std::make_pair(
nullptr,
"No Impeller context is available");
619 return std::make_pair(
nullptr,
"No texture bitmap is available");
621 const auto image_info =
bitmap->info();
622 const auto pixel_format =
ToPixelFormat(image_info.colorType());
624 std::string decode_error(
625 std::format(
"Unsupported pixel format (SkColorType={})",
626 static_cast<int>(image_info.colorType())));
628 return std::make_pair(
nullptr, decode_error);
633 texture_descriptor.
format = pixel_format.value();
634 texture_descriptor.
size = {image_info.width(), image_info.height()};
638 context->GetResourceAllocator()->CreateTexture(texture_descriptor);
640 std::string decode_error(
"Could not create Impeller texture.");
642 return std::make_pair(
nullptr, decode_error);
645 auto mapping = std::make_shared<fml::NonOwnedMapping>(
646 reinterpret_cast<const uint8_t*
>(
bitmap->getAddr(0, 0)),
648 [
bitmap](
auto,
auto)
mutable { bitmap.reset(); }
651 if (!
texture->SetContents(mapping)) {
652 std::string decode_error(
"Could not copy contents into Impeller texture.");
654 return std::make_pair(
nullptr, decode_error);
658 std::format(
"ui.Image({})",
static_cast<const void*
>(
texture.get()))
661 context->DisposeThreadLocalCachedResources();
675 auto raw_descriptor = descriptor.
get();
676 raw_descriptor->AddRef();
679 ui_runner = runners_.GetUITaskRunner()
680 ](
const auto&
image,
const auto& decode_error) {
681 ui_runner->PostTask([raw_descriptor, p_result,
image, decode_error]() {
682 raw_descriptor->Release();
683 p_result(std::move(
image), decode_error);
687 concurrent_task_runner_->PostTask(
689 context = context_.get(),
691 io_runner = runners_.GetIOTaskRunner(),
693 wide_gamut_enabled = wide_gamut_enabled_,
694 gpu_disabled_switch = gpu_disabled_switch_]() {
695#if FML_OS_IOS_SIMULATOR
703 result(
nullptr,
"No Impeller context is available");
706 auto max_size_supported =
707 context->GetResourceAllocator()->GetMaxTextureSizeSupported();
710 auto bitmap_result = DecompressTexture(
711 raw_descriptor, options, max_size_supported,
712 wide_gamut_enabled &&
713 context->GetCapabilities()->SupportsExtendedRangeFormats(),
714 context->GetCapabilities(), context->GetResourceAllocator());
715 if (!bitmap_result.ok()) {
716 result(
nullptr, std::string(bitmap_result.status().message()));
720 auto upload_texture_and_invoke_result = [result, context, bitmap_result,
721 gpu_disabled_switch]() {
722 UploadTextureToPrivate(result, context,
723 bitmap_result->device_buffer,
724 bitmap_result->image_info,
725 bitmap_result->resize_info,
730 if (context->GetBackendType() ==
732 io_runner->PostTask(upload_texture_and_invoke_result);
734 upload_texture_and_invoke_result();
739ImpellerAllocator::ImpellerAllocator(
740 std::shared_ptr<impeller::Allocator>
allocator)
752 const SkImageInfo& info =
bitmap->info();
753 if (kUnknown_SkColorType == info.colorType() || info.width() < 0 ||
754 info.height() < 0 || !info.validRowBytes(
bitmap->rowBytes())) {
763 std::shared_ptr<impeller::DeviceBuffer> device_buffer =
764 allocator_->CreateBuffer(descriptor);
765 if (!device_buffer) {
769 struct ImpellerPixelRef final :
public SkPixelRef {
770 ImpellerPixelRef(
int w,
int h,
void* s,
size_t r)
771 : SkPixelRef(w,
h, s, r) {}
773 ~ImpellerPixelRef()
override {}
776 auto pixel_ref = sk_sp<SkPixelRef>(
777 new ImpellerPixelRef(info.width(), info.height(),
778 device_buffer->OnGetContents(),
bitmap->rowBytes()));
780 bitmap->setPixelRef(std::move(pixel_ref), 0, 0);
781 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)