dawn/native: Add support for feature "indirect-first-instance"

Bug: dawn:1197
Change-Id: I9042b2dc178dfc01201bff55a2f5e43de28d335e
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/90526
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Enrico Galli <enrico.galli@intel.com>
diff --git a/dawn.json b/dawn.json
index 051d468..745167e 100644
--- a/dawn.json
+++ b/dawn.json
@@ -1214,6 +1214,7 @@
             {"name": "depth24 unorm stencil8", "type": "bool", "default": "false"},
             {"name": "depth32 float stencil8", "type": "bool", "default": "false"},
             {"name": "chromium experimental dp4a", "type": "bool", "default": "false"},
+            {"name": "indirect first instance", "type": "bool", "default": "false"},
             {"name": "invalid feature", "type": "bool", "default": "false"},
             {"name": "dawn internal usages", "type": "bool", "default": "false"},
             {"name": "dawn native", "type": "bool", "default": "false"},
diff --git a/src/dawn/native/Features.cpp b/src/dawn/native/Features.cpp
index e2b82ee..a813c68 100644
--- a/src/dawn/native/Features.cpp
+++ b/src/dawn/native/Features.cpp
@@ -78,6 +78,10 @@
      {"chromium-experimental-dp4a", "Support experimental DP4a instructions in WGSL",
       "https://bugs.chromium.org/p/tint/issues/detail?id=1497"},
      &WGPUDeviceProperties::chromiumExperimentalDp4a},
+    {Feature::IndirectFirstInstance,
+     {"indirect-first-instance", "Support non-zero first instance values on indirect draw calls",
+      "https://bugs.chromium.org/p/dawn/issues/detail?id=1197"},
+     &WGPUDeviceProperties::indirectFirstInstance},
     {Feature::DawnInternalUsages,
      {"dawn-internal-usages",
       "Add internal usages to resources to affect how the texture is allocated, but not "
@@ -117,6 +121,8 @@
             return Feature::Depth24UnormStencil8;
         case wgpu::FeatureName::Depth32FloatStencil8:
             return Feature::Depth32FloatStencil8;
+        case wgpu::FeatureName::IndirectFirstInstance:
+            return Feature::IndirectFirstInstance;
         case wgpu::FeatureName::DawnShaderFloat16:
             return Feature::ShaderFloat16;
         case wgpu::FeatureName::DawnInternalUsages:
@@ -127,9 +133,6 @@
             return Feature::DawnNative;
         case wgpu::FeatureName::ChromiumExperimentalDp4a:
             return Feature::ChromiumExperimentalDp4a;
-
-        case wgpu::FeatureName::IndirectFirstInstance:
-            return Feature::InvalidEnum;
     }
     return Feature::InvalidEnum;
 }
@@ -152,6 +155,8 @@
             return wgpu::FeatureName::Depth24UnormStencil8;
         case Feature::Depth32FloatStencil8:
             return wgpu::FeatureName::Depth32FloatStencil8;
+        case Feature::IndirectFirstInstance:
+            return wgpu::FeatureName::IndirectFirstInstance;
         case Feature::ShaderFloat16:
             return wgpu::FeatureName::DawnShaderFloat16;
         case Feature::DawnInternalUsages:
diff --git a/src/dawn/native/Features.h b/src/dawn/native/Features.h
index a87eaea..ebf804e 100644
--- a/src/dawn/native/Features.h
+++ b/src/dawn/native/Features.h
@@ -37,6 +37,7 @@
     Depth24UnormStencil8,
     Depth32FloatStencil8,
     ChromiumExperimentalDp4a,
+    IndirectFirstInstance,
 
     // Dawn-specific
     DawnInternalUsages,
diff --git a/src/dawn/native/IndirectDrawValidationEncoder.cpp b/src/dawn/native/IndirectDrawValidationEncoder.cpp
index 2c20820..0f13182 100644
--- a/src/dawn/native/IndirectDrawValidationEncoder.cpp
+++ b/src/dawn/native/IndirectDrawValidationEncoder.cpp
@@ -43,6 +43,7 @@
 constexpr uint32_t kDuplicateBaseVertexInstance = 1;
 constexpr uint32_t kIndexedDraw = 2;
 constexpr uint32_t kValidationEnabled = 4;
