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 ANGLE_WITH_MODERN_METAL_API 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// NOTE(hqle): Support R16_UINT once GLES3 is complete. 46constexpr std::array<IOSurfaceFormatInfo, 8> kIOSurfaceFormats = {{ 47 {GL_RED, GL_UNSIGNED_BYTE, 1, angle::FormatID::R8_UNORM}, 48 {GL_RED, GL_UNSIGNED_SHORT, 2, angle::FormatID::R16_UNORM}, 49 {GL_RG, GL_UNSIGNED_BYTE, 2, angle::FormatID::R8G8_UNORM}, 50 {GL_RG, GL_UNSIGNED_SHORT, 4, angle::FormatID::R16G16_UNORM}, 51 {GL_RGB, GL_UNSIGNED_BYTE, 4, angle::FormatID::B8G8R8A8_UNORM}, 52 {GL_BGRA_EXT, GL_UNSIGNED_BYTE, 4, angle::FormatID::B8G8R8A8_UNORM}, 53 {GL_RGBA, GL_HALF_FLOAT, 8, angle::FormatID::R16G16B16A16_FLOAT}, 54 {GL_RGB10_A2, GL_UNSIGNED_INT_2_10_10_10_REV, 4, angle::FormatID::B10G10R10A2_UNORM}, 55}}; 56// clang-format on 57 58int FindIOSurfaceFormatIndex(GLenum internalFormat, GLenum type) 59{ 60 for (int i = 0; i < static_cast<int>(kIOSurfaceFormats.size()); ++i) 61 { 62 const auto &formatInfo = kIOSurfaceFormats[i]; 63 if (formatInfo.internalFormat == internalFormat && formatInfo.type == type) 64 { 65 return i; 66 } 67 } 68 return -1; 69} 70 71} // anonymous namespace 72 73// IOSurfaceSurfaceMtl implementation. 74IOSurfaceSurfaceMtl::IOSurfaceSurfaceMtl(DisplayMtl *display, 75 const egl::SurfaceState &state, 76 EGLClientBuffer buffer, 77 const egl::AttributeMap &attribs) 78 : OffscreenSurfaceMtl(display, state, attribs), mIOSurface((__bridge IOSurfaceRef)(buffer)) 79{ 80 CFRetain(mIOSurface); 81 82 mIOSurfacePlane = static_cast<int>(attribs.get(EGL_IOSURFACE_PLANE_ANGLE)); 83 84 EGLAttrib internalFormat = attribs.get(EGL_TEXTURE_INTERNAL_FORMAT_ANGLE); 85 EGLAttrib type = attribs.get(EGL_TEXTURE_TYPE_ANGLE); 86 mIOSurfaceFormatIdx = 87 FindIOSurfaceFormatIndex(static_cast<GLenum>(internalFormat), static_cast<GLenum>(type)); 88 ASSERT(mIOSurfaceFormatIdx >= 0); 89 90 mColorFormat = 91 display->getPixelFormat(kIOSurfaceFormats[mIOSurfaceFormatIdx].nativeAngleFormatId); 92} 93IOSurfaceSurfaceMtl::~IOSurfaceSurfaceMtl() 94{ 95 if (mIOSurface != nullptr) 96 { 97 CFRelease(mIOSurface); 98 mIOSurface = nullptr; 99 } 100} 101 102egl::Error IOSurfaceSurfaceMtl::bindTexImage(const gl::Context *context, 103 gl::Texture *texture, 104 EGLint buffer) 105{ 106 ContextMtl *contextMtl = mtl::GetImpl(context); 107 StartFrameCapture(contextMtl); 108 109 // Initialize offscreen texture if needed: 110 ANGLE_TO_EGL_TRY(ensureColorTextureCreated(context)); 111 112 return OffscreenSurfaceMtl::bindTexImage(context, texture, buffer); 113} 114 115egl::Error IOSurfaceSurfaceMtl::releaseTexImage(const gl::Context *context, EGLint buffer) 116{ 117 egl::Error re = OffscreenSurfaceMtl::releaseTexImage(context, buffer); 118 StopFrameCapture(); 119 return re; 120} 121 122angle::Result IOSurfaceSurfaceMtl::getAttachmentRenderTarget( 123 const gl::Context *context, 124 GLenum binding, 125 const gl::ImageIndex &imageIndex, 126 GLsizei samples, 127 FramebufferAttachmentRenderTarget **rtOut) 128{ 129 // Initialize offscreen texture if needed: 130 ANGLE_TRY(ensureColorTextureCreated(context)); 131 132 return OffscreenSurfaceMtl::getAttachmentRenderTarget(context, binding, imageIndex, samples, 133 rtOut); 134} 135 136angle::Result IOSurfaceSurfaceMtl::ensureColorTextureCreated(const gl::Context *context) 137{ 138 if (mColorTexture) 139 { 140 return angle::Result::Continue; 141 } 142 ContextMtl *contextMtl = mtl::GetImpl(context); 143 ANGLE_MTL_OBJC_SCOPE 144 { 145 auto texDesc = 146 [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:mColorFormat.metalFormat 147 width:mSize.width 148 height:mSize.height 149 mipmapped:NO]; 150 151 texDesc.usage = MTLTextureUsageShaderRead | MTLTextureUsageRenderTarget; 152 153 id<MTLTexture> texture = 154 [contextMtl->getMetalDevice() newTextureWithDescriptor:texDesc 155 iosurface:mIOSurface 156 plane:mIOSurfacePlane]; 157 158 mColorTexture = mtl::Texture::MakeFromMetal([texture ANGLE_MTL_AUTORELEASE]); 159 } 160 161 mColorRenderTarget.set(mColorTexture, mtl::kZeroNativeMipLevel, 0, mColorFormat); 162 163 if (kIOSurfaceFormats[mIOSurfaceFormatIdx].internalFormat == GL_RGB) 164 { 165 // This format has emulated alpha channel. Initialize texture's alpha channel to 1.0. 166 const mtl::Format &rgbClearFormat = 167 contextMtl->getPixelFormat(angle::FormatID::R8G8B8_UNORM); 168 ANGLE_TRY(mtl::InitializeTextureContentsGPU( 169 context, mColorTexture, rgbClearFormat, 170 mtl::ImageNativeIndex::FromBaseZeroGLIndex(gl::ImageIndex::Make2D(0)), 171 MTLColorWriteMaskAlpha)); 172 173 // Disable subsequent rendering to alpha channel. 174 mColorTexture->setColorWritableMask(MTLColorWriteMaskAll & (~MTLColorWriteMaskAlpha)); 175 } 176 177 return angle::Result::Continue; 178} 179 180// static 181bool IOSurfaceSurfaceMtl::ValidateAttributes(EGLClientBuffer buffer, 182 const egl::AttributeMap &attribs) 183{ 184 IOSurfaceRef ioSurface = (__bridge IOSurfaceRef)(buffer); 185 186 // The plane must exist for this IOSurface. IOSurfaceGetPlaneCount can return 0 for non-planar 187 // ioSurfaces but we will treat non-planar like it is a single plane. 188 size_t surfacePlaneCount = std::max(size_t(1), IOSurfaceGetPlaneCount(ioSurface)); 189 EGLAttrib plane = attribs.get(EGL_IOSURFACE_PLANE_ANGLE); 190 if (plane < 0 || static_cast<size_t>(plane) >= surfacePlaneCount) 191 { 192 return false; 193 } 194 195 // The width height specified must be at least (1, 1) and at most the plane size 196 EGLAttrib width = attribs.get(EGL_WIDTH); 197 EGLAttrib height = attribs.get(EGL_HEIGHT); 198 if (width <= 0 || static_cast<size_t>(width) > IOSurfaceGetWidthOfPlane(ioSurface, plane) || 199 height <= 0 || static_cast<size_t>(height) > IOSurfaceGetHeightOfPlane(ioSurface, plane)) 200 { 201 return false; 202 } 203 204 // Find this IOSurface format 205 EGLAttrib internalFormat = attribs.get(EGL_TEXTURE_INTERNAL_FORMAT_ANGLE); 206 EGLAttrib type = attribs.get(EGL_TEXTURE_TYPE_ANGLE); 207 208 int formatIndex = 209 FindIOSurfaceFormatIndex(static_cast<GLenum>(internalFormat), static_cast<GLenum>(type)); 210 211 if (formatIndex < 0) 212 { 213 return false; 214 } 215 216 // Check that the format matches this IOSurface plane 217 if (IOSurfaceGetBytesPerElementOfPlane(ioSurface, plane) != 218 kIOSurfaceFormats[formatIndex].componentBytes) 219 { 220 return false; 221 } 222 223 return true; 224} 225} 226