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

source code of flutter_engine/third_party/angle/src/libANGLE/Texture.cpp