+constexpr uint32_t kIndirectFirstInstanceEnabled = 8;
 
 // Equivalent to the BatchInfo struct defined in the shader below.
 struct BatchInfo {
@@ -64,6 +65,7 @@
             let kDuplicateBaseVertexInstance = 1u;
             let kIndexedDraw = 2u;
             let kValidationEnabled = 4u;
+            let kIndirectFirstInstanceEnabled = 8u;
 
             struct BatchInfo {
                 numIndexBufferElementsLow: u32,
@@ -140,11 +142,13 @@
                 }
 
                 let inputIndex = batch.indirectOffsets[id.x];
-                // firstInstance is always the last parameter
-                let firstInstance = inputParams.data[inputIndex + numIndirectParamsPerDrawCallInput() - 1u];
-                if (firstInstance != 0u) {
-                    fail(id.x);
-                    return;
+                if(!bool(batch.flags & kIndirectFirstInstanceEnabled)) {
+                    // firstInstance is always the last parameter
+                    let firstInstance = inputParams.data[inputIndex + numIndirectParamsPerDrawCallInput() - 1u];
+                    if (firstInstance != 0u) {
+                        fail(id.x);
+                        return;
+                    }
                 }
 
                 if (!bool(batch.flags & kIndexedDraw)) {
@@ -334,6 +338,9 @@
             if (device->IsValidationEnabled()) {
                 newPass.flags |= kValidationEnabled;
             }
+            if (device->IsFeatureEnabled(Feature::IndirectFirstInstance)) {
+                newPass.flags |= kIndirectFirstInstanceEnabled;
+            }
             passes.push_back(std::move(newPass));
         }
     }
diff --git a/src/dawn/native/d3d12/AdapterD3D12.cpp b/src/dawn/native/d3d12/AdapterD3D12.cpp
index 4ac4134..f9a8a93 100644
--- a/src/dawn/native/d3d12/AdapterD3D12.cpp
+++ b/src/dawn/native/d3d12/AdapterD3D12.cpp
@@ -137,6 +137,7 @@
     mSupportedFeatures.EnableFeature(Feature::MultiPlanarFormats);
     mSupportedFeatures.EnableFeature(Feature::Depth24UnormStencil8);
     mSupportedFeatures.EnableFeature(Feature::Depth32FloatStencil8);
+    mSupportedFeatures.EnableFeature(Feature::IndirectFirstInstance);
 
     if (GetBackend()->GetFunctions()->IsDXCAvailable()) {
         uint64_t dxcVersion = 0;
diff --git a/src/dawn/native/metal/BackendMTL.mm b/src/dawn/native/metal/BackendMTL.mm
index 11430de..63d21b1 100644
--- a/src/dawn/native/metal/BackendMTL.mm
+++ b/src/dawn/native/metal/BackendMTL.mm
@@ -386,6 +386,8 @@
         }
 #endif
 
+        mSupportedFeatures.EnableFeature(Feature::IndirectFirstInstance);
+
         return {};
     }
 
diff --git a/src/dawn/native/opengl/BackendGL.cpp b/src/dawn/native/opengl/BackendGL.cpp
index 3164eda..8143eda 100644
--- a/src/dawn/native/opengl/BackendGL.cpp
+++ b/src/dawn/native/opengl/BackendGL.cpp
@@ -247,6 +247,15 @@
             mSupportedFeatures.EnableFeature(Feature::Depth24UnormStencil8);
         }
 
+        // Non-zero baseInstance requires at least desktop OpenGL 4.2, and it is not supported in
+        // OpenGL ES OpenGL:
+        // https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glDrawElementsIndirect.xhtml
+        // OpenGL ES:
+        // https://www.khronos.org/registry/OpenGL-Refpages/es3/html/glDrawElementsIndirect.xhtml
+        if (mFunctions.IsAtLeastGL(4, 2)) {
+            mSupportedFeatures.EnableFeature(Feature::IndirectFirstInstance);
+        }
+
         return {};
     }
 
diff --git a/src/dawn/native/vulkan/AdapterVk.cpp b/src/dawn/native/vulkan/AdapterVk.cpp
index eae25ad..fbdb709 100644
--- a/src/dawn/native/vulkan/AdapterVk.cpp
+++ b/src/dawn/native/vulkan/AdapterVk.cpp
@@ -163,6 +163,10 @@
         mSupportedFeatures.EnableFeature(Feature::Depth32FloatStencil8);
     }
 
+    if (mDeviceInfo.features.drawIndirectFirstInstance == VK_TRUE) {
+        mSupportedFeatures.EnableFeature(Feature::IndirectFirstInstance);
+    }
+
     if (mDeviceInfo.HasExt(DeviceExt::ShaderIntegerDotProduct) &&
         mDeviceInfo.shaderIntegerDotProductProperties
                 .integerDotProduct4x8BitPackedSignedAccelerated == VK_TRUE &&
diff --git a/src/dawn/tests/end2end/FirstIndexOffsetTests.cpp b/src/dawn/tests/end2end/FirstIndexOffsetTests.cpp
index c06416f..6eb45c8 100644
--- a/src/dawn/tests/end2end/FirstIndexOffsetTests.cpp
+++ b/src/dawn/tests/end2end/FirstIndexOffsetTests.cpp
@@ -63,6 +63,13 @@
         DAWN_SUPPRESS_TEST_IF(IsOpenGL() || IsOpenGLES());
     }
 
+    std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
+        if (!SupportsFeatures({wgpu::FeatureName::IndirectFirstInstance})) {
+            return {};
+        }
+        return {wgpu::FeatureName::IndirectFirstInstance};
+    }
+
   private:
     void TestImpl(DrawMode mode,
                   CheckIndex checkIndex,