OLD | NEW |
| (Empty) |
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 namespace WebCore { | |
35 | |
36 static uint32_t createTexture(IOSurfaceRef handle) | |
37 { | |
38 GLuint texture = 0; | |
39 GLuint format = GL_BGRA; | |
40 GLuint type = GL_UNSIGNED_INT_8_8_8_8_REV; | |
41 GLuint internalFormat = GL_RGBA; | |
42 CGLContextObj context = CGLGetCurrentContext(); | |
43 if (!context) | |
44 return 0; | |
45 | |
46 GLint prevTexture; | |
47 glGetIntegerv(GL_TEXTURE_RECTANGLE_ARB, &prevTexture); | |
48 | |
49 glGenTextures(1, &texture); | |
50 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, texture); | |
51 CGLError error = CGLTexImageIOSurface2D(context, GL_TEXTURE_RECTANGLE_ARB, i
nternalFormat, IOSurfaceGetWidth(handle), IOSurfaceGetHeight(handle), format, ty
pe, handle, 0); | |
52 if (error) { | |
53 glDeleteTextures(1, &texture); | |
54 texture = 0; | |
55 } | |
56 | |
57 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, prevTexture); | |
58 | |
59 return texture; | |
60 } | |
61 | |
62 struct GraphicsSurfacePrivate { | |
63 public: | |
64 GraphicsSurfacePrivate(const GraphicsSurfaceToken& token, const IntSize& siz
e) | |
65 : m_context(0) | |
66 , m_size(size) | |
67 , m_token(token) | |
68 , m_frontBufferTexture(0) | |
69 , m_frontBufferReadTexture(0) | |
70 , m_backBufferTexture(0) | |
71 , m_backBufferReadTexture(0) | |
72 , m_readFbo(0) | |
73 , m_drawFbo(0) | |
74 { | |
75 m_frontBuffer = IOSurfaceLookupFromMachPort(m_token.frontBufferHandle); | |
76 m_backBuffer = IOSurfaceLookupFromMachPort(m_token.backBufferHandle); | |
77 } | |
78 | |
79 GraphicsSurfacePrivate(const PlatformGraphicsContext3D shareContext, const I
ntSize& size, GraphicsSurface::Flags flags) | |
80 : m_context(0) | |
81 , m_size(size) | |
82 , m_frontBufferTexture(0) | |
83 , m_frontBufferReadTexture(0) | |
84 , m_backBufferTexture(0) | |
85 , m_backBufferReadTexture(0) | |
86 , m_readFbo(0) | |
87 , m_drawFbo(0) | |
88 { | |
89 unsigned pixelFormat = 'BGRA'; | |
90 unsigned bytesPerElement = 4; | |
91 int width = m_size.width(); | |
92 int height = m_size.height(); | |
93 | |
94 unsigned long bytesPerRow = IOSurfaceAlignProperty(kIOSurfaceBytesPerRow
, width * bytesPerElement); | |
95 if (!bytesPerRow) | |
96 return; | |
97 | |
98 unsigned long allocSize = IOSurfaceAlignProperty(kIOSurfaceAllocSize, he
ight * bytesPerRow); | |
99 if (!allocSize) | |
100 return; | |
101 | |
102 const void *keys[6]; | |
103 const void *values[6]; | |
104 keys[0] = kIOSurfaceWidth; | |
105 values[0] = CFNumberCreate(0, kCFNumberIntType, &width); | |
106 keys[1] = kIOSurfaceHeight; | |
107 values[1] = CFNumberCreate(0, kCFNumberIntType, &height); | |
108 keys[2] = kIOSurfacePixelFormat; | |
109 values[2] = CFNumberCreate(0, kCFNumberIntType, &pixelFormat); | |
110 keys[3] = kIOSurfaceBytesPerElement; | |
111 values[3] = CFNumberCreate(0, kCFNumberIntType, &bytesPerElement); | |
112 keys[4] = kIOSurfaceBytesPerRow; | |
113 values[4] = CFNumberCreate(0, kCFNumberLongType, &bytesPerRow); | |
114 keys[5] = kIOSurfaceAllocSize; | |
115 values[5] = CFNumberCreate(0, kCFNumberLongType, &allocSize); | |
116 | |
117 CFDictionaryRef dict = CFDictionaryCreate(0, keys, values, 6, &kCFTypeDi
ctionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); | |
118 for (unsigned i = 0; i < 6; i++) | |
119 CFRelease(values[i]); | |
120 | |
121 m_frontBuffer = IOSurfaceCreate(dict); | |
122 m_backBuffer = IOSurfaceCreate(dict); | |
123 | |
124 if (!(flags & GraphicsSurface::SupportsSharing)) | |
125 return; | |
126 | |
127 m_token = GraphicsSurfaceToken(IOSurfaceCreateMachPort(m_frontBuffer), I
OSurfaceCreateMachPort(m_backBuffer)); | |
128 } | |
129 | |
130 ~GraphicsSurfacePrivate() | |
131 { | |
132 if (m_frontBufferTexture) | |
133 glDeleteTextures(1, &m_frontBufferTexture); | |
134 | |
135 if (m_frontBufferReadTexture) | |
136 glDeleteTextures(1, &m_frontBufferReadTexture); | |
137 | |
138 if (m_backBufferTexture) | |
139 glDeleteTextures(1, &m_backBufferTexture); | |
140 | |
141 if (m_backBufferReadTexture) | |
142 glDeleteTextures(1, &m_backBufferReadTexture); | |
143 | |
144 if (m_frontBuffer) | |
145 CFRelease(IOSurfaceRef(m_frontBuffer)); | |
146 | |
147 if (m_backBuffer) | |
148 CFRelease(IOSurfaceRef(m_backBuffer)); | |
149 | |
150 if (m_readFbo) | |
151 glDeleteFramebuffers(1, &m_readFbo); | |
152 | |
153 if (m_drawFbo) | |
154 glDeleteFramebuffers(1, &m_drawFbo); | |
155 | |
156 if (m_context) | |
157 CGLReleaseContext(m_context); | |
158 | |
159 if (m_token.frontBufferHandle) | |
160 mach_port_deallocate(mach_task_self(), m_token.frontBufferHandle); | |
161 if (m_token.backBufferHandle) | |
162 mach_port_deallocate(mach_task_self(), m_token.backBufferHandle); | |
163 | |
164 } | |
165 | |
166 uint32_t swapBuffers() | |
167 { | |
168 std::swap(m_frontBuffer, m_backBuffer); | |
169 std::swap(m_frontBufferTexture, m_backBufferTexture); | |
170 std::swap(m_frontBufferReadTexture, m_backBufferReadTexture); | |
171 | |
172 return IOSurfaceGetID(m_frontBuffer); | |
173 } | |
174 | |
175 void makeCurrent() | |
176 { | |
177 m_detachedContext = CGLGetCurrentContext(); | |
178 | |
179 if (m_context) | |
180 CGLSetCurrentContext(m_context); | |
181 } | |
182 | |
183 void doneCurrent() | |
184 { | |
185 CGLSetCurrentContext(m_detachedContext); | |
186 m_detachedContext = 0; | |
187 } | |
188 | |
189 void copyFromTexture(uint32_t texture, const IntRect& sourceRect) | |
190 { | |
191 // FIXME: The following glFlush can possibly be replaced by using the GL
_ARB_sync extension. | |
192 glFlush(); // Make sure the texture has actually been completely written
in the original context. | |
193 | |
194 makeCurrent(); | |
195 | |
196 int x = sourceRect.x(); | |
197 int y = sourceRect.y(); | |
198 int width = sourceRect.width(); | |
199 int height = sourceRect.height(); | |
200 | |
201 glPushAttrib(GL_ALL_ATTRIB_BITS); | |
202 GLint previousFBO; | |
203 glGetIntegerv(GL_FRAMEBUFFER_BINDING, &previousFBO); | |
204 | |
205 if (!m_drawFbo) | |
206 glGenFramebuffers(1, &m_drawFbo); | |
207 | |
208 if (!m_readFbo) | |
209 glGenFramebuffers(1, &m_readFbo); | |
210 | |
211 glBindFramebuffer(GL_READ_FRAMEBUFFER, m_readFbo); | |
212 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEX
TURE_2D, texture, 0); | |
213 | |
214 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_drawFbo); | |
215 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEX
TURE_RECTANGLE_ARB, backBufferTextureID(), 0); | |
216 glBlitFramebuffer(x, y, width, height, x, x+height, y+width, y, GL_COLOR
_BUFFER_BIT, GL_LINEAR); // Flip the texture upside down. | |
217 | |
218 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEX
TURE_RECTANGLE_ARB, 0, 0); | |
219 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEX
TURE_2D, 0, 0); | |
220 glBindFramebuffer(GL_FRAMEBUFFER, previousFBO); | |
221 glPopAttrib(); | |
222 | |
223 // Flushing the gl command buffer is necessary to ensure the texture has
correctly been bound to the IOSurface. | |
224 glFlush(); | |
225 | |
226 swapBuffers(); | |
227 doneCurrent(); | |
228 } | |
229 | |
230 GraphicsSurfaceToken token() const | |
231 { | |
232 return m_token; | |
233 } | |
234 | |
235 uint32_t frontBufferTextureID() | |
236 { | |
237 if (!m_frontBufferReadTexture) | |
238 m_frontBufferReadTexture = createTexture(m_frontBuffer); | |
239 | |
240 return m_frontBufferReadTexture; | |
241 } | |
242 | |
243 uint32_t backBufferTextureID() | |
244 { | |
245 if (!m_backBufferTexture) | |
246 m_backBufferTexture = createTexture(m_backBuffer); | |
247 | |
248 return m_backBufferTexture; | |
249 } | |
250 | |
251 PlatformGraphicsSurface frontBuffer() const | |
252 { | |
253 return m_frontBuffer; | |
254 } | |
255 | |
256 PlatformGraphicsSurface backBuffer() const | |
257 { | |
258 return m_backBuffer; | |
259 } | |
260 | |
261 IntSize size() const | |
262 { | |
263 return m_size; | |
264 } | |
265 | |
266 private: | |
267 CGLContextObj m_context; | |
268 IntSize m_size; | |
269 CGLContextObj m_detachedContext; | |
270 PlatformGraphicsSurface m_frontBuffer; | |
271 PlatformGraphicsSurface m_backBuffer; | |
272 uint32_t m_frontBufferTexture; | |
273 uint32_t m_frontBufferReadTexture; | |
274 uint32_t m_backBufferTexture; | |
275 uint32_t m_backBufferReadTexture; | |
276 uint32_t m_readFbo; | |
277 uint32_t m_drawFbo; | |
278 GraphicsSurfaceToken m_token; | |
279 }; | |
280 | |
281 GraphicsSurfaceToken GraphicsSurface::platformExport() | |
282 { | |
283 return m_private->token(); | |
284 } | |
285 | |
286 uint32_t GraphicsSurface::platformGetTextureID() | |
287 { | |
288 return m_private->frontBufferTextureID(); | |
289 } | |
290 | |
291 void GraphicsSurface::platformCopyToGLTexture(uint32_t target, uint32_t id, cons
t IntRect& targetRect, const IntPoint& offset) | |
292 { | |
293 glPushAttrib(GL_ALL_ATTRIB_BITS); | |
294 if (!m_fbo) | |
295 glGenFramebuffers(1, &m_fbo); | |
296 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); | |
297 glBindTexture(target, id); | |
298 glBindFramebuffer(GL_READ_FRAMEBUFFER, m_fbo); | |
299 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE
_RECTANGLE_ARB, m_private->frontBufferTextureID(), 0); | |
300 glCopyTexSubImage2D(target, 0, targetRect.x(), targetRect.y(), offset.x(), o
ffset.y(), targetRect.width(), targetRect.height()); | |
301 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE
_RECTANGLE_ARB, 0, 0); | |
302 glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); | |
303 glPopAttrib(); | |
304 | |
305 // According to IOSurface's documentation, glBindFramebuffer is the one trig
gering an update of the surface's cache. | |
306 // If the web process starts rendering and unlocks the surface before this h
appens, we might copy contents | |
307 // of the currently rendering frame on our texture instead of the previously
completed frame. | |
308 // Flush the command buffer to reduce the odds of this happening, this would
not be necessary with double buffering. | |
309 glFlush(); | |
310 } | |
311 | |
312 void GraphicsSurface::platformCopyFromTexture(uint32_t texture, const IntRect& s
ourceRect) | |
313 { | |
314 m_private->copyFromTexture(texture, sourceRect); | |
315 } | |
316 | |
317 void GraphicsSurface::platformPaintToTextureMapper(TextureMapper* textureMapper,
const FloatRect& targetRect, const TransformationMatrix& transform, float opaci
ty) | |
318 { | |
319 IntSize size = m_private->size(); | |
320 FloatRect rectOnContents(FloatPoint::zero(), size); | |
321 TransformationMatrix adjustedTransform = transform; | |
322 adjustedTransform.multiply(TransformationMatrix::rectToRect(rectOnContents,
targetRect)); | |
323 static_cast<TextureMapperGL*>(textureMapper)->drawTexture(m_private->frontBu
fferTextureID(), TextureMapperGL::ShouldBlend | TextureMapperGL::ShouldUseARBTex
tureRect, size, rectOnContents, adjustedTransform, opacity); | |
324 } | |
325 | |
326 uint32_t GraphicsSurface::platformFrontBuffer() const | |
327 { | |
328 return IOSurfaceGetID(m_private->frontBuffer()); | |
329 } | |
330 | |
331 uint32_t GraphicsSurface::platformSwapBuffers() | |
332 { | |
333 return m_private->swapBuffers(); | |
334 } | |
335 | |
336 IntSize GraphicsSurface::platformSize() const | |
337 { | |
338 return m_private->size(); | |
339 } | |
340 | |
341 PassRefPtr<GraphicsSurface> GraphicsSurface::platformCreate(const IntSize& size,
Flags flags, const PlatformGraphicsContext3D shareContext) | |
342 { | |
343 // We currently disable support for CopyToTexture on Mac, because this is us
ed for single buffered Tiles. | |
344 // The single buffered nature of this requires a call to glFlush, as describ
ed in platformCopyToTexture. | |
345 // This call blocks the GPU for about 40ms, which makes smooth animations im
possible. | |
346 if (flags & SupportsCopyToTexture || flags & SupportsSingleBuffered) | |
347 return PassRefPtr<GraphicsSurface>(); | |
348 | |
349 RefPtr<GraphicsSurface> surface = adoptRef(new GraphicsSurface(size, flags))
; | |
350 surface->m_private = new GraphicsSurfacePrivate(shareContext, size, flags); | |
351 | |
352 if (!surface->m_private->frontBuffer() || !surface->m_private->backBuffer()) | |
353 return PassRefPtr<GraphicsSurface>(); | |
354 | |
355 return surface; | |
356 } | |
357 | |
358 PassRefPtr<GraphicsSurface> GraphicsSurface::platformImport(const IntSize& size,
Flags flags, const GraphicsSurfaceToken& token) | |
359 { | |
360 // We currently disable support for CopyToTexture on Mac, because this is us
ed for single buffered Tiles. | |
361 // The single buffered nature of this requires a call to glFlush, as describ
ed in platformCopyToTexture. | |
362 // This call blocks the GPU for about 40ms, which makes smooth animations im
possible. | |
363 if (flags & SupportsCopyToTexture || flags & SupportsSingleBuffered) | |
364 return PassRefPtr<GraphicsSurface>(); | |
365 | |
366 RefPtr<GraphicsSurface> surface = adoptRef(new GraphicsSurface(size, flags))
; | |
367 surface->m_private = new GraphicsSurfacePrivate(token, size); | |
368 | |
369 if (!surface->m_private->frontBuffer() || !surface->m_private->backBuffer()) | |
370 return PassRefPtr<GraphicsSurface>(); | |
371 | |
372 return surface; | |
373 } | |
374 | |
375 static int ioSurfaceLockOptions(int lockOptions) | |
376 { | |
377 int options = 0; | |
378 if (lockOptions & GraphicsSurface::ReadOnly) | |
379 options |= kIOSurfaceLockReadOnly; | |
380 if (!(lockOptions & GraphicsSurface::RetainPixels)) | |
381 options |= kIOSurfaceLockAvoidSync; | |
382 | |
383 return options; | |
384 } | |
385 | |
386 char* GraphicsSurface::platformLock(const IntRect& rect, int* outputStride, Lock
Options lockOptions) | |
387 { | |
388 // Locking is only necessary for single buffered use. | |
389 // In this case we only have a front buffer, so we only lock this one. | |
390 m_lockOptions = lockOptions; | |
391 IOReturn status = IOSurfaceLock(m_private->frontBuffer(), ioSurfaceLockOptio
ns(m_lockOptions), 0); | |
392 if (status == kIOReturnCannotLock) { | |
393 m_lockOptions |= RetainPixels; | |
394 IOSurfaceLock(m_private->frontBuffer(), ioSurfaceLockOptions(m_lockOptio
ns), 0); | |
395 } | |
396 | |
397 int stride = IOSurfaceGetBytesPerRow(m_private->frontBuffer()); | |
398 if (outputStride) | |
399 *outputStride = stride; | |
400 | |
401 char* base = static_cast<char*>(IOSurfaceGetBaseAddress(m_private->frontBuff
er())); | |
402 | |
403 return base + stride * rect.y() + rect.x() * 4; | |
404 } | |
405 | |
406 void GraphicsSurface::platformUnlock() | |
407 { | |
408 IOSurfaceUnlock(m_private->frontBuffer(), ioSurfaceLockOptions(m_lockOptions
), 0); | |
409 } | |
410 | |
411 void GraphicsSurface::platformDestroy() | |
412 { | |
413 if (m_fbo) | |
414 glDeleteFramebuffers(1, &m_fbo); | |
415 if (m_private) | |
416 delete m_private; | |
417 m_private = 0; | |
418 } | |
419 | |
420 } | |
421 #endif | |
OLD | NEW |