Autogenerate features

Features are now specified in a json file and autogenerated.  This is in
preparation for more autogeneration to support feature override in
tests.

This change doesn't yet fix the issues in anglebug.com/6435 and should
be a no-op.

Bug: angleproject:6435
Change-Id: Icdb63a94dc37b5fef0a356e0fc0b49937e083c8a
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/3579941
Reviewed-by: Yuly Novikov <ynovikov@chromium.org>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
diff --git a/include/platform/Feature.h b/include/platform/Feature.h
index 0c82fe5..7265713 100644
--- a/include/platform/Feature.h
+++ b/include/platform/Feature.h
@@ -25,41 +25,40 @@
 
 enum class FeatureCategory
 {
-    AppWorkarounds,
-    FrontendWorkarounds,
     FrontendFeatures,
+    FrontendWorkarounds,
     OpenGLWorkarounds,
     D3DWorkarounds,
-    D3DCompilerWorkarounds,
-    VulkanWorkarounds,
     VulkanFeatures,
+    VulkanWorkarounds,
+    VulkanAppWorkarounds,
     MetalFeatures,
     MetalWorkarounds,
 };
 
-constexpr char kFeatureCategoryFrontendWorkarounds[]    = "Frontend workarounds";
-constexpr char kFeatureCategoryFrontendFeatures[]       = "Frontend features";
-constexpr char kFeatureCategoryOpenGLWorkarounds[]      = "OpenGL workarounds";
-constexpr char kFeatureCategoryD3DWorkarounds[]         = "D3D workarounds";
-constexpr char kFeatureCategoryD3DCompilerWorkarounds[] = "D3D compiler workarounds";
-constexpr char kFeatureCategoryVulkanWorkarounds[]      = "Vulkan workarounds";
-constexpr char kFeatureCategoryVulkanFeatures[]         = "Vulkan features";
-constexpr char kFeatureCategoryMetalFeatures[]          = "Metal features";
-constexpr char kFeatureCategoryMetalWorkarounds[]       = "Metal workarounds";
-constexpr char kFeatureCategoryUnknown[]                = "Unknown";
+constexpr char kFeatureCategoryFrontendWorkarounds[]  = "Frontend workarounds";
+constexpr char kFeatureCategoryFrontendFeatures[]     = "Frontend features";
+constexpr char kFeatureCategoryOpenGLWorkarounds[]    = "OpenGL workarounds";
+constexpr char kFeatureCategoryD3DWorkarounds[]       = "D3D workarounds";
+constexpr char kFeatureCategoryVulkanAppWorkarounds[] = "Vulkan app workarounds";
+constexpr char kFeatureCategoryVulkanWorkarounds[]    = "Vulkan workarounds";
+constexpr char kFeatureCategoryVulkanFeatures[]       = "Vulkan features";
+constexpr char kFeatureCategoryMetalFeatures[]        = "Metal features";
+constexpr char kFeatureCategoryMetalWorkarounds[]     = "Metal workarounds";
+constexpr char kFeatureCategoryUnknown[]              = "Unknown";
 
 inline const char *FeatureCategoryToString(const FeatureCategory &fc)
 {
     switch (fc)
     {
-        case FeatureCategory::FrontendWorkarounds:
-            return kFeatureCategoryFrontendWorkarounds;
-            break;
-
         case FeatureCategory::FrontendFeatures:
             return kFeatureCategoryFrontendFeatures;
             break;
 
+        case FeatureCategory::FrontendWorkarounds:
+            return kFeatureCategoryFrontendWorkarounds;
+            break;
+
         case FeatureCategory::OpenGLWorkarounds:
             return kFeatureCategoryOpenGLWorkarounds;
             break;
@@ -68,16 +67,16 @@
             return kFeatureCategoryD3DWorkarounds;
             break;
 
-        case FeatureCategory::D3DCompilerWorkarounds:
-            return kFeatureCategoryD3DCompilerWorkarounds;
+        case FeatureCategory::VulkanFeatures:
+            return kFeatureCategoryVulkanFeatures;
             break;
 
         case FeatureCategory::VulkanWorkarounds:
             return kFeatureCategoryVulkanWorkarounds;
             break;
 
-        case FeatureCategory::VulkanFeatures:
-            return kFeatureCategoryVulkanFeatures;
+        case FeatureCategory::VulkanAppWorkarounds:
+            return kFeatureCategoryVulkanAppWorkarounds;
             break;
 
         case FeatureCategory::MetalFeatures:
diff --git a/include/platform/FeaturesD3D.h b/include/platform/FeaturesD3D.h
index fb0ae37..d20a824 100644
--- a/include/platform/FeaturesD3D.h
+++ b/include/platform/FeaturesD3D.h
@@ -1,9 +1,10 @@
+// GENERATED FILE - DO NOT EDIT.
+// Generated by gen_features.py using data from d3d_features.json.
 //
-// Copyright 2015 The ANGLE Project Authors. All rights reserved.
+// Copyright 2022 The ANGLE Project Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 //
-
 // FeaturesD3D.h: Features and workarounds for D3D driver bugs and other issues.
 
 #ifndef ANGLE_PLATFORM_FEATURESD3D_H_
@@ -14,209 +15,155 @@
 namespace angle
 {
 
-// Workarounds attached to each shader. Do not need to expose information about these workarounds so
-// a simple bool struct suffices.
-struct CompilerWorkaroundsD3D
-{
-    bool skipOptimization = false;
-
-    bool useMaxOptimization = false;
-
-    // IEEE strictness needs to be enabled for NANs to work.
-    bool enableIEEEStrictness = false;
-};
-
 struct FeaturesD3D : FeatureSetBase
 {
     FeaturesD3D();
     ~FeaturesD3D();
 
-    // On some systems, having extra rendertargets than necessary slows down the shader.
-    // We can fix this by optimizing those out of the shader. At the same time, we can
-    // work around a bug on some nVidia drivers that they ignore "null" render targets
-    // in D3D11, by compacting the active color attachments list to omit null entries.
-    Feature mrtPerfWorkaround = {"mrt_perf_workaround", FeatureCategory::D3DWorkarounds,
-                                 "Some drivers have a bug where they ignore null render targets",
-                                 &members};
+    Feature mrtPerfWorkaround = {
+        "mrt_perf_workaround",
+        FeatureCategory::D3DWorkarounds,
+        "Some drivers have a bug where they ignore null render targets",
+        &members,
+    };
 
-    Feature setDataFasterThanImageUpload = {"set_data_faster_than_image_upload",
-                                            FeatureCategory::D3DWorkarounds,
-                                            "Set data faster than image upload", &members};
+    Feature setDataFasterThanImageUpload = {
+        "set_data_faster_than_image_upload",
+        FeatureCategory::D3DWorkarounds,
+        "Set data faster than image upload",
+        &members,
+    };
 
-    // Some renderers can't disable mipmaps on a mipmapped texture (i.e. solely sample from level
-    // zero, and ignore the other levels). D3D11 Feature Level 10+ does this by setting MaxLOD to
-    // 0.0f in the Sampler state. D3D9 sets D3DSAMP_MIPFILTER to D3DTEXF_NONE. There is no
-    // equivalent to this in D3D11 Feature Level 9_3. This causes problems when (for example) an
-    // application creates a mipmapped texture2D, but sets GL_TEXTURE_MIN_FILTER to GL_NEAREST
-    // (i.e disables mipmaps). To work around this, D3D11 FL9_3 has to create two copies of the
-    // texture. The textures' level zeros are identical, but only one texture has mips.
-    Feature zeroMaxLodWorkaround = {"zero_max_lod", FeatureCategory::D3DWorkarounds,
-                                    "Missing an option to disable mipmaps on a mipmapped texture",
-                                    &members};
+    Feature zeroMaxLodWorkaround = {
+        "zero_max_lod",
+        FeatureCategory::D3DWorkarounds,
+        "Missing an option to disable mipmaps on a mipmapped texture",
+        &members,
+    };
 
-    // Some renderers do not support Geometry Shaders so the Geometry Shader-based PointSprite
-    // emulation will not work. To work around this, D3D11 FL9_3 has to use a different pointsprite
-    // emulation that is implemented using instanced quads.
     Feature useInstancedPointSpriteEmulation = {
-        "use_instanced_point_sprite_emulation", FeatureCategory::D3DWorkarounds,
-        "Some D3D11 renderers do not support geometry shaders for pointsprite emulation", &members};
+        "use_instanced_point_sprite_emulation",
+        FeatureCategory::D3DWorkarounds,
+        "Some D3D11 renderers do not support geometry shaders for pointsprite emulation",
+        &members,
+    };
 
-    // A bug fixed in NVIDIA driver version 347.88 < x <= 368.81 triggers a TDR when using
-    // CopySubresourceRegion from a staging texture to a depth/stencil in D3D11. The workaround
-    // is to use UpdateSubresource to trigger an extra copy. We disable this workaround on newer
-    // NVIDIA driver versions because of a second driver bug present with the workaround enabled.
-    // (See: http://anglebug.com/1452)
     Feature depthStencilBlitExtraCopy = {
         "depth_stencil_blit_extra_copy", FeatureCategory::D3DWorkarounds,
         "Bug in some drivers triggers a TDR when using CopySubresourceRegion from a staging "
         "texture to a depth/stencil",
         &members, "http://anglebug.com/1452"};
 
-    // The HLSL optimizer has a bug with optimizing "pow" in certain integer-valued expressions.
-    // We can work around this by expanding the pow into a series of multiplies if we're running
-    // under the affected compiler.
     Feature expandIntegerPowExpressions = {
-        "expand_integer_pow_expressions", FeatureCategory::D3DWorkarounds,
+        "expand_integer_pow_expressions",
+        FeatureCategory::D3DWorkarounds,
         "The HLSL optimizer has a bug with optimizing 'pow' in certain integer-valued expressions",
-        &members};
+        &members,
+    };
 
-    // NVIDIA drivers sometimes write out-of-order results to StreamOut buffers when transform
-    // feedback is used to repeatedly write to the same buffer positions.
     Feature flushAfterEndingTransformFeedback = {
-        "flush_after_ending_transform_feedback", FeatureCategory::D3DWorkarounds,
+        "flush_after_ending_transform_feedback",
+        FeatureCategory::D3DWorkarounds,
         "Some drivers sometimes write out-of-order results to StreamOut buffers when transform "
         "feedback is used to repeatedly write to the same buffer positions",
-        &members};
+        &members,
+    };
 
-    // Some drivers (NVIDIA) do not take into account the base level of the texture in the results
-    // of the HLSL GetDimensions builtin.
     Feature getDimensionsIgnoresBaseLevel = {
-        "get_dimensions_ignores_base_level", FeatureCategory::D3DWorkarounds,
+        "get_dimensions_ignores_base_level",
+        FeatureCategory::D3DWorkarounds,
         "Some drivers do not take into account the base level of the "
         "texture in the results of the HLSL GetDimensions builtin",
-        &members};
+        &members,
+    };
 
-    // On some Intel drivers, HLSL's function texture.Load returns 0 when the parameter Location
-    // is negative, even if the sum of Offset and Location is in range. This may cause errors when
-    // translating GLSL's function texelFetchOffset into texture.Load, as it is valid for
-    // texelFetchOffset to use negative texture coordinates as its parameter P when the sum of P
-    // and Offset is in range. To work around this, we translate texelFetchOffset into texelFetch
-    // by adding Offset directly to Location before reading the texture.
     Feature preAddTexelFetchOffsets = {
-        "pre_add_texel_fetch_offsets", FeatureCategory::D3DWorkarounds,
+        "pre_add_texel_fetch_offsets",
+        FeatureCategory::D3DWorkarounds,
         "HLSL's function texture.Load returns 0 when the parameter Location is negative, even if "
         "the sum of Offset and Location is in range",
-        &members};
+        &members,
+    };
 
-    // On some AMD drivers, 1x1 and 2x2 mips of depth/stencil textures aren't sampled correctly.
-    // We can work around this bug by doing an internal blit to a temporary single-channel texture
-    // before we sample.
     Feature emulateTinyStencilTextures = {
-        "emulate_tiny_stencil_textures", FeatureCategory::D3DWorkarounds,
-        "1x1 and 2x2 mips of depth/stencil textures aren't sampled correctly", &members};
+        "emulate_tiny_stencil_textures",
+        FeatureCategory::D3DWorkarounds,
+        "1x1 and 2x2 mips of depth/stencil textures aren't sampled correctly",
+        &members,
+    };
 
-    // In Intel driver, the data with format DXGI_FORMAT_B5G6R5_UNORM will be parsed incorrectly.
-    // This workaroud will disable B5G6R5 support when it's Intel driver. By default, it will use
-    // R8G8B8A8 format. This bug is fixed in version 4539 on Intel drivers.
-    // On older AMD drivers, the data in DXGI_FORMAT_B5G6R5_UNORM becomes corrupted for unknown
-    // reasons.
-    Feature disableB5G6R5Support = {"disable_b5g6r5_support", FeatureCategory::D3DWorkarounds,
-                                    "Textures with the format "
-                                    "DXGI_FORMAT_B5G6R5_UNORM have incorrect data",
-                                    &members};
+    Feature disableB5G6R5Support = {
+        "disable_b5g6r5_support",
+        FeatureCategory::D3DWorkarounds,
+        "Textures with the format "
+        "DXGI_FORMAT_B5G6R5_UNORM have incorrect data",
+        &members,
+    };
 
-    // On some Intel drivers, evaluating unary minus operator on integer may get wrong answer in
-    // vertex shaders. To work around this bug, we translate -(int) into ~(int)+1.
-    // This driver bug is fixed in 20.19.15.4624.
     Feature rewriteUnaryMinusOperator = {
-        "rewrite_unary_minus_operator", FeatureCategory::D3DWorkarounds,
+        "rewrite_unary_minus_operator",
+        FeatureCategory::D3DWorkarounds,
         "Evaluating unary minus operator on integer may get wrong answer in vertex shaders",
-        &members};
+        &members,
+    };
 
-    // On some Intel drivers, using isnan() on highp float will get wrong answer. To work around
-    // this bug, we use an expression to emulate function isnan().
-    // Tracking bug: https://crbug.com/650547
-    // This driver bug is fixed in 21.20.16.4542.
     Feature emulateIsnanFloat = {"emulate_isnan_float", FeatureCategory::D3DWorkarounds,
                                  "Using isnan() on highp float will get wrong answer", &members,
                                  "https://crbug.com/650547"};
 
-    // On some Intel drivers, using clear() may not take effect. To work around this bug, we call
-    // clear() twice on these platforms.
-    // Tracking bug: https://crbug.com/655534
     Feature callClearTwice = {"call_clear_twice", FeatureCategory::D3DWorkarounds,
                               "Using clear() may not take effect", &members,
                               "https://crbug.com/655534"};
 
-    // On some Intel drivers, copying from staging storage to constant buffer storage does not
-    // seem to work. Work around this by keeping system memory storage as a canonical reference
-    // for buffer data.
-    // D3D11-only workaround. See http://crbug.com/593024.
     Feature useSystemMemoryForConstantBuffers = {"use_system_memory_for_constant_buffers",
                                                  FeatureCategory::D3DWorkarounds,
                                                  "Copying from staging storage to constant buffer "
                                                  "storage does not work",
                                                  &members, "https://crbug.com/593024"};
 
-    // This workaround is for the ANGLE_multiview extension. If enabled the viewport or render
-    // target slice will be selected in the geometry shader stage. The workaround flag is added to
-    // make it possible to select the code path in end2end and performance tests.
     Feature selectViewInGeometryShader = {
-        "select_view_in_geometry_shader", FeatureCategory::D3DWorkarounds,
+        "select_view_in_geometry_shader",
+        FeatureCategory::D3DWorkarounds,
         "The viewport or render target slice will be selected in the geometry shader stage for "
         "the ANGLE_multiview extension",
-        &members};
+        &members,
+    };
 
-    // When rendering with no render target on D3D, two bugs lead to incorrect behavior on Intel
-    // drivers < 4815. The rendering samples always pass neglecting discard statements in pixel
-    // shader.
-    // 1. If rendertarget is not set, the pixel shader will be recompiled to drop 'SV_TARGET'.
-    // When using a pixel shader with no 'SV_TARGET' in a draw, the pixels are always generated even
-    // if they should be discard by 'discard' statements.
-    // 2. If ID3D11BlendState.RenderTarget[].RenderTargetWriteMask is 0 and rendertarget is not set,
-    // then rendering samples also pass neglecting discard statements in pixel shader.
-    // So we add a mock texture as render target in such case. See http://anglebug.com/2152
     Feature addMockTextureNoRenderTarget = {
         "add_mock_texture_no_render_target", FeatureCategory::D3DWorkarounds,
         "On some drivers when rendering with no render target, two bugs lead to incorrect behavior",
         &members, "http://anglebug.com/2152"};
 
-    // Don't use D3D constant register zero when allocating space for uniforms in the vertex shader.
-    // This is targeted to work around a bug in NVIDIA D3D driver version 388.59 where in very
-    // specific cases the driver would not handle constant register zero correctly.
     Feature skipVSConstantRegisterZero = {
-        "skip_vs_constant_register_zero", FeatureCategory::D3DWorkarounds,
-        "In specific cases the driver doesn't handle constant register zero correctly", &members};
+        "skip_vs_constant_register_zero",
+        FeatureCategory::D3DWorkarounds,
+        "In specific cases the driver doesn't handle constant register zero correctly",
+        &members,
+    };
 
-    // Forces the value returned from an atomic operations to be always be resolved. This is
-    // targeted to workaround a bug in NVIDIA D3D driver where the return value from
-    // RWByteAddressBuffer.InterlockedAdd does not get resolved when used in the .yzw components of
-    // a RWByteAddressBuffer.Store operation. Only has an effect on HLSL translation.
-    // http://anglebug.com/3246
     Feature forceAtomicValueResolution = {
         "force_atomic_value_resolution", FeatureCategory::D3DWorkarounds,
         "On some drivers the return value from RWByteAddressBuffer.InterlockedAdd does not resolve "
         "when used in the .yzw components of a RWByteAddressBuffer.Store operation",
         &members, "http://anglebug.com/3246"};
 
-    // Match chromium's robust resource init behaviour by always prefering to upload texture data
-    // instead of clearing. Clear calls have been observed to cause texture corruption for some
-    // formats.
     Feature allowClearForRobustResourceInit = {
         "allow_clear_for_robust_resource_init", FeatureCategory::D3DWorkarounds,
         "Some drivers corrupt texture data when clearing for robust resource initialization.",
         &members, "http://crbug.com/941620"};
 
-    // Allow translating uniform block to StructuredBuffer. This is targeted to work around a slow
-    // fxc compile performance issue with dynamic uniform indexing. http://anglebug.com/3682
     Feature allowTranslateUniformBlockToStructuredBuffer = {
         "allow_translate_uniform_block_to_structured_buffer", FeatureCategory::D3DWorkarounds,
         "There is a slow fxc compile performance issue with dynamic uniform indexing if "
         "translating a uniform block with a large array member to cbuffer.",
         &members, "http://anglebug.com/3682"};
 
-    Feature allowES3OnFL10_0 = {"allowES3OnFL10_0", FeatureCategory::D3DWorkarounds,
-                                "Allow ES3 on 10.0 devices", &members};
+    Feature allowES3OnFL10_0 = {
+        "allowES3OnFL10_0",
+        FeatureCategory::D3DWorkarounds,
+        "Allow ES3 on 10.0 devices",
+        &members,
+    };
 };
 
 inline FeaturesD3D::FeaturesD3D()  = default;
diff --git a/include/platform/FeaturesGL.h b/include/platform/FeaturesGL.h
index 2d11a65..fa1ec11 100644
--- a/include/platform/FeaturesGL.h
+++ b/include/platform/FeaturesGL.h
@@ -1,10 +1,11 @@
+// GENERATED FILE - DO NOT EDIT.
+// Generated by gen_features.py using data from gl_features.json.
 //
-// Copyright 2015 The ANGLE Project Authors. All rights reserved.
+// Copyright 2022 The ANGLE Project Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 //
-
-// FeaturesGL.h: Features and workarounds for GL driver bugs and other issues.
+// FeaturesGL.h: angle::Features and workarounds for GL driver bugs and other issues.
 
 #ifndef ANGLE_PLATFORM_FEATURESGL_H_
 #define ANGLE_PLATFORM_FEATURESGL_H_
@@ -19,328 +20,243 @@
     FeaturesGL();
     ~FeaturesGL();
 
-    // When writing a float to a normalized integer framebuffer, desktop OpenGL is allowed to write
-    // one of the two closest normalized integer representations (although round to nearest is
-    // preferred) (see section 2.3.5.2 of the GL 4.5 core specification). OpenGL ES requires that
-    // round-to-nearest is used (see "Conversion from Floating-Point to Framebuffer Fixed-Point" in
-    // section 2.1.2 of the OpenGL ES 2.0.25 spec).  This issue only shows up on AMD drivers on
-    // framebuffer formats that have 1-bit alpha, work around this by using higher precision formats
-    // instead.
-    Feature avoid1BitAlphaTextureFormats = {"avoid_1_bit_alpha_texture_formats",
-                                            FeatureCategory::OpenGLWorkarounds,
-                                            "Issue with 1-bit alpha framebuffer formats", &members};
+    Feature avoid1BitAlphaTextureFormats = {
+        "avoid_1_bit_alpha_texture_formats",
+        FeatureCategory::OpenGLWorkarounds,
+        "Issue with 1-bit alpha framebuffer formats",
+        &members,
+    };
 
-    // On some older Intel drivers, GL_RGBA4 is not color renderable, glCheckFramebufferStatus
-    // returns GL_FRAMEBUFFER_UNSUPPORTED. Work around this by using a known color-renderable
-    // format.
-    Feature rgba4IsNotSupportedForColorRendering = {"rgba4_is_not_supported_for_color_rendering",
-                                                    FeatureCategory::OpenGLWorkarounds,
-                                                    "GL_RGBA4 is not color renderable", &members};
+    Feature rgba4IsNotSupportedForColorRendering = {
+        "rgba4_is_not_supported_for_color_rendering",
+        FeatureCategory::OpenGLWorkarounds,
+        "GL_RGBA4 is not color renderable",
+        &members,
+    };
 
-    // Newer Intel GPUs natively support ETC2/EAC compressed texture formats.
-    Feature allowEtcFormats = {"allow_etc_formats", FeatureCategory::OpenGLWorkarounds,
-                               "Enable ETC2/EAC on desktop OpenGL", &members};
+    Feature allowEtcFormats = {
+        "allow_etc_formats",
+        FeatureCategory::OpenGLWorkarounds,
+        "Enable ETC2/EAC on desktop OpenGL",
+        &members,
+    };
 
-    // When clearing a framebuffer on Intel or AMD drivers, when GL_FRAMEBUFFER_SRGB is enabled, the
-    // driver clears to the linearized clear color despite the framebuffer not supporting SRGB
-    // blending.  It only seems to do this when the framebuffer has only linear attachments, mixed
-    // attachments appear to get the correct clear color.
     Feature doesSRGBClearsOnLinearFramebufferAttachments = {
-        "does_srgb_clears_on_linear_framebuffer_attachments", FeatureCategory::OpenGLWorkarounds,
+        "does_srgb_clears_on_linear_framebuffer_attachments",
+        FeatureCategory::OpenGLWorkarounds,
         "Issue clearing framebuffers with linear attachments when GL_FRAMEBUFFER_SRGB is enabled",
-        &members};
+        &members,
+    };
 
-    // On Mac some GLSL constructs involving do-while loops cause GPU hangs, such as the following:
-    //  int i = 1;
-    //  do {
-    //      i --;
-    //      continue;
-    //  } while (i > 0)
-    // Work around this by rewriting the do-while to use another GLSL construct (block + while)
     Feature doWhileGLSLCausesGPUHang = {
         "do_while_glsl_causes_gpu_hang", FeatureCategory::OpenGLWorkarounds,
         "Some GLSL constructs involving do-while loops cause GPU hangs", &members,
         "http://crbug.com/644669"};
 
-    // On Mac AMD GPU gl_VertexID in GLSL vertex shader doesn't include base vertex value,
-    // Work aronud this by replace gl_VertexID with (gl_VertexID - angle_BaseVertex) when
-    // angle_BaseVertex is present.
     Feature addBaseVertexToVertexID = {
-        "vertex_id_does_not_include_base_vertex", FeatureCategory::OpenGLWorkarounds,
-        "gl_VertexID in GLSL vertex shader doesn't include base vertex value", &members};
+        "vertex_id_does_not_include_base_vertex",
+        FeatureCategory::OpenGLWorkarounds,
+        "gl_VertexID in GLSL vertex shader doesn't include base vertex value",
+        &members,
+    };
 
-    // Calling glFinish doesn't cause all queries to report that the result is available on some
-    // (NVIDIA) drivers.  It was found that enabling GL_DEBUG_OUTPUT_SYNCHRONOUS before the finish
-    // causes it to fully finish.
     Feature finishDoesNotCauseQueriesToBeAvailable = {
-        "finish_does_not_cause_queries_to_be_available", FeatureCategory::OpenGLWorkarounds,
-        "glFinish doesn't cause all queries to report available result", &members};
+        "finish_does_not_cause_queries_to_be_available",
+        FeatureCategory::OpenGLWorkarounds,
+        "glFinish doesn't cause all queries to report available result",
+        &members,
+    };
 
-    // Always call useProgram after a successful link to avoid a driver bug.
-    // This workaround is meant to reproduce the use_current_program_after_successful_link
-    // workaround in Chromium (http://crbug.com/110263). It has been shown that this workaround is
-    // not necessary for MacOSX 10.9 and higher (http://crrev.com/39eb535b).
     Feature alwaysCallUseProgramAfterLink = {
         "always_call_use_program_after_link", FeatureCategory::OpenGLWorkarounds,
         "Always call useProgram after a successful link to avoid a driver bug", &members,
         "http://crbug.com/110263"};
 
-    // On NVIDIA, in the case of unpacking from a pixel unpack buffer, unpack overlapping rows row
-    // by row.
     Feature unpackOverlappingRowsSeparatelyUnpackBuffer = {
-        "unpack_overlapping_rows_separately_unpack_buffer", FeatureCategory::OpenGLWorkarounds,
+        "unpack_overlapping_rows_separately_unpack_buffer",
+        FeatureCategory::OpenGLWorkarounds,
         "In the case of unpacking from a pixel unpack buffer, unpack overlapping rows row by row",
-        &members};
+        &members,
+    };
 
