1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156

gpu / command_buffer / service / shared_image / shared_image_format_service_utils_mac.mm [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 "gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h"

#include <CoreVideo/CoreVideo.h>
#include <Metal/Metal.h>

#include "base/check_op.h"
#include "base/logging.h"
#include "base/notreached.h"
#include "components/viz/common/resources/shared_image_format.h"

#if BUILDFLAG(SKIA_USE_METAL)
#include "third_party/skia/include/gpu/graphite/mtl/MtlGraphiteTypes.h"
#endif

namespace gpu {

uint32_t SharedImageFormatToIOSurfacePixelFormat(viz::SharedImageFormat format,
                                                 bool override_rgba_to_bgra) {
  if (format.is_single_plane()) {
    if (format == viz::SinglePlaneFormat::kR_8) {
      return kCVPixelFormatType_OneComponent8;
    } else if (format == viz::SinglePlaneFormat::kRG_88) {
      return kCVPixelFormatType_TwoComponent8;
    } else if (format == viz::SinglePlaneFormat::kR_16) {
      return kCVPixelFormatType_OneComponent16;
    } else if (format == viz::SinglePlaneFormat::kRG_1616) {
      return kCVPixelFormatType_TwoComponent16;
    } else if (format == viz::SinglePlaneFormat::kBGRA_1010102) {
      return kCVPixelFormatType_ARGB2101010LEPacked;
    } else if (format == viz::SinglePlaneFormat::kBGRA_8888 ||
               format == viz::SinglePlaneFormat::kBGRX_8888) {
      return kCVPixelFormatType_32BGRA;
    } else if (format == viz::SinglePlaneFormat::kRGBA_8888 ||
               format == viz::SinglePlaneFormat::kRGBX_8888) {
      return override_rgba_to_bgra ? kCVPixelFormatType_32BGRA
                                   : kCVPixelFormatType_32RGBA;
    } else if (format == viz::SinglePlaneFormat::kRGBA_F16) {
      return kCVPixelFormatType_64RGBAHalf;
    } else if (format == viz::SinglePlaneFormat::kBGR_565 ||
               format == viz::SinglePlaneFormat::kRGBA_4444 ||
               format == viz::SinglePlaneFormat::kRGBA_1010102) {
      // Technically RGBA_1010102 should be accepted as 'R10k', but
      // then it won't be supported by CGLTexImageIOSurface2D(), so
      // it's best to reject it here.
      return 0;
    }
  } else if (format.is_multi_plane()) {
    if (format == viz::MultiPlaneFormat::kNV12) {
      return kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange;
    } else if (format == viz::MultiPlaneFormat::kNV16) {
      return kCVPixelFormatType_422YpCbCr8BiPlanarVideoRange;
    } else if (format == viz::MultiPlaneFormat::kNV24) {
      return kCVPixelFormatType_444YpCbCr8BiPlanarVideoRange;
    } else if (format == viz::MultiPlaneFormat::kNV12A) {
      return kCVPixelFormatType_420YpCbCr8VideoRange_8A_TriPlanar;
    } else if (format == viz::MultiPlaneFormat::kP010) {
      return kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange;
    } else if (format == viz::MultiPlaneFormat::kP210) {
      return kCVPixelFormatType_422YpCbCr10BiPlanarVideoRange;
    } else if (format == viz::MultiPlaneFormat::kP410) {
      return kCVPixelFormatType_444YpCbCr10BiPlanarVideoRange;
    } else if (format == viz::MultiPlaneFormat::kI420) {
      return kCVPixelFormatType_420YpCbCr8Planar;
    } else if (format == viz::MultiPlaneFormat::kYV12) {
      return 0;
    }
  }
  NOTREACHED();
}

unsigned int ToMTLPixelFormat(viz::SharedImageFormat format, int plane_index) {
  MTLPixelFormat mtl_pixel_format = MTLPixelFormatInvalid;
  if (format.is_single_plane()) {
    if (format == viz::SinglePlaneFormat::kR_8 ||
        format == viz::SinglePlaneFormat::kALPHA_8 ||
        format == viz::SinglePlaneFormat::kLUMINANCE_8) {
      mtl_pixel_format = MTLPixelFormatR8Unorm;
    } else if (format == viz::SinglePlaneFormat::kRG_88) {
      mtl_pixel_format = MTLPixelFormatRG8Unorm;
    } else if (format == viz::SinglePlaneFormat::kRGBA_8888) {
      mtl_pixel_format = MTLPixelFormatRGBA8Unorm;
    } else if (format == viz::SinglePlaneFormat::kBGRA_8888) {
      mtl_pixel_format = MTLPixelFormatBGRA8Unorm;
    } else {
      DLOG(ERROR) << "Invalid Metal pixel format:" << format.ToString();
    }
    return static_cast<unsigned int>(mtl_pixel_format);
  }

  // Does not support external sampler.
  if (format.PrefersExternalSampler()) {
    return static_cast<unsigned int>(MTLPixelFormatInvalid);
  }

  // For multiplanar formats without external sampler, Metal formats are per
  // plane.
  // For 1 channel 8-bit planes Y, U, V, A return MTLPixelFormatR8Unorm.
  // For 2 channel 8-bit plane UV return MTLPixelFormatRG8Unorm.
  // For 1 channel 10/16-bit planes Y, U, V, A return MTLPixelFormatR16Unorm.
  // For 2 channel 10/16-bit plane UV return MTLPixelFormatRG16Unorm.
  // For 1 channel 16-bit float planes Y, U, V, A return MTLPixelFormatR16Float.
  // For 2 channel 16-bit float plane UV return MTLPixelFormatRG16Float.
  int num_channels = format.NumChannelsInPlane(plane_index);
  DCHECK_LE(num_channels, 2);
  switch (format.channel_format()) {
    case viz::SharedImageFormat::ChannelFormat::k8:
      mtl_pixel_format =
          num_channels == 2 ? MTLPixelFormatRG8Unorm : MTLPixelFormatR8Unorm;
      break;
    case viz::SharedImageFormat::ChannelFormat::k10:
    case viz::SharedImageFormat::ChannelFormat::k16:
      mtl_pixel_format =
          num_channels == 2 ? MTLPixelFormatRG16Unorm : MTLPixelFormatR16Unorm;
      break;
    case viz::SharedImageFormat::ChannelFormat::k16F:
      mtl_pixel_format =
          num_channels == 2 ? MTLPixelFormatRG16Float : MTLPixelFormatR16Float;
      break;
  }
  return static_cast<unsigned int>(mtl_pixel_format);
}

#if BUILDFLAG(SKIA_USE_METAL)
skgpu::graphite::TextureInfo GraphiteMetalTextureInfo(
    viz::SharedImageFormat format,
    int plane_index,
    bool is_yuv_plane,
    bool mipmapped) {
  MTLPixelFormat mtl_pixel_format =
      static_cast<MTLPixelFormat>(ToMTLPixelFormat(format, plane_index));
  CHECK_NE(mtl_pixel_format, MTLPixelFormatInvalid);
  // Must match CreateMetalTexture in iosurface_image_backing.mm.
  // TODO(sunnyps): Move constants to a common utility header.
  skgpu::graphite::MtlTextureInfo mtl_texture_info;
  mtl_texture_info.fSampleCount = 1;
  mtl_texture_info.fFormat = mtl_pixel_format;
  mtl_texture_info.fUsage = MTLTextureUsageShaderRead;
  if (format.is_single_plane() && !is_yuv_plane) {
    mtl_texture_info.fUsage |= MTLTextureUsageRenderTarget;
  }
#if BUILDFLAG(IS_IOS)
  mtl_texture_info.fStorageMode = MTLStorageModeShared;
#else
  mtl_texture_info.fStorageMode = MTLStorageModeManaged;
#endif
  mtl_texture_info.fMipmapped =
      mipmapped ? skgpu::Mipmapped::kYes : skgpu::Mipmapped::kNo;
  return skgpu::graphite::TextureInfos::MakeMetal(mtl_texture_info);
}
#endif

}  // namespace gpu