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