-    // On NVIDIA, in the case of packing to a pixel pack buffer, pack overlapping rows row by row.
     Feature packOverlappingRowsSeparatelyPackBuffer = {
-        "pack_overlapping_rows_separately_pack_buffer", FeatureCategory::OpenGLWorkarounds,
+        "pack_overlapping_rows_separately_pack_buffer",
+        FeatureCategory::OpenGLWorkarounds,
         "In the case of packing to a pixel pack buffer, pack overlapping rows row by row",
-        &members};
+        &members,
+    };
 
-    // On NVIDIA, during initialization, assign the current vertex attributes to the spec-mandated
-    // defaults.
     Feature initializeCurrentVertexAttributes = {
-        "initialize_current_vertex_attributes", FeatureCategory::OpenGLWorkarounds,
+        "initialize_current_vertex_attributes",
+        FeatureCategory::OpenGLWorkarounds,
         "During initialization, assign the current vertex attributes to the spec-mandated defaults",
-        &members};
+        &members,
+    };
 
-    // abs(i) where i is an integer returns unexpected result on Intel Mac.
-    // Emulate abs(i) with i * sign(i).
     Feature emulateAbsIntFunction = {"emulate_abs_int_function", FeatureCategory::OpenGLWorkarounds,
                                      "abs(i) where i is an integer returns unexpected result",
                                      &members, "http://crbug.com/642227"};
 
-    // On Intel Mac, calculation of loop conditions in for and while loop has bug.
-    // Add "&& true" to the end of the condition expression to work around the bug.
     Feature addAndTrueToLoopCondition = {
-        "add_and_true_to_loop_condition", FeatureCategory::OpenGLWorkarounds,
-        "Calculation of loop conditions in for and while loop has bug", &members};
+        "add_and_true_to_loop_condition",
+        FeatureCategory::OpenGLWorkarounds,
+        "Calculation of loop conditions in for and while loop has bug",
+        &members,
+    };
 
-    // When uploading textures from an unpack buffer, some drivers count an extra row padding when
-    // checking if the pixel unpack buffer is big enough. Tracking bug: http://anglebug.com/1512
-    // For example considering the pixel buffer below where in memory, each row data (D) of the
-    // texture is followed by some unused data (the dots):
-    //     +-------+--+
-    //     |DDDDDDD|..|
-    //     |DDDDDDD|..|
-    //     |DDDDDDD|..|
-    //     |DDDDDDD|..|
-    //     +-------A--B
-    // The last pixel read will be A, but the driver will think it is B, causing it to generate an
-    // error when the pixel buffer is just big enough.
     Feature unpackLastRowSeparatelyForPaddingInclusion = {
         "unpack_last_row_separately_for_padding_inclusion", FeatureCategory::OpenGLWorkarounds,
         "When uploading textures from an unpack buffer, some drivers count an extra row padding",
         &members, "http://anglebug.com/1512"};
 
-    // Equivalent workaround when uploading data from a pixel pack buffer.
     Feature packLastRowSeparatelyForPaddingInclusion = {
         "pack_last_row_separately_for_padding_inclusion", FeatureCategory::OpenGLWorkarounds,
         "When uploading textures from an pack buffer, some drivers count an extra row padding",
         &members, "http://anglebug.com/1512"};
 
-    // On some Intel drivers, using isnan() on highp float will get wrong answer. To work around
-    // this bug, we use an expression to emulate function isnan().
-    // Tracking bug: http://crbug.com/650547
     Feature emulateIsnanFloat = {"emulate_isnan_float", FeatureCategory::OpenGLWorkarounds,
                                  "Using isnan() on highp float will get wrong answer", &members,
                                  "http://crbug.com/650547"};
 
-    // On Mac with OpenGL version 4.1, unused std140 or shared uniform blocks will be
-    // treated as inactive which is not consistent with WebGL2.0 spec. Reference all members in a
-    // unused std140 or shared uniform block at the beginning of main to work around it.
-    // Also used on Linux AMD.
     Feature useUnusedBlocksWithStandardOrSharedLayout = {
-        "use_unused_blocks_with_standard_or_shared_layout", FeatureCategory::OpenGLWorkarounds,
-        "Unused std140 or shared uniform blocks will be treated as inactive", &members};
+        "use_unused_blocks_with_standard_or_shared_layout",
+        FeatureCategory::OpenGLWorkarounds,
+        "Unused std140 or shared uniform blocks will be treated as inactive",
+        &members,
+    };
 
-    // This flag is used to fix spec difference between GLSL 4.1 or lower and ESSL3.
     Feature removeInvariantAndCentroidForESSL3 = {
-        "remove_invarient_and_centroid_for_essl3", FeatureCategory::OpenGLWorkarounds,
-        "Fix spec difference between GLSL 4.1 or lower and ESSL3", &members};
+        "remove_invarient_and_centroid_for_essl3",
+        FeatureCategory::OpenGLWorkarounds,
+        "Fix spec difference between GLSL 4.1 or lower and ESSL3",
+        &members,
+    };
 
-    // On Intel Mac OSX 10.11 driver, using "-float" will get wrong answer. Use "0.0 - float" to
-    // replace "-float".
-    // Tracking bug: http://crbug.com/308366
     Feature rewriteFloatUnaryMinusOperator = {
         "rewrite_float_unary_minus_operator", FeatureCategory::OpenGLWorkarounds,
         "Using '-<float>' will get wrong answer", &members, "http://crbug.com/308366"};
 
-    // On NVIDIA drivers, atan(y, x) may return a wrong answer.
-    // Tracking bug: http://crbug.com/672380
     Feature emulateAtan2Float = {"emulate_atan_2_float", FeatureCategory::OpenGLWorkarounds,
                                  "atan(y, x) may return a wrong answer", &members,
                                  "http://crbug.com/672380"};
 
-    // Some drivers seem to forget about UBO bindings when using program binaries. Work around
-    // this by re-applying the bindings after the program binary is loaded or saved.
-    // This only seems to affect AMD OpenGL drivers, and some Android devices.
-    // http://anglebug.com/1637
     Feature reapplyUBOBindingsAfterUsingBinaryProgram = {
         "reapply_ubo_bindings_after_using_binary_program", FeatureCategory::OpenGLWorkarounds,
         "Some drivers forget about UBO bindings when using program binaries", &members,
         "http://anglebug.com/1637"};
 
-    // Some Linux OpenGL drivers return 0 when we query MAX_VERTEX_ATTRIB_STRIDE in an OpenGL 4.4 or
-    // higher context.
-    // This only seems to affect AMD OpenGL drivers.
-    // Tracking bug: http://anglebug.com/1936
     Feature emulateMaxVertexAttribStride = {
         "emulate_max_vertex_attrib_stride", FeatureCategory::OpenGLWorkarounds,
         "Some drivers return 0 when MAX_VERTEX_ATTRIB_STRIED queried", &members,
         "http://anglebug.com/1936"};
 
-    // Initializing uninitialized locals caused odd behavior on Android Qualcomm in a few WebGL 2
-    // tests. Tracking bug: http://anglebug.com/2046
     Feature dontInitializeUninitializedLocals = {
         "dont_initialize_uninitialized_locals", FeatureCategory::OpenGLWorkarounds,
         "Initializing uninitialized locals caused odd behavior in a few WebGL 2 tests", &members,
         "http://anglebug.com/2046"};
 
-    // On some NVIDIA drivers the point size range reported from the API is inconsistent with the
-    // actual behavior. Clamp the point size to the value from the API to fix this.
     Feature clampPointSize = {
-        "clamp_point_size", FeatureCategory::OpenGLWorkarounds,
+        "clamp_point_size",
+        FeatureCategory::OpenGLWorkarounds,
         "The point size range reported from the API is inconsistent with the actual behavior",
-        &members};
+        &members,
+    };
 
-    // On some Android devices for loops used to initialize variables hit native GLSL compiler bugs.
     Feature dontUseLoopsToInitializeVariables = {
         "dont_use_loops_to_initialize_variables", FeatureCategory::OpenGLWorkarounds,
         "For loops used to initialize variables hit native GLSL compiler bugs", &members,
         "http://crbug.com/809422"};
 
-    // On some NVIDIA drivers gl_FragDepth is not clamped correctly when rendering to a floating
-    // point depth buffer. Clamp it in the translated shader to fix this.
     Feature clampFragDepth = {
-        "clamp_frag_depth", FeatureCategory::OpenGLWorkarounds,
+        "clamp_frag_depth",
+        FeatureCategory::OpenGLWorkarounds,
         "gl_FragDepth is not clamped correctly when rendering to a floating point depth buffer",
-        &members};
+        &members,
+    };
 
-    // On some NVIDIA drivers before version 397.31 repeated assignment to swizzled values inside a
-    // GLSL user-defined function have incorrect results. Rewrite this type of statements to fix
-    // this.
-    Feature rewriteRepeatedAssignToSwizzled = {"rewrite_repeated_assign_to_swizzled",
-                                               FeatureCategory::OpenGLWorkarounds,
-                                               "Repeated assignment to swizzled values inside a "
-                                               "GLSL user-defined function have incorrect results",
-                                               &members};
+    Feature rewriteRepeatedAssignToSwizzled = {
+        "rewrite_repeated_assign_to_swizzled",
+        FeatureCategory::OpenGLWorkarounds,
+        "Repeated assignment to swizzled values inside a "
+        "GLSL user-defined function have incorrect results",
+        &members,
+    };
 
-    // On some AMD and Intel GL drivers ARB_blend_func_extended does not pass the tests.
-    // It might be possible to work around the Intel bug by rewriting *FragData to *FragColor
-    // instead of disabling the functionality entirely. The AMD bug looked like incorrect blending,
-    // not sure if a workaround is feasible. http://anglebug.com/1085
     Feature disableBlendFuncExtended = {
         "disable_blend_func_extended", FeatureCategory::OpenGLWorkarounds,
         "ARB_blend_func_extended does not pass the tests", &members, "http://anglebug.com/1085"};
 
-    // Qualcomm drivers returns raw sRGB values instead of linearized values when calling
-    // glReadPixels on unsized sRGB texture formats. http://crbug.com/550292 and
-    // http://crbug.com/565179
     Feature unsizedsRGBReadPixelsDoesntTransform = {
         "unsized_srgb_read_pixels_doesnt_transform", FeatureCategory::OpenGLWorkarounds,
         "Drivers returning raw sRGB values instead of linearized values when calling glReadPixels "
         "on unsized sRGB texture formats",
-        &members, "http://crbug.com/565179"};
+        &members, "http://crbug.com/550292 http://crbug.com/565179"};
 
-    // Older Qualcomm drivers generate errors when querying the number of bits in timer queries, ex:
-    // GetQueryivEXT(GL_TIME_ELAPSED, GL_QUERY_COUNTER_BITS).  http://anglebug.com/3027
     Feature queryCounterBitsGeneratesErrors = {
         "query_counter_bits_generates_errors", FeatureCategory::OpenGLWorkarounds,
         "Drivers generate errors when querying the number of bits in timer queries", &members,
         "http://anglebug.com/3027"};
 
-    // Re-linking a program in parallel is buggy on some Intel Windows OpenGL drivers and Android
-    // platforms.
-    // http://anglebug.com/3045
     Feature dontRelinkProgramsInParallel = {
         "dont_relink_programs_in_parallel", FeatureCategory::OpenGLWorkarounds,
         "Relinking a program in parallel is buggy", &members, "http://anglebug.com/3045"};
 
-    // Some tests have been seen to fail using worker contexts, this switch allows worker contexts
-    // to be disabled for some platforms. http://crbug.com/849576
     Feature disableWorkerContexts = {"disable_worker_contexts", FeatureCategory::OpenGLWorkarounds,
                                      "Some tests have been seen to fail using worker contexts",
                                      &members, "http://crbug.com/849576"};
 
-    // Most Android devices fail to allocate a texture that is larger than 4096. Limit the caps
-    // instead of generating GL_OUT_OF_MEMORY errors. Also causes system to hang on some older
-    // intel mesa drivers on Linux.
     Feature limitMaxTextureSizeTo4096 = {"max_texture_size_limit_4096",
                                          FeatureCategory::OpenGLWorkarounds,
                                          "Limit max texture size to 4096 to avoid frequent "
                                          "out-of-memory errors",
                                          &members, "http://crbug.com/927470"};
 
-    // Prevent excessive MSAA allocations on Android devices, various rendering bugs have been
-    // observed and they tend to be high DPI anyways. http://crbug.com/797243
     Feature limitMaxMSAASamplesTo4 = {
         "max_msaa_sample_count_4", FeatureCategory::OpenGLWorkarounds,
         "Various rendering bugs have been observed when using higher MSAA counts", &members,
         "http://crbug.com/797243"};
 
-    // Prefer to do the robust resource init clear using a glClear. Calls to TexSubImage2D on large
-    // textures can take hundreds of milliseconds because of slow uploads on macOS. Do this only on
-    // macOS because clears are buggy on other drivers.
-    // https://crbug.com/848952 (slow uploads on macOS)
-    // https://crbug.com/883276 (buggy clears on Android)
     Feature allowClearForRobustResourceInit = {
         "allow_clear_for_robust_resource_init", FeatureCategory::OpenGLWorkarounds,
         "Using glClear for robust resource initialization is buggy on some drivers and leads to "
         "texture corruption. Default to data uploads except on MacOS where it is very slow.",
-        &members, "http://crbug.com/883276"};
+        &members, "https://crbug.com/848952 http://crbug.com/883276"};
 
-    // Some drivers automatically handle out-of-bounds uniform array access but others need manual
-    // clamping to satisfy the WebGL requirements.
     Feature clampArrayAccess = {"clamp_array_access", FeatureCategory::OpenGLWorkarounds,
                                 "Clamp uniform array access to avoid reading invalid memory.",
                                 &members, "http://anglebug.com/2978"};
 
-    // Reset glTexImage2D base level to workaround pixel comparison failure above Mac OS 10.12.4 on
-    // Intel Mac.
     Feature resetTexImage2DBaseLevel = {"reset_teximage2d_base_level",
                                         FeatureCategory::OpenGLWorkarounds,
                                         "Reset texture base level before calling glTexImage2D to "
                                         "work around pixel comparison failure.",
                                         &members, "https://crbug.com/705865"};
 
-    // glClearColor does not always work on Intel 6xxx Mac drivers when the clear color made up of
-    // all zeros and ones.
     Feature clearToZeroOrOneBroken = {
         "clear_to_zero_or_one_broken", FeatureCategory::OpenGLWorkarounds,
         "Clears when the clear color is all zeros or ones do not work.", &members,
         "https://crbug.com/710443"};
 
-    // Some older Linux Intel mesa drivers will hang the system when allocating large textures. Fix
-    // this by capping the max texture size.
     Feature limitMax3dArrayTextureSizeTo1024 = {
         "max_3d_array_texture_size_1024", FeatureCategory::OpenGLWorkarounds,
         "Limit max 3d texture size and max array texture layers to 1024 to avoid system hang",
         &members, "http://crbug.com/927470"};
 
-    // BlitFramebuffer has issues on some platforms with large source/dest texture sizes. This
-    // workaround adjusts the destination rectangle source and dest rectangle to fit within maximum
-    // twice the size of the framebuffer.
     Feature adjustSrcDstRegionBlitFramebuffer = {
         "adjust_src_dst_region_for_blitframebuffer", FeatureCategory::OpenGLWorkarounds,
         "Many platforms have issues with blitFramebuffer when the parameters are large.", &members,
         "http://crbug.com/830046"};
 
-    // BlitFramebuffer has issues on Mac when the source bounds aren't enclosed by the framebuffer.
-    // This workaround clips the source region and adjust the dest region proportionally.
     Feature clipSrcRegionBlitFramebuffer = {
         "clip_src_region_for_blitframebuffer", FeatureCategory::OpenGLWorkarounds,
         "Issues with blitFramebuffer when the parameters don't match the framebuffer size.",
         &members, "http://crbug.com/830046"};
 
-    // Mac Intel samples transparent black from GL_COMPRESSED_RGB_S3TC_DXT1_EXT
     Feature rgbDXT1TexturesSampleZeroAlpha = {
         "rgb_dxt1_textures_sample_zero_alpha", FeatureCategory::OpenGLWorkarounds,
         "Sampling BLACK texels from RGB DXT1 textures returns transparent black on Mac.", &members,
         "http://anglebug.com/3729"};
 
-    // Mac incorrectly executes both sides of && and || expressions when they should short-circuit.
     Feature unfoldShortCircuits = {
         "unfold_short_circuits", FeatureCategory::OpenGLWorkarounds,
         "Mac incorrectly executes both sides of && and || expressions when they should "
@@ -359,13 +275,11 @@
         "index is within the number of primitives being drawn.",
         &members, "http://anglebug.com/3997"};
 
-    // Dynamic indexing of swizzled l-values doesn't work correctly on various platforms.
     Feature removeDynamicIndexingOfSwizzledVector = {
         "remove_dynamic_indexing_of_swizzled_vector", FeatureCategory::OpenGLWorkarounds,
         "Dynamic indexing of swizzled l-values doesn't work correctly on various platforms.",
         &members, "http://crbug.com/709351"};
 
-    // Intel Mac drivers does not treat texelFetchOffset() correctly.
     Feature preAddTexelFetchOffsets = {
         "pre_add_texel_fetch_offsets", FeatureCategory::OpenGLWorkarounds,
         "Intel Mac drivers mistakenly consider the parameter position of nagative vaule as invalid "
@@ -374,57 +288,44 @@
         "position + offset, lod).",
         &members, "http://crbug.com/642605"};
 
-    // All Mac drivers do not handle struct scopes correctly. This workaround overwrites a struct
-    // name with a unique prefix
     Feature regenerateStructNames = {
         "regenerate_struct_names", FeatureCategory::OpenGLWorkarounds,
         "All Mac drivers do not handle struct scopes correctly. This workaround overwrites a struct"
         "name with a unique prefix.",
         &members, "http://crbug.com/403957"};
 
-    // Quite some OpenGL ES drivers don't implement readPixels for RGBA/UNSIGNED_SHORT from
-    // EXT_texture_norm16 correctly
     Feature readPixelsUsingImplementationColorReadFormatForNorm16 = {
         "read_pixels_using_implementation_color_read_format", FeatureCategory::OpenGLWorkarounds,
         "Quite some OpenGL ES drivers don't implement readPixels for RGBA/UNSIGNED_SHORT from "
         "EXT_texture_norm16 correctly",
         &members, "http://anglebug.com/4214"};
 
-    // Bugs exist in some Intel drivers where dependencies are incorrectly
-    // tracked for textures which are copy destinations (via CopyTexImage2D, for
-    // example). Flush before DeleteTexture if these entry points have been
-    // called recently.
     Feature flushBeforeDeleteTextureIfCopiedTo = {
         "flush_before_delete_texture_if_copied_to", FeatureCategory::OpenGLWorkarounds,
         "Some drivers track CopyTex{Sub}Image texture dependencies incorrectly. Flush"
         " before glDeleteTextures in this case",
         &members, "http://anglebug.com/4267"};
 
-    // Rewrite row-major matrices as column-major as a driver bug workaround if
-    // necessary.
     Feature rewriteRowMajorMatrices = {
         "rewrite_row_major_matrices", FeatureCategory::OpenGLWorkarounds,
         "Rewrite row major matrices in shaders as column major as a driver bug workaround",
         &members, "http://anglebug.com/2273"};
 
-    // Bugs exist in OpenGL AMD drivers on Windows that produce incorrect pipeline state for
-    // colorMaski calls.
-    Feature disableDrawBuffersIndexed = {"disable_draw_buffers_indexed",
-                                         FeatureCategory::OpenGLWorkarounds,
-                                         "Disable OES_draw_buffers_indexed extension.", &members};
+    Feature disableDrawBuffersIndexed = {
+        "disable_draw_buffers_indexed",
+        FeatureCategory::OpenGLWorkarounds,
+        "Disable OES_draw_buffers_indexed extension.",
+        &members,
+    };
 
-    // GL_EXT_semaphore_fd doesn't work properly with Mesa 19.3.4 and earlier versions.
     Feature disableSemaphoreFd = {"disable_semaphore_fd", FeatureCategory::OpenGLWorkarounds,
                                   "Disable GL_EXT_semaphore_fd extension", &members,
                                   "https://crbug.com/1046462"};
 
-    // GL_EXT_disjoint_timer_query doesn't work properly with Linux VMWare drivers.
     Feature disableTimestampQueries = {
         "disable_timestamp_queries", FeatureCategory::OpenGLWorkarounds,
         "Disable GL_EXT_disjoint_timer_query extension", &members, "https://crbug.com/811661"};
 
-    // Some drivers use linear blending when generating mipmaps for sRGB textures. Work around this
-    // by generating mipmaps in a linear texture and copying back to sRGB.
     Feature encodeAndDecodeSRGBForGenerateMipmap = {
         "decode_encode_srgb_for_generatemipmap", FeatureCategory::OpenGLWorkarounds,
         "Decode and encode before generateMipmap for srgb format textures.", &members,
@@ -440,7 +341,6 @@
         "Disable GPU switching support (use only the low-power GPU) on older MacBook Pros.",
         &members, "https://crbug.com/1091824"};
 
-    // KHR_parallel_shader_compile fails TSAN on Linux, so we avoid using it with this workaround.
     Feature disableNativeParallelCompile = {
         "disable_native_parallel_compile", FeatureCategory::OpenGLWorkarounds,
         "Do not use native KHR_parallel_shader_compile even when available.", &members,
@@ -451,14 +351,11 @@
         "GL_PACK_SKIP_ROWS and GL_PACK_SKIP_PIXELS are ignored in Apple's OpenGL driver.", &members,
         "https://anglebug.com/4849"};
 
-    // Some drivers return bogus/1hz values for GetMscRate, which we may want to clamp
     Feature clampMscRate = {
         "clamp_msc_rate", FeatureCategory::OpenGLWorkarounds,
         "Some drivers return bogus values for GetMscRate, so we clamp it to 30Hz", &members,
         "https://crbug.com/1042393"};
 
-    // Mac drivers generate GL_INVALID_VALUE when binding a transform feedback buffer with
-    // glBindBufferRange before first binding it to some generic binding point.
     Feature bindTransformFeedbackBufferBeforeBindBufferRange = {
         "bind_transform_feedback_buffer_before_bind_buffer_range",
         FeatureCategory::OpenGLWorkarounds,
@@ -466,62 +363,50 @@
         "glBindBufferBase or glBindBufferRange.",
         &members, "https://anglebug.com/5140"};
 
-    // Speculative fix for issues on Linux/Wayland where exposing GLX_OML_sync_control renders
-    // Chrome unusable
     Feature disableSyncControlSupport = {
         "disable_sync_control_support", FeatureCategory::OpenGLWorkarounds,
         "Speculative fix for issues on Linux/Wayland where exposing GLX_OML_sync_control renders "
         "Chrome unusable",
         &members, "https://crbug.com/1137851"};
 
-    // Buffers need to maintain a shadow copy of data when buffer data readback is not possible
-    // through the GL API
     Feature keepBufferShadowCopy = {
-        "keep_buffer_shadow_copy", FeatureCategory::OpenGLWorkarounds,
+        "keep_buffer_shadow_copy",
+        FeatureCategory::OpenGLWorkarounds,
         "Maintain a shadow copy of buffer data when the GL API does not permit reading data back.",
-        &members};
+        &members,
+    };
 
-    // glGenerateMipmap fails if the zero texture level is not set on some Mac drivers
     Feature setZeroLevelBeforeGenerateMipmap = {
-        "set_zero_level_before_generating_mipmap", FeatureCategory::OpenGLWorkarounds,
+        "set_zero_level_before_generating_mipmap",
+        FeatureCategory::OpenGLWorkarounds,
         "glGenerateMipmap fails if the zero texture level is not set on some Mac drivers.",
-        &members};
+        &members,
+    };
 
-    // On macOS with AMD GPUs, packed color formats like RGB565 and RGBA4444 are buggy. Promote them
-    // to 8 bit per channel formats.
     Feature promotePackedFormatsTo8BitPerChannel = {
         "promote_packed_formats_to_8_bit_per_channel", FeatureCategory::OpenGLWorkarounds,
         "Packed color formats are buggy on Macs with AMD GPUs", &members,
         "http://anglebug.com/5469"};
 
-    // If gl_FragColor is not written by fragment shader, it may cause context lost with Adreno 42x
-    // and 3xx.
     Feature initFragmentOutputVariables = {
         "init_fragment_output_variables", FeatureCategory::OpenGLWorkarounds,
         "No init gl_FragColor causes context lost", &members, "http://crbug.com/1171371"};
 
-    // On macOS with Intel GPUs, instanced array with divisor > 0 is buggy when first > 0 in
-    // drawArraysInstanced. Shift the attributes with extra offset to workaround.
     Feature shiftInstancedArrayDataWithExtraOffset = {
         "shift_instanced_array_data_with_offset", FeatureCategory::OpenGLWorkarounds,
         "glDrawArraysInstanced is buggy on certain new Mac Intel GPUs", &members,
         "http://crbug.com/1144207"};
 
-    // ANGLE needs to support devices that have no native VAOs. Sync everything to the default VAO.
     Feature syncVertexArraysToDefault = {
         "sync_vertex_arrays_to_default", FeatureCategory::OpenGLWorkarounds,
         "Only use the default VAO because of missing support or driver bugs", &members,
         "http://anglebug.com/5577"};
 
-    // On desktop Linux/AMD when using the amdgpu drivers, the precise kernel and DRM version are
-    // leaked via GL_RENDERER. We workaround this to improve user privacy.
     Feature sanitizeAmdGpuRendererString = {
         "sanitize_amdgpu_renderer_string", FeatureCategory::OpenGLWorkarounds,
         "Strip precise kernel and DRM version information from amdgpu renderer strings.", &members,
         "http://crbug.com/1181193"};
 
-    // Imagination GL drivers are buggy with context switching. We need to ubind fbo to workaround a
-    // crash in the driver.
     Feature unbindFBOOnContextSwitch = {"unbind_fbo_before_switching_context",
                                         FeatureCategory::OpenGLWorkarounds,
                                         "Imagination GL drivers are buggy with context switching.",
@@ -538,32 +423,20 @@
         "Many drivers have bugs when using GL_EXT_multisampled_render_to_texture", &members,
         "http://anglebug.com/2894"};
 
