#include "config.h"
#if USE(ACCELERATED_COMPOSITING)
#include "LayerRenderer.h"
#include "LayerCompositingThread.h"
#include "PlatformString.h"
#include "TextureCacheCompositingThread.h"
#include <BlackBerryPlatformLog.h>
#include <wtf/CurrentTime.h>
#include <wtf/text/CString.h>
#define ENABLE_SCISSOR 1
#define DEBUG_SHADER_COMPILATION 0
#define DEBUG_DIRTY_LAYERS 0 // Show dirty layers as red.
#define DEBUG_LAYER_ANIMATIONS 0 // Show running animations as green.
#define DEBUG_VIDEO_CLIPPING 0
using BlackBerry::Platform::Graphics::GLES2Context;
using namespace std;
namespace WebCore {
static void checkGLError()
{
#ifndef NDEBUG
if (GLenum error = glGetError())
LOG_ERROR("GL Error: 0x%x " , error);
#endif
}
static GLuint loadShader(GLenum type, const char* shaderSource)
{
GLuint shader = glCreateShader(type);
if (!shader)
return 0;
glShaderSource(shader, 1, &shaderSource, 0);
glCompileShader(shader);
GLint compiled;
glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
if (!compiled) {
#if DEBUG_SHADER_COMPILATION
char infoLog[2048];
GLsizei length;
glGetShaderInfoLog(shader, 2048, &length, infoLog);
fprintf(stderr, "Failed to compile shader: %s", infoLog);
#endif
glDeleteShader(shader);
return 0;
}
return shader;
}
static GLuint loadShaderProgram(const char* vertexShaderSource, const char* fragmentShaderSource)
{
GLuint vertexShader;
GLuint fragmentShader;
GLuint programObject;
GLint linked;
vertexShader = loadShader(GL_VERTEX_SHADER, vertexShaderSource);
if (!vertexShader)
return 0;
fragmentShader = loadShader(GL_FRAGMENT_SHADER, fragmentShaderSource);
if (!fragmentShader) {
glDeleteShader(vertexShader);
return 0;
}
programObject = glCreateProgram();
if (programObject) {
glAttachShader(programObject, vertexShader);
glAttachShader(programObject, fragmentShader);
glLinkProgram(programObject);
glGetProgramiv(programObject, GL_LINK_STATUS, &linked);
if (!linked) {
glDeleteProgram(programObject);
programObject = 0;
}
}
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
return programObject;
}
static TransformationMatrix orthoMatrix(float left, float right, float bottom, float top, float nearZ, float farZ)
{
float deltaX = right - left;
float deltaY = top - bottom;
float deltaZ = farZ - nearZ;
TransformationMatrix ortho;
if (!deltaX || !deltaY || !deltaZ)
return ortho;
ortho.setM11(2.0f / deltaX);
ortho.setM41(-(right + left) / deltaX);
ortho.setM22(2.0f / deltaY);
ortho.setM42(-(top + bottom) / deltaY);
ortho.setM33(-2.0f / deltaZ);
ortho.setM43(-(nearZ + farZ) / deltaZ);
return ortho;
}
static Vector<LayerCompositingThread*> rawPtrVectorFromRefPtrVector(const Vector<RefPtr<LayerCompositingThread> >& sublayers)
{
Vector<LayerCompositingThread*> sublayerList;
for (size_t i = 0; i < sublayers.size(); i++)
sublayerList.append(sublayers[i].get());
return sublayerList;
}
PassOwnPtr<LayerRenderer> LayerRenderer::create(GLES2Context* context)
{
return adoptPtr(new LayerRenderer(context));
}
LayerRenderer::LayerRenderer(GLES2Context* context)
: m_colorProgramObject(0)
, m_checkerProgramObject(0)
, m_positionLocation(0)
, m_texCoordLocation(1)
, m_fbo(0)
, m_currentLayerRendererSurface(0)
, m_clearSurfaceOnDrawLayers(true)
, m_context(context)
, m_needsCommit(false)
{
for (int i = 0; i < LayerData::NumberOfLayerProgramShaders; ++i)
m_layerProgramObject[i] = 0;
m_hardwareCompositing = initializeSharedGLObjects();
}
LayerRenderer::~LayerRenderer()
{
if (m_hardwareCompositing) {
makeContextCurrent();
if (m_fbo)
glDeleteFramebuffers(1, &m_fbo);
glDeleteProgram(m_colorProgramObject);
glDeleteProgram(m_checkerProgramObject);
for (int i = 0; i < LayerData::NumberOfLayerProgramShaders; ++i)
glDeleteProgram(m_layerProgramObject[i]);
while (m_layers.begin() != m_layers.end()) {
LayerSet::iterator iter = m_layers.begin();
(*iter)->deleteTextures();
(*iter)->setLayerRenderer(0);
removeLayer(*iter);
}
textureCacheCompositingThread()->clear();
}
}
void LayerRenderer::releaseLayerResources()
{
if (m_hardwareCompositing) {
makeContextCurrent();
for (LayerSet::iterator iter = m_layers.begin(); iter != m_layers.end(); ++iter)
(*iter)->deleteTextures();
textureCacheCompositingThread()->clear();
}
}
static inline bool compareLayerZ(const LayerCompositingThread* a, const LayerCompositingThread* b)
{
const TransformationMatrix& transformA = a->drawTransform();
const TransformationMatrix& transformB = b->drawTransform();
return transformA.m43() < transformB.m43();
}
void LayerRenderer::drawLayers(const FloatRect& visibleRect, const IntRect& layoutRect, const IntSize& contentsSize, const IntRect& dstRect)
{
ASSERT(m_hardwareCompositing);
if (!m_hardwareCompositing)
return;
bool wasEmpty = m_lastRenderingResults.isEmpty();
m_lastRenderingResults = LayerRenderingResults();
m_lastRenderingResults.wasEmpty = wasEmpty;
if (!m_rootLayer)
return;
m_visibleRect = visibleRect;
m_layoutRect = layoutRect;
m_contentsSize = contentsSize;
TransformationMatrix matrix = orthoMatrix(0, visibleRect.width(), visibleRect.height(), 0, -1000, 1000);
matrix.translate3d(-visibleRect.x(), -visibleRect.y(), 0);
int viewportY = std::max(0, m_context->surfaceSize().height() - dstRect.maxY());
m_viewport = IntRect(dstRect.x(), viewportY, dstRect.width(), dstRect.height());
double animationTime = currentTime();
#if DEBUG_VIDEO_CLIPPING
BlackBerry::Platform::log(BlackBerry::Platform::LogLevelInfo, "LayerRenderer::drawLayers() visible=(x=%.2f,y=%.2f,width=%.2f,height=%.2f), layout=(x=%d,y=%d,width=%d,height=%d), contents=(%dx%d), dst=(x=%d,y=%d,width=%d,height=%d).",
visibleRect.x(), visibleRect.y(), visibleRect.width(), visibleRect.height(),
layoutRect.x(), layoutRect.y(), layoutRect.width(), layoutRect.height(),
contentsSize.width(), contentsSize.height(),
dstRect.x(), dstRect.y(), dstRect.width(), dstRect.height());
#endif
Vector<RefPtr<LayerCompositingThread> > surfaceLayers;
const Vector<RefPtr<LayerCompositingThread> >& sublayers = m_rootLayer->getSublayers();
for (size_t i = 0; i < sublayers.size(); i++) {
float opacity = 1;
FloatRect clipRect(-1, -1, 2, 2);
updateLayersRecursive(sublayers[i].get(), matrix, surfaceLayers, opacity, clipRect, animationTime);
}
for (int i = 0; i < LayerRenderingResults::NumberOfDirtyRects; ++i) {
BlackBerry::Platform::IntRectRegion region(BlackBerry::Platform::IntRect(m_lastRenderingResults.dirtyRect(i)));
m_lastRenderingResults.dirtyRegion = BlackBerry::Platform::IntRectRegion::unionRegions(m_lastRenderingResults.dirtyRegion, region);
}
if (m_lastRenderingResults.isEmpty() && wasEmpty)
return;
if (!makeContextCurrent())
return;
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glEnableVertexAttribArray(m_positionLocation);
glEnableVertexAttribArray(m_texCoordLocation);
glActiveTexture(GL_TEXTURE0);
glDisable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
glEnable(GL_STENCIL_TEST);
glCullFace(GL_BACK);
glFrontFace(GL_CCW);
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glUseProgram(m_checkerProgramObject);
float bitmapScale = static_cast<float>(m_layoutRect.width()) / static_cast<float>(m_visibleRect.width());
glUniform1f(m_checkerScaleLocation, bitmapScale);
float scale = static_cast<float>(dstRect.width()) / static_cast<float>(m_visibleRect.width());
glUniform2f(m_checkerOriginLocation, m_visibleRect.x()*scale, m_visibleRect.y()*scale);
glUniform1f(m_checkerSurfaceHeightLocation, m_context->surfaceSize().height());
checkGLError();
drawLayersOnSurfaces(surfaceLayers);
#if ENABLE_SCISSOR
m_scissorRect = m_viewport;
glEnable(GL_SCISSOR_TEST);
glScissor(m_scissorRect.x(), m_scissorRect.y(), m_scissorRect.width(), m_scissorRect.height());
#endif
glClearStencil(0);
glClearColor(0, 0, 0, 0);
GLenum buffersToClear = GL_STENCIL_BUFFER_BIT;
if (m_clearSurfaceOnDrawLayers) {
buffersToClear |= GL_COLOR_BUFFER_BIT;
}
glClear(buffersToClear);
for (size_t i = 0; i < sublayers.size(); i++) {
int currentStencilValue = 0;
FloatRect clipRect(-1, -1, 2, 2);
compositeLayersRecursive(sublayers[i].get(), currentStencilValue, clipRect);
}
if (m_layersLockingTextureResources.size())
glFinish();
m_context->swapBuffers();
#if ENABLE_SCISSOR
glDisable(GL_SCISSOR_TEST);
#endif
glDisable(GL_STENCIL_TEST);
glBindTexture(GL_TEXTURE_2D, 0);
LayerSet::iterator iter = m_layersLockingTextureResources.begin();
for (; iter != m_layersLockingTextureResources.end(); ++iter)
(*iter)->releaseTextureResources();
m_layersLockingTextureResources.clear();
if (m_needsCommit) {
m_needsCommit = false;
m_rootLayer->scheduleCommit();
}
textureCacheCompositingThread()->collectGarbage();
}
bool LayerRenderer::useSurface(LayerRendererSurface* surface)
{
if (m_currentLayerRendererSurface == surface)
return true;
m_currentLayerRendererSurface = surface;
if (!surface) {
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glViewport(m_viewport.x(), m_viewport.y(), m_viewport.width(), m_viewport.height());
return true;
}
surface->ensureTexture();
if (!m_fbo)
glGenFramebuffers(1, &m_fbo);
glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, surface->texture()->textureId(), 0);
#ifndef NDEBUG
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE) {
fprintf(stderr, "glCheckFramebufferStatus error %x\n", status);
return false;
}
#endif
glViewport(0, 0, surface->size().width(), surface->size().height());
return true;
}
void LayerRenderer::drawLayersOnSurfaces(const Vector<RefPtr<LayerCompositingThread> >& surfaceLayers)
{
for (int i = surfaceLayers.size() - 1; i >= 0; i--) {
LayerCompositingThread* layer = surfaceLayers[i].get();
LayerRendererSurface* surface = layer->layerRendererSurface();
if (!surface || !useSurface(surface))
continue;
glDisable(GL_SCISSOR_TEST);
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT);
int currentStencilValue = 0;
FloatRect clipRect(-1, -1, 2, 2);
compositeLayersRecursive(surfaceLayers[i].get(), currentStencilValue, clipRect);
}
if (surfaceLayers.size())
useSurface(0);
else
glViewport(m_viewport.x(), m_viewport.y(), m_viewport.width(), m_viewport.height());
}
void LayerRenderer::setRootLayer(LayerCompositingThread* layer)
{
m_rootLayer = layer;
}
void LayerRenderer::addLayer(LayerCompositingThread* layer)
{
m_layers.add(layer);
}
bool LayerRenderer::removeLayer(LayerCompositingThread* layer)
{
LayerSet::iterator iter = m_layers.find(layer);
if (iter == m_layers.end())
return false;
m_layers.remove(layer);
return true;
}
void LayerRenderer::addLayerToReleaseTextureResourcesList(LayerCompositingThread* layer)
{
m_layersLockingTextureResources.add(layer);
}
IntRect LayerRenderer::toOpenGLWindowCoordinates(const FloatRect& r) const
{
float vw2 = m_viewport.width() / 2.0;
float vh2 = m_viewport.height() / 2.0;
float ox = m_viewport.x() + vw2;
float oy = m_viewport.y() + vh2;
return enclosingIntRect(FloatRect(r.x() * vw2 + ox, r.y() * vh2 + oy, r.width() * vw2, r.height() * vh2));
}
IntRect LayerRenderer::toWebKitWindowCoordinates(const FloatRect& r) const
{
float vw2 = m_viewport.width() / 2.0;
float vh2 = m_viewport.height() / 2.0;
float ox = m_viewport.x() + vw2;
float oy = m_context->surfaceSize().height() - (m_viewport.y() + vh2);
return enclosingIntRect(FloatRect(r.x() * vw2 + ox, -(r.y()+r.height()) * vh2 + oy, r.width() * vw2, r.height() * vh2));
}
IntRect LayerRenderer::toWebKitDocumentCoordinates(const FloatRect& r) const
{
float zoom = m_visibleRect.width() / m_viewport.width();
IntRect result = toWebKitWindowCoordinates(r);
result.scale(zoom);
return result;
}
void LayerRenderer::drawDebugBorder(LayerCompositingThread* layer)
{
Color borderColor = layer->borderColor();
#if DEBUG_DIRTY_LAYERS
if (layer->isDirty())
borderColor = Color(0xFF, 0x00, 0x00, 0xFF);
#endif
#if DEBUG_LAYER_ANIMATIONS
if (layer->hasRunningAnimations())
borderColor = Color(0x00, 0xFF, 0x00, 0xFF);
#endif
if (!borderColor.alpha())
return;
glUseProgram(m_colorProgramObject);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, &layer->getTransformedBounds());
glUniform4f(m_colorColorLocation, borderColor.red() / 255.0, borderColor.green() / 255.0, borderColor.blue() / 255.0, 1);
glLineWidth(layer->borderWidth());
glDrawArrays(GL_LINE_LOOP, 0, 4);
}
void LayerRenderer::drawHolePunchRect(LayerCompositingThread* layer)
{
glUseProgram(m_colorProgramObject);
glUniform4f(m_colorColorLocation, 0, 0, 0, 0);
glBlendFunc(GL_ONE, GL_ZERO);
FloatQuad hole = layer->getTransformedHolePunchRect();
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, &hole);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
checkGLError();
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
IntRect holeWC = toWebKitWindowCoordinates(hole.boundingBox());
m_lastRenderingResults.addHolePunchRect(holeWC);
}
void LayerRenderer::updateLayersRecursive(LayerCompositingThread* layer, const TransformationMatrix& matrix, Vector<RefPtr<LayerCompositingThread> >& surfaceLayers, float opacity, FloatRect clipRect, double currentTime)
{
layer->setLayerRenderer(this);
if (layer->maskLayer())
layer->maskLayer()->setLayerRenderer(this);
if (layer->replicaLayer()) {
LayerCompositingThread* replica = layer->replicaLayer();
replica->setLayerRenderer(this);
if (replica->maskLayer())
replica->maskLayer()->setLayerRenderer(this);
}
m_lastRenderingResults.needsAnimationFrame |= layer->updateAnimations(currentTime);
IntSize bounds = layer->bounds();
FloatPoint anchorPoint = layer->anchorPoint();
FloatPoint position = layer->position();
if (!layer->hasFixedContainer() && (layer->isFixedPosition() || layer->hasFixedAncestorInDOMTree())) {
float maximumScrollY = m_contentsSize.height() - m_visibleRect.height();
float visibleY = max(0.0f, m_visibleRect.y());
float layoutY = max(0.0f, min(maximumScrollY, (float)m_layoutRect.y()));
if (position.y() - anchorPoint.y() * bounds.height() > layoutY + m_layoutRect.height() / 2) {
visibleY = min<float>(m_contentsSize.height(), m_visibleRect.y() + m_visibleRect.height());
layoutY = min(m_contentsSize.height(), max(0, m_layoutRect.y()) + m_layoutRect.height());
}
position.setY(position.y() + (visibleY - layoutY));
}
float centerOffsetX = (0.5 - anchorPoint.x()) * bounds.width();
float centerOffsetY = (0.5 - anchorPoint.y()) * bounds.height();
TransformationMatrix localMatrix = matrix;
localMatrix.translate3d(position.x(), position.y(), layer->anchorPointZ());
localMatrix.multiply(layer->transform());
localMatrix.translate3d(centerOffsetX, centerOffsetY, -layer->anchorPointZ());
opacity *= layer->opacity();
bool useLayerRendererSurface = layer->maskLayer() || layer->replicaLayer();
if (!useLayerRendererSurface) {
layer->setDrawOpacity(opacity);
layer->clearLayerRendererSurface();
} else {
if (!layer->layerRendererSurface())
layer->createLayerRendererSurface();
LayerRendererSurface* surface = layer->layerRendererSurface();
layer->setDrawOpacity(1.0);
surface->setDrawOpacity(opacity);
surface->setDrawTransform(localMatrix);
if (layer->replicaLayer()) {
TransformationMatrix replicaMatrix = localMatrix;
replicaMatrix.translate3d(-0.5 * bounds.width(), -0.5 * bounds.height(), 0);
replicaMatrix.translate3d(layer->replicaLayer()->position().x(), layer->replicaLayer()->position().y(), 0);
replicaMatrix.multiply(layer->replicaLayer()->transform());
replicaMatrix.translate3d(centerOffsetX, centerOffsetY, 0);
surface->setReplicaDrawTransform(replicaMatrix);
}
IntRect drawRect = IntRect(IntPoint(), bounds);
surface->setContentRect(drawRect);
TransformationMatrix projectionMatrix = orthoMatrix(drawRect.x(), drawRect.maxX(), drawRect.y(), drawRect.maxY(), -1000, 1000);
TransformationMatrix drawTransform;
drawTransform.translate3d(0.5 * bounds.width(), 0.5 * bounds.height(), 0);
localMatrix = projectionMatrix * drawTransform;
surfaceLayers.append(layer);
}
layer->setDrawTransform(localMatrix);
#if ENABLE(VIDEO)
bool layerVisible = clipRect.intersects(layer->getDrawRect()) || layer->mediaPlayer();
#else
bool layerVisible = clipRect.intersects(layer->getDrawRect());
#endif
if (layer->needsTexture() && layerVisible) {
IntRect dirtyRect = toWebKitWindowCoordinates(intersection(layer->getDrawRect(), clipRect));
m_lastRenderingResults.addDirtyRect(dirtyRect);
}
if (layer->masksToBounds())
clipRect.intersect(layer->getDrawRect());
if (!layer->preserves3D()) {
localMatrix.setM13(0);
localMatrix.setM23(0);
localMatrix.setM31(0);
localMatrix.setM32(0);
localMatrix.setM33(0.001);
localMatrix.setM34(0);
localMatrix.setM43(0);
}
localMatrix.multiply(layer->sublayerTransform());
localMatrix.translate3d(-bounds.width() * 0.5, -bounds.height() * 0.5, 0);
const Vector<RefPtr<LayerCompositingThread> >& sublayers = layer->getSublayers();
for (size_t i = 0; i < sublayers.size(); i++)
updateLayersRecursive(sublayers[i].get(), localMatrix, surfaceLayers, opacity, clipRect, currentTime);
}
static bool hasRotationalComponent(const TransformationMatrix& m)
{
return m.m12() || m.m13() || m.m23() || m.m21() || m.m31() || m.m32();
}
bool LayerRenderer::layerAlreadyOnSurface(LayerCompositingThread* layer) const
{
return layer->layerRendererSurface() && layer->layerRendererSurface() != m_currentLayerRendererSurface;
}
static void collect3DPreservingLayers(Vector<LayerCompositingThread*>& layers)
{
for (size_t i = 0; i < layers.size(); ++i) {
LayerCompositingThread* layer = layers[i];
if (!layer->preserves3D() || !layer->getSublayers().size())
continue;
Vector<LayerCompositingThread*> sublayers = rawPtrVectorFromRefPtrVector(layer->getSublayers());
collect3DPreservingLayers(sublayers);
layers.insert(i+1, sublayers);
i += sublayers.size();
}
}
void LayerRenderer::compositeLayersRecursive(LayerCompositingThread* layer, int stencilValue, FloatRect clipRect)
{
FloatRect rect;
if (layerAlreadyOnSurface(layer))
rect = layer->layerRendererSurface()->drawRect();
else
rect = layer->getDrawRect();
#if ENABLE(VIDEO)
bool layerVisible = clipRect.intersects(rect) || layer->mediaPlayer();
#else
bool layerVisible = clipRect.intersects(rect);
#endif
layer->setVisible(layerVisible);
glStencilFunc(GL_EQUAL, stencilValue, 0xff);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
if (layer->needsTexture())
layer->updateTextureContentsIfNeeded();
if (layer->maskLayer() && layer->maskLayer()->needsTexture())
layer->maskLayer()->updateTextureContentsIfNeeded();
if (layer->replicaLayer()) {
LayerCompositingThread* replica = layer->replicaLayer();
if (replica->needsTexture())
replica->updateTextureContentsIfNeeded();
if (replica->maskLayer() && replica->maskLayer()->needsTexture())
replica->maskLayer()->updateTextureContentsIfNeeded();
}
if ((layer->needsTexture() || layer->layerRendererSurface()) && layerVisible) {
updateScissorIfNeeded(clipRect);
if (layer->doubleSided())
glDisable(GL_CULL_FACE);
else
glEnable(GL_CULL_FACE);
if (layer->hasVisibleHolePunchRect())
drawHolePunchRect(layer);
bool drawSurface = layerAlreadyOnSurface(layer);
LayerData::LayerProgramShader shader = drawSurface ? LayerData::LayerProgramShaderRGBA : layer->layerProgramShader();
if (!drawSurface) {
glUseProgram(m_layerProgramObject[shader]);
glUniform1f(m_alphaLocation[shader], layer->drawOpacity());
layer->drawTextures(m_positionLocation, m_texCoordLocation, m_visibleRect);
} else {
if (layer->replicaLayer()) {
LayerCompositingThread* mask = layer->maskLayer();
if (!mask && layer->replicaLayer())
mask = layer->replicaLayer()->maskLayer();
glUseProgram(mask ? m_layerMaskProgramObject[shader] : m_layerProgramObject[shader]);
glUniform1f(mask ? m_maskAlphaLocation[shader] : m_alphaLocation[shader], layer->layerRendererSurface()->drawOpacity());
layer->drawSurface(layer->layerRendererSurface()->replicaDrawTransform(), mask, m_positionLocation, m_texCoordLocation);
}
glUseProgram(layer->maskLayer() ? m_layerMaskProgramObject[shader] : m_layerProgramObject[shader]);
glUniform1f(layer->maskLayer() ? m_maskAlphaLocation[shader] : m_alphaLocation[shader], layer->layerRendererSurface()->drawOpacity());
layer->drawSurface(layer->layerRendererSurface()->drawTransform(), layer->maskLayer(), m_positionLocation, m_texCoordLocation);
}
if (layer->hasMissingTextures()) {
glDisable(GL_BLEND);
glUseProgram(m_checkerProgramObject);
layer->drawMissingTextures(m_positionLocation, m_texCoordLocation, m_visibleRect);
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
}
}
drawDebugBorder(layer);
if (layerAlreadyOnSurface(layer)) {
layer->layerRendererSurface()->releaseTexture();
return;
}
#if ENABLE_SCISSOR
bool stencilClip = layer->masksToBounds() && hasRotationalComponent(layer->drawTransform());
#else
bool stencilClip = layer->masksToBounds();
#endif
if (stencilClip) {
glStencilFunc(GL_EQUAL, stencilValue, 0xff);
glStencilOp(GL_KEEP, GL_INCR, GL_INCR);
updateScissorIfNeeded(clipRect);
glUseProgram(m_colorProgramObject);
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, &layer->getTransformedBounds());
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
}
if (layer->masksToBounds())
clipRect.intersect(layer->getDrawRect());
Vector<LayerCompositingThread*> sublayers = rawPtrVectorFromRefPtrVector(layer->getSublayers());
bool preserves3D = layer->preserves3D();
bool superlayerPreserves3D = layer->superlayer() && layer->superlayer()->preserves3D();
if (preserves3D && !superlayerPreserves3D) {
collect3DPreservingLayers(sublayers);
std::stable_sort(sublayers.begin(), sublayers.end(), compareLayerZ);
}
int newStencilValue = stencilClip ? stencilValue+1 : stencilValue;
for (size_t i = 0; i < sublayers.size(); i++) {
LayerCompositingThread* sublayer = sublayers[i];
if (preserves3D && superlayerPreserves3D)
continue;
compositeLayersRecursive(sublayer, newStencilValue, clipRect);
}
if (stencilClip) {
glStencilFunc(GL_LEQUAL, stencilValue, 0xff);
glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE);
updateScissorIfNeeded(clipRect);
glUseProgram(m_colorProgramObject);
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, &layer->getTransformedBounds());
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
}
}
void LayerRenderer::updateScissorIfNeeded(const FloatRect& clipRect)
{
#if ENABLE_SCISSOR
IntRect clipRectWC = toOpenGLWindowCoordinates(clipRect);
if (m_scissorRect == clipRectWC)
return;
m_scissorRect = clipRectWC;
glScissor(m_scissorRect.x(), m_scissorRect.y(), m_scissorRect.width(), m_scissorRect.height());
#endif
}
bool LayerRenderer::makeContextCurrent()
{
return m_context->makeCurrent();
}
void LayerRenderer::bindCommonAttribLocation(int location, const char* attribName)
{
for (int i = 0; i < LayerData::NumberOfLayerProgramShaders; ++i) {
glBindAttribLocation(m_layerProgramObject[i], location, attribName);
glBindAttribLocation(m_layerMaskProgramObject[i], location, attribName);
}
glBindAttribLocation(m_colorProgramObject, location, attribName);
glBindAttribLocation(m_checkerProgramObject, location, attribName);
}
bool LayerRenderer::initializeSharedGLObjects()
{
char vertexShaderString[] =
"attribute vec4 a_position; \n"
"attribute vec2 a_texCoord; \n"
"varying vec2 v_texCoord; \n"
"void main() \n"
"{ \n"
" gl_Position = a_position; \n"
" v_texCoord = a_texCoord; \n"
"} \n";
char fragmentShaderStringRGBA[] =
"varying mediump vec2 v_texCoord; \n"
"uniform lowp sampler2D s_texture; \n"
"uniform lowp float alpha; \n"
"void main() \n"
"{ \n"
" gl_FragColor = texture2D(s_texture, v_texCoord) * alpha; \n"
"} \n";
char fragmentShaderStringBGRA[] =
"varying mediump vec2 v_texCoord; \n"
"uniform lowp sampler2D s_texture; \n"
"uniform lowp float alpha; \n"
"void main() \n"
"{ \n"
" gl_FragColor = texture2D(s_texture, v_texCoord).bgra * alpha; \n"
"} \n";
char fragmentShaderStringMaskRGBA[] =
"varying mediump vec2 v_texCoord; \n"
"uniform lowp sampler2D s_texture; \n"
"uniform lowp sampler2D s_mask; \n"
"uniform lowp float alpha; \n"
"void main() \n"
"{ \n"
" lowp vec4 texColor = texture2D(s_texture, v_texCoord); \n"
" lowp vec4 maskColor = texture2D(s_mask, v_texCoord); \n"
" gl_FragColor = vec4(texColor.x, texColor.y, texColor.z, texColor.w) * alpha * maskColor.w; \n"
"} \n";
char fragmentShaderStringMaskBGRA[] =
"varying mediump vec2 v_texCoord; \n"
"uniform lowp sampler2D s_texture; \n"
"uniform lowp sampler2D s_mask; \n"
"uniform lowp float alpha; \n"
"void main() \n"
"{ \n"
" lowp vec4 texColor = texture2D(s_texture, v_texCoord).bgra; \n"
" lowp vec4 maskColor = texture2D(s_mask, v_texCoord).bgra; \n"
" gl_FragColor = vec4(texColor.x, texColor.y, texColor.z, texColor.w) * alpha * maskColor.w; \n"
"} \n";
char colorVertexShaderString[] =
"attribute vec4 a_position; \n"
"void main() \n"
"{ \n"
" gl_Position = a_position; \n"
"} \n";
char colorFragmentShaderString[] =
"uniform lowp vec4 color; \n"
"void main() \n"
"{ \n"
" gl_FragColor = color; \n"
"} \n";
static Color lightColor(0xfb, 0xfd, 0xff);
static Color darkColor(0xe8, 0xee, 0xf7);
int checkerSize = 20;
String tmp(
"uniform mediump float scale; \n"
"uniform mediump vec2 origin; \n"
"uniform mediump float surfaceHeight; \n"
"void main() \n"
"{ \n"
" const mediump float grid = GRID; \n"
" const lowp vec4 lightColor = LIGHT_COLOR; \n"
" const lowp vec4 darkColor = DARK_COLOR; \n"
" mediump float tmp = grid * scale; \n"
" gl_FragColor = mod(floor((gl_FragCoord.x + origin.x) / tmp) + floor((surfaceHeight - gl_FragCoord.y + origin.y) / tmp), 2.0) > 0.99 \n"
" ? lightColor : darkColor; \n"
"} \n");
tmp.replace("LIGHT_COLOR", String::format("vec4(%.4f, %.4f, %.4f, 1.0)", lightColor.red() / 255.0, lightColor.green() / 255.0, lightColor.blue() / 255.0));
tmp.replace("DARK_COLOR", String::format("vec4(%.4f, %.4f, %.4f, 1.0)", darkColor.red() / 255.0, darkColor.green() / 255.0, darkColor.blue() / 255.0));
tmp.replace("GRID", String::format("%.3f", (float)checkerSize));
CString checkerFragmentShaderString = tmp.latin1();
if (!makeContextCurrent())
return false;
m_layerProgramObject[LayerData::LayerProgramShaderRGBA] =
loadShaderProgram(vertexShaderString, fragmentShaderStringRGBA);
if (!m_layerProgramObject[LayerData::LayerProgramShaderRGBA]) {
LOG_ERROR("Failed to create shader program for RGBA layers");
return false;
}
m_layerProgramObject[LayerData::LayerProgramShaderBGRA] =
loadShaderProgram(vertexShaderString, fragmentShaderStringBGRA);
if (!m_layerProgramObject[LayerData::LayerProgramShaderBGRA]) {
LOG_ERROR("Failed to create shader program for BGRA layers");
return false;
}
m_layerMaskProgramObject[LayerData::LayerProgramShaderRGBA] =
loadShaderProgram(vertexShaderString, fragmentShaderStringMaskRGBA);
if (!m_layerMaskProgramObject[LayerData::LayerProgramShaderRGBA]) {
LOG_ERROR("Failed to create shader mask program for RGBA layers");
return false;
}
m_layerMaskProgramObject[LayerData::LayerProgramShaderBGRA] =
loadShaderProgram(vertexShaderString, fragmentShaderStringMaskBGRA);
if (!m_layerMaskProgramObject[LayerData::LayerProgramShaderBGRA]) {
LOG_ERROR("Failed to create shader mask program for BGRA layers");
return false;
}
m_colorProgramObject = loadShaderProgram(colorVertexShaderString, colorFragmentShaderString);
if (!m_colorProgramObject) {
LOG_ERROR("Failed to create shader program for debug borders");
return false;
}
m_checkerProgramObject = loadShaderProgram(colorVertexShaderString, checkerFragmentShaderString.data());
if (!m_checkerProgramObject) {
LOG_ERROR("Failed to create shader program for checkerboard pattern");
return false;
}
bindCommonAttribLocation(m_positionLocation, "a_position");
bindCommonAttribLocation(m_texCoordLocation, "a_texCoord");
checkGLError();
for (int i = 0; i < LayerData::NumberOfLayerProgramShaders; ++i) {
glLinkProgram(m_layerProgramObject[i]);
glLinkProgram(m_layerMaskProgramObject[i]);
}
glLinkProgram(m_colorProgramObject);
glLinkProgram(m_checkerProgramObject);
checkGLError();
for (int i = 0; i < LayerData::NumberOfLayerProgramShaders; ++i) {
m_samplerLocation[i] = glGetUniformLocation(m_layerProgramObject[i], "s_texture");
m_alphaLocation[i] = glGetUniformLocation(m_layerProgramObject[i], "alpha");
glUseProgram(m_layerProgramObject[i]);
glUniform1i(m_samplerLocation[i], 0);
m_maskSamplerLocation[i] = glGetUniformLocation(m_layerMaskProgramObject[i], "s_texture");
m_maskSamplerLocationMask[i] = glGetUniformLocation(m_layerMaskProgramObject[i], "s_mask");
m_maskAlphaLocation[i] = glGetUniformLocation(m_layerMaskProgramObject[i], "alpha");
glUseProgram(m_layerMaskProgramObject[i]);
glUniform1i(m_maskSamplerLocation[i], 0);
glUniform1i(m_maskSamplerLocationMask[i], 1);
}
m_colorColorLocation = glGetUniformLocation(m_colorProgramObject, "color");
m_checkerScaleLocation = glGetUniformLocation(m_checkerProgramObject, "scale");
m_checkerOriginLocation = glGetUniformLocation(m_checkerProgramObject, "origin");
m_checkerSurfaceHeightLocation = glGetUniformLocation(m_checkerProgramObject, "surfaceHeight");
return true;
}
IntRect LayerRenderingResults::holePunchRect(unsigned index) const
{
if (index >= m_holePunchRects.size())
return IntRect();
return m_holePunchRects.at(index);
}
void LayerRenderingResults::addHolePunchRect(const IntRect& rect)
{
#if DEBUG_VIDEO_CLIPPING
BlackBerry::Platform::log(BlackBerry::Platform::LogLevelInfo, "LayerRenderingResults::addHolePunchRect (x=%d,y=%d,width=%d,height=%d).", rect.x(), rect.y(), rect.width(), rect.height());
#endif
if (!rect.isEmpty())
m_holePunchRects.append(rect);
}
void LayerRenderingResults::addDirtyRect(const IntRect& rect)
{
IntRect dirtyUnion[NumberOfDirtyRects];
int smallestIncrease = INT_MAX;
int modifiedRect = 0;
for (int i = 0; i < NumberOfDirtyRects; ++i) {
dirtyUnion[i] = m_dirtyRects[i];
dirtyUnion[i].unite(rect);
int increase = dirtyUnion[i].width()*dirtyUnion[i].height() - m_dirtyRects[i].width()*m_dirtyRects[i].height();
if (increase < smallestIncrease) {
smallestIncrease = increase;
modifiedRect = i;
}
}
m_dirtyRects[modifiedRect] = dirtyUnion[modifiedRect];
}
bool LayerRenderingResults::isEmpty() const
{
for (int i = 0; i < NumberOfDirtyRects; ++i) {
if (!m_dirtyRects[i].isEmpty())
return false;
}
return true;
}
}
#endif // USE(ACCELERATED_COMPOSITING)