1/*
2 * Copyright (C) 2010, 2013 Apple Inc. All rights reserved.
3 * Copyright (C) 2011 Google Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include "config.h"
28
29#if ENABLE(GRAPHICS_CONTEXT_3D)
30
31#include "GraphicsContext3D.h"
32
33#if PLATFORM(IOS_FAMILY)
34#include "GraphicsContext3DIOS.h"
35#endif
36#include "Extensions3DOpenGL.h"
37#include "IntRect.h"
38#include "IntSize.h"
39#include "NotImplemented.h"
40#include "TemporaryOpenGLSetting.h"
41#include <algorithm>
42#include <cstring>
43#include <wtf/MainThread.h>
44#include <wtf/text/CString.h>
45
46#if USE(ACCELERATE)
47#include <Accelerate/Accelerate.h>
48#endif
49
50#if PLATFORM(GTK) || PLATFORM(WIN)
51#include "OpenGLShims.h"
52#elif USE(OPENGL_ES)
53#import <OpenGLES/ES2/glext.h>
54// From <OpenGLES/glext.h>
55#define GL_RGBA32F_ARB 0x8814
56#define GL_RGB32F_ARB 0x8815
57#elif USE(OPENGL)
58#define GL_DO_NOT_WARN_IF_MULTI_GL_VERSION_HEADERS_INCLUDED
59#include <OpenGL/gl.h>
60#include <OpenGL/gl3.h>
61#undef GL_DO_NOT_WARN_IF_MULTI_GL_VERSION_HEADERS_INCLUDED
62#endif
63
64namespace WebCore {
65
66void GraphicsContext3D::releaseShaderCompiler()
67{
68 makeContextCurrent();
69 notImplemented();
70}
71
72#if PLATFORM(MAC)
73static void wipeAlphaChannelFromPixels(int width, int height, unsigned char* pixels)
74{
75 // We can assume this doesn't overflow because the calling functions
76 // use checked arithmetic.
77 int totalBytes = width * height * 4;
78 for (int i = 0; i < totalBytes; i += 4)
79 pixels[i + 3] = 255;
80}
81#endif
82
83void GraphicsContext3D::readPixelsAndConvertToBGRAIfNecessary(int x, int y, int width, int height, unsigned char* pixels)
84{
85 // NVIDIA drivers have a bug where calling readPixels in BGRA can return the wrong values for the alpha channel when the alpha is off for the context.
86 if (!m_attrs.alpha && getExtensions().isNVIDIA()) {
87 ::glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
88#if USE(ACCELERATE)
89 vImage_Buffer src;
90 src.height = height;
91 src.width = width;
92 src.rowBytes = width * 4;
93 src.data = pixels;
94
95 vImage_Buffer dest;
96 dest.height = height;
97 dest.width = width;
98 dest.rowBytes = width * 4;
99 dest.data = pixels;
100
101 // Swap pixel channels from RGBA to BGRA.
102 const uint8_t map[4] = { 2, 1, 0, 3 };
103 vImagePermuteChannels_ARGB8888(&src, &dest, map, kvImageNoFlags);
104#else
105 int totalBytes = width * height * 4;
106 for (int i = 0; i < totalBytes; i += 4)
107 std::swap(pixels[i], pixels[i + 2]);
108#endif
109 } else
110 ::glReadPixels(x, y, width, height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, pixels);
111
112#if PLATFORM(MAC)
113 if (!m_attrs.alpha)
114 wipeAlphaChannelFromPixels(width, height, pixels);
115#endif
116}
117
118void GraphicsContext3D::validateAttributes()
119{
120 validateDepthStencil("GL_EXT_packed_depth_stencil");
121}
122
123bool GraphicsContext3D::reshapeFBOs(const IntSize& size)
124{
125 const int width = size.width();
126 const int height = size.height();
127 GLuint colorFormat, internalDepthStencilFormat = 0;
128 if (m_attrs.alpha) {
129 m_internalColorFormat = GL_RGBA8;
130 colorFormat = GL_RGBA;
131 } else {
132 m_internalColorFormat = GL_RGB8;
133 colorFormat = GL_RGB;
134 }
135 if (m_attrs.stencil || m_attrs.depth) {
136 // We don't allow the logic where stencil is required and depth is not.
137 // See GraphicsContext3D::validateAttributes.
138
139 Extensions3D& extensions = getExtensions();
140 // Use a 24 bit depth buffer where we know we have it.
141 if (extensions.supports("GL_EXT_packed_depth_stencil"))
142 internalDepthStencilFormat = GL_DEPTH24_STENCIL8_EXT;
143 else
144#if PLATFORM(COCOA) && USE(OPENGL_ES)
145 internalDepthStencilFormat = GL_DEPTH_COMPONENT16;
146#else
147 internalDepthStencilFormat = GL_DEPTH_COMPONENT;
148#endif
149 }
150
151 // Resize multisample FBO.
152 if (m_attrs.antialias) {
153 GLint maxSampleCount;
154 ::glGetIntegerv(GL_MAX_SAMPLES_EXT, &maxSampleCount);
155 // Using more than 4 samples is slow on some hardware and is unlikely to
156 // produce a significantly better result.
157 GLint sampleCount = std::min(4, maxSampleCount);
158 ::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_multisampleFBO);
159 ::glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, m_multisampleColorBuffer);
160#if PLATFORM(COCOA) && USE(OPENGL_ES)
161 ::glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, sampleCount, GL_RGBA8_OES, width, height);
162#else
163 ::glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, sampleCount, m_internalColorFormat, width, height);
164#endif
165 ::glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, m_multisampleColorBuffer);
166 if (m_attrs.stencil || m_attrs.depth) {
167 ::glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, m_multisampleDepthStencilBuffer);
168 ::glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, sampleCount, internalDepthStencilFormat, width, height);
169 if (m_attrs.stencil)
170 ::glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, m_multisampleDepthStencilBuffer);
171 if (m_attrs.depth)
172 ::glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, m_multisampleDepthStencilBuffer);
173 }
174 ::glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
175 if (glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) != GL_FRAMEBUFFER_COMPLETE_EXT) {
176 // FIXME: cleanup.
177 notImplemented();
178 }
179 }
180
181 // resize regular FBO
182 ::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_fbo);
183 ASSERT(m_texture);
184#if PLATFORM(COCOA)
185#if USE(OPENGL_ES)
186 ::glBindRenderbuffer(GL_RENDERBUFFER, m_texture);
187 ::glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_texture);
188 setRenderbufferStorageFromDrawable(m_currentWidth, m_currentHeight);
189#else
190 allocateIOSurfaceBackingStore(IntSize(width, height));
191 updateFramebufferTextureBackingStoreFromLayer();
192 ::glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_RECTANGLE_ARB, m_texture, 0);
193#endif // !USE(OPENGL_ES))
194#else
195 ::glBindTexture(GL_TEXTURE_2D, m_texture);
196 ::glTexImage2D(GL_TEXTURE_2D, 0, m_internalColorFormat, width, height, 0, colorFormat, GL_UNSIGNED_BYTE, 0);
197 ::glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, m_texture, 0);
198
199#if USE(COORDINATED_GRAPHICS)
200 if (m_compositorTexture) {
201 ::glBindTexture(GL_TEXTURE_2D, m_compositorTexture);
202 ::glTexImage2D(GL_TEXTURE_2D, 0, m_internalColorFormat, width, height, 0, colorFormat, GL_UNSIGNED_BYTE, 0);
203 ::glBindTexture(GL_TEXTURE_2D, 0);
204 ::glBindTexture(GL_TEXTURE_2D, m_intermediateTexture);
205 ::glTexImage2D(GL_TEXTURE_2D, 0, m_internalColorFormat, width, height, 0, colorFormat, GL_UNSIGNED_BYTE, 0);
206 ::glBindTexture(GL_TEXTURE_2D, 0);
207 }
208#endif
209#endif
210
211 attachDepthAndStencilBufferIfNeeded(internalDepthStencilFormat, width, height);
212
213 bool mustRestoreFBO = true;
214 if (m_attrs.antialias) {
215 ::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_multisampleFBO);
216 if (m_state.boundFBO == m_multisampleFBO)
217 mustRestoreFBO = false;
218 } else {
219 if (m_state.boundFBO == m_fbo)
220 mustRestoreFBO = false;
221 }
222
223 return mustRestoreFBO;
224}
225
226void GraphicsContext3D::attachDepthAndStencilBufferIfNeeded(GLuint internalDepthStencilFormat, int width, int height)
227{
228 if (!m_attrs.antialias && (m_attrs.stencil || m_attrs.depth)) {
229 ::glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, m_depthStencilBuffer);
230 ::glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, internalDepthStencilFormat, width, height);
231 if (m_attrs.stencil)
232 ::glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, m_depthStencilBuffer);
233 if (m_attrs.depth)
234 ::glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, m_depthStencilBuffer);
235 ::glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
236 }
237
238 if (glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) != GL_FRAMEBUFFER_COMPLETE_EXT) {
239 // FIXME: cleanup
240 notImplemented();
241 }
242}
243
244void GraphicsContext3D::resolveMultisamplingIfNecessary(const IntRect& rect)
245{
246 TemporaryOpenGLSetting scopedScissor(GL_SCISSOR_TEST, GL_FALSE);
247 TemporaryOpenGLSetting scopedDither(GL_DITHER, GL_FALSE);
248 TemporaryOpenGLSetting scopedDepth(GL_DEPTH_TEST, GL_FALSE);
249 TemporaryOpenGLSetting scopedStencil(GL_STENCIL_TEST, GL_FALSE);
250
251#if PLATFORM(COCOA) && USE(OPENGL_ES)
252 GLint boundFrameBuffer;
253 ::glGetIntegerv(GL_FRAMEBUFFER_BINDING, &boundFrameBuffer);
254#endif
255
256 ::glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, m_multisampleFBO);
257 ::glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, m_fbo);
258#if PLATFORM(COCOA) && USE(OPENGL_ES)
259 UNUSED_PARAM(rect);
260 ::glFlush();
261 ::glResolveMultisampleFramebufferAPPLE();
262 const GLenum discards[] = { GL_COLOR_ATTACHMENT0, GL_DEPTH_ATTACHMENT };
263 ::glDiscardFramebufferEXT(GL_READ_FRAMEBUFFER_APPLE, 2, discards);
264 ::glBindFramebuffer(GL_FRAMEBUFFER, boundFrameBuffer);
265#else
266 IntRect resolveRect = rect;
267 if (rect.isEmpty())
268 resolveRect = IntRect(0, 0, m_currentWidth, m_currentHeight);
269
270 ::glBlitFramebufferEXT(resolveRect.x(), resolveRect.y(), resolveRect.maxX(), resolveRect.maxY(), resolveRect.x(), resolveRect.y(), resolveRect.maxX(), resolveRect.maxY(), GL_COLOR_BUFFER_BIT, GL_LINEAR);
271#endif
272}
273
274void GraphicsContext3D::renderbufferStorage(GC3Denum target, GC3Denum internalformat, GC3Dsizei width, GC3Dsizei height)
275{
276 makeContextCurrent();
277#if USE(OPENGL)
278 switch (internalformat) {
279 case DEPTH_STENCIL:
280 internalformat = GL_DEPTH24_STENCIL8_EXT;
281 break;
282 case DEPTH_COMPONENT16:
283 internalformat = GL_DEPTH_COMPONENT;
284 break;
285 case RGBA4:
286 case RGB5_A1:
287 internalformat = GL_RGBA;
288 break;
289 case RGB565:
290 internalformat = GL_RGB;
291 break;
292 }
293#endif
294 ::glRenderbufferStorageEXT(target, internalformat, width, height);
295}
296
297void GraphicsContext3D::getIntegerv(GC3Denum pname, GC3Dint* value)
298{
299 // Need to emulate MAX_FRAGMENT/VERTEX_UNIFORM_VECTORS and MAX_VARYING_VECTORS
300 // because desktop GL's corresponding queries return the number of components
301 // whereas GLES2 return the number of vectors (each vector has 4 components).
302 // Therefore, the value returned by desktop GL needs to be divided by 4.
303 makeContextCurrent();
304 switch (pname) {
305#if USE(OPENGL)
306 case MAX_FRAGMENT_UNIFORM_VECTORS:
307 ::glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, value);
308 *value /= 4;
309 break;
310 case MAX_VERTEX_UNIFORM_VECTORS:
311 ::glGetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS, value);
312 *value /= 4;
313 break;
314 case MAX_VARYING_VECTORS:
315 if (isGLES2Compliant()) {
316 ASSERT(::glGetError() == GL_NO_ERROR);
317 ::glGetIntegerv(GL_MAX_VARYING_VECTORS, value);
318 if (::glGetError() == GL_INVALID_ENUM) {
319 ::glGetIntegerv(GL_MAX_VARYING_COMPONENTS, value);
320 *value /= 4;
321 }
322 } else {
323 ::glGetIntegerv(GL_MAX_VARYING_FLOATS, value);
324 *value /= 4;
325 }
326 break;
327#endif
328 case MAX_TEXTURE_SIZE:
329 ::glGetIntegerv(MAX_TEXTURE_SIZE, value);
330 if (getExtensions().requiresRestrictedMaximumTextureSize())
331 *value = std::min(4096, *value);
332 break;
333 case MAX_CUBE_MAP_TEXTURE_SIZE:
334 ::glGetIntegerv(MAX_CUBE_MAP_TEXTURE_SIZE, value);
335 if (getExtensions().requiresRestrictedMaximumTextureSize())
336 *value = std::min(1024, *value);
337 break;
338#if PLATFORM(MAC)
339 // Some older hardware advertises a larger maximum than they
340 // can actually handle. Rather than detecting such devices, simply
341 // clamp the maximum to 8192, which is big enough for a 5K display.
342 case MAX_RENDERBUFFER_SIZE:
343 ::glGetIntegerv(MAX_RENDERBUFFER_SIZE, value);
344 *value = std::min(8192, *value);
345 break;
346 case MAX_VIEWPORT_DIMS:
347 ::glGetIntegerv(MAX_VIEWPORT_DIMS, value);
348 value[0] = std::min(8192, value[0]);
349 value[1] = std::min(8192, value[1]);
350 break;
351#endif
352 default:
353 ::glGetIntegerv(pname, value);
354 }
355}
356
357void GraphicsContext3D::getShaderPrecisionFormat(GC3Denum shaderType, GC3Denum precisionType, GC3Dint* range, GC3Dint* precision)
358{
359 UNUSED_PARAM(shaderType);
360 ASSERT(range);
361 ASSERT(precision);
362
363 makeContextCurrent();
364
365 switch (precisionType) {
366 case GraphicsContext3D::LOW_INT:
367 case GraphicsContext3D::MEDIUM_INT:
368 case GraphicsContext3D::HIGH_INT:
369 // These values are for a 32-bit twos-complement integer format.
370 range[0] = 31;
371 range[1] = 30;
372 precision[0] = 0;
373 break;
374 case GraphicsContext3D::LOW_FLOAT:
375 case GraphicsContext3D::MEDIUM_FLOAT:
376 case GraphicsContext3D::HIGH_FLOAT:
377 // These values are for an IEEE single-precision floating-point format.
378 range[0] = 127;
379 range[1] = 127;
380 precision[0] = 23;
381 break;
382 default:
383 ASSERT_NOT_REACHED();
384 break;
385 }
386}
387
388bool GraphicsContext3D::texImage2D(GC3Denum target, GC3Dint level, GC3Denum internalformat, GC3Dsizei width, GC3Dsizei height, GC3Dint border, GC3Denum format, GC3Denum type, const void* pixels)
389{
390 if (width && height && !pixels) {
391 synthesizeGLError(INVALID_VALUE);
392 return false;
393 }
394
395 GC3Denum openGLFormat = format;
396 GC3Denum openGLInternalFormat = internalformat;
397#if USE(OPENGL)
398 if (type == GL_FLOAT) {
399 if (format == GL_RGBA)
400 openGLInternalFormat = GL_RGBA32F_ARB;
401 else if (format == GL_RGB)
402 openGLInternalFormat = GL_RGB32F_ARB;
403 } else if (type == HALF_FLOAT_OES) {
404 if (format == GL_RGBA)
405 openGLInternalFormat = GL_RGBA16F_ARB;
406 else if (format == GL_RGB)
407 openGLInternalFormat = GL_RGB16F_ARB;
408 else if (format == GL_LUMINANCE)
409 openGLInternalFormat = GL_LUMINANCE16F_ARB;
410 else if (format == GL_ALPHA)
411 openGLInternalFormat = GL_ALPHA16F_ARB;
412 else if (format == GL_LUMINANCE_ALPHA)
413 openGLInternalFormat = GL_LUMINANCE_ALPHA16F_ARB;
414 type = GL_HALF_FLOAT_ARB;
415 }
416
417 ASSERT(format != Extensions3D::SRGB8_ALPHA8_EXT);
418 if (format == Extensions3D::SRGB_ALPHA_EXT)
419 openGLFormat = GL_RGBA;
420 else if (format == Extensions3D::SRGB_EXT)
421 openGLFormat = GL_RGB;
422#endif
423
424 if (m_usingCoreProfile) {
425 // There are some format values used in WebGL that are deprecated when using a core profile, so we need
426 // to adapt them.
427 switch (openGLInternalFormat) {
428 case ALPHA:
429 // The format is a simple component containing an alpha value. It needs to be backed with a GL_RED plane.
430 // We change the formats to GL_RED (both need to be GL_ALPHA in WebGL) and instruct the texture to swizzle
431 // the red component values with the alpha component values.
432 openGLInternalFormat = openGLFormat = RED;
433 texParameteri(target, TEXTURE_SWIZZLE_A, RED);
434 break;
435 case LUMINANCE_ALPHA:
436 // The format has 2 components, an alpha one and a luminance one (same value for red, green and blue).
437 // It needs to be backed with a GL_RG plane, using the red component for the colors and the green component
438 // for alpha. We change the formats to GL_RG and swizzle the components.
439 openGLInternalFormat = openGLFormat = RG;
440 texParameteri(target, TEXTURE_SWIZZLE_R, RED);
441 texParameteri(target, TEXTURE_SWIZZLE_G, RED);
442 texParameteri(target, TEXTURE_SWIZZLE_B, RED);
443 texParameteri(target, TEXTURE_SWIZZLE_A, GREEN);
444 break;
445 default:
446 break;
447 }
448 }
449
450 texImage2DDirect(target, level, openGLInternalFormat, width, height, border, openGLFormat, type, pixels);
451 return true;
452}
453
454void GraphicsContext3D::depthRange(GC3Dclampf zNear, GC3Dclampf zFar)
455{
456 makeContextCurrent();
457#if PLATFORM(COCOA) && USE(OPENGL_ES)
458 ::glDepthRangef(static_cast<float>(zNear), static_cast<float>(zFar));
459#else
460 ::glDepthRange(zNear, zFar);
461#endif
462}
463
464void GraphicsContext3D::clearDepth(GC3Dclampf depth)
465{
466 makeContextCurrent();
467#if PLATFORM(COCOA) && USE(OPENGL_ES)
468 ::glClearDepthf(static_cast<float>(depth));
469#else
470 ::glClearDepth(depth);
471#endif
472}
473
474#if !PLATFORM(GTK)
475Extensions3D& GraphicsContext3D::getExtensions()
476{
477 if (!m_extensions)
478 m_extensions = std::make_unique<Extensions3DOpenGL>(this, isGLES2Compliant());
479 return *m_extensions;
480}
481#endif
482
483void GraphicsContext3D::readPixels(GC3Dint x, GC3Dint y, GC3Dsizei width, GC3Dsizei height, GC3Denum format, GC3Denum type, void* data)
484{
485 // FIXME: remove the two glFlush calls when the driver bug is fixed, i.e.,
486 // all previous rendering calls should be done before reading pixels.
487 makeContextCurrent();
488 ::glFlush();
489 if (m_attrs.antialias && m_state.boundFBO == m_multisampleFBO) {
490 resolveMultisamplingIfNecessary(IntRect(x, y, width, height));
491 ::glBindFramebufferEXT(GraphicsContext3D::FRAMEBUFFER, m_fbo);
492 ::glFlush();
493 }
494 ::glReadPixels(x, y, width, height, format, type, data);
495 if (m_attrs.antialias && m_state.boundFBO == m_multisampleFBO)
496 ::glBindFramebufferEXT(GraphicsContext3D::FRAMEBUFFER, m_multisampleFBO);
497
498#if PLATFORM(MAC)
499 if (!m_attrs.alpha && (format == GraphicsContext3D::RGBA || format == GraphicsContext3D::BGRA) && (m_state.boundFBO == m_fbo || (m_attrs.antialias && m_state.boundFBO == m_multisampleFBO)))
500 wipeAlphaChannelFromPixels(width, height, static_cast<unsigned char*>(data));
501#endif
502}
503
504}
505
506#endif // ENABLE(GRAPHICS_CONTEXT_3D)
507