-    // Mac OpenGL drivers often hang when calling glTexSubImage with >120kb of data. Instead, upload
-    // the data in <120kb chunks.
-    static constexpr const size_t kUploadTextureDataInChunksUploadSize = (120 * 1024) - 1;
-    Feature uploadTextureDataInChunks                                  = {
+    Feature uploadTextureDataInChunks = {
         "chunked_texture_upload", FeatureCategory::OpenGLWorkarounds,
         "Upload texture data in <120kb chunks to work around Mac driver hangs and crashes.",
         &members, "http://crbug.com/1181068"};
 
-    // Qualcomm drivers may sometimes reject immutable ASTC sliced 3D texture
-    // allocation. Instead, use non-immutable allocation internally.
     Feature emulateImmutableCompressedTexture3D = {
         "emulate_immutable_compressed_texture_3d", FeatureCategory::OpenGLWorkarounds,
         "Use non-immutable texture allocation to work around a driver bug.", &members,
         "https://crbug.com/1060012"};
 
-    // Desktop GL does not support RGB10 (without alpha) but it is required for
-    // GL_EXT_texture_type_2_10_10_10_REV. Emulate it by setting a sampler parameter to always
-    // sample 1 from alpha.
     Feature emulateRGB10 = {"emulate_rgb10", FeatureCategory::OpenGLWorkarounds,
                             "Emulate RGB10 support using RGB10_A2.", &members,
                             "https://crbug.com/1300575"};
 
-    // On NVIDIA, binding a texture level > 0 to a framebuffer color attachment and then
-    // binding a renderbuffer to the same attachment causes the driver to report that the FBO
-    // has an incomplete attachment. This workaround forces a call to framebufferTexture2D with
-    // texture_id and level set to 0 before binding a renderbuffer to bypass the issue.
     Feature alwaysUnbindFramebufferTexture2D = {
         "always_unbind_framebuffer_texture_2d", FeatureCategory::OpenGLWorkarounds,
         "Force unbind framebufferTexture2D before binding renderbuffer to work around driver bug.",
diff --git a/include/platform/FeaturesMtl.h b/include/platform/FeaturesMtl.h
index a85ce9b..f08fd8a 100644
--- a/include/platform/FeaturesMtl.h
+++ b/include/platform/FeaturesMtl.h
@@ -1,10 +1,11 @@
+// GENERATED FILE - DO NOT EDIT.
+// Generated by gen_features.py using data from mtl_features.json.
 //
-// Copyright 2019 The ANGLE Project Authors. All rights reserved.
+// Copyright 2022 The ANGLE Project Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 //
 // FeaturesMtl.h: Optional features for the Metal renderer.
-//
 
 #ifndef ANGLE_PLATFORM_FEATURESMTL_H_
 #define ANGLE_PLATFORM_FEATURESMTL_H_
@@ -16,118 +17,184 @@
 
 struct FeaturesMtl : FeatureSetBase
 {
-    // BaseVertex/Instanced draw support:
+    FeaturesMtl();
+    ~FeaturesMtl();
+
     Feature hasBaseVertexInstancedDraw = {
-        "has_base_vertex_instanced_draw", FeatureCategory::MetalFeatures,
-        "The renderer supports base vertex instanced draw", &members};
+        "has_base_vertex_instanced_draw",
+        FeatureCategory::MetalFeatures,
+        "The renderer supports base vertex instanced draw",
+        &members,
+    };
 
-    // Support explicit memory barrier
-    Feature hasExplicitMemBarrier = {"has_explicit_mem_barrier_mtl", FeatureCategory::MetalFeatures,
-                                     "The renderer supports explicit memory barrier", &members};
+    Feature hasExplicitMemBarrier = {
+        "has_explicit_mem_barrier_mtl",
+        FeatureCategory::MetalFeatures,
+        "The renderer supports explicit memory barrier",
+        &members,
+    };
 
-    // Some renderer can break render pass cheaply, i.e. desktop class GPUs.
-    Feature hasCheapRenderPass = {"has_cheap_render_pass_mtl", FeatureCategory::MetalFeatures,
-                                  "The renderer can cheaply break a render pass.", &members};
+    Feature hasCheapRenderPass = {
+        "has_cheap_render_pass_mtl",
+        FeatureCategory::MetalFeatures,
+        "The renderer can cheaply break a render pass.",
+        &members,
+    };
 
-    // Non-uniform compute shader dispatch support, i.e. Group size is not necessarily to be fixed:
     Feature hasNonUniformDispatch = {
-        "has_non_uniform_dispatch", FeatureCategory::MetalFeatures,
-        "The renderer supports non uniform compute shader dispatch's group size", &members};
+        "has_non_uniform_dispatch",
+        FeatureCategory::MetalFeatures,
+        "The renderer supports non uniform compute shader dispatch's group size",
+        &members,
+    };
 
-    // fragment stencil output support
-    Feature hasStencilOutput = {"has_shader_stencil_output", FeatureCategory::MetalFeatures,
-                                "The renderer supports stencil output from fragment shader",
-                                &members};
+    Feature hasStencilOutput = {
+        "has_shader_stencil_output",
+        FeatureCategory::MetalFeatures,
+        "The renderer supports stencil output from fragment shader",
+        &members,
+    };
 
-    // Texture swizzle support:
-    Feature hasTextureSwizzle = {"has_texture_swizzle", FeatureCategory::MetalFeatures,
-                                 "The renderer supports texture swizzle", &members};
+    Feature hasTextureSwizzle = {
+        "has_texture_swizzle",
+        FeatureCategory::MetalFeatures,
+        "The renderer supports texture swizzle",
+        &members,
+    };
 
     Feature hasDepthAutoResolve = {
-        "has_msaa_depth_auto_resolve", FeatureCategory::MetalFeatures,
-        "The renderer supports MSAA depth auto resolve at the end of render pass", &members};
+        "has_msaa_depth_auto_resolve",
+        FeatureCategory::MetalFeatures,
+        "The renderer supports MSAA depth auto resolve at the end of render pass",
+        &members,
+    };
 
     Feature hasStencilAutoResolve = {
-        "has_msaa_stencil_auto_resolve", FeatureCategory::MetalFeatures,
-        "The renderer supports MSAA stencil auto resolve at the end of render pass", &members};
+        "has_msaa_stencil_auto_resolve",
+        FeatureCategory::MetalFeatures,
+        "The renderer supports MSAA stencil auto resolve at the end of render pass",
+        &members,
+    };
 
-    Feature hasEvents = {"has_mtl_events", FeatureCategory::MetalFeatures,
-                         "The renderer supports MTL(Shared)Event", &members};
+    Feature hasEvents = {
+        "has_mtl_events",
+        FeatureCategory::MetalFeatures,
+        "The renderer supports MTL(Shared)Event",
+        &members,
+    };
 
     Feature allowInlineConstVertexData = {
-        "allow_inline_const_vertex_data", FeatureCategory::MetalFeatures,
-        "The renderer supports using inline constant data for small client vertex data", &members};
+        "allow_inline_const_vertex_data",
+        FeatureCategory::MetalFeatures,
+        "The renderer supports using inline constant data for small client vertex data",
+        &members,
+    };
 
-    // On macos, separate depth & stencil buffers are not supproted. However, on iOS devices,
-    // they are supproted:
     Feature allowSeparatedDepthStencilBuffers = {
-        "allow_separate_depth_stencil_buffers", FeatureCategory::MetalFeatures,
-        "Some Apple platforms such as iOS allows separate depth & stencil buffers, "
+        "allow_separate_depth_stencil_buffers",
+        FeatureCategory::MetalFeatures,
+        "Some Apple platforms such as iOS allows separate depth and stencil buffers, "
         "whereas others such as macOS don't",
-        &members};
+        &members,
+    };
 
     Feature allowRuntimeSamplerCompareMode = {
-        "allow_runtime_sampler_compare_mode", FeatureCategory::MetalFeatures,
-        "The renderer supports changing sampler's compare mode outside shaders", &members};
+        "allow_runtime_sampler_compare_mode",
+        FeatureCategory::MetalFeatures,
+        "The renderer supports changing sampler's compare mode outside shaders",
+        &members,
+    };
 
     Feature allowSamplerCompareGradient = {
-        "allow_sampler_compare_gradient", FeatureCategory::MetalFeatures,
-        "The renderer supports sample_compare with gradients", &members};
+        "allow_sampler_compare_gradient",
+        FeatureCategory::MetalFeatures,
+        "The renderer supports sample_compare with gradients",
+        &members,
+    };
 
-    Feature allowSamplerCompareLod = {"allow_sampler_compare_lod", FeatureCategory::MetalFeatures,
-                                      "The renderer supports sample_compare with lod", &members};
+    Feature allowSamplerCompareLod = {
+        "allow_sampler_compare_lod",
+        FeatureCategory::MetalFeatures,
+        "The renderer supports sample_compare with lod",
+        &members,
+    };
 
-    Feature allowBufferReadWrite = {"allow_buffer_read_write", FeatureCategory::MetalFeatures,
-                                    "The renderer supports buffer read & write in the same shader",
-                                    &members};
+    Feature allowBufferReadWrite = {
+        "allow_buffer_read_write",
+        FeatureCategory::MetalFeatures,
+        "The renderer supports buffer read and write in the same shader",
+        &members,
+    };
 
     Feature allowMultisampleStoreAndResolve = {
-        "allow_msaa_store_and_resolve", FeatureCategory::MetalFeatures,
-        "The renderer supports MSAA store and resolve in the same pass", &members};
+        "allow_msaa_store_and_resolve",
+        FeatureCategory::MetalFeatures,
+        "The renderer supports MSAA store and resolve in the same pass",
+        &members,
+    };
 
     Feature allowGenMultipleMipsPerPass = {
-        "gen_multiple_mips_per_pass", FeatureCategory::MetalFeatures,
-        "The renderer supports generating multiple mipmaps per pass", &members};
+        "gen_multiple_mips_per_pass",
+        FeatureCategory::MetalFeatures,
+        "The renderer supports generating multiple mipmaps per pass",
+        &members,
+    };
 
-    Feature forceD24S8AsUnsupported = {"force_d24s8_as_unsupported", FeatureCategory::MetalFeatures,
-                                       "Force Depth24Stencil8 format as unsupported.", &members};
+    Feature forceD24S8AsUnsupported = {
+        "force_d24s8_as_unsupported",
+        FeatureCategory::MetalFeatures,
+        "Force Depth24Stencil8 format as unsupported.",
+        &members,
+    };
 
     Feature forceBufferGPUStorage = {
-        "force_buffer_gpu_storage", FeatureCategory::MetalFeatures,
+        "force_buffer_gpu_storage",
+        FeatureCategory::MetalFeatures,
         "On systems that support both buffer' memory allocation on GPU and shared memory (such as "
         "macOS), force using GPU memory allocation for buffers everytime or not.",
-        &members};
+        &members,
+    };
 
-    // Generate Metal directly instead of generating SPIR-V and then using SPIR-V Cross.  Transitory
-    // feature until the work is complete.
     Feature directMetalGeneration = {"directMetalGeneration", FeatureCategory::MetalFeatures,
                                      "Direct translation to Metal.", &members,
                                      "http://anglebug.com/5505"};
 
     Feature forceNonCSBaseMipmapGeneration = {
-        "force_non_cs_mipmap_gen", FeatureCategory::MetalFeatures,
+        "force_non_cs_mipmap_gen",
+        FeatureCategory::MetalFeatures,
         "Turn this feature on to disallow Compute Shader based mipmap generation. Compute Shader "
         "based mipmap generation might cause GPU hang on some older iOS devices.",
-        &members};
+        &members,
+    };
 
     Feature emulateTransformFeedback = {
-        "emulate_transform_feedback", FeatureCategory::MetalFeatures,
-        "Turn this on to allow transform feedback in Metal using a 2-pass VS for GLES3.", &members};
+        "emulate_transform_feedback",
+        FeatureCategory::MetalFeatures,
+        "Turn this on to allow transform feedback in Metal using a 2-pass VS for GLES3.",
+        &members,
+    };
 
-    // Rewrite row-major matrices as column-major
-    Feature rewriteRowMajorMatrices = {"rewrite_row_major_matrices", FeatureCategory::MetalFeatures,
-                                       "Rewrite row major matrices in shaders as column major.",
-                                       &members};
+    Feature rewriteRowMajorMatrices = {
+        "rewrite_row_major_matrices",
+        FeatureCategory::MetalFeatures,
+        "Rewrite row major matrices in shaders as column major.",
+        &members,
+    };
 
     Feature intelExplicitBoolCastWorkaround = {
-        "intel_explicit_bool_cast_workaround", FeatureCategory::MetalWorkarounds,
+        "intel_explicit_bool_cast_workaround",
+        FeatureCategory::MetalWorkarounds,
         "Insert explicit casts for float/double/unsigned/signed int on macOS 10.15 with Intel "
         "driver",
-        &members};
+        &members,
+    };
 
     Feature intelDisableFastMath = {
-        "intel_disable_fast_math", FeatureCategory::MetalWorkarounds,
-        "Disable fast math in atan and invariance cases when running below macOS 12.0", &members};
+        "intel_disable_fast_math",
+        FeatureCategory::MetalWorkarounds,
+        "Disable fast math in atan and invariance cases when running below macOS 12.0",
+        &members,
+    };
 
     Feature multisampleColorFormatShaderReadWorkaround = {
         "multisample_color_format_shader_read_workaround", FeatureCategory::MetalWorkarounds,
@@ -135,6 +202,9 @@
         "http://anglebug.com/7049"};
 };
 
+inline FeaturesMtl::FeaturesMtl()  = default;
+inline FeaturesMtl::~FeaturesMtl() = default;
+
 }  // namespace angle
 
 #endif  // ANGLE_PLATFORM_FEATURESMTL_H_
diff --git a/include/platform/FeaturesVk.h b/include/platform/FeaturesVk.h
index 2a660c2..6329c6c 100644
--- a/include/platform/FeaturesVk.h
+++ b/include/platform/FeaturesVk.h
@@ -1,18 +1,17 @@
+// GENERATED FILE - DO NOT EDIT.
+// Generated by gen_features.py using data from vk_features.json.
 //
-// Copyright 2018 The ANGLE Project Authors. All rights reserved.
+// Copyright 2022 The ANGLE Project Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 //
 // FeaturesVk.h: Optional features for the Vulkan renderer.
-//
 
 #ifndef ANGLE_PLATFORM_FEATURESVK_H_
 #define ANGLE_PLATFORM_FEATURESVK_H_
 
 #include "platform/Feature.h"
 
-#include <array>
-
 namespace angle
 {
 
@@ -21,155 +20,159 @@
     FeaturesVk();
     ~FeaturesVk();
 
-    // Line segment rasterization must follow OpenGL rules. This means using an algorithm similar
-    // to Bresenham's. Vulkan uses a different algorithm. This feature enables the use of pixel
-    // shader patching to implement OpenGL basic line rasterization rules. This feature will
-    // normally always be enabled. Exposing it as an option enables performance testing.
     Feature basicGLLineRasterization = {
-        "basicGLLineRasterization", FeatureCategory::VulkanFeatures,
+        "basicGLLineRasterization",
+        FeatureCategory::VulkanFeatures,
         "Enable the use of pixel shader patching to implement OpenGL basic line "
         "rasterization rules",
-        &members};
+        &members,
+    };
 
-    // If the VK_EXT_line_rasterization extension is available we'll use it to get
-    // Bresenham line rasterization.
     Feature bresenhamLineRasterization = {
-        "bresenhamLineRasterization", FeatureCategory::VulkanFeatures,
-        "Enable Bresenham line rasterization via VK_EXT_line_rasterization extension", &members};
+        "bresenhamLineRasterization",
+        FeatureCategory::VulkanFeatures,
+        "Enable Bresenham line rasterization via VK_EXT_line_rasterization extension",
+        &members,
+    };
 
-    // If the VK_EXT_provoking_vertex extension is available, we'll use it to set
-    // the provoking vertex mode
-    Feature provokingVertex = {"provokingVertex", FeatureCategory::VulkanFeatures,
-                               "Enable provoking vertex mode via VK_EXT_provoking_vertex extension",
-                               &members};
+    Feature provokingVertex = {
+        "provokingVertex",
+        FeatureCategory::VulkanFeatures,
+        "Enable provoking vertex mode via VK_EXT_provoking_vertex extension",
+        &members,
+    };
 
-    // This flag is added for the sole purpose of end2end tests, to test the correctness
-    // of various algorithms when a fallback format is used, such as using a packed format to
-    // emulate a depth- or stencil-only format.
-    Feature forceFallbackFormat = {"forceFallbackFormat", FeatureCategory::VulkanWorkarounds,
-                                   "Force a fallback format for angle_end2end_tests", &members};
+    Feature forceFallbackFormat = {
+        "forceFallbackFormat",
+        FeatureCategory::VulkanWorkarounds,
+        "Force a fallback format for angle_end2end_tests",
+        &members,
+    };
 
-    // On some NVIDIA drivers the point size range reported from the API is inconsistent with the
-    // actual behavior. Clamp the point size to the value from the API to fix this.
-    // Tracked in http://anglebug.com/2970.
     Feature clampPointSize = {
         "clampPointSize", FeatureCategory::VulkanWorkarounds,
         "The point size range reported from the API is inconsistent with the actual behavior",
         &members, "http://anglebug.com/2970"};
 
-    // On some NVIDIA drivers the depth value is not clamped to [0,1] for floating point depth
-    // buffers. This is NVIDIA bug 3171019, see http://anglebug.com/3970 for details.
     Feature depthClamping = {
         "depth_clamping", FeatureCategory::VulkanWorkarounds,
         "The depth value is not clamped to [0,1] for floating point depth buffers.", &members,
         "http://anglebug.com/3970"};
 
-    Feature supportsRenderpass2 = {"supportsRenderpass2", FeatureCategory::VulkanFeatures,
-                                   "VkDevice supports the VK_KHR_create_renderpass2 extension",
-                                   &members};
+    Feature supportsRenderpass2 = {
+        "supportsRenderpass2",
+        FeatureCategory::VulkanFeatures,
+        "VkDevice supports the VK_KHR_create_renderpass2 extension",
+        &members,
+    };
 
-    // Whether the VkDevice supports the VK_KHR_incremental_present extension, on which the
-    // EGL_KHR_swap_buffers_with_damage extension can be layered.
     Feature supportsIncrementalPresent = {
-        "supportsIncrementalPresent", FeatureCategory::VulkanFeatures,
-        "VkDevice supports the VK_KHR_incremental_present extension", &members};
+        "supportsIncrementalPresent",
+        FeatureCategory::VulkanFeatures,
+        "VkDevice supports the VK_KHR_incremental_present extension",
+        &members,
+    };
 
-    // Whether the VkDevice supports the VK_ANDROID_external_memory_android_hardware_buffer
-    // extension, on which the EGL_ANDROID_image_native_buffer extension can be layered.
     Feature supportsAndroidHardwareBuffer = {
-        "supportsAndroidHardwareBuffer", FeatureCategory::VulkanFeatures,
+        "supportsAndroidHardwareBuffer",
+        FeatureCategory::VulkanFeatures,
         "VkDevice supports the VK_ANDROID_external_memory_android_hardware_buffer extension",
-        &members};
+        &members,
+    };
 
-    // Whether the VkDevice supports the VK_GGP_frame_token extension, on which
-    // the EGL_ANGLE_swap_with_frame_token extension can be layered.
-    Feature supportsGGPFrameToken = {"supportsGGPFrameToken", FeatureCategory::VulkanFeatures,
-                                     "VkDevice supports the VK_GGP_frame_token extension",
-                                     &members};
+    Feature supportsGGPFrameToken = {
+        "supportsGGPFrameToken",
+        FeatureCategory::VulkanFeatures,
+        "VkDevice supports the VK_GGP_frame_token extension",
+        &members,
+    };
 
-    // Whether the VkDevice supports the VK_KHR_external_memory_fd extension, on which the
-    // GL_EXT_memory_object_fd extension can be layered.
-    Feature supportsExternalMemoryFd = {"supportsExternalMemoryFd", FeatureCategory::VulkanFeatures,
-                                        "VkDevice supports the VK_KHR_external_memory_fd extension",
-                                        &members};
+    Feature supportsExternalMemoryFd = {
+        "supportsExternalMemoryFd",
+        FeatureCategory::VulkanFeatures,
+        "VkDevice supports the VK_KHR_external_memory_fd extension",
+        &members,
+    };
 
-    // Whether the VkDevice supports the VK_FUCHSIA_external_memory
-    // extension, on which the GL_ANGLE_memory_object_fuchsia extension can be layered.
     Feature supportsExternalMemoryFuchsia = {
-        "supportsExternalMemoryFuchsia", FeatureCategory::VulkanFeatures,
-        "VkDevice supports the VK_FUCHSIA_external_memory extension", &members};
+        "supportsExternalMemoryFuchsia",
+        FeatureCategory::VulkanFeatures,
+        "VkDevice supports the VK_FUCHSIA_external_memory extension",
+        &members,
+    };
 
     Feature supportsFilteringPrecision = {
-        "supportsFilteringPrecision", FeatureCategory::VulkanFeatures,
-        "VkDevice supports the VK_GOOGLE_sampler_filtering_precision extension", &members};
+        "supportsFilteringPrecision",
+        FeatureCategory::VulkanFeatures,
+        "VkDevice supports the VK_GOOGLE_sampler_filtering_precision extension",
+        &members,
+    };
 
-    // Whether the VkInstance supports the VK_KHR_external_fence_capabilities extension.
     Feature supportsExternalFenceCapabilities = {
-        "supportsExternalFenceCapabilities", FeatureCategory::VulkanFeatures,
-        "VkInstance supports the VK_KHR_external_fence_capabilities extension", &members};
+        "supportsExternalFenceCapabilities",
+        FeatureCategory::VulkanFeatures,
+        "VkInstance supports the VK_KHR_external_fence_capabilities extension",
+        &members,
+    };
 
-    // Whether the VkInstance supports the VK_KHR_external_semaphore_capabilities extension.
     Feature supportsExternalSemaphoreCapabilities = {
-        "supportsExternalSemaphoreCapabilities", FeatureCategory::VulkanFeatures,
-        "VkInstance supports the VK_KHR_external_semaphore_capabilities extension", &members};
+        "supportsExternalSemaphoreCapabilities",
+        FeatureCategory::VulkanFeatures,
+        "VkInstance supports the VK_KHR_external_semaphore_capabilities extension",
+        &members,
+    };
 
-    // Whether the VkDevice supports the VK_KHR_external_semaphore_fd extension, on which the
-    // GL_EXT_semaphore_fd extension can be layered.
     Feature supportsExternalSemaphoreFd = {
-        "supportsExternalSemaphoreFd", FeatureCategory::VulkanFeatures,
-        "VkDevice supports the VK_KHR_external_semaphore_fd extension", &members};
+        "supportsExternalSemaphoreFd",
+        FeatureCategory::VulkanFeatures,
+        "VkDevice supports the VK_KHR_external_semaphore_fd extension",
+        &members,
+    };
 
-    // Whether the VkDevice supports the VK_FUCHSIA_external_semaphore
-    // extension, on which the GL_ANGLE_semaphore_fuchsia extension can be layered.
-    angle::Feature supportsExternalSemaphoreFuchsia = {
-        "supportsExternalSemaphoreFuchsia", FeatureCategory::VulkanFeatures,
-        "VkDevice supports the VK_FUCHSIA_external_semaphore extension", &members};
+    Feature supportsExternalSemaphoreFuchsia = {
+        "supportsExternalSemaphoreFuchsia",
+        FeatureCategory::VulkanFeatures,
+        "VkDevice supports the VK_FUCHSIA_external_semaphore extension",
+        &members,
+    };
 
-    // Whether the VkDevice supports the VK_KHR_external_fence_fd extension, on which the
-    // EGL_ANDROID_native_fence extension can be layered.
     Feature supportsExternalFenceFd = {"supportsExternalFenceFd", FeatureCategory::VulkanFeatures,
                                        "VkDevice supports the VK_KHR_external_fence_fd extension",
                                        &members, "http://anglebug.com/2517"};
 
-    // Whether the VkDevice can support EGL_ANDROID_native_fence_sync extension.
     Feature supportsAndroidNativeFenceSync = {
         "supportsAndroidNativeFenceSync", FeatureCategory::VulkanFeatures,
         "VkDevice supports the EGL_ANDROID_native_fence_sync extension", &members,
         "http://anglebug.com/2517"};
 
-    // Whether the VkDevice can support the imageCubeArray feature properly.
     Feature supportsImageCubeArray = {"supportsImageCubeArray", FeatureCategory::VulkanFeatures,
                                       "VkDevice supports the imageCubeArray feature properly",
                                       &members, "http://anglebug.com/3584"};
 
-    // Whether the VkDevice supports the pipelineStatisticsQuery feature.
     Feature supportsPipelineStatisticsQuery = {
         "supportsPipelineStatisticsQuery", FeatureCategory::VulkanFeatures,
         "VkDevice supports the pipelineStatisticsQuery feature", &members,
         "http://anglebug.com/5430"};
 
-    // Whether the VkDevice supports the VK_EXT_shader_stencil_export extension, which is used to
-    // perform multisampled resolve of stencil buffer.  A multi-step workaround is used instead if
-    // this extension is not available.
     Feature supportsShaderStencilExport = {
-        "supportsShaderStencilExport", FeatureCategory::VulkanFeatures,
-        "VkDevice supports the VK_EXT_shader_stencil_export extension", &members};
+        "supportsShaderStencilExport",
+        FeatureCategory::VulkanFeatures,
+        "VkDevice supports the VK_EXT_shader_stencil_export extension",
+        &members,
+    };
 
-    // Whether the VkDevice supports the VK_KHR_sampler_ycbcr_conversion extension, which is needed
-    // to support Ycbcr conversion with external images.
     Feature supportsYUVSamplerConversion = {
-        "supportsYUVSamplerConversion", FeatureCategory::VulkanFeatures,
-        "VkDevice supports the VK_KHR_sampler_ycbcr_conversion extension", &members};
+        "supportsYUVSamplerConversion",
+        FeatureCategory::VulkanFeatures,
+        "VkDevice supports the VK_KHR_sampler_ycbcr_conversion extension",
+        &members,
+    };
 
-    // Where VK_EXT_transform_feedback is not support, an emulation path is used.
-    // http://anglebug.com/3205
     Feature emulateTransformFeedback = {
         "emulateTransformFeedback", FeatureCategory::VulkanFeatures,
         "Emulate transform feedback as the VK_EXT_transform_feedback is not present.", &members,
         "http://anglebug.com/3205"};
 
