1 | /* |
2 | * Copyright (C) 2009-2017 Apple Inc. All rights reserved. |
3 | * |
4 | * Redistribution and use in source and binary forms, with or without |
5 | * modification, are permitted provided that the following conditions |
6 | * are met: |
7 | * 1. Redistributions of source code must retain the above copyright |
8 | * notice, this list of conditions and the following disclaimer. |
9 | * 2. Redistributions in binary form must reproduce the above copyright |
10 | * notice, this list of conditions and the following disclaimer in the |
11 | * documentation and/or other materials provided with the distribution. |
12 | * |
13 | * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
14 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
16 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR |
17 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
18 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
19 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
20 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
21 | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
23 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
24 | */ |
25 | |
26 | #include "config.h" |
27 | #include "WebGLBuffer.h" |
28 | |
29 | #if ENABLE(WEBGL) |
30 | |
31 | #include "WebGLContextGroup.h" |
32 | #include "WebGLRenderingContextBase.h" |
33 | |
34 | namespace WebCore { |
35 | |
36 | Ref<WebGLBuffer> WebGLBuffer::create(WebGLRenderingContextBase& ctx) |
37 | { |
38 | return adoptRef(*new WebGLBuffer(ctx)); |
39 | } |
40 | |
41 | WebGLBuffer::WebGLBuffer(WebGLRenderingContextBase& ctx) |
42 | : WebGLSharedObject(ctx) |
43 | { |
44 | setObject(ctx.graphicsContext3D()->createBuffer()); |
45 | clearCachedMaxIndices(); |
46 | } |
47 | |
48 | WebGLBuffer::~WebGLBuffer() |
49 | { |
50 | deleteObject(0); |
51 | } |
52 | |
53 | void WebGLBuffer::deleteObjectImpl(GraphicsContext3D* context3d, Platform3DObject object) |
54 | { |
55 | context3d->deleteBuffer(object); |
56 | } |
57 | |
58 | bool WebGLBuffer::associateBufferDataImpl(const void* data, GC3Dsizeiptr byteLength) |
59 | { |
60 | if (byteLength < 0) |
61 | return false; |
62 | |
63 | switch (m_target) { |
64 | case GraphicsContext3D::ELEMENT_ARRAY_BUFFER: |
65 | if (byteLength > std::numeric_limits<unsigned>::max()) |
66 | return false; |
67 | m_byteLength = byteLength; |
68 | clearCachedMaxIndices(); |
69 | if (byteLength) { |
70 | m_elementArrayBuffer = ArrayBuffer::tryCreate(byteLength, 1); |
71 | if (!m_elementArrayBuffer) { |
72 | m_byteLength = 0; |
73 | return false; |
74 | } |
75 | if (data) { |
76 | // We must always clone the incoming data because client-side |
77 | // modifications without calling bufferData or bufferSubData |
78 | // must never be able to change the validation results. |
79 | memcpy(m_elementArrayBuffer->data(), data, byteLength); |
80 | } |
81 | } else |
82 | m_elementArrayBuffer = nullptr; |
83 | return true; |
84 | case GraphicsContext3D::ARRAY_BUFFER: |
85 | m_byteLength = byteLength; |
86 | return true; |
87 | default: |
88 | #if ENABLE(WEBGL2) |
89 | switch (m_target) { |
90 | case GraphicsContext3D::COPY_READ_BUFFER: |
91 | case GraphicsContext3D::COPY_WRITE_BUFFER: |
92 | case GraphicsContext3D::PIXEL_PACK_BUFFER: |
93 | case GraphicsContext3D::PIXEL_UNPACK_BUFFER: |
94 | case GraphicsContext3D::TRANSFORM_FEEDBACK_BUFFER: |
95 | case GraphicsContext3D::UNIFORM_BUFFER: |
96 | m_byteLength = byteLength; |
97 | return true; |
98 | } |
99 | #endif |
100 | return false; |
101 | } |
102 | } |
103 | |
104 | bool WebGLBuffer::associateBufferData(GC3Dsizeiptr size) |
105 | { |
106 | return associateBufferDataImpl(nullptr, size); |
107 | } |
108 | |
109 | bool WebGLBuffer::associateBufferData(ArrayBuffer* array) |
110 | { |
111 | if (!array) |
112 | return false; |
113 | return associateBufferDataImpl(array->data(), array->byteLength()); |
114 | } |
115 | |
116 | bool WebGLBuffer::associateBufferData(ArrayBufferView* array) |
117 | { |
118 | if (!array) |
119 | return false; |
120 | return associateBufferDataImpl(array->baseAddress(), array->byteLength()); |
121 | } |
122 | |
123 | bool WebGLBuffer::associateBufferSubDataImpl(GC3Dintptr offset, const void* data, GC3Dsizeiptr byteLength) |
124 | { |
125 | if (!data || offset < 0 || byteLength < 0) |
126 | return false; |
127 | |
128 | if (byteLength) { |
129 | Checked<GC3Dintptr, RecordOverflow> checkedBufferOffset(offset); |
130 | Checked<GC3Dsizeiptr, RecordOverflow> checkedDataLength(byteLength); |
131 | Checked<GC3Dintptr, RecordOverflow> checkedBufferMax = checkedBufferOffset + checkedDataLength; |
132 | if (checkedBufferMax.hasOverflowed() || offset > m_byteLength || checkedBufferMax.unsafeGet() > m_byteLength) |
133 | return false; |
134 | } |
135 | |
136 | switch (m_target) { |
137 | case GraphicsContext3D::ELEMENT_ARRAY_BUFFER: |
138 | clearCachedMaxIndices(); |
139 | if (byteLength) { |
140 | if (!m_elementArrayBuffer) |
141 | return false; |
142 | memcpy(static_cast<unsigned char*>(m_elementArrayBuffer->data()) + offset, data, byteLength); |
143 | } |
144 | return true; |
145 | case GraphicsContext3D::ARRAY_BUFFER: |
146 | return true; |
147 | default: |
148 | #if ENABLE(WEBGL2) |
149 | switch (m_target) { |
150 | case GraphicsContext3D::COPY_READ_BUFFER: |
151 | case GraphicsContext3D::COPY_WRITE_BUFFER: |
152 | case GraphicsContext3D::PIXEL_PACK_BUFFER: |
153 | case GraphicsContext3D::PIXEL_UNPACK_BUFFER: |
154 | case GraphicsContext3D::TRANSFORM_FEEDBACK_BUFFER: |
155 | case GraphicsContext3D::UNIFORM_BUFFER: |
156 | return true; |
157 | } |
158 | #endif |
159 | return false; |
160 | } |
161 | } |
162 | |
163 | bool WebGLBuffer::associateBufferSubData(GC3Dintptr offset, ArrayBuffer* array) |
164 | { |
165 | if (!array) |
166 | return false; |
167 | return associateBufferSubDataImpl(offset, array->data(), array->byteLength()); |
168 | } |
169 | |
170 | bool WebGLBuffer::associateBufferSubData(GC3Dintptr offset, ArrayBufferView* array) |
171 | { |
172 | if (!array) |
173 | return false; |
174 | return associateBufferSubDataImpl(offset, array->baseAddress(), array->byteLength()); |
175 | } |
176 | |
177 | bool WebGLBuffer::associateCopyBufferSubData(const WebGLBuffer& readBuffer, GC3Dintptr readOffset, GC3Dintptr writeOffset, GC3Dsizeiptr size) |
178 | { |
179 | if (readOffset < 0 || writeOffset < 0 || size < 0) |
180 | return false; |
181 | |
182 | if (size) { |
183 | Checked<GC3Dintptr, RecordOverflow> checkedReadBufferOffset(readOffset); |
184 | Checked<GC3Dsizeiptr, RecordOverflow> checkedDataLength(size); |
185 | Checked<GC3Dintptr, RecordOverflow> checkedReadBufferMax = checkedReadBufferOffset + checkedDataLength; |
186 | if (checkedReadBufferMax.hasOverflowed() || readOffset > readBuffer.byteLength() || checkedReadBufferMax.unsafeGet() > readBuffer.byteLength()) |
187 | return false; |
188 | |
189 | Checked<GC3Dintptr, RecordOverflow> checkedWriteBufferOffset(writeOffset); |
190 | Checked<GC3Dintptr, RecordOverflow> checkedWriteBufferMax = checkedWriteBufferOffset + checkedDataLength; |
191 | if (checkedWriteBufferMax.hasOverflowed() || writeOffset > m_byteLength || checkedWriteBufferMax.unsafeGet() > m_byteLength) |
192 | return false; |
193 | } |
194 | |
195 | switch (m_target) { |
196 | case GraphicsContext3D::ELEMENT_ARRAY_BUFFER: |
197 | clearCachedMaxIndices(); |
198 | if (size) { |
199 | if (!m_elementArrayBuffer) |
200 | return false; |
201 | memcpy(static_cast<unsigned char*>(m_elementArrayBuffer->data()) + writeOffset, static_cast<const unsigned char*>(readBuffer.elementArrayBuffer()->data()) + readOffset, size); |
202 | } |
203 | return true; |
204 | case GraphicsContext3D::ARRAY_BUFFER: |
205 | return true; |
206 | default: |
207 | #if ENABLE(WEBGL2) |
208 | switch (m_target) { |
209 | case GraphicsContext3D::COPY_READ_BUFFER: |
210 | case GraphicsContext3D::COPY_WRITE_BUFFER: |
211 | case GraphicsContext3D::PIXEL_PACK_BUFFER: |
212 | case GraphicsContext3D::PIXEL_UNPACK_BUFFER: |
213 | case GraphicsContext3D::TRANSFORM_FEEDBACK_BUFFER: |
214 | case GraphicsContext3D::UNIFORM_BUFFER: |
215 | return true; |
216 | } |
217 | #endif |
218 | return false; |
219 | } |
220 | } |
221 | |
222 | void WebGLBuffer::disassociateBufferData() |
223 | { |
224 | m_byteLength = 0; |
225 | clearCachedMaxIndices(); |
226 | } |
227 | |
228 | GC3Dsizeiptr WebGLBuffer::byteLength() const |
229 | { |
230 | return m_byteLength; |
231 | } |
232 | |
233 | Optional<unsigned> WebGLBuffer::getCachedMaxIndex(GC3Denum type) |
234 | { |
235 | for (auto& cache : m_maxIndexCache) { |
236 | if (cache.type == type) |
237 | return cache.maxIndex; |
238 | } |
239 | return WTF::nullopt; |
240 | } |
241 | |
242 | void WebGLBuffer::setCachedMaxIndex(GC3Denum type, unsigned value) |
243 | { |
244 | for (auto& cache : m_maxIndexCache) { |
245 | if (cache.type == type) { |
246 | cache.maxIndex = value; |
247 | return; |
248 | } |
249 | } |
250 | m_maxIndexCache[m_nextAvailableCacheEntry].type = type; |
251 | m_maxIndexCache[m_nextAvailableCacheEntry].maxIndex = value; |
252 | m_nextAvailableCacheEntry = (m_nextAvailableCacheEntry + 1) % WTF_ARRAY_LENGTH(m_maxIndexCache); |
253 | } |
254 | |
255 | void WebGLBuffer::setTarget(GC3Denum target, bool forWebGL2) |
256 | { |
257 | // In WebGL, a buffer is bound to one target in its lifetime |
258 | if (m_target) |
259 | return; |
260 | if (target == GraphicsContext3D::ARRAY_BUFFER || target == GraphicsContext3D::ELEMENT_ARRAY_BUFFER) |
261 | m_target = target; |
262 | else if (forWebGL2) { |
263 | #if ENABLE(WEBGL2) |
264 | switch (target) { |
265 | case GraphicsContext3D::COPY_READ_BUFFER: |
266 | case GraphicsContext3D::COPY_WRITE_BUFFER: |
267 | case GraphicsContext3D::PIXEL_PACK_BUFFER: |
268 | case GraphicsContext3D::PIXEL_UNPACK_BUFFER: |
269 | case GraphicsContext3D::TRANSFORM_FEEDBACK_BUFFER: |
270 | case GraphicsContext3D::UNIFORM_BUFFER: |
271 | m_target = target; |
272 | } |
273 | #endif |
274 | } |
275 | } |
276 | |
277 | void WebGLBuffer::clearCachedMaxIndices() |
278 | { |
279 | memset(m_maxIndexCache, 0, sizeof(m_maxIndexCache)); |
280 | } |
281 | |
282 | } |
283 | |
284 | #endif // ENABLE(WEBGL) |
285 | |