1/*
2 * Copyright (C) 2009 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
28#if ENABLE(WEBGL)
29
30#include "WebGLBuffer.h"
31
32#include "WebGLContextGroup.h"
33#include "WebGLRenderingContext.h"
34
35namespace WebCore {
36
37PassRefPtr<WebGLBuffer> WebGLBuffer::create(WebGLRenderingContext* ctx)
38{
39    return adoptRef(new WebGLBuffer(ctx));
40}
41
42WebGLBuffer::WebGLBuffer(WebGLRenderingContext* ctx)
43    : WebGLSharedObject(ctx)
44    , m_target(0)
45    , m_byteLength(0)
46    , m_nextAvailableCacheEntry(0)
47{
48    setObject(ctx->graphicsContext3D()->createBuffer());
49    clearCachedMaxIndices();
50}
51
52WebGLBuffer::~WebGLBuffer()
53{
54    deleteObject(0);
55}
56
57void WebGLBuffer::deleteObjectImpl(GraphicsContext3D* context3d, Platform3DObject object)
58{
59      context3d->deleteBuffer(object);
60}
61
62bool WebGLBuffer::associateBufferDataImpl(const void* data, GC3Dsizeiptr byteLength)
63{
64    if (byteLength < 0)
65        return false;
66
67    switch (m_target) {
68    case GraphicsContext3D::ELEMENT_ARRAY_BUFFER:
69        m_byteLength = byteLength;
70        clearCachedMaxIndices();
71        if (byteLength) {
72            m_elementArrayBuffer = ArrayBuffer::create(byteLength, 1);
73            if (!m_elementArrayBuffer) {
74                m_byteLength = 0;
75                return false;
76            }
77            if (data) {
78                // We must always clone the incoming data because client-side
79                // modifications without calling bufferData or bufferSubData
80                // must never be able to change the validation results.
81                memcpy(m_elementArrayBuffer->data(), data, byteLength);
82            }
83        } else
84            m_elementArrayBuffer = nullptr;
85        return true;
86    case GraphicsContext3D::ARRAY_BUFFER:
87        m_byteLength = byteLength;
88        return true;
89    default:
90        return false;
91    }
92}
93
94bool WebGLBuffer::associateBufferData(GC3Dsizeiptr size)
95{
96    return associateBufferDataImpl(0, size);
97}
98
99bool WebGLBuffer::associateBufferData(ArrayBuffer* array)
100{
101    if (!array)
102        return false;
103    return associateBufferDataImpl(array->data(), array->byteLength());
104}
105
106bool WebGLBuffer::associateBufferData(ArrayBufferView* array)
107{
108    if (!array)
109        return false;
110    return associateBufferDataImpl(array->baseAddress(), array->byteLength());
111}
112
113bool WebGLBuffer::associateBufferSubDataImpl(GC3Dintptr offset, const void* data, GC3Dsizeiptr byteLength)
114{
115    if (!data || offset < 0 || byteLength < 0)
116        return false;
117
118    if (byteLength) {
119        Checked<GC3Dintptr, RecordOverflow> checkedBufferOffset(offset);
120        Checked<GC3Dsizeiptr, RecordOverflow> checkedDataLength(byteLength);
121        Checked<GC3Dintptr, RecordOverflow> checkedBufferMax = checkedBufferOffset + checkedDataLength;
122        if (checkedBufferMax.hasOverflowed() || offset > m_byteLength || checkedBufferMax.unsafeGet() > m_byteLength)
123            return false;
124    }
125
126    switch (m_target) {
127    case GraphicsContext3D::ELEMENT_ARRAY_BUFFER:
128        clearCachedMaxIndices();
129        if (byteLength) {
130            if (!m_elementArrayBuffer)
131                return false;
132            memcpy(static_cast<unsigned char*>(m_elementArrayBuffer->data()) + offset, data, byteLength);
133        }
134        return true;
135    case GraphicsContext3D::ARRAY_BUFFER:
136        return true;
137    default:
138        return false;
139    }
140}
141
142bool WebGLBuffer::associateBufferSubData(GC3Dintptr offset, ArrayBuffer* array)
143{
144    if (!array)
145        return false;
146    return associateBufferSubDataImpl(offset, array->data(), array->byteLength());
147}
148
149bool WebGLBuffer::associateBufferSubData(GC3Dintptr offset, ArrayBufferView* array)
150{
151    if (!array)
152        return false;
153    return associateBufferSubDataImpl(offset, array->baseAddress(), array->byteLength());
154}
155
156void WebGLBuffer::disassociateBufferData()
157{
158    m_byteLength = 0;
159    clearCachedMaxIndices();
160}
161
162GC3Dsizeiptr WebGLBuffer::byteLength() const
163{
164    return m_byteLength;
165}
166
167int WebGLBuffer::getCachedMaxIndex(GC3Denum type)
168{
169    for (size_t i = 0; i < WTF_ARRAY_LENGTH(m_maxIndexCache); ++i)
170        if (m_maxIndexCache[i].type == type)
171            return m_maxIndexCache[i].maxIndex;
172    return -1;
173}
174
175void WebGLBuffer::setCachedMaxIndex(GC3Denum type, int value)
176{
177    size_t numEntries = WTF_ARRAY_LENGTH(m_maxIndexCache);
178    for (size_t i = 0; i < numEntries; ++i)
179        if (m_maxIndexCache[i].type == type) {
180            m_maxIndexCache[i].maxIndex = value;
181            return;
182        }
183    m_maxIndexCache[m_nextAvailableCacheEntry].type = type;
184    m_maxIndexCache[m_nextAvailableCacheEntry].maxIndex = value;
185    m_nextAvailableCacheEntry = (m_nextAvailableCacheEntry + 1) % numEntries;
186}
187
188void WebGLBuffer::setTarget(GC3Denum target)
189{
190    // In WebGL, a buffer is bound to one target in its lifetime
191    if (m_target)
192        return;
193    if (target == GraphicsContext3D::ARRAY_BUFFER || target == GraphicsContext3D::ELEMENT_ARRAY_BUFFER)
194        m_target = target;
195}
196
197void WebGLBuffer::clearCachedMaxIndices()
198{
199    memset(m_maxIndexCache, 0, sizeof(m_maxIndexCache));
200}
201
202}
203
204#endif // ENABLE(WEBGL)
205