-    // Where VK_EXT_transform_feedback is supported, it's preferred over an emulation path.
-    // http://anglebug.com/3206
     Feature supportsTransformFeedbackExtension = {
         "supportsTransformFeedbackExtension", FeatureCategory::VulkanFeatures,
         "Transform feedback uses the VK_EXT_transform_feedback extension.", &members,
@@ -180,100 +183,64 @@
         "Implementation supports the GeometryStreams SPIR-V capability.", &members,
         "http://anglebug.com/3206"};
 
-    // Whether the VkDevice supports the VK_EXT_index_type_uint8 extension
-    // http://anglebug.com/4405
     Feature supportsIndexTypeUint8 = {"supportsIndexTypeUint8", FeatureCategory::VulkanFeatures,
                                       "VkDevice supports the VK_EXT_index_type_uint8 extension",
                                       &members, "http://anglebug.com/4405"};
 
-    // Whether the VkDevice supports the VK_EXT_custom_border_color extension
-    // http://anglebug.com/3577
     Feature supportsCustomBorderColor = {
         "supportsCustomBorderColor", FeatureCategory::VulkanFeatures,
         "VkDevice supports the VK_EXT_custom_border_color extension", &members,
         "http://anglebug.com/3577"};
 
-    // Whether the VkDevice supports multiDrawIndirect (drawIndirect with drawCount > 1)
-    // http://anglebug.com/6439
     Feature supportsMultiDrawIndirect = {
         "supportsMultiDrawIndirect", FeatureCategory::VulkanFeatures,
         "VkDevice supports the multiDrawIndirect extension", &members, "http://anglebug.com/6439"};
 
-    // Whether the VkDevice supports the VK_KHR_depth_stencil_resolve extension with the
-    // independentResolveNone feature.
-    // http://anglebug.com/4836
     Feature supportsDepthStencilResolve = {"supportsDepthStencilResolve",
                                            FeatureCategory::VulkanFeatures,
                                            "VkDevice supports the VK_KHR_depth_stencil_resolve "
                                            "extension with the independentResolveNone feature",
                                            &members, "http://anglebug.com/4836"};
 
-    // Whether the VkDevice supports the VK_EXT_multisampled_render_to_single_sampled extension.
-    // http://anglebug.com/4836
     Feature supportsMultisampledRenderToSingleSampled = {
         "supportsMultisampledRenderToSingleSampled", FeatureCategory::VulkanFeatures,
         "VkDevice supports the VK_EXT_multisampled_render_to_single_sampled extension", &members,
         "http://anglebug.com/4836"};
 
-    // Whether the VkDevice supports the VK_KHR_multiview extension.  http://anglebug.com/6048
     Feature supportsMultiview = {"supportsMultiview", FeatureCategory::VulkanFeatures,
                                  "VkDevice supports the VK_KHR_multiview extension", &members,
                                  "http://anglebug.com/6048"};
 
-    // VK_PRESENT_MODE_FIFO_KHR causes random timeouts on Linux Intel. http://anglebug.com/3153
     Feature disableFifoPresentMode = {"disableFifoPresentMode", FeatureCategory::VulkanWorkarounds,
                                       "VK_PRESENT_MODE_FIFO_KHR causes random timeouts", &members,
                                       "http://anglebug.com/3153"};
 
-    // On Qualcomm, gaps in bound descriptor set indices causes the post-gap sets to misbehave.
-    // For example, binding only descriptor set 3 results in zero being read from a uniform buffer
-    // object within that set.  This flag results in empty descriptor sets being bound for any
-    // unused descriptor set to work around this issue.  http://anglebug.com/2727
     Feature bindEmptyForUnusedDescriptorSets = {
         "bindEmptyForUnusedDescriptorSets", FeatureCategory::VulkanWorkarounds,
         "Gaps in bound descriptor set indices causes the post-gap sets to misbehave", &members,
         "http://anglebug.com/2727"};
 
-    // OES_depth_texture is a commonly expected feature on Android. However it
-    // requires that D16_UNORM support texture filtering
-    // (e.g. VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT) and some devices
-    // do not. Work-around this by setting saying D16_UNORM supports filtering
-    // anyway.
     Feature forceD16TexFilter = {
         "forceD16TexFilter", FeatureCategory::VulkanWorkarounds,
         "VK_FORMAT_D16_UNORM does not support VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT, "
         "which prevents OES_depth_texture from being supported.",
         &members, "http://anglebug.com/3452"};
 
-    // On some android devices, vkCmdBlitImage with flipped coordinates blits incorrectly.  This
-    // workaround makes sure this path is avoided.  http://anglebug.com/3498
     Feature disableFlippingBlitWithCommand = {
         "disableFlippingBlitWithCommand", FeatureCategory::VulkanWorkarounds,
         "vkCmdBlitImage with flipped coordinates blits incorrectly.", &members,
         "http://anglebug.com/3498"};
 
-    // On platform with Intel or AMD GPU, a window resizing would not trigger the vulkan driver to
-    // return VK_ERROR_OUT_OF_DATE on swapchain present.  Work-around by query current window extent
-    // every frame to detect a window resizing.
-    // http://anglebug.com/3623, http://anglebug.com/3624, http://anglebug.com/3625
     Feature perFrameWindowSizeQuery = {
         "perFrameWindowSizeQuery", FeatureCategory::VulkanWorkarounds,
         "Vulkan swapchain is not returning VK_ERROR_OUT_OF_DATE when window resizing", &members,
         "http://anglebug.com/3623, http://anglebug.com/3624, http://anglebug.com/3625"};
 
-    // Seamful cube map emulation misbehaves on the AMD windows driver, so it's disallowed.
     Feature disallowSeamfulCubeMapEmulation = {
         "disallowSeamfulCubeMapEmulation", FeatureCategory::VulkanWorkarounds,
         "Seamful cube map emulation misbehaves on some drivers, so it's disallowed", &members,
         "http://anglebug.com/3243"};
 
-    // Vulkan considers vertex attribute accesses to count up to the last multiple of the stride.
-    // This additional access supports AMD's robust buffer access implementation.
-    // AMDVLK in particular will return incorrect values when the vertex access extends into the
-    // range that would be the stride padding and the buffer is too small.
-    // This workaround limits GL_MAX_VERTEX_ATTRIB_STRIDE to a reasonable value and pads out
-    // every buffer allocation size to be large enough to support a maximum vertex stride.
-    // http://anglebug.com/4428
     Feature padBuffersToMaxVertexAttribStride = {
         "padBuffersToMaxVertexAttribStride", FeatureCategory::VulkanWorkarounds,
         "Vulkan considers vertex attribute accesses to count up to the last multiple of the "
@@ -284,385 +251,335 @@
         "pads up every buffer allocation size to be a multiple of the maximum stride.",
         &members, "http://anglebug.com/4428"};
 
-    // Whether the VkDevice supports the VK_EXT_external_memory_dma_buf and
-    // VK_EXT_image_drm_format_modifier extensions.  These extensions are always used together to
-    // implement EGL_EXT_image_dma_buf_import and EGL_EXT_image_dma_buf_import_modifiers.
     Feature supportsExternalMemoryDmaBufAndModifiers = {
         "supportsExternalMemoryDmaBufAndModifiers", FeatureCategory::VulkanFeatures,
         "VkDevice supports the VK_EXT_external_memory_dma_buf and VK_EXT_image_drm_format_modifier "
         "extensions",
         &members, "http://anglebug.com/6248"};
 
-    // Whether the VkDevice supports the VK_EXT_external_memory_host extension, on which the
-    // ANGLE_iosurface_client_buffer extension can be layered.
     Feature supportsExternalMemoryHost = {
-        "supportsExternalMemoryHost", FeatureCategory::VulkanFeatures,
-        "VkDevice supports the VK_EXT_external_memory_host extension", &members};
+        "supportsExternalMemoryHost",
+        FeatureCategory::VulkanFeatures,
+        "VkDevice supports the VK_EXT_external_memory_host extension",
+        &members,
+    };
 
-    // Whether to fill new buffers and textures with nonzero data to sanitize robust resource
-    // initialization and flush out assumptions about zero init.
     Feature allocateNonZeroMemory = {
         "allocateNonZeroMemory", FeatureCategory::VulkanFeatures,
         "Fill new allocations with non-zero values to flush out errors.", &members,
         "http://anglebug.com/4384"};
 
-    // Whether to log each callback from the VK_EXT_device_memory_report extension.  This feature is
-    // used for trying to debug GPU memory leaks.
-    Feature logMemoryReportCallbacks = {"logMemoryReportCallbacks", FeatureCategory::VulkanFeatures,
-                                        "Log each callback from VK_EXT_device_memory_report",
-                                        &members};
+    Feature logMemoryReportCallbacks = {
+        "logMemoryReportCallbacks",
+        FeatureCategory::VulkanFeatures,
+        "Log each callback from VK_EXT_device_memory_report",
+        &members,
+    };
 
-    // Whether to log statistics from the VK_EXT_device_memory_report extension each eglSwapBuffer.
-    Feature logMemoryReportStats = {"logMemoryReportStats", FeatureCategory::VulkanFeatures,
-                                    "Log stats from VK_EXT_device_memory_report each swap",
-                                    &members};
+    Feature logMemoryReportStats = {
+        "logMemoryReportStats",
+        FeatureCategory::VulkanFeatures,
+        "Log stats from VK_EXT_device_memory_report each swap",
+        &members,
+    };
 
-    // Allocate a "shadow" buffer for GL buffer objects. For GPU-read only buffers
-    // glMap* latency can be reduced by maintaining a copy of the buffer which is
-    // writeable only by the CPU. We then return this shadow buffer on glMap* calls.
     Feature shadowBuffers = {
         "shadowBuffers", FeatureCategory::VulkanFeatures,
         "Allocate a shadow buffer for GL buffer objects to reduce glMap* latency.", &members,
         "http://anglebug.com/4339"};
 
-    // When we update buffer data we usually face a choice to either clone a buffer and copy the
-    // data or stage a buffer update and use the GPU to do the copy. For some GPUs, a performance
-    // penalty to use the GPU to do copies. Setting this flag to true will always try to create a
-    // new buffer and use the CPU to copy data when possible.
     Feature preferCPUForBufferSubData = {
         "preferCPUForBufferSubData", FeatureCategory::VulkanFeatures,
         "Prefer use CPU to do bufferSubData instead of staged update.", &members,
         "http://issuetracker.google.com/200067929"};
 
-    // Persistently map buffer memory until destroy, saves on map/unmap IOCTL overhead
-    // for buffers that are updated frequently.
     Feature persistentlyMappedBuffers = {
         "persistentlyMappedBuffers", FeatureCategory::VulkanFeatures,
         "Persistently map buffer memory to reduce map/unmap IOCTL overhead.", &members,
         "http://anglebug.com/2162"};
 
-    // Android needs to pre-rotate surfaces that are not oriented per the native device's
-    // orientation (e.g. a landscape application on a Pixel phone).  This feature works for
-    // full-screen applications. http://anglebug.com/3502
     Feature enablePreRotateSurfaces = {"enablePreRotateSurfaces", FeatureCategory::VulkanFeatures,
                                        "Enable Android pre-rotation for landscape applications",
                                        &members, "http://anglebug.com/3502"};
 
-    // Enable precision qualifiers for shaders generated by Vulkan backend http://anglebug.com/3078
     Feature enablePrecisionQualifiers = {
         "enablePrecisionQualifiers", FeatureCategory::VulkanFeatures,
         "Enable precision qualifiers in shaders", &members, "http://anglebug.com/3078"};
 
-    // Desktop (at least NVIDIA) devices prefer combining barriers into one vkCmdPipelineBarrier
-    // call over issuing multiple barrier calls with fine grained dependency information to have
-    // better performance. http://anglebug.com/4633
     Feature preferAggregateBarrierCalls = {
         "preferAggregateBarrierCalls", FeatureCategory::VulkanWorkarounds,
         "Single barrier call is preferred over multiple calls with "
         "fine grained pipeline stage dependency information",
         &members, "http://anglebug.com/4633"};
 
-    // When dealing with emulated formats that have extra channels, it's cheaper on desktop devices
-    // to skip invalidating framebuffer attachments compared to tiling devices where it's cheaper to
-    // invalidate and re-clear them. http://anglebug.com/6860
     Feature preferSkippingInvalidateForEmulatedFormats = {
         "preferSkippingInvalidateForEmulatedFormats", FeatureCategory::VulkanWorkarounds,
         "Skipping invalidate is preferred for emulated formats that have extra channels over "
         "re-clearing the image",
         &members, "http://anglebug.com/6860"};
 
-    // Tell the Vulkan back-end to use the async command queue to dispatch work to the GPU. Command
-    // buffer work will happened in a worker thread. Otherwise use Renderer::CommandQueue directly.
     Feature asyncCommandQueue = {"asyncCommandQueue", FeatureCategory::VulkanFeatures,
                                  "Use CommandQueue worker thread to dispatch work to GPU.",
                                  &members, "http://anglebug.com/4324"};
 
-    // Whether the VkDevice supports the VK_KHR_shader_float16_int8 extension and has the
-    // shaderFloat16 feature.
     Feature supportsShaderFloat16 = {"supportsShaderFloat16", FeatureCategory::VulkanFeatures,
                                      "VkDevice supports the VK_KHR_shader_float16_int8 extension "
                                      "and has the shaderFloat16 feature",
                                      &members, "http://anglebug.com/4551"};
 
-    // Some devices don't meet the limits required to perform mipmap generation using the built-in
-    // compute shader.  On some other devices, VK_IMAGE_USAGE_STORAGE_BIT is detrimental to
-    // performance, making this solution impractical.
     Feature allowGenerateMipmapWithCompute = {
         "allowGenerateMipmapWithCompute", FeatureCategory::VulkanFeatures,
         "Use the compute path to generate mipmaps on devices that meet the minimum requirements, "
         "and the performance is better.",
         &members, "http://anglebug.com/4551"};
 
-    // Whether the VkDevice supports the VK_QCOM_render_pass_store_ops extension
-    // http://anglebug.com/5505
     Feature supportsRenderPassStoreOpNoneQCOM = {
         "supportsRenderPassStoreOpNoneQCOM", FeatureCategory::VulkanFeatures,
         "VkDevice supports VK_QCOM_render_pass_store_ops extension.", &members,
         "http://anglebug.com/5055"};
 
-    // Whether the VkDevice supports the VK_EXT_load_store_op_none extension
-    // http://anglebug.com/5371
     Feature supportsRenderPassLoadStoreOpNone = {
         "supportsRenderPassLoadStoreOpNone", FeatureCategory::VulkanFeatures,
         "VkDevice supports VK_EXT_load_store_op_none extension.", &members,
         "http://anglebug.com/5371"};
 
-    // Whether the VkDevice supports the VK_EXT_depth_clip_control extension
-    // http://anglebug.com/5421
     Feature supportsDepthClipControl = {"supportsDepthClipControl", FeatureCategory::VulkanFeatures,
                                         "VkDevice supports VK_EXT_depth_clip_control extension.",
                                         &members, "http://anglebug.com/5421"};
 
-    // Whether the VkDevice supports the VK_EXT_blend_operation_advanced extension
-    // http://anglebug.com/3586
     Feature supportsBlendOperationAdvanced = {
         "supportsBlendOperationAdvanced", FeatureCategory::VulkanFeatures,
         "VkDevice supports VK_EXT_blend_operation_advanced extension.", &members,
         "http://anglebug.com/3586"};
 
-    // Force maxUniformBufferSize to 16K on Qualcomm's Adreno 540. Pixel2's Adreno540 reports
-    // maxUniformBufferSize 64k but various tests failed with that size. For that specific
-    // device, we set to 16k for now which is known to pass all tests.
-    // https://issuetracker.google.com/161903006
     Feature forceMaxUniformBufferSize16KB = {
         "forceMaxUniformBufferSize16KB", FeatureCategory::VulkanWorkarounds,
         "Force max uniform buffer size to 16K on some device due to bug", &members,
         "https://issuetracker.google.com/161903006"};
 
-    // Enable mutable bit by default for ICD's that support VK_KHR_image_format_list.
-    // http://anglebug.com/5281
     Feature supportsImageFormatList = {
         "supportsImageFormatList", FeatureCategory::VulkanFeatures,
         "Enable VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT by default for ICDs "
         "that support VK_KHR_image_format_list",
         &members, "http://anglebug.com/5281"};
 
-    // Swiftshader on mac fails to initialize WebGL context when EXT_multisampled_render_to_texture
-    // is used by Chromium.
-    // http://anglebug.com/4937
     Feature enableMultisampledRenderToTexture = {
         "enableMultisampledRenderToTexture", FeatureCategory::VulkanWorkarounds,
         "Expose EXT_multisampled_render_to_texture", &members, "http://anglebug.com/4937"};
 
-    // Manhattan is calling glFlush in the middle of renderpass which breaks renderpass and hurts
-    // performance on tile based GPU. When this is enabled, we will defer the glFlush call made in
-    // the middle of renderpass to the end of renderpass.
-    // https://issuetracker.google.com/issues/166475273
     Feature deferFlushUntilEndRenderPass = {
         "deferFlushUntilEndRenderPass", FeatureCategory::VulkanWorkarounds,
         "Allow glFlush to be deferred until renderpass ends", &members,
         "https://issuetracker.google.com/issues/166475273"};
 
-    // Android mistakenly destroys oldSwapchain passed to vkCreateSwapchainKHR, causing crashes on
-    // certain drivers.  http://anglebug.com/5061
     Feature waitIdleBeforeSwapchainRecreation = {
         "waitIdleBeforeSwapchainRecreation", FeatureCategory::VulkanWorkarounds,
         "Before passing an oldSwapchain to VkSwapchainCreateInfoKHR, wait for queue to be idle. "
         "Works around a bug on platforms which destroy oldSwapchain in vkCreateSwapchainKHR.",
         &members, "http://anglebug.com/5061"};
 
-    // Allow forcing an LOD offset on all sampling operations for performance comparisons. ANGLE is
-    // non-conformant if this feature is enabled.
-    std::array<angle::Feature, 4> forceTextureLODOffset = {
-        angle::Feature{"force_texture_lod_offset_1", angle::FeatureCategory::VulkanWorkarounds,
-                       "Increase the minimum texture level-of-detail by 1 when sampling.",
-                       &members},
-        angle::Feature{"force_texture_lod_offset_2", angle::FeatureCategory::VulkanWorkarounds,
-                       "Increase the minimum texture level-of-detail by 2 when sampling.",
-                       &members},
-        angle::Feature{"force_texture_lod_offset_3", angle::FeatureCategory::VulkanWorkarounds,
-                       "Increase the minimum texture level-of-detail by 3 when sampling.",
-                       &members},
-        angle::Feature{"force_texture_lod_offset_4", angle::FeatureCategory::VulkanWorkarounds,
-                       "Increase the minimum texture level-of-detail by 4 when sampling.",
-                       &members},
+    Feature forceTextureLodOffset1 = {
+        "force_texture_lod_offset_1",
+        FeatureCategory::VulkanWorkarounds,
+        "Increase the minimum texture level-of-detail by 1 when sampling.",
+        &members,
     };
 
-    // Translate non-nearest filtering modes to nearest for all samplers for performance
-    // comparisons. ANGLE is non-conformant if this feature is enabled.
-    Feature forceNearestFiltering = {"force_nearest_filtering", FeatureCategory::VulkanWorkarounds,
-                                     "Force nearest filtering when sampling.", &members};
+    Feature forceTextureLodOffset2 = {
+        "force_texture_lod_offset_2",
+        FeatureCategory::VulkanWorkarounds,
+        "Increase the minimum texture level-of-detail by 2 when sampling.",
+        &members,
+    };
 
-    // Translate  non-nearest mip filtering modes to nearest mip for all samplers for performance
-    // comparisons. ANGLE is non-conformant if this feature is enabled.
-    Feature forceNearestMipFiltering = {"forceNearestMipFiltering",
-                                        FeatureCategory::VulkanWorkarounds,
-                                        "Force nearest mip filtering when sampling.", &members};
+    Feature forceTextureLodOffset3 = {
+        "force_texture_lod_offset_3",
+        FeatureCategory::VulkanWorkarounds,
+        "Increase the minimum texture level-of-detail by 3 when sampling.",
+        &members,
+    };
 
-    // Compress float32 vertices in static buffers to float16 at draw time. ANGLE is non-conformant
-    // if this feature is enabled.
-    angle::Feature compressVertexData = {"compress_vertex_data",
-                                         angle::FeatureCategory::VulkanWorkarounds,
-                                         "Compress vertex data to smaller data types when "
-                                         "possible. Using this feature makes ANGLE non-conformant.",
-                                         &members};
+    Feature forceTextureLodOffset4 = {
+        "force_texture_lod_offset_4",
+        FeatureCategory::VulkanWorkarounds,
+        "Increase the minimum texture level-of-detail by 4 when sampling.",
+        &members,
+    };
 
-    // Qualcomm missynchronizes vkCmdClearAttachments in the middle of render pass.
-    // https://issuetracker.google.com/166809097
+    Feature forceNearestFiltering = {
+        "force_nearest_filtering",
+        FeatureCategory::VulkanWorkarounds,
+        "Force nearest filtering when sampling.",
+        &members,
+    };
+
+    Feature forceNearestMipFiltering = {
+        "forceNearestMipFiltering",
+        FeatureCategory::VulkanWorkarounds,
+        "Force nearest mip filtering when sampling.",
+        &members,
+    };
+
+    Feature compressVertexData = {
+        "compress_vertex_data",
+        FeatureCategory::VulkanWorkarounds,
+        "Compress vertex data to smaller data types when "
+        "possible. Using this feature makes ANGLE non-conformant.",
+        &members,
+    };
+
     Feature preferDrawClearOverVkCmdClearAttachments = {
         "preferDrawClearOverVkCmdClearAttachments", FeatureCategory::VulkanWorkarounds,
         "On some hardware, clear using a draw call instead of vkCmdClearAttachments in the middle "
         "of render pass due to bugs",
         &members, "https://issuetracker.google.com/166809097"};
 
-    // Whether prerotation is being emulated for testing.  90 degree rotation.
     Feature emulatedPrerotation90 = {"emulatedPrerotation90", FeatureCategory::VulkanFeatures,
                                      "Emulate 90-degree prerotation.", &members,
                                      "http://anglebug.com/4901"};
 
-    // Whether prerotation is being emulated for testing.  180 degree rotation.
     Feature emulatedPrerotation180 = {"emulatedPrerotation180", FeatureCategory::VulkanFeatures,
                                       "Emulate 180-degree prerotation.", &members,
                                       "http://anglebug.com/4901"};
 
-    // Whether prerotation is being emulated for testing.  270 degree rotation.
     Feature emulatedPrerotation270 = {"emulatedPrerotation270", FeatureCategory::VulkanFeatures,
                                       "Emulate 270-degree prerotation.", &members,
                                       "http://anglebug.com/4901"};
 
-    // Whether SPIR-V should be generated through glslang.  Transitory feature while confidence is
-    // built on the SPIR-V generation code.
     Feature generateSPIRVThroughGlslang = {
         "generateSPIRVThroughGlslang", FeatureCategory::VulkanFeatures,
         "Translate SPIR-V through glslang.", &members, "http://anglebug.com/4889"};
 
-    // Whether we should use driver uniforms over specialization constants for some shader
-    // modifications like yflip and rotation.
     Feature forceDriverUniformOverSpecConst = {
         "forceDriverUniformOverSpecConst", FeatureCategory::VulkanWorkarounds,
         "Forces using driver uniforms instead of specialization constants.", &members,
         "http://issuetracker.google.com/173636783"};
 
-    // Whether non-conformant configurations and extensions should be exposed.  When an extension is
-    // in development, or a GLES version is not supported on a device, we may still want to expose
-    // them for partial testing.  This feature is enabled by our test harness.
     Feature exposeNonConformantExtensionsAndVersions = {
         "exposeNonConformantExtensionsAndVersions", FeatureCategory::VulkanWorkarounds,
         "Expose GLES versions and extensions that are not conformant.", &members,
         "http://anglebug.com/5375"};
 
-    // imageAtomicExchange is expected to work for r32f formats, but support for atomic operations
-    // for VK_FORMAT_R32_SFLOAT is rare.  This support is emulated by using an r32ui format for such
-    // images instead.
     Feature emulateR32fImageAtomicExchange = {
         "emulateR32fImageAtomicExchange", FeatureCategory::VulkanWorkarounds,
         "Emulate r32f images with r32ui to support imageAtomicExchange.", &members,
         "http://anglebug.com/5535"};
 
     Feature supportsNegativeViewport = {
-        "supportsNegativeViewport", FeatureCategory::VulkanFeatures,
-        "The driver supports inverting the viewport with a negative height.", &members};
+        "supportsNegativeViewport",
+        FeatureCategory::VulkanFeatures,
+        "The driver supports inverting the viewport with a negative height.",
+        &members,
+    };
 
-    // Whether we should force any highp precision in the fragment shader to mediump.
-    // ANGLE is non-conformant if this feature is enabled.
     Feature forceFragmentShaderPrecisionHighpToMediump = {
         "forceFragmentShaderPrecisionHighpToMediump", FeatureCategory::VulkanWorkarounds,
         "Forces highp precision in fragment shader to mediump.", &members,
         "https://issuetracker.google.com/184850002"};
 
-    // Whether we should submit at each FBO boundary.
     Feature preferSubmitAtFBOBoundary = {
         "preferSubmitAtFBOBoundary", FeatureCategory::VulkanWorkarounds,
         "Submit commands to driver at each FBO boundary for performance improvements.", &members,
         "https://issuetracker.google.com/187425444"};
 
-    // Workaround for gap in Vulkan spec related to querying descriptor count for immutable samplers
-    // tied to an external format.
     Feature useMultipleDescriptorsForExternalFormats = {
         "useMultipleDescriptorsForExternalFormats", FeatureCategory::VulkanWorkarounds,
         "Return a default descriptor count for external formats.", &members,
         "http://anglebug.com/6141"};
 
-    // Whether the VkDevice can support Protected Memory.
     Feature supportsProtectedMemory = {"supportsProtectedMemory", FeatureCategory::VulkanFeatures,
                                        "VkDevice supports protected memory", &members,
                                        "http://anglebug.com/3965"};
 
-    // Whether the VkDevice supports the VK_EXT_host_query_reset extension
-    // http://anglebug.com/6692
     Feature supportsHostQueryReset = {"supportsHostQueryReset", FeatureCategory::VulkanFeatures,
                                       "VkDevice supports VK_EXT_host_query_reset extension",
                                       &members, "http://anglebug.com/6692"};
 
