1// 2// Copyright 2020 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 7// IOSurfaceSurfaceEAGL.mm: an implementation of PBuffers created from IOSurfaces using 8// EGL_ANGLE_iosurface_client_buffer 9 10#import "libANGLE/renderer/gl/eagl/IOSurfaceSurfaceEAGL.h" 11 12#import <OpenGLES/EAGL.h> 13#import <OpenGLES/EAGLDrawable.h> 14#import <OpenGLES/EAGLIOSurface.h> 15 16#import "common/debug.h" 17#import "common/platform.h" 18#import "libANGLE/AttributeMap.h" 19#import "libANGLE/renderer/gl/BlitGL.h" 20#import "libANGLE/renderer/gl/FramebufferGL.h" 21#import "libANGLE/renderer/gl/FunctionsGL.h" 22#import "libANGLE/renderer/gl/RendererGL.h" 23#import "libANGLE/renderer/gl/StateManagerGL.h" 24#import "libANGLE/renderer/gl/TextureGL.h" 25#import "libANGLE/renderer/gl/eagl/DisplayEAGL.h" 26 27namespace rx 28{ 29 30namespace 31{ 32 33struct IOSurfaceFormatInfo 34{ 35 GLenum internalFormat; 36 GLenum type; 37 38 GLenum nativeInternalFormat; 39 GLenum nativeFormat; 40 GLenum nativeType; 41}; 42 43// clang-format off 44 45static const IOSurfaceFormatInfo kIOSurfaceFormats[] = { 46 {GL_RED, GL_UNSIGNED_BYTE, GL_R8, GL_RED, GL_UNSIGNED_BYTE }, 47 {GL_RG, GL_UNSIGNED_BYTE, GL_RG8, GL_RG, GL_UNSIGNED_BYTE }, 48 {GL_RGBA, GL_UNSIGNED_BYTE, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE }, 49 {GL_RGB, GL_UNSIGNED_BYTE, GL_RGBA, GL_BGRA, GL_UNSIGNED_BYTE }, 50 {GL_BGRA_EXT, GL_UNSIGNED_BYTE, GL_RGBA, GL_BGRA, GL_UNSIGNED_BYTE }, 51 {GL_RGBA, GL_HALF_FLOAT, GL_RGBA, GL_RGBA, GL_HALF_FLOAT }, 52}; 53 54// clang-format on 55 56int FindIOSurfaceFormatIndex(GLenum internalFormat, GLenum type) 57{ 58 for (int i = 0; i < static_cast<int>(ArraySize(kIOSurfaceFormats)); ++i) 59 { 60 const auto &formatInfo = kIOSurfaceFormats[i]; 61 if (formatInfo.internalFormat == internalFormat && formatInfo.type == type) 62 { 63 return i; 64 } 65 } 66 return -1; 67} 68 69} // anonymous namespace 70 71IOSurfaceSurfaceEAGL::IOSurfaceSurfaceEAGL(const egl::SurfaceState &state, 72 RendererGL *renderer, 73 EAGLContextObj cglContext, 74 EGLClientBuffer buffer, 75 const egl::AttributeMap &attribs) 76 : SurfaceGL(state), 77 mFunctions(renderer->getFunctions()), 78 mStateManager(renderer->getStateManager()), 79 mEAGLContext(cglContext), 80 mIOSurface(nullptr), 81 mFramebufferID(0), 82 mTextureID(0), 83 mWidth(0), 84 mHeight(0), 85 mPlane(0), 86 mFormatIndex(-1), 87 mRowStrideInPixels(0), 88 mAlphaInitialized(false) 89{ 90 // Keep reference to the IOSurface so it doesn't get deleted while the pbuffer exists. 91 mIOSurface = reinterpret_cast<IOSurfaceRef>(buffer); 92 CFRetain(mIOSurface); 93 94 // Extract attribs useful for the call to EAGLTexImageIOSurface2D 95 mWidth = static_cast<int>(attribs.get(EGL_WIDTH)); 96 mHeight = static_cast<int>(attribs.get(EGL_HEIGHT)); 97 mPlane = static_cast<int>(attribs.get(EGL_IOSURFACE_PLANE_ANGLE)); 98 // Hopefully the number of bytes per row is always an integer number of pixels. We use 99 // glReadPixels to fill the IOSurface in the simulator and it can only support strides that are 100 // an integer number of pixels. 101 ASSERT(IOSurfaceGetBytesPerRowOfPlane(mIOSurface, mPlane) % 102 IOSurfaceGetBytesPerElementOfPlane(mIOSurface, mPlane) == 103 0); 104 mRowStrideInPixels = static_cast<int>(IOSurfaceGetBytesPerRowOfPlane(mIOSurface, mPlane) / 105 IOSurfaceGetBytesPerElementOfPlane(mIOSurface, mPlane)); 106 107 EGLAttrib internalFormat = attribs.get(EGL_TEXTURE_INTERNAL_FORMAT_ANGLE); 108 EGLAttrib type = attribs.get(EGL_TEXTURE_TYPE_ANGLE); 109 mFormatIndex = 110 FindIOSurfaceFormatIndex(static_cast<GLenum>(internalFormat), static_cast<GLenum>(type)); 111 ASSERT(mFormatIndex >= 0); 112 113 mAlphaInitialized = !hasEmulatedAlphaChannel(); 114 115#if ANGLE_PLATFORM_IOS_FAMILY_SIMULATOR 116 ANGLE_UNUSED_VARIABLE(mEAGLContext); 117 mBoundTextureID = 0; 118 EGLAttrib usageHint = 119 attribs.get(EGL_IOSURFACE_USAGE_HINT_ANGLE, 120 EGL_IOSURFACE_READ_HINT_ANGLE | EGL_IOSURFACE_WRITE_HINT_ANGLE); 121 mUploadFromIOSurface = ((usageHint & EGL_IOSURFACE_READ_HINT_ANGLE) != 0); 122 mReadbackToIOSurface = ((usageHint & EGL_IOSURFACE_WRITE_HINT_ANGLE) != 0); 123#endif 124} 125 126IOSurfaceSurfaceEAGL::~IOSurfaceSurfaceEAGL() 127{ 128 if (!mFramebufferID) 129 { 130 mStateManager->deleteFramebuffer(mFramebufferID); 131 mFramebufferID = 0; 132 mStateManager->deleteTexture(mTextureID); 133 mTextureID = 0; 134 } 135 if (mIOSurface != nullptr) 136 { 137 CFRelease(mIOSurface); 138 mIOSurface = nullptr; 139 } 140} 141 142egl::Error IOSurfaceSurfaceEAGL::initialize(const egl::Display *display) 143{ 144 return egl::NoError(); 145} 146 147egl::Error IOSurfaceSurfaceEAGL::makeCurrent(const gl::Context *context) 148{ 149 return egl::NoError(); 150} 151 152egl::Error IOSurfaceSurfaceEAGL::unMakeCurrent(const gl::Context *context) 153{ 154 GetFunctionsGL(context)->flush(); 155 return egl::NoError(); 156} 157 158egl::Error IOSurfaceSurfaceEAGL::swap(const gl::Context *context) 159{ 160 return egl::NoError(); 161} 162 163egl::Error IOSurfaceSurfaceEAGL::postSubBuffer(const gl::Context *context, 164 EGLint x, 165 EGLint y, 166 EGLint width, 167 EGLint height) 168{ 169 UNREACHABLE(); 170 return egl::NoError(); 171} 172 173egl::Error IOSurfaceSurfaceEAGL::querySurfacePointerANGLE(EGLint attribute, void **value) 174{ 175 UNREACHABLE(); 176 return egl::NoError(); 177} 178 179egl::Error IOSurfaceSurfaceEAGL::bindTexImage(const gl::Context *context, 180 gl::Texture *texture, 181 EGLint) 182{ 183 StateManagerGL *stateManager = GetStateManagerGL(context); 184 185 const TextureGL *textureGL = GetImplAs<TextureGL>(texture); 186 GLuint textureID = textureGL->getTextureID(); 187 stateManager->bindTexture(gl::TextureType::_2D, textureID); 188 const auto &format = kIOSurfaceFormats[mFormatIndex]; 189 190#if !ANGLE_PLATFORM_IOS_FAMILY_SIMULATOR 191 if (![mEAGLContext texImageIOSurface:mIOSurface 192 target:GL_TEXTURE_2D 193 internalFormat:format.nativeInternalFormat 194 width:mWidth 195 height:mHeight 196 format:format.nativeFormat 197 type:format.nativeType 198 plane:mPlane]) 199 { 200 return egl::EglContextLost() << "EAGLContext texImageIOSurface2D failed."; 201 } 202 203 if (IsError(initializeAlphaChannel(context, textureID))) 204 { 205 return egl::EglContextLost() << "Failed to initialize IOSurface alpha channel."; 206 } 207#else // !ANGLE_PLATFORM_IOS_FAMILY_SIMULATOR 208 const FunctionsGL *functions = GetFunctionsGL(context); 209 210 IOSurfaceLock(mIOSurface, getIOSurfaceLockOptions(), nullptr); 211 void *textureData = nullptr; 212 if (mUploadFromIOSurface) 213 { 214 // TODO(kbr): possibly more state to be set here, including setting any 215 // pixel unpack buffer to 0 when using ES 3.0 contexts. 216 gl::PixelUnpackState defaultUnpackState; 217 if (IsError(stateManager->setPixelUnpackState(context, defaultUnpackState))) 218 { 219 return egl::EglBadState() << "Failed to set pixel unpack state."; 220 } 221 textureData = IOSurfaceGetBaseAddress(mIOSurface); 222 } 223 224 // TODO(kbr): consider trying to optimize away texture reallocations by 225 // keeping track of which textures have already been allocated. 226 functions->texImage2D(GL_TEXTURE_2D, 0, format.nativeInternalFormat, mWidth, mHeight, 0, 227 format.nativeFormat, format.nativeType, textureData); 228 229 mBoundTextureID = textureID; 230#endif // !ANGLE_PLATFORM_IOS_FAMILY_SIMULATOR 231 232 return egl::NoError(); 233} 234 235egl::Error IOSurfaceSurfaceEAGL::releaseTexImage(const gl::Context *context, EGLint buffer) 236{ 237 const FunctionsGL *functions = GetFunctionsGL(context); 238#if !ANGLE_PLATFORM_IOS_FAMILY_SIMULATOR 239 functions->flush(); 240#else // !ANGLE_PLATFORM_IOS_FAMILY_SIMULATOR 241 if (mReadbackToIOSurface) 242 { 243 StateManagerGL *stateManager = GetStateManagerGL(context); 244 GLuint tempFBO = 0; 245 functions->genFramebuffers(1, &tempFBO); 246 stateManager->bindFramebuffer(GL_FRAMEBUFFER, tempFBO); 247 functions->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 248 mBoundTextureID, 0); 249 gl::PixelPackState state; 250 state.rowLength = mRowStrideInPixels; 251 state.alignment = 1; 252 if (IsError(stateManager->setPixelPackState(context, state))) 253 { 254 return egl::EglBadState() << "Failed to set pixel pack state."; 255 } 256 // TODO(kbr): possibly more state to be set here, including setting any 257 // pixel pack buffer to 0 when using ES 3.0 contexts. 258 const auto &format = kIOSurfaceFormats[mFormatIndex]; 259 functions->readPixels(0, 0, mWidth, mHeight, format.nativeFormat, format.nativeType, 260 IOSurfaceGetBaseAddress(mIOSurface)); 261 } 262 263 IOSurfaceUnlock(mIOSurface, getIOSurfaceLockOptions(), nullptr); 264#endif // !ANGLE_PLATFORM_IOS_FAMILY_SIMULATOR 265 266 return egl::NoError(); 267} 268 269void IOSurfaceSurfaceEAGL::setSwapInterval(EGLint interval) 270{ 271 UNREACHABLE(); 272} 273 274EGLint IOSurfaceSurfaceEAGL::getWidth() const 275{ 276 return mWidth; 277} 278 279EGLint IOSurfaceSurfaceEAGL::getHeight() const 280{ 281 return mHeight; 282} 283 284EGLint IOSurfaceSurfaceEAGL::isPostSubBufferSupported() const 285{ 286 UNREACHABLE(); 287 return EGL_FALSE; 288} 289 290EGLint IOSurfaceSurfaceEAGL::getSwapBehavior() const 291{ 292 // N/A because you can't MakeCurrent an IOSurface, return any valid value. 293 return EGL_BUFFER_PRESERVED; 294} 295 296// static 297bool IOSurfaceSurfaceEAGL::validateAttributes(EGLClientBuffer buffer, 298 const egl::AttributeMap &attribs) 299{ 300 IOSurfaceRef ioSurface = reinterpret_cast<IOSurfaceRef>(buffer); 301 302 // The plane must exist for this IOSurface. IOSurfaceGetPlaneCount can return 0 for non-planar 303 // ioSurfaces but we will treat non-planar like it is a single plane. 304 size_t surfacePlaneCount = std::max(size_t(1), IOSurfaceGetPlaneCount(ioSurface)); 305 EGLAttrib plane = attribs.get(EGL_IOSURFACE_PLANE_ANGLE); 306 if (plane < 0 || static_cast<size_t>(plane) >= surfacePlaneCount) 307 { 308 return false; 309 } 310 311 // The width height specified must be at least (1, 1) and at most the plane size 312 EGLAttrib width = attribs.get(EGL_WIDTH); 313 EGLAttrib height = attribs.get(EGL_HEIGHT); 314 if (width <= 0 || static_cast<size_t>(width) > IOSurfaceGetWidthOfPlane(ioSurface, plane) || 315 height <= 0 || static_cast<size_t>(height) > IOSurfaceGetHeightOfPlane(ioSurface, plane)) 316 { 317 return false; 318 } 319 320 // Find this IOSurface format 321 EGLAttrib internalFormat = attribs.get(EGL_TEXTURE_INTERNAL_FORMAT_ANGLE); 322 EGLAttrib type = attribs.get(EGL_TEXTURE_TYPE_ANGLE); 323 324 int formatIndex = 325 FindIOSurfaceFormatIndex(static_cast<GLenum>(internalFormat), static_cast<GLenum>(type)); 326 327 if (formatIndex < 0) 328 { 329 return false; 330 } 331 332 // FIXME: Check that the format matches this IOSurface plane for pixel formats that we know of. 333 // We could map IOSurfaceGetPixelFormat to expected type plane and format type. 334 // However, the caller might supply us non-public pixel format, which makes exhaustive checks 335 // problematic. 336 337 return true; 338} 339 340angle::Result IOSurfaceSurfaceEAGL::initializeAlphaChannel(const gl::Context *context, 341 GLuint texture) 342{ 343 if (mAlphaInitialized) 344 { 345 return angle::Result::Continue; 346 } 347 348 BlitGL *blitter = GetBlitGL(context); 349 ANGLE_TRY(blitter->clearRenderableTextureAlphaToOne(context, texture, 350 gl::TextureTarget::Rectangle, 0)); 351 mAlphaInitialized = true; 352 return angle::Result::Continue; 353} 354 355bool IOSurfaceSurfaceEAGL::hasEmulatedAlphaChannel() const 356{ 357 const auto &format = kIOSurfaceFormats[mFormatIndex]; 358 return format.internalFormat == GL_RGB; 359} 360 361egl::Error IOSurfaceSurfaceEAGL::attachToFramebuffer(const gl::Context *context, 362 gl::Framebuffer *framebuffer) 363{ 364 FramebufferGL *framebufferGL = GetImplAs<FramebufferGL>(framebuffer); 365 ASSERT(framebufferGL->getFramebufferID() == 0); 366 if (mFramebufferID == 0) 367 { 368 GLuint textureID = 0; 369 mFunctions->genTextures(1, &textureID); 370 mStateManager->bindTexture(gl::TextureType::_2D, textureID); 371 372#if !ANGLE_PLATFORM_IOS_FAMILY_SIMULATOR 373 const auto &format = kIOSurfaceFormats[mFormatIndex]; 374 375 if (![mEAGLContext texImageIOSurface:mIOSurface 376 target:GL_TEXTURE_2D 377 internalFormat:format.nativeInternalFormat 378 width:mWidth 379 height:mHeight 380 format:format.nativeFormat 381 type:format.nativeType 382 plane:mPlane]) 383 { 384 return egl::EglContextLost() << "[EAGLContext texImageIOSurface] failed"; 385 } 386#else // !ANGLE_PLATFORM_IOS_FAMILY_SIMULATOR 387 ERR() << "IOSurfaces with OpenGL ES not supported on iOS Simulator"; 388#endif // !ANGLE_PLATFORM_IOS_FAMILY_SIMULATOR 389 390 if (IsError(initializeAlphaChannel(context, textureID))) 391 { 392 return egl::EglContextLost() << "Failed to initialize IOSurface alpha channel."; 393 } 394 395 GLuint framebufferID = 0; 396 mFunctions->genFramebuffers(1, &framebufferID); 397 mStateManager->bindFramebuffer(GL_FRAMEBUFFER, framebufferID); 398 mStateManager->bindTexture(gl::TextureType::_2D, textureID); 399 mFunctions->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 400 textureID, 0); 401 mFramebufferID = framebufferID; 402 mTextureID = textureID; 403 } 404 framebufferGL->setFramebufferID(mFramebufferID); 405 framebufferGL->setHasEmulatedAlphaAttachment(hasEmulatedAlphaChannel()); 406 407 return egl::NoError(); 408} 409 410egl::Error IOSurfaceSurfaceEAGL::detachFromFramebuffer(const gl::Context *context, 411 gl::Framebuffer *framebuffer) 412{ 413 FramebufferGL *framebufferGL = GetImplAs<FramebufferGL>(framebuffer); 414 ASSERT(framebufferGL->getFramebufferID() == mFramebufferID); 415 framebufferGL->setFramebufferID(0); 416 return egl::NoError(); 417} 418 419#if ANGLE_PLATFORM_IOS_FAMILY_SIMULATOR 420IOSurfaceLockOptions IOSurfaceSurfaceEAGL::getIOSurfaceLockOptions() const 421{ 422 IOSurfaceLockOptions options = 0; 423 if (!mReadbackToIOSurface) 424 { 425 options |= kIOSurfaceLockReadOnly; 426 } 427 return options; 428} 429#endif // ANGLE_PLATFORM_IOS_FAMILY_SIMULATOR 430 431} // namespace rx 432