Move SPIRV-Cross functions into OpenGL backend.

Since the other backends no longer need it, move all of the
utility functions specific to SPIRV-Cross into the OpenGL backend.
This obviates the need for the DAWN_USE_SPIRV_CROSS define, so remove it.

Bug: dawn:1036

Change-Id: I67bb5a85dc128a6f343d09876046cf559395e05f
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/61541
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Stephen White <senorblanco@chromium.org>
diff --git a/src/common/BUILD.gn b/src/common/BUILD.gn
index ebb7018..afaa18f 100644
--- a/src/common/BUILD.gn
+++ b/src/common/BUILD.gn
@@ -85,10 +85,6 @@
     defines += [ "DAWN_USE_X11" ]
   }
 
-  if (dawn_use_spirv_cross) {
-    defines += [ "DAWN_USE_SPIRV_CROSS" ]
-  }
-
   if (dawn_enable_error_injection) {
     defines += [ "DAWN_ENABLE_ERROR_INJECTION" ]
   }
diff --git a/src/dawn_native/BUILD.gn b/src/dawn_native/BUILD.gn
index 27731a5..d44de36 100644
--- a/src/dawn_native/BUILD.gn
+++ b/src/dawn_native/BUILD.gn
@@ -313,13 +313,6 @@
     ]
   }
 
-  if (dawn_use_spirv_cross) {
-    sources += [
-      "SpirvUtils.cpp",
-      "SpirvUtils.h",
-    ]
-  }
-
   # Only win32 app needs to link with user32.lib
   # In UWP, all availiable APIs are defined in WindowsApp.lib
   if (is_win && !dawn_is_winuwp) {
@@ -527,6 +520,8 @@
       "opengl/SamplerGL.h",
       "opengl/ShaderModuleGL.cpp",
       "opengl/ShaderModuleGL.h",
+      "opengl/SpirvUtils.cpp",
+      "opengl/SpirvUtils.h",
       "opengl/SwapChainGL.cpp",
       "opengl/SwapChainGL.h",
       "opengl/TextureGL.cpp",
diff --git a/src/dawn_native/ShaderModule.cpp b/src/dawn_native/ShaderModule.cpp
index 87af445..96ae1ec 100644
--- a/src/dawn_native/ShaderModule.cpp
+++ b/src/dawn_native/ShaderModule.cpp
@@ -24,16 +24,10 @@
 #include "dawn_native/Pipeline.h"
 #include "dawn_native/PipelineLayout.h"
 #include "dawn_native/RenderPipeline.h"
-#if defined(DAWN_USE_SPIRV_CROSS)
-#    include "dawn_native/SpirvUtils.h"
-#endif
 #include "dawn_native/TintUtils.h"
 
 #include <spirv-tools/libspirv.hpp>
 #include <spirv-tools/optimizer.hpp>
-#if defined(DAWN_USE_SPIRV_CROSS)
-#    include <spirv_cross.hpp>
-#endif
 
 // Tint include must be after spirv_cross.hpp, because spirv-cross has its own
 // version of spirv_headers. We also need to undef SPV_REVISION because SPIRV-Cross
@@ -679,252 +673,6 @@
             return {};
         }
 
