From cd170a5c7216cfe26572bf380bdfd2e843c1a207 Mon Sep 17 00:00:00 2001 From: Ryan Harrison Date: Tue, 14 Jan 2020 16:29:41 +0000 Subject: [PATCH] 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 Reviewed-by: Corentin Wallez --- src/dawn_native/ShaderModule.cpp | 353 +++++++++++++++++++++++++------ src/dawn_native/ShaderModule.h | 7 + 2 files changed, 298 insertions(+), 62 deletions(-) diff --git a/src/dawn_native/ShaderModule.cpp b/src/dawn_native/ShaderModule.cpp index dfa9acf4a9..1cb3f217a6 100644 --- a/src/dawn_native/ShaderModule.cpp +++ b/src/dawn_native/ShaderModule.cpp @@ -66,6 +66,75 @@ namespace dawn_native { } } + 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 @@ namespace dawn_native { 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 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 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 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 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 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 @@ namespace dawn_native { } // Fill in bindingInfo with the SPIRV bindings - auto ExtractResourcesBinding = [this](const spirv_cross::SmallVector& - 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& 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 @@ namespace dawn_native { 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 @@ namespace dawn_native { 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 @@ namespace dawn_native { 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 da69c4d98c..9a6bcccc51 100644 --- a/src/dawn_native/ShaderModule.h +++ b/src/dawn_native/ShaderModule.h @@ -89,6 +89,13 @@ namespace dawn_native { 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 mCode;