GraphicsContext3DOpenGL.cpp [plain text]
#include "config.h"
#if ENABLE(WEBGL)
#include "GraphicsContext3D.h"
#include "WebGLObject.h"
#include "CanvasRenderingContext.h"
#include "Extensions3DOpenGL.h"
#include "GraphicsContext.h"
#include "HTMLCanvasElement.h"
#include "ImageBuffer.h"
#include "ImageData.h"
#include "NotImplemented.h"
#include <cstring>
#include <wtf/ArrayBuffer.h>
#include <wtf/ArrayBufferView.h>
#include <wtf/Float32Array.h>
#include <wtf/Int32Array.h>
#include <wtf/Uint8Array.h>
#include <wtf/UnusedParam.h>
#include <wtf/text/CString.h>
#if PLATFORM(MAC)
#include <OpenGL/gl.h>
#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");
m_attrs.depth = true;
} else
m_attrs.stencil = false;
}
if (m_attrs.antialias) {
bool isValidVendor = true;
const char* vendor = reinterpret_cast<const char*>(::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<unsigned char> 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) {
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<ImageData> GraphicsContext3D::paintRenderingResultsToImageData()
{
if (m_attrs.premultipliedAlpha)
return 0;
RefPtr<ImageData> 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);
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) {
Extensions3D* extensions = getExtensions();
if (extensions->supports("GL_EXT_packed_depth_stencil"))
internalDepthStencilFormat = GL_DEPTH24_STENCIL8_EXT;
else
internalDepthStencilFormat = GL_DEPTH_COMPONENT;
}
bool mustRestoreFBO = false;
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) {
notImplemented();
}
}
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) {
notImplemented();
}
if (m_attrs.antialias) {
::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_multisampleFBO);
if (m_boundFBO == m_multisampleFBO)
mustRestoreFBO = false;
}
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<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_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;
HashMap<Platform3DObject, ShaderSourceEntry>::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;
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(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<GLvoid*>(static_cast<intptr_t>(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]; 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]; 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<GC3Denum>::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)
{
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()
{
makeContextCurrent();
}
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)
{
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)
{
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)
{
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)
{
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)
{
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)
{
makeContextCurrent();
::glUniform4iv(location, size, array);
}
void GraphicsContext3D::uniformMatrix2fv(GC3Dint location, GC3Dboolean transpose, GC3Dfloat* array, GC3Dsizei size)
{
makeContextCurrent();
::glUniformMatrix2fv(location, size, transpose, array);
}
void GraphicsContext3D::uniformMatrix3fv(GC3Dint location, GC3Dboolean transpose, GC3Dfloat* array, GC3Dsizei size)
{
makeContextCurrent();
::glUniformMatrix3fv(location, size, transpose, array);
}
void GraphicsContext3D::uniformMatrix4fv(GC3Dint location, GC3Dboolean transpose, GC3Dfloat* array, GC3Dsizei size)
{
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<GLvoid*>(static_cast<intptr_t>(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; ::glGetFramebufferAttachmentParameterivEXT(target, attachment, pname, value);
}
void GraphicsContext3D::getIntegerv(GC3Denum pname, GC3Dint* value)
{
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<Platform3DObject, ShaderSourceEntry>::iterator result = m_shaderSourceMap.find(shader);
switch (pname) {
case DELETE_STATUS:
case SHADER_TYPE:
::glGetShaderiv(shader, pname, value);
break;
case COMPILE_STATUS:
if (result == m_shaderSourceMap.end()) {
(*value) = static_cast<int>(false);
return;
}
(*value) = static_cast<int>(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<Platform3DObject, ShaderSourceEntry>::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<Platform3DObject, ShaderSourceEntry>::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<GC3Dsizeiptr>(reinterpret_cast<intptr_t>(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();
::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)