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