-    // Whether the VkInstance supports the VK_KHR_get_surface_capabilities2 extension.
     Feature supportsSurfaceCapabilities2Extension = {
-        "supportsSurfaceCapabilities2Extension", FeatureCategory::VulkanFeatures,
-        "VkInstance supports the VK_KHR_get_surface_capabilities2 extension", &members};
+        "supportsSurfaceCapabilities2Extension",
+        FeatureCategory::VulkanFeatures,
+        "VkInstance supports the VK_KHR_get_surface_capabilities2 extension",
+        &members,
+    };
 
-    // Whether the VkInstance supports the VK_KHR_surface_protected_capabilities extension.
     Feature supportsSurfaceProtectedCapabilitiesExtension = {
-        "supportsSurfaceProtectedCapabilitiesExtension", FeatureCategory::VulkanFeatures,
-        "VkInstance supports the VK_KHR_surface_protected_capabilities extension", &members};
+        "supportsSurfaceProtectedCapabilitiesExtension",
+        FeatureCategory::VulkanFeatures,
+        "VkInstance supports the VK_KHR_surface_protected_capabilities extension",
+        &members,
+    };
 
-    // Whether the VkInstance supports the VK_GOOGLE_surfaceless_query extension.
     Feature supportsSurfacelessQueryExtension = {
-        "supportsSurfacelessQueryExtension", FeatureCategory::VulkanFeatures,
-        "VkInstance supports the VK_GOOGLE_surfaceless_query extension", &members};
+        "supportsSurfacelessQueryExtension",
+        FeatureCategory::VulkanFeatures,
+        "VkInstance supports the VK_GOOGLE_surfaceless_query extension",
+        &members,
+    };
 
-    // Whether the VkSurface supports protected swapchains from
-    // supportsSurfaceProtectedCapabilitiesExtension.
     Feature supportsSurfaceProtectedSwapchains = {
-        "supportsSurfaceProtectedSwapchains", FeatureCategory::VulkanFeatures,
-        "VkSurface supportsProtected for protected swapchains", &members};
+        "supportsSurfaceProtectedSwapchains",
+        FeatureCategory::VulkanFeatures,
+        "VkSurface supportsProtected for protected swapchains",
+        &members,
+    };
 
-    // Whether surface format GL_RGB8 should be overridden to GL_RGBA8.
     Feature overrideSurfaceFormatRGB8toRGBA8 = {
         "overrideSurfaceFormatRGB8toRGBA8", FeatureCategory::VulkanWorkarounds,
         "Override surface format GL_RGB8 to GL_RGBA8", &members, "http://anglebug.com/6651"};
 
-    // Whether the VkSurface supports VK_KHR_shared_presentable_images.
     Feature supportsSharedPresentableImageExtension = {
-        "supportsSharedPresentableImageExtension", FeatureCategory::VulkanFeatures,
-        "VkSurface supports the VK_KHR_shared_presentable_images extension", &members};
+        "supportsSharedPresentableImageExtension",
+        FeatureCategory::VulkanFeatures,
+        "VkSurface supports the VK_KHR_shared_presentable_images extension",
+        &members,
+    };
 
-    // Feature to control whether the Vulkan backend can support
-    // GL_EXT_shader_framebuffer_fetch
     Feature supportsShaderFramebufferFetch = {
-        "supportsShaderFramebufferFetch", FeatureCategory::VulkanFeatures,
-        "Whether the Vulkan backend supports coherent framebuffer fetch", &members};
+        "supportsShaderFramebufferFetch",
+        FeatureCategory::VulkanFeatures,
+        "Whether the Vulkan backend supports coherent framebuffer fetch",
+        &members,
+    };
 
-    // Feature to control whether the Vulkan backend can support
-    // GL_EXT_shader_framebuffer_fetch_non_coherent
     Feature supportsShaderFramebufferFetchNonCoherent = {
-        "supportsShaderFramebufferFetchNonCoherent", FeatureCategory::VulkanFeatures,
-        "Whether the Vulkan backend supports non-coherent framebuffer fetch", &members};
+        "supportsShaderFramebufferFetchNonCoherent",
+        FeatureCategory::VulkanFeatures,
+        "Whether the Vulkan backend supports non-coherent framebuffer fetch",
+        &members,
+    };
 
-    // Whether the Surface supports EGL_KHR_lock_surface3.
     Feature supportsLockSurfaceExtension = {
-        "supportsLockSurfaceExtension", FeatureCategory::VulkanFeatures,
-        "Surface supports the EGL_KHR_lock_surface3 extension", &members};
+        "supportsLockSurfaceExtension",
+        FeatureCategory::VulkanFeatures,
+        "Surface supports the EGL_KHR_lock_surface3 extension",
+        &members,
+    };
 
-    // When mutable_render_buffer goes into SINGLE_BUFFER mode, need to call swapbuffers at
-    // flush and finish so that the image is updated and presented to the display.
     Feature swapbuffersOnFlushOrFinishWithSingleBuffer = {
         "swapbuffersOnFlushOrFinishWithSingleBuffer", FeatureCategory::VulkanFeatures,
         "Bypass deferredFlush with calling swapbuffers on flush or finish when in Shared Present "
         "mode",
         &members, "http://anglebug.com/6878"};
 
-    // Whether dithering should be emulated.
     Feature emulateDithering = {"emulateDithering", FeatureCategory::VulkanFeatures,
                                 "Emulate OpenGL dithering", &members, "http://anglebug.com/6755"};
 
-    // Android bug workaround which assumes VkPresentRegionsKHR to have a bottom-left origin
-    // instead of top-left as specified by VK_KHR_incremental_present
     Feature bottomLeftOriginPresentRegionRectangles = {
-        "bottomLeftOriginPresentRegionRectangles", FeatureCategory::VulkanWorkarounds,
+        "bottomLeftOriginPresentRegionRectangles",
+        FeatureCategory::VulkanWorkarounds,
         "On some platforms present region rectangles are expected to have a bottom-left origin, "
         "instead of top-left origin as from spec",
-        &members};
+        &members,
+    };
 
-    // Whether we force submit updates to immutable textures.
     Feature forceSubmitImmutableTextureUpdates = {
-        "forceSubmitImmutableTextureUpdates", FeatureCategory::AppWorkarounds,
+        "forceSubmitImmutableTextureUpdates", FeatureCategory::VulkanAppWorkarounds,
         "Force submit updates to immutable textures", &members, "http://anglebug.com/6929"};
 
-    // Whether we retain SPIR-V debug information to aid in analyzing shader code.
     Feature retainSpirvDebugInfo = {"retainSpirvDebugInfo", FeatureCategory::VulkanFeatures,
                                     "Retain debug info in SPIR-V blob.", &members,
                                     "http://anglebug.com/5901"};
 
-    // Whether we create a Vulkan pipeline with "default" state during glLinkProgram
     Feature createPipelineDuringLink = {"createPipelineDuringLink", FeatureCategory::VulkanFeatures,
                                         "Create pipeline with default state during glLinkProgram",
                                         &members, "http://anglebug.com/7046"};
diff --git a/include/platform/FrontendFeatures.h b/include/platform/FrontendFeatures.h
index d4cee0b..a2b9d17 100644
--- a/include/platform/FrontendFeatures.h
+++ b/include/platform/FrontendFeatures.h
@@ -1,9 +1,10 @@
+// GENERATED FILE - DO NOT EDIT.
+// Generated by gen_features.py using data from frontend_features.json.
 //
-// Copyright 2016 The ANGLE Project Authors. All rights reserved.
+// Copyright 2022 The ANGLE Project Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 //
-
 // FrontendFeatures.h: Features/workarounds for driver bugs and other behaviors seen
 // on all platforms.
 
