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