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