Line data Source code
1 : /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /* This Source Code Form is subject to the terms of the Mozilla Public
3 : * License, v. 2.0. If a copy of the MPL was not distributed with this
4 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 :
6 : #include "WebGLTexture.h"
7 :
8 : #include <algorithm>
9 : #include "GLContext.h"
10 : #include "mozilla/dom/WebGLRenderingContextBinding.h"
11 : #include "mozilla/gfx/Logging.h"
12 : #include "mozilla/MathAlgorithms.h"
13 : #include "mozilla/Scoped.h"
14 : #include "mozilla/Unused.h"
15 : #include "ScopedGLHelpers.h"
16 : #include "WebGLContext.h"
17 : #include "WebGLContextUtils.h"
18 : #include "WebGLFramebuffer.h"
19 : #include "WebGLSampler.h"
20 : #include "WebGLTexelConversions.h"
21 :
22 : namespace mozilla {
23 :
24 3 : /*static*/ const WebGLTexture::ImageInfo WebGLTexture::ImageInfo::kUndefined;
25 :
26 : ////////////////////////////////////////
27 :
28 : template <typename T>
29 : static inline T&
30 0 : Mutable(const T& x)
31 : {
32 0 : return const_cast<T&>(x);
33 : }
34 :
35 : void
36 0 : WebGLTexture::ImageInfo::Clear(const char* funcName)
37 : {
38 0 : if (!IsDefined())
39 0 : return;
40 :
41 0 : OnRespecify(funcName);
42 :
43 0 : Mutable(mFormat) = LOCAL_GL_NONE;
44 0 : Mutable(mWidth) = 0;
45 0 : Mutable(mHeight) = 0;
46 0 : Mutable(mDepth) = 0;
47 :
48 0 : MOZ_ASSERT(!IsDefined());
49 : }
50 :
51 : void
52 0 : WebGLTexture::ImageInfo::Set(const char* funcName, const ImageInfo& a)
53 : {
54 0 : MOZ_ASSERT(a.IsDefined());
55 :
56 0 : Mutable(mFormat) = a.mFormat;
57 0 : Mutable(mWidth) = a.mWidth;
58 0 : Mutable(mHeight) = a.mHeight;
59 0 : Mutable(mDepth) = a.mDepth;
60 :
61 0 : mIsDataInitialized = a.mIsDataInitialized;
62 :
63 : // But *don't* transfer mAttachPoints!
64 0 : MOZ_ASSERT(a.mAttachPoints.empty());
65 0 : OnRespecify(funcName);
66 0 : }
67 :
68 : bool
69 0 : WebGLTexture::ImageInfo::IsPowerOfTwo() const
70 : {
71 0 : return mozilla::IsPowerOfTwo(mWidth) &&
72 0 : mozilla::IsPowerOfTwo(mHeight) &&
73 0 : mozilla::IsPowerOfTwo(mDepth);
74 : }
75 :
76 : void
77 0 : WebGLTexture::ImageInfo::AddAttachPoint(WebGLFBAttachPoint* attachPoint)
78 : {
79 0 : const auto pair = mAttachPoints.insert(attachPoint);
80 0 : DebugOnly<bool> didInsert = pair.second;
81 0 : MOZ_ASSERT(didInsert);
82 0 : }
83 :
84 : void
85 0 : WebGLTexture::ImageInfo::RemoveAttachPoint(WebGLFBAttachPoint* attachPoint)
86 : {
87 0 : DebugOnly<size_t> numElemsErased = mAttachPoints.erase(attachPoint);
88 0 : MOZ_ASSERT_IF(IsDefined(), numElemsErased == 1);
89 0 : }
90 :
91 : void
92 0 : WebGLTexture::ImageInfo::OnRespecify(const char* funcName) const
93 : {
94 0 : for (auto cur : mAttachPoints) {
95 0 : cur->OnBackingStoreRespecified(funcName);
96 : }
97 0 : }
98 :
99 : size_t
100 0 : WebGLTexture::ImageInfo::MemoryUsage() const
101 : {
102 0 : if (!IsDefined())
103 0 : return 0;
104 :
105 0 : const auto bytesPerTexel = mFormat->format->estimatedBytesPerPixel;
106 0 : return size_t(mWidth) * size_t(mHeight) * size_t(mDepth) * bytesPerTexel;
107 : }
108 :
109 : void
110 0 : WebGLTexture::ImageInfo::SetIsDataInitialized(bool isDataInitialized, WebGLTexture* tex)
111 : {
112 0 : MOZ_ASSERT(tex);
113 0 : MOZ_ASSERT(this >= &tex->mImageInfoArr[0]);
114 0 : MOZ_ASSERT(this < &tex->mImageInfoArr[kMaxLevelCount * kMaxFaceCount]);
115 :
116 0 : mIsDataInitialized = isDataInitialized;
117 0 : tex->InvalidateResolveCache();
118 0 : }
119 :
120 : ////////////////////////////////////////
121 :
122 : JSObject*
123 0 : WebGLTexture::WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) {
124 0 : return dom::WebGLTextureBinding::Wrap(cx, this, givenProto);
125 : }
126 :
127 0 : WebGLTexture::WebGLTexture(WebGLContext* webgl, GLuint tex)
128 : : WebGLRefCountedObject(webgl)
129 : , mGLName(tex)
130 : , mTarget(LOCAL_GL_NONE)
131 : , mFaceCount(0)
132 : , mMinFilter(LOCAL_GL_NEAREST_MIPMAP_LINEAR)
133 : , mMagFilter(LOCAL_GL_LINEAR)
134 : , mWrapS(LOCAL_GL_REPEAT)
135 : , mWrapT(LOCAL_GL_REPEAT)
136 : , mImmutable(false)
137 : , mImmutableLevelCount(0)
138 : , mBaseMipmapLevel(0)
139 : , mMaxMipmapLevel(1000)
140 : , mTexCompareMode(LOCAL_GL_NONE)
141 : , mIsResolved(false)
142 0 : , mResolved_Swizzle(nullptr)
143 : {
144 0 : mContext->mTextures.insertBack(this);
145 0 : }
146 :
147 : void
148 0 : WebGLTexture::Delete()
149 : {
150 0 : const char funcName[] = "WebGLTexture::Delete";
151 0 : for (auto& cur : mImageInfoArr) {
152 0 : cur.Clear(funcName);
153 : }
154 :
155 0 : mContext->MakeContextCurrent();
156 0 : mContext->gl->fDeleteTextures(1, &mGLName);
157 :
158 0 : LinkedListElement<WebGLTexture>::removeFrom(mContext->mTextures);
159 0 : }
160 :
161 : size_t
162 0 : WebGLTexture::MemoryUsage() const
163 : {
164 0 : if (IsDeleted())
165 0 : return 0;
166 :
167 0 : size_t accum = 0;
168 0 : for (const auto& cur : mImageInfoArr) {
169 0 : accum += cur.MemoryUsage();
170 : }
171 0 : return accum;
172 : }
173 :
174 : void
175 0 : WebGLTexture::SetImageInfo(const char* funcName, ImageInfo* target,
176 : const ImageInfo& newInfo)
177 : {
178 0 : target->Set(funcName, newInfo);
179 :
180 0 : InvalidateResolveCache();
181 0 : }
182 :
183 : void
184 0 : WebGLTexture::SetImageInfosAtLevel(const char* funcName, uint32_t level,
185 : const ImageInfo& newInfo)
186 : {
187 0 : for (uint8_t i = 0; i < mFaceCount; i++) {
188 0 : ImageInfoAtFace(i, level).Set(funcName, newInfo);
189 : }
190 :
191 0 : InvalidateResolveCache();
192 0 : }
193 :
194 : bool
195 0 : WebGLTexture::IsMipmapComplete(const char* funcName, uint32_t texUnit,
196 : bool* const out_initFailed)
197 : {
198 0 : *out_initFailed = false;
199 0 : MOZ_ASSERT(DoesMinFilterRequireMipmap());
200 : // GLES 3.0.4, p161
201 :
202 : uint32_t maxLevel;
203 0 : if (!MaxEffectiveMipmapLevel(texUnit, &maxLevel))
204 0 : return false;
205 :
206 : // "* `level_base <= level_max`"
207 0 : if (mBaseMipmapLevel > maxLevel)
208 0 : return false;
209 :
210 : // Make a copy so we can modify it.
211 0 : const ImageInfo& baseImageInfo = BaseImageInfo();
212 :
213 : // Reference dimensions based on the current level.
214 0 : uint32_t refWidth = baseImageInfo.mWidth;
215 0 : uint32_t refHeight = baseImageInfo.mHeight;
216 0 : uint32_t refDepth = baseImageInfo.mDepth;
217 0 : MOZ_ASSERT(refWidth && refHeight && refDepth);
218 :
219 0 : for (uint32_t level = mBaseMipmapLevel; level <= maxLevel; level++) {
220 0 : if (!EnsureLevelInitialized(funcName, level)) {
221 0 : *out_initFailed = true;
222 0 : return false;
223 : }
224 :
225 : // "A cube map texture is mipmap complete if each of the six texture images,
226 : // considered individually, is mipmap complete."
227 :
228 0 : for (uint8_t face = 0; face < mFaceCount; face++) {
229 0 : const ImageInfo& cur = ImageInfoAtFace(face, level);
230 :
231 : // "* The set of mipmap arrays `level_base` through `q` (where `q` is defined
232 : // the "Mipmapping" discussion of section 3.8.10) were each specified with
233 : // the same effective internal format."
234 :
235 : // "* The dimensions of the arrays follow the sequence described in the
236 : // "Mipmapping" discussion of section 3.8.10."
237 :
238 0 : if (cur.mWidth != refWidth ||
239 0 : cur.mHeight != refHeight ||
240 0 : cur.mDepth != refDepth ||
241 0 : cur.mFormat != baseImageInfo.mFormat)
242 : {
243 0 : return false;
244 : }
245 : }
246 :
247 : // GLES 3.0.4, p158:
248 : // "[...] until the last array is reached with dimension 1 x 1 x 1."
249 0 : if (mTarget == LOCAL_GL_TEXTURE_3D) {
250 0 : if (refWidth == 1 &&
251 0 : refHeight == 1 &&
252 : refDepth == 1)
253 : {
254 0 : break;
255 : }
256 :
257 0 : refDepth = std::max(uint32_t(1), refDepth / 2);
258 : } else {
259 : // TEXTURE_2D_ARRAY may have depth != 1, but that's normal.
260 0 : if (refWidth == 1 &&
261 : refHeight == 1)
262 : {
263 0 : break;
264 : }
265 : }
266 :
267 0 : refWidth = std::max(uint32_t(1), refWidth / 2);
268 0 : refHeight = std::max(uint32_t(1), refHeight / 2);
269 : }
270 :
271 0 : return true;
272 : }
273 :
274 : bool
275 0 : WebGLTexture::IsCubeComplete() const
276 : {
277 : // GLES 3.0.4, p161
278 : // "[...] a cube map texture is cube complete if the following conditions all hold
279 : // true:
280 : // * The `level_base` arrays of each of the six texture images making up the cube map
281 : // have identical, positive, and square dimensions.
282 : // * The `level_base` arrays were each specified with the same effective internal
283 : // format."
284 :
285 : // Note that "cube complete" does not imply "mipmap complete".
286 :
287 0 : const ImageInfo& reference = BaseImageInfo();
288 0 : if (!reference.IsDefined())
289 0 : return false;
290 :
291 0 : auto refWidth = reference.mWidth;
292 0 : auto refFormat = reference.mFormat;
293 :
294 0 : for (uint8_t face = 0; face < mFaceCount; face++) {
295 0 : const ImageInfo& cur = ImageInfoAtFace(face, mBaseMipmapLevel);
296 0 : if (!cur.IsDefined())
297 0 : return false;
298 :
299 0 : MOZ_ASSERT(cur.mDepth == 1);
300 0 : if (cur.mFormat != refFormat || // Check effective formats.
301 0 : cur.mWidth != refWidth || // Check both width and height against refWidth to
302 0 : cur.mHeight != refWidth) // to enforce positive and square dimensions.
303 : {
304 0 : return false;
305 : }
306 : }
307 :
308 0 : return true;
309 : }
310 :
311 : bool
312 0 : WebGLTexture::IsComplete(const char* funcName, uint32_t texUnit,
313 : const char** const out_reason, bool* const out_initFailed)
314 : {
315 0 : *out_initFailed = false;
316 :
317 0 : const auto maxLevel = kMaxLevelCount - 1;
318 0 : if (mBaseMipmapLevel > maxLevel) {
319 0 : *out_reason = "`level_base` too high.";
320 0 : return false;
321 : }
322 :
323 : // Texture completeness is established at GLES 3.0.4, p160-161.
324 : // "[A] texture is complete unless any of the following conditions hold true:"
325 :
326 : // "* Any dimension of the `level_base` array is not positive."
327 0 : const ImageInfo& baseImageInfo = BaseImageInfo();
328 0 : if (!baseImageInfo.IsDefined()) {
329 : // In case of undefined texture image, we don't print any message because this is
330 : // a very common and often legitimate case (asynchronous texture loading).
331 0 : *out_reason = nullptr;
332 0 : return false;
333 : }
334 :
335 0 : if (!baseImageInfo.mWidth || !baseImageInfo.mHeight || !baseImageInfo.mDepth) {
336 0 : *out_reason = "The dimensions of `level_base` are not all positive.";
337 0 : return false;
338 : }
339 :
340 : // "* The texture is a cube map texture, and is not cube complete."
341 0 : if (IsCubeMap() && !IsCubeComplete()) {
342 0 : *out_reason = "Cubemaps must be \"cube complete\".";
343 0 : return false;
344 : }
345 :
346 0 : WebGLSampler* sampler = mContext->mBoundSamplers[texUnit];
347 0 : TexMinFilter minFilter = sampler ? sampler->mMinFilter : mMinFilter;
348 0 : TexMagFilter magFilter = sampler ? sampler->mMagFilter : mMagFilter;
349 :
350 : // "* The minification filter requires a mipmap (is neither NEAREST nor LINEAR) and
351 : // the texture is not mipmap complete."
352 0 : const bool requiresMipmap = (minFilter != LOCAL_GL_NEAREST &&
353 0 : minFilter != LOCAL_GL_LINEAR);
354 0 : if (requiresMipmap && !IsMipmapComplete(funcName, texUnit, out_initFailed)) {
355 0 : if (*out_initFailed)
356 0 : return false;
357 :
358 0 : *out_reason = "Because the minification filter requires mipmapping, the texture"
359 : " must be \"mipmap complete\".";
360 0 : return false;
361 : }
362 :
363 0 : const bool isMinFilteringNearest = (minFilter == LOCAL_GL_NEAREST ||
364 0 : minFilter == LOCAL_GL_NEAREST_MIPMAP_NEAREST);
365 0 : const bool isMagFilteringNearest = (magFilter == LOCAL_GL_NEAREST);
366 0 : const bool isFilteringNearestOnly = (isMinFilteringNearest && isMagFilteringNearest);
367 0 : if (!isFilteringNearestOnly) {
368 0 : auto formatUsage = baseImageInfo.mFormat;
369 0 : auto format = formatUsage->format;
370 :
371 0 : bool isFilterable = formatUsage->isFilterable;
372 :
373 : // "* The effective internal format specified for the texture arrays is a sized
374 : // internal depth or depth and stencil format, the value of
375 : // TEXTURE_COMPARE_MODE is NONE[1], and either the magnification filter is not
376 : // NEAREST, or the minification filter is neither NEAREST nor
377 : // NEAREST_MIPMAP_NEAREST."
378 : // [1]: This sounds suspect, but is explicitly noted in the change log for GLES
379 : // 3.0.1:
380 : // "* Clarify that a texture is incomplete if it has a depth component, no
381 : // shadow comparison, and linear filtering (also Bug 9481)."
382 0 : if (format->d && mTexCompareMode != LOCAL_GL_NONE) {
383 0 : isFilterable = true;
384 : }
385 :
386 : // "* The effective internal format specified for the texture arrays is a sized
387 : // internal color format that is not texture-filterable, and either the
388 : // magnification filter is not NEAREST or the minification filter is neither
389 : // NEAREST nor NEAREST_MIPMAP_NEAREST."
390 : // Since all (GLES3) unsized color formats are filterable just like their sized
391 : // equivalents, we don't have to care whether its sized or not.
392 0 : if (!isFilterable) {
393 0 : *out_reason = "Because minification or magnification filtering is not NEAREST"
394 : " or NEAREST_MIPMAP_NEAREST, and the texture's format must be"
395 : " \"texture-filterable\".";
396 0 : return false;
397 : }
398 : }
399 :
400 : // Texture completeness is effectively (though not explicitly) amended for GLES2 by
401 : // the "Texture Access" section under $3.8 "Fragment Shaders". This also applies to
402 : // vertex shaders, as noted on GLES 2.0.25, p41.
403 0 : if (!mContext->IsWebGL2()) {
404 : // GLES 2.0.25, p87-88:
405 : // "Calling a sampler from a fragment shader will return (R,G,B,A)=(0,0,0,1) if
406 : // any of the following conditions are true:"
407 :
408 : // "* A two-dimensional sampler is called, the minification filter is one that
409 : // requires a mipmap[...], and the sampler's associated texture object is not
410 : // complete[.]"
411 : // (already covered)
412 :
413 : // "* A two-dimensional sampler is called, the minification filter is not one that
414 : // requires a mipmap (either NEAREST nor[sic] LINEAR), and either dimension of
415 : // the level zero array of the associated texture object is not positive."
416 : // (already covered)
417 :
418 : // "* A two-dimensional sampler is called, the corresponding texture image is a
419 : // non-power-of-two image[...], and either the texture wrap mode is not
420 : // CLAMP_TO_EDGE, or the minification filter is neither NEAREST nor LINEAR."
421 :
422 : // "* A cube map sampler is called, any of the corresponding texture images are
423 : // non-power-of-two images, and either the texture wrap mode is not
424 : // CLAMP_TO_EDGE, or the minification filter is neither NEAREST nor LINEAR."
425 0 : if (!baseImageInfo.IsPowerOfTwo()) {
426 0 : TexWrap wrapS = sampler ? sampler->mWrapS : mWrapS;
427 0 : TexWrap wrapT = sampler ? sampler->mWrapT : mWrapT;
428 : // "either the texture wrap mode is not CLAMP_TO_EDGE"
429 0 : if (wrapS != LOCAL_GL_CLAMP_TO_EDGE ||
430 0 : wrapT != LOCAL_GL_CLAMP_TO_EDGE)
431 : {
432 0 : *out_reason = "Non-power-of-two textures must have a wrap mode of"
433 : " CLAMP_TO_EDGE.";
434 0 : return false;
435 : }
436 :
437 : // "or the minification filter is neither NEAREST nor LINEAR"
438 0 : if (requiresMipmap) {
439 0 : *out_reason = "Mipmapping requires power-of-two textures.";
440 0 : return false;
441 : }
442 : }
443 :
444 : // "* A cube map sampler is called, and either the corresponding cube map texture
445 : // image is not cube complete, or TEXTURE_MIN_FILTER is one that requires a
446 : // mipmap and the texture is not mipmap cube complete."
447 : // (already covered)
448 : }
449 :
450 0 : if (!EnsureLevelInitialized(funcName, mBaseMipmapLevel)) {
451 0 : *out_initFailed = true;
452 0 : return false;
453 : }
454 :
455 0 : return true;
456 : }
457 :
458 : bool
459 0 : WebGLTexture::MaxEffectiveMipmapLevel(uint32_t texUnit, uint32_t* const out) const
460 : {
461 0 : WebGLSampler* sampler = mContext->mBoundSamplers[texUnit];
462 0 : TexMinFilter minFilter = sampler ? sampler->mMinFilter : mMinFilter;
463 0 : if (minFilter == LOCAL_GL_NEAREST ||
464 0 : minFilter == LOCAL_GL_LINEAR)
465 : {
466 : // No extra mips used.
467 0 : *out = mBaseMipmapLevel;
468 0 : return true;
469 : }
470 :
471 0 : const auto& imageInfo = BaseImageInfo();
472 0 : if (!imageInfo.IsDefined())
473 0 : return false;
474 :
475 0 : uint32_t maxLevelBySize = mBaseMipmapLevel + imageInfo.PossibleMipmapLevels() - 1;
476 0 : *out = std::min<uint32_t>(maxLevelBySize, mMaxMipmapLevel);
477 0 : return true;
478 : }
479 :
480 : bool
481 0 : WebGLTexture::GetFakeBlackType(const char* funcName, uint32_t texUnit,
482 : FakeBlackType* const out_fakeBlack)
483 : {
484 : const char* incompleteReason;
485 0 : bool initFailed = false;
486 0 : if (!IsComplete(funcName, texUnit, &incompleteReason, &initFailed)) {
487 0 : if (initFailed) {
488 0 : mContext->ErrorOutOfMemory("%s: Failed to initialize texture data.",
489 0 : funcName);
490 0 : return false; // The world just exploded.
491 : }
492 :
493 0 : if (incompleteReason) {
494 0 : mContext->GenerateWarning("%s: Active texture %u for target 0x%04x is"
495 : " 'incomplete', and will be rendered as"
496 : " RGBA(0,0,0,1), as per the GLES 2.0.24 $3.8.2: %s",
497 : funcName, texUnit, mTarget.get(),
498 0 : incompleteReason);
499 : }
500 0 : *out_fakeBlack = FakeBlackType::RGBA0001;
501 0 : return true;
502 : }
503 :
504 :
505 0 : *out_fakeBlack = FakeBlackType::None;
506 0 : return true;
507 : }
508 :
509 : static void
510 0 : SetSwizzle(gl::GLContext* gl, TexTarget target, const GLint* swizzle)
511 : {
512 : static const GLint kNoSwizzle[4] = { LOCAL_GL_RED, LOCAL_GL_GREEN, LOCAL_GL_BLUE,
513 : LOCAL_GL_ALPHA };
514 0 : if (!swizzle) {
515 0 : swizzle = kNoSwizzle;
516 0 : } else if (!gl->IsSupported(gl::GLFeature::texture_swizzle)) {
517 0 : MOZ_CRASH("GFX: Needs swizzle feature to swizzle!");
518 : }
519 :
520 0 : gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_SWIZZLE_R, swizzle[0]);
521 0 : gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_SWIZZLE_G, swizzle[1]);
522 0 : gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_SWIZZLE_B, swizzle[2]);
523 0 : gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_SWIZZLE_A, swizzle[3]);
524 0 : }
525 :
526 : bool
527 0 : WebGLTexture::ResolveForDraw(const char* funcName, uint32_t texUnit,
528 : FakeBlackType* const out_fakeBlack)
529 : {
530 0 : if (!mIsResolved) {
531 0 : if (!GetFakeBlackType(funcName, texUnit, &mResolved_FakeBlack))
532 0 : return false;
533 :
534 : // Check which swizzle we should use. Since the texture must be complete at this
535 : // point, just grab the format off any valid image.
536 0 : const GLint* newSwizzle = nullptr;
537 0 : if (mResolved_FakeBlack == FakeBlackType::None) {
538 0 : const auto& cur = ImageInfoAtFace(0, mBaseMipmapLevel);
539 0 : newSwizzle = cur.mFormat->textureSwizzleRGBA;
540 : }
541 :
542 : // Only set the swizzle if it changed since last time we did it.
543 0 : if (newSwizzle != mResolved_Swizzle) {
544 0 : mResolved_Swizzle = newSwizzle;
545 :
546 : // Set the new swizzle!
547 0 : mContext->gl->fActiveTexture(LOCAL_GL_TEXTURE0 + texUnit);
548 0 : SetSwizzle(mContext->gl, mTarget, mResolved_Swizzle);
549 0 : mContext->gl->fActiveTexture(LOCAL_GL_TEXTURE0 + mContext->mActiveTexture);
550 : }
551 :
552 0 : mIsResolved = true;
553 : }
554 :
555 0 : *out_fakeBlack = mResolved_FakeBlack;
556 0 : return true;
557 : }
558 :
559 : bool
560 0 : WebGLTexture::EnsureImageDataInitialized(const char* funcName, TexImageTarget target,
561 : uint32_t level)
562 : {
563 0 : auto& imageInfo = ImageInfoAt(target, level);
564 0 : if (!imageInfo.IsDefined())
565 0 : return true;
566 :
567 0 : if (imageInfo.IsDataInitialized())
568 0 : return true;
569 :
570 0 : return InitializeImageData(funcName, target, level);
571 : }
572 :
573 : bool
574 0 : WebGLTexture::EnsureLevelInitialized(const char* funcName, uint32_t level)
575 : {
576 0 : if (mTarget != LOCAL_GL_TEXTURE_CUBE_MAP)
577 0 : return EnsureImageDataInitialized(funcName, mTarget.get(), level);
578 :
579 0 : for (GLenum texImageTarget = LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X;
580 0 : texImageTarget <= LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z;
581 : ++texImageTarget)
582 : {
583 0 : if (!EnsureImageDataInitialized(funcName, texImageTarget, level))
584 0 : return false;
585 : }
586 0 : return true;
587 : }
588 :
589 : static void
590 0 : ZeroANGLEDepthTexture(WebGLContext* webgl, GLuint tex,
591 : const webgl::FormatUsageInfo* usage, uint32_t width,
592 : uint32_t height)
593 : {
594 0 : const auto& format = usage->format;
595 0 : GLenum attachPoint = 0;
596 0 : GLbitfield clearBits = 0;
597 :
598 0 : if (format->d) {
599 0 : attachPoint = LOCAL_GL_DEPTH_ATTACHMENT;
600 0 : clearBits |= LOCAL_GL_DEPTH_BUFFER_BIT;
601 : }
602 :
603 0 : if (format->s) {
604 0 : attachPoint = (format->d ? LOCAL_GL_DEPTH_STENCIL_ATTACHMENT
605 : : LOCAL_GL_STENCIL_ATTACHMENT);
606 0 : clearBits |= LOCAL_GL_STENCIL_BUFFER_BIT;
607 : }
608 :
609 0 : MOZ_RELEASE_ASSERT(attachPoint && clearBits, "GFX: No bits cleared.");
610 :
611 : ////
612 0 : const auto& gl = webgl->gl;
613 0 : MOZ_ASSERT(gl->IsCurrent());
614 :
615 0 : gl::ScopedFramebuffer scopedFB(gl);
616 0 : const gl::ScopedBindFramebuffer scopedBindFB(gl, scopedFB.FB());
617 :
618 0 : gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, attachPoint, LOCAL_GL_TEXTURE_2D,
619 0 : tex, 0);
620 :
621 0 : const auto& status = gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
622 0 : MOZ_RELEASE_ASSERT(status == LOCAL_GL_FRAMEBUFFER_COMPLETE);
623 :
624 : ////
625 :
626 0 : const bool fakeNoAlpha = false;
627 0 : webgl->ForceClearFramebufferWithDefaultValues(clearBits, fakeNoAlpha);
628 0 : }
629 :
630 : static bool
631 0 : ZeroTextureData(WebGLContext* webgl, const char* funcName, GLuint tex,
632 : TexImageTarget target, uint32_t level,
633 : const webgl::FormatUsageInfo* usage, uint32_t width, uint32_t height,
634 : uint32_t depth)
635 : {
636 : // This has two usecases:
637 : // 1. Lazy zeroing of uninitialized textures:
638 : // a. Before draw, when FakeBlack isn't viable. (TexStorage + Draw*)
639 : // b. Before partial upload. (TexStorage + TexSubImage)
640 : // 2. Zero subrects from out-of-bounds blits. (CopyTex(Sub)Image)
641 :
642 : // We have no sympathy for any of these cases.
643 :
644 : // "Doctor, it hurts when I do this!" "Well don't do that!"
645 : webgl->GenerateWarning("%s: This operation requires zeroing texture data. This is"
646 : " slow.",
647 0 : funcName);
648 :
649 0 : gl::GLContext* gl = webgl->GL();
650 0 : gl->MakeCurrent();
651 :
652 : GLenum scopeBindTarget;
653 0 : switch (target.get()) {
654 : case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X:
655 : case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
656 : case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
657 : case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
658 : case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
659 : case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
660 0 : scopeBindTarget = LOCAL_GL_TEXTURE_CUBE_MAP;
661 0 : break;
662 : default:
663 0 : scopeBindTarget = target.get();
664 0 : break;
665 : }
666 0 : const gl::ScopedBindTexture scopeBindTexture(gl, tex, scopeBindTarget);
667 0 : auto compression = usage->format->compression;
668 0 : if (compression) {
669 0 : auto sizedFormat = usage->format->sizedFormat;
670 0 : MOZ_RELEASE_ASSERT(sizedFormat, "GFX: texture sized format not set");
671 :
672 0 : const auto fnSizeInBlocks = [](CheckedUint32 pixels, uint8_t pixelsPerBlock) {
673 0 : return RoundUpToMultipleOf(pixels, pixelsPerBlock) / pixelsPerBlock;
674 0 : };
675 :
676 0 : const auto widthBlocks = fnSizeInBlocks(width, compression->blockWidth);
677 0 : const auto heightBlocks = fnSizeInBlocks(height, compression->blockHeight);
678 :
679 0 : CheckedUint32 checkedByteCount = compression->bytesPerBlock;
680 0 : checkedByteCount *= widthBlocks;
681 0 : checkedByteCount *= heightBlocks;
682 0 : checkedByteCount *= depth;
683 :
684 0 : if (!checkedByteCount.isValid())
685 0 : return false;
686 :
687 0 : const size_t byteCount = checkedByteCount.value();
688 :
689 0 : UniqueBuffer zeros = calloc(1, byteCount);
690 0 : if (!zeros)
691 0 : return false;
692 :
693 0 : ScopedUnpackReset scopedReset(webgl);
694 0 : gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 1); // Don't bother with striding it
695 : // well.
696 :
697 0 : const auto error = DoCompressedTexSubImage(gl, target.get(), level, 0, 0, 0,
698 : width, height, depth, sizedFormat,
699 0 : byteCount, zeros.get());
700 0 : return !error;
701 : }
702 :
703 0 : const auto driverUnpackInfo = usage->idealUnpack;
704 0 : MOZ_RELEASE_ASSERT(driverUnpackInfo, "GFX: ideal unpack info not set.");
705 :
706 0 : if (webgl->IsExtensionEnabled(WebGLExtensionID::WEBGL_depth_texture) &&
707 0 : gl->IsANGLE() &&
708 0 : usage->format->d)
709 : {
710 : // ANGLE_depth_texture does not allow uploads, so we have to clear.
711 : // (Restriction because of D3D9)
712 0 : MOZ_ASSERT(target == LOCAL_GL_TEXTURE_2D);
713 0 : MOZ_ASSERT(level == 0);
714 0 : ZeroANGLEDepthTexture(webgl, tex, usage, width, height);
715 0 : return true;
716 : }
717 :
718 0 : const webgl::PackingInfo packing = driverUnpackInfo->ToPacking();
719 :
720 0 : const auto bytesPerPixel = webgl::BytesPerPixel(packing);
721 :
722 0 : CheckedUint32 checkedByteCount = bytesPerPixel;
723 0 : checkedByteCount *= width;
724 0 : checkedByteCount *= height;
725 0 : checkedByteCount *= depth;
726 :
727 0 : if (!checkedByteCount.isValid())
728 0 : return false;
729 :
730 0 : const size_t byteCount = checkedByteCount.value();
731 :
732 0 : UniqueBuffer zeros = calloc(1, byteCount);
733 0 : if (!zeros)
734 0 : return false;
735 :
736 0 : ScopedUnpackReset scopedReset(webgl);
737 0 : gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 1); // Don't bother with striding it well.
738 0 : const auto error = DoTexSubImage(gl, target, level, 0, 0, 0, width, height, depth,
739 0 : packing, zeros.get());
740 0 : return !error;
741 : }
742 :
743 : bool
744 0 : WebGLTexture::InitializeImageData(const char* funcName, TexImageTarget target,
745 : uint32_t level)
746 : {
747 0 : auto& imageInfo = ImageInfoAt(target, level);
748 0 : MOZ_ASSERT(imageInfo.IsDefined());
749 0 : MOZ_ASSERT(!imageInfo.IsDataInitialized());
750 :
751 0 : const auto& usage = imageInfo.mFormat;
752 0 : const auto& width = imageInfo.mWidth;
753 0 : const auto& height = imageInfo.mHeight;
754 0 : const auto& depth = imageInfo.mDepth;
755 :
756 0 : if (!ZeroTextureData(mContext, funcName, mGLName, target, level, usage, width, height,
757 : depth))
758 : {
759 0 : return false;
760 : }
761 :
762 0 : imageInfo.SetIsDataInitialized(true, this);
763 0 : return true;
764 : }
765 :
766 : void
767 0 : WebGLTexture::ClampLevelBaseAndMax()
768 : {
769 0 : if (!mImmutable)
770 0 : return;
771 :
772 : // GLES 3.0.4, p158:
773 : // "For immutable-format textures, `level_base` is clamped to the range
774 : // `[0, levels-1]`, `level_max` is then clamped to the range `
775 : // `[level_base, levels-1]`, where `levels` is the parameter passed to
776 : // TexStorage* for the texture object."
777 0 : mBaseMipmapLevel = Clamp<uint32_t>(mBaseMipmapLevel, 0, mImmutableLevelCount - 1);
778 0 : mMaxMipmapLevel = Clamp<uint32_t>(mMaxMipmapLevel, mBaseMipmapLevel,
779 0 : mImmutableLevelCount - 1);
780 : }
781 :
782 : void
783 0 : WebGLTexture::PopulateMipChain(const char* funcName, uint32_t firstLevel,
784 : uint32_t lastLevel)
785 : {
786 0 : const ImageInfo& baseImageInfo = ImageInfoAtFace(0, firstLevel);
787 0 : MOZ_ASSERT(baseImageInfo.IsDefined());
788 :
789 0 : uint32_t refWidth = baseImageInfo.mWidth;
790 0 : uint32_t refHeight = baseImageInfo.mHeight;
791 0 : uint32_t refDepth = baseImageInfo.mDepth;
792 0 : if (!refWidth || !refHeight || !refDepth)
793 0 : return;
794 :
795 0 : for (uint32_t level = firstLevel + 1; level <= lastLevel; level++) {
796 0 : bool isMinimal = (refWidth == 1 &&
797 0 : refHeight == 1);
798 0 : if (mTarget == LOCAL_GL_TEXTURE_3D) {
799 0 : isMinimal &= (refDepth == 1);
800 : }
801 :
802 : // Higher levels are unaffected.
803 0 : if (isMinimal)
804 0 : break;
805 :
806 0 : refWidth = std::max(uint32_t(1), refWidth / 2);
807 0 : refHeight = std::max(uint32_t(1), refHeight / 2);
808 0 : if (mTarget == LOCAL_GL_TEXTURE_3D) { // But not TEXTURE_2D_ARRAY!
809 0 : refDepth = std::max(uint32_t(1), refDepth / 2);
810 : }
811 :
812 0 : const ImageInfo cur(baseImageInfo.mFormat, refWidth, refHeight, refDepth,
813 0 : baseImageInfo.IsDataInitialized());
814 :
815 0 : SetImageInfosAtLevel(funcName, level, cur);
816 : }
817 : }
818 :
819 : //////////////////////////////////////////////////////////////////////////////////////////
820 : // GL calls
821 :
822 : bool
823 0 : WebGLTexture::BindTexture(TexTarget texTarget)
824 : {
825 0 : if (IsDeleted()) {
826 0 : mContext->ErrorInvalidOperation("bindTexture: Cannot bind a deleted object.");
827 0 : return false;
828 : }
829 :
830 0 : const bool isFirstBinding = !HasEverBeenBound();
831 0 : if (!isFirstBinding && mTarget != texTarget) {
832 0 : mContext->ErrorInvalidOperation("bindTexture: This texture has already been bound"
833 0 : " to a different target.");
834 0 : return false;
835 : }
836 :
837 0 : mTarget = texTarget;
838 :
839 0 : mContext->gl->fBindTexture(mTarget.get(), mGLName);
840 :
841 0 : if (isFirstBinding) {
842 0 : mFaceCount = IsCubeMap() ? 6 : 1;
843 :
844 0 : gl::GLContext* gl = mContext->gl;
845 :
846 : // Thanks to the WebKit people for finding this out: GL_TEXTURE_WRAP_R
847 : // is not present in GLES 2, but is present in GL and it seems as if for
848 : // cube maps we need to set it to GL_CLAMP_TO_EDGE to get the expected
849 : // GLES behavior.
850 : // If we are WebGL 2 though, we'll want to leave it as REPEAT.
851 0 : const bool hasWrapR = gl->IsSupported(gl::GLFeature::texture_3D);
852 0 : if (IsCubeMap() && hasWrapR && !mContext->IsWebGL2()) {
853 0 : gl->fTexParameteri(texTarget.get(), LOCAL_GL_TEXTURE_WRAP_R,
854 0 : LOCAL_GL_CLAMP_TO_EDGE);
855 : }
856 : }
857 :
858 0 : return true;
859 : }
860 :
861 :
862 : void
863 0 : WebGLTexture::GenerateMipmap(TexTarget texTarget)
864 : {
865 0 : const char funcName[] = "generateMipmap";
866 : // GLES 3.0.4 p160:
867 : // "Mipmap generation replaces texel array levels level base + 1 through q with arrays
868 : // derived from the level base array, regardless of their previous contents. All
869 : // other mipmap arrays, including the level base array, are left unchanged by this
870 : // computation."
871 0 : const ImageInfo& baseImageInfo = BaseImageInfo();
872 0 : if (!baseImageInfo.IsDefined()) {
873 0 : mContext->ErrorInvalidOperation("%s: The base level of the texture is not"
874 : " defined.",
875 0 : funcName);
876 0 : return;
877 : }
878 :
879 0 : if (IsCubeMap() && !IsCubeComplete()) {
880 0 : mContext->ErrorInvalidOperation("%s: Cube maps must be \"cube complete\".",
881 0 : funcName);
882 0 : return;
883 : }
884 :
885 0 : if (!mContext->IsWebGL2() && !baseImageInfo.IsPowerOfTwo()) {
886 0 : mContext->ErrorInvalidOperation("%s: The base level of the texture does not have"
887 : " power-of-two dimensions.",
888 0 : funcName);
889 0 : return;
890 : }
891 :
892 0 : auto format = baseImageInfo.mFormat->format;
893 0 : if (format->compression) {
894 0 : mContext->ErrorInvalidOperation("%s: Texture data at base level is compressed.",
895 0 : funcName);
896 0 : return;
897 : }
898 :
899 0 : if (format->d) {
900 0 : mContext->ErrorInvalidOperation("%s: Depth textures are not supported.",
901 0 : funcName);
902 0 : return;
903 : }
904 :
905 : // OpenGL ES 3.0.4 p160:
906 : // If the level base array was not specified with an unsized internal format from
907 : // table 3.3 or a sized internal format that is both color-renderable and
908 : // texture-filterable according to table 3.13, an INVALID_OPERATION error
909 : // is generated.
910 0 : const auto usage = baseImageInfo.mFormat;
911 0 : bool canGenerateMipmap = (usage->IsRenderable() && usage->isFilterable);
912 0 : switch (usage->format->effectiveFormat) {
913 : case webgl::EffectiveFormat::Luminance8:
914 : case webgl::EffectiveFormat::Alpha8:
915 : case webgl::EffectiveFormat::Luminance8Alpha8:
916 : // Non-color-renderable formats from Table 3.3.
917 0 : canGenerateMipmap = true;
918 0 : break;
919 : default:
920 0 : break;
921 : }
922 :
923 0 : if (!canGenerateMipmap) {
924 0 : mContext->ErrorInvalidOperation("%s: Texture at base level is not unsized"
925 : " internal format or is not"
926 : " color-renderable or texture-filterable.",
927 0 : funcName);
928 0 : return;
929 : }
930 :
931 : // Done with validation. Do the operation.
932 :
933 0 : mContext->MakeContextCurrent();
934 0 : gl::GLContext* gl = mContext->gl;
935 :
936 0 : if (gl->WorkAroundDriverBugs()) {
937 : // bug 696495 - to work around failures in the texture-mips.html test on various drivers, we
938 : // set the minification filter before calling glGenerateMipmap. This should not carry a significant performance
939 : // overhead so we do it unconditionally.
940 : //
941 : // note that the choice of GL_NEAREST_MIPMAP_NEAREST really matters. See Chromium bug 101105.
942 0 : gl->fTexParameteri(texTarget.get(), LOCAL_GL_TEXTURE_MIN_FILTER,
943 0 : LOCAL_GL_NEAREST_MIPMAP_NEAREST);
944 0 : gl->fGenerateMipmap(texTarget.get());
945 0 : gl->fTexParameteri(texTarget.get(), LOCAL_GL_TEXTURE_MIN_FILTER,
946 0 : mMinFilter.get());
947 : } else {
948 0 : gl->fGenerateMipmap(texTarget.get());
949 : }
950 :
951 : // Record the results.
952 : // Note that we don't use MaxEffectiveMipmapLevel() here, since that returns
953 : // mBaseMipmapLevel if the min filter doesn't require mipmaps.
954 0 : const uint32_t maxLevel = mBaseMipmapLevel + baseImageInfo.PossibleMipmapLevels() - 1;
955 0 : PopulateMipChain(funcName, mBaseMipmapLevel, maxLevel);
956 : }
957 :
958 : JS::Value
959 0 : WebGLTexture::GetTexParameter(TexTarget texTarget, GLenum pname)
960 : {
961 0 : mContext->MakeContextCurrent();
962 :
963 0 : GLint i = 0;
964 0 : GLfloat f = 0.0f;
965 :
966 0 : switch (pname) {
967 : case LOCAL_GL_TEXTURE_MIN_FILTER:
968 0 : return JS::NumberValue(uint32_t(mMinFilter.get()));
969 :
970 : case LOCAL_GL_TEXTURE_MAG_FILTER:
971 0 : return JS::NumberValue(uint32_t(mMagFilter.get()));
972 :
973 : case LOCAL_GL_TEXTURE_WRAP_S:
974 0 : return JS::NumberValue(uint32_t(mWrapS.get()));
975 :
976 : case LOCAL_GL_TEXTURE_WRAP_T:
977 0 : return JS::NumberValue(uint32_t(mWrapT.get()));
978 :
979 : case LOCAL_GL_TEXTURE_BASE_LEVEL:
980 0 : return JS::NumberValue(mBaseMipmapLevel);
981 :
982 : case LOCAL_GL_TEXTURE_COMPARE_MODE:
983 0 : return JS::NumberValue(uint32_t(mTexCompareMode));
984 :
985 : case LOCAL_GL_TEXTURE_MAX_LEVEL:
986 0 : return JS::NumberValue(mMaxMipmapLevel);
987 :
988 : case LOCAL_GL_TEXTURE_IMMUTABLE_FORMAT:
989 0 : return JS::BooleanValue(mImmutable);
990 :
991 : case LOCAL_GL_TEXTURE_IMMUTABLE_LEVELS:
992 0 : return JS::NumberValue(uint32_t(mImmutableLevelCount));
993 :
994 : case LOCAL_GL_TEXTURE_COMPARE_FUNC:
995 : case LOCAL_GL_TEXTURE_WRAP_R:
996 0 : mContext->gl->fGetTexParameteriv(texTarget.get(), pname, &i);
997 0 : return JS::NumberValue(uint32_t(i));
998 :
999 : case LOCAL_GL_TEXTURE_MAX_ANISOTROPY_EXT:
1000 : case LOCAL_GL_TEXTURE_MAX_LOD:
1001 : case LOCAL_GL_TEXTURE_MIN_LOD:
1002 0 : mContext->gl->fGetTexParameterfv(texTarget.get(), pname, &f);
1003 0 : return JS::NumberValue(float(f));
1004 :
1005 : default:
1006 0 : MOZ_CRASH("GFX: Unhandled pname.");
1007 : }
1008 : }
1009 :
1010 : bool
1011 0 : WebGLTexture::IsTexture() const
1012 : {
1013 0 : return HasEverBeenBound() && !IsDeleted();
1014 : }
1015 :
1016 : // Here we have to support all pnames with both int and float params.
1017 : // See this discussion:
1018 : // https://www.khronos.org/webgl/public-mailing-list/archives/1008/msg00014.html
1019 : void
1020 0 : WebGLTexture::TexParameter(TexTarget texTarget, GLenum pname, const FloatOrInt& param)
1021 : {
1022 0 : bool isPNameValid = false;
1023 0 : switch (pname) {
1024 : // GLES 2.0.25 p76:
1025 : case LOCAL_GL_TEXTURE_WRAP_S:
1026 : case LOCAL_GL_TEXTURE_WRAP_T:
1027 : case LOCAL_GL_TEXTURE_MIN_FILTER:
1028 : case LOCAL_GL_TEXTURE_MAG_FILTER:
1029 0 : isPNameValid = true;
1030 0 : break;
1031 :
1032 : // GLES 3.0.4 p149-150:
1033 : case LOCAL_GL_TEXTURE_BASE_LEVEL:
1034 : case LOCAL_GL_TEXTURE_COMPARE_MODE:
1035 : case LOCAL_GL_TEXTURE_COMPARE_FUNC:
1036 : case LOCAL_GL_TEXTURE_MAX_LEVEL:
1037 : case LOCAL_GL_TEXTURE_MAX_LOD:
1038 : case LOCAL_GL_TEXTURE_MIN_LOD:
1039 : case LOCAL_GL_TEXTURE_WRAP_R:
1040 0 : if (mContext->IsWebGL2())
1041 0 : isPNameValid = true;
1042 0 : break;
1043 :
1044 : case LOCAL_GL_TEXTURE_MAX_ANISOTROPY_EXT:
1045 0 : if (mContext->IsExtensionEnabled(WebGLExtensionID::EXT_texture_filter_anisotropic))
1046 0 : isPNameValid = true;
1047 0 : break;
1048 : }
1049 :
1050 0 : if (!isPNameValid) {
1051 0 : mContext->ErrorInvalidEnumInfo("texParameter: pname", pname);
1052 0 : return;
1053 : }
1054 :
1055 : ////////////////
1056 : // Validate params and invalidate if needed.
1057 :
1058 0 : bool paramBadEnum = false;
1059 0 : bool paramBadValue = false;
1060 :
1061 0 : switch (pname) {
1062 : case LOCAL_GL_TEXTURE_BASE_LEVEL:
1063 : case LOCAL_GL_TEXTURE_MAX_LEVEL:
1064 0 : paramBadValue = (param.i < 0);
1065 0 : break;
1066 :
1067 : case LOCAL_GL_TEXTURE_COMPARE_MODE:
1068 0 : paramBadValue = (param.i != LOCAL_GL_NONE &&
1069 0 : param.i != LOCAL_GL_COMPARE_REF_TO_TEXTURE);
1070 0 : break;
1071 :
1072 : case LOCAL_GL_TEXTURE_COMPARE_FUNC:
1073 0 : switch (param.i) {
1074 : case LOCAL_GL_LEQUAL:
1075 : case LOCAL_GL_GEQUAL:
1076 : case LOCAL_GL_LESS:
1077 : case LOCAL_GL_GREATER:
1078 : case LOCAL_GL_EQUAL:
1079 : case LOCAL_GL_NOTEQUAL:
1080 : case LOCAL_GL_ALWAYS:
1081 : case LOCAL_GL_NEVER:
1082 0 : break;
1083 :
1084 : default:
1085 0 : paramBadValue = true;
1086 0 : break;
1087 : }
1088 0 : break;
1089 :
1090 : case LOCAL_GL_TEXTURE_MIN_FILTER:
1091 0 : switch (param.i) {
1092 : case LOCAL_GL_NEAREST:
1093 : case LOCAL_GL_LINEAR:
1094 : case LOCAL_GL_NEAREST_MIPMAP_NEAREST:
1095 : case LOCAL_GL_LINEAR_MIPMAP_NEAREST:
1096 : case LOCAL_GL_NEAREST_MIPMAP_LINEAR:
1097 : case LOCAL_GL_LINEAR_MIPMAP_LINEAR:
1098 0 : break;
1099 :
1100 : default:
1101 0 : paramBadEnum = true;
1102 0 : break;
1103 : }
1104 0 : break;
1105 :
1106 : case LOCAL_GL_TEXTURE_MAG_FILTER:
1107 0 : switch (param.i) {
1108 : case LOCAL_GL_NEAREST:
1109 : case LOCAL_GL_LINEAR:
1110 0 : break;
1111 :
1112 : default:
1113 0 : paramBadEnum = true;
1114 0 : break;
1115 : }
1116 0 : break;
1117 :
1118 : case LOCAL_GL_TEXTURE_WRAP_S:
1119 : case LOCAL_GL_TEXTURE_WRAP_T:
1120 : case LOCAL_GL_TEXTURE_WRAP_R:
1121 0 : switch (param.i) {
1122 : case LOCAL_GL_CLAMP_TO_EDGE:
1123 : case LOCAL_GL_MIRRORED_REPEAT:
1124 : case LOCAL_GL_REPEAT:
1125 0 : break;
1126 :
1127 : default:
1128 0 : paramBadEnum = true;
1129 0 : break;
1130 : }
1131 0 : break;
1132 :
1133 : case LOCAL_GL_TEXTURE_MAX_ANISOTROPY_EXT:
1134 0 : if (param.f < 1.0f)
1135 0 : paramBadValue = true;
1136 :
1137 0 : break;
1138 : }
1139 :
1140 0 : if (paramBadEnum) {
1141 0 : if (!param.isFloat) {
1142 0 : mContext->ErrorInvalidEnum("texParameteri: pname 0x%04x: Invalid param"
1143 : " 0x%04x.",
1144 0 : pname, param.i);
1145 : } else {
1146 0 : mContext->ErrorInvalidEnum("texParameterf: pname 0x%04x: Invalid param %g.",
1147 0 : pname, param.f);
1148 : }
1149 0 : return;
1150 : }
1151 :
1152 0 : if (paramBadValue) {
1153 0 : if (!param.isFloat) {
1154 0 : mContext->ErrorInvalidValue("texParameteri: pname 0x%04x: Invalid param %i"
1155 : " (0x%x).",
1156 0 : pname, param.i, param.i);
1157 : } else {
1158 0 : mContext->ErrorInvalidValue("texParameterf: pname 0x%04x: Invalid param %g.",
1159 0 : pname, param.f);
1160 : }
1161 0 : return;
1162 : }
1163 :
1164 : ////////////////
1165 : // Store any needed values
1166 :
1167 0 : FloatOrInt clamped = param;
1168 0 : switch (pname) {
1169 : case LOCAL_GL_TEXTURE_BASE_LEVEL:
1170 0 : mBaseMipmapLevel = clamped.i;
1171 0 : ClampLevelBaseAndMax();
1172 0 : clamped = FloatOrInt(GLint(mBaseMipmapLevel));
1173 0 : break;
1174 :
1175 : case LOCAL_GL_TEXTURE_MAX_LEVEL:
1176 0 : mMaxMipmapLevel = clamped.i;
1177 0 : ClampLevelBaseAndMax();
1178 0 : clamped = FloatOrInt(GLint(mMaxMipmapLevel));
1179 0 : break;
1180 :
1181 : case LOCAL_GL_TEXTURE_MIN_FILTER:
1182 0 : mMinFilter = clamped.i;
1183 0 : break;
1184 :
1185 : case LOCAL_GL_TEXTURE_MAG_FILTER:
1186 0 : mMagFilter = clamped.i;
1187 0 : break;
1188 :
1189 : case LOCAL_GL_TEXTURE_WRAP_S:
1190 0 : mWrapS = clamped.i;
1191 0 : break;
1192 :
1193 : case LOCAL_GL_TEXTURE_WRAP_T:
1194 0 : mWrapT = clamped.i;
1195 0 : break;
1196 :
1197 : case LOCAL_GL_TEXTURE_COMPARE_MODE:
1198 0 : mTexCompareMode = clamped.i;
1199 0 : break;
1200 :
1201 : // We don't actually need to store the WRAP_R, since it doesn't change texture
1202 : // completeness rules.
1203 : }
1204 :
1205 : // Only a couple of pnames don't need to invalidate our resolve status cache.
1206 0 : switch (pname) {
1207 : case LOCAL_GL_TEXTURE_MAX_ANISOTROPY_EXT:
1208 : case LOCAL_GL_TEXTURE_WRAP_R:
1209 0 : break;
1210 :
1211 : default:
1212 0 : InvalidateResolveCache();
1213 0 : break;
1214 : }
1215 :
1216 : ////////////////
1217 :
1218 0 : mContext->MakeContextCurrent();
1219 0 : if (!clamped.isFloat)
1220 0 : mContext->gl->fTexParameteri(texTarget.get(), pname, clamped.i);
1221 : else
1222 0 : mContext->gl->fTexParameterf(texTarget.get(), pname, clamped.f);
1223 : }
1224 :
1225 : ////////////////////////////////////////////////////////////////////////////////
1226 :
1227 0 : NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLTexture)
1228 :
1229 0 : NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLTexture, AddRef)
1230 0 : NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLTexture, Release)
1231 :
1232 : } // namespace mozilla
|