diff options
Diffstat (limited to 'WebKit/chromium/src/WebGraphicsContext3DDefaultImpl.cpp')
-rw-r--r-- | WebKit/chromium/src/WebGraphicsContext3DDefaultImpl.cpp | 1634 |
1 files changed, 1634 insertions, 0 deletions
diff --git a/WebKit/chromium/src/WebGraphicsContext3DDefaultImpl.cpp b/WebKit/chromium/src/WebGraphicsContext3DDefaultImpl.cpp new file mode 100644 index 0000000..9a74601 --- /dev/null +++ b/WebKit/chromium/src/WebGraphicsContext3DDefaultImpl.cpp @@ -0,0 +1,1634 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(3D_CANVAS) + +#include "WebGraphicsContext3DDefaultImpl.h" + +#include "app/gfx/gl/gl_bindings.h" +#include "app/gfx/gl/gl_context.h" +#include "app/gfx/gl/gl_implementation.h" +#include "NotImplemented.h" +#include "WebView.h" +#include <wtf/OwnArrayPtr.h> +#include <wtf/PassOwnPtr.h> +#include <wtf/text/StringBuilder.h> +#include <wtf/text/WTFString.h> + +#include <stdio.h> +#include <string.h> + +namespace WebKit { + +enum { + MAX_VERTEX_UNIFORM_VECTORS = 0x8DFB, + MAX_VARYING_VECTORS = 0x8DFC, + MAX_FRAGMENT_UNIFORM_VECTORS = 0x8DFD +}; + +WebGraphicsContext3DDefaultImpl::VertexAttribPointerState::VertexAttribPointerState() + : enabled(false) + , buffer(0) + , indx(0) + , size(0) + , type(0) + , normalized(false) + , stride(0) + , offset(0) +{ +} + +WebGraphicsContext3DDefaultImpl::WebGraphicsContext3DDefaultImpl() + : m_initialized(false) + , m_renderDirectlyToWebView(false) + , m_isGLES2(false) + , m_haveEXTFramebufferObject(false) + , m_haveEXTFramebufferMultisample(false) + , m_haveANGLEFramebufferMultisample(false) + , m_texture(0) + , m_fbo(0) + , m_depthStencilBuffer(0) + , m_cachedWidth(0) + , m_cachedHeight(0) + , m_multisampleFBO(0) + , m_multisampleDepthStencilBuffer(0) + , m_multisampleColorBuffer(0) + , m_boundFBO(0) + , m_boundTexture(0) + , m_copyTextureToParentTextureFBO(0) +#ifdef FLIP_FRAMEBUFFER_VERTICALLY + , m_scanline(0) +#endif + , m_boundArrayBuffer(0) + , m_fragmentCompiler(0) + , m_vertexCompiler(0) +{ +} + +WebGraphicsContext3DDefaultImpl::~WebGraphicsContext3DDefaultImpl() +{ + if (m_initialized) { + makeContextCurrent(); + + if (m_attributes.antialias) { + glDeleteRenderbuffersEXT(1, &m_multisampleColorBuffer); + if (m_attributes.depth || m_attributes.stencil) + glDeleteRenderbuffersEXT(1, &m_multisampleDepthStencilBuffer); + glDeleteFramebuffersEXT(1, &m_multisampleFBO); + } else { + if (m_attributes.depth || m_attributes.stencil) + glDeleteRenderbuffersEXT(1, &m_depthStencilBuffer); + } + glDeleteTextures(1, &m_texture); + glDeleteFramebuffersEXT(1, &m_copyTextureToParentTextureFBO); +#ifdef FLIP_FRAMEBUFFER_VERTICALLY + if (m_scanline) + delete[] m_scanline; +#endif + glDeleteFramebuffersEXT(1, &m_fbo); + + m_glContext->Destroy(); + + for (ShaderSourceMap::iterator ii = m_shaderSourceMap.begin(); ii != m_shaderSourceMap.end(); ++ii) { + if (ii->second) + delete ii->second; + } + angleDestroyCompilers(); + } +} + +bool WebGraphicsContext3DDefaultImpl::initialize(WebGraphicsContext3D::Attributes attributes, WebView* webView, bool renderDirectlyToWebView) +{ + if (!gfx::GLContext::InitializeOneOff()) + return false; + + m_renderDirectlyToWebView = renderDirectlyToWebView; + gfx::GLContext* shareContext = 0; + + if (!renderDirectlyToWebView) { + // Pick up the compositor's context to share resources with. + WebGraphicsContext3D* viewContext = webView->graphicsContext3D(); + if (viewContext) { + WebGraphicsContext3DDefaultImpl* contextImpl = static_cast<WebGraphicsContext3DDefaultImpl*>(viewContext); + shareContext = contextImpl->m_glContext.get(); + } else { + // The compositor's context didn't get created + // successfully, so conceptually there is no way we can + // render successfully to the WebView. + m_renderDirectlyToWebView = false; + } + } + + // This implementation always renders offscreen regardless of + // whether renderDirectlyToWebView is true. Both DumpRenderTree + // and test_shell paint first to an intermediate offscreen buffer + // and from there to the window, and WebViewImpl::paint already + // correctly handles the case where the compositor is active but + // the output needs to go to a WebCanvas. + m_glContext = WTF::adoptPtr(gfx::GLContext::CreateOffscreenGLContext(shareContext)); + if (!m_glContext) + return false; + + m_attributes = attributes; + + // FIXME: for the moment we disable multisampling for the compositor. + // It actually works in this implementation, but there are a few + // considerations. First, we likely want to reduce the fuzziness in + // these tests as much as possible because we want to run pixel tests. + // Second, Mesa's multisampling doesn't seem to antialias straight + // edges in some CSS 3D samples. Third, we don't have multisampling + // support for the compositor in the normal case at the time of this + // writing. + if (renderDirectlyToWebView) + m_attributes.antialias = false; + + m_isGLES2 = gfx::GetGLImplementation() == gfx::kGLImplementationEGLGLES2; + const char* extensions = reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS)); + m_haveEXTFramebufferObject = strstr(extensions, "GL_EXT_framebuffer_object"); + m_haveEXTFramebufferMultisample = strstr(extensions, "GL_EXT_framebuffer_multisample"); + m_haveANGLEFramebufferMultisample = strstr(extensions, "GL_ANGLE_framebuffer_multisample"); + + validateAttributes(); + + if (!m_isGLES2) { + glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); + glEnable(GL_POINT_SPRITE); + } + + if (!angleCreateCompilers()) { + angleDestroyCompilers(); + return false; + } + + glGenFramebuffersEXT(1, &m_copyTextureToParentTextureFBO); + + m_initialized = true; + return true; +} + +void WebGraphicsContext3DDefaultImpl::validateAttributes() +{ + const char* extensions = reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS)); + + if (m_attributes.stencil) { + if (strstr(extensions, "GL_OES_packed_depth_stencil") + || strstr(extensions, "GL_EXT_packed_depth_stencil")) { + if (!m_attributes.depth) + m_attributes.depth = true; + } else + m_attributes.stencil = false; + } + if (m_attributes.antialias) { + bool isValidVendor = true; +#if PLATFORM(CG) + // Currently in Mac we only turn on antialias if vendor is NVIDIA. + const char* vendor = reinterpret_cast<const char*>(glGetString(GL_VENDOR)); + if (!strstr(vendor, "NVIDIA")) + isValidVendor = false; +#endif + if (!(isValidVendor + && (m_haveEXTFramebufferMultisample + || (m_haveANGLEFramebufferMultisample && strstr(extensions, "GL_OES_rgb8_rgba8"))))) + m_attributes.antialias = false; + + // Don't antialias when using Mesa to ensure more reliable testing and + // because it doesn't appear to multisample straight lines correctly. + const char* renderer = reinterpret_cast<const char*>(glGetString(GL_RENDERER)); + if (!strncmp(renderer, "Mesa", 4)) + m_attributes.antialias = false; + } + // FIXME: instead of enforcing premultipliedAlpha = true, implement the + // correct behavior when premultipliedAlpha = false is requested. + m_attributes.premultipliedAlpha = true; +} + +void WebGraphicsContext3DDefaultImpl::resolveMultisampledFramebuffer(unsigned x, unsigned y, unsigned width, unsigned height) +{ + if (m_attributes.antialias) { + glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, m_multisampleFBO); + glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, m_fbo); + if (m_haveEXTFramebufferMultisample) + glBlitFramebufferEXT(x, y, x + width, y + height, x, y, x + width, y + height, GL_COLOR_BUFFER_BIT, GL_NEAREST); + else { + ASSERT(m_haveANGLEFramebufferMultisample); + glBlitFramebufferANGLE(x, y, x + width, y + height, x, y, x + width, y + height, GL_COLOR_BUFFER_BIT, GL_NEAREST); + } + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_boundFBO); + } +} + +bool WebGraphicsContext3DDefaultImpl::makeContextCurrent() +{ + return m_glContext->MakeCurrent(); +} + +int WebGraphicsContext3DDefaultImpl::width() +{ + return m_cachedWidth; +} + +int WebGraphicsContext3DDefaultImpl::height() +{ + return m_cachedHeight; +} + +int WebGraphicsContext3DDefaultImpl::sizeInBytes(int type) +{ + switch (type) { + case GL_BYTE: + return sizeof(GLbyte); + case GL_UNSIGNED_BYTE: + return sizeof(GLubyte); + case GL_SHORT: + return sizeof(GLshort); + case GL_UNSIGNED_SHORT: + return sizeof(GLushort); + case GL_INT: + return sizeof(GLint); + case GL_UNSIGNED_INT: + return sizeof(GLuint); + case GL_FLOAT: + return sizeof(GLfloat); + } + return 0; +} + +bool WebGraphicsContext3DDefaultImpl::isGLES2Compliant() +{ + return m_isGLES2; +} + +unsigned int WebGraphicsContext3DDefaultImpl::getPlatformTextureId() +{ + return m_texture; +} + +void WebGraphicsContext3DDefaultImpl::prepareTexture() +{ + if (!m_renderDirectlyToWebView) { + // We need to prepare our rendering results for the compositor. + makeContextCurrent(); + resolveMultisampledFramebuffer(0, 0, m_cachedWidth, m_cachedHeight); + } +} + +static int createTextureObject(GLenum target) +{ + GLuint texture = 0; + glGenTextures(1, &texture); + glBindTexture(target, texture); + glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + return texture; +} + +void WebGraphicsContext3DDefaultImpl::reshape(int width, int height) +{ + m_cachedWidth = width; + m_cachedHeight = height; + makeContextCurrent(); + + GLenum target = GL_TEXTURE_2D; + + if (!m_texture) { + // Generate the texture object + m_texture = createTextureObject(target); + // Generate the framebuffer object + glGenFramebuffersEXT(1, &m_fbo); + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_fbo); + m_boundFBO = m_fbo; + if (m_attributes.depth || m_attributes.stencil) + glGenRenderbuffersEXT(1, &m_depthStencilBuffer); + // Generate the multisample framebuffer object + if (m_attributes.antialias) { + glGenFramebuffersEXT(1, &m_multisampleFBO); + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_multisampleFBO); + m_boundFBO = m_multisampleFBO; + glGenRenderbuffersEXT(1, &m_multisampleColorBuffer); + if (m_attributes.depth || m_attributes.stencil) + glGenRenderbuffersEXT(1, &m_multisampleDepthStencilBuffer); + } + } + + GLint internalMultisampledColorFormat, internalColorFormat, colorFormat, internalDepthStencilFormat = 0; + if (m_attributes.alpha) { + // GL_RGBA8_OES == GL_RGBA8 + internalMultisampledColorFormat = GL_RGBA8; + internalColorFormat = m_isGLES2 ? GL_RGBA : GL_RGBA8; + colorFormat = GL_RGBA; + } else { + // GL_RGB8_OES == GL_RGB8 + internalMultisampledColorFormat = GL_RGB8; + internalColorFormat = m_isGLES2 ? GL_RGB : GL_RGB8; + colorFormat = GL_RGB; + } + if (m_attributes.stencil || m_attributes.depth) { + // We don't allow the logic where stencil is required and depth is not. + // See GraphicsContext3DInternal constructor. + if (m_attributes.stencil && m_attributes.depth) + internalDepthStencilFormat = GL_DEPTH24_STENCIL8_EXT; + else { + if (m_isGLES2) + internalDepthStencilFormat = GL_DEPTH_COMPONENT16; + else + internalDepthStencilFormat = GL_DEPTH_COMPONENT; + } + } + + bool mustRestoreFBO = false; + + // Resize multisampling FBO + if (m_attributes.antialias) { + GLint maxSampleCount; + glGetIntegerv(GL_MAX_SAMPLES_EXT, &maxSampleCount); + GLint sampleCount = std::min(8, maxSampleCount); + if (m_boundFBO != m_multisampleFBO) { + mustRestoreFBO = true; + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_multisampleFBO); + } + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, m_multisampleColorBuffer); + if (m_haveEXTFramebufferMultisample) + glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, sampleCount, internalMultisampledColorFormat, width, height); + else { + ASSERT(m_haveANGLEFramebufferMultisample); + glRenderbufferStorageMultisampleANGLE(GL_RENDERBUFFER_EXT, sampleCount, internalMultisampledColorFormat, width, height); + } + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, m_multisampleColorBuffer); + if (m_attributes.stencil || m_attributes.depth) { + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, m_multisampleDepthStencilBuffer); + if (m_haveEXTFramebufferMultisample) + glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, sampleCount, internalDepthStencilFormat, width, height); + else { + ASSERT(m_haveANGLEFramebufferMultisample); + glRenderbufferStorageMultisampleANGLE(GL_RENDERBUFFER_EXT, sampleCount, internalDepthStencilFormat, width, height); + } + if (m_attributes.stencil) + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, m_multisampleDepthStencilBuffer); + if (m_attributes.depth) + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, m_multisampleDepthStencilBuffer); + } + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0); + GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); + if (status != GL_FRAMEBUFFER_COMPLETE_EXT) { + printf("GraphicsContext3D: multisampling framebuffer was incomplete\n"); + + // FIXME: cleanup. + notImplemented(); + } + } + + // Resize regular FBO + if (m_boundFBO != m_fbo) { + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_fbo); + mustRestoreFBO = true; + } + glBindTexture(target, m_texture); + glTexImage2D(target, 0, internalColorFormat, width, height, 0, colorFormat, GL_UNSIGNED_BYTE, 0); + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, target, m_texture, 0); + glBindTexture(target, 0); + if (!m_attributes.antialias && (m_attributes.stencil || m_attributes.depth)) { + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, m_depthStencilBuffer); + glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, internalDepthStencilFormat, width, height); + if (m_attributes.stencil) + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, m_depthStencilBuffer); + if (m_attributes.depth) + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, m_depthStencilBuffer); + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0); + } + GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); + if (status != GL_FRAMEBUFFER_COMPLETE_EXT) { + printf("WebGraphicsContext3DDefaultImpl: framebuffer was incomplete\n"); + + // FIXME: cleanup. + notImplemented(); + } + + if (m_attributes.antialias) { + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_multisampleFBO); + if (m_boundFBO == m_multisampleFBO) + mustRestoreFBO = false; + } + + // Initialize renderbuffers to 0. + GLfloat clearColor[] = {0, 0, 0, 0}, clearDepth = 0; + GLint clearStencil = 0; + GLboolean colorMask[] = {GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE}, depthMask = GL_TRUE; + GLuint stencilMask = 0xffffffff; + GLboolean isScissorEnabled = GL_FALSE; + GLboolean isDitherEnabled = GL_FALSE; + GLbitfield clearMask = GL_COLOR_BUFFER_BIT; + glGetFloatv(GL_COLOR_CLEAR_VALUE, clearColor); + glClearColor(0, 0, 0, 0); + glGetBooleanv(GL_COLOR_WRITEMASK, colorMask); + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + if (m_attributes.depth) { + glGetFloatv(GL_DEPTH_CLEAR_VALUE, &clearDepth); + glClearDepth(1); + glGetBooleanv(GL_DEPTH_WRITEMASK, &depthMask); + glDepthMask(GL_TRUE); + clearMask |= GL_DEPTH_BUFFER_BIT; + } + if (m_attributes.stencil) { + glGetIntegerv(GL_STENCIL_CLEAR_VALUE, &clearStencil); + glClearStencil(0); + glGetIntegerv(GL_STENCIL_WRITEMASK, reinterpret_cast<GLint*>(&stencilMask)); + glStencilMaskSeparate(GL_FRONT, 0xffffffff); + clearMask |= GL_STENCIL_BUFFER_BIT; + } + isScissorEnabled = glIsEnabled(GL_SCISSOR_TEST); + glDisable(GL_SCISSOR_TEST); + isDitherEnabled = glIsEnabled(GL_DITHER); + glDisable(GL_DITHER); + + glClear(clearMask); + + glClearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); + glColorMask(colorMask[0], colorMask[1], colorMask[2], colorMask[3]); + if (m_attributes.depth) { + glClearDepth(clearDepth); + glDepthMask(depthMask); + } + if (m_attributes.stencil) { + glClearStencil(clearStencil); + glStencilMaskSeparate(GL_FRONT, stencilMask); + } + if (isScissorEnabled) + glEnable(GL_SCISSOR_TEST); + else + glDisable(GL_SCISSOR_TEST); + if (isDitherEnabled) + glEnable(GL_DITHER); + else + glDisable(GL_DITHER); + + if (mustRestoreFBO) + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_boundFBO); + +#ifdef FLIP_FRAMEBUFFER_VERTICALLY + if (m_scanline) { + delete[] m_scanline; + m_scanline = 0; + } + m_scanline = new unsigned char[width * 4]; +#endif // FLIP_FRAMEBUFFER_VERTICALLY +} + +#ifdef FLIP_FRAMEBUFFER_VERTICALLY +void WebGraphicsContext3DDefaultImpl::flipVertically(unsigned char* framebuffer, + unsigned int width, + unsigned int height) +{ + unsigned char* scanline = m_scanline; + if (!scanline) + return; + unsigned int rowBytes = width * 4; + unsigned int count = height / 2; + for (unsigned int i = 0; i < count; i++) { + unsigned char* rowA = framebuffer + i * rowBytes; + unsigned char* rowB = framebuffer + (height - i - 1) * rowBytes; + // FIXME: this is where the multiplication of the alpha + // channel into the color buffer will need to occur if the + // user specifies the "premultiplyAlpha" flag in the context + // creation attributes. + memcpy(scanline, rowB, rowBytes); + memcpy(rowB, rowA, rowBytes); + memcpy(rowA, scanline, rowBytes); + } +} +#endif + +bool WebGraphicsContext3DDefaultImpl::readBackFramebuffer(unsigned char* pixels, size_t bufferSize) +{ + if (bufferSize != static_cast<size_t>(4 * width() * height())) + return false; + + makeContextCurrent(); + + // Earlier versions of this code used the GPU to flip the + // framebuffer vertically before reading it back for compositing + // via software. This code was quite complicated, used a lot of + // GPU memory, and didn't provide an obvious speedup. Since this + // vertical flip is only a temporary solution anyway until Chrome + // is fully GPU composited, it wasn't worth the complexity. + + resolveMultisampledFramebuffer(0, 0, m_cachedWidth, m_cachedHeight); + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_fbo); + + GLint packAlignment = 4; + bool mustRestorePackAlignment = false; + glGetIntegerv(GL_PACK_ALIGNMENT, &packAlignment); + if (packAlignment > 4) { + glPixelStorei(GL_PACK_ALIGNMENT, 4); + mustRestorePackAlignment = true; + } + + if (m_isGLES2) { + // FIXME: consider testing for presence of GL_OES_read_format + // and GL_EXT_read_format_bgra, and using GL_BGRA_EXT here + // directly. + glReadPixels(0, 0, m_cachedWidth, m_cachedHeight, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + for (size_t i = 0; i < bufferSize; i += 4) + std::swap(pixels[i], pixels[i + 2]); + } else + glReadPixels(0, 0, m_cachedWidth, m_cachedHeight, GL_BGRA, GL_UNSIGNED_BYTE, pixels); + + if (mustRestorePackAlignment) + glPixelStorei(GL_PACK_ALIGNMENT, packAlignment); + + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_boundFBO); + +#ifdef FLIP_FRAMEBUFFER_VERTICALLY + if (pixels) + flipVertically(pixels, m_cachedWidth, m_cachedHeight); +#endif + + return true; +} + +void WebGraphicsContext3DDefaultImpl::synthesizeGLError(unsigned long error) +{ + m_syntheticErrors.add(error); +} + +void* WebGraphicsContext3DDefaultImpl::mapBufferSubDataCHROMIUM(unsigned target, int offset, int size, unsigned access) +{ + return 0; +} + +void WebGraphicsContext3DDefaultImpl::unmapBufferSubDataCHROMIUM(const void* mem) +{ +} + +void* WebGraphicsContext3DDefaultImpl::mapTexSubImage2DCHROMIUM(unsigned target, int level, int xoffset, int yoffset, int width, int height, unsigned format, unsigned type, unsigned access) +{ + return 0; +} + +void WebGraphicsContext3DDefaultImpl::unmapTexSubImage2DCHROMIUM(const void* mem) +{ +} + +void WebGraphicsContext3DDefaultImpl::copyTextureToParentTextureCHROMIUM(unsigned id, unsigned id2) +{ + if (!glGetTexLevelParameteriv) + return; + + makeContextCurrent(); + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_copyTextureToParentTextureFBO); + glFramebufferTexture2DEXT(GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, + id, + 0); // level + glBindTexture(GL_TEXTURE_2D, id2); + GLsizei width, height; + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width); + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &height); + glCopyTexImage2D(GL_TEXTURE_2D, + 0, // level + GL_RGBA, + 0, 0, // x, y + width, + height, + 0); // border + glBindTexture(GL_TEXTURE_2D, m_boundTexture); + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_boundFBO); +} + +// Helper macros to reduce the amount of code. + +#define DELEGATE_TO_GL(name, glname) \ +void WebGraphicsContext3DDefaultImpl::name() \ +{ \ + makeContextCurrent(); \ + gl##glname(); \ +} + +#define DELEGATE_TO_GL_1(name, glname, t1) \ +void WebGraphicsContext3DDefaultImpl::name(t1 a1) \ +{ \ + makeContextCurrent(); \ + gl##glname(a1); \ +} + +#define DELEGATE_TO_GL_1R(name, glname, t1, rt) \ +rt WebGraphicsContext3DDefaultImpl::name(t1 a1) \ +{ \ + makeContextCurrent(); \ + return gl##glname(a1); \ +} + +#define DELEGATE_TO_GL_2(name, glname, t1, t2) \ +void WebGraphicsContext3DDefaultImpl::name(t1 a1, t2 a2) \ +{ \ + makeContextCurrent(); \ + gl##glname(a1, a2); \ +} + +#define DELEGATE_TO_GL_2R(name, glname, t1, t2, rt) \ +rt WebGraphicsContext3DDefaultImpl::name(t1 a1, t2 a2) \ +{ \ + makeContextCurrent(); \ + return gl##glname(a1, a2); \ +} + +#define DELEGATE_TO_GL_3(name, glname, t1, t2, t3) \ +void WebGraphicsContext3DDefaultImpl::name(t1 a1, t2 a2, t3 a3) \ +{ \ + makeContextCurrent(); \ + gl##glname(a1, a2, a3); \ +} + +#define DELEGATE_TO_GL_4(name, glname, t1, t2, t3, t4) \ +void WebGraphicsContext3DDefaultImpl::name(t1 a1, t2 a2, t3 a3, t4 a4) \ +{ \ + makeContextCurrent(); \ + gl##glname(a1, a2, a3, a4); \ +} + +#define DELEGATE_TO_GL_5(name, glname, t1, t2, t3, t4, t5) \ +void WebGraphicsContext3DDefaultImpl::name(t1 a1, t2 a2, t3 a3, t4 a4, t5 a5) \ +{ \ + makeContextCurrent(); \ + gl##glname(a1, a2, a3, a4, a5); \ +} + +#define DELEGATE_TO_GL_6(name, glname, t1, t2, t3, t4, t5, t6) \ +void WebGraphicsContext3DDefaultImpl::name(t1 a1, t2 a2, t3 a3, t4 a4, t5 a5, t6 a6) \ +{ \ + makeContextCurrent(); \ + gl##glname(a1, a2, a3, a4, a5, a6); \ +} + +#define DELEGATE_TO_GL_7(name, glname, t1, t2, t3, t4, t5, t6, t7) \ +void WebGraphicsContext3DDefaultImpl::name(t1 a1, t2 a2, t3 a3, t4 a4, t5 a5, t6 a6, t7 a7) \ +{ \ + makeContextCurrent(); \ + gl##glname(a1, a2, a3, a4, a5, a6, a7); \ +} + +#define DELEGATE_TO_GL_8(name, glname, t1, t2, t3, t4, t5, t6, t7, t8) \ +void WebGraphicsContext3DDefaultImpl::name(t1 a1, t2 a2, t3 a3, t4 a4, t5 a5, t6 a6, t7 a7, t8 a8) \ +{ \ + makeContextCurrent(); \ + gl##glname(a1, a2, a3, a4, a5, a6, a7, a8); \ +} + +#define DELEGATE_TO_GL_9(name, glname, t1, t2, t3, t4, t5, t6, t7, t8, t9) \ +void WebGraphicsContext3DDefaultImpl::name(t1 a1, t2 a2, t3 a3, t4 a4, t5 a5, t6 a6, t7 a7, t8 a8, t9 a9) \ +{ \ + makeContextCurrent(); \ + gl##glname(a1, a2, a3, a4, a5, a6, a7, a8, a9); \ +} + +void WebGraphicsContext3DDefaultImpl::activeTexture(unsigned long texture) +{ + // FIXME: query number of textures available. + if (texture < GL_TEXTURE0 || texture > GL_TEXTURE0+32) + // FIXME: raise exception. + return; + + makeContextCurrent(); + glActiveTexture(texture); +} + +DELEGATE_TO_GL_2(attachShader, AttachShader, WebGLId, WebGLId) + +DELEGATE_TO_GL_3(bindAttribLocation, BindAttribLocation, WebGLId, unsigned long, const char*) + +void WebGraphicsContext3DDefaultImpl::bindBuffer(unsigned long target, WebGLId buffer) +{ + makeContextCurrent(); + if (target == GL_ARRAY_BUFFER) + m_boundArrayBuffer = buffer; + glBindBuffer(target, buffer); +} + +void WebGraphicsContext3DDefaultImpl::bindFramebuffer(unsigned long target, WebGLId framebuffer) +{ + makeContextCurrent(); + if (!framebuffer) + framebuffer = (m_attributes.antialias ? m_multisampleFBO : m_fbo); + if (framebuffer != m_boundFBO) { + glBindFramebufferEXT(target, framebuffer); + m_boundFBO = framebuffer; + } +} + +DELEGATE_TO_GL_2(bindRenderbuffer, BindRenderbufferEXT, unsigned long, WebGLId) + +void WebGraphicsContext3DDefaultImpl::bindTexture(unsigned long target, WebGLId texture) +{ + makeContextCurrent(); + glBindTexture(target, texture); + m_boundTexture = texture; +} + +DELEGATE_TO_GL_4(blendColor, BlendColor, double, double, double, double) + +DELEGATE_TO_GL_1(blendEquation, BlendEquation, unsigned long) + +DELEGATE_TO_GL_2(blendEquationSeparate, BlendEquationSeparate, unsigned long, unsigned long) + +DELEGATE_TO_GL_2(blendFunc, BlendFunc, unsigned long, unsigned long) + +DELEGATE_TO_GL_4(blendFuncSeparate, BlendFuncSeparate, unsigned long, unsigned long, unsigned long, unsigned long) + +DELEGATE_TO_GL_4(bufferData, BufferData, unsigned long, int, const void*, unsigned long) + +DELEGATE_TO_GL_4(bufferSubData, BufferSubData, unsigned long, long, int, const void*) + +DELEGATE_TO_GL_1R(checkFramebufferStatus, CheckFramebufferStatusEXT, unsigned long, unsigned long) + +DELEGATE_TO_GL_1(clear, Clear, unsigned long) + +DELEGATE_TO_GL_4(clearColor, ClearColor, double, double, double, double) + +DELEGATE_TO_GL_1(clearDepth, ClearDepth, double) + +DELEGATE_TO_GL_1(clearStencil, ClearStencil, long) + +DELEGATE_TO_GL_4(colorMask, ColorMask, bool, bool, bool, bool) + +void WebGraphicsContext3DDefaultImpl::compileShader(WebGLId shader) +{ + makeContextCurrent(); + + ShaderSourceMap::iterator result = m_shaderSourceMap.find(shader); + if (result == m_shaderSourceMap.end()) { + // Passing down to gl driver to generate the correct error; or the case + // where the shader deletion is delayed when it's attached to a program. + glCompileShader(shader); + return; + } + ShaderSourceEntry* entry = result->second; + ASSERT(entry); + + if (!angleValidateShaderSource(*entry)) + return; // Shader didn't validate, don't move forward with compiling translated source + + int shaderLength = entry->translatedSource ? strlen(entry->translatedSource) : 0; + glShaderSource(shader, 1, const_cast<const char**>(&entry->translatedSource), &shaderLength); + glCompileShader(shader); + +#ifndef NDEBUG + int compileStatus; + glGetShaderiv(shader, GL_COMPILE_STATUS, &compileStatus); + // ASSERT that ANGLE generated GLSL will be accepted by OpenGL + ASSERT(compileStatus == GL_TRUE); +#endif +} + +void WebGraphicsContext3DDefaultImpl::copyTexImage2D(unsigned long target, long level, unsigned long internalformat, + long x, long y, unsigned long width, unsigned long height, long border) +{ + makeContextCurrent(); + + bool needsResolve = (m_attributes.antialias && m_boundFBO == m_multisampleFBO); + if (needsResolve) { + resolveMultisampledFramebuffer(x, y, width, height); + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_fbo); + } + + glCopyTexImage2D(target, level, internalformat, x, y, width, height, border); + + if (needsResolve) + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_boundFBO); +} + +void WebGraphicsContext3DDefaultImpl::copyTexSubImage2D(unsigned long target, long level, long xoffset, long yoffset, + long x, long y, unsigned long width, unsigned long height) +{ + makeContextCurrent(); + + bool needsResolve = (m_attributes.antialias && m_boundFBO == m_multisampleFBO); + if (needsResolve) { + resolveMultisampledFramebuffer(x, y, width, height); + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_fbo); + } + + glCopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height); + + if (needsResolve) + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_boundFBO); +} + +DELEGATE_TO_GL_1(cullFace, CullFace, unsigned long) + +DELEGATE_TO_GL_1(depthFunc, DepthFunc, unsigned long) + +DELEGATE_TO_GL_1(depthMask, DepthMask, bool) + +DELEGATE_TO_GL_2(depthRange, DepthRange, double, double) + +DELEGATE_TO_GL_2(detachShader, DetachShader, WebGLId, WebGLId) + +DELEGATE_TO_GL_1(disable, Disable, unsigned long) + +void WebGraphicsContext3DDefaultImpl::disableVertexAttribArray(unsigned long index) +{ + makeContextCurrent(); + if (index < NumTrackedPointerStates) + m_vertexAttribPointerState[index].enabled = false; + glDisableVertexAttribArray(index); +} + +DELEGATE_TO_GL_3(drawArrays, DrawArrays, unsigned long, long, long) + +void WebGraphicsContext3DDefaultImpl::drawElements(unsigned long mode, unsigned long count, unsigned long type, long offset) +{ + makeContextCurrent(); + glDrawElements(mode, count, type, + reinterpret_cast<void*>(static_cast<intptr_t>(offset))); +} + +DELEGATE_TO_GL_1(enable, Enable, unsigned long) + +void WebGraphicsContext3DDefaultImpl::enableVertexAttribArray(unsigned long index) +{ + makeContextCurrent(); + if (index < NumTrackedPointerStates) + m_vertexAttribPointerState[index].enabled = true; + glEnableVertexAttribArray(index); +} + +DELEGATE_TO_GL(finish, Finish) + +DELEGATE_TO_GL(flush, Flush) + +DELEGATE_TO_GL_4(framebufferRenderbuffer, FramebufferRenderbufferEXT, unsigned long, unsigned long, unsigned long, WebGLId) + +DELEGATE_TO_GL_5(framebufferTexture2D, FramebufferTexture2DEXT, unsigned long, unsigned long, unsigned long, WebGLId, long) + +DELEGATE_TO_GL_1(frontFace, FrontFace, unsigned long) + +void WebGraphicsContext3DDefaultImpl::generateMipmap(unsigned long target) +{ + makeContextCurrent(); + if (m_isGLES2 || m_haveEXTFramebufferObject) + glGenerateMipmapEXT(target); + // FIXME: provide alternative code path? This will be unpleasant + // to implement if glGenerateMipmapEXT is not available -- it will + // require a texture readback and re-upload. +} + +bool WebGraphicsContext3DDefaultImpl::getActiveAttrib(WebGLId program, unsigned long index, ActiveInfo& info) +{ + makeContextCurrent(); + if (!program) { + synthesizeGLError(GL_INVALID_VALUE); + return false; + } + GLint maxNameLength = -1; + glGetProgramiv(program, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &maxNameLength); + if (maxNameLength < 0) + return false; + GLchar* name = 0; + if (!tryFastMalloc(maxNameLength * sizeof(GLchar)).getValue(name)) { + synthesizeGLError(GL_OUT_OF_MEMORY); + return false; + } + GLsizei length = 0; + GLint size = -1; + GLenum type = 0; + glGetActiveAttrib(program, index, maxNameLength, + &length, &size, &type, name); + if (size < 0) { + fastFree(name); + return false; + } + info.name = WebString::fromUTF8(name, length); + info.type = type; + info.size = size; + fastFree(name); + return true; +} + +bool WebGraphicsContext3DDefaultImpl::getActiveUniform(WebGLId program, unsigned long index, ActiveInfo& info) +{ + makeContextCurrent(); + GLint maxNameLength = -1; + glGetProgramiv(program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxNameLength); + if (maxNameLength < 0) + return false; + GLchar* name = 0; + if (!tryFastMalloc(maxNameLength * sizeof(GLchar)).getValue(name)) { + synthesizeGLError(GL_OUT_OF_MEMORY); + return false; + } + GLsizei length = 0; + GLint size = -1; + GLenum type = 0; + glGetActiveUniform(program, index, maxNameLength, + &length, &size, &type, name); + if (size < 0) { + fastFree(name); + return false; + } + info.name = WebString::fromUTF8(name, length); + info.type = type; + info.size = size; + fastFree(name); + return true; +} + +DELEGATE_TO_GL_4(getAttachedShaders, GetAttachedShaders, WebGLId, int, int*, unsigned int*) + +DELEGATE_TO_GL_2R(getAttribLocation, GetAttribLocation, WebGLId, const char*, int) + +DELEGATE_TO_GL_2(getBooleanv, GetBooleanv, unsigned long, unsigned char*) + +DELEGATE_TO_GL_3(getBufferParameteriv, GetBufferParameteriv, unsigned long, unsigned long, int*) + +WebGraphicsContext3D::Attributes WebGraphicsContext3DDefaultImpl::getContextAttributes() +{ + return m_attributes; +} + +unsigned long WebGraphicsContext3DDefaultImpl::getError() +{ + if (m_syntheticErrors.size() > 0) { + ListHashSet<unsigned long>::iterator iter = m_syntheticErrors.begin(); + unsigned long err = *iter; + m_syntheticErrors.remove(iter); + return err; + } + + makeContextCurrent(); + return glGetError(); +} + +bool WebGraphicsContext3DDefaultImpl::isContextLost() +{ + return false; +} + +DELEGATE_TO_GL_2(getFloatv, GetFloatv, unsigned long, float*) + +void WebGraphicsContext3DDefaultImpl::getFramebufferAttachmentParameteriv(unsigned long target, unsigned long attachment, + unsigned long pname, int* value) +{ + makeContextCurrent(); + if (attachment == GL_DEPTH_STENCIL_ATTACHMENT) + attachment = GL_DEPTH_ATTACHMENT; // Or GL_STENCIL_ATTACHMENT, either works. + glGetFramebufferAttachmentParameterivEXT(target, attachment, pname, value); +} + +void WebGraphicsContext3DDefaultImpl::getIntegerv(unsigned long pname, int* value) +{ + makeContextCurrent(); + if (m_isGLES2) { + glGetIntegerv(pname, value); + return; + } + // Need to emulate MAX_FRAGMENT/VERTEX_UNIFORM_VECTORS and MAX_VARYING_VECTORS + // because desktop GL's corresponding queries return the number of components + // whereas GLES2 return the number of vectors (each vector has 4 components). + // Therefore, the value returned by desktop GL needs to be divided by 4. + switch (pname) { + case MAX_FRAGMENT_UNIFORM_VECTORS: + glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, value); + *value /= 4; + break; + case MAX_VERTEX_UNIFORM_VECTORS: + glGetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS, value); + *value /= 4; + break; + case MAX_VARYING_VECTORS: + glGetIntegerv(GL_MAX_VARYING_FLOATS, value); + *value /= 4; + break; + default: + glGetIntegerv(pname, value); + } +} + +DELEGATE_TO_GL_3(getProgramiv, GetProgramiv, WebGLId, unsigned long, int*) + +WebString WebGraphicsContext3DDefaultImpl::getProgramInfoLog(WebGLId program) +{ + makeContextCurrent(); + GLint logLength; + glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logLength); + if (!logLength) + return WebString(); + GLchar* log = 0; + if (!tryFastMalloc(logLength * sizeof(GLchar)).getValue(log)) + return WebString(); + GLsizei returnedLogLength; + glGetProgramInfoLog(program, logLength, &returnedLogLength, log); + ASSERT(logLength == returnedLogLength + 1); + WebString res = WebString::fromUTF8(log, returnedLogLength); + fastFree(log); + return res; +} + +DELEGATE_TO_GL_3(getRenderbufferParameteriv, GetRenderbufferParameterivEXT, unsigned long, unsigned long, int*) + +void WebGraphicsContext3DDefaultImpl::getShaderiv(WebGLId shader, unsigned long pname, int* value) +{ + makeContextCurrent(); + + ShaderSourceMap::iterator result = m_shaderSourceMap.find(shader); + if (result != m_shaderSourceMap.end()) { + ShaderSourceEntry* entry = result->second; + ASSERT(entry); + switch (pname) { + case GL_COMPILE_STATUS: + if (!entry->isValid) { + *value = 0; + return; + } + break; + case GL_INFO_LOG_LENGTH: + if (!entry->isValid) { + *value = entry->log ? strlen(entry->log) : 0; + if (*value) + (*value)++; + return; + } + break; + case GL_SHADER_SOURCE_LENGTH: + *value = entry->source ? strlen(entry->source) : 0; + if (*value) + (*value)++; + return; + } + } + + glGetShaderiv(shader, pname, value); +} + +WebString WebGraphicsContext3DDefaultImpl::getShaderInfoLog(WebGLId shader) +{ + makeContextCurrent(); + + ShaderSourceMap::iterator result = m_shaderSourceMap.find(shader); + if (result != m_shaderSourceMap.end()) { + ShaderSourceEntry* entry = result->second; + ASSERT(entry); + if (!entry->isValid) { + if (!entry->log) + return WebString(); + WebString res = WebString::fromUTF8(entry->log, strlen(entry->log)); + return res; + } + } + + GLint logLength = 0; + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logLength); + if (logLength <= 1) + return WebString(); + GLchar* log = 0; + if (!tryFastMalloc(logLength * sizeof(GLchar)).getValue(log)) + return WebString(); + GLsizei returnedLogLength; + glGetShaderInfoLog(shader, logLength, &returnedLogLength, log); + ASSERT(logLength == returnedLogLength + 1); + WebString res = WebString::fromUTF8(log, returnedLogLength); + fastFree(log); + return res; +} + +WebString WebGraphicsContext3DDefaultImpl::getShaderSource(WebGLId shader) +{ + makeContextCurrent(); + + ShaderSourceMap::iterator result = m_shaderSourceMap.find(shader); + if (result != m_shaderSourceMap.end()) { + ShaderSourceEntry* entry = result->second; + ASSERT(entry); + if (!entry->source) + return WebString(); + WebString res = WebString::fromUTF8(entry->source, strlen(entry->source)); + return res; + } + + GLint logLength = 0; + glGetShaderiv(shader, GL_SHADER_SOURCE_LENGTH, &logLength); + if (logLength <= 1) + return WebString(); + GLchar* log = 0; + if (!tryFastMalloc(logLength * sizeof(GLchar)).getValue(log)) + return WebString(); + GLsizei returnedLogLength; + glGetShaderSource(shader, logLength, &returnedLogLength, log); + ASSERT(logLength == returnedLogLength + 1); + WebString res = WebString::fromUTF8(log, returnedLogLength); + fastFree(log); + return res; +} + +WebString WebGraphicsContext3DDefaultImpl::getString(unsigned long name) +{ + makeContextCurrent(); + StringBuilder result; + result.append(reinterpret_cast<const char*>(glGetString(name))); + if (name == GL_EXTENSIONS) { + // GL_CHROMIUM_copy_texture_to_parent_texture requires the + // desktopGL-only function glGetTexLevelParameteriv (GLES2 + // doesn't support it). + if (!m_isGLES2) + result.append(" GL_CHROMIUM_copy_texture_to_parent_texture"); + } + return WebString(result.toString()); +} + +DELEGATE_TO_GL_3(getTexParameterfv, GetTexParameterfv, unsigned long, unsigned long, float*) + +DELEGATE_TO_GL_3(getTexParameteriv, GetTexParameteriv, unsigned long, unsigned long, int*) + +DELEGATE_TO_GL_3(getUniformfv, GetUniformfv, WebGLId, long, float*) + +DELEGATE_TO_GL_3(getUniformiv, GetUniformiv, WebGLId, long, int*) + +DELEGATE_TO_GL_2R(getUniformLocation, GetUniformLocation, WebGLId, const char*, long) + +DELEGATE_TO_GL_3(getVertexAttribfv, GetVertexAttribfv, unsigned long, unsigned long, float*) + +DELEGATE_TO_GL_3(getVertexAttribiv, GetVertexAttribiv, unsigned long, unsigned long, int*) + +long WebGraphicsContext3DDefaultImpl::getVertexAttribOffset(unsigned long index, unsigned long pname) +{ + makeContextCurrent(); + void* pointer; + glGetVertexAttribPointerv(index, pname, &pointer); + return reinterpret_cast<long>(pointer); +} + +DELEGATE_TO_GL_2(hint, Hint, unsigned long, unsigned long) + +DELEGATE_TO_GL_1R(isBuffer, IsBuffer, WebGLId, bool) + +DELEGATE_TO_GL_1R(isEnabled, IsEnabled, unsigned long, bool) + +DELEGATE_TO_GL_1R(isFramebuffer, IsFramebufferEXT, WebGLId, bool) + +DELEGATE_TO_GL_1R(isProgram, IsProgram, WebGLId, bool) + +DELEGATE_TO_GL_1R(isRenderbuffer, IsRenderbufferEXT, WebGLId, bool) + +DELEGATE_TO_GL_1R(isShader, IsShader, WebGLId, bool) + +DELEGATE_TO_GL_1R(isTexture, IsTexture, WebGLId, bool) + +DELEGATE_TO_GL_1(lineWidth, LineWidth, double) + +DELEGATE_TO_GL_1(linkProgram, LinkProgram, WebGLId) + +DELEGATE_TO_GL_2(pixelStorei, PixelStorei, unsigned long, long) + +DELEGATE_TO_GL_2(polygonOffset, PolygonOffset, double, double) + +void WebGraphicsContext3DDefaultImpl::readPixels(long x, long y, unsigned long width, unsigned long height, unsigned long format, unsigned long type, void* pixels) +{ + makeContextCurrent(); + // FIXME: remove the two glFlush calls when the driver bug is fixed, i.e., + // all previous rendering calls should be done before reading pixels. + glFlush(); + bool needsResolve = (m_attributes.antialias && m_boundFBO == m_multisampleFBO); + if (needsResolve) { + resolveMultisampledFramebuffer(x, y, width, height); + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_fbo); + glFlush(); + } + + glReadPixels(x, y, width, height, format, type, pixels); + + if (needsResolve) + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_boundFBO); +} + +void WebGraphicsContext3DDefaultImpl::releaseShaderCompiler() +{ +} + +void WebGraphicsContext3DDefaultImpl::renderbufferStorage(unsigned long target, + unsigned long internalformat, + unsigned long width, + unsigned long height) +{ + makeContextCurrent(); + switch (internalformat) { + case GL_DEPTH_STENCIL: + internalformat = GL_DEPTH24_STENCIL8_EXT; + break; + case GL_DEPTH_COMPONENT16: + internalformat = GL_DEPTH_COMPONENT; + break; + case GL_RGBA4: + case GL_RGB5_A1: + internalformat = GL_RGBA; + break; + case 0x8D62: // GL_RGB565 + internalformat = GL_RGB; + break; + } + glRenderbufferStorageEXT(target, internalformat, width, height); +} + +DELEGATE_TO_GL_2(sampleCoverage, SampleCoverage, double, bool) + +DELEGATE_TO_GL_4(scissor, Scissor, long, long, unsigned long, unsigned long) + +unsigned bytesPerComponent(unsigned type) +{ + switch (type) { + case GL_BYTE: + case GL_UNSIGNED_BYTE: + return 1; + case GL_SHORT: + case GL_UNSIGNED_SHORT: + case GL_UNSIGNED_SHORT_5_6_5: + case GL_UNSIGNED_SHORT_4_4_4_4: + case GL_UNSIGNED_SHORT_5_5_5_1: + return 2; + case GL_FLOAT: + return 4; + default: + return 4; + } +} + +unsigned componentsPerPixel(unsigned format, unsigned type) +{ + switch (type) { + case GL_UNSIGNED_SHORT_5_6_5: + case GL_UNSIGNED_SHORT_4_4_4_4: + case GL_UNSIGNED_SHORT_5_5_5_1: + return 1; + default: + break; + } + switch (format) { + case GL_LUMINANCE: + return 1; + case GL_LUMINANCE_ALPHA: + return 2; + case GL_RGB: + return 3; + case GL_RGBA: + case GL_BGRA_EXT: + return 4; + default: + return 4; + } +} + +// N.B.: This code does not protect against integer overflow (as the command +// buffer implementation does), so it should not be considered robust enough +// for use in the browser. Since this implementation is only used for layout +// tests, this should be ok for now. +size_t imageSizeInBytes(unsigned width, unsigned height, unsigned format, unsigned type) +{ + return width * height * bytesPerComponent(type) * componentsPerPixel(format, type); +} + +void WebGraphicsContext3DDefaultImpl::texImage2D(unsigned target, unsigned level, unsigned internalFormat, unsigned width, unsigned height, unsigned border, unsigned format, unsigned type, const void* pixels) +{ + OwnArrayPtr<uint8> zero; + if (!pixels) { + size_t size = imageSizeInBytes(width, height, format, type); + zero.set(new uint8[size]); + memset(zero.get(), 0, size); + pixels = zero.get(); + } + glTexImage2D(target, level, internalFormat, width, height, border, format, type, pixels); +} + +void WebGraphicsContext3DDefaultImpl::shaderSource(WebGLId shader, const char* string) +{ + makeContextCurrent(); + GLint length = string ? strlen(string) : 0; + ShaderSourceMap::iterator result = m_shaderSourceMap.find(shader); + if (result != m_shaderSourceMap.end()) { + ShaderSourceEntry* entry = result->second; + ASSERT(entry); + if (entry->source) { + fastFree(entry->source); + entry->source = 0; + } + if (!tryFastMalloc((length + 1) * sizeof(char)).getValue(entry->source)) + return; // FIXME: generate an error? + memcpy(entry->source, string, (length + 1) * sizeof(char)); + } else + glShaderSource(shader, 1, &string, &length); +} + +DELEGATE_TO_GL_3(stencilFunc, StencilFunc, unsigned long, long, unsigned long) + +DELEGATE_TO_GL_4(stencilFuncSeparate, StencilFuncSeparate, unsigned long, unsigned long, long, unsigned long) + +DELEGATE_TO_GL_1(stencilMask, StencilMask, unsigned long) + +DELEGATE_TO_GL_2(stencilMaskSeparate, StencilMaskSeparate, unsigned long, unsigned long) + +DELEGATE_TO_GL_3(stencilOp, StencilOp, unsigned long, unsigned long, unsigned long) + +DELEGATE_TO_GL_4(stencilOpSeparate, StencilOpSeparate, unsigned long, unsigned long, unsigned long, unsigned long) + +DELEGATE_TO_GL_3(texParameterf, TexParameterf, unsigned, unsigned, float); + +DELEGATE_TO_GL_3(texParameteri, TexParameteri, unsigned, unsigned, int); + +DELEGATE_TO_GL_9(texSubImage2D, TexSubImage2D, unsigned, unsigned, unsigned, unsigned, unsigned, unsigned, unsigned, unsigned, const void*) + +DELEGATE_TO_GL_2(uniform1f, Uniform1f, long, float) + +DELEGATE_TO_GL_3(uniform1fv, Uniform1fv, long, int, float*) + +DELEGATE_TO_GL_2(uniform1i, Uniform1i, long, int) + +DELEGATE_TO_GL_3(uniform1iv, Uniform1iv, long, int, int*) + +DELEGATE_TO_GL_3(uniform2f, Uniform2f, long, float, float) + +DELEGATE_TO_GL_3(uniform2fv, Uniform2fv, long, int, float*) + +DELEGATE_TO_GL_3(uniform2i, Uniform2i, long, int, int) + +DELEGATE_TO_GL_3(uniform2iv, Uniform2iv, long, int, int*) + +DELEGATE_TO_GL_4(uniform3f, Uniform3f, long, float, float, float) + +DELEGATE_TO_GL_3(uniform3fv, Uniform3fv, long, int, float*) + +DELEGATE_TO_GL_4(uniform3i, Uniform3i, long, int, int, int) + +DELEGATE_TO_GL_3(uniform3iv, Uniform3iv, long, int, int*) + +DELEGATE_TO_GL_5(uniform4f, Uniform4f, long, float, float, float, float) + +DELEGATE_TO_GL_3(uniform4fv, Uniform4fv, long, int, float*) + +DELEGATE_TO_GL_5(uniform4i, Uniform4i, long, int, int, int, int) + +DELEGATE_TO_GL_3(uniform4iv, Uniform4iv, long, int, int*) + +DELEGATE_TO_GL_4(uniformMatrix2fv, UniformMatrix2fv, long, int, bool, const float*) + +DELEGATE_TO_GL_4(uniformMatrix3fv, UniformMatrix3fv, long, int, bool, const float*) + +DELEGATE_TO_GL_4(uniformMatrix4fv, UniformMatrix4fv, long, int, bool, const float*) + +DELEGATE_TO_GL_1(useProgram, UseProgram, WebGLId) + +DELEGATE_TO_GL_1(validateProgram, ValidateProgram, WebGLId) + +DELEGATE_TO_GL_2(vertexAttrib1f, VertexAttrib1f, unsigned long, float) + +DELEGATE_TO_GL_2(vertexAttrib1fv, VertexAttrib1fv, unsigned long, const float*) + +DELEGATE_TO_GL_3(vertexAttrib2f, VertexAttrib2f, unsigned long, float, float) + +DELEGATE_TO_GL_2(vertexAttrib2fv, VertexAttrib2fv, unsigned long, const float*) + +DELEGATE_TO_GL_4(vertexAttrib3f, VertexAttrib3f, unsigned long, float, float, float) + +DELEGATE_TO_GL_2(vertexAttrib3fv, VertexAttrib3fv, unsigned long, const float*) + +DELEGATE_TO_GL_5(vertexAttrib4f, VertexAttrib4f, unsigned long, float, float, float, float) + +DELEGATE_TO_GL_2(vertexAttrib4fv, VertexAttrib4fv, unsigned long, const float*) + +void WebGraphicsContext3DDefaultImpl::vertexAttribPointer(unsigned long indx, int size, int type, bool normalized, + unsigned long stride, unsigned long offset) +{ + makeContextCurrent(); + + if (m_boundArrayBuffer <= 0) { + // FIXME: raise exception. + // LogMessagef(("bufferData: no buffer bound")); + return; + } + + if (indx < NumTrackedPointerStates) { + VertexAttribPointerState& state = m_vertexAttribPointerState[indx]; + state.buffer = m_boundArrayBuffer; + state.indx = indx; + state.size = size; + state.type = type; + state.normalized = normalized; + state.stride = stride; + state.offset = offset; + } + + glVertexAttribPointer(indx, size, type, normalized, stride, + reinterpret_cast<void*>(static_cast<intptr_t>(offset))); +} + +DELEGATE_TO_GL_4(viewport, Viewport, long, long, unsigned long, unsigned long) + +unsigned WebGraphicsContext3DDefaultImpl::createBuffer() +{ + makeContextCurrent(); + GLuint o; + glGenBuffersARB(1, &o); + return o; +} + +unsigned WebGraphicsContext3DDefaultImpl::createFramebuffer() +{ + makeContextCurrent(); + GLuint o = 0; + glGenFramebuffersEXT(1, &o); + return o; +} + +unsigned WebGraphicsContext3DDefaultImpl::createProgram() +{ + makeContextCurrent(); + return glCreateProgram(); +} + +unsigned WebGraphicsContext3DDefaultImpl::createRenderbuffer() +{ + makeContextCurrent(); + GLuint o; + glGenRenderbuffersEXT(1, &o); + return o; +} + +unsigned WebGraphicsContext3DDefaultImpl::createShader(unsigned long shaderType) +{ + makeContextCurrent(); + ASSERT(shaderType == GL_VERTEX_SHADER || shaderType == GL_FRAGMENT_SHADER); + unsigned shader = glCreateShader(shaderType); + if (shader) { + ShaderSourceMap::iterator result = m_shaderSourceMap.find(shader); + if (result != m_shaderSourceMap.end()) + delete result->second; + m_shaderSourceMap.set(shader, new ShaderSourceEntry(shaderType)); + } + + return shader; +} + +unsigned WebGraphicsContext3DDefaultImpl::createTexture() +{ + makeContextCurrent(); + GLuint o; + glGenTextures(1, &o); + return o; +} + +void WebGraphicsContext3DDefaultImpl::deleteBuffer(unsigned buffer) +{ + makeContextCurrent(); + glDeleteBuffersARB(1, &buffer); +} + +void WebGraphicsContext3DDefaultImpl::deleteFramebuffer(unsigned framebuffer) +{ + makeContextCurrent(); + glDeleteFramebuffersEXT(1, &framebuffer); +} + +void WebGraphicsContext3DDefaultImpl::deleteProgram(unsigned program) +{ + makeContextCurrent(); + glDeleteProgram(program); +} + +void WebGraphicsContext3DDefaultImpl::deleteRenderbuffer(unsigned renderbuffer) +{ + makeContextCurrent(); + glDeleteRenderbuffersEXT(1, &renderbuffer); +} + +void WebGraphicsContext3DDefaultImpl::deleteShader(unsigned shader) +{ + makeContextCurrent(); + + ShaderSourceMap::iterator result = m_shaderSourceMap.find(shader); + if (result != m_shaderSourceMap.end()) + delete result->second; + m_shaderSourceMap.remove(result); + glDeleteShader(shader); +} + +void WebGraphicsContext3DDefaultImpl::deleteTexture(unsigned texture) +{ + makeContextCurrent(); + glDeleteTextures(1, &texture); +} + +bool WebGraphicsContext3DDefaultImpl::angleCreateCompilers() +{ + if (!ShInitialize()) + return false; + + ShBuiltInResources resources; + ShInitBuiltInResources(&resources); + getIntegerv(GL_MAX_VERTEX_ATTRIBS, &resources.MaxVertexAttribs); + getIntegerv(MAX_VERTEX_UNIFORM_VECTORS, &resources.MaxVertexUniformVectors); + getIntegerv(MAX_VARYING_VECTORS, &resources.MaxVaryingVectors); + getIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &resources.MaxVertexTextureImageUnits); + getIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &resources.MaxCombinedTextureImageUnits); + getIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &resources.MaxTextureImageUnits); + getIntegerv(MAX_FRAGMENT_UNIFORM_VECTORS, &resources.MaxFragmentUniformVectors); + // Always set to 1 for OpenGL ES. + resources.MaxDrawBuffers = 1; + + m_fragmentCompiler = ShConstructCompiler(SH_FRAGMENT_SHADER, SH_WEBGL_SPEC, &resources); + m_vertexCompiler = ShConstructCompiler(SH_VERTEX_SHADER, SH_WEBGL_SPEC, &resources); + return (m_fragmentCompiler && m_vertexCompiler); +} + +void WebGraphicsContext3DDefaultImpl::angleDestroyCompilers() +{ + if (m_fragmentCompiler) { + ShDestruct(m_fragmentCompiler); + m_fragmentCompiler = 0; + } + if (m_vertexCompiler) { + ShDestruct(m_vertexCompiler); + m_vertexCompiler = 0; + } +} + +bool WebGraphicsContext3DDefaultImpl::angleValidateShaderSource(ShaderSourceEntry& entry) +{ + entry.isValid = false; + if (entry.translatedSource) { + fastFree(entry.translatedSource); + entry.translatedSource = 0; + } + if (entry.log) { + fastFree(entry.log); + entry.log = 0; + } + + ShHandle compiler = 0; + switch (entry.type) { + case GL_FRAGMENT_SHADER: + compiler = m_fragmentCompiler; + break; + case GL_VERTEX_SHADER: + compiler = m_vertexCompiler; + break; + } + if (!compiler) + return false; + + if (!ShCompile(compiler, &entry.source, 1, SH_OBJECT_CODE)) { + int logSize = 0; + ShGetInfo(compiler, SH_INFO_LOG_LENGTH, &logSize); + if (logSize > 1 && tryFastMalloc(logSize * sizeof(char)).getValue(entry.log)) + ShGetInfoLog(compiler, entry.log); + return false; + } + + int length = 0; + if (m_isGLES2) { + // ANGLE does not yet have a GLSL ES backend. Therefore if the + // compile succeeds we send the original source down. + length = strlen(entry.source); + if (length > 0) + ++length; // Add null terminator + } else + ShGetInfo(compiler, SH_OBJECT_CODE_LENGTH, &length); + if (length > 1) { + if (!tryFastMalloc(length * sizeof(char)).getValue(entry.translatedSource)) + return false; + if (m_isGLES2) + strncpy(entry.translatedSource, entry.source, length); + else + ShGetObjectCode(compiler, entry.translatedSource); + } + entry.isValid = true; + return true; +} + +} // namespace WebKit + +#endif // ENABLE(3D_CANVAS) |