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