Home | History | Annotate | Download | only in gl
      1 /*
      2  * Copyright 2016 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #include "GrGLBuffer.h"
      9 #include "GrGLGpu.h"
     10 #include "GrGpuResourcePriv.h"
     11 #include "SkTraceMemoryDump.h"
     12 
     13 #define GL_CALL(X) GR_GL_CALL(this->glGpu()->glInterface(), X)
     14 #define GL_CALL_RET(RET, X) GR_GL_CALL_RET(this->glGpu()->glInterface(), RET, X)
     15 
     16 #if GR_GL_CHECK_ALLOC_WITH_GET_ERROR
     17     #define CLEAR_ERROR_BEFORE_ALLOC(iface)   GrGLClearErr(iface)
     18     #define GL_ALLOC_CALL(iface, call)        GR_GL_CALL_NOERRCHECK(iface, call)
     19     #define CHECK_ALLOC_ERROR(iface)          GR_GL_GET_ERROR(iface)
     20 #else
     21     #define CLEAR_ERROR_BEFORE_ALLOC(iface)
     22     #define GL_ALLOC_CALL(iface, call)        GR_GL_CALL(iface, call)
     23     #define CHECK_ALLOC_ERROR(iface)          GR_GL_NO_ERROR
     24 #endif
     25 
     26 #ifdef SK_DEBUG
     27 #define VALIDATE() this->validate()
     28 #else
     29 #define VALIDATE() do {} while(false)
     30 #endif
     31 
     32 GrGLBuffer* GrGLBuffer::Create(GrGLGpu* gpu, size_t size, GrBufferType intendedType,
     33                                GrAccessPattern accessPattern, const void* data) {
     34     if (gpu->glCaps().transferBufferType() == GrGLCaps::kNone_TransferBufferType &&
     35         (kXferCpuToGpu_GrBufferType == intendedType ||
     36          kXferGpuToCpu_GrBufferType == intendedType)) {
     37         return nullptr;
     38     }
     39 
     40     sk_sp<GrGLBuffer> buffer(new GrGLBuffer(gpu, size, intendedType, accessPattern, data));
     41     if (0 == buffer->bufferID()) {
     42         return nullptr;
     43     }
     44     return buffer.release();
     45 }
     46 
     47 // GL_STREAM_DRAW triggers an optimization in Chromium's GPU process where a client's vertex buffer
     48 // objects are implemented as client-side-arrays on tile-deferred architectures.
     49 #define DYNAMIC_DRAW_PARAM GR_GL_STREAM_DRAW
     50 
     51 inline static GrGLenum gr_to_gl_access_pattern(GrBufferType bufferType,
     52                                                GrAccessPattern accessPattern) {
     53     static const GrGLenum drawUsages[] = {
     54         DYNAMIC_DRAW_PARAM,  // TODO: Do we really want to use STREAM_DRAW here on non-Chromium?
     55         GR_GL_STATIC_DRAW,   // kStatic_GrAccessPattern
     56         GR_GL_STREAM_DRAW    // kStream_GrAccessPattern
     57     };
     58 
     59     static const GrGLenum readUsages[] = {
     60         GR_GL_DYNAMIC_READ,  // kDynamic_GrAccessPattern
     61         GR_GL_STATIC_READ,   // kStatic_GrAccessPattern
     62         GR_GL_STREAM_READ    // kStream_GrAccessPattern
     63     };
     64 
     65     GR_STATIC_ASSERT(0 == kDynamic_GrAccessPattern);
     66     GR_STATIC_ASSERT(1 == kStatic_GrAccessPattern);
     67     GR_STATIC_ASSERT(2 == kStream_GrAccessPattern);
     68     GR_STATIC_ASSERT(SK_ARRAY_COUNT(drawUsages) == 1 + kLast_GrAccessPattern);
     69     GR_STATIC_ASSERT(SK_ARRAY_COUNT(readUsages) == 1 + kLast_GrAccessPattern);
     70 
     71     static GrGLenum const* const usageTypes[] = {
     72         drawUsages,  // kVertex_GrBufferType,
     73         drawUsages,  // kIndex_GrBufferType,
     74         drawUsages,  // kTexel_GrBufferType,
     75         drawUsages,  // kDrawIndirect_GrBufferType,
     76         drawUsages,  // kXferCpuToGpu_GrBufferType,
     77         readUsages   // kXferGpuToCpu_GrBufferType,
     78     };
     79 
     80     GR_STATIC_ASSERT(0 == kVertex_GrBufferType);
     81     GR_STATIC_ASSERT(1 == kIndex_GrBufferType);
     82     GR_STATIC_ASSERT(2 == kTexel_GrBufferType);
     83     GR_STATIC_ASSERT(3 == kDrawIndirect_GrBufferType);
     84     GR_STATIC_ASSERT(4 == kXferCpuToGpu_GrBufferType);
     85     GR_STATIC_ASSERT(5 == kXferGpuToCpu_GrBufferType);
     86     GR_STATIC_ASSERT(SK_ARRAY_COUNT(usageTypes) == kGrBufferTypeCount);
     87 
     88     SkASSERT(bufferType >= 0 && bufferType <= kLast_GrBufferType);
     89     SkASSERT(accessPattern >= 0 && accessPattern <= kLast_GrAccessPattern);
     90 
     91     return usageTypes[bufferType][accessPattern];
     92 }
     93 
     94 GrGLBuffer::GrGLBuffer(GrGLGpu* gpu, size_t size, GrBufferType intendedType,
     95                        GrAccessPattern accessPattern, const void* data)
     96     : INHERITED(gpu, size, intendedType, accessPattern)
     97     , fIntendedType(intendedType)
     98     , fBufferID(0)
     99     , fUsage(gr_to_gl_access_pattern(intendedType, accessPattern))
    100     , fGLSizeInBytes(0)
    101     , fHasAttachedToTexture(false) {
    102     GL_CALL(GenBuffers(1, &fBufferID));
    103     if (fBufferID) {
    104         GrGLenum target = gpu->bindBuffer(fIntendedType, this);
    105         CLEAR_ERROR_BEFORE_ALLOC(gpu->glInterface());
    106         // make sure driver can allocate memory for this buffer
    107         GL_ALLOC_CALL(gpu->glInterface(), BufferData(target,
    108                                                      (GrGLsizeiptr) size,
    109                                                      data,
    110                                                      fUsage));
    111         if (CHECK_ALLOC_ERROR(gpu->glInterface()) != GR_GL_NO_ERROR) {
    112             GL_CALL(DeleteBuffers(1, &fBufferID));
    113             fBufferID = 0;
    114         } else {
    115             fGLSizeInBytes = size;
    116         }
    117     }
    118     VALIDATE();
    119     this->registerWithCache(SkBudgeted::kYes);
    120     if (!fBufferID) {
    121         this->resourcePriv().removeScratchKey();
    122     }
    123 }
    124 
    125 inline GrGLGpu* GrGLBuffer::glGpu() const {
    126     SkASSERT(!this->wasDestroyed());
    127     return static_cast<GrGLGpu*>(this->getGpu());
    128 }
    129 
    130 inline const GrGLCaps& GrGLBuffer::glCaps() const {
    131     return this->glGpu()->glCaps();
    132 }
    133 
    134 void GrGLBuffer::onRelease() {
    135     if (!this->wasDestroyed()) {
    136         VALIDATE();
    137         // make sure we've not been abandoned or already released
    138         if (fBufferID) {
    139             GL_CALL(DeleteBuffers(1, &fBufferID));
    140             fBufferID = 0;
    141             fGLSizeInBytes = 0;
    142             this->glGpu()->notifyBufferReleased(this);
    143         }
    144         fMapPtr = nullptr;
    145         VALIDATE();
    146     }
    147 
    148     INHERITED::onRelease();
    149 }
    150 
    151 void GrGLBuffer::onAbandon() {
    152     fBufferID = 0;
    153     fGLSizeInBytes = 0;
    154     fMapPtr = nullptr;
    155     VALIDATE();
    156     INHERITED::onAbandon();
    157 }
    158 
    159 void GrGLBuffer::onMap() {
    160     SkASSERT(fBufferID);
    161     if (this->wasDestroyed()) {
    162         return;
    163     }
    164 
    165     VALIDATE();
    166     SkASSERT(!this->isMapped());
    167 
    168     // TODO: Make this a function parameter.
    169     bool readOnly = (kXferGpuToCpu_GrBufferType == fIntendedType);
    170 
    171     // Handling dirty context is done in the bindBuffer call
    172     switch (this->glCaps().mapBufferType()) {
    173         case GrGLCaps::kNone_MapBufferType:
    174             break;
    175         case GrGLCaps::kMapBuffer_MapBufferType: {
    176             GrGLenum target = this->glGpu()->bindBuffer(fIntendedType, this);
    177             // Let driver know it can discard the old data
    178             if (this->glCaps().useBufferDataNullHint() || fGLSizeInBytes != this->sizeInBytes()) {
    179                 GL_CALL(BufferData(target, this->sizeInBytes(), nullptr, fUsage));
    180             }
    181             GL_CALL_RET(fMapPtr, MapBuffer(target, readOnly ? GR_GL_READ_ONLY : GR_GL_WRITE_ONLY));
    182             break;
    183         }
    184         case GrGLCaps::kMapBufferRange_MapBufferType: {
    185             GrGLenum target = this->glGpu()->bindBuffer(fIntendedType, this);
    186             // Make sure the GL buffer size agrees with fDesc before mapping.
    187             if (fGLSizeInBytes != this->sizeInBytes()) {
    188                 GL_CALL(BufferData(target, this->sizeInBytes(), nullptr, fUsage));
    189             }
    190             GrGLbitfield writeAccess = GR_GL_MAP_WRITE_BIT;
    191             if (kXferCpuToGpu_GrBufferType != fIntendedType) {
    192                 // TODO: Make this a function parameter.
    193                 writeAccess |= GR_GL_MAP_INVALIDATE_BUFFER_BIT;
    194             }
    195             GL_CALL_RET(fMapPtr, MapBufferRange(target, 0, this->sizeInBytes(),
    196                                                 readOnly ?  GR_GL_MAP_READ_BIT : writeAccess));
    197             break;
    198         }
    199         case GrGLCaps::kChromium_MapBufferType: {
    200             GrGLenum target = this->glGpu()->bindBuffer(fIntendedType, this);
    201             // Make sure the GL buffer size agrees with fDesc before mapping.
    202             if (fGLSizeInBytes != this->sizeInBytes()) {
    203                 GL_CALL(BufferData(target, this->sizeInBytes(), nullptr, fUsage));
    204             }
    205             GL_CALL_RET(fMapPtr, MapBufferSubData(target, 0, this->sizeInBytes(),
    206                                                   readOnly ?  GR_GL_READ_ONLY : GR_GL_WRITE_ONLY));
    207             break;
    208         }
    209     }
    210     fGLSizeInBytes = this->sizeInBytes();
    211     VALIDATE();
    212 }
    213 
    214 void GrGLBuffer::onUnmap() {
    215     SkASSERT(fBufferID);
    216     if (this->wasDestroyed()) {
    217         return;
    218     }
    219 
    220     VALIDATE();
    221     SkASSERT(this->isMapped());
    222     if (0 == fBufferID) {
    223         fMapPtr = nullptr;
    224         return;
    225     }
    226     // bind buffer handles the dirty context
    227     switch (this->glCaps().mapBufferType()) {
    228         case GrGLCaps::kNone_MapBufferType:
    229             SkDEBUGFAIL("Shouldn't get here.");
    230             return;
    231         case GrGLCaps::kMapBuffer_MapBufferType: // fall through
    232         case GrGLCaps::kMapBufferRange_MapBufferType: {
    233             GrGLenum target = this->glGpu()->bindBuffer(fIntendedType, this);
    234             GL_CALL(UnmapBuffer(target));
    235             break;
    236         }
    237         case GrGLCaps::kChromium_MapBufferType:
    238             this->glGpu()->bindBuffer(fIntendedType, this); // TODO: Is this needed?
    239             GL_CALL(UnmapBufferSubData(fMapPtr));
    240             break;
    241     }
    242     fMapPtr = nullptr;
    243 }
    244 
    245 bool GrGLBuffer::onUpdateData(const void* src, size_t srcSizeInBytes) {
    246     SkASSERT(fBufferID);
    247     if (this->wasDestroyed()) {
    248         return false;
    249     }
    250 
    251     SkASSERT(!this->isMapped());
    252     VALIDATE();
    253     if (srcSizeInBytes > this->sizeInBytes()) {
    254         return false;
    255     }
    256     SkASSERT(srcSizeInBytes <= this->sizeInBytes());
    257     // bindbuffer handles dirty context
    258     GrGLenum target = this->glGpu()->bindBuffer(fIntendedType, this);
    259 
    260     if (this->glCaps().useBufferDataNullHint()) {
    261         if (this->sizeInBytes() == srcSizeInBytes) {
    262             GL_CALL(BufferData(target, (GrGLsizeiptr) srcSizeInBytes, src, fUsage));
    263         } else {
    264             // Before we call glBufferSubData we give the driver a hint using
    265             // glBufferData with nullptr. This makes the old buffer contents
    266             // inaccessible to future draws. The GPU may still be processing
    267             // draws that reference the old contents. With this hint it can
    268             // assign a different allocation for the new contents to avoid
    269             // flushing the gpu past draws consuming the old contents.
    270             // TODO I think we actually want to try calling bufferData here
    271             GL_CALL(BufferData(target, this->sizeInBytes(), nullptr, fUsage));
    272             GL_CALL(BufferSubData(target, 0, (GrGLsizeiptr) srcSizeInBytes, src));
    273         }
    274         fGLSizeInBytes = this->sizeInBytes();
    275     } else {
    276         // Note that we're cheating on the size here. Currently no methods
    277         // allow a partial update that preserves contents of non-updated
    278         // portions of the buffer (map() does a glBufferData(..size, nullptr..))
    279         GL_CALL(BufferData(target, srcSizeInBytes, src, fUsage));
    280         fGLSizeInBytes = srcSizeInBytes;
    281     }
    282     VALIDATE();
    283     return true;
    284 }
    285 
    286 void GrGLBuffer::setMemoryBacking(SkTraceMemoryDump* traceMemoryDump,
    287                                        const SkString& dumpName) const {
    288     SkString buffer_id;
    289     buffer_id.appendU32(this->bufferID());
    290     traceMemoryDump->setMemoryBacking(dumpName.c_str(), "gl_buffer",
    291                                       buffer_id.c_str());
    292 }
    293 
    294 #ifdef SK_DEBUG
    295 
    296 void GrGLBuffer::validate() const {
    297     SkASSERT(0 != fBufferID || 0 == fGLSizeInBytes);
    298     SkASSERT(nullptr == fMapPtr || fGLSizeInBytes <= this->sizeInBytes());
    299 }
    300 
    301 #endif
    302