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_R16UI, GL_UNSIGNED_SHORT, 2, GL_RED, GL_RED, GL_UNSIGNED_SHORT },
50 {GL_RG, GL_UNSIGNED_BYTE, 2, GL_RG, GL_RG, GL_UNSIGNED_BYTE },
51 {GL_RG, GL_UNSIGNED_SHORT, 4, GL_RG, GL_RG, GL_UNSIGNED_SHORT },
52 {GL_RGB, GL_UNSIGNED_BYTE, 4, GL_RGBA, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV },
53 {GL_BGRA_EXT, GL_UNSIGNED_BYTE, 4, GL_RGBA, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV },
54 {GL_RGB10_A2, GL_UNSIGNED_INT_2_10_10_10_REV, 4, GL_RGBA, GL_BGRA, GL_UNSIGNED_INT_2_10_10_10_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,RendererGL * renderer,CGLContextObj cglContext,EGLClientBuffer buffer,const egl::AttributeMap & attribs)74 IOSurfaceSurfaceCGL::IOSurfaceSurfaceCGL(const egl::SurfaceState &state,
75 RendererGL *renderer,
76 CGLContextObj cglContext,
77 EGLClientBuffer buffer,
78 const egl::AttributeMap &attribs)
79 : SurfaceGL(state),
80 mFunctions(renderer->getFunctions()),
81 mStateManager(renderer->getStateManager()),
82 mCGLContext(cglContext),
83 mIOSurface(nullptr),
84 mWidth(0),
85 mHeight(0),
86 mPlane(0),
87 mFormatIndex(-1),
88 mAlphaInitialized(false),
89 mTextureID(0),
90 mFramebufferID(0)
91 {
92 // Keep reference to the IOSurface so it doesn't get deleted while the pbuffer exists.
93 mIOSurface = reinterpret_cast<IOSurfaceRef>(buffer);
94 CFRetain(mIOSurface);
95
96 // Extract attribs useful for the call to CGLTexImageIOSurface2D
97 mWidth = static_cast<int>(attribs.get(EGL_WIDTH));
98 mHeight = static_cast<int>(attribs.get(EGL_HEIGHT));
99 mPlane = static_cast<int>(attribs.get(EGL_IOSURFACE_PLANE_ANGLE));
100
101 EGLAttrib internalFormat = attribs.get(EGL_TEXTURE_INTERNAL_FORMAT_ANGLE);
102 EGLAttrib type = attribs.get(EGL_TEXTURE_TYPE_ANGLE);
103 mFormatIndex =
104 FindIOSurfaceFormatIndex(static_cast<GLenum>(internalFormat), static_cast<GLenum>(type));
105 ASSERT(mFormatIndex >= 0);
106
107 mAlphaInitialized = !hasEmulatedAlphaChannel();
108 }
109
~IOSurfaceSurfaceCGL()110 IOSurfaceSurfaceCGL::~IOSurfaceSurfaceCGL()
111 {
112 if (mFramebufferID != 0)
113 {
114 mStateManager->deleteFramebuffer(mFramebufferID);
115 mFramebufferID = 0;
116 mStateManager->deleteTexture(mTextureID);
117 mTextureID = 0;
118 }
119
120 if (mIOSurface != nullptr)
121 {
122 CFRelease(mIOSurface);
123 mIOSurface = nullptr;
124 }
125 }
126
initialize(const egl::Display * display)127 egl::Error IOSurfaceSurfaceCGL::initialize(const egl::Display *display)
128 {
129 return egl::NoError();
130 }
131
makeCurrent(const gl::Context * context)132 egl::Error IOSurfaceSurfaceCGL::makeCurrent(const gl::Context *context)
133 {
134 return egl::NoError();
135 }
136
unMakeCurrent(const gl::Context * context)137 egl::Error IOSurfaceSurfaceCGL::unMakeCurrent(const gl::Context *context)
138 {
139 GetFunctionsGL(context)->flush();
140 return egl::NoError();
141 }
142
swap(const gl::Context * context)143 egl::Error IOSurfaceSurfaceCGL::swap(const gl::Context *context)
144 {
145 return egl::NoError();
146 }
147
postSubBuffer(const gl::Context * context,EGLint x,EGLint y,EGLint width,EGLint height)148 egl::Error IOSurfaceSurfaceCGL::postSubBuffer(const gl::Context *context,
149 EGLint x,
150 EGLint y,
151 EGLint width,
152 EGLint height)
153 {
154 UNREACHABLE();
155 return egl::NoError();
156 }
157
querySurfacePointerANGLE(EGLint attribute,void ** value)158 egl::Error IOSurfaceSurfaceCGL::querySurfacePointerANGLE(EGLint attribute, void **value)
159 {
160 UNREACHABLE();
161 return egl::NoError();
162 }
163
bindTexImage(const gl::Context * context,gl::Texture * texture,EGLint buffer)164 egl::Error IOSurfaceSurfaceCGL::bindTexImage(const gl::Context *context,
165 gl::Texture *texture,
166 EGLint buffer)
167 {
168 StateManagerGL *stateManager = GetStateManagerGL(context);
169
170 const TextureGL *textureGL = GetImplAs<TextureGL>(texture);
171 GLuint textureID = textureGL->getTextureID();
172 stateManager->bindTexture(gl::TextureType::Rectangle, textureID);
173
174 const auto &format = kIOSurfaceFormats[mFormatIndex];
175 CGLError error = CGLTexImageIOSurface2D(
176 mCGLContext, GL_TEXTURE_RECTANGLE, format.nativeInternalFormat, mWidth, mHeight,
177 format.nativeFormat, format.nativeType, mIOSurface, mPlane);
178
179 if (error != kCGLNoError)
180 {
181 return egl::EglContextLost() << "CGLTexImageIOSurface2D failed: " << CGLErrorString(error);
182 }
183
184 if (IsError(initializeAlphaChannel(context, textureID)))
185 {
186 return egl::EglContextLost() << "Failed to initialize IOSurface alpha channel.";
187 }
188
189 return egl::NoError();
190 }
191
releaseTexImage(const gl::Context * context,EGLint buffer)192 egl::Error IOSurfaceSurfaceCGL::releaseTexImage(const gl::Context *context, EGLint buffer)
193 {
194 const FunctionsGL *functions = GetFunctionsGL(context);
195 functions->flush();
196 return egl::NoError();
197 }
198
setSwapInterval(EGLint interval)199 void IOSurfaceSurfaceCGL::setSwapInterval(EGLint interval)
200 {
201 UNREACHABLE();
202 }
203
getWidth() const204 EGLint IOSurfaceSurfaceCGL::getWidth() const
205 {
206 return mWidth;
207 }
208
getHeight() const209 EGLint IOSurfaceSurfaceCGL::getHeight() const
210 {
211 return mHeight;
212 }
213
isPostSubBufferSupported() const214 EGLint IOSurfaceSurfaceCGL::isPostSubBufferSupported() const
215 {
216 UNREACHABLE();
217 return EGL_FALSE;
218 }
219
getSwapBehavior() const220 EGLint IOSurfaceSurfaceCGL::getSwapBehavior() const
221 {
222 // N/A because you can't MakeCurrent an IOSurface, return any valid value.
223 return EGL_BUFFER_PRESERVED;
224 }
225
226 // static
validateAttributes(EGLClientBuffer buffer,const egl::AttributeMap & attribs)227 bool IOSurfaceSurfaceCGL::validateAttributes(EGLClientBuffer buffer,
228 const egl::AttributeMap &attribs)
229 {
230 IOSurfaceRef ioSurface = reinterpret_cast<IOSurfaceRef>(buffer);
231
232 // The plane must exist for this IOSurface. IOSurfaceGetPlaneCount can return 0 for non-planar
233 // ioSurfaces but we will treat non-planar like it is a single plane.
234 size_t surfacePlaneCount = std::max(size_t(1), IOSurfaceGetPlaneCount(ioSurface));
235 EGLAttrib plane = attribs.get(EGL_IOSURFACE_PLANE_ANGLE);
236 if (plane < 0 || static_cast<size_t>(plane) >= surfacePlaneCount)
237 {
238 return false;
239 }
240
241 // The width height specified must be at least (1, 1) and at most the plane size
242 EGLAttrib width = attribs.get(EGL_WIDTH);
243 EGLAttrib height = attribs.get(EGL_HEIGHT);
244 if (width <= 0 || static_cast<size_t>(width) > IOSurfaceGetWidthOfPlane(ioSurface, plane) ||
245 height <= 0 || static_cast<size_t>(height) > IOSurfaceGetHeightOfPlane(ioSurface, plane))
246 {
247 return false;
248 }
249
250 // Find this IOSurface format
251 EGLAttrib internalFormat = attribs.get(EGL_TEXTURE_INTERNAL_FORMAT_ANGLE);
252 EGLAttrib type = attribs.get(EGL_TEXTURE_TYPE_ANGLE);
253
254 int formatIndex =
255 FindIOSurfaceFormatIndex(static_cast<GLenum>(internalFormat), static_cast<GLenum>(type));
256
257 if (formatIndex < 0)
258 {
259 return false;
260 }
261
262 // FIXME: Check that the format matches this IOSurface plane for pixel formats that we know of.
263 // We could map IOSurfaceGetPixelFormat to expected type plane and format type.
264 // However, the caller might supply us non-public pixel format, which makes exhaustive checks
265 // problematic.
266 if (IOSurfaceGetBytesPerElementOfPlane(ioSurface, plane) !=
267 kIOSurfaceFormats[formatIndex].componentBytes)
268 {
269 WARN() << "IOSurface bytes per elements does not match the pbuffer internal format.";
270 }
271
272 return true;
273 }
274
initializeAlphaChannel(const gl::Context * context,GLuint texture)275 angle::Result IOSurfaceSurfaceCGL::initializeAlphaChannel(const gl::Context *context,
276 GLuint texture)
277 {
278 if (mAlphaInitialized)
279 {
280 return angle::Result::Continue;
281 }
282
283 BlitGL *blitter = GetBlitGL(context);
284 ANGLE_TRY(blitter->clearRenderableTextureAlphaToOne(context, texture,
285 gl::TextureTarget::Rectangle, 0));
286 mAlphaInitialized = true;
287 return angle::Result::Continue;
288 }
289
hasEmulatedAlphaChannel() const290 bool IOSurfaceSurfaceCGL::hasEmulatedAlphaChannel() const
291 {
292 const auto &format = kIOSurfaceFormats[mFormatIndex];
293 return format.internalFormat == GL_RGB;
294 }
295
attachToFramebuffer(const gl::Context * context,gl::Framebuffer * framebuffer)296 egl::Error IOSurfaceSurfaceCGL::attachToFramebuffer(const gl::Context *context,
297 gl::Framebuffer *framebuffer)
298 {
299 FramebufferGL *framebufferGL = GetImplAs<FramebufferGL>(framebuffer);
300 ASSERT(framebufferGL->getFramebufferID() == 0);
301 if (mFramebufferID == 0)
302 {
303 GLuint textureID = 0;
304 mFunctions->genTextures(1, &textureID);
305 const auto &format = kIOSurfaceFormats[mFormatIndex];
306 mStateManager->bindTexture(gl::TextureType::Rectangle, textureID);
307 CGLError error = CGLTexImageIOSurface2D(
308 mCGLContext, GL_TEXTURE_RECTANGLE, format.nativeInternalFormat, mWidth, mHeight,
309 format.nativeFormat, format.nativeType, mIOSurface, mPlane);
310 if (error != kCGLNoError)
311 {
312 return egl::EglContextLost()
313 << "CGLTexImageIOSurface2D failed: " << CGLErrorString(error);
314 }
315 ASSERT(error == kCGLNoError);
316
317 // TODO: pass context
318 if (IsError(initializeAlphaChannel(context, textureID)))
319 {
320 return egl::EglContextLost() << "Failed to initialize IOSurface alpha channel.";
321 }
322
323 GLuint framebufferID = 0;
324 mFunctions->genFramebuffers(1, &framebufferID);
325 mStateManager->bindFramebuffer(GL_FRAMEBUFFER, framebufferID);
326 mStateManager->bindTexture(gl::TextureType::Rectangle, textureID);
327 mFunctions->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE,
328 textureID, 0);
329 mTextureID = textureID;
330 mFramebufferID = framebufferID;
331 }
332
333 framebufferGL->setFramebufferID(mFramebufferID);
334 return egl::NoError();
335 }
336
detachFromFramebuffer(const gl::Context * context,gl::Framebuffer * framebuffer)337 egl::Error IOSurfaceSurfaceCGL::detachFromFramebuffer(const gl::Context *context,
338 gl::Framebuffer *framebuffer)
339 {
340 FramebufferGL *framebufferGL = GetImplAs<FramebufferGL>(framebuffer);
341 ASSERT(framebufferGL->getFramebufferID() == mFramebufferID);
342
343 framebufferGL->setFramebufferID(0);
344 return egl::NoError();
345 }
346
347 } // namespace rx
348