• 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 "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