-#if defined(DAWN_USE_SPIRV_CROSS)
-        ResultOrError<std::unique_ptr<EntryPointMetadata>> ExtractSpirvInfo(
-            const DeviceBase* device,
-            const spirv_cross::Compiler& compiler,
-            const std::string& entryPointName,
-            SingleShaderStage stage) {
-            std::unique_ptr<EntryPointMetadata> metadata = std::make_unique<EntryPointMetadata>();
-            metadata->stage = stage;
-
-            const auto& resources = compiler.get_shader_resources();
-
-            if (resources.push_constant_buffers.size() > 0) {
-                return DAWN_VALIDATION_ERROR("Push constants aren't supported.");
-            }
-
-            if (resources.sampled_images.size() > 0) {
-                return DAWN_VALIDATION_ERROR("Combined images and samplers aren't supported.");
-            }
-
-            // Fill in bindingInfo with the SPIRV bindings
-            auto ExtractResourcesBinding =
-                [](const DeviceBase* device,
-                   const spirv_cross::SmallVector<spirv_cross::Resource>& resources,
-                   const spirv_cross::Compiler& compiler, BindingInfoType bindingType,
-                   EntryPointMetadata::BindingInfoArray* metadataBindings,
-                   bool isStorageBuffer = false) -> MaybeError {
-                for (const auto& resource : resources) {
-                    if (!compiler.get_decoration_bitset(resource.id).get(spv::DecorationBinding)) {
-                        return DAWN_VALIDATION_ERROR("No Binding decoration set for resource");
-                    }
-
-                    if (!compiler.get_decoration_bitset(resource.id)
-                             .get(spv::DecorationDescriptorSet)) {
-                        return DAWN_VALIDATION_ERROR("No Descriptor Decoration set for resource");
-                    }
-
-                    BindingNumber bindingNumber(
-                        compiler.get_decoration(resource.id, spv::DecorationBinding));
-                    BindGroupIndex bindGroupIndex(
-                        compiler.get_decoration(resource.id, spv::DecorationDescriptorSet));
-
-                    if (bindGroupIndex >= kMaxBindGroupsTyped) {
-                        return DAWN_VALIDATION_ERROR("Bind group index over limits in the SPIRV");
-                    }
-
-                    const auto& it = (*metadataBindings)[bindGroupIndex].emplace(
-                        bindingNumber, EntryPointMetadata::ShaderBindingInfo{});
-                    if (!it.second) {
-                        return DAWN_VALIDATION_ERROR("Shader has duplicate bindings");
-                    }
-
-                    EntryPointMetadata::ShaderBindingInfo* info = &it.first->second;
-                    info->id = resource.id;
-                    info->base_type_id = resource.base_type_id;
-                    info->bindingType = bindingType;
-
-                    switch (bindingType) {
-                        case BindingInfoType::Texture: {
-                            spirv_cross::SPIRType::ImageType imageType =
-                                compiler.get_type(info->base_type_id).image;
-                            spirv_cross::SPIRType::BaseType textureComponentType =
-                                compiler.get_type(imageType.type).basetype;
-
-                            info->texture.viewDimension =
-                                SpirvDimToTextureViewDimension(imageType.dim, imageType.arrayed);
-                            info->texture.multisampled = imageType.ms;
-                            info->texture.compatibleSampleTypes =
-                                SpirvBaseTypeToSampleTypeBit(textureComponentType);
-
-                            if (imageType.depth) {
-                                if ((info->texture.compatibleSampleTypes & SampleTypeBit::Float) ==
-                                    0) {
-                                    return DAWN_VALIDATION_ERROR(
-                                        "Depth textures must have a float type");
-                                }
-                                info->texture.compatibleSampleTypes = SampleTypeBit::Depth;
-                            }
-
-                            if (imageType.ms && imageType.arrayed) {
-                                return DAWN_VALIDATION_ERROR(
-                                    "Multisampled array textures aren't supported");
-                            }
-                            break;
-                        }
-                        case BindingInfoType::Buffer: {
-                            // Determine buffer size, with a minimum of 1 element in the runtime
-                            // array
-                            spirv_cross::SPIRType type = compiler.get_type(info->base_type_id);
-                            info->buffer.minBindingSize =
-                                compiler.get_declared_struct_size_runtime_array(type, 1);
-
-                            // Differentiate between readonly storage bindings and writable ones
-                            // based on the NonWritable decoration.
-                            // TODO(dawn:527): Could isStorageBuffer be determined by calling
-                            // compiler.get_storage_class(resource.id)?
-                            if (isStorageBuffer) {
-                                spirv_cross::Bitset flags =
-                                    compiler.get_buffer_block_flags(resource.id);
-                                if (flags.get(spv::DecorationNonWritable)) {
-                                    info->buffer.type = wgpu::BufferBindingType::ReadOnlyStorage;
-                                } else {
-                                    info->buffer.type = wgpu::BufferBindingType::Storage;
-                                }
-                            } else {
-                                info->buffer.type = wgpu::BufferBindingType::Uniform;
-                            }
-                            break;
-                        }
-                        case BindingInfoType::StorageTexture: {
-                            spirv_cross::Bitset flags = compiler.get_decoration_bitset(resource.id);
-                            if (flags.get(spv::DecorationNonReadable)) {
-                                info->storageTexture.access = wgpu::StorageTextureAccess::WriteOnly;
-                            } else if (flags.get(spv::DecorationNonWritable)) {
-                                info->storageTexture.access = wgpu::StorageTextureAccess::ReadOnly;
-                            } else {
-                                return DAWN_VALIDATION_ERROR(
-                                    "Read-write storage textures are not supported");
-                            }
-
-                            spirv_cross::SPIRType::ImageType imageType =
-                                compiler.get_type(info->base_type_id).image;
-                            wgpu::TextureFormat storageTextureFormat =
-                                SpirvImageFormatToTextureFormat(imageType.format);
-                            if (storageTextureFormat == wgpu::TextureFormat::Undefined) {
-                                return DAWN_VALIDATION_ERROR(
-                                    "Invalid image format declaration on storage image");
-                            }
-                            const Format& format =
-                                device->GetValidInternalFormat(storageTextureFormat);
-                            if (!format.supportsStorageUsage) {
-                                return DAWN_VALIDATION_ERROR(
-                                    "The storage texture format is not supported");
-                            }
-                            if (imageType.ms) {
-                                return DAWN_VALIDATION_ERROR(
-                                    "Multisampled storage textures aren't supported");
-                            }
-                            if (imageType.depth) {
-                                return DAWN_VALIDATION_ERROR(
-                                    "Depth storage textures aren't supported");
-                            }
-                            info->storageTexture.format = storageTextureFormat;
-                            info->storageTexture.viewDimension =
-                                SpirvDimToTextureViewDimension(imageType.dim, imageType.arrayed);
-                            break;
-                        }
-                        case BindingInfoType::Sampler: {
-                            info->sampler.isComparison = false;
-                            break;
-                        }
-                        case BindingInfoType::ExternalTexture: {
-                            return DAWN_VALIDATION_ERROR("External textures are not supported.");
-                        }
-                    }
-                }
-                return {};
-            };
-
-            DAWN_TRY(ExtractResourcesBinding(device, resources.uniform_buffers, compiler,
-                                             BindingInfoType::Buffer, &metadata->bindings));
-            DAWN_TRY(ExtractResourcesBinding(device, resources.separate_images, compiler,
-                                             BindingInfoType::Texture, &metadata->bindings));
-            DAWN_TRY(ExtractResourcesBinding(device, resources.separate_samplers, compiler,
-                                             BindingInfoType::Sampler, &metadata->bindings));
-            DAWN_TRY(ExtractResourcesBinding(device, resources.storage_buffers, compiler,
-                                             BindingInfoType::Buffer, &metadata->bindings, true));
-            // ReadonlyStorageTexture is used as a tag to do general storage texture handling.
-            DAWN_TRY(ExtractResourcesBinding(device, resources.storage_images, compiler,
-                                             BindingInfoType::StorageTexture, &metadata->bindings));
-
-            // Extract the vertex attributes
-            if (stage == SingleShaderStage::Vertex) {
-                for (const auto& attrib : resources.stage_inputs) {
-                    if (!(compiler.get_decoration_bitset(attrib.id).get(spv::DecorationLocation))) {
-                        return DAWN_VALIDATION_ERROR(
-                            "Unable to find Location decoration for Vertex input");
-                    }
-                    uint32_t unsanitizedLocation =
-                        compiler.get_decoration(attrib.id, spv::DecorationLocation);
-
-                    if (unsanitizedLocation >= kMaxVertexAttributes) {
-                        return DAWN_VALIDATION_ERROR("Attribute location over limits in the SPIRV");
-                    }
-                    VertexAttributeLocation location(static_cast<uint8_t>(unsanitizedLocation));
-
-                    spirv_cross::SPIRType::BaseType inputBaseType =
-                        compiler.get_type(attrib.base_type_id).basetype;
-                    metadata->vertexInputBaseTypes[location] =
-                        SpirvBaseTypeToVertexFormatBaseType(inputBaseType);
-                    metadata->usedVertexInputs.set(location);
-                }
-
-                // Without a location qualifier on vertex outputs, spirv_cross::CompilerMSL gives
-                // them all the location 0, causing a compile error.
-                for (const auto& attrib : resources.stage_outputs) {
-                    if (!compiler.get_decoration_bitset(attrib.id).get(spv::DecorationLocation)) {
-                        return DAWN_VALIDATION_ERROR("Need location qualifier on vertex output");
-                    }
-                }
-            }
-
-            if (stage == SingleShaderStage::Fragment) {
-                // Without a location qualifier on vertex inputs, spirv_cross::CompilerMSL gives
-                // them all the location 0, causing a compile error.
-                for (const auto& attrib : resources.stage_inputs) {
-                    if (!compiler.get_decoration_bitset(attrib.id).get(spv::DecorationLocation)) {
-                        return DAWN_VALIDATION_ERROR("Need location qualifier on fragment input");
-                    }
-                }
-
-                for (const auto& fragmentOutput : resources.stage_outputs) {
-                    if (!compiler.get_decoration_bitset(fragmentOutput.id)
-                             .get(spv::DecorationLocation)) {
-                        return DAWN_VALIDATION_ERROR(
-                            "Unable to find Location decoration for Fragment output");
-                    }
-                    uint32_t unsanitizedAttachment =
-                        compiler.get_decoration(fragmentOutput.id, spv::DecorationLocation);
-
-                    if (unsanitizedAttachment >= kMaxColorAttachments) {
-                        return DAWN_VALIDATION_ERROR(
-                            "Fragment output index must be less than max number of color "
-                            "attachments");
-                    }
-                    ColorAttachmentIndex attachment(static_cast<uint8_t>(unsanitizedAttachment));
-
-                    spirv_cross::SPIRType::BaseType shaderFragmentOutputBaseType =
-                        compiler.get_type(fragmentOutput.base_type_id).basetype;
-                    metadata->fragmentOutputFormatBaseTypes[attachment] =
-                        SpirvBaseTypeToTextureComponentType(shaderFragmentOutputBaseType);
-                    metadata->fragmentOutputsWritten.set(attachment);
-                }
-            }
-
-            if (stage == SingleShaderStage::Compute) {
-                const spirv_cross::SPIREntryPoint& spirEntryPoint =
-                    compiler.get_entry_point(entryPointName, spv::ExecutionModelGLCompute);
-                metadata->localWorkgroupSize.x = spirEntryPoint.workgroup_size.x;
-                metadata->localWorkgroupSize.y = spirEntryPoint.workgroup_size.y;
-                metadata->localWorkgroupSize.z = spirEntryPoint.workgroup_size.z;
-            }
-
-            return {std::move(metadata)};
-        }
-#endif
-
         ResultOrError<EntryPointMetadataTable> ReflectShaderUsingTint(
             DeviceBase*,
             const tint::Program* program) {
@@ -1628,26 +1376,6 @@
         return {};
     }
 
