rsdAllocation.cpp revision cf27eb46f97cff087ebfc5b81fe998eabe0569cf
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 = malloc(alloc->mHal.state.type->getSizeBytes());
223    if (!ptr) {
224        free(drv);
225        return false;
226    }
227
228    drv->glTarget = GL_NONE;
229    if (alloc->mHal.state.usageFlags & RS_ALLOCATION_USAGE_GRAPHICS_TEXTURE) {
230        if (alloc->mHal.state.hasFaces) {
231            drv->glTarget = GL_TEXTURE_CUBE_MAP;
232        } else {
233            drv->glTarget = GL_TEXTURE_2D;
234        }
235    } else {
236        if (alloc->mHal.state.usageFlags & RS_ALLOCATION_USAGE_GRAPHICS_VERTEX) {
237            drv->glTarget = GL_ARRAY_BUFFER;
238        }
239    }
240
241    drv->glType = rsdTypeToGLType(alloc->mHal.state.type->getElement()->getComponent().getType());
242    drv->glFormat = rsdKindToGLFormat(alloc->mHal.state.type->getElement()->getComponent().getKind());
243
244
245    alloc->mHal.drvState.mallocPtr = ptr;
246    drv->mallocPtr = (uint8_t *)ptr;
247    alloc->mHal.drv = drv;
248    if (forceZero) {
249        memset(ptr, 0, alloc->mHal.state.type->getSizeBytes());
250    }
251
252    if (alloc->mHal.state.usageFlags & ~RS_ALLOCATION_USAGE_SCRIPT) {
253        drv->uploadDeferred = true;
254    }
255
256    drv->readBackFBO = NULL;
257
258    return true;
259}
260
261void rsdAllocationDestroy(const Context *rsc, Allocation *alloc) {
262    DrvAllocation *drv = (DrvAllocation *)alloc->mHal.drv;
263
264    if (drv->bufferID) {
265        // Causes a SW crash....
266        //ALOGV(" mBufferID %i", mBufferID);
267        //glDeleteBuffers(1, &mBufferID);
268        //mBufferID = 0;
269    }
270    if (drv->textureID) {
271        RSD_CALL_GL(glDeleteTextures, 1, &drv->textureID);
272        drv->textureID = 0;
273    }
274    if (drv->renderTargetID) {
275        RSD_CALL_GL(glDeleteRenderbuffers, 1, &drv->renderTargetID);
276        drv->renderTargetID = 0;
277    }
278
279    if (drv->mallocPtr) {
280        free(drv->mallocPtr);
281        drv->mallocPtr = NULL;
282    }
283    if (drv->readBackFBO != NULL) {
284        delete drv->readBackFBO;
285        drv->readBackFBO = NULL;
286    }
287    free(drv);
288    alloc->mHal.drv = NULL;
289}
290
291void rsdAllocationResize(const Context *rsc, const Allocation *alloc,
292                         const Type *newType, bool zeroNew) {
293    DrvAllocation *drv = (DrvAllocation *)alloc->mHal.drv;
294
295    drv->mallocPtr = (uint8_t *)realloc(drv->mallocPtr, newType->getSizeBytes());
296
297    // fixme
298    ((Allocation *)alloc)->mHal.drvState.mallocPtr = drv->mallocPtr;
299
300    const uint32_t oldDimX = alloc->mHal.state.dimensionX;
301    const uint32_t dimX = newType->getDimX();
302
303    if (dimX > oldDimX) {
304        const Element *e = alloc->mHal.state.type->getElement();
305        uint32_t stride = e->getSizeBytes();
306        memset(((uint8_t *)drv->mallocPtr) + stride * oldDimX, 0, stride * (dimX - oldDimX));
307    }
308}
309
310static void rsdAllocationSyncFromFBO(const Context *rsc, const Allocation *alloc) {
311    if (!alloc->getIsScript()) {
312        return; // nothing to sync
313    }
314
315    RsdHal *dc = (RsdHal *)rsc->mHal.drv;
316    RsdFrameBufferObj *lastFbo = dc->gl.currentFrameBuffer;
317
318    DrvAllocation *drv = (DrvAllocation *)alloc->mHal.drv;
319    if (!drv->textureID && !drv->renderTargetID) {
320        return; // nothing was rendered here yet, so nothing to sync
321    }
322    if (drv->readBackFBO == NULL) {
323        drv->readBackFBO = new RsdFrameBufferObj();
324        drv->readBackFBO->setColorTarget(drv, 0);
325        drv->readBackFBO->setDimensions(alloc->getType()->getDimX(),
326                                        alloc->getType()->getDimY());
327    }
328
329    // Bind the framebuffer object so we can read back from it
330    drv->readBackFBO->setActive(rsc);
331
332    // Do the readback
333    RSD_CALL_GL(glReadPixels, 0, 0, alloc->getType()->getDimX(), alloc->getType()->getDimY(),
334                 drv->glFormat, drv->glType, alloc->getPtr());
335
336    // Revert framebuffer to its original
337    lastFbo->setActive(rsc);
338}
339
340
341void rsdAllocationSyncAll(const Context *rsc, const Allocation *alloc,
342                         RsAllocationUsageType src) {
343    DrvAllocation *drv = (DrvAllocation *)alloc->mHal.drv;
344
345    if (src == RS_ALLOCATION_USAGE_GRAPHICS_RENDER_TARGET) {
346        if(!alloc->getIsRenderTarget()) {
347            rsc->setError(RS_ERROR_FATAL_DRIVER,
348                          "Attempting to sync allocation from render target, "
349                          "for non-render target allocation");
350        } else if (alloc->getType()->getElement()->getKind() != RS_KIND_PIXEL_RGBA) {
351            rsc->setError(RS_ERROR_FATAL_DRIVER, "Cannot only sync from RGBA"
352                                                 "render target");
353        } else {
354            rsdAllocationSyncFromFBO(rsc, alloc);
355        }
356        return;
357    }
358
359    rsAssert(src == RS_ALLOCATION_USAGE_SCRIPT);
360
361    if (alloc->mHal.state.usageFlags & RS_ALLOCATION_USAGE_GRAPHICS_TEXTURE) {
362        UploadToTexture(rsc, alloc);
363    } else {
364        if (alloc->mHal.state.usageFlags & RS_ALLOCATION_USAGE_GRAPHICS_RENDER_TARGET) {
365            AllocateRenderTarget(rsc, alloc);
366        }
367    }
368    if (alloc->mHal.state.usageFlags & RS_ALLOCATION_USAGE_GRAPHICS_VERTEX) {
369        UploadToBufferObject(rsc, alloc);
370    }
371
372    drv->uploadDeferred = false;
373}
374
375void rsdAllocationMarkDirty(const Context *rsc, const Allocation *alloc) {
376    DrvAllocation *drv = (DrvAllocation *)alloc->mHal.drv;
377    drv->uploadDeferred = true;
378}
379
380int32_t rsdAllocationInitSurfaceTexture(const Context *rsc, const Allocation *alloc) {
381    DrvAllocation *drv = (DrvAllocation *)alloc->mHal.drv;
382    UploadToTexture(rsc, alloc);
383    return drv->textureID;
384}
385
386void rsdAllocationData1D(const Context *rsc, const Allocation *alloc,
387                         uint32_t xoff, uint32_t lod, uint32_t count,
388                         const void *data, uint32_t sizeBytes) {
389    DrvAllocation *drv = (DrvAllocation *)alloc->mHal.drv;
390
391    const uint32_t eSize = alloc->mHal.state.type->getElementSizeBytes();
392    uint8_t * ptr = drv->mallocPtr;
393    ptr += eSize * xoff;
394    uint32_t size = count * eSize;
395
396    if (alloc->mHal.state.hasReferences) {
397        alloc->incRefs(data, count);
398        alloc->decRefs(ptr, count);
399    }
400
401    memcpy(ptr, data, size);
402    drv->uploadDeferred = true;
403}
404
405void rsdAllocationData2D(const Context *rsc, const Allocation *alloc,
406                         uint32_t xoff, uint32_t yoff, uint32_t lod, RsAllocationCubemapFace face,
407                         uint32_t w, uint32_t h, const void *data, uint32_t sizeBytes) {
408    DrvAllocation *drv = (DrvAllocation *)alloc->mHal.drv;
409
410    uint32_t eSize = alloc->mHal.state.elementSizeBytes;
411    uint32_t lineSize = eSize * w;
412    uint32_t destW = alloc->mHal.state.dimensionX;
413
414    if (drv->mallocPtr) {
415        const uint8_t *src = static_cast<const uint8_t *>(data);
416        uint8_t *dst = drv->mallocPtr;
417        dst += alloc->mHal.state.type->getLODFaceOffset(lod, face, xoff, yoff);
418
419        for (uint32_t line=yoff; line < (yoff+h); line++) {
420            if (alloc->mHal.state.hasReferences) {
421                alloc->incRefs(src, w);
422                alloc->decRefs(dst, w);
423            }
424            memcpy(dst, src, lineSize);
425            src += lineSize;
426            dst += destW * eSize;
427        }
428        drv->uploadDeferred = true;
429    } else {
430        Update2DTexture(rsc, alloc, data, xoff, yoff, lod, face, w, h);
431    }
432}
433
434void rsdAllocationData3D(const Context *rsc, const Allocation *alloc,
435                         uint32_t xoff, uint32_t yoff, uint32_t zoff,
436                         uint32_t lod, RsAllocationCubemapFace face,
437                         uint32_t w, uint32_t h, uint32_t d, const void *data, uint32_t sizeBytes) {
438
439}
440
441void rsdAllocationData1D_alloc(const android::renderscript::Context *rsc,
442                               const android::renderscript::Allocation *dstAlloc,
443                               uint32_t dstXoff, uint32_t dstLod, uint32_t count,
444                               const android::renderscript::Allocation *srcAlloc,
445                               uint32_t srcXoff, uint32_t srcLod) {
446}
447
448uint8_t *getOffsetPtr(const android::renderscript::Allocation *alloc,
449                      uint32_t xoff, uint32_t yoff, uint32_t lod,
450                      RsAllocationCubemapFace face) {
451    uint8_t *ptr = static_cast<uint8_t *>(alloc->getPtr());
452    ptr += alloc->getType()->getLODOffset(lod, xoff, yoff);
453
454    if (face != 0) {
455        uint32_t totalSizeBytes = alloc->getType()->getSizeBytes();
456        uint32_t faceOffset = totalSizeBytes / 6;
457        ptr += faceOffset * (uint32_t)face;
458    }
459    return ptr;
460}
461
462
463void rsdAllocationData2D_alloc_script(const android::renderscript::Context *rsc,
464                                      const android::renderscript::Allocation *dstAlloc,
465                                      uint32_t dstXoff, uint32_t dstYoff, uint32_t dstLod,
466                                      RsAllocationCubemapFace dstFace, uint32_t w, uint32_t h,
467                                      const android::renderscript::Allocation *srcAlloc,
468                                      uint32_t srcXoff, uint32_t srcYoff, uint32_t srcLod,
469                                      RsAllocationCubemapFace srcFace) {
470    uint32_t elementSize = dstAlloc->getType()->getElementSizeBytes();
471    for (uint32_t i = 0; i < h; i ++) {
472        uint8_t *dstPtr = getOffsetPtr(dstAlloc, dstXoff, dstYoff + i, dstLod, dstFace);
473        uint8_t *srcPtr = getOffsetPtr(srcAlloc, srcXoff, srcYoff + i, srcLod, srcFace);
474        memcpy(dstPtr, srcPtr, w * elementSize);
475
476        //ALOGE("COPIED dstXoff(%u), dstYoff(%u), dstLod(%u), dstFace(%u), w(%u), h(%u), srcXoff(%u), srcYoff(%u), srcLod(%u), srcFace(%u)",
477        //     dstXoff, dstYoff, dstLod, dstFace, w, h, srcXoff, srcYoff, srcLod, srcFace);
478    }
479}
480
481void rsdAllocationData2D_alloc(const android::renderscript::Context *rsc,
482                               const android::renderscript::Allocation *dstAlloc,
483                               uint32_t dstXoff, uint32_t dstYoff, uint32_t dstLod,
484                               RsAllocationCubemapFace dstFace, uint32_t w, uint32_t h,
485                               const android::renderscript::Allocation *srcAlloc,
486                               uint32_t srcXoff, uint32_t srcYoff, uint32_t srcLod,
487                               RsAllocationCubemapFace srcFace) {
488    if (!dstAlloc->getIsScript() && !srcAlloc->getIsScript()) {
489        rsc->setError(RS_ERROR_FATAL_DRIVER, "Non-script allocation copies not "
490                                             "yet implemented.");
491        return;
492    }
493    rsdAllocationData2D_alloc_script(rsc, dstAlloc, dstXoff, dstYoff,
494                                     dstLod, dstFace, w, h, srcAlloc,
495                                     srcXoff, srcYoff, srcLod, srcFace);
496}
497
498void rsdAllocationData3D_alloc(const android::renderscript::Context *rsc,
499                               const android::renderscript::Allocation *dstAlloc,
500                               uint32_t dstXoff, uint32_t dstYoff, uint32_t dstZoff,
501                               uint32_t dstLod, RsAllocationCubemapFace dstFace,
502                               uint32_t w, uint32_t h, uint32_t d,
503                               const android::renderscript::Allocation *srcAlloc,
504                               uint32_t srcXoff, uint32_t srcYoff, uint32_t srcZoff,
505                               uint32_t srcLod, RsAllocationCubemapFace srcFace) {
506}
507
508void rsdAllocationElementData1D(const Context *rsc, const Allocation *alloc,
509                                uint32_t x,
510                                const void *data, uint32_t cIdx, uint32_t sizeBytes) {
511    DrvAllocation *drv = (DrvAllocation *)alloc->mHal.drv;
512
513    uint32_t eSize = alloc->mHal.state.elementSizeBytes;
514    uint8_t * ptr = drv->mallocPtr;
515    ptr += eSize * x;
516
517    const Element * e = alloc->mHal.state.type->getElement()->getField(cIdx);
518    ptr += alloc->mHal.state.type->getElement()->getFieldOffsetBytes(cIdx);
519
520    if (alloc->mHal.state.hasReferences) {
521        e->incRefs(data);
522        e->decRefs(ptr);
523    }
524
525    memcpy(ptr, data, sizeBytes);
526    drv->uploadDeferred = true;
527}
528
529void rsdAllocationElementData2D(const Context *rsc, const Allocation *alloc,
530                                uint32_t x, uint32_t y,
531                                const void *data, uint32_t cIdx, uint32_t sizeBytes) {
532    DrvAllocation *drv = (DrvAllocation *)alloc->mHal.drv;
533
534    uint32_t eSize = alloc->mHal.state.elementSizeBytes;
535    uint8_t * ptr = drv->mallocPtr;
536    ptr += eSize * (x + y * alloc->mHal.state.dimensionX);
537
538    const Element * e = alloc->mHal.state.type->getElement()->getField(cIdx);
539    ptr += alloc->mHal.state.type->getElement()->getFieldOffsetBytes(cIdx);
540
541    if (alloc->mHal.state.hasReferences) {
542        e->incRefs(data);
543        e->decRefs(ptr);
544    }
545
546    memcpy(ptr, data, sizeBytes);
547    drv->uploadDeferred = true;
548}
549
550
551