@@ -15,72 +16,69 @@
 namespace angle
 {
 
-struct FrontendFeatures : angle::FeatureSetBase
+struct FrontendFeatures : FeatureSetBase
 {
     FrontendFeatures();
     ~FrontendFeatures();
 
-    // Force the context to be lost (via KHR_robustness) if a GL_OUT_OF_MEMORY error occurs. The
-    // driver may be in an inconsistent state if this happens, and some users of ANGLE rely on this
-    // notification to prevent further execution.
-    angle::Feature loseContextOnOutOfMemory = {
-        "lose_context_on_out_of_memory", angle::FeatureCategory::FrontendWorkarounds,
-        "Some users rely on a lost context notification if a GL_OUT_OF_MEMORY "
-        "error occurs",
-        &members};
+    Feature loseContextOnOutOfMemory = {
+        "lose_context_on_out_of_memory",
+        FeatureCategory::FrontendWorkarounds,
+        "Some users rely on a lost context notification if a GL_OUT_OF_MEMORY error occurs",
+        &members,
+    };
 
-    // Program binaries don't contain transform feedback varyings on Qualcomm GPUs.
-    // Work around this by disabling the program cache for programs with transform feedback.
-    angle::Feature disableProgramCachingForTransformFeedback = {
+    Feature disableProgramCachingForTransformFeedback = {
         "disable_program_caching_for_transform_feedback",
-        angle::FeatureCategory::FrontendWorkarounds,
-        "On some GPUs, program binaries don't contain transform feedback varyings", &members};
+        FeatureCategory::FrontendWorkarounds,
+        "On some GPUs, program binaries don't contain transform feedback varyings",
+        &members,
+    };
 
-    angle::Feature scalarizeVecAndMatConstructorArgs = {
-        "scalarize_vec_and_mat_constructor_args", angle::FeatureCategory::FrontendWorkarounds,
+    Feature scalarizeVecAndMatConstructorArgs = {
+        "scalarize_vec_and_mat_constructor_args", FeatureCategory::FrontendWorkarounds,
         "Always rewrite vec/mat constructors to be consistent", &members,
         "http://crbug.com/1165751"};
 
-    // Disable support for GL_OES_get_program_binary
-    angle::Feature disableProgramBinary = {
-        "disable_program_binary", angle::FeatureCategory::FrontendFeatures,
-        "Disable support for GL_OES_get_program_binary", &members, "http://anglebug.com/5007"};
+    Feature disableProgramBinary = {"disable_program_binary", FeatureCategory::FrontendFeatures,
+                                    "Disable support for GL_OES_get_program_binary", &members,
+                                    "http://anglebug.com/5007"};
 
-    // Allow disabling of GL_EXT_texture_filter_anisotropic through a runtime feature for
-    // performance comparisons.
-    angle::Feature disableAnisotropicFiltering = {
-        "disable_anisotropic_filtering", angle::FeatureCategory::FrontendWorkarounds,
-        "Disable support for anisotropic filtering", &members};
+    Feature disableAnisotropicFiltering = {
+        "disable_anisotropic_filtering",
+        FeatureCategory::FrontendWorkarounds,
+        "Disable support for anisotropic filtering",
+        &members,
+    };
 
-    // We can use this feature to override compressed format support for portability.
-    angle::Feature allowCompressedFormats = {"allow_compressed_formats",
-                                             angle::FeatureCategory::FrontendWorkarounds,
-                                             "Allow compressed formats", &members};
+    Feature allowCompressedFormats = {
+        "allow_compressed_formats",
+        FeatureCategory::FrontendWorkarounds,
+        "Allow compressed formats",
+        &members,
+    };
 
-    angle::Feature captureLimits = {"enable_capture_limits",
-                                    angle::FeatureCategory::FrontendFeatures,
-                                    "Set the context limits like frame capturing was enabled",
-                                    &members, "http://anglebug.com/5750"};
+    Feature captureLimits = {"enable_capture_limits", FeatureCategory::FrontendFeatures,
+                             "Set the context limits like frame capturing was enabled", &members,
+                             "http://anglebug.com/5750"};
 
-    // Whether we should compress pipeline cache in thread pool before it's stored in blob cache.
-    // http://anglebug.com/4722
-    angle::Feature enableCompressingPipelineCacheInThreadPool = {
-        "enableCompressingPipelineCacheInThreadPool", angle::FeatureCategory::FrontendWorkarounds,
+    Feature enableCompressingPipelineCacheInThreadPool = {
+        "enableCompressingPipelineCacheInThreadPool", FeatureCategory::FrontendWorkarounds,
         "Enable compressing pipeline cache in thread pool.", &members, "http://anglebug.com/4722"};
 
-    // Forces on robust resource init. Useful for some tests to avoid undefined values.
-    angle::Feature forceRobustResourceInit = {
-        "forceRobustResourceInit", angle::FeatureCategory::FrontendFeatures,
-        "Force-enable robust resource init", &members, "http://anglebug.com/6041"};
+    Feature forceRobustResourceInit = {"forceRobustResourceInit", FeatureCategory::FrontendFeatures,
+                                       "Force-enable robust resource init", &members,
+                                       "http://anglebug.com/6041"};
 
-    // Forces on shader variable init to avoid undefined values in tests. This feature is enabled
-    // for WebGL and frame capture, which both require deterministic results.
-    angle::Feature forceInitShaderVariables = {
-        "forceInitShaderVariables", angle::FeatureCategory::FrontendFeatures,
-        "Force-enable shader variable initialization", &members};
+    Feature forceInitShaderVariables = {
+        "forceInitShaderVariables",
+        FeatureCategory::FrontendFeatures,
+        "Force-enable shader variable initialization",
+        &members,
+    };
 
-    angle::Feature enableProgramBinaryForCapture = {
-        "enableProgramBinaryForCapture", angle::FeatureCategory::FrontendFeatures,
+    Feature enableProgramBinaryForCapture = {
+        "enableProgramBinaryForCapture", FeatureCategory::FrontendFeatures,
         "Even if FrameCapture is enabled, enable GL_OES_get_program_binary", &members,
         "http://anglebug.com/5658"};
 };
diff --git a/include/platform/d3d_features.json b/include/platform/d3d_features.json
new file mode 100644
index 0000000..4204434
--- /dev/null
+++ b/include/platform/d3d_features.json
@@ -0,0 +1,225 @@
+{
+    "description": [
+        "Copyright 2022 The ANGLE Project Authors. All rights reserved.",
+        "Use of this source code is governed by a BSD-style license that can be",
+        "found in the LICENSE file.",
+        "",
+        "d3d_features.json: Features and workarounds for D3D driver bugs and other issues."
+    ],
+    "features": [
+        {
+            "name": "mrtPerfWorkaround",
+            "display_name": "mrt_perf_workaround",
+            "category": "Workarounds",
+            "description": [
+                "Some drivers have a bug where they ignore null render targets"
+            ]
+        },
+
+        {
+            "name": "setDataFasterThanImageUpload",
+            "display_name": "set_data_faster_than_image_upload",
+            "category": "Workarounds",
+            "description": [
+                "Set data faster than image upload"
+            ]
+        },
+
+        {
+            "name": "zeroMaxLodWorkaround",
+            "display_name": "zero_max_lod",
+            "category": "Workarounds",
+            "description": [
+                "Missing an option to disable mipmaps on a mipmapped texture"
+            ]
+        },
+
+        {
+            "name": "useInstancedPointSpriteEmulation",
+            "display_name": "use_instanced_point_sprite_emulation",
+            "category": "Workarounds",
+            "description": [
+                "Some D3D11 renderers do not support geometry shaders for pointsprite emulation"
+            ]
+        },
+
+        {
+            "name": "depthStencilBlitExtraCopy",
+            "display_name": "depth_stencil_blit_extra_copy",
+            "category": "Workarounds",
+            "description": [
+                "Bug in some drivers triggers a TDR when using CopySubresourceRegion from a staging ",
+                "texture to a depth/stencil"
+            ],
+            "issue": "http://anglebug.com/1452"
+        },
+
+        {
+            "name": "expandIntegerPowExpressions",
+            "display_name": "expand_integer_pow_expressions",
+            "category": "Workarounds",
+            "description": [
+                "The HLSL optimizer has a bug with optimizing 'pow' in certain integer-valued expressions"
+            ]
+        },
+
+        {
+            "name": "flushAfterEndingTransformFeedback",
+            "display_name": "flush_after_ending_transform_feedback",
+            "category": "Workarounds",
+            "description": [
+                "Some drivers sometimes write out-of-order results to StreamOut buffers when transform ",
+                "feedback is used to repeatedly write to the same buffer positions"
+            ]
+        },
+
+        {
+            "name": "getDimensionsIgnoresBaseLevel",
+            "display_name": "get_dimensions_ignores_base_level",
+            "category": "Workarounds",
+            "description": [
+                "Some drivers do not take into account the base level of the ",
+                "texture in the results of the HLSL GetDimensions builtin"
+            ]
+        },
+
+        {
+            "name": "preAddTexelFetchOffsets",
+            "display_name": "pre_add_texel_fetch_offsets",
+            "category": "Workarounds",
+            "description": [
+                "HLSL's function texture.Load returns 0 when the parameter Location is negative, even if ",
+                "the sum of Offset and Location is in range"
+            ]
+        },
+
+        {
+            "name": "emulateTinyStencilTextures",
+            "display_name": "emulate_tiny_stencil_textures",
+            "category": "Workarounds",
+            "description": [
+                "1x1 and 2x2 mips of depth/stencil textures aren't sampled correctly"
+            ]
+        },
+
+        {
+            "name": "disableB5G6R5Support",
+            "display_name": "disable_b5g6r5_support",
+            "category": "Workarounds",
+            "description": [
+                "Textures with the format ",
+                "DXGI_FORMAT_B5G6R5_UNORM have incorrect data"
+            ]
+        },
+
+        {
+            "name": "rewriteUnaryMinusOperator",
+            "display_name": "rewrite_unary_minus_operator",
+            "category": "Workarounds",
+            "description": [
+                "Evaluating unary minus operator on integer may get wrong answer in vertex shaders"
+            ]
+        },
+
+        {
+            "name": "emulateIsnanFloat",
+            "display_name": "emulate_isnan_float",
+            "category": "Workarounds",
+            "description": [
+                "Using isnan() on highp float will get wrong answer"
+            ],
+            "issue": "https://crbug.com/650547"
+        },
+
+        {
+            "name": "callClearTwice",
+            "display_name": "call_clear_twice",
+            "category": "Workarounds",
+            "description": [
+                "Using clear() may not take effect"
+            ],
+            "issue": "https://crbug.com/655534"
+        },
+
+        {
+            "name": "useSystemMemoryForConstantBuffers",
+            "display_name": "use_system_memory_for_constant_buffers",
+            "category": "Workarounds",
+            "description": [
+                "Copying from staging storage to constant buffer ",
+                "storage does not work"
+            ],
+            "issue": "https://crbug.com/593024"
+        },
+
+        {
+            "name": "selectViewInGeometryShader",
+            "display_name": "select_view_in_geometry_shader",
+            "category": "Workarounds",
+            "description": [
+                "The viewport or render target slice will be selected in the geometry shader stage for ",
+                "the ANGLE_multiview extension"
+            ]
+        },
+
+        {
+            "name": "addMockTextureNoRenderTarget",
+            "display_name": "add_mock_texture_no_render_target",
+            "category": "Workarounds",
+            "description": [
+                "On some drivers when rendering with no render target, two bugs lead to incorrect behavior"
+            ],
+            "issue": "http://anglebug.com/2152"
+        },
+
+        {
+            "name": "skipVSConstantRegisterZero",
+            "display_name": "skip_vs_constant_register_zero",
+            "category": "Workarounds",
+            "description": [
+                "In specific cases the driver doesn't handle constant register zero correctly"
+            ]
+        },
+
+        {
+            "name": "forceAtomicValueResolution",
+            "display_name": "force_atomic_value_resolution",
+            "category": "Workarounds",
+            "description": [
+                "On some drivers the return value from RWByteAddressBuffer.InterlockedAdd does not resolve ",
+                "when used in the .yzw components of a RWByteAddressBuffer.Store operation"
+            ],
+            "issue": "http://anglebug.com/3246"
+        },
+
+        {
+            "name": "allowClearForRobustResourceInit",
+            "display_name": "allow_clear_for_robust_resource_init",
+            "category": "Workarounds",
+            "description": [
+                "Some drivers corrupt texture data when clearing for robust resource initialization."
+            ],
+            "issue": "http://crbug.com/941620"
+        },
+
+        {
+            "name": "allowTranslateUniformBlockToStructuredBuffer",
+            "display_name": "allow_translate_uniform_block_to_structured_buffer",
+            "category": "Workarounds",
+            "description": [
+                "There is a slow fxc compile performance issue with dynamic uniform indexing if ",
+                "translating a uniform block with a large array member to cbuffer."
+            ],
+            "issue": "http://anglebug.com/3682"
+        },
+
+        {
+            "name": "allowES3OnFL10_0",
+            "display_name": "allowES3OnFL10_0",
+            "category": "Workarounds",
+            "description": [
+                "Allow ES3 on 10.0 devices"
+            ]
+        }
+    ]
+}
diff --git a/include/platform/frontend_features.json b/include/platform/frontend_features.json
new file mode 100644
index 0000000..5f7ef32
--- /dev/null
+++ b/include/platform/frontend_features.json
@@ -0,0 +1,116 @@
+{
+    "description": [
+        "Copyright 2022 The ANGLE Project Authors. All rights reserved.",
+        "Use of this source code is governed by a BSD-style license that can be",
+        "found in the LICENSE file.",
+        "",
+        "frontend_features.json: Features/workarounds for driver bugs and other behaviors seen",
+        "on all platforms."
+    ],
+    "features": [
+        {
+            "name": "loseContextOnOutOfMemory",
+            "display_name": "lose_context_on_out_of_memory",
+            "category": "Workarounds",
+            "description": [
+                "Some users rely on a lost context notification if a GL_OUT_OF_MEMORY error occurs"
+            ]
+        },
+
+        {
+            "name": "disableProgramCachingForTransformFeedback",
+            "display_name": "disable_program_caching_for_transform_feedback",
+            "category": "Workarounds",
+            "description": [
+                "On some GPUs, program binaries don't contain transform feedback varyings"
+            ]
+        },
+
+        {
+            "name": "scalarizeVecAndMatConstructorArgs",
+            "display_name": "scalarize_vec_and_mat_constructor_args",
+            "category": "Workarounds",
+            "description": [
+                "Always rewrite vec/mat constructors to be consistent"
+            ],
+            "issue": "http://crbug.com/1165751"
+        },
+
+        {
+            "name": "disableProgramBinary",
+            "display_name": "disable_program_binary",
+            "category": "Features",
+            "description": [
+                "Disable support for GL_OES_get_program_binary"
+            ],
+            "issue": "http://anglebug.com/5007"
+        },
+
+        {
+            "name": "disableAnisotropicFiltering",
+            "display_name": "disable_anisotropic_filtering",
+            "category": "Workarounds",
+            "description": [
+                "Disable support for anisotropic filtering"
+            ]
+        },
+
+        {
+            "name": "allowCompressedFormats",
+            "display_name": "allow_compressed_formats",
+            "category": "Workarounds",
+            "description": [
+                "Allow compressed formats"
+            ]
+        },
+
+        {
+            "name": "captureLimits",
+            "display_name": "enable_capture_limits",
+            "category": "Features",
+            "description": [
+                "Set the context limits like frame capturing was enabled"
+            ],
+            "issue": "http://anglebug.com/5750"
+        },
+
+        {
+            "name": "enableCompressingPipelineCacheInThreadPool",
+            "display_name": "enableCompressingPipelineCacheInThreadPool",
+            "category": "Workarounds",
+            "description": [
+                "Enable compressing pipeline cache in thread pool."
+            ],
+            "issue": "http://anglebug.com/4722"
+        },
+
+        {
+            "name": "forceRobustResourceInit",
+            "display_name": "forceRobustResourceInit",
+            "category": "Features",
+            "description": [
+                "Force-enable robust resource init"
+            ],
+            "issue": "http://anglebug.com/6041"
+        },
+
+        {
+            "name": "forceInitShaderVariables",
+            "display_name": "forceInitShaderVariables",
+            "category": "Features",
+            "description": [
+                "Force-enable shader variable initialization"
+            ]
+        },
+
+        {
+            "name": "enableProgramBinaryForCapture",
+            "display_name": "enableProgramBinaryForCapture",
+            "category": "Features",
+            "description": [
+                "Even if FrameCapture is enabled, enable GL_OES_get_program_binary"
+            ],
+            "issue": "http://anglebug.com/5658"
+        }
+    ]
+}
diff --git a/include/platform/gen_features.py b/include/platform/gen_features.py
new file mode 100755
index 0000000..d9638c4
--- /dev/null
+++ b/include/platform/gen_features.py
@@ -0,0 +1,104 @@
+#! /usr/bin/python3
+
+# Copyright 2022 The ANGLE Project Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+#
+# gen_features.py:
+#  Code generation for ANGLE features.
+#  NOTE: don't run this script directly. Run scripts/run_code_generation.py.
+
+from collections import namedtuple
+import json
+import os
+import sys
+
+feature_files = {
+    'd3d_features.json': ('D3D', 'FeaturesD3D.h'),
+    'frontend_features.json': ('Frontend', 'FrontendFeatures.h'),
+    'gl_features.json': ('OpenGL', 'FeaturesGL.h'),
+    'mtl_features.json': ('Metal', 'FeaturesMtl.h'),
+    'vk_features.json': ('Vulkan', 'FeaturesVk.h'),
+}
+
+template_header = u"""// GENERATED FILE - DO NOT EDIT.
+// Generated by {script_name} using data from {input_file_name}.
+//
+{description}
+
+#ifndef ANGLE_PLATFORM_{NAME}_H_
+#define ANGLE_PLATFORM_{NAME}_H_
+
+#include "platform/Feature.h"
+
+namespace angle
+{{
+
+struct {name} : FeatureSetBase
+{{
+    {name}();
+    ~{name}();
+
+{features}
+}};
+
+inline {name}::{name}()  = default;
+inline {name}::~{name}() = default;
+
+}}  // namespace angle
+
+#endif  // ANGLE_PLATFORM_{NAME}_H_
+"""
+
+template_feature = u"""Feature {name} = {{
+     "{display_name}", FeatureCategory::{category},
+     {description},
+     &members, {issue}
+}};
+"""
+
+
+def main():
+    if len(sys.argv) == 2 and sys.argv[1] == 'inputs':
+        print(','.join(list(feature_files.keys())))
+        return
+    if len(sys.argv) == 2 and sys.argv[1] == 'outputs':
+        print(','.join([header for (_, header) in feature_files.values()]))
+        return
+
+    for src_file, (category_prefix, header_file) in feature_files.items():
+        with open(src_file) as fin:
+            src = json.loads(fin.read())
+
+        features_json = src['features']
+        features = []
+
+        for feature_json in features_json:
+            issue = feature_json.get('issue', None)
+            feature = template_feature.format(
+                name=feature_json['name'],
+                display_name=feature_json['display_name'],
+                category=category_prefix + feature_json['category'],
+                description='\n'.join('"' + line + '"' for line in feature_json['description']),
+                issue='' if issue is None else '"' + issue + '"')
+
+            features.append(feature)
+
+        description = '\n'.join(['// ' + line for line in src['description']])
+        name = header_file[:-2]
+
+        header = template_header.format(
+            script_name=os.path.basename(__file__),
+            input_file_name=src_file,
+            description=description.replace(src_file, header_file),
+            name=name,
+            NAME=name.upper(),
+            features='\n'.join(features))
+
+        with open(header_file, 'w') as fout:
+            fout.write(header)
+            fout.close()
+
+
+if __name__ == '__main__':
+    sys.exit(main())
diff --git a/include/platform/gl_features.json b/include/platform/gl_features.json
new file mode 100644
index 0000000..6f798b5
--- /dev/null
+++ b/include/platform/gl_features.json
@@ -0,0 +1,769 @@
+{
+    "description": [
+        "Copyright 2022 The ANGLE Project Authors. All rights reserved.",
+        "Use of this source code is governed by a BSD-style license that can be",
+        "found in the LICENSE file.",
+        "",
+        "gl_features.json: angle::Features and workarounds for GL driver bugs and other issues."
+    ],
+    "features": [
+        {
+            "name": "avoid1BitAlphaTextureFormats",
+            "display_name": "avoid_1_bit_alpha_texture_formats",
+            "category": "Workarounds",
+            "description": [
+                "Issue with 1-bit alpha framebuffer formats"
+            ]
+        },
+
+        {
+            "name": "rgba4IsNotSupportedForColorRendering",
+            "display_name": "rgba4_is_not_supported_for_color_rendering",
+            "category": "Workarounds",
+            "description": [
+                "GL_RGBA4 is not color renderable"
+            ]
+        },
+
+        {
+            "name": "allowEtcFormats",
+            "display_name": "allow_etc_formats",
+            "category": "Workarounds",
+            "description": [
+                "Enable ETC2/EAC on desktop OpenGL"
+            ]
+        },
+
+        {
+            "name": "doesSRGBClearsOnLinearFramebufferAttachments",
+            "display_name": "does_srgb_clears_on_linear_framebuffer_attachments",
+            "category": "Workarounds",
+            "description": [
+                "Issue clearing framebuffers with linear attachments when GL_FRAMEBUFFER_SRGB is enabled"
+            ]
+        },
+
+        {
+            "name": "doWhileGLSLCausesGPUHang",
+            "display_name": "do_while_glsl_causes_gpu_hang",
+            "category": "Workarounds",
+            "description": [
+                "Some GLSL constructs involving do-while loops cause GPU hangs"
+            ],
+            "issue": "http://crbug.com/644669"
+        },
+
+        {
+            "name": "addBaseVertexToVertexID",
+            "display_name": "vertex_id_does_not_include_base_vertex",
+            "category": "Workarounds",
+            "description": [
+                "gl_VertexID in GLSL vertex shader doesn't include base vertex value"
+            ]
+        },
+
+        {
+            "name": "finishDoesNotCauseQueriesToBeAvailable",
+            "display_name": "finish_does_not_cause_queries_to_be_available",
+            "category": "Workarounds",
+            "description": [
+                "glFinish doesn't cause all queries to report available result"
+            ]
+        },
+
+        {
+            "name": "alwaysCallUseProgramAfterLink",
+            "display_name": "always_call_use_program_after_link",
+            "category": "Workarounds",
+            "description": [
+                "Always call useProgram after a successful link to avoid a driver bug"
+            ],
+            "issue": "http://crbug.com/110263"
+        },
+
+        {
+            "name": "unpackOverlappingRowsSeparatelyUnpackBuffer",
+            "display_name": "unpack_overlapping_rows_separately_unpack_buffer",
+            "category": "Workarounds",
+            "description": [
+                "In the case of unpacking from a pixel unpack buffer, unpack overlapping rows row by row"
+            ]
+        },
+
+        {
+            "name": "packOverlappingRowsSeparatelyPackBuffer",
+            "display_name": "pack_overlapping_rows_separately_pack_buffer",
+            "category": "Workarounds",
+            "description": [
+                "In the case of packing to a pixel pack buffer, pack overlapping rows row by row"
+            ]
+        },
+
+        {
+            "name": "initializeCurrentVertexAttributes",
+            "display_name": "initialize_current_vertex_attributes",
+            "category": "Workarounds",
+            "description": [
+                "During initialization, assign the current vertex attributes to the spec-mandated defaults"
+            ]
+        },
+
+        {
+            "name": "emulateAbsIntFunction",
+            "display_name": "emulate_abs_int_function",
+            "category": "Workarounds",
+            "description": [
+                "abs(i) where i is an integer returns unexpected result"
+            ],
+            "issue": "http://crbug.com/642227"
+        },
+
+        {
+            "name": "addAndTrueToLoopCondition",
+            "display_name": "add_and_true_to_loop_condition",
+            "category": "Workarounds",
+            "description": [
+                "Calculation of loop conditions in for and while loop has bug"
+            ]
+        },
+
+        {
+            "name": "unpackLastRowSeparatelyForPaddingInclusion",
+            "display_name": "unpack_last_row_separately_for_padding_inclusion",
+            "category": "Workarounds",
+            "description": [
+                "When uploading textures from an unpack buffer, some drivers count an extra row padding"
+            ],
+            "issue": "http://anglebug.com/1512"
+        },
+
+        {
+            "name": "packLastRowSeparatelyForPaddingInclusion",
+            "display_name": "pack_last_row_separately_for_padding_inclusion",
+            "category": "Workarounds",
+            "description": [
+                "When uploading textures from an pack buffer, some drivers count an extra row padding"
+            ],
+            "issue": "http://anglebug.com/1512"
+        },
+
+        {
+            "name": "emulateIsnanFloat",
+            "display_name": "emulate_isnan_float",
+            "category": "Workarounds",
+            "description": [
+                "Using isnan() on highp float will get wrong answer"
+            ],
+            "issue": "http://crbug.com/650547"
+        },
+
+        {
+            "name": "useUnusedBlocksWithStandardOrSharedLayout",
+            "display_name": "use_unused_blocks_with_standard_or_shared_layout",
+            "category": "Workarounds",
+            "description": [
+                "Unused std140 or shared uniform blocks will be treated as inactive"
+            ]
+        },
+
+        {
+            "name": "removeInvariantAndCentroidForESSL3",
+            "display_name": "remove_invarient_and_centroid_for_essl3",
+            "category": "Workarounds",
+            "description": [
+                "Fix spec difference between GLSL 4.1 or lower and ESSL3"
+            ]
+        },
+
+        {
+            "name": "rewriteFloatUnaryMinusOperator",
+            "display_name": "rewrite_float_unary_minus_operator",
+            "category": "Workarounds",
+            "description": [
+                "Using '-<float>' will get wrong answer"
+            ],
+            "issue": "http://crbug.com/308366"
+        },
+
+        {
+            "name": "emulateAtan2Float",
+            "display_name": "emulate_atan_2_float",
+            "category": "Workarounds",
+            "description": [
+                "atan(y, x) may return a wrong answer"
+            ],
+            "issue": "http://crbug.com/672380"
+        },
+
+        {
+            "name": "reapplyUBOBindingsAfterUsingBinaryProgram",
+            "display_name": "reapply_ubo_bindings_after_using_binary_program",
+            "category": "Workarounds",
+            "description": [
+                "Some drivers forget about UBO bindings when using program binaries"
+            ],
+            "issue": "http://anglebug.com/1637"
+        },
+
+        {
+            "name": "emulateMaxVertexAttribStride",
+            "display_name": "emulate_max_vertex_attrib_stride",
+            "category": "Workarounds",
+            "description": [
+                "Some drivers return 0 when MAX_VERTEX_ATTRIB_STRIED queried"
+            ],
+            "issue": "http://anglebug.com/1936"
+        },
+
+        {
+            "name": "dontInitializeUninitializedLocals",
+            "display_name": "dont_initialize_uninitialized_locals",
+            "category": "Workarounds",
+            "description": [
+                "Initializing uninitialized locals caused odd behavior in a few WebGL 2 tests"
+            ],
+            "issue": "http://anglebug.com/2046"
+        },
+
+        {
+            "name": "clampPointSize",
+            "display_name": "clamp_point_size",
+            "category": "Workarounds",
+            "description": [
+                "The point size range reported from the API is inconsistent with the actual behavior"
+            ]
+        },
+
+        {
+            "name": "dontUseLoopsToInitializeVariables",
+            "display_name": "dont_use_loops_to_initialize_variables",
+            "category": "Workarounds",
+            "description": [
+                "For loops used to initialize variables hit native GLSL compiler bugs"
+            ],
+            "issue": "http://crbug.com/809422"
+        },
+
+        {
+            "name": "clampFragDepth",
+            "display_name": "clamp_frag_depth",
+            "category": "Workarounds",
+            "description": [
+                "gl_FragDepth is not clamped correctly when rendering to a floating point depth buffer"
+            ]
+        },
+
+        {
+            "name": "rewriteRepeatedAssignToSwizzled",
+            "display_name": "rewrite_repeated_assign_to_swizzled",
+            "category": "Workarounds",
+            "description": [
+                "Repeated assignment to swizzled values inside a ",
+                "GLSL user-defined function have incorrect results"
+            ]
+        },
+
+        {
+            "name": "disableBlendFuncExtended",
+            "display_name": "disable_blend_func_extended",
+            "category": "Workarounds",
+            "description": [
+                "ARB_blend_func_extended does not pass the tests"
+            ],
+            "issue": "http://anglebug.com/1085"
+        },
+
+        {
+            "name": "unsizedsRGBReadPixelsDoesntTransform",
+            "display_name": "unsized_srgb_read_pixels_doesnt_transform",
+            "category": "Workarounds",
+            "description": [
+                "Drivers returning raw sRGB values instead of linearized values when calling glReadPixels ",
+                "on unsized sRGB texture formats"
+            ],
+            "issue": "http://crbug.com/550292 http://crbug.com/565179"
+        },
+
+        {
+            "name": "queryCounterBitsGeneratesErrors",
+            "display_name": "query_counter_bits_generates_errors",
+            "category": "Workarounds",
+            "description": [
+                "Drivers generate errors when querying the number of bits in timer queries"
+            ],
+            "issue": "http://anglebug.com/3027"
+        },
+
+        {
+            "name": "dontRelinkProgramsInParallel",
+            "display_name": "dont_relink_programs_in_parallel",
+            "category": "Workarounds",
+            "description": [
+                "Relinking a program in parallel is buggy"
+            ],
+            "issue": "http://anglebug.com/3045"
+        },
+
+        {
+            "name": "disableWorkerContexts",
+            "display_name": "disable_worker_contexts",
+            "category": "Workarounds",
+            "description": [
+                "Some tests have been seen to fail using worker contexts"
+            ],
+            "issue": "http://crbug.com/849576"
+        },
+
+        {
+            "name": "limitMaxTextureSizeTo4096",
+            "display_name": "max_texture_size_limit_4096",
+            "category": "Workarounds",
+            "description": [
+                "Limit max texture size to 4096 to avoid frequent ",
+                "out-of-memory errors"
+            ],
+            "issue": "http://crbug.com/927470"
+        },
+
+        {
+            "name": "limitMaxMSAASamplesTo4",
+            "display_name": "max_msaa_sample_count_4",
+            "category": "Workarounds",
+            "description": [
+                "Various rendering bugs have been observed when using higher MSAA counts"
+            ],
+            "issue": "http://crbug.com/797243"
+        },
+
+        {
+            "name": "allowClearForRobustResourceInit",
+            "display_name": "allow_clear_for_robust_resource_init",
+            "category": "Workarounds",
+            "description": [
+                "Using glClear for robust resource initialization is buggy on some drivers and leads to ",
+                "texture corruption. Default to data uploads except on MacOS where it is very slow."
+            ],
+            "issue": "https://crbug.com/848952 http://crbug.com/883276"
+        },
+
+        {
+            "name": "clampArrayAccess",
+            "display_name": "clamp_array_access",
+            "category": "Workarounds",
+            "description": [
+                "Clamp uniform array access to avoid reading invalid memory."
+            ],
+            "issue": "http://anglebug.com/2978"
+        },
+
+        {
+            "name": "resetTexImage2DBaseLevel",
+            "display_name": "reset_teximage2d_base_level",
+            "category": "Workarounds",
+            "description": [
+                "Reset texture base level before calling glTexImage2D to ",
+                "work around pixel comparison failure."
+            ],
+            "issue": "https://crbug.com/705865"
+        },
+
+        {
+            "name": "clearToZeroOrOneBroken",
+            "display_name": "clear_to_zero_or_one_broken",
+            "category": "Workarounds",
+            "description": [
+                "Clears when the clear color is all zeros or ones do not work."
+            ],
+            "issue": "https://crbug.com/710443"
+        },
+
+        {
+            "name": "limitMax3dArrayTextureSizeTo1024",
+            "display_name": "max_3d_array_texture_size_1024",
+            "category": "Workarounds",
+            "description": [
+                "Limit max 3d texture size and max array texture layers to 1024 to avoid system hang"
+            ],
+            "issue": "http://crbug.com/927470"
+        },
+
+        {
+            "name": "adjustSrcDstRegionBlitFramebuffer",
+            "display_name": "adjust_src_dst_region_for_blitframebuffer",
+            "category": "Workarounds",
+            "description": [
+                "Many platforms have issues with blitFramebuffer when the parameters are large."
+            ],
+            "issue": "http://crbug.com/830046"
+        },
+
+        {
+            "name": "clipSrcRegionBlitFramebuffer",
+            "display_name": "clip_src_region_for_blitframebuffer",
+            "category": "Workarounds",
+            "description": [
+                "Issues with blitFramebuffer when the parameters don't match the framebuffer size."
+            ],
+            "issue": "http://crbug.com/830046"
+        },
+
+        {
+            "name": "rgbDXT1TexturesSampleZeroAlpha",
+            "display_name": "rgb_dxt1_textures_sample_zero_alpha",
+            "category": "Workarounds",
+            "description": [
+                "Sampling BLACK texels from RGB DXT1 textures returns transparent black on Mac."
+            ],
+            "issue": "http://anglebug.com/3729"
+        },
+
+        {
+            "name": "unfoldShortCircuits",
+            "display_name": "unfold_short_circuits",
+            "category": "Workarounds",
+            "description": [
+                "Mac incorrectly executes both sides of && and || expressions when they should ",
+                "short-circuit."
+            ],
+            "issue": "http://anglebug.com/482"
+        },
+
+        {
+            "name": "emulatePrimitiveRestartFixedIndex",
+            "display_name": "emulate_primitive_restart_fixed_index",
+            "category": "Workarounds",
+            "description": [
+                "When GL_PRIMITIVE_RESTART_FIXED_INDEX is not available, emulate it with ",
+                "GL_PRIMITIVE_RESTART and glPrimitiveRestartIndex."
+            ],
+            "issue": "http://anglebug.com/3997"
+        },
+
+        {
+            "name": "setPrimitiveRestartFixedIndexForDrawArrays",
+            "display_name": "set_primitive_restart_fixed_index_for_draw_arrays",
+            "category": "Workarounds",
+            "description": [
+                "Some drivers discard vertex data in DrawArrays calls when the fixed primitive restart ",
+                "index is within the number of primitives being drawn."
+            ],
+            "issue": "http://anglebug.com/3997"
+        },
+
+        {
+            "name": "removeDynamicIndexingOfSwizzledVector",
+            "display_name": "remove_dynamic_indexing_of_swizzled_vector",
+            "category": "Workarounds",
+            "description": [
+                "Dynamic indexing of swizzled l-values doesn't work correctly on various platforms."
+            ],
+            "issue": "http://crbug.com/709351"
+        },
+
+        {
+            "name": "preAddTexelFetchOffsets",
+            "display_name": "pre_add_texel_fetch_offsets",
+            "category": "Workarounds",
+            "description": [
+                "Intel Mac drivers mistakenly consider the parameter position of nagative vaule as invalid ",
+                "even if the sum of position and offset is in range, so we need to add workarounds by ",
+                "rewriting texelFetchOffset(sampler, position, lod, offset) into texelFetch(sampler, ",
+                "position + offset, lod)."
+            ],
+            "issue": "http://crbug.com/642605"
+        },
+
+        {
+            "name": "regenerateStructNames",
+            "display_name": "regenerate_struct_names",
+            "category": "Workarounds",
+            "description": [
+                "All Mac drivers do not handle struct scopes correctly. This workaround overwrites a struct",
+                "name with a unique prefix."
+            ],
+            "issue": "http://crbug.com/403957"
+        },
+
+        {
+            "name": "readPixelsUsingImplementationColorReadFormatForNorm16",
+            "display_name": "read_pixels_using_implementation_color_read_format",
+            "category": "Workarounds",
+            "description": [
+                "Quite some OpenGL ES drivers don't implement readPixels for RGBA/UNSIGNED_SHORT from ",
+                "EXT_texture_norm16 correctly"
+            ],
+            "issue": "http://anglebug.com/4214"
+        },
+
+        {
+            "name": "flushBeforeDeleteTextureIfCopiedTo",
+            "display_name": "flush_before_delete_texture_if_copied_to",
+            "category": "Workarounds",
+            "description": [
+                "Some drivers track CopyTex{Sub}Image texture dependencies incorrectly. Flush",
+                " before glDeleteTextures in this case"
+            ],
+            "issue": "http://anglebug.com/4267"
+        },
+
+        {
+            "name": "rewriteRowMajorMatrices",
+            "display_name": "rewrite_row_major_matrices",
+            "category": "Workarounds",
+            "description": [
+                "Rewrite row major matrices in shaders as column major as a driver bug workaround"
+            ],
+            "issue": "http://anglebug.com/2273"
+        },
+
+        {
+            "name": "disableDrawBuffersIndexed",
+            "display_name": "disable_draw_buffers_indexed",
+            "category": "Workarounds",
+            "description": [
+                "Disable OES_draw_buffers_indexed extension."
+            ]
+        },
+
+        {
+            "name": "disableSemaphoreFd",
+            "display_name": "disable_semaphore_fd",
+            "category": "Workarounds",
+            "description": [
+                "Disable GL_EXT_semaphore_fd extension"
+            ],
+            "issue": "https://crbug.com/1046462"
+        },
+
+        {
+            "name": "disableTimestampQueries",
+            "display_name": "disable_timestamp_queries",
+            "category": "Workarounds",
+            "description": [
+                "Disable GL_EXT_disjoint_timer_query extension"
+            ],
+            "issue": "https://crbug.com/811661"
+        },
+
+        {
+            "name": "encodeAndDecodeSRGBForGenerateMipmap",
+            "display_name": "decode_encode_srgb_for_generatemipmap",
+            "category": "Workarounds",
+            "description": [
+                "Decode and encode before generateMipmap for srgb format textures."
+            ],
+            "issue": "http://anglebug.com/4646"
+        },
+
+        {
+            "name": "emulateCopyTexImage2DFromRenderbuffers",
+            "display_name": "emulate_copyteximage2d_from_renderbuffers",
+            "category": "Workarounds",
+            "description": [
+                "CopyTexImage2D spuriously returns errors on iOS when copying from renderbuffers."
+            ],
+            "issue": "https://anglebug.com/4674"
+        },
+
+        {
+            "name": "disableGPUSwitchingSupport",
+            "display_name": "disable_gpu_switching_support",
+            "category": "Workarounds",
+            "description": [
+                "Disable GPU switching support (use only the low-power GPU) on older MacBook Pros."
+            ],
+            "issue": "https://crbug.com/1091824"
+        },
+
+        {
+            "name": "disableNativeParallelCompile",
+            "display_name": "disable_native_parallel_compile",
+            "category": "Workarounds",
+            "description": [
+                "Do not use native KHR_parallel_shader_compile even when available."
+            ],
+            "issue": "http://crbug.com/1094869"
+        },
+
+        {
+            "name": "emulatePackSkipRowsAndPackSkipPixels",
+            "display_name": "emulate_pack_skip_rows_and_pack_skip_pixels",
+            "category": "Workarounds",
+            "description": [
+                "GL_PACK_SKIP_ROWS and GL_PACK_SKIP_PIXELS are ignored in Apple's OpenGL driver."
+            ],
+            "issue": "https://anglebug.com/4849"
+        },
+
+        {
+            "name": "clampMscRate",
+            "display_name": "clamp_msc_rate",
+            "category": "Workarounds",
+            "description": [
+                "Some drivers return bogus values for GetMscRate, so we clamp it to 30Hz"
+            ],
+            "issue": "https://crbug.com/1042393"
+        },
+
+        {
+            "name": "bindTransformFeedbackBufferBeforeBindBufferRange",
+            "display_name": "bind_transform_feedback_buffer_before_bind_buffer_range",
+            "category": "Workarounds",
+            "description": [
+                "Bind transform feedback buffers to the generic binding point before calling ",
+                "glBindBufferBase or glBindBufferRange."
+            ],
+            "issue": "https://anglebug.com/5140"
+        },
+
+        {
+            "name": "disableSyncControlSupport",
+            "display_name": "disable_sync_control_support",
+            "category": "Workarounds",
+            "description": [
+                "Speculative fix for issues on Linux/Wayland where exposing GLX_OML_sync_control renders ",
+                "Chrome unusable"
+            ],
+            "issue": "https://crbug.com/1137851"
+        },
+
+        {
+            "name": "keepBufferShadowCopy",
+            "display_name": "keep_buffer_shadow_copy",
+            "category": "Workarounds",
+            "description": [
+                "Maintain a shadow copy of buffer data when the GL API does not permit reading data back."
+            ]
+        },
+
+        {
+            "name": "setZeroLevelBeforeGenerateMipmap",
+            "display_name": "set_zero_level_before_generating_mipmap",
+            "category": "Workarounds",
+            "description": [
+                "glGenerateMipmap fails if the zero texture level is not set on some Mac drivers."
+            ]
+        },
+
+        {
+            "name": "promotePackedFormatsTo8BitPerChannel",
+            "display_name": "promote_packed_formats_to_8_bit_per_channel",
+            "category": "Workarounds",
+            "description": [
+                "Packed color formats are buggy on Macs with AMD GPUs"
+            ],
+            "issue": "http://anglebug.com/5469"
+        },
+
+        {
+            "name": "initFragmentOutputVariables",
+            "display_name": "init_fragment_output_variables",
+            "category": "Workarounds",
+            "description": [
+                "No init gl_FragColor causes context lost"
+            ],
+            "issue": "http://crbug.com/1171371"
+        },
+
+        {
+            "name": "shiftInstancedArrayDataWithExtraOffset",
+            "display_name": "shift_instanced_array_data_with_offset",
+            "category": "Workarounds",
+            "description": [
+                "glDrawArraysInstanced is buggy on certain new Mac Intel GPUs"
+            ],
+            "issue": "http://crbug.com/1144207"
+        },
+
+        {
+            "name": "syncVertexArraysToDefault",
+            "display_name": "sync_vertex_arrays_to_default",
+            "category": "Workarounds",
+            "description": [
+                "Only use the default VAO because of missing support or driver bugs"
+            ],
+            "issue": "http://anglebug.com/5577"
+        },
+
+        {
+            "name": "sanitizeAmdGpuRendererString",
+            "display_name": "sanitize_amdgpu_renderer_string",
+            "category": "Workarounds",
+            "description": [
+                "Strip precise kernel and DRM version information from amdgpu renderer strings."
+            ],
+            "issue": "http://crbug.com/1181193"
+        },
+
+        {
+            "name": "unbindFBOOnContextSwitch",
+            "display_name": "unbind_fbo_before_switching_context",
+            "category": "Workarounds",
+            "description": [
+                "Imagination GL drivers are buggy with context switching."
+            ],
+            "issue": "http://crbug.com/1181193"
+        },
+
+        {
+            "name": "flushOnFramebufferChange",
+            "display_name": "flush_on_framebuffer_change",
+            "category": "Workarounds",
+            "description": [
+                "Switching framebuffers without a flush can lead to ",
+                "crashes on Intel 9th Generation GPU Macs."
+            ],
+            "issue": "http://crbug.com/1181068"
+        },
+
+        {
+            "name": "disableMultisampledRenderToTexture",
+            "display_name": "disable_mutlisampled_render_to_texture",
+            "category": "Workarounds",
+            "description": [
+                "Many drivers have bugs when using GL_EXT_multisampled_render_to_texture"
+            ],
+            "issue": "http://anglebug.com/2894"
+        },
+
+        {
+            "name": "uploadTextureDataInChunks",
+            "display_name": "chunked_texture_upload",
+            "category": "Workarounds",
+            "description": [
+                "Upload texture data in <120kb chunks to work around Mac driver hangs and crashes."
+            ],
+            "issue": "http://crbug.com/1181068"
+        },
+
+        {
+            "name": "emulateImmutableCompressedTexture3D",
+            "display_name": "emulate_immutable_compressed_texture_3d",
+            "category": "Workarounds",
+            "description": [
+                "Use non-immutable texture allocation to work around a driver bug."
+            ],
+            "issue": "https://crbug.com/1060012"
+        },
+
+        {
+            "name": "emulateRGB10",
+            "display_name": "emulate_rgb10",
+            "category": "Workarounds",
+            "description": [
+                "Emulate RGB10 support using RGB10_A2."
+            ],
+            "issue": "https://crbug.com/1300575"
+        },
+
+        {
+            "name": "alwaysUnbindFramebufferTexture2D",
+            "display_name": "always_unbind_framebuffer_texture_2d",
+            "category": "Workarounds",
+            "description": [
+                "Force unbind framebufferTexture2D before binding renderbuffer to work around driver bug."
+            ],
+            "issue": "https://anglebug.com/5536"
+        }
+    ]
+}
diff --git a/include/platform/mtl_features.json b/include/platform/mtl_features.json
new file mode 100644
index 0000000..f902945
--- /dev/null
+++ b/include/platform/mtl_features.json
@@ -0,0 +1,250 @@
+{
+    "description": [
+        "Copyright 2022 The ANGLE Project Authors. All rights reserved.",
+        "Use of this source code is governed by a BSD-style license that can be",
+        "found in the LICENSE file.",
+        "",
+        "mtl_features.json: Optional features for the Metal renderer."
+    ],
+    "features": [
+        {
+            "name": "hasBaseVertexInstancedDraw",
+            "display_name": "has_base_vertex_instanced_draw",
+            "category": "Features",
+            "description": [
+                "The renderer supports base vertex instanced draw"
+            ]
+        },
+
+        {
+            "name": "hasExplicitMemBarrier",
+            "display_name": "has_explicit_mem_barrier_mtl",
+            "category": "Features",
+            "description": [
+                "The renderer supports explicit memory barrier"
+            ]
+        },
+
+        {
+            "name": "hasCheapRenderPass",
+            "display_name": "has_cheap_render_pass_mtl",
+            "category": "Features",
+            "description": [
+                "The renderer can cheaply break a render pass."
+            ]
+        },
+
+        {
+            "name": "hasNonUniformDispatch",
+            "display_name": "has_non_uniform_dispatch",
+            "category": "Features",
+            "description": [
+                "The renderer supports non uniform compute shader dispatch's group size"
+            ]
+        },
+
+        {
+            "name": "hasStencilOutput",
+            "display_name": "has_shader_stencil_output",
+            "category": "Features",
+            "description": [
+                "The renderer supports stencil output from fragment shader"
+            ]
+        },
+
+        {
+            "name": "hasTextureSwizzle",
+            "display_name": "has_texture_swizzle",
+            "category": "Features",
+            "description": [
+                "The renderer supports texture swizzle"
+            ]
+        },
+
+        {
+            "name": "hasDepthAutoResolve",
+            "display_name": "has_msaa_depth_auto_resolve",
+            "category": "Features",
+            "description": [
+                "The renderer supports MSAA depth auto resolve at the end of render pass"
+            ]
+        },
+
+        {
+            "name": "hasStencilAutoResolve",
+            "display_name": "has_msaa_stencil_auto_resolve",
+            "category": "Features",
+            "description": [
+                "The renderer supports MSAA stencil auto resolve at the end of render pass"
+            ]
+        },
+
+        {
+            "name": "hasEvents",
+            "display_name": "has_mtl_events",
+            "category": "Features",
+            "description": [
+                "The renderer supports MTL(Shared)Event"
+            ]
+        },
+
+        {
+            "name": "allowInlineConstVertexData",
+            "display_name": "allow_inline_const_vertex_data",
+            "category": "Features",
+            "description": [
+                "The renderer supports using inline constant data for small client vertex data"
+            ]
+        },
+
+        {
+            "name": "allowSeparatedDepthStencilBuffers",
+            "display_name": "allow_separate_depth_stencil_buffers",
+            "category": "Features",
+            "description": [
+                "Some Apple platforms such as iOS allows separate depth and stencil buffers, ",
+                "whereas others such as macOS don't"
+            ]
+        },
+
+        {
+            "name": "allowRuntimeSamplerCompareMode",
+            "display_name": "allow_runtime_sampler_compare_mode",
+            "category": "Features",
+            "description": [
+                "The renderer supports changing sampler's compare mode outside shaders"
+            ]
+        },
+
+        {
+            "name": "allowSamplerCompareGradient",
+            "display_name": "allow_sampler_compare_gradient",
+            "category": "Features",
+            "description": [
+                "The renderer supports sample_compare with gradients"
+            ]
+        },
+
+        {
+            "name": "allowSamplerCompareLod",
+            "display_name": "allow_sampler_compare_lod",
+            "category": "Features",
+            "description": [
+                "The renderer supports sample_compare with lod"
+            ]
+        },
+
+        {
+            "name": "allowBufferReadWrite",
+            "display_name": "allow_buffer_read_write",
+            "category": "Features",
+            "description": [
+                "The renderer supports buffer read and write in the same shader"
+            ]
+        },
+
+        {
+            "name": "allowMultisampleStoreAndResolve",
+            "display_name": "allow_msaa_store_and_resolve",
+            "category": "Features",
+            "description": [
+                "The renderer supports MSAA store and resolve in the same pass"
+            ]
+        },
+
+        {
+            "name": "allowGenMultipleMipsPerPass",
+            "display_name": "gen_multiple_mips_per_pass",
+            "category": "Features",
+            "description": [
+                "The renderer supports generating multiple mipmaps per pass"
+            ]
+        },
+
+        {
+            "name": "forceD24S8AsUnsupported",
+            "display_name": "force_d24s8_as_unsupported",
+            "category": "Features",
+            "description": [
+                "Force Depth24Stencil8 format as unsupported."
+            ]
+        },
+
+        {
+            "name": "forceBufferGPUStorage",
+            "display_name": "force_buffer_gpu_storage",
+            "category": "Features",
+            "description": [
+                "On systems that support both buffer' memory allocation on GPU and shared memory (such as ",
+                    "macOS), force using GPU memory allocation for buffers everytime or not."
+            ]
+        },
+
+        {
+            "name": "directMetalGeneration",
+            "display_name": "directMetalGeneration",
+            "category": "Features",
+            "description": [
+                "Direct translation to Metal."
+            ],
+            "issue": "http://anglebug.com/5505"
+        },
+
+        {
+            "name": "forceNonCSBaseMipmapGeneration",
+            "display_name": "force_non_cs_mipmap_gen",
+            "category": "Features",
+            "description": [
+                "Turn this feature on to disallow Compute Shader based mipmap generation. Compute Shader ",
+                "based mipmap generation might cause GPU hang on some older iOS devices."
+            ]
+        },
+
+        {
+            "name": "emulateTransformFeedback",
+            "display_name": "emulate_transform_feedback",
+            "category": "Features",
+            "description": [
+                "Turn this on to allow transform feedback in Metal using a 2-pass VS for GLES3."
+            ]
+        },
+
+        {
+            "name": "rewriteRowMajorMatrices",
+            "display_name": "rewrite_row_major_matrices",
+            "category": "Features",
+            "description": [
+                "Rewrite row major matrices in shaders as column major."
+            ]
+        },
+
+        {
+            "name": "intelExplicitBoolCastWorkaround",
+            "display_name": "intel_explicit_bool_cast_workaround",
+            "category": "Workarounds",
+            "description": [
+                "Insert explicit casts for float/double/unsigned/signed int on macOS 10.15 with Intel ",
+                "driver"
+            ]
+        },
+
+        {
+            "name": "intelDisableFastMath",
+            "display_name": "intel_disable_fast_math",
+            "category": "Workarounds",
+            "description": [
+                "Disable fast math in atan and invariance cases when running below macOS 12.0"
+            ]
+        },
+
+        {
+            "name": "multisampleColorFormatShaderReadWorkaround",
+            "display_name": "multisample_color_format_shader_read_workaround",
+            "category": "Workarounds",
+            "description": [
+                "Add shaderRead usage to some multisampled texture formats"
+            ],
+            "issue": "http://anglebug.com/7049"
+        }
+    ]
+}
diff --git a/include/platform/vk_features.json b/include/platform/vk_features.json
new file mode 100644
index 0000000..0e99fa2
--- /dev/null
+++ b/include/platform/vk_features.json
@@ -0,0 +1,982 @@
+{
+    "description": [
+        "Copyright 2022 The ANGLE Project Authors. All rights reserved.",
+        "Use of this source code is governed by a BSD-style license that can be",
+        "found in the LICENSE file.",
+        "",
+        "vk_features.json: Optional features for the Vulkan renderer."
+    ],
+    "features": [
+        {
+            "name": "basicGLLineRasterization",
+            "display_name": "basicGLLineRasterization",
+            "category": "Features",
+            "description": [
+                "Enable the use of pixel shader patching to implement OpenGL basic line ",
+                "rasterization rules"
+            ]
+        },
+
+        {
+            "name": "bresenhamLineRasterization",
+            "display_name": "bresenhamLineRasterization",
+            "category": "Features",
+            "description": [
+                "Enable Bresenham line rasterization via VK_EXT_line_rasterization extension"
+            ]
+        },
+
+        {
+            "name": "provokingVertex",
+            "display_name": "provokingVertex",
+            "category": "Features",
+            "description": [
+                "Enable provoking vertex mode via VK_EXT_provoking_vertex extension"
+            ]
+        },
+
+        {
+            "name": "forceFallbackFormat",
+            "display_name": "forceFallbackFormat",
+            "category": "Workarounds",
+            "description": [
+                "Force a fallback format for angle_end2end_tests"
+            ]
+        },
+
+        {
+            "name": "clampPointSize",
+            "display_name": "clampPointSize",
+            "category": "Workarounds",
+            "description": [
+                "The point size range reported from the API is inconsistent with the actual behavior"
+            ],
+            "issue": "http://anglebug.com/2970"
+        },
+
+        {
+            "name": "depthClamping",
+            "display_name": "depth_clamping",
+            "category": "Workarounds",
+            "description": [
+                "The depth value is not clamped to [0,1] for floating point depth buffers."
+            ],
+            "issue": "http://anglebug.com/3970"
+        },
+
+        {
+            "name": "supportsRenderpass2",
+            "display_name": "supportsRenderpass2",
+            "category": "Features",
+            "description": [
+                "VkDevice supports the VK_KHR_create_renderpass2 extension"
+            ]
+        },
+
+        {
+            "name": "supportsIncrementalPresent",
+            "display_name": "supportsIncrementalPresent",
+            "category": "Features",
+            "description": [
+                "VkDevice supports the VK_KHR_incremental_present extension"
+            ]
+        },
+
+        {
+            "name": "supportsAndroidHardwareBuffer",
+            "display_name": "supportsAndroidHardwareBuffer",
+            "category": "Features",
+            "description": [
+                "VkDevice supports the VK_ANDROID_external_memory_android_hardware_buffer extension"
+            ]
+        },
+
+        {
+            "name": "supportsGGPFrameToken",
+            "display_name": "supportsGGPFrameToken",
+            "category": "Features",
+            "description": [
+                "VkDevice supports the VK_GGP_frame_token extension"
+            ]
+        },
+
+        {
+            "name": "supportsExternalMemoryFd",
+            "display_name": "supportsExternalMemoryFd",
+            "category": "Features",
+            "description": [
+                "VkDevice supports the VK_KHR_external_memory_fd extension"
+            ]
+        },
+
+        {
+            "name": "supportsExternalMemoryFuchsia",
+            "display_name": "supportsExternalMemoryFuchsia",
+            "category": "Features",
+            "description": [
+                "VkDevice supports the VK_FUCHSIA_external_memory extension"
+            ]
+        },
+
+        {
+            "name": "supportsFilteringPrecision",
+            "display_name": "supportsFilteringPrecision",
+            "category": "Features",
+            "description": [
+                "VkDevice supports the VK_GOOGLE_sampler_filtering_precision extension"
+            ]
+        },
+
+        {
+            "name": "supportsExternalFenceCapabilities",
+            "display_name": "supportsExternalFenceCapabilities",
+            "category": "Features",
+            "description": [
+                "VkInstance supports the VK_KHR_external_fence_capabilities extension"
+            ]
+        },
+
+        {
+            "name": "supportsExternalSemaphoreCapabilities",
+            "display_name": "supportsExternalSemaphoreCapabilities",
+            "category": "Features",
+            "description": [
+                "VkInstance supports the VK_KHR_external_semaphore_capabilities extension"
+            ]
+        },
+
+        {
+            "name": "supportsExternalSemaphoreFd",
+            "display_name": "supportsExternalSemaphoreFd",
+            "category": "Features",
+            "description": [
+                "VkDevice supports the VK_KHR_external_semaphore_fd extension"
+            ]
+        },
+
+        {
+            "name": "supportsExternalSemaphoreFuchsia",
+            "display_name": "supportsExternalSemaphoreFuchsia",
+            "category": "Features",
+            "description": [
+                "VkDevice supports the VK_FUCHSIA_external_semaphore extension"
+            ]
+        },
+
+        {
+            "name": "supportsExternalFenceFd",
+            "display_name": "supportsExternalFenceFd",
+            "category": "Features",
+            "description": [
+                "VkDevice supports the VK_KHR_external_fence_fd extension"
+            ],
+            "issue": "http://anglebug.com/2517"
+        },
+
+        {
+            "name": "supportsAndroidNativeFenceSync",
+            "display_name": "supportsAndroidNativeFenceSync",
+            "category": "Features",
+            "description": [
+                "VkDevice supports the EGL_ANDROID_native_fence_sync extension"
+            ],
+            "issue": "http://anglebug.com/2517"
+        },
+
+        {
+            "name": "supportsImageCubeArray",
+            "display_name": "supportsImageCubeArray",
+            "category": "Features",
+            "description": [
+                "VkDevice supports the imageCubeArray feature properly"
+            ],
+            "issue": "http://anglebug.com/3584"
+        },
+
+        {
+            "name": "supportsPipelineStatisticsQuery",
+            "display_name": "supportsPipelineStatisticsQuery",
+            "category": "Features",
+            "description": [
+                "VkDevice supports the pipelineStatisticsQuery feature"
+            ],
+            "issue": "http://anglebug.com/5430"
+        },
+
+        {
+            "name": "supportsShaderStencilExport",
+            "display_name": "supportsShaderStencilExport",
+            "category": "Features",
+            "description": [
+                "VkDevice supports the VK_EXT_shader_stencil_export extension"
+            ]
+        },
+
+        {
+            "name": "supportsYUVSamplerConversion",
+            "display_name": "supportsYUVSamplerConversion",
+            "category": "Features",
+            "description": [
+                "VkDevice supports the VK_KHR_sampler_ycbcr_conversion extension"
+            ]
+        },
+
+        {
+            "name": "emulateTransformFeedback",
+            "display_name": "emulateTransformFeedback",
+            "category": "Features",
+            "description": [
+                "Emulate transform feedback as the VK_EXT_transform_feedback is not present."
+            ],
+            "issue": "http://anglebug.com/3205"
+        },
+
+        {
+            "name": "supportsTransformFeedbackExtension",
+            "display_name": "supportsTransformFeedbackExtension",
+            "category": "Features",
+            "description": [
+                "Transform feedback uses the VK_EXT_transform_feedback extension."
+            ],
+            "issue": "http://anglebug.com/3206"
+        },
+
+        {
+            "name": "supportsGeometryStreamsCapability",
+            "display_name": "supportsGeometryStreamsCapability",
+            "category": "Features",
+            "description": [
+                "Implementation supports the GeometryStreams SPIR-V capability."
+            ],
+            "issue": "http://anglebug.com/3206"
+        },
+
+        {
+            "name": "supportsIndexTypeUint8",
+            "display_name": "supportsIndexTypeUint8",
+            "category": "Features",
+            "description": [
+                "VkDevice supports the VK_EXT_index_type_uint8 extension"
+            ],
+            "issue": "http://anglebug.com/4405"
+        },
+
+        {
+            "name": "supportsCustomBorderColor",
+            "display_name": "supportsCustomBorderColor",
+            "category": "Features",
+            "description": [
+                "VkDevice supports the VK_EXT_custom_border_color extension"
+            ],
+            "issue": "http://anglebug.com/3577"
+        },
+
+        {
+            "name": "supportsMultiDrawIndirect",
+            "display_name": "supportsMultiDrawIndirect",
+            "category": "Features",
+            "description": [
+                "VkDevice supports the multiDrawIndirect extension"
+            ],
+            "issue": "http://anglebug.com/6439"
+        },
+
+        {
+            "name": "supportsDepthStencilResolve",
+            "display_name": "supportsDepthStencilResolve",
+            "category": "Features",
+            "description": [
+                "VkDevice supports the VK_KHR_depth_stencil_resolve ",
+                "extension with the independentResolveNone feature"
+            ],
+            "issue": "http://anglebug.com/4836"
+        },
+
+        {
+            "name": "supportsMultisampledRenderToSingleSampled",
+            "display_name": "supportsMultisampledRenderToSingleSampled",
+            "category": "Features",
+            "description": [
+                "VkDevice supports the VK_EXT_multisampled_render_to_single_sampled extension"
+            ],
+            "issue": "http://anglebug.com/4836"
+        },
+
+        {
+            "name": "supportsMultiview",
+            "display_name": "supportsMultiview",
+            "category": "Features",
+            "description": [
+                "VkDevice supports the VK_KHR_multiview extension"
+            ],
+            "issue": "http://anglebug.com/6048"
+        },
+
+        {
+            "name": "disableFifoPresentMode",
+            "display_name": "disableFifoPresentMode",
+            "category": "Workarounds",
+            "description": [
+                "VK_PRESENT_MODE_FIFO_KHR causes random timeouts"
+            ],
+            "issue": "http://anglebug.com/3153"
+        },
+
+        {
+            "name": "bindEmptyForUnusedDescriptorSets",
+            "display_name": "bindEmptyForUnusedDescriptorSets",
+            "category": "Workarounds",
+            "description": [
+                "Gaps in bound descriptor set indices causes the post-gap sets to misbehave"
+            ],
+            "issue": "http://anglebug.com/2727"
+        },
+
+        {
+            "name": "forceD16TexFilter",
+            "display_name": "forceD16TexFilter",
+            "category": "Workarounds",
+            "description": [
+                "VK_FORMAT_D16_UNORM does not support VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT, ",
+                "which prevents OES_depth_texture from being supported."
+            ],
+            "issue": "http://anglebug.com/3452"
+        },
+
+        {
+            "name": "disableFlippingBlitWithCommand",
+            "display_name": "disableFlippingBlitWithCommand",
+            "category": "Workarounds",
+            "description": [
+                "vkCmdBlitImage with flipped coordinates blits incorrectly."
+            ],
+            "issue": "http://anglebug.com/3498"
+        },
+
+        {
+            "name": "perFrameWindowSizeQuery",
+            "display_name": "perFrameWindowSizeQuery",
+            "category": "Workarounds",
+            "description": [
+                "Vulkan swapchain is not returning VK_ERROR_OUT_OF_DATE when window resizing"
+            ],
+            "issue": "http://anglebug.com/3623, http://anglebug.com/3624, http://anglebug.com/3625"
+        },
+
+        {
+            "name": "disallowSeamfulCubeMapEmulation",
+            "display_name": "disallowSeamfulCubeMapEmulation",
+            "category": "Workarounds",
+            "description": [
+                "Seamful cube map emulation misbehaves on some drivers, so it's disallowed"
+            ],
+            "issue": "http://anglebug.com/3243"
+        },
+
+        {
+            "name": "padBuffersToMaxVertexAttribStride",
+            "display_name": "padBuffersToMaxVertexAttribStride",
+            "category": "Workarounds",
+            "description": [
+                "Vulkan considers vertex attribute accesses to count up to the last multiple of the ",
+                "stride. This additional access supports AMD's robust buffer access implementation. ",
+                "AMDVLK in particular will return incorrect values when the vertex access extends into ",
+                "the range that would be the stride padding and the buffer is too small. ",
+                "This workaround limits GL_MAX_VERTEX_ATTRIB_STRIDE to a maximum value and ",
+                "pads up every buffer allocation size to be a multiple of the maximum stride."
+            ],
+            "issue": "http://anglebug.com/4428"
+        },
+
+        {
+            "name": "supportsExternalMemoryDmaBufAndModifiers",
+            "display_name": "supportsExternalMemoryDmaBufAndModifiers",
+            "category": "Features",
+            "description": [
+                "VkDevice supports the VK_EXT_external_memory_dma_buf and VK_EXT_image_drm_format_modifier ",
+                "extensions"
+            ],
+            "issue": "http://anglebug.com/6248"
+        },
+
+        {
+            "name": "supportsExternalMemoryHost",
+            "display_name": "supportsExternalMemoryHost",
+            "category": "Features",
+            "description": [
+                "VkDevice supports the VK_EXT_external_memory_host extension"
+            ]
+        },
+
+        {
+            "name": "allocateNonZeroMemory",
+            "display_name": "allocateNonZeroMemory",
+            "category": "Features",
+            "description": [
+                "Fill new allocations with non-zero values to flush out errors."
+            ],
+            "issue": "http://anglebug.com/4384"
+        },
+
+        {
+            "name": "logMemoryReportCallbacks",
+            "display_name": "logMemoryReportCallbacks",
+            "category": "Features",
+            "description": [
+                "Log each callback from VK_EXT_device_memory_report"
+            ]
+        },
+
+        {
+            "name": "logMemoryReportStats",
+            "display_name": "logMemoryReportStats",
+            "category": "Features",
+            "description": [
+                "Log stats from VK_EXT_device_memory_report each swap"
+            ]
+        },
+
+        {
+            "name": "shadowBuffers",
+            "display_name": "shadowBuffers",
+            "category": "Features",
+            "description": [
+                "Allocate a shadow buffer for GL buffer objects to reduce glMap* latency."
+            ],
+            "issue": "http://anglebug.com/4339"
+        },
+
+        {
+            "name": "preferCPUForBufferSubData",
+            "display_name": "preferCPUForBufferSubData",
+            "category": "Features",
+            "description": [
+                "Prefer use CPU to do bufferSubData instead of staged update."
+            ],
+            "issue": "http://issuetracker.google.com/200067929"
+        },
+
+        {
+            "name": "persistentlyMappedBuffers",
+            "display_name": "persistentlyMappedBuffers",
+            "category": "Features",
+            "description": [
+                "Persistently map buffer memory to reduce map/unmap IOCTL overhead."
+            ],
+            "issue": "http://anglebug.com/2162"
+        },
+
+        {
+            "name": "enablePreRotateSurfaces",
+            "display_name": "enablePreRotateSurfaces",
+            "category": "Features",
+            "description": [
+                "Enable Android pre-rotation for landscape applications"
+            ],
+            "issue": "http://anglebug.com/3502"
+        },
+
+        {
+            "name": "enablePrecisionQualifiers",
+            "display_name": "enablePrecisionQualifiers",
+            "category": "Features",
+            "description": [
+                "Enable precision qualifiers in shaders"
+            ],
+            "issue": "http://anglebug.com/3078"
+        },
+
+        {
+            "name": "preferAggregateBarrierCalls",
+            "display_name": "preferAggregateBarrierCalls",
+            "category": "Workarounds",
+            "description": [
+                "Single barrier call is preferred over multiple calls with ",
+                "fine grained pipeline stage dependency information"
+            ],
+            "issue": "http://anglebug.com/4633"
+        },
+
+        {
+            "name": "preferSkippingInvalidateForEmulatedFormats",
+            "display_name": "preferSkippingInvalidateForEmulatedFormats",
+            "category": "Workarounds",
+            "description": [
+                "Skipping invalidate is preferred for emulated formats that have extra channels over ",
+                "re-clearing the image"
+            ],
+            "issue": "http://anglebug.com/6860"
+        },
+
+        {
+            "name": "asyncCommandQueue",
+            "display_name": "asyncCommandQueue",
+            "category": "Features",
+            "description": [
+                "Use CommandQueue worker thread to dispatch work to GPU."
+            ],
+            "issue": "http://anglebug.com/4324"
+        },
+
+        {
+            "name": "supportsShaderFloat16",
+            "display_name": "supportsShaderFloat16",
+            "category": "Features",
+            "description": [
+                "VkDevice supports the VK_KHR_shader_float16_int8 extension ",
+                "and has the shaderFloat16 feature"
+            ],
+            "issue": "http://anglebug.com/4551"
+        },
+
+        {
+            "name": "allowGenerateMipmapWithCompute",
+            "display_name": "allowGenerateMipmapWithCompute",
+            "category": "Features",
+            "description": [
+                "Use the compute path to generate mipmaps on devices that meet the minimum requirements, ",
+                "and the performance is better."
+            ],
+            "issue": "http://anglebug.com/4551"
+        },
+
+        {
+            "name": "supportsRenderPassStoreOpNoneQCOM",
+            "display_name": "supportsRenderPassStoreOpNoneQCOM",
+            "category": "Features",
+            "description": [
+                "VkDevice supports VK_QCOM_render_pass_store_ops extension."
+            ],
+            "issue": "http://anglebug.com/5055"
+        },
+
+        {
+            "name": "supportsRenderPassLoadStoreOpNone",
+            "display_name": "supportsRenderPassLoadStoreOpNone",
+            "category": "Features",
+            "description": [
+                "VkDevice supports VK_EXT_load_store_op_none extension."
+            ],
+            "issue": "http://anglebug.com/5371"
+        },
+
+        {
+            "name": "supportsDepthClipControl",
+            "display_name": "supportsDepthClipControl",
+            "category": "Features",
+            "description": [
+                "VkDevice supports VK_EXT_depth_clip_control extension."
+            ],
+            "issue": "http://anglebug.com/5421"
+        },
+
+        {
+            "name": "supportsBlendOperationAdvanced",
+            "display_name": "supportsBlendOperationAdvanced",
+            "category": "Features",
+            "description": [
+                "VkDevice supports VK_EXT_blend_operation_advanced extension."
+            ],
+            "issue": "http://anglebug.com/3586"
+        },
+
+        {
+            "name": "forceMaxUniformBufferSize16KB",
+            "display_name": "forceMaxUniformBufferSize16KB",
+            "category": "Workarounds",
+            "description": [
+                "Force max uniform buffer size to 16K on some device due to bug"
+            ],
+            "issue": "https://issuetracker.google.com/161903006"
+        },
+
+        {
+            "name": "supportsImageFormatList",
+            "display_name": "supportsImageFormatList",
+            "category": "Features",
+            "description": [
+                "Enable VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT by default for ICDs ",
+                "that support VK_KHR_image_format_list"
+            ],
+            "issue": "http://anglebug.com/5281"
+        },
+
+        {
+            "name": "enableMultisampledRenderToTexture",
+            "display_name": "enableMultisampledRenderToTexture",
+            "category": "Workarounds",
+            "description": [
+                "Expose EXT_multisampled_render_to_texture"
+            ],
+            "issue": "http://anglebug.com/4937"
+        },
+
+        {
+            "name": "deferFlushUntilEndRenderPass",
+            "display_name": "deferFlushUntilEndRenderPass",
+            "category": "Workarounds",
+            "description": [
+                "Allow glFlush to be deferred until renderpass ends"
+            ],
+            "issue": "https://issuetracker.google.com/issues/166475273"
+        },
+
+        {
+            "name": "waitIdleBeforeSwapchainRecreation",
+            "display_name": "waitIdleBeforeSwapchainRecreation",
+            "category": "Workarounds",
+            "description": [
+                "Before passing an oldSwapchain to VkSwapchainCreateInfoKHR, wait for queue to be idle. ",
+                "Works around a bug on platforms which destroy oldSwapchain in vkCreateSwapchainKHR."
+            ],
+            "issue": "http://anglebug.com/5061"
+        },
+
+        {
+            "name": "forceTextureLodOffset1",
+            "display_name": "force_texture_lod_offset_1",
+            "category": "Workarounds",
+            "description": [
+                "Increase the minimum texture level-of-detail by 1 when sampling."
+            ]
+        },
+
+        {
+            "name": "forceTextureLodOffset2",
+            "display_name": "force_texture_lod_offset_2",
+            "category": "Workarounds",
+            "description": [
+                "Increase the minimum texture level-of-detail by 2 when sampling."
+            ]
+        },
+
+        {
+            "name": "forceTextureLodOffset3",
+            "display_name": "force_texture_lod_offset_3",
+            "category": "Workarounds",
+            "description": [
+                "Increase the minimum texture level-of-detail by 3 when sampling."
+            ]
+        },
+
+        {
+            "name": "forceTextureLodOffset4",
+            "display_name": "force_texture_lod_offset_4",
+            "category": "Workarounds",
+            "description": [
+                "Increase the minimum texture level-of-detail by 4 when sampling."
+            ]
+        },
+
+        {
+            "name": "forceNearestFiltering",
+            "display_name": "force_nearest_filtering",
+            "category": "Workarounds",
+            "description": [
+                "Force nearest filtering when sampling."
+            ]
+        },
+
+        {
+            "name": "forceNearestMipFiltering",
+            "display_name": "forceNearestMipFiltering",
+            "category": "Workarounds",
+            "description": [
+                "Force nearest mip filtering when sampling."
+            ]
+        },
+
+        {
+            "name": "compressVertexData",
+            "display_name": "compress_vertex_data",
+            "category": "Workarounds",
+            "description": [
+                "Compress vertex data to smaller data types when ",
+                "possible. Using this feature makes ANGLE non-conformant."
+            ]
+        },
+
+        {
+            "name": "preferDrawClearOverVkCmdClearAttachments",
+            "display_name": "preferDrawClearOverVkCmdClearAttachments",
+            "category": "Workarounds",
+            "description": [
+                "On some hardware, clear using a draw call instead of vkCmdClearAttachments in the middle ",
+                "of render pass due to bugs"
+            ],
+            "issue": "https://issuetracker.google.com/166809097"
+        },
+
+        {
+            "name": "emulatedPrerotation90",
+            "display_name": "emulatedPrerotation90",
+            "category": "Features",
+            "description": [
+                "Emulate 90-degree prerotation."
+            ],
+            "issue": "http://anglebug.com/4901"
+        },
+
+        {
+            "name": "emulatedPrerotation180",
+            "display_name": "emulatedPrerotation180",
+            "category": "Features",
+            "description": [
+                "Emulate 180-degree prerotation."
+            ],
+            "issue": "http://anglebug.com/4901"
+        },
+
+        {
+            "name": "emulatedPrerotation270",
+            "display_name": "emulatedPrerotation270",
+            "category": "Features",
+            "description": [
+                "Emulate 270-degree prerotation."
+            ],
+            "issue": "http://anglebug.com/4901"
+        },
+
+        {
+            "name": "generateSPIRVThroughGlslang",
+            "display_name": "generateSPIRVThroughGlslang",
+            "category": "Features",
+            "description": [
+                "Translate SPIR-V through glslang."
+            ],
+            "issue": "http://anglebug.com/4889"
+        },
+
+        {
+            "name": "forceDriverUniformOverSpecConst",
+            "display_name": "forceDriverUniformOverSpecConst",
+            "category": "Workarounds",
+            "description": [
+                "Forces using driver uniforms instead of specialization constants."
+            ],
+            "issue": "http://issuetracker.google.com/173636783"
+        },
+
+        {
+            "name": "exposeNonConformantExtensionsAndVersions",
+            "display_name": "exposeNonConformantExtensionsAndVersions",
+            "category": "Workarounds",
+            "description": [
+                "Expose GLES versions and extensions that are not conformant."
+            ],
+            "issue": "http://anglebug.com/5375"
+        },
+
+        {
+            "name": "emulateR32fImageAtomicExchange",
+            "display_name": "emulateR32fImageAtomicExchange",
+            "category": "Workarounds",
+            "description": [
+                "Emulate r32f images with r32ui to support imageAtomicExchange."
+            ],
+            "issue": "http://anglebug.com/5535"
+        },
+
+        {
+            "name": "supportsNegativeViewport",
+            "display_name": "supportsNegativeViewport",
+            "category": "Features",
+            "description": [
+                "The driver supports inverting the viewport with a negative height."
+            ]
+        },
+
+        {
+            "name": "forceFragmentShaderPrecisionHighpToMediump",
+            "display_name": "forceFragmentShaderPrecisionHighpToMediump",
+            "category": "Workarounds",
+            "description": [
+                "Forces highp precision in fragment shader to mediump."
+            ],
+            "issue": "https://issuetracker.google.com/184850002"
+        },
+
+        {
+            "name": "preferSubmitAtFBOBoundary",
+            "display_name": "preferSubmitAtFBOBoundary",
+            "category": "Workarounds",
+            "description": [
+                "Submit commands to driver at each FBO boundary for performance improvements."
+            ],
+            "issue": "https://issuetracker.google.com/187425444"
+        },
+
+        {
+            "name": "useMultipleDescriptorsForExternalFormats",
+            "display_name": "useMultipleDescriptorsForExternalFormats",
+            "category": "Workarounds",
+            "description": [
+                "Return a default descriptor count for external formats."
+            ],
+            "issue": "http://anglebug.com/6141"
+        },
+
+        {
+            "name": "supportsProtectedMemory",
+            "display_name": "supportsProtectedMemory",
+            "category": "Features",
+            "description": [
+                "VkDevice supports protected memory"
+            ],
+            "issue": "http://anglebug.com/3965"
+        },
+
+        {
+            "name": "supportsHostQueryReset",
+            "display_name": "supportsHostQueryReset",
+            "category": "Features",
+            "description": [
+                "VkDevice supports VK_EXT_host_query_reset extension"
+            ],
+            "issue": "http://anglebug.com/6692"
+        },
+
+        {
+            "name": "supportsSurfaceCapabilities2Extension",
+            "display_name": "supportsSurfaceCapabilities2Extension",
+            "category": "Features",
+            "description": [
+                "VkInstance supports the VK_KHR_get_surface_capabilities2 extension"
+            ]
+        },
+
+        {
+            "name": "supportsSurfaceProtectedCapabilitiesExtension",
+            "display_name": "supportsSurfaceProtectedCapabilitiesExtension",
+            "category": "Features",
+            "description": [
+                "VkInstance supports the VK_KHR_surface_protected_capabilities extension"
+            ]
+        },
+
+        {
+            "name": "supportsSurfacelessQueryExtension",
+            "display_name": "supportsSurfacelessQueryExtension",
+            "category": "Features",
+            "description": [
+                "VkInstance supports the VK_GOOGLE_surfaceless_query extension"
+            ]
+        },
+
+        {
+            "name": "supportsSurfaceProtectedSwapchains",
+            "display_name": "supportsSurfaceProtectedSwapchains",
+            "category": "Features",
+            "description": [
+                "VkSurface supportsProtected for protected swapchains"
+            ]
+        },
+
+        {
+            "name": "overrideSurfaceFormatRGB8toRGBA8",
+            "display_name": "overrideSurfaceFormatRGB8toRGBA8",
+            "category": "Workarounds",
+            "description": [
+                "Override surface format GL_RGB8 to GL_RGBA8"
+            ],
+            "issue": "http://anglebug.com/6651"
+        },
+
+        {
+            "name": "supportsSharedPresentableImageExtension",
+            "display_name": "supportsSharedPresentableImageExtension",
+            "category": "Features",
+            "description": [
+                "VkSurface supports the VK_KHR_shared_presentable_images extension"
+            ]
+        },
+
+        {
+            "name": "supportsShaderFramebufferFetch",
+            "display_name": "supportsShaderFramebufferFetch",
+            "category": "Features",
+            "description": [
+                "Whether the Vulkan backend supports coherent framebuffer fetch"
+            ]
+        },
+
+        {
+            "name": "supportsShaderFramebufferFetchNonCoherent",
+            "display_name": "supportsShaderFramebufferFetchNonCoherent",
+            "category": "Features",
+            "description": [
+                "Whether the Vulkan backend supports non-coherent framebuffer fetch"
+            ]
+        },
+
+        {
+            "name": "supportsLockSurfaceExtension",
+            "display_name": "supportsLockSurfaceExtension",
+            "category": "Features",
+            "description": [
+                "Surface supports the EGL_KHR_lock_surface3 extension"
+            ]
+        },
+
+        {
+            "name": "swapbuffersOnFlushOrFinishWithSingleBuffer",
+            "display_name": "swapbuffersOnFlushOrFinishWithSingleBuffer",
+            "category": "Features",
+            "description": [
+                "Bypass deferredFlush with calling swapbuffers on flush or finish when in Shared Present ",
+                "mode"
+            ],
+            "issue": "http://anglebug.com/6878"
+        },
+
+        {
+            "name": "emulateDithering",
+            "display_name": "emulateDithering",
+            "category": "Features",
+            "description": [
+                "Emulate OpenGL dithering"
+            ],
+            "issue": "http://anglebug.com/6755"
+        },
+
+        {
+            "name": "bottomLeftOriginPresentRegionRectangles",
+            "display_name": "bottomLeftOriginPresentRegionRectangles",
+            "category": "Workarounds",
+            "description": [
+                "On some platforms present region rectangles are expected to have a bottom-left origin, ",
+                "instead of top-left origin as from spec"
+            ]
+        },
+
+        {
+            "name": "forceSubmitImmutableTextureUpdates",
+            "display_name": "forceSubmitImmutableTextureUpdates",
+            "category": "AppWorkarounds",
+            "description": [
+                "Force submit updates to immutable textures"
+            ],
+            "issue": "http://anglebug.com/6929"
+        },
+
+        {
+            "name": "retainSpirvDebugInfo",
+            "display_name": "retainSpirvDebugInfo",
+            "category": "Features",
+            "description": [
+                "Retain debug info in SPIR-V blob."
+            ],
+            "issue": "http://anglebug.com/5901"
+        },
+
+        {
+            "name": "createPipelineDuringLink",
+            "display_name": "createPipelineDuringLink",
+            "category": "Features",
+            "description": [
+                "Create pipeline with default state during glLinkProgram"
+            ],
+            "issue": "http://anglebug.com/7046"
+        }
+    ]
+}
diff --git a/scripts/code_generation_hashes/ANGLE_features.json b/scripts/code_generation_hashes/ANGLE_features.json
new file mode 100644
index 0000000..7296f90
--- /dev/null
+++ b/scripts/code_generation_hashes/ANGLE_features.json
@@ -0,0 +1,24 @@
+{
+  "include/platform/FeaturesD3D.h":
+    "f00de369db90a8419c4e441613c0ef20",
+  "include/platform/FeaturesGL.h":
+    "c614038e0da8d0c45251dd4122dbff93",
+  "include/platform/FeaturesMtl.h":
+    "7db69f3efdd4e89db97060522de4cb16",
+  "include/platform/FeaturesVk.h":
+    "9e973029b1986ecb25bc9a6114dba0b5",
+  "include/platform/FrontendFeatures.h":
+    "4f3d20ecdcd9ccea3cb80b0becd2b056",
+  "include/platform/d3d_features.json":
+    "abdcf90e79eb2aae4f55a33b1ca17b72",
+  "include/platform/frontend_features.json":
+    "cf60ba62068fbd82274a20fbbb5a3030",
+  "include/platform/gen_features.py":
+    "2394959a18335f95de22ec1f60a41671",
+  "include/platform/gl_features.json":
+    "ff2f4dac740e071f511f751f59f3ef31",
+  "include/platform/mtl_features.json":
+    "db7d8f4e3fe242a05a2f6c788f775f48",
+  "include/platform/vk_features.json":
+    "64a8f21b89fe6ad4dbdb0c953ab55d31"
+}
\ No newline at end of file
diff --git a/scripts/run_code_generation.py b/scripts/run_code_generation.py
index fa1a029..b3df40b 100755
--- a/scripts/run_code_generation.py
+++ b/scripts/run_code_generation.py
@@ -80,6 +80,8 @@
 
 
 generators = {
+    'ANGLE features':
+        'include/platform/gen_features.py',
     'ANGLE format':
         'src/libANGLE/renderer/gen_angle_format_table.py',
     'ANGLE load functions table':
diff --git a/src/libANGLE/renderer/d3d/ProgramD3D.cpp b/src/libANGLE/renderer/d3d/ProgramD3D.cpp
index 6970928..eb57441 100644
--- a/src/libANGLE/renderer/d3d/ProgramD3D.cpp
+++ b/src/libANGLE/renderer/d3d/ProgramD3D.cpp
@@ -1121,7 +1121,7 @@
     {
         stream->readString(&mShaderHLSL[shaderType]);
         stream->readBytes(reinterpret_cast<unsigned char *>(&mShaderWorkarounds[shaderType]),
-                          sizeof(angle::CompilerWorkaroundsD3D));
+                          sizeof(CompilerWorkaroundsD3D));
     }
 
     stream->readBool(&mUsesFragDepth);
@@ -1417,7 +1417,7 @@
     {
         stream->writeString(mShaderHLSL[shaderType]);
         stream->writeBytes(reinterpret_cast<unsigned char *>(&mShaderWorkarounds[shaderType]),
-                           sizeof(angle::CompilerWorkaroundsD3D));
+                           sizeof(CompilerWorkaroundsD3D));
     }
 
     stream->writeBool(mUsesFragDepth);
@@ -1656,8 +1656,8 @@
     ShaderExecutableD3D *geometryExecutable = nullptr;
     angle::Result result                    = mRenderer->compileToExecutable(
         context, *currentInfoLog, geometryHLSL, gl::ShaderType::Geometry, mStreamOutVaryings,
-        (mState.getTransformFeedbackBufferMode() == GL_SEPARATE_ATTRIBS),
-        angle::CompilerWorkaroundsD3D(), &geometryExecutable);
+        (mState.getTransformFeedbackBufferMode() == GL_SEPARATE_ATTRIBS), CompilerWorkaroundsD3D(),
+        &geometryExecutable);
 
     if (!infoLog && result == angle::Result::Stop)
     {
@@ -2019,9 +2019,9 @@
     gl::InfoLog tempInfoLog;
     gl::InfoLog *currentInfoLog = infoLog ? infoLog : &tempInfoLog;
 
-    ANGLE_TRY(mRenderer->compileToExecutable(
-        context, *currentInfoLog, finalComputeHLSL, gl::ShaderType::Compute,
-        std::vector<D3DVarying>(), false, angle::CompilerWorkaroundsD3D(), &computeExecutable));
+    ANGLE_TRY(mRenderer->compileToExecutable(context, *currentInfoLog, finalComputeHLSL,
+                                             gl::ShaderType::Compute, std::vector<D3DVarying>(),
+                                             false, CompilerWorkaroundsD3D(), &computeExecutable));
 
     if (computeExecutable)
     {
diff --git a/src/libANGLE/renderer/d3d/ProgramD3D.h b/src/libANGLE/renderer/d3d/ProgramD3D.h
index cfe183c..113b25d 100644
--- a/src/libANGLE/renderer/d3d/ProgramD3D.h
+++ b/src/libANGLE/renderer/d3d/ProgramD3D.h
@@ -545,7 +545,7 @@
     std::vector<std::unique_ptr<ComputeExecutable>> mComputeExecutables;
 
     gl::ShaderMap<std::string> mShaderHLSL;
-    gl::ShaderMap<angle::CompilerWorkaroundsD3D> mShaderWorkarounds;
+    gl::ShaderMap<CompilerWorkaroundsD3D> mShaderWorkarounds;
 
     bool mUsesFragDepth;
     bool mHasANGLEMultiviewEnabled;
diff --git a/src/libANGLE/renderer/d3d/RendererD3D.h b/src/libANGLE/renderer/d3d/RendererD3D.h
index 7698096..d681e5b 100644
--- a/src/libANGLE/renderer/d3d/RendererD3D.h
+++ b/src/libANGLE/renderer/d3d/RendererD3D.h
@@ -19,6 +19,7 @@
 #include "libANGLE/Version.h"
 #include "libANGLE/angletypes.h"
 #include "libANGLE/formatutils.h"
+#include "libANGLE/renderer/d3d/ShaderD3D.h"
 #include "libANGLE/renderer/d3d/VertexDataManager.h"
 #include "libANGLE/renderer/d3d/formatutilsD3D.h"
 #include "libANGLE/renderer/renderer_utils.h"
@@ -277,7 +278,7 @@
                                               gl::ShaderType type,
                                               const std::vector<D3DVarying> &streamOutVaryings,
                                               bool separatedOutputBuffers,
-                                              const angle::CompilerWorkaroundsD3D &workarounds,
+                                              const CompilerWorkaroundsD3D &workarounds,
                                               ShaderExecutableD3D **outExectuable) = 0;
     virtual angle::Result ensureHLSLCompilerInitialized(d3d::Context *context)     = 0;
 
diff --git a/src/libANGLE/renderer/d3d/ShaderD3D.cpp b/src/libANGLE/renderer/d3d/ShaderD3D.cpp
index 67ec693..07e4597 100644
--- a/src/libANGLE/renderer/d3d/ShaderD3D.cpp
+++ b/src/libANGLE/renderer/d3d/ShaderD3D.cpp
@@ -175,7 +175,7 @@
     mDebugInfo.clear();
 }
 
-void ShaderD3D::generateWorkarounds(angle::CompilerWorkaroundsD3D *workarounds) const
+void ShaderD3D::generateWorkarounds(CompilerWorkaroundsD3D *workarounds) const
 {
     if (mUsesDiscardRewriting)
     {
diff --git a/src/libANGLE/renderer/d3d/ShaderD3D.h b/src/libANGLE/renderer/d3d/ShaderD3D.h
index 4d5c609..f498709 100644
--- a/src/libANGLE/renderer/d3d/ShaderD3D.h
+++ b/src/libANGLE/renderer/d3d/ShaderD3D.h
@@ -15,7 +15,6 @@
 
 namespace angle
 {
-struct CompilerWorkaroundsD3D;
 struct FeaturesD3D;
 }  // namespace angle
 
@@ -30,6 +29,18 @@
 class RendererD3D;
 struct D3DUniform;
 
+// Workarounds attached to each shader. Do not need to expose information about these workarounds so
+// a simple bool struct suffices.
+struct CompilerWorkaroundsD3D
+{
+    bool skipOptimization = false;
+
+    bool useMaxOptimization = false;
+
+    // IEEE strictness needs to be enabled for NANs to work.
+    bool enableIEEEStrictness = false;
+};
+
 class ShaderD3D : public ShaderImpl
 {
   public:
@@ -62,7 +73,7 @@
     const std::set<std::string> &getSlowCompilingUniformBlockSet() const;
     void appendDebugInfo(const std::string &info) const { mDebugInfo += info; }
 
-    void generateWorkarounds(angle::CompilerWorkaroundsD3D *workarounds) const;
+    void generateWorkarounds(CompilerWorkaroundsD3D *workarounds) const;
 
     bool usesMultipleRenderTargets() const { return mUsesMultipleRenderTargets; }
     bool usesFragColor() const { return mUsesFragColor; }
diff --git a/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp b/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp
index 24f3501..e71891e 100644
--- a/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp
+++ b/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp
@@ -3087,7 +3087,7 @@
                                               gl::ShaderType type,
                                               const std::vector<D3DVarying> &streamOutVaryings,
                                               bool separatedOutputBuffers,
-                                              const angle::CompilerWorkaroundsD3D &workarounds,
+                                              const CompilerWorkaroundsD3D &workarounds,
                                               ShaderExecutableD3D **outExectuable)
 {
     std::stringstream profileStream;
diff --git a/src/libANGLE/renderer/d3d/d3d11/Renderer11.h b/src/libANGLE/renderer/d3d/d3d11/Renderer11.h
index 84c9c2c..04821c4 100644
--- a/src/libANGLE/renderer/d3d/d3d11/Renderer11.h
+++ b/src/libANGLE/renderer/d3d/d3d11/Renderer11.h
@@ -207,7 +207,7 @@
                                       gl::ShaderType type,
                                       const std::vector<D3DVarying> &streamOutVaryings,
                                       bool separatedOutputBuffers,
-                                      const angle::CompilerWorkaroundsD3D &workarounds,
+                                      const CompilerWorkaroundsD3D &workarounds,
                                       ShaderExecutableD3D **outExectuable) override;
     angle::Result ensureHLSLCompilerInitialized(d3d::Context *context) override;
 
diff --git a/src/libANGLE/renderer/d3d/d3d9/Renderer9.cpp b/src/libANGLE/renderer/d3d/d3d9/Renderer9.cpp
index b6c2b83..42932bd 100644
--- a/src/libANGLE/renderer/d3d/d3d9/Renderer9.cpp
+++ b/src/libANGLE/renderer/d3d/d3d9/Renderer9.cpp
@@ -2669,7 +2669,7 @@
                                              gl::ShaderType type,
                                              const std::vector<D3DVarying> &streamOutVaryings,
                                              bool separatedOutputBuffers,
-                                             const angle::CompilerWorkaroundsD3D &workarounds,
+                                             const CompilerWorkaroundsD3D &workarounds,
                                              ShaderExecutableD3D **outExectuable)
 {
     // Transform feedback is not supported in ES2 or D3D9
diff --git a/src/libANGLE/renderer/d3d/d3d9/Renderer9.h b/src/libANGLE/renderer/d3d/d3d9/Renderer9.h
index f8af8e4..8a306a0 100644
--- a/src/libANGLE/renderer/d3d/d3d9/Renderer9.h
+++ b/src/libANGLE/renderer/d3d/d3d9/Renderer9.h
@@ -249,7 +249,7 @@
                                       gl::ShaderType type,
                                       const std::vector<D3DVarying> &streamOutVaryings,
                                       bool separatedOutputBuffers,
-                                      const angle::CompilerWorkaroundsD3D &workarounds,
+                                      const CompilerWorkaroundsD3D &workarounds,
                                       ShaderExecutableD3D **outExectuable) override;
     angle::Result ensureHLSLCompilerInitialized(d3d::Context *context) override;
 
diff --git a/src/libANGLE/renderer/gl/TextureGL.cpp b/src/libANGLE/renderer/gl/TextureGL.cpp
index 285051a..eaf4652 100644
--- a/src/libANGLE/renderer/gl/TextureGL.cpp
+++ b/src/libANGLE/renderer/gl/TextureGL.cpp
@@ -38,6 +38,8 @@
 
 namespace
 {
+// For use with the uploadTextureDataInChunks feature.  See http://crbug.com/1181068
+constexpr const size_t kUploadTextureDataInChunksUploadSize = (120 * 1024) - 1;
 
 size_t GetLevelInfoIndex(gl::TextureTarget target, size_t level)
 {
@@ -365,9 +367,9 @@
 
     if (features.uploadTextureDataInChunks.enabled)
     {
-        return setSubImageRowByRowWorkaround(
-            context, target, level, area, format, type, unpack, unpackBuffer,
-            angle::FeaturesGL::kUploadTextureDataInChunksUploadSize, pixels);
+        return setSubImageRowByRowWorkaround(context, target, level, area, format, type, unpack,
+                                             unpackBuffer, kUploadTextureDataInChunksUploadSize,
+                                             pixels);
     }
 
     if (nativegl::UseTexImage2D(getType()))
@@ -2110,7 +2112,7 @@
                 ANGLE_TRY(setSubImageRowByRowWorkaround(
                     context, imageIndex.getTarget(), imageIndex.getLevelIndex(), area,
                     nativeSubImageFormat.format, nativeSubImageFormat.type, unpackState, nullptr,
-                    angle::FeaturesGL::kUploadTextureDataInChunksUploadSize, zero->data()));
+                    kUploadTextureDataInChunksUploadSize, zero->data()));
             }
             else
             {
diff --git a/src/libANGLE/renderer/vulkan/RendererVk.cpp b/src/libANGLE/renderer/vulkan/RendererVk.cpp
index caf7a61..8892fda 100644
--- a/src/libANGLE/renderer/vulkan/RendererVk.cpp
+++ b/src/libANGLE/renderer/vulkan/RendererVk.cpp
@@ -3248,16 +3248,6 @@
     // Android mistakenly destroys the old swapchain when creating a new one.
     ANGLE_FEATURE_CONDITION(&mFeatures, waitIdleBeforeSwapchainRecreation, IsAndroid() && isARM);
 
-    for (size_t lodOffsetFeatureIdx = 0;
-         lodOffsetFeatureIdx < mFeatures.forceTextureLODOffset.size(); lodOffsetFeatureIdx++)
-    {
-        ANGLE_FEATURE_CONDITION(&mFeatures, forceTextureLODOffset[lodOffsetFeatureIdx], false);
-    }
-    ANGLE_FEATURE_CONDITION(&mFeatures, forceNearestFiltering, false);
-    ANGLE_FEATURE_CONDITION(&mFeatures, forceNearestMipFiltering, false);
-
-    ANGLE_FEATURE_CONDITION(&mFeatures, compressVertexData, false);
-
     // vkCmdClearAttachments races with draw calls on Qualcomm hardware as observed on Pixel2 and
     // Pixel4.  https://issuetracker.google.com/issues/166809097
     ANGLE_FEATURE_CONDITION(&mFeatures, preferDrawClearOverVkCmdClearAttachments, isQualcomm);
diff --git a/src/libANGLE/renderer/vulkan/vk_cache_utils.cpp b/src/libANGLE/renderer/vulkan/vk_cache_utils.cpp
index a240efe..7c58b6c 100644
--- a/src/libANGLE/renderer/vulkan/vk_cache_utils.cpp
+++ b/src/libANGLE/renderer/vulkan/vk_cache_utils.cpp
@@ -3436,15 +3436,21 @@
 {
     const angle::FeaturesVk &featuresVk = contextVk->getFeatures();
     mMipLodBias                         = 0.0f;
-    for (size_t lodOffsetFeatureIdx = 0;
-         lodOffsetFeatureIdx < featuresVk.forceTextureLODOffset.size(); lodOffsetFeatureIdx++)
+    if (featuresVk.forceTextureLodOffset1.enabled)
     {
-        if (featuresVk.forceTextureLODOffset[lodOffsetFeatureIdx].enabled)
-        {
-            // Make sure only one forceTextureLODOffset feature is set.
-            ASSERT(mMipLodBias == 0.0f);
-            mMipLodBias = static_cast<float>(lodOffsetFeatureIdx + 1);
-        }
+        mMipLodBias = 1.0f;
+    }
+    else if (featuresVk.forceTextureLodOffset2.enabled)
+    {
+        mMipLodBias = 2.0f;
+    }
+    else if (featuresVk.forceTextureLodOffset3.enabled)
+    {
+        mMipLodBias = 3.0f;
+    }
+    else if (featuresVk.forceTextureLodOffset4.enabled)
+    {
+        mMipLodBias = 4.0f;
     }
 
     mMaxAnisotropy = samplerState.getMaxAnisotropy();