• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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