OpenGLRenderer.cpp revision d55a86120dd1e8ebcc6906c9ffd463f7460348da
1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#define LOG_TAG "OpenGLRenderer" 18 19#include <stdlib.h> 20#include <stdint.h> 21#include <sys/types.h> 22 23#include <SkCanvas.h> 24 25#include <utils/Log.h> 26 27#include "OpenGLRenderer.h" 28 29namespace android { 30namespace uirenderer { 31 32/////////////////////////////////////////////////////////////////////////////// 33// Defines 34/////////////////////////////////////////////////////////////////////////////// 35 36#define SV(x, y) { { x, y } } 37#define FV(x, y, u, v) { { x, y }, { u, v } } 38 39/////////////////////////////////////////////////////////////////////////////// 40// Globals 41/////////////////////////////////////////////////////////////////////////////// 42 43static const SimpleVertex gDrawColorVertices[] = { 44 SV(0.0f, 0.0f), 45 SV(1.0f, 0.0f), 46 SV(0.0f, 1.0f), 47 SV(1.0f, 1.0f) 48}; 49static const GLsizei gDrawColorVertexStride = sizeof(SimpleVertex); 50static const GLsizei gDrawColorVertexCount = 4; 51 52// This array is never used directly but used as a memcpy source in the 53// OpenGLRenderer constructor 54static const TextureVertex gDrawTextureVertices[] = { 55 FV(0.0f, 0.0f, 0.0f, 1.0f), 56 FV(1.0f, 0.0f, 1.0f, 1.0f), 57 FV(0.0f, 1.0f, 0.0f, 0.0f), 58 FV(1.0f, 1.0f, 1.0f, 0.0f) 59}; 60static const GLsizei gDrawTextureVertexStride = sizeof(TextureVertex); 61static const GLsizei gDrawTextureVertexCount = 4; 62 63// In this array, the index of each Blender equals the value of the first 64// entry. For instance, gBlends[1] == gBlends[SkXfermode::kSrc_Mode] 65static const Blender gBlends[] = { 66 { SkXfermode::kClear_Mode, GL_ZERO, GL_ZERO }, 67 { SkXfermode::kSrc_Mode, GL_ONE, GL_ZERO }, 68 { SkXfermode::kDst_Mode, GL_ZERO, GL_ONE }, 69 { SkXfermode::kSrcOver_Mode, GL_ONE, GL_ONE_MINUS_SRC_ALPHA }, 70 { SkXfermode::kDstOver_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE }, 71 { SkXfermode::kSrcIn_Mode, GL_DST_ALPHA, GL_ZERO }, 72 { SkXfermode::kDstIn_Mode, GL_ZERO, GL_SRC_ALPHA }, 73 { SkXfermode::kSrcOut_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ZERO }, 74 { SkXfermode::kDstOut_Mode, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA }, 75 { SkXfermode::kSrcATop_Mode, GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA }, 76 { SkXfermode::kDstATop_Mode, GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA }, 77 { SkXfermode::kXor_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA } 78}; 79 80/////////////////////////////////////////////////////////////////////////////// 81// Constructors/destructor 82/////////////////////////////////////////////////////////////////////////////// 83 84OpenGLRenderer::OpenGLRenderer() { 85 LOGD("Create OpenGLRenderer"); 86 87 mDrawColorShader = new DrawColorProgram; 88 mDrawTextureShader = new DrawTextureProgram; 89 90 memcpy(mDrawTextureVertices, gDrawTextureVertices, sizeof(gDrawTextureVertices)); 91} 92 93OpenGLRenderer::~OpenGLRenderer() { 94 LOGD("Destroy OpenGLRenderer"); 95} 96 97/////////////////////////////////////////////////////////////////////////////// 98// Setup 99/////////////////////////////////////////////////////////////////////////////// 100 101void OpenGLRenderer::setViewport(int width, int height) { 102 glViewport(0, 0, width, height); 103 104 mat4 ortho; 105 ortho.loadOrtho(0, width, height, 0, -1, 1); 106 ortho.copyTo(mOrthoMatrix); 107 108 mWidth = width; 109 mHeight = height; 110} 111 112void OpenGLRenderer::prepare() { 113 mSnapshot = &mFirstSnapshot; 114 mSaveCount = 0; 115 116 glDisable(GL_SCISSOR_TEST); 117 118 glClearColor(0.0f, 0.0f, 0.0f, 0.0f); 119 glClear(GL_COLOR_BUFFER_BIT); 120 121 glEnable(GL_SCISSOR_TEST); 122 glScissor(0, 0, mWidth, mHeight); 123 124 mSnapshot->clipRect.set(0.0f, 0.0f, mWidth, mHeight); 125} 126 127/////////////////////////////////////////////////////////////////////////////// 128// State management 129/////////////////////////////////////////////////////////////////////////////// 130 131int OpenGLRenderer::getSaveCount() const { 132 return mSaveCount; 133} 134 135int OpenGLRenderer::save(int flags) { 136 return saveSnapshot(); 137} 138 139void OpenGLRenderer::restore() { 140 if (mSaveCount == 0) return; 141 142 if (restoreSnapshot()) { 143 setScissorFromClip(); 144 } 145} 146 147void OpenGLRenderer::restoreToCount(int saveCount) { 148 if (saveCount <= 0 || saveCount > mSaveCount) return; 149 150 bool restoreClip = false; 151 152 while (mSaveCount != saveCount - 1) { 153 restoreClip |= restoreSnapshot(); 154 } 155 156 if (restoreClip) { 157 setScissorFromClip(); 158 } 159} 160 161int OpenGLRenderer::saveSnapshot() { 162 mSnapshot = new Snapshot(mSnapshot); 163 return ++mSaveCount; 164} 165 166bool OpenGLRenderer::restoreSnapshot() { 167 bool restoreClip = mSnapshot->flags & Snapshot::kFlagClipSet; 168 bool restoreLayer = mSnapshot->flags & Snapshot::kFlagIsLayer; 169 170 sp<Snapshot> current = mSnapshot; 171 sp<Snapshot> previous = mSnapshot->previous; 172 173 if (restoreLayer) { 174 composeLayer(current, previous); 175 } 176 177 mSnapshot = previous; 178 mSaveCount--; 179 180 return restoreClip; 181} 182 183void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) { 184 // Unbind current FBO and restore previous one 185 // Most of the time, previous->fbo will be 0 to bind the default buffer 186 glBindFramebuffer(GL_FRAMEBUFFER, previous->fbo); 187 188 // Restore the clip from the previous snapshot 189 const Rect& clip = previous->getMappedClip(); 190 glScissor(clip.left, mHeight - clip.bottom, clip.getWidth(), clip.getHeight()); 191 192 // Compute the correct texture coordinates for the FBO texture 193 // The texture is currently as big as the window but drawn with 194 // a quad of the appropriate size 195 const Rect& layer = current->layer; 196 Rect texCoords(current->layer); 197 mSnapshot->transform.mapRect(texCoords); 198 199 const float u1 = texCoords.left / float(mWidth); 200 const float v1 = (mHeight - texCoords.top) / float(mHeight); 201 const float u2 = texCoords.right / float(mWidth); 202 const float v2 = (mHeight - texCoords.bottom) / float(mHeight); 203 204 resetDrawTextureTexCoords(u1, v1, u2, v1); 205 206 drawTextureRect(layer.left, layer.top, layer.right, layer.bottom, 207 current->texture, current->alpha, current->mode, true); 208 209 resetDrawTextureTexCoords(0.0f, 1.0f, 1.0f, 0.0f); 210 211 glDeleteFramebuffers(1, ¤t->fbo); 212 glDeleteTextures(1, ¤t->texture); 213} 214 215/////////////////////////////////////////////////////////////////////////////// 216// Layers 217/////////////////////////////////////////////////////////////////////////////// 218 219int OpenGLRenderer::saveLayer(float left, float top, float right, float bottom, 220 const SkPaint* p, int flags) { 221 int count = saveSnapshot(); 222 223 int alpha = 255; 224 SkXfermode::Mode mode; 225 226 if (p) { 227 alpha = p->getAlpha(); 228 const bool isMode = SkXfermode::IsMode(p->getXfermode(), &mode); 229 if (!isMode) { 230 // Assume SRC_OVER 231 mode = SkXfermode::kSrcOver_Mode; 232 } 233 } else { 234 mode = SkXfermode::kSrcOver_Mode; 235 } 236 237 createLayer(mSnapshot, left, top, right, bottom, alpha, mode, flags); 238 239 return count; 240} 241 242int OpenGLRenderer::saveLayerAlpha(float left, float top, float right, float bottom, 243 int alpha, int flags) { 244 int count = saveSnapshot(); 245 createLayer(mSnapshot, left, top, right, bottom, alpha, SkXfermode::kSrcOver_Mode, flags); 246 return count; 247} 248 249bool OpenGLRenderer::createLayer(sp<Snapshot> snapshot, float left, float top, 250 float right, float bottom, int alpha, SkXfermode::Mode mode,int flags) { 251 // Generate the FBO and attach the texture 252 glGenFramebuffers(1, &snapshot->fbo); 253 glBindFramebuffer(GL_FRAMEBUFFER, snapshot->fbo); 254 255 // Generate the texture in which the FBO will draw 256 glGenTextures(1, &snapshot->texture); 257 glBindTexture(GL_TEXTURE_2D, snapshot->texture); 258 259 // The FBO will not be scaled, so we can use lower quality filtering 260 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 261 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 262 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 263 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 264 265 // TODO ***** IMPORTANT ***** 266 // Creating a texture-backed FBO works only if the texture is the same size 267 // as the original rendering buffer (in this case, mWidth and mHeight.) 268 // This is expensive and wasteful and must be fixed. 269 // TODO Additionally we should use an FBO cache 270 271 const GLsizei width = mWidth; //right - left; 272 const GLsizei height = mHeight; //bottom - right; 273 274 const GLint format = (flags & SkCanvas::kHasAlphaLayer_SaveFlag) ? GL_RGBA : GL_RGB; 275 glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, NULL); 276 glBindTexture(GL_TEXTURE_2D, 0); 277 278 // Bind texture to FBO 279 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 280 snapshot->texture, 0); 281 282 GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); 283 if (status != GL_FRAMEBUFFER_COMPLETE) { 284 LOGD("Framebuffer incomplete %d", status); 285 286 glDeleteFramebuffers(1, &snapshot->fbo); 287 glDeleteTextures(1, &snapshot->texture); 288 289 return false; 290 } 291 292 snapshot->flags |= Snapshot::kFlagIsLayer; 293 snapshot->mode = mode; 294 snapshot->alpha = alpha / 255.0f; 295 snapshot->layer.set(left, top, right, bottom); 296 297 return true; 298} 299 300/////////////////////////////////////////////////////////////////////////////// 301// Transforms 302/////////////////////////////////////////////////////////////////////////////// 303 304void OpenGLRenderer::translate(float dx, float dy) { 305 mSnapshot->transform.translate(dx, dy, 0.0f); 306 mSnapshot->flags |= Snapshot::kFlagDirtyTransform; 307} 308 309void OpenGLRenderer::rotate(float degrees) { 310 mSnapshot->transform.rotate(degrees, 0.0f, 0.0f, 1.0f); 311 mSnapshot->flags |= Snapshot::kFlagDirtyTransform; 312} 313 314void OpenGLRenderer::scale(float sx, float sy) { 315 mSnapshot->transform.scale(sx, sy, 1.0f); 316 mSnapshot->flags |= Snapshot::kFlagDirtyTransform; 317} 318 319void OpenGLRenderer::setMatrix(SkMatrix* matrix) { 320 mSnapshot->transform.load(*matrix); 321 mSnapshot->flags |= Snapshot::kFlagDirtyTransform; 322} 323 324void OpenGLRenderer::getMatrix(SkMatrix* matrix) { 325 mSnapshot->transform.copyTo(*matrix); 326} 327 328void OpenGLRenderer::concatMatrix(SkMatrix* matrix) { 329 mat4 m(*matrix); 330 mSnapshot->transform.multiply(m); 331 mSnapshot->flags |= Snapshot::kFlagDirtyTransform; 332} 333 334/////////////////////////////////////////////////////////////////////////////// 335// Clipping 336/////////////////////////////////////////////////////////////////////////////// 337 338void OpenGLRenderer::setScissorFromClip() { 339 const Rect& clip = mSnapshot->getMappedClip(); 340 glScissor(clip.left, mHeight - clip.bottom, clip.getWidth(), clip.getHeight()); 341} 342 343const Rect& OpenGLRenderer::getClipBounds() { 344 return mSnapshot->clipRect; 345} 346 347bool OpenGLRenderer::quickReject(float left, float top, float right, float bottom) { 348 /* 349 * The documentation of quickReject() indicates that the specified rect 350 * is transformed before being compared to the clip rect. However, the 351 * clip rect is not stored transformed in the snapshot and can thus be 352 * compared directly 353 * 354 * The following code can be used instead to performed a mapped comparison: 355 * 356 * mSnapshot->transform.mapRect(r); 357 * const Rect& clip = mSnapshot->getMappedClip(); 358 * return !clip.intersects(r); 359 */ 360 Rect r(left, top, right, bottom); 361 return !mSnapshot->clipRect.intersects(r); 362} 363 364bool OpenGLRenderer::clipRect(float left, float top, float right, float bottom) { 365 bool clipped = mSnapshot->clipRect.intersect(left, top, right, bottom); 366 if (clipped) { 367 mSnapshot->flags |= Snapshot::kFlagClipSet; 368 setScissorFromClip(); 369 } 370 return clipped; 371} 372 373/////////////////////////////////////////////////////////////////////////////// 374// Drawing 375/////////////////////////////////////////////////////////////////////////////// 376 377void OpenGLRenderer::drawColor(int color, SkXfermode::Mode mode) { 378 const Rect& clip = mSnapshot->clipRect; 379 drawColorRect(clip.left, clip.top, clip.right, clip.bottom, color, mode); 380} 381 382void OpenGLRenderer::drawRect(float left, float top, float right, float bottom, const SkPaint* p) { 383 SkXfermode::Mode mode; 384 385 const bool isMode = SkXfermode::IsMode(p->getXfermode(), &mode); 386 if (!isMode) { 387 // Assume SRC_OVER 388 mode = SkXfermode::kSrcOver_Mode; 389 } 390 391 // Skia draws using the color's alpha channel if < 255 392 // Otherwise, it uses the paint's alpha 393 int color = p->getColor(); 394 if (((color >> 24) & 0xFF) == 255) { 395 color |= p->getAlpha() << 24; 396 } 397 398 drawColorRect(left, top, right, bottom, color, mode); 399} 400 401void OpenGLRenderer::drawColorRect(float left, float top, float right, float bottom, 402 int color, SkXfermode::Mode mode) { 403 const int alpha = (color >> 24) & 0xFF; 404 const bool blend = alpha < 255 || mode != SkXfermode::kSrcOver_Mode; 405 406 const GLfloat a = alpha / 255.0f; 407 const GLfloat r = ((color >> 16) & 0xFF) / 255.0f; 408 const GLfloat g = ((color >> 8) & 0xFF) / 255.0f; 409 const GLfloat b = ((color ) & 0xFF) / 255.0f; 410 411 if (blend) { 412 glEnable(GL_BLEND); 413 glBlendFunc(gBlends[mode].src, gBlends[mode].dst); 414 } 415 416 mModelView.loadTranslate(left, top, 0.0f); 417 mModelView.scale(right - left, bottom - top, 1.0f); 418 419 mDrawColorShader->use(&mOrthoMatrix[0], &mModelView.data[0], &mSnapshot->transform.data[0]); 420 421 const GLvoid* p = &gDrawColorVertices[0].position[0]; 422 423 glEnableVertexAttribArray(mDrawColorShader->position); 424 glVertexAttribPointer(mDrawColorShader->position, 2, GL_FLOAT, GL_FALSE, 425 gDrawColorVertexStride, p); 426 glVertexAttrib4f(mDrawColorShader->color, r, g, b, a); 427 428 glDrawArrays(GL_TRIANGLE_STRIP, 0, gDrawColorVertexCount); 429 430 glDisableVertexAttribArray(mDrawColorShader->position); 431 432 if (blend) { 433 glDisable(GL_BLEND); 434 } 435} 436 437void OpenGLRenderer::drawTextureRect(float left, float top, float right, float bottom, 438 GLuint texture, float alpha, SkXfermode::Mode mode, bool isPremultiplied) { 439 mModelView.loadTranslate(left, top, 0.0f); 440 mModelView.scale(right - left, bottom - top, 1.0f); 441 442 mDrawTextureShader->use(&mOrthoMatrix[0], &mModelView.data[0], &mSnapshot->transform.data[0]); 443 444 GLenum sourceMode = gBlends[mode].src; 445 if (!isPremultiplied && sourceMode == GL_ONE) { 446 sourceMode = GL_SRC_ALPHA; 447 } 448 449 // TODO: Try to disable blending when the texture is opaque and alpha == 1.0f 450 glEnable(GL_BLEND); 451 glBlendFunc(sourceMode, gBlends[mode].dst); 452 453 glBindTexture(GL_TEXTURE_2D, texture); 454 455 glActiveTexture(GL_TEXTURE0); 456 glUniform1i(mDrawTextureShader->sampler, 0); 457 458 const GLvoid* p = &mDrawTextureVertices[0].position[0]; 459 const GLvoid* t = &mDrawTextureVertices[0].texture[0]; 460 461 glEnableVertexAttribArray(mDrawTextureShader->position); 462 glVertexAttribPointer(mDrawTextureShader->position, 2, GL_FLOAT, GL_FALSE, 463 gDrawTextureVertexStride, p); 464 465 glEnableVertexAttribArray(mDrawTextureShader->texCoords); 466 glVertexAttribPointer(mDrawTextureShader->texCoords, 2, GL_FLOAT, GL_FALSE, 467 gDrawTextureVertexStride, t); 468 469 glVertexAttrib4f(mDrawTextureShader->color, 1.0f, 1.0f, 1.0f, alpha); 470 471 glDrawArrays(GL_TRIANGLE_STRIP, 0, gDrawTextureVertexCount); 472 473 glDisableVertexAttribArray(mDrawTextureShader->position); 474 glDisableVertexAttribArray(mDrawTextureShader->texCoords); 475 476 glBindTexture(GL_TEXTURE_2D, 0); 477 glDisable(GL_BLEND); 478} 479 480void OpenGLRenderer::resetDrawTextureTexCoords(float u1, float v1, float u2, float v2) { 481 mDrawTextureVertices[0].texture[0] = u1; 482 mDrawTextureVertices[0].texture[1] = v2; 483 mDrawTextureVertices[1].texture[0] = u2; 484 mDrawTextureVertices[1].texture[1] = v2; 485 mDrawTextureVertices[2].texture[0] = u1; 486 mDrawTextureVertices[2].texture[1] = v1; 487 mDrawTextureVertices[3].texture[0] = u2; 488 mDrawTextureVertices[3].texture[1] = v1; 489} 490 491}; // namespace uirenderer 492}; // namespace android 493