2 * Copyright 2010, The Android Open Source Project
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 #include "ShaderProgram.h"
29 #if USE(ACCELERATED_COMPOSITING)
31 #include "FloatPoint3D.h"
34 #include <GLES2/gl2.h>
35 #include <GLES2/gl2ext.h>
36 #include <cutils/log.h>
37 #include <wtf/CurrentTime.h>
38 #include <wtf/text/CString.h>
41 #define XLOG(...) android_printLog(ANDROID_LOG_DEBUG, "ShaderProgram", __VA_ARGS__)
45 static const char gVertexShader[] =
46 "attribute vec4 vPosition;\n"
47 "uniform mat4 projectionMatrix;\n"
48 "varying vec2 v_texCoord;\n"
50 " gl_Position = projectionMatrix * vPosition;\n"
51 " v_texCoord = vec2(vPosition);\n"
54 static const char gFragmentShader[] =
55 "precision mediump float;\n"
56 "varying vec2 v_texCoord; \n"
57 "uniform float alpha; \n"
58 "uniform sampler2D s_texture; \n"
60 " gl_FragColor = texture2D(s_texture, v_texCoord); \n"
61 " gl_FragColor *= alpha; "
64 static const char gVideoVertexShader[] =
65 "attribute vec4 vPosition;\n"
66 "uniform mat4 textureMatrix;\n"
67 "uniform mat4 projectionMatrix;\n"
68 "varying vec2 v_texCoord;\n"
70 " gl_Position = projectionMatrix * vPosition;\n"
71 " v_texCoord = vec2(textureMatrix * vec4(vPosition.x, 1.0 - vPosition.y, 0.0, 1.0));\n"
74 static const char gVideoFragmentShader[] =
75 "#extension GL_OES_EGL_image_external : require\n"
76 "precision mediump float;\n"
77 "uniform samplerExternalOES s_yuvTexture;\n"
78 "varying vec2 v_texCoord;\n"
80 " gl_FragColor = texture2D(s_yuvTexture, v_texCoord);\n"
83 // In the long run, the gSurfaceTextureOESFragmentShader is the official way of
84 // doing Surface Texture for RGBA format.
85 // Now since the driver is not ready for it yet, we had to support both to be
86 // ready for the switch.
87 // TODO: remove SurfaceTexture2D support after switching to OES method.
88 static const char gSurfaceTexture2DFragmentShader[] =
89 "#extension GL_OES_EGL_image_external : require\n"
90 "precision mediump float;\n"
91 "varying vec2 v_texCoord; \n"
92 "uniform float alpha; \n"
93 "uniform sampler2D s_texture; \n"
95 " gl_FragColor = texture2D(s_texture, v_texCoord); \n"
96 " gl_FragColor *= alpha; "
99 static const char gSurfaceTextureOESFragmentShader[] =
100 "#extension GL_OES_EGL_image_external : require\n"
101 "precision mediump float;\n"
102 "varying vec2 v_texCoord; \n"
103 "uniform float alpha; \n"
104 "uniform samplerExternalOES s_texture; \n"
106 " gl_FragColor = texture2D(s_texture, v_texCoord); \n"
107 " gl_FragColor *= alpha; "
110 GLuint ShaderProgram::loadShader(GLenum shaderType, const char* pSource)
112 GLuint shader = glCreateShader(shaderType);
114 glShaderSource(shader, 1, &pSource, 0);
115 glCompileShader(shader);
117 glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
120 glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
122 char* buf = (char*) malloc(infoLen);
124 glGetShaderInfoLog(shader, infoLen, 0, buf);
125 XLOG("could not compile shader %d:\n%s\n", shaderType, buf);
128 glDeleteShader(shader);
136 GLuint ShaderProgram::createProgram(const char* pVertexSource, const char* pFragmentSource)
138 GLuint vertexShader = loadShader(GL_VERTEX_SHADER, pVertexSource);
140 XLOG("couldn't load the vertex shader!");
144 GLuint pixelShader = loadShader(GL_FRAGMENT_SHADER, pFragmentSource);
146 XLOG("couldn't load the pixel shader!");
150 GLuint program = glCreateProgram();
152 glAttachShader(program, vertexShader);
153 GLUtils::checkGlError("glAttachShader vertex");
154 glAttachShader(program, pixelShader);
155 GLUtils::checkGlError("glAttachShader pixel");
156 glLinkProgram(program);
157 GLint linkStatus = GL_FALSE;
158 glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
159 if (linkStatus != GL_TRUE) {
161 glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength);
163 char* buf = (char*) malloc(bufLength);
165 glGetProgramInfoLog(program, bufLength, 0, buf);
166 XLOG("could not link program:\n%s\n", buf);
170 glDeleteProgram(program);
177 ShaderProgram::ShaderProgram()
178 : m_blendingEnabled(false)
183 void ShaderProgram::init()
185 m_program = createProgram(gVertexShader, gFragmentShader);
186 m_videoProgram = createProgram(gVideoVertexShader, gVideoFragmentShader);
188 createProgram(gVertexShader, gSurfaceTexture2DFragmentShader);
189 m_surfTexOESProgram =
190 createProgram(gVertexShader, gSurfaceTextureOESFragmentShader);
193 || m_videoProgram == -1
194 || m_surfTex2DProgram == -1
195 || m_surfTexOESProgram == -1)
198 m_hProjectionMatrix = glGetUniformLocation(m_program, "projectionMatrix");
199 m_hAlpha = glGetUniformLocation(m_program, "alpha");
200 m_hTexSampler = glGetUniformLocation(m_program, "s_texture");
201 m_hPosition = glGetAttribLocation(m_program, "vPosition");
203 m_hVideoProjectionMatrix =
204 glGetUniformLocation(m_videoProgram, "projectionMatrix");
205 m_hVideoTextureMatrix = glGetUniformLocation(m_videoProgram, "textureMatrix");
206 m_hVideoTexSampler = glGetUniformLocation(m_videoProgram, "s_yuvTexture");
207 m_hVideoPosition = glGetAttribLocation(m_program, "vPosition");
209 m_hST2DProjectionMatrix =
210 glGetUniformLocation(m_surfTex2DProgram, "projectionMatrix");
211 m_hST2DAlpha = glGetUniformLocation(m_surfTex2DProgram, "alpha");
212 m_hST2DTexSampler = glGetUniformLocation(m_surfTex2DProgram, "s_texture");
213 m_hST2DPosition = glGetAttribLocation(m_surfTex2DProgram, "vPosition");
215 m_hSTOESProjectionMatrix =
216 glGetUniformLocation(m_surfTexOESProgram, "projectionMatrix");
217 m_hSTOESAlpha = glGetUniformLocation(m_surfTexOESProgram, "alpha");
218 m_hSTOESTexSampler = glGetUniformLocation(m_surfTexOESProgram, "s_texture");
219 m_hSTOESPosition = glGetAttribLocation(m_surfTexOESProgram, "vPosition");
222 const GLfloat coord[] = {
229 glGenBuffers(1, m_textureBuffer);
230 glBindBuffer(GL_ARRAY_BUFFER, m_textureBuffer[0]);
231 glBufferData(GL_ARRAY_BUFFER, 2 * 4 * sizeof(GLfloat), coord, GL_STATIC_DRAW);
233 GLUtils::checkGlError("init");
236 void ShaderProgram::resetBlending()
239 glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
240 glBlendEquation(GL_FUNC_ADD);
241 m_blendingEnabled = false;
244 void ShaderProgram::setBlendingState(bool enableBlending)
246 if (enableBlending == m_blendingEnabled)
254 m_blendingEnabled = enableBlending;
257 /////////////////////////////////////////////////////////////////////////////////////////
259 /////////////////////////////////////////////////////////////////////////////////////////
261 void ShaderProgram::setViewport(SkRect& viewport)
263 TransformationMatrix ortho;
264 GLUtils::setOrthographicMatrix(ortho, viewport.fLeft, viewport.fTop,
265 viewport.fRight, viewport.fBottom, -1000, 1000);
266 m_projectionMatrix = ortho;
267 m_viewport = viewport;
270 void ShaderProgram::setProjectionMatrix(SkRect& geometry)
272 TransformationMatrix translate;
273 translate.translate3d(geometry.fLeft, geometry.fTop, 0.0);
274 TransformationMatrix scale;
275 scale.scale3d(geometry.width(), geometry.height(), 1.0);
277 TransformationMatrix total = m_projectionMatrix * translate * scale;
279 GLfloat projectionMatrix[16];
280 GLUtils::toGLMatrix(projectionMatrix, total);
281 glUniformMatrix4fv(m_hProjectionMatrix, 1, GL_FALSE, projectionMatrix);
284 void ShaderProgram::drawQuadInternal(SkRect& geometry,
289 GLenum textureTarget,
293 glUseProgram(program);
294 setProjectionMatrix(geometry);
296 glActiveTexture(GL_TEXTURE0);
297 glUniform1i(texSampler, 0);
298 glBindTexture(textureTarget, textureId);
299 glTexParameteri(textureTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
300 glTexParameteri(textureTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
301 glTexParameteri(textureTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
302 glTexParameteri(textureTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
304 glBindBuffer(GL_ARRAY_BUFFER, m_textureBuffer[0]);
305 glEnableVertexAttribArray(position);
306 glVertexAttribPointer(position, 2, GL_FLOAT, GL_FALSE, 0, 0);
307 glUniform1f(alpha, opacity);
309 setBlendingState(opacity < 1.0);
310 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
313 void ShaderProgram::drawQuad(SkRect& geometry, int textureId, float opacity,
314 GLenum textureTarget)
316 if (textureTarget == GL_TEXTURE_2D) {
317 drawQuadInternal(geometry, textureId, opacity, m_program,
318 m_hTexSampler, GL_TEXTURE_2D,
319 m_hPosition, alpha());
320 } else if (textureTarget == GL_TEXTURE_EXTERNAL_OES) {
321 drawQuadInternal(geometry, textureId, opacity, m_surfTexOESProgram,
322 m_hSTOESTexSampler, GL_TEXTURE_EXTERNAL_OES,
323 m_hSTOESPosition, m_hSTOESAlpha);
324 } else if (!textureTarget) {
325 drawQuadInternal(geometry, textureId, opacity, m_surfTex2DProgram,
326 m_hST2DTexSampler, GL_TEXTURE_2D,
327 m_hST2DPosition, m_hST2DAlpha);
329 GLUtils::checkGlError("drawQuad");
332 void ShaderProgram::setViewRect(const IntRect& viewRect)
334 m_viewRect = viewRect;
336 // We do clipping using glScissor, which needs to take
337 // coordinates in screen space. The following matrix transform
338 // content coordinates in screen coordinates.
339 TransformationMatrix translate;
340 translate.translate(1.0, 1.0);
342 TransformationMatrix scale;
343 scale.scale3d(m_viewRect.width() * 0.5f, m_viewRect.height() * 0.5f, 1);
345 m_documentToScreenMatrix = scale * translate * m_projectionMatrix;
347 translate.scale3d(1, -1, 1);
348 m_documentToInvScreenMatrix = scale * translate * m_projectionMatrix;
351 // This function transform a clip rect extracted from the current layer
352 // into a clip rect in screen coordinates -- used by the clipping rects
353 FloatRect ShaderProgram::rectInScreenCoord(const TransformationMatrix& drawMatrix, const IntSize& size)
355 FloatRect srect(0, 0, size.width(), size.height());
356 TransformationMatrix renderMatrix = m_documentToScreenMatrix * drawMatrix;
357 return renderMatrix.mapRect(srect);
360 // used by the partial screen invals
361 FloatRect ShaderProgram::rectInInvScreenCoord(const TransformationMatrix& drawMatrix, const IntSize& size)
363 FloatRect srect(0, 0, size.width(), size.height());
364 TransformationMatrix renderMatrix = m_documentToInvScreenMatrix * drawMatrix;
365 return renderMatrix.mapRect(srect);
368 FloatRect ShaderProgram::rectInInvScreenCoord(const FloatRect& rect)
370 return m_documentToInvScreenMatrix.mapRect(rect);
373 FloatRect ShaderProgram::rectInScreenCoord(const FloatRect& rect)
375 return m_documentToScreenMatrix.mapRect(rect);
378 FloatRect ShaderProgram::convertInvScreenCoordToScreenCoord(const FloatRect& rect)
380 FloatRect documentRect = m_documentToInvScreenMatrix.inverse().mapRect(rect);
381 return rectInScreenCoord(documentRect);
384 FloatRect ShaderProgram::convertScreenCoordToInvScreenCoord(const FloatRect& rect)
386 FloatRect documentRect = m_documentToScreenMatrix.inverse().mapRect(rect);
387 return rectInInvScreenCoord(documentRect);
390 void ShaderProgram::setScreenClip(const IntRect& clip)
393 IntRect mclip = clip;
395 // the clip from frameworks is in full screen coordinates
396 mclip.setY(clip.y() - m_webViewRect.y() - m_titleBarHeight);
397 FloatRect tclip = convertInvScreenCoordToScreenCoord(mclip);
398 IntRect screenClip(tclip.x(), tclip.y(), tclip.width(), tclip.height());
399 m_screenClip = screenClip;
402 // clip is in screen coordinates
403 void ShaderProgram::clip(const FloatRect& clip)
405 if (clip == m_clipRect)
408 // we should only call glScissor in this function, so that we can easily
409 // track the current clipping rect.
411 IntRect screenClip(clip.x(),
413 clip.width(), clip.height());
415 if (!m_screenClip.isEmpty())
416 screenClip.intersect(m_screenClip);
418 screenClip.setY(screenClip.y() + m_viewRect.y());
419 if (screenClip.x() < 0) {
420 int w = screenClip.width();
423 screenClip.setWidth(w);
425 if (screenClip.y() < 0) {
426 int h = screenClip.height();
429 screenClip.setHeight(h);
432 glScissor(screenClip.x(), screenClip.y(), screenClip.width(), screenClip.height());
437 IntRect ShaderProgram::clippedRectWithViewport(const IntRect& rect, int margin)
439 IntRect viewport(m_viewport.fLeft - margin, m_viewport.fTop - margin,
440 m_viewport.width() + margin, m_viewport.height() + margin);
441 viewport.intersect(rect);
445 float ShaderProgram::zValue(const TransformationMatrix& drawMatrix, float w, float h)
447 TransformationMatrix modifiedDrawMatrix = drawMatrix;
448 modifiedDrawMatrix.scale3d(w, h, 1);
449 TransformationMatrix renderMatrix = m_projectionMatrix * modifiedDrawMatrix;
450 FloatPoint3D point(0.5, 0.5, 0.0);
451 FloatPoint3D result = renderMatrix.mapPoint(point);
455 void ShaderProgram::drawLayerQuadInternal(const GLfloat* projectionMatrix,
456 int textureId, float opacity,
457 GLenum textureTarget, GLint program,
458 GLint matrix, GLint texSample,
459 GLint position, GLint alpha)
461 glUseProgram(program);
462 glUniformMatrix4fv(matrix, 1, GL_FALSE, projectionMatrix);
464 glActiveTexture(GL_TEXTURE0);
465 glUniform1i(texSample, 0);
466 glBindTexture(textureTarget, textureId);
467 glTexParameteri(textureTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
468 glTexParameteri(textureTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
469 glTexParameteri(textureTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
470 glTexParameteri(textureTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
473 glBindBuffer(GL_ARRAY_BUFFER, m_textureBuffer[0]);
474 glEnableVertexAttribArray(position);
475 glVertexAttribPointer(position, 2, GL_FLOAT, GL_FALSE, 0, 0);
476 glUniform1f(alpha, opacity);
480 void ShaderProgram::drawLayerQuad(const TransformationMatrix& drawMatrix,
481 SkRect& geometry, int textureId, float opacity,
482 bool forceBlending, GLenum textureTarget)
485 TransformationMatrix modifiedDrawMatrix = drawMatrix;
486 // move the drawing depending on where the texture is on the layer
487 modifiedDrawMatrix.translate(geometry.fLeft, geometry.fTop);
488 modifiedDrawMatrix.scale3d(geometry.width(), geometry.height(), 1);
489 TransformationMatrix renderMatrix = m_projectionMatrix * modifiedDrawMatrix;
491 GLfloat projectionMatrix[16];
492 GLUtils::toGLMatrix(projectionMatrix, renderMatrix);
493 if (textureTarget == GL_TEXTURE_2D) {
494 drawLayerQuadInternal(projectionMatrix, textureId, opacity,
495 GL_TEXTURE_2D, m_program,
496 m_hProjectionMatrix, m_hTexSampler,
497 m_hPosition, alpha());
498 } else if (textureTarget == GL_TEXTURE_EXTERNAL_OES) {
499 drawLayerQuadInternal(projectionMatrix, textureId, opacity,
500 GL_TEXTURE_EXTERNAL_OES, m_surfTexOESProgram,
501 m_hSTOESProjectionMatrix, m_hSTOESTexSampler,
502 m_hSTOESPosition, m_hSTOESAlpha);
503 } else if (!textureTarget) {
504 drawLayerQuadInternal(projectionMatrix, textureId, opacity,
505 GL_TEXTURE_2D, m_surfTex2DProgram,
506 m_hST2DProjectionMatrix, m_hST2DTexSampler,
507 m_hST2DPosition, m_hST2DAlpha);
510 setBlendingState(forceBlending || opacity < 1.0);
511 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
513 GLUtils::checkGlError("drawLayerQuad");
516 void ShaderProgram::drawVideoLayerQuad(const TransformationMatrix& drawMatrix,
517 float* textureMatrix, SkRect& geometry,
520 // switch to our custom yuv video rendering program
521 glUseProgram(m_videoProgram);
523 TransformationMatrix modifiedDrawMatrix = drawMatrix;
524 modifiedDrawMatrix.translate(geometry.fLeft, geometry.fTop);
525 modifiedDrawMatrix.scale3d(geometry.width(), geometry.height(), 1);
526 TransformationMatrix renderMatrix = m_projectionMatrix * modifiedDrawMatrix;
528 GLfloat projectionMatrix[16];
529 GLUtils::toGLMatrix(projectionMatrix, renderMatrix);
530 glUniformMatrix4fv(m_hVideoProjectionMatrix, 1, GL_FALSE, projectionMatrix);
531 glUniformMatrix4fv(m_hVideoTextureMatrix, 1, GL_FALSE, textureMatrix);
533 glActiveTexture(GL_TEXTURE0);
534 glUniform1i(m_hVideoTexSampler, 0);
535 glBindTexture(GL_TEXTURE_EXTERNAL_OES, textureId);
537 glBindBuffer(GL_ARRAY_BUFFER, m_textureBuffer[0]);
538 glEnableVertexAttribArray(m_hVideoPosition);
539 glVertexAttribPointer(m_hVideoPosition, 2, GL_FLOAT, GL_FALSE, 0, 0);
541 setBlendingState(false);
542 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
545 } // namespace WebCore
547 #endif // USE(ACCELERATED_COMPOSITING)