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