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 #include "OpenGLRenderer.h"
18
19 #include "DeferredDisplayList.h"
20 #include "GammaFontRenderer.h"
21 #include "Glop.h"
22 #include "GlopBuilder.h"
23 #include "Patch.h"
24 #include "PathTessellator.h"
25 #include "Properties.h"
26 #include "RenderNode.h"
27 #include "renderstate/MeshState.h"
28 #include "renderstate/RenderState.h"
29 #include "ShadowTessellator.h"
30 #include "SkiaShader.h"
31 #include "Vector.h"
32 #include "VertexBuffer.h"
33 #include "utils/GLUtils.h"
34 #include "utils/PaintUtils.h"
35 #include "utils/TraceUtils.h"
36
37 #include <stdlib.h>
38 #include <stdint.h>
39 #include <sys/types.h>
40
41 #include <SkCanvas.h>
42 #include <SkColor.h>
43 #include <SkPathOps.h>
44 #include <SkShader.h>
45 #include <SkTypeface.h>
46
47 #include <utils/Log.h>
48 #include <utils/StopWatch.h>
49
50 #include <private/hwui/DrawGlInfo.h>
51
52 #include <ui/Rect.h>
53
54 #if DEBUG_DETAILED_EVENTS
55 #define EVENT_LOGD(...) eventMarkDEBUG(__VA_ARGS__)
56 #else
57 #define EVENT_LOGD(...)
58 #endif
59
60 namespace android {
61 namespace uirenderer {
62
63 ///////////////////////////////////////////////////////////////////////////////
64 // Constructors/destructor
65 ///////////////////////////////////////////////////////////////////////////////
66
OpenGLRenderer(RenderState & renderState)67 OpenGLRenderer::OpenGLRenderer(RenderState& renderState)
68 : mState(*this)
69 , mCaches(Caches::getInstance())
70 , mRenderState(renderState)
71 , mFrameStarted(false)
72 , mScissorOptimizationDisabled(false)
73 , mSuppressTiling(false)
74 , mFirstFrameAfterResize(true)
75 , mDirty(false)
76 , mLightCenter((Vector3){FLT_MIN, FLT_MIN, FLT_MIN})
77 , mLightRadius(FLT_MIN)
78 , mAmbientShadowAlpha(0)
79 , mSpotShadowAlpha(0) {
80 }
81
~OpenGLRenderer()82 OpenGLRenderer::~OpenGLRenderer() {
83 // The context has already been destroyed at this point, do not call
84 // GL APIs. All GL state should be kept in Caches.h
85 }
86
initProperties()87 void OpenGLRenderer::initProperties() {
88 char property[PROPERTY_VALUE_MAX];
89 if (property_get(PROPERTY_DISABLE_SCISSOR_OPTIMIZATION, property, "false")) {
90 mScissorOptimizationDisabled = !strcasecmp(property, "true");
91 INIT_LOGD(" Scissor optimization %s",
92 mScissorOptimizationDisabled ? "disabled" : "enabled");
93 } else {
94 INIT_LOGD(" Scissor optimization enabled");
95 }
96 }
97
initLight(float lightRadius,uint8_t ambientShadowAlpha,uint8_t spotShadowAlpha)98 void OpenGLRenderer::initLight(float lightRadius, uint8_t ambientShadowAlpha,
99 uint8_t spotShadowAlpha) {
100 mLightRadius = lightRadius;
101 mAmbientShadowAlpha = ambientShadowAlpha;
102 mSpotShadowAlpha = spotShadowAlpha;
103 }
104
setLightCenter(const Vector3 & lightCenter)105 void OpenGLRenderer::setLightCenter(const Vector3& lightCenter) {
106 mLightCenter = lightCenter;
107 }
108
109 ///////////////////////////////////////////////////////////////////////////////
110 // Setup
111 ///////////////////////////////////////////////////////////////////////////////
112
onViewportInitialized()113 void OpenGLRenderer::onViewportInitialized() {
114 glDisable(GL_DITHER);
115 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
116 mFirstFrameAfterResize = true;
117 }
118
setupFrameState(float left,float top,float right,float bottom,bool opaque)119 void OpenGLRenderer::setupFrameState(float left, float top,
120 float right, float bottom, bool opaque) {
121 mCaches.clearGarbage();
122 mState.initializeSaveStack(left, top, right, bottom, mLightCenter);
123 mOpaque = opaque;
124 mTilingClip.set(left, top, right, bottom);
125 }
126
startFrame()127 void OpenGLRenderer::startFrame() {
128 if (mFrameStarted) return;
129 mFrameStarted = true;
130
131 mState.setDirtyClip(true);
132
133 discardFramebuffer(mTilingClip.left, mTilingClip.top, mTilingClip.right, mTilingClip.bottom);
134
135 mRenderState.setViewport(mState.getWidth(), mState.getHeight());
136
137 // Functors break the tiling extension in pretty spectacular ways
138 // This ensures we don't use tiling when a functor is going to be
139 // invoked during the frame
140 mSuppressTiling = mCaches.hasRegisteredFunctors()
141 || mFirstFrameAfterResize;
142 mFirstFrameAfterResize = false;
143
144 startTilingCurrentClip(true);
145
146 debugOverdraw(true, true);
147
148 clear(mTilingClip.left, mTilingClip.top,
149 mTilingClip.right, mTilingClip.bottom, mOpaque);
150 }
151
prepareDirty(float left,float top,float right,float bottom,bool opaque)152 void OpenGLRenderer::prepareDirty(float left, float top,
153 float right, float bottom, bool opaque) {
154
155 setupFrameState(left, top, right, bottom, opaque);
156
157 // Layer renderers will start the frame immediately
158 // The framebuffer renderer will first defer the display list
159 // for each layer and wait until the first drawing command
160 // to start the frame
161 if (currentSnapshot()->fbo == 0) {
162 mRenderState.blend().syncEnabled();
163 updateLayers();
164 } else {
165 startFrame();
166 }
167 }
168
discardFramebuffer(float left,float top,float right,float bottom)169 void OpenGLRenderer::discardFramebuffer(float left, float top, float right, float bottom) {
170 // If we know that we are going to redraw the entire framebuffer,
171 // perform a discard to let the driver know we don't need to preserve
172 // the back buffer for this frame.
173 if (mCaches.extensions().hasDiscardFramebuffer() &&
174 left <= 0.0f && top <= 0.0f && right >= mState.getWidth() && bottom >= mState.getHeight()) {
175 const bool isFbo = getTargetFbo() == 0;
176 const GLenum attachments[] = {
177 isFbo ? (const GLenum) GL_COLOR_EXT : (const GLenum) GL_COLOR_ATTACHMENT0,
178 isFbo ? (const GLenum) GL_STENCIL_EXT : (const GLenum) GL_STENCIL_ATTACHMENT };
179 glDiscardFramebufferEXT(GL_FRAMEBUFFER, 1, attachments);
180 }
181 }
182
clear(float left,float top,float right,float bottom,bool opaque)183 void OpenGLRenderer::clear(float left, float top, float right, float bottom, bool opaque) {
184 if (!opaque) {
185 mRenderState.scissor().setEnabled(true);
186 mRenderState.scissor().set(left, getViewportHeight() - bottom, right - left, bottom - top);
187 glClear(GL_COLOR_BUFFER_BIT);
188 mDirty = true;
189 return;
190 }
191
192 mRenderState.scissor().reset();
193 }
194
startTilingCurrentClip(bool opaque,bool expand)195 void OpenGLRenderer::startTilingCurrentClip(bool opaque, bool expand) {
196 if (!mSuppressTiling) {
197 const Snapshot* snapshot = currentSnapshot();
198
199 const Rect* clip = &mTilingClip;
200 if (snapshot->flags & Snapshot::kFlagFboTarget) {
201 clip = &(snapshot->layer->clipRect);
202 }
203
204 startTiling(*clip, getViewportHeight(), opaque, expand);
205 }
206 }
207
startTiling(const Rect & clip,int windowHeight,bool opaque,bool expand)208 void OpenGLRenderer::startTiling(const Rect& clip, int windowHeight, bool opaque, bool expand) {
209 if (!mSuppressTiling) {
210 if(expand) {
211 // Expand the startTiling region by 1
212 int leftNotZero = (clip.left > 0) ? 1 : 0;
213 int topNotZero = (windowHeight - clip.bottom > 0) ? 1 : 0;
214
215 mCaches.startTiling(
216 clip.left - leftNotZero,
217 windowHeight - clip.bottom - topNotZero,
218 clip.right - clip.left + leftNotZero + 1,
219 clip.bottom - clip.top + topNotZero + 1,
220 opaque);
221 } else {
222 mCaches.startTiling(clip.left, windowHeight - clip.bottom,
223 clip.right - clip.left, clip.bottom - clip.top, opaque);
224 }
225 }
226 }
227
endTiling()228 void OpenGLRenderer::endTiling() {
229 if (!mSuppressTiling) mCaches.endTiling();
230 }
231
finish()232 bool OpenGLRenderer::finish() {
233 renderOverdraw();
234 endTiling();
235 mTempPaths.clear();
236
237 // When finish() is invoked on FBO 0 we've reached the end
238 // of the current frame
239 if (getTargetFbo() == 0) {
240 mCaches.pathCache.trim();
241 mCaches.tessellationCache.trim();
242 }
243
244 if (!suppressErrorChecks()) {
245 #if DEBUG_OPENGL
246 GLUtils::dumpGLErrors();
247 #endif
248
249 #if DEBUG_MEMORY_USAGE
250 mCaches.dumpMemoryUsage();
251 #else
252 if (Properties::debugLevel & kDebugMemory) {
253 mCaches.dumpMemoryUsage();
254 }
255 #endif
256 }
257
258 mFrameStarted = false;
259
260 return reportAndClearDirty();
261 }
262
resumeAfterLayer()263 void OpenGLRenderer::resumeAfterLayer() {
264 mRenderState.setViewport(getViewportWidth(), getViewportHeight());
265 mRenderState.bindFramebuffer(currentSnapshot()->fbo);
266 debugOverdraw(true, false);
267
268 mRenderState.scissor().reset();
269 dirtyClip();
270 }
271
callDrawGLFunction(Functor * functor,Rect & dirty)272 void OpenGLRenderer::callDrawGLFunction(Functor* functor, Rect& dirty) {
273 if (mState.currentlyIgnored()) return;
274
275 Rect clip(mState.currentClipRect());
276 clip.snapToPixelBoundaries();
277
278 // Since we don't know what the functor will draw, let's dirty
279 // the entire clip region
280 if (hasLayer()) {
281 dirtyLayerUnchecked(clip, getRegion());
282 }
283
284 DrawGlInfo info;
285 info.clipLeft = clip.left;
286 info.clipTop = clip.top;
287 info.clipRight = clip.right;
288 info.clipBottom = clip.bottom;
289 info.isLayer = hasLayer();
290 info.width = getViewportWidth();
291 info.height = getViewportHeight();
292 currentTransform()->copyTo(&info.transform[0]);
293
294 bool prevDirtyClip = mState.getDirtyClip();
295 // setup GL state for functor
296 if (mState.getDirtyClip()) {
297 setStencilFromClip(); // can issue draws, so must precede enableScissor()/interrupt()
298 }
299 if (mRenderState.scissor().setEnabled(true) || prevDirtyClip) {
300 setScissorFromClip();
301 }
302
303 mRenderState.invokeFunctor(functor, DrawGlInfo::kModeDraw, &info);
304 // Scissor may have been modified, reset dirty clip
305 dirtyClip();
306
307 mDirty = true;
308 }
309
310 ///////////////////////////////////////////////////////////////////////////////
311 // Debug
312 ///////////////////////////////////////////////////////////////////////////////
313
eventMarkDEBUG(const char * fmt,...) const314 void OpenGLRenderer::eventMarkDEBUG(const char* fmt, ...) const {
315 #if DEBUG_DETAILED_EVENTS
316 const int BUFFER_SIZE = 256;
317 va_list ap;
318 char buf[BUFFER_SIZE];
319
320 va_start(ap, fmt);
321 vsnprintf(buf, BUFFER_SIZE, fmt, ap);
322 va_end(ap);
323
324 eventMark(buf);
325 #endif
326 }
327
328
eventMark(const char * name) const329 void OpenGLRenderer::eventMark(const char* name) const {
330 mCaches.eventMark(0, name);
331 }
332
startMark(const char * name) const333 void OpenGLRenderer::startMark(const char* name) const {
334 mCaches.startMark(0, name);
335 }
336
endMark() const337 void OpenGLRenderer::endMark() const {
338 mCaches.endMark();
339 }
340
debugOverdraw(bool enable,bool clear)341 void OpenGLRenderer::debugOverdraw(bool enable, bool clear) {
342 mRenderState.debugOverdraw(enable, clear);
343 }
344
renderOverdraw()345 void OpenGLRenderer::renderOverdraw() {
346 if (Properties::debugOverdraw && getTargetFbo() == 0) {
347 const Rect* clip = &mTilingClip;
348
349 mRenderState.scissor().setEnabled(true);
350 mRenderState.scissor().set(clip->left,
351 mState.firstSnapshot()->getViewportHeight() - clip->bottom,
352 clip->right - clip->left,
353 clip->bottom - clip->top);
354
355 // 1x overdraw
356 mRenderState.stencil().enableDebugTest(2);
357 drawColor(mCaches.getOverdrawColor(1), SkXfermode::kSrcOver_Mode);
358
359 // 2x overdraw
360 mRenderState.stencil().enableDebugTest(3);
361 drawColor(mCaches.getOverdrawColor(2), SkXfermode::kSrcOver_Mode);
362
363 // 3x overdraw
364 mRenderState.stencil().enableDebugTest(4);
365 drawColor(mCaches.getOverdrawColor(3), SkXfermode::kSrcOver_Mode);
366
367 // 4x overdraw and higher
368 mRenderState.stencil().enableDebugTest(4, true);
369 drawColor(mCaches.getOverdrawColor(4), SkXfermode::kSrcOver_Mode);
370
371 mRenderState.stencil().disable();
372 }
373 }
374
375 ///////////////////////////////////////////////////////////////////////////////
376 // Layers
377 ///////////////////////////////////////////////////////////////////////////////
378
updateLayer(Layer * layer,bool inFrame)379 bool OpenGLRenderer::updateLayer(Layer* layer, bool inFrame) {
380 if (layer->deferredUpdateScheduled && layer->renderer
381 && layer->renderNode.get() && layer->renderNode->isRenderable()) {
382
383 if (inFrame) {
384 endTiling();
385 debugOverdraw(false, false);
386 }
387
388 if (CC_UNLIKELY(inFrame || Properties::drawDeferDisabled)) {
389 layer->render(*this);
390 } else {
391 layer->defer(*this);
392 }
393
394 if (inFrame) {
395 resumeAfterLayer();
396 startTilingCurrentClip();
397 }
398
399 layer->debugDrawUpdate = Properties::debugLayersUpdates;
400 layer->hasDrawnSinceUpdate = false;
401
402 return true;
403 }
404
405 return false;
406 }
407
updateLayers()408 void OpenGLRenderer::updateLayers() {
409 // If draw deferring is enabled this method will simply defer
410 // the display list of each individual layer. The layers remain
411 // in the layer updates list which will be cleared by flushLayers().
412 int count = mLayerUpdates.size();
413 if (count > 0) {
414 if (CC_UNLIKELY(Properties::drawDeferDisabled)) {
415 startMark("Layer Updates");
416 } else {
417 startMark("Defer Layer Updates");
418 }
419
420 // Note: it is very important to update the layers in order
421 for (int i = 0; i < count; i++) {
422 Layer* layer = mLayerUpdates.itemAt(i).get();
423 updateLayer(layer, false);
424 }
425
426 if (CC_UNLIKELY(Properties::drawDeferDisabled)) {
427 mLayerUpdates.clear();
428 mRenderState.bindFramebuffer(getTargetFbo());
429 }
430 endMark();
431 }
432 }
433
flushLayers()434 void OpenGLRenderer::flushLayers() {
435 int count = mLayerUpdates.size();
436 if (count > 0) {
437 startMark("Apply Layer Updates");
438
439 // Note: it is very important to update the layers in order
440 for (int i = 0; i < count; i++) {
441 mLayerUpdates.itemAt(i)->flush();
442 }
443
444 mLayerUpdates.clear();
445 mRenderState.bindFramebuffer(getTargetFbo());
446
447 endMark();
448 }
449 }
450
pushLayerUpdate(Layer * layer)451 void OpenGLRenderer::pushLayerUpdate(Layer* layer) {
452 if (layer) {
453 // Make sure we don't introduce duplicates.
454 // SortedVector would do this automatically but we need to respect
455 // the insertion order. The linear search is not an issue since
456 // this list is usually very short (typically one item, at most a few)
457 for (int i = mLayerUpdates.size() - 1; i >= 0; i--) {
458 if (mLayerUpdates.itemAt(i) == layer) {
459 return;
460 }
461 }
462 mLayerUpdates.push_back(layer);
463 }
464 }
465
cancelLayerUpdate(Layer * layer)466 void OpenGLRenderer::cancelLayerUpdate(Layer* layer) {
467 if (layer) {
468 for (int i = mLayerUpdates.size() - 1; i >= 0; i--) {
469 if (mLayerUpdates.itemAt(i) == layer) {
470 mLayerUpdates.removeAt(i);
471 break;
472 }
473 }
474 }
475 }
476
flushLayerUpdates()477 void OpenGLRenderer::flushLayerUpdates() {
478 ATRACE_NAME("Update HW Layers");
479 mRenderState.blend().syncEnabled();
480 updateLayers();
481 flushLayers();
482 // Wait for all the layer updates to be executed
483 glFinish();
484 }
485
markLayersAsBuildLayers()486 void OpenGLRenderer::markLayersAsBuildLayers() {
487 for (size_t i = 0; i < mLayerUpdates.size(); i++) {
488 mLayerUpdates[i]->wasBuildLayered = true;
489 }
490 }
491
492 ///////////////////////////////////////////////////////////////////////////////
493 // State management
494 ///////////////////////////////////////////////////////////////////////////////
495
onSnapshotRestored(const Snapshot & removed,const Snapshot & restored)496 void OpenGLRenderer::onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {
497 bool restoreViewport = removed.flags & Snapshot::kFlagIsFboLayer;
498 bool restoreClip = removed.flags & Snapshot::kFlagClipSet;
499 bool restoreLayer = removed.flags & Snapshot::kFlagIsLayer;
500
501 if (restoreViewport) {
502 mRenderState.setViewport(getViewportWidth(), getViewportHeight());
503 }
504
505 if (restoreClip) {
506 dirtyClip();
507 }
508
509 if (restoreLayer) {
510 endMark(); // Savelayer
511 ATRACE_END(); // SaveLayer
512 startMark("ComposeLayer");
513 composeLayer(removed, restored);
514 endMark();
515 }
516 }
517
518 ///////////////////////////////////////////////////////////////////////////////
519 // Layers
520 ///////////////////////////////////////////////////////////////////////////////
521
saveLayer(float left,float top,float right,float bottom,const SkPaint * paint,int flags,const SkPath * convexMask)522 int OpenGLRenderer::saveLayer(float left, float top, float right, float bottom,
523 const SkPaint* paint, int flags, const SkPath* convexMask) {
524 // force matrix/clip isolation for layer
525 flags |= SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag;
526
527 const int count = mState.saveSnapshot(flags);
528
529 if (!mState.currentlyIgnored()) {
530 createLayer(left, top, right, bottom, paint, flags, convexMask);
531 }
532
533 return count;
534 }
535
calculateLayerBoundsAndClip(Rect & bounds,Rect & clip,bool fboLayer)536 void OpenGLRenderer::calculateLayerBoundsAndClip(Rect& bounds, Rect& clip, bool fboLayer) {
537 const Rect untransformedBounds(bounds);
538
539 currentTransform()->mapRect(bounds);
540
541 // Layers only make sense if they are in the framebuffer's bounds
542 if (bounds.intersect(mState.currentClipRect())) {
543 // We cannot work with sub-pixels in this case
544 bounds.snapToPixelBoundaries();
545
546 // When the layer is not an FBO, we may use glCopyTexImage so we
547 // need to make sure the layer does not extend outside the bounds
548 // of the framebuffer
549 const Snapshot& previous = *(currentSnapshot()->previous);
550 Rect previousViewport(0, 0, previous.getViewportWidth(), previous.getViewportHeight());
551 if (!bounds.intersect(previousViewport)) {
552 bounds.setEmpty();
553 } else if (fboLayer) {
554 clip.set(bounds);
555 mat4 inverse;
556 inverse.loadInverse(*currentTransform());
557 inverse.mapRect(clip);
558 clip.snapToPixelBoundaries();
559 if (clip.intersect(untransformedBounds)) {
560 clip.translate(-untransformedBounds.left, -untransformedBounds.top);
561 bounds.set(untransformedBounds);
562 } else {
563 clip.setEmpty();
564 }
565 }
566 } else {
567 bounds.setEmpty();
568 }
569 }
570
updateSnapshotIgnoreForLayer(const Rect & bounds,const Rect & clip,bool fboLayer,int alpha)571 void OpenGLRenderer::updateSnapshotIgnoreForLayer(const Rect& bounds, const Rect& clip,
572 bool fboLayer, int alpha) {
573 if (bounds.isEmpty() || bounds.getWidth() > mCaches.maxTextureSize ||
574 bounds.getHeight() > mCaches.maxTextureSize ||
575 (fboLayer && clip.isEmpty())) {
576 writableSnapshot()->empty = fboLayer;
577 } else {
578 writableSnapshot()->invisible = writableSnapshot()->invisible || (alpha <= 0 && fboLayer);
579 }
580 }
581
saveLayerDeferred(float left,float top,float right,float bottom,const SkPaint * paint,int flags)582 int OpenGLRenderer::saveLayerDeferred(float left, float top, float right, float bottom,
583 const SkPaint* paint, int flags) {
584 const int count = mState.saveSnapshot(flags);
585
586 if (!mState.currentlyIgnored() && (flags & SkCanvas::kClipToLayer_SaveFlag)) {
587 // initialize the snapshot as though it almost represents an FBO layer so deferred draw
588 // operations will be able to store and restore the current clip and transform info, and
589 // quick rejection will be correct (for display lists)
590
591 Rect bounds(left, top, right, bottom);
592 Rect clip;
593 calculateLayerBoundsAndClip(bounds, clip, true);
594 updateSnapshotIgnoreForLayer(bounds, clip, true, getAlphaDirect(paint));
595
596 if (!mState.currentlyIgnored()) {
597 writableSnapshot()->resetTransform(-bounds.left, -bounds.top, 0.0f);
598 writableSnapshot()->resetClip(clip.left, clip.top, clip.right, clip.bottom);
599 writableSnapshot()->initializeViewport(bounds.getWidth(), bounds.getHeight());
600 writableSnapshot()->roundRectClipState = nullptr;
601 }
602 }
603
604 return count;
605 }
606
607 /**
608 * Layers are viewed by Skia are slightly different than layers in image editing
609 * programs (for instance.) When a layer is created, previously created layers
610 * and the frame buffer still receive every drawing command. For instance, if a
611 * layer is created and a shape intersecting the bounds of the layers and the
612 * framebuffer is draw, the shape will be drawn on both (unless the layer was
613 * created with the SkCanvas::kClipToLayer_SaveFlag flag.)
614 *
615 * A way to implement layers is to create an FBO for each layer, backed by an RGBA
616 * texture. Unfortunately, this is inefficient as it requires every primitive to
617 * be drawn n + 1 times, where n is the number of active layers. In practice this
618 * means, for every primitive:
619 * - Switch active frame buffer
620 * - Change viewport, clip and projection matrix
621 * - Issue the drawing
622 *
623 * Switching rendering target n + 1 times per drawn primitive is extremely costly.
624 * To avoid this, layers are implemented in a different way here, at least in the
625 * general case. FBOs are used, as an optimization, when the "clip to layer" flag
626 * is set. When this flag is set we can redirect all drawing operations into a
627 * single FBO.
628 *
629 * This implementation relies on the frame buffer being at least RGBA 8888. When
630 * a layer is created, only a texture is created, not an FBO. The content of the
631 * frame buffer contained within the layer's bounds is copied into this texture
632 * using glCopyTexImage2D(). The layer's region is then cleared(1) in the frame
633 * buffer and drawing continues as normal. This technique therefore treats the
634 * frame buffer as a scratch buffer for the layers.
635 *
636 * To compose the layers back onto the frame buffer, each layer texture
637 * (containing the original frame buffer data) is drawn as a simple quad over
638 * the frame buffer. The trick is that the quad is set as the composition
639 * destination in the blending equation, and the frame buffer becomes the source
640 * of the composition.
641 *
642 * Drawing layers with an alpha value requires an extra step before composition.
643 * An empty quad is drawn over the layer's region in the frame buffer. This quad
644 * is drawn with the rgba color (0,0,0,alpha). The alpha value offered by the
645 * quad is used to multiply the colors in the frame buffer. This is achieved by
646 * changing the GL blend functions for the GL_FUNC_ADD blend equation to
647 * GL_ZERO, GL_SRC_ALPHA.
648 *
649 * Because glCopyTexImage2D() can be slow, an alternative implementation might
650 * be use to draw a single clipped layer. The implementation described above
651 * is correct in every case.
652 *
653 * (1) The frame buffer is actually not cleared right away. To allow the GPU
654 * to potentially optimize series of calls to glCopyTexImage2D, the frame
655 * buffer is left untouched until the first drawing operation. Only when
656 * something actually gets drawn are the layers regions cleared.
657 */
createLayer(float left,float top,float right,float bottom,const SkPaint * paint,int flags,const SkPath * convexMask)658 bool OpenGLRenderer::createLayer(float left, float top, float right, float bottom,
659 const SkPaint* paint, int flags, const SkPath* convexMask) {
660 LAYER_LOGD("Requesting layer %.2fx%.2f", right - left, bottom - top);
661 LAYER_LOGD("Layer cache size = %d", mCaches.layerCache.getSize());
662
663 const bool fboLayer = flags & SkCanvas::kClipToLayer_SaveFlag;
664
665 // Window coordinates of the layer
666 Rect clip;
667 Rect bounds(left, top, right, bottom);
668 calculateLayerBoundsAndClip(bounds, clip, fboLayer);
669 updateSnapshotIgnoreForLayer(bounds, clip, fboLayer, getAlphaDirect(paint));
670
671 // Bail out if we won't draw in this snapshot
672 if (mState.currentlyIgnored()) {
673 return false;
674 }
675
676 mCaches.textureState().activateTexture(0);
677 Layer* layer = mCaches.layerCache.get(mRenderState, bounds.getWidth(), bounds.getHeight());
678 if (!layer) {
679 return false;
680 }
681
682 layer->setPaint(paint);
683 layer->layer.set(bounds);
684 layer->texCoords.set(0.0f, bounds.getHeight() / float(layer->getHeight()),
685 bounds.getWidth() / float(layer->getWidth()), 0.0f);
686
687 layer->setBlend(true);
688 layer->setDirty(false);
689 layer->setConvexMask(convexMask); // note: the mask must be cleared before returning to the cache
690
691 // Save the layer in the snapshot
692 writableSnapshot()->flags |= Snapshot::kFlagIsLayer;
693 writableSnapshot()->layer = layer;
694
695 ATRACE_FORMAT_BEGIN("%ssaveLayer %ux%u",
696 fboLayer ? "" : "unclipped ",
697 layer->getWidth(), layer->getHeight());
698 startMark("SaveLayer");
699 if (fboLayer) {
700 return createFboLayer(layer, bounds, clip);
701 } else {
702 // Copy the framebuffer into the layer
703 layer->bindTexture();
704 if (!bounds.isEmpty()) {
705 if (layer->isEmpty()) {
706 // Workaround for some GL drivers. When reading pixels lying outside
707 // of the window we should get undefined values for those pixels.
708 // Unfortunately some drivers will turn the entire target texture black
709 // when reading outside of the window.
710 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, layer->getWidth(), layer->getHeight(),
711 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
712 layer->setEmpty(false);
713 }
714
715 glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,
716 bounds.left, getViewportHeight() - bounds.bottom,
717 bounds.getWidth(), bounds.getHeight());
718
719 // Enqueue the buffer coordinates to clear the corresponding region later
720 mLayers.push_back(Rect(bounds));
721 }
722 }
723
724 return true;
725 }
726
createFboLayer(Layer * layer,Rect & bounds,Rect & clip)727 bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, Rect& clip) {
728 layer->clipRect.set(clip);
729 layer->setFbo(mCaches.fboCache.get());
730
731 writableSnapshot()->region = &writableSnapshot()->layer->region;
732 writableSnapshot()->flags |= Snapshot::kFlagFboTarget | Snapshot::kFlagIsFboLayer;
733 writableSnapshot()->fbo = layer->getFbo();
734 writableSnapshot()->resetTransform(-bounds.left, -bounds.top, 0.0f);
735 writableSnapshot()->resetClip(clip.left, clip.top, clip.right, clip.bottom);
736 writableSnapshot()->initializeViewport(bounds.getWidth(), bounds.getHeight());
737 writableSnapshot()->roundRectClipState = nullptr;
738
739 endTiling();
740 debugOverdraw(false, false);
741 // Bind texture to FBO
742 mRenderState.bindFramebuffer(layer->getFbo());
743 layer->bindTexture();
744
745 // Initialize the texture if needed
746 if (layer->isEmpty()) {
747 layer->allocateTexture();
748 layer->setEmpty(false);
749 }
750
751 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
752 layer->getTextureId(), 0);
753
754 // Expand the startTiling region by 1
755 startTilingCurrentClip(true, true);
756
757 // Clear the FBO, expand the clear region by 1 to get nice bilinear filtering
758 mRenderState.scissor().setEnabled(true);
759 mRenderState.scissor().set(clip.left - 1.0f, bounds.getHeight() - clip.bottom - 1.0f,
760 clip.getWidth() + 2.0f, clip.getHeight() + 2.0f);
761 glClear(GL_COLOR_BUFFER_BIT);
762
763 dirtyClip();
764
765 // Change the ortho projection
766 mRenderState.setViewport(bounds.getWidth(), bounds.getHeight());
767 return true;
768 }
769
770 /**
771 * Read the documentation of createLayer() before doing anything in this method.
772 */
composeLayer(const Snapshot & removed,const Snapshot & restored)773 void OpenGLRenderer::composeLayer(const Snapshot& removed, const Snapshot& restored) {
774 if (!removed.layer) {
775 ALOGE("Attempting to compose a layer that does not exist");
776 return;
777 }
778
779 Layer* layer = removed.layer;
780 const Rect& rect = layer->layer;
781 const bool fboLayer = removed.flags & Snapshot::kFlagIsFboLayer;
782
783 bool clipRequired = false;
784 mState.calculateQuickRejectForScissor(rect.left, rect.top, rect.right, rect.bottom,
785 &clipRequired, nullptr, false); // safely ignore return, should never be rejected
786 mRenderState.scissor().setEnabled(mScissorOptimizationDisabled || clipRequired);
787
788 if (fboLayer) {
789 endTiling();
790
791 // Detach the texture from the FBO
792 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
793
794 layer->removeFbo(false);
795
796 // Unbind current FBO and restore previous one
797 mRenderState.bindFramebuffer(restored.fbo);
798 debugOverdraw(true, false);
799
800 startTilingCurrentClip();
801 }
802
803 if (!fboLayer && layer->getAlpha() < 255) {
804 SkPaint layerPaint;
805 layerPaint.setAlpha(layer->getAlpha());
806 layerPaint.setXfermodeMode(SkXfermode::kDstIn_Mode);
807 layerPaint.setColorFilter(layer->getColorFilter());
808
809 drawColorRect(rect.left, rect.top, rect.right, rect.bottom, &layerPaint, true);
810 // Required below, composeLayerRect() will divide by 255
811 layer->setAlpha(255);
812 }
813
814 mRenderState.meshState().unbindMeshBuffer();
815
816 mCaches.textureState().activateTexture(0);
817
818 // When the layer is stored in an FBO, we can save a bit of fillrate by
819 // drawing only the dirty region
820 if (fboLayer) {
821 dirtyLayer(rect.left, rect.top, rect.right, rect.bottom, *restored.transform);
822 composeLayerRegion(layer, rect);
823 } else if (!rect.isEmpty()) {
824 dirtyLayer(rect.left, rect.top, rect.right, rect.bottom);
825
826 save(0);
827 // the layer contains screen buffer content that shouldn't be alpha modulated
828 // (and any necessary alpha modulation was handled drawing into the layer)
829 writableSnapshot()->alpha = 1.0f;
830 composeLayerRectSwapped(layer, rect);
831 restore();
832 }
833
834 dirtyClip();
835
836 // Failing to add the layer to the cache should happen only if the layer is too large
837 layer->setConvexMask(nullptr);
838 if (!mCaches.layerCache.put(layer)) {
839 LAYER_LOGD("Deleting layer");
840 layer->decStrong(nullptr);
841 }
842 }
843
drawTextureLayer(Layer * layer,const Rect & rect)844 void OpenGLRenderer::drawTextureLayer(Layer* layer, const Rect& rect) {
845 const bool tryToSnap = !layer->getForceFilter()
846 && layer->getWidth() == (uint32_t) rect.getWidth()
847 && layer->getHeight() == (uint32_t) rect.getHeight();
848 Glop glop;
849 GlopBuilder(mRenderState, mCaches, &glop)
850 .setRoundRectClipState(currentSnapshot()->roundRectClipState)
851 .setMeshTexturedUvQuad(nullptr, Rect(0, 1, 1, 0)) // TODO: simplify with VBO
852 .setFillTextureLayer(*layer, getLayerAlpha(layer))
853 .setTransform(*currentSnapshot(), TransformFlags::None)
854 .setModelViewMapUnitToRectOptionalSnap(tryToSnap, rect)
855 .build();
856 renderGlop(glop);
857 }
858
composeLayerRectSwapped(Layer * layer,const Rect & rect)859 void OpenGLRenderer::composeLayerRectSwapped(Layer* layer, const Rect& rect) {
860 Glop glop;
861 GlopBuilder(mRenderState, mCaches, &glop)
862 .setRoundRectClipState(currentSnapshot()->roundRectClipState)
863 .setMeshTexturedUvQuad(nullptr, layer->texCoords)
864 .setFillLayer(layer->getTexture(), layer->getColorFilter(),
865 getLayerAlpha(layer), layer->getMode(), Blend::ModeOrderSwap::Swap)
866 .setTransform(*currentSnapshot(), TransformFlags::MeshIgnoresCanvasTransform)
867 .setModelViewMapUnitToRect(rect)
868 .build();
869 renderGlop(glop);
870 }
871
composeLayerRect(Layer * layer,const Rect & rect)872 void OpenGLRenderer::composeLayerRect(Layer* layer, const Rect& rect) {
873 if (layer->isTextureLayer()) {
874 EVENT_LOGD("composeTextureLayerRect");
875 drawTextureLayer(layer, rect);
876 } else {
877 EVENT_LOGD("composeHardwareLayerRect");
878
879 const bool tryToSnap = layer->getWidth() == static_cast<uint32_t>(rect.getWidth())
880 && layer->getHeight() == static_cast<uint32_t>(rect.getHeight());
881 Glop glop;
882 GlopBuilder(mRenderState, mCaches, &glop)
883 .setRoundRectClipState(currentSnapshot()->roundRectClipState)
884 .setMeshTexturedUvQuad(nullptr, layer->texCoords)
885 .setFillLayer(layer->getTexture(), layer->getColorFilter(), getLayerAlpha(layer), layer->getMode(), Blend::ModeOrderSwap::NoSwap)
886 .setTransform(*currentSnapshot(), TransformFlags::None)
887 .setModelViewMapUnitToRectOptionalSnap(tryToSnap, rect)
888 .build();
889 renderGlop(glop);
890 }
891 }
892
893 /**
894 * Issues the command X, and if we're composing a save layer to the fbo or drawing a newly updated
895 * hardware layer with overdraw debug on, draws again to the stencil only, so that these draw
896 * operations are correctly counted twice for overdraw. NOTE: assumes composeLayerRegion only used
897 * by saveLayer's restore
898 */
899 #define DRAW_DOUBLE_STENCIL_IF(COND, DRAW_COMMAND) { \
900 DRAW_COMMAND; \
901 if (CC_UNLIKELY(Properties::debugOverdraw && getTargetFbo() == 0 && COND)) { \
902 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); \
903 DRAW_COMMAND; \
904 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); \
905 } \
906 }
907
908 #define DRAW_DOUBLE_STENCIL(DRAW_COMMAND) DRAW_DOUBLE_STENCIL_IF(true, DRAW_COMMAND)
909
910 // This class is purely for inspection. It inherits from SkShader, but Skia does not know how to
911 // use it. The OpenGLRenderer will look at it to find its Layer and whether it is opaque.
912 class LayerShader : public SkShader {
913 public:
LayerShader(Layer * layer,const SkMatrix * localMatrix)914 LayerShader(Layer* layer, const SkMatrix* localMatrix)
915 : INHERITED(localMatrix)
916 , mLayer(layer) {
917 }
918
asACustomShader(void ** data) const919 virtual bool asACustomShader(void** data) const override {
920 if (data) {
921 *data = static_cast<void*>(mLayer);
922 }
923 return true;
924 }
925
isOpaque() const926 virtual bool isOpaque() const override {
927 return !mLayer->isBlend();
928 }
929
930 protected:
shadeSpan(int x,int y,SkPMColor[],int count)931 virtual void shadeSpan(int x, int y, SkPMColor[], int count) {
932 LOG_ALWAYS_FATAL("LayerShader should never be drawn with raster backend.");
933 }
934
flatten(SkWriteBuffer &) const935 virtual void flatten(SkWriteBuffer&) const override {
936 LOG_ALWAYS_FATAL("LayerShader should never be flattened.");
937 }
938
getFactory() const939 virtual Factory getFactory() const override {
940 LOG_ALWAYS_FATAL("LayerShader should never be created from a stream.");
941 return nullptr;
942 }
943 private:
944 // Unowned.
945 Layer* mLayer;
946 typedef SkShader INHERITED;
947 };
948
composeLayerRegion(Layer * layer,const Rect & rect)949 void OpenGLRenderer::composeLayerRegion(Layer* layer, const Rect& rect) {
950 if (CC_UNLIKELY(layer->region.isEmpty())) return; // nothing to draw
951
952 if (layer->getConvexMask()) {
953 save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
954
955 // clip to the area of the layer the mask can be larger
956 clipRect(rect.left, rect.top, rect.right, rect.bottom, SkRegion::kIntersect_Op);
957
958 SkPaint paint;
959 paint.setAntiAlias(true);
960 paint.setColor(SkColorSetARGB(int(getLayerAlpha(layer) * 255), 0, 0, 0));
961
962 // create LayerShader to map SaveLayer content into subsequent draw
963 SkMatrix shaderMatrix;
964 shaderMatrix.setTranslate(rect.left, rect.bottom);
965 shaderMatrix.preScale(1, -1);
966 LayerShader layerShader(layer, &shaderMatrix);
967 paint.setShader(&layerShader);
968
969 // Since the drawing primitive is defined in local drawing space,
970 // we don't need to modify the draw matrix
971 const SkPath* maskPath = layer->getConvexMask();
972 DRAW_DOUBLE_STENCIL(drawConvexPath(*maskPath, &paint));
973
974 paint.setShader(nullptr);
975 restore();
976
977 return;
978 }
979
980 if (layer->region.isRect()) {
981 layer->setRegionAsRect();
982
983 DRAW_DOUBLE_STENCIL(composeLayerRect(layer, layer->regionRect));
984
985 layer->region.clear();
986 return;
987 }
988
989 EVENT_LOGD("composeLayerRegion");
990 // standard Region based draw
991 size_t count;
992 const android::Rect* rects;
993 Region safeRegion;
994 if (CC_LIKELY(hasRectToRectTransform())) {
995 rects = layer->region.getArray(&count);
996 } else {
997 safeRegion = Region::createTJunctionFreeRegion(layer->region);
998 rects = safeRegion.getArray(&count);
999 }
1000
1001 const float texX = 1.0f / float(layer->getWidth());
1002 const float texY = 1.0f / float(layer->getHeight());
1003 const float height = rect.getHeight();
1004
1005 TextureVertex quadVertices[count * 4];
1006 TextureVertex* mesh = &quadVertices[0];
1007 for (size_t i = 0; i < count; i++) {
1008 const android::Rect* r = &rects[i];
1009
1010 const float u1 = r->left * texX;
1011 const float v1 = (height - r->top) * texY;
1012 const float u2 = r->right * texX;
1013 const float v2 = (height - r->bottom) * texY;
1014
1015 // TODO: Reject quads outside of the clip
1016 TextureVertex::set(mesh++, r->left, r->top, u1, v1);
1017 TextureVertex::set(mesh++, r->right, r->top, u2, v1);
1018 TextureVertex::set(mesh++, r->left, r->bottom, u1, v2);
1019 TextureVertex::set(mesh++, r->right, r->bottom, u2, v2);
1020 }
1021 Rect modelRect = Rect(rect.getWidth(), rect.getHeight());
1022 Glop glop;
1023 GlopBuilder(mRenderState, mCaches, &glop)
1024 .setRoundRectClipState(currentSnapshot()->roundRectClipState)
1025 .setMeshTexturedIndexedQuads(&quadVertices[0], count * 6)
1026 .setFillLayer(layer->getTexture(), layer->getColorFilter(), getLayerAlpha(layer), layer->getMode(), Blend::ModeOrderSwap::NoSwap)
1027 .setTransform(*currentSnapshot(), TransformFlags::None)
1028 .setModelViewOffsetRectSnap(rect.left, rect.top, modelRect)
1029 .build();
1030 DRAW_DOUBLE_STENCIL_IF(!layer->hasDrawnSinceUpdate, renderGlop(glop));
1031
1032 #if DEBUG_LAYERS_AS_REGIONS
1033 drawRegionRectsDebug(layer->region);
1034 #endif
1035
1036 layer->region.clear();
1037 }
1038
1039 #if DEBUG_LAYERS_AS_REGIONS
drawRegionRectsDebug(const Region & region)1040 void OpenGLRenderer::drawRegionRectsDebug(const Region& region) {
1041 size_t count;
1042 const android::Rect* rects = region.getArray(&count);
1043
1044 uint32_t colors[] = {
1045 0x7fff0000, 0x7f00ff00,
1046 0x7f0000ff, 0x7fff00ff,
1047 };
1048
1049 int offset = 0;
1050 int32_t top = rects[0].top;
1051
1052 for (size_t i = 0; i < count; i++) {
1053 if (top != rects[i].top) {
1054 offset ^= 0x2;
1055 top = rects[i].top;
1056 }
1057
1058 SkPaint paint;
1059 paint.setColor(colors[offset + (i & 0x1)]);
1060 Rect r(rects[i].left, rects[i].top, rects[i].right, rects[i].bottom);
1061 drawColorRect(r.left, r.top, r.right, r.bottom, paint);
1062 }
1063 }
1064 #endif
1065
drawRegionRects(const SkRegion & region,const SkPaint & paint,bool dirty)1066 void OpenGLRenderer::drawRegionRects(const SkRegion& region, const SkPaint& paint, bool dirty) {
1067 Vector<float> rects;
1068
1069 SkRegion::Iterator it(region);
1070 while (!it.done()) {
1071 const SkIRect& r = it.rect();
1072 rects.push(r.fLeft);
1073 rects.push(r.fTop);
1074 rects.push(r.fRight);
1075 rects.push(r.fBottom);
1076 it.next();
1077 }
1078
1079 drawColorRects(rects.array(), rects.size(), &paint, true, dirty, false);
1080 }
1081
dirtyLayer(const float left,const float top,const float right,const float bottom,const Matrix4 & transform)1082 void OpenGLRenderer::dirtyLayer(const float left, const float top,
1083 const float right, const float bottom, const Matrix4& transform) {
1084 if (hasLayer()) {
1085 Rect bounds(left, top, right, bottom);
1086 transform.mapRect(bounds);
1087 dirtyLayerUnchecked(bounds, getRegion());
1088 }
1089 }
1090
dirtyLayer(const float left,const float top,const float right,const float bottom)1091 void OpenGLRenderer::dirtyLayer(const float left, const float top,
1092 const float right, const float bottom) {
1093 if (hasLayer()) {
1094 Rect bounds(left, top, right, bottom);
1095 dirtyLayerUnchecked(bounds, getRegion());
1096 }
1097 }
1098
dirtyLayerUnchecked(Rect & bounds,Region * region)1099 void OpenGLRenderer::dirtyLayerUnchecked(Rect& bounds, Region* region) {
1100 if (CC_LIKELY(!bounds.isEmpty() && bounds.intersect(mState.currentClipRect()))) {
1101 bounds.snapToPixelBoundaries();
1102 android::Rect dirty(bounds.left, bounds.top, bounds.right, bounds.bottom);
1103 if (!dirty.isEmpty()) {
1104 region->orSelf(dirty);
1105 }
1106 }
1107 }
1108
clearLayerRegions()1109 void OpenGLRenderer::clearLayerRegions() {
1110 const size_t quadCount = mLayers.size();
1111 if (quadCount == 0) return;
1112
1113 if (!mState.currentlyIgnored()) {
1114 EVENT_LOGD("clearLayerRegions");
1115 // Doing several glScissor/glClear here can negatively impact
1116 // GPUs with a tiler architecture, instead we draw quads with
1117 // the Clear blending mode
1118
1119 // The list contains bounds that have already been clipped
1120 // against their initial clip rect, and the current clip
1121 // is likely different so we need to disable clipping here
1122 bool scissorChanged = mRenderState.scissor().setEnabled(false);
1123
1124 Vertex mesh[quadCount * 4];
1125 Vertex* vertex = mesh;
1126
1127 for (uint32_t i = 0; i < quadCount; i++) {
1128 const Rect& bounds = mLayers[i];
1129
1130 Vertex::set(vertex++, bounds.left, bounds.top);
1131 Vertex::set(vertex++, bounds.right, bounds.top);
1132 Vertex::set(vertex++, bounds.left, bounds.bottom);
1133 Vertex::set(vertex++, bounds.right, bounds.bottom);
1134 }
1135 // We must clear the list of dirty rects before we
1136 // call clearLayerRegions() in renderGlop to prevent
1137 // stencil setup from doing the same thing again
1138 mLayers.clear();
1139
1140 const int transformFlags = TransformFlags::MeshIgnoresCanvasTransform;
1141 Glop glop;
1142 GlopBuilder(mRenderState, mCaches, &glop)
1143 .setRoundRectClipState(nullptr) // clear ignores clip state
1144 .setMeshIndexedQuads(&mesh[0], quadCount)
1145 .setFillClear()
1146 .setTransform(*currentSnapshot(), transformFlags)
1147 .setModelViewOffsetRect(0, 0, Rect(currentSnapshot()->getClipRect()))
1148 .build();
1149 renderGlop(glop, GlopRenderType::LayerClear);
1150
1151 if (scissorChanged) mRenderState.scissor().setEnabled(true);
1152 } else {
1153 mLayers.clear();
1154 }
1155 }
1156
1157 ///////////////////////////////////////////////////////////////////////////////
1158 // State Deferral
1159 ///////////////////////////////////////////////////////////////////////////////
1160
storeDisplayState(DeferredDisplayState & state,int stateDeferFlags)1161 bool OpenGLRenderer::storeDisplayState(DeferredDisplayState& state, int stateDeferFlags) {
1162 const Rect& currentClip = mState.currentClipRect();
1163 const mat4* currentMatrix = currentTransform();
1164
1165 if (stateDeferFlags & kStateDeferFlag_Draw) {
1166 // state has bounds initialized in local coordinates
1167 if (!state.mBounds.isEmpty()) {
1168 currentMatrix->mapRect(state.mBounds);
1169 Rect clippedBounds(state.mBounds);
1170 // NOTE: if we ever want to use this clipping info to drive whether the scissor
1171 // is used, it should more closely duplicate the quickReject logic (in how it uses
1172 // snapToPixelBoundaries)
1173
1174 if (!clippedBounds.intersect(currentClip)) {
1175 // quick rejected
1176 return true;
1177 }
1178
1179 state.mClipSideFlags = kClipSide_None;
1180 if (!currentClip.contains(state.mBounds)) {
1181 int& flags = state.mClipSideFlags;
1182 // op partially clipped, so record which sides are clipped for clip-aware merging
1183 if (currentClip.left > state.mBounds.left) flags |= kClipSide_Left;
1184 if (currentClip.top > state.mBounds.top) flags |= kClipSide_Top;
1185 if (currentClip.right < state.mBounds.right) flags |= kClipSide_Right;
1186 if (currentClip.bottom < state.mBounds.bottom) flags |= kClipSide_Bottom;
1187 }
1188 state.mBounds.set(clippedBounds);
1189 } else {
1190 // Empty bounds implies size unknown. Label op as conservatively clipped to disable
1191 // overdraw avoidance (since we don't know what it overlaps)
1192 state.mClipSideFlags = kClipSide_ConservativeFull;
1193 state.mBounds.set(currentClip);
1194 }
1195 }
1196
1197 state.mClipValid = (stateDeferFlags & kStateDeferFlag_Clip);
1198 if (state.mClipValid) {
1199 state.mClip.set(currentClip);
1200 }
1201
1202 // Transform and alpha always deferred, since they are used by state operations
1203 // (Note: saveLayer/restore use colorFilter and alpha, so we just save restore everything)
1204 state.mMatrix.load(*currentMatrix);
1205 state.mAlpha = currentSnapshot()->alpha;
1206
1207 // always store/restore, since these are just pointers
1208 state.mRoundRectClipState = currentSnapshot()->roundRectClipState;
1209 state.mProjectionPathMask = currentSnapshot()->projectionPathMask;
1210 return false;
1211 }
1212
restoreDisplayState(const DeferredDisplayState & state,bool skipClipRestore)1213 void OpenGLRenderer::restoreDisplayState(const DeferredDisplayState& state, bool skipClipRestore) {
1214 setMatrix(state.mMatrix);
1215 writableSnapshot()->alpha = state.mAlpha;
1216 writableSnapshot()->roundRectClipState = state.mRoundRectClipState;
1217 writableSnapshot()->projectionPathMask = state.mProjectionPathMask;
1218
1219 if (state.mClipValid && !skipClipRestore) {
1220 writableSnapshot()->setClip(state.mClip.left, state.mClip.top,
1221 state.mClip.right, state.mClip.bottom);
1222 dirtyClip();
1223 }
1224 }
1225
1226 /**
1227 * Merged multidraw (such as in drawText and drawBitmaps rely on the fact that no clipping is done
1228 * in the draw path. Instead, clipping is done ahead of time - either as a single clip rect (when at
1229 * least one op is clipped), or disabled entirely (because no merged op is clipped)
1230 *
1231 * This method should be called when restoreDisplayState() won't be restoring the clip
1232 */
setupMergedMultiDraw(const Rect * clipRect)1233 void OpenGLRenderer::setupMergedMultiDraw(const Rect* clipRect) {
1234 if (clipRect != nullptr) {
1235 writableSnapshot()->setClip(clipRect->left, clipRect->top, clipRect->right, clipRect->bottom);
1236 } else {
1237 writableSnapshot()->setClip(0, 0, mState.getWidth(), mState.getHeight());
1238 }
1239 dirtyClip();
1240 bool enableScissor = (clipRect != nullptr) || mScissorOptimizationDisabled;
1241 mRenderState.scissor().setEnabled(enableScissor);
1242 }
1243
1244 ///////////////////////////////////////////////////////////////////////////////
1245 // Clipping
1246 ///////////////////////////////////////////////////////////////////////////////
1247
setScissorFromClip()1248 void OpenGLRenderer::setScissorFromClip() {
1249 Rect clip(mState.currentClipRect());
1250 clip.snapToPixelBoundaries();
1251
1252 if (mRenderState.scissor().set(clip.left, getViewportHeight() - clip.bottom,
1253 clip.getWidth(), clip.getHeight())) {
1254 mState.setDirtyClip(false);
1255 }
1256 }
1257
ensureStencilBuffer()1258 void OpenGLRenderer::ensureStencilBuffer() {
1259 // Thanks to the mismatch between EGL and OpenGL ES FBO we
1260 // cannot attach a stencil buffer to fbo0 dynamically. Let's
1261 // just hope we have one when hasLayer() returns false.
1262 if (hasLayer()) {
1263 attachStencilBufferToLayer(currentSnapshot()->layer);
1264 }
1265 }
1266
attachStencilBufferToLayer(Layer * layer)1267 void OpenGLRenderer::attachStencilBufferToLayer(Layer* layer) {
1268 // The layer's FBO is already bound when we reach this stage
1269 if (!layer->getStencilRenderBuffer()) {
1270 // GL_QCOM_tiled_rendering doesn't like it if a renderbuffer
1271 // is attached after we initiated tiling. We must turn it off,
1272 // attach the new render buffer then turn tiling back on
1273 endTiling();
1274
1275 RenderBuffer* buffer = mCaches.renderBufferCache.get(
1276 Stencil::getLayerStencilFormat(),
1277 layer->getWidth(), layer->getHeight());
1278 layer->setStencilRenderBuffer(buffer);
1279
1280 startTiling(layer->clipRect, layer->layer.getHeight());
1281 }
1282 }
1283
handlePoint(std::vector<Vertex> & rectangleVertices,const Matrix4 & transform,float x,float y)1284 static void handlePoint(std::vector<Vertex>& rectangleVertices, const Matrix4& transform,
1285 float x, float y) {
1286 Vertex v;
1287 v.x = x;
1288 v.y = y;
1289 transform.mapPoint(v.x, v.y);
1290 rectangleVertices.push_back(v);
1291 }
1292
handlePointNoTransform(std::vector<Vertex> & rectangleVertices,float x,float y)1293 static void handlePointNoTransform(std::vector<Vertex>& rectangleVertices, float x, float y) {
1294 Vertex v;
1295 v.x = x;
1296 v.y = y;
1297 rectangleVertices.push_back(v);
1298 }
1299
drawRectangleList(const RectangleList & rectangleList)1300 void OpenGLRenderer::drawRectangleList(const RectangleList& rectangleList) {
1301 int quadCount = rectangleList.getTransformedRectanglesCount();
1302 std::vector<Vertex> rectangleVertices(quadCount * 4);
1303 Rect scissorBox = rectangleList.calculateBounds();
1304 scissorBox.snapToPixelBoundaries();
1305 for (int i = 0; i < quadCount; ++i) {
1306 const TransformedRectangle& tr(rectangleList.getTransformedRectangle(i));
1307 const Matrix4& transform = tr.getTransform();
1308 Rect bounds = tr.getBounds();
1309 if (transform.rectToRect()) {
1310 transform.mapRect(bounds);
1311 if (!bounds.intersect(scissorBox)) {
1312 bounds.setEmpty();
1313 } else {
1314 handlePointNoTransform(rectangleVertices, bounds.left, bounds.top);
1315 handlePointNoTransform(rectangleVertices, bounds.right, bounds.top);
1316 handlePointNoTransform(rectangleVertices, bounds.left, bounds.bottom);
1317 handlePointNoTransform(rectangleVertices, bounds.right, bounds.bottom);
1318 }
1319 } else {
1320 handlePoint(rectangleVertices, transform, bounds.left, bounds.top);
1321 handlePoint(rectangleVertices, transform, bounds.right, bounds.top);
1322 handlePoint(rectangleVertices, transform, bounds.left, bounds.bottom);
1323 handlePoint(rectangleVertices, transform, bounds.right, bounds.bottom);
1324 }
1325 }
1326
1327 mRenderState.scissor().set(scissorBox.left, getViewportHeight() - scissorBox.bottom,
1328 scissorBox.getWidth(), scissorBox.getHeight());
1329 const int transformFlags = TransformFlags::MeshIgnoresCanvasTransform;
1330 Glop glop;
1331 Vertex* vertices = &rectangleVertices[0];
1332 GlopBuilder(mRenderState, mCaches, &glop)
1333 .setRoundRectClipState(currentSnapshot()->roundRectClipState)
1334 .setMeshIndexedQuads(vertices, rectangleVertices.size() / 4)
1335 .setFillBlack()
1336 .setTransform(*currentSnapshot(), transformFlags)
1337 .setModelViewOffsetRect(0, 0, scissorBox)
1338 .build();
1339 renderGlop(glop);
1340 }
1341
setStencilFromClip()1342 void OpenGLRenderer::setStencilFromClip() {
1343 if (!Properties::debugOverdraw) {
1344 if (!currentSnapshot()->clipIsSimple()) {
1345 int incrementThreshold;
1346 EVENT_LOGD("setStencilFromClip - enabling");
1347
1348 // NOTE: The order here is important, we must set dirtyClip to false
1349 // before any draw call to avoid calling back into this method
1350 mState.setDirtyClip(false);
1351
1352 ensureStencilBuffer();
1353
1354 const ClipArea& clipArea = currentSnapshot()->getClipArea();
1355
1356 bool isRectangleList = clipArea.isRectangleList();
1357 if (isRectangleList) {
1358 incrementThreshold = clipArea.getRectangleList().getTransformedRectanglesCount();
1359 } else {
1360 incrementThreshold = 0;
1361 }
1362
1363 mRenderState.stencil().enableWrite(incrementThreshold);
1364
1365 // Clean and update the stencil, but first make sure we restrict drawing
1366 // to the region's bounds
1367 bool resetScissor = mRenderState.scissor().setEnabled(true);
1368 if (resetScissor) {
1369 // The scissor was not set so we now need to update it
1370 setScissorFromClip();
1371 }
1372
1373 mRenderState.stencil().clear();
1374
1375 // stash and disable the outline clip state, since stencil doesn't account for outline
1376 bool storedSkipOutlineClip = mSkipOutlineClip;
1377 mSkipOutlineClip = true;
1378
1379 SkPaint paint;
1380 paint.setColor(SK_ColorBLACK);
1381 paint.setXfermodeMode(SkXfermode::kSrc_Mode);
1382
1383 if (isRectangleList) {
1384 drawRectangleList(clipArea.getRectangleList());
1385 } else {
1386 // NOTE: We could use the region contour path to generate a smaller mesh
1387 // Since we are using the stencil we could use the red book path
1388 // drawing technique. It might increase bandwidth usage though.
1389
1390 // The last parameter is important: we are not drawing in the color buffer
1391 // so we don't want to dirty the current layer, if any
1392 drawRegionRects(clipArea.getClipRegion(), paint, false);
1393 }
1394 if (resetScissor) mRenderState.scissor().setEnabled(false);
1395 mSkipOutlineClip = storedSkipOutlineClip;
1396
1397 mRenderState.stencil().enableTest(incrementThreshold);
1398
1399 // Draw the region used to generate the stencil if the appropriate debug
1400 // mode is enabled
1401 // TODO: Implement for rectangle list clip areas
1402 if (Properties::debugStencilClip == StencilClipDebug::ShowRegion
1403 && !clipArea.isRectangleList()) {
1404 paint.setColor(0x7f0000ff);
1405 paint.setXfermodeMode(SkXfermode::kSrcOver_Mode);
1406 drawRegionRects(currentSnapshot()->getClipRegion(), paint);
1407 }
1408 } else {
1409 EVENT_LOGD("setStencilFromClip - disabling");
1410 mRenderState.stencil().disable();
1411 }
1412 }
1413 }
1414
1415 /**
1416 * Returns false and sets scissor enable based upon bounds if drawing won't be clipped out.
1417 *
1418 * @param paint if not null, the bounds will be expanded to account for stroke depending on paint
1419 * style, and tessellated AA ramp
1420 */
quickRejectSetupScissor(float left,float top,float right,float bottom,const SkPaint * paint)1421 bool OpenGLRenderer::quickRejectSetupScissor(float left, float top, float right, float bottom,
1422 const SkPaint* paint) {
1423 bool snapOut = paint && paint->isAntiAlias();
1424
1425 if (paint && paint->getStyle() != SkPaint::kFill_Style) {
1426 float outset = paint->getStrokeWidth() * 0.5f;
1427 left -= outset;
1428 top -= outset;
1429 right += outset;
1430 bottom += outset;
1431 }
1432
1433 bool clipRequired = false;
1434 bool roundRectClipRequired = false;
1435 if (mState.calculateQuickRejectForScissor(left, top, right, bottom,
1436 &clipRequired, &roundRectClipRequired, snapOut)) {
1437 return true;
1438 }
1439
1440 // not quick rejected, so enable the scissor if clipRequired
1441 mRenderState.scissor().setEnabled(mScissorOptimizationDisabled || clipRequired);
1442 mSkipOutlineClip = !roundRectClipRequired;
1443 return false;
1444 }
1445
debugClip()1446 void OpenGLRenderer::debugClip() {
1447 #if DEBUG_CLIP_REGIONS
1448 if (!currentSnapshot()->clipRegion->isEmpty()) {
1449 SkPaint paint;
1450 paint.setColor(0x7f00ff00);
1451 drawRegionRects(*(currentSnapshot()->clipRegion, paint);
1452
1453 }
1454 #endif
1455 }
1456
1457 void OpenGLRenderer::renderGlop(const Glop& glop, GlopRenderType type) {
1458 // TODO: It would be best if we could do this before quickRejectSetupScissor()
1459 // changes the scissor test state
1460 if (type != GlopRenderType::LayerClear) {
1461 // Regular draws need to clear the dirty area on the layer before they start drawing on top
1462 // of it. If this draw *is* a layer clear, it skips the clear step (since it would
1463 // infinitely recurse)
1464 clearLayerRegions();
1465 }
1466
1467 if (mState.getDirtyClip()) {
1468 if (mRenderState.scissor().isEnabled()) {
1469 setScissorFromClip();
1470 }
1471
1472 setStencilFromClip();
1473 }
1474 mRenderState.render(glop);
1475 if (type == GlopRenderType::Standard && !mRenderState.stencil().isWriteEnabled()) {
1476 // TODO: specify more clearly when a draw should dirty the layer.
1477 // is writing to the stencil the only time we should ignore this?
1478 dirtyLayer(glop.bounds.left, glop.bounds.top, glop.bounds.right, glop.bounds.bottom);
1479 mDirty = true;
1480 }
1481 }
1482
1483 ///////////////////////////////////////////////////////////////////////////////
1484 // Drawing
1485 ///////////////////////////////////////////////////////////////////////////////
1486
1487 void OpenGLRenderer::drawRenderNode(RenderNode* renderNode, Rect& dirty, int32_t replayFlags) {
1488 // All the usual checks and setup operations (quickReject, setupDraw, etc.)
1489 // will be performed by the display list itself
1490 if (renderNode && renderNode->isRenderable()) {
1491 // compute 3d ordering
1492 renderNode->computeOrdering();
1493 if (CC_UNLIKELY(Properties::drawDeferDisabled)) {
1494 startFrame();
1495 ReplayStateStruct replayStruct(*this, dirty, replayFlags);
1496 renderNode->replay(replayStruct, 0);
1497 return;
1498 }
1499
1500 // Don't avoid overdraw when visualizing, since that makes it harder to
1501 // debug where it's coming from, and when the problem occurs.
1502 bool avoidOverdraw = !Properties::debugOverdraw;
1503 DeferredDisplayList deferredList(mState.currentClipRect(), avoidOverdraw);
1504 DeferStateStruct deferStruct(deferredList, *this, replayFlags);
1505 renderNode->defer(deferStruct, 0);
1506
1507 flushLayers();
1508 startFrame();
1509
1510 deferredList.flush(*this, dirty);
1511 } else {
1512 // Even if there is no drawing command(Ex: invisible),
1513 // it still needs startFrame to clear buffer and start tiling.
1514 startFrame();
1515 }
1516 }
1517
1518 /**
1519 * Important note: this method is intended to draw batches of bitmaps and
1520 * will not set the scissor enable or dirty the current layer, if any.
1521 * The caller is responsible for properly dirtying the current layer.
1522 */
1523 void OpenGLRenderer::drawBitmaps(const SkBitmap* bitmap, AssetAtlas::Entry* entry,
1524 int bitmapCount, TextureVertex* vertices, bool pureTranslate,
1525 const Rect& bounds, const SkPaint* paint) {
1526 Texture* texture = entry ? entry->texture : mCaches.textureCache.get(bitmap);
1527 if (!texture) return;
1528
1529 const AutoTexture autoCleanup(texture);
1530
1531 // TODO: remove layer dirty in multi-draw callers
1532 // TODO: snap doesn't need to touch transform, only texture filter.
1533 bool snap = pureTranslate;
1534 const float x = floorf(bounds.left + 0.5f);
1535 const float y = floorf(bounds.top + 0.5f);
1536
1537 const int textureFillFlags = (bitmap->colorType() == kAlpha_8_SkColorType)
1538 ? TextureFillFlags::IsAlphaMaskTexture : TextureFillFlags::None;
1539 const int transformFlags = TransformFlags::MeshIgnoresCanvasTransform;
1540 Glop glop;
1541 GlopBuilder(mRenderState, mCaches, &glop)
1542 .setRoundRectClipState(currentSnapshot()->roundRectClipState)
1543 .setMeshTexturedMesh(vertices, bitmapCount * 6)
1544 .setFillTexturePaint(*texture, textureFillFlags, paint, currentSnapshot()->alpha)
1545 .setTransform(*currentSnapshot(), transformFlags)
1546 .setModelViewOffsetRectOptionalSnap(snap, x, y, Rect(0, 0, bounds.getWidth(), bounds.getHeight()))
1547 .build();
1548 renderGlop(glop, GlopRenderType::Multi);
1549 }
1550
1551 void OpenGLRenderer::drawBitmap(const SkBitmap* bitmap, const SkPaint* paint) {
1552 if (quickRejectSetupScissor(0, 0, bitmap->width(), bitmap->height())) {
1553 return;
1554 }
1555
1556 mCaches.textureState().activateTexture(0);
1557 Texture* texture = getTexture(bitmap);
1558 if (!texture) return;
1559 const AutoTexture autoCleanup(texture);
1560
1561 const int textureFillFlags = (bitmap->colorType() == kAlpha_8_SkColorType)
1562 ? TextureFillFlags::IsAlphaMaskTexture : TextureFillFlags::None;
1563 Glop glop;
1564 GlopBuilder(mRenderState, mCaches, &glop)
1565 .setRoundRectClipState(currentSnapshot()->roundRectClipState)
1566 .setMeshTexturedUnitQuad(texture->uvMapper)
1567 .setFillTexturePaint(*texture, textureFillFlags, paint, currentSnapshot()->alpha)
1568 .setTransform(*currentSnapshot(), TransformFlags::None)
1569 .setModelViewMapUnitToRectSnap(Rect(0, 0, texture->width, texture->height))
1570 .build();
1571 renderGlop(glop);
1572 }
1573
1574 void OpenGLRenderer::drawBitmapMesh(const SkBitmap* bitmap, int meshWidth, int meshHeight,
1575 const float* vertices, const int* colors, const SkPaint* paint) {
1576 if (!vertices || mState.currentlyIgnored()) {
1577 return;
1578 }
1579
1580 float left = FLT_MAX;
1581 float top = FLT_MAX;
1582 float right = FLT_MIN;
1583 float bottom = FLT_MIN;
1584
1585 const uint32_t elementCount = meshWidth * meshHeight * 6;
1586
1587 std::unique_ptr<ColorTextureVertex[]> mesh(new ColorTextureVertex[elementCount]);
1588 ColorTextureVertex* vertex = &mesh[0];
1589
1590 std::unique_ptr<int[]> tempColors;
1591 if (!colors) {
1592 uint32_t colorsCount = (meshWidth + 1) * (meshHeight + 1);
1593 tempColors.reset(new int[colorsCount]);
1594 memset(tempColors.get(), 0xff, colorsCount * sizeof(int));
1595 colors = tempColors.get();
1596 }
1597
1598 Texture* texture = mRenderState.assetAtlas().getEntryTexture(bitmap);
1599 const UvMapper& mapper(getMapper(texture));
1600
1601 for (int32_t y = 0; y < meshHeight; y++) {
1602 for (int32_t x = 0; x < meshWidth; x++) {
1603 uint32_t i = (y * (meshWidth + 1) + x) * 2;
1604
1605 float u1 = float(x) / meshWidth;
1606 float u2 = float(x + 1) / meshWidth;
1607 float v1 = float(y) / meshHeight;
1608 float v2 = float(y + 1) / meshHeight;
1609
1610 mapper.map(u1, v1, u2, v2);
1611
1612 int ax = i + (meshWidth + 1) * 2;
1613 int ay = ax + 1;
1614 int bx = i;
1615 int by = bx + 1;
1616 int cx = i + 2;
1617 int cy = cx + 1;
1618 int dx = i + (meshWidth + 1) * 2 + 2;
1619 int dy = dx + 1;
1620
1621 ColorTextureVertex::set(vertex++, vertices[dx], vertices[dy], u2, v2, colors[dx / 2]);
1622 ColorTextureVertex::set(vertex++, vertices[ax], vertices[ay], u1, v2, colors[ax / 2]);
1623 ColorTextureVertex::set(vertex++, vertices[bx], vertices[by], u1, v1, colors[bx / 2]);
1624
1625 ColorTextureVertex::set(vertex++, vertices[dx], vertices[dy], u2, v2, colors[dx / 2]);
1626 ColorTextureVertex::set(vertex++, vertices[bx], vertices[by], u1, v1, colors[bx / 2]);
1627 ColorTextureVertex::set(vertex++, vertices[cx], vertices[cy], u2, v1, colors[cx / 2]);
1628
1629 left = std::min(left, std::min(vertices[ax], std::min(vertices[bx], vertices[cx])));
1630 top = std::min(top, std::min(vertices[ay], std::min(vertices[by], vertices[cy])));
1631 right = std::max(right, std::max(vertices[ax], std::max(vertices[bx], vertices[cx])));
1632 bottom = std::max(bottom, std::max(vertices[ay], std::max(vertices[by], vertices[cy])));
1633 }
1634 }
1635
1636 if (quickRejectSetupScissor(left, top, right, bottom)) {
1637 return;
1638 }
1639
1640 if (!texture) {
1641 texture = mCaches.textureCache.get(bitmap);
1642 if (!texture) {
1643 return;
1644 }
1645 }
1646 const AutoTexture autoCleanup(texture);
1647
1648 /*
1649 * TODO: handle alpha_8 textures correctly by applying paint color, but *not*
1650 * shader in that case to mimic the behavior in SkiaCanvas::drawBitmapMesh.
1651 */
1652 const int textureFillFlags = TextureFillFlags::None;
1653 Glop glop;
1654 GlopBuilder(mRenderState, mCaches, &glop)
1655 .setRoundRectClipState(currentSnapshot()->roundRectClipState)
1656 .setMeshColoredTexturedMesh(mesh.get(), elementCount)
1657 .setFillTexturePaint(*texture, textureFillFlags, paint, currentSnapshot()->alpha)
1658 .setTransform(*currentSnapshot(), TransformFlags::None)
1659 .setModelViewOffsetRect(0, 0, Rect(left, top, right, bottom))
1660 .build();
1661 renderGlop(glop);
1662 }
1663
1664 void OpenGLRenderer::drawBitmap(const SkBitmap* bitmap, Rect src, Rect dst, const SkPaint* paint) {
1665 if (quickRejectSetupScissor(dst)) {
1666 return;
1667 }
1668
1669 Texture* texture = getTexture(bitmap);
1670 if (!texture) return;
1671 const AutoTexture autoCleanup(texture);
1672
1673 Rect uv(std::max(0.0f, src.left / texture->width),
1674 std::max(0.0f, src.top / texture->height),
1675 std::min(1.0f, src.right / texture->width),
1676 std::min(1.0f, src.bottom / texture->height));
1677
1678 const int textureFillFlags = (bitmap->colorType() == kAlpha_8_SkColorType)
1679 ? TextureFillFlags::IsAlphaMaskTexture : TextureFillFlags::None;
1680 const bool tryToSnap = MathUtils::areEqual(src.getWidth(), dst.getWidth())
1681 && MathUtils::areEqual(src.getHeight(), dst.getHeight());
1682 Glop glop;
1683 GlopBuilder(mRenderState, mCaches, &glop)
1684 .setRoundRectClipState(currentSnapshot()->roundRectClipState)
1685 .setMeshTexturedUvQuad(texture->uvMapper, uv)
1686 .setFillTexturePaint(*texture, textureFillFlags, paint, currentSnapshot()->alpha)
1687 .setTransform(*currentSnapshot(), TransformFlags::None)
1688 .setModelViewMapUnitToRectOptionalSnap(tryToSnap, dst)
1689 .build();
1690 renderGlop(glop);
1691 }
1692
1693 void OpenGLRenderer::drawPatch(const SkBitmap* bitmap, const Patch* mesh,
1694 AssetAtlas::Entry* entry, float left, float top, float right, float bottom,
1695 const SkPaint* paint) {
1696 if (!mesh || !mesh->verticesCount || quickRejectSetupScissor(left, top, right, bottom)) {
1697 return;
1698 }
1699
1700 Texture* texture = entry ? entry->texture : mCaches.textureCache.get(bitmap);
1701 if (!texture) return;
1702
1703 // 9 patches are built for stretching - always filter
1704 int textureFillFlags = TextureFillFlags::ForceFilter;
1705 if (bitmap->colorType() == kAlpha_8_SkColorType) {
1706 textureFillFlags |= TextureFillFlags::IsAlphaMaskTexture;
1707 }
1708 Glop glop;
1709 GlopBuilder(mRenderState, mCaches, &glop)
1710 .setRoundRectClipState(currentSnapshot()->roundRectClipState)
1711 .setMeshPatchQuads(*mesh)
1712 .setFillTexturePaint(*texture, textureFillFlags, paint, currentSnapshot()->alpha)
1713 .setTransform(*currentSnapshot(), TransformFlags::None)
1714 .setModelViewOffsetRectSnap(left, top, Rect(0, 0, right - left, bottom - top)) // TODO: get minimal bounds from patch
1715 .build();
1716 renderGlop(glop);
1717 }
1718
1719 /**
1720 * Important note: this method is intended to draw batches of 9-patch objects and
1721 * will not set the scissor enable or dirty the current layer, if any.
1722 * The caller is responsible for properly dirtying the current layer.
1723 */
1724 void OpenGLRenderer::drawPatches(const SkBitmap* bitmap, AssetAtlas::Entry* entry,
1725 TextureVertex* vertices, uint32_t elementCount, const SkPaint* paint) {
1726 mCaches.textureState().activateTexture(0);
1727 Texture* texture = entry ? entry->texture : mCaches.textureCache.get(bitmap);
1728 if (!texture) return;
1729 const AutoTexture autoCleanup(texture);
1730
1731 // TODO: get correct bounds from caller
1732 const int transformFlags = TransformFlags::MeshIgnoresCanvasTransform;
1733 // 9 patches are built for stretching - always filter
1734 int textureFillFlags = TextureFillFlags::ForceFilter;
1735 if (bitmap->colorType() == kAlpha_8_SkColorType) {
1736 textureFillFlags |= TextureFillFlags::IsAlphaMaskTexture;
1737 }
1738 Glop glop;
1739 GlopBuilder(mRenderState, mCaches, &glop)
1740 .setRoundRectClipState(currentSnapshot()->roundRectClipState)
1741 .setMeshTexturedIndexedQuads(vertices, elementCount)
1742 .setFillTexturePaint(*texture, textureFillFlags, paint, currentSnapshot()->alpha)
1743 .setTransform(*currentSnapshot(), transformFlags)
1744 .setModelViewOffsetRect(0, 0, Rect(0, 0, 0, 0))
1745 .build();
1746 renderGlop(glop, GlopRenderType::Multi);
1747 }
1748
1749 void OpenGLRenderer::drawVertexBuffer(float translateX, float translateY,
1750 const VertexBuffer& vertexBuffer, const SkPaint* paint, int displayFlags) {
1751 // not missing call to quickReject/dirtyLayer, always done at a higher level
1752 if (!vertexBuffer.getVertexCount()) {
1753 // no vertices to draw
1754 return;
1755 }
1756
1757 bool shadowInterp = displayFlags & kVertexBuffer_ShadowInterp;
1758 const int transformFlags = TransformFlags::OffsetByFudgeFactor;
1759 Glop glop;
1760 GlopBuilder(mRenderState, mCaches, &glop)
1761 .setRoundRectClipState(currentSnapshot()->roundRectClipState)
1762 .setMeshVertexBuffer(vertexBuffer, shadowInterp)
1763 .setFillPaint(*paint, currentSnapshot()->alpha)
1764 .setTransform(*currentSnapshot(), transformFlags)
1765 .setModelViewOffsetRect(translateX, translateY, vertexBuffer.getBounds())
1766 .build();
1767 renderGlop(glop);
1768 }
1769
1770 /**
1771 * Renders a convex path via tessellation. For AA paths, this function uses a similar approach to
1772 * that of AA lines in the drawLines() function. We expand the convex path by a half pixel in
1773 * screen space in all directions. However, instead of using a fragment shader to compute the
1774 * translucency of the color from its position, we simply use a varying parameter to define how far
1775 * a given pixel is from the edge. For non-AA paths, the expansion and alpha varying are not used.
1776 *
1777 * Doesn't yet support joins, caps, or path effects.
1778 */
1779 void OpenGLRenderer::drawConvexPath(const SkPath& path, const SkPaint* paint) {
1780 VertexBuffer vertexBuffer;
1781 // TODO: try clipping large paths to viewport
1782
1783 PathTessellator::tessellatePath(path, paint, *currentTransform(), vertexBuffer);
1784 drawVertexBuffer(vertexBuffer, paint);
1785 }
1786
1787 /**
1788 * We create tristrips for the lines much like shape stroke tessellation, using a per-vertex alpha
1789 * and additional geometry for defining an alpha slope perimeter.
1790 *
1791 * Using GL_LINES can be difficult because the rasterization rules for those lines produces some
1792 * unexpected results, and may vary between hardware devices. Previously we used a varying-base
1793 * in-shader alpha region, but found it to be taxing on some GPUs.
1794 *
1795 * TODO: try using a fixed input buffer for non-capped lines as in text rendering. this may reduce
1796 * memory transfer by removing need for degenerate vertices.
1797 */
1798 void OpenGLRenderer::drawLines(const float* points, int count, const SkPaint* paint) {
1799 if (mState.currentlyIgnored() || count < 4) return;
1800
1801 count &= ~0x3; // round down to nearest four
1802
1803 VertexBuffer buffer;
1804 PathTessellator::tessellateLines(points, count, paint, *currentTransform(), buffer);
1805 const Rect& bounds = buffer.getBounds();
1806
1807 if (quickRejectSetupScissor(bounds.left, bounds.top, bounds.right, bounds.bottom)) {
1808 return;
1809 }
1810
1811 int displayFlags = paint->isAntiAlias() ? 0 : kVertexBuffer_Offset;
1812 drawVertexBuffer(buffer, paint, displayFlags);
1813 }
1814
1815 void OpenGLRenderer::drawPoints(const float* points, int count, const SkPaint* paint) {
1816 if (mState.currentlyIgnored() || count < 2) return;
1817
1818 count &= ~0x1; // round down to nearest two
1819
1820 VertexBuffer buffer;
1821 PathTessellator::tessellatePoints(points, count, paint, *currentTransform(), buffer);
1822
1823 const Rect& bounds = buffer.getBounds();
1824 if (quickRejectSetupScissor(bounds.left, bounds.top, bounds.right, bounds.bottom)) {
1825 return;
1826 }
1827
1828 int displayFlags = paint->isAntiAlias() ? 0 : kVertexBuffer_Offset;
1829 drawVertexBuffer(buffer, paint, displayFlags);
1830
1831 mDirty = true;
1832 }
1833
1834 void OpenGLRenderer::drawColor(int color, SkXfermode::Mode mode) {
1835 // No need to check against the clip, we fill the clip region
1836 if (mState.currentlyIgnored()) return;
1837
1838 Rect clip(mState.currentClipRect());
1839 clip.snapToPixelBoundaries();
1840
1841 SkPaint paint;
1842 paint.setColor(color);
1843 paint.setXfermodeMode(mode);
1844
1845 drawColorRect(clip.left, clip.top, clip.right, clip.bottom, &paint, true);
1846
1847 mDirty = true;
1848 }
1849
1850 void OpenGLRenderer::drawShape(float left, float top, PathTexture* texture,
1851 const SkPaint* paint) {
1852 if (!texture) return;
1853 const AutoTexture autoCleanup(texture);
1854
1855 const float x = left + texture->left - texture->offset;
1856 const float y = top + texture->top - texture->offset;
1857
1858 drawPathTexture(texture, x, y, paint);
1859
1860 mDirty = true;
1861 }
1862
1863 void OpenGLRenderer::drawRoundRect(float left, float top, float right, float bottom,
1864 float rx, float ry, const SkPaint* p) {
1865 if (mState.currentlyIgnored()
1866 || quickRejectSetupScissor(left, top, right, bottom, p)
1867 || PaintUtils::paintWillNotDraw(*p)) {
1868 return;
1869 }
1870
1871 if (p->getPathEffect() != nullptr) {
1872 mCaches.textureState().activateTexture(0);
1873 PathTexture* texture = mCaches.pathCache.getRoundRect(
1874 right - left, bottom - top, rx, ry, p);
1875 drawShape(left, top, texture, p);
1876 } else {
1877 const VertexBuffer* vertexBuffer = mCaches.tessellationCache.getRoundRect(
1878 *currentTransform(), *p, right - left, bottom - top, rx, ry);
1879 drawVertexBuffer(left, top, *vertexBuffer, p);
1880 }
1881 }
1882
1883 void OpenGLRenderer::drawCircle(float x, float y, float radius, const SkPaint* p) {
1884 if (mState.currentlyIgnored()
1885 || quickRejectSetupScissor(x - radius, y - radius, x + radius, y + radius, p)
1886 || PaintUtils::paintWillNotDraw(*p)) {
1887 return;
1888 }
1889
1890 if (p->getPathEffect() != nullptr) {
1891 mCaches.textureState().activateTexture(0);
1892 PathTexture* texture = mCaches.pathCache.getCircle(radius, p);
1893 drawShape(x - radius, y - radius, texture, p);
1894 return;
1895 }
1896
1897 SkPath path;
1898 if (p->getStyle() == SkPaint::kStrokeAndFill_Style) {
1899 path.addCircle(x, y, radius + p->getStrokeWidth() / 2);
1900 } else {
1901 path.addCircle(x, y, radius);
1902 }
1903
1904 if (CC_UNLIKELY(currentSnapshot()->projectionPathMask != nullptr)) {
1905 // mask ripples with projection mask
1906 SkPath maskPath = *(currentSnapshot()->projectionPathMask->projectionMask);
1907
1908 Matrix4 screenSpaceTransform;
1909 currentSnapshot()->buildScreenSpaceTransform(&screenSpaceTransform);
1910
1911 Matrix4 totalTransform;
1912 totalTransform.loadInverse(screenSpaceTransform);
1913 totalTransform.multiply(currentSnapshot()->projectionPathMask->projectionMaskTransform);
1914
1915 SkMatrix skTotalTransform;
1916 totalTransform.copyTo(skTotalTransform);
1917 maskPath.transform(skTotalTransform);
1918
1919 // Mask the ripple path by the projection mask, now that it's
1920 // in local space. Note that this can create CCW paths.
1921 Op(path, maskPath, kIntersect_PathOp, &path);
1922 }
1923 drawConvexPath(path, p);
1924 }
1925
1926 void OpenGLRenderer::drawOval(float left, float top, float right, float bottom,
1927 const SkPaint* p) {
1928 if (mState.currentlyIgnored()
1929 || quickRejectSetupScissor(left, top, right, bottom, p)
1930 || PaintUtils::paintWillNotDraw(*p)) {
1931 return;
1932 }
1933
1934 if (p->getPathEffect() != nullptr) {
1935 mCaches.textureState().activateTexture(0);
1936 PathTexture* texture = mCaches.pathCache.getOval(right - left, bottom - top, p);
1937 drawShape(left, top, texture, p);
1938 } else {
1939 SkPath path;
1940 SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
1941 if (p->getStyle() == SkPaint::kStrokeAndFill_Style) {
1942 rect.outset(p->getStrokeWidth() / 2, p->getStrokeWidth() / 2);
1943 }
1944 path.addOval(rect);
1945 drawConvexPath(path, p);
1946 }
1947 }
1948
1949 void OpenGLRenderer::drawArc(float left, float top, float right, float bottom,
1950 float startAngle, float sweepAngle, bool useCenter, const SkPaint* p) {
1951 if (mState.currentlyIgnored()
1952 || quickRejectSetupScissor(left, top, right, bottom, p)
1953 || PaintUtils::paintWillNotDraw(*p)) {
1954 return;
1955 }
1956
1957 // TODO: support fills (accounting for concavity if useCenter && sweepAngle > 180)
1958 if (p->getStyle() != SkPaint::kStroke_Style || p->getPathEffect() != nullptr || useCenter) {
1959 mCaches.textureState().activateTexture(0);
1960 PathTexture* texture = mCaches.pathCache.getArc(right - left, bottom - top,
1961 startAngle, sweepAngle, useCenter, p);
1962 drawShape(left, top, texture, p);
1963 return;
1964 }
1965 SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
1966 if (p->getStyle() == SkPaint::kStrokeAndFill_Style) {
1967 rect.outset(p->getStrokeWidth() / 2, p->getStrokeWidth() / 2);
1968 }
1969
1970 SkPath path;
1971 if (useCenter) {
1972 path.moveTo(rect.centerX(), rect.centerY());
1973 }
1974 path.arcTo(rect, startAngle, sweepAngle, !useCenter);
1975 if (useCenter) {
1976 path.close();
1977 }
1978 drawConvexPath(path, p);
1979 }
1980
1981 // See SkPaintDefaults.h
1982 #define SkPaintDefaults_MiterLimit SkIntToScalar(4)
1983
1984 void OpenGLRenderer::drawRect(float left, float top, float right, float bottom,
1985 const SkPaint* p) {
1986 if (mState.currentlyIgnored()
1987 || quickRejectSetupScissor(left, top, right, bottom, p)
1988 || PaintUtils::paintWillNotDraw(*p)) {
1989 return;
1990 }
1991
1992 if (p->getStyle() != SkPaint::kFill_Style) {
1993 // only fill style is supported by drawConvexPath, since others have to handle joins
1994 if (p->getPathEffect() != nullptr || p->getStrokeJoin() != SkPaint::kMiter_Join ||
1995 p->getStrokeMiter() != SkPaintDefaults_MiterLimit) {
1996 mCaches.textureState().activateTexture(0);
1997 PathTexture* texture =
1998 mCaches.pathCache.getRect(right - left, bottom - top, p);
1999 drawShape(left, top, texture, p);
2000 } else {
2001 SkPath path;
2002 SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
2003 if (p->getStyle() == SkPaint::kStrokeAndFill_Style) {
2004 rect.outset(p->getStrokeWidth() / 2, p->getStrokeWidth() / 2);
2005 }
2006 path.addRect(rect);
2007 drawConvexPath(path, p);
2008 }
2009 } else {
2010 if (p->isAntiAlias() && !currentTransform()->isSimple()) {
2011 SkPath path;
2012 path.addRect(left, top, right, bottom);
2013 drawConvexPath(path, p);
2014 } else {
2015 drawColorRect(left, top, right, bottom, p);
2016
2017 mDirty = true;
2018 }
2019 }
2020 }
2021
2022 void OpenGLRenderer::drawTextShadow(const SkPaint* paint, const char* text,
2023 int bytesCount, int count, const float* positions,
2024 FontRenderer& fontRenderer, int alpha, float x, float y) {
2025 mCaches.textureState().activateTexture(0);
2026
2027 TextShadow textShadow;
2028 if (!getTextShadow(paint, &textShadow)) {
2029 LOG_ALWAYS_FATAL("failed to query shadow attributes");
2030 }
2031
2032 // NOTE: The drop shadow will not perform gamma correction
2033 // if shader-based correction is enabled
2034 mCaches.dropShadowCache.setFontRenderer(fontRenderer);
2035 ShadowTexture* texture = mCaches.dropShadowCache.get(
2036 paint, text, bytesCount, count, textShadow.radius, positions);
2037 // If the drop shadow exceeds the max texture size or couldn't be
2038 // allocated, skip drawing
2039 if (!texture) return;
2040 const AutoTexture autoCleanup(texture);
2041
2042 const float sx = x - texture->left + textShadow.dx;
2043 const float sy = y - texture->top + textShadow.dy;
2044
2045 Glop glop;
2046 GlopBuilder(mRenderState, mCaches, &glop)
2047 .setRoundRectClipState(currentSnapshot()->roundRectClipState)
2048 .setMeshTexturedUnitQuad(nullptr)
2049 .setFillShadowTexturePaint(*texture, textShadow.color, *paint, currentSnapshot()->alpha)
2050 .setTransform(*currentSnapshot(), TransformFlags::None)
2051 .setModelViewMapUnitToRect(Rect(sx, sy, sx + texture->width, sy + texture->height))
2052 .build();
2053 renderGlop(glop);
2054 }
2055
2056 bool OpenGLRenderer::canSkipText(const SkPaint* paint) const {
2057 float alpha = (hasTextShadow(paint) ? 1.0f : paint->getAlpha()) * currentSnapshot()->alpha;
2058 return MathUtils::isZero(alpha)
2059 && PaintUtils::getXfermode(paint->getXfermode()) == SkXfermode::kSrcOver_Mode;
2060 }
2061
2062 void OpenGLRenderer::drawPosText(const char* text, int bytesCount, int count,
2063 const float* positions, const SkPaint* paint) {
2064 if (text == nullptr || count == 0 || mState.currentlyIgnored() || canSkipText(paint)) {
2065 return;
2066 }
2067
2068 // NOTE: Skia does not support perspective transform on drawPosText yet
2069 if (!currentTransform()->isSimple()) {
2070 return;
2071 }
2072
2073 mRenderState.scissor().setEnabled(true);
2074
2075 float x = 0.0f;
2076 float y = 0.0f;
2077 const bool pureTranslate = currentTransform()->isPureTranslate();
2078 if (pureTranslate) {
2079 x = floorf(x + currentTransform()->getTranslateX() + 0.5f);
2080 y = floorf(y + currentTransform()->getTranslateY() + 0.5f);
2081 }
2082
2083 FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint);
2084 fontRenderer.setFont(paint, SkMatrix::I());
2085
2086 int alpha;
2087 SkXfermode::Mode mode;
2088 getAlphaAndMode(paint, &alpha, &mode);
2089
2090 if (CC_UNLIKELY(hasTextShadow(paint))) {
2091 drawTextShadow(paint, text, bytesCount, count, positions, fontRenderer,
2092 alpha, 0.0f, 0.0f);
2093 }
2094
2095 // Pick the appropriate texture filtering
2096 bool linearFilter = currentTransform()->changesBounds();
2097 if (pureTranslate && !linearFilter) {
2098 linearFilter = fabs(y - (int) y) > 0.0f || fabs(x - (int) x) > 0.0f;
2099 }
2100 fontRenderer.setTextureFiltering(linearFilter);
2101
2102 const Rect& clip(pureTranslate ? writableSnapshot()->getClipRect() : writableSnapshot()->getLocalClip());
2103 Rect bounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
2104
2105 TextDrawFunctor functor(this, x, y, pureTranslate, alpha, mode, paint);
2106 if (fontRenderer.renderPosText(paint, &clip, text, 0, bytesCount, count, x, y,
2107 positions, hasLayer() ? &bounds : nullptr, &functor)) {
2108 dirtyLayer(bounds.left, bounds.top, bounds.right, bounds.bottom, *currentTransform());
2109 mDirty = true;
2110 }
2111
2112 }
2113
2114 bool OpenGLRenderer::findBestFontTransform(const mat4& transform, SkMatrix* outMatrix) const {
2115 if (CC_LIKELY(transform.isPureTranslate())) {
2116 outMatrix->setIdentity();
2117 return false;
2118 } else if (CC_UNLIKELY(transform.isPerspective())) {
2119 outMatrix->setIdentity();
2120 return true;
2121 }
2122
2123 /**
2124 * Input is a non-perspective, scaling transform. Generate a scale-only transform,
2125 * with values rounded to the nearest int.
2126 */
2127 float sx, sy;
2128 transform.decomposeScale(sx, sy);
2129 outMatrix->setScale(
2130 roundf(std::max(1.0f, sx)),
2131 roundf(std::max(1.0f, sy)));
2132 return true;
2133 }
2134
2135 int OpenGLRenderer::getSaveCount() const {
2136 return mState.getSaveCount();
2137 }
2138
2139 int OpenGLRenderer::save(int flags) {
2140 return mState.save(flags);
2141 }
2142
2143 void OpenGLRenderer::restore() {
2144 mState.restore();
2145 }
2146
2147 void OpenGLRenderer::restoreToCount(int saveCount) {
2148 mState.restoreToCount(saveCount);
2149 }
2150
2151
2152 void OpenGLRenderer::translate(float dx, float dy, float dz) {
2153 mState.translate(dx, dy, dz);
2154 }
2155
2156 void OpenGLRenderer::rotate(float degrees) {
2157 mState.rotate(degrees);
2158 }
2159
2160 void OpenGLRenderer::scale(float sx, float sy) {
2161 mState.scale(sx, sy);
2162 }
2163
2164 void OpenGLRenderer::skew(float sx, float sy) {
2165 mState.skew(sx, sy);
2166 }
2167
2168 void OpenGLRenderer::setMatrix(const Matrix4& matrix) {
2169 mState.setMatrix(matrix);
2170 }
2171
2172 void OpenGLRenderer::setLocalMatrix(const SkMatrix& matrix) {
2173 mState.setMatrix(mBaseTransform);
2174 mState.concatMatrix(matrix);
2175 }
2176
2177 void OpenGLRenderer::concatMatrix(const Matrix4& matrix) {
2178 mState.concatMatrix(matrix);
2179 }
2180
2181 bool OpenGLRenderer::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) {
2182 return mState.clipRect(left, top, right, bottom, op);
2183 }
2184
2185 bool OpenGLRenderer::clipPath(const SkPath* path, SkRegion::Op op) {
2186 return mState.clipPath(path, op);
2187 }
2188
2189 bool OpenGLRenderer::clipRegion(const SkRegion* region, SkRegion::Op op) {
2190 return mState.clipRegion(region, op);
2191 }
2192
2193 void OpenGLRenderer::setClippingOutline(LinearAllocator& allocator, const Outline* outline) {
2194 mState.setClippingOutline(allocator, outline);
2195 }
2196
2197 void OpenGLRenderer::setClippingRoundRect(LinearAllocator& allocator,
2198 const Rect& rect, float radius, bool highPriority) {
2199 mState.setClippingRoundRect(allocator, rect, radius, highPriority);
2200 }
2201
2202 void OpenGLRenderer::setProjectionPathMask(LinearAllocator& allocator, const SkPath* path) {
2203 mState.setProjectionPathMask(allocator, path);
2204 }
2205
2206 void OpenGLRenderer::drawText(const char* text, int bytesCount, int count, float x, float y,
2207 const float* positions, const SkPaint* paint, float totalAdvance, const Rect& bounds,
2208 DrawOpMode drawOpMode) {
2209
2210 if (drawOpMode == DrawOpMode::kImmediate) {
2211 // The checks for corner-case ignorable text and quick rejection is only done for immediate
2212 // drawing as ops from DeferredDisplayList are already filtered for these
2213 if (text == nullptr || count == 0 || mState.currentlyIgnored() || canSkipText(paint) ||
2214 quickRejectSetupScissor(bounds)) {
2215 return;
2216 }
2217 }
2218
2219 const float oldX = x;
2220 const float oldY = y;
2221
2222 const mat4& transform = *currentTransform();
2223 const bool pureTranslate = transform.isPureTranslate();
2224
2225 if (CC_LIKELY(pureTranslate)) {
2226 x = floorf(x + transform.getTranslateX() + 0.5f);
2227 y = floorf(y + transform.getTranslateY() + 0.5f);
2228 }
2229
2230 int alpha;
2231 SkXfermode::Mode mode;
2232 getAlphaAndMode(paint, &alpha, &mode);
2233
2234 FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint);
2235
2236 if (CC_UNLIKELY(hasTextShadow(paint))) {
2237 fontRenderer.setFont(paint, SkMatrix::I());
2238 drawTextShadow(paint, text, bytesCount, count, positions, fontRenderer,
2239 alpha, oldX, oldY);
2240 }
2241
2242 const bool hasActiveLayer = hasLayer();
2243
2244 // We only pass a partial transform to the font renderer. That partial
2245 // matrix defines how glyphs are rasterized. Typically we want glyphs
2246 // to be rasterized at their final size on screen, which means the partial
2247 // matrix needs to take the scale factor into account.
2248 // When a partial matrix is used to transform glyphs during rasterization,
2249 // the mesh is generated with the inverse transform (in the case of scale,
2250 // the mesh is generated at 1.0 / scale for instance.) This allows us to
2251 // apply the full transform matrix at draw time in the vertex shader.
2252 // Applying the full matrix in the shader is the easiest way to handle
2253 // rotation and perspective and allows us to always generated quads in the
2254 // font renderer which greatly simplifies the code, clipping in particular.
2255 SkMatrix fontTransform;
2256 bool linearFilter = findBestFontTransform(transform, &fontTransform)
2257 || fabs(y - (int) y) > 0.0f
2258 || fabs(x - (int) x) > 0.0f;
2259 fontRenderer.setFont(paint, fontTransform);
2260 fontRenderer.setTextureFiltering(linearFilter);
2261
2262 // TODO: Implement better clipping for scaled/rotated text
2263 const Rect* clip = !pureTranslate ? nullptr : &mState.currentClipRect();
2264 Rect layerBounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
2265
2266 bool status;
2267 TextDrawFunctor functor(this, x, y, pureTranslate, alpha, mode, paint);
2268
2269 // don't call issuedrawcommand, do it at end of batch
2270 bool forceFinish = (drawOpMode != DrawOpMode::kDefer);
2271 if (CC_UNLIKELY(paint->getTextAlign() != SkPaint::kLeft_Align)) {
2272 SkPaint paintCopy(*paint);
2273 paintCopy.setTextAlign(SkPaint::kLeft_Align);
2274 status = fontRenderer.renderPosText(&paintCopy, clip, text, 0, bytesCount, count, x, y,
2275 positions, hasActiveLayer ? &layerBounds : nullptr, &functor, forceFinish);
2276 } else {
2277 status = fontRenderer.renderPosText(paint, clip, text, 0, bytesCount, count, x, y,
2278 positions, hasActiveLayer ? &layerBounds : nullptr, &functor, forceFinish);
2279 }
2280
2281 if ((status || drawOpMode != DrawOpMode::kImmediate) && hasActiveLayer) {
2282 if (!pureTranslate) {
2283 transform.mapRect(layerBounds);
2284 }
2285 dirtyLayerUnchecked(layerBounds, getRegion());
2286 }
2287
2288 drawTextDecorations(totalAdvance, oldX, oldY, paint);
2289
2290 mDirty = true;
2291 }
2292
2293 void OpenGLRenderer::drawTextOnPath(const char* text, int bytesCount, int count,
2294 const SkPath* path, float hOffset, float vOffset, const SkPaint* paint) {
2295 if (text == nullptr || count == 0 || mState.currentlyIgnored() || canSkipText(paint)) {
2296 return;
2297 }
2298
2299 // TODO: avoid scissor by calculating maximum bounds using path bounds + font metrics
2300 mRenderState.scissor().setEnabled(true);
2301
2302 FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint);
2303 fontRenderer.setFont(paint, SkMatrix::I());
2304 fontRenderer.setTextureFiltering(true);
2305
2306 int alpha;
2307 SkXfermode::Mode mode;
2308 getAlphaAndMode(paint, &alpha, &mode);
2309 TextDrawFunctor functor(this, 0.0f, 0.0f, false, alpha, mode, paint);
2310
2311 const Rect* clip = &writableSnapshot()->getLocalClip();
2312 Rect bounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
2313
2314 if (fontRenderer.renderTextOnPath(paint, clip, text, 0, bytesCount, count, path,
2315 hOffset, vOffset, hasLayer() ? &bounds : nullptr, &functor)) {
2316 dirtyLayer(bounds.left, bounds.top, bounds.right, bounds.bottom, *currentTransform());
2317 mDirty = true;
2318 }
2319 }
2320
2321 void OpenGLRenderer::drawPath(const SkPath* path, const SkPaint* paint) {
2322 if (mState.currentlyIgnored()) return;
2323
2324 mCaches.textureState().activateTexture(0);
2325
2326 PathTexture* texture = mCaches.pathCache.get(path, paint);
2327 if (!texture) return;
2328 const AutoTexture autoCleanup(texture);
2329
2330 const float x = texture->left - texture->offset;
2331 const float y = texture->top - texture->offset;
2332
2333 drawPathTexture(texture, x, y, paint);
2334 mDirty = true;
2335 }
2336
2337 void OpenGLRenderer::drawLayer(Layer* layer, float x, float y) {
2338 if (!layer) {
2339 return;
2340 }
2341
2342 mat4* transform = nullptr;
2343 if (layer->isTextureLayer()) {
2344 transform = &layer->getTransform();
2345 if (!transform->isIdentity()) {
2346 save(SkCanvas::kMatrix_SaveFlag);
2347 concatMatrix(*transform);
2348 }
2349 }
2350
2351 bool clipRequired = false;
2352 const bool rejected = mState.calculateQuickRejectForScissor(
2353 x, y, x + layer->layer.getWidth(), y + layer->layer.getHeight(),
2354 &clipRequired, nullptr, false);
2355
2356 if (rejected) {
2357 if (transform && !transform->isIdentity()) {
2358 restore();
2359 }
2360 return;
2361 }
2362
2363 EVENT_LOGD("drawLayer," RECT_STRING ", clipRequired %d", x, y,
2364 x + layer->layer.getWidth(), y + layer->layer.getHeight(), clipRequired);
2365
2366 updateLayer(layer, true);
2367
2368 mRenderState.scissor().setEnabled(mScissorOptimizationDisabled || clipRequired);
2369 mCaches.textureState().activateTexture(0);
2370
2371 if (CC_LIKELY(!layer->region.isEmpty())) {
2372 if (layer->region.isRect()) {
2373 DRAW_DOUBLE_STENCIL_IF(!layer->hasDrawnSinceUpdate,
2374 composeLayerRect(layer, layer->regionRect));
2375 } else if (layer->mesh) {
2376 Glop glop;
2377 GlopBuilder(mRenderState, mCaches, &glop)
2378 .setRoundRectClipState(currentSnapshot()->roundRectClipState)
2379 .setMeshTexturedIndexedQuads(layer->mesh, layer->meshElementCount)
2380 .setFillLayer(layer->getTexture(), layer->getColorFilter(), getLayerAlpha(layer), layer->getMode(), Blend::ModeOrderSwap::NoSwap)
2381 .setTransform(*currentSnapshot(), TransformFlags::None)
2382 .setModelViewOffsetRectSnap(x, y, Rect(0, 0, layer->layer.getWidth(), layer->layer.getHeight()))
2383 .build();
2384 DRAW_DOUBLE_STENCIL_IF(!layer->hasDrawnSinceUpdate, renderGlop(glop));
2385 #if DEBUG_LAYERS_AS_REGIONS
2386 drawRegionRectsDebug(layer->region);
2387 #endif
2388 }
2389
2390 if (layer->debugDrawUpdate) {
2391 layer->debugDrawUpdate = false;
2392
2393 SkPaint paint;
2394 paint.setColor(0x7f00ff00);
2395 drawColorRect(x, y, x + layer->layer.getWidth(), y + layer->layer.getHeight(), &paint);
2396 }
2397 }
2398 layer->hasDrawnSinceUpdate = true;
2399
2400 if (transform && !transform->isIdentity()) {
2401 restore();
2402 }
2403
2404 mDirty = true;
2405 }
2406
2407 ///////////////////////////////////////////////////////////////////////////////
2408 // Draw filters
2409 ///////////////////////////////////////////////////////////////////////////////
2410 void OpenGLRenderer::setDrawFilter(SkDrawFilter* filter) {
2411 // We should never get here since we apply the draw filter when stashing
2412 // the paints in the DisplayList.
2413 LOG_ALWAYS_FATAL("OpenGLRenderer does not directly support DrawFilters");
2414 }
2415
2416 ///////////////////////////////////////////////////////////////////////////////
2417 // Drawing implementation
2418 ///////////////////////////////////////////////////////////////////////////////
2419
2420 Texture* OpenGLRenderer::getTexture(const SkBitmap* bitmap) {
2421 Texture* texture = mRenderState.assetAtlas().getEntryTexture(bitmap);
2422 if (!texture) {
2423 return mCaches.textureCache.get(bitmap);
2424 }
2425 return texture;
2426 }
2427
2428 void OpenGLRenderer::drawPathTexture(PathTexture* texture, float x, float y,
2429 const SkPaint* paint) {
2430 if (quickRejectSetupScissor(x, y, x + texture->width, y + texture->height)) {
2431 return;
2432 }
2433
2434 Glop glop;
2435 GlopBuilder(mRenderState, mCaches, &glop)
2436 .setRoundRectClipState(currentSnapshot()->roundRectClipState)
2437 .setMeshTexturedUnitQuad(nullptr)
2438 .setFillPathTexturePaint(*texture, *paint, currentSnapshot()->alpha)
2439 .setTransform(*currentSnapshot(), TransformFlags::None)
2440 .setModelViewMapUnitToRect(Rect(x, y, x + texture->width, y + texture->height))
2441 .build();
2442 renderGlop(glop);
2443 }
2444
2445 // Same values used by Skia
2446 #define kStdStrikeThru_Offset (-6.0f / 21.0f)
2447 #define kStdUnderline_Offset (1.0f / 9.0f)
2448 #define kStdUnderline_Thickness (1.0f / 18.0f)
2449
2450 void OpenGLRenderer::drawTextDecorations(float underlineWidth, float x, float y,
2451 const SkPaint* paint) {
2452 // Handle underline and strike-through
2453 uint32_t flags = paint->getFlags();
2454 if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) {
2455 SkPaint paintCopy(*paint);
2456
2457 if (CC_LIKELY(underlineWidth > 0.0f)) {
2458 const float textSize = paintCopy.getTextSize();
2459 const float strokeWidth = std::max(textSize * kStdUnderline_Thickness, 1.0f);
2460
2461 const float left = x;
2462 float top = 0.0f;
2463
2464 int linesCount = 0;
2465 if (flags & SkPaint::kUnderlineText_Flag) linesCount++;
2466 if (flags & SkPaint::kStrikeThruText_Flag) linesCount++;
2467
2468 const int pointsCount = 4 * linesCount;
2469 float points[pointsCount];
2470 int currentPoint = 0;
2471
2472 if (flags & SkPaint::kUnderlineText_Flag) {
2473 top = y + textSize * kStdUnderline_Offset;
2474 points[currentPoint++] = left;
2475 points[currentPoint++] = top;
2476 points[currentPoint++] = left + underlineWidth;
2477 points[currentPoint++] = top;
2478 }
2479
2480 if (flags & SkPaint::kStrikeThruText_Flag) {
2481 top = y + textSize * kStdStrikeThru_Offset;
2482 points[currentPoint++] = left;
2483 points[currentPoint++] = top;
2484 points[currentPoint++] = left + underlineWidth;
2485 points[currentPoint++] = top;
2486 }
2487
2488 paintCopy.setStrokeWidth(strokeWidth);
2489
2490 drawLines(&points[0], pointsCount, &paintCopy);
2491 }
2492 }
2493 }
2494
2495 void OpenGLRenderer::drawRects(const float* rects, int count, const SkPaint* paint) {
2496 if (mState.currentlyIgnored()) {
2497 return;
2498 }
2499
2500 drawColorRects(rects, count, paint, false, true, true);
2501 }
2502
2503 void OpenGLRenderer::drawShadow(float casterAlpha,
2504 const VertexBuffer* ambientShadowVertexBuffer, const VertexBuffer* spotShadowVertexBuffer) {
2505 if (mState.currentlyIgnored()) return;
2506
2507 // TODO: use quickRejectWithScissor. For now, always force enable scissor.
2508 mRenderState.scissor().setEnabled(true);
2509
2510 SkPaint paint;
2511 paint.setAntiAlias(true); // want to use AlphaVertex
2512
2513 // The caller has made sure casterAlpha > 0.
2514 float ambientShadowAlpha = mAmbientShadowAlpha;
2515 if (CC_UNLIKELY(Properties::overrideAmbientShadowStrength >= 0)) {
2516 ambientShadowAlpha = Properties::overrideAmbientShadowStrength;
2517 }
2518 if (ambientShadowVertexBuffer && ambientShadowAlpha > 0) {
2519 paint.setARGB(casterAlpha * ambientShadowAlpha, 0, 0, 0);
2520 drawVertexBuffer(*ambientShadowVertexBuffer, &paint, kVertexBuffer_ShadowInterp);
2521 }
2522
2523 float spotShadowAlpha = mSpotShadowAlpha;
2524 if (CC_UNLIKELY(Properties::overrideSpotShadowStrength >= 0)) {
2525 spotShadowAlpha = Properties::overrideSpotShadowStrength;
2526 }
2527 if (spotShadowVertexBuffer && spotShadowAlpha > 0) {
2528 paint.setARGB(casterAlpha * spotShadowAlpha, 0, 0, 0);
2529 drawVertexBuffer(*spotShadowVertexBuffer, &paint, kVertexBuffer_ShadowInterp);
2530 }
2531
2532 mDirty=true;
2533 }
2534
2535 void OpenGLRenderer::drawColorRects(const float* rects, int count, const SkPaint* paint,
2536 bool ignoreTransform, bool dirty, bool clip) {
2537 if (count == 0) {
2538 return;
2539 }
2540
2541 float left = FLT_MAX;
2542 float top = FLT_MAX;
2543 float right = FLT_MIN;
2544 float bottom = FLT_MIN;
2545
2546 Vertex mesh[count];
2547 Vertex* vertex = mesh;
2548
2549 for (int index = 0; index < count; index += 4) {
2550 float l = rects[index + 0];
2551 float t = rects[index + 1];
2552 float r = rects[index + 2];
2553 float b = rects[index + 3];
2554
2555 Vertex::set(vertex++, l, t);
2556 Vertex::set(vertex++, r, t);
2557 Vertex::set(vertex++, l, b);
2558 Vertex::set(vertex++, r, b);
2559
2560 left = std::min(left, l);
2561 top = std::min(top, t);
2562 right = std::max(right, r);
2563 bottom = std::max(bottom, b);
2564 }
2565
2566 if (clip && quickRejectSetupScissor(left, top, right, bottom)) {
2567 return;
2568 }
2569
2570 const int transformFlags = ignoreTransform
2571 ? TransformFlags::MeshIgnoresCanvasTransform : TransformFlags::None;
2572 Glop glop;
2573 GlopBuilder(mRenderState, mCaches, &glop)
2574 .setRoundRectClipState(currentSnapshot()->roundRectClipState)
2575 .setMeshIndexedQuads(&mesh[0], count / 4)
2576 .setFillPaint(*paint, currentSnapshot()->alpha)
2577 .setTransform(*currentSnapshot(), transformFlags)
2578 .setModelViewOffsetRect(0, 0, Rect(left, top, right, bottom))
2579 .build();
2580 renderGlop(glop);
2581 }
2582
2583 void OpenGLRenderer::drawColorRect(float left, float top, float right, float bottom,
2584 const SkPaint* paint, bool ignoreTransform) {
2585 const int transformFlags = ignoreTransform
2586 ? TransformFlags::MeshIgnoresCanvasTransform : TransformFlags::None;
2587 Glop glop;
2588 GlopBuilder(mRenderState, mCaches, &glop)
2589 .setRoundRectClipState(currentSnapshot()->roundRectClipState)
2590 .setMeshUnitQuad()
2591 .setFillPaint(*paint, currentSnapshot()->alpha)
2592 .setTransform(*currentSnapshot(), transformFlags)
2593 .setModelViewMapUnitToRect(Rect(left, top, right, bottom))
2594 .build();
2595 renderGlop(glop);
2596 }
2597
2598 void OpenGLRenderer::getAlphaAndMode(const SkPaint* paint, int* alpha,
2599 SkXfermode::Mode* mode) const {
2600 getAlphaAndModeDirect(paint, alpha, mode);
2601 *alpha *= currentSnapshot()->alpha;
2602 }
2603
2604 float OpenGLRenderer::getLayerAlpha(const Layer* layer) const {
2605 return (layer->getAlpha() / 255.0f) * currentSnapshot()->alpha;
2606 }
2607
2608 }; // namespace uirenderer
2609 }; // namespace android
2610