rsdGL.cpp revision 8ce904d48f335650596e0e22e34c45a3dcc3cb4b
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/SurfaceTextureClient.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    dc->gl.shaderCache->cleanupAll();
158    delete dc->gl.shaderCache;
159    delete dc->gl.vertexArrayState;
160
161    if (dc->gl.egl.context != EGL_NO_CONTEXT) {
162        RSD_CALL_GL(eglMakeCurrent, dc->gl.egl.display,
163                    EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
164        RSD_CALL_GL(eglDestroySurface, dc->gl.egl.display, dc->gl.egl.surfaceDefault);
165        if (dc->gl.egl.surface != EGL_NO_SURFACE) {
166            RSD_CALL_GL(eglDestroySurface, dc->gl.egl.display, dc->gl.egl.surface);
167        }
168        RSD_CALL_GL(eglDestroyContext, dc->gl.egl.display, dc->gl.egl.context);
169        checkEglError("eglDestroyContext");
170    }
171
172    gGLContextCount--;
173    if (!gGLContextCount) {
174        RSD_CALL_GL(eglTerminate, dc->gl.egl.display);
175    }
176}
177
178void getConfigData(const Context *rsc,
179                   EGLint *configAttribs, size_t configAttribsLen,
180                   uint32_t numSamples) {
181    memset(configAttribs, 0, configAttribsLen*sizeof(*configAttribs));
182
183    EGLint *configAttribsPtr = configAttribs;
184
185    configAttribsPtr[0] = EGL_SURFACE_TYPE;
186    configAttribsPtr[1] = EGL_WINDOW_BIT;
187    configAttribsPtr += 2;
188
189    configAttribsPtr[0] = EGL_RENDERABLE_TYPE;
190    configAttribsPtr[1] = EGL_OPENGL_ES2_BIT;
191    configAttribsPtr += 2;
192
193    configAttribsPtr[0] = EGL_RED_SIZE;
194    configAttribsPtr[1] = 8;
195    configAttribsPtr += 2;
196
197    configAttribsPtr[0] = EGL_GREEN_SIZE;
198    configAttribsPtr[1] = 8;
199    configAttribsPtr += 2;
200
201    configAttribsPtr[0] = EGL_BLUE_SIZE;
202    configAttribsPtr[1] = 8;
203    configAttribsPtr += 2;
204
205    if (rsc->mUserSurfaceConfig.alphaMin > 0) {
206        configAttribsPtr[0] = EGL_ALPHA_SIZE;
207        configAttribsPtr[1] = rsc->mUserSurfaceConfig.alphaMin;
208        configAttribsPtr += 2;
209    }
210
211    if (rsc->mUserSurfaceConfig.depthMin > 0) {
212        configAttribsPtr[0] = EGL_DEPTH_SIZE;
213        configAttribsPtr[1] = rsc->mUserSurfaceConfig.depthMin;
214        configAttribsPtr += 2;
215    }
216
217    if (rsc->mDev->mForceSW) {
218        configAttribsPtr[0] = EGL_CONFIG_CAVEAT;
219        configAttribsPtr[1] = EGL_SLOW_CONFIG;
220        configAttribsPtr += 2;
221    }
222
223    if (numSamples > 1) {
224        configAttribsPtr[0] = EGL_SAMPLE_BUFFERS;
225        configAttribsPtr[1] = 1;
226        configAttribsPtr[2] = EGL_SAMPLES;
227        configAttribsPtr[3] = numSamples;
228        configAttribsPtr += 4;
229    }
230
231    configAttribsPtr[0] = EGL_NONE;
232    rsAssert(configAttribsPtr < (configAttribs + configAttribsLen));
233}
234
235bool rsdGLInit(const Context *rsc) {
236    RsdHal *dc = (RsdHal *)rsc->mHal.drv;
237
238    dc->gl.egl.numConfigs = -1;
239
240    EGLint configAttribs[128];
241    EGLint context_attribs2[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
242
243    ALOGV("%p initEGL start", rsc);
244    rsc->setWatchdogGL("eglGetDisplay", __LINE__, __FILE__);
245    dc->gl.egl.display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
246    checkEglError("eglGetDisplay");
247
248    RSD_CALL_GL(eglInitialize, dc->gl.egl.display,
249                &dc->gl.egl.majorVersion, &dc->gl.egl.minorVersion);
250    checkEglError("eglInitialize");
251
252    EGLBoolean ret;
253
254    EGLint numConfigs = -1, n = 0;
255    rsc->setWatchdogGL("eglChooseConfig", __LINE__, __FILE__);
256
257    // Try minding a multisample config that matches the user request
258    uint32_t minSample = rsc->mUserSurfaceConfig.samplesMin;
259    uint32_t prefSample = rsc->mUserSurfaceConfig.samplesPref;
260    for (uint32_t sampleCount = prefSample; sampleCount >= minSample; sampleCount--) {
261        getConfigData(rsc, configAttribs, (sizeof(configAttribs) / sizeof(EGLint)), sampleCount);
262        ret = eglChooseConfig(dc->gl.egl.display, configAttribs, 0, 0, &numConfigs);
263        checkEglError("eglGetConfigs", ret);
264        if (numConfigs > 0) {
265            break;
266        }
267    }
268
269    eglSwapInterval(dc->gl.egl.display, 0);
270
271    if (numConfigs) {
272        EGLConfig* const configs = new EGLConfig[numConfigs];
273
274        rsc->setWatchdogGL("eglChooseConfig", __LINE__, __FILE__);
275        ret = eglChooseConfig(dc->gl.egl.display,
276                configAttribs, configs, numConfigs, &n);
277        if (!ret || !n) {
278            checkEglError("eglChooseConfig", ret);
279            ALOGE("%p, couldn't find an EGLConfig matching the screen format\n", rsc);
280        }
281
282        // The first config is guaranteed to over-satisfy the constraints
283        dc->gl.egl.config = configs[0];
284
285        // go through the list and skip configs that over-satisfy our needs
286        for (int i=0 ; i<n ; i++) {
287            if (rsc->mUserSurfaceConfig.alphaMin <= 0) {
288                EGLint alphaSize;
289                eglGetConfigAttrib(dc->gl.egl.display,
290                        configs[i], EGL_ALPHA_SIZE, &alphaSize);
291                if (alphaSize > 0) {
292                    continue;
293                }
294            }
295
296            if (rsc->mUserSurfaceConfig.depthMin <= 0) {
297                EGLint depthSize;
298                eglGetConfigAttrib(dc->gl.egl.display,
299                        configs[i], EGL_DEPTH_SIZE, &depthSize);
300                if (depthSize > 0) {
301                    continue;
302                }
303            }
304
305            // Found one!
306            dc->gl.egl.config = configs[i];
307            break;
308        }
309
310        delete [] configs;
311    }
312
313    //if (props.mLogVisual) {
314    if (0) {
315        printEGLConfiguration(dc->gl.egl.display, dc->gl.egl.config);
316    }
317    //}
318
319    rsc->setWatchdogGL("eglCreateContext", __LINE__, __FILE__);
320    dc->gl.egl.context = eglCreateContext(dc->gl.egl.display, dc->gl.egl.config,
321                                          EGL_NO_CONTEXT, context_attribs2);
322    checkEglError("eglCreateContext");
323    if (dc->gl.egl.context == EGL_NO_CONTEXT) {
324        ALOGE("%p, eglCreateContext returned EGL_NO_CONTEXT", rsc);
325        rsc->setWatchdogGL(NULL, 0, NULL);
326        return false;
327    }
328    gGLContextCount++;
329
330    // Create a BufferQueue with a fake consumer
331    sp<BufferQueue> bq = new BufferQueue();
332    sp<DummyConsumer> dummy = new DummyConsumer(bq);
333    sp<SurfaceTextureClient> stc(new SurfaceTextureClient(static_cast<sp<ISurfaceTexture> >(bq)));
334
335    dc->gl.egl.surfaceDefault = eglCreateWindowSurface(dc->gl.egl.display, dc->gl.egl.config,
336                                                       static_cast<ANativeWindow*>(stc.get()),
337                                                       NULL);
338
339    checkEglError("eglCreateWindowSurface");
340    if (dc->gl.egl.surfaceDefault == EGL_NO_SURFACE) {
341        ALOGE("eglCreateWindowSurface returned EGL_NO_SURFACE");
342        rsdGLShutdown(rsc);
343        rsc->setWatchdogGL(NULL, 0, NULL);
344        return false;
345    }
346
347    rsc->setWatchdogGL("eglMakeCurrent", __LINE__, __FILE__);
348    ret = eglMakeCurrent(dc->gl.egl.display, dc->gl.egl.surfaceDefault,
349                         dc->gl.egl.surfaceDefault, dc->gl.egl.context);
350    if (ret == EGL_FALSE) {
351        ALOGE("eglMakeCurrent returned EGL_FALSE");
352        checkEglError("eglMakeCurrent", ret);
353        rsdGLShutdown(rsc);
354        rsc->setWatchdogGL(NULL, 0, NULL);
355        return false;
356    }
357
358    dc->gl.gl.version = glGetString(GL_VERSION);
359    dc->gl.gl.vendor = glGetString(GL_VENDOR);
360    dc->gl.gl.renderer = glGetString(GL_RENDERER);
361    dc->gl.gl.extensions = glGetString(GL_EXTENSIONS);
362
363    //ALOGV("EGL Version %i %i", mEGL.mMajorVersion, mEGL.mMinorVersion);
364    //ALOGV("GL Version %s", mGL.mVersion);
365    //ALOGV("GL Vendor %s", mGL.mVendor);
366    //ALOGV("GL Renderer %s", mGL.mRenderer);
367    //ALOGV("GL Extensions %s", mGL.mExtensions);
368
369    const char *verptr = NULL;
370    if (strlen((const char *)dc->gl.gl.version) > 9) {
371        if (!memcmp(dc->gl.gl.version, "OpenGL ES-CM", 12)) {
372            verptr = (const char *)dc->gl.gl.version + 12;
373        }
374        if (!memcmp(dc->gl.gl.version, "OpenGL ES ", 10)) {
375            verptr = (const char *)dc->gl.gl.version + 9;
376        }
377    }
378
379    if (!verptr) {
380        ALOGE("Error, OpenGL ES Lite not supported");
381        rsdGLShutdown(rsc);
382        rsc->setWatchdogGL(NULL, 0, NULL);
383        return false;
384    } else {
385        sscanf(verptr, " %i.%i", &dc->gl.gl.majorVersion, &dc->gl.gl.minorVersion);
386    }
387
388    glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &dc->gl.gl.maxVertexAttribs);
389    glGetIntegerv(GL_MAX_VERTEX_UNIFORM_VECTORS, &dc->gl.gl.maxVertexUniformVectors);
390    glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &dc->gl.gl.maxVertexTextureUnits);
391
392    glGetIntegerv(GL_MAX_VARYING_VECTORS, &dc->gl.gl.maxVaryingVectors);
393    glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &dc->gl.gl.maxTextureImageUnits);
394
395    glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &dc->gl.gl.maxFragmentTextureImageUnits);
396    glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_VECTORS, &dc->gl.gl.maxFragmentUniformVectors);
397
398    dc->gl.gl.OES_texture_npot = NULL != strstr((const char *)dc->gl.gl.extensions,
399                                                "GL_OES_texture_npot");
400    dc->gl.gl.IMG_texture_npot = NULL != strstr((const char *)dc->gl.gl.extensions,
401                                                   "GL_IMG_texture_npot");
402    dc->gl.gl.NV_texture_npot_2D_mipmap = NULL != strstr((const char *)dc->gl.gl.extensions,
403                                                            "GL_NV_texture_npot_2D_mipmap");
404    dc->gl.gl.EXT_texture_max_aniso = 1.0f;
405    bool hasAniso = NULL != strstr((const char *)dc->gl.gl.extensions,
406                                   "GL_EXT_texture_filter_anisotropic");
407    if (hasAniso) {
408        glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &dc->gl.gl.EXT_texture_max_aniso);
409    }
410
411    if (0) {
412        DumpDebug(dc);
413    }
414
415    dc->gl.shaderCache = new RsdShaderCache();
416    dc->gl.vertexArrayState = new RsdVertexArrayState();
417    dc->gl.vertexArrayState->init(dc->gl.gl.maxVertexAttribs);
418    dc->gl.currentFrameBuffer = NULL;
419    dc->mHasGraphics = true;
420
421    ALOGV("%p initGLThread end", rsc);
422    rsc->setWatchdogGL(NULL, 0, NULL);
423    return true;
424}
425
426
427bool rsdGLSetSurface(const Context *rsc, uint32_t w, uint32_t h, RsNativeWindow sur) {
428    RsdHal *dc = (RsdHal *)rsc->mHal.drv;
429
430    EGLBoolean ret;
431    // WAR: Some drivers fail to handle 0 size surfaces correcntly.
432    // Use the pbuffer to avoid this pitfall.
433    if ((dc->gl.egl.surface != NULL) || (w == 0) || (h == 0)) {
434        rsc->setWatchdogGL("eglMakeCurrent", __LINE__, __FILE__);
435        ret = eglMakeCurrent(dc->gl.egl.display, dc->gl.egl.surfaceDefault,
436                             dc->gl.egl.surfaceDefault, dc->gl.egl.context);
437        checkEglError("eglMakeCurrent", ret);
438
439        rsc->setWatchdogGL("eglDestroySurface", __LINE__, __FILE__);
440        ret = eglDestroySurface(dc->gl.egl.display, dc->gl.egl.surface);
441        checkEglError("eglDestroySurface", ret);
442
443        dc->gl.egl.surface = NULL;
444        dc->gl.width = 1;
445        dc->gl.height = 1;
446    }
447
448    if (dc->gl.wndSurface != NULL) {
449        dc->gl.wndSurface->decStrong(NULL);
450    }
451
452    dc->gl.wndSurface = (ANativeWindow *)sur;
453    if (dc->gl.wndSurface != NULL) {
454        dc->gl.wndSurface->incStrong(NULL);
455        dc->gl.width = w;
456        dc->gl.height = h;
457
458        rsc->setWatchdogGL("eglCreateWindowSurface", __LINE__, __FILE__);
459        dc->gl.egl.surface = eglCreateWindowSurface(dc->gl.egl.display, dc->gl.egl.config,
460                                                    dc->gl.wndSurface, NULL);
461        checkEglError("eglCreateWindowSurface");
462        if (dc->gl.egl.surface == EGL_NO_SURFACE) {
463            ALOGE("eglCreateWindowSurface returned EGL_NO_SURFACE");
464        }
465
466        rsc->setWatchdogGL("eglMakeCurrent", __LINE__, __FILE__);
467        ret = eglMakeCurrent(dc->gl.egl.display, dc->gl.egl.surface,
468                             dc->gl.egl.surface, dc->gl.egl.context);
469        checkEglError("eglMakeCurrent", ret);
470    }
471    rsc->setWatchdogGL(NULL, 0, NULL);
472    return true;
473}
474
475void rsdGLSwap(const android::renderscript::Context *rsc) {
476    RsdHal *dc = (RsdHal *)rsc->mHal.drv;
477    RSD_CALL_GL(eglSwapBuffers, dc->gl.egl.display, dc->gl.egl.surface);
478}
479
480void rsdGLSetPriority(const Context *rsc, int32_t priority) {
481    if (priority > 0) {
482        // Mark context as low priority.
483        ALOGV("low pri");
484    } else {
485        ALOGV("normal pri");
486    }
487}
488
489void rsdGLCheckError(const android::renderscript::Context *rsc,
490                     const char *msg, bool isFatal) {
491    GLenum err = glGetError();
492    if (err != GL_NO_ERROR) {
493        char buf[1024];
494        snprintf(buf, sizeof(buf), "GL Error = 0x%08x, from: %s", err, msg);
495
496        if (isFatal) {
497            rsc->setError(RS_ERROR_FATAL_DRIVER, buf);
498        } else {
499            switch (err) {
500            case GL_OUT_OF_MEMORY:
501                rsc->setError(RS_ERROR_OUT_OF_MEMORY, buf);
502                break;
503            default:
504                rsc->setError(RS_ERROR_DRIVER, buf);
505                break;
506            }
507        }
508
509        ALOGE("%p, %s", rsc, buf);
510    }
511
512}
513
514void rsdGLClearColor(const android::renderscript::Context *rsc,
515                     float r, float g, float b, float a) {
516    RSD_CALL_GL(glClearColor, r, g, b, a);
517    RSD_CALL_GL(glClear, GL_COLOR_BUFFER_BIT);
518}
519
520void rsdGLClearDepth(const android::renderscript::Context *rsc, float v) {
521    RSD_CALL_GL(glClearDepthf, v);
522    RSD_CALL_GL(glClear, GL_DEPTH_BUFFER_BIT);
523}
524
525void rsdGLFinish(const android::renderscript::Context *rsc) {
526    RSD_CALL_GL(glFinish);
527}
528
529void rsdGLDrawQuadTexCoords(const android::renderscript::Context *rsc,
530                            float x1, float y1, float z1, float u1, float v1,
531                            float x2, float y2, float z2, float u2, float v2,
532                            float x3, float y3, float z3, float u3, float v3,
533                            float x4, float y4, float z4, float u4, float v4) {
534
535    float vtx[] = {x1,y1,z1, x2,y2,z2, x3,y3,z3, x4,y4,z4};
536    const float tex[] = {u1,v1, u2,v2, u3,v3, u4,v4};
537
538    RsdVertexArray::Attrib attribs[2];
539    attribs[0].set(GL_FLOAT, 3, 12, false, (uint32_t)vtx, "ATTRIB_position");
540    attribs[1].set(GL_FLOAT, 2, 8, false, (uint32_t)tex, "ATTRIB_texture0");
541
542    RsdVertexArray va(attribs, 2);
543    va.setup(rsc);
544
545    RSD_CALL_GL(glDrawArrays, GL_TRIANGLE_FAN, 0, 4);
546}
547