1// 2// Copyright 2019 The ANGLE Project Authors. All rights reserved. 3// Use of this source code is governed by a BSD-style license that can be 4// found in the LICENSE file. 5// 6// IOSurfaceSurfaceMtl.mm: 7// Implements the class methods for IOSurfaceSurfaceMtl. 8// 9 10#include "libANGLE/renderer/metal/IOSurfaceSurfaceMtl.h" 11 12#include <TargetConditionals.h> 13 14#include "libANGLE/Display.h" 15#include "libANGLE/Surface.h" 16#include "libANGLE/renderer/metal/ContextMtl.h" 17#include "libANGLE/renderer/metal/DisplayMtl.h" 18#include "libANGLE/renderer/metal/FrameBufferMtl.h" 19#include "libANGLE/renderer/metal/mtl_format_utils.h" 20#include "libANGLE/renderer/metal/mtl_utils.h" 21 22// Compiler can turn on programmatical frame capture in release build by defining 23// ANGLE_METAL_FRAME_CAPTURE flag. 24#if defined(NDEBUG) && !defined(ANGLE_METAL_FRAME_CAPTURE) 25# define ANGLE_METAL_FRAME_CAPTURE_ENABLED 0 26#else 27# define ANGLE_METAL_FRAME_CAPTURE_ENABLED 1 28#endif 29namespace rx 30{ 31 32namespace 33{ 34 35struct IOSurfaceFormatInfo 36{ 37 GLenum internalFormat; 38 GLenum type; 39 size_t componentBytes; 40 41 angle::FormatID nativeAngleFormatId; 42}; 43 44// clang-format off 45// GL_RGB is a special case. The native angle::FormatID would be either R8G8B8X8_UNORM 46// or B8G8R8X8_UNORM based on the IOSurface's pixel format. 47constexpr std::array<IOSurfaceFormatInfo, 9> kIOSurfaceFormats = {{ 48 {GL_RED, GL_UNSIGNED_BYTE, 1, angle::FormatID::R8_UNORM}, 49 {GL_RED, GL_UNSIGNED_SHORT, 2, angle::FormatID::R16_UNORM}, 50 {GL_RG, GL_UNSIGNED_BYTE, 2, angle::FormatID::R8G8_UNORM}, 51 {GL_RG, GL_UNSIGNED_SHORT, 4, angle::FormatID::R16G16_UNORM}, 52 {GL_RGB, GL_UNSIGNED_BYTE, 4, angle::FormatID::NONE}, 53 {GL_RGBA, GL_UNSIGNED_BYTE, 4, angle::FormatID::R8G8B8A8_UNORM}, 54 {GL_BGRA_EXT, GL_UNSIGNED_BYTE, 4, angle::FormatID::B8G8R8A8_UNORM}, 55 {GL_RGBA, GL_HALF_FLOAT, 8, angle::FormatID::R16G16B16A16_FLOAT}, 56 {GL_RGB10_A2, GL_UNSIGNED_INT_2_10_10_10_REV, 4, angle::FormatID::B10G10R10A2_UNORM}, 57}}; 58// clang-format on 59 60int FindIOSurfaceFormatIndex(GLenum internalFormat, GLenum type) 61{ 62 for (int i = 0; i < static_cast<int>(kIOSurfaceFormats.size()); ++i) 63 { 64 const auto &formatInfo = kIOSurfaceFormats[i]; 65 if (formatInfo.internalFormat == internalFormat && formatInfo.type == type) 66 { 67 return i; 68 } 69 } 70 return -1; 71} 72 73} // anonymous namespace 74 75// IOSurfaceSurfaceMtl implementation. 76IOSurfaceSurfaceMtl::IOSurfaceSurfaceMtl(DisplayMtl *display, 77 const egl::SurfaceState &state, 78 EGLClientBuffer buffer, 79 const egl::AttributeMap &attribs) 80 : OffscreenSurfaceMtl(display, state, attribs), mIOSurface((__bridge IOSurfaceRef)(buffer)) 81{ 82 CFRetain(mIOSurface); 83 84 mIOSurfacePlane = static_cast<int>(attribs.get(EGL_IOSURFACE_PLANE_ANGLE)); 85 86 EGLAttrib internalFormat = attribs.get(EGL_TEXTURE_INTERNAL_FORMAT_ANGLE); 87 EGLAttrib type = attribs.get(EGL_TEXTURE_TYPE_ANGLE); 88 mIOSurfaceFormatIdx = 89 FindIOSurfaceFormatIndex(static_cast<GLenum>(internalFormat), static_cast<GLenum>(type)); 90 ASSERT(mIOSurfaceFormatIdx >= 0); 91 92 angle::FormatID actualAngleFormatId = 93 kIOSurfaceFormats[mIOSurfaceFormatIdx].nativeAngleFormatId; 94 if (actualAngleFormatId == angle::FormatID::NONE) 95 { 96 // The actual angle::Format depends on the IOSurface's format. 97 ASSERT(internalFormat == GL_RGB); 98 switch (IOSurfaceGetPixelFormat(mIOSurface)) 99 { 100 case 'BGRA': 101 actualAngleFormatId = angle::FormatID::B8G8R8X8_UNORM; 102 break; 103 case 'RGBA': 104 actualAngleFormatId = angle::FormatID::R8G8B8X8_UNORM; 105 break; 106 default: 107 UNREACHABLE(); 108 } 109 } 110 111 mColorFormat = display->getPixelFormat(actualAngleFormatId); 112} 113IOSurfaceSurfaceMtl::~IOSurfaceSurfaceMtl() 114{ 115 if (mIOSurface != nullptr) 116 { 117 CFRelease(mIOSurface); 118 mIOSurface = nullptr; 119 } 120} 121 122egl::Error IOSurfaceSurfaceMtl::bindTexImage(const gl::Context *context, 123 gl::Texture *texture, 124 EGLint buffer) 125{ 126 ContextMtl *contextMtl = mtl::GetImpl(context); 127 StartFrameCapture(contextMtl); 128 129 // Initialize offscreen texture if needed: 130 ANGLE_TO_EGL_TRY(ensureColorTextureCreated(context)); 131 132 return OffscreenSurfaceMtl::bindTexImage(context, texture, buffer); 133} 134 135egl::Error IOSurfaceSurfaceMtl::releaseTexImage(const gl::Context *context, EGLint buffer) 136{ 137 egl::Error re = OffscreenSurfaceMtl::releaseTexImage(context, buffer); 138 StopFrameCapture(); 139 return re; 140} 141 142angle::Result IOSurfaceSurfaceMtl::getAttachmentRenderTarget( 143 const gl::Context *context, 144 GLenum binding, 145 const gl::ImageIndex &imageIndex, 146 GLsizei samples, 147 FramebufferAttachmentRenderTarget **rtOut) 148{ 149 // Initialize offscreen texture if needed: 150 ANGLE_TRY(ensureColorTextureCreated(context)); 151 152 return OffscreenSurfaceMtl::getAttachmentRenderTarget(context, binding, imageIndex, samples, 153 rtOut); 154} 155 156angle::Result IOSurfaceSurfaceMtl::ensureColorTextureCreated(const gl::Context *context) 157{ 158 if (mColorTexture) 159 { 160 return angle::Result::Continue; 161 } 162 ContextMtl *contextMtl = mtl::GetImpl(context); 163 ANGLE_MTL_OBJC_SCOPE 164 { 165 auto texDesc = 166 [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:mColorFormat.metalFormat 167 width:mSize.width 168 height:mSize.height 169 mipmapped:NO]; 170 171 texDesc.usage = MTLTextureUsageShaderRead | MTLTextureUsageRenderTarget; 172 173 mColorTexture = 174 mtl::Texture::MakeFromMetal(contextMtl->getMetalDevice().newTextureWithDescriptor( 175 texDesc, mIOSurface, mIOSurfacePlane)); 176 177 if (mColorTexture) 178 { 179 size_t resourceSize = EstimateTextureSizeInBytes( 180 mColorFormat, mColorTexture->widthAt0(), mColorTexture->heightAt0(), 181 mColorTexture->depthAt0(), mColorTexture->samples(), mColorTexture->mipmapLevels()); 182 183 mColorTexture->setEstimatedByteSize(resourceSize); 184 } 185 } 186 187 mColorRenderTarget.set(mColorTexture, mtl::kZeroNativeMipLevel, 0, mColorFormat); 188 189 if (kIOSurfaceFormats[mIOSurfaceFormatIdx].internalFormat == GL_RGB) 190 { 191 // This format has emulated alpha channel. Initialize texture's alpha channel to 1.0. 192 const mtl::Format &rgbClearFormat = 193 contextMtl->getPixelFormat(angle::FormatID::R8G8B8_UNORM); 194 ANGLE_TRY(mtl::InitializeTextureContentsGPU( 195 context, mColorTexture, rgbClearFormat, 196 mtl::ImageNativeIndex::FromBaseZeroGLIndex(gl::ImageIndex::Make2D(0)), 197 MTLColorWriteMaskAlpha)); 198 199 // Disable subsequent rendering to alpha channel. 200 mColorTexture->setColorWritableMask(MTLColorWriteMaskAll & (~MTLColorWriteMaskAlpha)); 201 } 202 // Robust resource init: currently we do not allow passing contents with IOSurfaces. 203 mColorTextureInitialized = false; 204 205 return angle::Result::Continue; 206} 207 208// static 209bool IOSurfaceSurfaceMtl::ValidateAttributes(EGLClientBuffer buffer, 210 const egl::AttributeMap &attribs) 211{ 212 IOSurfaceRef ioSurface = (__bridge IOSurfaceRef)(buffer); 213 214 // The plane must exist for this IOSurface. IOSurfaceGetPlaneCount can return 0 for non-planar 215 // ioSurfaces but we will treat non-planar like it is a single plane. 216 size_t surfacePlaneCount = std::max(size_t(1), IOSurfaceGetPlaneCount(ioSurface)); 217 EGLAttrib plane = attribs.get(EGL_IOSURFACE_PLANE_ANGLE); 218 if (plane < 0 || static_cast<size_t>(plane) >= surfacePlaneCount) 219 { 220 return false; 221 } 222 223 // The width height specified must be at least (1, 1) and at most the plane size 224 EGLAttrib width = attribs.get(EGL_WIDTH); 225 EGLAttrib height = attribs.get(EGL_HEIGHT); 226 if (width <= 0 || static_cast<size_t>(width) > IOSurfaceGetWidthOfPlane(ioSurface, plane) || 227 height <= 0 || static_cast<size_t>(height) > IOSurfaceGetHeightOfPlane(ioSurface, plane)) 228 { 229 return false; 230 } 231 232 // Find this IOSurface format 233 EGLAttrib internalFormat = attribs.get(EGL_TEXTURE_INTERNAL_FORMAT_ANGLE); 234 EGLAttrib type = attribs.get(EGL_TEXTURE_TYPE_ANGLE); 235 236 int formatIndex = 237 FindIOSurfaceFormatIndex(static_cast<GLenum>(internalFormat), static_cast<GLenum>(type)); 238 239 if (formatIndex < 0) 240 { 241 return false; 242 } 243 244 // FIXME: Check that the format matches this IOSurface plane for pixel formats that we know of. 245 // We could map IOSurfaceGetPixelFormat to expected type plane and format type. 246 // However, the caller might supply us non-public pixel format, which makes exhaustive checks 247 // problematic. 248 if (IOSurfaceGetBytesPerElementOfPlane(ioSurface, plane) != 249 kIOSurfaceFormats[formatIndex].componentBytes) 250 { 251 WARN() << "IOSurface bytes per elements does not match the pbuffer internal format."; 252 } 253 254 return true; 255} 256} // namespace rx 257