rsdAllocation.cpp revision 41e373d91a60043afa0f9abd026218b49cbc1201
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 18#include "rsdCore.h" 19#include "rsdBcc.h" 20#include "rsdRuntime.h" 21#include "rsdAllocation.h" 22#include "rsdFrameBufferObj.h" 23 24#include "rsAllocation.h" 25 26#include <GLES/gl.h> 27#include <GLES2/gl2.h> 28#include <GLES/glext.h> 29 30using namespace android; 31using namespace android::renderscript; 32 33 34 35const static GLenum gFaceOrder[] = { 36 GL_TEXTURE_CUBE_MAP_POSITIVE_X, 37 GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 38 GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 39 GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 40 GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 41 GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 42}; 43 44 45GLenum rsdTypeToGLType(RsDataType t) { 46 switch (t) { 47 case RS_TYPE_UNSIGNED_5_6_5: return GL_UNSIGNED_SHORT_5_6_5; 48 case RS_TYPE_UNSIGNED_5_5_5_1: return GL_UNSIGNED_SHORT_5_5_5_1; 49 case RS_TYPE_UNSIGNED_4_4_4_4: return GL_UNSIGNED_SHORT_4_4_4_4; 50 51 //case RS_TYPE_FLOAT_16: return GL_HALF_FLOAT; 52 case RS_TYPE_FLOAT_32: return GL_FLOAT; 53 case RS_TYPE_UNSIGNED_8: return GL_UNSIGNED_BYTE; 54 case RS_TYPE_UNSIGNED_16: return GL_UNSIGNED_SHORT; 55 case RS_TYPE_SIGNED_8: return GL_BYTE; 56 case RS_TYPE_SIGNED_16: return GL_SHORT; 57 default: break; 58 } 59 return 0; 60} 61 62GLenum rsdKindToGLFormat(RsDataKind k) { 63 switch (k) { 64 case RS_KIND_PIXEL_L: return GL_LUMINANCE; 65 case RS_KIND_PIXEL_A: return GL_ALPHA; 66 case RS_KIND_PIXEL_LA: return GL_LUMINANCE_ALPHA; 67 case RS_KIND_PIXEL_RGB: return GL_RGB; 68 case RS_KIND_PIXEL_RGBA: return GL_RGBA; 69 case RS_KIND_PIXEL_DEPTH: return GL_DEPTH_COMPONENT16; 70 default: break; 71 } 72 return 0; 73} 74 75 76static void Update2DTexture(const Context *rsc, const Allocation *alloc, const void *ptr, 77 uint32_t xoff, uint32_t yoff, uint32_t lod, 78 RsAllocationCubemapFace face, uint32_t w, uint32_t h) { 79 DrvAllocation *drv = (DrvAllocation *)alloc->mHal.drv; 80 81 rsAssert(drv->textureID); 82 RSD_CALL_GL(glBindTexture, drv->glTarget, drv->textureID); 83 RSD_CALL_GL(glPixelStorei, GL_UNPACK_ALIGNMENT, 1); 84 GLenum t = GL_TEXTURE_2D; 85 if (alloc->mHal.state.hasFaces) { 86 t = gFaceOrder[face]; 87 } 88 RSD_CALL_GL(glTexSubImage2D, t, lod, xoff, yoff, w, h, drv->glFormat, drv->glType, ptr); 89} 90 91 92static void Upload2DTexture(const Context *rsc, const Allocation *alloc, bool isFirstUpload) { 93 DrvAllocation *drv = (DrvAllocation *)alloc->mHal.drv; 94 95 RSD_CALL_GL(glBindTexture, drv->glTarget, drv->textureID); 96 RSD_CALL_GL(glPixelStorei, GL_UNPACK_ALIGNMENT, 1); 97 98 uint32_t faceCount = 1; 99 if (alloc->mHal.state.hasFaces) { 100 faceCount = 6; 101 } 102 103 rsdGLCheckError(rsc, "Upload2DTexture 1 "); 104 for (uint32_t face = 0; face < faceCount; face ++) { 105 for (uint32_t lod = 0; lod < alloc->mHal.state.type->getLODCount(); lod++) { 106 const uint8_t *p = (const uint8_t *)drv->mallocPtr; 107 p += alloc->mHal.state.type->getLODFaceOffset(lod, (RsAllocationCubemapFace)face, 0, 0); 108 109 GLenum t = GL_TEXTURE_2D; 110 if (alloc->mHal.state.hasFaces) { 111 t = gFaceOrder[face]; 112 } 113 114 if (isFirstUpload) { 115 RSD_CALL_GL(glTexImage2D, t, lod, drv->glFormat, 116 alloc->mHal.state.type->getLODDimX(lod), 117 alloc->mHal.state.type->getLODDimY(lod), 118 0, drv->glFormat, drv->glType, p); 119 } else { 120 RSD_CALL_GL(glTexSubImage2D, t, lod, 0, 0, 121 alloc->mHal.state.type->getLODDimX(lod), 122 alloc->mHal.state.type->getLODDimY(lod), 123 drv->glFormat, drv->glType, p); 124 } 125 } 126 } 127 128 if (alloc->mHal.state.mipmapControl == RS_ALLOCATION_MIPMAP_ON_SYNC_TO_TEXTURE) { 129 RSD_CALL_GL(glGenerateMipmap, drv->glTarget); 130 } 131 rsdGLCheckError(rsc, "Upload2DTexture"); 132} 133 134static void UploadToTexture(const Context *rsc, const Allocation *alloc) { 135 DrvAllocation *drv = (DrvAllocation *)alloc->mHal.drv; 136 137 if (alloc->mHal.state.usageFlags & RS_ALLOCATION_USAGE_GRAPHICS_SURFACE_TEXTURE_INPUT_OPAQUE) { 138 if (!drv->textureID) { 139 RSD_CALL_GL(glGenTextures, 1, &drv->textureID); 140 } 141 return; 142 } 143 144 if (!drv->glType || !drv->glFormat) { 145 return; 146 } 147 148 if (!alloc->getPtr()) { 149 return; 150 } 151 152 bool isFirstUpload = false; 153 154 if (!drv->textureID) { 155 RSD_CALL_GL(glGenTextures, 1, &drv->textureID); 156 isFirstUpload = true; 157 } 158 159 Upload2DTexture(rsc, alloc, isFirstUpload); 160 161 if (!(alloc->mHal.state.usageFlags & RS_ALLOCATION_USAGE_SCRIPT)) { 162 if (drv->mallocPtr) { 163 free(drv->mallocPtr); 164 drv->mallocPtr = NULL; 165 } 166 } 167 rsdGLCheckError(rsc, "UploadToTexture"); 168} 169 170static void AllocateRenderTarget(const Context *rsc, const Allocation *alloc) { 171 DrvAllocation *drv = (DrvAllocation *)alloc->mHal.drv; 172 173 if (!drv->glFormat) { 174 return; 175 } 176 177 if (!drv->renderTargetID) { 178 RSD_CALL_GL(glGenRenderbuffers, 1, &drv->renderTargetID); 179 180 if (!drv->renderTargetID) { 181 // This should generally not happen 182 ALOGE("allocateRenderTarget failed to gen mRenderTargetID"); 183 rsc->dumpDebug(); 184 return; 185 } 186 RSD_CALL_GL(glBindRenderbuffer, GL_RENDERBUFFER, drv->renderTargetID); 187 RSD_CALL_GL(glRenderbufferStorage, GL_RENDERBUFFER, drv->glFormat, 188 alloc->mHal.state.dimensionX, alloc->mHal.state.dimensionY); 189 } 190 rsdGLCheckError(rsc, "AllocateRenderTarget"); 191} 192 193static void UploadToBufferObject(const Context *rsc, const Allocation *alloc) { 194 DrvAllocation *drv = (DrvAllocation *)alloc->mHal.drv; 195 196 rsAssert(!alloc->mHal.state.type->getDimY()); 197 rsAssert(!alloc->mHal.state.type->getDimZ()); 198 199 //alloc->mHal.state.usageFlags |= RS_ALLOCATION_USAGE_GRAPHICS_VERTEX; 200 201 if (!drv->bufferID) { 202 RSD_CALL_GL(glGenBuffers, 1, &drv->bufferID); 203 } 204 if (!drv->bufferID) { 205 ALOGE("Upload to buffer object failed"); 206 drv->uploadDeferred = true; 207 return; 208 } 209 RSD_CALL_GL(glBindBuffer, drv->glTarget, drv->bufferID); 210 RSD_CALL_GL(glBufferData, drv->glTarget, alloc->mHal.state.type->getSizeBytes(), 211 drv->mallocPtr, GL_DYNAMIC_DRAW); 212 RSD_CALL_GL(glBindBuffer, drv->glTarget, 0); 213 rsdGLCheckError(rsc, "UploadToBufferObject"); 214} 215 216bool rsdAllocationInit(const Context *rsc, Allocation *alloc, bool forceZero) { 217 DrvAllocation *drv = (DrvAllocation *)calloc(1, sizeof(DrvAllocation)); 218 if (!drv) { 219 return false; 220 } 221 222 void * ptr = alloc->mHal.state.usrPtr; 223 if (!ptr) { 224 ptr = malloc(alloc->mHal.state.type->getSizeBytes()); 225 if (!ptr) { 226 free(drv); 227 return false; 228 } 229 } 230 231 drv->glTarget = GL_NONE; 232 if (alloc->mHal.state.usageFlags & RS_ALLOCATION_USAGE_GRAPHICS_TEXTURE) { 233 if (alloc->mHal.state.hasFaces) { 234 drv->glTarget = GL_TEXTURE_CUBE_MAP; 235 } else { 236 drv->glTarget = GL_TEXTURE_2D; 237 } 238 } else { 239 if (alloc->mHal.state.usageFlags & RS_ALLOCATION_USAGE_GRAPHICS_VERTEX) { 240 drv->glTarget = GL_ARRAY_BUFFER; 241 } 242 } 243 244 drv->glType = rsdTypeToGLType(alloc->mHal.state.type->getElement()->getComponent().getType()); 245 drv->glFormat = rsdKindToGLFormat(alloc->mHal.state.type->getElement()->getComponent().getKind()); 246 247 248 alloc->mHal.drvState.mallocPtr = ptr; 249 drv->mallocPtr = (uint8_t *)ptr; 250 alloc->mHal.drv = drv; 251 if (forceZero) { 252 memset(ptr, 0, alloc->mHal.state.type->getSizeBytes()); 253 } 254 255 if (alloc->mHal.state.usageFlags & ~RS_ALLOCATION_USAGE_SCRIPT) { 256 drv->uploadDeferred = true; 257 } 258 259 drv->readBackFBO = NULL; 260 261 return true; 262} 263 264void rsdAllocationDestroy(const Context *rsc, Allocation *alloc) { 265 DrvAllocation *drv = (DrvAllocation *)alloc->mHal.drv; 266 267 if (drv->bufferID) { 268 // Causes a SW crash.... 269 //ALOGV(" mBufferID %i", mBufferID); 270 //glDeleteBuffers(1, &mBufferID); 271 //mBufferID = 0; 272 } 273 if (drv->textureID) { 274 RSD_CALL_GL(glDeleteTextures, 1, &drv->textureID); 275 drv->textureID = 0; 276 } 277 if (drv->renderTargetID) { 278 RSD_CALL_GL(glDeleteRenderbuffers, 1, &drv->renderTargetID); 279 drv->renderTargetID = 0; 280 } 281 282 if (drv->mallocPtr && !alloc->mHal.state.usrPtr) { 283 free(drv->mallocPtr); 284 drv->mallocPtr = NULL; 285 } 286 if (drv->readBackFBO != NULL) { 287 delete drv->readBackFBO; 288 drv->readBackFBO = NULL; 289 } 290 free(drv); 291 alloc->mHal.drv = NULL; 292} 293 294void rsdAllocationResize(const Context *rsc, const Allocation *alloc, 295 const Type *newType, bool zeroNew) { 296 DrvAllocation *drv = (DrvAllocation *)alloc->mHal.drv; 297 298 drv->mallocPtr = (uint8_t *)realloc(drv->mallocPtr, newType->getSizeBytes()); 299 300 // fixme 301 ((Allocation *)alloc)->mHal.drvState.mallocPtr = drv->mallocPtr; 302 303 const uint32_t oldDimX = alloc->mHal.state.dimensionX; 304 const uint32_t dimX = newType->getDimX(); 305 306 if (dimX > oldDimX) { 307 const Element *e = alloc->mHal.state.type->getElement(); 308 uint32_t stride = e->getSizeBytes(); 309 memset(((uint8_t *)drv->mallocPtr) + stride * oldDimX, 0, stride * (dimX - oldDimX)); 310 } 311} 312 313static void rsdAllocationSyncFromFBO(const Context *rsc, const Allocation *alloc) { 314 if (!alloc->getIsScript()) { 315 return; // nothing to sync 316 } 317 318 RsdHal *dc = (RsdHal *)rsc->mHal.drv; 319 RsdFrameBufferObj *lastFbo = dc->gl.currentFrameBuffer; 320 321 DrvAllocation *drv = (DrvAllocation *)alloc->mHal.drv; 322 if (!drv->textureID && !drv->renderTargetID) { 323 return; // nothing was rendered here yet, so nothing to sync 324 } 325 if (drv->readBackFBO == NULL) { 326 drv->readBackFBO = new RsdFrameBufferObj(); 327 drv->readBackFBO->setColorTarget(drv, 0); 328 drv->readBackFBO->setDimensions(alloc->getType()->getDimX(), 329 alloc->getType()->getDimY()); 330 } 331 332 // Bind the framebuffer object so we can read back from it 333 drv->readBackFBO->setActive(rsc); 334 335 // Do the readback 336 RSD_CALL_GL(glReadPixels, 0, 0, alloc->getType()->getDimX(), alloc->getType()->getDimY(), 337 drv->glFormat, drv->glType, alloc->getPtr()); 338 339 // Revert framebuffer to its original 340 lastFbo->setActive(rsc); 341} 342 343 344void rsdAllocationSyncAll(const Context *rsc, const Allocation *alloc, 345 RsAllocationUsageType src) { 346 DrvAllocation *drv = (DrvAllocation *)alloc->mHal.drv; 347 348 if (src == RS_ALLOCATION_USAGE_GRAPHICS_RENDER_TARGET) { 349 if(!alloc->getIsRenderTarget()) { 350 rsc->setError(RS_ERROR_FATAL_DRIVER, 351 "Attempting to sync allocation from render target, " 352 "for non-render target allocation"); 353 } else if (alloc->getType()->getElement()->getKind() != RS_KIND_PIXEL_RGBA) { 354 rsc->setError(RS_ERROR_FATAL_DRIVER, "Cannot only sync from RGBA" 355 "render target"); 356 } else { 357 rsdAllocationSyncFromFBO(rsc, alloc); 358 } 359 return; 360 } 361 362 rsAssert(src == RS_ALLOCATION_USAGE_SCRIPT); 363 364 if (alloc->mHal.state.usageFlags & RS_ALLOCATION_USAGE_GRAPHICS_TEXTURE) { 365 UploadToTexture(rsc, alloc); 366 } else { 367 if (alloc->mHal.state.usageFlags & RS_ALLOCATION_USAGE_GRAPHICS_RENDER_TARGET) { 368 AllocateRenderTarget(rsc, alloc); 369 } 370 } 371 if (alloc->mHal.state.usageFlags & RS_ALLOCATION_USAGE_GRAPHICS_VERTEX) { 372 UploadToBufferObject(rsc, alloc); 373 } 374 375 drv->uploadDeferred = false; 376} 377 378void rsdAllocationMarkDirty(const Context *rsc, const Allocation *alloc) { 379 DrvAllocation *drv = (DrvAllocation *)alloc->mHal.drv; 380 drv->uploadDeferred = true; 381} 382 383int32_t rsdAllocationInitSurfaceTexture(const Context *rsc, const Allocation *alloc) { 384 DrvAllocation *drv = (DrvAllocation *)alloc->mHal.drv; 385 UploadToTexture(rsc, alloc); 386 return drv->textureID; 387} 388 389void rsdAllocationData1D(const Context *rsc, const Allocation *alloc, 390 uint32_t xoff, uint32_t lod, uint32_t count, 391 const void *data, uint32_t sizeBytes) { 392 DrvAllocation *drv = (DrvAllocation *)alloc->mHal.drv; 393 394 const uint32_t eSize = alloc->mHal.state.type->getElementSizeBytes(); 395 uint8_t * ptr = drv->mallocPtr; 396 ptr += eSize * xoff; 397 uint32_t size = count * eSize; 398 399 if (alloc->mHal.state.hasReferences) { 400 alloc->incRefs(data, count); 401 alloc->decRefs(ptr, count); 402 } 403 404 memcpy(ptr, data, size); 405 drv->uploadDeferred = true; 406} 407 408void rsdAllocationData2D(const Context *rsc, const Allocation *alloc, 409 uint32_t xoff, uint32_t yoff, uint32_t lod, RsAllocationCubemapFace face, 410 uint32_t w, uint32_t h, const void *data, uint32_t sizeBytes) { 411 DrvAllocation *drv = (DrvAllocation *)alloc->mHal.drv; 412 413 uint32_t eSize = alloc->mHal.state.elementSizeBytes; 414 uint32_t lineSize = eSize * w; 415 uint32_t destW = alloc->mHal.state.dimensionX; 416 417 if (drv->mallocPtr) { 418 const uint8_t *src = static_cast<const uint8_t *>(data); 419 uint8_t *dst = drv->mallocPtr; 420 dst += alloc->mHal.state.type->getLODFaceOffset(lod, face, xoff, yoff); 421 422 for (uint32_t line=yoff; line < (yoff+h); line++) { 423 if (alloc->mHal.state.hasReferences) { 424 alloc->incRefs(src, w); 425 alloc->decRefs(dst, w); 426 } 427 memcpy(dst, src, lineSize); 428 src += lineSize; 429 dst += destW * eSize; 430 } 431 drv->uploadDeferred = true; 432 } else { 433 Update2DTexture(rsc, alloc, data, xoff, yoff, lod, face, w, h); 434 } 435} 436 437void rsdAllocationData3D(const Context *rsc, const Allocation *alloc, 438 uint32_t xoff, uint32_t yoff, uint32_t zoff, 439 uint32_t lod, RsAllocationCubemapFace face, 440 uint32_t w, uint32_t h, uint32_t d, const void *data, uint32_t sizeBytes) { 441 442} 443 444void rsdAllocationData1D_alloc(const android::renderscript::Context *rsc, 445 const android::renderscript::Allocation *dstAlloc, 446 uint32_t dstXoff, uint32_t dstLod, uint32_t count, 447 const android::renderscript::Allocation *srcAlloc, 448 uint32_t srcXoff, uint32_t srcLod) { 449} 450 451uint8_t *getOffsetPtr(const android::renderscript::Allocation *alloc, 452 uint32_t xoff, uint32_t yoff, uint32_t lod, 453 RsAllocationCubemapFace face) { 454 uint8_t *ptr = static_cast<uint8_t *>(alloc->getPtr()); 455 ptr += alloc->getType()->getLODOffset(lod, xoff, yoff); 456 457 if (face != 0) { 458 uint32_t totalSizeBytes = alloc->getType()->getSizeBytes(); 459 uint32_t faceOffset = totalSizeBytes / 6; 460 ptr += faceOffset * (uint32_t)face; 461 } 462 return ptr; 463} 464 465 466void rsdAllocationData2D_alloc_script(const android::renderscript::Context *rsc, 467 const android::renderscript::Allocation *dstAlloc, 468 uint32_t dstXoff, uint32_t dstYoff, uint32_t dstLod, 469 RsAllocationCubemapFace dstFace, uint32_t w, uint32_t h, 470 const android::renderscript::Allocation *srcAlloc, 471 uint32_t srcXoff, uint32_t srcYoff, uint32_t srcLod, 472 RsAllocationCubemapFace srcFace) { 473 uint32_t elementSize = dstAlloc->getType()->getElementSizeBytes(); 474 for (uint32_t i = 0; i < h; i ++) { 475 uint8_t *dstPtr = getOffsetPtr(dstAlloc, dstXoff, dstYoff + i, dstLod, dstFace); 476 uint8_t *srcPtr = getOffsetPtr(srcAlloc, srcXoff, srcYoff + i, srcLod, srcFace); 477 memcpy(dstPtr, srcPtr, w * elementSize); 478 479 //ALOGE("COPIED dstXoff(%u), dstYoff(%u), dstLod(%u), dstFace(%u), w(%u), h(%u), srcXoff(%u), srcYoff(%u), srcLod(%u), srcFace(%u)", 480 // dstXoff, dstYoff, dstLod, dstFace, w, h, srcXoff, srcYoff, srcLod, srcFace); 481 } 482} 483 484void rsdAllocationData2D_alloc(const android::renderscript::Context *rsc, 485 const android::renderscript::Allocation *dstAlloc, 486 uint32_t dstXoff, uint32_t dstYoff, uint32_t dstLod, 487 RsAllocationCubemapFace dstFace, uint32_t w, uint32_t h, 488 const android::renderscript::Allocation *srcAlloc, 489 uint32_t srcXoff, uint32_t srcYoff, uint32_t srcLod, 490 RsAllocationCubemapFace srcFace) { 491 if (!dstAlloc->getIsScript() && !srcAlloc->getIsScript()) { 492 rsc->setError(RS_ERROR_FATAL_DRIVER, "Non-script allocation copies not " 493 "yet implemented."); 494 return; 495 } 496 rsdAllocationData2D_alloc_script(rsc, dstAlloc, dstXoff, dstYoff, 497 dstLod, dstFace, w, h, srcAlloc, 498 srcXoff, srcYoff, srcLod, srcFace); 499} 500 501void rsdAllocationData3D_alloc(const android::renderscript::Context *rsc, 502 const android::renderscript::Allocation *dstAlloc, 503 uint32_t dstXoff, uint32_t dstYoff, uint32_t dstZoff, 504 uint32_t dstLod, RsAllocationCubemapFace dstFace, 505 uint32_t w, uint32_t h, uint32_t d, 506 const android::renderscript::Allocation *srcAlloc, 507 uint32_t srcXoff, uint32_t srcYoff, uint32_t srcZoff, 508 uint32_t srcLod, RsAllocationCubemapFace srcFace) { 509} 510 511void rsdAllocationElementData1D(const Context *rsc, const Allocation *alloc, 512 uint32_t x, 513 const void *data, uint32_t cIdx, uint32_t sizeBytes) { 514 DrvAllocation *drv = (DrvAllocation *)alloc->mHal.drv; 515 516 uint32_t eSize = alloc->mHal.state.elementSizeBytes; 517 uint8_t * ptr = drv->mallocPtr; 518 ptr += eSize * x; 519 520 const Element * e = alloc->mHal.state.type->getElement()->getField(cIdx); 521 ptr += alloc->mHal.state.type->getElement()->getFieldOffsetBytes(cIdx); 522 523 if (alloc->mHal.state.hasReferences) { 524 e->incRefs(data); 525 e->decRefs(ptr); 526 } 527 528 memcpy(ptr, data, sizeBytes); 529 drv->uploadDeferred = true; 530} 531 532void rsdAllocationElementData2D(const Context *rsc, const Allocation *alloc, 533 uint32_t x, uint32_t y, 534 const void *data, uint32_t cIdx, uint32_t sizeBytes) { 535 DrvAllocation *drv = (DrvAllocation *)alloc->mHal.drv; 536 537 uint32_t eSize = alloc->mHal.state.elementSizeBytes; 538 uint8_t * ptr = drv->mallocPtr; 539 ptr += eSize * (x + y * alloc->mHal.state.dimensionX); 540 541 const Element * e = alloc->mHal.state.type->getElement()->getField(cIdx); 542 ptr += alloc->mHal.state.type->getElement()->getFieldOffsetBytes(cIdx); 543 544 if (alloc->mHal.state.hasReferences) { 545 e->incRefs(data); 546 e->decRefs(ptr); 547 } 548 549 memcpy(ptr, data, sizeBytes); 550 drv->uploadDeferred = true; 551} 552 553 554