Implement using spvc in ExtractSpirvInfo
BUG=dawn:291
Change-Id: I6c12c1874afb9b2c60e326f5c80230fba5fa748c
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/15000
Commit-Queue: Ryan Harrison <rharrison@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/dawn_native/ShaderModule.cpp b/src/dawn_native/ShaderModule.cpp
index dfa9acf..1cb3f21 100644
--- a/src/dawn_native/ShaderModule.cpp
+++ b/src/dawn_native/ShaderModule.cpp
@@ -66,6 +66,75 @@
}
}
+ wgpu::TextureViewDimension ToWGPUTextureViewDimension(
+ shaderc_spvc_texture_view_dimension dim) {
+ switch (dim) {
+ case shaderc_spvc_texture_view_dimension_undefined:
+ return wgpu::TextureViewDimension::Undefined;
+ case shaderc_spvc_texture_view_dimension_e1D:
+ return wgpu::TextureViewDimension::e1D;
+ case shaderc_spvc_texture_view_dimension_e2D:
+ return wgpu::TextureViewDimension::e2D;
+ case shaderc_spvc_texture_view_dimension_e2D_array:
+ return wgpu::TextureViewDimension::e2DArray;
+ case shaderc_spvc_texture_view_dimension_cube:
+ return wgpu::TextureViewDimension::Cube;
+ case shaderc_spvc_texture_view_dimension_cube_array:
+ return wgpu::TextureViewDimension::CubeArray;
+ case shaderc_spvc_texture_view_dimension_e3D:
+ return wgpu::TextureViewDimension::e3D;
+ }
+ UNREACHABLE();
+ }
+
+ Format::Type ToDawnFormatType(shaderc_spvc_texture_format_type type) {
+ switch (type) {
+ case shaderc_spvc_texture_format_type_float:
+ return Format::Type::Float;
+ case shaderc_spvc_texture_format_type_sint:
+ return Format::Type::Sint;
+ case shaderc_spvc_texture_format_type_uint:
+ return Format::Type::Uint;
+ case shaderc_spvc_texture_format_type_other:
+ return Format::Type::Other;
+ }
+ UNREACHABLE();
+ }
+
+ wgpu::BindingType ToWGPUBindingType(shaderc_spvc_binding_type type) {
+ switch (type) {
+ case shaderc_spvc_binding_type_uniform_buffer:
+ return wgpu::BindingType::UniformBuffer;
+ case shaderc_spvc_binding_type_storage_buffer:
+ return wgpu::BindingType::StorageBuffer;
+ case shaderc_spvc_binding_type_readonly_storage_buffer:
+ return wgpu::BindingType::ReadonlyStorageBuffer;
+ case shaderc_spvc_binding_type_sampler:
+ return wgpu::BindingType::Sampler;
+ case shaderc_spvc_binding_type_sampled_texture:
+ return wgpu::BindingType::SampledTexture;
+ case shaderc_spvc_binding_type_storage_texture:
+ return wgpu::BindingType::StorageTexture;
+ }
+ UNREACHABLE();
+ }
+
+ // TODO(rharrison): Convert this to ResultOrError once ExtractSPIRVInfo turns an error
+ SingleShaderStage ToSingleShaderStage(shaderc_spvc_execution_model execution_model) {
+ switch (execution_model) {
+ case shaderc_spvc_execution_model_vertex:
+ return SingleShaderStage::Vertex;
+ case shaderc_spvc_execution_model_fragment:
+ return SingleShaderStage::Fragment;
+ case shaderc_spvc_execution_model_glcompute:
+ return SingleShaderStage::Compute;
+ default:
+ // This will be converted to an error return when the whole
+ // calling stack is changed to passing errors.
+ UNREACHABLE();
+ return SingleShaderStage::Vertex;
+ }
+ }
} // anonymous namespace
MaybeError ValidateShaderModuleDescriptor(DeviceBase*,
@@ -131,8 +200,159 @@
void ShaderModuleBase::ExtractSpirvInfo(const spirv_cross::Compiler& compiler) {
ASSERT(!IsError());
+ if (GetDevice()->IsToggleEnabled(Toggle::UseSpvc)) {
+ ExtractSpirvInfoWithSpvc(compiler);
+ } else {
+ ExtractSpirvInfoWithSpirvCross(compiler);
+ }
+ }
- DeviceBase* device = GetDevice();
+ void ShaderModuleBase::ExtractSpirvInfoWithSpvc(const spirv_cross::Compiler& compiler) {
+ shaderc_spvc_execution_model execution_model;
+ if (!CheckSpvcSuccess(mSpvcContext.GetExecutionModel(&execution_model),
+ "Unable to get execution model for shader.")) {
+ return;
+ }
+
+ mExecutionModel = ToSingleShaderStage(execution_model);
+
+ size_t push_constant_buffers_count;
+ if (!CheckSpvcSuccess(mSpvcContext.GetPushConstantBufferCount(&push_constant_buffers_count),
+ "Unable to get push constant buffer count for shader.")) {
+ return;
+ }
+
+ // TODO(rharrison): This should be handled by spirv-val pass in spvc,
+ // but need to confirm.
+ if (push_constant_buffers_count > 0) {
+ GetDevice()->HandleError(wgpu::ErrorType::Validation,
+ "Push constants aren't supported.");
+ return;
+ }
+
+ // Fill in bindingInfo with the SPIRV bindings
+ auto ExtractResourcesBinding = [this](std::vector<shaderc_spvc_binding_info> bindings) {
+ for (const auto& binding : bindings) {
+ if (binding.binding >= kMaxBindingsPerGroup || binding.set >= kMaxBindGroups) {
+ GetDevice()->HandleError(wgpu::ErrorType::Validation,
+ "Binding over limits in the SPIRV");
+ continue;
+ }
+
+ BindingInfo* info = &mBindingInfo[binding.set][binding.binding];
+ *info = {};
+ info->used = true;
+ info->id = binding.id;
+ info->base_type_id = binding.base_type_id;
+ if (binding.binding_type == shaderc_spvc_binding_type_sampled_texture) {
+ info->multisampled = binding.multisampled;
+ info->textureDimension = ToWGPUTextureViewDimension(binding.texture_dimension);
+ info->textureComponentType = ToDawnFormatType(binding.texture_component_type);
+ }
+ info->type = ToWGPUBindingType(binding.binding_type);
+ }
+ };
+
+ std::vector<shaderc_spvc_binding_info> resource_bindings;
+ if (!CheckSpvcSuccess(mSpvcContext.GetBindingInfo(
+ shaderc_spvc_shader_resource_uniform_buffers,
+ shaderc_spvc_binding_type_uniform_buffer, &resource_bindings),
+ "Unable to get binding info for uniform buffers from shader")) {
+ return;
+ }
+ ExtractResourcesBinding(resource_bindings);
+
+ if (!CheckSpvcSuccess(mSpvcContext.GetBindingInfo(
+ shaderc_spvc_shader_resource_separate_images,
+ shaderc_spvc_binding_type_sampled_texture, &resource_bindings),
+ "Unable to get binding info for sampled textures from shader")) {
+ return;
+ }
+ ExtractResourcesBinding(resource_bindings);
+
+ if (!CheckSpvcSuccess(
+ mSpvcContext.GetBindingInfo(shaderc_spvc_shader_resource_separate_samplers,
+ shaderc_spvc_binding_type_sampler, &resource_bindings),
+ "Unable to get binding info for samples from shader")) {
+ return;
+ }
+ ExtractResourcesBinding(resource_bindings);
+
+ if (!CheckSpvcSuccess(mSpvcContext.GetBindingInfo(
+ shaderc_spvc_shader_resource_storage_buffers,
+ shaderc_spvc_binding_type_storage_buffer, &resource_bindings),
+ "Unable to get binding info for storage buffers from shader")) {
+ return;
+ }
+ ExtractResourcesBinding(resource_bindings);
+
+ std::vector<shaderc_spvc_resource_location_info> input_stage_locations;
+ if (!CheckSpvcSuccess(mSpvcContext.GetInputStageLocationInfo(&input_stage_locations),
+ "Unable to get input stage location information from shader")) {
+ input_stage_locations.clear();
+ return;
+ }
+
+ for (const auto& input : input_stage_locations) {
+ if (mExecutionModel == SingleShaderStage::Vertex) {
+ if (input.location >= kMaxVertexAttributes) {
+ GetDevice()->HandleError(wgpu::ErrorType::Validation,
+ "Attribute location over limits in the SPIRV");
+ return;
+ }
+ mUsedVertexAttributes.set(input.location);
+ } else if (mExecutionModel == SingleShaderStage::Fragment) {
+ // Without a location qualifier on vertex inputs, spirv_cross::CompilerMSL gives
+ // them all the location 0, causing a compile error.
+ if (!input.has_location) {
+ GetDevice()->HandleError(wgpu::ErrorType::Validation,
+ "Need location qualifier on fragment input");
+ return;
+ }
+ }
+ }
+
+ std::vector<shaderc_spvc_resource_location_info> output_stage_locations;
+ if (!CheckSpvcSuccess(mSpvcContext.GetOutputStageLocationInfo(&output_stage_locations),
+ "Unable to get output stage location information from shader")) {
+ output_stage_locations.clear();
+ return;
+ }
+
+ for (const auto& output : output_stage_locations) {
+ if (mExecutionModel == SingleShaderStage::Vertex) {
+ // Without a location qualifier on vertex outputs, spirv_cross::CompilerMSL
+ // gives them all the location 0, causing a compile error.
+ if (!output.has_location) {
+ GetDevice()->HandleError(wgpu::ErrorType::Validation,
+ "Need location qualifier on vertex output");
+ return;
+ }
+ } else if (mExecutionModel == SingleShaderStage::Fragment) {
+ if (output.location >= kMaxColorAttachments) {
+ GetDevice()->HandleError(wgpu::ErrorType::Validation,
+ "Fragment output location over limits in the SPIRV");
+ return;
+ }
+ }
+ }
+
+ if (mExecutionModel == SingleShaderStage::Fragment) {
+ std::vector<shaderc_spvc_resource_type_info> output_types;
+ if (!CheckSpvcSuccess(mSpvcContext.GetOutputStageTypeInfo(&output_types),
+ "Unable to get output stage type information from shader")) {
+ output_stage_locations.clear();
+ return;
+ }
+
+ for (const auto& output : output_types) {
+ ASSERT(output.type != shaderc_spvc_texture_format_type_other);
+ mFragmentOutputFormatBaseTypes[output.location] = ToDawnFormatType(output.type);
+ }
+ }
+ }
+
+ void ShaderModuleBase::ExtractSpirvInfoWithSpirvCross(const spirv_cross::Compiler& compiler) {
// TODO(cwallez@chromium.org): make errors here creation errors
// currently errors here do not prevent the shadermodule from being used
const auto& resources = compiler.get_shader_resources();
@@ -157,58 +377,59 @@
}
// Fill in bindingInfo with the SPIRV bindings
- auto ExtractResourcesBinding = [this](const spirv_cross::SmallVector<spirv_cross::Resource>&
- resources,
- const spirv_cross::Compiler& compiler,
- wgpu::BindingType bindingType) {
- for (const auto& resource : resources) {
- ASSERT(compiler.get_decoration_bitset(resource.id).get(spv::DecorationBinding));
- ASSERT(
- compiler.get_decoration_bitset(resource.id).get(spv::DecorationDescriptorSet));
+ auto ExtractResourcesBinding =
+ [this](const spirv_cross::SmallVector<spirv_cross::Resource>& resources,
+ const spirv_cross::Compiler& compiler, wgpu::BindingType bindingType) {
+ for (const auto& resource : resources) {
+ ASSERT(compiler.get_decoration_bitset(resource.id).get(spv::DecorationBinding));
+ ASSERT(compiler.get_decoration_bitset(resource.id)
+ .get(spv::DecorationDescriptorSet));
- uint32_t binding = compiler.get_decoration(resource.id, spv::DecorationBinding);
- uint32_t set = compiler.get_decoration(resource.id, spv::DecorationDescriptorSet);
+ uint32_t binding = compiler.get_decoration(resource.id, spv::DecorationBinding);
+ uint32_t set =
+ compiler.get_decoration(resource.id, spv::DecorationDescriptorSet);
- if (binding >= kMaxBindingsPerGroup || set >= kMaxBindGroups) {
- GetDevice()->HandleError(wgpu::ErrorType::Validation,
- "Binding over limits in the SPIRV");
- continue;
+ if (binding >= kMaxBindingsPerGroup || set >= kMaxBindGroups) {
+ GetDevice()->HandleError(wgpu::ErrorType::Validation,
+ "Binding over limits in the SPIRV");
+ continue;
+ }
+
+ BindingInfo* info = &mBindingInfo[set][binding];
+ *info = {};
+ info->used = true;
+ info->id = resource.id;
+ info->base_type_id = resource.base_type_id;
+ switch (bindingType) {
+ case wgpu::BindingType::SampledTexture: {
+ 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->multisampled = imageType.ms;
+ info->textureDimension =
+ SpirvDimToTextureViewDimension(imageType.dim, imageType.arrayed);
+ info->textureComponentType =
+ SpirvCrossBaseTypeToFormatType(textureComponentType);
+ info->type = bindingType;
+ } break;
+ case wgpu::BindingType::StorageBuffer: {
+ // Differentiate between readonly storage bindings and writable ones
+ // based on the NonWritable decoration
+ spirv_cross::Bitset flags =
+ compiler.get_buffer_block_flags(resource.id);
+ if (flags.get(spv::DecorationNonWritable)) {
+ info->type = wgpu::BindingType::ReadonlyStorageBuffer;
+ } else {
+ info->type = wgpu::BindingType::StorageBuffer;
+ }
+ } break;
+ default:
+ info->type = bindingType;
+ }
}
-
- BindingInfo* info = &mBindingInfo[set][binding];
- *info = {};
- info->used = true;
- info->id = resource.id;
- info->base_type_id = resource.base_type_id;
- switch (bindingType) {
- case wgpu::BindingType::SampledTexture: {
- 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->multisampled = imageType.ms;
- info->textureDimension =
- SpirvDimToTextureViewDimension(imageType.dim, imageType.arrayed);
- info->textureComponentType =
- SpirvCrossBaseTypeToFormatType(textureComponentType);
- info->type = bindingType;
- } break;
- case wgpu::BindingType::StorageBuffer: {
- // Differentiate between readonly storage bindings and writable ones based
- // on the NonWritable decoration
- spirv_cross::Bitset flags = compiler.get_buffer_block_flags(resource.id);
- if (flags.get(spv::DecorationNonWritable)) {
- info->type = wgpu::BindingType::ReadonlyStorageBuffer;
- } else {
- info->type = wgpu::BindingType::StorageBuffer;
- }
- } break;
- default:
- info->type = bindingType;
- }
- }
- };
+ };
ExtractResourcesBinding(resources.uniform_buffers, compiler,
wgpu::BindingType::UniformBuffer);
@@ -225,32 +446,32 @@
uint32_t location = compiler.get_decoration(attrib.id, spv::DecorationLocation);
if (location >= kMaxVertexAttributes) {
- device->HandleError(wgpu::ErrorType::Validation,
- "Attribute location over limits in the SPIRV");
+ GetDevice()->HandleError(wgpu::ErrorType::Validation,
+ "Attribute location over limits in the SPIRV");
return;
}
mUsedVertexAttributes.set(location);
}
- // Without a location qualifier on vertex outputs, spirv_cross::CompilerMSL gives them
- // all the location 0, causing a compile error.
+ // 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)) {
- device->HandleError(wgpu::ErrorType::Validation,
- "Need location qualifier on vertex output");
+ GetDevice()->HandleError(wgpu::ErrorType::Validation,
+ "Need location qualifier on vertex output");
return;
}
}
}
if (mExecutionModel == SingleShaderStage::Fragment) {
- // Without a location qualifier on vertex inputs, spirv_cross::CompilerMSL gives them
- // all the location 0, causing a compile error.
+ // 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)) {
- device->HandleError(wgpu::ErrorType::Validation,
- "Need location qualifier on fragment input");
+ GetDevice()->HandleError(wgpu::ErrorType::Validation,
+ "Need location qualifier on fragment input");
return;
}
}
@@ -261,8 +482,8 @@
uint32_t location =
compiler.get_decoration(fragmentOutput.id, spv::DecorationLocation);
if (location >= kMaxColorAttachments) {
- device->HandleError(wgpu::ErrorType::Validation,
- "Fragment output location over limits in the SPIRV");
+ GetDevice()->HandleError(wgpu::ErrorType::Validation,
+ "Fragment output location over limits in the SPIRV");
return;
}
@@ -379,4 +600,12 @@
return a->mCode == b->mCode;
}
+ bool ShaderModuleBase::CheckSpvcSuccess(shaderc_spvc_status status, const char* error_msg) {
+ if (status != shaderc_spvc_status_success) {
+ GetDevice()->HandleError(wgpu::ErrorType::Validation, error_msg);
+ return false;
+ }
+ return true;
+ }
+
} // namespace dawn_native
diff --git a/src/dawn_native/ShaderModule.h b/src/dawn_native/ShaderModule.h
index da69c4d..9a6bccc 100644
--- a/src/dawn_native/ShaderModule.h
+++ b/src/dawn_native/ShaderModule.h
@@ -89,6 +89,13 @@
bool IsCompatibleWithBindGroupLayout(size_t group, const BindGroupLayoutBase* layout) const;
+ // Different implementations reflection into the shader depending on
+ // whether using spvc, or directly accessing spirv-cross.
+ void ExtractSpirvInfoWithSpvc(const spirv_cross::Compiler& compiler);
+ void ExtractSpirvInfoWithSpirvCross(const spirv_cross::Compiler& compiler);
+
+ bool CheckSpvcSuccess(shaderc_spvc_status status, const char* error_msg);
+
// TODO(cwallez@chromium.org): The code is only stored for deduplication. We could maybe
// store a cryptographic hash of the code instead?
std::vector<uint32_t> mCode;