-#if defined(DAWN_USE_SPIRV_CROSS)
-    ResultOrError<EntryPointMetadataTable> ShaderModuleBase::ReflectShaderUsingSPIRVCross(
-        DeviceBase* device,
-        const std::vector<uint32_t>& spirv) {
-        EntryPointMetadataTable result;
-        spirv_cross::Compiler compiler(spirv);
-        for (const spirv_cross::EntryPoint& entryPoint : compiler.get_entry_points_and_stages()) {
-            ASSERT(result.count(entryPoint.name) == 0);
-
-            SingleShaderStage stage = ExecutionModelToShaderStage(entryPoint.execution_model);
-            compiler.set_entry_point(entryPoint.name, entryPoint.execution_model);
-
-            std::unique_ptr<EntryPointMetadata> metadata;
-            DAWN_TRY_ASSIGN(metadata, ExtractSpirvInfo(device, compiler, entryPoint.name, stage));
-            result[entryPoint.name] = std::move(metadata);
-        }
-        return std::move(result);
-    }
-#endif
-
     size_t PipelineLayoutEntryPointPairHashFunc::operator()(
         const PipelineLayoutEntryPointPair& pair) const {
         size_t hash = 0;
diff --git a/src/dawn_native/ShaderModule.h b/src/dawn_native/ShaderModule.h
index 7d92c42..3f17ceb 100644
--- a/src/dawn_native/ShaderModule.h
+++ b/src/dawn_native/ShaderModule.h
@@ -240,11 +240,6 @@
 
       protected:
         MaybeError InitializeBase(ShaderModuleParseResult* parseResult);
-#if defined(DAWN_USE_SPIRV_CROSS)
-        static ResultOrError<EntryPointMetadataTable> ReflectShaderUsingSPIRVCross(
-            DeviceBase* device,
-            const std::vector<uint32_t>& spirv);
-#endif
 
       private:
         ShaderModuleBase(DeviceBase* device, ObjectBase::ErrorTag tag);
diff --git a/src/dawn_native/d3d12/ShaderModuleD3D12.cpp b/src/dawn_native/d3d12/ShaderModuleD3D12.cpp
index 28228a7..3a59890 100644
--- a/src/dawn_native/d3d12/ShaderModuleD3D12.cpp
+++ b/src/dawn_native/d3d12/ShaderModuleD3D12.cpp
@@ -17,7 +17,6 @@
 #include "common/Assert.h"
 #include "common/BitSetIterator.h"
 #include "common/Log.h"
-#include "dawn_native/SpirvUtils.h"
 #include "dawn_native/TintUtils.h"
 #include "dawn_native/d3d12/BindGroupLayoutD3D12.h"
 #include "dawn_native/d3d12/D3D12Error.h"
diff --git a/src/dawn_native/opengl/ShaderModuleGL.cpp b/src/dawn_native/opengl/ShaderModuleGL.cpp
index c56d950..a5a3dd0 100644
--- a/src/dawn_native/opengl/ShaderModuleGL.cpp
+++ b/src/dawn_native/opengl/ShaderModuleGL.cpp
@@ -17,10 +17,10 @@
 #include "common/Assert.h"
 #include "common/Platform.h"
 #include "dawn_native/BindGroupLayout.h"
-#include "dawn_native/SpirvUtils.h"
 #include "dawn_native/TintUtils.h"
 #include "dawn_native/opengl/DeviceGL.h"
 #include "dawn_native/opengl/PipelineLayoutGL.h"
+#include "dawn_native/opengl/SpirvUtils.h"
 
 #include <spirv_glsl.hpp>
 
@@ -64,6 +64,247 @@
         return o.str();
     }
 
