OpenGL: Use non-filtering samplers for int/uint texture.

Using a sampler with filtering on int / uint textures makes them
incomplete, causing them to sample black on very conformant drivers.
Each opengl::Sampler is updated to create to GL sampler, a filtering one
and a non-filtering one.

PipelineGL and CommandBufferGL takes advantage of the new
BGLBinding::textureComponentType to know which of the two samplers to
use.

BUG=dawn:128

Change-Id: Idbf5668213bbe6a8639847d57e2be1244f97800c
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/10282
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Austin Eng <enga@chromium.org>
diff --git a/src/dawn_native/opengl/CommandBufferGL.cpp b/src/dawn_native/opengl/CommandBufferGL.cpp
index 9fafb6f..2dccfe5 100644
--- a/src/dawn_native/opengl/CommandBufferGL.cpp
+++ b/src/dawn_native/opengl/CommandBufferGL.cpp
@@ -246,12 +246,18 @@
                     } break;
 
                     case dawn::BindingType::Sampler: {
-                        GLuint sampler =
-                            ToBackend(group->GetBindingAsSampler(bindingIndex))->GetHandle();
+                        Sampler* sampler = ToBackend(group->GetBindingAsSampler(bindingIndex));
                         GLuint samplerIndex = indices[bindingIndex];
 
-                        for (auto unit : pipeline->GetTextureUnitsForSampler(samplerIndex)) {
-                            gl.BindSampler(unit, sampler);
+                        for (PipelineGL::SamplerUnit unit :
+                             pipeline->GetTextureUnitsForSampler(samplerIndex)) {
+                            // Only use filtering for certain texture units, because int and uint
+                            // texture are only complete without filtering
+                            if (unit.shouldUseFiltering) {
+                                gl.BindSampler(unit.unit, sampler->GetFilteringHandle());
+                            } else {
+                                gl.BindSampler(unit.unit, sampler->GetNonFilteringHandle());
+                            }
                         }
                     } break;
 
diff --git a/src/dawn_native/opengl/PipelineGL.cpp b/src/dawn_native/opengl/PipelineGL.cpp
index e04dbc7..f7adf95 100644
--- a/src/dawn_native/opengl/PipelineGL.cpp
+++ b/src/dawn_native/opengl/PipelineGL.cpp
@@ -173,20 +173,27 @@
 
                 gl.Uniform1i(location, textureUnit);
 
-                GLuint samplerIndex =
-                    indices[combined.samplerLocation.group][combined.samplerLocation.binding];
-                mUnitsForSamplers[samplerIndex].push_back(textureUnit);
-
                 GLuint textureIndex =
                     indices[combined.textureLocation.group][combined.textureLocation.binding];
                 mUnitsForTextures[textureIndex].push_back(textureUnit);
 
+                dawn::TextureComponentType componentType =
+                    layout->GetBindGroupLayout(combined.textureLocation.group)
+                        ->GetBindingInfo()
+                        .textureComponentTypes[combined.textureLocation.binding];
+                bool shouldUseFiltering = componentType == dawn::TextureComponentType::Float;
+
+                GLuint samplerIndex =
+                    indices[combined.samplerLocation.group][combined.samplerLocation.binding];
+                mUnitsForSamplers[samplerIndex].push_back({textureUnit, shouldUseFiltering});
+
                 textureUnit++;
             }
         }
     }
 
-    const std::vector<GLuint>& PipelineGL::GetTextureUnitsForSampler(GLuint index) const {
+    const std::vector<PipelineGL::SamplerUnit>& PipelineGL::GetTextureUnitsForSampler(
+        GLuint index) const {
         ASSERT(index < mUnitsForSamplers.size());
         return mUnitsForSamplers[index];
     }
diff --git a/src/dawn_native/opengl/PipelineGL.h b/src/dawn_native/opengl/PipelineGL.h
index b574589..6a081c1 100644
--- a/src/dawn_native/opengl/PipelineGL.h
+++ b/src/dawn_native/opengl/PipelineGL.h
@@ -39,7 +39,13 @@
         using BindingLocations =
             std::array<std::array<GLint, kMaxBindingsPerGroup>, kMaxBindGroups>;
 
-        const std::vector<GLuint>& GetTextureUnitsForSampler(GLuint index) const;
+        // For each unit a sampler is bound to we need to know if we should use filtering or not
+        // because int and uint texture are only complete without filtering.
+        struct SamplerUnit {
+            GLuint unit;
+            bool shouldUseFiltering;
+        };
+        const std::vector<SamplerUnit>& GetTextureUnitsForSampler(GLuint index) const;
         const std::vector<GLuint>& GetTextureUnitsForTextureView(GLuint index) const;
         GLuint GetProgramHandle() const;
 
@@ -47,7 +53,7 @@
 
       private:
         GLuint mProgram;
-        std::vector<std::vector<GLuint>> mUnitsForSamplers;
+        std::vector<std::vector<SamplerUnit>> mUnitsForSamplers;
         std::vector<std::vector<GLuint>> mUnitsForTextures;
     };
 
