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