1 //
2 // Copyright 2015 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 // TextureGL.cpp: Implements the class methods for TextureGL.
8
9 #include "libANGLE/renderer/gl/TextureGL.h"
10
11 #include "common/bitset_utils.h"
12 #include "common/debug.h"
13 #include "common/utilities.h"
14 #include "libANGLE/Context.h"
15 #include "libANGLE/MemoryObject.h"
16 #include "libANGLE/State.h"
17 #include "libANGLE/Surface.h"
18 #include "libANGLE/angletypes.h"
19 #include "libANGLE/formatutils.h"
20 #include "libANGLE/queryconversions.h"
21 #include "libANGLE/renderer/gl/BlitGL.h"
22 #include "libANGLE/renderer/gl/BufferGL.h"
23 #include "libANGLE/renderer/gl/ContextGL.h"
24 #include "libANGLE/renderer/gl/FramebufferGL.h"
25 #include "libANGLE/renderer/gl/FunctionsGL.h"
26 #include "libANGLE/renderer/gl/ImageGL.h"
27 #include "libANGLE/renderer/gl/MemoryObjectGL.h"
28 #include "libANGLE/renderer/gl/StateManagerGL.h"
29 #include "libANGLE/renderer/gl/SurfaceGL.h"
30 #include "libANGLE/renderer/gl/formatutilsgl.h"
31 #include "libANGLE/renderer/gl/renderergl_utils.h"
32 #include "platform/FeaturesGL.h"
33
34 using angle::CheckedNumeric;
35
36 namespace rx
37 {
38
39 namespace
40 {
41
GetLevelInfoIndex(gl::TextureTarget target,size_t level)42 size_t GetLevelInfoIndex(gl::TextureTarget target, size_t level)
43 {
44 return gl::IsCubeMapFaceTarget(target)
45 ? ((level * gl::kCubeFaceCount) + gl::CubeMapTextureTargetToFaceIndex(target))
46 : level;
47 }
48
IsLUMAFormat(GLenum format)49 bool IsLUMAFormat(GLenum format)
50 {
51 return format == GL_LUMINANCE || format == GL_ALPHA || format == GL_LUMINANCE_ALPHA;
52 }
53
GetLUMAWorkaroundInfo(GLenum originalFormat,GLenum destinationFormat)54 LUMAWorkaroundGL GetLUMAWorkaroundInfo(GLenum originalFormat, GLenum destinationFormat)
55 {
56 if (IsLUMAFormat(originalFormat))
57 {
58 return LUMAWorkaroundGL(!IsLUMAFormat(destinationFormat), destinationFormat);
59 }
60 else
61 {
62 return LUMAWorkaroundGL(false, GL_NONE);
63 }
64 }
65
GetDepthStencilWorkaround(GLenum format)66 bool GetDepthStencilWorkaround(GLenum format)
67 {
68 return format == GL_DEPTH_COMPONENT || format == GL_DEPTH_STENCIL;
69 }
70
GetEmulatedAlphaChannel(const angle::FeaturesGL & features,GLenum internalFormat)71 bool GetEmulatedAlphaChannel(const angle::FeaturesGL &features, GLenum internalFormat)
72 {
73 return features.rgbDXT1TexturesSampleZeroAlpha.enabled &&
74 internalFormat == GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
75 }
76
GetLevelInfo(const angle::FeaturesGL & features,GLenum originalInternalFormat,GLenum destinationInternalFormat)77 LevelInfoGL GetLevelInfo(const angle::FeaturesGL &features,
78 GLenum originalInternalFormat,
79 GLenum destinationInternalFormat)
80 {
81 GLenum originalFormat = gl::GetUnsizedFormat(originalInternalFormat);
82 GLenum destinationFormat = gl::GetUnsizedFormat(destinationInternalFormat);
83 return LevelInfoGL(originalFormat, destinationInternalFormat,
84 GetDepthStencilWorkaround(originalFormat),
85 GetLUMAWorkaroundInfo(originalFormat, destinationFormat),
86 GetEmulatedAlphaChannel(features, originalFormat));
87 }
88
GetLevelWorkaroundDirtyBits()89 gl::Texture::DirtyBits GetLevelWorkaroundDirtyBits()
90 {
91 gl::Texture::DirtyBits bits;
92 bits.set(gl::Texture::DIRTY_BIT_SWIZZLE_RED);
93 bits.set(gl::Texture::DIRTY_BIT_SWIZZLE_GREEN);
94 bits.set(gl::Texture::DIRTY_BIT_SWIZZLE_BLUE);
95 bits.set(gl::Texture::DIRTY_BIT_SWIZZLE_ALPHA);
96 return bits;
97 }
98
GetMaxLevelInfoCountForTextureType(gl::TextureType type)99 size_t GetMaxLevelInfoCountForTextureType(gl::TextureType type)
100 {
101 switch (type)
102 {
103 case gl::TextureType::CubeMap:
104 return (gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS + 1) * gl::kCubeFaceCount;
105
106 case gl::TextureType::External:
107 return 1;
108
109 default:
110 return gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS + 1;
111 }
112 }
113
114 } // anonymous namespace
115
LUMAWorkaroundGL()116 LUMAWorkaroundGL::LUMAWorkaroundGL() : LUMAWorkaroundGL(false, GL_NONE) {}
117
LUMAWorkaroundGL(bool enabled_,GLenum workaroundFormat_)118 LUMAWorkaroundGL::LUMAWorkaroundGL(bool enabled_, GLenum workaroundFormat_)
119 : enabled(enabled_), workaroundFormat(workaroundFormat_)
120 {}
121
LevelInfoGL()122 LevelInfoGL::LevelInfoGL() : LevelInfoGL(GL_NONE, GL_NONE, false, LUMAWorkaroundGL(), false) {}
123
LevelInfoGL(GLenum sourceFormat_,GLenum nativeInternalFormat_,bool depthStencilWorkaround_,const LUMAWorkaroundGL & lumaWorkaround_,bool emulatedAlphaChannel_)124 LevelInfoGL::LevelInfoGL(GLenum sourceFormat_,
125 GLenum nativeInternalFormat_,
126 bool depthStencilWorkaround_,
127 const LUMAWorkaroundGL &lumaWorkaround_,
128 bool emulatedAlphaChannel_)
129 : sourceFormat(sourceFormat_),
130 nativeInternalFormat(nativeInternalFormat_),
131 depthStencilWorkaround(depthStencilWorkaround_),
132 lumaWorkaround(lumaWorkaround_),
133 emulatedAlphaChannel(emulatedAlphaChannel_)
134 {}
135
TextureGL(const gl::TextureState & state,GLuint id)136 TextureGL::TextureGL(const gl::TextureState &state, GLuint id)
137 : TextureImpl(state),
138 mAppliedSwizzle(state.getSwizzleState()),
139 mAppliedSampler(state.getSamplerState()),
140 mAppliedBaseLevel(state.getEffectiveBaseLevel()),
141 mAppliedMaxLevel(state.getEffectiveMaxLevel()),
142 mTextureID(id)
143 {
144 mLevelInfo.resize(GetMaxLevelInfoCountForTextureType(getType()));
145 }
146
~TextureGL()147 TextureGL::~TextureGL()
148 {
149 ASSERT(mTextureID == 0);
150 }
151
onDestroy(const gl::Context * context)152 void TextureGL::onDestroy(const gl::Context *context)
153 {
154 GetImplAs<ContextGL>(context)->flushIfNecessaryBeforeDeleteTextures();
155 StateManagerGL *stateManager = GetStateManagerGL(context);
156 stateManager->deleteTexture(mTextureID);
157 mTextureID = 0;
158 }
159
setImage(const gl::Context * context,const gl::ImageIndex & index,GLenum internalFormat,const gl::Extents & size,GLenum format,GLenum type,const gl::PixelUnpackState & unpack,gl::Buffer * unpackBuffer,const uint8_t * pixels)160 angle::Result TextureGL::setImage(const gl::Context *context,
161 const gl::ImageIndex &index,
162 GLenum internalFormat,
163 const gl::Extents &size,
164 GLenum format,
165 GLenum type,
166 const gl::PixelUnpackState &unpack,
167 gl::Buffer *unpackBuffer,
168 const uint8_t *pixels)
169 {
170 const angle::FeaturesGL &features = GetFeaturesGL(context);
171
172 gl::TextureTarget target = index.getTarget();
173 size_t level = static_cast<size_t>(index.getLevelIndex());
174
175 if (features.unpackOverlappingRowsSeparatelyUnpackBuffer.enabled && unpackBuffer &&
176 unpack.rowLength != 0 && unpack.rowLength < size.width)
177 {
178 // The rows overlap in unpack memory. Upload the texture row by row to work around
179 // driver bug.
180 ANGLE_TRY(
181 reserveTexImageToBeFilled(context, target, level, internalFormat, size, format, type));
182
183 if (size.width == 0 || size.height == 0 || size.depth == 0)
184 {
185 return angle::Result::Continue;
186 }
187
188 gl::Box area(0, 0, 0, size.width, size.height, size.depth);
189 return setSubImageRowByRowWorkaround(context, target, level, area, format, type, unpack,
190 unpackBuffer, pixels);
191 }
192
193 if (features.unpackLastRowSeparatelyForPaddingInclusion.enabled)
194 {
195 bool apply = false;
196 ANGLE_TRY(ShouldApplyLastRowPaddingWorkaround(
197 GetImplAs<ContextGL>(context), size, unpack, unpackBuffer, format, type,
198 nativegl::UseTexImage3D(getType()), pixels, &apply));
199
200 // The driver will think the pixel buffer doesn't have enough data, work around this bug
201 // by uploading the last row (and last level if 3D) separately.
202 if (apply)
203 {
204 ANGLE_TRY(reserveTexImageToBeFilled(context, target, level, internalFormat, size,
205 format, type));
206
207 if (size.width == 0 || size.height == 0 || size.depth == 0)
208 {
209 return angle::Result::Continue;
210 }
211
212 gl::Box area(0, 0, 0, size.width, size.height, size.depth);
213 return setSubImagePaddingWorkaround(context, target, level, area, format, type, unpack,
214 unpackBuffer, pixels);
215 }
216 }
217
218 ANGLE_TRY(setImageHelper(context, target, level, internalFormat, size, format, type, pixels));
219
220 return angle::Result::Continue;
221 }
222
setImageHelper(const gl::Context * context,gl::TextureTarget target,size_t level,GLenum internalFormat,const gl::Extents & size,GLenum format,GLenum type,const uint8_t * pixels)223 angle::Result TextureGL::setImageHelper(const gl::Context *context,
224 gl::TextureTarget target,
225 size_t level,
226 GLenum internalFormat,
227 const gl::Extents &size,
228 GLenum format,
229 GLenum type,
230 const uint8_t *pixels)
231 {
232 ASSERT(TextureTargetToType(target) == getType());
233
234 const FunctionsGL *functions = GetFunctionsGL(context);
235 StateManagerGL *stateManager = GetStateManagerGL(context);
236 const angle::FeaturesGL &features = GetFeaturesGL(context);
237
238 nativegl::TexImageFormat texImageFormat =
239 nativegl::GetTexImageFormat(functions, features, internalFormat, format, type);
240
241 stateManager->bindTexture(getType(), mTextureID);
242
243 if (features.resetTexImage2DBaseLevel.enabled)
244 {
245 // setBaseLevel doesn't ever generate errors.
246 (void)setBaseLevel(context, 0);
247 }
248
249 if (nativegl::UseTexImage2D(getType()))
250 {
251 ASSERT(size.depth == 1);
252 ANGLE_GL_TRY_ALWAYS_CHECK(
253 context, functions->texImage2D(nativegl::GetTextureBindingTarget(target),
254 static_cast<GLint>(level), texImageFormat.internalFormat,
255 size.width, size.height, 0, texImageFormat.format,
256 texImageFormat.type, pixels));
257 }
258 else
259 {
260 ASSERT(nativegl::UseTexImage3D(getType()));
261 ANGLE_GL_TRY_ALWAYS_CHECK(
262 context, functions->texImage3D(ToGLenum(target), static_cast<GLint>(level),
263 texImageFormat.internalFormat, size.width, size.height,
264 size.depth, 0, texImageFormat.format,
265 texImageFormat.type, pixels));
266 }
267
268 LevelInfoGL levelInfo = GetLevelInfo(features, internalFormat, texImageFormat.internalFormat);
269 setLevelInfo(context, target, level, 1, levelInfo);
270
271 if (features.setZeroLevelBeforeGenerateMipmap.enabled && getType() == gl::TextureType::_2D &&
272 level != 0 && mLevelInfo[0].nativeInternalFormat == GL_NONE)
273 {
274 // Only fill level zero if it's possible that mipmaps can be generated with this format
275 const gl::InternalFormat &internalFormatInfo =
276 gl::GetInternalFormatInfo(internalFormat, type);
277 if (!internalFormatInfo.sized ||
278 (internalFormatInfo.filterSupport(context->getClientVersion(),
279 context->getExtensions()) &&
280 internalFormatInfo.textureAttachmentSupport(context->getClientVersion(),
281 context->getExtensions())))
282 {
283 ANGLE_GL_TRY_ALWAYS_CHECK(
284 context,
285 functions->texImage2D(nativegl::GetTextureBindingTarget(target), 0,
286 texImageFormat.internalFormat, 1, 1, 0, texImageFormat.format,
287 texImageFormat.type, nullptr));
288 setLevelInfo(context, target, 0, 1, levelInfo);
289 }
290 }
291
292 return angle::Result::Continue;
293 }
294
reserveTexImageToBeFilled(const gl::Context * context,gl::TextureTarget target,size_t level,GLenum internalFormat,const gl::Extents & size,GLenum format,GLenum type)295 angle::Result TextureGL::reserveTexImageToBeFilled(const gl::Context *context,
296 gl::TextureTarget target,
297 size_t level,
298 GLenum internalFormat,
299 const gl::Extents &size,
300 GLenum format,
301 GLenum type)
302 {
303 StateManagerGL *stateManager = GetStateManagerGL(context);
304 ANGLE_TRY(stateManager->setPixelUnpackBuffer(context, nullptr));
305 ANGLE_TRY(setImageHelper(context, target, level, internalFormat, size, format, type, nullptr));
306 return angle::Result::Continue;
307 }
308
setSubImage(const gl::Context * context,const gl::ImageIndex & index,const gl::Box & area,GLenum format,GLenum type,const gl::PixelUnpackState & unpack,gl::Buffer * unpackBuffer,const uint8_t * pixels)309 angle::Result TextureGL::setSubImage(const gl::Context *context,
310 const gl::ImageIndex &index,
311 const gl::Box &area,
312 GLenum format,
313 GLenum type,
314 const gl::PixelUnpackState &unpack,
315 gl::Buffer *unpackBuffer,
316 const uint8_t *pixels)
317 {
318 ASSERT(TextureTargetToType(index.getTarget()) == getType());
319
320 const FunctionsGL *functions = GetFunctionsGL(context);
321 StateManagerGL *stateManager = GetStateManagerGL(context);
322 const angle::FeaturesGL &features = GetFeaturesGL(context);
323
324 nativegl::TexSubImageFormat texSubImageFormat =
325 nativegl::GetTexSubImageFormat(functions, features, format, type);
326
327 gl::TextureTarget target = index.getTarget();
328 size_t level = static_cast<size_t>(index.getLevelIndex());
329
330 ASSERT(getLevelInfo(target, level).lumaWorkaround.enabled ==
331 GetLevelInfo(features, format, texSubImageFormat.format).lumaWorkaround.enabled);
332
333 stateManager->bindTexture(getType(), mTextureID);
334 if (features.unpackOverlappingRowsSeparatelyUnpackBuffer.enabled && unpackBuffer &&
335 unpack.rowLength != 0 && unpack.rowLength < area.width)
336 {
337 return setSubImageRowByRowWorkaround(context, target, level, area, format, type, unpack,
338 unpackBuffer, pixels);
339 }
340
341 if (features.unpackLastRowSeparatelyForPaddingInclusion.enabled)
342 {
343 gl::Extents size(area.width, area.height, area.depth);
344
345 bool apply = false;
346 ANGLE_TRY(ShouldApplyLastRowPaddingWorkaround(
347 GetImplAs<ContextGL>(context), size, unpack, unpackBuffer, format, type,
348 nativegl::UseTexImage3D(getType()), pixels, &apply));
349
350 // The driver will think the pixel buffer doesn't have enough data, work around this bug
351 // by uploading the last row (and last level if 3D) separately.
352 if (apply)
353 {
354 return setSubImagePaddingWorkaround(context, target, level, area, format, type, unpack,
355 unpackBuffer, pixels);
356 }
357 }
358
359 if (nativegl::UseTexImage2D(getType()))
360 {
361 ASSERT(area.z == 0 && area.depth == 1);
362 ANGLE_GL_TRY(context,
363 functions->texSubImage2D(nativegl::GetTextureBindingTarget(target),
364 static_cast<GLint>(level), area.x, area.y, area.width,
365 area.height, texSubImageFormat.format,
366 texSubImageFormat.type, pixels));
367 }
368 else
369 {
370 ASSERT(nativegl::UseTexImage3D(getType()));
371 ANGLE_GL_TRY(context, functions->texSubImage3D(
372 ToGLenum(target), static_cast<GLint>(level), area.x, area.y,
373 area.z, area.width, area.height, area.depth,
374 texSubImageFormat.format, texSubImageFormat.type, pixels));
375 }
376
377 return angle::Result::Continue;
378 }
379
setSubImageRowByRowWorkaround(const gl::Context * context,gl::TextureTarget target,size_t level,const gl::Box & area,GLenum format,GLenum type,const gl::PixelUnpackState & unpack,const gl::Buffer * unpackBuffer,const uint8_t * pixels)380 angle::Result TextureGL::setSubImageRowByRowWorkaround(const gl::Context *context,
381 gl::TextureTarget target,
382 size_t level,
383 const gl::Box &area,
384 GLenum format,
385 GLenum type,
386 const gl::PixelUnpackState &unpack,
387 const gl::Buffer *unpackBuffer,
388 const uint8_t *pixels)
389 {
390 ContextGL *contextGL = GetImplAs<ContextGL>(context);
391 const FunctionsGL *functions = GetFunctionsGL(context);
392 StateManagerGL *stateManager = GetStateManagerGL(context);
393
394 gl::PixelUnpackState directUnpack;
395 directUnpack.alignment = 1;
396 ANGLE_TRY(stateManager->setPixelUnpackState(context, directUnpack));
397 ANGLE_TRY(stateManager->setPixelUnpackBuffer(context, unpackBuffer));
398
399 const gl::InternalFormat &glFormat = gl::GetInternalFormatInfo(format, type);
400 GLuint rowBytes = 0;
401 ANGLE_CHECK_GL_MATH(contextGL, glFormat.computeRowPitch(type, area.width, unpack.alignment,
402 unpack.rowLength, &rowBytes));
403 GLuint imageBytes = 0;
404 ANGLE_CHECK_GL_MATH(contextGL, glFormat.computeDepthPitch(area.height, unpack.imageHeight,
405 rowBytes, &imageBytes));
406
407 bool useTexImage3D = nativegl::UseTexImage3D(getType());
408 GLuint skipBytes = 0;
409 ANGLE_CHECK_GL_MATH(contextGL, glFormat.computeSkipBytes(type, rowBytes, imageBytes, unpack,
410 useTexImage3D, &skipBytes));
411
412 const uint8_t *pixelsWithSkip = pixels + skipBytes;
413 if (useTexImage3D)
414 {
415 for (GLint image = 0; image < area.depth; ++image)
416 {
417 GLint imageByteOffset = image * imageBytes;
418 for (GLint row = 0; row < area.height; ++row)
419 {
420 GLint byteOffset = imageByteOffset + row * rowBytes;
421 const GLubyte *rowPixels = pixelsWithSkip + byteOffset;
422 ANGLE_GL_TRY(context,
423 functions->texSubImage3D(ToGLenum(target), static_cast<GLint>(level),
424 area.x, row + area.y, image + area.z,
425 area.width, 1, 1, format, type, rowPixels));
426 }
427 }
428 }
429 else
430 {
431 ASSERT(nativegl::UseTexImage2D(getType()));
432 for (GLint row = 0; row < area.height; ++row)
433 {
434 GLint byteOffset = row * rowBytes;
435 const GLubyte *rowPixels = pixelsWithSkip + byteOffset;
436 ANGLE_GL_TRY(context, functions->texSubImage2D(
437 ToGLenum(target), static_cast<GLint>(level), area.x,
438 row + area.y, area.width, 1, format, type, rowPixels));
439 }
440 }
441 return angle::Result::Continue;
442 }
443
setSubImagePaddingWorkaround(const gl::Context * context,gl::TextureTarget target,size_t level,const gl::Box & area,GLenum format,GLenum type,const gl::PixelUnpackState & unpack,const gl::Buffer * unpackBuffer,const uint8_t * pixels)444 angle::Result TextureGL::setSubImagePaddingWorkaround(const gl::Context *context,
445 gl::TextureTarget target,
446 size_t level,
447 const gl::Box &area,
448 GLenum format,
449 GLenum type,
450 const gl::PixelUnpackState &unpack,
451 const gl::Buffer *unpackBuffer,
452 const uint8_t *pixels)
453 {
454 ContextGL *contextGL = GetImplAs<ContextGL>(context);
455 const FunctionsGL *functions = GetFunctionsGL(context);
456 StateManagerGL *stateManager = GetStateManagerGL(context);
457
458 const gl::InternalFormat &glFormat = gl::GetInternalFormatInfo(format, type);
459 GLuint rowBytes = 0;
460 ANGLE_CHECK_GL_MATH(contextGL, glFormat.computeRowPitch(type, area.width, unpack.alignment,
461 unpack.rowLength, &rowBytes));
462 GLuint imageBytes = 0;
463 ANGLE_CHECK_GL_MATH(contextGL, glFormat.computeDepthPitch(area.height, unpack.imageHeight,
464 rowBytes, &imageBytes));
465 bool useTexImage3D = nativegl::UseTexImage3D(getType());
466 GLuint skipBytes = 0;
467 ANGLE_CHECK_GL_MATH(contextGL, glFormat.computeSkipBytes(type, rowBytes, imageBytes, unpack,
468 useTexImage3D, &skipBytes));
469
470 ANGLE_TRY(stateManager->setPixelUnpackState(context, unpack));
471 ANGLE_TRY(stateManager->setPixelUnpackBuffer(context, unpackBuffer));
472
473 gl::PixelUnpackState directUnpack;
474 directUnpack.alignment = 1;
475
476 if (useTexImage3D)
477 {
478 // Upload all but the last slice
479 if (area.depth > 1)
480 {
481 ANGLE_GL_TRY(context,
482 functions->texSubImage3D(ToGLenum(target), static_cast<GLint>(level),
483 area.x, area.y, area.z, area.width, area.height,
484 area.depth - 1, format, type, pixels));
485 }
486
487 // Upload the last slice but its last row
488 if (area.height > 1)
489 {
490 // Do not include skipBytes in the last image pixel start offset as it will be done by
491 // the driver
492 GLint lastImageOffset = (area.depth - 1) * imageBytes;
493 const GLubyte *lastImagePixels = pixels + lastImageOffset;
494 ANGLE_GL_TRY(context, functions->texSubImage3D(
495 ToGLenum(target), static_cast<GLint>(level), area.x, area.y,
496 area.z + area.depth - 1, area.width, area.height - 1, 1,
497 format, type, lastImagePixels));
498 }
499
500 // Upload the last row of the last slice "manually"
501 ANGLE_TRY(stateManager->setPixelUnpackState(context, directUnpack));
502
503 GLint lastRowOffset =
504 skipBytes + (area.depth - 1) * imageBytes + (area.height - 1) * rowBytes;
505 const GLubyte *lastRowPixels = pixels + lastRowOffset;
506 ANGLE_GL_TRY(context,
507 functions->texSubImage3D(ToGLenum(target), static_cast<GLint>(level), area.x,
508 area.y + area.height - 1, area.z + area.depth - 1,
509 area.width, 1, 1, format, type, lastRowPixels));
510 }
511 else
512 {
513 ASSERT(nativegl::UseTexImage2D(getType()));
514
515 // Upload all but the last row
516 if (area.height > 1)
517 {
518 ANGLE_GL_TRY(context, functions->texSubImage2D(
519 ToGLenum(target), static_cast<GLint>(level), area.x, area.y,
520 area.width, area.height - 1, format, type, pixels));
521 }
522
523 // Upload the last row "manually"
524 ANGLE_TRY(stateManager->setPixelUnpackState(context, directUnpack));
525
526 GLint lastRowOffset = skipBytes + (area.height - 1) * rowBytes;
527 const GLubyte *lastRowPixels = pixels + lastRowOffset;
528 ANGLE_GL_TRY(context, functions->texSubImage2D(ToGLenum(target), static_cast<GLint>(level),
529 area.x, area.y + area.height - 1, area.width,
530 1, format, type, lastRowPixels));
531 }
532
533 return angle::Result::Continue;
534 }
535
setCompressedImage(const gl::Context * context,const gl::ImageIndex & index,GLenum internalFormat,const gl::Extents & size,const gl::PixelUnpackState & unpack,size_t imageSize,const uint8_t * pixels)536 angle::Result TextureGL::setCompressedImage(const gl::Context *context,
537 const gl::ImageIndex &index,
538 GLenum internalFormat,
539 const gl::Extents &size,
540 const gl::PixelUnpackState &unpack,
541 size_t imageSize,
542 const uint8_t *pixels)
543 {
544 const FunctionsGL *functions = GetFunctionsGL(context);
545 StateManagerGL *stateManager = GetStateManagerGL(context);
546 const angle::FeaturesGL &features = GetFeaturesGL(context);
547
548 gl::TextureTarget target = index.getTarget();
549 size_t level = static_cast<size_t>(index.getLevelIndex());
550 ASSERT(TextureTargetToType(target) == getType());
551
552 nativegl::CompressedTexImageFormat compressedTexImageFormat =
553 nativegl::GetCompressedTexImageFormat(functions, features, internalFormat);
554
555 stateManager->bindTexture(getType(), mTextureID);
556 if (nativegl::UseTexImage2D(getType()))
557 {
558 ASSERT(size.depth == 1);
559 ANGLE_GL_TRY_ALWAYS_CHECK(
560 context, functions->compressedTexImage2D(ToGLenum(target), static_cast<GLint>(level),
561 compressedTexImageFormat.internalFormat,
562 size.width, size.height, 0,
563 static_cast<GLsizei>(imageSize), pixels));
564 }
565 else
566 {
567 ASSERT(nativegl::UseTexImage3D(getType()));
568 ANGLE_GL_TRY_ALWAYS_CHECK(
569 context, functions->compressedTexImage3D(ToGLenum(target), static_cast<GLint>(level),
570 compressedTexImageFormat.internalFormat,
571 size.width, size.height, size.depth, 0,
572 static_cast<GLsizei>(imageSize), pixels));
573 }
574
575 LevelInfoGL levelInfo =
576 GetLevelInfo(features, internalFormat, compressedTexImageFormat.internalFormat);
577 ASSERT(!levelInfo.lumaWorkaround.enabled);
578 setLevelInfo(context, target, level, 1, levelInfo);
579
580 return angle::Result::Continue;
581 }
582
setCompressedSubImage(const gl::Context * context,const gl::ImageIndex & index,const gl::Box & area,GLenum format,const gl::PixelUnpackState & unpack,size_t imageSize,const uint8_t * pixels)583 angle::Result TextureGL::setCompressedSubImage(const gl::Context *context,
584 const gl::ImageIndex &index,
585 const gl::Box &area,
586 GLenum format,
587 const gl::PixelUnpackState &unpack,
588 size_t imageSize,
589 const uint8_t *pixels)
590 {
591 const FunctionsGL *functions = GetFunctionsGL(context);
592 StateManagerGL *stateManager = GetStateManagerGL(context);
593 const angle::FeaturesGL &features = GetFeaturesGL(context);
594
595 gl::TextureTarget target = index.getTarget();
596 size_t level = static_cast<size_t>(index.getLevelIndex());
597 ASSERT(TextureTargetToType(target) == getType());
598
599 nativegl::CompressedTexSubImageFormat compressedTexSubImageFormat =
600 nativegl::GetCompressedSubTexImageFormat(functions, features, format);
601
602 stateManager->bindTexture(getType(), mTextureID);
603 if (nativegl::UseTexImage2D(getType()))
604 {
605 ASSERT(area.z == 0 && area.depth == 1);
606 ANGLE_GL_TRY(context, functions->compressedTexSubImage2D(
607 ToGLenum(target), static_cast<GLint>(level), area.x, area.y,
608 area.width, area.height, compressedTexSubImageFormat.format,
609 static_cast<GLsizei>(imageSize), pixels));
610 }
611 else
612 {
613 ASSERT(nativegl::UseTexImage3D(getType()));
614 ANGLE_GL_TRY(context,
615 functions->compressedTexSubImage3D(
616 ToGLenum(target), static_cast<GLint>(level), area.x, area.y, area.z,
617 area.width, area.height, area.depth, compressedTexSubImageFormat.format,
618 static_cast<GLsizei>(imageSize), pixels));
619 }
620
621 ASSERT(
622 !getLevelInfo(target, level).lumaWorkaround.enabled &&
623 !GetLevelInfo(features, format, compressedTexSubImageFormat.format).lumaWorkaround.enabled);
624
625 return angle::Result::Continue;
626 }
627
copyImage(const gl::Context * context,const gl::ImageIndex & index,const gl::Rectangle & sourceArea,GLenum internalFormat,gl::Framebuffer * source)628 angle::Result TextureGL::copyImage(const gl::Context *context,
629 const gl::ImageIndex &index,
630 const gl::Rectangle &sourceArea,
631 GLenum internalFormat,
632 gl::Framebuffer *source)
633 {
634 ContextGL *contextGL = GetImplAs<ContextGL>(context);
635 const FunctionsGL *functions = GetFunctionsGL(context);
636 StateManagerGL *stateManager = GetStateManagerGL(context);
637 const angle::FeaturesGL &features = GetFeaturesGL(context);
638
639 gl::TextureTarget target = index.getTarget();
640 size_t level = static_cast<size_t>(index.getLevelIndex());
641 GLenum type = source->getImplementationColorReadType(context);
642 nativegl::CopyTexImageImageFormat copyTexImageFormat =
643 nativegl::GetCopyTexImageImageFormat(functions, features, internalFormat, type);
644
645 stateManager->bindTexture(getType(), mTextureID);
646
647 const FramebufferGL *sourceFramebufferGL = GetImplAs<FramebufferGL>(source);
648 gl::Extents fbSize = sourceFramebufferGL->getState().getReadAttachment()->getSize();
649
650 // Did the read area go outside the framebuffer?
651 bool outside = sourceArea.x < 0 || sourceArea.y < 0 ||
652 sourceArea.x + sourceArea.width > fbSize.width ||
653 sourceArea.y + sourceArea.height > fbSize.height;
654
655 // TODO: Find a way to initialize the texture entirely in the gl level with ensureInitialized.
656 // Right now there is no easy way to pre-fill the texture when it is being redefined with
657 // partially uninitialized data.
658 bool requiresInitialization =
659 outside && (context->isRobustResourceInitEnabled() || context->isWebGL());
660
661 // When robust resource initialization is enabled, the area outside the framebuffer must be
662 // zeroed. We just zero the whole thing before copying into the area that overlaps the
663 // framebuffer.
664 if (requiresInitialization)
665 {
666 GLuint pixelBytes =
667 gl::GetInternalFormatInfo(copyTexImageFormat.internalFormat, type).pixelBytes;
668 angle::MemoryBuffer *zero;
669 ANGLE_CHECK_GL_ALLOC(
670 contextGL,
671 context->getZeroFilledBuffer(sourceArea.width * sourceArea.height * pixelBytes, &zero));
672
673 gl::PixelUnpackState unpack;
674 unpack.alignment = 1;
675 ANGLE_TRY(stateManager->setPixelUnpackState(context, unpack));
676 ANGLE_TRY(stateManager->setPixelUnpackBuffer(context, nullptr));
677
678 ANGLE_GL_TRY_ALWAYS_CHECK(
679 context, functions->texImage2D(ToGLenum(target), static_cast<GLint>(level),
680 copyTexImageFormat.internalFormat, sourceArea.width,
681 sourceArea.height, 0,
682 gl::GetUnsizedFormat(copyTexImageFormat.internalFormat),
683 type, zero->data()));
684 }
685
686 // Clip source area to framebuffer and copy if remaining area is not empty.
687 gl::Rectangle clippedArea;
688 if (ClipRectangle(sourceArea, gl::Rectangle(0, 0, fbSize.width, fbSize.height), &clippedArea))
689 {
690 // If fbo's read buffer and the target texture are the same texture but different levels,
691 // and if the read buffer is a non-base texture level, then implementations glTexImage2D
692 // may change the target texture and make the original texture mipmap incomplete, which in
693 // turn makes the fbo incomplete.
694 // To avoid that, we clamp BASE_LEVEL and MAX_LEVEL to the same texture level as the fbo's
695 // read buffer attachment. See http://crbug.com/797235
696 const gl::FramebufferAttachment *readBuffer = source->getReadColorAttachment();
697 if (readBuffer && readBuffer->type() == GL_TEXTURE)
698 {
699 TextureGL *sourceTexture = GetImplAs<TextureGL>(readBuffer->getTexture());
700 if (sourceTexture && sourceTexture->mTextureID == mTextureID)
701 {
702 GLuint attachedTextureLevel = readBuffer->mipLevel();
703 if (attachedTextureLevel != mState.getEffectiveBaseLevel())
704 {
705 ANGLE_TRY(setBaseLevel(context, attachedTextureLevel));
706 ANGLE_TRY(setMaxLevel(context, attachedTextureLevel));
707 }
708 }
709 }
710
711 LevelInfoGL levelInfo =
712 GetLevelInfo(features, internalFormat, copyTexImageFormat.internalFormat);
713 gl::Offset destOffset(clippedArea.x - sourceArea.x, clippedArea.y - sourceArea.y, 0);
714
715 if (levelInfo.lumaWorkaround.enabled)
716 {
717 BlitGL *blitter = GetBlitGL(context);
718
719 if (requiresInitialization)
720 {
721 ANGLE_TRY(blitter->copySubImageToLUMAWorkaroundTexture(
722 context, mTextureID, getType(), target, levelInfo.sourceFormat, level,
723 destOffset, clippedArea, source));
724 }
725 else
726 {
727 ANGLE_TRY(blitter->copyImageToLUMAWorkaroundTexture(
728 context, mTextureID, getType(), target, levelInfo.sourceFormat, level,
729 clippedArea, copyTexImageFormat.internalFormat, source));
730 }
731 }
732 else
733 {
734 ASSERT(nativegl::UseTexImage2D(getType()));
735 stateManager->bindFramebuffer(GL_READ_FRAMEBUFFER,
736 sourceFramebufferGL->getFramebufferID());
737 if (features.emulateCopyTexImage2DFromRenderbuffers.enabled && readBuffer &&
738 readBuffer->type() == GL_RENDERBUFFER)
739 {
740 BlitGL *blitter = GetBlitGL(context);
741 ANGLE_TRY(blitter->blitColorBufferWithShader(
742 context, source, mTextureID, target, level, clippedArea,
743 gl::Rectangle(destOffset.x, destOffset.y, clippedArea.width,
744 clippedArea.height),
745 GL_NEAREST, true));
746 }
747 else if (requiresInitialization)
748 {
749 ANGLE_GL_TRY(context, functions->copyTexSubImage2D(
750 ToGLenum(target), static_cast<GLint>(level), destOffset.x,
751 destOffset.y, clippedArea.x, clippedArea.y,
752 clippedArea.width, clippedArea.height));
753 }
754 else
755 {
756 ANGLE_GL_TRY_ALWAYS_CHECK(
757 context, functions->copyTexImage2D(ToGLenum(target), static_cast<GLint>(level),
758 copyTexImageFormat.internalFormat,
759 clippedArea.x, clippedArea.y,
760 clippedArea.width, clippedArea.height, 0));
761 }
762 }
763 setLevelInfo(context, target, level, 1, levelInfo);
764 }
765
766 if (features.flushBeforeDeleteTextureIfCopiedTo.enabled)
767 {
768 contextGL->setNeedsFlushBeforeDeleteTextures();
769 }
770
771 return angle::Result::Continue;
772 }
773
copySubImage(const gl::Context * context,const gl::ImageIndex & index,const gl::Offset & destOffset,const gl::Rectangle & sourceArea,gl::Framebuffer * source)774 angle::Result TextureGL::copySubImage(const gl::Context *context,
775 const gl::ImageIndex &index,
776 const gl::Offset &destOffset,
777 const gl::Rectangle &sourceArea,
778 gl::Framebuffer *source)
779 {
780 const FunctionsGL *functions = GetFunctionsGL(context);
781 StateManagerGL *stateManager = GetStateManagerGL(context);
782 const angle::FeaturesGL &features = GetFeaturesGL(context);
783
784 gl::TextureTarget target = index.getTarget();
785 size_t level = static_cast<size_t>(index.getLevelIndex());
786 const FramebufferGL *sourceFramebufferGL = GetImplAs<FramebufferGL>(source);
787
788 // Clip source area to framebuffer.
789 const gl::Extents fbSize = sourceFramebufferGL->getState().getReadAttachment()->getSize();
790 gl::Rectangle clippedArea;
791 if (!ClipRectangle(sourceArea, gl::Rectangle(0, 0, fbSize.width, fbSize.height), &clippedArea))
792 {
793 // nothing to do
794 return angle::Result::Continue;
795 }
796 gl::Offset clippedOffset(destOffset.x + clippedArea.x - sourceArea.x,
797 destOffset.y + clippedArea.y - sourceArea.y, destOffset.z);
798
799 stateManager->bindTexture(getType(), mTextureID);
800 stateManager->bindFramebuffer(GL_READ_FRAMEBUFFER, sourceFramebufferGL->getFramebufferID());
801
802 const LevelInfoGL &levelInfo = getLevelInfo(target, level);
803 if (levelInfo.lumaWorkaround.enabled)
804 {
805 BlitGL *blitter = GetBlitGL(context);
806 ANGLE_TRY(blitter->copySubImageToLUMAWorkaroundTexture(
807 context, mTextureID, getType(), target, levelInfo.sourceFormat, level, clippedOffset,
808 clippedArea, source));
809 }
810 else
811 {
812 if (nativegl::UseTexImage2D(getType()))
813 {
814 ASSERT(clippedOffset.z == 0);
815 if (features.emulateCopyTexImage2DFromRenderbuffers.enabled &&
816 source->getReadColorAttachment() &&
817 source->getReadColorAttachment()->type() == GL_RENDERBUFFER)
818 {
819 BlitGL *blitter = GetBlitGL(context);
820 ANGLE_TRY(blitter->blitColorBufferWithShader(
821 context, source, mTextureID, target, level, clippedArea,
822 gl::Rectangle(clippedOffset.x, clippedOffset.y, clippedArea.width,
823 clippedArea.height),
824 GL_NEAREST, true));
825 }
826 else
827 {
828 ANGLE_GL_TRY(context, functions->copyTexSubImage2D(
829 ToGLenum(target), static_cast<GLint>(level),
830 clippedOffset.x, clippedOffset.y, clippedArea.x,
831 clippedArea.y, clippedArea.width, clippedArea.height));
832 }
833 }
834 else
835 {
836 ASSERT(nativegl::UseTexImage3D(getType()));
837 ANGLE_GL_TRY(context, functions->copyTexSubImage3D(
838 ToGLenum(target), static_cast<GLint>(level), clippedOffset.x,
839 clippedOffset.y, clippedOffset.z, clippedArea.x,
840 clippedArea.y, clippedArea.width, clippedArea.height));
841 }
842 }
843
844 if (features.flushBeforeDeleteTextureIfCopiedTo.enabled)
845 {
846 ContextGL *contextGL = GetImplAs<ContextGL>(context);
847 contextGL->setNeedsFlushBeforeDeleteTextures();
848 }
849
850 return angle::Result::Continue;
851 }
852
copyTexture(const gl::Context * context,const gl::ImageIndex & index,GLenum internalFormat,GLenum type,GLint sourceLevel,bool unpackFlipY,bool unpackPremultiplyAlpha,bool unpackUnmultiplyAlpha,const gl::Texture * source)853 angle::Result TextureGL::copyTexture(const gl::Context *context,
854 const gl::ImageIndex &index,
855 GLenum internalFormat,
856 GLenum type,
857 GLint sourceLevel,
858 bool unpackFlipY,
859 bool unpackPremultiplyAlpha,
860 bool unpackUnmultiplyAlpha,
861 const gl::Texture *source)
862 {
863 gl::TextureTarget target = index.getTarget();
864 size_t level = static_cast<size_t>(index.getLevelIndex());
865 const TextureGL *sourceGL = GetImplAs<TextureGL>(source);
866 const gl::ImageDesc &sourceImageDesc =
867 sourceGL->mState.getImageDesc(NonCubeTextureTypeToTarget(source->getType()), sourceLevel);
868 gl::Rectangle sourceArea(0, 0, sourceImageDesc.size.width, sourceImageDesc.size.height);
869
870 ANGLE_TRY(reserveTexImageToBeFilled(context, target, level, internalFormat,
871 sourceImageDesc.size, gl::GetUnsizedFormat(internalFormat),
872 type));
873
874 const gl::InternalFormat &destFormatInfo = gl::GetInternalFormatInfo(internalFormat, type);
875 return copySubTextureHelper(context, target, level, gl::Offset(0, 0, 0), sourceLevel,
876 sourceArea, destFormatInfo, unpackFlipY, unpackPremultiplyAlpha,
877 unpackUnmultiplyAlpha, source);
878 }
879
copySubTexture(const gl::Context * context,const gl::ImageIndex & index,const gl::Offset & destOffset,GLint sourceLevel,const gl::Box & sourceBox,bool unpackFlipY,bool unpackPremultiplyAlpha,bool unpackUnmultiplyAlpha,const gl::Texture * source)880 angle::Result TextureGL::copySubTexture(const gl::Context *context,
881 const gl::ImageIndex &index,
882 const gl::Offset &destOffset,
883 GLint sourceLevel,
884 const gl::Box &sourceBox,
885 bool unpackFlipY,
886 bool unpackPremultiplyAlpha,
887 bool unpackUnmultiplyAlpha,
888 const gl::Texture *source)
889 {
890 gl::TextureTarget target = index.getTarget();
891 size_t level = static_cast<size_t>(index.getLevelIndex());
892 const gl::InternalFormat &destFormatInfo = *mState.getImageDesc(target, level).format.info;
893 return copySubTextureHelper(context, target, level, destOffset, sourceLevel, sourceBox.toRect(),
894 destFormatInfo, unpackFlipY, unpackPremultiplyAlpha,
895 unpackUnmultiplyAlpha, source);
896 }
897
copySubTextureHelper(const gl::Context * context,gl::TextureTarget target,size_t level,const gl::Offset & destOffset,GLint sourceLevel,const gl::Rectangle & sourceArea,const gl::InternalFormat & destFormat,bool unpackFlipY,bool unpackPremultiplyAlpha,bool unpackUnmultiplyAlpha,const gl::Texture * source)898 angle::Result TextureGL::copySubTextureHelper(const gl::Context *context,
899 gl::TextureTarget target,
900 size_t level,
901 const gl::Offset &destOffset,
902 GLint sourceLevel,
903 const gl::Rectangle &sourceArea,
904 const gl::InternalFormat &destFormat,
905 bool unpackFlipY,
906 bool unpackPremultiplyAlpha,
907 bool unpackUnmultiplyAlpha,
908 const gl::Texture *source)
909 {
910 const FunctionsGL *functions = GetFunctionsGL(context);
911 const angle::FeaturesGL &features = GetFeaturesGL(context);
912 BlitGL *blitter = GetBlitGL(context);
913
914 TextureGL *sourceGL = GetImplAs<TextureGL>(source);
915 const gl::ImageDesc &sourceImageDesc =
916 sourceGL->mState.getImageDesc(NonCubeTextureTypeToTarget(source->getType()), sourceLevel);
917
918 if (features.flushBeforeDeleteTextureIfCopiedTo.enabled)
919 {
920 // Conservatively indicate that this workaround is necessary. Not clear
921 // if it is on this code path, but added for symmetry.
922 ContextGL *contextGL = GetImplAs<ContextGL>(context);
923 contextGL->setNeedsFlushBeforeDeleteTextures();
924 }
925
926 // Check is this is a simple copySubTexture that can be done with a copyTexSubImage
927 ASSERT(sourceGL->getType() == gl::TextureType::_2D ||
928 source->getType() == gl::TextureType::External ||
929 source->getType() == gl::TextureType::Rectangle);
930 const LevelInfoGL &sourceLevelInfo =
931 sourceGL->getLevelInfo(NonCubeTextureTypeToTarget(source->getType()), sourceLevel);
932 bool needsLumaWorkaround = sourceLevelInfo.lumaWorkaround.enabled;
933
934 const gl::InternalFormat &sourceFormatInfo = *sourceImageDesc.format.info;
935 GLenum sourceFormat = sourceFormatInfo.format;
936 bool sourceFormatContainSupersetOfDestFormat =
937 (sourceFormat == destFormat.format && sourceFormat != GL_BGRA_EXT) ||
938 (sourceFormat == GL_RGBA && destFormat.format == GL_RGB);
939
940 GLenum sourceComponentType = sourceFormatInfo.componentType;
941 GLenum destComponentType = destFormat.componentType;
942 bool destSRGB = destFormat.colorEncoding == GL_SRGB;
943 if (!unpackFlipY && unpackPremultiplyAlpha == unpackUnmultiplyAlpha && !needsLumaWorkaround &&
944 sourceFormatContainSupersetOfDestFormat && sourceComponentType == destComponentType &&
945 !destSRGB && sourceGL->getType() == gl::TextureType::_2D)
946 {
947 bool copySucceeded = false;
948 ANGLE_TRY(blitter->copyTexSubImage(context, sourceGL, sourceLevel, this, target, level,
949 sourceArea, destOffset, ©Succeeded));
950 if (copySucceeded)
951 {
952 return angle::Result::Continue;
953 }
954 }
955
956 // Check if the destination is renderable and copy on the GPU
957 const LevelInfoGL &destLevelInfo = getLevelInfo(target, level);
958 // todo(jonahr): http://crbug.com/773861
959 // Behavior for now is to fallback to CPU readback implementation if the destination texture
960 // is a luminance format. The correct solution is to handle both source and destination in the
961 // luma workaround.
962 if (!destSRGB && !destLevelInfo.lumaWorkaround.enabled &&
963 nativegl::SupportsNativeRendering(functions, getType(), destLevelInfo.nativeInternalFormat))
964 {
965 bool copySucceeded = false;
966 ANGLE_TRY(blitter->copySubTexture(
967 context, sourceGL, sourceLevel, sourceComponentType, mTextureID, target, level,
968 destComponentType, sourceImageDesc.size, sourceArea, destOffset, needsLumaWorkaround,
969 sourceLevelInfo.sourceFormat, unpackFlipY, unpackPremultiplyAlpha,
970 unpackUnmultiplyAlpha, ©Succeeded));
971 if (copySucceeded)
972 {
973 return angle::Result::Continue;
974 }
975 }
976
977 // Fall back to CPU-readback
978 return blitter->copySubTextureCPUReadback(
979 context, sourceGL, sourceLevel, sourceFormatInfo.sizedInternalFormat, this, target, level,
980 destFormat.format, destFormat.type, sourceImageDesc.size, sourceArea, destOffset,
981 needsLumaWorkaround, sourceLevelInfo.sourceFormat, unpackFlipY, unpackPremultiplyAlpha,
982 unpackUnmultiplyAlpha);
983 }
984
setStorage(const gl::Context * context,gl::TextureType type,size_t levels,GLenum internalFormat,const gl::Extents & size)985 angle::Result TextureGL::setStorage(const gl::Context *context,
986 gl::TextureType type,
987 size_t levels,
988 GLenum internalFormat,
989 const gl::Extents &size)
990 {
991 ContextGL *contextGL = GetImplAs<ContextGL>(context);
992 const FunctionsGL *functions = GetFunctionsGL(context);
993 StateManagerGL *stateManager = GetStateManagerGL(context);
994 const angle::FeaturesGL &features = GetFeaturesGL(context);
995
996 nativegl::TexStorageFormat texStorageFormat =
997 nativegl::GetTexStorageFormat(functions, features, internalFormat);
998
999 stateManager->bindTexture(getType(), mTextureID);
1000 if (nativegl::UseTexImage2D(getType()))
1001 {
1002 ASSERT(size.depth == 1);
1003 if (functions->texStorage2D)
1004 {
1005 ANGLE_GL_TRY_ALWAYS_CHECK(
1006 context,
1007 functions->texStorage2D(ToGLenum(type), static_cast<GLsizei>(levels),
1008 texStorageFormat.internalFormat, size.width, size.height));
1009 }
1010 else
1011 {
1012 // Make sure no pixel unpack buffer is bound
1013 stateManager->bindBuffer(gl::BufferBinding::PixelUnpack, 0);
1014
1015 const gl::InternalFormat &internalFormatInfo =
1016 gl::GetSizedInternalFormatInfo(internalFormat);
1017
1018 // Internal format must be sized
1019 ASSERT(internalFormatInfo.sized);
1020
1021 for (size_t level = 0; level < levels; level++)
1022 {
1023 gl::Extents levelSize(std::max(size.width >> level, 1),
1024 std::max(size.height >> level, 1), 1);
1025
1026 if (getType() == gl::TextureType::_2D || getType() == gl::TextureType::Rectangle)
1027 {
1028 if (internalFormatInfo.compressed)
1029 {
1030 nativegl::CompressedTexSubImageFormat compressedTexImageFormat =
1031 nativegl::GetCompressedSubTexImageFormat(functions, features,
1032 internalFormat);
1033
1034 GLuint dataSize = 0;
1035 ANGLE_CHECK_GL_MATH(
1036 contextGL,
1037 internalFormatInfo.computeCompressedImageSize(levelSize, &dataSize));
1038 ANGLE_GL_TRY_ALWAYS_CHECK(
1039 context,
1040 functions->compressedTexImage2D(
1041 ToGLenum(type), static_cast<GLint>(level),
1042 compressedTexImageFormat.format, levelSize.width, levelSize.height,
1043 0, static_cast<GLsizei>(dataSize), nullptr));
1044 }
1045 else
1046 {
1047 nativegl::TexImageFormat texImageFormat = nativegl::GetTexImageFormat(
1048 functions, features, internalFormat, internalFormatInfo.format,
1049 internalFormatInfo.type);
1050
1051 ANGLE_GL_TRY_ALWAYS_CHECK(
1052 context,
1053 functions->texImage2D(ToGLenum(type), static_cast<GLint>(level),
1054 texImageFormat.internalFormat, levelSize.width,
1055 levelSize.height, 0, texImageFormat.format,
1056 texImageFormat.type, nullptr));
1057 }
1058 }
1059 else
1060 {
1061 ASSERT(getType() == gl::TextureType::CubeMap);
1062 for (gl::TextureTarget face : gl::AllCubeFaceTextureTargets())
1063 {
1064 if (internalFormatInfo.compressed)
1065 {
1066 nativegl::CompressedTexSubImageFormat compressedTexImageFormat =
1067 nativegl::GetCompressedSubTexImageFormat(functions, features,
1068 internalFormat);
1069
1070 GLuint dataSize = 0;
1071 ANGLE_CHECK_GL_MATH(contextGL,
1072 internalFormatInfo.computeCompressedImageSize(
1073 levelSize, &dataSize));
1074 ANGLE_GL_TRY_ALWAYS_CHECK(
1075 context,
1076 functions->compressedTexImage2D(
1077 ToGLenum(face), static_cast<GLint>(level),
1078 compressedTexImageFormat.format, levelSize.width,
1079 levelSize.height, 0, static_cast<GLsizei>(dataSize), nullptr));
1080 }
1081 else
1082 {
1083 nativegl::TexImageFormat texImageFormat = nativegl::GetTexImageFormat(
1084 functions, features, internalFormat, internalFormatInfo.format,
1085 internalFormatInfo.type);
1086
1087 ANGLE_GL_TRY_ALWAYS_CHECK(
1088 context, functions->texImage2D(
1089 ToGLenum(face), static_cast<GLint>(level),
1090 texImageFormat.internalFormat, levelSize.width,
1091 levelSize.height, 0, texImageFormat.format,
1092 texImageFormat.type, nullptr));
1093 }
1094 }
1095 }
1096 }
1097 }
1098 }
1099 else
1100 {
1101 ASSERT(nativegl::UseTexImage3D(getType()));
1102 if (functions->texStorage3D)
1103 {
1104 ANGLE_GL_TRY_ALWAYS_CHECK(
1105 context, functions->texStorage3D(ToGLenum(type), static_cast<GLsizei>(levels),
1106 texStorageFormat.internalFormat, size.width,
1107 size.height, size.depth));
1108 }
1109 else
1110 {
1111 // Make sure no pixel unpack buffer is bound
1112 stateManager->bindBuffer(gl::BufferBinding::PixelUnpack, 0);
1113
1114 const gl::InternalFormat &internalFormatInfo =
1115 gl::GetSizedInternalFormatInfo(internalFormat);
1116
1117 // Internal format must be sized
1118 ASSERT(internalFormatInfo.sized);
1119
1120 for (GLsizei i = 0; i < static_cast<GLsizei>(levels); i++)
1121 {
1122 gl::Extents levelSize(
1123 std::max(size.width >> i, 1), std::max(size.height >> i, 1),
1124 getType() == gl::TextureType::_3D ? std::max(size.depth >> i, 1) : size.depth);
1125
1126 if (internalFormatInfo.compressed)
1127 {
1128 nativegl::CompressedTexSubImageFormat compressedTexImageFormat =
1129 nativegl::GetCompressedSubTexImageFormat(functions, features,
1130 internalFormat);
1131
1132 GLuint dataSize = 0;
1133 ANGLE_CHECK_GL_MATH(contextGL, internalFormatInfo.computeCompressedImageSize(
1134 levelSize, &dataSize));
1135 ANGLE_GL_TRY_ALWAYS_CHECK(
1136 context, functions->compressedTexImage3D(
1137 ToGLenum(type), i, compressedTexImageFormat.format,
1138 levelSize.width, levelSize.height, levelSize.depth, 0,
1139 static_cast<GLsizei>(dataSize), nullptr));
1140 }
1141 else
1142 {
1143 nativegl::TexImageFormat texImageFormat = nativegl::GetTexImageFormat(
1144 functions, features, internalFormat, internalFormatInfo.format,
1145 internalFormatInfo.type);
1146
1147 ANGLE_GL_TRY_ALWAYS_CHECK(
1148 context,
1149 functions->texImage3D(ToGLenum(type), i, texImageFormat.internalFormat,
1150 levelSize.width, levelSize.height, levelSize.depth, 0,
1151 texImageFormat.format, texImageFormat.type, nullptr));
1152 }
1153 }
1154 }
1155 }
1156
1157 setLevelInfo(context, type, 0, levels,
1158 GetLevelInfo(features, internalFormat, texStorageFormat.internalFormat));
1159
1160 return angle::Result::Continue;
1161 }
1162
setImageExternal(const gl::Context * context,const gl::ImageIndex & index,GLenum internalFormat,const gl::Extents & size,GLenum format,GLenum type)1163 angle::Result TextureGL::setImageExternal(const gl::Context *context,
1164 const gl::ImageIndex &index,
1165 GLenum internalFormat,
1166 const gl::Extents &size,
1167 GLenum format,
1168 GLenum type)
1169 {
1170 const FunctionsGL *functions = GetFunctionsGL(context);
1171 const angle::FeaturesGL &features = GetFeaturesGL(context);
1172
1173 gl::TextureTarget target = index.getTarget();
1174 size_t level = static_cast<size_t>(index.getLevelIndex());
1175 nativegl::TexImageFormat texImageFormat =
1176 nativegl::GetTexImageFormat(functions, features, internalFormat, format, type);
1177
1178 setLevelInfo(context, target, level, 1,
1179 GetLevelInfo(features, internalFormat, texImageFormat.internalFormat));
1180 return angle::Result::Continue;
1181 }
1182
setStorageMultisample(const gl::Context * context,gl::TextureType type,GLsizei samples,GLint internalformat,const gl::Extents & size,bool fixedSampleLocations)1183 angle::Result TextureGL::setStorageMultisample(const gl::Context *context,
1184 gl::TextureType type,
1185 GLsizei samples,
1186 GLint internalformat,
1187 const gl::Extents &size,
1188 bool fixedSampleLocations)
1189 {
1190 const FunctionsGL *functions = GetFunctionsGL(context);
1191 StateManagerGL *stateManager = GetStateManagerGL(context);
1192 const angle::FeaturesGL &features = GetFeaturesGL(context);
1193
1194 nativegl::TexStorageFormat texStorageFormat =
1195 nativegl::GetTexStorageFormat(functions, features, internalformat);
1196
1197 stateManager->bindTexture(getType(), mTextureID);
1198
1199 if (nativegl::UseTexImage2D(getType()))
1200 {
1201 ASSERT(size.depth == 1);
1202 if (functions->texStorage2DMultisample)
1203 {
1204 ANGLE_GL_TRY_ALWAYS_CHECK(
1205 context, functions->texStorage2DMultisample(
1206 ToGLenum(type), samples, texStorageFormat.internalFormat, size.width,
1207 size.height, gl::ConvertToGLBoolean(fixedSampleLocations)));
1208 }
1209 else
1210 {
1211 // texImage2DMultisample is similar to texStorage2DMultisample of es 3.1 core feature,
1212 // On macos and some old drivers which doesn't support OpenGL ES 3.1, the function can
1213 // be supported by ARB_texture_multisample or OpenGL 3.2 core feature.
1214 ANGLE_GL_TRY_ALWAYS_CHECK(
1215 context, functions->texImage2DMultisample(
1216 ToGLenum(type), samples, texStorageFormat.internalFormat, size.width,
1217 size.height, gl::ConvertToGLBoolean(fixedSampleLocations)));
1218 }
1219 }
1220 else
1221 {
1222 ASSERT(nativegl::UseTexImage3D(getType()));
1223 ANGLE_GL_TRY_ALWAYS_CHECK(
1224 context, functions->texStorage3DMultisample(
1225 ToGLenum(type), samples, texStorageFormat.internalFormat, size.width,
1226 size.height, size.depth, gl::ConvertToGLBoolean(fixedSampleLocations)));
1227 }
1228
1229 setLevelInfo(context, type, 0, 1,
1230 GetLevelInfo(features, internalformat, texStorageFormat.internalFormat));
1231
1232 return angle::Result::Continue;
1233 }
1234
setStorageExternalMemory(const gl::Context * context,gl::TextureType type,size_t levels,GLenum internalFormat,const gl::Extents & size,gl::MemoryObject * memoryObject,GLuint64 offset,GLbitfield createFlags,GLbitfield usageFlags)1235 angle::Result TextureGL::setStorageExternalMemory(const gl::Context *context,
1236 gl::TextureType type,
1237 size_t levels,
1238 GLenum internalFormat,
1239 const gl::Extents &size,
1240 gl::MemoryObject *memoryObject,
1241 GLuint64 offset,
1242 GLbitfield createFlags,
1243 GLbitfield usageFlags)
1244 {
1245 // GL_ANGLE_external_objects_flags not supported.
1246 ASSERT(createFlags == 0);
1247 ASSERT(usageFlags == std::numeric_limits<uint32_t>::max());
1248
1249 const FunctionsGL *functions = GetFunctionsGL(context);
1250 StateManagerGL *stateManager = GetStateManagerGL(context);
1251 const angle::FeaturesGL &features = GetFeaturesGL(context);
1252
1253 MemoryObjectGL *memoryObjectGL = GetImplAs<MemoryObjectGL>(memoryObject);
1254
1255 nativegl::TexStorageFormat texStorageFormat =
1256 nativegl::GetTexStorageFormat(functions, features, internalFormat);
1257
1258 stateManager->bindTexture(getType(), mTextureID);
1259 if (nativegl::UseTexImage2D(getType()))
1260 {
1261 ANGLE_GL_TRY_ALWAYS_CHECK(
1262 context,
1263 functions->texStorageMem2DEXT(ToGLenum(type), static_cast<GLsizei>(levels),
1264 texStorageFormat.internalFormat, size.width, size.height,
1265 memoryObjectGL->getMemoryObjectID(), offset));
1266 }
1267 else
1268 {
1269 ASSERT(nativegl::UseTexImage3D(getType()));
1270 ANGLE_GL_TRY_ALWAYS_CHECK(
1271 context,
1272 functions->texStorageMem3DEXT(ToGLenum(type), static_cast<GLsizei>(levels),
1273 texStorageFormat.internalFormat, size.width, size.height,
1274 size.depth, memoryObjectGL->getMemoryObjectID(), offset));
1275 }
1276
1277 setLevelInfo(context, type, 0, levels,
1278 GetLevelInfo(features, internalFormat, texStorageFormat.internalFormat));
1279
1280 return angle::Result::Continue;
1281 }
1282
setImageExternal(const gl::Context * context,gl::TextureType type,egl::Stream * stream,const egl::Stream::GLTextureDescription & desc)1283 angle::Result TextureGL::setImageExternal(const gl::Context *context,
1284 gl::TextureType type,
1285 egl::Stream *stream,
1286 const egl::Stream::GLTextureDescription &desc)
1287 {
1288 ANGLE_GL_UNREACHABLE(GetImplAs<ContextGL>(context));
1289 return angle::Result::Stop;
1290 }
1291
generateMipmap(const gl::Context * context)1292 angle::Result TextureGL::generateMipmap(const gl::Context *context)
1293 {
1294 const FunctionsGL *functions = GetFunctionsGL(context);
1295 StateManagerGL *stateManager = GetStateManagerGL(context);
1296 const angle::FeaturesGL &features = GetFeaturesGL(context);
1297
1298 const GLuint effectiveBaseLevel = mState.getEffectiveBaseLevel();
1299 const GLuint maxLevel = mState.getMipmapMaxLevel();
1300
1301 const gl::ImageDesc &baseLevelDesc = mState.getBaseLevelDesc();
1302 const gl::InternalFormat &baseLevelInternalFormat = *baseLevelDesc.format.info;
1303
1304 stateManager->bindTexture(getType(), mTextureID);
1305 if (baseLevelInternalFormat.colorEncoding == GL_SRGB &&
1306 features.encodeAndDecodeSRGBForGenerateMipmap.enabled && getType() == gl::TextureType::_2D)
1307 {
1308 nativegl::TexImageFormat texImageFormat = nativegl::GetTexImageFormat(
1309 functions, features, baseLevelInternalFormat.internalFormat,
1310 baseLevelInternalFormat.format, baseLevelInternalFormat.type);
1311
1312 // Manually allocate the mip levels of this texture if they don't exist
1313 GLuint levelCount = maxLevel - effectiveBaseLevel + 1;
1314 for (GLuint levelIdx = 1; levelIdx < levelCount; levelIdx++)
1315 {
1316 gl::Extents levelSize(std::max(baseLevelDesc.size.width >> levelIdx, 1),
1317 std::max(baseLevelDesc.size.height >> levelIdx, 1), 1);
1318
1319 const gl::ImageDesc &levelDesc =
1320 mState.getImageDesc(gl::TextureTarget::_2D, effectiveBaseLevel + levelIdx);
1321
1322 // Make sure no pixel unpack buffer is bound
1323 stateManager->bindBuffer(gl::BufferBinding::PixelUnpack, 0);
1324
1325 if (levelDesc.size != levelSize || *levelDesc.format.info != baseLevelInternalFormat)
1326 {
1327 ANGLE_GL_TRY_ALWAYS_CHECK(
1328 context, functions->texImage2D(
1329 ToGLenum(getType()), effectiveBaseLevel + levelIdx,
1330 texImageFormat.internalFormat, levelSize.width, levelSize.height,
1331 0, texImageFormat.format, texImageFormat.type, nullptr));
1332 }
1333 }
1334
1335 // Use the blitter to generate the mips
1336 BlitGL *blitter = GetBlitGL(context);
1337 ANGLE_TRY(blitter->generateSRGBMipmap(context, this, effectiveBaseLevel, levelCount,
1338 baseLevelDesc.size));
1339 }
1340 else
1341 {
1342 ANGLE_GL_TRY_ALWAYS_CHECK(context, functions->generateMipmap(ToGLenum(getType())));
1343 }
1344
1345 setLevelInfo(context, getType(), effectiveBaseLevel, maxLevel - effectiveBaseLevel,
1346 getBaseLevelInfo());
1347
1348 return angle::Result::Continue;
1349 }
1350
bindTexImage(const gl::Context * context,egl::Surface * surface)1351 angle::Result TextureGL::bindTexImage(const gl::Context *context, egl::Surface *surface)
1352 {
1353 ASSERT(getType() == gl::TextureType::_2D || getType() == gl::TextureType::Rectangle);
1354
1355 StateManagerGL *stateManager = GetStateManagerGL(context);
1356
1357 // Make sure this texture is bound
1358 stateManager->bindTexture(getType(), mTextureID);
1359
1360 SurfaceGL *surfaceGL = GetImplAs<SurfaceGL>(surface);
1361
1362 const gl::Format &surfaceFormat = surface->getBindTexImageFormat();
1363 setLevelInfo(context, getType(), 0, 1,
1364 LevelInfoGL(surfaceFormat.info->format, surfaceFormat.info->internalFormat, false,
1365 LUMAWorkaroundGL(), surfaceGL->hasEmulatedAlphaChannel()));
1366 return angle::Result::Continue;
1367 }
1368
releaseTexImage(const gl::Context * context)1369 angle::Result TextureGL::releaseTexImage(const gl::Context *context)
1370 {
1371 ASSERT(getType() == gl::TextureType::_2D || getType() == gl::TextureType::Rectangle);
1372
1373 const angle::FeaturesGL &features = GetFeaturesGL(context);
1374 if (!features.resettingTexturesGeneratesErrors.enabled)
1375 {
1376 // Not all Surface implementations reset the size of mip 0 when releasing, do it manually
1377 const FunctionsGL *functions = GetFunctionsGL(context);
1378 StateManagerGL *stateManager = GetStateManagerGL(context);
1379
1380 stateManager->bindTexture(getType(), mTextureID);
1381 ASSERT(nativegl::UseTexImage2D(getType()));
1382 ANGLE_GL_TRY(context, functions->texImage2D(ToGLenum(getType()), 0, GL_RGBA, 0, 0, 0,
1383 GL_RGBA, GL_UNSIGNED_BYTE, nullptr));
1384 }
1385
1386 return angle::Result::Continue;
1387 }
1388
setEGLImageTarget(const gl::Context * context,gl::TextureType type,egl::Image * image)1389 angle::Result TextureGL::setEGLImageTarget(const gl::Context *context,
1390 gl::TextureType type,
1391 egl::Image *image)
1392 {
1393 const angle::FeaturesGL &features = GetFeaturesGL(context);
1394
1395 ImageGL *imageGL = GetImplAs<ImageGL>(image);
1396
1397 GLenum imageNativeInternalFormat = GL_NONE;
1398 ANGLE_TRY(imageGL->setTexture2D(context, type, this, &imageNativeInternalFormat));
1399
1400 setLevelInfo(
1401 context, type, 0, 1,
1402 GetLevelInfo(features, image->getFormat().info->internalFormat, imageNativeInternalFormat));
1403
1404 return angle::Result::Continue;
1405 }
1406
getNativeID() const1407 GLint TextureGL::getNativeID() const
1408 {
1409 return mTextureID;
1410 }
1411
syncState(const gl::Context * context,const gl::Texture::DirtyBits & dirtyBits,gl::Command source)1412 angle::Result TextureGL::syncState(const gl::Context *context,
1413 const gl::Texture::DirtyBits &dirtyBits,
1414 gl::Command source)
1415 {
1416 if (dirtyBits.none() && mLocalDirtyBits.none())
1417 {
1418 return angle::Result::Continue;
1419 }
1420
1421 const FunctionsGL *functions = GetFunctionsGL(context);
1422 StateManagerGL *stateManager = GetStateManagerGL(context);
1423
1424 stateManager->bindTexture(getType(), mTextureID);
1425
1426 if (dirtyBits[gl::Texture::DIRTY_BIT_BASE_LEVEL] || dirtyBits[gl::Texture::DIRTY_BIT_MAX_LEVEL])
1427 {
1428 // Don't know if the previous base level was using any workarounds, always re-sync the
1429 // workaround dirty bits
1430 mLocalDirtyBits |= GetLevelWorkaroundDirtyBits();
1431 }
1432 for (auto dirtyBit : (dirtyBits | mLocalDirtyBits))
1433 {
1434
1435 switch (dirtyBit)
1436 {
1437 case gl::Texture::DIRTY_BIT_MIN_FILTER:
1438 mAppliedSampler.setMinFilter(mState.getSamplerState().getMinFilter());
1439 ANGLE_GL_TRY(context, functions->texParameteri(
1440 nativegl::GetTextureBindingTarget(getType()),
1441 GL_TEXTURE_MIN_FILTER, mAppliedSampler.getMinFilter()));
1442 break;
1443 case gl::Texture::DIRTY_BIT_MAG_FILTER:
1444 mAppliedSampler.setMagFilter(mState.getSamplerState().getMagFilter());
1445 ANGLE_GL_TRY(context, functions->texParameteri(
1446 nativegl::GetTextureBindingTarget(getType()),
1447 GL_TEXTURE_MAG_FILTER, mAppliedSampler.getMagFilter()));
1448 break;
1449 case gl::Texture::DIRTY_BIT_WRAP_S:
1450 mAppliedSampler.setWrapS(mState.getSamplerState().getWrapS());
1451 ANGLE_GL_TRY(context, functions->texParameteri(
1452 nativegl::GetTextureBindingTarget(getType()),
1453 GL_TEXTURE_WRAP_S, mAppliedSampler.getWrapS()));
1454 break;
1455 case gl::Texture::DIRTY_BIT_WRAP_T:
1456 mAppliedSampler.setWrapT(mState.getSamplerState().getWrapT());
1457 ANGLE_GL_TRY(context, functions->texParameteri(
1458 nativegl::GetTextureBindingTarget(getType()),
1459 GL_TEXTURE_WRAP_T, mAppliedSampler.getWrapT()));
1460 break;
1461 case gl::Texture::DIRTY_BIT_WRAP_R:
1462 mAppliedSampler.setWrapR(mState.getSamplerState().getWrapR());
1463 ANGLE_GL_TRY(context, functions->texParameteri(
1464 nativegl::GetTextureBindingTarget(getType()),
1465 GL_TEXTURE_WRAP_R, mAppliedSampler.getWrapR()));
1466 break;
1467 case gl::Texture::DIRTY_BIT_MAX_ANISOTROPY:
1468 mAppliedSampler.setMaxAnisotropy(mState.getSamplerState().getMaxAnisotropy());
1469 ANGLE_GL_TRY(context,
1470 functions->texParameterf(nativegl::GetTextureBindingTarget(getType()),
1471 GL_TEXTURE_MAX_ANISOTROPY_EXT,
1472 mAppliedSampler.getMaxAnisotropy()));
1473 break;
1474 case gl::Texture::DIRTY_BIT_MIN_LOD:
1475 mAppliedSampler.setMinLod(mState.getSamplerState().getMinLod());
1476 ANGLE_GL_TRY(context, functions->texParameterf(
1477 nativegl::GetTextureBindingTarget(getType()),
1478 GL_TEXTURE_MIN_LOD, mAppliedSampler.getMinLod()));
1479 break;
1480 case gl::Texture::DIRTY_BIT_MAX_LOD:
1481 mAppliedSampler.setMaxLod(mState.getSamplerState().getMaxLod());
1482 ANGLE_GL_TRY(context, functions->texParameterf(
1483 nativegl::GetTextureBindingTarget(getType()),
1484 GL_TEXTURE_MAX_LOD, mAppliedSampler.getMaxLod()));
1485 break;
1486 case gl::Texture::DIRTY_BIT_COMPARE_MODE:
1487 mAppliedSampler.setCompareMode(mState.getSamplerState().getCompareMode());
1488 ANGLE_GL_TRY(context,
1489 functions->texParameteri(nativegl::GetTextureBindingTarget(getType()),
1490 GL_TEXTURE_COMPARE_MODE,
1491 mAppliedSampler.getCompareMode()));
1492 break;
1493 case gl::Texture::DIRTY_BIT_COMPARE_FUNC:
1494 mAppliedSampler.setCompareFunc(mState.getSamplerState().getCompareFunc());
1495 ANGLE_GL_TRY(context,
1496 functions->texParameteri(nativegl::GetTextureBindingTarget(getType()),
1497 GL_TEXTURE_COMPARE_FUNC,
1498 mAppliedSampler.getCompareFunc()));
1499 break;
1500 case gl::Texture::DIRTY_BIT_SRGB_DECODE:
1501 mAppliedSampler.setSRGBDecode(mState.getSamplerState().getSRGBDecode());
1502 ANGLE_GL_TRY(context,
1503 functions->texParameteri(nativegl::GetTextureBindingTarget(getType()),
1504 GL_TEXTURE_SRGB_DECODE_EXT,
1505 mAppliedSampler.getSRGBDecode()));
1506 break;
1507 case gl::Texture::DIRTY_BIT_BORDER_COLOR:
1508 {
1509 const angle::ColorGeneric &borderColor(mState.getSamplerState().getBorderColor());
1510 mAppliedSampler.setBorderColor(borderColor);
1511 switch (borderColor.type)
1512 {
1513 case angle::ColorGeneric::Type::Float:
1514 ANGLE_GL_TRY(context,
1515 functions->texParameterfv(
1516 nativegl::GetTextureBindingTarget(getType()),
1517 GL_TEXTURE_BORDER_COLOR, &borderColor.colorF.red));
1518 break;
1519 case angle::ColorGeneric::Type::Int:
1520 ANGLE_GL_TRY(context,
1521 functions->texParameterIiv(
1522 nativegl::GetTextureBindingTarget(getType()),
1523 GL_TEXTURE_BORDER_COLOR, &borderColor.colorI.red));
1524 break;
1525 case angle::ColorGeneric::Type::UInt:
1526 ANGLE_GL_TRY(context,
1527 functions->texParameterIuiv(
1528 nativegl::GetTextureBindingTarget(getType()),
1529 GL_TEXTURE_BORDER_COLOR, &borderColor.colorUI.red));
1530 break;
1531 default:
1532 UNREACHABLE();
1533 break;
1534 }
1535 break;
1536 }
1537
1538 // Texture state
1539 case gl::Texture::DIRTY_BIT_SWIZZLE_RED:
1540 ANGLE_TRY(syncTextureStateSwizzle(context, functions, GL_TEXTURE_SWIZZLE_R,
1541 mState.getSwizzleState().swizzleRed,
1542 &mAppliedSwizzle.swizzleRed));
1543 break;
1544 case gl::Texture::DIRTY_BIT_SWIZZLE_GREEN:
1545 ANGLE_TRY(syncTextureStateSwizzle(context, functions, GL_TEXTURE_SWIZZLE_G,
1546 mState.getSwizzleState().swizzleGreen,
1547 &mAppliedSwizzle.swizzleGreen));
1548 break;
1549 case gl::Texture::DIRTY_BIT_SWIZZLE_BLUE:
1550 ANGLE_TRY(syncTextureStateSwizzle(context, functions, GL_TEXTURE_SWIZZLE_B,
1551 mState.getSwizzleState().swizzleBlue,
1552 &mAppliedSwizzle.swizzleBlue));
1553 break;
1554 case gl::Texture::DIRTY_BIT_SWIZZLE_ALPHA:
1555 ANGLE_TRY(syncTextureStateSwizzle(context, functions, GL_TEXTURE_SWIZZLE_A,
1556 mState.getSwizzleState().swizzleAlpha,
1557 &mAppliedSwizzle.swizzleAlpha));
1558 break;
1559 case gl::Texture::DIRTY_BIT_BASE_LEVEL:
1560 mAppliedBaseLevel = mState.getEffectiveBaseLevel();
1561 ANGLE_GL_TRY(context,
1562 functions->texParameteri(nativegl::GetTextureBindingTarget(getType()),
1563 GL_TEXTURE_BASE_LEVEL, mAppliedBaseLevel));
1564 break;
1565 case gl::Texture::DIRTY_BIT_MAX_LEVEL:
1566 mAppliedMaxLevel = mState.getEffectiveMaxLevel();
1567 ANGLE_GL_TRY(context,
1568 functions->texParameteri(nativegl::GetTextureBindingTarget(getType()),
1569 GL_TEXTURE_MAX_LEVEL, mAppliedMaxLevel));
1570 break;
1571 case gl::Texture::DIRTY_BIT_DEPTH_STENCIL_TEXTURE_MODE:
1572 {
1573 GLenum mDepthStencilTextureMode = mState.getDepthStencilTextureMode();
1574 ANGLE_GL_TRY(context, functions->texParameteri(
1575 nativegl::GetTextureBindingTarget(getType()),
1576 GL_DEPTH_STENCIL_TEXTURE_MODE, mDepthStencilTextureMode));
1577 break;
1578 }
1579 case gl::Texture::DIRTY_BIT_USAGE:
1580 break;
1581 case gl::Texture::DIRTY_BIT_LABEL:
1582 break;
1583
1584 case gl::Texture::DIRTY_BIT_IMPLEMENTATION:
1585 // This special dirty bit is used to signal the front-end that the implementation
1586 // has local dirty bits. The real dirty bits are in mLocalDirty bits.
1587 break;
1588 case gl::Texture::DIRTY_BIT_BOUND_AS_IMAGE:
1589 // Only used for Vulkan.
1590 break;
1591
1592 default:
1593 UNREACHABLE();
1594 }
1595 }
1596
1597 mLocalDirtyBits.reset();
1598 return angle::Result::Continue;
1599 }
1600
hasAnyDirtyBit() const1601 bool TextureGL::hasAnyDirtyBit() const
1602 {
1603 return mLocalDirtyBits.any();
1604 }
1605
setBaseLevel(const gl::Context * context,GLuint baseLevel)1606 angle::Result TextureGL::setBaseLevel(const gl::Context *context, GLuint baseLevel)
1607 {
1608 if (baseLevel != mAppliedBaseLevel)
1609 {
1610 const FunctionsGL *functions = GetFunctionsGL(context);
1611 StateManagerGL *stateManager = GetStateManagerGL(context);
1612
1613 mAppliedBaseLevel = baseLevel;
1614 mLocalDirtyBits.set(gl::Texture::DIRTY_BIT_BASE_LEVEL);
1615
1616 // Signal to the GL layer that the Impl has dirty bits.
1617 onStateChange(angle::SubjectMessage::DirtyBitsFlagged);
1618
1619 stateManager->bindTexture(getType(), mTextureID);
1620 ANGLE_GL_TRY(context, functions->texParameteri(ToGLenum(getType()), GL_TEXTURE_BASE_LEVEL,
1621 baseLevel));
1622 }
1623 return angle::Result::Continue;
1624 }
1625
setMaxLevel(const gl::Context * context,GLuint maxLevel)1626 angle::Result TextureGL::setMaxLevel(const gl::Context *context, GLuint maxLevel)
1627 {
1628 if (maxLevel != mAppliedMaxLevel)
1629 {
1630 const FunctionsGL *functions = GetFunctionsGL(context);
1631 StateManagerGL *stateManager = GetStateManagerGL(context);
1632
1633 mAppliedMaxLevel = maxLevel;
1634 mLocalDirtyBits.set(gl::Texture::DIRTY_BIT_MAX_LEVEL);
1635
1636 // Signal to the GL layer that the Impl has dirty bits.
1637 onStateChange(angle::SubjectMessage::DirtyBitsFlagged);
1638
1639 stateManager->bindTexture(getType(), mTextureID);
1640 ANGLE_GL_TRY(context,
1641 functions->texParameteri(ToGLenum(getType()), GL_TEXTURE_MAX_LEVEL, maxLevel));
1642 }
1643 return angle::Result::Continue;
1644 }
1645
setMinFilter(const gl::Context * context,GLenum filter)1646 angle::Result TextureGL::setMinFilter(const gl::Context *context, GLenum filter)
1647 {
1648 if (filter != mAppliedSampler.getMinFilter())
1649 {
1650 const FunctionsGL *functions = GetFunctionsGL(context);
1651 StateManagerGL *stateManager = GetStateManagerGL(context);
1652
1653 mAppliedSampler.setMinFilter(filter);
1654 mLocalDirtyBits.set(gl::Texture::DIRTY_BIT_MIN_FILTER);
1655
1656 // Signal to the GL layer that the Impl has dirty bits.
1657 onStateChange(angle::SubjectMessage::DirtyBitsFlagged);
1658
1659 stateManager->bindTexture(getType(), mTextureID);
1660 ANGLE_GL_TRY(context,
1661 functions->texParameteri(ToGLenum(getType()), GL_TEXTURE_MIN_FILTER, filter));
1662 }
1663 return angle::Result::Continue;
1664 }
setMagFilter(const gl::Context * context,GLenum filter)1665 angle::Result TextureGL::setMagFilter(const gl::Context *context, GLenum filter)
1666 {
1667 if (filter != mAppliedSampler.getMagFilter())
1668 {
1669 const FunctionsGL *functions = GetFunctionsGL(context);
1670 StateManagerGL *stateManager = GetStateManagerGL(context);
1671
1672 mAppliedSampler.setMagFilter(filter);
1673 mLocalDirtyBits.set(gl::Texture::DIRTY_BIT_MAG_FILTER);
1674
1675 // Signal to the GL layer that the Impl has dirty bits.
1676 onStateChange(angle::SubjectMessage::DirtyBitsFlagged);
1677
1678 stateManager->bindTexture(getType(), mTextureID);
1679 ANGLE_GL_TRY(context,
1680 functions->texParameteri(ToGLenum(getType()), GL_TEXTURE_MAG_FILTER, filter));
1681 }
1682 return angle::Result::Continue;
1683 }
1684
setSwizzle(const gl::Context * context,GLint swizzle[4])1685 angle::Result TextureGL::setSwizzle(const gl::Context *context, GLint swizzle[4])
1686 {
1687 gl::SwizzleState resultingSwizzle =
1688 gl::SwizzleState(swizzle[0], swizzle[1], swizzle[2], swizzle[3]);
1689
1690 if (resultingSwizzle != mAppliedSwizzle)
1691 {
1692 const FunctionsGL *functions = GetFunctionsGL(context);
1693 StateManagerGL *stateManager = GetStateManagerGL(context);
1694
1695 mAppliedSwizzle = resultingSwizzle;
1696 mLocalDirtyBits.set(gl::Texture::DIRTY_BIT_SWIZZLE_RED);
1697 mLocalDirtyBits.set(gl::Texture::DIRTY_BIT_SWIZZLE_GREEN);
1698 mLocalDirtyBits.set(gl::Texture::DIRTY_BIT_SWIZZLE_BLUE);
1699 mLocalDirtyBits.set(gl::Texture::DIRTY_BIT_SWIZZLE_ALPHA);
1700
1701 // Signal to the GL layer that the Impl has dirty bits.
1702 onStateChange(angle::SubjectMessage::DirtyBitsFlagged);
1703
1704 stateManager->bindTexture(getType(), mTextureID);
1705 if (functions->standard == STANDARD_GL_ES)
1706 {
1707 ANGLE_GL_TRY(context, functions->texParameteri(ToGLenum(getType()),
1708 GL_TEXTURE_SWIZZLE_R, swizzle[0]));
1709 ANGLE_GL_TRY(context, functions->texParameteri(ToGLenum(getType()),
1710 GL_TEXTURE_SWIZZLE_G, swizzle[1]));
1711 ANGLE_GL_TRY(context, functions->texParameteri(ToGLenum(getType()),
1712 GL_TEXTURE_SWIZZLE_B, swizzle[2]));
1713 ANGLE_GL_TRY(context, functions->texParameteri(ToGLenum(getType()),
1714 GL_TEXTURE_SWIZZLE_A, swizzle[3]));
1715 }
1716 else
1717 {
1718 ANGLE_GL_TRY(context, functions->texParameteriv(ToGLenum(getType()),
1719 GL_TEXTURE_SWIZZLE_RGBA, swizzle));
1720 }
1721 }
1722 return angle::Result::Continue;
1723 }
1724
setBuffer(const gl::Context * context,GLenum internalFormat)1725 angle::Result TextureGL::setBuffer(const gl::Context *context, GLenum internalFormat)
1726 {
1727 const FunctionsGL *functions = GetFunctionsGL(context);
1728 const BufferGL *bufferGL = GetImplAs<BufferGL>(mState.mBuffer.get());
1729 ANGLE_GL_TRY(context, functions->texBufferRange(
1730 GL_TEXTURE_BUFFER, internalFormat, bufferGL->getBufferID(),
1731 mState.mBuffer.getOffset(), mState.mBuffer.getSize()));
1732
1733 return angle::Result::Continue;
1734 }
1735
getNativeInternalFormat(const gl::ImageIndex & index) const1736 GLenum TextureGL::getNativeInternalFormat(const gl::ImageIndex &index) const
1737 {
1738 return getLevelInfo(index.getTarget(), index.getLevelIndex()).nativeInternalFormat;
1739 }
1740
hasEmulatedAlphaChannel(const gl::ImageIndex & index) const1741 bool TextureGL::hasEmulatedAlphaChannel(const gl::ImageIndex &index) const
1742 {
1743 return getLevelInfo(index.getTargetOrFirstCubeFace(), index.getLevelIndex())
1744 .emulatedAlphaChannel;
1745 }
1746
syncTextureStateSwizzle(const gl::Context * context,const FunctionsGL * functions,GLenum name,GLenum value,GLenum * outValue)1747 angle::Result TextureGL::syncTextureStateSwizzle(const gl::Context *context,
1748 const FunctionsGL *functions,
1749 GLenum name,
1750 GLenum value,
1751 GLenum *outValue)
1752 {
1753 const LevelInfoGL &levelInfo = getBaseLevelInfo();
1754 GLenum resultSwizzle = value;
1755 if (levelInfo.lumaWorkaround.enabled)
1756 {
1757 switch (value)
1758 {
1759 case GL_RED:
1760 case GL_GREEN:
1761 case GL_BLUE:
1762 if (levelInfo.sourceFormat == GL_LUMINANCE ||
1763 levelInfo.sourceFormat == GL_LUMINANCE_ALPHA)
1764 {
1765 // Texture is backed by a RED or RG texture, point all color channels at the
1766 // red channel.
1767 ASSERT(levelInfo.lumaWorkaround.workaroundFormat == GL_RED ||
1768 levelInfo.lumaWorkaround.workaroundFormat == GL_RG);
1769 resultSwizzle = GL_RED;
1770 }
1771 else
1772 {
1773 ASSERT(levelInfo.sourceFormat == GL_ALPHA);
1774 // Color channels are not supposed to exist, make them always sample 0.
1775 resultSwizzle = GL_ZERO;
1776 }
1777 break;
1778
1779 case GL_ALPHA:
1780 if (levelInfo.sourceFormat == GL_LUMINANCE)
1781 {
1782 // Alpha channel is not supposed to exist, make it always sample 1.
1783 resultSwizzle = GL_ONE;
1784 }
1785 else if (levelInfo.sourceFormat == GL_ALPHA)
1786 {
1787 // Texture is backed by a RED texture, point the alpha channel at the red
1788 // channel.
1789 ASSERT(levelInfo.lumaWorkaround.workaroundFormat == GL_RED);
1790 resultSwizzle = GL_RED;
1791 }
1792 else
1793 {
1794 ASSERT(levelInfo.sourceFormat == GL_LUMINANCE_ALPHA);
1795 // Texture is backed by an RG texture, point the alpha channel at the green
1796 // channel.
1797 ASSERT(levelInfo.lumaWorkaround.workaroundFormat == GL_RG);
1798 resultSwizzle = GL_GREEN;
1799 }
1800 break;
1801
1802 case GL_ZERO:
1803 case GL_ONE:
1804 // Don't modify the swizzle state when requesting ZERO or ONE.
1805 resultSwizzle = value;
1806 break;
1807
1808 default:
1809 UNREACHABLE();
1810 break;
1811 }
1812 }
1813 else if (levelInfo.depthStencilWorkaround)
1814 {
1815 switch (value)
1816 {
1817 case GL_RED:
1818 // Don't modify the swizzle state when requesting the red channel.
1819 resultSwizzle = value;
1820 break;
1821
1822 case GL_GREEN:
1823 case GL_BLUE:
1824 if (context->getClientMajorVersion() <= 2)
1825 {
1826 // In OES_depth_texture/ARB_depth_texture, depth
1827 // textures are treated as luminance.
1828 resultSwizzle = GL_RED;
1829 }
1830 else
1831 {
1832 // In GLES 3.0, depth textures are treated as RED
1833 // textures, so green and blue should be 0.
1834 resultSwizzle = GL_ZERO;
1835 }
1836 break;
1837
1838 case GL_ALPHA:
1839 // Depth textures should sample 1 from the alpha channel.
1840 resultSwizzle = GL_ONE;
1841 break;
1842
1843 case GL_ZERO:
1844 case GL_ONE:
1845 // Don't modify the swizzle state when requesting ZERO or ONE.
1846 resultSwizzle = value;
1847 break;
1848
1849 default:
1850 UNREACHABLE();
1851 break;
1852 }
1853 }
1854 else if (levelInfo.emulatedAlphaChannel)
1855 {
1856 if (value == GL_ALPHA)
1857 {
1858 resultSwizzle = GL_ONE;
1859 }
1860 }
1861
1862 *outValue = resultSwizzle;
1863 ANGLE_GL_TRY(context, functions->texParameteri(ToGLenum(getType()), name, resultSwizzle));
1864
1865 return angle::Result::Continue;
1866 }
1867
setLevelInfo(const gl::Context * context,gl::TextureTarget target,size_t level,size_t levelCount,const LevelInfoGL & levelInfo)1868 void TextureGL::setLevelInfo(const gl::Context *context,
1869 gl::TextureTarget target,
1870 size_t level,
1871 size_t levelCount,
1872 const LevelInfoGL &levelInfo)
1873 {
1874 ASSERT(levelCount > 0);
1875
1876 bool updateWorkarounds = levelInfo.depthStencilWorkaround || levelInfo.lumaWorkaround.enabled ||
1877 levelInfo.emulatedAlphaChannel;
1878
1879 for (size_t i = level; i < level + levelCount; i++)
1880 {
1881 size_t index = GetLevelInfoIndex(target, i);
1882 ASSERT(index < mLevelInfo.size());
1883 auto &curLevelInfo = mLevelInfo[index];
1884
1885 updateWorkarounds |= curLevelInfo.depthStencilWorkaround;
1886 updateWorkarounds |= curLevelInfo.lumaWorkaround.enabled;
1887 updateWorkarounds |= curLevelInfo.emulatedAlphaChannel;
1888
1889 curLevelInfo = levelInfo;
1890 }
1891
1892 if (updateWorkarounds)
1893 {
1894 mLocalDirtyBits |= GetLevelWorkaroundDirtyBits();
1895 onStateChange(angle::SubjectMessage::DirtyBitsFlagged);
1896 }
1897 }
1898
setLevelInfo(const gl::Context * context,gl::TextureType type,size_t level,size_t levelCount,const LevelInfoGL & levelInfo)1899 void TextureGL::setLevelInfo(const gl::Context *context,
1900 gl::TextureType type,
1901 size_t level,
1902 size_t levelCount,
1903 const LevelInfoGL &levelInfo)
1904 {
1905 if (type == gl::TextureType::CubeMap)
1906 {
1907 for (gl::TextureTarget target : gl::AllCubeFaceTextureTargets())
1908 {
1909 setLevelInfo(context, target, level, levelCount, levelInfo);
1910 }
1911 }
1912 else
1913 {
1914 setLevelInfo(context, NonCubeTextureTypeToTarget(type), level, levelCount, levelInfo);
1915 }
1916 }
1917
getLevelInfo(gl::TextureTarget target,size_t level) const1918 const LevelInfoGL &TextureGL::getLevelInfo(gl::TextureTarget target, size_t level) const
1919 {
1920 return mLevelInfo[GetLevelInfoIndex(target, level)];
1921 }
1922
getBaseLevelInfo() const1923 const LevelInfoGL &TextureGL::getBaseLevelInfo() const
1924 {
1925 GLint effectiveBaseLevel = mState.getEffectiveBaseLevel();
1926 gl::TextureTarget target = getType() == gl::TextureType::CubeMap
1927 ? gl::kCubeMapTextureTargetMin
1928 : gl::NonCubeTextureTypeToTarget(getType());
1929 return getLevelInfo(target, effectiveBaseLevel);
1930 }
1931
getType() const1932 gl::TextureType TextureGL::getType() const
1933 {
1934 return mState.mType;
1935 }
1936
initializeContents(const gl::Context * context,const gl::ImageIndex & imageIndex)1937 angle::Result TextureGL::initializeContents(const gl::Context *context,
1938 const gl::ImageIndex &imageIndex)
1939 {
1940 ContextGL *contextGL = GetImplAs<ContextGL>(context);
1941 const FunctionsGL *functions = GetFunctionsGL(context);
1942 StateManagerGL *stateManager = GetStateManagerGL(context);
1943 const angle::FeaturesGL &features = GetFeaturesGL(context);
1944
1945 bool shouldUseClear = !nativegl::SupportsTexImage(getType());
1946 GLenum nativeInternalFormat =
1947 getLevelInfo(imageIndex.getTarget(), imageIndex.getLevelIndex()).nativeInternalFormat;
1948 if ((features.allowClearForRobustResourceInit.enabled || shouldUseClear) &&
1949 nativegl::SupportsNativeRendering(functions, mState.getType(), nativeInternalFormat))
1950 {
1951 BlitGL *blitter = GetBlitGL(context);
1952
1953 int levelDepth = mState.getImageDesc(imageIndex).size.depth;
1954
1955 bool clearSucceeded = false;
1956 ANGLE_TRY(blitter->clearRenderableTexture(context, this, nativeInternalFormat, levelDepth,
1957 imageIndex, &clearSucceeded));
1958 if (clearSucceeded)
1959 {
1960 return angle::Result::Continue;
1961 }
1962 }
1963
1964 // Either the texture is not renderable or was incomplete when clearing, fall back to a data
1965 // upload
1966 ASSERT(nativegl::SupportsTexImage(getType()));
1967 const gl::ImageDesc &desc = mState.getImageDesc(imageIndex);
1968 const gl::InternalFormat &internalFormatInfo = *desc.format.info;
1969
1970 gl::PixelUnpackState unpackState;
1971 unpackState.alignment = 1;
1972 ANGLE_TRY(stateManager->setPixelUnpackState(context, unpackState));
1973
1974 GLuint prevUnpackBuffer = stateManager->getBufferID(gl::BufferBinding::PixelUnpack);
1975 stateManager->bindBuffer(gl::BufferBinding::PixelUnpack, 0);
1976
1977 stateManager->bindTexture(getType(), mTextureID);
1978 if (internalFormatInfo.compressed)
1979 {
1980 nativegl::CompressedTexSubImageFormat nativeSubImageFormat =
1981 nativegl::GetCompressedSubTexImageFormat(functions, features,
1982 internalFormatInfo.internalFormat);
1983
1984 GLuint imageSize = 0;
1985 ANGLE_CHECK_GL_MATH(contextGL,
1986 internalFormatInfo.computeCompressedImageSize(desc.size, &imageSize));
1987
1988 angle::MemoryBuffer *zero;
1989 ANGLE_CHECK_GL_ALLOC(contextGL, context->getZeroFilledBuffer(imageSize, &zero));
1990
1991 // WebGL spec requires that zero data is uploaded to compressed textures even if it might
1992 // not result in zero color data.
1993 if (nativegl::UseTexImage2D(getType()))
1994 {
1995 ANGLE_GL_TRY(context, functions->compressedTexSubImage2D(
1996 ToGLenum(imageIndex.getTarget()), imageIndex.getLevelIndex(),
1997 0, 0, desc.size.width, desc.size.height,
1998 nativeSubImageFormat.format, imageSize, zero->data()));
1999 }
2000 else
2001 {
2002 ASSERT(nativegl::UseTexImage3D(getType()));
2003 ANGLE_GL_TRY(context, functions->compressedTexSubImage3D(
2004 ToGLenum(imageIndex.getTarget()), imageIndex.getLevelIndex(),
2005 0, 0, 0, desc.size.width, desc.size.height, desc.size.depth,
2006 nativeSubImageFormat.format, imageSize, zero->data()));
2007 }
2008 }
2009 else
2010 {
2011 nativegl::TexSubImageFormat nativeSubImageFormat = nativegl::GetTexSubImageFormat(
2012 functions, features, internalFormatInfo.format, internalFormatInfo.type);
2013
2014 GLuint imageSize = 0;
2015 ANGLE_CHECK_GL_MATH(contextGL, internalFormatInfo.computePackUnpackEndByte(
2016 nativeSubImageFormat.type, desc.size, unpackState,
2017 nativegl::UseTexImage3D(getType()), &imageSize));
2018
2019 angle::MemoryBuffer *zero;
2020 ANGLE_CHECK_GL_ALLOC(contextGL, context->getZeroFilledBuffer(imageSize, &zero));
2021
2022 if (nativegl::UseTexImage2D(getType()))
2023 {
2024 ANGLE_GL_TRY(context,
2025 functions->texSubImage2D(ToGLenum(imageIndex.getTarget()),
2026 imageIndex.getLevelIndex(), 0, 0, desc.size.width,
2027 desc.size.height, nativeSubImageFormat.format,
2028 nativeSubImageFormat.type, zero->data()));
2029 }
2030 else
2031 {
2032 ASSERT(nativegl::UseTexImage3D(getType()));
2033 ANGLE_GL_TRY(context,
2034 functions->texSubImage3D(
2035 ToGLenum(imageIndex.getTarget()), imageIndex.getLevelIndex(), 0, 0, 0,
2036 desc.size.width, desc.size.height, desc.size.depth,
2037 nativeSubImageFormat.format, nativeSubImageFormat.type, zero->data()));
2038 }
2039 }
2040
2041 // Reset the pixel unpack state. Because this call is made after synchronizing dirty bits in a
2042 // glTexImage call, we need to make sure that the texture data to be uploaded later has the
2043 // expected unpack state.
2044 ANGLE_TRY(stateManager->setPixelUnpackState(context, context->getState().getUnpackState()));
2045 stateManager->bindBuffer(gl::BufferBinding::PixelUnpack, prevUnpackBuffer);
2046
2047 return angle::Result::Continue;
2048 }
2049
2050 } // namespace rx
2051