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