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