diff --git a/src/dawn_native/opengl/SamplerGL.cpp b/src/dawn_native/opengl/SamplerGL.cpp
index 832a4ff..17bf353 100644
--- a/src/dawn_native/opengl/SamplerGL.cpp
+++ b/src/dawn_native/opengl/SamplerGL.cpp
@@ -76,26 +76,53 @@
         : SamplerBase(device, descriptor) {
         const OpenGLFunctions& gl = ToBackend(GetDevice())->gl;
 
-        gl.GenSamplers(1, &mHandle);
-        gl.SamplerParameteri(mHandle, GL_TEXTURE_MAG_FILTER, MagFilterMode(descriptor->magFilter));
-        gl.SamplerParameteri(mHandle, GL_TEXTURE_MIN_FILTER,
-                             MinFilterMode(descriptor->minFilter, descriptor->mipmapFilter));
-        gl.SamplerParameteri(mHandle, GL_TEXTURE_WRAP_R, WrapMode(descriptor->addressModeW));
-        gl.SamplerParameteri(mHandle, GL_TEXTURE_WRAP_S, WrapMode(descriptor->addressModeU));
-        gl.SamplerParameteri(mHandle, GL_TEXTURE_WRAP_T, WrapMode(descriptor->addressModeV));
+        gl.GenSamplers(1, &mFilteringHandle);
+        SetupGLSampler(mFilteringHandle, descriptor, false);
 
-        gl.SamplerParameterf(mHandle, GL_TEXTURE_MIN_LOD, descriptor->lodMinClamp);
-        gl.SamplerParameterf(mHandle, GL_TEXTURE_MAX_LOD, descriptor->lodMaxClamp);
+        gl.GenSamplers(1, &mNonFilteringHandle);
+        SetupGLSampler(mNonFilteringHandle, descriptor, true);
+    }
+
+    Sampler::~Sampler() {
+        const OpenGLFunctions& gl = ToBackend(GetDevice())->gl;
+        gl.DeleteSamplers(1, &mFilteringHandle);
+        gl.DeleteSamplers(1, &mNonFilteringHandle);
+    }
+
+    void Sampler::SetupGLSampler(GLuint sampler,
+                                 const SamplerDescriptor* descriptor,
+                                 bool forceNearest) {
+        const OpenGLFunctions& gl = ToBackend(GetDevice())->gl;
+
+        if (forceNearest) {
+            gl.SamplerParameteri(sampler, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+            gl.SamplerParameteri(sampler, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
+        } else {
+            gl.SamplerParameteri(sampler, GL_TEXTURE_MAG_FILTER,
+                                 MagFilterMode(descriptor->magFilter));
+            gl.SamplerParameteri(sampler, GL_TEXTURE_MIN_FILTER,
+                                 MinFilterMode(descriptor->minFilter, descriptor->mipmapFilter));
+        }
+        gl.SamplerParameteri(sampler, GL_TEXTURE_WRAP_R, WrapMode(descriptor->addressModeW));
+        gl.SamplerParameteri(sampler, GL_TEXTURE_WRAP_S, WrapMode(descriptor->addressModeU));
+        gl.SamplerParameteri(sampler, GL_TEXTURE_WRAP_T, WrapMode(descriptor->addressModeV));
+
+        gl.SamplerParameterf(sampler, GL_TEXTURE_MIN_LOD, descriptor->lodMinClamp);
+        gl.SamplerParameterf(sampler, GL_TEXTURE_MAX_LOD, descriptor->lodMaxClamp);
 
         if (ToOpenGLCompareFunction(descriptor->compare) != GL_NEVER) {
-            gl.SamplerParameteri(mHandle, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
-            gl.SamplerParameteri(mHandle, GL_TEXTURE_COMPARE_FUNC,
+            gl.SamplerParameteri(sampler, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
+            gl.SamplerParameteri(sampler, GL_TEXTURE_COMPARE_FUNC,
                                  ToOpenGLCompareFunction(descriptor->compare));
         }
     }
 
-    GLuint Sampler::GetHandle() const {
-        return mHandle;
+    GLuint Sampler::GetFilteringHandle() const {
+        return mFilteringHandle;
+    }
+
+    GLuint Sampler::GetNonFilteringHandle() const {
+        return mNonFilteringHandle;
     }
 
 }}  // namespace dawn_native::opengl
diff --git a/src/dawn_native/opengl/SamplerGL.h b/src/dawn_native/opengl/SamplerGL.h
index 3c4911b..19b185b 100644
--- a/src/dawn_native/opengl/SamplerGL.h
+++ b/src/dawn_native/opengl/SamplerGL.h
@@ -26,11 +26,19 @@
     class Sampler : public SamplerBase {
       public:
         Sampler(Device* device, const SamplerDescriptor* descriptor);
+        ~Sampler();
 
-        GLuint GetHandle() const;
+        GLuint GetFilteringHandle() const;
+        GLuint GetNonFilteringHandle() const;
 
       private:
-        GLuint mHandle;
+        void SetupGLSampler(GLuint sampler, const SamplerDescriptor* descriptor, bool forceNearest);
+
+        GLuint mFilteringHandle;
+
+        // This is a sampler equivalent to mFilteringHandle except that it uses NEAREST filtering
+        // for everything, which is important to preserve texture completeness for u/int textures.
+        GLuint mNonFilteringHandle;
     };
 
 }}  // namespace dawn_native::opengl
diff --git a/src/tests/end2end/TextureFormatTests.cpp b/src/tests/end2end/TextureFormatTests.cpp
index b530c696..79029cf 100644
--- a/src/tests/end2end/TextureFormatTests.cpp
+++ b/src/tests/end2end/TextureFormatTests.cpp
@@ -498,146 +498,92 @@
 
 // Test the R8Uint format
 TEST_P(TextureFormatTest, R8Uint) {
-    // TODO(cwallez@chromium.org): This fails on the Intel GL driver, understand why.
-    DAWN_SKIP_TEST_IF(IsOpenGL() && IsIntel());
-
     DoUintTest<uint8_t>({dawn::TextureFormat::R8Uint, 1, dawn::TextureComponentType::Uint, 1});
 }
 
 // Test the RG8Uint format
 TEST_P(TextureFormatTest, RG8Uint) {
-    // TODO(cwallez@chromium.org): This fails on the Intel GL driver, understand why.
-    DAWN_SKIP_TEST_IF(IsOpenGL() && IsIntel());
-
     DoUintTest<uint8_t>({dawn::TextureFormat::RG8Uint, 2, dawn::TextureComponentType::Uint, 2});
 }
 
 // Test the RGBA8Uint format
 TEST_P(TextureFormatTest, RGBA8Uint) {
-    // TODO(cwallez@chromium.org): This fails on the Intel GL driver, understand why.
-    DAWN_SKIP_TEST_IF(IsOpenGL() && IsIntel());
-
     DoUintTest<uint8_t>({dawn::TextureFormat::RGBA8Uint, 4, dawn::TextureComponentType::Uint, 4});
 }
 
 // Test the R16Uint format
 TEST_P(TextureFormatTest, R16Uint) {
-    // TODO(cwallez@chromium.org): This fails on the Intel GL driver, understand why.
-    DAWN_SKIP_TEST_IF(IsOpenGL() && IsIntel());
-
     DoUintTest<uint16_t>({dawn::TextureFormat::R16Uint, 2, dawn::TextureComponentType::Uint, 1});
 }
 
 // Test the RG16Uint format
 TEST_P(TextureFormatTest, RG16Uint) {
-    // TODO(cwallez@chromium.org): This fails on the Intel GL driver, understand why.
-    DAWN_SKIP_TEST_IF(IsOpenGL() && IsIntel());
-
     DoUintTest<uint16_t>({dawn::TextureFormat::RG16Uint, 4, dawn::TextureComponentType::Uint, 2});
 }
 
 // Test the RGBA16Uint format
 TEST_P(TextureFormatTest, RGBA16Uint) {
-    // TODO(cwallez@chromium.org): This fails on the Intel GL driver, understand why.
-    DAWN_SKIP_TEST_IF(IsOpenGL() && IsIntel());
-
     DoUintTest<uint16_t>({dawn::TextureFormat::RGBA16Uint, 8, dawn::TextureComponentType::Uint, 4});
 }
 
 // Test the R32Uint format
 TEST_P(TextureFormatTest, R32Uint) {
-    // TODO(cwallez@chromium.org): This fails on the Intel GL driver, understand why.
-    DAWN_SKIP_TEST_IF(IsOpenGL() && IsIntel());
-
     DoUintTest<uint32_t>({dawn::TextureFormat::R32Uint, 4, dawn::TextureComponentType::Uint, 1});
 }
 
 // Test the RG32Uint format
 TEST_P(TextureFormatTest, RG32Uint) {
-    // TODO(cwallez@chromium.org): This fails on the Intel GL driver, understand why.
-    DAWN_SKIP_TEST_IF(IsOpenGL() && IsIntel());
-
     DoUintTest<uint32_t>({dawn::TextureFormat::RG32Uint, 8, dawn::TextureComponentType::Uint, 2});
 }
 
 // Test the RGBA32Uint format
 TEST_P(TextureFormatTest, RGBA32Uint) {
-    // TODO(cwallez@chromium.org): This fails on the Intel GL driver, understand why.
-    DAWN_SKIP_TEST_IF(IsOpenGL() && IsIntel());
-
     DoUintTest<uint32_t>(
         {dawn::TextureFormat::RGBA32Uint, 16, dawn::TextureComponentType::Uint, 4});
 }
 
 // Test the R8Sint format
 TEST_P(TextureFormatTest, R8Sint) {
-    // TODO(cwallez@chromium.org): This fails on the Intel GL driver, understand why.
-    DAWN_SKIP_TEST_IF(IsOpenGL() && IsIntel());
-
     DoSintTest<int8_t>({dawn::TextureFormat::R8Sint, 1, dawn::TextureComponentType::Sint, 1});
 }
 
 // Test the RG8Sint format
 TEST_P(TextureFormatTest, RG8Sint) {
-    // TODO(cwallez@chromium.org): This fails on the Intel GL driver, understand why.
-    DAWN_SKIP_TEST_IF(IsOpenGL() && IsIntel());
-
     DoSintTest<int8_t>({dawn::TextureFormat::RG8Sint, 2, dawn::TextureComponentType::Sint, 2});
 }
 
 // Test the RGBA8Sint format
 TEST_P(TextureFormatTest, RGBA8Sint) {
-    // TODO(cwallez@chromium.org): This fails on the Intel GL driver, understand why.
-    DAWN_SKIP_TEST_IF(IsOpenGL() && IsIntel());
-
     DoSintTest<int8_t>({dawn::TextureFormat::RGBA8Sint, 4, dawn::TextureComponentType::Sint, 4});
 }
 
 // Test the R16Sint format
 TEST_P(TextureFormatTest, R16Sint) {
-    // TODO(cwallez@chromium.org): This fails on the Intel GL driver, understand why.
-    DAWN_SKIP_TEST_IF(IsOpenGL() && IsIntel());
-
     DoSintTest<int16_t>({dawn::TextureFormat::R16Sint, 2, dawn::TextureComponentType::Sint, 1});
 }
 
 // Test the RG16Sint format
 TEST_P(TextureFormatTest, RG16Sint) {
-    // TODO(cwallez@chromium.org): This fails on the Intel GL driver, understand why.
-    DAWN_SKIP_TEST_IF(IsOpenGL() && IsIntel());
-
     DoSintTest<int16_t>({dawn::TextureFormat::RG16Sint, 4, dawn::TextureComponentType::Sint, 2});
 }
 
 // Test the RGBA16Sint format
 TEST_P(TextureFormatTest, RGBA16Sint) {
-    // TODO(cwallez@chromium.org): This fails on the Intel GL driver, understand why.
-    DAWN_SKIP_TEST_IF(IsOpenGL() && IsIntel());
-
     DoSintTest<int16_t>({dawn::TextureFormat::RGBA16Sint, 8, dawn::TextureComponentType::Sint, 4});
 }
 
 // Test the R32Sint format
 TEST_P(TextureFormatTest, R32Sint) {
-    // TODO(cwallez@chromium.org): This fails on the Intel GL driver, understand why.
-    DAWN_SKIP_TEST_IF(IsOpenGL() && IsIntel());
-
     DoSintTest<int32_t>({dawn::TextureFormat::R32Sint, 4, dawn::TextureComponentType::Sint, 1});
 }
 
 // Test the RG32Sint format
 TEST_P(TextureFormatTest, RG32Sint) {
-    // TODO(cwallez@chromium.org): This fails on the Intel GL driver, understand why.
-    DAWN_SKIP_TEST_IF(IsOpenGL() && IsIntel());
-
     DoSintTest<int32_t>({dawn::TextureFormat::RG32Sint, 8, dawn::TextureComponentType::Sint, 2});
 }
 
 // Test the RGBA32Sint format
 TEST_P(TextureFormatTest, RGBA32Sint) {
-    // TODO(cwallez@chromium.org): This fails on the Intel GL driver, understand why.
-    DAWN_SKIP_TEST_IF(IsOpenGL() && IsIntel());
-
     DoSintTest<int32_t>({dawn::TextureFormat::RGBA32Sint, 16, dawn::TextureComponentType::Sint, 4});
 }