Add WebGLContextGroup as step toward sharing WebGL resources
[WebKit-https.git] / Source / WebCore / html / canvas / WebGLTexture.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(WEBGL)
29
30 #include "WebGLTexture.h"
31
32 #include "WebGLContextGroup.h"
33 #include "WebGLFramebuffer.h"
34 #include "WebGLRenderingContext.h"
35
36 namespace WebCore {
37
38 PassRefPtr<WebGLTexture> WebGLTexture::create(WebGLRenderingContext* ctx)
39 {
40     return adoptRef(new WebGLTexture(ctx));
41 }
42
43 WebGLTexture::WebGLTexture(WebGLRenderingContext* ctx)
44     : WebGLSharedObject(ctx)
45     , m_target(0)
46     , m_minFilter(GraphicsContext3D::NEAREST_MIPMAP_LINEAR)
47     , m_magFilter(GraphicsContext3D::LINEAR)
48     , m_wrapS(GraphicsContext3D::REPEAT)
49     , m_wrapT(GraphicsContext3D::REPEAT)
50     , m_isNPOT(false)
51     , m_isComplete(false)
52     , m_needToUseBlackTexture(false)
53 {
54     setObject(ctx->graphicsContext3D()->createTexture());
55 }
56
57 WebGLTexture::~WebGLTexture()
58 {
59     deleteObject(0);
60 }
61
62 void WebGLTexture::setTarget(GC3Denum target, GC3Dint maxLevel)
63 {
64     if (!object())
65         return;
66     // Target is finalized the first time bindTexture() is called.
67     if (m_target)
68         return;
69     switch (target) {
70     case GraphicsContext3D::TEXTURE_2D:
71         m_target = target;
72         m_info.resize(1);
73         m_info[0].resize(maxLevel);
74         break;
75     case GraphicsContext3D::TEXTURE_CUBE_MAP:
76         m_target = target;
77         m_info.resize(6);
78         for (int ii = 0; ii < 6; ++ii)
79             m_info[ii].resize(maxLevel);
80         break;
81     }
82 }
83
84 void WebGLTexture::setParameteri(GC3Denum pname, GC3Dint param)
85 {
86     if (!object() || !m_target)
87         return;
88     switch (pname) {
89     case GraphicsContext3D::TEXTURE_MIN_FILTER:
90         switch (param) {
91         case GraphicsContext3D::NEAREST:
92         case GraphicsContext3D::LINEAR:
93         case GraphicsContext3D::NEAREST_MIPMAP_NEAREST:
94         case GraphicsContext3D::LINEAR_MIPMAP_NEAREST:
95         case GraphicsContext3D::NEAREST_MIPMAP_LINEAR:
96         case GraphicsContext3D::LINEAR_MIPMAP_LINEAR:
97             m_minFilter = param;
98             break;
99         }
100         break;
101     case GraphicsContext3D::TEXTURE_MAG_FILTER:
102         switch (param) {
103         case GraphicsContext3D::NEAREST:
104         case GraphicsContext3D::LINEAR:
105             m_magFilter = param;
106             break;
107         }
108         break;
109     case GraphicsContext3D::TEXTURE_WRAP_S:
110         switch (param) {
111         case GraphicsContext3D::CLAMP_TO_EDGE:
112         case GraphicsContext3D::MIRRORED_REPEAT:
113         case GraphicsContext3D::REPEAT:
114             m_wrapS = param;
115             break;
116         }
117         break;
118     case GraphicsContext3D::TEXTURE_WRAP_T:
119         switch (param) {
120         case GraphicsContext3D::CLAMP_TO_EDGE:
121         case GraphicsContext3D::MIRRORED_REPEAT:
122         case GraphicsContext3D::REPEAT:
123             m_wrapT = param;
124             break;
125         }
126         break;
127     default:
128         return;
129     }
130     update();
131 }
132
133 void WebGLTexture::setParameterf(GC3Denum pname, GC3Dfloat param)
134 {
135     if (!object() || !m_target)
136         return;
137     GC3Dint iparam = static_cast<GC3Dint>(param);
138     setParameteri(pname, iparam);
139 }
140
141 void WebGLTexture::setLevelInfo(GC3Denum target, GC3Dint level, GC3Denum internalFormat, GC3Dsizei width, GC3Dsizei height, GC3Denum type)
142 {
143     if (!object() || !m_target)
144         return;
145     // We assume level, internalFormat, width, height, and type have all been
146     // validated already.
147     int index = mapTargetToIndex(target);
148     if (index < 0)
149         return;
150     m_info[index][level].setInfo(internalFormat, width, height, type);
151     update();
152 }
153
154 void WebGLTexture::generateMipmapLevelInfo()
155 {
156     if (!object() || !m_target)
157         return;
158     if (!canGenerateMipmaps())
159         return;
160     if (!m_isComplete) {
161         for (size_t ii = 0; ii < m_info.size(); ++ii) {
162             const LevelInfo& info0 = m_info[ii][0];
163             GC3Dsizei width = info0.width;
164             GC3Dsizei height = info0.height;
165             GC3Dint levelCount = computeLevelCount(width, height);
166             for (GC3Dint level = 1; level < levelCount; ++level) {
167                 width = std::max(1, width >> 1);
168                 height = std::max(1, height >> 1);
169                 LevelInfo& info = m_info[ii][level];
170                 info.setInfo(info0.internalFormat, width, height, info0.type);
171             }
172         }
173         m_isComplete = true;
174     }
175     m_needToUseBlackTexture = false;
176 }
177
178 GC3Denum WebGLTexture::getInternalFormat(GC3Denum target, GC3Dint level) const
179 {
180     const LevelInfo* info = getLevelInfo(target, level);
181     if (!info)
182         return 0;
183     return info->internalFormat;
184 }
185
186 GC3Denum WebGLTexture::getType(GC3Denum target, GC3Dint level) const
187 {
188     const LevelInfo* info = getLevelInfo(target, level);
189     if (!info)
190         return 0;
191     return info->type;
192 }
193
194 GC3Dsizei WebGLTexture::getWidth(GC3Denum target, GC3Dint level) const
195 {
196     const LevelInfo* info = getLevelInfo(target, level);
197     if (!info)
198         return 0;
199     return info->width;
200 }
201
202 GC3Dsizei WebGLTexture::getHeight(GC3Denum target, GC3Dint level) const
203 {
204     const LevelInfo* info = getLevelInfo(target, level);
205     if (!info)
206         return 0;
207     return info->height;
208 }
209
210 bool WebGLTexture::isNPOT(GC3Dsizei width, GC3Dsizei height)
211 {
212     ASSERT(width >= 0 && height >= 0);
213     if (!width || !height)
214         return false;
215     if ((width & (width - 1)) || (height & (height - 1)))
216         return true;
217     return false;
218 }
219
220 bool WebGLTexture::isNPOT() const
221 {
222     if (!object())
223         return false;
224     return m_isNPOT;
225 }
226
227 bool WebGLTexture::needToUseBlackTexture() const
228 {
229     if (!object())
230         return false;
231     return m_needToUseBlackTexture;
232 }
233
234 void WebGLTexture::deleteObjectImpl(GraphicsContext3D* context3d, Platform3DObject object)
235 {
236     context3d->deleteTexture(object);
237 }
238
239 int WebGLTexture::mapTargetToIndex(GC3Denum target) const
240 {
241     if (m_target == GraphicsContext3D::TEXTURE_2D) {
242         if (target == GraphicsContext3D::TEXTURE_2D)
243             return 0;
244     } else if (m_target == GraphicsContext3D::TEXTURE_CUBE_MAP) {
245         switch (target) {
246         case GraphicsContext3D::TEXTURE_CUBE_MAP_POSITIVE_X:
247             return 0;
248         case GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_X:
249             return 1;
250         case GraphicsContext3D::TEXTURE_CUBE_MAP_POSITIVE_Y:
251             return 2;
252         case GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_Y:
253             return 3;
254         case GraphicsContext3D::TEXTURE_CUBE_MAP_POSITIVE_Z:
255             return 4;
256         case GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_Z:
257             return 5;
258         }
259     }
260     return -1;
261 }
262
263 bool WebGLTexture::canGenerateMipmaps()
264 {
265     if (isNPOT())
266         return false;
267     const LevelInfo& first = m_info[0][0];
268     for (size_t ii = 0; ii < m_info.size(); ++ii) {
269         const LevelInfo& info = m_info[ii][0];
270         if (!info.valid
271             || info.width != first.width || info.height != first.height
272             || info.internalFormat != first.internalFormat || info.type != first.type)
273             return false;
274     }
275     return true;
276 }
277
278 GC3Dint WebGLTexture::computeLevelCount(GC3Dsizei width, GC3Dsizei height)
279 {
280     // return 1 + log2Floor(std::max(width, height));
281     GC3Dsizei n = std::max(width, height);
282     if (n <= 0)
283         return 0;
284     GC3Dint log = 0;
285     GC3Dsizei value = n;
286     for (int ii = 4; ii >= 0; --ii) {
287         int shift = (1 << ii);
288         GC3Dsizei x = (value >> shift);
289         if (x) {
290             value = x;
291             log += shift;
292         }
293     }
294     ASSERT(value == 1);
295     return log + 1;
296 }
297
298 void WebGLTexture::update()
299 {
300     m_isNPOT = false;
301     for (size_t ii = 0; ii < m_info.size(); ++ii) {
302         if (isNPOT(m_info[ii][0].width, m_info[ii][0].height)) {
303             m_isNPOT = true;
304             break;
305         }
306     }
307     m_isComplete = true;
308     const LevelInfo& first = m_info[0][0];
309     GC3Dint levelCount = computeLevelCount(first.width, first.height);
310     if (levelCount < 1)
311         m_isComplete = false;
312     else {
313         for (size_t ii = 0; ii < m_info.size() && m_isComplete; ++ii) {
314             const LevelInfo& info0 = m_info[ii][0];
315             if (!info0.valid
316                 || info0.width != first.width || info0.height != first.height
317                 || info0.internalFormat != first.internalFormat || info0.type != first.type) {
318                 m_isComplete = false;
319                 break;
320             }
321             GC3Dsizei width = info0.width;
322             GC3Dsizei height = info0.height;
323             for (GC3Dint level = 1; level < levelCount; ++level) {
324                 width = std::max(1, width >> 1);
325                 height = std::max(1, height >> 1);
326                 const LevelInfo& info = m_info[ii][level];
327                 if (!info.valid
328                     || info.width != width || info.height != height
329                     || info.internalFormat != info0.internalFormat || info.type != info0.type) {
330                     m_isComplete = false;
331                     break;
332                 }
333
334             }
335         }
336     }
337
338     m_needToUseBlackTexture = false;
339     // NPOT
340     if (m_isNPOT && ((m_minFilter != GraphicsContext3D::NEAREST && m_minFilter != GraphicsContext3D::LINEAR)
341                      || m_wrapS != GraphicsContext3D::CLAMP_TO_EDGE || m_wrapT != GraphicsContext3D::CLAMP_TO_EDGE))
342         m_needToUseBlackTexture = true;
343     // Completeness
344     if (!m_isComplete && m_minFilter != GraphicsContext3D::NEAREST && m_minFilter != GraphicsContext3D::LINEAR)
345         m_needToUseBlackTexture = true;
346 }
347
348 const WebGLTexture::LevelInfo* WebGLTexture::getLevelInfo(GC3Denum target, GC3Dint level) const
349 {
350     if (!object() || !m_target)
351         return 0;
352     int targetIndex = mapTargetToIndex(target);
353     if (targetIndex < 0 || targetIndex >= static_cast<int>(m_info.size()))
354         return 0;
355     if (level < 0 || level >= static_cast<GC3Dint>(m_info[targetIndex].size()))
356         return 0;
357     return &(m_info[targetIndex][level]);
358 }
359
360 }
361
362 #endif // ENABLE(WEBGL)