blob: 13fdcdb3b2475bbeb8d5fefd5a93277ec87b2a17 [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/viz/common/resources/shared_image_format.h"
#include <compare>
#include <optional>
#include <type_traits>
#include "base/check_op.h"
#include "base/logging.h"
#include "base/notreached.h"
#include "base/strings/stringprintf.h"
namespace viz {
namespace {
static size_t ConvertBitsToBytes(size_t bits) {
size_t bytes = bits / 8;
// Don't add anything to `bits` to avoid potential overflow.
if ((bits & 7) != 0) {
++bytes;
}
return bytes;
}
const char* SinglePlaneFormatToString(SharedImageFormat format) {
CHECK(format.is_single_plane());
if (format == SinglePlaneFormat::kRGBA_8888) {
return "RGBA_8888";
} else if (format == SinglePlaneFormat::kRGBA_4444) {
return "RGBA_4444";
} else if (format == SinglePlaneFormat::kBGRA_8888) {
return "BGRA_8888";
} else if (format == SinglePlaneFormat::kALPHA_8) {
return "ALPHA_8";
} else if (format == SinglePlaneFormat::kLUMINANCE_8) {
return "LUMINANCE_8";
} else if (format == SinglePlaneFormat::kRGB_565) {
return "RGB_565";
} else if (format == SinglePlaneFormat::kBGR_565) {
return "BGR_565";
} else if (format == SinglePlaneFormat::kETC1) {
return "ETC1";
} else if (format == SinglePlaneFormat::kR_8) {
return "R_8";
} else if (format == SinglePlaneFormat::kRG_88) {
return "RG_88";
} else if (format == SinglePlaneFormat::kLUMINANCE_F16) {
return "LUMINANCE_F16";
} else if (format == SinglePlaneFormat::kRGBA_F16) {
return "RGBA_F16";
} else if (format == SinglePlaneFormat::kR_16) {
return "R_16";
} else if (format == SinglePlaneFormat::kRG_1616) {
return "RG_1616";
} else if (format == SinglePlaneFormat::kRGBX_8888) {
return "RGBX_8888";
} else if (format == SinglePlaneFormat::kBGRX_8888) {
return "BGRX_8888";
} else if (format == SinglePlaneFormat::kRGBA_1010102) {
return "RGBA_1010102";
} else if (format == SinglePlaneFormat::kBGRA_1010102) {
return "BGRA_1010102";
} else if (format == LegacyMultiPlaneFormat::kYV12) {
return "YV12_LEGACY";
} else if (format == LegacyMultiPlaneFormat::kNV12) {
return "NV12_LEGACY";
} else if (format == LegacyMultiPlaneFormat::kNV12A) {
return "NV12A_LEGACY";
} else if (format == LegacyMultiPlaneFormat::kP010) {
return "P010_LEGACY";
} else if (format == SinglePlaneFormat::kR_F16) {
return "R_F16";
}
NOTREACHED_NORETURN();
}
int BitsPerPixelForTrueSinglePlaneFormat(SharedImageFormat format) {
CHECK(format.is_single_plane() && !format.IsLegacyMultiplanar());
if (format == SinglePlaneFormat::kRGBA_F16) {
return 64;
} else if (format == SinglePlaneFormat::kBGRA_8888 ||
format == SinglePlaneFormat::kRGBA_8888 ||
format == SinglePlaneFormat::kRGBX_8888 ||
format == SinglePlaneFormat::kBGRX_8888 ||
format == SinglePlaneFormat::kRGBA_1010102 ||
format == SinglePlaneFormat::kBGRA_1010102 ||
format == SinglePlaneFormat::kRG_1616) {
return 32;
} else if (format == SinglePlaneFormat::kRGBA_4444 ||
format == SinglePlaneFormat::kRGB_565 ||
format == SinglePlaneFormat::kLUMINANCE_F16 ||
format == SinglePlaneFormat::kR_F16 ||
format == SinglePlaneFormat::kR_16 ||
format == SinglePlaneFormat::kBGR_565 ||
format == SinglePlaneFormat::kRG_88) {
return 16;
} else if (format == SinglePlaneFormat::kALPHA_8 ||
format == SinglePlaneFormat::kLUMINANCE_8 ||
format == SinglePlaneFormat::kR_8) {
return 8;
} else if (format == SinglePlaneFormat::kETC1) {
return 4;
}
NOTREACHED_NORETURN();
}
uint64_t StorageBytesPerElement(SharedImageFormat::ChannelFormat channel) {
switch (channel) {
case SharedImageFormat::ChannelFormat::k8:
return 1;
// 10 bit formats like P010 still use 2 bytes per element.
case SharedImageFormat::ChannelFormat::k10:
case SharedImageFormat::ChannelFormat::k16:
case SharedImageFormat::ChannelFormat::k16F:
return 2;
}
}
const char* PlaneConfigToString(SharedImageFormat::PlaneConfig plane) {
switch (plane) {
case SharedImageFormat::PlaneConfig::kY_U_V:
return "Y_U_V";
case SharedImageFormat::PlaneConfig::kY_V_U:
return "Y_V_U";
case SharedImageFormat::PlaneConfig::kY_UV:
return "Y_UV";
case SharedImageFormat::PlaneConfig::kY_UV_A:
return "Y_UV_A";
case SharedImageFormat::PlaneConfig::kY_U_V_A:
return "Y_U_V_A";
}
}
const char* SubsamplingToString(SharedImageFormat::Subsampling subsampling) {
switch (subsampling) {
case SharedImageFormat::Subsampling::k420:
return "420";
case SharedImageFormat::Subsampling::k422:
return "422";
case SharedImageFormat::Subsampling::k444:
return "444";
}
}
const char* ChannelFormatToString(SharedImageFormat::ChannelFormat channel) {
switch (channel) {
case SharedImageFormat::ChannelFormat::k8:
return "8unorm";
case SharedImageFormat::ChannelFormat::k10:
return "10unorm";
case SharedImageFormat::ChannelFormat::k16:
return "16unorm";
case SharedImageFormat::ChannelFormat::k16F:
return "16float";
}
}
const char* PrefersExternalSamplerToString(SharedImageFormat format) {
return format.PrefersExternalSampler() ? "ExtSamplerOn" : "ExtSamplerOff";
}
SharedImageFormat GetEquivalentMultiplanarFormat(SharedImageFormat format) {
if (format == LegacyMultiPlaneFormat::kYV12) {
return MultiPlaneFormat::kYV12;
} else if (format == LegacyMultiPlaneFormat::kNV12) {
return MultiPlaneFormat::kNV12;
} else if (format == LegacyMultiPlaneFormat::kNV12A) {
return MultiPlaneFormat::kNV12A;
} else if (format == LegacyMultiPlaneFormat::kP010) {
return MultiPlaneFormat::kP010;
}
NOTREACHED_IN_MIGRATION();
return SinglePlaneFormat::kRGBA_8888;
}
} // namespace
// Ensure that SharedImageFormat is suitable for passing around by value.
static_assert(sizeof(SharedImageFormat) <= 8);
static_assert(std::is_trivially_destructible_v<SharedImageFormat>);
static_assert(std::is_trivially_copyable_v<SharedImageFormat>);
// TODO(kylechar): Ideally SharedImageFormat would be "trivially comparable" so
// that operator==() is just memcmp(). That would probably require something
// like manually packing bits into a single uint64_t for storage.
bool SharedImageFormat::IsBitmapFormatSupported() const {
return is_single_plane() &&
singleplanar_format() == mojom::SingleplanarFormat::RGBA_8888;
}
int SharedImageFormat::NumberOfPlanes() const {
if (is_single_plane()) {
return 1;
}
switch (plane_config()) {
case PlaneConfig::kY_U_V:
case PlaneConfig::kY_V_U:
return 3;
case PlaneConfig::kY_UV:
return 2;
case PlaneConfig::kY_UV_A:
return 3;
case PlaneConfig::kY_U_V_A:
return 4;
}
}
bool SharedImageFormat::IsValidPlaneIndex(int plane_index) const {
return plane_index >= 0 && plane_index < NumberOfPlanes();
}
std::optional<size_t> SharedImageFormat::MaybeEstimatedPlaneSizeInBytes(
int plane_index,
const gfx::Size& size) const {
DCHECK(!size.IsEmpty());
if (is_single_plane()) {
if (IsLegacyMultiplanar()) {
return GetEquivalentMultiplanarFormat(*this)
.MaybeEstimatedPlaneSizeInBytes(plane_index, size);
}
DCHECK_EQ(plane_index, 0);
base::CheckedNumeric<size_t> bits_per_row =
BitsPerPixelForTrueSinglePlaneFormat(*this);
bits_per_row *= size.width();
if (!bits_per_row.IsValid()) {
return std::nullopt;
}
base::CheckedNumeric<size_t> estimated_bytes =
ConvertBitsToBytes(bits_per_row.ValueOrDie());
estimated_bytes *= size.height();
if (!estimated_bytes.IsValid()) {
return std::nullopt;
}
return estimated_bytes.ValueOrDie();
}
size_t bytes_per_element = StorageBytesPerElement(channel_format());
gfx::Size plane_size = GetPlaneSize(plane_index, size);
base::CheckedNumeric<size_t> plane_estimated_bytes =
bytes_per_element * NumChannelsInPlane(plane_index);
DCHECK(plane_estimated_bytes.IsValid());
plane_estimated_bytes *= plane_size.width();
plane_estimated_bytes *= plane_size.height();
if (!plane_estimated_bytes.IsValid()) {
return std::nullopt;
}
return plane_estimated_bytes.ValueOrDie();
}
std::optional<size_t> SharedImageFormat::MaybeEstimatedSizeInBytes(
const gfx::Size& size) const {
DCHECK(!size.IsEmpty());
if (is_single_plane()) {
if (IsLegacyMultiplanar()) {
return GetEquivalentMultiplanarFormat(*this).MaybeEstimatedSizeInBytes(
size);
}
return MaybeEstimatedPlaneSizeInBytes(0, size);
}
base::CheckedNumeric<size_t> total_estimated_bytes = 0;
for (int plane_index = 0; plane_index < NumberOfPlanes(); ++plane_index) {
std::optional<size_t> plane_estimated_bytes =
MaybeEstimatedPlaneSizeInBytes(plane_index, size);
if (!plane_estimated_bytes.has_value()) {
return std::nullopt;
}
total_estimated_bytes += plane_estimated_bytes.value();
if (!total_estimated_bytes.IsValid()) {
return std::nullopt;
}
}
return total_estimated_bytes.ValueOrDie();
}
size_t SharedImageFormat::EstimatedSizeInBytes(const gfx::Size& size) const {
return MaybeEstimatedSizeInBytes(size).value_or(0);
}
bool SharedImageFormat::VerifySizeInBytes(const gfx::Size& size) const {
return MaybeEstimatedSizeInBytes(size).has_value();
}
gfx::Size SharedImageFormat::GetPlaneSize(int plane_index,
const gfx::Size& size) const {
DCHECK(IsValidPlaneIndex(plane_index));
if (is_single_plane()) {
return size;
}
// First plane is always Y plane and it is always size (not subsampled).
if (plane_index == 0) {
return size;
}
// A plane is always size
if (plane_config() == PlaneConfig::kY_UV_A && plane_index == 2) {
return size;
}
if (plane_config() == PlaneConfig::kY_U_V_A && plane_index == 3) {
return size;
}
// UV scales
float width_scale = 1.0;
float height_scale = 1.0;
switch (subsampling()) {
case Subsampling::k420:
width_scale = 0.5;
height_scale = 0.5;
break;
case Subsampling::k422:
width_scale = 0.5;
break;
case Subsampling::k444:
break;
}
return gfx::ScaleToCeiledSize(size, width_scale, height_scale);
}
// For multiplanar formats.
int SharedImageFormat::NumChannelsInPlane(int plane_index) const {
DCHECK(IsValidPlaneIndex(plane_index));
switch (plane_config()) {
case PlaneConfig::kY_U_V:
case PlaneConfig::kY_V_U:
case PlaneConfig::kY_U_V_A:
return 1;
case PlaneConfig::kY_UV:
return plane_index == 1 ? 2 : 1;
case PlaneConfig::kY_UV_A:
return plane_index == 1 ? 2 : 1;
}
NOTREACHED_IN_MIGRATION();
return 0;
}
// For multiplanar formats.
int SharedImageFormat::MultiplanarBitDepth() const {
switch (channel_format()) {
case ChannelFormat::k8:
return 8;
case ChannelFormat::k10:
return 10;
case ChannelFormat::k16:
case ChannelFormat::k16F:
return 16;
}
NOTREACHED_IN_MIGRATION();
return 0;
}
std::string SharedImageFormat::ToString() const {
switch (plane_type_) {
case PlaneType::kUnknown:
return "Unknown";
case PlaneType::kSinglePlane:
return SinglePlaneFormatToString(*this);
case PlaneType::kMultiPlane:
return base::StringPrintf("(%s, %s, %s, %s)",
PlaneConfigToString(plane_config()),
SubsamplingToString(subsampling()),
ChannelFormatToString(channel_format()),
PrefersExternalSamplerToString(*this));
}
}
std::string SharedImageFormat::ToTestParamString() const {
switch (plane_type_) {
case PlaneType::kUnknown:
return "Unknown";
case PlaneType::kSinglePlane:
return SinglePlaneFormatToString(*this);
case PlaneType::kMultiPlane:
return base::StringPrintf("%s_%s_%s_%s",
PlaneConfigToString(plane_config()),
SubsamplingToString(subsampling()),
ChannelFormatToString(channel_format()),
PrefersExternalSamplerToString(*this));
}
}
bool SharedImageFormat::HasAlpha() const {
if (is_single_plane()) {
switch (singleplanar_format()) {
case mojom::SingleplanarFormat::RGBA_8888:
case mojom::SingleplanarFormat::RGBA_4444:
case mojom::SingleplanarFormat::RGBA_1010102:
case mojom::SingleplanarFormat::BGRA_8888:
case mojom::SingleplanarFormat::BGRA_1010102:
case mojom::SingleplanarFormat::ALPHA_8:
case mojom::SingleplanarFormat::RGBA_F16:
case mojom::SingleplanarFormat::NV12A_LEGACY:
return true;
default:
return false;
}
}
switch (plane_config()) {
case PlaneConfig::kY_U_V:
case PlaneConfig::kY_V_U:
case PlaneConfig::kY_UV:
return false;
case PlaneConfig::kY_UV_A:
case PlaneConfig::kY_U_V_A:
return true;
}
}
bool SharedImageFormat::IsCompressed() const {
return is_single_plane() &&
singleplanar_format() == mojom::SingleplanarFormat::ETC1;
}
bool SharedImageFormat::IsLegacyMultiplanar() const {
if (!is_single_plane()) {
return false;
}
switch (singleplanar_format()) {
case mojom::SingleplanarFormat::YV12_LEGACY:
case mojom::SingleplanarFormat::NV12_LEGACY:
case mojom::SingleplanarFormat::NV12A_LEGACY:
case mojom::SingleplanarFormat::P010_LEGACY:
return true;
default:
return false;
}
}
int SharedImageFormat::BitsPerPixel() const {
CHECK(is_single_plane());
switch (singleplanar_format()) {
case mojom::SingleplanarFormat::RGBA_F16:
return 64;
case mojom::SingleplanarFormat::BGRA_8888:
case mojom::SingleplanarFormat::RGBA_8888:
case mojom::SingleplanarFormat::RGBX_8888:
case mojom::SingleplanarFormat::BGRX_8888:
case mojom::SingleplanarFormat::RGBA_1010102:
case mojom::SingleplanarFormat::BGRA_1010102:
case mojom::SingleplanarFormat::RG_1616:
return 32;
case mojom::SingleplanarFormat::RGBA_4444:
case mojom::SingleplanarFormat::RGB_565:
case mojom::SingleplanarFormat::LUMINANCE_F16:
case mojom::SingleplanarFormat::R_F16:
case mojom::SingleplanarFormat::R_16:
case mojom::SingleplanarFormat::BGR_565:
case mojom::SingleplanarFormat::RG_88:
return 16;
case mojom::SingleplanarFormat::ALPHA_8:
case mojom::SingleplanarFormat::LUMINANCE_8:
case mojom::SingleplanarFormat::R_8:
return 8;
case mojom::SingleplanarFormat::ETC1:
return 4;
case mojom::SingleplanarFormat::P010_LEGACY:
case mojom::SingleplanarFormat::NV12A_LEGACY:
case mojom::SingleplanarFormat::YV12_LEGACY:
case mojom::SingleplanarFormat::NV12_LEGACY:
// Legacy multiplanar formats are not supported.
CHECK(0);
}
NOTREACHED_NORETURN();
}
bool SharedImageFormat::operator==(const SharedImageFormat& o) const {
if (plane_type_ != o.plane_type()) {
return false;
}
switch (plane_type_) {
case PlaneType::kUnknown:
return true;
case PlaneType::kSinglePlane:
return singleplanar_format() == o.singleplanar_format();
case PlaneType::kMultiPlane:
return multiplanar_format() == o.multiplanar_format();
}
}
std::weak_ordering SharedImageFormat::operator<=>(
const SharedImageFormat& o) const {
if (plane_type_ != o.plane_type()) {
return plane_type_ <=> o.plane_type();
}
switch (plane_type_) {
case PlaneType::kUnknown:
return std::weak_ordering::equivalent;
case PlaneType::kSinglePlane:
return singleplanar_format() <=> o.singleplanar_format();
case PlaneType::kMultiPlane:
return multiplanar_format() <=> o.multiplanar_format();
}
}
bool SharedImageFormat::SharedImageFormatUnion::MultiplanarFormat::operator==(
const MultiplanarFormat& o) const {
return plane_config == o.plane_config && subsampling == o.subsampling &&
channel_format == o.channel_format;
}
std::weak_ordering
SharedImageFormat::SharedImageFormatUnion::MultiplanarFormat::operator<=>(
const MultiplanarFormat& o) const {
return std::tie(plane_config, subsampling, channel_format) <=>
std::tie(o.plane_config, o.subsampling, o.channel_format);
}
} // namespace viz