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