• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright 2015 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 // PBufferSurfaceCGL.cpp: an implementation of PBuffers created from IOSurfaces using
8 //                        EGL_ANGLE_iosurface_client_buffer
9 
10 #include "common/platform.h"
11 
12 #if defined(ANGLE_PLATFORM_MACOS) || defined(ANGLE_PLATFORM_MACCATALYST)
13 
14 #    include "libANGLE/renderer/gl/cgl/IOSurfaceSurfaceCGL.h"
15 
16 #    include <IOSurface/IOSurface.h>
17 #    include <OpenGL/CGLIOSurface.h>
18 #    include <OpenGL/OpenGL.h>
19 
20 #    include "common/debug.h"
21 #    include "common/gl/cgl/FunctionsCGL.h"
22 #    include "libANGLE/AttributeMap.h"
23 #    include "libANGLE/renderer/gl/BlitGL.h"
24 #    include "libANGLE/renderer/gl/FramebufferGL.h"
25 #    include "libANGLE/renderer/gl/FunctionsGL.h"
26 #    include "libANGLE/renderer/gl/RendererGL.h"
27 #    include "libANGLE/renderer/gl/StateManagerGL.h"
28 #    include "libANGLE/renderer/gl/TextureGL.h"
29 #    include "libANGLE/renderer/gl/cgl/DisplayCGL.h"
30 
31 namespace rx
32 {
33 
34 namespace
35 {
36 
37 struct IOSurfaceFormatInfo
38 {
39     GLenum internalFormat;
40     GLenum type;
41 
42     size_t componentBytes;
43 
44     GLenum nativeInternalFormat;
45     GLenum nativeFormat;
46     GLenum nativeType;
47 };
48 
49 // clang-format off
50 static const IOSurfaceFormatInfo kIOSurfaceFormats[] = {
51     {GL_RED,      GL_UNSIGNED_BYTE,                1, GL_RED,  GL_RED,  GL_UNSIGNED_BYTE              },
52     {GL_RED,      GL_UNSIGNED_SHORT,               2, GL_RED,  GL_RED,  GL_UNSIGNED_SHORT             },
53     {GL_R16UI,    GL_UNSIGNED_SHORT,               2, GL_RED,  GL_RED,  GL_UNSIGNED_SHORT             },
54     {GL_RG,       GL_UNSIGNED_BYTE,                2, GL_RG,   GL_RG,   GL_UNSIGNED_BYTE              },
55     {GL_RG,       GL_UNSIGNED_SHORT,               4, GL_RG,   GL_RG,   GL_UNSIGNED_SHORT             },
56     {GL_RGB,      GL_UNSIGNED_BYTE,                4, GL_RGBA, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV   },
57     {GL_BGRA_EXT, GL_UNSIGNED_BYTE,                4, GL_RGBA, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV   },
58     {GL_RGB10_A2, GL_UNSIGNED_INT_2_10_10_10_REV,  4, GL_RGBA, GL_BGRA, GL_UNSIGNED_INT_2_10_10_10_REV},
59     {GL_RGBA,     GL_HALF_FLOAT,                   8, GL_RGBA, GL_RGBA, GL_HALF_FLOAT                 },
60 };
61 // clang-format on
62 
FindIOSurfaceFormatIndex(GLenum internalFormat,GLenum type)63 int FindIOSurfaceFormatIndex(GLenum internalFormat, GLenum type)
64 {
65     for (int i = 0; i < static_cast<int>(ArraySize(kIOSurfaceFormats)); ++i)
66     {
67         const auto &formatInfo = kIOSurfaceFormats[i];
68         if (formatInfo.internalFormat == internalFormat && formatInfo.type == type)
69         {
70             return i;
71         }
72     }
73     return -1;
74 }
75 
76 }  // anonymous namespace
77 
IOSurfaceSurfaceCGL(const egl::SurfaceState & state,CGLContextObj cglContext,EGLClientBuffer buffer,const egl::AttributeMap & attribs)78 IOSurfaceSurfaceCGL::IOSurfaceSurfaceCGL(const egl::SurfaceState &state,
79                                          CGLContextObj cglContext,
80                                          EGLClientBuffer buffer,
81                                          const egl::AttributeMap &attribs)
82     : SurfaceGL(state),
83       mCGLContext(cglContext),
84       mIOSurface(nullptr),
85       mWidth(0),
86       mHeight(0),
87       mPlane(0),
88       mFormatIndex(-1),
89       mAlphaInitialized(false)
90 {
91     // Keep reference to the IOSurface so it doesn't get deleted while the pbuffer exists.
92     mIOSurface = reinterpret_cast<IOSurfaceRef>(buffer);
93     CFRetain(mIOSurface);
94 
95     // Extract attribs useful for the call to CGLTexImageIOSurface2D
96     mWidth  = static_cast<int>(attribs.get(EGL_WIDTH));
97     mHeight = static_cast<int>(attribs.get(EGL_HEIGHT));
98     mPlane  = static_cast<int>(attribs.get(EGL_IOSURFACE_PLANE_ANGLE));
99 
100     EGLAttrib internalFormat = attribs.get(EGL_TEXTURE_INTERNAL_FORMAT_ANGLE);
101     EGLAttrib type           = attribs.get(EGL_TEXTURE_TYPE_ANGLE);
102     mFormatIndex =
103         FindIOSurfaceFormatIndex(static_cast<GLenum>(internalFormat), static_cast<GLenum>(type));
104     ASSERT(mFormatIndex >= 0);
105 
106     mAlphaInitialized = !hasEmulatedAlphaChannel();
107 }
108 
~IOSurfaceSurfaceCGL()109 IOSurfaceSurfaceCGL::~IOSurfaceSurfaceCGL()
110 {
111     if (mIOSurface != nullptr)
112     {
113         CFRelease(mIOSurface);
114         mIOSurface = nullptr;
115     }
116 }
117 
initialize(const egl::Display * display)118 egl::Error IOSurfaceSurfaceCGL::initialize(const egl::Display *display)
119 {
120     return egl::NoError();
121 }
122 
makeCurrent(const gl::Context * context)123 egl::Error IOSurfaceSurfaceCGL::makeCurrent(const gl::Context *context)
124 {
125     return egl::NoError();
126 }
127 
unMakeCurrent(const gl::Context * context)128 egl::Error IOSurfaceSurfaceCGL::unMakeCurrent(const gl::Context *context)
129 {
130     GetFunctionsGL(context)->flush();
131     return egl::NoError();
132 }
133 
swap(const gl::Context * context)134 egl::Error IOSurfaceSurfaceCGL::swap(const gl::Context *context)
135 {
136     return egl::NoError();
137 }
138 
postSubBuffer(const gl::Context * context,EGLint x,EGLint y,EGLint width,EGLint height)139 egl::Error IOSurfaceSurfaceCGL::postSubBuffer(const gl::Context *context,
140                                               EGLint x,
141                                               EGLint y,
142                                               EGLint width,
143                                               EGLint height)
144 {
145     UNREACHABLE();
146     return egl::NoError();
147 }
148 
querySurfacePointerANGLE(EGLint attribute,void ** value)149 egl::Error IOSurfaceSurfaceCGL::querySurfacePointerANGLE(EGLint attribute, void **value)
150 {
151     UNREACHABLE();
152     return egl::NoError();
153 }
154 
bindTexImage(const gl::Context * context,gl::Texture * texture,EGLint buffer)155 egl::Error IOSurfaceSurfaceCGL::bindTexImage(const gl::Context *context,
156                                              gl::Texture *texture,
157                                              EGLint buffer)
158 {
159     StateManagerGL *stateManager = GetStateManagerGL(context);
160 
161     const TextureGL *textureGL = GetImplAs<TextureGL>(texture);
162     GLuint textureID           = textureGL->getTextureID();
163     stateManager->bindTexture(gl::TextureType::Rectangle, textureID);
164 
165     const auto &format = kIOSurfaceFormats[mFormatIndex];
166     CGLError error     = CGLTexImageIOSurface2D(
167         mCGLContext, GL_TEXTURE_RECTANGLE, format.nativeInternalFormat, mWidth, mHeight,
168         format.nativeFormat, format.nativeType, mIOSurface, mPlane);
169 
170     if (error != kCGLNoError)
171     {
172         return egl::EglContextLost() << "CGLTexImageIOSurface2D failed: " << CGLErrorString(error);
173     }
174 
175     if (IsError(initializeAlphaChannel(context, textureID)))
176     {
177         return egl::EglContextLost() << "Failed to initialize IOSurface alpha channel.";
178     }
179 
180     return egl::NoError();
181 }
182 
releaseTexImage(const gl::Context * context,EGLint buffer)183 egl::Error IOSurfaceSurfaceCGL::releaseTexImage(const gl::Context *context, EGLint buffer)
184 {
185     const FunctionsGL *functions = GetFunctionsGL(context);
186     functions->flush();
187     return egl::NoError();
188 }
189 
setSwapInterval(EGLint interval)190 void IOSurfaceSurfaceCGL::setSwapInterval(EGLint interval)
191 {
192     UNREACHABLE();
193 }
194 
getWidth() const195 EGLint IOSurfaceSurfaceCGL::getWidth() const
196 {
197     return mWidth;
198 }
199 
getHeight() const200 EGLint IOSurfaceSurfaceCGL::getHeight() const
201 {
202     return mHeight;
203 }
204 
isPostSubBufferSupported() const205 EGLint IOSurfaceSurfaceCGL::isPostSubBufferSupported() const
206 {
207     UNREACHABLE();
208     return EGL_FALSE;
209 }
210 
getSwapBehavior() const211 EGLint IOSurfaceSurfaceCGL::getSwapBehavior() const
212 {
213     // N/A because you can't MakeCurrent an IOSurface, return any valid value.
214     return EGL_BUFFER_PRESERVED;
215 }
216 
217 // static
validateAttributes(EGLClientBuffer buffer,const egl::AttributeMap & attribs)218 bool IOSurfaceSurfaceCGL::validateAttributes(EGLClientBuffer buffer,
219                                              const egl::AttributeMap &attribs)
220 {
221     IOSurfaceRef ioSurface = reinterpret_cast<IOSurfaceRef>(buffer);
222 
223     // The plane must exist for this IOSurface. IOSurfaceGetPlaneCount can return 0 for non-planar
224     // ioSurfaces but we will treat non-planar like it is a single plane.
225     size_t surfacePlaneCount = std::max(size_t(1), IOSurfaceGetPlaneCount(ioSurface));
226     EGLAttrib plane          = attribs.get(EGL_IOSURFACE_PLANE_ANGLE);
227     if (plane < 0 || static_cast<size_t>(plane) >= surfacePlaneCount)
228     {
229         return false;
230     }
231 
232     // The width height specified must be at least (1, 1) and at most the plane size
233     EGLAttrib width  = attribs.get(EGL_WIDTH);
234     EGLAttrib height = attribs.get(EGL_HEIGHT);
235     if (width <= 0 || static_cast<size_t>(width) > IOSurfaceGetWidthOfPlane(ioSurface, plane) ||
236         height <= 0 || static_cast<size_t>(height) > IOSurfaceGetHeightOfPlane(ioSurface, plane))
237     {
238         return false;
239     }
240 
241     // Find this IOSurface format
242     EGLAttrib internalFormat = attribs.get(EGL_TEXTURE_INTERNAL_FORMAT_ANGLE);
243     EGLAttrib type           = attribs.get(EGL_TEXTURE_TYPE_ANGLE);
244 
245     int formatIndex =
246         FindIOSurfaceFormatIndex(static_cast<GLenum>(internalFormat), static_cast<GLenum>(type));
247 
248     if (formatIndex < 0)
249     {
250         return false;
251     }
252 
253     // FIXME: Check that the format matches this IOSurface plane for pixel formats that we know of.
254     // We could map IOSurfaceGetPixelFormat to expected type plane and format type.
255     // However, the caller might supply us non-public pixel format, which makes exhaustive checks
256     // problematic.
257     if (IOSurfaceGetBytesPerElementOfPlane(ioSurface, plane) !=
258         kIOSurfaceFormats[formatIndex].componentBytes)
259     {
260         WARN() << "IOSurface bytes per elements does not match the pbuffer internal format.";
261     }
262 
263     return true;
264 }
265 
266 // Wraps a FramebufferGL to hook the destroy function to delete the texture associated with the
267 // framebuffer.
268 class IOSurfaceFramebuffer : public FramebufferGL
269 {
270   public:
IOSurfaceFramebuffer(const gl::FramebufferState & data,GLuint id,GLuint textureId,bool isDefault,bool emulatedAlpha)271     IOSurfaceFramebuffer(const gl::FramebufferState &data,
272                          GLuint id,
273                          GLuint textureId,
274                          bool isDefault,
275                          bool emulatedAlpha)
276         : FramebufferGL(data, id, isDefault, emulatedAlpha), mTextureId(textureId)
277     {}
destroy(const gl::Context * context)278     void destroy(const gl::Context *context) override
279     {
280         GetFunctionsGL(context)->deleteTextures(1, &mTextureId);
281         FramebufferGL::destroy(context);
282     }
283 
284   private:
285     GLuint mTextureId;
286 };
287 
createDefaultFramebuffer(const gl::Context * context,const gl::FramebufferState & state)288 FramebufferImpl *IOSurfaceSurfaceCGL::createDefaultFramebuffer(const gl::Context *context,
289                                                                const gl::FramebufferState &state)
290 {
291     const FunctionsGL *functions = GetFunctionsGL(context);
292     StateManagerGL *stateManager = GetStateManagerGL(context);
293 
294     GLuint texture = 0;
295     functions->genTextures(1, &texture);
296     const auto &format = kIOSurfaceFormats[mFormatIndex];
297     stateManager->bindTexture(gl::TextureType::Rectangle, texture);
298     CGLError error = CGLTexImageIOSurface2D(
299         mCGLContext, GL_TEXTURE_RECTANGLE, format.nativeInternalFormat, mWidth, mHeight,
300         format.nativeFormat, format.nativeType, mIOSurface, mPlane);
301     if (error != kCGLNoError)
302     {
303         ERR() << "CGLTexImageIOSurface2D failed: " << CGLErrorString(error);
304     }
305     ASSERT(error == kCGLNoError);
306 
307     if (IsError(initializeAlphaChannel(context, texture)))
308     {
309         ERR() << "Failed to initialize IOSurface alpha channel.";
310     }
311 
312     GLuint framebuffer = 0;
313     functions->genFramebuffers(1, &framebuffer);
314     stateManager->bindFramebuffer(GL_FRAMEBUFFER, framebuffer);
315     stateManager->bindTexture(gl::TextureType::Rectangle, texture);
316     functions->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE,
317                                     texture, 0);
318 
319     return new IOSurfaceFramebuffer(state, framebuffer, texture, true, hasEmulatedAlphaChannel());
320 }
321 
initializeAlphaChannel(const gl::Context * context,GLuint texture)322 angle::Result IOSurfaceSurfaceCGL::initializeAlphaChannel(const gl::Context *context,
323                                                           GLuint texture)
324 {
325     if (mAlphaInitialized)
326     {
327         return angle::Result::Continue;
328     }
329 
330     BlitGL *blitter = GetBlitGL(context);
331     ANGLE_TRY(blitter->clearRenderableTextureAlphaToOne(context, texture,
332                                                         gl::TextureTarget::Rectangle, 0));
333     mAlphaInitialized = true;
334     return angle::Result::Continue;
335 }
336 
hasEmulatedAlphaChannel() const337 bool IOSurfaceSurfaceCGL::hasEmulatedAlphaChannel() const
338 {
339     const auto &format = kIOSurfaceFormats[mFormatIndex];
340     return format.internalFormat == GL_RGB;
341 }
342 
343 }  // namespace rx
344 
345 #endif  // defined(ANGLE_PLATFORM_MACOS) || defined(ANGLE_PLATFORM_MACCATALYST)
346