Add basic supports of storage textures on OpenGL

This patch adds the basic supports of read-only and write-only storage
textures on OpenGL backend. Currently on OpenGL backend we only support
using either a layer of a texture or the entire texture as either read-
only or write-only storage texture.

BUG=dawn:267
TEST=dawn_end2end_tests

Change-Id: I235b98d8d961a17739ea35eec9726dcc80889c4b
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/22180
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Jiawei Shao <jiawei.shao@intel.com>
diff --git a/src/dawn_native/BindGroup.cpp b/src/dawn_native/BindGroup.cpp
index 647587c..d239372 100644
--- a/src/dawn_native/BindGroup.cpp
+++ b/src/dawn_native/BindGroup.cpp
@@ -194,8 +194,6 @@
                 case wgpu::BindingType::ComparisonSampler:
                     DAWN_TRY(ValidateSamplerBinding(device, entry, bindingInfo.type));
                     break;
-                // TODO(jiawei.shao@intel.com): support creating bind group with read-only and
-                // write-only storage textures.
                 case wgpu::BindingType::ReadonlyStorageTexture:
                 case wgpu::BindingType::WriteonlyStorageTexture:
                     DAWN_TRY(ValidateTextureBinding(device, entry, wgpu::TextureUsage::Storage,
diff --git a/src/dawn_native/opengl/BindGroupGL.cpp b/src/dawn_native/opengl/BindGroupGL.cpp
index 383607b..d96baea 100644
--- a/src/dawn_native/opengl/BindGroupGL.cpp
+++ b/src/dawn_native/opengl/BindGroupGL.cpp
@@ -14,11 +14,54 @@
 
 #include "dawn_native/opengl/BindGroupGL.h"
 
+#include "dawn_native/Texture.h"
 #include "dawn_native/opengl/BindGroupLayoutGL.h"
 #include "dawn_native/opengl/DeviceGL.h"
 
 namespace dawn_native { namespace opengl {
 
+    MaybeError ValidateGLBindGroupDescriptor(const BindGroupDescriptor* descriptor) {
+        const BindGroupLayoutBase::BindingMap& bindingMap = descriptor->layout->GetBindingMap();
+        for (uint32_t i = 0; i < descriptor->entryCount; ++i) {
+            const BindGroupEntry& entry = descriptor->entries[i];
+
+            const auto& it = bindingMap.find(BindingNumber(entry.binding));
+            BindingIndex bindingIndex = it->second;
+            ASSERT(bindingIndex < descriptor->layout->GetBindingCount());
+
+            const BindingInfo& bindingInfo = descriptor->layout->GetBindingInfo(bindingIndex);
+            switch (bindingInfo.type) {
+                case wgpu::BindingType::ReadonlyStorageTexture:
+                case wgpu::BindingType::WriteonlyStorageTexture: {
+                    ASSERT(entry.textureView != nullptr);
+                    const uint32_t textureViewLayerCount = entry.textureView->GetLayerCount();
+                    if (textureViewLayerCount != 1 &&
+                        textureViewLayerCount !=
+                            entry.textureView->GetTexture()->GetArrayLayers()) {
+                        return DAWN_VALIDATION_ERROR(
+                            "Currently the OpenGL backend only supports either binding a layer or "
+                            "the entire texture as storage texture.");
+                    }
+                } break;
+
+                case wgpu::BindingType::UniformBuffer:
+                case wgpu::BindingType::StorageBuffer:
+                case wgpu::BindingType::ReadonlyStorageBuffer:
+                case wgpu::BindingType::SampledTexture:
+                case wgpu::BindingType::Sampler:
+                case wgpu::BindingType::ComparisonSampler:
+                    break;
+
+                case wgpu::BindingType::StorageTexture:
+                default:
+                    UNREACHABLE();
+                    break;
+            }
+        }
+
+        return {};
+    }
+
     BindGroup::BindGroup(Device* device, const BindGroupDescriptor* descriptor)
         : BindGroupBase(this, device, descriptor) {
     }
diff --git a/src/dawn_native/opengl/BindGroupGL.h b/src/dawn_native/opengl/BindGroupGL.h
index ad69b64..f9f1151 100644
--- a/src/dawn_native/opengl/BindGroupGL.h
+++ b/src/dawn_native/opengl/BindGroupGL.h
@@ -23,6 +23,8 @@
     class BindGroupLayout;
     class Device;
 
+    MaybeError ValidateGLBindGroupDescriptor(const BindGroupDescriptor* descriptor);
+
     class BindGroup final : public BindGroupBase, public PlacementAllocated {
       public:
         BindGroup(Device* device, const BindGroupDescriptor* descriptor);
diff --git a/src/dawn_native/opengl/CommandBufferGL.cpp b/src/dawn_native/opengl/CommandBufferGL.cpp
index 05a787c..acaafe1 100644
--- a/src/dawn_native/opengl/CommandBufferGL.cpp
+++ b/src/dawn_native/opengl/CommandBufferGL.cpp
@@ -312,9 +312,45 @@
                             break;
                         }
 
-                        case wgpu::BindingType::StorageTexture:
                         case wgpu::BindingType::ReadonlyStorageTexture:
-                        case wgpu::BindingType::WriteonlyStorageTexture:
+                        case wgpu::BindingType::WriteonlyStorageTexture: {
+                            TextureView* view =
+                                ToBackend(group->GetBindingAsTextureView(bindingIndex));
+                            Texture* texture = ToBackend(view->GetTexture());
+                            GLuint handle = texture->GetHandle();
+                            GLuint imageIndex = indices[bindingIndex];
+
+                            GLenum access;
+                            switch (bindingInfo.type) {
+                                case wgpu::BindingType::ReadonlyStorageTexture:
+                                    access = GL_READ_ONLY;
+                                    break;
+                                case wgpu::BindingType::WriteonlyStorageTexture:
+                                    access = GL_WRITE_ONLY;
+                                    break;
+                                default:
+                                    UNREACHABLE();
+                                    break;
+                            }
+
+                            // OpenGL ES only supports either binding a layer or the entire texture
+                            // in glBindImageTexture().
+                            GLboolean isLayered;
+                            if (view->GetLayerCount() == 1) {
+                                isLayered = GL_FALSE;
+                            } else if (texture->GetArrayLayers() == view->GetLayerCount()) {
+                                isLayered = GL_TRUE;
+                            } else {
+                                UNREACHABLE();
+                            }
+
+                            gl.BindImageTexture(imageIndex, handle, view->GetBaseMipLevel(),
+                                                isLayered, view->GetBaseArrayLayer(), access,
+                                                texture->GetGLFormat().internalFormat);
+                            break;
+                        }
+
+                        case wgpu::BindingType::StorageTexture:
                             UNREACHABLE();
                             break;
 
diff --git a/src/dawn_native/opengl/DeviceGL.cpp b/src/dawn_native/opengl/DeviceGL.cpp
index 7e9e42d..e2e81f2 100644
--- a/src/dawn_native/opengl/DeviceGL.cpp
+++ b/src/dawn_native/opengl/DeviceGL.cpp
@@ -94,6 +94,7 @@
 
     ResultOrError<BindGroupBase*> Device::CreateBindGroupImpl(
         const BindGroupDescriptor* descriptor) {
+        DAWN_TRY(ValidateGLBindGroupDescriptor(descriptor));
         return BindGroup::Create(this, descriptor);
     }
     ResultOrError<BindGroupLayoutBase*> Device::CreateBindGroupLayoutImpl(
diff --git a/src/dawn_native/opengl/PipelineGL.cpp b/src/dawn_native/opengl/PipelineGL.cpp
index 926efc5..d19e5be 100644
--- a/src/dawn_native/opengl/PipelineGL.cpp
+++ b/src/dawn_native/opengl/PipelineGL.cpp
@@ -142,9 +142,16 @@
                         // emulation
                         break;
 
-                    case wgpu::BindingType::StorageTexture:
                     case wgpu::BindingType::ReadonlyStorageTexture:
-                    case wgpu::BindingType::WriteonlyStorageTexture:
+                    case wgpu::BindingType::WriteonlyStorageTexture: {
+                        GLint location = gl.GetUniformLocation(mProgram, name.c_str());
+                        if (location != -1) {
+                            gl.Uniform1i(location, indices[group][bindingIndex]);
+                        }
+                        break;
+                    }
+
+                    case wgpu::BindingType::StorageTexture:
                         UNREACHABLE();
                         break;
 
diff --git a/src/dawn_native/opengl/PipelineLayoutGL.cpp b/src/dawn_native/opengl/PipelineLayoutGL.cpp
index 082a25b..d951b83 100644
--- a/src/dawn_native/opengl/PipelineLayoutGL.cpp
+++ b/src/dawn_native/opengl/PipelineLayoutGL.cpp
@@ -26,6 +26,7 @@
         GLuint samplerIndex = 0;
         GLuint sampledTextureIndex = 0;
         GLuint ssboIndex = 0;
+        GLuint storageTextureIndex = 0;
 
         for (uint32_t group : IterateBitSet(GetBindGroupLayoutsMask())) {
             const BindGroupLayoutBase* bgl = GetBindGroupLayout(group);
@@ -53,9 +54,13 @@
                         ssboIndex++;
                         break;
 
-                    case wgpu::BindingType::StorageTexture:
                     case wgpu::BindingType::ReadonlyStorageTexture:
                     case wgpu::BindingType::WriteonlyStorageTexture:
+                        mIndexInfo[group][bindingIndex] = storageTextureIndex;
+                        storageTextureIndex++;
+                        break;
+
+                    case wgpu::BindingType::StorageTexture:
                         UNREACHABLE();
                         break;
 
diff --git a/src/dawn_native/opengl/ShaderModuleGL.cpp b/src/dawn_native/opengl/ShaderModuleGL.cpp
index 1995b75..789da6d 100644
--- a/src/dawn_native/opengl/ShaderModuleGL.cpp
+++ b/src/dawn_native/opengl/ShaderModuleGL.cpp
@@ -176,12 +176,26 @@
                 BindingNumber bindingNumber = it.first;
                 const auto& info = it.second;
 
+                uint32_t resourceId;
+                switch (info.type) {
+                    // When the resource is a uniform or shader storage block, we should change the
+                    // block name instead of the instance name.
+                    case wgpu::BindingType::ReadonlyStorageBuffer:
+                    case wgpu::BindingType::StorageBuffer:
+                    case wgpu::BindingType::UniformBuffer:
+                        resourceId = info.base_type_id;
+                        break;
+                    default:
+                        resourceId = info.id;
+                        break;
+                }
+
                 if (GetDevice()->IsToggleEnabled(Toggle::UseSpvc)) {
-                    mSpvcContext.SetName(info.base_type_id, GetBindingName(group, bindingNumber));
+                    mSpvcContext.SetName(resourceId, GetBindingName(group, bindingNumber));
                     mSpvcContext.UnsetDecoration(info.id, shaderc_spvc_decoration_binding);
                     mSpvcContext.UnsetDecoration(info.id, shaderc_spvc_decoration_descriptorset);
                 } else {
-                    compiler->set_name(info.base_type_id, GetBindingName(group, bindingNumber));
+                    compiler->set_name(resourceId, GetBindingName(group, bindingNumber));
                     compiler->unset_decoration(info.id, spv::DecorationBinding);
                     compiler->unset_decoration(info.id, spv::DecorationDescriptorSet);
                 }
diff --git a/src/tests/end2end/StorageTextureTests.cpp b/src/tests/end2end/StorageTextureTests.cpp
index 03daee5..799f9ec 100644
--- a/src/tests/end2end/StorageTextureTests.cpp
+++ b/src/tests/end2end/StorageTextureTests.cpp
@@ -342,9 +342,6 @@
 
 // Test that read-only storage textures are supported in compute shader.
 TEST_P(StorageTextureTests, ReadonlyStorageTextureInComputeShader) {
-    // TODO(jiawei.shao@intel.com): support read-only storage texture on OpenGL.
-    DAWN_SKIP_TEST_IF(IsOpenGL());
-
     // When we run dawn_end2end_tests with "--use-spvc-parser", extracting the binding type of a
     // read-only image will always return shaderc_spvc_binding_type_writeonly_storage_texture.
     // TODO(jiawei.shao@intel.com): enable this test when we specify "--use-spvc-parser" after the
@@ -380,9 +377,6 @@
 
 // Test that read-only storage textures are supported in vertex shader.
 TEST_P(StorageTextureTests, ReadonlyStorageTextureInVertexShader) {
-    // TODO(jiawei.shao@intel.com): support read-only storage texture on OpenGL.
-    DAWN_SKIP_TEST_IF(IsOpenGL());
-
     // When we run dawn_end2end_tests with "--use-spvc-parser", extracting the binding type of a
     // read-only image will always return shaderc_spvc_binding_type_writeonly_storage_texture.
     // TODO(jiawei.shao@intel.com): enable this test when we specify "--use-spvc-parser" after the
@@ -424,9 +418,6 @@
 
 // Test that read-only storage textures are supported in fragment shader.
 TEST_P(StorageTextureTests, ReadonlyStorageTextureInFragmentShader) {
-    // TODO(jiawei.shao@intel.com): support read-only storage texture on OpenGL.
-    DAWN_SKIP_TEST_IF(IsOpenGL());
-
     // When we run dawn_end2end_tests with "--use-spvc-parser", extracting the binding type of a
     // read-only image will always return shaderc_spvc_binding_type_writeonly_storage_texture.
     // TODO(jiawei.shao@intel.com): enable this test when we specify "--use-spvc-parser" after the
@@ -460,9 +451,6 @@
 
 // Test that write-only storage textures are supported in compute shader.
 TEST_P(StorageTextureTests, WriteonlyStorageTextureInComputeShader) {
-    // TODO(jiawei.shao@intel.com): support read-only storage texture on D3D12 and OpenGL.
-    DAWN_SKIP_TEST_IF(IsOpenGL());
-
     // When we run dawn_end2end_tests with "--use-spvc-parser", extracting the binding type of a
     // read-only image will always return shaderc_spvc_binding_type_writeonly_storage_texture.
     // TODO(jiawei.shao@intel.com): enable this test when we specify "--use-spvc-parser" after the
@@ -482,9 +470,6 @@
 
 // Test that write-only storage textures are supported in fragment shader.
 TEST_P(StorageTextureTests, WriteonlyStorageTextureInFragmentShader) {
-    // TODO(jiawei.shao@intel.com): support read-only storage texture on D3D12 and OpenGL.
-    DAWN_SKIP_TEST_IF(IsOpenGL());
-
     // When we run dawn_end2end_tests with "--use-spvc-parser", extracting the binding type of a
     // read-only image will always return shaderc_spvc_binding_type_writeonly_storage_texture.
     // TODO(jiawei.shao@intel.com): enable this test when we specify "--use-spvc-parser" after the
@@ -504,9 +489,6 @@
 
 // Verify 2D array read-only storage texture works correctly.
 TEST_P(StorageTextureTests, Readonly2DArrayStorageTexture) {
-    // TODO(jiawei.shao@intel.com): support read-only storage texture on OpenGL.
-    DAWN_SKIP_TEST_IF(IsOpenGL());
-
     // When we run dawn_end2end_tests with "--use-spvc-parser", extracting the binding type of a
     // read-only image will always return shaderc_spvc_binding_type_writeonly_storage_texture.
     // TODO(jiawei.shao@intel.com): enable this test when we specify "--use-spvc-parser" after the
@@ -556,9 +538,6 @@
 
 // Verify 2D array write-only storage texture works correctly.
 TEST_P(StorageTextureTests, Writeonly2DArrayStorageTexture) {
-    // TODO(jiawei.shao@intel.com): support write-only storage texture on D3D12 and OpenGL.
-    DAWN_SKIP_TEST_IF(IsOpenGL());
-
     // When we run dawn_end2end_tests with "--use-spvc-parser", extracting the binding type of a
     // read-only image will always return shaderc_spvc_binding_type_writeonly_storage_texture.
     // TODO(jiawei.shao@intel.com): enable this test when we specify "--use-spvc-parser" after the
@@ -633,9 +612,6 @@
 // Verify that the texture is correctly cleared to 0 before its first usage as a read-only storage
 // texture in a render pass.
 TEST_P(StorageTextureZeroInitTests, ReadonlyStorageTextureClearsToZeroInRenderPass) {
-    // TODO(jiawei.shao@intel.com): support read-only storage texture on OpenGL.
-    DAWN_SKIP_TEST_IF(IsOpenGL());
-
     // When we run dawn_end2end_tests with "--use-spvc-parser", extracting the binding type of a
     // read-only image will always return shaderc_spvc_binding_type_writeonly_storage_texture.
     // TODO(jiawei.shao@intel.com): enable this test when we specify "--use-spvc-parser" after the
@@ -668,9 +644,6 @@
 // Verify that the texture is correctly cleared to 0 before its first usage as a read-only storage
 // texture in a compute pass.
 TEST_P(StorageTextureZeroInitTests, ReadonlyStorageTextureClearsToZeroInComputePass) {
-    // TODO(jiawei.shao@intel.com): support read-only storage texture on OpenGL.
-    DAWN_SKIP_TEST_IF(IsOpenGL());
-
     // When we run dawn_end2end_tests with "--use-spvc-parser", extracting the binding type of a
     // read-only image will always return shaderc_spvc_binding_type_writeonly_storage_texture.
     // TODO(jiawei.shao@intel.com): enable this test when we specify "--use-spvc-parser" after the
@@ -704,9 +677,6 @@
 // Verify that the texture is correctly cleared to 0 before its first usage as a write-only storage
 // storage texture in a render pass.
 TEST_P(StorageTextureZeroInitTests, WriteonlyStorageTextureClearsToZeroInRenderPass) {
-    // TODO(jiawei.shao@intel.com): support read-only storage texture on D3D12 and OpenGL.
-    DAWN_SKIP_TEST_IF(IsOpenGL());
-
     // When we run dawn_end2end_tests with "--use-spvc-parser", extracting the binding type of a
     // read-only image will always return shaderc_spvc_binding_type_writeonly_storage_texture.
     // TODO(jiawei.shao@intel.com): enable this test when we specify "--use-spvc-parser" after the
@@ -726,9 +696,6 @@
 // Verify that the texture is correctly cleared to 0 before its first usage as a write-only storage
 // texture in a compute pass.
 TEST_P(StorageTextureZeroInitTests, WriteonlyStorageTextureClearsToZeroInComputePass) {
-    // TODO(jiawei.shao@intel.com): support read-only storage texture on D3D12 and OpenGL.
-    DAWN_SKIP_TEST_IF(IsOpenGL());
-
     // When we run dawn_end2end_tests with "--use-spvc-parser", extracting the binding type of a
     // read-only image will always return shaderc_spvc_binding_type_writeonly_storage_texture.
     // TODO(jiawei.shao@intel.com): enable this test when we specify "--use-spvc-parser" after the