• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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