BakedOpRenderer.cpp revision 98787e6c9b2c10b1ab7820bdac168686025b924a
1/*
2 * Copyright (C) 2015 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 "BakedOpRenderer.h"
18
19#include "Caches.h"
20#include "Glop.h"
21#include "GlopBuilder.h"
22#include "renderstate/OffscreenBufferPool.h"
23#include "renderstate/RenderState.h"
24#include "utils/GLUtils.h"
25#include "VertexBuffer.h"
26
27namespace android {
28namespace uirenderer {
29
30////////////////////////////////////////////////////////////////////////////////
31// BakedOpRenderer
32////////////////////////////////////////////////////////////////////////////////
33
34OffscreenBuffer* BakedOpRenderer::startTemporaryLayer(uint32_t width, uint32_t height) {
35    LOG_ALWAYS_FATAL_IF(mRenderTarget.offscreenBuffer, "already has layer...");
36
37    OffscreenBuffer* buffer = mRenderState.layerPool().get(mRenderState, width, height);
38    startRepaintLayer(buffer, Rect(width, height));
39    return buffer;
40}
41
42void BakedOpRenderer::startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) {
43    LOG_ALWAYS_FATAL_IF(mRenderTarget.offscreenBuffer, "already has layer...");
44
45    mRenderTarget.offscreenBuffer = offscreenBuffer;
46
47    // create and bind framebuffer
48    mRenderTarget.frameBufferId = mRenderState.genFramebuffer();
49    mRenderState.bindFramebuffer(mRenderTarget.frameBufferId);
50
51    // attach the texture to the FBO
52    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
53            offscreenBuffer->texture.id, 0);
54    LOG_ALWAYS_FATAL_IF(GLUtils::dumpGLErrors(), "startLayer FAILED");
55    LOG_ALWAYS_FATAL_IF(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE,
56            "framebuffer incomplete!");
57
58    // Change the viewport & ortho projection
59    setViewport(offscreenBuffer->viewportWidth, offscreenBuffer->viewportHeight);
60
61    clearColorBuffer(repaintRect);
62}
63
64void BakedOpRenderer::endLayer() {
65    mRenderTarget.offscreenBuffer->updateMeshFromRegion();
66    mRenderTarget.offscreenBuffer = nullptr;
67
68    // Detach the texture from the FBO
69    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
70    LOG_ALWAYS_FATAL_IF(GLUtils::dumpGLErrors(), "endLayer FAILED");
71    mRenderState.deleteFramebuffer(mRenderTarget.frameBufferId);
72    mRenderTarget.frameBufferId = -1;
73}
74
75void BakedOpRenderer::startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) {
76    mRenderState.bindFramebuffer(0);
77    setViewport(width, height);
78    mCaches.clearGarbage();
79
80    if (!mOpaque) {
81        clearColorBuffer(repaintRect);
82    }
83}
84
85void BakedOpRenderer::endFrame() {
86    mCaches.pathCache.trim();
87    mCaches.tessellationCache.trim();
88
89#if DEBUG_OPENGL
90    GLUtils::dumpGLErrors();
91#endif
92
93#if DEBUG_MEMORY_USAGE
94    mCaches.dumpMemoryUsage();
95#else
96    if (Properties::debugLevel & kDebugMemory) {
97        mCaches.dumpMemoryUsage();
98    }
99#endif
100}
101
102void BakedOpRenderer::setViewport(uint32_t width, uint32_t height) {
103    mRenderTarget.viewportWidth = width;
104    mRenderTarget.viewportHeight = height;
105    mRenderTarget.orthoMatrix.loadOrtho(width, height);
106
107    mRenderState.setViewport(width, height);
108    mRenderState.blend().syncEnabled();
109}
110
111void BakedOpRenderer::clearColorBuffer(const Rect& rect) {
112    if (Rect(mRenderTarget.viewportWidth, mRenderTarget.viewportHeight).contains(rect)) {
113        // Full viewport is being cleared - disable scissor
114        mRenderState.scissor().setEnabled(false);
115    } else {
116        // Requested rect is subset of viewport - scissor to it to avoid over-clearing
117        mRenderState.scissor().setEnabled(true);
118        mRenderState.scissor().set(rect.left, mRenderTarget.viewportHeight - rect.bottom,
119                rect.getWidth(), rect.getHeight());
120    }
121    glClear(GL_COLOR_BUFFER_BIT);
122    if (!mRenderTarget.frameBufferId) mHasDrawn = true;
123}
124
125Texture* BakedOpRenderer::getTexture(const SkBitmap* bitmap) {
126    Texture* texture = mRenderState.assetAtlas().getEntryTexture(bitmap);
127    if (!texture) {
128        return mCaches.textureCache.get(bitmap);
129    }
130    return texture;
131}
132
133void BakedOpRenderer::renderGlop(const BakedOpState& state, const Glop& glop) {
134    bool useScissor = state.computedState.clipSideFlags != OpClipSideFlags::None;
135    mRenderState.scissor().setEnabled(useScissor);
136    if (useScissor) {
137        const Rect& clip = state.computedState.clipRect;
138        mRenderState.scissor().set(clip.left, mRenderTarget.viewportHeight - clip.bottom,
139            clip.getWidth(), clip.getHeight());
140    }
141    if (mRenderTarget.offscreenBuffer) { // TODO: not with multi-draw
142        // register layer damage to draw-back region
143        const Rect& uiDirty = state.computedState.clippedBounds;
144        android::Rect dirty(uiDirty.left, uiDirty.top, uiDirty.right, uiDirty.bottom);
145        mRenderTarget.offscreenBuffer->region.orSelf(dirty);
146    }
147    mRenderState.render(glop, mRenderTarget.orthoMatrix);
148    if (!mRenderTarget.frameBufferId) mHasDrawn = true;
149}
150
151////////////////////////////////////////////////////////////////////////////////
152// static BakedOpDispatcher methods
153////////////////////////////////////////////////////////////////////////////////
154
155void BakedOpDispatcher::onRenderNodeOp(BakedOpRenderer&, const RenderNodeOp&, const BakedOpState&) {
156    LOG_ALWAYS_FATAL("unsupported operation");
157}
158
159void BakedOpDispatcher::onBeginLayerOp(BakedOpRenderer& renderer, const BeginLayerOp& op, const BakedOpState& state) {
160    LOG_ALWAYS_FATAL("unsupported operation");
161}
162
163void BakedOpDispatcher::onEndLayerOp(BakedOpRenderer& renderer, const EndLayerOp& op, const BakedOpState& state) {
164    LOG_ALWAYS_FATAL("unsupported operation");
165}
166
167void BakedOpDispatcher::onBitmapOp(BakedOpRenderer& renderer, const BitmapOp& op, const BakedOpState& state) {
168    renderer.caches().textureState().activateTexture(0); // TODO: should this be automatic, and/or elsewhere?
169    Texture* texture = renderer.getTexture(op.bitmap);
170    if (!texture) return;
171    const AutoTexture autoCleanup(texture);
172
173    const int textureFillFlags = (op.bitmap->colorType() == kAlpha_8_SkColorType)
174            ? TextureFillFlags::IsAlphaMaskTexture : TextureFillFlags::None;
175    Glop glop;
176    GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
177            .setRoundRectClipState(state.roundRectClipState)
178            .setMeshTexturedUnitQuad(texture->uvMapper)
179            .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
180            .setTransform(state.computedState.transform, TransformFlags::None)
181            .setModelViewMapUnitToRectSnap(Rect(0, 0, texture->width, texture->height))
182            .build();
183    renderer.renderGlop(state, glop);
184}
185
186void BakedOpDispatcher::onRectOp(BakedOpRenderer& renderer, const RectOp& op, const BakedOpState& state) {
187    Glop glop;
188    GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
189            .setRoundRectClipState(state.roundRectClipState)
190            .setMeshUnitQuad()
191            .setFillPaint(*op.paint, state.alpha)
192            .setTransform(state.computedState.transform, TransformFlags::None)
193            .setModelViewMapUnitToRect(op.unmappedBounds)
194            .build();
195    renderer.renderGlop(state, glop);
196}
197
198namespace VertexBufferRenderFlags {
199    enum {
200        Offset = 0x1,
201        ShadowInterp = 0x2,
202    };
203}
204
205static void renderVertexBuffer(BakedOpRenderer& renderer, const BakedOpState& state,
206        const VertexBuffer& vertexBuffer, float translateX, float translateY,
207        SkPaint& paint, int vertexBufferRenderFlags) {
208    if (CC_LIKELY(vertexBuffer.getVertexCount())) {
209        bool shadowInterp = vertexBufferRenderFlags & VertexBufferRenderFlags::ShadowInterp;
210        const int transformFlags = TransformFlags::OffsetByFudgeFactor;
211        Glop glop;
212        GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
213                .setRoundRectClipState(state.roundRectClipState)
214                .setMeshVertexBuffer(vertexBuffer, shadowInterp)
215                .setFillPaint(paint, state.alpha)
216                .setTransform(state.computedState.transform, transformFlags)
217                .setModelViewOffsetRect(translateX, translateY, vertexBuffer.getBounds())
218                .build();
219        renderer.renderGlop(state, glop);
220    }
221}
222
223static void renderShadow(BakedOpRenderer& renderer, const BakedOpState& state, float casterAlpha,
224        const VertexBuffer* ambientShadowVertexBuffer, const VertexBuffer* spotShadowVertexBuffer) {
225    SkPaint paint;
226    paint.setAntiAlias(true); // want to use AlphaVertex
227
228    // The caller has made sure casterAlpha > 0.
229    uint8_t ambientShadowAlpha = renderer.getLightInfo().ambientShadowAlpha;
230    if (CC_UNLIKELY(Properties::overrideAmbientShadowStrength >= 0)) {
231        ambientShadowAlpha = Properties::overrideAmbientShadowStrength;
232    }
233    if (ambientShadowVertexBuffer && ambientShadowAlpha > 0) {
234        paint.setAlpha((uint8_t)(casterAlpha * ambientShadowAlpha));
235        renderVertexBuffer(renderer, state, *ambientShadowVertexBuffer, 0, 0,
236                paint, VertexBufferRenderFlags::ShadowInterp);
237    }
238
239    uint8_t spotShadowAlpha = renderer.getLightInfo().spotShadowAlpha;
240    if (CC_UNLIKELY(Properties::overrideSpotShadowStrength >= 0)) {
241        spotShadowAlpha = Properties::overrideSpotShadowStrength;
242    }
243    if (spotShadowVertexBuffer && spotShadowAlpha > 0) {
244        paint.setAlpha((uint8_t)(casterAlpha * spotShadowAlpha));
245        renderVertexBuffer(renderer, state, *spotShadowVertexBuffer, 0, 0,
246                paint, VertexBufferRenderFlags::ShadowInterp);
247    }
248}
249
250void BakedOpDispatcher::onShadowOp(BakedOpRenderer& renderer, const ShadowOp& op, const BakedOpState& state) {
251    TessellationCache::vertexBuffer_pair_t buffers;
252    renderer.caches().tessellationCache.getShadowBuffers(&state.computedState.transform,
253            op.localClipRect, op.casterAlpha >= 1.0f, op.casterPath,
254            &op.shadowMatrixXY, &op.shadowMatrixZ,
255            op.lightCenter, renderer.getLightInfo().lightRadius,
256            buffers);
257
258    renderShadow(renderer, state, op.casterAlpha, buffers.first, buffers.second);
259}
260
261void BakedOpDispatcher::onSimpleRectsOp(BakedOpRenderer& renderer, const SimpleRectsOp& op, const BakedOpState& state) {
262    Glop glop;
263    GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
264            .setRoundRectClipState(state.roundRectClipState)
265            .setMeshIndexedQuads(&op.vertices[0], op.vertexCount / 4)
266            .setFillPaint(*op.paint, state.alpha)
267            .setTransform(state.computedState.transform, TransformFlags::None)
268            .setModelViewOffsetRect(0, 0, op.unmappedBounds)
269            .build();
270    renderer.renderGlop(state, glop);
271}
272
273void BakedOpDispatcher::onLayerOp(BakedOpRenderer& renderer, const LayerOp& op, const BakedOpState& state) {
274    OffscreenBuffer* buffer = *op.layerHandle;
275
276    // TODO: extend this to handle HW layers & paint properties which
277    // reside in node.properties().layerProperties()
278    float layerAlpha = op.alpha * state.alpha;
279    Glop glop;
280    GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
281            .setRoundRectClipState(state.roundRectClipState)
282            .setMeshTexturedIndexedVbo(buffer->vbo, buffer->elementCount)
283            .setFillLayer(buffer->texture, op.colorFilter, layerAlpha, op.mode, Blend::ModeOrderSwap::NoSwap)
284            .setTransform(state.computedState.transform, TransformFlags::None)
285            .setModelViewOffsetRectSnap(op.unmappedBounds.left, op.unmappedBounds.top,
286                    Rect(op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight()))
287            .build();
288    renderer.renderGlop(state, glop);
289
290    if (op.destroy) {
291        renderer.renderState().layerPool().putOrDelete(buffer);
292    }
293}
294
295} // namespace uirenderer
296} // namespace android
297