/* * Copyright (C) 2010 Apple 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: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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(WEBGL) #include "GraphicsContext3D.h" #include "ArrayBuffer.h" #include "ArrayBufferView.h" #include "WebGLObject.h" #include "CanvasRenderingContext.h" #include "Extensions3DOpenGL.h" #include "Float32Array.h" #include "GraphicsContext.h" #include "HTMLCanvasElement.h" #include "ImageBuffer.h" #include "ImageData.h" #include "Int32Array.h" #include "NotImplemented.h" #include "Uint8Array.h" #include #include #include #if PLATFORM(MAC) #include #elif PLATFORM(GTK) #include "OpenGLShims.h" #endif namespace WebCore { void GraphicsContext3D::validateAttributes() { Extensions3D* extensions = getExtensions(); if (m_attrs.stencil) { if (extensions->supports("GL_EXT_packed_depth_stencil")) { extensions->ensureEnabled("GL_EXT_packed_depth_stencil"); // Force depth if stencil is true. m_attrs.depth = true; } else m_attrs.stencil = false; } if (m_attrs.antialias) { bool isValidVendor = true; // Currently in Mac we only turn on antialias if vendor is NVIDIA. const char* vendor = reinterpret_cast(::glGetString(GL_VENDOR)); if (!std::strstr(vendor, "NVIDIA")) isValidVendor = false; if (!isValidVendor || !extensions->supports("GL_ANGLE_framebuffer_multisample")) m_attrs.antialias = false; else extensions->ensureEnabled("GL_ANGLE_framebuffer_multisample"); } } void GraphicsContext3D::readRenderingResults(unsigned char *pixels, int pixelsSize) { if (pixelsSize < m_currentWidth * m_currentHeight * 4) return; makeContextCurrent(); bool mustRestoreFBO = false; if (m_attrs.antialias) { ::glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, m_multisampleFBO); ::glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, m_fbo); ::glBlitFramebufferEXT(0, 0, m_currentWidth, m_currentHeight, 0, 0, m_currentWidth, m_currentHeight, GL_COLOR_BUFFER_BIT, GL_LINEAR); ::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_fbo); mustRestoreFBO = true; } else { if (m_boundFBO != m_fbo) { mustRestoreFBO = true; ::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; } ::glReadPixels(0, 0, m_currentWidth, m_currentHeight, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, pixels); if (mustRestorePackAlignment) ::glPixelStorei(GL_PACK_ALIGNMENT, packAlignment); if (mustRestoreFBO) ::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_boundFBO); } void GraphicsContext3D::paintRenderingResultsToCanvas(CanvasRenderingContext* context) { HTMLCanvasElement* canvas = context->canvas(); ImageBuffer* imageBuffer = canvas->buffer(); int rowBytes = m_currentWidth * 4; int totalBytes = rowBytes * m_currentHeight; OwnArrayPtr pixels = adoptArrayPtr(new unsigned char[totalBytes]); if (!pixels) return; readRenderingResults(pixels.get(), totalBytes); if (!m_attrs.premultipliedAlpha) { for (int i = 0; i < totalBytes; i += 4) { // Premultiply alpha pixels[i + 0] = std::min(255, pixels[i + 0] * pixels[i + 3] / 255); pixels[i + 1] = std::min(255, pixels[i + 1] * pixels[i + 3] / 255); pixels[i + 2] = std::min(255, pixels[i + 2] * pixels[i + 3] / 255); } } paintToCanvas(pixels.get(), m_currentWidth, m_currentHeight, canvas->width(), canvas->height(), imageBuffer->context()->platformContext()); } PassRefPtr GraphicsContext3D::paintRenderingResultsToImageData() { // Reading premultiplied alpha would involve unpremultiplying, which is // lossy if (m_attrs.premultipliedAlpha) return 0; RefPtr imageData = ImageData::create(IntSize(m_currentWidth, m_currentHeight)); unsigned char* pixels = imageData->data()->data()->data(); int totalBytes = 4 * m_currentWidth * m_currentHeight; readRenderingResults(pixels, totalBytes); // Convert to RGBA for (int i = 0; i < totalBytes; i += 4) std::swap(pixels[i], pixels[i + 2]); return imageData.release(); } void GraphicsContext3D::reshape(int width, int height) { if (!platformGraphicsContext3D()) return; if (width == m_currentWidth && height == m_currentHeight) return; m_currentWidth = width; m_currentHeight = height; makeContextCurrent(); validateAttributes(); GLuint colorFormat, internalDepthStencilFormat = 0; if (m_attrs.alpha) { m_internalColorFormat = GL_RGBA8; colorFormat = GL_RGBA; } else { m_internalColorFormat = GL_RGB8; colorFormat = GL_RGB; } if (m_attrs.stencil || m_attrs.depth) { // We don't allow the logic where stencil is required and depth is not. // See GraphicsContext3D::validateAttributes. Extensions3D* extensions = getExtensions(); // Use a 24 bit depth buffer where we know we have it if (extensions->supports("GL_EXT_packed_depth_stencil")) internalDepthStencilFormat = GL_DEPTH24_STENCIL8_EXT; else internalDepthStencilFormat = GL_DEPTH_COMPONENT; } bool mustRestoreFBO = false; // resize multisample FBO if (m_attrs.antialias) { GLint maxSampleCount; ::glGetIntegerv(GL_MAX_SAMPLES_EXT, &maxSampleCount); GLint sampleCount = std::min(8, maxSampleCount); if (sampleCount > maxSampleCount) sampleCount = maxSampleCount; if (m_boundFBO != m_multisampleFBO) { ::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_multisampleFBO); mustRestoreFBO = true; } ::glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, m_multisampleColorBuffer); ::glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, sampleCount, m_internalColorFormat, width, height); ::glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, m_multisampleColorBuffer); if (m_attrs.stencil || m_attrs.depth) { ::glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, m_multisampleDepthStencilBuffer); ::glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, sampleCount, internalDepthStencilFormat, width, height); if (m_attrs.stencil) ::glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, m_multisampleDepthStencilBuffer); if (m_attrs.depth) ::glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, m_multisampleDepthStencilBuffer); } ::glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0); if (glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) != GL_FRAMEBUFFER_COMPLETE_EXT) { // FIXME: cleanup. notImplemented(); } } // resize regular FBO if (m_boundFBO != m_fbo) { mustRestoreFBO = true; ::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_fbo); } ::glBindTexture(GL_TEXTURE_2D, m_texture); ::glTexImage2D(GL_TEXTURE_2D, 0, m_internalColorFormat, width, height, 0, colorFormat, GL_UNSIGNED_BYTE, 0); ::glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, m_texture, 0); ::glBindTexture(GL_TEXTURE_2D, m_compositorTexture); ::glTexImage2D(GL_TEXTURE_2D, 0, m_internalColorFormat, width, height, 0, colorFormat, GL_UNSIGNED_BYTE, 0); ::glBindTexture(GL_TEXTURE_2D, 0); if (!m_attrs.antialias && (m_attrs.stencil || m_attrs.depth)) { ::glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, m_depthStencilBuffer); ::glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, internalDepthStencilFormat, width, height); if (m_attrs.stencil) ::glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, m_depthStencilBuffer); if (m_attrs.depth) ::glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, m_depthStencilBuffer); ::glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0); } if (glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) != GL_FRAMEBUFFER_COMPLETE_EXT) { // FIXME: cleanup notImplemented(); } if (m_attrs.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_attrs.depth) { ::glGetFloatv(GL_DEPTH_CLEAR_VALUE, &clearDepth); ::glClearDepth(1); ::glGetBooleanv(GL_DEPTH_WRITEMASK, &depthMask); ::glDepthMask(GL_TRUE); clearMask |= GL_DEPTH_BUFFER_BIT; } if (m_attrs.stencil) { ::glGetIntegerv(GL_STENCIL_CLEAR_VALUE, &clearStencil); ::glClearStencil(0); ::glGetIntegerv(GL_STENCIL_WRITEMASK, reinterpret_cast(&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_attrs.depth) { ::glClearDepth(clearDepth); ::glDepthMask(depthMask); } if (m_attrs.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); ::glFlush(); } IntSize GraphicsContext3D::getInternalFramebufferSize() { return IntSize(m_currentWidth, m_currentHeight); } void GraphicsContext3D::prepareTexture() { if (m_layerComposited) return; makeContextCurrent(); if (m_attrs.antialias) { ::glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, m_multisampleFBO); ::glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, m_fbo); ::glBlitFramebufferEXT(0, 0, m_currentWidth, m_currentHeight, 0, 0, m_currentWidth, m_currentHeight, GL_COLOR_BUFFER_BIT, GL_LINEAR); } ::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_fbo); ::glActiveTexture(0); ::glBindTexture(GL_TEXTURE_2D, m_compositorTexture); ::glCopyTexImage2D(GL_TEXTURE_2D, 0, m_internalColorFormat, 0, 0, m_currentWidth, m_currentHeight, 0); ::glBindTexture(GL_TEXTURE_2D, m_boundTexture0); ::glActiveTexture(m_activeTexture); ::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_boundFBO); ::glFinish(); m_layerComposited = true; } void GraphicsContext3D::activeTexture(GC3Denum texture) { makeContextCurrent(); m_activeTexture = texture; ::glActiveTexture(texture); } void GraphicsContext3D::attachShader(Platform3DObject program, Platform3DObject shader) { ASSERT(program); ASSERT(shader); makeContextCurrent(); ::glAttachShader(program, shader); } void GraphicsContext3D::bindAttribLocation(Platform3DObject program, GC3Duint index, const String& name) { ASSERT(program); makeContextCurrent(); ::glBindAttribLocation(program, index, name.utf8().data()); } void GraphicsContext3D::bindBuffer(GC3Denum target, Platform3DObject buffer) { makeContextCurrent(); ::glBindBuffer(target, buffer); } void GraphicsContext3D::bindFramebuffer(GC3Denum target, Platform3DObject buffer) { makeContextCurrent(); GLuint fbo; if (buffer) fbo = buffer; else fbo = (m_attrs.antialias ? m_multisampleFBO : m_fbo); if (fbo != m_boundFBO) { ::glBindFramebufferEXT(target, fbo); m_boundFBO = fbo; } } void GraphicsContext3D::bindRenderbuffer(GC3Denum target, Platform3DObject renderbuffer) { makeContextCurrent(); ::glBindRenderbufferEXT(target, renderbuffer); } void GraphicsContext3D::bindTexture(GC3Denum target, Platform3DObject texture) { makeContextCurrent(); if (m_activeTexture && target == GL_TEXTURE_2D) m_boundTexture0 = texture; ::glBindTexture(target, texture); } void GraphicsContext3D::blendColor(GC3Dclampf red, GC3Dclampf green, GC3Dclampf blue, GC3Dclampf alpha) { makeContextCurrent(); ::glBlendColor(red, green, blue, alpha); } void GraphicsContext3D::blendEquation(GC3Denum mode) { makeContextCurrent(); ::glBlendEquation(mode); } void GraphicsContext3D::blendEquationSeparate(GC3Denum modeRGB, GC3Denum modeAlpha) { makeContextCurrent(); ::glBlendEquationSeparate(modeRGB, modeAlpha); } void GraphicsContext3D::blendFunc(GC3Denum sfactor, GC3Denum dfactor) { makeContextCurrent(); ::glBlendFunc(sfactor, dfactor); } void GraphicsContext3D::blendFuncSeparate(GC3Denum srcRGB, GC3Denum dstRGB, GC3Denum srcAlpha, GC3Denum dstAlpha) { makeContextCurrent(); ::glBlendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha); } void GraphicsContext3D::bufferData(GC3Denum target, GC3Dsizeiptr size, GC3Denum usage) { makeContextCurrent(); ::glBufferData(target, size, 0, usage); } void GraphicsContext3D::bufferData(GC3Denum target, GC3Dsizeiptr size, const void* data, GC3Denum usage) { makeContextCurrent(); ::glBufferData(target, size, data, usage); } void GraphicsContext3D::bufferSubData(GC3Denum target, GC3Dintptr offset, GC3Dsizeiptr size, const void* data) { makeContextCurrent(); ::glBufferSubData(target, offset, size, data); } GC3Denum GraphicsContext3D::checkFramebufferStatus(GC3Denum target) { makeContextCurrent(); return ::glCheckFramebufferStatusEXT(target); } void GraphicsContext3D::clearColor(GC3Dclampf r, GC3Dclampf g, GC3Dclampf b, GC3Dclampf a) { makeContextCurrent(); ::glClearColor(r, g, b, a); } void GraphicsContext3D::clear(GC3Dbitfield mask) { makeContextCurrent(); ::glClear(mask); } void GraphicsContext3D::clearDepth(GC3Dclampf depth) { makeContextCurrent(); ::glClearDepth(depth); } void GraphicsContext3D::clearStencil(GC3Dint s) { makeContextCurrent(); ::glClearStencil(s); } void GraphicsContext3D::colorMask(GC3Dboolean red, GC3Dboolean green, GC3Dboolean blue, GC3Dboolean alpha) { makeContextCurrent(); ::glColorMask(red, green, blue, alpha); } void GraphicsContext3D::compileShader(Platform3DObject shader) { ASSERT(shader); makeContextCurrent(); int GLshaderType; ANGLEShaderType shaderType; glGetShaderiv(shader, SHADER_TYPE, &GLshaderType); if (GLshaderType == VERTEX_SHADER) shaderType = SHADER_TYPE_VERTEX; else if (GLshaderType == FRAGMENT_SHADER) shaderType = SHADER_TYPE_FRAGMENT; else return; // Invalid shader type. HashMap::iterator result = m_shaderSourceMap.find(shader); if (result == m_shaderSourceMap.end()) return; ShaderSourceEntry& entry = result->second; String translatedShaderSource; String shaderInfoLog; bool isValid = m_compiler.validateShaderSource(entry.source.utf8().data(), shaderType, translatedShaderSource, shaderInfoLog); entry.log = shaderInfoLog; entry.isValid = isValid; if (!isValid) return; // Shader didn't validate, don't move forward with compiling translated source int translatedShaderLength = translatedShaderSource.length(); const CString& translatedShaderCString = translatedShaderSource.utf8(); const char* translatedShaderPtr = translatedShaderCString.data(); ::glShaderSource(shader, 1, &translatedShaderPtr, &translatedShaderLength); ::glCompileShader(shader); int GLCompileSuccess; ::glGetShaderiv(shader, COMPILE_STATUS, &GLCompileSuccess); // ASSERT that ANGLE generated GLSL will be accepted by OpenGL ASSERT(GLCompileSuccess == GL_TRUE); } void GraphicsContext3D::copyTexImage2D(GC3Denum target, GC3Dint level, GC3Denum internalformat, GC3Dint x, GC3Dint y, GC3Dsizei width, GC3Dsizei height, GC3Dint border) { makeContextCurrent(); if (m_attrs.antialias && m_boundFBO == m_multisampleFBO) { ::glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, m_multisampleFBO); ::glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, m_fbo); ::glBlitFramebufferEXT(x, y, x + width, y + height, x, y, x + width, y + height, GL_COLOR_BUFFER_BIT, GL_LINEAR); ::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_fbo); } ::glCopyTexImage2D(target, level, internalformat, x, y, width, height, border); if (m_attrs.antialias && m_boundFBO == m_multisampleFBO) ::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_multisampleFBO); } void GraphicsContext3D::copyTexSubImage2D(GC3Denum target, GC3Dint level, GC3Dint xoffset, GC3Dint yoffset, GC3Dint x, GC3Dint y, GC3Dsizei width, GC3Dsizei height) { makeContextCurrent(); if (m_attrs.antialias && m_boundFBO == m_multisampleFBO) { ::glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, m_multisampleFBO); ::glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, m_fbo); ::glBlitFramebufferEXT(x, y, x + width, y + height, x, y, x + width, y + height, GL_COLOR_BUFFER_BIT, GL_LINEAR); ::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_fbo); } ::glCopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height); if (m_attrs.antialias && m_boundFBO == m_multisampleFBO) ::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_multisampleFBO); } void GraphicsContext3D::cullFace(GC3Denum mode) { makeContextCurrent(); ::glCullFace(mode); } void GraphicsContext3D::depthFunc(GC3Denum func) { makeContextCurrent(); ::glDepthFunc(func); } void GraphicsContext3D::depthMask(GC3Dboolean flag) { makeContextCurrent(); ::glDepthMask(flag); } void GraphicsContext3D::depthRange(GC3Dclampf zNear, GC3Dclampf zFar) { makeContextCurrent(); ::glDepthRange(zNear, zFar); } void GraphicsContext3D::detachShader(Platform3DObject program, Platform3DObject shader) { ASSERT(program); ASSERT(shader); makeContextCurrent(); ::glDetachShader(program, shader); } void GraphicsContext3D::disable(GC3Denum cap) { makeContextCurrent(); ::glDisable(cap); } void GraphicsContext3D::disableVertexAttribArray(GC3Duint index) { makeContextCurrent(); ::glDisableVertexAttribArray(index); } void GraphicsContext3D::drawArrays(GC3Denum mode, GC3Dint first, GC3Dsizei count) { makeContextCurrent(); ::glDrawArrays(mode, first, count); } void GraphicsContext3D::drawElements(GC3Denum mode, GC3Dsizei count, GC3Denum type, GC3Dintptr offset) { makeContextCurrent(); ::glDrawElements(mode, count, type, reinterpret_cast(static_cast(offset))); } void GraphicsContext3D::enable(GC3Denum cap) { makeContextCurrent(); ::glEnable(cap); } void GraphicsContext3D::enableVertexAttribArray(GC3Duint index) { makeContextCurrent(); ::glEnableVertexAttribArray(index); } void GraphicsContext3D::finish() { makeContextCurrent(); ::glFinish(); } void GraphicsContext3D::flush() { makeContextCurrent(); ::glFlush(); } void GraphicsContext3D::framebufferRenderbuffer(GC3Denum target, GC3Denum attachment, GC3Denum renderbuffertarget, Platform3DObject buffer) { makeContextCurrent(); ::glFramebufferRenderbufferEXT(target, attachment, renderbuffertarget, buffer); } void GraphicsContext3D::framebufferTexture2D(GC3Denum target, GC3Denum attachment, GC3Denum textarget, Platform3DObject texture, GC3Dint level) { makeContextCurrent(); ::glFramebufferTexture2DEXT(target, attachment, textarget, texture, level); } void GraphicsContext3D::frontFace(GC3Denum mode) { makeContextCurrent(); ::glFrontFace(mode); } void GraphicsContext3D::generateMipmap(GC3Denum target) { makeContextCurrent(); ::glGenerateMipmapEXT(target); } bool GraphicsContext3D::getActiveAttrib(Platform3DObject program, GC3Duint index, ActiveInfo& info) { if (!program) { synthesizeGLError(INVALID_VALUE); return false; } makeContextCurrent(); GLint maxAttributeSize = 0; ::glGetProgramiv(program, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &maxAttributeSize); GLchar name[maxAttributeSize]; // GL_ACTIVE_ATTRIBUTE_MAX_LENGTH includes null termination GLsizei nameLength = 0; GLint size = 0; GLenum type = 0; ::glGetActiveAttrib(program, index, maxAttributeSize, &nameLength, &size, &type, name); if (!nameLength) return false; info.name = String(name, nameLength); info.type = type; info.size = size; return true; } bool GraphicsContext3D::getActiveUniform(Platform3DObject program, GC3Duint index, ActiveInfo& info) { if (!program) { synthesizeGLError(INVALID_VALUE); return false; } makeContextCurrent(); GLint maxUniformSize = 0; ::glGetProgramiv(program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxUniformSize); GLchar name[maxUniformSize]; // GL_ACTIVE_UNIFORM_MAX_LENGTH includes null termination GLsizei nameLength = 0; GLint size = 0; GLenum type = 0; ::glGetActiveUniform(program, index, maxUniformSize, &nameLength, &size, &type, name); if (!nameLength) return false; info.name = String(name, nameLength); info.type = type; info.size = size; return true; } void GraphicsContext3D::getAttachedShaders(Platform3DObject program, GC3Dsizei maxCount, GC3Dsizei* count, Platform3DObject* shaders) { if (!program) { synthesizeGLError(INVALID_VALUE); return; } makeContextCurrent(); ::glGetAttachedShaders(program, maxCount, count, shaders); } int GraphicsContext3D::getAttribLocation(Platform3DObject program, const String& name) { if (!program) return -1; makeContextCurrent(); return ::glGetAttribLocation(program, name.utf8().data()); } GraphicsContext3D::Attributes GraphicsContext3D::getContextAttributes() { return m_attrs; } GC3Denum GraphicsContext3D::getError() { if (m_syntheticErrors.size() > 0) { ListHashSet::iterator iter = m_syntheticErrors.begin(); GC3Denum err = *iter; m_syntheticErrors.remove(iter); return err; } makeContextCurrent(); return ::glGetError(); } String GraphicsContext3D::getString(GC3Denum name) { makeContextCurrent(); return String((const char*) ::glGetString(name)); } void GraphicsContext3D::hint(GC3Denum target, GC3Denum mode) { makeContextCurrent(); ::glHint(target, mode); } GC3Dboolean GraphicsContext3D::isBuffer(Platform3DObject buffer) { if (!buffer) return GL_FALSE; makeContextCurrent(); return ::glIsBuffer(buffer); } GC3Dboolean GraphicsContext3D::isEnabled(GC3Denum cap) { makeContextCurrent(); return ::glIsEnabled(cap); } GC3Dboolean GraphicsContext3D::isFramebuffer(Platform3DObject framebuffer) { if (!framebuffer) return GL_FALSE; makeContextCurrent(); return ::glIsFramebufferEXT(framebuffer); } GC3Dboolean GraphicsContext3D::isProgram(Platform3DObject program) { if (!program) return GL_FALSE; makeContextCurrent(); return ::glIsProgram(program); } GC3Dboolean GraphicsContext3D::isRenderbuffer(Platform3DObject renderbuffer) { if (!renderbuffer) return GL_FALSE; makeContextCurrent(); return ::glIsRenderbufferEXT(renderbuffer); } GC3Dboolean GraphicsContext3D::isShader(Platform3DObject shader) { if (!shader) return GL_FALSE; makeContextCurrent(); return ::glIsShader(shader); } GC3Dboolean GraphicsContext3D::isTexture(Platform3DObject texture) { if (!texture) return GL_FALSE; makeContextCurrent(); return ::glIsTexture(texture); } void GraphicsContext3D::lineWidth(GC3Dfloat width) { makeContextCurrent(); ::glLineWidth(width); } void GraphicsContext3D::linkProgram(Platform3DObject program) { ASSERT(program); makeContextCurrent(); ::glLinkProgram(program); } void GraphicsContext3D::pixelStorei(GC3Denum pname, GC3Dint param) { makeContextCurrent(); ::glPixelStorei(pname, param); } void GraphicsContext3D::polygonOffset(GC3Dfloat factor, GC3Dfloat units) { makeContextCurrent(); ::glPolygonOffset(factor, units); } void GraphicsContext3D::readPixels(GC3Dint x, GC3Dint y, GC3Dsizei width, GC3Dsizei height, GC3Denum format, GC3Denum type, void* data) { // FIXME: remove the two glFlush calls when the driver bug is fixed, i.e., // all previous rendering calls should be done before reading pixels. makeContextCurrent(); ::glFlush(); if (m_attrs.antialias && m_boundFBO == m_multisampleFBO) { ::glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, m_multisampleFBO); ::glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, m_fbo); ::glBlitFramebufferEXT(x, y, x + width, y + height, x, y, x + width, y + height, GL_COLOR_BUFFER_BIT, GL_LINEAR); ::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_fbo); ::glFlush(); } ::glReadPixels(x, y, width, height, format, type, data); if (m_attrs.antialias && m_boundFBO == m_multisampleFBO) ::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_multisampleFBO); } void GraphicsContext3D::releaseShaderCompiler() { // FIXME: This is not implemented on desktop OpenGL. We need to have ifdefs for the different GL variants makeContextCurrent(); //::glReleaseShaderCompiler(); } void GraphicsContext3D::renderbufferStorage(GC3Denum target, GC3Denum internalformat, GC3Dsizei width, GC3Dsizei height) { makeContextCurrent(); switch (internalformat) { case DEPTH_STENCIL: internalformat = GL_DEPTH24_STENCIL8_EXT; break; case DEPTH_COMPONENT16: internalformat = GL_DEPTH_COMPONENT; break; case RGBA4: case RGB5_A1: internalformat = GL_RGBA; break; case RGB565: internalformat = GL_RGB; break; } ::glRenderbufferStorageEXT(target, internalformat, width, height); } void GraphicsContext3D::sampleCoverage(GC3Dclampf value, GC3Dboolean invert) { makeContextCurrent(); ::glSampleCoverage(value, invert); } void GraphicsContext3D::scissor(GC3Dint x, GC3Dint y, GC3Dsizei width, GC3Dsizei height) { makeContextCurrent(); ::glScissor(x, y, width, height); } void GraphicsContext3D::shaderSource(Platform3DObject shader, const String& string) { ASSERT(shader); makeContextCurrent(); ShaderSourceEntry entry; entry.source = string; m_shaderSourceMap.set(shader, entry); } void GraphicsContext3D::stencilFunc(GC3Denum func, GC3Dint ref, GC3Duint mask) { makeContextCurrent(); ::glStencilFunc(func, ref, mask); } void GraphicsContext3D::stencilFuncSeparate(GC3Denum face, GC3Denum func, GC3Dint ref, GC3Duint mask) { makeContextCurrent(); ::glStencilFuncSeparate(face, func, ref, mask); } void GraphicsContext3D::stencilMask(GC3Duint mask) { makeContextCurrent(); ::glStencilMask(mask); } void GraphicsContext3D::stencilMaskSeparate(GC3Denum face, GC3Duint mask) { makeContextCurrent(); ::glStencilMaskSeparate(face, mask); } void GraphicsContext3D::stencilOp(GC3Denum fail, GC3Denum zfail, GC3Denum zpass) { makeContextCurrent(); ::glStencilOp(fail, zfail, zpass); } void GraphicsContext3D::stencilOpSeparate(GC3Denum face, GC3Denum fail, GC3Denum zfail, GC3Denum zpass) { makeContextCurrent(); ::glStencilOpSeparate(face, fail, zfail, zpass); } void GraphicsContext3D::texParameterf(GC3Denum target, GC3Denum pname, GC3Dfloat value) { makeContextCurrent(); ::glTexParameterf(target, pname, value); } void GraphicsContext3D::texParameteri(GC3Denum target, GC3Denum pname, GC3Dint value) { makeContextCurrent(); ::glTexParameteri(target, pname, value); } void GraphicsContext3D::uniform1f(GC3Dint location, GC3Dfloat v0) { makeContextCurrent(); ::glUniform1f(location, v0); } void GraphicsContext3D::uniform1fv(GC3Dint location, GC3Dfloat* array, GC3Dsizei size) { makeContextCurrent(); ::glUniform1fv(location, size, array); } void GraphicsContext3D::uniform2f(GC3Dint location, GC3Dfloat v0, GC3Dfloat v1) { makeContextCurrent(); ::glUniform2f(location, v0, v1); } void GraphicsContext3D::uniform2fv(GC3Dint location, GC3Dfloat* array, GC3Dsizei size) { // FIXME: length needs to be a multiple of 2 makeContextCurrent(); ::glUniform2fv(location, size, array); } void GraphicsContext3D::uniform3f(GC3Dint location, GC3Dfloat v0, GC3Dfloat v1, GC3Dfloat v2) { makeContextCurrent(); ::glUniform3f(location, v0, v1, v2); } void GraphicsContext3D::uniform3fv(GC3Dint location, GC3Dfloat* array, GC3Dsizei size) { // FIXME: length needs to be a multiple of 3 makeContextCurrent(); ::glUniform3fv(location, size, array); } void GraphicsContext3D::uniform4f(GC3Dint location, GC3Dfloat v0, GC3Dfloat v1, GC3Dfloat v2, GC3Dfloat v3) { makeContextCurrent(); ::glUniform4f(location, v0, v1, v2, v3); } void GraphicsContext3D::uniform4fv(GC3Dint location, GC3Dfloat* array, GC3Dsizei size) { // FIXME: length needs to be a multiple of 4 makeContextCurrent(); ::glUniform4fv(location, size, array); } void GraphicsContext3D::uniform1i(GC3Dint location, GC3Dint v0) { makeContextCurrent(); ::glUniform1i(location, v0); } void GraphicsContext3D::uniform1iv(GC3Dint location, GC3Dint* array, GC3Dsizei size) { makeContextCurrent(); ::glUniform1iv(location, size, array); } void GraphicsContext3D::uniform2i(GC3Dint location, GC3Dint v0, GC3Dint v1) { makeContextCurrent(); ::glUniform2i(location, v0, v1); } void GraphicsContext3D::uniform2iv(GC3Dint location, GC3Dint* array, GC3Dsizei size) { // FIXME: length needs to be a multiple of 2 makeContextCurrent(); ::glUniform2iv(location, size, array); } void GraphicsContext3D::uniform3i(GC3Dint location, GC3Dint v0, GC3Dint v1, GC3Dint v2) { makeContextCurrent(); ::glUniform3i(location, v0, v1, v2); } void GraphicsContext3D::uniform3iv(GC3Dint location, GC3Dint* array, GC3Dsizei size) { // FIXME: length needs to be a multiple of 3 makeContextCurrent(); ::glUniform3iv(location, size, array); } void GraphicsContext3D::uniform4i(GC3Dint location, GC3Dint v0, GC3Dint v1, GC3Dint v2, GC3Dint v3) { makeContextCurrent(); ::glUniform4i(location, v0, v1, v2, v3); } void GraphicsContext3D::uniform4iv(GC3Dint location, GC3Dint* array, GC3Dsizei size) { // FIXME: length needs to be a multiple of 4 makeContextCurrent(); ::glUniform4iv(location, size, array); } void GraphicsContext3D::uniformMatrix2fv(GC3Dint location, GC3Dboolean transpose, GC3Dfloat* array, GC3Dsizei size) { // FIXME: length needs to be a multiple of 4 makeContextCurrent(); ::glUniformMatrix2fv(location, size, transpose, array); } void GraphicsContext3D::uniformMatrix3fv(GC3Dint location, GC3Dboolean transpose, GC3Dfloat* array, GC3Dsizei size) { // FIXME: length needs to be a multiple of 9 makeContextCurrent(); ::glUniformMatrix3fv(location, size, transpose, array); } void GraphicsContext3D::uniformMatrix4fv(GC3Dint location, GC3Dboolean transpose, GC3Dfloat* array, GC3Dsizei size) { // FIXME: length needs to be a multiple of 16 makeContextCurrent(); ::glUniformMatrix4fv(location, size, transpose, array); } void GraphicsContext3D::useProgram(Platform3DObject program) { makeContextCurrent(); ::glUseProgram(program); } void GraphicsContext3D::validateProgram(Platform3DObject program) { ASSERT(program); makeContextCurrent(); ::glValidateProgram(program); } void GraphicsContext3D::vertexAttrib1f(GC3Duint index, GC3Dfloat v0) { makeContextCurrent(); ::glVertexAttrib1f(index, v0); } void GraphicsContext3D::vertexAttrib1fv(GC3Duint index, GC3Dfloat* array) { makeContextCurrent(); ::glVertexAttrib1fv(index, array); } void GraphicsContext3D::vertexAttrib2f(GC3Duint index, GC3Dfloat v0, GC3Dfloat v1) { makeContextCurrent(); ::glVertexAttrib2f(index, v0, v1); } void GraphicsContext3D::vertexAttrib2fv(GC3Duint index, GC3Dfloat* array) { makeContextCurrent(); ::glVertexAttrib2fv(index, array); } void GraphicsContext3D::vertexAttrib3f(GC3Duint index, GC3Dfloat v0, GC3Dfloat v1, GC3Dfloat v2) { makeContextCurrent(); ::glVertexAttrib3f(index, v0, v1, v2); } void GraphicsContext3D::vertexAttrib3fv(GC3Duint index, GC3Dfloat* array) { makeContextCurrent(); ::glVertexAttrib3fv(index, array); } void GraphicsContext3D::vertexAttrib4f(GC3Duint index, GC3Dfloat v0, GC3Dfloat v1, GC3Dfloat v2, GC3Dfloat v3) { makeContextCurrent(); ::glVertexAttrib4f(index, v0, v1, v2, v3); } void GraphicsContext3D::vertexAttrib4fv(GC3Duint index, GC3Dfloat* array) { makeContextCurrent(); ::glVertexAttrib4fv(index, array); } void GraphicsContext3D::vertexAttribPointer(GC3Duint index, GC3Dint size, GC3Denum type, GC3Dboolean normalized, GC3Dsizei stride, GC3Dintptr offset) { makeContextCurrent(); ::glVertexAttribPointer(index, size, type, normalized, stride, reinterpret_cast(static_cast(offset))); } void GraphicsContext3D::viewport(GC3Dint x, GC3Dint y, GC3Dsizei width, GC3Dsizei height) { makeContextCurrent(); ::glViewport(x, y, width, height); } void GraphicsContext3D::getBooleanv(GC3Denum pname, GC3Dboolean* value) { makeContextCurrent(); ::glGetBooleanv(pname, value); } void GraphicsContext3D::getBufferParameteriv(GC3Denum target, GC3Denum pname, GC3Dint* value) { makeContextCurrent(); ::glGetBufferParameteriv(target, pname, value); } void GraphicsContext3D::getFloatv(GC3Denum pname, GC3Dfloat* value) { makeContextCurrent(); ::glGetFloatv(pname, value); } void GraphicsContext3D::getFramebufferAttachmentParameteriv(GC3Denum target, GC3Denum attachment, GC3Denum pname, GC3Dint* value) { makeContextCurrent(); if (attachment == DEPTH_STENCIL_ATTACHMENT) attachment = DEPTH_ATTACHMENT; // Or STENCIL_ATTACHMENT, either works. ::glGetFramebufferAttachmentParameterivEXT(target, attachment, pname, value); } void GraphicsContext3D::getIntegerv(GC3Denum pname, GC3Dint* value) { // 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. makeContextCurrent(); 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); } } void GraphicsContext3D::getProgramiv(Platform3DObject program, GC3Denum pname, GC3Dint* value) { makeContextCurrent(); ::glGetProgramiv(program, pname, value); } String GraphicsContext3D::getProgramInfoLog(Platform3DObject program) { ASSERT(program); makeContextCurrent(); GLint length; ::glGetProgramiv(program, GL_INFO_LOG_LENGTH, &length); if (!length) return ""; GLsizei size; GLchar* info = (GLchar*) fastMalloc(length); ::glGetProgramInfoLog(program, length, &size, info); String s(info); fastFree(info); return s; } void GraphicsContext3D::getRenderbufferParameteriv(GC3Denum target, GC3Denum pname, GC3Dint* value) { makeContextCurrent(); ::glGetRenderbufferParameterivEXT(target, pname, value); } void GraphicsContext3D::getShaderiv(Platform3DObject shader, GC3Denum pname, GC3Dint* value) { ASSERT(shader); makeContextCurrent(); HashMap::iterator result = m_shaderSourceMap.find(shader); switch (pname) { case DELETE_STATUS: case SHADER_TYPE: // Let OpenGL handle these. ::glGetShaderiv(shader, pname, value); break; case COMPILE_STATUS: if (result == m_shaderSourceMap.end()) { (*value) = static_cast(false); return; } (*value) = static_cast(result->second.isValid); break; case INFO_LOG_LENGTH: if (result == m_shaderSourceMap.end()) { (*value) = 0; return; } (*value) = getShaderInfoLog(shader).length(); break; case SHADER_SOURCE_LENGTH: (*value) = getShaderSource(shader).length(); break; default: synthesizeGLError(INVALID_ENUM); } } String GraphicsContext3D::getShaderInfoLog(Platform3DObject shader) { ASSERT(shader); makeContextCurrent(); HashMap::iterator result = m_shaderSourceMap.find(shader); if (result == m_shaderSourceMap.end()) return ""; ShaderSourceEntry entry = result->second; if (entry.isValid) { GLint length; ::glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &length); if (!length) return ""; GLsizei size; GLchar* info = (GLchar*) fastMalloc(length); ::glGetShaderInfoLog(shader, length, &size, info); String s(info); fastFree(info); return s; } else return entry.log; } String GraphicsContext3D::getShaderSource(Platform3DObject shader) { ASSERT(shader); makeContextCurrent(); HashMap::iterator result = m_shaderSourceMap.find(shader); if (result == m_shaderSourceMap.end()) return ""; return result->second.source; } void GraphicsContext3D::getTexParameterfv(GC3Denum target, GC3Denum pname, GC3Dfloat* value) { makeContextCurrent(); ::glGetTexParameterfv(target, pname, value); } void GraphicsContext3D::getTexParameteriv(GC3Denum target, GC3Denum pname, GC3Dint* value) { makeContextCurrent(); ::glGetTexParameteriv(target, pname, value); } void GraphicsContext3D::getUniformfv(Platform3DObject program, GC3Dint location, GC3Dfloat* value) { makeContextCurrent(); ::glGetUniformfv(program, location, value); } void GraphicsContext3D::getUniformiv(Platform3DObject program, GC3Dint location, GC3Dint* value) { makeContextCurrent(); ::glGetUniformiv(program, location, value); } GC3Dint GraphicsContext3D::getUniformLocation(Platform3DObject program, const String& name) { ASSERT(program); makeContextCurrent(); return ::glGetUniformLocation(program, name.utf8().data()); } void GraphicsContext3D::getVertexAttribfv(GC3Duint index, GC3Denum pname, GC3Dfloat* value) { makeContextCurrent(); ::glGetVertexAttribfv(index, pname, value); } void GraphicsContext3D::getVertexAttribiv(GC3Duint index, GC3Denum pname, GC3Dint* value) { makeContextCurrent(); ::glGetVertexAttribiv(index, pname, value); } GC3Dsizeiptr GraphicsContext3D::getVertexAttribOffset(GC3Duint index, GC3Denum pname) { makeContextCurrent(); GLvoid* pointer = 0; ::glGetVertexAttribPointerv(index, pname, &pointer); return static_cast(reinterpret_cast(pointer)); } bool GraphicsContext3D::texImage2D(GC3Denum target, GC3Dint level, GC3Denum internalformat, GC3Dsizei width, GC3Dsizei height, GC3Dint border, GC3Denum format, GC3Denum type, const void* pixels) { if (width && height && !pixels) { synthesizeGLError(INVALID_VALUE); return false; } makeContextCurrent(); GC3Denum openGLInternalFormat = internalformat; if (type == GL_FLOAT) { if (format == GL_RGBA) openGLInternalFormat = GL_RGBA32F_ARB; else if (format == GL_RGB) openGLInternalFormat = GL_RGB32F_ARB; } ::glTexImage2D(target, level, openGLInternalFormat, width, height, border, format, type, pixels); return true; } void GraphicsContext3D::texSubImage2D(GC3Denum target, GC3Dint level, GC3Dint xoff, GC3Dint yoff, GC3Dsizei width, GC3Dsizei height, GC3Denum format, GC3Denum type, const void* pixels) { makeContextCurrent(); // FIXME: we will need to deal with PixelStore params when dealing with image buffers that differ from the subimage size ::glTexSubImage2D(target, level, xoff, yoff, width, height, format, type, pixels); } Platform3DObject GraphicsContext3D::createBuffer() { makeContextCurrent(); GLuint o = 0; glGenBuffers(1, &o); return o; } Platform3DObject GraphicsContext3D::createFramebuffer() { makeContextCurrent(); GLuint o = 0; glGenFramebuffersEXT(1, &o); return o; } Platform3DObject GraphicsContext3D::createProgram() { makeContextCurrent(); return glCreateProgram(); } Platform3DObject GraphicsContext3D::createRenderbuffer() { makeContextCurrent(); GLuint o = 0; glGenRenderbuffersEXT(1, &o); return o; } Platform3DObject GraphicsContext3D::createShader(GC3Denum type) { makeContextCurrent(); return glCreateShader((type == FRAGMENT_SHADER) ? GL_FRAGMENT_SHADER : GL_VERTEX_SHADER); } Platform3DObject GraphicsContext3D::createTexture() { makeContextCurrent(); GLuint o = 0; glGenTextures(1, &o); return o; } void GraphicsContext3D::deleteBuffer(Platform3DObject buffer) { makeContextCurrent(); glDeleteBuffers(1, &buffer); } void GraphicsContext3D::deleteFramebuffer(Platform3DObject framebuffer) { makeContextCurrent(); glDeleteFramebuffersEXT(1, &framebuffer); } void GraphicsContext3D::deleteProgram(Platform3DObject program) { makeContextCurrent(); glDeleteProgram(program); } void GraphicsContext3D::deleteRenderbuffer(Platform3DObject renderbuffer) { makeContextCurrent(); glDeleteRenderbuffersEXT(1, &renderbuffer); } void GraphicsContext3D::deleteShader(Platform3DObject shader) { makeContextCurrent(); glDeleteShader(shader); } void GraphicsContext3D::deleteTexture(Platform3DObject texture) { makeContextCurrent(); glDeleteTextures(1, &texture); } void GraphicsContext3D::synthesizeGLError(GC3Denum error) { m_syntheticErrors.add(error); } void GraphicsContext3D::markContextChanged() { m_layerComposited = false; } void GraphicsContext3D::markLayerComposited() { m_layerComposited = true; } bool GraphicsContext3D::layerComposited() const { return m_layerComposited; } Extensions3D* GraphicsContext3D::getExtensions() { if (!m_extensions) m_extensions = adoptPtr(new Extensions3DOpenGL(this)); return m_extensions.get(); } } #endif // ENABLE(WEBGL)