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