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