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