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
34namespace WebCore {
35
36Ref<WebGLBuffer> WebGLBuffer::create(WebGLRenderingContextBase& ctx)
37{
38 return adoptRef(*new WebGLBuffer(ctx));
39}
40
41WebGLBuffer::WebGLBuffer(WebGLRenderingContextBase& ctx)
42 : WebGLSharedObject(ctx)
43{
44 setObject(ctx.graphicsContext3D()->createBuffer());
45 clearCachedMaxIndices();
46}
47
48WebGLBuffer::~WebGLBuffer()
49{
50 deleteObject(0);
51}
52
53void WebGLBuffer::deleteObjectImpl(GraphicsContext3D* context3d, Platform3DObject object)
54{
55 context3d->deleteBuffer(object);
56}
57
58bool 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
104bool WebGLBuffer::associateBufferData(GC3Dsizeiptr size)
105{
106 return associateBufferDataImpl(nullptr, size);
107}
108
109bool WebGLBuffer::associateBufferData(ArrayBuffer* array)
110{
111 if (!array)
112 return false;
113 return associateBufferDataImpl(array->data(), array->byteLength());
114}
115
116bool WebGLBuffer::associateBufferData(ArrayBufferView* array)
117{
118 if (!array)
119 return false;
120 return associateBufferDataImpl(array->baseAddress(), array->byteLength());
121}
122
123bool 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
163bool WebGLBuffer::associateBufferSubData(GC3Dintptr offset, ArrayBuffer* array)
164{
165 if (!array)
166 return false;
167 return associateBufferSubDataImpl(offset, array->data(), array->byteLength());
168}
169
170bool WebGLBuffer::associateBufferSubData(GC3Dintptr offset, ArrayBufferView* array)
171{
172 if (!array)
173 return false;
174 return associateBufferSubDataImpl(offset, array->baseAddress(), array->byteLength());
175}
176
177bool 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
222void WebGLBuffer::disassociateBufferData()
223{
224 m_byteLength = 0;
225 clearCachedMaxIndices();
226}
227
228GC3Dsizeiptr WebGLBuffer::byteLength() const
229{
230 return m_byteLength;
231}
232
233Optional<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
242void 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
255void 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
277void WebGLBuffer::clearCachedMaxIndices()
278{
279 memset(m_maxIndexCache, 0, sizeof(m_maxIndexCache));
280}
281
282}
283
284#endif // ENABLE(WEBGL)
285