#include "config.h"
#if ENABLE(ACCELERATED_2D_CANVAS) || ENABLE(WEBGL)
#include "DrawingBuffer.h"
#include "Extensions3D.h"
#include "GraphicsContext3D.h"
#include "ImageData.h"
namespace WebCore {
#if PLATFORM(CHROMIUM) // Currently, this cap only exists for chromium.
static int s_maximumResourceUsePixels = 16 * 1024 * 1024;
#elif !PLATFORM(BLACKBERRY)
static int s_maximumResourceUsePixels = 0;
#endif
static int s_currentResourceUsePixels = 0;
static const float s_resourceAdjustedRatio = 0.5;
PassRefPtr<DrawingBuffer> DrawingBuffer::create(GraphicsContext3D* context, const IntSize& size, PreserveDrawingBuffer preserve, AlphaRequirement alpha)
{
Extensions3D* extensions = context->getExtensions();
bool multisampleSupported = extensions->supports("GL_ANGLE_framebuffer_blit") && extensions->supports("GL_ANGLE_framebuffer_multisample") && extensions->supports("GL_OES_rgb8_rgba8");
if (multisampleSupported) {
extensions->ensureEnabled("GL_ANGLE_framebuffer_blit");
extensions->ensureEnabled("GL_ANGLE_framebuffer_multisample");
extensions->ensureEnabled("GL_OES_rgb8_rgba8");
}
bool packedDepthStencilSupported = extensions->supports("GL_OES_packed_depth_stencil");
if (packedDepthStencilSupported)
extensions->ensureEnabled("GL_OES_packed_depth_stencil");
RefPtr<DrawingBuffer> drawingBuffer = adoptRef(new DrawingBuffer(context, size, multisampleSupported, packedDepthStencilSupported, preserve, alpha));
return (drawingBuffer->m_context) ? drawingBuffer.release() : 0;
}
void DrawingBuffer::clear()
{
if (!m_context)
return;
m_context->makeContextCurrent();
if (!m_size.isEmpty()) {
s_currentResourceUsePixels -= m_size.width() * m_size.height();
m_size = IntSize();
}
if (m_colorBuffer) {
m_context->deleteTexture(m_colorBuffer);
m_colorBuffer = 0;
}
if (m_frontColorBuffer) {
m_context->deleteTexture(m_frontColorBuffer);
m_frontColorBuffer = 0;
}
if (m_multisampleColorBuffer) {
m_context->deleteRenderbuffer(m_multisampleColorBuffer);
m_multisampleColorBuffer = 0;
}
if (m_depthStencilBuffer) {
m_context->deleteRenderbuffer(m_depthStencilBuffer);
m_depthStencilBuffer = 0;
}
if (m_depthBuffer) {
m_context->deleteRenderbuffer(m_depthBuffer);
m_depthBuffer = 0;
}
if (m_stencilBuffer) {
m_context->deleteRenderbuffer(m_stencilBuffer);
m_stencilBuffer = 0;
}
if (m_multisampleFBO) {
m_context->deleteFramebuffer(m_multisampleFBO);
m_multisampleFBO = 0;
}
if (m_fbo) {
m_context->deleteFramebuffer(m_fbo);
m_fbo = 0;
}
}
void DrawingBuffer::createSecondaryBuffers()
{
if (multisample()) {
m_multisampleFBO = m_context->createFramebuffer();
m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_multisampleFBO);
m_multisampleColorBuffer = m_context->createRenderbuffer();
}
}
void DrawingBuffer::resizeDepthStencil(int sampleCount)
{
const GraphicsContext3D::Attributes& attributes = m_context->getContextAttributes();
if (attributes.depth && attributes.stencil && m_packedDepthStencilExtensionSupported) {
if (!m_depthStencilBuffer)
m_depthStencilBuffer = m_context->createRenderbuffer();
m_context->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, m_depthStencilBuffer);
if (multisample())
m_context->getExtensions()->renderbufferStorageMultisample(GraphicsContext3D::RENDERBUFFER, sampleCount, Extensions3D::DEPTH24_STENCIL8, m_size.width(), m_size.height());
else
m_context->renderbufferStorage(GraphicsContext3D::RENDERBUFFER, Extensions3D::DEPTH24_STENCIL8, m_size.width(), m_size.height());
m_context->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::STENCIL_ATTACHMENT, GraphicsContext3D::RENDERBUFFER, m_depthStencilBuffer);
m_context->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::DEPTH_ATTACHMENT, GraphicsContext3D::RENDERBUFFER, m_depthStencilBuffer);
} else {
if (attributes.depth) {
if (!m_depthBuffer)
m_depthBuffer = m_context->createRenderbuffer();
m_context->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, m_depthBuffer);
if (multisample())
m_context->getExtensions()->renderbufferStorageMultisample(GraphicsContext3D::RENDERBUFFER, sampleCount, GraphicsContext3D::DEPTH_COMPONENT16, m_size.width(), m_size.height());
else
m_context->renderbufferStorage(GraphicsContext3D::RENDERBUFFER, GraphicsContext3D::DEPTH_COMPONENT16, m_size.width(), m_size.height());
m_context->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::DEPTH_ATTACHMENT, GraphicsContext3D::RENDERBUFFER, m_depthBuffer);
}
if (attributes.stencil) {
if (!m_stencilBuffer)
m_stencilBuffer = m_context->createRenderbuffer();
m_context->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, m_stencilBuffer);
if (multisample())
m_context->getExtensions()->renderbufferStorageMultisample(GraphicsContext3D::RENDERBUFFER, sampleCount, GraphicsContext3D::STENCIL_INDEX8, m_size.width(), m_size.height());
else
m_context->renderbufferStorage(GraphicsContext3D::RENDERBUFFER, GraphicsContext3D::STENCIL_INDEX8, m_size.width(), m_size.height());
m_context->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::STENCIL_ATTACHMENT, GraphicsContext3D::RENDERBUFFER, m_stencilBuffer);
}
}
m_context->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, 0);
}
void DrawingBuffer::clearFramebuffers(GC3Dbitfield clearMask)
{
m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_multisampleFBO ? m_multisampleFBO : m_fbo);
m_context->clear(clearMask);
if (m_multisampleFBO) {
m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_fbo);
m_context->clear(GraphicsContext3D::COLOR_BUFFER_BIT);
m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_multisampleFBO);
}
}
bool DrawingBuffer::reset(const IntSize& newSize)
{
if (!m_context)
return false;
m_context->makeContextCurrent();
int maxTextureSize = 0;
m_context->getIntegerv(GraphicsContext3D::MAX_TEXTURE_SIZE, &maxTextureSize);
if (newSize.height() > maxTextureSize || newSize.width() > maxTextureSize) {
clear();
return false;
}
int pixelDelta = newSize.width() * newSize.height();
int oldSize = 0;
if (!m_size.isEmpty()) {
oldSize = m_size.width() * m_size.height();
pixelDelta -= oldSize;
}
IntSize adjustedSize = newSize;
if (s_maximumResourceUsePixels) {
while ((s_currentResourceUsePixels + pixelDelta) > s_maximumResourceUsePixels) {
adjustedSize.scale(s_resourceAdjustedRatio);
if (adjustedSize.isEmpty()) {
clear();
return false;
}
pixelDelta = adjustedSize.width() * adjustedSize.height();
pixelDelta -= oldSize;
}
}
const GraphicsContext3D::Attributes& attributes = m_context->getContextAttributes();
if (adjustedSize != m_size) {
unsigned internalColorFormat, colorFormat, internalRenderbufferFormat;
if (attributes.alpha) {
internalColorFormat = GraphicsContext3D::RGBA;
colorFormat = GraphicsContext3D::RGBA;
internalRenderbufferFormat = Extensions3D::RGBA8_OES;
} else {
internalColorFormat = GraphicsContext3D::RGB;
colorFormat = GraphicsContext3D::RGB;
internalRenderbufferFormat = Extensions3D::RGB8_OES;
}
do {
m_size = adjustedSize;
if (multisample()) {
int maxSampleCount = 0;
m_context->getIntegerv(Extensions3D::MAX_SAMPLES, &maxSampleCount);
int sampleCount = std::min(4, maxSampleCount);
m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_multisampleFBO);
m_context->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, m_multisampleColorBuffer);
m_context->getExtensions()->renderbufferStorageMultisample(GraphicsContext3D::RENDERBUFFER, sampleCount, internalRenderbufferFormat, m_size.width(), m_size.height());
m_context->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::COLOR_ATTACHMENT0, GraphicsContext3D::RENDERBUFFER, m_multisampleColorBuffer);
resizeDepthStencil(sampleCount);
if (m_context->checkFramebufferStatus(GraphicsContext3D::FRAMEBUFFER) != GraphicsContext3D::FRAMEBUFFER_COMPLETE) {
adjustedSize.scale(s_resourceAdjustedRatio);
continue;
}
}
m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_fbo);
m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, m_colorBuffer);
m_context->texImage2D(GraphicsContext3D::TEXTURE_2D, 0, internalColorFormat, m_size.width(), m_size.height(), 0, colorFormat, GraphicsContext3D::UNSIGNED_BYTE, 0);
m_context->framebufferTexture2D(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::COLOR_ATTACHMENT0, GraphicsContext3D::TEXTURE_2D, m_colorBuffer, 0);
if (m_separateFrontTexture) {
m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, m_frontColorBuffer);
m_context->texImage2D(GraphicsContext3D::TEXTURE_2D, 0, internalColorFormat, m_size.width(), m_size.height(), 0, colorFormat, GraphicsContext3D::UNSIGNED_BYTE, 0);
}
m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, 0);
if (!multisample())
resizeDepthStencil(0);
if (m_context->checkFramebufferStatus(GraphicsContext3D::FRAMEBUFFER) == GraphicsContext3D::FRAMEBUFFER_COMPLETE)
break;
adjustedSize.scale(s_resourceAdjustedRatio);
} while (!adjustedSize.isEmpty());
pixelDelta = m_size.width() * m_size.height();
pixelDelta -= oldSize;
s_currentResourceUsePixels += pixelDelta;
if (!newSize.isEmpty() && adjustedSize.isEmpty()) {
clear();
return false;
}
}
m_context->disable(GraphicsContext3D::SCISSOR_TEST);
m_context->clearColor(0, 0, 0, 0);
m_context->colorMask(true, true, true, true);
GC3Dbitfield clearMask = GraphicsContext3D::COLOR_BUFFER_BIT;
if (attributes.depth) {
m_context->clearDepth(1.0f);
clearMask |= GraphicsContext3D::DEPTH_BUFFER_BIT;
m_context->depthMask(true);
}
if (attributes.stencil) {
m_context->clearStencil(0);
clearMask |= GraphicsContext3D::STENCIL_BUFFER_BIT;
m_context->stencilMaskSeparate(GraphicsContext3D::FRONT, 0xFFFFFFFF);
}
clearFramebuffers(clearMask);
return true;
}
void DrawingBuffer::commit(long x, long y, long width, long height)
{
if (!m_context)
return;
if (width < 0)
width = m_size.width();
if (height < 0)
height = m_size.height();
m_context->makeContextCurrent();
if (m_multisampleFBO) {
m_context->bindFramebuffer(Extensions3D::READ_FRAMEBUFFER, m_multisampleFBO);
m_context->bindFramebuffer(Extensions3D::DRAW_FRAMEBUFFER, m_fbo);
if (m_scissorEnabled)
m_context->disable(GraphicsContext3D::SCISSOR_TEST);
m_context->getExtensions()->blitFramebuffer(x, y, width, height, x, y, width, height, GraphicsContext3D::COLOR_BUFFER_BIT, GraphicsContext3D::NEAREST);
if (m_scissorEnabled)
m_context->enable(GraphicsContext3D::SCISSOR_TEST);
}
m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_fbo);
}
void DrawingBuffer::restoreFramebufferBinding()
{
if (!m_context || !m_framebufferBinding)
return;
m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_framebufferBinding);
}
bool DrawingBuffer::multisample() const
{
return m_context && m_context->getContextAttributes().antialias && m_multisampleExtensionSupported;
}
PassRefPtr<ImageData> DrawingBuffer::paintRenderingResultsToImageData()
{
return m_context->paintRenderingResultsToImageData(this);
}
void DrawingBuffer::discardResources()
{
m_colorBuffer = 0;
m_frontColorBuffer = 0;
m_multisampleColorBuffer = 0;
m_depthStencilBuffer = 0;
m_depthBuffer = 0;
m_stencilBuffer = 0;
m_multisampleFBO = 0;
m_fbo = 0;
}
void DrawingBuffer::bind()
{
if (!m_context)
return;
m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_multisampleFBO ? m_multisampleFBO : m_fbo);
}
}
#endif