rsdGL.cpp revision 4961cceab2b71bf0ab59e1b66a7559f67ed28781
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#include <ui/FramebufferNativeWindow.h>
18#include <ui/PixelFormat.h>
19
20#include <system/window.h>
21
22#include <sys/types.h>
23#include <sys/resource.h>
24#include <sched.h>
25
26#include <cutils/properties.h>
27
28#include <GLES/gl.h>
29#include <GLES/glext.h>
30#include <GLES2/gl2.h>
31#include <GLES2/gl2ext.h>
32
33#include <string.h>
34
35#include "rsdCore.h"
36#include "rsdGL.h"
37
38#include <malloc.h>
39#include "rsContext.h"
40#include "rsDevice.h"
41#include "rsdShaderCache.h"
42#include "rsdVertexArray.h"
43#include "rsdFrameBufferObj.h"
44
45#include <gui/Surface.h>
46#include <gui/DummyConsumer.h>
47
48using namespace android;
49using namespace android::renderscript;
50
51static int32_t gGLContextCount = 0;
52
53static void checkEglError(const char* op, EGLBoolean returnVal = EGL_TRUE) {
54    struct EGLUtils {
55        static const char *strerror(EGLint err) {
56            switch (err){
57                case EGL_SUCCESS:           return "EGL_SUCCESS";
58                case EGL_NOT_INITIALIZED:   return "EGL_NOT_INITIALIZED";
59                case EGL_BAD_ACCESS:        return "EGL_BAD_ACCESS";
60                case EGL_BAD_ALLOC:         return "EGL_BAD_ALLOC";
61                case EGL_BAD_ATTRIBUTE:     return "EGL_BAD_ATTRIBUTE";
62                case EGL_BAD_CONFIG:        return "EGL_BAD_CONFIG";
63                case EGL_BAD_CONTEXT:       return "EGL_BAD_CONTEXT";
64                case EGL_BAD_CURRENT_SURFACE: return "EGL_BAD_CURRENT_SURFACE";
65                case EGL_BAD_DISPLAY:       return "EGL_BAD_DISPLAY";
66                case EGL_BAD_MATCH:         return "EGL_BAD_MATCH";
67                case EGL_BAD_NATIVE_PIXMAP: return "EGL_BAD_NATIVE_PIXMAP";
68                case EGL_BAD_NATIVE_WINDOW: return "EGL_BAD_NATIVE_WINDOW";
69                case EGL_BAD_PARAMETER:     return "EGL_BAD_PARAMETER";
70                case EGL_BAD_SURFACE:       return "EGL_BAD_SURFACE";
71                case EGL_CONTEXT_LOST:      return "EGL_CONTEXT_LOST";
72                default: return "UNKNOWN";
73            }
74        }
75    };
76
77    if (returnVal != EGL_TRUE) {
78        fprintf(stderr, "%s() returned %d\n", op, returnVal);
79    }
80
81    for (EGLint error = eglGetError(); error != EGL_SUCCESS; error
82            = eglGetError()) {
83        fprintf(stderr, "after %s() eglError %s (0x%x)\n", op, EGLUtils::strerror(error),
84                error);
85    }
86}
87
88static void printEGLConfiguration(EGLDisplay dpy, EGLConfig config) {
89
90#define X(VAL) {VAL, #VAL}
91    struct {EGLint attribute; const char* name;} names[] = {
92    X(EGL_BUFFER_SIZE),
93    X(EGL_ALPHA_SIZE),
94    X(EGL_BLUE_SIZE),
95    X(EGL_GREEN_SIZE),
96    X(EGL_RED_SIZE),
97    X(EGL_DEPTH_SIZE),
98    X(EGL_STENCIL_SIZE),
99    X(EGL_CONFIG_CAVEAT),
100    X(EGL_CONFIG_ID),
101    X(EGL_LEVEL),
102    X(EGL_MAX_PBUFFER_HEIGHT),
103    X(EGL_MAX_PBUFFER_PIXELS),
104    X(EGL_MAX_PBUFFER_WIDTH),
105    X(EGL_NATIVE_RENDERABLE),
106    X(EGL_NATIVE_VISUAL_ID),
107    X(EGL_NATIVE_VISUAL_TYPE),
108    X(EGL_SAMPLES),
109    X(EGL_SAMPLE_BUFFERS),
110    X(EGL_SURFACE_TYPE),
111    X(EGL_TRANSPARENT_TYPE),
112    X(EGL_TRANSPARENT_RED_VALUE),
113    X(EGL_TRANSPARENT_GREEN_VALUE),
114    X(EGL_TRANSPARENT_BLUE_VALUE),
115    X(EGL_BIND_TO_TEXTURE_RGB),
116    X(EGL_BIND_TO_TEXTURE_RGBA),
117    X(EGL_MIN_SWAP_INTERVAL),
118    X(EGL_MAX_SWAP_INTERVAL),
119    X(EGL_LUMINANCE_SIZE),
120    X(EGL_ALPHA_MASK_SIZE),
121    X(EGL_COLOR_BUFFER_TYPE),
122    X(EGL_RENDERABLE_TYPE),
123    X(EGL_CONFORMANT),
124   };
125#undef X
126
127    for (size_t j = 0; j < sizeof(names) / sizeof(names[0]); j++) {
128        EGLint value = -1;
129        EGLBoolean returnVal = eglGetConfigAttrib(dpy, config, names[j].attribute, &value);
130        if (returnVal) {
131            ALOGV(" %s: %d (0x%x)", names[j].name, value, value);
132        }
133    }
134}
135
136static void DumpDebug(RsdHal *dc) {
137    ALOGE(" EGL ver %i %i", dc->gl.egl.majorVersion, dc->gl.egl.minorVersion);
138    ALOGE(" EGL context %p  surface %p,  Display=%p", dc->gl.egl.context, dc->gl.egl.surface,
139         dc->gl.egl.display);
140    ALOGE(" GL vendor: %s", dc->gl.gl.vendor);
141    ALOGE(" GL renderer: %s", dc->gl.gl.renderer);
142    ALOGE(" GL Version: %s", dc->gl.gl.version);
143    ALOGE(" GL Extensions: %s", dc->gl.gl.extensions);
144    ALOGE(" GL int Versions %i %i", dc->gl.gl.majorVersion, dc->gl.gl.minorVersion);
145
146    ALOGV("MAX Textures %i, %i  %i", dc->gl.gl.maxVertexTextureUnits,
147         dc->gl.gl.maxFragmentTextureImageUnits, dc->gl.gl.maxTextureImageUnits);
148    ALOGV("MAX Attribs %i", dc->gl.gl.maxVertexAttribs);
149    ALOGV("MAX Uniforms %i, %i", dc->gl.gl.maxVertexUniformVectors,
150         dc->gl.gl.maxFragmentUniformVectors);
151    ALOGV("MAX Varyings %i", dc->gl.gl.maxVaryingVectors);
152}
153
154void rsdGLShutdown(const Context *rsc) {
155    RsdHal *dc = (RsdHal *)rsc->mHal.drv;
156
157    rsdGLSetSurface(rsc, 0, 0, NULL);
158    dc->gl.shaderCache->cleanupAll();
159    delete dc->gl.shaderCache;
160    delete dc->gl.vertexArrayState;
161
162    if (dc->gl.egl.context != EGL_NO_CONTEXT) {
163        RSD_CALL_GL(eglMakeCurrent, dc->gl.egl.display,
164                    EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
165        RSD_CALL_GL(eglDestroySurface, dc->gl.egl.display, dc->gl.egl.surfaceDefault);
166        if (dc->gl.egl.surface != EGL_NO_SURFACE) {
167            RSD_CALL_GL(eglDestroySurface, dc->gl.egl.display, dc->gl.egl.surface);
168        }
169        RSD_CALL_GL(eglDestroyContext, dc->gl.egl.display, dc->gl.egl.context);
170        checkEglError("eglDestroyContext");
171    }
172
173    gGLContextCount--;
174    if (!gGLContextCount) {
175        RSD_CALL_GL(eglTerminate, dc->gl.egl.display);
176    }
177}
178
179void getConfigData(const Context *rsc,
180                   EGLint *configAttribs, size_t configAttribsLen,
181                   uint32_t numSamples) {
182    memset(configAttribs, 0, configAttribsLen*sizeof(*configAttribs));
183
184    EGLint *configAttribsPtr = configAttribs;
185
186    configAttribsPtr[0] = EGL_SURFACE_TYPE;
187    configAttribsPtr[1] = EGL_WINDOW_BIT;
188    configAttribsPtr += 2;
189
190    configAttribsPtr[0] = EGL_RENDERABLE_TYPE;
191    configAttribsPtr[1] = EGL_OPENGL_ES2_BIT;
192    configAttribsPtr += 2;
193
194    configAttribsPtr[0] = EGL_RED_SIZE;
195    configAttribsPtr[1] = 8;
196    configAttribsPtr += 2;
197
198    configAttribsPtr[0] = EGL_GREEN_SIZE;
199    configAttribsPtr[1] = 8;
200    configAttribsPtr += 2;
201
202    configAttribsPtr[0] = EGL_BLUE_SIZE;
203    configAttribsPtr[1] = 8;
204    configAttribsPtr += 2;
205
206    if (rsc->mUserSurfaceConfig.alphaMin > 0) {
207        configAttribsPtr[0] = EGL_ALPHA_SIZE;
208        configAttribsPtr[1] = rsc->mUserSurfaceConfig.alphaMin;
209        configAttribsPtr += 2;
210    }
211
212    if (rsc->mUserSurfaceConfig.depthMin > 0) {
213        configAttribsPtr[0] = EGL_DEPTH_SIZE;
214        configAttribsPtr[1] = rsc->mUserSurfaceConfig.depthMin;
215        configAttribsPtr += 2;
216    }
217
218    if (rsc->mDev->mForceSW) {
219        configAttribsPtr[0] = EGL_CONFIG_CAVEAT;
220        configAttribsPtr[1] = EGL_SLOW_CONFIG;
221        configAttribsPtr += 2;
222    }
223
224    if (numSamples > 1) {
225        configAttribsPtr[0] = EGL_SAMPLE_BUFFERS;
226        configAttribsPtr[1] = 1;
227        configAttribsPtr[2] = EGL_SAMPLES;
228        configAttribsPtr[3] = numSamples;
229        configAttribsPtr += 4;
230    }
231
232    configAttribsPtr[0] = EGL_NONE;
233    rsAssert(configAttribsPtr < (configAttribs + configAttribsLen));
234}
235
236bool rsdGLInit(const Context *rsc) {
237    RsdHal *dc = (RsdHal *)rsc->mHal.drv;
238
239    dc->gl.egl.numConfigs = -1;
240
241    EGLint configAttribs[128];
242    EGLint context_attribs2[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
243
244    ALOGV("%p initEGL start", rsc);
245    rsc->setWatchdogGL("eglGetDisplay", __LINE__, __FILE__);
246    dc->gl.egl.display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
247    checkEglError("eglGetDisplay");
248
249    RSD_CALL_GL(eglInitialize, dc->gl.egl.display,
250                &dc->gl.egl.majorVersion, &dc->gl.egl.minorVersion);
251    checkEglError("eglInitialize");
252
253    EGLBoolean ret;
254
255    EGLint numConfigs = -1, n = 0;
256    rsc->setWatchdogGL("eglChooseConfig", __LINE__, __FILE__);
257
258    // Try minding a multisample config that matches the user request
259    uint32_t minSample = rsc->mUserSurfaceConfig.samplesMin;
260    uint32_t prefSample = rsc->mUserSurfaceConfig.samplesPref;
261    for (uint32_t sampleCount = prefSample; sampleCount >= minSample; sampleCount--) {
262        getConfigData(rsc, configAttribs, (sizeof(configAttribs) / sizeof(EGLint)), sampleCount);
263        ret = eglChooseConfig(dc->gl.egl.display, configAttribs, 0, 0, &numConfigs);
264        checkEglError("eglGetConfigs", ret);
265        if (numConfigs > 0) {
266            break;
267        }
268    }
269
270    if (numConfigs) {
271        EGLConfig* const configs = new EGLConfig[numConfigs];
272
273        rsc->setWatchdogGL("eglChooseConfig", __LINE__, __FILE__);
274        ret = eglChooseConfig(dc->gl.egl.display,
275                configAttribs, configs, numConfigs, &n);
276        if (!ret || !n) {
277            checkEglError("eglChooseConfig", ret);
278            ALOGE("%p, couldn't find an EGLConfig matching the screen format\n", rsc);
279        }
280
281        // The first config is guaranteed to over-satisfy the constraints
282        dc->gl.egl.config = configs[0];
283
284        // go through the list and skip configs that over-satisfy our needs
285        for (int i=0 ; i<n ; i++) {
286            if (rsc->mUserSurfaceConfig.alphaMin <= 0) {
287                EGLint alphaSize;
288                eglGetConfigAttrib(dc->gl.egl.display,
289                        configs[i], EGL_ALPHA_SIZE, &alphaSize);
290                if (alphaSize > 0) {
291                    continue;
292                }
293            }
294
295            if (rsc->mUserSurfaceConfig.depthMin <= 0) {
296                EGLint depthSize;
297                eglGetConfigAttrib(dc->gl.egl.display,
298                        configs[i], EGL_DEPTH_SIZE, &depthSize);
299                if (depthSize > 0) {
300                    continue;
301                }
302            }
303
304            // Found one!
305            dc->gl.egl.config = configs[i];
306            break;
307        }
308
309        delete [] configs;
310    }
311
312    //if (props.mLogVisual) {
313    if (0) {
314        printEGLConfiguration(dc->gl.egl.display, dc->gl.egl.config);
315    }
316    //}
317
318    rsc->setWatchdogGL("eglCreateContext", __LINE__, __FILE__);
319    dc->gl.egl.context = eglCreateContext(dc->gl.egl.display, dc->gl.egl.config,
320                                          EGL_NO_CONTEXT, context_attribs2);
321    checkEglError("eglCreateContext");
322    if (dc->gl.egl.context == EGL_NO_CONTEXT) {
323        ALOGE("%p, eglCreateContext returned EGL_NO_CONTEXT", rsc);
324        rsc->setWatchdogGL(NULL, 0, NULL);
325        return false;
326    }
327    gGLContextCount++;
328
329    // Create a BufferQueue with a fake consumer
330    sp<BufferQueue> bq = new BufferQueue();
331    bq->consumerConnect(new DummyConsumer());
332    sp<Surface> stc(new Surface(static_cast<sp<IGraphicBufferProducer> >(bq)));
333
334    dc->gl.egl.surfaceDefault = eglCreateWindowSurface(dc->gl.egl.display, dc->gl.egl.config,
335                                                       static_cast<ANativeWindow*>(stc.get()),
336                                                       NULL);
337
338    checkEglError("eglCreateWindowSurface");
339    if (dc->gl.egl.surfaceDefault == EGL_NO_SURFACE) {
340        ALOGE("eglCreateWindowSurface returned EGL_NO_SURFACE");
341        rsdGLShutdown(rsc);
342        rsc->setWatchdogGL(NULL, 0, NULL);
343        return false;
344    }
345
346    rsc->setWatchdogGL("eglMakeCurrent", __LINE__, __FILE__);
347    ret = eglMakeCurrent(dc->gl.egl.display, dc->gl.egl.surfaceDefault,
348                         dc->gl.egl.surfaceDefault, dc->gl.egl.context);
349    if (ret == EGL_FALSE) {
350        ALOGE("eglMakeCurrent returned EGL_FALSE");
351        checkEglError("eglMakeCurrent", ret);
352        rsdGLShutdown(rsc);
353        rsc->setWatchdogGL(NULL, 0, NULL);
354        return false;
355    }
356
357    dc->gl.gl.version = glGetString(GL_VERSION);
358    dc->gl.gl.vendor = glGetString(GL_VENDOR);
359    dc->gl.gl.renderer = glGetString(GL_RENDERER);
360    dc->gl.gl.extensions = glGetString(GL_EXTENSIONS);
361
362    //ALOGV("EGL Version %i %i", mEGL.mMajorVersion, mEGL.mMinorVersion);
363    //ALOGV("GL Version %s", mGL.mVersion);
364    //ALOGV("GL Vendor %s", mGL.mVendor);
365    //ALOGV("GL Renderer %s", mGL.mRenderer);
366    //ALOGV("GL Extensions %s", mGL.mExtensions);
367
368    const char *verptr = NULL;
369    if (strlen((const char *)dc->gl.gl.version) > 9) {
370        if (!memcmp(dc->gl.gl.version, "OpenGL ES-CM", 12)) {
371            verptr = (const char *)dc->gl.gl.version + 12;
372        }
373        if (!memcmp(dc->gl.gl.version, "OpenGL ES ", 10)) {
374            verptr = (const char *)dc->gl.gl.version + 9;
375        }
376    }
377
378    if (!verptr) {
379        ALOGE("Error, OpenGL ES Lite not supported");
380        rsdGLShutdown(rsc);
381        rsc->setWatchdogGL(NULL, 0, NULL);
382        return false;
383    } else {
384        sscanf(verptr, " %i.%i", &dc->gl.gl.majorVersion, &dc->gl.gl.minorVersion);
385    }
386
387    glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &dc->gl.gl.maxVertexAttribs);
388    glGetIntegerv(GL_MAX_VERTEX_UNIFORM_VECTORS, &dc->gl.gl.maxVertexUniformVectors);
389    glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &dc->gl.gl.maxVertexTextureUnits);
390
391    glGetIntegerv(GL_MAX_VARYING_VECTORS, &dc->gl.gl.maxVaryingVectors);
392    glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &dc->gl.gl.maxTextureImageUnits);
393
394    glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &dc->gl.gl.maxFragmentTextureImageUnits);
395    glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_VECTORS, &dc->gl.gl.maxFragmentUniformVectors);
396
397    dc->gl.gl.OES_texture_npot = NULL != strstr((const char *)dc->gl.gl.extensions,
398                                                "GL_OES_texture_npot");
399    dc->gl.gl.IMG_texture_npot = NULL != strstr((const char *)dc->gl.gl.extensions,
400                                                   "GL_IMG_texture_npot");
401    dc->gl.gl.NV_texture_npot_2D_mipmap = NULL != strstr((const char *)dc->gl.gl.extensions,
402                                                            "GL_NV_texture_npot_2D_mipmap");
403    dc->gl.gl.EXT_texture_max_aniso = 1.0f;
404    bool hasAniso = NULL != strstr((const char *)dc->gl.gl.extensions,
405                                   "GL_EXT_texture_filter_anisotropic");
406    if (hasAniso) {
407        glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &dc->gl.gl.EXT_texture_max_aniso);
408    }
409
410    if (0) {
411        DumpDebug(dc);
412    }
413
414    dc->gl.shaderCache = new RsdShaderCache();
415    dc->gl.vertexArrayState = new RsdVertexArrayState();
416    dc->gl.vertexArrayState->init(dc->gl.gl.maxVertexAttribs);
417    dc->gl.currentFrameBuffer = NULL;
418    dc->mHasGraphics = true;
419
420    ALOGV("%p initGLThread end", rsc);
421    rsc->setWatchdogGL(NULL, 0, NULL);
422    return true;
423}
424
425
426bool rsdGLSetInternalSurface(const Context *rsc, RsNativeWindow sur) {
427    RsdHal *dc = (RsdHal *)rsc->mHal.drv;
428
429    EGLBoolean ret;
430    if (dc->gl.egl.surface != NULL) {
431        rsc->setWatchdogGL("eglMakeCurrent", __LINE__, __FILE__);
432        ret = eglMakeCurrent(dc->gl.egl.display, dc->gl.egl.surfaceDefault,
433                             dc->gl.egl.surfaceDefault, dc->gl.egl.context);
434        checkEglError("eglMakeCurrent", ret);
435
436        rsc->setWatchdogGL("eglDestroySurface", __LINE__, __FILE__);
437        ret = eglDestroySurface(dc->gl.egl.display, dc->gl.egl.surface);
438        checkEglError("eglDestroySurface", ret);
439
440        dc->gl.egl.surface = NULL;
441    }
442
443    if (dc->gl.currentWndSurface != NULL) {
444        dc->gl.currentWndSurface->decStrong(NULL);
445    }
446
447    dc->gl.currentWndSurface = (ANativeWindow *)sur;
448    if (dc->gl.currentWndSurface != NULL) {
449        dc->gl.currentWndSurface->incStrong(NULL);
450
451        rsc->setWatchdogGL("eglCreateWindowSurface", __LINE__, __FILE__);
452        dc->gl.egl.surface = eglCreateWindowSurface(dc->gl.egl.display, dc->gl.egl.config,
453                                                    dc->gl.currentWndSurface, NULL);
454        checkEglError("eglCreateWindowSurface");
455        if (dc->gl.egl.surface == EGL_NO_SURFACE) {
456            ALOGE("eglCreateWindowSurface returned EGL_NO_SURFACE");
457        }
458
459        rsc->setWatchdogGL("eglMakeCurrent", __LINE__, __FILE__);
460        ret = eglMakeCurrent(dc->gl.egl.display, dc->gl.egl.surface,
461                             dc->gl.egl.surface, dc->gl.egl.context);
462        checkEglError("eglMakeCurrent", ret);
463    }
464    rsc->setWatchdogGL(NULL, 0, NULL);
465    return true;
466}
467
468bool rsdGLSetSurface(const Context *rsc, uint32_t w, uint32_t h, RsNativeWindow sur) {
469    RsdHal *dc = (RsdHal *)rsc->mHal.drv;
470
471    if (dc->gl.wndSurface != NULL) {
472        dc->gl.wndSurface->decStrong(NULL);
473        dc->gl.wndSurface = NULL;
474    }
475    if(w && h) {
476        // WAR: Some drivers fail to handle 0 size surfaces correctly. Use the
477        // pbuffer to avoid this pitfall.
478        dc->gl.wndSurface = (ANativeWindow *)sur;
479        if (dc->gl.wndSurface != NULL) {
480            dc->gl.wndSurface->incStrong(NULL);
481        }
482    }
483
484    return rsdGLSetInternalSurface(rsc, sur);
485}
486
487void rsdGLSwap(const android::renderscript::Context *rsc) {
488    RsdHal *dc = (RsdHal *)rsc->mHal.drv;
489    RSD_CALL_GL(eglSwapBuffers, dc->gl.egl.display, dc->gl.egl.surface);
490}
491
492void rsdGLSetPriority(const Context *rsc, int32_t priority) {
493    if (priority > 0) {
494        // Mark context as low priority.
495        ALOGV("low pri");
496    } else {
497        ALOGV("normal pri");
498    }
499}
500
501void rsdGLCheckError(const android::renderscript::Context *rsc,
502                     const char *msg, bool isFatal) {
503    GLenum err = glGetError();
504    if (err != GL_NO_ERROR) {
505        char buf[1024];
506        snprintf(buf, sizeof(buf), "GL Error = 0x%08x, from: %s", err, msg);
507
508        if (isFatal) {
509            rsc->setError(RS_ERROR_FATAL_DRIVER, buf);
510        } else {
511            switch (err) {
512            case GL_OUT_OF_MEMORY:
513                rsc->setError(RS_ERROR_OUT_OF_MEMORY, buf);
514                break;
515            default:
516                rsc->setError(RS_ERROR_DRIVER, buf);
517                break;
518            }
519        }
520
521        ALOGE("%p, %s", rsc, buf);
522    }
523
524}
525
526void rsdGLClearColor(const android::renderscript::Context *rsc,
527                     float r, float g, float b, float a) {
528    RSD_CALL_GL(glClearColor, r, g, b, a);
529    RSD_CALL_GL(glClear, GL_COLOR_BUFFER_BIT);
530}
531
532void rsdGLClearDepth(const android::renderscript::Context *rsc, float v) {
533    RSD_CALL_GL(glClearDepthf, v);
534    RSD_CALL_GL(glClear, GL_DEPTH_BUFFER_BIT);
535}
536
537void rsdGLFinish(const android::renderscript::Context *rsc) {
538    RSD_CALL_GL(glFinish);
539}
540
541void rsdGLDrawQuadTexCoords(const android::renderscript::Context *rsc,
542                            float x1, float y1, float z1, float u1, float v1,
543                            float x2, float y2, float z2, float u2, float v2,
544                            float x3, float y3, float z3, float u3, float v3,
545                            float x4, float y4, float z4, float u4, float v4) {
546
547    float vtx[] = {x1,y1,z1, x2,y2,z2, x3,y3,z3, x4,y4,z4};
548    const float tex[] = {u1,v1, u2,v2, u3,v3, u4,v4};
549
550    RsdVertexArray::Attrib attribs[2];
551    attribs[0].set(GL_FLOAT, 3, 12, false, (uint32_t)vtx, "ATTRIB_position");
552    attribs[1].set(GL_FLOAT, 2, 8, false, (uint32_t)tex, "ATTRIB_texture0");
553
554    RsdVertexArray va(attribs, 2);
555    va.setup(rsc);
556
557    RSD_CALL_GL(glDrawArrays, GL_TRIANGLE_FAN, 0, 4);
558}
559