blob: 5b6ff42a12a77aee8c6493dba126896d76c48b94 [file] [log] [blame]
Mathias Agopian875d8e12013-06-07 15:35:48 -07001/*
2 * Copyright 2013 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
Mark Salyzyn7823e122016-09-29 08:08:05 -070017#include <log/log.h>
Mathias Agopian3f844832013-08-07 21:24:32 -070018#include <ui/Rect.h>
19#include <ui/Region.h>
Mathias Agopian875d8e12013-06-07 15:35:48 -070020
21#include "RenderEngine.h"
Mathias Agopian3f844832013-08-07 21:24:32 -070022#include "GLES20RenderEngine.h"
Mathias Agopian875d8e12013-06-07 15:35:48 -070023#include "GLExtensions.h"
Mathias Agopian3f844832013-08-07 21:24:32 -070024#include "Mesh.h"
Mathias Agopian875d8e12013-06-07 15:35:48 -070025
Fabien Sanglardc93afd52017-03-13 13:02:42 -070026#include <vector>
27#include <SurfaceFlinger.h>
28
Jiyong Park00b15b82017-08-10 20:30:56 +090029extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
Jesse Hall19e87292013-12-23 21:02:15 -080030
Mathias Agopian875d8e12013-06-07 15:35:48 -070031// ---------------------------------------------------------------------------
32namespace android {
33// ---------------------------------------------------------------------------
34
Jesse Hall19e87292013-12-23 21:02:15 -080035static bool findExtension(const char* exts, const char* name) {
36 if (!exts)
37 return false;
38 size_t len = strlen(name);
Jesse Hall05f8c702013-12-23 20:44:38 -080039
Jesse Hall19e87292013-12-23 21:02:15 -080040 const char* pos = exts;
41 while ((pos = strstr(pos, name)) != NULL) {
42 if (pos[len] == '\0' || pos[len] == ' ')
43 return true;
44 pos += len;
Mathias Agopian2185f8b2013-09-18 16:20:26 -070045 }
46
Jesse Hall19e87292013-12-23 21:02:15 -080047 return false;
48}
49
Chia-I Wub2c76242017-11-09 17:17:07 -080050std::unique_ptr<RenderEngine> RenderEngine::create(EGLDisplay display,
51 int hwcFormat, uint32_t featureFlags) {
Jesse Hall19e87292013-12-23 21:02:15 -080052 // EGL_ANDROIDX_no_config_context is an experimental extension with no
53 // written specification. It will be replaced by something more formal.
54 // SurfaceFlinger is using it to allow a single EGLContext to render to
55 // both a 16-bit primary display framebuffer and a 32-bit virtual display
56 // framebuffer.
57 //
Courtney Goeltzenleuchter0ebaac32017-04-13 12:17:03 -060058 // EGL_KHR_no_config_context is official extension to allow creating a
59 // context that works with any surface of a display.
60 //
Jesse Hall19e87292013-12-23 21:02:15 -080061 // The code assumes that ES2 or later is available if this extension is
62 // supported.
63 EGLConfig config = EGL_NO_CONFIG;
Courtney Goeltzenleuchter0ebaac32017-04-13 12:17:03 -060064 if (!findExtension(eglQueryStringImplementationANDROID(display, EGL_EXTENSIONS),
65 "EGL_ANDROIDX_no_config_context") &&
66 !findExtension(eglQueryStringImplementationANDROID(display, EGL_EXTENSIONS),
67 "EGL_KHR_no_config_context")) {
Steven Thomasd7f49c52017-07-26 18:48:28 -070068 config = chooseEglConfig(display, hwcFormat, /*logConfig*/ true);
Jesse Hall19e87292013-12-23 21:02:15 -080069 }
70
71 EGLint renderableType = 0;
72 if (config == EGL_NO_CONFIG) {
73 renderableType = EGL_OPENGL_ES2_BIT;
74 } else if (!eglGetConfigAttrib(display, config,
75 EGL_RENDERABLE_TYPE, &renderableType)) {
76 LOG_ALWAYS_FATAL("can't query EGLConfig RENDERABLE_TYPE");
77 }
78 EGLint contextClientVersion = 0;
Mathias Agopian2185f8b2013-09-18 16:20:26 -070079 if (renderableType & EGL_OPENGL_ES2_BIT) {
80 contextClientVersion = 2;
81 } else if (renderableType & EGL_OPENGL_ES_BIT) {
82 contextClientVersion = 1;
83 } else {
84 LOG_ALWAYS_FATAL("no supported EGL_RENDERABLE_TYPEs");
85 }
86
Fabien Sanglardc93afd52017-03-13 13:02:42 -070087 std::vector<EGLint> contextAttributes;
88 contextAttributes.reserve(6);
89 contextAttributes.push_back(EGL_CONTEXT_CLIENT_VERSION);
90 contextAttributes.push_back(contextClientVersion);
Mathias Agopian875d8e12013-06-07 15:35:48 -070091#ifdef EGL_IMG_context_priority
Fabien Sanglardc93afd52017-03-13 13:02:42 -070092 if (SurfaceFlinger::useContextPriority) {
93 contextAttributes.push_back(EGL_CONTEXT_PRIORITY_LEVEL_IMG);
94 contextAttributes.push_back(EGL_CONTEXT_PRIORITY_HIGH_IMG);
95 }
Mathias Agopian875d8e12013-06-07 15:35:48 -070096#endif
Fabien Sanglardc93afd52017-03-13 13:02:42 -070097 contextAttributes.push_back(EGL_NONE);
98 contextAttributes.push_back(EGL_NONE);
99
100 EGLContext ctxt = eglCreateContext(display, config, NULL,
101 contextAttributes.data());
Mathias Agopian875d8e12013-06-07 15:35:48 -0700102
103 // if can't create a GL context, we can only abort.
104 LOG_ALWAYS_FATAL_IF(ctxt==EGL_NO_CONTEXT, "EGLContext creation failed");
105
106
107 // now figure out what version of GL did we actually get
108 // NOTE: a dummy surface is not needed if KHR_create_context is supported
109
Jesse Hall19e87292013-12-23 21:02:15 -0800110 EGLConfig dummyConfig = config;
111 if (dummyConfig == EGL_NO_CONFIG) {
Steven Thomasd7f49c52017-07-26 18:48:28 -0700112 dummyConfig = chooseEglConfig(display, hwcFormat, /*logConfig*/ true);
Jesse Hall19e87292013-12-23 21:02:15 -0800113 }
Mathias Agopian875d8e12013-06-07 15:35:48 -0700114 EGLint attribs[] = { EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE, EGL_NONE };
Jesse Hall19e87292013-12-23 21:02:15 -0800115 EGLSurface dummy = eglCreatePbufferSurface(display, dummyConfig, attribs);
Mathias Agopian875d8e12013-06-07 15:35:48 -0700116 LOG_ALWAYS_FATAL_IF(dummy==EGL_NO_SURFACE, "can't create dummy pbuffer");
117 EGLBoolean success = eglMakeCurrent(display, dummy, dummy, ctxt);
118 LOG_ALWAYS_FATAL_IF(!success, "can't make dummy pbuffer current");
119
120 GLExtensions& extensions(GLExtensions::getInstance());
121 extensions.initWithGLStrings(
122 glGetString(GL_VENDOR),
123 glGetString(GL_RENDERER),
124 glGetString(GL_VERSION),
125 glGetString(GL_EXTENSIONS));
126
127 GlesVersion version = parseGlesVersion( extensions.getVersion() );
128
129 // initialize the renderer while GL is current
130
Chia-I Wub2c76242017-11-09 17:17:07 -0800131 std::unique_ptr<RenderEngine> engine;
Mathias Agopian875d8e12013-06-07 15:35:48 -0700132 switch (version) {
133 case GLES_VERSION_1_0:
Mathias Agopian875d8e12013-06-07 15:35:48 -0700134 case GLES_VERSION_1_1:
Fabien Sanglardefb93452016-10-04 14:02:11 -0700135 LOG_ALWAYS_FATAL("SurfaceFlinger requires OpenGL ES 2.0 minimum to run.");
Mathias Agopian875d8e12013-06-07 15:35:48 -0700136 break;
137 case GLES_VERSION_2_0:
138 case GLES_VERSION_3_0:
Chia-I Wub2c76242017-11-09 17:17:07 -0800139 engine = std::make_unique<GLES20RenderEngine>(featureFlags);
Mathias Agopian875d8e12013-06-07 15:35:48 -0700140 break;
141 }
Chia-I Wu2b6386e2017-11-09 13:03:17 -0800142 engine->setEGLHandles(display, config, ctxt);
Mathias Agopian875d8e12013-06-07 15:35:48 -0700143
144 ALOGI("OpenGL ES informations:");
145 ALOGI("vendor : %s", extensions.getVendor());
146 ALOGI("renderer : %s", extensions.getRenderer());
147 ALOGI("version : %s", extensions.getVersion());
148 ALOGI("extensions: %s", extensions.getExtension());
Michael Lentine9ae79d82014-07-30 16:42:12 -0700149 ALOGI("GL_MAX_TEXTURE_SIZE = %zu", engine->getMaxTextureSize());
150 ALOGI("GL_MAX_VIEWPORT_DIMS = %zu", engine->getMaxViewportDims());
Mathias Agopian875d8e12013-06-07 15:35:48 -0700151
152 eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
153 eglDestroySurface(display, dummy);
154
155 return engine;
156}
157
Chia-I Wu2b6386e2017-11-09 13:03:17 -0800158RenderEngine::RenderEngine() : mEGLDisplay(EGL_NO_DISPLAY), mEGLConfig(NULL),
159 mEGLContext(EGL_NO_CONTEXT) {
Mathias Agopian875d8e12013-06-07 15:35:48 -0700160}
161
162RenderEngine::~RenderEngine() {
163}
164
Chia-I Wu2b6386e2017-11-09 13:03:17 -0800165void RenderEngine::setEGLHandles(EGLDisplay display, EGLConfig config, EGLContext ctxt) {
166 mEGLDisplay = display;
Jesse Hall05f8c702013-12-23 20:44:38 -0800167 mEGLConfig = config;
Mathias Agopian875d8e12013-06-07 15:35:48 -0700168 mEGLContext = ctxt;
169}
170
Chia-I Wu2b6386e2017-11-09 13:03:17 -0800171EGLDisplay RenderEngine::getEGLDisplay() const {
172 return mEGLDisplay;
173}
174
175EGLConfig RenderEngine::getEGLConfig() const {
Jesse Hall05f8c702013-12-23 20:44:38 -0800176 return mEGLConfig;
177}
178
Mathias Agopian875d8e12013-06-07 15:35:48 -0700179EGLContext RenderEngine::getEGLContext() const {
180 return mEGLContext;
181}
182
183void RenderEngine::checkErrors() const {
184 do {
185 // there could be more than one error flag
186 GLenum error = glGetError();
187 if (error == GL_NO_ERROR)
188 break;
189 ALOGE("GL error 0x%04x", int(error));
190 } while (true);
191}
192
193RenderEngine::GlesVersion RenderEngine::parseGlesVersion(const char* str) {
194 int major, minor;
195 if (sscanf(str, "OpenGL ES-CM %d.%d", &major, &minor) != 2) {
196 if (sscanf(str, "OpenGL ES %d.%d", &major, &minor) != 2) {
197 ALOGW("Unable to parse GL_VERSION string: \"%s\"", str);
198 return GLES_VERSION_1_0;
199 }
200 }
201
202 if (major == 1 && minor == 0) return GLES_VERSION_1_0;
203 if (major == 1 && minor >= 1) return GLES_VERSION_1_1;
204 if (major == 2 && minor >= 0) return GLES_VERSION_2_0;
205 if (major == 3 && minor >= 0) return GLES_VERSION_3_0;
206
207 ALOGW("Unrecognized OpenGL ES version: %d.%d", major, minor);
208 return GLES_VERSION_1_0;
209}
210
Mathias Agopian3f844832013-08-07 21:24:32 -0700211void RenderEngine::fillRegionWithColor(const Region& region, uint32_t height,
212 float red, float green, float blue, float alpha) {
213 size_t c;
214 Rect const* r = region.getArray(&c);
215 Mesh mesh(Mesh::TRIANGLES, c*6, 2);
Mathias Agopianff2ed702013-09-01 21:36:12 -0700216 Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>());
Mathias Agopian3f844832013-08-07 21:24:32 -0700217 for (size_t i=0 ; i<c ; i++, r++) {
Mathias Agopian5cdc8992013-08-13 20:51:23 -0700218 position[i*6 + 0].x = r->left;
219 position[i*6 + 0].y = height - r->top;
220 position[i*6 + 1].x = r->left;
221 position[i*6 + 1].y = height - r->bottom;
222 position[i*6 + 2].x = r->right;
223 position[i*6 + 2].y = height - r->bottom;
224 position[i*6 + 3].x = r->left;
225 position[i*6 + 3].y = height - r->top;
226 position[i*6 + 4].x = r->right;
227 position[i*6 + 4].y = height - r->bottom;
228 position[i*6 + 5].x = r->right;
229 position[i*6 + 5].y = height - r->top;
Mathias Agopian3f844832013-08-07 21:24:32 -0700230 }
Mathias Agopian19733a32013-08-28 18:13:56 -0700231 setupFillWithColor(red, green, blue, alpha);
232 drawMesh(mesh);
Mathias Agopian3f844832013-08-07 21:24:32 -0700233}
234
Riley Andrews9707f4d2014-10-23 16:17:04 -0700235void RenderEngine::flush() {
236 glFlush();
237}
238
Mathias Agopian3f844832013-08-07 21:24:32 -0700239void RenderEngine::clearWithColor(float red, float green, float blue, float alpha) {
240 glClearColor(red, green, blue, alpha);
241 glClear(GL_COLOR_BUFFER_BIT);
242}
243
244void RenderEngine::setScissor(
245 uint32_t left, uint32_t bottom, uint32_t right, uint32_t top) {
246 glScissor(left, bottom, right, top);
247 glEnable(GL_SCISSOR_TEST);
248}
249
250void RenderEngine::disableScissor() {
251 glDisable(GL_SCISSOR_TEST);
252}
253
254void RenderEngine::genTextures(size_t count, uint32_t* names) {
255 glGenTextures(count, names);
256}
257
258void RenderEngine::deleteTextures(size_t count, uint32_t const* names) {
259 glDeleteTextures(count, names);
260}
261
Mathias Agopiand5556842013-09-19 17:08:37 -0700262void RenderEngine::readPixels(size_t l, size_t b, size_t w, size_t h, uint32_t* pixels) {
263 glReadPixels(l, b, w, h, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
264}
265
Mathias Agopian458197d2013-08-15 14:56:51 -0700266void RenderEngine::dump(String8& result) {
267 const GLExtensions& extensions(GLExtensions::getInstance());
268 result.appendFormat("GLES: %s, %s, %s\n",
269 extensions.getVendor(),
270 extensions.getRenderer(),
271 extensions.getVersion());
272 result.appendFormat("%s\n", extensions.getExtension());
273}
274
Mathias Agopian3f844832013-08-07 21:24:32 -0700275// ---------------------------------------------------------------------------
276
277RenderEngine::BindImageAsFramebuffer::BindImageAsFramebuffer(
278 RenderEngine& engine, EGLImageKHR image) : mEngine(engine)
279{
Mathias Agopian458197d2013-08-15 14:56:51 -0700280 mEngine.bindImageAsFramebuffer(image, &mTexName, &mFbName, &mStatus);
281
Mathias Agopian3f844832013-08-07 21:24:32 -0700282 ALOGE_IF(mStatus != GL_FRAMEBUFFER_COMPLETE_OES,
283 "glCheckFramebufferStatusOES error %d", mStatus);
Mathias Agopian3f844832013-08-07 21:24:32 -0700284}
285
286RenderEngine::BindImageAsFramebuffer::~BindImageAsFramebuffer() {
287 // back to main framebuffer
Mathias Agopian458197d2013-08-15 14:56:51 -0700288 mEngine.unbindFramebuffer(mTexName, mFbName);
Mathias Agopian3f844832013-08-07 21:24:32 -0700289}
290
291status_t RenderEngine::BindImageAsFramebuffer::getStatus() const {
292 return mStatus == GL_FRAMEBUFFER_COMPLETE_OES ? NO_ERROR : BAD_VALUE;
293}
294
Mathias Agopian875d8e12013-06-07 15:35:48 -0700295// ---------------------------------------------------------------------------
Jesse Hall05f8c702013-12-23 20:44:38 -0800296
297static status_t selectConfigForAttribute(EGLDisplay dpy, EGLint const* attrs,
298 EGLint attribute, EGLint wanted, EGLConfig* outConfig) {
Jesse Hall05f8c702013-12-23 20:44:38 -0800299 EGLint numConfigs = -1, n = 0;
300 eglGetConfigs(dpy, NULL, 0, &numConfigs);
301 EGLConfig* const configs = new EGLConfig[numConfigs];
302 eglChooseConfig(dpy, attrs, configs, numConfigs, &n);
303
304 if (n) {
305 if (attribute != EGL_NONE) {
306 for (int i=0 ; i<n ; i++) {
307 EGLint value = 0;
308 eglGetConfigAttrib(dpy, configs[i], attribute, &value);
309 if (wanted == value) {
310 *outConfig = configs[i];
311 delete [] configs;
312 return NO_ERROR;
313 }
314 }
315 } else {
316 // just pick the first one
317 *outConfig = configs[0];
318 delete [] configs;
319 return NO_ERROR;
320 }
321 }
322 delete [] configs;
323 return NAME_NOT_FOUND;
324}
325
326class EGLAttributeVector {
327 struct Attribute;
328 class Adder;
329 friend class Adder;
330 KeyedVector<Attribute, EGLint> mList;
331 struct Attribute {
Pablo Ceballos53390e12015-08-04 11:25:59 -0700332 Attribute() : v(0) {};
Chih-Hung Hsiehc4067912016-05-03 14:03:27 -0700333 explicit Attribute(EGLint v) : v(v) { }
Jesse Hall05f8c702013-12-23 20:44:38 -0800334 EGLint v;
335 bool operator < (const Attribute& other) const {
336 // this places EGL_NONE at the end
337 EGLint lhs(v);
338 EGLint rhs(other.v);
339 if (lhs == EGL_NONE) lhs = 0x7FFFFFFF;
340 if (rhs == EGL_NONE) rhs = 0x7FFFFFFF;
341 return lhs < rhs;
342 }
343 };
344 class Adder {
345 friend class EGLAttributeVector;
346 EGLAttributeVector& v;
347 EGLint attribute;
348 Adder(EGLAttributeVector& v, EGLint attribute)
349 : v(v), attribute(attribute) {
350 }
351 public:
352 void operator = (EGLint value) {
353 if (attribute != EGL_NONE) {
Chih-Hung Hsiehc4067912016-05-03 14:03:27 -0700354 v.mList.add(Attribute(attribute), value);
Jesse Hall05f8c702013-12-23 20:44:38 -0800355 }
356 }
357 operator EGLint () const { return v.mList[attribute]; }
358 };
359public:
360 EGLAttributeVector() {
Chih-Hung Hsiehc4067912016-05-03 14:03:27 -0700361 mList.add(Attribute(EGL_NONE), EGL_NONE);
Jesse Hall05f8c702013-12-23 20:44:38 -0800362 }
363 void remove(EGLint attribute) {
364 if (attribute != EGL_NONE) {
Chih-Hung Hsiehc4067912016-05-03 14:03:27 -0700365 mList.removeItem(Attribute(attribute));
Jesse Hall05f8c702013-12-23 20:44:38 -0800366 }
367 }
368 Adder operator [] (EGLint attribute) {
369 return Adder(*this, attribute);
370 }
371 EGLint operator [] (EGLint attribute) const {
372 return mList[attribute];
373 }
374 // cast-operator to (EGLint const*)
375 operator EGLint const* () const { return &mList.keyAt(0).v; }
376};
377
378
379static status_t selectEGLConfig(EGLDisplay display, EGLint format,
380 EGLint renderableType, EGLConfig* config) {
381 // select our EGLConfig. It must support EGL_RECORDABLE_ANDROID if
382 // it is to be used with WIFI displays
383 status_t err;
384 EGLint wantedAttribute;
385 EGLint wantedAttributeValue;
386
387 EGLAttributeVector attribs;
388 if (renderableType) {
389 attribs[EGL_RENDERABLE_TYPE] = renderableType;
390 attribs[EGL_RECORDABLE_ANDROID] = EGL_TRUE;
391 attribs[EGL_SURFACE_TYPE] = EGL_WINDOW_BIT|EGL_PBUFFER_BIT;
392 attribs[EGL_FRAMEBUFFER_TARGET_ANDROID] = EGL_TRUE;
393 attribs[EGL_RED_SIZE] = 8;
394 attribs[EGL_GREEN_SIZE] = 8;
395 attribs[EGL_BLUE_SIZE] = 8;
neo.hee7f39722017-03-21 11:48:36 +0800396 attribs[EGL_ALPHA_SIZE] = 8;
Jesse Hall05f8c702013-12-23 20:44:38 -0800397 wantedAttribute = EGL_NONE;
398 wantedAttributeValue = EGL_NONE;
399 } else {
400 // if no renderable type specified, fallback to a simplified query
401 wantedAttribute = EGL_NATIVE_VISUAL_ID;
402 wantedAttributeValue = format;
403 }
404
405 err = selectConfigForAttribute(display, attribs,
406 wantedAttribute, wantedAttributeValue, config);
407 if (err == NO_ERROR) {
408 EGLint caveat;
409 if (eglGetConfigAttrib(display, *config, EGL_CONFIG_CAVEAT, &caveat))
410 ALOGW_IF(caveat == EGL_SLOW_CONFIG, "EGL_SLOW_CONFIG selected!");
411 }
412
413 return err;
414}
415
Steven Thomasd7f49c52017-07-26 18:48:28 -0700416EGLConfig RenderEngine::chooseEglConfig(EGLDisplay display, int format,
417 bool logConfig) {
Jesse Hall05f8c702013-12-23 20:44:38 -0800418 status_t err;
419 EGLConfig config;
420
421 // First try to get an ES2 config
422 err = selectEGLConfig(display, format, EGL_OPENGL_ES2_BIT, &config);
423 if (err != NO_ERROR) {
424 // If ES2 fails, try ES1
425 err = selectEGLConfig(display, format, EGL_OPENGL_ES_BIT, &config);
426 if (err != NO_ERROR) {
427 // still didn't work, probably because we're on the emulator...
428 // try a simplified query
429 ALOGW("no suitable EGLConfig found, trying a simpler query");
430 err = selectEGLConfig(display, format, 0, &config);
431 if (err != NO_ERROR) {
432 // this EGL is too lame for android
433 LOG_ALWAYS_FATAL("no suitable EGLConfig found, giving up");
434 }
435 }
436 }
437
Steven Thomasd7f49c52017-07-26 18:48:28 -0700438 if (logConfig) {
439 // print some debugging info
440 EGLint r,g,b,a;
441 eglGetConfigAttrib(display, config, EGL_RED_SIZE, &r);
442 eglGetConfigAttrib(display, config, EGL_GREEN_SIZE, &g);
443 eglGetConfigAttrib(display, config, EGL_BLUE_SIZE, &b);
444 eglGetConfigAttrib(display, config, EGL_ALPHA_SIZE, &a);
445 ALOGI("EGL information:");
446 ALOGI("vendor : %s", eglQueryString(display, EGL_VENDOR));
447 ALOGI("version : %s", eglQueryString(display, EGL_VERSION));
448 ALOGI("extensions: %s", eglQueryString(display, EGL_EXTENSIONS));
449 ALOGI("Client API: %s", eglQueryString(display, EGL_CLIENT_APIS)?:"Not Supported");
450 ALOGI("EGLSurface: %d-%d-%d-%d, config=%p", r, g, b, a, config);
451 }
Jesse Hall05f8c702013-12-23 20:44:38 -0800452
453 return config;
454}
455
Dan Stoza4e637772016-07-28 13:31:51 -0700456
457void RenderEngine::primeCache() const {
458 // Getting the ProgramCache instance causes it to prime its shader cache,
459 // which is performed in its constructor
460 ProgramCache::getInstance();
461}
462
Jesse Hall05f8c702013-12-23 20:44:38 -0800463// ---------------------------------------------------------------------------
Mathias Agopian875d8e12013-06-07 15:35:48 -0700464}; // namespace android
465// ---------------------------------------------------------------------------