1 //
2 // Copyright 2002 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6
7 // Texture.cpp: Implements the gl::Texture class. [OpenGL ES 2.0.24] section 3.7 page 63.
8
9 #include "libANGLE/Texture.h"
10
11 #include "common/mathutil.h"
12 #include "common/utilities.h"
13 #include "libANGLE/Config.h"
14 #include "libANGLE/Context.h"
15 #include "libANGLE/Image.h"
16 #include "libANGLE/State.h"
17 #include "libANGLE/Surface.h"
18 #include "libANGLE/formatutils.h"
19 #include "libANGLE/renderer/GLImplFactory.h"
20 #include "libANGLE/renderer/TextureImpl.h"
21
22 namespace gl
23 {
24
25 namespace
26 {
27 constexpr angle::SubjectIndex kBufferSubjectIndex = 2;
28 static_assert(kBufferSubjectIndex != rx::kTextureImageImplObserverMessageIndex, "Index collision");
29 static_assert(kBufferSubjectIndex != rx::kTextureImageSiblingMessageIndex, "Index collision");
30
IsPointSampled(const SamplerState & samplerState)31 bool IsPointSampled(const SamplerState &samplerState)
32 {
33 return (samplerState.getMagFilter() == GL_NEAREST &&
34 (samplerState.getMinFilter() == GL_NEAREST ||
35 samplerState.getMinFilter() == GL_NEAREST_MIPMAP_NEAREST));
36 }
37
GetImageDescIndex(TextureTarget target,size_t level)38 size_t GetImageDescIndex(TextureTarget target, size_t level)
39 {
40 return IsCubeMapFaceTarget(target) ? (level * 6 + CubeMapTextureTargetToFaceIndex(target))
41 : level;
42 }
43
DetermineInitState(const Context * context,Buffer * unpackBuffer,const uint8_t * pixels)44 InitState DetermineInitState(const Context *context, Buffer *unpackBuffer, const uint8_t *pixels)
45 {
46 // Can happen in tests.
47 if (!context || !context->isRobustResourceInitEnabled())
48 {
49 return InitState::Initialized;
50 }
51
52 return (!pixels && !unpackBuffer) ? InitState::MayNeedInit : InitState::Initialized;
53 }
54 } // namespace
55
IsMipmapFiltered(GLenum minFilterMode)56 bool IsMipmapFiltered(GLenum minFilterMode)
57 {
58 switch (minFilterMode)
59 {
60 case GL_NEAREST:
61 case GL_LINEAR:
62 return false;
63 case GL_NEAREST_MIPMAP_NEAREST:
64 case GL_LINEAR_MIPMAP_NEAREST:
65 case GL_NEAREST_MIPMAP_LINEAR:
66 case GL_LINEAR_MIPMAP_LINEAR:
67 return true;
68 default:
69 UNREACHABLE();
70 return false;
71 }
72 }
73
ConvertToNearestFilterMode(GLenum filterMode)74 GLenum ConvertToNearestFilterMode(GLenum filterMode)
75 {
76 switch (filterMode)
77 {
78 case GL_LINEAR:
79 return GL_NEAREST;
80 case GL_LINEAR_MIPMAP_NEAREST:
81 return GL_NEAREST_MIPMAP_NEAREST;
82 case GL_LINEAR_MIPMAP_LINEAR:
83 return GL_NEAREST_MIPMAP_LINEAR;
84 default:
85 return filterMode;
86 }
87 }
88
ConvertToNearestMipFilterMode(GLenum filterMode)89 GLenum ConvertToNearestMipFilterMode(GLenum filterMode)
90 {
91 switch (filterMode)
92 {
93 case GL_LINEAR_MIPMAP_LINEAR:
94 return GL_LINEAR_MIPMAP_NEAREST;
95 case GL_NEAREST_MIPMAP_LINEAR:
96 return GL_NEAREST_MIPMAP_NEAREST;
97 default:
98 return filterMode;
99 }
100 }
101
IsMipmapSupported(const TextureType & type)102 bool IsMipmapSupported(const TextureType &type)
103 {
104 if (type == TextureType::_2DMultisample || type == TextureType::Buffer)
105 {
106 return false;
107 }
108 return true;
109 }
110
SwizzleState()111 SwizzleState::SwizzleState()
112 : swizzleRed(GL_RED), swizzleGreen(GL_GREEN), swizzleBlue(GL_BLUE), swizzleAlpha(GL_ALPHA)
113 {}
114
SwizzleState(GLenum red,GLenum green,GLenum blue,GLenum alpha)115 SwizzleState::SwizzleState(GLenum red, GLenum green, GLenum blue, GLenum alpha)
116 : swizzleRed(red), swizzleGreen(green), swizzleBlue(blue), swizzleAlpha(alpha)
117 {}
118
swizzleRequired() const119 bool SwizzleState::swizzleRequired() const
120 {
121 return swizzleRed != GL_RED || swizzleGreen != GL_GREEN || swizzleBlue != GL_BLUE ||
122 swizzleAlpha != GL_ALPHA;
123 }
124
operator ==(const SwizzleState & other) const125 bool SwizzleState::operator==(const SwizzleState &other) const
126 {
127 return swizzleRed == other.swizzleRed && swizzleGreen == other.swizzleGreen &&
128 swizzleBlue == other.swizzleBlue && swizzleAlpha == other.swizzleAlpha;
129 }
130
operator !=(const SwizzleState & other) const131 bool SwizzleState::operator!=(const SwizzleState &other) const
132 {
133 return !(*this == other);
134 }
135
TextureState(TextureType type)136 TextureState::TextureState(TextureType type)
137 : mType(type),
138 mSamplerState(SamplerState::CreateDefaultForTarget(type)),
139 mSrgbOverride(SrgbOverride::Default),
140 mBaseLevel(0),
141 mMaxLevel(kInitialMaxLevel),
142 mDepthStencilTextureMode(GL_DEPTH_COMPONENT),
143 mHasBeenBoundAsImage(false),
144 mHasBeenBoundAsAttachment(false),
145 mImmutableFormat(false),
146 mImmutableLevels(0),
147 mUsage(GL_NONE),
148 mHasProtectedContent(false),
149 mImageDescs((IMPLEMENTATION_MAX_TEXTURE_LEVELS + 1) * (type == TextureType::CubeMap ? 6 : 1)),
150 mCropRect(0, 0, 0, 0),
151 mGenerateMipmapHint(GL_FALSE),
152 mInitState(InitState::Initialized),
153 mCachedSamplerFormat(SamplerFormat::InvalidEnum),
154 mCachedSamplerCompareMode(GL_NONE),
155 mCachedSamplerFormatValid(false)
156 {}
157
~TextureState()158 TextureState::~TextureState() {}
159
swizzleRequired() const160 bool TextureState::swizzleRequired() const
161 {
162 return mSwizzleState.swizzleRequired();
163 }
164
getEffectiveBaseLevel() const165 GLuint TextureState::getEffectiveBaseLevel() const
166 {
167 if (mImmutableFormat)
168 {
169 // GLES 3.0.4 section 3.8.10
170 return std::min(mBaseLevel, mImmutableLevels - 1);
171 }
172 // Some classes use the effective base level to index arrays with level data. By clamping the
173 // effective base level to max levels these arrays need just one extra item to store properties
174 // that should be returned for all out-of-range base level values, instead of needing special
175 // handling for out-of-range base levels.
176 return std::min(mBaseLevel, static_cast<GLuint>(IMPLEMENTATION_MAX_TEXTURE_LEVELS));
177 }
178
getEffectiveMaxLevel() const179 GLuint TextureState::getEffectiveMaxLevel() const
180 {
181 if (mImmutableFormat)
182 {
183 // GLES 3.0.4 section 3.8.10
184 GLuint clampedMaxLevel = std::max(mMaxLevel, getEffectiveBaseLevel());
185 clampedMaxLevel = std::min(clampedMaxLevel, mImmutableLevels - 1);
186 return clampedMaxLevel;
187 }
188 return mMaxLevel;
189 }
190
getMipmapMaxLevel() const191 GLuint TextureState::getMipmapMaxLevel() const
192 {
193 const ImageDesc &baseImageDesc = getImageDesc(getBaseImageTarget(), getEffectiveBaseLevel());
194 GLuint expectedMipLevels = 0;
195 if (mType == TextureType::_3D)
196 {
197 const int maxDim = std::max(std::max(baseImageDesc.size.width, baseImageDesc.size.height),
198 baseImageDesc.size.depth);
199 expectedMipLevels = static_cast<GLuint>(log2(maxDim));
200 }
201 else
202 {
203 expectedMipLevels = static_cast<GLuint>(
204 log2(std::max(baseImageDesc.size.width, baseImageDesc.size.height)));
205 }
206
207 return std::min<GLuint>(getEffectiveBaseLevel() + expectedMipLevels, getEffectiveMaxLevel());
208 }
209
setBaseLevel(GLuint baseLevel)210 bool TextureState::setBaseLevel(GLuint baseLevel)
211 {
212 if (mBaseLevel != baseLevel)
213 {
214 mBaseLevel = baseLevel;
215 return true;
216 }
217 return false;
218 }
219
setMaxLevel(GLuint maxLevel)220 bool TextureState::setMaxLevel(GLuint maxLevel)
221 {
222 if (mMaxLevel != maxLevel)
223 {
224 mMaxLevel = maxLevel;
225 return true;
226 }
227
228 return false;
229 }
230
231 // Tests for cube texture completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.
232 // According to [OpenGL ES 3.0.5] section 3.8.13 Texture Completeness page 160 any
233 // per-level checks begin at the base-level.
234 // For OpenGL ES2 the base level is always zero.
isCubeComplete() const235 bool TextureState::isCubeComplete() const
236 {
237 ASSERT(mType == TextureType::CubeMap);
238
239 angle::EnumIterator<TextureTarget> face = kCubeMapTextureTargetMin;
240 const ImageDesc &baseImageDesc = getImageDesc(*face, getEffectiveBaseLevel());
241 if (baseImageDesc.size.width == 0 || baseImageDesc.size.width != baseImageDesc.size.height)
242 {
243 return false;
244 }
245
246 ++face;
247
248 for (; face != kAfterCubeMapTextureTargetMax; ++face)
249 {
250 const ImageDesc &faceImageDesc = getImageDesc(*face, getEffectiveBaseLevel());
251 if (faceImageDesc.size.width != baseImageDesc.size.width ||
252 faceImageDesc.size.height != baseImageDesc.size.height ||
253 !Format::SameSized(faceImageDesc.format, baseImageDesc.format))
254 {
255 return false;
256 }
257 }
258
259 return true;
260 }
261
getBaseLevelDesc() const262 const ImageDesc &TextureState::getBaseLevelDesc() const
263 {
264 ASSERT(mType != TextureType::CubeMap || isCubeComplete());
265 return getImageDesc(getBaseImageTarget(), getEffectiveBaseLevel());
266 }
267
getLevelZeroDesc() const268 const ImageDesc &TextureState::getLevelZeroDesc() const
269 {
270 ASSERT(mType != TextureType::CubeMap || isCubeComplete());
271 return getImageDesc(getBaseImageTarget(), 0);
272 }
273
setCrop(const Rectangle & rect)274 void TextureState::setCrop(const Rectangle &rect)
275 {
276 mCropRect = rect;
277 }
278
getCrop() const279 const Rectangle &TextureState::getCrop() const
280 {
281 return mCropRect;
282 }
283
setGenerateMipmapHint(GLenum hint)284 void TextureState::setGenerateMipmapHint(GLenum hint)
285 {
286 mGenerateMipmapHint = hint;
287 }
288
getGenerateMipmapHint() const289 GLenum TextureState::getGenerateMipmapHint() const
290 {
291 return mGenerateMipmapHint;
292 }
293
computeRequiredSamplerFormat(const SamplerState & samplerState) const294 SamplerFormat TextureState::computeRequiredSamplerFormat(const SamplerState &samplerState) const
295 {
296 const ImageDesc &baseImageDesc = getImageDesc(getBaseImageTarget(), getEffectiveBaseLevel());
297 if ((baseImageDesc.format.info->format == GL_DEPTH_COMPONENT ||
298 baseImageDesc.format.info->format == GL_DEPTH_STENCIL) &&
299 samplerState.getCompareMode() != GL_NONE)
300 {
301 return SamplerFormat::Shadow;
302 }
303 else
304 {
305 switch (baseImageDesc.format.info->componentType)
306 {
307 case GL_UNSIGNED_NORMALIZED:
308 case GL_SIGNED_NORMALIZED:
309 case GL_FLOAT:
310 return SamplerFormat::Float;
311 case GL_INT:
312 return SamplerFormat::Signed;
313 case GL_UNSIGNED_INT:
314 return SamplerFormat::Unsigned;
315 default:
316 return SamplerFormat::InvalidEnum;
317 }
318 }
319 }
320
computeSamplerCompleteness(const SamplerState & samplerState,const State & state) const321 bool TextureState::computeSamplerCompleteness(const SamplerState &samplerState,
322 const State &state) const
323 {
324 // Buffer textures cannot be incomplete.
325 if (mType == TextureType::Buffer)
326 {
327 return true;
328 }
329
330 if (!mImmutableFormat && mBaseLevel > mMaxLevel)
331 {
332 return false;
333 }
334 const ImageDesc &baseImageDesc = getImageDesc(getBaseImageTarget(), getEffectiveBaseLevel());
335 if (baseImageDesc.size.width == 0 || baseImageDesc.size.height == 0 ||
336 baseImageDesc.size.depth == 0)
337 {
338 return false;
339 }
340 // The cases where the texture is incomplete because base level is out of range should be
341 // handled by the above condition.
342 ASSERT(mBaseLevel < IMPLEMENTATION_MAX_TEXTURE_LEVELS || mImmutableFormat);
343
344 if (mType == TextureType::CubeMap && baseImageDesc.size.width != baseImageDesc.size.height)
345 {
346 return false;
347 }
348
349 // According to es 3.1 spec, texture is justified as incomplete if sized internalformat is
350 // unfilterable(table 20.11) and filter is not GL_NEAREST(8.16). The default value of minFilter
351 // is NEAREST_MIPMAP_LINEAR and magFilter is LINEAR(table 20.11,). For multismaple texture,
352 // filter state of multisample texture is ignored(11.1.3.3). So it shouldn't be judged as
353 // incomplete texture. So, we ignore filtering for multisample texture completeness here.
354 if (!IsMultisampled(mType) &&
355 !baseImageDesc.format.info->filterSupport(state.getClientVersion(),
356 state.getExtensions()) &&
357 !IsPointSampled(samplerState))
358 {
359 return false;
360 }
361 bool npotSupport = state.getExtensions().textureNpotOES || state.getClientMajorVersion() >= 3;
362 if (!npotSupport)
363 {
364 if ((samplerState.getWrapS() != GL_CLAMP_TO_EDGE &&
365 samplerState.getWrapS() != GL_CLAMP_TO_BORDER && !isPow2(baseImageDesc.size.width)) ||
366 (samplerState.getWrapT() != GL_CLAMP_TO_EDGE &&
367 samplerState.getWrapT() != GL_CLAMP_TO_BORDER && !isPow2(baseImageDesc.size.height)))
368 {
369 return false;
370 }
371 }
372
373 if (IsMipmapSupported(mType) && IsMipmapFiltered(samplerState.getMinFilter()))
374 {
375 if (!npotSupport)
376 {
377 if (!isPow2(baseImageDesc.size.width) || !isPow2(baseImageDesc.size.height))
378 {
379 return false;
380 }
381 }
382
383 if (!computeMipmapCompleteness())
384 {
385 return false;
386 }
387 }
388 else
389 {
390 if (mType == TextureType::CubeMap && !isCubeComplete())
391 {
392 return false;
393 }
394 }
395
396 // From GL_OES_EGL_image_external_essl3: If state is present in a sampler object bound to a
397 // texture unit that would have been rejected by a call to TexParameter* for the texture bound
398 // to that unit, the behavior of the implementation is as if the texture were incomplete. For
399 // example, if TEXTURE_WRAP_S or TEXTURE_WRAP_T is set to anything but CLAMP_TO_EDGE on the
400 // sampler object bound to a texture unit and the texture bound to that unit is an external
401 // texture and EXT_EGL_image_external_wrap_modes is not enabled, the texture will be considered
402 // incomplete.
403 // Sampler object state which does not affect sampling for the type of texture bound
404 // to a texture unit, such as TEXTURE_WRAP_R for an external texture, does not affect
405 // completeness.
406 if (mType == TextureType::External)
407 {
408 if (!state.getExtensions().EGLImageExternalWrapModesEXT)
409 {
410 if (samplerState.getWrapS() != GL_CLAMP_TO_EDGE ||
411 samplerState.getWrapT() != GL_CLAMP_TO_EDGE)
412 {
413 return false;
414 }
415 }
416
417 if (samplerState.getMinFilter() != GL_LINEAR && samplerState.getMinFilter() != GL_NEAREST)
418 {
419 return false;
420 }
421 }
422
423 // OpenGLES 3.0.2 spec section 3.8.13 states that a texture is not mipmap complete if:
424 // The internalformat specified for the texture arrays is a sized internal depth or
425 // depth and stencil format (see table 3.13), the value of TEXTURE_COMPARE_-
426 // MODE is NONE, and either the magnification filter is not NEAREST or the mini-
427 // fication filter is neither NEAREST nor NEAREST_MIPMAP_NEAREST.
428 if (!IsMultisampled(mType) && baseImageDesc.format.info->depthBits > 0 &&
429 state.getClientMajorVersion() >= 3)
430 {
431 // Note: we restrict this validation to sized types. For the OES_depth_textures
432 // extension, due to some underspecification problems, we must allow linear filtering
433 // for legacy compatibility with WebGL 1.
434 // See http://crbug.com/649200
435 if (samplerState.getCompareMode() == GL_NONE && baseImageDesc.format.info->sized)
436 {
437 if ((samplerState.getMinFilter() != GL_NEAREST &&
438 samplerState.getMinFilter() != GL_NEAREST_MIPMAP_NEAREST) ||
439 samplerState.getMagFilter() != GL_NEAREST)
440 {
441 return false;
442 }
443 }
444 }
445
446 // OpenGLES 3.1 spec section 8.16 states that a texture is not mipmap complete if:
447 // The internalformat specified for the texture is DEPTH_STENCIL format, the value of
448 // DEPTH_STENCIL_TEXTURE_MODE is STENCIL_INDEX, and either the magnification filter is
449 // not NEAREST or the minification filter is neither NEAREST nor NEAREST_MIPMAP_NEAREST.
450 // However, the ES 3.1 spec differs from the statement above, because it is incorrect.
451 // See the issue at https://github.com/KhronosGroup/OpenGL-API/issues/33.
452 // For multismaple texture, filter state of multisample texture is ignored(11.1.3.3).
453 // So it shouldn't be judged as incomplete texture. So, we ignore filtering for multisample
454 // texture completeness here.
455 if (!IsMultisampled(mType) && baseImageDesc.format.info->depthBits > 0 &&
456 mDepthStencilTextureMode == GL_STENCIL_INDEX)
457 {
458 if ((samplerState.getMinFilter() != GL_NEAREST &&
459 samplerState.getMinFilter() != GL_NEAREST_MIPMAP_NEAREST) ||
460 samplerState.getMagFilter() != GL_NEAREST)
461 {
462 return false;
463 }
464 }
465
466 return true;
467 }
468
computeMipmapCompleteness() const469 bool TextureState::computeMipmapCompleteness() const
470 {
471 const GLuint maxLevel = getMipmapMaxLevel();
472
473 for (GLuint level = getEffectiveBaseLevel(); level <= maxLevel; level++)
474 {
475 if (mType == TextureType::CubeMap)
476 {
477 for (TextureTarget face : AllCubeFaceTextureTargets())
478 {
479 if (!computeLevelCompleteness(face, level))
480 {
481 return false;
482 }
483 }
484 }
485 else
486 {
487 if (!computeLevelCompleteness(NonCubeTextureTypeToTarget(mType), level))
488 {
489 return false;
490 }
491 }
492 }
493
494 return true;
495 }
496
computeLevelCompleteness(TextureTarget target,size_t level) const497 bool TextureState::computeLevelCompleteness(TextureTarget target, size_t level) const
498 {
499 ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS);
500
501 if (mImmutableFormat)
502 {
503 return true;
504 }
505
506 const ImageDesc &baseImageDesc = getImageDesc(getBaseImageTarget(), getEffectiveBaseLevel());
507 if (baseImageDesc.size.width == 0 || baseImageDesc.size.height == 0 ||
508 baseImageDesc.size.depth == 0)
509 {
510 return false;
511 }
512
513 const ImageDesc &levelImageDesc = getImageDesc(target, level);
514 if (levelImageDesc.size.width == 0 || levelImageDesc.size.height == 0 ||
515 levelImageDesc.size.depth == 0)
516 {
517 return false;
518 }
519
520 if (!Format::SameSized(levelImageDesc.format, baseImageDesc.format))
521 {
522 return false;
523 }
524
525 ASSERT(level >= getEffectiveBaseLevel());
526 const size_t relativeLevel = level - getEffectiveBaseLevel();
527 if (levelImageDesc.size.width != std::max(1, baseImageDesc.size.width >> relativeLevel))
528 {
529 return false;
530 }
531
532 if (levelImageDesc.size.height != std::max(1, baseImageDesc.size.height >> relativeLevel))
533 {
534 return false;
535 }
536
537 if (mType == TextureType::_3D)
538 {
539 if (levelImageDesc.size.depth != std::max(1, baseImageDesc.size.depth >> relativeLevel))
540 {
541 return false;
542 }
543 }
544 else if (IsArrayTextureType(mType))
545 {
546 if (levelImageDesc.size.depth != baseImageDesc.size.depth)
547 {
548 return false;
549 }
550 }
551
552 return true;
553 }
554
getBaseImageTarget() const555 TextureTarget TextureState::getBaseImageTarget() const
556 {
557 return mType == TextureType::CubeMap ? kCubeMapTextureTargetMin
558 : NonCubeTextureTypeToTarget(mType);
559 }
560
getEnabledLevelCount() const561 GLuint TextureState::getEnabledLevelCount() const
562 {
563 GLuint levelCount = 0;
564 const GLuint baseLevel = getEffectiveBaseLevel();
565 const GLuint maxLevel = std::min(getEffectiveMaxLevel(), getMipmapMaxLevel());
566
567 // The mip chain will have either one or more sequential levels, or max levels,
568 // but not a sparse one.
569 Optional<Extents> expectedSize;
570 for (size_t enabledLevel = baseLevel; enabledLevel <= maxLevel; ++enabledLevel, ++levelCount)
571 {
572 // Note: for cube textures, we only check the first face.
573 TextureTarget target = TextureTypeToTarget(mType, 0);
574 size_t descIndex = GetImageDescIndex(target, enabledLevel);
575 const Extents &levelSize = mImageDescs[descIndex].size;
576
577 if (levelSize.empty())
578 {
579 break;
580 }
581 if (expectedSize.valid())
582 {
583 Extents newSize = expectedSize.value();
584 newSize.width = std::max(1, newSize.width >> 1);
585 newSize.height = std::max(1, newSize.height >> 1);
586
587 if (!IsArrayTextureType(mType))
588 {
589 newSize.depth = std::max(1, newSize.depth >> 1);
590 }
591
592 if (newSize != levelSize)
593 {
594 break;
595 }
596 }
597 expectedSize = levelSize;
598 }
599
600 return levelCount;
601 }
602
ImageDesc()603 ImageDesc::ImageDesc()
604 : ImageDesc(Extents(0, 0, 0), Format::Invalid(), 0, GL_TRUE, InitState::Initialized)
605 {}
606
ImageDesc(const Extents & size,const Format & format,const InitState initState)607 ImageDesc::ImageDesc(const Extents &size, const Format &format, const InitState initState)
608 : size(size), format(format), samples(0), fixedSampleLocations(GL_TRUE), initState(initState)
609 {}
610
ImageDesc(const Extents & size,const Format & format,const GLsizei samples,const bool fixedSampleLocations,const InitState initState)611 ImageDesc::ImageDesc(const Extents &size,
612 const Format &format,
613 const GLsizei samples,
614 const bool fixedSampleLocations,
615 const InitState initState)
616 : size(size),
617 format(format),
618 samples(samples),
619 fixedSampleLocations(fixedSampleLocations),
620 initState(initState)
621 {}
622
getMemorySize() const623 GLint ImageDesc::getMemorySize() const
624 {
625 // Assume allocated size is around width * height * depth * samples * pixelBytes
626 angle::CheckedNumeric<GLint> levelSize = 1;
627 levelSize *= format.info->pixelBytes;
628 levelSize *= size.width;
629 levelSize *= size.height;
630 levelSize *= size.depth;
631 levelSize *= std::max(samples, 1);
632 return levelSize.ValueOrDefault(std::numeric_limits<GLint>::max());
633 }
634
getImageDesc(TextureTarget target,size_t level) const635 const ImageDesc &TextureState::getImageDesc(TextureTarget target, size_t level) const
636 {
637 size_t descIndex = GetImageDescIndex(target, level);
638 ASSERT(descIndex < mImageDescs.size());
639 return mImageDescs[descIndex];
640 }
641
setImageDesc(TextureTarget target,size_t level,const ImageDesc & desc)642 void TextureState::setImageDesc(TextureTarget target, size_t level, const ImageDesc &desc)
643 {
644 size_t descIndex = GetImageDescIndex(target, level);
645 ASSERT(descIndex < mImageDescs.size());
646 mImageDescs[descIndex] = desc;
647 if (desc.initState == InitState::MayNeedInit)
648 {
649 mInitState = InitState::MayNeedInit;
650 }
651 else
652 {
653 // Scan for any uninitialized images. If there are none, set the init state of the entire
654 // texture to initialized. The cost of the scan is only paid after doing image
655 // initialization which is already very expensive.
656 bool allImagesInitialized = true;
657
658 for (const ImageDesc &initDesc : mImageDescs)
659 {
660 if (initDesc.initState == InitState::MayNeedInit)
661 {
662 allImagesInitialized = false;
663 break;
664 }
665 }
666
667 if (allImagesInitialized)
668 {
669 mInitState = InitState::Initialized;
670 }
671 }
672 }
673
674 // Note that an ImageIndex that represents an entire level of a cube map corresponds to 6
675 // ImageDescs, so if the cube map is cube complete, we return the ImageDesc of the first cube
676 // face, and we don't allow using this function when the cube map is not cube complete.
getImageDesc(const ImageIndex & imageIndex) const677 const ImageDesc &TextureState::getImageDesc(const ImageIndex &imageIndex) const
678 {
679 if (imageIndex.isEntireLevelCubeMap())
680 {
681 ASSERT(isCubeComplete());
682 const GLint levelIndex = imageIndex.getLevelIndex();
683 return getImageDesc(kCubeMapTextureTargetMin, levelIndex);
684 }
685
686 return getImageDesc(imageIndex.getTarget(), imageIndex.getLevelIndex());
687 }
688
setImageDescChain(GLuint baseLevel,GLuint maxLevel,Extents baseSize,const Format & format,InitState initState)689 void TextureState::setImageDescChain(GLuint baseLevel,
690 GLuint maxLevel,
691 Extents baseSize,
692 const Format &format,
693 InitState initState)
694 {
695 for (GLuint level = baseLevel; level <= maxLevel; level++)
696 {
697 int relativeLevel = (level - baseLevel);
698 Extents levelSize(std::max<int>(baseSize.width >> relativeLevel, 1),
699 std::max<int>(baseSize.height >> relativeLevel, 1),
700 (IsArrayTextureType(mType))
701 ? baseSize.depth
702 : std::max<int>(baseSize.depth >> relativeLevel, 1));
703 ImageDesc levelInfo(levelSize, format, initState);
704
705 if (mType == TextureType::CubeMap)
706 {
707 for (TextureTarget face : AllCubeFaceTextureTargets())
708 {
709 setImageDesc(face, level, levelInfo);
710 }
711 }
712 else
713 {
714 setImageDesc(NonCubeTextureTypeToTarget(mType), level, levelInfo);
715 }
716 }
717 }
718
setImageDescChainMultisample(Extents baseSize,const Format & format,GLsizei samples,bool fixedSampleLocations,InitState initState)719 void TextureState::setImageDescChainMultisample(Extents baseSize,
720 const Format &format,
721 GLsizei samples,
722 bool fixedSampleLocations,
723 InitState initState)
724 {
725 ASSERT(mType == TextureType::_2DMultisample || mType == TextureType::_2DMultisampleArray);
726 ImageDesc levelInfo(baseSize, format, samples, fixedSampleLocations, initState);
727 setImageDesc(NonCubeTextureTypeToTarget(mType), 0, levelInfo);
728 }
729
clearImageDesc(TextureTarget target,size_t level)730 void TextureState::clearImageDesc(TextureTarget target, size_t level)
731 {
732 setImageDesc(target, level, ImageDesc());
733 }
734
clearImageDescs()735 void TextureState::clearImageDescs()
736 {
737 for (size_t descIndex = 0; descIndex < mImageDescs.size(); descIndex++)
738 {
739 mImageDescs[descIndex] = ImageDesc();
740 }
741 }
742
Texture(rx::GLImplFactory * factory,TextureID id,TextureType type)743 Texture::Texture(rx::GLImplFactory *factory, TextureID id, TextureType type)
744 : RefCountObject(factory->generateSerial(), id),
745 mState(type),
746 mTexture(factory->createTexture(mState)),
747 mImplObserver(this, rx::kTextureImageImplObserverMessageIndex),
748 mBufferObserver(this, kBufferSubjectIndex),
749 mBoundSurface(nullptr),
750 mBoundStream(nullptr)
751 {
752 mImplObserver.bind(mTexture);
753
754 // Initially assume the implementation is dirty.
755 mDirtyBits.set(DIRTY_BIT_IMPLEMENTATION);
756 }
757
onDestroy(const Context * context)758 void Texture::onDestroy(const Context *context)
759 {
760 if (mBoundSurface)
761 {
762 ANGLE_SWALLOW_ERR(mBoundSurface->releaseTexImage(context, EGL_BACK_BUFFER));
763 mBoundSurface = nullptr;
764 }
765 if (mBoundStream)
766 {
767 mBoundStream->releaseTextures();
768 mBoundStream = nullptr;
769 }
770
771 egl::RefCountObjectReleaser<egl::Image> releaseImage;
772 (void)orphanImages(context, &releaseImage);
773
774 mState.mBuffer.set(context, nullptr, 0, 0);
775
776 if (mTexture)
777 {
778 mTexture->onDestroy(context);
779 }
780 }
781
~Texture()782 Texture::~Texture()
783 {
784 SafeDelete(mTexture);
785 }
786
setLabel(const Context * context,const std::string & label)787 void Texture::setLabel(const Context *context, const std::string &label)
788 {
789 mState.mLabel = label;
790
791 if (mTexture)
792 {
793 mTexture->onLabelUpdate();
794 }
795 }
796
getLabel() const797 const std::string &Texture::getLabel() const
798 {
799 return mState.mLabel;
800 }
801
setSwizzleRed(const Context * context,GLenum swizzleRed)802 void Texture::setSwizzleRed(const Context *context, GLenum swizzleRed)
803 {
804 mState.mSwizzleState.swizzleRed = swizzleRed;
805 signalDirtyState(DIRTY_BIT_SWIZZLE_RED);
806 }
807
getSwizzleRed() const808 GLenum Texture::getSwizzleRed() const
809 {
810 return mState.mSwizzleState.swizzleRed;
811 }
812
setSwizzleGreen(const Context * context,GLenum swizzleGreen)813 void Texture::setSwizzleGreen(const Context *context, GLenum swizzleGreen)
814 {
815 mState.mSwizzleState.swizzleGreen = swizzleGreen;
816 signalDirtyState(DIRTY_BIT_SWIZZLE_GREEN);
817 }
818
getSwizzleGreen() const819 GLenum Texture::getSwizzleGreen() const
820 {
821 return mState.mSwizzleState.swizzleGreen;
822 }
823
setSwizzleBlue(const Context * context,GLenum swizzleBlue)824 void Texture::setSwizzleBlue(const Context *context, GLenum swizzleBlue)
825 {
826 mState.mSwizzleState.swizzleBlue = swizzleBlue;
827 signalDirtyState(DIRTY_BIT_SWIZZLE_BLUE);
828 }
829
getSwizzleBlue() const830 GLenum Texture::getSwizzleBlue() const
831 {
832 return mState.mSwizzleState.swizzleBlue;
833 }
834
setSwizzleAlpha(const Context * context,GLenum swizzleAlpha)835 void Texture::setSwizzleAlpha(const Context *context, GLenum swizzleAlpha)
836 {
837 mState.mSwizzleState.swizzleAlpha = swizzleAlpha;
838 signalDirtyState(DIRTY_BIT_SWIZZLE_ALPHA);
839 }
840
getSwizzleAlpha() const841 GLenum Texture::getSwizzleAlpha() const
842 {
843 return mState.mSwizzleState.swizzleAlpha;
844 }
845
setMinFilter(const Context * context,GLenum minFilter)846 void Texture::setMinFilter(const Context *context, GLenum minFilter)
847 {
848 if (mState.mSamplerState.setMinFilter(minFilter))
849 {
850 signalDirtyState(DIRTY_BIT_MIN_FILTER);
851 }
852 }
853
getMinFilter() const854 GLenum Texture::getMinFilter() const
855 {
856 return mState.mSamplerState.getMinFilter();
857 }
858
setMagFilter(const Context * context,GLenum magFilter)859 void Texture::setMagFilter(const Context *context, GLenum magFilter)
860 {
861 if (mState.mSamplerState.setMagFilter(magFilter))
862 {
863 signalDirtyState(DIRTY_BIT_MAG_FILTER);
864 }
865 }
866
getMagFilter() const867 GLenum Texture::getMagFilter() const
868 {
869 return mState.mSamplerState.getMagFilter();
870 }
871
setWrapS(const Context * context,GLenum wrapS)872 void Texture::setWrapS(const Context *context, GLenum wrapS)
873 {
874 if (mState.mSamplerState.setWrapS(wrapS))
875 {
876 signalDirtyState(DIRTY_BIT_WRAP_S);
877 }
878 }
879
getWrapS() const880 GLenum Texture::getWrapS() const
881 {
882 return mState.mSamplerState.getWrapS();
883 }
884
setWrapT(const Context * context,GLenum wrapT)885 void Texture::setWrapT(const Context *context, GLenum wrapT)
886 {
887 if (mState.mSamplerState.getWrapT() == wrapT)
888 return;
889 if (mState.mSamplerState.setWrapT(wrapT))
890 {
891 signalDirtyState(DIRTY_BIT_WRAP_T);
892 }
893 }
894
getWrapT() const895 GLenum Texture::getWrapT() const
896 {
897 return mState.mSamplerState.getWrapT();
898 }
899
setWrapR(const Context * context,GLenum wrapR)900 void Texture::setWrapR(const Context *context, GLenum wrapR)
901 {
902 if (mState.mSamplerState.setWrapR(wrapR))
903 {
904 signalDirtyState(DIRTY_BIT_WRAP_R);
905 }
906 }
907
getWrapR() const908 GLenum Texture::getWrapR() const
909 {
910 return mState.mSamplerState.getWrapR();
911 }
912
setMaxAnisotropy(const Context * context,float maxAnisotropy)913 void Texture::setMaxAnisotropy(const Context *context, float maxAnisotropy)
914 {
915 if (mState.mSamplerState.setMaxAnisotropy(maxAnisotropy))
916 {
917 signalDirtyState(DIRTY_BIT_MAX_ANISOTROPY);
918 }
919 }
920
getMaxAnisotropy() const921 float Texture::getMaxAnisotropy() const
922 {
923 return mState.mSamplerState.getMaxAnisotropy();
924 }
925
setMinLod(const Context * context,GLfloat minLod)926 void Texture::setMinLod(const Context *context, GLfloat minLod)
927 {
928 if (mState.mSamplerState.setMinLod(minLod))
929 {
930 signalDirtyState(DIRTY_BIT_MIN_LOD);
931 }
932 }
933
getMinLod() const934 GLfloat Texture::getMinLod() const
935 {
936 return mState.mSamplerState.getMinLod();
937 }
938
setMaxLod(const Context * context,GLfloat maxLod)939 void Texture::setMaxLod(const Context *context, GLfloat maxLod)
940 {
941 if (mState.mSamplerState.setMaxLod(maxLod))
942 {
943 signalDirtyState(DIRTY_BIT_MAX_LOD);
944 }
945 }
946
getMaxLod() const947 GLfloat Texture::getMaxLod() const
948 {
949 return mState.mSamplerState.getMaxLod();
950 }
951
setCompareMode(const Context * context,GLenum compareMode)952 void Texture::setCompareMode(const Context *context, GLenum compareMode)
953 {
954 if (mState.mSamplerState.setCompareMode(compareMode))
955 {
956 signalDirtyState(DIRTY_BIT_COMPARE_MODE);
957 }
958 }
959
getCompareMode() const960 GLenum Texture::getCompareMode() const
961 {
962 return mState.mSamplerState.getCompareMode();
963 }
964
setCompareFunc(const Context * context,GLenum compareFunc)965 void Texture::setCompareFunc(const Context *context, GLenum compareFunc)
966 {
967 if (mState.mSamplerState.setCompareFunc(compareFunc))
968 {
969 signalDirtyState(DIRTY_BIT_COMPARE_FUNC);
970 }
971 }
972
getCompareFunc() const973 GLenum Texture::getCompareFunc() const
974 {
975 return mState.mSamplerState.getCompareFunc();
976 }
977
setSRGBDecode(const Context * context,GLenum sRGBDecode)978 void Texture::setSRGBDecode(const Context *context, GLenum sRGBDecode)
979 {
980 if (mState.mSamplerState.setSRGBDecode(sRGBDecode))
981 {
982 signalDirtyState(DIRTY_BIT_SRGB_DECODE);
983 }
984 }
985
getSRGBDecode() const986 GLenum Texture::getSRGBDecode() const
987 {
988 return mState.mSamplerState.getSRGBDecode();
989 }
990
setSRGBOverride(const Context * context,GLenum sRGBOverride)991 void Texture::setSRGBOverride(const Context *context, GLenum sRGBOverride)
992 {
993 SrgbOverride oldOverride = mState.mSrgbOverride;
994 mState.mSrgbOverride = (sRGBOverride == GL_SRGB) ? SrgbOverride::SRGB : SrgbOverride::Default;
995 if (mState.mSrgbOverride != oldOverride)
996 {
997 signalDirtyState(DIRTY_BIT_SRGB_OVERRIDE);
998 }
999 }
1000
getSRGBOverride() const1001 GLenum Texture::getSRGBOverride() const
1002 {
1003 return (mState.mSrgbOverride == SrgbOverride::SRGB) ? GL_SRGB : GL_NONE;
1004 }
1005
getSamplerState() const1006 const SamplerState &Texture::getSamplerState() const
1007 {
1008 return mState.mSamplerState;
1009 }
1010
setBaseLevel(const Context * context,GLuint baseLevel)1011 angle::Result Texture::setBaseLevel(const Context *context, GLuint baseLevel)
1012 {
1013 if (mState.setBaseLevel(baseLevel))
1014 {
1015 ANGLE_TRY(mTexture->setBaseLevel(context, mState.getEffectiveBaseLevel()));
1016 signalDirtyState(DIRTY_BIT_BASE_LEVEL);
1017 }
1018
1019 return angle::Result::Continue;
1020 }
1021
getBaseLevel() const1022 GLuint Texture::getBaseLevel() const
1023 {
1024 return mState.mBaseLevel;
1025 }
1026
setMaxLevel(const Context * context,GLuint maxLevel)1027 void Texture::setMaxLevel(const Context *context, GLuint maxLevel)
1028 {
1029 if (mState.setMaxLevel(maxLevel))
1030 {
1031 signalDirtyState(DIRTY_BIT_MAX_LEVEL);
1032 }
1033 }
1034
getMaxLevel() const1035 GLuint Texture::getMaxLevel() const
1036 {
1037 return mState.mMaxLevel;
1038 }
1039
setDepthStencilTextureMode(const Context * context,GLenum mode)1040 void Texture::setDepthStencilTextureMode(const Context *context, GLenum mode)
1041 {
1042 if (mState.mDepthStencilTextureMode != mode)
1043 {
1044 mState.mDepthStencilTextureMode = mode;
1045 signalDirtyState(DIRTY_BIT_DEPTH_STENCIL_TEXTURE_MODE);
1046 }
1047 }
1048
getDepthStencilTextureMode() const1049 GLenum Texture::getDepthStencilTextureMode() const
1050 {
1051 return mState.mDepthStencilTextureMode;
1052 }
1053
getImmutableFormat() const1054 bool Texture::getImmutableFormat() const
1055 {
1056 return mState.mImmutableFormat;
1057 }
1058
getImmutableLevels() const1059 GLuint Texture::getImmutableLevels() const
1060 {
1061 return mState.mImmutableLevels;
1062 }
1063
setUsage(const Context * context,GLenum usage)1064 void Texture::setUsage(const Context *context, GLenum usage)
1065 {
1066 mState.mUsage = usage;
1067 signalDirtyState(DIRTY_BIT_USAGE);
1068 }
1069
getUsage() const1070 GLenum Texture::getUsage() const
1071 {
1072 return mState.mUsage;
1073 }
1074
setProtectedContent(Context * context,bool hasProtectedContent)1075 void Texture::setProtectedContent(Context *context, bool hasProtectedContent)
1076 {
1077 mState.mHasProtectedContent = hasProtectedContent;
1078 }
1079
hasProtectedContent() const1080 bool Texture::hasProtectedContent() const
1081 {
1082 return mState.mHasProtectedContent;
1083 }
1084
getTextureState() const1085 const TextureState &Texture::getTextureState() const
1086 {
1087 return mState;
1088 }
1089
getExtents(TextureTarget target,size_t level) const1090 const Extents &Texture::getExtents(TextureTarget target, size_t level) const
1091 {
1092 ASSERT(TextureTargetToType(target) == mState.mType);
1093 return mState.getImageDesc(target, level).size;
1094 }
1095
getWidth(TextureTarget target,size_t level) const1096 size_t Texture::getWidth(TextureTarget target, size_t level) const
1097 {
1098 ASSERT(TextureTargetToType(target) == mState.mType);
1099 return mState.getImageDesc(target, level).size.width;
1100 }
1101
getHeight(TextureTarget target,size_t level) const1102 size_t Texture::getHeight(TextureTarget target, size_t level) const
1103 {
1104 ASSERT(TextureTargetToType(target) == mState.mType);
1105 return mState.getImageDesc(target, level).size.height;
1106 }
1107
getDepth(TextureTarget target,size_t level) const1108 size_t Texture::getDepth(TextureTarget target, size_t level) const
1109 {
1110 ASSERT(TextureTargetToType(target) == mState.mType);
1111 return mState.getImageDesc(target, level).size.depth;
1112 }
1113
getFormat(TextureTarget target,size_t level) const1114 const Format &Texture::getFormat(TextureTarget target, size_t level) const
1115 {
1116 ASSERT(TextureTargetToType(target) == mState.mType);
1117 return mState.getImageDesc(target, level).format;
1118 }
1119
getSamples(TextureTarget target,size_t level) const1120 GLsizei Texture::getSamples(TextureTarget target, size_t level) const
1121 {
1122 ASSERT(TextureTargetToType(target) == mState.mType);
1123 return mState.getImageDesc(target, level).samples;
1124 }
1125
getFixedSampleLocations(TextureTarget target,size_t level) const1126 bool Texture::getFixedSampleLocations(TextureTarget target, size_t level) const
1127 {
1128 ASSERT(TextureTargetToType(target) == mState.mType);
1129 return mState.getImageDesc(target, level).fixedSampleLocations;
1130 }
1131
getMipmapMaxLevel() const1132 GLuint Texture::getMipmapMaxLevel() const
1133 {
1134 return mState.getMipmapMaxLevel();
1135 }
1136
isMipmapComplete() const1137 bool Texture::isMipmapComplete() const
1138 {
1139 return mState.computeMipmapCompleteness();
1140 }
1141
getBoundSurface() const1142 egl::Surface *Texture::getBoundSurface() const
1143 {
1144 return mBoundSurface;
1145 }
1146
getBoundStream() const1147 egl::Stream *Texture::getBoundStream() const
1148 {
1149 return mBoundStream;
1150 }
1151
getMemorySize() const1152 GLint Texture::getMemorySize() const
1153 {
1154 GLint implSize = mTexture->getMemorySize();
1155 if (implSize > 0)
1156 {
1157 return implSize;
1158 }
1159
1160 angle::CheckedNumeric<GLint> size = 0;
1161 for (const ImageDesc &imageDesc : mState.mImageDescs)
1162 {
1163 size += imageDesc.getMemorySize();
1164 }
1165 return size.ValueOrDefault(std::numeric_limits<GLint>::max());
1166 }
1167
getLevelMemorySize(TextureTarget target,GLint level) const1168 GLint Texture::getLevelMemorySize(TextureTarget target, GLint level) const
1169 {
1170 GLint implSize = mTexture->getLevelMemorySize(target, level);
1171 if (implSize > 0)
1172 {
1173 return implSize;
1174 }
1175
1176 return mState.getImageDesc(target, level).getMemorySize();
1177 }
1178
signalDirtyStorage(InitState initState)1179 void Texture::signalDirtyStorage(InitState initState)
1180 {
1181 mState.mInitState = initState;
1182 invalidateCompletenessCache();
1183 mState.mCachedSamplerFormatValid = false;
1184 onStateChange(angle::SubjectMessage::SubjectChanged);
1185 }
1186
signalDirtyState(size_t dirtyBit)1187 void Texture::signalDirtyState(size_t dirtyBit)
1188 {
1189 mDirtyBits.set(dirtyBit);
1190 invalidateCompletenessCache();
1191 mState.mCachedSamplerFormatValid = false;
1192 onStateChange(angle::SubjectMessage::DirtyBitsFlagged);
1193 }
1194
setImage(Context * context,const PixelUnpackState & unpackState,Buffer * unpackBuffer,TextureTarget target,GLint level,GLenum internalFormat,const Extents & size,GLenum format,GLenum type,const uint8_t * pixels)1195 angle::Result Texture::setImage(Context *context,
1196 const PixelUnpackState &unpackState,
1197 Buffer *unpackBuffer,
1198 TextureTarget target,
1199 GLint level,
1200 GLenum internalFormat,
1201 const Extents &size,
1202 GLenum format,
1203 GLenum type,
1204 const uint8_t *pixels)
1205 {
1206 ASSERT(TextureTargetToType(target) == mState.mType);
1207
1208 // Release from previous calls to eglBindTexImage, to avoid calling the Impl after
1209 ANGLE_TRY(releaseTexImageInternal(context));
1210
1211 egl::RefCountObjectReleaser<egl::Image> releaseImage;
1212 ANGLE_TRY(orphanImages(context, &releaseImage));
1213
1214 ImageIndex index = ImageIndex::MakeFromTarget(target, level, size.depth);
1215
1216 ANGLE_TRY(mTexture->setImage(context, index, internalFormat, size, format, type, unpackState,
1217 unpackBuffer, pixels));
1218
1219 InitState initState = DetermineInitState(context, unpackBuffer, pixels);
1220 mState.setImageDesc(target, level, ImageDesc(size, Format(internalFormat, type), initState));
1221
1222 ANGLE_TRY(handleMipmapGenerationHint(context, level));
1223
1224 signalDirtyStorage(initState);
1225
1226 return angle::Result::Continue;
1227 }
1228
setSubImage(Context * context,const PixelUnpackState & unpackState,Buffer * unpackBuffer,TextureTarget target,GLint level,const Box & area,GLenum format,GLenum type,const uint8_t * pixels)1229 angle::Result Texture::setSubImage(Context *context,
1230 const PixelUnpackState &unpackState,
1231 Buffer *unpackBuffer,
1232 TextureTarget target,
1233 GLint level,
1234 const Box &area,
1235 GLenum format,
1236 GLenum type,
1237 const uint8_t *pixels)
1238 {
1239 ASSERT(TextureTargetToType(target) == mState.mType);
1240
1241 ImageIndex index = ImageIndex::MakeFromTarget(target, level, area.depth);
1242 ANGLE_TRY(ensureSubImageInitialized(context, index, area));
1243
1244 ANGLE_TRY(mTexture->setSubImage(context, index, area, format, type, unpackState, unpackBuffer,
1245 pixels));
1246
1247 ANGLE_TRY(handleMipmapGenerationHint(context, level));
1248
1249 onStateChange(angle::SubjectMessage::ContentsChanged);
1250
1251 return angle::Result::Continue;
1252 }
1253
setCompressedImage(Context * context,const PixelUnpackState & unpackState,TextureTarget target,GLint level,GLenum internalFormat,const Extents & size,size_t imageSize,const uint8_t * pixels)1254 angle::Result Texture::setCompressedImage(Context *context,
1255 const PixelUnpackState &unpackState,
1256 TextureTarget target,
1257 GLint level,
1258 GLenum internalFormat,
1259 const Extents &size,
1260 size_t imageSize,
1261 const uint8_t *pixels)
1262 {
1263 ASSERT(TextureTargetToType(target) == mState.mType);
1264
1265 // Release from previous calls to eglBindTexImage, to avoid calling the Impl after
1266 ANGLE_TRY(releaseTexImageInternal(context));
1267
1268 egl::RefCountObjectReleaser<egl::Image> releaseImage;
1269 ANGLE_TRY(orphanImages(context, &releaseImage));
1270
1271 ImageIndex index = ImageIndex::MakeFromTarget(target, level, size.depth);
1272
1273 ANGLE_TRY(mTexture->setCompressedImage(context, index, internalFormat, size, unpackState,
1274 imageSize, pixels));
1275
1276 Buffer *unpackBuffer = context->getState().getTargetBuffer(BufferBinding::PixelUnpack);
1277
1278 InitState initState = DetermineInitState(context, unpackBuffer, pixels);
1279 mState.setImageDesc(target, level, ImageDesc(size, Format(internalFormat), initState));
1280 signalDirtyStorage(initState);
1281
1282 return angle::Result::Continue;
1283 }
1284
setCompressedSubImage(const Context * context,const PixelUnpackState & unpackState,TextureTarget target,GLint level,const Box & area,GLenum format,size_t imageSize,const uint8_t * pixels)1285 angle::Result Texture::setCompressedSubImage(const Context *context,
1286 const PixelUnpackState &unpackState,
1287 TextureTarget target,
1288 GLint level,
1289 const Box &area,
1290 GLenum format,
1291 size_t imageSize,
1292 const uint8_t *pixels)
1293 {
1294 ASSERT(TextureTargetToType(target) == mState.mType);
1295
1296 ImageIndex index = ImageIndex::MakeFromTarget(target, level, area.depth);
1297 ANGLE_TRY(ensureSubImageInitialized(context, index, area));
1298
1299 ANGLE_TRY(mTexture->setCompressedSubImage(context, index, area, format, unpackState, imageSize,
1300 pixels));
1301
1302 onStateChange(angle::SubjectMessage::ContentsChanged);
1303
1304 return angle::Result::Continue;
1305 }
1306
copyImage(Context * context,TextureTarget target,GLint level,const Rectangle & sourceArea,GLenum internalFormat,Framebuffer * source)1307 angle::Result Texture::copyImage(Context *context,
1308 TextureTarget target,
1309 GLint level,
1310 const Rectangle &sourceArea,
1311 GLenum internalFormat,
1312 Framebuffer *source)
1313 {
1314 ASSERT(TextureTargetToType(target) == mState.mType);
1315
1316 // Release from previous calls to eglBindTexImage, to avoid calling the Impl after
1317 ANGLE_TRY(releaseTexImageInternal(context));
1318
1319 egl::RefCountObjectReleaser<egl::Image> releaseImage;
1320 ANGLE_TRY(orphanImages(context, &releaseImage));
1321
1322 ImageIndex index = ImageIndex::MakeFromTarget(target, level, 1);
1323
1324 const InternalFormat &internalFormatInfo =
1325 GetInternalFormatInfo(internalFormat, GL_UNSIGNED_BYTE);
1326
1327 // Most if not all renderers clip these copies to the size of the source framebuffer, leaving
1328 // other pixels untouched. For safety in robust resource initialization, assume that that
1329 // clipping is going to occur when computing the region for which to ensure initialization. If
1330 // the copy lies entirely off the source framebuffer, initialize as though a zero-size box is
1331 // going to be set during the copy operation.
1332 Box destBox;
1333 bool forceCopySubImage = false;
1334 if (context->isRobustResourceInitEnabled())
1335 {
1336 const FramebufferAttachment *sourceReadAttachment = source->getReadColorAttachment();
1337 Extents fbSize = sourceReadAttachment->getSize();
1338 // Force using copySubImage when the source area is out of bounds AND
1339 // we're not copying to and from the same texture
1340 forceCopySubImage = ((sourceArea.x < 0) || (sourceArea.y < 0) ||
1341 ((sourceArea.x + sourceArea.width) > fbSize.width) ||
1342 ((sourceArea.y + sourceArea.height) > fbSize.height)) &&
1343 (sourceReadAttachment->getResource() != this);
1344 Rectangle clippedArea;
1345 if (ClipRectangle(sourceArea, Rectangle(0, 0, fbSize.width, fbSize.height), &clippedArea))
1346 {
1347 const Offset clippedOffset(clippedArea.x - sourceArea.x, clippedArea.y - sourceArea.y,
1348 0);
1349 destBox = Box(clippedOffset.x, clippedOffset.y, clippedOffset.z, clippedArea.width,
1350 clippedArea.height, 1);
1351 }
1352 }
1353
1354 InitState initState = DetermineInitState(context, nullptr, nullptr);
1355
1356 // If we need to initialize the destination texture we split the call into a create call,
1357 // an initializeContents call, and then a copySubImage call. This ensures the destination
1358 // texture exists before we try to clear it.
1359 Extents size(sourceArea.width, sourceArea.height, 1);
1360 if (forceCopySubImage || doesSubImageNeedInit(context, index, destBox))
1361 {
1362 ANGLE_TRY(mTexture->setImage(context, index, internalFormat, size,
1363 internalFormatInfo.format, internalFormatInfo.type,
1364 PixelUnpackState(), nullptr, nullptr));
1365 mState.setImageDesc(target, level, ImageDesc(size, Format(internalFormatInfo), initState));
1366 ANGLE_TRY(ensureSubImageInitialized(context, index, destBox));
1367 ANGLE_TRY(mTexture->copySubImage(context, index, Offset(), sourceArea, source));
1368 }
1369 else
1370 {
1371 ANGLE_TRY(mTexture->copyImage(context, index, sourceArea, internalFormat, source));
1372 }
1373
1374 mState.setImageDesc(target, level,
1375 ImageDesc(size, Format(internalFormatInfo), InitState::Initialized));
1376
1377 ANGLE_TRY(handleMipmapGenerationHint(context, level));
1378
1379 // Because this could affect the texture storage we might need to init other layers/levels.
1380 signalDirtyStorage(initState);
1381
1382 return angle::Result::Continue;
1383 }
1384
copySubImage(Context * context,const ImageIndex & index,const Offset & destOffset,const Rectangle & sourceArea,Framebuffer * source)1385 angle::Result Texture::copySubImage(Context *context,
1386 const ImageIndex &index,
1387 const Offset &destOffset,
1388 const Rectangle &sourceArea,
1389 Framebuffer *source)
1390 {
1391 ASSERT(TextureTargetToType(index.getTarget()) == mState.mType);
1392
1393 // Most if not all renderers clip these copies to the size of the source framebuffer, leaving
1394 // other pixels untouched. For safety in robust resource initialization, assume that that
1395 // clipping is going to occur when computing the region for which to ensure initialization. If
1396 // the copy lies entirely off the source framebuffer, initialize as though a zero-size box is
1397 // going to be set during the copy operation. Note that this assumes that
1398 // ensureSubImageInitialized ensures initialization of the entire destination texture, and not
1399 // just a sub-region.
1400 Box destBox;
1401 if (context->isRobustResourceInitEnabled())
1402 {
1403 Extents fbSize = source->getReadColorAttachment()->getSize();
1404 Rectangle clippedArea;
1405 if (ClipRectangle(sourceArea, Rectangle(0, 0, fbSize.width, fbSize.height), &clippedArea))
1406 {
1407 const Offset clippedOffset(destOffset.x + clippedArea.x - sourceArea.x,
1408 destOffset.y + clippedArea.y - sourceArea.y, 0);
1409 destBox = Box(clippedOffset.x, clippedOffset.y, clippedOffset.z, clippedArea.width,
1410 clippedArea.height, 1);
1411 }
1412 }
1413
1414 ANGLE_TRY(ensureSubImageInitialized(context, index, destBox));
1415
1416 ANGLE_TRY(mTexture->copySubImage(context, index, destOffset, sourceArea, source));
1417 ANGLE_TRY(handleMipmapGenerationHint(context, index.getLevelIndex()));
1418
1419 onStateChange(angle::SubjectMessage::ContentsChanged);
1420
1421 return angle::Result::Continue;
1422 }
1423
copyRenderbufferSubData(Context * context,const gl::Renderbuffer * srcBuffer,GLint srcLevel,GLint srcX,GLint srcY,GLint srcZ,GLint dstLevel,GLint dstX,GLint dstY,GLint dstZ,GLsizei srcWidth,GLsizei srcHeight,GLsizei srcDepth)1424 angle::Result Texture::copyRenderbufferSubData(Context *context,
1425 const gl::Renderbuffer *srcBuffer,
1426 GLint srcLevel,
1427 GLint srcX,
1428 GLint srcY,
1429 GLint srcZ,
1430 GLint dstLevel,
1431 GLint dstX,
1432 GLint dstY,
1433 GLint dstZ,
1434 GLsizei srcWidth,
1435 GLsizei srcHeight,
1436 GLsizei srcDepth)
1437 {
1438 ANGLE_TRY(mTexture->copyRenderbufferSubData(context, srcBuffer, srcLevel, srcX, srcY, srcZ,
1439 dstLevel, dstX, dstY, dstZ, srcWidth, srcHeight,
1440 srcDepth));
1441
1442 signalDirtyStorage(InitState::Initialized);
1443
1444 return angle::Result::Continue;
1445 }
1446
copyTextureSubData(Context * context,const gl::Texture * srcTexture,GLint srcLevel,GLint srcX,GLint srcY,GLint srcZ,GLint dstLevel,GLint dstX,GLint dstY,GLint dstZ,GLsizei srcWidth,GLsizei srcHeight,GLsizei srcDepth)1447 angle::Result Texture::copyTextureSubData(Context *context,
1448 const gl::Texture *srcTexture,
1449 GLint srcLevel,
1450 GLint srcX,
1451 GLint srcY,
1452 GLint srcZ,
1453 GLint dstLevel,
1454 GLint dstX,
1455 GLint dstY,
1456 GLint dstZ,
1457 GLsizei srcWidth,
1458 GLsizei srcHeight,
1459 GLsizei srcDepth)
1460 {
1461 ANGLE_TRY(mTexture->copyTextureSubData(context, srcTexture, srcLevel, srcX, srcY, srcZ,
1462 dstLevel, dstX, dstY, dstZ, srcWidth, srcHeight,
1463 srcDepth));
1464
1465 signalDirtyStorage(InitState::Initialized);
1466
1467 return angle::Result::Continue;
1468 }
1469
copyTexture(Context * context,TextureTarget target,GLint level,GLenum internalFormat,GLenum type,GLint sourceLevel,bool unpackFlipY,bool unpackPremultiplyAlpha,bool unpackUnmultiplyAlpha,Texture * source)1470 angle::Result Texture::copyTexture(Context *context,
1471 TextureTarget target,
1472 GLint level,
1473 GLenum internalFormat,
1474 GLenum type,
1475 GLint sourceLevel,
1476 bool unpackFlipY,
1477 bool unpackPremultiplyAlpha,
1478 bool unpackUnmultiplyAlpha,
1479 Texture *source)
1480 {
1481 ASSERT(TextureTargetToType(target) == mState.mType);
1482 ASSERT(source->getType() != TextureType::CubeMap);
1483
1484 // Release from previous calls to eglBindTexImage, to avoid calling the Impl after
1485 ANGLE_TRY(releaseTexImageInternal(context));
1486
1487 egl::RefCountObjectReleaser<egl::Image> releaseImage;
1488 ANGLE_TRY(orphanImages(context, &releaseImage));
1489
1490 // Initialize source texture.
1491 // Note: we don't have a way to notify which portions of the image changed currently.
1492 ANGLE_TRY(source->ensureInitialized(context));
1493
1494 ImageIndex index = ImageIndex::MakeFromTarget(target, level, ImageIndex::kEntireLevel);
1495
1496 ANGLE_TRY(mTexture->copyTexture(context, index, internalFormat, type, sourceLevel, unpackFlipY,
1497 unpackPremultiplyAlpha, unpackUnmultiplyAlpha, source));
1498
1499 const auto &sourceDesc =
1500 source->mState.getImageDesc(NonCubeTextureTypeToTarget(source->getType()), sourceLevel);
1501 const InternalFormat &internalFormatInfo = GetInternalFormatInfo(internalFormat, type);
1502 mState.setImageDesc(
1503 target, level,
1504 ImageDesc(sourceDesc.size, Format(internalFormatInfo), InitState::Initialized));
1505
1506 signalDirtyStorage(InitState::Initialized);
1507
1508 return angle::Result::Continue;
1509 }
1510
copySubTexture(const Context * context,TextureTarget target,GLint level,const Offset & destOffset,GLint sourceLevel,const Box & sourceBox,bool unpackFlipY,bool unpackPremultiplyAlpha,bool unpackUnmultiplyAlpha,Texture * source)1511 angle::Result Texture::copySubTexture(const Context *context,
1512 TextureTarget target,
1513 GLint level,
1514 const Offset &destOffset,
1515 GLint sourceLevel,
1516 const Box &sourceBox,
1517 bool unpackFlipY,
1518 bool unpackPremultiplyAlpha,
1519 bool unpackUnmultiplyAlpha,
1520 Texture *source)
1521 {
1522 ASSERT(TextureTargetToType(target) == mState.mType);
1523
1524 // Ensure source is initialized.
1525 ANGLE_TRY(source->ensureInitialized(context));
1526
1527 Box destBox(destOffset.x, destOffset.y, destOffset.z, sourceBox.width, sourceBox.height,
1528 sourceBox.depth);
1529 ImageIndex index = ImageIndex::MakeFromTarget(target, level, sourceBox.depth);
1530 ANGLE_TRY(ensureSubImageInitialized(context, index, destBox));
1531
1532 ANGLE_TRY(mTexture->copySubTexture(context, index, destOffset, sourceLevel, sourceBox,
1533 unpackFlipY, unpackPremultiplyAlpha, unpackUnmultiplyAlpha,
1534 source));
1535
1536 onStateChange(angle::SubjectMessage::ContentsChanged);
1537
1538 return angle::Result::Continue;
1539 }
1540
copyCompressedTexture(Context * context,const Texture * source)1541 angle::Result Texture::copyCompressedTexture(Context *context, const Texture *source)
1542 {
1543 // Release from previous calls to eglBindTexImage, to avoid calling the Impl after
1544 ANGLE_TRY(releaseTexImageInternal(context));
1545
1546 egl::RefCountObjectReleaser<egl::Image> releaseImage;
1547 ANGLE_TRY(orphanImages(context, &releaseImage));
1548
1549 ANGLE_TRY(mTexture->copyCompressedTexture(context, source));
1550
1551 ASSERT(source->getType() != TextureType::CubeMap && getType() != TextureType::CubeMap);
1552 const auto &sourceDesc =
1553 source->mState.getImageDesc(NonCubeTextureTypeToTarget(source->getType()), 0);
1554 mState.setImageDesc(NonCubeTextureTypeToTarget(getType()), 0, sourceDesc);
1555
1556 return angle::Result::Continue;
1557 }
1558
setStorage(Context * context,TextureType type,GLsizei levels,GLenum internalFormat,const Extents & size)1559 angle::Result Texture::setStorage(Context *context,
1560 TextureType type,
1561 GLsizei levels,
1562 GLenum internalFormat,
1563 const Extents &size)
1564 {
1565 ASSERT(type == mState.mType);
1566
1567 // Release from previous calls to eglBindTexImage, to avoid calling the Impl after
1568 ANGLE_TRY(releaseTexImageInternal(context));
1569
1570 egl::RefCountObjectReleaser<egl::Image> releaseImage;
1571 ANGLE_TRY(orphanImages(context, &releaseImage));
1572
1573 mState.mImmutableFormat = true;
1574 mState.mImmutableLevels = static_cast<GLuint>(levels);
1575 mState.clearImageDescs();
1576 InitState initState = DetermineInitState(context, nullptr, nullptr);
1577 mState.setImageDescChain(0, static_cast<GLuint>(levels - 1), size, Format(internalFormat),
1578 initState);
1579
1580 ANGLE_TRY(mTexture->setStorage(context, type, levels, internalFormat, size));
1581
1582 // Changing the texture to immutable can trigger a change in the base and max levels:
1583 // GLES 3.0.4 section 3.8.10 pg 158:
1584 // "For immutable-format textures, levelbase is clamped to the range[0;levels],levelmax is then
1585 // clamped to the range[levelbase;levels].
1586 mDirtyBits.set(DIRTY_BIT_BASE_LEVEL);
1587 mDirtyBits.set(DIRTY_BIT_MAX_LEVEL);
1588
1589 signalDirtyStorage(initState);
1590
1591 return angle::Result::Continue;
1592 }
1593
setImageExternal(Context * context,TextureTarget target,GLint level,GLenum internalFormat,const Extents & size,GLenum format,GLenum type)1594 angle::Result Texture::setImageExternal(Context *context,
1595 TextureTarget target,
1596 GLint level,
1597 GLenum internalFormat,
1598 const Extents &size,
1599 GLenum format,
1600 GLenum type)
1601 {
1602 ASSERT(TextureTargetToType(target) == mState.mType);
1603
1604 // Release from previous calls to eglBindTexImage, to avoid calling the Impl after
1605 ANGLE_TRY(releaseTexImageInternal(context));
1606
1607 egl::RefCountObjectReleaser<egl::Image> releaseImage;
1608 ANGLE_TRY(orphanImages(context, &releaseImage));
1609
1610 ImageIndex index = ImageIndex::MakeFromTarget(target, level, size.depth);
1611
1612 ANGLE_TRY(mTexture->setImageExternal(context, index, internalFormat, size, format, type));
1613
1614 InitState initState = InitState::Initialized;
1615 mState.setImageDesc(target, level, ImageDesc(size, Format(internalFormat, type), initState));
1616
1617 ANGLE_TRY(handleMipmapGenerationHint(context, level));
1618
1619 signalDirtyStorage(initState);
1620
1621 return angle::Result::Continue;
1622 }
1623
setStorageMultisample(Context * context,TextureType type,GLsizei samplesIn,GLint internalFormat,const Extents & size,bool fixedSampleLocations)1624 angle::Result Texture::setStorageMultisample(Context *context,
1625 TextureType type,
1626 GLsizei samplesIn,
1627 GLint internalFormat,
1628 const Extents &size,
1629 bool fixedSampleLocations)
1630 {
1631 ASSERT(type == mState.mType);
1632
1633 // Release from previous calls to eglBindTexImage, to avoid calling the Impl after
1634 ANGLE_TRY(releaseTexImageInternal(context));
1635
1636 egl::RefCountObjectReleaser<egl::Image> releaseImage;
1637 ANGLE_TRY(orphanImages(context, &releaseImage));
1638
1639 // Potentially adjust "samples" to a supported value
1640 const TextureCaps &formatCaps = context->getTextureCaps().get(internalFormat);
1641 GLsizei samples = formatCaps.getNearestSamples(samplesIn);
1642
1643 mState.mImmutableFormat = true;
1644 mState.mImmutableLevels = static_cast<GLuint>(1);
1645 mState.clearImageDescs();
1646 InitState initState = DetermineInitState(context, nullptr, nullptr);
1647 mState.setImageDescChainMultisample(size, Format(internalFormat), samples, fixedSampleLocations,
1648 initState);
1649
1650 ANGLE_TRY(mTexture->setStorageMultisample(context, type, samples, internalFormat, size,
1651 fixedSampleLocations));
1652 signalDirtyStorage(initState);
1653
1654 return angle::Result::Continue;
1655 }
1656
setStorageExternalMemory(Context * context,TextureType type,GLsizei levels,GLenum internalFormat,const Extents & size,MemoryObject * memoryObject,GLuint64 offset,GLbitfield createFlags,GLbitfield usageFlags,const void * imageCreateInfoPNext)1657 angle::Result Texture::setStorageExternalMemory(Context *context,
1658 TextureType type,
1659 GLsizei levels,
1660 GLenum internalFormat,
1661 const Extents &size,
1662 MemoryObject *memoryObject,
1663 GLuint64 offset,
1664 GLbitfield createFlags,
1665 GLbitfield usageFlags,
1666 const void *imageCreateInfoPNext)
1667 {
1668 ASSERT(type == mState.mType);
1669
1670 // Release from previous calls to eglBindTexImage, to avoid calling the Impl after
1671 ANGLE_TRY(releaseTexImageInternal(context));
1672
1673 egl::RefCountObjectReleaser<egl::Image> releaseImage;
1674 ANGLE_TRY(orphanImages(context, &releaseImage));
1675
1676 ANGLE_TRY(mTexture->setStorageExternalMemory(context, type, levels, internalFormat, size,
1677 memoryObject, offset, createFlags, usageFlags,
1678 imageCreateInfoPNext));
1679
1680 mState.mImmutableFormat = true;
1681 mState.mImmutableLevels = static_cast<GLuint>(levels);
1682 mState.clearImageDescs();
1683 InitState initState = DetermineInitState(context, nullptr, nullptr);
1684 mState.setImageDescChain(0, static_cast<GLuint>(levels - 1), size, Format(internalFormat),
1685 initState);
1686
1687 // Changing the texture to immutable can trigger a change in the base and max levels:
1688 // GLES 3.0.4 section 3.8.10 pg 158:
1689 // "For immutable-format textures, levelbase is clamped to the range[0;levels],levelmax is then
1690 // clamped to the range[levelbase;levels].
1691 mDirtyBits.set(DIRTY_BIT_BASE_LEVEL);
1692 mDirtyBits.set(DIRTY_BIT_MAX_LEVEL);
1693
1694 signalDirtyStorage(initState);
1695
1696 return angle::Result::Continue;
1697 }
1698
generateMipmap(Context * context)1699 angle::Result Texture::generateMipmap(Context *context)
1700 {
1701 // Release from previous calls to eglBindTexImage, to avoid calling the Impl after
1702 ANGLE_TRY(releaseTexImageInternal(context));
1703
1704 // EGL_KHR_gl_image states that images are only orphaned when generating mipmaps if the texture
1705 // is not mip complete.
1706 egl::RefCountObjectReleaser<egl::Image> releaseImage;
1707 if (!isMipmapComplete())
1708 {
1709 ANGLE_TRY(orphanImages(context, &releaseImage));
1710 }
1711
1712 const GLuint baseLevel = mState.getEffectiveBaseLevel();
1713 const GLuint maxLevel = mState.getMipmapMaxLevel();
1714
1715 if (maxLevel <= baseLevel)
1716 {
1717 return angle::Result::Continue;
1718 }
1719
1720 // If any dimension is zero, this is a no-op:
1721 //
1722 // > Otherwise, if level_base is not defined, or if any dimension is zero, all mipmap levels are
1723 // > left unchanged. This is not an error.
1724 const ImageDesc &baseImageInfo = mState.getImageDesc(mState.getBaseImageTarget(), baseLevel);
1725 if (baseImageInfo.size.empty())
1726 {
1727 return angle::Result::Continue;
1728 }
1729
1730 // Clear the base image(s) immediately if needed
1731 if (context->isRobustResourceInitEnabled())
1732 {
1733 ImageIndexIterator it =
1734 ImageIndexIterator::MakeGeneric(mState.mType, baseLevel, baseLevel + 1,
1735 ImageIndex::kEntireLevel, ImageIndex::kEntireLevel);
1736 while (it.hasNext())
1737 {
1738 const ImageIndex index = it.next();
1739 const ImageDesc &desc = mState.getImageDesc(index.getTarget(), index.getLevelIndex());
1740
1741 if (desc.initState == InitState::MayNeedInit)
1742 {
1743 ANGLE_TRY(initializeContents(context, index));
1744 }
1745 }
1746 }
1747
1748 ANGLE_TRY(syncState(context, Command::GenerateMipmap));
1749 ANGLE_TRY(mTexture->generateMipmap(context));
1750
1751 // Propagate the format and size of the base mip to the smaller ones. Cube maps are guaranteed
1752 // to have faces of the same size and format so any faces can be picked.
1753 mState.setImageDescChain(baseLevel, maxLevel, baseImageInfo.size, baseImageInfo.format,
1754 InitState::Initialized);
1755
1756 signalDirtyStorage(InitState::Initialized);
1757
1758 return angle::Result::Continue;
1759 }
1760
bindTexImageFromSurface(Context * context,egl::Surface * surface)1761 angle::Result Texture::bindTexImageFromSurface(Context *context, egl::Surface *surface)
1762 {
1763 ASSERT(surface);
1764
1765 if (mBoundSurface)
1766 {
1767 ANGLE_TRY(releaseTexImageFromSurface(context));
1768 }
1769
1770 ANGLE_TRY(mTexture->bindTexImage(context, surface));
1771 mBoundSurface = surface;
1772
1773 // Set the image info to the size and format of the surface
1774 ASSERT(mState.mType == TextureType::_2D || mState.mType == TextureType::Rectangle);
1775 Extents size(surface->getWidth(), surface->getHeight(), 1);
1776 ImageDesc desc(size, surface->getBindTexImageFormat(), InitState::Initialized);
1777 mState.setImageDesc(NonCubeTextureTypeToTarget(mState.mType), 0, desc);
1778 mState.mHasProtectedContent = surface->hasProtectedContent();
1779 signalDirtyStorage(InitState::Initialized);
1780 return angle::Result::Continue;
1781 }
1782
releaseTexImageFromSurface(const Context * context)1783 angle::Result Texture::releaseTexImageFromSurface(const Context *context)
1784 {
1785 ASSERT(mBoundSurface);
1786 mBoundSurface = nullptr;
1787 ANGLE_TRY(mTexture->releaseTexImage(context));
1788
1789 // Erase the image info for level 0
1790 ASSERT(mState.mType == TextureType::_2D || mState.mType == TextureType::Rectangle);
1791 mState.clearImageDesc(NonCubeTextureTypeToTarget(mState.mType), 0);
1792 mState.mHasProtectedContent = false;
1793 signalDirtyStorage(InitState::Initialized);
1794 return angle::Result::Continue;
1795 }
1796
bindStream(egl::Stream * stream)1797 void Texture::bindStream(egl::Stream *stream)
1798 {
1799 ASSERT(stream);
1800
1801 // It should not be possible to bind a texture already bound to another stream
1802 ASSERT(mBoundStream == nullptr);
1803
1804 mBoundStream = stream;
1805
1806 ASSERT(mState.mType == TextureType::External);
1807 }
1808
releaseStream()1809 void Texture::releaseStream()
1810 {
1811 ASSERT(mBoundStream);
1812 mBoundStream = nullptr;
1813 }
1814
acquireImageFromStream(const Context * context,const egl::Stream::GLTextureDescription & desc)1815 angle::Result Texture::acquireImageFromStream(const Context *context,
1816 const egl::Stream::GLTextureDescription &desc)
1817 {
1818 ASSERT(mBoundStream != nullptr);
1819 ANGLE_TRY(mTexture->setImageExternal(context, mState.mType, mBoundStream, desc));
1820
1821 Extents size(desc.width, desc.height, 1);
1822 mState.setImageDesc(NonCubeTextureTypeToTarget(mState.mType), 0,
1823 ImageDesc(size, Format(desc.internalFormat), InitState::Initialized));
1824 signalDirtyStorage(InitState::Initialized);
1825 return angle::Result::Continue;
1826 }
1827
releaseImageFromStream(const Context * context)1828 angle::Result Texture::releaseImageFromStream(const Context *context)
1829 {
1830 ASSERT(mBoundStream != nullptr);
1831 ANGLE_TRY(mTexture->setImageExternal(context, mState.mType, nullptr,
1832 egl::Stream::GLTextureDescription()));
1833
1834 // Set to incomplete
1835 mState.clearImageDesc(NonCubeTextureTypeToTarget(mState.mType), 0);
1836 signalDirtyStorage(InitState::Initialized);
1837 return angle::Result::Continue;
1838 }
1839
releaseTexImageInternal(Context * context)1840 angle::Result Texture::releaseTexImageInternal(Context *context)
1841 {
1842 if (mBoundSurface)
1843 {
1844 // Notify the surface
1845 egl::Error eglErr = mBoundSurface->releaseTexImageFromTexture(context);
1846 // TODO(jmadill): Remove this once refactor is complete. http://anglebug.com/3041
1847 if (eglErr.isError())
1848 {
1849 context->handleError(GL_INVALID_OPERATION, "Error releasing tex image from texture",
1850 __FILE__, ANGLE_FUNCTION, __LINE__);
1851 }
1852
1853 // Then, call the same method as from the surface
1854 ANGLE_TRY(releaseTexImageFromSurface(context));
1855 }
1856 return angle::Result::Continue;
1857 }
1858
setEGLImageTarget(Context * context,TextureType type,egl::Image * imageTarget)1859 angle::Result Texture::setEGLImageTarget(Context *context,
1860 TextureType type,
1861 egl::Image *imageTarget)
1862 {
1863 ASSERT(type == mState.mType);
1864 ASSERT(type == TextureType::_2D || type == TextureType::External ||
1865 type == TextureType::_2DArray);
1866
1867 // Release from previous calls to eglBindTexImage, to avoid calling the Impl after
1868 ANGLE_TRY(releaseTexImageInternal(context));
1869
1870 egl::RefCountObjectReleaser<egl::Image> releaseImage;
1871 ANGLE_TRY(orphanImages(context, &releaseImage));
1872
1873 ANGLE_TRY(mTexture->setEGLImageTarget(context, type, imageTarget));
1874
1875 setTargetImage(context, imageTarget);
1876
1877 Extents size(static_cast<int>(imageTarget->getWidth()),
1878 static_cast<int>(imageTarget->getHeight()), 1);
1879
1880 auto initState = imageTarget->sourceInitState();
1881
1882 mState.clearImageDescs();
1883 mState.setImageDesc(NonCubeTextureTypeToTarget(type), 0,
1884 ImageDesc(size, imageTarget->getFormat(), initState));
1885 mState.mHasProtectedContent = imageTarget->hasProtectedContent();
1886 signalDirtyStorage(initState);
1887
1888 return angle::Result::Continue;
1889 }
1890
getAttachmentSize(const ImageIndex & imageIndex) const1891 Extents Texture::getAttachmentSize(const ImageIndex &imageIndex) const
1892 {
1893 // As an ImageIndex that represents an entire level of a cube map corresponds to 6 ImageDescs,
1894 // we only allow querying ImageDesc on a complete cube map, and this ImageDesc is exactly the
1895 // one that belongs to the first face of the cube map.
1896 if (imageIndex.isEntireLevelCubeMap())
1897 {
1898 // A cube map texture is cube complete if the following conditions all hold true:
1899 // - The levelbase arrays of each of the six texture images making up the cube map have
1900 // identical, positive, and square dimensions.
1901 if (!mState.isCubeComplete())
1902 {
1903 return Extents();
1904 }
1905 }
1906
1907 return mState.getImageDesc(imageIndex).size;
1908 }
1909
getAttachmentFormat(GLenum,const ImageIndex & imageIndex) const1910 Format Texture::getAttachmentFormat(GLenum /*binding*/, const ImageIndex &imageIndex) const
1911 {
1912 // As an ImageIndex that represents an entire level of a cube map corresponds to 6 ImageDescs,
1913 // we only allow querying ImageDesc on a complete cube map, and this ImageDesc is exactly the
1914 // one that belongs to the first face of the cube map.
1915 if (imageIndex.isEntireLevelCubeMap())
1916 {
1917 // A cube map texture is cube complete if the following conditions all hold true:
1918 // - The levelbase arrays were each specified with the same effective internal format.
1919 if (!mState.isCubeComplete())
1920 {
1921 return Format::Invalid();
1922 }
1923 }
1924 return mState.getImageDesc(imageIndex).format;
1925 }
1926
getAttachmentSamples(const ImageIndex & imageIndex) const1927 GLsizei Texture::getAttachmentSamples(const ImageIndex &imageIndex) const
1928 {
1929 // We do not allow querying TextureTarget by an ImageIndex that represents an entire level of a
1930 // cube map (See comments in function TextureTypeToTarget() in ImageIndex.cpp).
1931 if (imageIndex.isEntireLevelCubeMap())
1932 {
1933 return 0;
1934 }
1935
1936 return getSamples(imageIndex.getTarget(), imageIndex.getLevelIndex());
1937 }
1938
isRenderable(const Context * context,GLenum binding,const ImageIndex & imageIndex) const1939 bool Texture::isRenderable(const Context *context,
1940 GLenum binding,
1941 const ImageIndex &imageIndex) const
1942 {
1943 if (isEGLImageTarget())
1944 {
1945 return ImageSibling::isRenderable(context, binding, imageIndex);
1946 }
1947
1948 // Surfaces bound to textures are always renderable. This avoids issues with surfaces with ES3+
1949 // formats not being renderable when bound to textures in ES2 contexts.
1950 if (mBoundSurface)
1951 {
1952 return true;
1953 }
1954
1955 return getAttachmentFormat(binding, imageIndex)
1956 .info->textureAttachmentSupport(context->getClientVersion(), context->getExtensions());
1957 }
1958
getAttachmentFixedSampleLocations(const ImageIndex & imageIndex) const1959 bool Texture::getAttachmentFixedSampleLocations(const ImageIndex &imageIndex) const
1960 {
1961 // We do not allow querying TextureTarget by an ImageIndex that represents an entire level of a
1962 // cube map (See comments in function TextureTypeToTarget() in ImageIndex.cpp).
1963 if (imageIndex.isEntireLevelCubeMap())
1964 {
1965 return true;
1966 }
1967
1968 // ES3.1 (section 9.4) requires that the value of TEXTURE_FIXED_SAMPLE_LOCATIONS should be
1969 // the same for all attached textures.
1970 return getFixedSampleLocations(imageIndex.getTarget(), imageIndex.getLevelIndex());
1971 }
1972
setBorderColor(const Context * context,const ColorGeneric & color)1973 void Texture::setBorderColor(const Context *context, const ColorGeneric &color)
1974 {
1975 mState.mSamplerState.setBorderColor(color);
1976 signalDirtyState(DIRTY_BIT_BORDER_COLOR);
1977 }
1978
getBorderColor() const1979 const ColorGeneric &Texture::getBorderColor() const
1980 {
1981 return mState.mSamplerState.getBorderColor();
1982 }
1983
getRequiredTextureImageUnits(const Context * context) const1984 GLint Texture::getRequiredTextureImageUnits(const Context *context) const
1985 {
1986 // Only external texture types can return non-1.
1987 if (mState.mType != TextureType::External)
1988 {
1989 return 1;
1990 }
1991
1992 return mTexture->getRequiredExternalTextureImageUnits(context);
1993 }
1994
setCrop(const Rectangle & rect)1995 void Texture::setCrop(const Rectangle &rect)
1996 {
1997 mState.setCrop(rect);
1998 }
1999
getCrop() const2000 const Rectangle &Texture::getCrop() const
2001 {
2002 return mState.getCrop();
2003 }
2004
setGenerateMipmapHint(GLenum hint)2005 void Texture::setGenerateMipmapHint(GLenum hint)
2006 {
2007 mState.setGenerateMipmapHint(hint);
2008 }
2009
getGenerateMipmapHint() const2010 GLenum Texture::getGenerateMipmapHint() const
2011 {
2012 return mState.getGenerateMipmapHint();
2013 }
2014
setBuffer(const gl::Context * context,gl::Buffer * buffer,GLenum internalFormat)2015 angle::Result Texture::setBuffer(const gl::Context *context,
2016 gl::Buffer *buffer,
2017 GLenum internalFormat)
2018 {
2019 // Use 0 to indicate that the size is taken from whatever size the buffer has when the texture
2020 // buffer is used.
2021 return setBufferRange(context, buffer, internalFormat, 0, 0);
2022 }
2023
setBufferRange(const gl::Context * context,gl::Buffer * buffer,GLenum internalFormat,GLintptr offset,GLsizeiptr size)2024 angle::Result Texture::setBufferRange(const gl::Context *context,
2025 gl::Buffer *buffer,
2026 GLenum internalFormat,
2027 GLintptr offset,
2028 GLsizeiptr size)
2029 {
2030 mState.mImmutableFormat = true;
2031 mState.mBuffer.set(context, buffer, offset, size);
2032 ANGLE_TRY(mTexture->setBuffer(context, internalFormat));
2033
2034 mState.clearImageDescs();
2035 if (buffer == nullptr)
2036 {
2037 mBufferObserver.reset();
2038 InitState initState = DetermineInitState(context, nullptr, nullptr);
2039 signalDirtyStorage(initState);
2040 return angle::Result::Continue;
2041 }
2042
2043 size = GetBoundBufferAvailableSize(mState.mBuffer);
2044
2045 mState.mImmutableLevels = static_cast<GLuint>(1);
2046 InternalFormat internalFormatInfo = GetSizedInternalFormatInfo(internalFormat);
2047 Format format(internalFormat);
2048 Extents extents(static_cast<GLuint>(size / internalFormatInfo.pixelBytes), 1, 1);
2049 InitState initState = buffer->initState();
2050 mState.setImageDesc(TextureTarget::Buffer, 0, ImageDesc(extents, format, initState));
2051
2052 signalDirtyStorage(initState);
2053
2054 // Observe modifications to the buffer, so that extents can be updated.
2055 mBufferObserver.bind(buffer);
2056
2057 return angle::Result::Continue;
2058 }
2059
getBuffer() const2060 const OffsetBindingPointer<Buffer> &Texture::getBuffer() const
2061 {
2062 return mState.mBuffer;
2063 }
2064
onAttach(const Context * context,rx::Serial framebufferSerial)2065 void Texture::onAttach(const Context *context, rx::Serial framebufferSerial)
2066 {
2067 addRef();
2068
2069 // Duplicates allowed for multiple attachment points. See the comment in the header.
2070 mBoundFramebufferSerials.push_back(framebufferSerial);
2071
2072 if (!mState.mHasBeenBoundAsAttachment)
2073 {
2074 mDirtyBits.set(DIRTY_BIT_BOUND_AS_ATTACHMENT);
2075 mState.mHasBeenBoundAsAttachment = true;
2076 }
2077 }
2078
onDetach(const Context * context,rx::Serial framebufferSerial)2079 void Texture::onDetach(const Context *context, rx::Serial framebufferSerial)
2080 {
2081 // Erase first instance. If there are multiple bindings, leave the others.
2082 ASSERT(isBoundToFramebuffer(framebufferSerial));
2083 mBoundFramebufferSerials.remove_and_permute(framebufferSerial);
2084
2085 release(context);
2086 }
2087
getId() const2088 GLuint Texture::getId() const
2089 {
2090 return id().value;
2091 }
2092
getNativeID() const2093 GLuint Texture::getNativeID() const
2094 {
2095 return mTexture->getNativeID();
2096 }
2097
syncState(const Context * context,Command source)2098 angle::Result Texture::syncState(const Context *context, Command source)
2099 {
2100 ASSERT(hasAnyDirtyBit() || source == Command::GenerateMipmap);
2101 ANGLE_TRY(mTexture->syncState(context, mDirtyBits, source));
2102 mDirtyBits.reset();
2103 mState.mInitState = InitState::Initialized;
2104 return angle::Result::Continue;
2105 }
2106
getAttachmentImpl() const2107 rx::FramebufferAttachmentObjectImpl *Texture::getAttachmentImpl() const
2108 {
2109 return mTexture;
2110 }
2111
isSamplerComplete(const Context * context,const Sampler * optionalSampler)2112 bool Texture::isSamplerComplete(const Context *context, const Sampler *optionalSampler)
2113 {
2114 const auto &samplerState =
2115 optionalSampler ? optionalSampler->getSamplerState() : mState.mSamplerState;
2116 const auto &contextState = context->getState();
2117
2118 if (contextState.getContextID() != mCompletenessCache.context ||
2119 !mCompletenessCache.samplerState.sameCompleteness(samplerState))
2120 {
2121 mCompletenessCache.context = context->getState().getContextID();
2122 mCompletenessCache.samplerState = samplerState;
2123 mCompletenessCache.samplerComplete =
2124 mState.computeSamplerCompleteness(samplerState, contextState);
2125 }
2126
2127 return mCompletenessCache.samplerComplete;
2128 }
2129
SamplerCompletenessCache()2130 Texture::SamplerCompletenessCache::SamplerCompletenessCache()
2131 : context({0}), samplerState(), samplerComplete(false)
2132 {}
2133
invalidateCompletenessCache() const2134 void Texture::invalidateCompletenessCache() const
2135 {
2136 mCompletenessCache.context = {0};
2137 }
2138
ensureInitialized(const Context * context)2139 angle::Result Texture::ensureInitialized(const Context *context)
2140 {
2141 if (!context->isRobustResourceInitEnabled() || mState.mInitState == InitState::Initialized)
2142 {
2143 return angle::Result::Continue;
2144 }
2145
2146 bool anyDirty = false;
2147
2148 ImageIndexIterator it =
2149 ImageIndexIterator::MakeGeneric(mState.mType, 0, IMPLEMENTATION_MAX_TEXTURE_LEVELS + 1,
2150 ImageIndex::kEntireLevel, ImageIndex::kEntireLevel);
2151 while (it.hasNext())
2152 {
2153 const ImageIndex index = it.next();
2154 ImageDesc &desc =
2155 mState.mImageDescs[GetImageDescIndex(index.getTarget(), index.getLevelIndex())];
2156 if (desc.initState == InitState::MayNeedInit && !desc.size.empty())
2157 {
2158 ASSERT(mState.mInitState == InitState::MayNeedInit);
2159 ANGLE_TRY(initializeContents(context, index));
2160 desc.initState = InitState::Initialized;
2161 anyDirty = true;
2162 }
2163 }
2164 if (anyDirty)
2165 {
2166 signalDirtyStorage(InitState::Initialized);
2167 }
2168 mState.mInitState = InitState::Initialized;
2169
2170 return angle::Result::Continue;
2171 }
2172
initState(const ImageIndex & imageIndex) const2173 InitState Texture::initState(const ImageIndex &imageIndex) const
2174 {
2175 // As an ImageIndex that represents an entire level of a cube map corresponds to 6 ImageDescs,
2176 // we need to check all the related ImageDescs.
2177 if (imageIndex.isEntireLevelCubeMap())
2178 {
2179 const GLint levelIndex = imageIndex.getLevelIndex();
2180 for (TextureTarget cubeFaceTarget : AllCubeFaceTextureTargets())
2181 {
2182 if (mState.getImageDesc(cubeFaceTarget, levelIndex).initState == InitState::MayNeedInit)
2183 {
2184 return InitState::MayNeedInit;
2185 }
2186 }
2187 return InitState::Initialized;
2188 }
2189
2190 return mState.getImageDesc(imageIndex).initState;
2191 }
2192
setInitState(const ImageIndex & imageIndex,InitState initState)2193 void Texture::setInitState(const ImageIndex &imageIndex, InitState initState)
2194 {
2195 // As an ImageIndex that represents an entire level of a cube map corresponds to 6 ImageDescs,
2196 // we need to update all the related ImageDescs.
2197 if (imageIndex.isEntireLevelCubeMap())
2198 {
2199 const GLint levelIndex = imageIndex.getLevelIndex();
2200 for (TextureTarget cubeFaceTarget : AllCubeFaceTextureTargets())
2201 {
2202 setInitState(ImageIndex::MakeCubeMapFace(cubeFaceTarget, levelIndex), initState);
2203 }
2204 }
2205 else
2206 {
2207 ImageDesc newDesc = mState.getImageDesc(imageIndex);
2208 newDesc.initState = initState;
2209 mState.setImageDesc(imageIndex.getTarget(), imageIndex.getLevelIndex(), newDesc);
2210 }
2211 }
2212
setInitState(InitState initState)2213 void Texture::setInitState(InitState initState)
2214 {
2215 for (ImageDesc &imageDesc : mState.mImageDescs)
2216 {
2217 // Only modify defined images, undefined images will remain in the initialized state
2218 if (!imageDesc.size.empty())
2219 {
2220 imageDesc.initState = initState;
2221 }
2222 }
2223 mState.mInitState = initState;
2224 }
2225
doesSubImageNeedInit(const Context * context,const ImageIndex & imageIndex,const Box & area) const2226 bool Texture::doesSubImageNeedInit(const Context *context,
2227 const ImageIndex &imageIndex,
2228 const Box &area) const
2229 {
2230 if (!context->isRobustResourceInitEnabled() || mState.mInitState == InitState::Initialized)
2231 {
2232 return false;
2233 }
2234
2235 // Pre-initialize the texture contents if necessary.
2236 const ImageDesc &desc = mState.getImageDesc(imageIndex);
2237 if (desc.initState != InitState::MayNeedInit)
2238 {
2239 return false;
2240 }
2241
2242 ASSERT(mState.mInitState == InitState::MayNeedInit);
2243 return !area.coversSameExtent(desc.size);
2244 }
2245
ensureSubImageInitialized(const Context * context,const ImageIndex & imageIndex,const Box & area)2246 angle::Result Texture::ensureSubImageInitialized(const Context *context,
2247 const ImageIndex &imageIndex,
2248 const Box &area)
2249 {
2250 if (doesSubImageNeedInit(context, imageIndex, area))
2251 {
2252 // NOTE: do not optimize this to only initialize the passed area of the texture, or the
2253 // initialization logic in copySubImage will be incorrect.
2254 ANGLE_TRY(initializeContents(context, imageIndex));
2255 }
2256 setInitState(imageIndex, InitState::Initialized);
2257 return angle::Result::Continue;
2258 }
2259
handleMipmapGenerationHint(Context * context,int level)2260 angle::Result Texture::handleMipmapGenerationHint(Context *context, int level)
2261 {
2262 if (getGenerateMipmapHint() == GL_TRUE && level == 0)
2263 {
2264 ANGLE_TRY(generateMipmap(context));
2265 }
2266
2267 return angle::Result::Continue;
2268 }
2269
onSubjectStateChange(angle::SubjectIndex index,angle::SubjectMessage message)2270 void Texture::onSubjectStateChange(angle::SubjectIndex index, angle::SubjectMessage message)
2271 {
2272 switch (message)
2273 {
2274 case angle::SubjectMessage::ContentsChanged:
2275 if (index != kBufferSubjectIndex)
2276 {
2277 // ContentsChange originates from TextureStorage11::resolveAndReleaseTexture
2278 // which resolves the underlying multisampled texture if it exists and so
2279 // Texture will signal dirty storage to invalidate its own cache and the
2280 // attached framebuffer's cache.
2281 signalDirtyStorage(InitState::Initialized);
2282 }
2283 break;
2284 case angle::SubjectMessage::DirtyBitsFlagged:
2285 signalDirtyState(DIRTY_BIT_IMPLEMENTATION);
2286
2287 // Notify siblings that we are dirty.
2288 if (index == rx::kTextureImageImplObserverMessageIndex)
2289 {
2290 notifySiblings(message);
2291 }
2292 break;
2293 case angle::SubjectMessage::SubjectChanged:
2294 mState.mInitState = InitState::MayNeedInit;
2295 signalDirtyState(DIRTY_BIT_IMPLEMENTATION);
2296 onStateChange(angle::SubjectMessage::ContentsChanged);
2297
2298 // Notify siblings that we are dirty.
2299 if (index == rx::kTextureImageImplObserverMessageIndex)
2300 {
2301 notifySiblings(message);
2302 }
2303 else if (index == kBufferSubjectIndex)
2304 {
2305 const gl::Buffer *buffer = mState.mBuffer.get();
2306 ASSERT(buffer != nullptr);
2307
2308 // Update cached image desc based on buffer size.
2309 GLsizeiptr size = GetBoundBufferAvailableSize(mState.mBuffer);
2310
2311 ImageDesc desc = mState.getImageDesc(TextureTarget::Buffer, 0);
2312 const GLuint pixelBytes = desc.format.info->pixelBytes;
2313 desc.size.width = static_cast<GLuint>(size / pixelBytes);
2314
2315 mState.setImageDesc(TextureTarget::Buffer, 0, desc);
2316 }
2317 break;
2318 case angle::SubjectMessage::StorageReleased:
2319 // When the TextureStorage is released, it needs to update the
2320 // RenderTargetCache of the Framebuffer attaching this Texture.
2321 // This is currently only for D3D back-end. See http://crbug.com/1234829
2322 if (index == rx::kTextureImageImplObserverMessageIndex)
2323 {
2324 onStateChange(angle::SubjectMessage::StorageReleased);
2325 }
2326 break;
2327 case angle::SubjectMessage::SubjectMapped:
2328 case angle::SubjectMessage::SubjectUnmapped:
2329 case angle::SubjectMessage::BindingChanged:
2330 ASSERT(index == kBufferSubjectIndex);
2331 break;
2332 case angle::SubjectMessage::InitializationComplete:
2333 ASSERT(index == rx::kTextureImageImplObserverMessageIndex);
2334 setInitState(InitState::Initialized);
2335 break;
2336 case angle::SubjectMessage::InternalMemoryAllocationChanged:
2337 // Need to mark the texture dirty to give the back end a chance to handle the new
2338 // buffer. For example, the Vulkan back end needs to create a new buffer view that
2339 // points to the newly allocated buffer and update the texture descriptor set.
2340 signalDirtyState(DIRTY_BIT_IMPLEMENTATION);
2341 break;
2342 default:
2343 UNREACHABLE();
2344 break;
2345 }
2346 }
2347
getImplementationColorReadFormat(const Context * context) const2348 GLenum Texture::getImplementationColorReadFormat(const Context *context) const
2349 {
2350 return mTexture->getColorReadFormat(context);
2351 }
2352
getImplementationColorReadType(const Context * context) const2353 GLenum Texture::getImplementationColorReadType(const Context *context) const
2354 {
2355 return mTexture->getColorReadType(context);
2356 }
2357
getTexImage(const Context * context,const PixelPackState & packState,Buffer * packBuffer,TextureTarget target,GLint level,GLenum format,GLenum type,void * pixels)2358 angle::Result Texture::getTexImage(const Context *context,
2359 const PixelPackState &packState,
2360 Buffer *packBuffer,
2361 TextureTarget target,
2362 GLint level,
2363 GLenum format,
2364 GLenum type,
2365 void *pixels)
2366 {
2367 // No-op if the image level is empty.
2368 if (getExtents(target, level).empty())
2369 {
2370 return angle::Result::Continue;
2371 }
2372
2373 return mTexture->getTexImage(context, packState, packBuffer, target, level, format, type,
2374 pixels);
2375 }
2376
getCompressedTexImage(const Context * context,const PixelPackState & packState,Buffer * packBuffer,TextureTarget target,GLint level,void * pixels)2377 angle::Result Texture::getCompressedTexImage(const Context *context,
2378 const PixelPackState &packState,
2379 Buffer *packBuffer,
2380 TextureTarget target,
2381 GLint level,
2382 void *pixels)
2383 {
2384 // No-op if the image level is empty.
2385 if (getExtents(target, level).empty())
2386 {
2387 return angle::Result::Continue;
2388 }
2389
2390 return mTexture->getCompressedTexImage(context, packState, packBuffer, target, level, pixels);
2391 }
2392
onBindAsImageTexture()2393 void Texture::onBindAsImageTexture()
2394 {
2395 if (!mState.mHasBeenBoundAsImage)
2396 {
2397 mDirtyBits.set(DIRTY_BIT_BOUND_AS_IMAGE);
2398 mState.mHasBeenBoundAsImage = true;
2399 }
2400 }
2401
2402 } // namespace gl
2403