blob: e48ae50e5c917626abafeb5441a1bf8b83361c52 [file] [log] [blame]
// Copyright 2023 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_utils.h"
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <array>
#include "base/check_op.h"
#include "base/logging.h"
#include "base/notreached.h"
#include "components/viz/common/resources/shared_image_format.h"
#include "third_party/skia/include/core/SkColorType.h"
#include "ui/gfx/buffer_types.h"
namespace viz {
namespace {
constexpr auto kMappableSharedImageFormats = std::to_array<SharedImageFormat>(
{SinglePlaneFormat::kR_8, SinglePlaneFormat::kR_16,
SinglePlaneFormat::kRG_88, SinglePlaneFormat::kRG_1616,
SinglePlaneFormat::kBGR_565, SinglePlaneFormat::kRGBA_4444,
SinglePlaneFormat::kRGBX_8888, SinglePlaneFormat::kRGBA_8888,
SinglePlaneFormat::kBGRX_8888, SinglePlaneFormat::kBGRA_1010102,
SinglePlaneFormat::kRGBA_1010102, SinglePlaneFormat::kBGRA_8888,
SinglePlaneFormat::kRGBA_F16, MultiPlaneFormat::kNV12,
MultiPlaneFormat::kYV12, MultiPlaneFormat::kNV12A,
MultiPlaneFormat::kP010});
} // namespace
SkColorType ToClosestSkColorType(SharedImageFormat format) {
CHECK(format.is_single_plane());
if (format == SinglePlaneFormat::kRGBA_4444) {
return kARGB_4444_SkColorType;
} else if (format == SinglePlaneFormat::kRGBA_8888) {
return kRGBA_8888_SkColorType;
} else if (format == SinglePlaneFormat::kBGRA_8888) {
return kBGRA_8888_SkColorType;
} else if (format == SinglePlaneFormat::kALPHA_8) {
return kAlpha_8_SkColorType;
} else if (format == SinglePlaneFormat::kBGR_565) {
return kRGB_565_SkColorType;
} else if (format == SinglePlaneFormat::kRGBX_8888 ||
format == SinglePlaneFormat::kBGRX_8888 ||
format == SinglePlaneFormat::kETC1) {
return kRGB_888x_SkColorType;
} else if (format == SinglePlaneFormat::kRGBA_1010102 ||
// This intentionally returns kRGBA_1010102_SkColorType for
// BGRA_1010102 even though kBGRA_1010102_SkColorType exists. It
// should only be used on macOS (outside of tests).
format == SinglePlaneFormat::kBGRA_1010102) {
return kRGBA_1010102_SkColorType;
} else if (format == SinglePlaneFormat::kR_8) {
return kAlpha_8_SkColorType;
} else if (format == SinglePlaneFormat::kR_16) {
return kA16_unorm_SkColorType;
} else if (format == SinglePlaneFormat::kRG_1616) {
return kR16G16_unorm_SkColorType;
} else if (format == SinglePlaneFormat::kLUMINANCE_F16 ||
format == SinglePlaneFormat::kR_F16) {
return kA16_float_SkColorType;
} else if (format == SinglePlaneFormat::kRG_88) {
return kR8G8_unorm_SkColorType;
} else if (format == SinglePlaneFormat::kRGBA_F16) {
return kRGBA_F16_SkColorType;
}
NOTREACHED();
}
SkColorType ToClosestSkColorType(SharedImageFormat format, int plane_index) {
CHECK(format.IsValidPlaneIndex(plane_index));
if (format.is_single_plane()) {
return ToClosestSkColorType(format);
}
// No external sampling, format is per plane.
CHECK(!format.PrefersExternalSampler());
int num_channels = format.NumChannelsInPlane(plane_index);
DCHECK_GT(num_channels, 0);
DCHECK_LE(num_channels, 2);
switch (format.channel_format()) {
case SharedImageFormat::ChannelFormat::k8:
return num_channels == 1 ? kAlpha_8_SkColorType : kR8G8_unorm_SkColorType;
case SharedImageFormat::ChannelFormat::k10:
case SharedImageFormat::ChannelFormat::k16:
return num_channels == 1 ? kA16_unorm_SkColorType
: kR16G16_unorm_SkColorType;
case SharedImageFormat::ChannelFormat::k16F:
return num_channels == 1 ? kA16_float_SkColorType
: kR16G16_float_SkColorType;
}
}
SharedImageFormat SkColorTypeToSinglePlaneSharedImageFormat(
SkColorType color_type) {
switch (color_type) {
case kARGB_4444_SkColorType:
return SinglePlaneFormat::kRGBA_4444;
case kBGRA_8888_SkColorType:
return SinglePlaneFormat::kBGRA_8888;
case kRGBA_8888_SkColorType:
return SinglePlaneFormat::kRGBA_8888;
case kRGBA_F16_SkColorType:
return SinglePlaneFormat::kRGBA_F16;
case kAlpha_8_SkColorType:
return SinglePlaneFormat::kALPHA_8;
case kRGB_565_SkColorType:
return SinglePlaneFormat::kBGR_565;
case kRGB_888x_SkColorType:
return SinglePlaneFormat::kRGBX_8888;
case kRGBA_1010102_SkColorType:
return SinglePlaneFormat::kRGBA_1010102;
case kBGRA_1010102_SkColorType:
return SinglePlaneFormat::kBGRA_1010102;
case kR8G8_unorm_SkColorType:
return SinglePlaneFormat::kRG_88;
case kA16_float_SkColorType:
return SinglePlaneFormat::kR_F16;
case kA16_unorm_SkColorType:
return SinglePlaneFormat::kR_16;
case kR16G16_unorm_SkColorType:
return SinglePlaneFormat::kRG_1616;
// These colortypes are just for reading from - not to render to.
case kR16G16_float_SkColorType:
case kR16G16B16A16_unorm_SkColorType:
case kUnknown_SkColorType:
// These colortypes are don't have an equivalent in SharedImageFormat.
case kRGB_101010x_SkColorType:
case kBGR_101010x_SkColorType:
case kRGBA_F16Norm_SkColorType:
case kRGBA_F32_SkColorType:
case kSRGBA_8888_SkColorType:
// Default case is for new color types added to Skia.
default:
break;
}
NOTREACHED();
}
bool CanCreateGpuMemoryBufferForSinglePlaneSharedImageFormat(
SharedImageFormat format) {
CHECK(format.is_single_plane());
return (format == SinglePlaneFormat::kBGRA_8888 ||
format == SinglePlaneFormat::kR_8 ||
format == SinglePlaneFormat::kRG_88 ||
#if BUILDFLAG(IS_APPLE)
format == SinglePlaneFormat::kBGRX_8888 ||
format == SinglePlaneFormat::kRGBX_8888 ||
format == SinglePlaneFormat::kR_16 ||
format == SinglePlaneFormat::kRG_1616 ||
#endif
format == SinglePlaneFormat::kRGBA_4444 ||
format == SinglePlaneFormat::kRGBA_8888 ||
format == SinglePlaneFormat::kRGBA_1010102 ||
format == SinglePlaneFormat::kBGRA_1010102 ||
format == SinglePlaneFormat::kRGBA_F16);
}
bool HasEquivalentBufferFormat(SharedImageFormat format) {
return format == SinglePlaneFormat::kBGRA_8888 ||
format == SinglePlaneFormat::kR_8 ||
format == SinglePlaneFormat::kR_16 ||
format == SinglePlaneFormat::kRG_1616 ||
format == SinglePlaneFormat::kRGBA_4444 ||
format == SinglePlaneFormat::kRGBA_8888 ||
format == SinglePlaneFormat::kRGBA_F16 ||
format == SinglePlaneFormat::kBGR_565 ||
format == SinglePlaneFormat::kRG_88 ||
format == SinglePlaneFormat::kRGBX_8888 ||
format == SinglePlaneFormat::kBGRX_8888 ||
format == SinglePlaneFormat::kRGBA_1010102 ||
format == SinglePlaneFormat::kBGRA_1010102 ||
format == MultiPlaneFormat::kYV12 ||
format == MultiPlaneFormat::kNV12 ||
format == MultiPlaneFormat::kNV12A ||
format == MultiPlaneFormat::kP010;
}
gfx::BufferFormat SinglePlaneSharedImageFormatToBufferFormat(
SharedImageFormat format) {
CHECK(format.is_single_plane());
if (format == SinglePlaneFormat::kBGRA_8888) {
return gfx::BufferFormat::BGRA_8888;
} else if (format == SinglePlaneFormat::kR_8) {
return gfx::BufferFormat::R_8;
} else if (format == SinglePlaneFormat::kR_16) {
return gfx::BufferFormat::R_16;
} else if (format == SinglePlaneFormat::kRG_1616) {
return gfx::BufferFormat::RG_1616;
} else if (format == SinglePlaneFormat::kRGBA_4444) {
return gfx::BufferFormat::RGBA_4444;
} else if (format == SinglePlaneFormat::kRGBA_8888) {
return gfx::BufferFormat::RGBA_8888;
} else if (format == SinglePlaneFormat::kRGBA_F16) {
return gfx::BufferFormat::RGBA_F16;
} else if (format == SinglePlaneFormat::kBGR_565) {
return gfx::BufferFormat::BGR_565;
} else if (format == SinglePlaneFormat::kRG_88) {
return gfx::BufferFormat::RG_88;
} else if (format == SinglePlaneFormat::kRGBX_8888) {
return gfx::BufferFormat::RGBX_8888;
} else if (format == SinglePlaneFormat::kBGRX_8888) {
return gfx::BufferFormat::BGRX_8888;
} else if (format == SinglePlaneFormat::kRGBA_1010102) {
return gfx::BufferFormat::RGBA_1010102;
} else if (format == SinglePlaneFormat::kBGRA_1010102) {
return gfx::BufferFormat::BGRA_1010102;
} else {
// CanCreateGpuMemoryBufferForSinglePlaneSharedImageFormat() returns
// false for all other types, so give a default value that will not be used.
return gfx::BufferFormat::RGBA_8888;
}
}
SharedImageFormat GetSharedImageFormat(gfx::BufferFormat buffer_format) {
switch (buffer_format) {
case gfx::BufferFormat::BGRA_8888:
return SinglePlaneFormat::kBGRA_8888;
case gfx::BufferFormat::R_8:
return SinglePlaneFormat::kR_8;
case gfx::BufferFormat::R_16:
return SinglePlaneFormat::kR_16;
case gfx::BufferFormat::RG_1616:
return SinglePlaneFormat::kRG_1616;
case gfx::BufferFormat::RGBA_4444:
return SinglePlaneFormat::kRGBA_4444;
case gfx::BufferFormat::RGBA_8888:
return SinglePlaneFormat::kRGBA_8888;
case gfx::BufferFormat::RGBA_F16:
return SinglePlaneFormat::kRGBA_F16;
case gfx::BufferFormat::BGR_565:
return SinglePlaneFormat::kBGR_565;
case gfx::BufferFormat::RG_88:
return SinglePlaneFormat::kRG_88;
case gfx::BufferFormat::RGBX_8888:
return SinglePlaneFormat::kRGBX_8888;
case gfx::BufferFormat::BGRX_8888:
return SinglePlaneFormat::kBGRX_8888;
case gfx::BufferFormat::RGBA_1010102:
return SinglePlaneFormat::kRGBA_1010102;
case gfx::BufferFormat::BGRA_1010102:
return SinglePlaneFormat::kBGRA_1010102;
case gfx::BufferFormat::YVU_420:
return MultiPlaneFormat::kYV12;
case gfx::BufferFormat::YUV_420_BIPLANAR:
return MultiPlaneFormat::kNV12;
case gfx::BufferFormat::YUVA_420_TRIPLANAR:
return MultiPlaneFormat::kNV12A;
case gfx::BufferFormat::P010:
return MultiPlaneFormat::kP010;
}
NOTREACHED();
}
size_t SharedMemoryOffsetForSharedImageFormat(SharedImageFormat format,
int plane_index,
const gfx::Size& size) {
if (format.is_single_plane()) {
return 0;
}
size_t offset = 0;
for (int plane = 0; plane < plane_index; ++plane) {
std::optional<size_t> plane_size =
format.MaybeEstimatedPlaneSizeInBytes(plane, size);
if (!plane_size) {
DLOG(ERROR) << "Could not calculate plane size for plane " << plane;
return 0;
}
offset += *plane_size;
}
return offset;
}
std::optional<size_t> SharedMemoryRowSizeForSharedImageFormat(
SharedImageFormat format,
int plane_index,
int width) {
if (!format.IsValidPlaneIndex(plane_index)) {
return std::nullopt;
}
if (format.is_single_plane()) {
CHECK_NE(format, SinglePlaneFormat::kETC1);
DCHECK_EQ(plane_index, 0);
base::CheckedNumeric<size_t> bytes_per_row = format.BytesPerPixel();
bytes_per_row *= width;
// Row size must be aligned to 4 bytes.
bytes_per_row += 3;
bytes_per_row -= bytes_per_row % 4;
if (!bytes_per_row.IsValid()) {
return std::nullopt;
}
return bytes_per_row.ValueOrDie();
}
int plane_width =
format.GetPlaneSize(plane_index, gfx::Size(width, 0)).width();
int num_channels = format.NumChannelsInPlane(plane_index);
base::CheckedNumeric<size_t> bytes_per_row =
format.MultiplanarStorageBytesPerChannel();
bytes_per_row *= num_channels;
bytes_per_row *= plane_width;
if (!bytes_per_row.IsValid()) {
return std::nullopt;
}
return bytes_per_row.ValueOrDie();
}
std::optional<size_t> SharedMemoryPlaneSizeForSharedImageFormat(
SharedImageFormat format,
int plane_index,
const gfx::Size& size) {
std::optional<size_t> row_size = SharedMemoryRowSizeForSharedImageFormat(
format, plane_index, base::checked_cast<size_t>(size.width()));
if (!row_size) {
return std::nullopt;
}
base::CheckedNumeric<size_t> plane_size = row_size.value();
plane_size *= format.GetPlaneSize(plane_index, size).height();
if (!plane_size.IsValid()) {
return std::nullopt;
}
return plane_size.ValueOrDie();
}
std::optional<size_t> SharedMemorySizeForSharedImageFormat(
SharedImageFormat format,
const gfx::Size& size) {
base::CheckedNumeric<size_t> buffer_size = 0;
for (int plane = 0; plane < format.NumberOfPlanes(); plane++) {
auto plane_size =
SharedMemoryPlaneSizeForSharedImageFormat(format, plane, size);
if (!plane_size) {
return std::nullopt;
}
buffer_size += plane_size.value();
if (!buffer_size.IsValid()) {
return std::nullopt;
}
}
return buffer_size.ValueOrDie();
}
bool IsOddSizeMultiPlanarBuffersAllowed() {
#if BUILDFLAG(IS_APPLE)
return true;
#else
return false;
#endif
}
base::span<const SharedImageFormat> GetMappableSharedImageFormatForTesting() {
return kMappableSharedImageFormats;
}
gfx::BufferFormat SharedImageFormatToBufferFormat(SharedImageFormat format) {
if (!HasEquivalentBufferFormat(format)) {
DUMP_WILL_BE_NOTREACHED() << "format=" << format.ToString();
return gfx::BufferFormat::RGBA_8888;
}
if (format.is_single_plane()) {
return SinglePlaneSharedImageFormatToBufferFormat(format);
}
if (format == MultiPlaneFormat::kYV12) {
return gfx::BufferFormat::YVU_420;
} else if (format == MultiPlaneFormat::kNV12) {
return gfx::BufferFormat::YUV_420_BIPLANAR;
} else if (format == MultiPlaneFormat::kNV12A) {
return gfx::BufferFormat::YUVA_420_TRIPLANAR;
} else if (format == MultiPlaneFormat::kP010) {
return gfx::BufferFormat::P010;
}
DUMP_WILL_BE_NOTREACHED() << "format=" << format.ToString();
return gfx::BufferFormat::RGBA_8888;
}
} // namespace viz