+    ResultOrError<std::unique_ptr<EntryPointMetadata>> ExtractSpirvInfo(
+        const DeviceBase* device,
+        const spirv_cross::Compiler& compiler,
+        const std::string& entryPointName,
+        SingleShaderStage stage) {
+        std::unique_ptr<EntryPointMetadata> metadata = std::make_unique<EntryPointMetadata>();
+        metadata->stage = stage;
+
+        const auto& resources = compiler.get_shader_resources();
+
+        if (resources.push_constant_buffers.size() > 0) {
+            return DAWN_VALIDATION_ERROR("Push constants aren't supported.");
+        }
+
+        if (resources.sampled_images.size() > 0) {
+            return DAWN_VALIDATION_ERROR("Combined images and samplers aren't supported.");
+        }
+
+        // Fill in bindingInfo with the SPIRV bindings
+        auto ExtractResourcesBinding =
+            [](const DeviceBase* device,
+               const spirv_cross::SmallVector<spirv_cross::Resource>& resources,
+               const spirv_cross::Compiler& compiler, BindingInfoType bindingType,
+               EntryPointMetadata::BindingInfoArray* metadataBindings,
+               bool isStorageBuffer = false) -> MaybeError {
+            for (const auto& resource : resources) {
+                if (!compiler.get_decoration_bitset(resource.id).get(spv::DecorationBinding)) {
+                    return DAWN_VALIDATION_ERROR("No Binding decoration set for resource");
+                }
+
+                if (!compiler.get_decoration_bitset(resource.id)
+                         .get(spv::DecorationDescriptorSet)) {
+                    return DAWN_VALIDATION_ERROR("No Descriptor Decoration set for resource");
+                }
+
+                BindingNumber bindingNumber(
+                    compiler.get_decoration(resource.id, spv::DecorationBinding));
+                BindGroupIndex bindGroupIndex(
+                    compiler.get_decoration(resource.id, spv::DecorationDescriptorSet));
+
+                if (bindGroupIndex >= kMaxBindGroupsTyped) {
+                    return DAWN_VALIDATION_ERROR("Bind group index over limits in the SPIRV");
+                }
+
+                const auto& it = (*metadataBindings)[bindGroupIndex].emplace(
+                    bindingNumber, EntryPointMetadata::ShaderBindingInfo{});
+                if (!it.second) {
+                    return DAWN_VALIDATION_ERROR("Shader has duplicate bindings");
+                }
+
+                EntryPointMetadata::ShaderBindingInfo* info = &it.first->second;
+                info->id = resource.id;
+                info->base_type_id = resource.base_type_id;
+                info->bindingType = bindingType;
+
+                switch (bindingType) {
+                    case BindingInfoType::Texture: {
+                        spirv_cross::SPIRType::ImageType imageType =
+                            compiler.get_type(info->base_type_id).image;
+                        spirv_cross::SPIRType::BaseType textureComponentType =
+                            compiler.get_type(imageType.type).basetype;
+
+                        info->texture.viewDimension =
+                            SpirvDimToTextureViewDimension(imageType.dim, imageType.arrayed);
+                        info->texture.multisampled = imageType.ms;
+                        info->texture.compatibleSampleTypes =
+                            SpirvBaseTypeToSampleTypeBit(textureComponentType);
+
+                        if (imageType.depth) {
+                            if ((info->texture.compatibleSampleTypes & SampleTypeBit::Float) == 0) {
+                                return DAWN_VALIDATION_ERROR(
+                                    "Depth textures must have a float type");
+                            }
+                            info->texture.compatibleSampleTypes = SampleTypeBit::Depth;
+                        }
+
+                        if (imageType.ms && imageType.arrayed) {
+                            return DAWN_VALIDATION_ERROR(
+                                "Multisampled array textures aren't supported");
+                        }
+                        break;
+                    }
+                    case BindingInfoType::Buffer: {
+                        // Determine buffer size, with a minimum of 1 element in the runtime
+                        // array
+                        spirv_cross::SPIRType type = compiler.get_type(info->base_type_id);
+                        info->buffer.minBindingSize =
+                            compiler.get_declared_struct_size_runtime_array(type, 1);
+
+                        // Differentiate between readonly storage bindings and writable ones
+                        // based on the NonWritable decoration.
+                        // TODO(dawn:527): Could isStorageBuffer be determined by calling
+                        // compiler.get_storage_class(resource.id)?
+                        if (isStorageBuffer) {
+                            spirv_cross::Bitset flags =
+                                compiler.get_buffer_block_flags(resource.id);
+                            if (flags.get(spv::DecorationNonWritable)) {
+                                info->buffer.type = wgpu::BufferBindingType::ReadOnlyStorage;
+                            } else {
+                                info->buffer.type = wgpu::BufferBindingType::Storage;
+                            }
+                        } else {
+                            info->buffer.type = wgpu::BufferBindingType::Uniform;
+                        }
+                        break;
+                    }
+                    case BindingInfoType::StorageTexture: {
+                        spirv_cross::Bitset flags = compiler.get_decoration_bitset(resource.id);
+                        if (flags.get(spv::DecorationNonReadable)) {
+                            info->storageTexture.access = wgpu::StorageTextureAccess::WriteOnly;
+                        } else if (flags.get(spv::DecorationNonWritable)) {
+                            info->storageTexture.access = wgpu::StorageTextureAccess::ReadOnly;
+                        } else {
+                            return DAWN_VALIDATION_ERROR(
+                                "Read-write storage textures are not supported");
+                        }
+
+                        spirv_cross::SPIRType::ImageType imageType =
+                            compiler.get_type(info->base_type_id).image;
+                        wgpu::TextureFormat storageTextureFormat =
+                            SpirvImageFormatToTextureFormat(imageType.format);
+                        if (storageTextureFormat == wgpu::TextureFormat::Undefined) {
+                            return DAWN_VALIDATION_ERROR(
+                                "Invalid image format declaration on storage image");
+                        }
+                        const Format& format = device->GetValidInternalFormat(storageTextureFormat);
+                        if (!format.supportsStorageUsage) {
+                            return DAWN_VALIDATION_ERROR(
+                                "The storage texture format is not supported");
+                        }
+                        if (imageType.ms) {
+                            return DAWN_VALIDATION_ERROR(
+                                "Multisampled storage textures aren't supported");
+                        }
+                        if (imageType.depth) {
+                            return DAWN_VALIDATION_ERROR("Depth storage textures aren't supported");
+                        }
+                        info->storageTexture.format = storageTextureFormat;
+                        info->storageTexture.viewDimension =
+                            SpirvDimToTextureViewDimension(imageType.dim, imageType.arrayed);
+                        break;
+                    }
+                    case BindingInfoType::Sampler: {
+                        info->sampler.isComparison = false;
+                        break;
+                    }
+                    case BindingInfoType::ExternalTexture: {
+                        return DAWN_VALIDATION_ERROR("External textures are not supported.");
+                    }
+                }
+            }
+            return {};
+        };
+
+        DAWN_TRY(ExtractResourcesBinding(device, resources.uniform_buffers, compiler,
+                                         BindingInfoType::Buffer, &metadata->bindings));
+        DAWN_TRY(ExtractResourcesBinding(device, resources.separate_images, compiler,
+                                         BindingInfoType::Texture, &metadata->bindings));
+        DAWN_TRY(ExtractResourcesBinding(device, resources.separate_samplers, compiler,
+                                         BindingInfoType::Sampler, &metadata->bindings));
+        DAWN_TRY(ExtractResourcesBinding(device, resources.storage_buffers, compiler,
+                                         BindingInfoType::Buffer, &metadata->bindings, true));
+        // ReadonlyStorageTexture is used as a tag to do general storage texture handling.
+        DAWN_TRY(ExtractResourcesBinding(device, resources.storage_images, compiler,
+                                         BindingInfoType::StorageTexture, &metadata->bindings));
+
+        // Extract the vertex attributes
+        if (stage == SingleShaderStage::Vertex) {
+            for (const auto& attrib : resources.stage_inputs) {
+                if (!(compiler.get_decoration_bitset(attrib.id).get(spv::DecorationLocation))) {
+                    return DAWN_VALIDATION_ERROR(
+                        "Unable to find Location decoration for Vertex input");
+                }
+                uint32_t unsanitizedLocation =
+                    compiler.get_decoration(attrib.id, spv::DecorationLocation);
+
+                if (unsanitizedLocation >= kMaxVertexAttributes) {
+                    return DAWN_VALIDATION_ERROR("Attribute location over limits in the SPIRV");
+                }
+                VertexAttributeLocation location(static_cast<uint8_t>(unsanitizedLocation));
+
+                spirv_cross::SPIRType::BaseType inputBaseType =
+                    compiler.get_type(attrib.base_type_id).basetype;
+                metadata->vertexInputBaseTypes[location] =
+                    SpirvBaseTypeToVertexFormatBaseType(inputBaseType);
+                metadata->usedVertexInputs.set(location);
+            }
+
+            // Without a location qualifier on vertex outputs, spirv_cross::CompilerMSL gives
+            // them all the location 0, causing a compile error.
+            for (const auto& attrib : resources.stage_outputs) {
+                if (!compiler.get_decoration_bitset(attrib.id).get(spv::DecorationLocation)) {
+                    return DAWN_VALIDATION_ERROR("Need location qualifier on vertex output");
+                }
+            }
+        }
+
+        if (stage == SingleShaderStage::Fragment) {
+            // Without a location qualifier on vertex inputs, spirv_cross::CompilerMSL gives
+            // them all the location 0, causing a compile error.
+            for (const auto& attrib : resources.stage_inputs) {
+                if (!compiler.get_decoration_bitset(attrib.id).get(spv::DecorationLocation)) {
+                    return DAWN_VALIDATION_ERROR("Need location qualifier on fragment input");
+                }
+            }
+
+            for (const auto& fragmentOutput : resources.stage_outputs) {
+                if (!compiler.get_decoration_bitset(fragmentOutput.id)
+                         .get(spv::DecorationLocation)) {
+                    return DAWN_VALIDATION_ERROR(
+                        "Unable to find Location decoration for Fragment output");
+                }
+                uint32_t unsanitizedAttachment =
+                    compiler.get_decoration(fragmentOutput.id, spv::DecorationLocation);
+
+                if (unsanitizedAttachment >= kMaxColorAttachments) {
+                    return DAWN_VALIDATION_ERROR(
+                        "Fragment output index must be less than max number of color "
+                        "attachments");
+                }
+                ColorAttachmentIndex attachment(static_cast<uint8_t>(unsanitizedAttachment));
+
+                spirv_cross::SPIRType::BaseType shaderFragmentOutputBaseType =
+                    compiler.get_type(fragmentOutput.base_type_id).basetype;
+                metadata->fragmentOutputFormatBaseTypes[attachment] =
+                    SpirvBaseTypeToTextureComponentType(shaderFragmentOutputBaseType);
+                metadata->fragmentOutputsWritten.set(attachment);
+            }
+        }
+
+        if (stage == SingleShaderStage::Compute) {
+            const spirv_cross::SPIREntryPoint& spirEntryPoint =
+                compiler.get_entry_point(entryPointName, spv::ExecutionModelGLCompute);
+            metadata->localWorkgroupSize.x = spirEntryPoint.workgroup_size.x;
+            metadata->localWorkgroupSize.y = spirEntryPoint.workgroup_size.y;
+            metadata->localWorkgroupSize.z = spirEntryPoint.workgroup_size.z;
+        }
+
+        return {std::move(metadata)};
+    }
+
     // static
     ResultOrError<Ref<ShaderModule>> ShaderModule::Create(Device* device,
                                                           const ShaderModuleDescriptor* descriptor,
@@ -77,6 +318,25 @@
         : ShaderModuleBase(device, descriptor) {
     }
 
+    // static
+    ResultOrError<EntryPointMetadataTable> ShaderModule::ReflectShaderUsingSPIRVCross(
+        DeviceBase* device,
+        const std::vector<uint32_t>& spirv) {
+        EntryPointMetadataTable result;
+        spirv_cross::Compiler compiler(spirv);
+        for (const spirv_cross::EntryPoint& entryPoint : compiler.get_entry_points_and_stages()) {
+            ASSERT(result.count(entryPoint.name) == 0);
+
+            SingleShaderStage stage = ExecutionModelToShaderStage(entryPoint.execution_model);
+            compiler.set_entry_point(entryPoint.name, entryPoint.execution_model);
+
+            std::unique_ptr<EntryPointMetadata> metadata;
+            DAWN_TRY_ASSIGN(metadata, ExtractSpirvInfo(device, compiler, entryPoint.name, stage));
+            result[entryPoint.name] = std::move(metadata);
+        }
+        return std::move(result);
+    }
+
     MaybeError ShaderModule::Initialize(ShaderModuleParseResult* parseResult) {
         ScopedTintICEHandler scopedICEHandler(GetDevice());
 
diff --git a/src/dawn_native/opengl/ShaderModuleGL.h b/src/dawn_native/opengl/ShaderModuleGL.h
index d6d9f74..78a2f2a 100644
--- a/src/dawn_native/opengl/ShaderModuleGL.h
+++ b/src/dawn_native/opengl/ShaderModuleGL.h
@@ -60,6 +60,9 @@
         ShaderModule(Device* device, const ShaderModuleDescriptor* descriptor);
         ~ShaderModule() override = default;
         MaybeError Initialize(ShaderModuleParseResult* parseResult);
+        static ResultOrError<EntryPointMetadataTable> ReflectShaderUsingSPIRVCross(
+            DeviceBase* device,
+            const std::vector<uint32_t>& spirv);
 
         EntryPointMetadataTable mGLEntryPoints;
     };
diff --git a/src/dawn_native/SpirvUtils.cpp b/src/dawn_native/opengl/SpirvUtils.cpp
similarity index 99%
rename from src/dawn_native/SpirvUtils.cpp
rename to src/dawn_native/opengl/SpirvUtils.cpp
index 01749de..a274300 100644
--- a/src/dawn_native/SpirvUtils.cpp
+++ b/src/dawn_native/opengl/SpirvUtils.cpp
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "dawn_native/SpirvUtils.h"
+#include "dawn_native/opengl/SpirvUtils.h"
 
 namespace dawn_native {
 
diff --git a/src/dawn_native/SpirvUtils.h b/src/dawn_native/opengl/SpirvUtils.h
similarity index 89%
rename from src/dawn_native/SpirvUtils.h
rename to src/dawn_native/opengl/SpirvUtils.h
index 3719794..844b0b7 100644
--- a/src/dawn_native/SpirvUtils.h
+++ b/src/dawn_native/opengl/SpirvUtils.h
@@ -15,12 +15,8 @@
 // This file contains utilities to convert from-to spirv.hpp datatypes without polluting other
 // headers with spirv.hpp
 
-#ifndef DAWNNATIVE_SPIRV_UTILS_H_
-#define DAWNNATIVE_SPIRV_UTILS_H_
-
-#if !defined(DAWN_USE_SPIRV_CROSS)
-#    error "SpirvCross.h should not be included if dawn_use_spirv_cross is false"
-#endif
+#ifndef DAWNNATIVE_OPENGL_SPIRV_UTILS_H_
+#define DAWNNATIVE_OPENGL_SPIRV_UTILS_H_
 
 #include "dawn_native/Format.h"
 #include "dawn_native/PerStage.h"
@@ -52,4 +48,4 @@
 
 }  // namespace dawn_native
 
-#endif  // DAWNNATIVE_SPIRV_UTILS_H_
+#endif  // DAWNNATIVE_OPENGL_SPIRV_UTILS_H_