bsalomon@google.com | e49ad45 | 2013-02-20 19:33:20 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2013 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 "GrGLBufferImpl.h" |
| 9 | #include "GrGpuGL.h" |
| 10 | |
| 11 | #define GL_CALL(GPU, X) GR_GL_CALL(GPU->glInterface(), X) |
| 12 | |
commit-bot@chromium.org | 515dcd3 | 2013-08-28 14:17:03 +0000 | [diff] [blame] | 13 | #ifdef SK_DEBUG |
bsalomon@google.com | ee3bc3b | 2013-02-21 14:33:46 +0000 | [diff] [blame] | 14 | #define VALIDATE() this->validate() |
| 15 | #else |
| 16 | #define VALIDATE() do {} while(false) |
| 17 | #endif |
| 18 | |
bsalomon@google.com | 306b2ce | 2013-03-13 15:31:11 +0000 | [diff] [blame] | 19 | // GL_STREAM_DRAW triggers an optimization in Chromium's GPU process where a client's vertex buffer |
| 20 | // objects are implemented as client-side-arrays on tile-deferred architectures. |
| 21 | #define DYNAMIC_USAGE_PARAM GR_GL_STREAM_DRAW |
| 22 | |
bsalomon@google.com | e49ad45 | 2013-02-20 19:33:20 +0000 | [diff] [blame] | 23 | GrGLBufferImpl::GrGLBufferImpl(GrGpuGL* gpu, const Desc& desc, GrGLenum bufferType) |
| 24 | : fDesc(desc) |
| 25 | , fBufferType(bufferType) |
| 26 | , fLockPtr(NULL) { |
bsalomon@google.com | ee3bc3b | 2013-02-21 14:33:46 +0000 | [diff] [blame] | 27 | if (0 == desc.fID) { |
| 28 | fCPUData = sk_malloc_flags(desc.fSizeInBytes, SK_MALLOC_THROW); |
| 29 | } else { |
| 30 | fCPUData = NULL; |
| 31 | } |
| 32 | VALIDATE(); |
bsalomon@google.com | e49ad45 | 2013-02-20 19:33:20 +0000 | [diff] [blame] | 33 | } |
| 34 | |
| 35 | void GrGLBufferImpl::release(GrGpuGL* gpu) { |
bsalomon@google.com | ee3bc3b | 2013-02-21 14:33:46 +0000 | [diff] [blame] | 36 | // make sure we've not been abandoned or already released |
| 37 | if (NULL != fCPUData) { |
| 38 | VALIDATE(); |
| 39 | sk_free(fCPUData); |
| 40 | fCPUData = NULL; |
| 41 | } else if (fDesc.fID && !fDesc.fIsWrapped) { |
| 42 | VALIDATE(); |
bsalomon@google.com | e49ad45 | 2013-02-20 19:33:20 +0000 | [diff] [blame] | 43 | GL_CALL(gpu, DeleteBuffers(1, &fDesc.fID)); |
| 44 | if (GR_GL_ARRAY_BUFFER == fBufferType) { |
| 45 | gpu->notifyVertexBufferDelete(fDesc.fID); |
| 46 | } else { |
tfarina@chromium.org | f6de475 | 2013-08-17 00:02:59 +0000 | [diff] [blame] | 47 | SkASSERT(GR_GL_ELEMENT_ARRAY_BUFFER == fBufferType); |
bsalomon@google.com | e49ad45 | 2013-02-20 19:33:20 +0000 | [diff] [blame] | 48 | gpu->notifyIndexBufferDelete(fDesc.fID); |
| 49 | } |
| 50 | fDesc.fID = 0; |
| 51 | } |
bsalomon@google.com | ee3bc3b | 2013-02-21 14:33:46 +0000 | [diff] [blame] | 52 | fLockPtr = NULL; |
bsalomon@google.com | e49ad45 | 2013-02-20 19:33:20 +0000 | [diff] [blame] | 53 | } |
| 54 | |
| 55 | void GrGLBufferImpl::abandon() { |
| 56 | fDesc.fID = 0; |
| 57 | fLockPtr = NULL; |
bsalomon@google.com | ee3bc3b | 2013-02-21 14:33:46 +0000 | [diff] [blame] | 58 | sk_free(fCPUData); |
| 59 | fCPUData = NULL; |
bsalomon@google.com | e49ad45 | 2013-02-20 19:33:20 +0000 | [diff] [blame] | 60 | } |
| 61 | |
| 62 | void GrGLBufferImpl::bind(GrGpuGL* gpu) const { |
bsalomon@google.com | ee3bc3b | 2013-02-21 14:33:46 +0000 | [diff] [blame] | 63 | VALIDATE(); |
bsalomon@google.com | e49ad45 | 2013-02-20 19:33:20 +0000 | [diff] [blame] | 64 | if (GR_GL_ARRAY_BUFFER == fBufferType) { |
bsalomon@google.com | 6918d48 | 2013-03-07 19:09:11 +0000 | [diff] [blame] | 65 | gpu->bindVertexBuffer(fDesc.fID); |
bsalomon@google.com | e49ad45 | 2013-02-20 19:33:20 +0000 | [diff] [blame] | 66 | } else { |
tfarina@chromium.org | f6de475 | 2013-08-17 00:02:59 +0000 | [diff] [blame] | 67 | SkASSERT(GR_GL_ELEMENT_ARRAY_BUFFER == fBufferType); |
bsalomon@google.com | 6918d48 | 2013-03-07 19:09:11 +0000 | [diff] [blame] | 68 | gpu->bindIndexBufferAndDefaultVertexArray(fDesc.fID); |
bsalomon@google.com | e49ad45 | 2013-02-20 19:33:20 +0000 | [diff] [blame] | 69 | } |
| 70 | } |
| 71 | |
| 72 | void* GrGLBufferImpl::lock(GrGpuGL* gpu) { |
bsalomon@google.com | ee3bc3b | 2013-02-21 14:33:46 +0000 | [diff] [blame] | 73 | VALIDATE(); |
tfarina@chromium.org | f6de475 | 2013-08-17 00:02:59 +0000 | [diff] [blame] | 74 | SkASSERT(!this->isLocked()); |
bsalomon@google.com | ee3bc3b | 2013-02-21 14:33:46 +0000 | [diff] [blame] | 75 | if (0 == fDesc.fID) { |
| 76 | fLockPtr = fCPUData; |
bsalomon@google.com | bcce892 | 2013-03-25 15:38:39 +0000 | [diff] [blame] | 77 | } else if (gpu->caps()->bufferLockSupport()) { |
bsalomon@google.com | e49ad45 | 2013-02-20 19:33:20 +0000 | [diff] [blame] | 78 | this->bind(gpu); |
| 79 | // Let driver know it can discard the old data |
| 80 | GL_CALL(gpu, BufferData(fBufferType, |
robertphillips@google.com | e9cd27d | 2013-10-16 17:48:11 +0000 | [diff] [blame] | 81 | (GrGLsizeiptr) fDesc.fSizeInBytes, |
bsalomon@google.com | e49ad45 | 2013-02-20 19:33:20 +0000 | [diff] [blame] | 82 | NULL, |
bsalomon@google.com | 306b2ce | 2013-03-13 15:31:11 +0000 | [diff] [blame] | 83 | fDesc.fDynamic ? DYNAMIC_USAGE_PARAM : GR_GL_STATIC_DRAW)); |
bsalomon@google.com | e49ad45 | 2013-02-20 19:33:20 +0000 | [diff] [blame] | 84 | GR_GL_CALL_RET(gpu->glInterface(), |
| 85 | fLockPtr, |
| 86 | MapBuffer(fBufferType, GR_GL_WRITE_ONLY)); |
bsalomon@google.com | e49ad45 | 2013-02-20 19:33:20 +0000 | [diff] [blame] | 87 | } |
bsalomon@google.com | ee3bc3b | 2013-02-21 14:33:46 +0000 | [diff] [blame] | 88 | return fLockPtr; |
bsalomon@google.com | e49ad45 | 2013-02-20 19:33:20 +0000 | [diff] [blame] | 89 | } |
| 90 | |
| 91 | void GrGLBufferImpl::unlock(GrGpuGL* gpu) { |
bsalomon@google.com | ee3bc3b | 2013-02-21 14:33:46 +0000 | [diff] [blame] | 92 | VALIDATE(); |
tfarina@chromium.org | f6de475 | 2013-08-17 00:02:59 +0000 | [diff] [blame] | 93 | SkASSERT(this->isLocked()); |
bsalomon@google.com | ee3bc3b | 2013-02-21 14:33:46 +0000 | [diff] [blame] | 94 | if (0 != fDesc.fID) { |
tfarina@chromium.org | f6de475 | 2013-08-17 00:02:59 +0000 | [diff] [blame] | 95 | SkASSERT(gpu->caps()->bufferLockSupport()); |
bsalomon@google.com | ee3bc3b | 2013-02-21 14:33:46 +0000 | [diff] [blame] | 96 | this->bind(gpu); |
| 97 | GL_CALL(gpu, UnmapBuffer(fBufferType)); |
| 98 | } |
bsalomon@google.com | e49ad45 | 2013-02-20 19:33:20 +0000 | [diff] [blame] | 99 | fLockPtr = NULL; |
| 100 | } |
| 101 | |
| 102 | bool GrGLBufferImpl::isLocked() const { |
bsalomon@google.com | ee3bc3b | 2013-02-21 14:33:46 +0000 | [diff] [blame] | 103 | VALIDATE(); |
bsalomon@google.com | e49ad45 | 2013-02-20 19:33:20 +0000 | [diff] [blame] | 104 | return NULL != fLockPtr; |
| 105 | } |
| 106 | |
| 107 | bool GrGLBufferImpl::updateData(GrGpuGL* gpu, const void* src, size_t srcSizeInBytes) { |
tfarina@chromium.org | f6de475 | 2013-08-17 00:02:59 +0000 | [diff] [blame] | 108 | SkASSERT(!this->isLocked()); |
bsalomon@google.com | ee3bc3b | 2013-02-21 14:33:46 +0000 | [diff] [blame] | 109 | VALIDATE(); |
bsalomon@google.com | e49ad45 | 2013-02-20 19:33:20 +0000 | [diff] [blame] | 110 | if (srcSizeInBytes > fDesc.fSizeInBytes) { |
| 111 | return false; |
| 112 | } |
| 113 | if (0 == fDesc.fID) { |
bsalomon@google.com | ee3bc3b | 2013-02-21 14:33:46 +0000 | [diff] [blame] | 114 | memcpy(fCPUData, src, srcSizeInBytes); |
| 115 | return true; |
bsalomon@google.com | e49ad45 | 2013-02-20 19:33:20 +0000 | [diff] [blame] | 116 | } |
| 117 | this->bind(gpu); |
bsalomon@google.com | 306b2ce | 2013-03-13 15:31:11 +0000 | [diff] [blame] | 118 | GrGLenum usage = fDesc.fDynamic ? DYNAMIC_USAGE_PARAM : GR_GL_STATIC_DRAW; |
bsalomon@google.com | e49ad45 | 2013-02-20 19:33:20 +0000 | [diff] [blame] | 119 | |
| 120 | #if GR_GL_USE_BUFFER_DATA_NULL_HINT |
| 121 | if (fDesc.fSizeInBytes == srcSizeInBytes) { |
robertphillips@google.com | e9cd27d | 2013-10-16 17:48:11 +0000 | [diff] [blame] | 122 | GL_CALL(gpu, BufferData(fBufferType, (GrGLsizeiptr) srcSizeInBytes, src, usage)); |
bsalomon@google.com | e49ad45 | 2013-02-20 19:33:20 +0000 | [diff] [blame] | 123 | } else { |
| 124 | // Before we call glBufferSubData we give the driver a hint using |
| 125 | // glBufferData with NULL. This makes the old buffer contents |
| 126 | // inaccessible to future draws. The GPU may still be processing |
| 127 | // draws that reference the old contents. With this hint it can |
| 128 | // assign a different allocation for the new contents to avoid |
| 129 | // flushing the gpu past draws consuming the old contents. |
robertphillips@google.com | e9cd27d | 2013-10-16 17:48:11 +0000 | [diff] [blame] | 130 | GL_CALL(gpu, BufferData(fBufferType, (GrGLsizeiptr) fDesc.fSizeInBytes, NULL, usage)); |
| 131 | GL_CALL(gpu, BufferSubData(fBufferType, 0, (GrGLsizeiptr) srcSizeInBytes, src)); |
bsalomon@google.com | e49ad45 | 2013-02-20 19:33:20 +0000 | [diff] [blame] | 132 | } |
| 133 | #else |
| 134 | // Note that we're cheating on the size here. Currently no methods |
| 135 | // allow a partial update that preserves contents of non-updated |
| 136 | // portions of the buffer (lock() does a glBufferData(..size, NULL..)) |
| 137 | bool doSubData = false; |
| 138 | #if GR_GL_MAC_BUFFER_OBJECT_PERFOMANCE_WORKAROUND |
| 139 | static int N = 0; |
| 140 | // 128 was chosen experimentally. At 256 a slight hitchiness was noticed |
| 141 | // when dragging a Chromium window around with a canvas tab backgrounded. |
| 142 | doSubData = 0 == (N % 128); |
| 143 | ++N; |
| 144 | #endif |
| 145 | if (doSubData) { |
| 146 | // The workaround is to do a glBufferData followed by glBufferSubData. |
| 147 | // Chromium's command buffer may turn a glBufferSubData where the size |
| 148 | // exactly matches the buffer size into a glBufferData. So we tack 1 |
| 149 | // extra byte onto the glBufferData. |
| 150 | GL_CALL(gpu, BufferData(fBufferType, srcSizeInBytes + 1, NULL, usage)); |
| 151 | GL_CALL(gpu, BufferSubData(fBufferType, 0, srcSizeInBytes, src)); |
| 152 | } else { |
| 153 | GL_CALL(gpu, BufferData(fBufferType, srcSizeInBytes, src, usage)); |
| 154 | } |
| 155 | #endif |
| 156 | return true; |
| 157 | } |
bsalomon@google.com | ee3bc3b | 2013-02-21 14:33:46 +0000 | [diff] [blame] | 158 | |
| 159 | void GrGLBufferImpl::validate() const { |
tfarina@chromium.org | f6de475 | 2013-08-17 00:02:59 +0000 | [diff] [blame] | 160 | SkASSERT(GR_GL_ARRAY_BUFFER == fBufferType || GR_GL_ELEMENT_ARRAY_BUFFER == fBufferType); |
bsalomon@google.com | 1b8014c | 2013-05-08 12:49:49 +0000 | [diff] [blame] | 161 | // The following assert isn't valid when the buffer has been abandoned: |
tfarina@chromium.org | f6de475 | 2013-08-17 00:02:59 +0000 | [diff] [blame] | 162 | // SkASSERT((0 == fDesc.fID) == (NULL != fCPUData)); |
| 163 | SkASSERT(0 != fDesc.fID || !fDesc.fIsWrapped); |
| 164 | SkASSERT(NULL == fCPUData || NULL == fLockPtr || fCPUData == fLockPtr); |
bsalomon@google.com | ee3bc3b | 2013-02-21 14:33:46 +0000 | [diff] [blame] | 165 | } |