• 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     // Check that the format matches this IOSurface plane
254     if (IOSurfaceGetBytesPerElementOfPlane(ioSurface, plane) !=
255         kIOSurfaceFormats[formatIndex].componentBytes)
256     {
257         return false;
258     }
259 
260     return true;
261 }
262 
263 // Wraps a FramebufferGL to hook the destroy function to delete the texture associated with the
264 // framebuffer.
265 class IOSurfaceFramebuffer : public FramebufferGL
266 {
267   public:
IOSurfaceFramebuffer(const gl::FramebufferState & data,GLuint id,GLuint textureId,bool isDefault,bool emulatedAlpha)268     IOSurfaceFramebuffer(const gl::FramebufferState &data,
269                          GLuint id,
270                          GLuint textureId,
271                          bool isDefault,
272                          bool emulatedAlpha)
273         : FramebufferGL(data, id, isDefault, emulatedAlpha), mTextureId(textureId)
274     {}
destroy(const gl::Context * context)275     void destroy(const gl::Context *context) override
276     {
277         GetFunctionsGL(context)->deleteTextures(1, &mTextureId);
278         FramebufferGL::destroy(context);
279     }
280 
281   private:
282     GLuint mTextureId;
283 };
284 
createDefaultFramebuffer(const gl::Context * context,const gl::FramebufferState & state)285 FramebufferImpl *IOSurfaceSurfaceCGL::createDefaultFramebuffer(const gl::Context *context,
286                                                                const gl::FramebufferState &state)
287 {
288     const FunctionsGL *functions = GetFunctionsGL(context);
289     StateManagerGL *stateManager = GetStateManagerGL(context);
290 
291     GLuint texture = 0;
292     functions->genTextures(1, &texture);
293     const auto &format = kIOSurfaceFormats[mFormatIndex];
294     stateManager->bindTexture(gl::TextureType::Rectangle, texture);
295     CGLError error = CGLTexImageIOSurface2D(
296         mCGLContext, GL_TEXTURE_RECTANGLE, format.nativeInternalFormat, mWidth, mHeight,
297         format.nativeFormat, format.nativeType, mIOSurface, mPlane);
298     if (error != kCGLNoError)
299     {
300         ERR() << "CGLTexImageIOSurface2D failed: " << CGLErrorString(error);
301     }
302     ASSERT(error == kCGLNoError);
303 
304     if (IsError(initializeAlphaChannel(context, texture)))
305     {
306         ERR() << "Failed to initialize IOSurface alpha channel.";
307     }
308 
309     GLuint framebuffer = 0;
310     functions->genFramebuffers(1, &framebuffer);
311     stateManager->bindFramebuffer(GL_FRAMEBUFFER, framebuffer);
312     stateManager->bindTexture(gl::TextureType::Rectangle, texture);
313     functions->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE,
314                                     texture, 0);
315 
316     return new IOSurfaceFramebuffer(state, framebuffer, texture, true, hasEmulatedAlphaChannel());
317 }
318 
initializeAlphaChannel(const gl::Context * context,GLuint texture)319 angle::Result IOSurfaceSurfaceCGL::initializeAlphaChannel(const gl::Context *context,
320                                                           GLuint texture)
321 {
322     if (mAlphaInitialized)
323     {
324         return angle::Result::Continue;
325     }
326 
327     BlitGL *blitter = GetBlitGL(context);
328     ANGLE_TRY(blitter->clearRenderableTextureAlphaToOne(context, texture,
329                                                         gl::TextureTarget::Rectangle, 0));
330     mAlphaInitialized = true;
331     return angle::Result::Continue;
332 }
333 
hasEmulatedAlphaChannel() const334 bool IOSurfaceSurfaceCGL::hasEmulatedAlphaChannel() const
335 {
336     const auto &format = kIOSurfaceFormats[mFormatIndex];
337     return format.internalFormat == GL_RGB;
338 }
339 
340 }  // namespace rx
341 
342 #endif  // defined(ANGLE_PLATFORM_MACOS) || defined(ANGLE_PLATFORM_MACCATALYST)
343