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 // vk_format_utils:
7 // Helper for Vulkan format code.
8
9 #include "libANGLE/renderer/vulkan/vk_format_utils.h"
10
11 #include "libANGLE/Texture.h"
12 #include "libANGLE/formatutils.h"
13 #include "libANGLE/renderer/load_functions_table.h"
14 #include "libANGLE/renderer/vulkan/ContextVk.h"
15 #include "libANGLE/renderer/vulkan/RendererVk.h"
16 #include "libANGLE/renderer/vulkan/vk_caps_utils.h"
17
18 namespace rx
19 {
20 namespace
21 {
FillTextureFormatCaps(RendererVk * renderer,angle::FormatID formatID,gl::TextureCaps * outTextureCaps)22 void FillTextureFormatCaps(RendererVk *renderer,
23 angle::FormatID formatID,
24 gl::TextureCaps *outTextureCaps)
25 {
26 const VkPhysicalDeviceLimits &physicalDeviceLimits =
27 renderer->getPhysicalDeviceProperties().limits;
28 bool hasColorAttachmentFeatureBit =
29 renderer->hasImageFormatFeatureBits(formatID, VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT);
30 bool hasDepthAttachmentFeatureBit = renderer->hasImageFormatFeatureBits(
31 formatID, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT);
32
33 outTextureCaps->texturable =
34 renderer->hasImageFormatFeatureBits(formatID, VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT);
35 outTextureCaps->filterable = renderer->hasImageFormatFeatureBits(
36 formatID, VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT);
37 outTextureCaps->blendable =
38 renderer->hasImageFormatFeatureBits(formatID, VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT);
39
40 // For renderbuffer and texture attachments we require transfer and sampling for
41 // GLES 2.0 CopyTexImage support. Sampling is also required for other features like
42 // blits and EGLImages.
43 outTextureCaps->textureAttachment =
44 outTextureCaps->texturable &&
45 (hasColorAttachmentFeatureBit || hasDepthAttachmentFeatureBit);
46 outTextureCaps->renderbuffer = outTextureCaps->textureAttachment;
47
48 if (outTextureCaps->renderbuffer)
49 {
50 if (hasColorAttachmentFeatureBit)
51 {
52 vk_gl::AddSampleCounts(physicalDeviceLimits.framebufferColorSampleCounts,
53 &outTextureCaps->sampleCounts);
54 }
55 if (hasDepthAttachmentFeatureBit)
56 {
57 // Some drivers report different depth and stencil sample counts. We'll AND those
58 // counts together, limiting all depth and/or stencil formats to the lower number of
59 // sample counts.
60 vk_gl::AddSampleCounts((physicalDeviceLimits.framebufferDepthSampleCounts &
61 physicalDeviceLimits.framebufferStencilSampleCounts),
62 &outTextureCaps->sampleCounts);
63 }
64 }
65 }
66
HasFullBufferFormatSupport(RendererVk * renderer,angle::FormatID formatID)67 bool HasFullBufferFormatSupport(RendererVk *renderer, angle::FormatID formatID)
68 {
69 // Note: GL_EXT_texture_buffer support uses the same vkBufferFormat that is determined by
70 // Format::initBufferFallback, which uses this function. That relies on the fact that formats
71 // required for GL_EXT_texture_buffer all have mandatory VERTEX_BUFFER feature support in
72 // Vulkan. If this function is changed to test for more features in such a way that makes any
73 // of those formats use a fallback format, the implementation of GL_EXT_texture_buffer must be
74 // modified not to use vkBufferFormat.
75 return renderer->hasBufferFormatFeatureBits(formatID, VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT);
76 }
77
78 using SupportTest = bool (*)(RendererVk *renderer, angle::FormatID formatID);
79
80 template <class FormatInitInfo>
FindSupportedFormat(RendererVk * renderer,const FormatInitInfo * info,size_t skip,int numInfo,SupportTest hasSupport)81 int FindSupportedFormat(RendererVk *renderer,
82 const FormatInitInfo *info,
83 size_t skip,
84 int numInfo,
85 SupportTest hasSupport)
86 {
87 ASSERT(numInfo > 0);
88 const int last = numInfo - 1;
89
90 for (int i = static_cast<int>(skip); i < last; ++i)
91 {
92 ASSERT(info[i].format != angle::FormatID::NONE);
93 if (hasSupport(renderer, info[i].format))
94 return i;
95 }
96
97 if (skip > 0 && !hasSupport(renderer, info[last].format))
98 {
99 // We couldn't find a valid fallback, try again without skip
100 return FindSupportedFormat(renderer, info, 0, numInfo, hasSupport);
101 }
102
103 ASSERT(info[last].format != angle::FormatID::NONE);
104 ASSERT(hasSupport(renderer, info[last].format));
105 return last;
106 }
107
HasNonFilterableTextureFormatSupport(RendererVk * renderer,angle::FormatID formatID)108 bool HasNonFilterableTextureFormatSupport(RendererVk *renderer, angle::FormatID formatID)
109 {
110 constexpr uint32_t kBitsColor =
111 VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT;
112 constexpr uint32_t kBitsDepth = VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT;
113
114 return renderer->hasImageFormatFeatureBits(formatID, kBitsColor) ||
115 renderer->hasImageFormatFeatureBits(formatID, kBitsDepth);
116 }
117 } // anonymous namespace
118
119 namespace vk
120 {
121 // Format implementation.
Format()122 Format::Format()
123 : mIntendedFormatID(angle::FormatID::NONE),
124 mIntendedGLFormat(GL_NONE),
125 mActualSampleOnlyImageFormatID(angle::FormatID::NONE),
126 mActualRenderableImageFormatID(angle::FormatID::NONE),
127 mActualBufferFormatID(angle::FormatID::NONE),
128 mActualCompressedBufferFormatID(angle::FormatID::NONE),
129 mImageInitializerFunction(nullptr),
130 mTextureLoadFunctions(),
131 mRenderableTextureLoadFunctions(),
132 mVertexLoadFunction(nullptr),
133 mCompressedVertexLoadFunction(nullptr),
134 mVertexLoadRequiresConversion(false),
135 mCompressedVertexLoadRequiresConversion(false),
136 mVkBufferFormatIsPacked(false),
137 mVkFormatIsInt(false),
138 mVkFormatIsUnsigned(false)
139 {}
140
initImageFallback(RendererVk * renderer,const ImageFormatInitInfo * info,int numInfo)141 void Format::initImageFallback(RendererVk *renderer, const ImageFormatInitInfo *info, int numInfo)
142 {
143 size_t skip = renderer->getFeatures().forceFallbackFormat.enabled ? 1 : 0;
144 SupportTest testFunction = HasNonRenderableTextureFormatSupport;
145 const angle::Format &format = angle::Format::Get(info[0].format);
146 if (format.isInt() || (format.isFloat() && format.redBits >= 32))
147 {
148 // Integer formats don't support filtering in GL, so don't test for it.
149 // Filtering of 32-bit float textures is not supported on Android, and
150 // it's enabled by the extension OES_texture_float_linear, which is
151 // enabled automatically by examining format capabilities.
152 testFunction = HasNonFilterableTextureFormatSupport;
153 }
154
155 int i = FindSupportedFormat(renderer, info, skip, static_cast<uint32_t>(numInfo), testFunction);
156 mActualSampleOnlyImageFormatID = info[i].format;
157 mImageInitializerFunction = info[i].initializer;
158
159 // Set renderable format.
160 if (testFunction != HasNonFilterableTextureFormatSupport && !format.isSnorm() &&
161 !format.isBlock)
162 {
163 // Rendering to SNORM textures is not supported on Android, and it's
164 // enabled by the extension EXT_render_snorm.
165 // Compressed textures also need to perform this check.
166 testFunction = HasFullTextureFormatSupport;
167 i = FindSupportedFormat(renderer, info, skip, static_cast<uint32_t>(numInfo), testFunction);
168 mActualRenderableImageFormatID = info[i].format;
169 }
170 }
171
initBufferFallback(RendererVk * renderer,const BufferFormatInitInfo * info,int numInfo,int compressedStartIndex)172 void Format::initBufferFallback(RendererVk *renderer,
173 const BufferFormatInitInfo *info,
174 int numInfo,
175 int compressedStartIndex)
176 {
177 {
178 size_t skip = renderer->getFeatures().forceFallbackFormat.enabled ? 1 : 0;
179 int i = FindSupportedFormat(renderer, info, skip, compressedStartIndex,
180 HasFullBufferFormatSupport);
181
182 mActualBufferFormatID = info[i].format;
183 mVkBufferFormatIsPacked = info[i].vkFormatIsPacked;
184 mVertexLoadFunction = info[i].vertexLoadFunction;
185 mVertexLoadRequiresConversion = info[i].vertexLoadRequiresConversion;
186 }
187
188 if (renderer->getFeatures().compressVertexData.enabled && compressedStartIndex < numInfo)
189 {
190 int i = FindSupportedFormat(renderer, info, compressedStartIndex, numInfo,
191 HasFullBufferFormatSupport);
192
193 mActualCompressedBufferFormatID = info[i].format;
194 mVkCompressedBufferFormatIsPacked = info[i].vkFormatIsPacked;
195 mCompressedVertexLoadFunction = info[i].vertexLoadFunction;
196 mCompressedVertexLoadRequiresConversion = info[i].vertexLoadRequiresConversion;
197 }
198 }
199
getVertexInputAlignment(bool compressed) const200 size_t Format::getVertexInputAlignment(bool compressed) const
201 {
202 const angle::Format &bufferFormat = getActualBufferFormat(compressed);
203 size_t pixelBytes = bufferFormat.pixelBytes;
204 return mVkBufferFormatIsPacked ? pixelBytes : (pixelBytes / bufferFormat.channelCount);
205 }
206
HasEmulatedImageChannels(const angle::Format & intendedFormat,const angle::Format & actualFormat)207 bool HasEmulatedImageChannels(const angle::Format &intendedFormat,
208 const angle::Format &actualFormat)
209 {
210 return (intendedFormat.alphaBits == 0 && actualFormat.alphaBits > 0) ||
211 (intendedFormat.blueBits == 0 && actualFormat.blueBits > 0) ||
212 (intendedFormat.greenBits == 0 && actualFormat.greenBits > 0) ||
213 (intendedFormat.depthBits == 0 && actualFormat.depthBits > 0) ||
214 (intendedFormat.stencilBits == 0 && actualFormat.stencilBits > 0);
215 }
216
HasEmulatedImageFormat(angle::FormatID intendedFormatID,angle::FormatID actualFormatID)217 bool HasEmulatedImageFormat(angle::FormatID intendedFormatID, angle::FormatID actualFormatID)
218 {
219 return actualFormatID != intendedFormatID;
220 }
221
operator ==(const Format & lhs,const Format & rhs)222 bool operator==(const Format &lhs, const Format &rhs)
223 {
224 return &lhs == &rhs;
225 }
226
operator !=(const Format & lhs,const Format & rhs)227 bool operator!=(const Format &lhs, const Format &rhs)
228 {
229 return &lhs != &rhs;
230 }
231
232 // FormatTable implementation.
FormatTable()233 FormatTable::FormatTable() {}
234
~FormatTable()235 FormatTable::~FormatTable() {}
236
initialize(RendererVk * renderer,gl::TextureCapsMap * outTextureCapsMap,std::vector<GLenum> * outCompressedTextureFormats)237 void FormatTable::initialize(RendererVk *renderer,
238 gl::TextureCapsMap *outTextureCapsMap,
239 std::vector<GLenum> *outCompressedTextureFormats)
240 {
241 for (size_t formatIndex = 0; formatIndex < angle::kNumANGLEFormats; ++formatIndex)
242 {
243 Format &format = mFormatData[formatIndex];
244 const auto intendedFormatID = static_cast<angle::FormatID>(formatIndex);
245 const angle::Format &intendedAngleFormat = angle::Format::Get(intendedFormatID);
246
247 format.initialize(renderer, intendedAngleFormat);
248 format.mIntendedFormatID = intendedFormatID;
249
250 if (!format.valid())
251 {
252 continue;
253 }
254
255 // No sample-able or render-able formats, so nothing left to do. This includes skipping the
256 // rest of the loop for buffer-only formats, since they are not texturable.
257 if (format.mActualSampleOnlyImageFormatID == angle::FormatID::NONE)
258 {
259 continue;
260 }
261
262 if (intendedAngleFormat.isBlock)
263 {
264 outCompressedTextureFormats->push_back(format.mIntendedGLFormat);
265 }
266
267 if (format.mActualRenderableImageFormatID == angle::FormatID::NONE)
268 {
269 // If renderable format was not set, it means there is no fallback format for
270 // renderable. We populate this the same formatID as sampleOnly formatID so that
271 // getActualFormatID() will be simpler.
272 format.mActualRenderableImageFormatID = format.mActualSampleOnlyImageFormatID;
273 }
274
275 gl::TextureCaps textureCaps;
276 FillTextureFormatCaps(renderer, format.mActualSampleOnlyImageFormatID, &textureCaps);
277
278 if (textureCaps.texturable)
279 {
280 format.mTextureLoadFunctions = GetLoadFunctionsMap(
281 format.mIntendedGLFormat, format.mActualSampleOnlyImageFormatID);
282 }
283
284 if (format.mActualRenderableImageFormatID == format.mActualSampleOnlyImageFormatID)
285 {
286 outTextureCapsMap->set(intendedFormatID, textureCaps);
287 format.mRenderableTextureLoadFunctions = format.mTextureLoadFunctions;
288 }
289 else
290 {
291 FillTextureFormatCaps(renderer, format.mActualRenderableImageFormatID, &textureCaps);
292 outTextureCapsMap->set(intendedFormatID, textureCaps);
293 if (textureCaps.texturable)
294 {
295 format.mRenderableTextureLoadFunctions = GetLoadFunctionsMap(
296 format.mIntendedGLFormat, format.mActualRenderableImageFormatID);
297 }
298 }
299 }
300 }
301
GetImageCopyBufferAlignment(angle::FormatID actualFormatID)302 size_t GetImageCopyBufferAlignment(angle::FormatID actualFormatID)
303 {
304 // vkCmdCopyBufferToImage must have an offset that is a multiple of 4 as well as a multiple
305 // of the texel size (if uncompressed) or pixel block size (if compressed).
306 // https://www.khronos.org/registry/vulkan/specs/1.0/man/html/VkBufferImageCopy.html
307 //
308 // We need lcm(4, texelSize) (lcm = least common multiplier). For compressed images,
309 // |texelSize| would contain the block size. Since 4 is constant, this can be calculated as:
310 //
311 // | texelSize texelSize % 4 == 0
312 // | 4 * texelSize texelSize % 4 == 1
313 // lcm(4, texelSize) = <
314 // | 2 * texelSize texelSize % 4 == 2
315 // | 4 * texelSize texelSize % 4 == 3
316 //
317 // This means:
318 //
319 // - texelSize % 2 != 0 gives a 4x multiplier
320 // - else texelSize % 4 != 0 gives a 2x multiplier
321 // - else there's no multiplier.
322 //
323 const angle::Format &actualFormat = angle::Format::Get(actualFormatID);
324
325 ASSERT(actualFormat.pixelBytes != 0);
326 const size_t texelSize = actualFormat.pixelBytes;
327 const size_t multiplier = texelSize % 2 != 0 ? 4 : texelSize % 4 != 0 ? 2 : 1;
328 const size_t alignment = multiplier * texelSize;
329
330 return alignment;
331 }
332
GetValidImageCopyBufferAlignment(angle::FormatID intendedFormatID,angle::FormatID actualFormatID)333 size_t GetValidImageCopyBufferAlignment(angle::FormatID intendedFormatID,
334 angle::FormatID actualFormatID)
335 {
336 constexpr size_t kMinimumAlignment = 16;
337 return (intendedFormatID == angle::FormatID::NONE)
338 ? kMinimumAlignment
339 : GetImageCopyBufferAlignment(actualFormatID);
340 }
341
GetMaximalImageUsageFlags(RendererVk * renderer,angle::FormatID formatID)342 VkImageUsageFlags GetMaximalImageUsageFlags(RendererVk *renderer, angle::FormatID formatID)
343 {
344 constexpr VkFormatFeatureFlags kImageUsageFeatureBits =
345 VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT |
346 VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT |
347 VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT;
348 VkFormatFeatureFlags featureBits =
349 renderer->getImageFormatFeatureBits(formatID, kImageUsageFeatureBits);
350 VkImageUsageFlags imageUsageFlags = 0;
351 if (featureBits & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)
352 imageUsageFlags |= VK_IMAGE_USAGE_SAMPLED_BIT;
353 if (featureBits & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT)
354 imageUsageFlags |= VK_IMAGE_USAGE_STORAGE_BIT;
355 if (featureBits & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT)
356 imageUsageFlags |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
357 if (featureBits & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT)
358 imageUsageFlags |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
359 if (featureBits & VK_FORMAT_FEATURE_TRANSFER_SRC_BIT)
360 imageUsageFlags |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
361 if (featureBits & VK_FORMAT_FEATURE_TRANSFER_DST_BIT)
362 imageUsageFlags |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
363 imageUsageFlags |= VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
364 return imageUsageFlags;
365 }
366 } // namespace vk
367
HasFullTextureFormatSupport(RendererVk * renderer,angle::FormatID formatID)368 bool HasFullTextureFormatSupport(RendererVk *renderer, angle::FormatID formatID)
369 {
370 constexpr uint32_t kBitsColor = VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT |
371 VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT |
372 VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT;
373
374 // In OpenGL ES, all renderable formats except 32-bit floating-point support blending.
375 // 32-bit floating-point case validation is handled by ANGLE's frontend.
376 uint32_t kBitsColorFull = kBitsColor;
377 switch (formatID)
378 {
379 case angle::FormatID::R32_FLOAT:
380 case angle::FormatID::R32G32_FLOAT:
381 case angle::FormatID::R32G32B32A32_FLOAT:
382 break;
383 default:
384 kBitsColorFull |= VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT;
385 break;
386 }
387
388 constexpr uint32_t kBitsDepth = VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT;
389
390 return renderer->hasImageFormatFeatureBits(formatID, kBitsColorFull) ||
391 renderer->hasImageFormatFeatureBits(formatID, kBitsDepth);
392 }
393
HasNonRenderableTextureFormatSupport(RendererVk * renderer,angle::FormatID formatID)394 bool HasNonRenderableTextureFormatSupport(RendererVk *renderer, angle::FormatID formatID)
395 {
396 constexpr uint32_t kBitsColor =
397 VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT;
398 constexpr uint32_t kBitsDepth = VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT;
399
400 return renderer->hasImageFormatFeatureBits(formatID, kBitsColor) ||
401 renderer->hasImageFormatFeatureBits(formatID, kBitsDepth);
402 }
403
GetSwizzleStateComponent(const gl::SwizzleState & swizzleState,GLenum component)404 GLenum GetSwizzleStateComponent(const gl::SwizzleState &swizzleState, GLenum component)
405 {
406 switch (component)
407 {
408 case GL_RED:
409 return swizzleState.swizzleRed;
410 case GL_GREEN:
411 return swizzleState.swizzleGreen;
412 case GL_BLUE:
413 return swizzleState.swizzleBlue;
414 case GL_ALPHA:
415 return swizzleState.swizzleAlpha;
416 default:
417 return component;
418 }
419 }
420
ApplySwizzle(const gl::SwizzleState & formatSwizzle,const gl::SwizzleState & toApply)421 gl::SwizzleState ApplySwizzle(const gl::SwizzleState &formatSwizzle,
422 const gl::SwizzleState &toApply)
423 {
424 gl::SwizzleState result;
425
426 result.swizzleRed = GetSwizzleStateComponent(formatSwizzle, toApply.swizzleRed);
427 result.swizzleGreen = GetSwizzleStateComponent(formatSwizzle, toApply.swizzleGreen);
428 result.swizzleBlue = GetSwizzleStateComponent(formatSwizzle, toApply.swizzleBlue);
429 result.swizzleAlpha = GetSwizzleStateComponent(formatSwizzle, toApply.swizzleAlpha);
430
431 return result;
432 }
433
GetFormatSwizzle(const ContextVk * contextVk,const angle::Format & angleFormat,const bool sized)434 gl::SwizzleState GetFormatSwizzle(const ContextVk *contextVk,
435 const angle::Format &angleFormat,
436 const bool sized)
437 {
438 gl::SwizzleState internalSwizzle;
439
440 if (angleFormat.isLUMA())
441 {
442 GLenum swizzleRGB, swizzleA;
443 if (angleFormat.luminanceBits > 0)
444 {
445 swizzleRGB = GL_RED;
446 swizzleA = (angleFormat.alphaBits > 0 ? GL_GREEN : GL_ONE);
447 }
448 else
449 {
450 swizzleRGB = GL_ZERO;
451 swizzleA = GL_RED;
452 }
453 internalSwizzle.swizzleRed = swizzleRGB;
454 internalSwizzle.swizzleGreen = swizzleRGB;
455 internalSwizzle.swizzleBlue = swizzleRGB;
456 internalSwizzle.swizzleAlpha = swizzleA;
457 }
458 else
459 {
460 if (angleFormat.hasDepthOrStencilBits())
461 {
462 // In OES_depth_texture/ARB_depth_texture, depth
463 // textures are treated as luminance.
464 // If the internalformat was not sized, use OES_depth_texture behavior
465 bool hasGB = (angleFormat.depthBits > 0) && !sized;
466
467 internalSwizzle.swizzleRed = GL_RED;
468 internalSwizzle.swizzleGreen = hasGB ? GL_RED : GL_ZERO;
469 internalSwizzle.swizzleBlue = hasGB ? GL_RED : GL_ZERO;
470 internalSwizzle.swizzleAlpha = GL_ONE;
471 }
472 else
473 {
474 // Color bits are all zero for blocked formats
475 if (!angleFormat.isBlock)
476 {
477 // Set any missing channel to default in case the emulated format has that channel.
478 internalSwizzle.swizzleRed = angleFormat.redBits > 0 ? GL_RED : GL_ZERO;
479 internalSwizzle.swizzleGreen = angleFormat.greenBits > 0 ? GL_GREEN : GL_ZERO;
480 internalSwizzle.swizzleBlue = angleFormat.blueBits > 0 ? GL_BLUE : GL_ZERO;
481 internalSwizzle.swizzleAlpha = angleFormat.alphaBits > 0 ? GL_ALPHA : GL_ONE;
482 }
483 }
484 }
485
486 return internalSwizzle;
487 }
488 } // namespace rx
489