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 | |
36 | namespace WebCore { |
37 | |
38 | Ref<WebGLTexture> WebGLTexture::create(WebGLRenderingContextBase& ctx) |
39 | { |
40 | return adoptRef(*new WebGLTexture(ctx)); |
41 | } |
42 | |
43 | WebGLTexture::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 | |
61 | WebGLTexture::~WebGLTexture() |
62 | { |
63 | deleteObject(0); |
64 | } |
65 | |
66 | void 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 | |
88 | void 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 | |
137 | void 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 | |
145 | void 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 | |
158 | void 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 | |
182 | GC3Denum 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 | |
190 | GC3Denum 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 | |
199 | GC3Dsizei 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 | |
207 | GC3Dsizei 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 | |
215 | bool 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 | |
223 | void 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 | |
232 | bool 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 | |
242 | bool WebGLTexture::isNPOT() const |
243 | { |
244 | if (!object()) |
245 | return false; |
246 | return m_isNPOT; |
247 | } |
248 | |
249 | bool 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 | |
264 | bool WebGLTexture::isCompressed() const |
265 | { |
266 | if (!object()) |
267 | return false; |
268 | return m_isCompressed; |
269 | } |
270 | |
271 | void WebGLTexture::setCompressed() |
272 | { |
273 | ASSERT(object()); |
274 | m_isCompressed = true; |
275 | } |
276 | |
277 | void WebGLTexture::deleteObjectImpl(GraphicsContext3D* context3d, Platform3DObject object) |
278 | { |
279 | context3d->deleteTexture(object); |
280 | } |
281 | |
282 | int 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 | |
306 | bool 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 | |
321 | GC3Dint 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 | |
341 | static 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 | |
356 | static 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 | |
371 | void 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 | |
457 | const 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 | |