/* * Copyright 2010, The Android Open Source Project * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #define LOG_TAG "ShaderProgram" #define LOG_NDEBUG 1 #include "config.h" #include "ShaderProgram.h" #if USE(ACCELERATED_COMPOSITING) #include "AndroidLog.h" #include "DrawQuadData.h" #include "FloatPoint3D.h" #include "GLUtils.h" #include "TilesManager.h" #include #include #define EPSILON 0.00001f namespace WebCore { // fillPortion.xy = starting UV coordinates. // fillPortion.zw = UV coordinates width and height. static const char gVertexShader[] = "attribute vec4 vPosition;\n" "uniform mat4 projectionMatrix;\n" "uniform vec4 fillPortion;\n" "varying vec2 v_texCoord;\n" "void main() {\n" " gl_Position = projectionMatrix * vPosition;\n" " v_texCoord = vPosition.xy * fillPortion.zw + fillPortion.xy;\n" "}\n"; static const char gRepeatTexFragmentShader[] = "precision mediump float;\n" "varying vec2 v_texCoord; \n" "uniform float alpha; \n" "uniform sampler2D s_texture; \n" "uniform vec2 repeatScale;\n" "void main() {\n" " vec2 repeatedTexCoord; " " repeatedTexCoord.x = v_texCoord.x - floor(v_texCoord.x); " " repeatedTexCoord.y = v_texCoord.y - floor(v_texCoord.y); " " repeatedTexCoord.x = repeatedTexCoord.x * repeatScale.x; " " repeatedTexCoord.y = repeatedTexCoord.y * repeatScale.y; " " gl_FragColor = texture2D(s_texture, repeatedTexCoord); \n" " gl_FragColor *= alpha; " "}\n"; static const char gRepeatTexFragmentShaderInverted[] = "precision mediump float;\n" "varying vec2 v_texCoord; \n" "uniform float alpha; \n" "uniform float contrast; \n" "uniform sampler2D s_texture; \n" "uniform vec2 repeatScale;\n" "void main() {\n" " vec2 repeatedTexCoord; " " repeatedTexCoord.x = v_texCoord.x - floor(v_texCoord.x); " " repeatedTexCoord.y = v_texCoord.y - floor(v_texCoord.y); " " repeatedTexCoord.x = repeatedTexCoord.x * repeatScale.x; " " repeatedTexCoord.y = repeatedTexCoord.y * repeatScale.y; " " vec4 pixel = texture2D(s_texture, repeatedTexCoord); \n" " float a = pixel.a; \n" " float color = a - (0.2989 * pixel.r + 0.5866 * pixel.g + 0.1145 * pixel.b);\n" " color = ((color - a/2.0) * contrast) + a/2.0; \n" " pixel.rgb = vec3(color, color, color); \n " " gl_FragColor = pixel; \n" " gl_FragColor *= alpha; " "}\n"; static const char gFragmentShader[] = "precision mediump float;\n" "varying vec2 v_texCoord; \n" "uniform float alpha; \n" "uniform sampler2D s_texture; \n" "void main() {\n" " gl_FragColor = texture2D(s_texture, v_texCoord); \n" " gl_FragColor *= alpha; " "}\n"; // We could pass the pureColor into either Vertex or Frag Shader. // The reason we passed the color into the Vertex Shader is that some driver // might create redundant copy when uniforms in fragment shader changed. static const char gPureColorVertexShader[] = "attribute vec4 vPosition;\n" "uniform mat4 projectionMatrix;\n" "uniform vec4 inputColor;\n" "varying vec4 v_color;\n" "void main() {\n" " gl_Position = projectionMatrix * vPosition;\n" " v_color = inputColor;\n" "}\n"; static const char gPureColorFragmentShader[] = "precision mediump float;\n" "varying vec4 v_color;\n" "void main() {\n" " gl_FragColor = v_color;\n" "}\n"; static const char gFragmentShaderInverted[] = "precision mediump float;\n" "varying vec2 v_texCoord; \n" "uniform float alpha; \n" "uniform float contrast; \n" "uniform sampler2D s_texture; \n" "void main() {\n" " vec4 pixel = texture2D(s_texture, v_texCoord); \n" " float a = pixel.a; \n" " float color = a - (0.2989 * pixel.r + 0.5866 * pixel.g + 0.1145 * pixel.b);\n" " color = ((color - a/2.0) * contrast) + a/2.0; \n" " pixel.rgb = vec3(color, color, color); \n " " gl_FragColor = pixel; \n" " gl_FragColor *= alpha; \n" "}\n"; static const char gVideoVertexShader[] = "attribute vec4 vPosition;\n" "uniform mat4 textureMatrix;\n" "uniform mat4 projectionMatrix;\n" "varying vec2 v_texCoord;\n" "void main() {\n" " gl_Position = projectionMatrix * vPosition;\n" " v_texCoord = vec2(textureMatrix * vec4(vPosition.x, 1.0 - vPosition.y, 0.0, 1.0));\n" "}\n"; static const char gVideoFragmentShader[] = "#extension GL_OES_EGL_image_external : require\n" "precision mediump float;\n" "uniform samplerExternalOES s_yuvTexture;\n" "varying vec2 v_texCoord;\n" "void main() {\n" " gl_FragColor = texture2D(s_yuvTexture, v_texCoord);\n" "}\n"; static const char gSurfaceTextureOESFragmentShader[] = "#extension GL_OES_EGL_image_external : require\n" "precision mediump float;\n" "varying vec2 v_texCoord; \n" "uniform float alpha; \n" "uniform samplerExternalOES s_texture; \n" "void main() {\n" " gl_FragColor = texture2D(s_texture, v_texCoord); \n" " gl_FragColor *= alpha; " "}\n"; static const char gSurfaceTextureOESFragmentShaderInverted[] = "#extension GL_OES_EGL_image_external : require\n" "precision mediump float;\n" "varying vec2 v_texCoord; \n" "uniform float alpha; \n" "uniform float contrast; \n" "uniform samplerExternalOES s_texture; \n" "void main() {\n" " vec4 pixel = texture2D(s_texture, v_texCoord); \n" " float a = pixel.a; \n" " float color = a - (0.2989 * pixel.r + 0.5866 * pixel.g + 0.1145 * pixel.b);\n" " color = ((color - a/2.0) * contrast) + a/2.0; \n" " pixel.rgb = vec3(color, color, color); \n " " gl_FragColor = pixel; \n" " gl_FragColor *= alpha; \n" "}\n"; GLuint ShaderProgram::loadShader(GLenum shaderType, const char* pSource) { GLuint shader = glCreateShader(shaderType); if (shader) { glShaderSource(shader, 1, &pSource, 0); glCompileShader(shader); GLint compiled = 0; glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); if (!compiled) { GLint infoLen = 0; glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen); if (infoLen) { char* buf = (char*) malloc(infoLen); if (buf) { glGetShaderInfoLog(shader, infoLen, 0, buf); ALOGE("could not compile shader %d:\n%s\n", shaderType, buf); free(buf); } glDeleteShader(shader); shader = 0; } } } return shader; } GLint ShaderProgram::createProgram(const char* pVertexSource, const char* pFragmentSource) { GLuint vertexShader = loadShader(GL_VERTEX_SHADER, pVertexSource); if (!vertexShader) { ALOGE("couldn't load the vertex shader!"); return -1; } GLuint pixelShader = loadShader(GL_FRAGMENT_SHADER, pFragmentSource); if (!pixelShader) { ALOGE("couldn't load the pixel shader!"); return -1; } GLuint program = glCreateProgram(); if (program) { glAttachShader(program, vertexShader); GLUtils::checkGlError("glAttachShader vertex"); glAttachShader(program, pixelShader); GLUtils::checkGlError("glAttachShader pixel"); glLinkProgram(program); GLint linkStatus = GL_FALSE; glGetProgramiv(program, GL_LINK_STATUS, &linkStatus); if (linkStatus != GL_TRUE) { GLint bufLength = 0; glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength); if (bufLength) { char* buf = (char*) malloc(bufLength); if (buf) { glGetProgramInfoLog(program, bufLength, 0, buf); ALOGE("could not link program:\n%s\n", buf); free(buf); } } glDeleteProgram(program); program = -1; } } ShaderResource newResource(program, vertexShader, pixelShader); m_resources.append(newResource); return program; } void ShaderProgram::initProgram(ShaderType type) { // initialize shader's static texSampler and position values glUseProgram(m_handleArray[type].programHandle); if (m_handleArray[type].texSamplerHandle != -1) glUniform1i(m_handleArray[type].texSamplerHandle, 0); glEnableVertexAttribArray(m_handleArray[type].positionHandle); } ShaderProgram::ShaderProgram() : m_blendingEnabled(false) , m_contrast(1) , m_alphaLayer(false) , m_currentScale(1.0f) , m_needsInit(true) { // initialize the matrix to calculate z values correctly, since it can be // used for that before setupDrawing is called. GLUtils::setOrthographicMatrix(m_visibleContentRectProjectionMatrix, 0,0,1,1, -1000, 1000); } void ShaderProgram::cleanupGLResources() { for (unsigned int i = 0; i < m_resources.size(); i++) { glDetachShader(m_resources[i].program, m_resources[i].vertexShader); glDetachShader(m_resources[i].program, m_resources[i].fragmentShader); glDeleteShader(m_resources[i].vertexShader); glDeleteShader(m_resources[i].fragmentShader); glDeleteProgram(m_resources[i].program); } glDeleteBuffers(1, m_textureBuffer); m_resources.clear(); m_needsInit = true; GLUtils::checkGlError("cleanupGLResources"); return; } void ShaderProgram::initGLResources() { // To detect whether or not resources for ShaderProgram allocated // successfully, we clean up pre-existing errors here and will check for // new errors at the end of this function. GLUtils::checkGlError("before initGLResources"); GLint tex2DProgram = createProgram(gVertexShader, gFragmentShader); GLint pureColorProgram = createProgram(gPureColorVertexShader, gPureColorFragmentShader); GLint tex2DInvProgram = createProgram(gVertexShader, gFragmentShaderInverted); GLint videoProgram = createProgram(gVideoVertexShader, gVideoFragmentShader); GLint texOESProgram = createProgram(gVertexShader, gSurfaceTextureOESFragmentShader); GLint texOESInvProgram = createProgram(gVertexShader, gSurfaceTextureOESFragmentShaderInverted); GLint repeatTexProgram = createProgram(gVertexShader, gRepeatTexFragmentShader); GLint repeatTexInvProgram = createProgram(gVertexShader, gRepeatTexFragmentShaderInverted); if (tex2DProgram == -1 || pureColorProgram == -1 || tex2DInvProgram == -1 || videoProgram == -1 || texOESProgram == -1 || texOESInvProgram == -1 || repeatTexProgram == -1 || repeatTexInvProgram == -1) { m_needsInit = true; return; } GLint pureColorPosition = glGetAttribLocation(pureColorProgram, "vPosition"); GLint pureColorProjMtx = glGetUniformLocation(pureColorProgram, "projectionMatrix"); GLint pureColorValue = glGetUniformLocation(pureColorProgram, "inputColor"); m_handleArray[PureColor].init(-1, -1, pureColorPosition, pureColorProgram, pureColorProjMtx, pureColorValue, -1, -1, -1, -1); initProgram(PureColor); GLint tex2DAlpha = glGetUniformLocation(tex2DProgram, "alpha"); GLint tex2DPosition = glGetAttribLocation(tex2DProgram, "vPosition"); GLint tex2DProjMtx = glGetUniformLocation(tex2DProgram, "projectionMatrix"); GLint tex2DTexSampler = glGetUniformLocation(tex2DProgram, "s_texture"); GLint tex2DFillPortion = glGetUniformLocation(tex2DProgram, "fillPortion"); m_handleArray[Tex2D].init(tex2DAlpha, -1, tex2DPosition, tex2DProgram, tex2DProjMtx, -1, tex2DTexSampler, -1, tex2DFillPortion, -1); initProgram(Tex2D); GLint tex2DInvAlpha = glGetUniformLocation(tex2DInvProgram, "alpha"); GLint tex2DInvContrast = glGetUniformLocation(tex2DInvProgram, "contrast"); GLint tex2DInvPosition = glGetAttribLocation(tex2DInvProgram, "vPosition"); GLint tex2DInvProjMtx = glGetUniformLocation(tex2DInvProgram, "projectionMatrix"); GLint tex2DInvTexSampler = glGetUniformLocation(tex2DInvProgram, "s_texture"); GLint tex2DInvFillPortion = glGetUniformLocation(tex2DInvProgram, "fillPortion"); m_handleArray[Tex2DInv].init(tex2DInvAlpha, tex2DInvContrast, tex2DInvPosition, tex2DInvProgram, tex2DInvProjMtx, -1, tex2DInvTexSampler, -1, tex2DInvFillPortion, -1); initProgram(Tex2DInv); GLint repeatTexAlpha = glGetUniformLocation(repeatTexProgram, "alpha"); GLint repeatTexPosition = glGetAttribLocation(repeatTexProgram, "vPosition"); GLint repeatTexProjMtx = glGetUniformLocation(repeatTexProgram, "projectionMatrix"); GLint repeatTexTexSampler = glGetUniformLocation(repeatTexProgram, "s_texture"); GLint repeatTexFillPortion = glGetUniformLocation(repeatTexProgram, "fillPortion"); GLint repeatTexScale = glGetUniformLocation(repeatTexProgram, "repeatScale"); m_handleArray[RepeatTex].init(repeatTexAlpha, -1, repeatTexPosition, repeatTexProgram,repeatTexProjMtx, -1, repeatTexTexSampler, -1, repeatTexFillPortion, repeatTexScale); initProgram(RepeatTex); GLint repeatTexInvAlpha = glGetUniformLocation(repeatTexInvProgram, "alpha"); GLint repeatTexInvContrast = glGetUniformLocation(tex2DInvProgram, "contrast"); GLint repeatTexInvPosition = glGetAttribLocation(repeatTexInvProgram, "vPosition"); GLint repeatTexInvProjMtx = glGetUniformLocation(repeatTexInvProgram, "projectionMatrix"); GLint repeatTexInvTexSampler = glGetUniformLocation(repeatTexInvProgram, "s_texture"); GLint repeatTexInvFillPortion = glGetUniformLocation(repeatTexInvProgram, "fillPortion"); GLint repeatTexInvScale = glGetUniformLocation(repeatTexInvProgram, "repeatScale"); m_handleArray[RepeatTexInv].init(repeatTexInvAlpha, repeatTexInvContrast, repeatTexInvPosition, repeatTexInvProgram, repeatTexInvProjMtx, -1, repeatTexInvTexSampler, -1, repeatTexInvFillPortion, repeatTexInvScale); initProgram(RepeatTexInv); GLint texOESAlpha = glGetUniformLocation(texOESProgram, "alpha"); GLint texOESPosition = glGetAttribLocation(texOESProgram, "vPosition"); GLint texOESProjMtx = glGetUniformLocation(texOESProgram, "projectionMatrix"); GLint texOESTexSampler = glGetUniformLocation(texOESProgram, "s_texture"); GLint texOESFillPortion = glGetUniformLocation(texOESProgram, "fillPortion"); m_handleArray[TexOES].init(texOESAlpha, -1, texOESPosition, texOESProgram, texOESProjMtx, -1, texOESTexSampler, -1, texOESFillPortion, -1); initProgram(TexOES); GLint texOESInvAlpha = glGetUniformLocation(texOESInvProgram, "alpha"); GLint texOESInvContrast = glGetUniformLocation(texOESInvProgram, "contrast"); GLint texOESInvPosition = glGetAttribLocation(texOESInvProgram, "vPosition"); GLint texOESInvProjMtx = glGetUniformLocation(texOESInvProgram, "projectionMatrix"); GLint texOESInvTexSampler = glGetUniformLocation(texOESInvProgram, "s_texture"); GLint texOESInvFillPortion = glGetUniformLocation(texOESInvProgram, "fillPortion"); m_handleArray[TexOESInv].init(texOESInvAlpha, texOESInvContrast, texOESInvPosition, texOESInvProgram, texOESInvProjMtx, -1, texOESInvTexSampler, -1, texOESInvFillPortion, -1); initProgram(TexOESInv); GLint videoPosition = glGetAttribLocation(videoProgram, "vPosition"); GLint videoProjMtx = glGetUniformLocation(videoProgram, "projectionMatrix"); GLint videoTexSampler = glGetUniformLocation(videoProgram, "s_yuvTexture"); GLint videoTexMtx = glGetUniformLocation(videoProgram, "textureMatrix"); m_handleArray[Video].init(-1, -1, videoPosition, videoProgram, videoProjMtx, -1, videoTexSampler, videoTexMtx, -1, -1); initProgram(Video); const GLfloat coord[] = { 0.0f, 0.0f, // C 1.0f, 0.0f, // D 0.0f, 1.0f, // A 1.0f, 1.0f // B }; glGenBuffers(1, m_textureBuffer); glBindBuffer(GL_ARRAY_BUFFER, m_textureBuffer[0]); glBufferData(GL_ARRAY_BUFFER, 2 * 4 * sizeof(GLfloat), coord, GL_STATIC_DRAW); TransformationMatrix matrix; // Map x,y from (0,1) to (-1, 1) matrix.scale3d(2, 2, 1); matrix.translate3d(-0.5, -0.5, 0); GLUtils::toGLMatrix(m_transferProjMtx, matrix); m_needsInit = GLUtils::checkGlError("initGLResources"); return; } void ShaderProgram::resetBlending() { glDisable(GL_BLEND); glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); glBlendEquation(GL_FUNC_ADD); m_blendingEnabled = false; } void ShaderProgram::setBlendingState(bool enableBlending) { if (enableBlending == m_blendingEnabled) return; if (enableBlending) glEnable(GL_BLEND); else glDisable(GL_BLEND); m_blendingEnabled = enableBlending; } ///////////////////////////////////////////////////////////////////////////////////////// // Drawing ///////////////////////////////////////////////////////////////////////////////////////// // We have multiple coordinates to deal with: first is the screen coordinates, // second is the view coordinates and the last one is content(document) coordinates. // Both screen and view coordinates are in pixels. // All these coordinates start from upper left, but for the purpose of OpenGL // operations, we may need a inverted Y version of such coordinates which // start from lower left. // // invScreenRect - inv screen coordinates starting from lower left. // visibleContentRect - local content(document) coordinates starting from upper left. // screenRect - screen coordinates starting from upper left. // screenClip - screen coordinates starting from upper left. // ------------------------------------------ // |(origin of screen) | // |screen | // | --------------------------------- | // | | (origin of view) | | // | | webview | | // | | -------- | | // | | | clip | | | // | | | | | | // | | -------- | | // | | | | // | |(origin of inv view) | | // | --------------------------------- | // |(origin of inv screen) | // ------------------------------------------ void ShaderProgram::setupDrawing(const IntRect& invScreenRect, const SkRect& visibleContentRect, const IntRect& screenRect, int titleBarHeight, const IntRect& screenClip, float scale) { m_screenRect = screenRect; m_titleBarHeight = titleBarHeight; //// viewport //// GLUtils::setOrthographicMatrix(m_visibleContentRectProjectionMatrix, visibleContentRect.fLeft, visibleContentRect.fTop, visibleContentRect.fRight, visibleContentRect.fBottom, -1000, 1000); ALOGV("set m_clipProjectionMatrix, %d, %d, %d, %d", screenClip.x(), screenClip.y(), screenClip.x() + screenClip.width(), screenClip.y() + screenClip.height()); // In order to incorporate the animation delta X and Y, using the clip as // the GL viewport can save all the trouble of re-position from screenRect // to final position. GLUtils::setOrthographicMatrix(m_clipProjectionMatrix, screenClip.x(), screenClip.y(), screenClip.x() + screenClip.width(), screenClip.y() + screenClip.height(), -1000, 1000); glViewport(screenClip.x(), m_targetHeight - screenClip.y() - screenClip.height() , screenClip.width(), screenClip.height()); m_visibleContentRect = visibleContentRect; m_currentScale = scale; //// viewRect //// m_invScreenRect = invScreenRect; // The following matrices transform content coordinates into view coordinates // and inv view coordinates. // Note that GLUtils::setOrthographicMatrix is inverting the Y. TransformationMatrix viewTranslate; viewTranslate.translate(1.0, 1.0); TransformationMatrix viewScale; viewScale.scale3d(m_invScreenRect.width() * 0.5f, m_invScreenRect.height() * 0.5f, 1); m_contentToInvViewMatrix = viewScale * viewTranslate * m_visibleContentRectProjectionMatrix; viewTranslate.scale3d(1, -1, 1); m_contentToViewMatrix = viewScale * viewTranslate * m_visibleContentRectProjectionMatrix; IntRect invViewRect(0, 0, m_screenRect.width(), m_screenRect.height()); m_contentViewport = m_contentToInvViewMatrix.inverse().mapRect(invViewRect); //// clipping //// IntRect viewClip = screenClip; // The incoming screenClip is in screen coordinates, we first // translate it into view coordinates. // Then we convert it into inverted view coordinates. // Therefore, in the clip() function, we need to convert things back from // inverted view coordinates to inverted screen coordinates which is used by GL. viewClip.setX(screenClip.x() - m_screenRect.x()); viewClip.setY(screenClip.y() - m_screenRect.y() - m_titleBarHeight); FloatRect invViewClip = convertViewCoordToInvViewCoord(viewClip); m_invViewClip.setLocation(IntPoint(invViewClip.x(), invViewClip.y())); // use ceilf to handle view -> doc -> view coord rounding errors m_invViewClip.setSize(IntSize(ceilf(invViewClip.width()), ceilf(invViewClip.height()))); resetBlending(); // Set up m_clipProjectionMatrix, m_currentScale and m_webViewMatrix before // calling this function. setupSurfaceProjectionMatrix(); //// initialize frame-constant values //// glActiveTexture(GL_TEXTURE0); glBindBuffer(GL_ARRAY_BUFFER, m_textureBuffer[0]); //// initialize GL cache //// m_cachedProgramType = UndefinedShader; m_cachedOpacity = -1; m_cachedFillPortion = FloatRect(); m_cachedPureColor = Color(); } // Calculate the right color value sent into the shader considering the (0,1) // clamp and alpha blending. Color ShaderProgram::shaderColor(Color pureColor, float opacity) { float r = pureColor.red() / 255.0; float g = pureColor.green() / 255.0; float b = pureColor.blue() / 255.0; float a = pureColor.alpha() / 255.0; if (TilesManager::instance()->invertedScreen()) { float intensity = a - (0.2989 * r + 0.5866 * g + 0.1145 * b); intensity = ((intensity - a / 2.0) * m_contrast) + a / 2.0; intensity *= opacity; return Color(intensity, intensity, intensity, a * opacity); } return Color(r * opacity, g * opacity, b * opacity, a * opacity); } // For shaders using texture, it is easy to get the type from the textureTarget. ShaderType ShaderProgram::getTextureShaderType(GLenum textureTarget, bool hasRepeatScale) { ShaderType type = UndefinedShader; if (textureTarget == GL_TEXTURE_2D) { if (!TilesManager::instance()->invertedScreen()) type = hasRepeatScale ? RepeatTex : Tex2D; else { // With the new GPU texture upload path, we do not use an FBO // to blit the texture we receive from the TexturesGenerator thread. // To implement inverted rendering, we thus have to do the rendering // live, by using a different shader. type = hasRepeatScale ? RepeatTexInv : Tex2DInv; } } else if (textureTarget == GL_TEXTURE_EXTERNAL_OES) { if (!TilesManager::instance()->invertedScreen()) type = TexOES; else type = TexOESInv; } return type; } // This function transform a clip rect extracted from the current layer // into a clip rect in InvView coordinates -- used by the clipping rects FloatRect ShaderProgram::rectInInvViewCoord(const TransformationMatrix& drawMatrix, const IntSize& size) { FloatRect srect(0, 0, size.width(), size.height()); TransformationMatrix renderMatrix = m_contentToInvViewMatrix * drawMatrix; return renderMatrix.mapRect(srect); } // used by the partial screen invals FloatRect ShaderProgram::rectInViewCoord(const TransformationMatrix& drawMatrix, const IntSize& size) { FloatRect srect(0, 0, size.width(), size.height()); TransformationMatrix renderMatrix = m_contentToViewMatrix * drawMatrix; return renderMatrix.mapRect(srect); } FloatRect ShaderProgram::rectInViewCoord(const FloatRect& rect) { return m_contentToViewMatrix.mapRect(rect); } FloatRect ShaderProgram::rectInInvViewCoord(const FloatRect& rect) { return m_contentToInvViewMatrix.mapRect(rect); } FloatRect ShaderProgram::convertInvViewCoordToContentCoord(const FloatRect& rect) { return m_contentToInvViewMatrix.inverse().mapRect(rect); } FloatRect ShaderProgram::convertViewCoordToInvViewCoord(const FloatRect& rect) { FloatRect visibleContentRect = m_contentToViewMatrix.inverse().mapRect(rect); return rectInInvViewCoord(visibleContentRect); } FloatRect ShaderProgram::convertInvViewCoordToViewCoord(const FloatRect& rect) { FloatRect visibleContentRect = m_contentToInvViewMatrix.inverse().mapRect(rect); return rectInViewCoord(visibleContentRect); } // clip is in screen coordinates void ShaderProgram::clip(const FloatRect& clip) { if (clip == m_clipRect) return; ALOGV("--clipping rect %f %f, %f x %f", clip.x(), clip.y(), clip.width(), clip.height()); // we should only call glScissor in this function, so that we can easily // track the current clipping rect. IntRect screenClip(clip.x(), clip.y(), clip.width(), clip.height()); if (!m_invViewClip.isEmpty()) screenClip.intersect(m_invViewClip); // The previous intersection calculation is using local screen coordinates. // Now we need to convert things from local screen coordinates to global // screen coordinates and pass to the GL functions. screenClip.setX(screenClip.x() + m_invScreenRect.x()); screenClip.setY(screenClip.y() + m_invScreenRect.y()); if (screenClip.x() < 0) { int w = screenClip.width(); w += screenClip.x(); screenClip.setX(0); screenClip.setWidth(w); } if (screenClip.y() < 0) { int h = screenClip.height(); h += screenClip.y(); screenClip.setY(0); screenClip.setHeight(h); } glScissor(screenClip.x(), screenClip.y(), screenClip.width(), screenClip.height()); m_clipRect = clip; } IntRect ShaderProgram::clippedRectWithVisibleContentRect(const IntRect& rect, int margin) { IntRect viewport(m_visibleContentRect.fLeft - margin, m_visibleContentRect.fTop - margin, m_visibleContentRect.width() + margin, m_visibleContentRect.height() + margin); viewport.intersect(rect); return viewport; } float ShaderProgram::zValue(const TransformationMatrix& drawMatrix, float w, float h) { TransformationMatrix modifiedDrawMatrix = drawMatrix; modifiedDrawMatrix.scale3d(w, h, 1); TransformationMatrix renderMatrix = m_visibleContentRectProjectionMatrix * modifiedDrawMatrix; FloatPoint3D point(0.5, 0.5, 0.0); FloatPoint3D result = renderMatrix.mapPoint(point); return result.z(); } void ShaderProgram::drawQuadInternal(ShaderType type, const GLfloat* matrix, int textureId, float opacity, GLenum textureTarget, GLenum filter, const Color& pureColor, const FloatRect& fillPortion, const FloatSize& repeatScale) { if (m_cachedProgramType != type) { glUseProgram(m_handleArray[type].programHandle); glVertexAttribPointer(m_handleArray[type].positionHandle, 2, GL_FLOAT, GL_FALSE, 0, 0); m_cachedProgramType = type; m_cachedOpacity = -1; // reset cache for variable shared by multiple programs } glUniformMatrix4fv(m_handleArray[type].projMtxHandle, 1, GL_FALSE, matrix); if (type != PureColor) { glBindTexture(textureTarget, textureId); glTexParameteri(textureTarget, GL_TEXTURE_MIN_FILTER, filter); glTexParameteri(textureTarget, GL_TEXTURE_MAG_FILTER, filter); if (m_cachedOpacity != opacity) { glUniform1f(m_handleArray[type].alphaHandle, opacity); m_cachedOpacity = opacity; } GLint contrastHandle = m_handleArray[type].contrastHandle; if (contrastHandle != -1) glUniform1f(contrastHandle, m_contrast); if (m_cachedFillPortion != fillPortion) { glUniform4f(m_handleArray[type].fillPortionHandle, fillPortion.x(), fillPortion.y(), fillPortion.width(), fillPortion.height()); m_cachedFillPortion = fillPortion; } // Only when we have repeat scale, this handle can be >= 0; if (m_handleArray[type].scaleHandle != -1) { glUniform2f(m_handleArray[type].scaleHandle, repeatScale.width(), repeatScale.height()); } } else { if (m_cachedPureColor != pureColor) { glUniform4f(m_handleArray[type].pureColorHandle, pureColor.red() / 255.0, pureColor.green() / 255.0, pureColor.blue() / 255.0, pureColor.alpha() / 255.0); m_cachedPureColor = pureColor; } } glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); } // Put the common matrix computation at higher level to avoid redundancy. void ShaderProgram::setupSurfaceProjectionMatrix() { TransformationMatrix scaleMatrix; scaleMatrix.scale3d(m_currentScale, m_currentScale, 1); m_surfaceProjectionMatrix = m_clipProjectionMatrix * m_webViewMatrix * scaleMatrix; } // Calculate the matrix given the geometry. GLfloat* ShaderProgram::getTileProjectionMatrix(const DrawQuadData* data) { DrawQuadType type = data->type(); if (type == Blit) return m_transferProjMtx; const TransformationMatrix* matrix = data->drawMatrix(); const SkRect* geometry = data->geometry(); FloatRect fillPortion = data->fillPortion(); ALOGV("fillPortion " FLOAT_RECT_FORMAT, FLOAT_RECT_ARGS(fillPortion)); // This modifiedDrawMatrix tranform (0,0)(1x1) to the final rect in screen // coordinates, before applying the m_webViewMatrix. // It first scale and translate the vertex array from (0,0)(1x1) to real // tile position and size. Then apply the transform from the layer's. // Finally scale to the currentScale to support zooming. // Note the geometry contains the tile zoom scale, so visually we will see // the tiles scale at a ratio as (m_currentScale/tile's scale). TransformationMatrix modifiedDrawMatrix; if (type == LayerQuad) modifiedDrawMatrix = *matrix; modifiedDrawMatrix.translate(geometry->fLeft + geometry->width() * fillPortion.x(), geometry->fTop + geometry->height() * fillPortion.y()); modifiedDrawMatrix.scale3d(geometry->width() * fillPortion.width(), geometry->height() * fillPortion.height(), 1); // Even when we are on a alpha layer or not, we need to respect the // m_webViewMatrix, it may contain the layout offset. Normally it is // identity. TransformationMatrix renderMatrix; renderMatrix = m_surfaceProjectionMatrix * modifiedDrawMatrix; #if DEBUG_MATRIX debugMatrixInfo(m_currentScale, m_clipProjectionMatrix, m_webViewMatrix, modifiedDrawMatrix, matrix); #endif GLUtils::toGLMatrix(m_tileProjMatrix, renderMatrix); return m_tileProjMatrix; } void ShaderProgram::drawQuad(const DrawQuadData* data) { GLfloat* matrix = getTileProjectionMatrix(data); float opacity = data->opacity(); bool forceBlending = data->forceBlending(); bool enableBlending = forceBlending || opacity < 1.0; ShaderType shaderType = UndefinedShader; int textureId = 0; GLint textureFilter = 0; GLenum textureTarget = 0; Color quadColor = data->quadColor(); if (data->pureColor()) { shaderType = PureColor; quadColor = shaderColor(quadColor, opacity); enableBlending = enableBlending || quadColor.hasAlpha(); if (!quadColor.alpha() && enableBlending) return; } else { textureId = data->textureId(); textureFilter = data->textureFilter(); textureTarget = data->textureTarget(); shaderType = getTextureShaderType(textureTarget, data->hasRepeatScale()); } setBlendingState(enableBlending); drawQuadInternal(shaderType, matrix, textureId, opacity, textureTarget, textureFilter, quadColor, data->fillPortion(), data->repeatScale()); } void ShaderProgram::drawVideoLayerQuad(const TransformationMatrix& drawMatrix, float* textureMatrix, SkRect& geometry, int textureId) { // switch to our custom yuv video rendering program if (m_cachedProgramType != Video) { glUseProgram(m_handleArray[Video].programHandle); glVertexAttribPointer(m_handleArray[Video].positionHandle, 2, GL_FLOAT, GL_FALSE, 0, 0); m_cachedProgramType = Video; } // TODO: Merge drawVideoLayerQuad into drawQuad. TransformationMatrix modifiedDrawMatrix; modifiedDrawMatrix.scale3d(m_currentScale, m_currentScale, 1); modifiedDrawMatrix.multiply(drawMatrix); modifiedDrawMatrix.translate(geometry.fLeft, geometry.fTop); modifiedDrawMatrix.scale3d(geometry.width(), geometry.height(), 1); TransformationMatrix renderMatrix = m_clipProjectionMatrix * m_webViewMatrix * modifiedDrawMatrix; GLfloat projectionMatrix[16]; GLUtils::toGLMatrix(projectionMatrix, renderMatrix); glUniformMatrix4fv(m_handleArray[Video].projMtxHandle, 1, GL_FALSE, projectionMatrix); glUniformMatrix4fv(m_handleArray[Video].videoMtxHandle, 1, GL_FALSE, textureMatrix); glBindTexture(GL_TEXTURE_EXTERNAL_OES, textureId); setBlendingState(false); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); } void ShaderProgram::setGLDrawInfo(const android::uirenderer::DrawGlInfo* info) { GLUtils::convertToTransformationMatrix(info->transform, m_webViewMatrix); m_alphaLayer = info->isLayer; m_targetHeight = info->height; } // This function is called per tileGrid to minimize the computation overhead. // The ortho projection and glViewport will map 1:1, so we don't need to // worry about them here. Basically, if the current zoom scale / tile's scale // plus the webview and layer transformation ends up at scale factor 1.0, // then we can use point sampling. bool ShaderProgram::usePointSampling(float tileScale, const TransformationMatrix* layerTransform) { const float testSize = 1.0; FloatRect rect(0, 0, testSize, testSize); TransformationMatrix matrix; matrix.scale3d(m_currentScale, m_currentScale, 1); if (layerTransform) matrix.multiply(*layerTransform); matrix.scale3d(1.0 / tileScale, 1.0 / tileScale, 1); matrix = m_webViewMatrix * matrix; rect = matrix.mapRect(rect); float deltaWidth = abs(rect.width() - testSize); float deltaHeight = abs(rect.height() - testSize); if (deltaWidth < EPSILON && deltaHeight < EPSILON) { ALOGV("Point sampling : deltaWidth is %f, deltaHeight is %f", deltaWidth, deltaHeight); return true; } return false; } #if DEBUG_MATRIX FloatRect ShaderProgram::debugMatrixTransform(const TransformationMatrix& matrix, const char* matrixName) { FloatRect rect(0.0, 0.0, 1.0, 1.0); rect = matrix.mapRect(rect); ALOGV("After %s matrix:\n %f, %f rect.width() %f rect.height() %f", matrixName, rect.x(), rect.y(), rect.width(), rect.height()); return rect; } void ShaderProgram::debugMatrixInfo(float currentScale, const TransformationMatrix& clipProjectionMatrix, const TransformationMatrix& webViewMatrix, const TransformationMatrix& modifiedDrawMatrix, const TransformationMatrix* layerMatrix) { int viewport[4]; glGetIntegerv(GL_VIEWPORT, viewport); ALOGV("viewport %d, %d, %d, %d , currentScale %f", viewport[0], viewport[1], viewport[2], viewport[3], currentScale); IntRect currentGLViewport(viewport[0], viewport[1], viewport[2], viewport[3]); TransformationMatrix scaleMatrix; scaleMatrix.scale3d(currentScale, currentScale, 1.0); if (layerMatrix) debugMatrixTransform(*layerMatrix, "layerMatrix"); TransformationMatrix debugMatrix = scaleMatrix * modifiedDrawMatrix; debugMatrixTransform(debugMatrix, "scaleMatrix * modifiedDrawMatrix"); debugMatrix = webViewMatrix * debugMatrix; debugMatrixTransform(debugMatrix, "webViewMatrix * scaleMatrix * modifiedDrawMatrix"); debugMatrix = clipProjectionMatrix * debugMatrix; FloatRect finalRect = debugMatrixTransform(debugMatrix, "all Matrix"); // After projection, we will be in a (-1, 1) range and now we can map it back // to the (x,y) -> (x+width, y+height) ALOGV("final convert to screen coord x, y %f, %f width %f height %f , ", (finalRect.x() + 1) / 2 * currentGLViewport.width() + currentGLViewport.x(), (finalRect.y() + 1) / 2 * currentGLViewport.height() + currentGLViewport.y(), finalRect.width() * currentGLViewport.width() / 2, finalRect.height() * currentGLViewport.height() / 2); } #endif // DEBUG_MATRIX } // namespace WebCore #endif // USE(ACCELERATED_COMPOSITING)