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 | |
23 | namespace gl |
24 | { |
25 | |
26 | namespace |
27 | { |
28 | constexpr angle::SubjectIndex kBufferSubjectIndex = 2; |
29 | static_assert(kBufferSubjectIndex != rx::kTextureImageImplObserverMessageIndex, "Index collision" ); |
30 | static_assert(kBufferSubjectIndex != rx::kTextureImageSiblingMessageIndex, "Index collision" ); |
31 | |
32 | bool 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 | |
39 | size_t GetImageDescIndex(TextureTarget target, size_t level) |
40 | { |
41 | return IsCubeMapFaceTarget(target) ? (level * 6 + CubeMapTextureTargetToFaceIndex(target)) |
42 | : level; |
43 | } |
44 | |
45 | InitState 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 | |
57 | GLenum 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 | |
72 | GLenum 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 | |
85 | bool IsMipmapSupported(const TextureType &type) |
86 | { |
87 | if (type == TextureType::_2DMultisample || type == TextureType::Buffer) |
88 | { |
89 | return false; |
90 | } |
91 | return true; |
92 | } |
93 | |
94 | SwizzleState::SwizzleState() |
95 | : swizzleRed(GL_RED), swizzleGreen(GL_GREEN), swizzleBlue(GL_BLUE), swizzleAlpha(GL_ALPHA) |
96 | {} |
97 | |
98 | SwizzleState::SwizzleState(GLenum red, GLenum green, GLenum blue, GLenum alpha) |
99 | : swizzleRed(red), swizzleGreen(green), swizzleBlue(blue), swizzleAlpha(alpha) |
100 | {} |
101 | |
102 | bool SwizzleState::swizzleRequired() const |
103 | { |
104 | return swizzleRed != GL_RED || swizzleGreen != GL_GREEN || swizzleBlue != GL_BLUE || |
105 | swizzleAlpha != GL_ALPHA; |
106 | } |
107 | |
108 | bool SwizzleState::operator==(const SwizzleState &other) const |
109 | { |
110 | return swizzleRed == other.swizzleRed && swizzleGreen == other.swizzleGreen && |
111 | swizzleBlue == other.swizzleBlue && swizzleAlpha == other.swizzleAlpha; |
112 | } |
113 | |
114 | bool SwizzleState::operator!=(const SwizzleState &other) const |
115 | { |
116 | return !(*this == other); |
117 | } |
118 | |
119 | TextureState::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 | |
142 | TextureState::~TextureState() {} |
143 | |
144 | bool TextureState::swizzleRequired() const |
145 | { |
146 | return mSwizzleState.swizzleRequired(); |
147 | } |
148 | |
149 | GLuint 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 | |
163 | GLuint 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 | |
175 | GLuint 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 | |
194 | bool TextureState::setBaseLevel(GLuint baseLevel) |
195 | { |
196 | if (mBaseLevel != baseLevel) |
197 | { |
198 | mBaseLevel = baseLevel; |
199 | return true; |
200 | } |
201 | return false; |
202 | } |
203 | |
204 | bool 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. |
219 | bool 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 | |
246 | const ImageDesc &TextureState::getBaseLevelDesc() const |
247 | { |
248 | ASSERT(mType != TextureType::CubeMap || isCubeComplete()); |
249 | return getImageDesc(target: getBaseImageTarget(), level: getEffectiveBaseLevel()); |
250 | } |
251 | |
252 | const ImageDesc &TextureState::getLevelZeroDesc() const |
253 | { |
254 | ASSERT(mType != TextureType::CubeMap || isCubeComplete()); |
255 | return getImageDesc(target: getBaseImageTarget(), level: 0); |
256 | } |
257 | |
258 | void TextureState::setCrop(const Rectangle &rect) |
259 | { |
260 | mCropRect = rect; |
261 | } |
262 | |
263 | const Rectangle &TextureState::getCrop() const |
264 | { |
265 | return mCropRect; |
266 | } |
267 | |
268 | void TextureState::setGenerateMipmapHint(GLenum hint) |
269 | { |
270 | mGenerateMipmapHint = hint; |
271 | } |
272 | |
273 | GLenum TextureState::getGenerateMipmapHint() const |
274 | { |
275 | return mGenerateMipmapHint; |
276 | } |
277 | |
278 | SamplerFormat 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 | |
311 | bool 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 |
390 | bool 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 | |
483 | bool 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 | |
511 | bool 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 | |
569 | TextureTarget TextureState::getBaseImageTarget() const |
570 | { |
571 | return mType == TextureType::CubeMap ? kCubeMapTextureTargetMin |
572 | : NonCubeTextureTypeToTarget(type: mType); |
573 | } |
574 | |
575 | GLuint 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 | |
617 | ImageDesc::ImageDesc() |
618 | : ImageDesc(Extents(0, 0, 0), Format::Invalid(), 0, GL_TRUE, InitState::Initialized) |
619 | {} |
620 | |
621 | ImageDesc::ImageDesc(const Extents &size, const Format &format, const InitState initState) |
622 | : size(size), format(format), samples(0), fixedSampleLocations(GL_TRUE), initState(initState) |
623 | {} |
624 | |
625 | ImageDesc::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 | |
637 | GLint 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 | |
649 | const 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 | |
656 | void 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. |
691 | const 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 | |
703 | void 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 | |
733 | void 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 | |
744 | void TextureState::clearImageDesc(TextureTarget target, size_t level) |
745 | { |
746 | setImageDesc(target, level, desc: ImageDesc()); |
747 | } |
748 | |
749 | void TextureState::clearImageDescs() |
750 | { |
751 | for (size_t descIndex = 0; descIndex < mImageDescs.size(); descIndex++) |
752 | { |
753 | mImageDescs[descIndex] = ImageDesc(); |
754 | } |
755 | } |
756 | |
757 | TextureBufferContentsObservers::TextureBufferContentsObservers(Texture *texture) : mTexture(texture) |
758 | {} |
759 | |
760 | void TextureBufferContentsObservers::enableForBuffer(Buffer *buffer) |
761 | { |
762 | buffer->addContentsObserver(texture: mTexture); |
763 | } |
764 | |
765 | void TextureBufferContentsObservers::disableForBuffer(Buffer *buffer) |
766 | { |
767 | buffer->removeContentsObserver(texture: mTexture); |
768 | } |
769 | |
770 | Texture::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 | |
790 | void 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 | |
816 | Texture::~Texture() |
817 | { |
818 | SafeDelete(resource&: mTexture); |
819 | } |
820 | |
821 | angle::Result Texture::setLabel(const Context *context, const std::string &label) |
822 | { |
823 | mState.mLabel = label; |
824 | return mTexture->onLabelUpdate(context); |
825 | } |
826 | |
827 | const std::string &Texture::getLabel() const |
828 | { |
829 | return mState.mLabel; |
830 | } |
831 | |
832 | void 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 | |
841 | GLenum Texture::getSwizzleRed() const |
842 | { |
843 | return mState.mSwizzleState.swizzleRed; |
844 | } |
845 | |
846 | void 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 | |
855 | GLenum Texture::getSwizzleGreen() const |
856 | { |
857 | return mState.mSwizzleState.swizzleGreen; |
858 | } |
859 | |
860 | void 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 | |
869 | GLenum Texture::getSwizzleBlue() const |
870 | { |
871 | return mState.mSwizzleState.swizzleBlue; |
872 | } |
873 | |
874 | void 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 | |
883 | GLenum Texture::getSwizzleAlpha() const |
884 | { |
885 | return mState.mSwizzleState.swizzleAlpha; |
886 | } |
887 | |
888 | void 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 | |
896 | GLenum Texture::getMinFilter() const |
897 | { |
898 | return mState.mSamplerState.getMinFilter(); |
899 | } |
900 | |
901 | void 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 | |
909 | GLenum Texture::getMagFilter() const |
910 | { |
911 | return mState.mSamplerState.getMagFilter(); |
912 | } |
913 | |
914 | void 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 | |
922 | GLenum Texture::getWrapS() const |
923 | { |
924 | return mState.mSamplerState.getWrapS(); |
925 | } |
926 | |
927 | void 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 | |
937 | GLenum Texture::getWrapT() const |
938 | { |
939 | return mState.mSamplerState.getWrapT(); |
940 | } |
941 | |
942 | void 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 | |
950 | GLenum Texture::getWrapR() const |
951 | { |
952 | return mState.mSamplerState.getWrapR(); |
953 | } |
954 | |
955 | void 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 | |
963 | float Texture::getMaxAnisotropy() const |
964 | { |
965 | return mState.mSamplerState.getMaxAnisotropy(); |
966 | } |
967 | |
968 | void 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 | |
976 | GLfloat Texture::getMinLod() const |
977 | { |
978 | return mState.mSamplerState.getMinLod(); |
979 | } |
980 | |
981 | void 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 | |
989 | GLfloat Texture::getMaxLod() const |
990 | { |
991 | return mState.mSamplerState.getMaxLod(); |
992 | } |
993 | |
994 | void 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 | |
1002 | GLenum Texture::getCompareMode() const |
1003 | { |
1004 | return mState.mSamplerState.getCompareMode(); |
1005 | } |
1006 | |
1007 | void 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 | |
1015 | GLenum Texture::getCompareFunc() const |
1016 | { |
1017 | return mState.mSamplerState.getCompareFunc(); |
1018 | } |
1019 | |
1020 | void 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 | |
1028 | GLenum Texture::getSRGBDecode() const |
1029 | { |
1030 | return mState.mSamplerState.getSRGBDecode(); |
1031 | } |
1032 | |
1033 | void 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 | |
1043 | GLenum Texture::getSRGBOverride() const |
1044 | { |
1045 | return (mState.mSrgbOverride == SrgbOverride::SRGB) ? GL_SRGB : GL_NONE; |
1046 | } |
1047 | |
1048 | const SamplerState &Texture::getSamplerState() const |
1049 | { |
1050 | return mState.mSamplerState; |
1051 | } |
1052 | |
1053 | angle::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 | |
1064 | GLuint Texture::getBaseLevel() const |
1065 | { |
1066 | return mState.mBaseLevel; |
1067 | } |
1068 | |
1069 | void Texture::setMaxLevel(const Context *context, GLuint maxLevel) |
1070 | { |
1071 | if (mState.setMaxLevel(maxLevel)) |
1072 | { |
1073 | signalDirtyState(dirtyBit: DIRTY_BIT_MAX_LEVEL); |
1074 | } |
1075 | } |
1076 | |
1077 | GLuint Texture::getMaxLevel() const |
1078 | { |
1079 | return mState.mMaxLevel; |
1080 | } |
1081 | |
1082 | void 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 | |
1091 | GLenum Texture::getDepthStencilTextureMode() const |
1092 | { |
1093 | return mState.mDepthStencilTextureMode; |
1094 | } |
1095 | |
1096 | bool Texture::getImmutableFormat() const |
1097 | { |
1098 | return mState.mImmutableFormat; |
1099 | } |
1100 | |
1101 | GLuint Texture::getImmutableLevels() const |
1102 | { |
1103 | return mState.mImmutableLevels; |
1104 | } |
1105 | |
1106 | void Texture::setUsage(const Context *context, GLenum usage) |
1107 | { |
1108 | mState.mUsage = usage; |
1109 | signalDirtyState(dirtyBit: DIRTY_BIT_USAGE); |
1110 | } |
1111 | |
1112 | GLenum Texture::getUsage() const |
1113 | { |
1114 | return mState.mUsage; |
1115 | } |
1116 | |
1117 | void Texture::setProtectedContent(Context *context, bool hasProtectedContent) |
1118 | { |
1119 | mState.mHasProtectedContent = hasProtectedContent; |
1120 | } |
1121 | |
1122 | bool Texture::hasProtectedContent() const |
1123 | { |
1124 | return mState.mHasProtectedContent; |
1125 | } |
1126 | |
1127 | void Texture::setRenderabilityValidation(Context *context, bool renderabilityValidation) |
1128 | { |
1129 | mState.mRenderabilityValidation = renderabilityValidation; |
1130 | signalDirtyState(dirtyBit: DIRTY_BIT_RENDERABILITY_VALIDATION_ANGLE); |
1131 | } |
1132 | |
1133 | const TextureState &Texture::getTextureState() const |
1134 | { |
1135 | return mState; |
1136 | } |
1137 | |
1138 | const 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 | |
1144 | size_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 | |
1150 | size_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 | |
1156 | size_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 | |
1162 | const 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 | |
1168 | GLsizei Texture::getSamples(TextureTarget target, size_t level) const |
1169 | { |
1170 | ASSERT(TextureTargetToType(target) == mState.mType); |
1171 | return mState.getImageDesc(target, level).samples; |
1172 | } |
1173 | |
1174 | bool Texture::getFixedSampleLocations(TextureTarget target, size_t level) const |
1175 | { |
1176 | ASSERT(TextureTargetToType(target) == mState.mType); |
1177 | return mState.getImageDesc(target, level).fixedSampleLocations; |
1178 | } |
1179 | |
1180 | GLuint Texture::getMipmapMaxLevel() const |
1181 | { |
1182 | return mState.getMipmapMaxLevel(); |
1183 | } |
1184 | |
1185 | bool Texture::isMipmapComplete() const |
1186 | { |
1187 | return mState.computeMipmapCompleteness(); |
1188 | } |
1189 | |
1190 | egl::Surface *Texture::getBoundSurface() const |
1191 | { |
1192 | return mBoundSurface; |
1193 | } |
1194 | |
1195 | egl::Stream *Texture::getBoundStream() const |
1196 | { |
1197 | return mBoundStream; |
1198 | } |
1199 | |
1200 | GLint 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 | |
1216 | GLint 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 | |
1227 | void Texture::signalDirtyStorage(InitState initState) |
1228 | { |
1229 | mState.mInitState = initState; |
1230 | invalidateCompletenessCache(); |
1231 | mState.mCachedSamplerFormatValid = false; |
1232 | onStateChange(message: angle::SubjectMessage::SubjectChanged); |
1233 | } |
1234 | |
1235 | void 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 | |
1251 | angle::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 | |
1285 | angle::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 | |
1310 | angle::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 | |
1341 | angle::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 | |
1363 | angle::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 | |
1441 | angle::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 | |
1480 | angle::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 | |
1503 | angle::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 | |
1526 | angle::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 | |
1567 | angle::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 | |
1597 | angle::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 | |
1615 | angle::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 | |
1650 | angle::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 | |
1680 | angle::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 | |
1713 | angle::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 | |
1754 | angle::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 | |
1816 | angle::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 | |
1840 | angle::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 | |
1854 | void 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 | |
1866 | void Texture::releaseStream() |
1867 | { |
1868 | ASSERT(mBoundStream); |
1869 | mBoundStream = nullptr; |
1870 | } |
1871 | |
1872 | angle::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 | |
1885 | angle::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 | |
1897 | angle::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 | |
1916 | angle::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 | |
1945 | angle::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 | |
1955 | angle::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 | |
1979 | Extents 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 | |
1998 | Format 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 | |
2015 | GLsizei 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 | |
2027 | bool 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 | |
2060 | bool 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 | |
2074 | void Texture::setBorderColor(const Context *context, const ColorGeneric &color) |
2075 | { |
2076 | mState.mSamplerState.setBorderColor(color); |
2077 | signalDirtyState(dirtyBit: DIRTY_BIT_BORDER_COLOR); |
2078 | } |
2079 | |
2080 | const ColorGeneric &Texture::getBorderColor() const |
2081 | { |
2082 | return mState.mSamplerState.getBorderColor(); |
2083 | } |
2084 | |
2085 | GLint 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 | |
2096 | void Texture::setCrop(const Rectangle &rect) |
2097 | { |
2098 | mState.setCrop(rect); |
2099 | } |
2100 | |
2101 | const Rectangle &Texture::getCrop() const |
2102 | { |
2103 | return mState.getCrop(); |
2104 | } |
2105 | |
2106 | void Texture::setGenerateMipmapHint(GLenum hint) |
2107 | { |
2108 | mState.setGenerateMipmapHint(hint); |
2109 | } |
2110 | |
2111 | GLenum Texture::getGenerateMipmapHint() const |
2112 | { |
2113 | return mState.getGenerateMipmapHint(); |
2114 | } |
2115 | |
2116 | angle::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 | |
2125 | angle::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 | |
2161 | const OffsetBindingPointer<Buffer> &Texture::getBuffer() const |
2162 | { |
2163 | return mState.mBuffer; |
2164 | } |
2165 | |
2166 | void 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 | |
2180 | void 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 | |
2189 | GLuint Texture::getId() const |
2190 | { |
2191 | return id().value; |
2192 | } |
2193 | |
2194 | GLuint Texture::getNativeID() const |
2195 | { |
2196 | return mTexture->getNativeID(); |
2197 | } |
2198 | |
2199 | angle::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 | |
2208 | rx::FramebufferAttachmentObjectImpl *Texture::getAttachmentImpl() const |
2209 | { |
2210 | return mTexture; |
2211 | } |
2212 | |
2213 | bool 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 |
2232 | bool 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 | |
2241 | Texture::SamplerCompletenessCache::SamplerCompletenessCache() |
2242 | : context({.value: 0}), samplerState(), samplerComplete(false) |
2243 | {} |
2244 | |
2245 | void Texture::invalidateCompletenessCache() const |
2246 | { |
2247 | mCompletenessCache.context = {.value: 0}; |
2248 | } |
2249 | |
2250 | angle::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 | |
2284 | InitState 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 | |
2304 | void 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 | |
2325 | void 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 | |
2338 | bool 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 | |
2358 | angle::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 | |
2373 | angle::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 | |
2383 | void 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 | |
2469 | void Texture::onBufferContentsChange() |
2470 | { |
2471 | mState.mInitState = InitState::MayNeedInit; |
2472 | signalDirtyState(dirtyBit: DIRTY_BIT_IMPLEMENTATION); |
2473 | onStateChange(message: angle::SubjectMessage::ContentsChanged); |
2474 | } |
2475 | |
2476 | GLenum Texture::getImplementationColorReadFormat(const Context *context) const |
2477 | { |
2478 | return mTexture->getColorReadFormat(context); |
2479 | } |
2480 | |
2481 | GLenum Texture::getImplementationColorReadType(const Context *context) const |
2482 | { |
2483 | return mTexture->getColorReadType(context); |
2484 | } |
2485 | |
2486 | bool 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 | |
2502 | angle::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 | |
2521 | angle::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 | |
2537 | void 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 | |