1/* 2 Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies) 3 4 This library is free software; you can redistribute it and/or 5 modify it under the terms of the GNU Library General Public 6 License as published by the Free Software Foundation; either 7 version 2 of the License, or (at your option) any later version. 8 9 This library is distributed in the hope that it will be useful, 10 but WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 Library General Public License for more details. 13 14 You should have received a copy of the GNU Library General Public License 15 along with this library; see the file COPYING.LIB. If not, write to 16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 17 Boston, MA 02110-1301, USA. 18 */ 19 20#include "config.h" 21#include "GraphicsSurface.h" 22 23#if USE(GRAPHICS_SURFACE) && OS(DARWIN) 24#include "TextureMapperGL.h" 25#include <CFNumber.h> 26#include <CGLContext.h> 27#include <CGLCurrent.h> 28#include <CGLIOSurface.h> 29#include <IOSurface/IOSurface.h> 30#include <OpenGL/OpenGL.h> 31#include <OpenGL/gl.h> 32#include <mach/mach.h> 33 34#if PLATFORM(QT) 35#include <QGuiApplication> 36#include <QOpenGLContext> 37#include <qpa/qplatformnativeinterface.h> 38#endif 39 40namespace WebCore { 41 42static uint32_t createTexture(IOSurfaceRef handle) 43{ 44 GLuint texture = 0; 45 GLuint format = GL_BGRA; 46 GLuint type = GL_UNSIGNED_INT_8_8_8_8_REV; 47 GLuint internalFormat = GL_RGBA; 48 CGLContextObj context = CGLGetCurrentContext(); 49 if (!context) 50 return 0; 51 52 GLint prevTexture; 53 glGetIntegerv(GL_TEXTURE_RECTANGLE_ARB, &prevTexture); 54 55 glGenTextures(1, &texture); 56 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, texture); 57 CGLError error = CGLTexImageIOSurface2D(context, GL_TEXTURE_RECTANGLE_ARB, internalFormat, IOSurfaceGetWidth(handle), IOSurfaceGetHeight(handle), format, type, handle, 0); 58 if (error) { 59 glDeleteTextures(1, &texture); 60 texture = 0; 61 } 62 63 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, prevTexture); 64 65 return texture; 66} 67 68struct GraphicsSurfacePrivate { 69public: 70 GraphicsSurfacePrivate(const GraphicsSurfaceToken& token, const IntSize& size) 71 : m_context(0) 72 , m_size(size) 73 , m_token(token) 74 , m_frontBufferTexture(0) 75 , m_frontBufferReadTexture(0) 76 , m_backBufferTexture(0) 77 , m_backBufferReadTexture(0) 78 , m_readFbo(0) 79 , m_drawFbo(0) 80 { 81 m_frontBuffer = IOSurfaceLookupFromMachPort(m_token.frontBufferHandle); 82 m_backBuffer = IOSurfaceLookupFromMachPort(m_token.backBufferHandle); 83 } 84 85 GraphicsSurfacePrivate(const PlatformGraphicsContext3D shareContext, const IntSize& size, GraphicsSurface::Flags flags) 86 : m_context(0) 87 , m_size(size) 88 , m_frontBufferTexture(0) 89 , m_frontBufferReadTexture(0) 90 , m_backBufferTexture(0) 91 , m_backBufferReadTexture(0) 92 , m_readFbo(0) 93 , m_drawFbo(0) 94 { 95#if PLATFORM(QT) 96 QPlatformNativeInterface* nativeInterface = QGuiApplication::platformNativeInterface(); 97 CGLContextObj shareContextObject = static_cast<CGLContextObj>(nativeInterface->nativeResourceForContext(QByteArrayLiteral("cglContextObj"), shareContext)); 98 if (!shareContextObject) 99 return; 100 101 CGLPixelFormatObj pixelFormatObject = CGLGetPixelFormat(shareContextObject); 102 if (kCGLNoError != CGLCreateContext(pixelFormatObject, shareContextObject, &m_context)) 103 return; 104 105 CGLRetainContext(m_context); 106#endif 107 108 unsigned pixelFormat = 'BGRA'; 109 unsigned bytesPerElement = 4; 110 int width = m_size.width(); 111 int height = m_size.height(); 112 113 unsigned long bytesPerRow = IOSurfaceAlignProperty(kIOSurfaceBytesPerRow, width * bytesPerElement); 114 if (!bytesPerRow) 115 return; 116 117 unsigned long allocSize = IOSurfaceAlignProperty(kIOSurfaceAllocSize, height * bytesPerRow); 118 if (!allocSize) 119 return; 120 121 const void *keys[6]; 122 const void *values[6]; 123 keys[0] = kIOSurfaceWidth; 124 values[0] = CFNumberCreate(0, kCFNumberIntType, &width); 125 keys[1] = kIOSurfaceHeight; 126 values[1] = CFNumberCreate(0, kCFNumberIntType, &height); 127 keys[2] = kIOSurfacePixelFormat; 128 values[2] = CFNumberCreate(0, kCFNumberIntType, &pixelFormat); 129 keys[3] = kIOSurfaceBytesPerElement; 130 values[3] = CFNumberCreate(0, kCFNumberIntType, &bytesPerElement); 131 keys[4] = kIOSurfaceBytesPerRow; 132 values[4] = CFNumberCreate(0, kCFNumberLongType, &bytesPerRow); 133 keys[5] = kIOSurfaceAllocSize; 134 values[5] = CFNumberCreate(0, kCFNumberLongType, &allocSize); 135 136 CFDictionaryRef dict = CFDictionaryCreate(0, keys, values, 6, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 137 for (unsigned i = 0; i < 6; i++) 138 CFRelease(values[i]); 139 140 m_frontBuffer = IOSurfaceCreate(dict); 141 m_backBuffer = IOSurfaceCreate(dict); 142 143 if (!(flags & GraphicsSurface::SupportsSharing)) 144 return; 145 146 m_token = GraphicsSurfaceToken(IOSurfaceCreateMachPort(m_frontBuffer), IOSurfaceCreateMachPort(m_backBuffer)); 147 } 148 149 ~GraphicsSurfacePrivate() 150 { 151 if (m_frontBufferTexture) 152 glDeleteTextures(1, &m_frontBufferTexture); 153 154 if (m_frontBufferReadTexture) 155 glDeleteTextures(1, &m_frontBufferReadTexture); 156 157 if (m_backBufferTexture) 158 glDeleteTextures(1, &m_backBufferTexture); 159 160 if (m_backBufferReadTexture) 161 glDeleteTextures(1, &m_backBufferReadTexture); 162 163 if (m_frontBuffer) 164 CFRelease(IOSurfaceRef(m_frontBuffer)); 165 166 if (m_backBuffer) 167 CFRelease(IOSurfaceRef(m_backBuffer)); 168 169 if (m_readFbo) 170 glDeleteFramebuffers(1, &m_readFbo); 171 172 if (m_drawFbo) 173 glDeleteFramebuffers(1, &m_drawFbo); 174 175 if (m_context) 176 CGLReleaseContext(m_context); 177 178 if (m_token.frontBufferHandle) 179 mach_port_deallocate(mach_task_self(), m_token.frontBufferHandle); 180 if (m_token.backBufferHandle) 181 mach_port_deallocate(mach_task_self(), m_token.backBufferHandle); 182 183 } 184 185 uint32_t swapBuffers() 186 { 187 std::swap(m_frontBuffer, m_backBuffer); 188 std::swap(m_frontBufferTexture, m_backBufferTexture); 189 std::swap(m_frontBufferReadTexture, m_backBufferReadTexture); 190 191 return IOSurfaceGetID(m_frontBuffer); 192 } 193 194 void makeCurrent() 195 { 196 m_detachedContext = CGLGetCurrentContext(); 197 198 if (m_context) 199 CGLSetCurrentContext(m_context); 200 } 201 202 void doneCurrent() 203 { 204 CGLSetCurrentContext(m_detachedContext); 205 m_detachedContext = 0; 206 } 207 208 void copyFromTexture(uint32_t texture, const IntRect& sourceRect) 209 { 210 // FIXME: The following glFlush can possibly be replaced by using the GL_ARB_sync extension. 211 glFlush(); // Make sure the texture has actually been completely written in the original context. 212 213 makeCurrent(); 214 215 int x = sourceRect.x(); 216 int y = sourceRect.y(); 217 int width = sourceRect.width(); 218 int height = sourceRect.height(); 219 220 glPushAttrib(GL_ALL_ATTRIB_BITS); 221 GLint previousFBO; 222 glGetIntegerv(GL_FRAMEBUFFER_BINDING, &previousFBO); 223 224 if (!m_drawFbo) 225 glGenFramebuffers(1, &m_drawFbo); 226 227 if (!m_readFbo) 228 glGenFramebuffers(1, &m_readFbo); 229 230 glBindFramebuffer(GL_READ_FRAMEBUFFER, m_readFbo); 231 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); 232 233 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_drawFbo); 234 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE_ARB, backBufferTextureID(), 0); 235 glBlitFramebuffer(x, y, width, height, x, x+height, y+width, y, GL_COLOR_BUFFER_BIT, GL_LINEAR); // Flip the texture upside down. 236 237 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE_ARB, 0, 0); 238 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); 239 glBindFramebuffer(GL_FRAMEBUFFER, previousFBO); 240 glPopAttrib(); 241 242 // Flushing the gl command buffer is necessary to ensure the texture has correctly been bound to the IOSurface. 243 glFlush(); 244 245 swapBuffers(); 246 doneCurrent(); 247 } 248 249 GraphicsSurfaceToken token() const 250 { 251 return m_token; 252 } 253 254 uint32_t frontBufferTextureID() 255 { 256 if (!m_frontBufferReadTexture) 257 m_frontBufferReadTexture = createTexture(m_frontBuffer); 258 259 return m_frontBufferReadTexture; 260 } 261 262 uint32_t backBufferTextureID() 263 { 264 if (!m_backBufferTexture) 265 m_backBufferTexture = createTexture(m_backBuffer); 266 267 return m_backBufferTexture; 268 } 269 270 PlatformGraphicsSurface frontBuffer() const 271 { 272 return m_frontBuffer; 273 } 274 275 PlatformGraphicsSurface backBuffer() const 276 { 277 return m_backBuffer; 278 } 279 280 IntSize size() const 281 { 282 return m_size; 283 } 284 285private: 286 CGLContextObj m_context; 287 IntSize m_size; 288 CGLContextObj m_detachedContext; 289 PlatformGraphicsSurface m_frontBuffer; 290 PlatformGraphicsSurface m_backBuffer; 291 uint32_t m_frontBufferTexture; 292 uint32_t m_frontBufferReadTexture; 293 uint32_t m_backBufferTexture; 294 uint32_t m_backBufferReadTexture; 295 uint32_t m_readFbo; 296 uint32_t m_drawFbo; 297 GraphicsSurfaceToken m_token; 298}; 299 300GraphicsSurfaceToken GraphicsSurface::platformExport() 301{ 302 return m_private->token(); 303} 304 305uint32_t GraphicsSurface::platformGetTextureID() 306{ 307 return m_private->frontBufferTextureID(); 308} 309 310void GraphicsSurface::platformCopyToGLTexture(uint32_t target, uint32_t id, const IntRect& targetRect, const IntPoint& offset) 311{ 312 glPushAttrib(GL_ALL_ATTRIB_BITS); 313 if (!m_fbo) 314 glGenFramebuffers(1, &m_fbo); 315 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); 316 glBindTexture(target, id); 317 glBindFramebuffer(GL_READ_FRAMEBUFFER, m_fbo); 318 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE_ARB, m_private->frontBufferTextureID(), 0); 319 glCopyTexSubImage2D(target, 0, targetRect.x(), targetRect.y(), offset.x(), offset.y(), targetRect.width(), targetRect.height()); 320 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE_ARB, 0, 0); 321 glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); 322 glPopAttrib(); 323 324 // According to IOSurface's documentation, glBindFramebuffer is the one triggering an update of the surface's cache. 325 // If the web process starts rendering and unlocks the surface before this happens, we might copy contents 326 // of the currently rendering frame on our texture instead of the previously completed frame. 327 // Flush the command buffer to reduce the odds of this happening, this would not be necessary with double buffering. 328 glFlush(); 329} 330 331void GraphicsSurface::platformCopyFromTexture(uint32_t texture, const IntRect& sourceRect) 332{ 333 m_private->copyFromTexture(texture, sourceRect); 334} 335 336void GraphicsSurface::platformPaintToTextureMapper(TextureMapper* textureMapper, const FloatRect& targetRect, const TransformationMatrix& transform, float opacity) 337{ 338 IntSize size = m_private->size(); 339 FloatRect rectOnContents(FloatPoint::zero(), size); 340 TransformationMatrix adjustedTransform = transform; 341 adjustedTransform.multiply(TransformationMatrix::rectToRect(rectOnContents, targetRect)); 342 static_cast<TextureMapperGL*>(textureMapper)->drawTexture(m_private->frontBufferTextureID(), TextureMapperGL::ShouldBlend | TextureMapperGL::ShouldUseARBTextureRect, size, rectOnContents, adjustedTransform, opacity); 343} 344 345uint32_t GraphicsSurface::platformFrontBuffer() const 346{ 347 return IOSurfaceGetID(m_private->frontBuffer()); 348} 349 350uint32_t GraphicsSurface::platformSwapBuffers() 351{ 352 return m_private->swapBuffers(); 353} 354 355IntSize GraphicsSurface::platformSize() const 356{ 357 return m_private->size(); 358} 359 360PassRefPtr<GraphicsSurface> GraphicsSurface::platformCreate(const IntSize& size, Flags flags, const PlatformGraphicsContext3D shareContext) 361{ 362 // We currently disable support for CopyToTexture on Mac, because this is used for single buffered Tiles. 363 // The single buffered nature of this requires a call to glFlush, as described in platformCopyToTexture. 364 // This call blocks the GPU for about 40ms, which makes smooth animations impossible. 365 if (flags & SupportsCopyToTexture || flags & SupportsSingleBuffered) 366 return PassRefPtr<GraphicsSurface>(); 367 368 RefPtr<GraphicsSurface> surface = adoptRef(new GraphicsSurface(size, flags)); 369 surface->m_private = new GraphicsSurfacePrivate(shareContext, size, flags); 370 371 if (!surface->m_private->frontBuffer() || !surface->m_private->backBuffer()) 372 return PassRefPtr<GraphicsSurface>(); 373 374 return surface; 375} 376 377PassRefPtr<GraphicsSurface> GraphicsSurface::platformImport(const IntSize& size, Flags flags, const GraphicsSurfaceToken& token) 378{ 379 // We currently disable support for CopyToTexture on Mac, because this is used for single buffered Tiles. 380 // The single buffered nature of this requires a call to glFlush, as described in platformCopyToTexture. 381 // This call blocks the GPU for about 40ms, which makes smooth animations impossible. 382 if (flags & SupportsCopyToTexture || flags & SupportsSingleBuffered) 383 return PassRefPtr<GraphicsSurface>(); 384 385 RefPtr<GraphicsSurface> surface = adoptRef(new GraphicsSurface(size, flags)); 386 surface->m_private = new GraphicsSurfacePrivate(token, size); 387 388 if (!surface->m_private->frontBuffer() || !surface->m_private->backBuffer()) 389 return PassRefPtr<GraphicsSurface>(); 390 391 return surface; 392} 393 394static int ioSurfaceLockOptions(int lockOptions) 395{ 396 int options = 0; 397 if (lockOptions & GraphicsSurface::ReadOnly) 398 options |= kIOSurfaceLockReadOnly; 399 if (!(lockOptions & GraphicsSurface::RetainPixels)) 400 options |= kIOSurfaceLockAvoidSync; 401 402 return options; 403} 404 405char* GraphicsSurface::platformLock(const IntRect& rect, int* outputStride, LockOptions lockOptions) 406{ 407 // Locking is only necessary for single buffered use. 408 // In this case we only have a front buffer, so we only lock this one. 409 m_lockOptions = lockOptions; 410 IOReturn status = IOSurfaceLock(m_private->frontBuffer(), ioSurfaceLockOptions(m_lockOptions), 0); 411 if (status == kIOReturnCannotLock) { 412 m_lockOptions |= RetainPixels; 413 IOSurfaceLock(m_private->frontBuffer(), ioSurfaceLockOptions(m_lockOptions), 0); 414 } 415 416 int stride = IOSurfaceGetBytesPerRow(m_private->frontBuffer()); 417 if (outputStride) 418 *outputStride = stride; 419 420 char* base = static_cast<char*>(IOSurfaceGetBaseAddress(m_private->frontBuffer())); 421 422 return base + stride * rect.y() + rect.x() * 4; 423} 424 425void GraphicsSurface::platformUnlock() 426{ 427 IOSurfaceUnlock(m_private->frontBuffer(), ioSurfaceLockOptions(m_lockOptions), 0); 428} 429 430void GraphicsSurface::platformDestroy() 431{ 432 if (m_fbo) 433 glDeleteFramebuffers(1, &m_fbo); 434 if (m_private) 435 delete m_private; 436 m_private = 0; 437} 438 439} 440#endif 441