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