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 "WebGLTexture.h"
31
32#include "WebGLContextGroup.h"
33#include "WebGLFramebuffer.h"
34#include "WebGLRenderingContextBase.h"
35
36namespace WebCore {
37
38Ref<WebGLTexture> WebGLTexture::create(WebGLRenderingContextBase& ctx)
39{
40 return adoptRef(*new WebGLTexture(ctx));
41}
42
43WebGLTexture::WebGLTexture(WebGLRenderingContextBase& 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 , m_isCompressed(false)
54 , m_isFloatType(false)
55 , m_isHalfFloatType(false)
56 , m_isForWebGL1(ctx.isWebGL1())
57{
58 setObject(ctx.graphicsContext3D()->createTexture());
59}
60
61WebGLTexture::~WebGLTexture()
62{
63 deleteObject(0);
64}
65
66void WebGLTexture::setTarget(GC3Denum target, GC3Dint maxLevel)
67{
68 if (!object())
69 return;
70 // Target is finalized the first time bindTexture() is called.
71 if (m_target)
72 return;
73 switch (target) {
74 case GraphicsContext3D::TEXTURE_2D:
75 m_target = target;
76 m_info.resize(1);
77 m_info[0].resize(maxLevel);
78 break;
79 case GraphicsContext3D::TEXTURE_CUBE_MAP:
80 m_target = target;
81 m_info.resize(6);
82 for (int ii = 0; ii < 6; ++ii)
83 m_info[ii].resize(maxLevel);
84 break;
85 }
86}
87
88void WebGLTexture::setParameteri(GC3Denum pname, GC3Dint param)
89{
90 if (!object() || !m_target)
91 return;
92 switch (pname) {
93 case GraphicsContext3D::TEXTURE_MIN_FILTER:
94 switch (param) {
95 case GraphicsContext3D::NEAREST:
96 case GraphicsContext3D::LINEAR:
97 case GraphicsContext3D::NEAREST_MIPMAP_NEAREST:
98 case GraphicsContext3D::LINEAR_MIPMAP_NEAREST:
99 case GraphicsContext3D::NEAREST_MIPMAP_LINEAR:
100 case GraphicsContext3D::LINEAR_MIPMAP_LINEAR:
101 m_minFilter = param;
102 break;
103 }
104 break;
105 case GraphicsContext3D::TEXTURE_MAG_FILTER:
106 switch (param) {
107 case GraphicsContext3D::NEAREST:
108 case GraphicsContext3D::LINEAR:
109 m_magFilter = param;
110 break;
111 }
112 break;
113 case GraphicsContext3D::TEXTURE_WRAP_S:
114 switch (param) {
115 case GraphicsContext3D::CLAMP_TO_EDGE:
116 case GraphicsContext3D::MIRRORED_REPEAT:
117 case GraphicsContext3D::REPEAT:
118 m_wrapS = param;
119 break;
120 }
121 break;
122 case GraphicsContext3D::TEXTURE_WRAP_T:
123 switch (param) {
124 case GraphicsContext3D::CLAMP_TO_EDGE:
125 case GraphicsContext3D::MIRRORED_REPEAT:
126 case GraphicsContext3D::REPEAT:
127 m_wrapT = param;
128 break;
129 }
130 break;
131 default:
132 return;
133 }
134 update();
135}
136
137void WebGLTexture::setParameterf(GC3Denum pname, GC3Dfloat param)
138{
139 if (!object() || !m_target)
140 return;
141 GC3Dint iparam = static_cast<GC3Dint>(param);
142 setParameteri(pname, iparam);
143}
144
145void WebGLTexture::setLevelInfo(GC3Denum target, GC3Dint level, GC3Denum internalFormat, GC3Dsizei width, GC3Dsizei height, GC3Denum type)
146{
147 if (!object() || !m_target)
148 return;
149 // We assume level, internalFormat, width, height, and type have all been
150 // validated already.
151 int index = mapTargetToIndex(target);
152 if (index < 0)
153 return;
154 m_info[index][level].setInfo(internalFormat, width, height, type);
155 update();
156}
157
158void WebGLTexture::generateMipmapLevelInfo()
159{
160 if (!object() || !m_target)
161 return;
162 if (!canGenerateMipmaps())
163 return;
164 if (!m_isComplete) {
165 for (size_t ii = 0; ii < m_info.size(); ++ii) {
166 const LevelInfo& info0 = m_info[ii][0];
167 GC3Dsizei width = info0.width;
168 GC3Dsizei height = info0.height;
169 GC3Dint levelCount = computeLevelCount(width, height);
170 for (GC3Dint level = 1; level < levelCount; ++level) {
171 width = std::max(1, width >> 1);
172 height = std::max(1, height >> 1);
173 LevelInfo& info = m_info[ii][level];
174 info.setInfo(info0.internalFormat, width, height, info0.type);
175 }
176 }
177 m_isComplete = true;
178 }
179 m_needToUseBlackTexture = false;
180}
181
182GC3Denum WebGLTexture::getInternalFormat(GC3Denum target, GC3Dint level) const
183{
184 const LevelInfo* info = getLevelInfo(target, level);
185 if (!info)
186 return 0;
187 return info->internalFormat;
188}
189
190GC3Denum WebGLTexture::getType(GC3Denum target, GC3Dint level) const
191{
192 ASSERT(m_isForWebGL1);
193 const LevelInfo* info = getLevelInfo(target, level);
194 if (!info)
195 return 0;
196 return info->type;
197}
198
199GC3Dsizei WebGLTexture::getWidth(GC3Denum target, GC3Dint level) const
200{
201 const LevelInfo* info = getLevelInfo(target, level);
202 if (!info)
203 return 0;
204 return info->width;
205}
206
207GC3Dsizei WebGLTexture::getHeight(GC3Denum target, GC3Dint level) const
208{
209 const LevelInfo* info = getLevelInfo(target, level);
210 if (!info)
211 return 0;
212 return info->height;
213}
214
215bool WebGLTexture::isValid(GC3Denum target, GC3Dint level) const
216{
217 const LevelInfo* info = getLevelInfo(target, level);
218 if (!info)
219 return 0;
220 return info->valid;
221}
222
223void WebGLTexture::markInvalid(GC3Denum target, GC3Dint level)
224{
225 int index = mapTargetToIndex(target);
226 if (index < 0)
227 return;
228 m_info[index][level].valid = false;
229 update();
230}
231
232bool WebGLTexture::isNPOT(GC3Dsizei width, GC3Dsizei height)
233{
234 ASSERT(width >= 0 && height >= 0);
235 if (!width || !height)
236 return false;
237 if ((width & (width - 1)) || (height & (height - 1)))
238 return true;
239 return false;
240}
241
242bool WebGLTexture::isNPOT() const
243{
244 if (!object())
245 return false;
246 return m_isNPOT;
247}
248
249bool WebGLTexture::needToUseBlackTexture(TextureExtensionFlag extensions) const
250{
251 if (!object())
252 return false;
253 if (m_needToUseBlackTexture)
254 return true;
255 if (m_magFilter == GraphicsContext3D::NEAREST && (m_minFilter == GraphicsContext3D::NEAREST || m_minFilter == GraphicsContext3D::NEAREST_MIPMAP_NEAREST))
256 return false;
257 if (m_isForWebGL1 && m_isHalfFloatType && !(extensions & TextureExtensionHalfFloatLinearEnabled))
258 return true;
259 if (m_isFloatType && !(extensions & TextureExtensionFloatLinearEnabled))
260 return true;
261 return false;
262}
263
264bool WebGLTexture::isCompressed() const
265{
266 if (!object())
267 return false;
268 return m_isCompressed;
269}
270
271void WebGLTexture::setCompressed()
272{
273 ASSERT(object());
274 m_isCompressed = true;
275}
276
277void WebGLTexture::deleteObjectImpl(GraphicsContext3D* context3d, Platform3DObject object)
278{
279 context3d->deleteTexture(object);
280}
281
282int WebGLTexture::mapTargetToIndex(GC3Denum target) const
283{
284 if (m_target == GraphicsContext3D::TEXTURE_2D) {
285 if (target == GraphicsContext3D::TEXTURE_2D)
286 return 0;
287 } else if (m_target == GraphicsContext3D::TEXTURE_CUBE_MAP) {
288 switch (target) {
289 case GraphicsContext3D::TEXTURE_CUBE_MAP_POSITIVE_X:
290 return 0;
291 case GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_X:
292 return 1;
293 case GraphicsContext3D::TEXTURE_CUBE_MAP_POSITIVE_Y:
294 return 2;
295 case GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_Y:
296 return 3;
297 case GraphicsContext3D::TEXTURE_CUBE_MAP_POSITIVE_Z:
298 return 4;
299 case GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_Z:
300 return 5;
301 }
302 }
303 return -1;
304}
305
306bool WebGLTexture::canGenerateMipmaps()
307{
308 if (isNPOT())
309 return false;
310 const LevelInfo& first = m_info[0][0];
311 for (size_t ii = 0; ii < m_info.size(); ++ii) {
312 const LevelInfo& info = m_info[ii][0];
313 if (!info.valid
314 || info.width != first.width || info.height != first.height
315 || info.internalFormat != first.internalFormat || (m_isForWebGL1 && info.type != first.type))
316 return false;
317 }
318 return true;
319}
320
321GC3Dint WebGLTexture::computeLevelCount(GC3Dsizei width, GC3Dsizei height)
322{
323 // return 1 + log2Floor(std::max(width, height));
324 GC3Dsizei n = std::max(width, height);
325 if (n <= 0)
326 return 0;
327 GC3Dint log = 0;
328 GC3Dsizei value = n;
329 for (int ii = 4; ii >= 0; --ii) {
330 int shift = (1 << ii);
331 GC3Dsizei x = (value >> shift);
332 if (x) {
333 value = x;
334 log += shift;
335 }
336 }
337 ASSERT(value == 1);
338 return log + 1;
339}
340
341static bool internalFormatIsFloatType(GC3Denum internalFormat)
342{
343 switch (internalFormat) {
344 case GraphicsContext3D::R32F:
345 case GraphicsContext3D::RG32F:
346 case GraphicsContext3D::RGB32F:
347 case GraphicsContext3D::RGBA32F:
348 case GraphicsContext3D::DEPTH_COMPONENT32F:
349 case GraphicsContext3D::DEPTH32F_STENCIL8:
350 return true;
351 default:
352 return false;
353 }
354}
355
356static bool internalFormatIsHalfFloatType(GC3Denum internalFormat)
357{
358 switch (internalFormat) {
359 case GraphicsContext3D::R16F:
360 case GraphicsContext3D::RG16F:
361 case GraphicsContext3D::R11F_G11F_B10F:
362 case GraphicsContext3D::RGB9_E5:
363 case GraphicsContext3D::RGB16F:
364 case GraphicsContext3D::RGBA16F:
365 return true;
366 default:
367 return false;
368 }
369}
370
371void WebGLTexture::update()
372{
373 m_isNPOT = false;
374 for (size_t ii = 0; ii < m_info.size(); ++ii) {
375 if (isNPOT(m_info[ii][0].width, m_info[ii][0].height)) {
376 m_isNPOT = true;
377 break;
378 }
379 }
380 m_isComplete = true;
381 const LevelInfo& first = m_info[0][0];
382 GC3Dint levelCount = computeLevelCount(first.width, first.height);
383 if (levelCount < 1)
384 m_isComplete = false;
385 else {
386 for (size_t ii = 0; ii < m_info.size() && m_isComplete; ++ii) {
387 const LevelInfo& info0 = m_info[ii][0];
388 if (!info0.valid
389 || info0.width != first.width || info0.height != first.height
390 || info0.internalFormat != first.internalFormat || (m_isForWebGL1 && info0.type != first.type)) {
391 m_isComplete = false;
392 break;
393 }
394 GC3Dsizei width = info0.width;
395 GC3Dsizei height = info0.height;
396 for (GC3Dint level = 1; level < levelCount; ++level) {
397 width = std::max(1, width >> 1);
398 height = std::max(1, height >> 1);
399 const LevelInfo& info = m_info[ii][level];
400 if (!info.valid
401 || info.width != width || info.height != height
402 || info.internalFormat != info0.internalFormat || (m_isForWebGL1 && info.type != info0.type)) {
403 m_isComplete = false;
404 break;
405 }
406
407 }
408 }
409 }
410
411 m_isFloatType = false;
412 if (m_isForWebGL1) {
413 if (m_isComplete) {
414 if (m_isForWebGL1)
415 m_isFloatType = m_info[0][0].type == GraphicsContext3D::FLOAT;
416 else
417 m_isFloatType = internalFormatIsFloatType(m_info[0][0].internalFormat);
418 } else {
419 for (size_t ii = 0; ii < m_info.size(); ++ii) {
420 if ((m_isForWebGL1 && m_info[ii][0].type == GraphicsContext3D::FLOAT)
421 || (!m_isForWebGL1 && internalFormatIsFloatType(m_info[ii][0].internalFormat))) {
422 m_isFloatType = true;
423 break;
424 }
425 }
426 }
427 }
428
429 m_isHalfFloatType = false;
430 if (m_isForWebGL1) {
431 if (m_isComplete) {
432 if (m_isForWebGL1)
433 m_isHalfFloatType = internalFormatIsHalfFloatType(m_info[0][0].internalFormat);
434 else
435 m_isHalfFloatType = m_info[0][0].type == GraphicsContext3D::HALF_FLOAT_OES;
436 } else {
437 for (size_t ii = 0; ii < m_info.size(); ++ii) {
438 if ((m_isForWebGL1 && m_info[ii][0].type == GraphicsContext3D::HALF_FLOAT_OES)
439 || (!m_isForWebGL1 && internalFormatIsHalfFloatType(m_info[ii][0].internalFormat))) {
440 m_isHalfFloatType = true;
441 break;
442 }
443 }
444 }
445 }
446
447 m_needToUseBlackTexture = false;
448 // NPOT
449 if (m_isNPOT && ((m_minFilter != GraphicsContext3D::NEAREST && m_minFilter != GraphicsContext3D::LINEAR)
450 || m_wrapS != GraphicsContext3D::CLAMP_TO_EDGE || m_wrapT != GraphicsContext3D::CLAMP_TO_EDGE))
451 m_needToUseBlackTexture = true;
452 // Completeness
453 if (!m_isComplete && m_minFilter != GraphicsContext3D::NEAREST && m_minFilter != GraphicsContext3D::LINEAR)
454 m_needToUseBlackTexture = true;
455}
456
457const WebGLTexture::LevelInfo* WebGLTexture::getLevelInfo(GC3Denum target, GC3Dint level) const
458{
459 if (!object() || !m_target)
460 return 0;
461 int targetIndex = mapTargetToIndex(target);
462 if (targetIndex < 0 || targetIndex >= static_cast<int>(m_info.size()))
463 return 0;
464 if (level < 0 || level >= static_cast<GC3Dint>(m_info[targetIndex].size()))
465 return 0;
466 return &(m_info[targetIndex][level]);
467}
468
469}
470
471#endif // ENABLE(WEBGL)
472