1 //
2 // Copyright 2016 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 // renderer_utils:
7 // Helper methods pertaining to most or all back-ends.
8 //
9
10 #include "libANGLE/renderer/renderer_utils.h"
11
12 #include "common/string_utils.h"
13 #include "common/system_utils.h"
14 #include "common/utilities.h"
15 #include "image_util/copyimage.h"
16 #include "image_util/imageformats.h"
17 #include "libANGLE/AttributeMap.h"
18 #include "libANGLE/Context.h"
19 #include "libANGLE/Context.inl.h"
20 #include "libANGLE/Display.h"
21 #include "libANGLE/formatutils.h"
22 #include "libANGLE/renderer/ContextImpl.h"
23 #include "libANGLE/renderer/Format.h"
24 #include "platform/Feature.h"
25
26 #include <string.h>
27 #include <cctype>
28
29 namespace angle
30 {
31 namespace
32 {
33 // For the sake of feature name matching, underscore is ignored, and the names are matched
34 // case-insensitive. This allows feature names to be overriden both in snake_case (previously used
35 // by ANGLE) and camelCase.
FeatureNameMatch(const std::string & a,const std::string & b)36 bool FeatureNameMatch(const std::string &a, const std::string &b)
37 {
38 size_t ai = 0;
39 size_t bi = 0;
40
41 while (ai < a.size() && bi < b.size())
42 {
43 if (a[ai] == '_')
44 {
45 ++ai;
46 }
47 if (b[bi] == '_')
48 {
49 ++bi;
50 }
51 if (std::tolower(a[ai++]) != std::tolower(b[bi++]))
52 {
53 return false;
54 }
55 }
56
57 return ai == a.size() && bi == b.size();
58 }
59
60 // Search for a feature by name, matching it loosely so that both snake_case and camelCase names are
61 // matched.
FindFeatureByName(FeatureMap * features,const std::string & name)62 FeatureInfo *FindFeatureByName(FeatureMap *features, const std::string &name)
63 {
64 for (auto iter : *features)
65 {
66 if (FeatureNameMatch(iter.first, name))
67 {
68 return iter.second;
69 }
70 }
71 return nullptr;
72 }
73 } // anonymous namespace
74
75 // FeatureSetBase implementation
overrideFeatures(const std::vector<std::string> & featureNames,bool enabled)76 void FeatureSetBase::overrideFeatures(const std::vector<std::string> &featureNames, bool enabled)
77 {
78 for (const std::string &name : featureNames)
79 {
80 FeatureInfo *feature = FindFeatureByName(&members, name);
81 if (feature != nullptr)
82 {
83 feature->enabled = enabled;
84 }
85 }
86 }
87
populateFeatureList(FeatureList * features) const88 void FeatureSetBase::populateFeatureList(FeatureList *features) const
89 {
90 for (FeatureMap::const_iterator it = members.begin(); it != members.end(); it++)
91 {
92 features->push_back(it->second);
93 }
94 }
95 } // namespace angle
96
97 namespace rx
98 {
99
100 namespace
101 {
102 // Both D3D and Vulkan support the same set of standard sample positions for 1, 2, 4, 8, and 16
103 // samples. See:
104 //
105 // - https://msdn.microsoft.com/en-us/library/windows/desktop/ff476218.aspx
106 //
107 // -
108 // https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#primsrast-multisampling
109 using SamplePositionsArray = std::array<float, 32>;
110 constexpr std::array<SamplePositionsArray, 5> kSamplePositions = {
111 {{{0.5f, 0.5f}},
112 {{0.75f, 0.75f, 0.25f, 0.25f}},
113 {{0.375f, 0.125f, 0.875f, 0.375f, 0.125f, 0.625f, 0.625f, 0.875f}},
114 {{0.5625f, 0.3125f, 0.4375f, 0.6875f, 0.8125f, 0.5625f, 0.3125f, 0.1875f, 0.1875f, 0.8125f,
115 0.0625f, 0.4375f, 0.6875f, 0.9375f, 0.9375f, 0.0625f}},
116 {{0.5625f, 0.5625f, 0.4375f, 0.3125f, 0.3125f, 0.625f, 0.75f, 0.4375f,
117 0.1875f, 0.375f, 0.625f, 0.8125f, 0.8125f, 0.6875f, 0.6875f, 0.1875f,
118 0.375f, 0.875f, 0.5f, 0.0625f, 0.25f, 0.125f, 0.125f, 0.75f,
119 0.0f, 0.5f, 0.9375f, 0.25f, 0.875f, 0.9375f, 0.0625f, 0.0f}}}};
120
121 struct IncompleteTextureParameters
122 {
123 GLenum sizedInternalFormat;
124 GLenum format;
125 GLenum type;
126 GLubyte clearColor[4];
127 };
128
129 // Note that for gl::SamplerFormat::Shadow, the clearColor datatype needs to be GLushort and as such
130 // we will reinterpret GLubyte[4] as GLushort[2].
131 constexpr angle::PackedEnumMap<gl::SamplerFormat, IncompleteTextureParameters>
132 kIncompleteTextureParameters = {
133 {gl::SamplerFormat::Float, {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, {0, 0, 0, 255}}},
134 {gl::SamplerFormat::Unsigned,
135 {GL_RGBA8UI, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE, {0, 0, 0, 255}}},
136 {gl::SamplerFormat::Signed, {GL_RGBA8I, GL_RGBA_INTEGER, GL_BYTE, {0, 0, 0, 127}}},
137 {gl::SamplerFormat::Shadow,
138 {GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, {0, 0, 0, 0}}}};
139
CopyColor(gl::ColorF * color)140 void CopyColor(gl::ColorF *color)
141 {
142 // No-op
143 }
144
PremultiplyAlpha(gl::ColorF * color)145 void PremultiplyAlpha(gl::ColorF *color)
146 {
147 color->red *= color->alpha;
148 color->green *= color->alpha;
149 color->blue *= color->alpha;
150 }
151
UnmultiplyAlpha(gl::ColorF * color)152 void UnmultiplyAlpha(gl::ColorF *color)
153 {
154 if (color->alpha != 0.0f)
155 {
156 float invAlpha = 1.0f / color->alpha;
157 color->red *= invAlpha;
158 color->green *= invAlpha;
159 color->blue *= invAlpha;
160 }
161 }
162
ClipChannelsR(gl::ColorF * color)163 void ClipChannelsR(gl::ColorF *color)
164 {
165 color->green = 0.0f;
166 color->blue = 0.0f;
167 color->alpha = 1.0f;
168 }
169
ClipChannelsRG(gl::ColorF * color)170 void ClipChannelsRG(gl::ColorF *color)
171 {
172 color->blue = 0.0f;
173 color->alpha = 1.0f;
174 }
175
ClipChannelsRGB(gl::ColorF * color)176 void ClipChannelsRGB(gl::ColorF *color)
177 {
178 color->alpha = 1.0f;
179 }
180
ClipChannelsLuminance(gl::ColorF * color)181 void ClipChannelsLuminance(gl::ColorF *color)
182 {
183 color->alpha = 1.0f;
184 }
185
ClipChannelsAlpha(gl::ColorF * color)186 void ClipChannelsAlpha(gl::ColorF *color)
187 {
188 color->red = 0.0f;
189 color->green = 0.0f;
190 color->blue = 0.0f;
191 }
192
ClipChannelsNoOp(gl::ColorF * color)193 void ClipChannelsNoOp(gl::ColorF *color) {}
194
WriteUintColor(const gl::ColorF & color,PixelWriteFunction colorWriteFunction,uint8_t * destPixelData)195 void WriteUintColor(const gl::ColorF &color,
196 PixelWriteFunction colorWriteFunction,
197 uint8_t *destPixelData)
198 {
199 gl::ColorUI destColor(
200 static_cast<unsigned int>(color.red * 255), static_cast<unsigned int>(color.green * 255),
201 static_cast<unsigned int>(color.blue * 255), static_cast<unsigned int>(color.alpha * 255));
202 colorWriteFunction(reinterpret_cast<const uint8_t *>(&destColor), destPixelData);
203 }
204
WriteFloatColor(const gl::ColorF & color,PixelWriteFunction colorWriteFunction,uint8_t * destPixelData)205 void WriteFloatColor(const gl::ColorF &color,
206 PixelWriteFunction colorWriteFunction,
207 uint8_t *destPixelData)
208 {
209 colorWriteFunction(reinterpret_cast<const uint8_t *>(&color), destPixelData);
210 }
211
212 template <int cols, int rows, bool IsColumnMajor>
GetFlattenedIndex(int col,int row)213 inline int GetFlattenedIndex(int col, int row)
214 {
215 if (IsColumnMajor)
216 {
217 return col * rows + row;
218 }
219 else
220 {
221 return row * cols + col;
222 }
223 }
224
225 template <typename T,
226 bool IsSrcColumnMajor,
227 int colsSrc,
228 int rowsSrc,
229 bool IsDstColumnMajor,
230 int colsDst,
231 int rowsDst>
ExpandMatrix(T * target,const GLfloat * value)232 void ExpandMatrix(T *target, const GLfloat *value)
233 {
234 static_assert(colsSrc <= colsDst && rowsSrc <= rowsDst, "Can only expand!");
235
236 constexpr int kDstFlatSize = colsDst * rowsDst;
237 T staging[kDstFlatSize] = {0};
238
239 for (int r = 0; r < rowsSrc; r++)
240 {
241 for (int c = 0; c < colsSrc; c++)
242 {
243 int srcIndex = GetFlattenedIndex<colsSrc, rowsSrc, IsSrcColumnMajor>(c, r);
244 int dstIndex = GetFlattenedIndex<colsDst, rowsDst, IsDstColumnMajor>(c, r);
245
246 staging[dstIndex] = static_cast<T>(value[srcIndex]);
247 }
248 }
249
250 memcpy(target, staging, kDstFlatSize * sizeof(T));
251 }
252
253 template <bool IsSrcColumMajor,
254 int colsSrc,
255 int rowsSrc,
256 bool IsDstColumnMajor,
257 int colsDst,
258 int rowsDst>
SetFloatUniformMatrix(unsigned int arrayElementOffset,unsigned int elementCount,GLsizei countIn,const GLfloat * value,uint8_t * targetData)259 void SetFloatUniformMatrix(unsigned int arrayElementOffset,
260 unsigned int elementCount,
261 GLsizei countIn,
262 const GLfloat *value,
263 uint8_t *targetData)
264 {
265 unsigned int count =
266 std::min(elementCount - arrayElementOffset, static_cast<unsigned int>(countIn));
267
268 const unsigned int targetMatrixStride = colsDst * rowsDst;
269 GLfloat *target = reinterpret_cast<GLfloat *>(
270 targetData + arrayElementOffset * sizeof(GLfloat) * targetMatrixStride);
271
272 for (unsigned int i = 0; i < count; i++)
273 {
274 ExpandMatrix<GLfloat, IsSrcColumMajor, colsSrc, rowsSrc, IsDstColumnMajor, colsDst,
275 rowsDst>(target, value);
276
277 target += targetMatrixStride;
278 value += colsSrc * rowsSrc;
279 }
280 }
281
SetFloatUniformMatrixFast(unsigned int arrayElementOffset,unsigned int elementCount,GLsizei countIn,size_t matrixSize,const GLfloat * value,uint8_t * targetData)282 void SetFloatUniformMatrixFast(unsigned int arrayElementOffset,
283 unsigned int elementCount,
284 GLsizei countIn,
285 size_t matrixSize,
286 const GLfloat *value,
287 uint8_t *targetData)
288 {
289 const unsigned int count =
290 std::min(elementCount - arrayElementOffset, static_cast<unsigned int>(countIn));
291
292 const uint8_t *valueData = reinterpret_cast<const uint8_t *>(value);
293 targetData = targetData + arrayElementOffset * matrixSize;
294
295 memcpy(targetData, valueData, matrixSize * count);
296 }
297 } // anonymous namespace
298
RotateRectangle(const SurfaceRotation rotation,const bool flipY,const int framebufferWidth,const int framebufferHeight,const gl::Rectangle & incoming,gl::Rectangle * outgoing)299 void RotateRectangle(const SurfaceRotation rotation,
300 const bool flipY,
301 const int framebufferWidth,
302 const int framebufferHeight,
303 const gl::Rectangle &incoming,
304 gl::Rectangle *outgoing)
305 {
306 // GLES's y-axis points up; Vulkan's points down.
307 switch (rotation)
308 {
309 case SurfaceRotation::Identity:
310 // Do not rotate gl_Position (surface matches the device's orientation):
311 outgoing->x = incoming.x;
312 outgoing->y = flipY ? framebufferHeight - incoming.y - incoming.height : incoming.y;
313 outgoing->width = incoming.width;
314 outgoing->height = incoming.height;
315 break;
316 case SurfaceRotation::Rotated90Degrees:
317 // Rotate gl_Position 90 degrees:
318 outgoing->x = incoming.y;
319 outgoing->y = flipY ? incoming.x : framebufferWidth - incoming.x - incoming.width;
320 outgoing->width = incoming.height;
321 outgoing->height = incoming.width;
322 break;
323 case SurfaceRotation::Rotated180Degrees:
324 // Rotate gl_Position 180 degrees:
325 outgoing->x = framebufferWidth - incoming.x - incoming.width;
326 outgoing->y = flipY ? incoming.y : framebufferHeight - incoming.y - incoming.height;
327 outgoing->width = incoming.width;
328 outgoing->height = incoming.height;
329 break;
330 case SurfaceRotation::Rotated270Degrees:
331 // Rotate gl_Position 270 degrees:
332 outgoing->x = framebufferHeight - incoming.y - incoming.height;
333 outgoing->y = flipY ? framebufferWidth - incoming.x - incoming.width : incoming.x;
334 outgoing->width = incoming.height;
335 outgoing->height = incoming.width;
336 break;
337 default:
338 UNREACHABLE();
339 break;
340 }
341 }
342
PackPixelsParams()343 PackPixelsParams::PackPixelsParams()
344 : destFormat(nullptr),
345 outputPitch(0),
346 packBuffer(nullptr),
347 offset(0),
348 rotation(SurfaceRotation::Identity)
349 {}
350
PackPixelsParams(const gl::Rectangle & areaIn,const angle::Format & destFormat,GLuint outputPitchIn,bool reverseRowOrderIn,gl::Buffer * packBufferIn,ptrdiff_t offsetIn)351 PackPixelsParams::PackPixelsParams(const gl::Rectangle &areaIn,
352 const angle::Format &destFormat,
353 GLuint outputPitchIn,
354 bool reverseRowOrderIn,
355 gl::Buffer *packBufferIn,
356 ptrdiff_t offsetIn)
357 : area(areaIn),
358 destFormat(&destFormat),
359 outputPitch(outputPitchIn),
360 packBuffer(packBufferIn),
361 reverseRowOrder(reverseRowOrderIn),
362 offset(offsetIn),
363 rotation(SurfaceRotation::Identity)
364 {}
365
PackPixels(const PackPixelsParams & params,const angle::Format & sourceFormat,int inputPitchIn,const uint8_t * sourceIn,uint8_t * destWithoutOffset)366 void PackPixels(const PackPixelsParams ¶ms,
367 const angle::Format &sourceFormat,
368 int inputPitchIn,
369 const uint8_t *sourceIn,
370 uint8_t *destWithoutOffset)
371 {
372 uint8_t *destWithOffset = destWithoutOffset + params.offset;
373
374 const uint8_t *source = sourceIn;
375 int inputPitch = inputPitchIn;
376 int destWidth = params.area.width;
377 int destHeight = params.area.height;
378 int xAxisPitch = 0;
379 int yAxisPitch = 0;
380 switch (params.rotation)
381 {
382 case SurfaceRotation::Identity:
383 // The source image is not rotated (i.e. matches the device's orientation), and may or
384 // may not be y-flipped. The image is row-major. Each source row (one step along the
385 // y-axis for each step in the dest y-axis) is inputPitch past the previous row. Along
386 // a row, each source pixel (one step along the x-axis for each step in the dest
387 // x-axis) is sourceFormat.pixelBytes past the previous pixel.
388 xAxisPitch = sourceFormat.pixelBytes;
389 if (params.reverseRowOrder)
390 {
391 // The source image is y-flipped, which means we start at the last row, and each
392 // source row is BEFORE the previous row.
393 source += inputPitchIn * (params.area.height - 1);
394 inputPitch = -inputPitch;
395 yAxisPitch = -inputPitchIn;
396 }
397 else
398 {
399 yAxisPitch = inputPitchIn;
400 }
401 break;
402 case SurfaceRotation::Rotated90Degrees:
403 // The source image is rotated 90 degrees counter-clockwise. Y-flip is always applied
404 // to rotated images. The image is column-major. Each source column (one step along
405 // the source x-axis for each step in the dest y-axis) is inputPitch past the previous
406 // column. Along a column, each source pixel (one step along the y-axis for each step
407 // in the dest x-axis) is sourceFormat.pixelBytes past the previous pixel.
408 xAxisPitch = inputPitchIn;
409 yAxisPitch = sourceFormat.pixelBytes;
410 destWidth = params.area.height;
411 destHeight = params.area.width;
412 break;
413 case SurfaceRotation::Rotated180Degrees:
414 // The source image is rotated 180 degrees. Y-flip is always applied to rotated
415 // images. The image is row-major, but upside down. Each source row (one step along
416 // the y-axis for each step in the dest y-axis) is inputPitch after the previous row.
417 // Along a row, each source pixel (one step along the x-axis for each step in the dest
418 // x-axis) is sourceFormat.pixelBytes BEFORE the previous pixel.
419 xAxisPitch = -static_cast<int>(sourceFormat.pixelBytes);
420 yAxisPitch = inputPitchIn;
421 source += sourceFormat.pixelBytes * (params.area.width - 1);
422 break;
423 case SurfaceRotation::Rotated270Degrees:
424 // The source image is rotated 270 degrees counter-clockwise (or 90 degrees clockwise).
425 // Y-flip is always applied to rotated images. The image is column-major, where each
426 // column (one step in the source x-axis for one step in the dest y-axis) is inputPitch
427 // BEFORE the previous column. Along a column, each source pixel (one step along the
428 // y-axis for each step in the dest x-axis) is sourceFormat.pixelBytes BEFORE the
429 // previous pixel. The first pixel is at the end of the source.
430 xAxisPitch = -inputPitchIn;
431 yAxisPitch = -static_cast<int>(sourceFormat.pixelBytes);
432 destWidth = params.area.height;
433 destHeight = params.area.width;
434 source += inputPitch * (params.area.height - 1) +
435 sourceFormat.pixelBytes * (params.area.width - 1);
436 break;
437 default:
438 UNREACHABLE();
439 break;
440 }
441
442 if (params.rotation == SurfaceRotation::Identity && sourceFormat == *params.destFormat)
443 {
444 // Direct copy possible
445 for (int y = 0; y < params.area.height; ++y)
446 {
447 memcpy(destWithOffset + y * params.outputPitch, source + y * inputPitch,
448 params.area.width * sourceFormat.pixelBytes);
449 }
450 return;
451 }
452
453 FastCopyFunction fastCopyFunc = sourceFormat.fastCopyFunctions.get(params.destFormat->id);
454
455 if (fastCopyFunc)
456 {
457 // Fast copy is possible through some special function
458 fastCopyFunc(source, xAxisPitch, yAxisPitch, destWithOffset, params.destFormat->pixelBytes,
459 params.outputPitch, destWidth, destHeight);
460 return;
461 }
462
463 PixelWriteFunction pixelWriteFunction = params.destFormat->pixelWriteFunction;
464 ASSERT(pixelWriteFunction != nullptr);
465
466 // Maximum size of any Color<T> type used.
467 uint8_t temp[16];
468 static_assert(sizeof(temp) >= sizeof(gl::ColorF) && sizeof(temp) >= sizeof(gl::ColorUI) &&
469 sizeof(temp) >= sizeof(gl::ColorI) &&
470 sizeof(temp) >= sizeof(angle::DepthStencil),
471 "Unexpected size of pixel struct.");
472
473 PixelReadFunction pixelReadFunction = sourceFormat.pixelReadFunction;
474 ASSERT(pixelReadFunction != nullptr);
475
476 for (int y = 0; y < destHeight; ++y)
477 {
478 for (int x = 0; x < destWidth; ++x)
479 {
480 uint8_t *dest =
481 destWithOffset + y * params.outputPitch + x * params.destFormat->pixelBytes;
482 const uint8_t *src = source + y * yAxisPitch + x * xAxisPitch;
483
484 // readFunc and writeFunc will be using the same type of color, CopyTexImage
485 // will not allow the copy otherwise.
486 pixelReadFunction(src, temp);
487 pixelWriteFunction(temp, dest);
488 }
489 }
490 }
491
has(angle::FormatID formatID) const492 bool FastCopyFunctionMap::has(angle::FormatID formatID) const
493 {
494 return (get(formatID) != nullptr);
495 }
496
497 namespace
498 {
499
getEntry(const FastCopyFunctionMap::Entry * entry,size_t numEntries,angle::FormatID formatID)500 const FastCopyFunctionMap::Entry *getEntry(const FastCopyFunctionMap::Entry *entry,
501 size_t numEntries,
502 angle::FormatID formatID)
503 {
504 const FastCopyFunctionMap::Entry *end = entry + numEntries;
505 while (entry != end)
506 {
507 if (entry->formatID == formatID)
508 {
509 return entry;
510 }
511 ++entry;
512 }
513
514 return nullptr;
515 }
516
517 } // namespace
518
get(angle::FormatID formatID) const519 FastCopyFunction FastCopyFunctionMap::get(angle::FormatID formatID) const
520 {
521 const FastCopyFunctionMap::Entry *entry = getEntry(mData, mSize, formatID);
522 return entry ? entry->func : nullptr;
523 }
524
ShouldUseDebugLayers(const egl::AttributeMap & attribs)525 bool ShouldUseDebugLayers(const egl::AttributeMap &attribs)
526 {
527 EGLAttrib debugSetting =
528 attribs.get(EGL_PLATFORM_ANGLE_DEBUG_LAYERS_ENABLED_ANGLE, EGL_DONT_CARE);
529
530 // Prefer to enable debug layers when available.
531 #if defined(ANGLE_ENABLE_ASSERTS)
532 return (debugSetting != EGL_FALSE);
533 #else
534 return (debugSetting == EGL_TRUE);
535 #endif // defined(ANGLE_ENABLE_ASSERTS)
536 }
537
CopyImageCHROMIUM(const uint8_t * sourceData,size_t sourceRowPitch,size_t sourcePixelBytes,size_t sourceDepthPitch,PixelReadFunction pixelReadFunction,uint8_t * destData,size_t destRowPitch,size_t destPixelBytes,size_t destDepthPitch,PixelWriteFunction pixelWriteFunction,GLenum destUnsizedFormat,GLenum destComponentType,size_t width,size_t height,size_t depth,bool unpackFlipY,bool unpackPremultiplyAlpha,bool unpackUnmultiplyAlpha)538 void CopyImageCHROMIUM(const uint8_t *sourceData,
539 size_t sourceRowPitch,
540 size_t sourcePixelBytes,
541 size_t sourceDepthPitch,
542 PixelReadFunction pixelReadFunction,
543 uint8_t *destData,
544 size_t destRowPitch,
545 size_t destPixelBytes,
546 size_t destDepthPitch,
547 PixelWriteFunction pixelWriteFunction,
548 GLenum destUnsizedFormat,
549 GLenum destComponentType,
550 size_t width,
551 size_t height,
552 size_t depth,
553 bool unpackFlipY,
554 bool unpackPremultiplyAlpha,
555 bool unpackUnmultiplyAlpha)
556 {
557 using ConversionFunction = void (*)(gl::ColorF *);
558 ConversionFunction conversionFunction = CopyColor;
559 if (unpackPremultiplyAlpha != unpackUnmultiplyAlpha)
560 {
561 if (unpackPremultiplyAlpha)
562 {
563 conversionFunction = PremultiplyAlpha;
564 }
565 else
566 {
567 conversionFunction = UnmultiplyAlpha;
568 }
569 }
570
571 auto clipChannelsFunction = ClipChannelsNoOp;
572 switch (destUnsizedFormat)
573 {
574 case GL_RED:
575 clipChannelsFunction = ClipChannelsR;
576 break;
577 case GL_RG:
578 clipChannelsFunction = ClipChannelsRG;
579 break;
580 case GL_RGB:
581 clipChannelsFunction = ClipChannelsRGB;
582 break;
583 case GL_LUMINANCE:
584 clipChannelsFunction = ClipChannelsLuminance;
585 break;
586 case GL_ALPHA:
587 clipChannelsFunction = ClipChannelsAlpha;
588 break;
589 }
590
591 auto writeFunction = (destComponentType == GL_UNSIGNED_INT) ? WriteUintColor : WriteFloatColor;
592
593 for (size_t z = 0; z < depth; z++)
594 {
595 for (size_t y = 0; y < height; y++)
596 {
597 for (size_t x = 0; x < width; x++)
598 {
599 const uint8_t *sourcePixelData =
600 sourceData + y * sourceRowPitch + x * sourcePixelBytes + z * sourceDepthPitch;
601
602 gl::ColorF sourceColor;
603 pixelReadFunction(sourcePixelData, reinterpret_cast<uint8_t *>(&sourceColor));
604
605 conversionFunction(&sourceColor);
606 clipChannelsFunction(&sourceColor);
607
608 size_t destY = 0;
609 if (unpackFlipY)
610 {
611 destY += (height - 1);
612 destY -= y;
613 }
614 else
615 {
616 destY += y;
617 }
618
619 uint8_t *destPixelData =
620 destData + destY * destRowPitch + x * destPixelBytes + z * destDepthPitch;
621 writeFunction(sourceColor, pixelWriteFunction, destPixelData);
622 }
623 }
624 }
625 }
626
627 // IncompleteTextureSet implementation.
IncompleteTextureSet()628 IncompleteTextureSet::IncompleteTextureSet() : mIncompleteTextureBufferAttachment(nullptr) {}
629
~IncompleteTextureSet()630 IncompleteTextureSet::~IncompleteTextureSet() {}
631
onDestroy(const gl::Context * context)632 void IncompleteTextureSet::onDestroy(const gl::Context *context)
633 {
634 // Clear incomplete textures.
635 for (auto &incompleteTextures : mIncompleteTextures)
636 {
637 for (auto &incompleteTexture : incompleteTextures)
638 {
639 if (incompleteTexture.get() != nullptr)
640 {
641 incompleteTexture->onDestroy(context);
642 incompleteTexture.set(context, nullptr);
643 }
644 }
645 }
646 if (mIncompleteTextureBufferAttachment != nullptr)
647 {
648 mIncompleteTextureBufferAttachment->onDestroy(context);
649 mIncompleteTextureBufferAttachment = nullptr;
650 }
651 }
652
getIncompleteTexture(const gl::Context * context,gl::TextureType type,gl::SamplerFormat format,MultisampleTextureInitializer * multisampleInitializer,gl::Texture ** textureOut)653 angle::Result IncompleteTextureSet::getIncompleteTexture(
654 const gl::Context *context,
655 gl::TextureType type,
656 gl::SamplerFormat format,
657 MultisampleTextureInitializer *multisampleInitializer,
658 gl::Texture **textureOut)
659 {
660 *textureOut = mIncompleteTextures[format][type].get();
661 if (*textureOut != nullptr)
662 {
663 return angle::Result::Continue;
664 }
665
666 ContextImpl *implFactory = context->getImplementation();
667
668 gl::Extents colorSize(1, 1, 1);
669 gl::PixelUnpackState unpack;
670 unpack.alignment = 1;
671 gl::Box area(0, 0, 0, 1, 1, 1);
672 const IncompleteTextureParameters &incompleteTextureParam =
673 kIncompleteTextureParameters[format];
674
675 // Cube map arrays are expected to have layer counts that are multiples of 6
676 constexpr int kCubeMapArraySize = 6;
677 if (type == gl::TextureType::CubeMapArray)
678 {
679 // From the GLES 3.2 spec:
680 // 8.18. IMMUTABLE-FORMAT TEXTURE IMAGES
681 // TexStorage3D Errors
682 // An INVALID_OPERATION error is generated if any of the following conditions hold:
683 // * target is TEXTURE_CUBE_MAP_ARRAY and depth is not a multiple of 6
684 // Since ANGLE treats incomplete textures as immutable, respect that here.
685 colorSize.depth = kCubeMapArraySize;
686 area.depth = kCubeMapArraySize;
687 }
688
689 // If a texture is external use a 2D texture for the incomplete texture
690 gl::TextureType createType = (type == gl::TextureType::External) ? gl::TextureType::_2D : type;
691
692 gl::Texture *tex =
693 new gl::Texture(implFactory, {std::numeric_limits<GLuint>::max()}, createType);
694 angle::UniqueObjectPointer<gl::Texture, gl::Context> t(tex, context);
695
696 // This is a bit of a kludge but is necessary to consume the error.
697 gl::Context *mutableContext = const_cast<gl::Context *>(context);
698
699 if (createType == gl::TextureType::Buffer)
700 {
701 constexpr uint32_t kBufferInitData = 0;
702 mIncompleteTextureBufferAttachment =
703 new gl::Buffer(implFactory, {std::numeric_limits<GLuint>::max()});
704 ANGLE_TRY(mIncompleteTextureBufferAttachment->bufferData(
705 mutableContext, gl::BufferBinding::Texture, &kBufferInitData, sizeof(kBufferInitData),
706 gl::BufferUsage::StaticDraw));
707 }
708 else if (createType == gl::TextureType::_2DMultisample)
709 {
710 ANGLE_TRY(t->setStorageMultisample(mutableContext, createType, 1,
711 incompleteTextureParam.sizedInternalFormat, colorSize,
712 true));
713 }
714 else
715 {
716 ANGLE_TRY(t->setStorage(mutableContext, createType, 1,
717 incompleteTextureParam.sizedInternalFormat, colorSize));
718 }
719
720 if (type == gl::TextureType::CubeMap)
721 {
722 for (gl::TextureTarget face : gl::AllCubeFaceTextureTargets())
723 {
724 ANGLE_TRY(t->setSubImage(mutableContext, unpack, nullptr, face, 0, area,
725 incompleteTextureParam.format, incompleteTextureParam.type,
726 incompleteTextureParam.clearColor));
727 }
728 }
729 else if (type == gl::TextureType::CubeMapArray)
730 {
731 // We need to provide enough pixel data to fill the array of six faces
732 GLubyte incompleteCubeArrayPixels[kCubeMapArraySize][4];
733 for (int i = 0; i < kCubeMapArraySize; ++i)
734 {
735 incompleteCubeArrayPixels[i][0] = incompleteTextureParam.clearColor[0];
736 incompleteCubeArrayPixels[i][1] = incompleteTextureParam.clearColor[1];
737 incompleteCubeArrayPixels[i][2] = incompleteTextureParam.clearColor[2];
738 incompleteCubeArrayPixels[i][3] = incompleteTextureParam.clearColor[3];
739 }
740
741 ANGLE_TRY(t->setSubImage(mutableContext, unpack, nullptr,
742 gl::NonCubeTextureTypeToTarget(createType), 0, area,
743 incompleteTextureParam.format, incompleteTextureParam.type,
744 *incompleteCubeArrayPixels));
745 }
746 else if (type == gl::TextureType::_2DMultisample)
747 {
748 // Call a specialized clear function to init a multisample texture.
749 ANGLE_TRY(multisampleInitializer->initializeMultisampleTextureToBlack(context, t.get()));
750 }
751 else if (type == gl::TextureType::Buffer)
752 {
753 ANGLE_TRY(t->setBuffer(context, mIncompleteTextureBufferAttachment,
754 incompleteTextureParam.sizedInternalFormat));
755 }
756 else
757 {
758 ANGLE_TRY(t->setSubImage(mutableContext, unpack, nullptr,
759 gl::NonCubeTextureTypeToTarget(createType), 0, area,
760 incompleteTextureParam.format, incompleteTextureParam.type,
761 incompleteTextureParam.clearColor));
762 }
763
764 if (format == gl::SamplerFormat::Shadow)
765 {
766 // To avoid the undefined spec behavior for shadow samplers with a depth texture, we set the
767 // compare mode to GL_COMPARE_REF_TO_TEXTURE
768 ASSERT(!t->hasObservers());
769 t->setCompareMode(context, GL_COMPARE_REF_TO_TEXTURE);
770 }
771
772 ANGLE_TRY(t->syncState(context, gl::Command::Other));
773
774 mIncompleteTextures[format][type].set(context, t.release());
775 *textureOut = mIncompleteTextures[format][type].get();
776 return angle::Result::Continue;
777 }
778
779 #define ANGLE_INSTANTIATE_SET_UNIFORM_MATRIX_FUNC(api, cols, rows) \
780 template void SetFloatUniformMatrix##api<cols, rows>::Run( \
781 unsigned int, unsigned int, GLsizei, GLboolean, const GLfloat *, uint8_t *)
782
783 ANGLE_INSTANTIATE_SET_UNIFORM_MATRIX_FUNC(GLSL, 2, 2);
784 ANGLE_INSTANTIATE_SET_UNIFORM_MATRIX_FUNC(GLSL, 3, 3);
785 ANGLE_INSTANTIATE_SET_UNIFORM_MATRIX_FUNC(GLSL, 2, 3);
786 ANGLE_INSTANTIATE_SET_UNIFORM_MATRIX_FUNC(GLSL, 3, 2);
787 ANGLE_INSTANTIATE_SET_UNIFORM_MATRIX_FUNC(GLSL, 4, 2);
788 ANGLE_INSTANTIATE_SET_UNIFORM_MATRIX_FUNC(GLSL, 4, 3);
789
790 ANGLE_INSTANTIATE_SET_UNIFORM_MATRIX_FUNC(HLSL, 2, 2);
791 ANGLE_INSTANTIATE_SET_UNIFORM_MATRIX_FUNC(HLSL, 3, 3);
792 ANGLE_INSTANTIATE_SET_UNIFORM_MATRIX_FUNC(HLSL, 2, 3);
793 ANGLE_INSTANTIATE_SET_UNIFORM_MATRIX_FUNC(HLSL, 3, 2);
794 ANGLE_INSTANTIATE_SET_UNIFORM_MATRIX_FUNC(HLSL, 2, 4);
795 ANGLE_INSTANTIATE_SET_UNIFORM_MATRIX_FUNC(HLSL, 3, 4);
796
797 #undef ANGLE_INSTANTIATE_SET_UNIFORM_MATRIX_FUNC
798
799 #define ANGLE_SPECIALIZATION_ROWS_SET_UNIFORM_MATRIX_FUNC(api, cols, rows) \
800 template void SetFloatUniformMatrix##api<cols, 4>::Run(unsigned int, unsigned int, GLsizei, \
801 GLboolean, const GLfloat *, uint8_t *)
802
803 template <int cols>
804 struct SetFloatUniformMatrixGLSL<cols, 4>
805 {
806 static void Run(unsigned int arrayElementOffset,
807 unsigned int elementCount,
808 GLsizei countIn,
809 GLboolean transpose,
810 const GLfloat *value,
811 uint8_t *targetData);
812 };
813
814 ANGLE_SPECIALIZATION_ROWS_SET_UNIFORM_MATRIX_FUNC(GLSL, 2, 4);
815 ANGLE_SPECIALIZATION_ROWS_SET_UNIFORM_MATRIX_FUNC(GLSL, 3, 4);
816 ANGLE_SPECIALIZATION_ROWS_SET_UNIFORM_MATRIX_FUNC(GLSL, 4, 4);
817
818 #undef ANGLE_SPECIALIZATION_ROWS_SET_UNIFORM_MATRIX_FUNC
819
820 #define ANGLE_SPECIALIZATION_COLS_SET_UNIFORM_MATRIX_FUNC(api, cols, rows) \
821 template void SetFloatUniformMatrix##api<4, rows>::Run(unsigned int, unsigned int, GLsizei, \
822 GLboolean, const GLfloat *, uint8_t *)
823
824 template <int rows>
825 struct SetFloatUniformMatrixHLSL<4, rows>
826 {
827 static void Run(unsigned int arrayElementOffset,
828 unsigned int elementCount,
829 GLsizei countIn,
830 GLboolean transpose,
831 const GLfloat *value,
832 uint8_t *targetData);
833 };
834
835 ANGLE_SPECIALIZATION_COLS_SET_UNIFORM_MATRIX_FUNC(HLSL, 4, 2);
836 ANGLE_SPECIALIZATION_COLS_SET_UNIFORM_MATRIX_FUNC(HLSL, 4, 3);
837 ANGLE_SPECIALIZATION_COLS_SET_UNIFORM_MATRIX_FUNC(HLSL, 4, 4);
838
839 #undef ANGLE_SPECIALIZATION_COLS_SET_UNIFORM_MATRIX_FUNC
840
841 template <int cols>
Run(unsigned int arrayElementOffset,unsigned int elementCount,GLsizei countIn,GLboolean transpose,const GLfloat * value,uint8_t * targetData)842 void SetFloatUniformMatrixGLSL<cols, 4>::Run(unsigned int arrayElementOffset,
843 unsigned int elementCount,
844 GLsizei countIn,
845 GLboolean transpose,
846 const GLfloat *value,
847 uint8_t *targetData)
848 {
849 const bool isSrcColumnMajor = !transpose;
850 if (isSrcColumnMajor)
851 {
852 // Both src and dst matrixs are has same layout,
853 // a single memcpy updates all the matrices
854 constexpr size_t srcMatrixSize = sizeof(GLfloat) * cols * 4;
855 SetFloatUniformMatrixFast(arrayElementOffset, elementCount, countIn, srcMatrixSize, value,
856 targetData);
857 }
858 else
859 {
860 // fallback to general cases
861 SetFloatUniformMatrix<false, cols, 4, true, cols, 4>(arrayElementOffset, elementCount,
862 countIn, value, targetData);
863 }
864 }
865
866 template <int cols, int rows>
Run(unsigned int arrayElementOffset,unsigned int elementCount,GLsizei countIn,GLboolean transpose,const GLfloat * value,uint8_t * targetData)867 void SetFloatUniformMatrixGLSL<cols, rows>::Run(unsigned int arrayElementOffset,
868 unsigned int elementCount,
869 GLsizei countIn,
870 GLboolean transpose,
871 const GLfloat *value,
872 uint8_t *targetData)
873 {
874 const bool isSrcColumnMajor = !transpose;
875 // GLSL expects matrix uniforms to be column-major, and each column is padded to 4 rows.
876 if (isSrcColumnMajor)
877 {
878 SetFloatUniformMatrix<true, cols, rows, true, cols, 4>(arrayElementOffset, elementCount,
879 countIn, value, targetData);
880 }
881 else
882 {
883 SetFloatUniformMatrix<false, cols, rows, true, cols, 4>(arrayElementOffset, elementCount,
884 countIn, value, targetData);
885 }
886 }
887
888 template <int rows>
Run(unsigned int arrayElementOffset,unsigned int elementCount,GLsizei countIn,GLboolean transpose,const GLfloat * value,uint8_t * targetData)889 void SetFloatUniformMatrixHLSL<4, rows>::Run(unsigned int arrayElementOffset,
890 unsigned int elementCount,
891 GLsizei countIn,
892 GLboolean transpose,
893 const GLfloat *value,
894 uint8_t *targetData)
895 {
896 const bool isSrcColumnMajor = !transpose;
897 if (!isSrcColumnMajor)
898 {
899 // Both src and dst matrixs are has same layout,
900 // a single memcpy updates all the matrices
901 constexpr size_t srcMatrixSize = sizeof(GLfloat) * 4 * rows;
902 SetFloatUniformMatrixFast(arrayElementOffset, elementCount, countIn, srcMatrixSize, value,
903 targetData);
904 }
905 else
906 {
907 // fallback to general cases
908 SetFloatUniformMatrix<true, 4, rows, false, 4, rows>(arrayElementOffset, elementCount,
909 countIn, value, targetData);
910 }
911 }
912
913 template <int cols, int rows>
Run(unsigned int arrayElementOffset,unsigned int elementCount,GLsizei countIn,GLboolean transpose,const GLfloat * value,uint8_t * targetData)914 void SetFloatUniformMatrixHLSL<cols, rows>::Run(unsigned int arrayElementOffset,
915 unsigned int elementCount,
916 GLsizei countIn,
917 GLboolean transpose,
918 const GLfloat *value,
919 uint8_t *targetData)
920 {
921 const bool isSrcColumnMajor = !transpose;
922 // Internally store matrices as row-major to accomodate HLSL matrix indexing. Each row is
923 // padded to 4 columns.
924 if (!isSrcColumnMajor)
925 {
926 SetFloatUniformMatrix<false, cols, rows, false, 4, rows>(arrayElementOffset, elementCount,
927 countIn, value, targetData);
928 }
929 else
930 {
931 SetFloatUniformMatrix<true, cols, rows, false, 4, rows>(arrayElementOffset, elementCount,
932 countIn, value, targetData);
933 }
934 }
935
936 template void GetMatrixUniform<GLint>(GLenum, GLint *, const GLint *, bool);
937 template void GetMatrixUniform<GLuint>(GLenum, GLuint *, const GLuint *, bool);
938
GetMatrixUniform(GLenum type,GLfloat * dataOut,const GLfloat * source,bool transpose)939 void GetMatrixUniform(GLenum type, GLfloat *dataOut, const GLfloat *source, bool transpose)
940 {
941 int columns = gl::VariableColumnCount(type);
942 int rows = gl::VariableRowCount(type);
943 for (GLint col = 0; col < columns; ++col)
944 {
945 for (GLint row = 0; row < rows; ++row)
946 {
947 GLfloat *outptr = dataOut + ((col * rows) + row);
948 const GLfloat *inptr =
949 transpose ? source + ((row * 4) + col) : source + ((col * 4) + row);
950 *outptr = *inptr;
951 }
952 }
953 }
954
955 template <typename NonFloatT>
GetMatrixUniform(GLenum type,NonFloatT * dataOut,const NonFloatT * source,bool transpose)956 void GetMatrixUniform(GLenum type, NonFloatT *dataOut, const NonFloatT *source, bool transpose)
957 {
958 UNREACHABLE();
959 }
960
GetFormatFromFormatType(GLenum format,GLenum type)961 const angle::Format &GetFormatFromFormatType(GLenum format, GLenum type)
962 {
963 GLenum sizedInternalFormat = gl::GetInternalFormatInfo(format, type).sizedInternalFormat;
964 angle::FormatID angleFormatID = angle::Format::InternalFormatToID(sizedInternalFormat);
965 return angle::Format::Get(angleFormatID);
966 }
967
ComputeStartVertex(ContextImpl * contextImpl,const gl::IndexRange & indexRange,GLint baseVertex,GLint * firstVertexOut)968 angle::Result ComputeStartVertex(ContextImpl *contextImpl,
969 const gl::IndexRange &indexRange,
970 GLint baseVertex,
971 GLint *firstVertexOut)
972 {
973 // The entire index range should be within the limits of a 32-bit uint because the largest
974 // GL index type is GL_UNSIGNED_INT.
975 ASSERT(indexRange.start <= std::numeric_limits<uint32_t>::max() &&
976 indexRange.end <= std::numeric_limits<uint32_t>::max());
977
978 // The base vertex is only used in DrawElementsIndirect. Given the assertion above and the
979 // type of mBaseVertex (GLint), adding them both as 64-bit ints is safe.
980 int64_t startVertexInt64 =
981 static_cast<int64_t>(baseVertex) + static_cast<int64_t>(indexRange.start);
982
983 // OpenGL ES 3.2 spec section 10.5: "Behavior of DrawElementsOneInstance is undefined if the
984 // vertex ID is negative for any element"
985 ANGLE_CHECK_GL_MATH(contextImpl, startVertexInt64 >= 0);
986
987 // OpenGL ES 3.2 spec section 10.5: "If the vertex ID is larger than the maximum value
988 // representable by type, it should behave as if the calculation were upconverted to 32-bit
989 // unsigned integers(with wrapping on overflow conditions)." ANGLE does not fully handle
990 // these rules, an overflow error is returned if the start vertex cannot be stored in a
991 // 32-bit signed integer.
992 ANGLE_CHECK_GL_MATH(contextImpl, startVertexInt64 <= std::numeric_limits<GLint>::max());
993
994 *firstVertexOut = static_cast<GLint>(startVertexInt64);
995 return angle::Result::Continue;
996 }
997
GetVertexRangeInfo(const gl::Context * context,GLint firstVertex,GLsizei vertexOrIndexCount,gl::DrawElementsType indexTypeOrInvalid,const void * indices,GLint baseVertex,GLint * startVertexOut,size_t * vertexCountOut)998 angle::Result GetVertexRangeInfo(const gl::Context *context,
999 GLint firstVertex,
1000 GLsizei vertexOrIndexCount,
1001 gl::DrawElementsType indexTypeOrInvalid,
1002 const void *indices,
1003 GLint baseVertex,
1004 GLint *startVertexOut,
1005 size_t *vertexCountOut)
1006 {
1007 if (indexTypeOrInvalid != gl::DrawElementsType::InvalidEnum)
1008 {
1009 gl::IndexRange indexRange;
1010 ANGLE_TRY(context->getState().getVertexArray()->getIndexRange(
1011 context, indexTypeOrInvalid, vertexOrIndexCount, indices, &indexRange));
1012 ANGLE_TRY(ComputeStartVertex(context->getImplementation(), indexRange, baseVertex,
1013 startVertexOut));
1014 *vertexCountOut = indexRange.vertexCount();
1015 }
1016 else
1017 {
1018 *startVertexOut = firstVertex;
1019 *vertexCountOut = vertexOrIndexCount;
1020 }
1021 return angle::Result::Continue;
1022 }
1023
ClipRectToScissor(const gl::State & glState,const gl::Rectangle & rect,bool invertY)1024 gl::Rectangle ClipRectToScissor(const gl::State &glState, const gl::Rectangle &rect, bool invertY)
1025 {
1026 // If the scissor test isn't enabled, assume it has infinite size. Its intersection with the
1027 // rect would be the rect itself.
1028 //
1029 // Note that on Vulkan, returning this (as opposed to a fixed max-int-sized rect) could lead to
1030 // unnecessary pipeline creations if two otherwise identical pipelines are used on framebuffers
1031 // with different sizes. If such usage is observed in an application, we should investigate
1032 // possible optimizations.
1033 if (!glState.isScissorTestEnabled())
1034 {
1035 return rect;
1036 }
1037
1038 gl::Rectangle clippedRect;
1039 if (!gl::ClipRectangle(glState.getScissor(), rect, &clippedRect))
1040 {
1041 return gl::Rectangle();
1042 }
1043
1044 if (invertY)
1045 {
1046 clippedRect.y = rect.height - clippedRect.y - clippedRect.height;
1047 }
1048
1049 return clippedRect;
1050 }
1051
LogFeatureStatus(const angle::FeatureSetBase & features,const std::vector<std::string> & featureNames,bool enabled)1052 void LogFeatureStatus(const angle::FeatureSetBase &features,
1053 const std::vector<std::string> &featureNames,
1054 bool enabled)
1055 {
1056 for (const std::string &name : featureNames)
1057 {
1058 if (features.getFeatures().find(name) != features.getFeatures().end())
1059 {
1060 INFO() << "Feature: " << name << (enabled ? " enabled" : " disabled");
1061 }
1062 }
1063 }
1064
ApplyFeatureOverrides(angle::FeatureSetBase * features,const egl::DisplayState & state)1065 void ApplyFeatureOverrides(angle::FeatureSetBase *features, const egl::DisplayState &state)
1066 {
1067 features->overrideFeatures(state.featureOverridesEnabled, true);
1068 features->overrideFeatures(state.featureOverridesDisabled, false);
1069
1070 // Override with environment as well.
1071 constexpr char kAngleFeatureOverridesEnabledEnvName[] = "ANGLE_FEATURE_OVERRIDES_ENABLED";
1072 constexpr char kAngleFeatureOverridesDisabledEnvName[] = "ANGLE_FEATURE_OVERRIDES_DISABLED";
1073 constexpr char kAngleFeatureOverridesEnabledPropertyName[] =
1074 "debug.angle.feature_overrides_enabled";
1075 constexpr char kAngleFeatureOverridesDisabledPropertyName[] =
1076 "debug.angle.feature_overrides_disabled";
1077 std::vector<std::string> overridesEnabled =
1078 angle::GetCachedStringsFromEnvironmentVarOrAndroidProperty(
1079 kAngleFeatureOverridesEnabledEnvName, kAngleFeatureOverridesEnabledPropertyName, ":");
1080 std::vector<std::string> overridesDisabled =
1081 angle::GetCachedStringsFromEnvironmentVarOrAndroidProperty(
1082 kAngleFeatureOverridesDisabledEnvName, kAngleFeatureOverridesDisabledPropertyName, ":");
1083
1084 features->overrideFeatures(overridesEnabled, true);
1085 LogFeatureStatus(*features, overridesEnabled, true);
1086
1087 features->overrideFeatures(overridesDisabled, false);
1088 LogFeatureStatus(*features, overridesDisabled, false);
1089 }
1090
GetSamplePosition(GLsizei sampleCount,size_t index,GLfloat * xy)1091 void GetSamplePosition(GLsizei sampleCount, size_t index, GLfloat *xy)
1092 {
1093 ASSERT(gl::isPow2(sampleCount));
1094 if (sampleCount > 16)
1095 {
1096 // Vulkan (and D3D11) doesn't have standard sample positions for 32 and 64 samples (and no
1097 // drivers are known to support that many samples)
1098 xy[0] = 0.5f;
1099 xy[1] = 0.5f;
1100 }
1101 else
1102 {
1103 size_t indexKey = static_cast<size_t>(gl::log2(sampleCount));
1104 ASSERT(indexKey < kSamplePositions.size() &&
1105 (2 * index + 1) < kSamplePositions[indexKey].size());
1106
1107 xy[0] = kSamplePositions[indexKey][2 * index];
1108 xy[1] = kSamplePositions[indexKey][2 * index + 1];
1109 }
1110 }
1111
1112 // These macros are to avoid code too much duplication for variations of multi draw types
1113 #define DRAW_ARRAYS__ contextImpl->drawArrays(context, mode, firsts[drawID], counts[drawID])
1114 #define DRAW_ARRAYS_INSTANCED_ \
1115 contextImpl->drawArraysInstanced(context, mode, firsts[drawID], counts[drawID], \
1116 instanceCounts[drawID])
1117 #define DRAW_ELEMENTS__ \
1118 contextImpl->drawElements(context, mode, counts[drawID], type, indices[drawID])
1119 #define DRAW_ELEMENTS_INSTANCED_ \
1120 contextImpl->drawElementsInstanced(context, mode, counts[drawID], type, indices[drawID], \
1121 instanceCounts[drawID])
1122 #define DRAW_ARRAYS_INSTANCED_BASE_INSTANCE \
1123 contextImpl->drawArraysInstancedBaseInstance(context, mode, firsts[drawID], counts[drawID], \
1124 instanceCounts[drawID], baseInstances[drawID])
1125 #define DRAW_ELEMENTS_INSTANCED_BASE_VERTEX_BASE_INSTANCE \
1126 contextImpl->drawElementsInstancedBaseVertexBaseInstance( \
1127 context, mode, counts[drawID], type, indices[drawID], instanceCounts[drawID], \
1128 baseVertices[drawID], baseInstances[drawID])
1129 #define DRAW_CALL(drawType, instanced, bvbi) DRAW_##drawType##instanced##bvbi
1130
1131 #define MULTI_DRAW_BLOCK(drawType, instanced, bvbi, hasDrawID, hasBaseVertex, hasBaseInstance) \
1132 for (GLsizei drawID = 0; drawID < drawcount; ++drawID) \
1133 { \
1134 if (ANGLE_NOOP_DRAW(instanced)) \
1135 { \
1136 ANGLE_TRY(contextImpl->handleNoopDrawEvent()); \
1137 continue; \
1138 } \
1139 ANGLE_SET_DRAW_ID_UNIFORM(hasDrawID)(drawID); \
1140 ANGLE_SET_BASE_VERTEX_UNIFORM(hasBaseVertex)(baseVertices[drawID]); \
1141 ANGLE_SET_BASE_INSTANCE_UNIFORM(hasBaseInstance)(baseInstances[drawID]); \
1142 ANGLE_TRY(DRAW_CALL(drawType, instanced, bvbi)); \
1143 ANGLE_MARK_TRANSFORM_FEEDBACK_USAGE(instanced); \
1144 gl::MarkShaderStorageUsage(context); \
1145 }
1146
MultiDrawArraysGeneral(ContextImpl * contextImpl,const gl::Context * context,gl::PrimitiveMode mode,const GLint * firsts,const GLsizei * counts,GLsizei drawcount)1147 angle::Result MultiDrawArraysGeneral(ContextImpl *contextImpl,
1148 const gl::Context *context,
1149 gl::PrimitiveMode mode,
1150 const GLint *firsts,
1151 const GLsizei *counts,
1152 GLsizei drawcount)
1153 {
1154 gl::Program *programObject = context->getState().getLinkedProgram(context);
1155 const bool hasDrawID = programObject && programObject->hasDrawIDUniform();
1156 if (hasDrawID)
1157 {
1158 MULTI_DRAW_BLOCK(ARRAYS, _, _, 1, 0, 0)
1159 }
1160 else
1161 {
1162 MULTI_DRAW_BLOCK(ARRAYS, _, _, 0, 0, 0)
1163 }
1164
1165 return angle::Result::Continue;
1166 }
1167
MultiDrawArraysIndirectGeneral(ContextImpl * contextImpl,const gl::Context * context,gl::PrimitiveMode mode,const void * indirect,GLsizei drawcount,GLsizei stride)1168 angle::Result MultiDrawArraysIndirectGeneral(ContextImpl *contextImpl,
1169 const gl::Context *context,
1170 gl::PrimitiveMode mode,
1171 const void *indirect,
1172 GLsizei drawcount,
1173 GLsizei stride)
1174 {
1175 const GLubyte *indirectPtr = static_cast<const GLubyte *>(indirect);
1176
1177 for (auto count = 0; count < drawcount; count++)
1178 {
1179 ANGLE_TRY(contextImpl->drawArraysIndirect(
1180 context, mode, reinterpret_cast<const gl::DrawArraysIndirectCommand *>(indirectPtr)));
1181 if (stride == 0)
1182 {
1183 indirectPtr += sizeof(gl::DrawArraysIndirectCommand);
1184 }
1185 else
1186 {
1187 indirectPtr += stride;
1188 }
1189 }
1190
1191 return angle::Result::Continue;
1192 }
1193
MultiDrawArraysInstancedGeneral(ContextImpl * contextImpl,const gl::Context * context,gl::PrimitiveMode mode,const GLint * firsts,const GLsizei * counts,const GLsizei * instanceCounts,GLsizei drawcount)1194 angle::Result MultiDrawArraysInstancedGeneral(ContextImpl *contextImpl,
1195 const gl::Context *context,
1196 gl::PrimitiveMode mode,
1197 const GLint *firsts,
1198 const GLsizei *counts,
1199 const GLsizei *instanceCounts,
1200 GLsizei drawcount)
1201 {
1202 gl::Program *programObject = context->getState().getLinkedProgram(context);
1203 const bool hasDrawID = programObject && programObject->hasDrawIDUniform();
1204 if (hasDrawID)
1205 {
1206 MULTI_DRAW_BLOCK(ARRAYS, _INSTANCED, _, 1, 0, 0)
1207 }
1208 else
1209 {
1210 MULTI_DRAW_BLOCK(ARRAYS, _INSTANCED, _, 0, 0, 0)
1211 }
1212
1213 return angle::Result::Continue;
1214 }
1215
MultiDrawElementsGeneral(ContextImpl * contextImpl,const gl::Context * context,gl::PrimitiveMode mode,const GLsizei * counts,gl::DrawElementsType type,const GLvoid * const * indices,GLsizei drawcount)1216 angle::Result MultiDrawElementsGeneral(ContextImpl *contextImpl,
1217 const gl::Context *context,
1218 gl::PrimitiveMode mode,
1219 const GLsizei *counts,
1220 gl::DrawElementsType type,
1221 const GLvoid *const *indices,
1222 GLsizei drawcount)
1223 {
1224 gl::Program *programObject = context->getState().getLinkedProgram(context);
1225 const bool hasDrawID = programObject && programObject->hasDrawIDUniform();
1226 if (hasDrawID)
1227 {
1228 MULTI_DRAW_BLOCK(ELEMENTS, _, _, 1, 0, 0)
1229 }
1230 else
1231 {
1232 MULTI_DRAW_BLOCK(ELEMENTS, _, _, 0, 0, 0)
1233 }
1234
1235 return angle::Result::Continue;
1236 }
1237
MultiDrawElementsIndirectGeneral(ContextImpl * contextImpl,const gl::Context * context,gl::PrimitiveMode mode,gl::DrawElementsType type,const void * indirect,GLsizei drawcount,GLsizei stride)1238 angle::Result MultiDrawElementsIndirectGeneral(ContextImpl *contextImpl,
1239 const gl::Context *context,
1240 gl::PrimitiveMode mode,
1241 gl::DrawElementsType type,
1242 const void *indirect,
1243 GLsizei drawcount,
1244 GLsizei stride)
1245 {
1246 const GLubyte *indirectPtr = static_cast<const GLubyte *>(indirect);
1247
1248 for (auto count = 0; count < drawcount; count++)
1249 {
1250 ANGLE_TRY(contextImpl->drawElementsIndirect(
1251 context, mode, type,
1252 reinterpret_cast<const gl::DrawElementsIndirectCommand *>(indirectPtr)));
1253 if (stride == 0)
1254 {
1255 indirectPtr += sizeof(gl::DrawElementsIndirectCommand);
1256 }
1257 else
1258 {
1259 indirectPtr += stride;
1260 }
1261 }
1262
1263 return angle::Result::Continue;
1264 }
1265
MultiDrawElementsInstancedGeneral(ContextImpl * contextImpl,const gl::Context * context,gl::PrimitiveMode mode,const GLsizei * counts,gl::DrawElementsType type,const GLvoid * const * indices,const GLsizei * instanceCounts,GLsizei drawcount)1266 angle::Result MultiDrawElementsInstancedGeneral(ContextImpl *contextImpl,
1267 const gl::Context *context,
1268 gl::PrimitiveMode mode,
1269 const GLsizei *counts,
1270 gl::DrawElementsType type,
1271 const GLvoid *const *indices,
1272 const GLsizei *instanceCounts,
1273 GLsizei drawcount)
1274 {
1275 gl::Program *programObject = context->getState().getLinkedProgram(context);
1276 const bool hasDrawID = programObject && programObject->hasDrawIDUniform();
1277 if (hasDrawID)
1278 {
1279 MULTI_DRAW_BLOCK(ELEMENTS, _INSTANCED, _, 1, 0, 0)
1280 }
1281 else
1282 {
1283 MULTI_DRAW_BLOCK(ELEMENTS, _INSTANCED, _, 0, 0, 0)
1284 }
1285
1286 return angle::Result::Continue;
1287 }
1288
MultiDrawArraysInstancedBaseInstanceGeneral(ContextImpl * contextImpl,const gl::Context * context,gl::PrimitiveMode mode,const GLint * firsts,const GLsizei * counts,const GLsizei * instanceCounts,const GLuint * baseInstances,GLsizei drawcount)1289 angle::Result MultiDrawArraysInstancedBaseInstanceGeneral(ContextImpl *contextImpl,
1290 const gl::Context *context,
1291 gl::PrimitiveMode mode,
1292 const GLint *firsts,
1293 const GLsizei *counts,
1294 const GLsizei *instanceCounts,
1295 const GLuint *baseInstances,
1296 GLsizei drawcount)
1297 {
1298 gl::Program *programObject = context->getState().getLinkedProgram(context);
1299 const bool hasDrawID = programObject && programObject->hasDrawIDUniform();
1300 const bool hasBaseInstance = programObject && programObject->hasBaseInstanceUniform();
1301 ResetBaseVertexBaseInstance resetUniforms(programObject, false, hasBaseInstance);
1302
1303 if (hasDrawID && hasBaseInstance)
1304 {
1305 MULTI_DRAW_BLOCK(ARRAYS, _INSTANCED, _BASE_INSTANCE, 1, 0, 1)
1306 }
1307 else if (hasDrawID)
1308 {
1309 MULTI_DRAW_BLOCK(ARRAYS, _INSTANCED, _BASE_INSTANCE, 1, 0, 0)
1310 }
1311 else if (hasBaseInstance)
1312 {
1313 MULTI_DRAW_BLOCK(ARRAYS, _INSTANCED, _BASE_INSTANCE, 0, 0, 1)
1314 }
1315 else
1316 {
1317 MULTI_DRAW_BLOCK(ARRAYS, _INSTANCED, _BASE_INSTANCE, 0, 0, 0)
1318 }
1319
1320 return angle::Result::Continue;
1321 }
1322
MultiDrawElementsInstancedBaseVertexBaseInstanceGeneral(ContextImpl * contextImpl,const gl::Context * context,gl::PrimitiveMode mode,const GLsizei * counts,gl::DrawElementsType type,const GLvoid * const * indices,const GLsizei * instanceCounts,const GLint * baseVertices,const GLuint * baseInstances,GLsizei drawcount)1323 angle::Result MultiDrawElementsInstancedBaseVertexBaseInstanceGeneral(ContextImpl *contextImpl,
1324 const gl::Context *context,
1325 gl::PrimitiveMode mode,
1326 const GLsizei *counts,
1327 gl::DrawElementsType type,
1328 const GLvoid *const *indices,
1329 const GLsizei *instanceCounts,
1330 const GLint *baseVertices,
1331 const GLuint *baseInstances,
1332 GLsizei drawcount)
1333 {
1334 gl::Program *programObject = context->getState().getLinkedProgram(context);
1335 const bool hasDrawID = programObject && programObject->hasDrawIDUniform();
1336 const bool hasBaseVertex = programObject && programObject->hasBaseVertexUniform();
1337 const bool hasBaseInstance = programObject && programObject->hasBaseInstanceUniform();
1338 ResetBaseVertexBaseInstance resetUniforms(programObject, hasBaseVertex, hasBaseInstance);
1339
1340 if (hasDrawID)
1341 {
1342 if (hasBaseVertex)
1343 {
1344 if (hasBaseInstance)
1345 {
1346 MULTI_DRAW_BLOCK(ELEMENTS, _INSTANCED, _BASE_VERTEX_BASE_INSTANCE, 1, 1, 1)
1347 }
1348 else
1349 {
1350 MULTI_DRAW_BLOCK(ELEMENTS, _INSTANCED, _BASE_VERTEX_BASE_INSTANCE, 1, 1, 0)
1351 }
1352 }
1353 else
1354 {
1355 if (hasBaseInstance)
1356 {
1357 MULTI_DRAW_BLOCK(ELEMENTS, _INSTANCED, _BASE_VERTEX_BASE_INSTANCE, 1, 0, 1)
1358 }
1359 else
1360 {
1361 MULTI_DRAW_BLOCK(ELEMENTS, _INSTANCED, _BASE_VERTEX_BASE_INSTANCE, 1, 0, 0)
1362 }
1363 }
1364 }
1365 else
1366 {
1367 if (hasBaseVertex)
1368 {
1369 if (hasBaseInstance)
1370 {
1371 MULTI_DRAW_BLOCK(ELEMENTS, _INSTANCED, _BASE_VERTEX_BASE_INSTANCE, 0, 1, 1)
1372 }
1373 else
1374 {
1375 MULTI_DRAW_BLOCK(ELEMENTS, _INSTANCED, _BASE_VERTEX_BASE_INSTANCE, 0, 1, 0)
1376 }
1377 }
1378 else
1379 {
1380 if (hasBaseInstance)
1381 {
1382 MULTI_DRAW_BLOCK(ELEMENTS, _INSTANCED, _BASE_VERTEX_BASE_INSTANCE, 0, 0, 1)
1383 }
1384 else
1385 {
1386 MULTI_DRAW_BLOCK(ELEMENTS, _INSTANCED, _BASE_VERTEX_BASE_INSTANCE, 0, 0, 0)
1387 }
1388 }
1389 }
1390
1391 return angle::Result::Continue;
1392 }
1393
ResetBaseVertexBaseInstance(gl::Program * programObject,bool resetBaseVertex,bool resetBaseInstance)1394 ResetBaseVertexBaseInstance::ResetBaseVertexBaseInstance(gl::Program *programObject,
1395 bool resetBaseVertex,
1396 bool resetBaseInstance)
1397 : mProgramObject(programObject),
1398 mResetBaseVertex(resetBaseVertex),
1399 mResetBaseInstance(resetBaseInstance)
1400 {}
1401
~ResetBaseVertexBaseInstance()1402 ResetBaseVertexBaseInstance::~ResetBaseVertexBaseInstance()
1403 {
1404 if (mProgramObject)
1405 {
1406 // Reset emulated uniforms to zero to avoid affecting other draw calls
1407 if (mResetBaseVertex)
1408 {
1409 mProgramObject->setBaseVertexUniform(0);
1410 }
1411
1412 if (mResetBaseInstance)
1413 {
1414 mProgramObject->setBaseInstanceUniform(0);
1415 }
1416 }
1417 }
1418
ConvertToSRGB(angle::FormatID formatID)1419 angle::FormatID ConvertToSRGB(angle::FormatID formatID)
1420 {
1421 switch (formatID)
1422 {
1423 case angle::FormatID::R8_UNORM:
1424 return angle::FormatID::R8_UNORM_SRGB;
1425 case angle::FormatID::R8G8_UNORM:
1426 return angle::FormatID::R8G8_UNORM_SRGB;
1427 case angle::FormatID::R8G8B8_UNORM:
1428 return angle::FormatID::R8G8B8_UNORM_SRGB;
1429 case angle::FormatID::R8G8B8A8_UNORM:
1430 return angle::FormatID::R8G8B8A8_UNORM_SRGB;
1431 case angle::FormatID::B8G8R8A8_UNORM:
1432 return angle::FormatID::B8G8R8A8_UNORM_SRGB;
1433 case angle::FormatID::BC1_RGB_UNORM_BLOCK:
1434 return angle::FormatID::BC1_RGB_UNORM_SRGB_BLOCK;
1435 case angle::FormatID::BC1_RGBA_UNORM_BLOCK:
1436 return angle::FormatID::BC1_RGBA_UNORM_SRGB_BLOCK;
1437 case angle::FormatID::BC2_RGBA_UNORM_BLOCK:
1438 return angle::FormatID::BC2_RGBA_UNORM_SRGB_BLOCK;
1439 case angle::FormatID::BC3_RGBA_UNORM_BLOCK:
1440 return angle::FormatID::BC3_RGBA_UNORM_SRGB_BLOCK;
1441 case angle::FormatID::BC7_RGBA_UNORM_BLOCK:
1442 return angle::FormatID::BC7_RGBA_UNORM_SRGB_BLOCK;
1443 case angle::FormatID::ETC2_R8G8B8_UNORM_BLOCK:
1444 return angle::FormatID::ETC2_R8G8B8_SRGB_BLOCK;
1445 case angle::FormatID::ETC2_R8G8B8A1_UNORM_BLOCK:
1446 return angle::FormatID::ETC2_R8G8B8A1_SRGB_BLOCK;
1447 case angle::FormatID::ETC2_R8G8B8A8_UNORM_BLOCK:
1448 return angle::FormatID::ETC2_R8G8B8A8_SRGB_BLOCK;
1449 case angle::FormatID::ASTC_4x4_UNORM_BLOCK:
1450 return angle::FormatID::ASTC_4x4_SRGB_BLOCK;
1451 case angle::FormatID::ASTC_5x4_UNORM_BLOCK:
1452 return angle::FormatID::ASTC_5x4_SRGB_BLOCK;
1453 case angle::FormatID::ASTC_5x5_UNORM_BLOCK:
1454 return angle::FormatID::ASTC_5x5_SRGB_BLOCK;
1455 case angle::FormatID::ASTC_6x5_UNORM_BLOCK:
1456 return angle::FormatID::ASTC_6x5_SRGB_BLOCK;
1457 case angle::FormatID::ASTC_6x6_UNORM_BLOCK:
1458 return angle::FormatID::ASTC_6x6_SRGB_BLOCK;
1459 case angle::FormatID::ASTC_8x5_UNORM_BLOCK:
1460 return angle::FormatID::ASTC_8x5_SRGB_BLOCK;
1461 case angle::FormatID::ASTC_8x6_UNORM_BLOCK:
1462 return angle::FormatID::ASTC_8x6_SRGB_BLOCK;
1463 case angle::FormatID::ASTC_8x8_UNORM_BLOCK:
1464 return angle::FormatID::ASTC_8x8_SRGB_BLOCK;
1465 case angle::FormatID::ASTC_10x5_UNORM_BLOCK:
1466 return angle::FormatID::ASTC_10x5_SRGB_BLOCK;
1467 case angle::FormatID::ASTC_10x6_UNORM_BLOCK:
1468 return angle::FormatID::ASTC_10x6_SRGB_BLOCK;
1469 case angle::FormatID::ASTC_10x8_UNORM_BLOCK:
1470 return angle::FormatID::ASTC_10x8_SRGB_BLOCK;
1471 case angle::FormatID::ASTC_10x10_UNORM_BLOCK:
1472 return angle::FormatID::ASTC_10x10_SRGB_BLOCK;
1473 case angle::FormatID::ASTC_12x10_UNORM_BLOCK:
1474 return angle::FormatID::ASTC_12x10_SRGB_BLOCK;
1475 case angle::FormatID::ASTC_12x12_UNORM_BLOCK:
1476 return angle::FormatID::ASTC_12x12_SRGB_BLOCK;
1477 default:
1478 return angle::FormatID::NONE;
1479 }
1480 }
1481
ConvertToLinear(angle::FormatID formatID)1482 angle::FormatID ConvertToLinear(angle::FormatID formatID)
1483 {
1484 switch (formatID)
1485 {
1486 case angle::FormatID::R8_UNORM_SRGB:
1487 return angle::FormatID::R8_UNORM;
1488 case angle::FormatID::R8G8_UNORM_SRGB:
1489 return angle::FormatID::R8G8_UNORM;
1490 case angle::FormatID::R8G8B8_UNORM_SRGB:
1491 return angle::FormatID::R8G8B8_UNORM;
1492 case angle::FormatID::R8G8B8A8_UNORM_SRGB:
1493 return angle::FormatID::R8G8B8A8_UNORM;
1494 case angle::FormatID::B8G8R8A8_UNORM_SRGB:
1495 return angle::FormatID::B8G8R8A8_UNORM;
1496 case angle::FormatID::BC1_RGB_UNORM_SRGB_BLOCK:
1497 return angle::FormatID::BC1_RGB_UNORM_BLOCK;
1498 case angle::FormatID::BC1_RGBA_UNORM_SRGB_BLOCK:
1499 return angle::FormatID::BC1_RGBA_UNORM_BLOCK;
1500 case angle::FormatID::BC2_RGBA_UNORM_SRGB_BLOCK:
1501 return angle::FormatID::BC2_RGBA_UNORM_BLOCK;
1502 case angle::FormatID::BC3_RGBA_UNORM_SRGB_BLOCK:
1503 return angle::FormatID::BC3_RGBA_UNORM_BLOCK;
1504 case angle::FormatID::BC7_RGBA_UNORM_SRGB_BLOCK:
1505 return angle::FormatID::BC7_RGBA_UNORM_BLOCK;
1506 case angle::FormatID::ETC2_R8G8B8_SRGB_BLOCK:
1507 return angle::FormatID::ETC2_R8G8B8_UNORM_BLOCK;
1508 case angle::FormatID::ETC2_R8G8B8A1_SRGB_BLOCK:
1509 return angle::FormatID::ETC2_R8G8B8A1_UNORM_BLOCK;
1510 case angle::FormatID::ETC2_R8G8B8A8_SRGB_BLOCK:
1511 return angle::FormatID::ETC2_R8G8B8A8_UNORM_BLOCK;
1512 case angle::FormatID::ASTC_4x4_SRGB_BLOCK:
1513 return angle::FormatID::ASTC_4x4_UNORM_BLOCK;
1514 case angle::FormatID::ASTC_5x4_SRGB_BLOCK:
1515 return angle::FormatID::ASTC_5x4_UNORM_BLOCK;
1516 case angle::FormatID::ASTC_5x5_SRGB_BLOCK:
1517 return angle::FormatID::ASTC_5x5_UNORM_BLOCK;
1518 case angle::FormatID::ASTC_6x5_SRGB_BLOCK:
1519 return angle::FormatID::ASTC_6x5_UNORM_BLOCK;
1520 case angle::FormatID::ASTC_6x6_SRGB_BLOCK:
1521 return angle::FormatID::ASTC_6x6_UNORM_BLOCK;
1522 case angle::FormatID::ASTC_8x5_SRGB_BLOCK:
1523 return angle::FormatID::ASTC_8x5_UNORM_BLOCK;
1524 case angle::FormatID::ASTC_8x6_SRGB_BLOCK:
1525 return angle::FormatID::ASTC_8x6_UNORM_BLOCK;
1526 case angle::FormatID::ASTC_8x8_SRGB_BLOCK:
1527 return angle::FormatID::ASTC_8x8_UNORM_BLOCK;
1528 case angle::FormatID::ASTC_10x5_SRGB_BLOCK:
1529 return angle::FormatID::ASTC_10x5_UNORM_BLOCK;
1530 case angle::FormatID::ASTC_10x6_SRGB_BLOCK:
1531 return angle::FormatID::ASTC_10x6_UNORM_BLOCK;
1532 case angle::FormatID::ASTC_10x8_SRGB_BLOCK:
1533 return angle::FormatID::ASTC_10x8_UNORM_BLOCK;
1534 case angle::FormatID::ASTC_10x10_SRGB_BLOCK:
1535 return angle::FormatID::ASTC_10x10_UNORM_BLOCK;
1536 case angle::FormatID::ASTC_12x10_SRGB_BLOCK:
1537 return angle::FormatID::ASTC_12x10_UNORM_BLOCK;
1538 case angle::FormatID::ASTC_12x12_SRGB_BLOCK:
1539 return angle::FormatID::ASTC_12x12_UNORM_BLOCK;
1540 default:
1541 return angle::FormatID::NONE;
1542 }
1543 }
1544
IsOverridableLinearFormat(angle::FormatID formatID)1545 bool IsOverridableLinearFormat(angle::FormatID formatID)
1546 {
1547 return ConvertToSRGB(formatID) != angle::FormatID::NONE;
1548 }
1549 } // namespace rx
1550