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