1 /*
2 * Copyright (C) 2011 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 <ui/Rect.h>
20
21 #include "LayerCache.h"
22 #include "LayerRenderer.h"
23 #include "Matrix.h"
24 #include "Properties.h"
25 #include "Rect.h"
26
27 namespace android {
28 namespace uirenderer {
29
30 ///////////////////////////////////////////////////////////////////////////////
31 // Rendering
32 ///////////////////////////////////////////////////////////////////////////////
33
prepareDirty(float left,float top,float right,float bottom,bool opaque)34 void LayerRenderer::prepareDirty(float left, float top, float right, float bottom, bool opaque) {
35 LAYER_RENDERER_LOGD("Rendering into layer, fbo = %d", mLayer->getFbo());
36
37 glBindFramebuffer(GL_FRAMEBUFFER, mLayer->getFbo());
38
39 const float width = mLayer->layer.getWidth();
40 const float height = mLayer->layer.getHeight();
41
42 #if RENDER_LAYERS_AS_REGIONS
43 Rect dirty(left, top, right, bottom);
44 if (dirty.isEmpty() || (dirty.left <= 0 && dirty.top <= 0 &&
45 dirty.right >= width && dirty.bottom >= height)) {
46 mLayer->region.clear();
47 dirty.set(0.0f, 0.0f, width, height);
48 } else {
49 dirty.intersect(0.0f, 0.0f, width, height);
50 android::Rect r(dirty.left, dirty.top, dirty.right, dirty.bottom);
51 mLayer->region.subtractSelf(r);
52 }
53
54 OpenGLRenderer::prepareDirty(dirty.left, dirty.top, dirty.right, dirty.bottom, opaque);
55 #else
56 OpenGLRenderer::prepareDirty(0.0f, 0.0f, width, height, opaque);
57 #endif
58 }
59
finish()60 void LayerRenderer::finish() {
61 OpenGLRenderer::finish();
62
63 generateMesh();
64
65 LAYER_RENDERER_LOGD("Finished rendering into layer, fbo = %d", mLayer->getFbo());
66
67 // No need to unbind our FBO, this will be taken care of by the caller
68 // who will invoke OpenGLRenderer::resume()
69 }
70
getTargetFbo()71 GLint LayerRenderer::getTargetFbo() {
72 return mLayer->getFbo();
73 }
74
75 ///////////////////////////////////////////////////////////////////////////////
76 // Dirty region tracking
77 ///////////////////////////////////////////////////////////////////////////////
78
hasLayer()79 bool LayerRenderer::hasLayer() {
80 return true;
81 }
82
getRegion()83 Region* LayerRenderer::getRegion() {
84 #if RENDER_LAYERS_AS_REGIONS
85 if (getSnapshot()->flags & Snapshot::kFlagFboTarget) {
86 return OpenGLRenderer::getRegion();
87 }
88 return &mLayer->region;
89 #else
90 return OpenGLRenderer::getRegion();
91 #endif
92 }
93
94 // TODO: This implementation is flawed and can generate T-junctions
95 // in the mesh, which will in turn produce cracks when the
96 // mesh is rotated/skewed. The easiest way to fix this would
97 // be, for each row, to add new vertices shared with the previous
98 // row when the two rows share an edge.
99 // In practice, T-junctions do not appear often so this has yet
100 // to be fixed.
generateMesh()101 void LayerRenderer::generateMesh() {
102 #if RENDER_LAYERS_AS_REGIONS
103 if (mLayer->region.isRect() || mLayer->region.isEmpty()) {
104 if (mLayer->mesh) {
105 delete mLayer->mesh;
106 delete mLayer->meshIndices;
107
108 mLayer->mesh = NULL;
109 mLayer->meshIndices = NULL;
110 mLayer->meshElementCount = 0;
111 }
112
113 mLayer->setRegionAsRect();
114 return;
115 }
116
117 size_t count;
118 const android::Rect* rects = mLayer->region.getArray(&count);
119
120 GLsizei elementCount = count * 6;
121
122 if (mLayer->mesh && mLayer->meshElementCount < elementCount) {
123 delete mLayer->mesh;
124 delete mLayer->meshIndices;
125
126 mLayer->mesh = NULL;
127 mLayer->meshIndices = NULL;
128 }
129
130 bool rebuildIndices = false;
131 if (!mLayer->mesh) {
132 mLayer->mesh = new TextureVertex[count * 4];
133 mLayer->meshIndices = new uint16_t[elementCount];
134 rebuildIndices = true;
135 }
136 mLayer->meshElementCount = elementCount;
137
138 const float texX = 1.0f / float(mLayer->getWidth());
139 const float texY = 1.0f / float(mLayer->getHeight());
140 const float height = mLayer->layer.getHeight();
141
142 TextureVertex* mesh = mLayer->mesh;
143 uint16_t* indices = mLayer->meshIndices;
144
145 for (size_t i = 0; i < count; i++) {
146 const android::Rect* r = &rects[i];
147
148 const float u1 = r->left * texX;
149 const float v1 = (height - r->top) * texY;
150 const float u2 = r->right * texX;
151 const float v2 = (height - r->bottom) * texY;
152
153 TextureVertex::set(mesh++, r->left, r->top, u1, v1);
154 TextureVertex::set(mesh++, r->right, r->top, u2, v1);
155 TextureVertex::set(mesh++, r->left, r->bottom, u1, v2);
156 TextureVertex::set(mesh++, r->right, r->bottom, u2, v2);
157
158 if (rebuildIndices) {
159 uint16_t quad = i * 4;
160 int index = i * 6;
161 indices[index ] = quad; // top-left
162 indices[index + 1] = quad + 1; // top-right
163 indices[index + 2] = quad + 2; // bottom-left
164 indices[index + 3] = quad + 2; // bottom-left
165 indices[index + 4] = quad + 1; // top-right
166 indices[index + 5] = quad + 3; // bottom-right
167 }
168 }
169 #endif
170 }
171
172 ///////////////////////////////////////////////////////////////////////////////
173 // Layers management
174 ///////////////////////////////////////////////////////////////////////////////
175
createLayer(uint32_t width,uint32_t height,bool isOpaque)176 Layer* LayerRenderer::createLayer(uint32_t width, uint32_t height, bool isOpaque) {
177 LAYER_RENDERER_LOGD("Requesting new render layer %dx%d", width, height);
178
179 GLuint fbo = Caches::getInstance().fboCache.get();
180 if (!fbo) {
181 LOGW("Could not obtain an FBO");
182 return NULL;
183 }
184
185 glActiveTexture(GL_TEXTURE0);
186 Layer* layer = Caches::getInstance().layerCache.get(width, height);
187 if (!layer) {
188 LOGW("Could not obtain a layer");
189 return NULL;
190 }
191
192 layer->setFbo(fbo);
193 layer->layer.set(0.0f, 0.0f, width, height);
194 layer->texCoords.set(0.0f, height / float(layer->getHeight()),
195 width / float(layer->getWidth()), 0.0f);
196 layer->setAlpha(255, SkXfermode::kSrcOver_Mode);
197 layer->setBlend(!isOpaque);
198 layer->setColorFilter(NULL);
199 layer->region.clear();
200
201 GLuint previousFbo;
202 glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*) &previousFbo);
203
204 glBindFramebuffer(GL_FRAMEBUFFER, layer->getFbo());
205 layer->bindTexture();
206
207 // Initialize the texture if needed
208 if (layer->isEmpty()) {
209 layer->setEmpty(false);
210 layer->allocateTexture(GL_RGBA, GL_UNSIGNED_BYTE);
211
212 if (glGetError() != GL_NO_ERROR) {
213 LOGD("Could not allocate texture");
214
215 glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
216 Caches::getInstance().fboCache.put(fbo);
217
218 layer->deleteTexture();
219 delete layer;
220
221 return NULL;
222 }
223 }
224
225 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
226 layer->getTexture(), 0);
227
228 glDisable(GL_SCISSOR_TEST);
229 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
230 glClear(GL_COLOR_BUFFER_BIT);
231 glEnable(GL_SCISSOR_TEST);
232
233 glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
234
235 return layer;
236 }
237
resizeLayer(Layer * layer,uint32_t width,uint32_t height)238 bool LayerRenderer::resizeLayer(Layer* layer, uint32_t width, uint32_t height) {
239 if (layer) {
240 LAYER_RENDERER_LOGD("Resizing layer fbo = %d to %dx%d", layer->getFbo(), width, height);
241
242 if (Caches::getInstance().layerCache.resize(layer, width, height)) {
243 layer->layer.set(0.0f, 0.0f, width, height);
244 layer->texCoords.set(0.0f, height / float(layer->getHeight()),
245 width / float(layer->getWidth()), 0.0f);
246 } else {
247 layer->deleteTexture();
248 delete layer;
249 return false;
250 }
251 }
252
253 return true;
254 }
255
createTextureLayer(bool isOpaque)256 Layer* LayerRenderer::createTextureLayer(bool isOpaque) {
257 LAYER_RENDERER_LOGD("Creating new texture layer");
258
259 Layer* layer = new Layer(0, 0);
260 layer->setCacheable(false);
261 layer->setTextureLayer(true);
262 layer->setBlend(!isOpaque);
263 layer->setEmpty(true);
264 layer->setFbo(0);
265 layer->setAlpha(255, SkXfermode::kSrcOver_Mode);
266 layer->layer.set(0.0f, 0.0f, 0.0f, 0.0f);
267 layer->texCoords.set(0.0f, 1.0f, 0.0f, 1.0f);
268 layer->region.clear();
269 layer->setRenderTarget(GL_NONE); // see ::updateTextureLayer()
270
271 glActiveTexture(GL_TEXTURE0);
272 layer->generateTexture();
273
274 return layer;
275 }
276
updateTextureLayer(Layer * layer,uint32_t width,uint32_t height,bool isOpaque,GLenum renderTarget,float * transform)277 void LayerRenderer::updateTextureLayer(Layer* layer, uint32_t width, uint32_t height,
278 bool isOpaque, GLenum renderTarget, float* transform) {
279 if (layer) {
280 layer->setBlend(!isOpaque);
281 layer->setSize(width, height);
282 layer->layer.set(0.0f, 0.0f, width, height);
283 layer->region.set(width, height);
284 layer->regionRect.set(0.0f, 0.0f, width, height);
285 layer->getTexTransform().load(transform);
286
287 if (renderTarget != layer->getRenderTarget()) {
288 layer->setRenderTarget(renderTarget);
289 layer->bindTexture();
290 layer->setFilter(GL_NEAREST, GL_NEAREST, false, true);
291 layer->setWrap(GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, false, true);
292 }
293 }
294 }
295
destroyLayer(Layer * layer)296 void LayerRenderer::destroyLayer(Layer* layer) {
297 if (layer) {
298 LAYER_RENDERER_LOGD("Recycling layer, %dx%d fbo = %d",
299 layer->getWidth(), layer->getHeight(), layer->getFbo());
300
301 if (layer->getFbo()) {
302 Caches::getInstance().fboCache.put(layer->getFbo());
303 }
304
305 if (!Caches::getInstance().layerCache.put(layer)) {
306 LAYER_RENDERER_LOGD(" Destroyed!");
307 layer->deleteTexture();
308 delete layer;
309 } else {
310 LAYER_RENDERER_LOGD(" Cached!");
311 #if DEBUG_LAYER_RENDERER
312 Caches::getInstance().layerCache.dump();
313 #endif
314 layer->region.clear();
315 }
316 }
317 }
318
destroyLayerDeferred(Layer * layer)319 void LayerRenderer::destroyLayerDeferred(Layer* layer) {
320 if (layer) {
321 LAYER_RENDERER_LOGD("Deferring layer destruction, fbo = %d", layer->getFbo());
322
323 Caches::getInstance().deleteLayerDeferred(layer);
324 }
325 }
326
copyLayer(Layer * layer,SkBitmap * bitmap)327 bool LayerRenderer::copyLayer(Layer* layer, SkBitmap* bitmap) {
328 Caches& caches = Caches::getInstance();
329 if (layer && layer->isTextureLayer() && bitmap->width() <= caches.maxTextureSize &&
330 bitmap->height() <= caches.maxTextureSize) {
331
332 GLuint fbo = caches.fboCache.get();
333 if (!fbo) {
334 LOGW("Could not obtain an FBO");
335 return false;
336 }
337
338 SkAutoLockPixels alp(*bitmap);
339
340 GLuint texture;
341 GLuint previousFbo;
342
343 GLenum format;
344 GLenum type;
345
346 GLenum error = GL_NO_ERROR;
347 bool status = false;
348
349 switch (bitmap->config()) {
350 case SkBitmap::kA8_Config:
351 format = GL_ALPHA;
352 type = GL_UNSIGNED_BYTE;
353 break;
354 case SkBitmap::kRGB_565_Config:
355 format = GL_RGB;
356 type = GL_UNSIGNED_SHORT_5_6_5;
357 break;
358 case SkBitmap::kARGB_4444_Config:
359 format = GL_RGBA;
360 type = GL_UNSIGNED_SHORT_4_4_4_4;
361 break;
362 case SkBitmap::kARGB_8888_Config:
363 default:
364 format = GL_RGBA;
365 type = GL_UNSIGNED_BYTE;
366 break;
367 }
368
369 float alpha = layer->getAlpha();
370 SkXfermode::Mode mode = layer->getMode();
371
372 layer->setAlpha(255, SkXfermode::kSrc_Mode);
373 layer->setFbo(fbo);
374
375 glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*) &previousFbo);
376 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
377
378 glGenTextures(1, &texture);
379 if ((error = glGetError()) != GL_NO_ERROR) goto error;
380
381 glActiveTexture(GL_TEXTURE0);
382 glBindTexture(GL_TEXTURE_2D, texture);
383
384 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
385 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
386
387 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
388 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
389
390 glTexImage2D(GL_TEXTURE_2D, 0, format, bitmap->width(), bitmap->height(),
391 0, format, type, NULL);
392 if ((error = glGetError()) != GL_NO_ERROR) goto error;
393
394 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
395 GL_TEXTURE_2D, texture, 0);
396 if ((error = glGetError()) != GL_NO_ERROR) goto error;
397
398 {
399 LayerRenderer renderer(layer);
400 renderer.setViewport(bitmap->width(), bitmap->height());
401 renderer.OpenGLRenderer::prepareDirty(0.0f, 0.0f,
402 bitmap->width(), bitmap->height(), !layer->isBlend());
403 if ((error = glGetError()) != GL_NO_ERROR) goto error;
404
405 {
406 Rect bounds;
407 bounds.set(0.0f, 0.0f, bitmap->width(), bitmap->height());
408 renderer.drawTextureLayer(layer, bounds);
409
410 glReadPixels(0, 0, bitmap->width(), bitmap->height(), format,
411 type, bitmap->getPixels());
412
413 if ((error = glGetError()) != GL_NO_ERROR) goto error;
414 }
415
416 status = true;
417 }
418
419 error:
420 #if DEBUG_OPENGL
421 if (error != GL_NO_ERROR) {
422 LOGD("GL error while copying layer into bitmap = 0x%x", error);
423 }
424 #endif
425
426 glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
427 layer->setAlpha(alpha, mode);
428 layer->setFbo(0);
429 glDeleteTextures(1, &texture);
430 caches.fboCache.put(fbo);
431
432 return status;
433 }
434 return false;
435 }
436
437 }; // namespace uirenderer
438 }; // namespace android
439