Make ShaderModuleBase use an internal EntryPointMetadata

WGSL and SPIR-V modules can contain multiple entrypoints, for different
shader stages, that the pipelines can choose from. This is the first CL
in a stack that will change Dawn internals to not rely on ShaderModules
having a single entrypoint.

EntryPointMetadata is introduced that will contain all reflection data
for an entrypoint of a shader module. To ease review this CL doesn't
introduce any functional changes and doesn't expose the
EntryPointMetadata at the ShaderModuleBase interface. Instead
ShaderModuleBase contains a single metadata object for its single entry
point, and layout-related queries and proxied to the EntryPointMetadata
object.

Finally some small renames and formatting changes are done.

Bug: dawn:216
Change-Id: I0f4d12a5075ba14c5e8fd666be4073d34288f6f9
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/27240
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Austin Eng <enga@chromium.org>
This commit is contained in:
Corentin Wallez 2020-08-26 09:57:52 +00:00 committed by Commit Bot service account
parent c3e3c30b0d
commit 4f8bdaf473
2 changed files with 422 additions and 395 deletions

View File

@ -371,7 +371,6 @@ namespace dawn_native {
} }
} }
#endif #endif
} // anonymous namespace
MaybeError ValidateSpirv(DeviceBase*, const uint32_t* code, uint32_t codeSize) { MaybeError ValidateSpirv(DeviceBase*, const uint32_t* code, uint32_t codeSize) {
spvtools::SpirvTools spirvTools(SPV_ENV_VULKAN_1_1); spvtools::SpirvTools spirvTools(SPV_ENV_VULKAN_1_1);
@ -386,14 +385,16 @@ namespace dawn_native {
case SPV_MSG_FATAL: case SPV_MSG_FATAL:
case SPV_MSG_INTERNAL_ERROR: case SPV_MSG_INTERNAL_ERROR:
case SPV_MSG_ERROR: case SPV_MSG_ERROR:
errorStream << "error: line " << position.index << ": " << message << std::endl; errorStream << "error: line " << position.index << ": " << message
<< std::endl;
break; break;
case SPV_MSG_WARNING: case SPV_MSG_WARNING:
errorStream << "warning: line " << position.index << ": " << message errorStream << "warning: line " << position.index << ": " << message
<< std::endl; << std::endl;
break; break;
case SPV_MSG_INFO: case SPV_MSG_INFO:
errorStream << "info: line " << position.index << ": " << message << std::endl; errorStream << "info: line " << position.index << ": " << message
<< std::endl;
break; break;
default: default:
break; break;
@ -547,6 +548,153 @@ namespace dawn_native {
} }
#endif // DAWN_ENABLE_WGSL #endif // DAWN_ENABLE_WGSL
std::vector<uint64_t> GetBindGroupMinBufferSizes(
const ShaderModuleBase::BindingInfoMap& shaderBindings,
const BindGroupLayoutBase* layout) {
std::vector<uint64_t> requiredBufferSizes(layout->GetUnverifiedBufferCount());
uint32_t packedIdx = 0;
for (BindingIndex bindingIndex{0}; bindingIndex < layout->GetBufferCount();
++bindingIndex) {
const BindingInfo& bindingInfo = layout->GetBindingInfo(bindingIndex);
if (bindingInfo.minBufferBindingSize != 0) {
// Skip bindings that have minimum buffer size set in the layout
continue;
}
ASSERT(packedIdx < requiredBufferSizes.size());
const auto& shaderInfo = shaderBindings.find(bindingInfo.binding);
if (shaderInfo != shaderBindings.end()) {
requiredBufferSizes[packedIdx] = shaderInfo->second.minBufferBindingSize;
} else {
// We have to include buffers if they are included in the bind group's
// packed vector. We don't actually need to check these at draw time, so
// if this is a problem in the future we can optimize it further.
requiredBufferSizes[packedIdx] = 0;
}
++packedIdx;
}
return requiredBufferSizes;
}
MaybeError ValidateCompatibilityWithBindGroupLayout(
BindGroupIndex group,
const ShaderModuleBase::EntryPointMetadata& entryPoint,
const BindGroupLayoutBase* layout) {
const BindGroupLayoutBase::BindingMap& layoutBindings = layout->GetBindingMap();
// Iterate over all bindings used by this group in the shader, and find the
// corresponding binding in the BindGroupLayout, if it exists.
for (const auto& it : entryPoint.bindings[group]) {
BindingNumber bindingNumber = it.first;
const ShaderModuleBase::ShaderBindingInfo& shaderInfo = it.second;
const auto& bindingIt = layoutBindings.find(bindingNumber);
if (bindingIt == layoutBindings.end()) {
return DAWN_VALIDATION_ERROR("Missing bind group layout entry for " +
GetShaderDeclarationString(group, bindingNumber));
}
BindingIndex bindingIndex(bindingIt->second);
const BindingInfo& layoutInfo = layout->GetBindingInfo(bindingIndex);
if (layoutInfo.type != shaderInfo.type) {
// Binding mismatch between shader and bind group is invalid. For example, a
// writable binding in the shader with a readonly storage buffer in the bind
// group layout is invalid. However, a readonly binding in the shader with a
// writable storage buffer in the bind group layout is valid.
bool validBindingConversion =
layoutInfo.type == wgpu::BindingType::StorageBuffer &&
shaderInfo.type == wgpu::BindingType::ReadonlyStorageBuffer;
// TODO(crbug.com/dawn/367): Temporarily allow using either a sampler or a
// comparison sampler until we can perform the proper shader analysis of what
// type is used in the shader module.
validBindingConversion |=
(layoutInfo.type == wgpu::BindingType::Sampler &&
shaderInfo.type == wgpu::BindingType::ComparisonSampler);
validBindingConversion |=
(layoutInfo.type == wgpu::BindingType::ComparisonSampler &&
shaderInfo.type == wgpu::BindingType::Sampler);
if (!validBindingConversion) {
return DAWN_VALIDATION_ERROR(
"The binding type of the bind group layout entry conflicts " +
GetShaderDeclarationString(group, bindingNumber));
}
}
if ((layoutInfo.visibility & StageBit(entryPoint.stage)) == 0) {
return DAWN_VALIDATION_ERROR("The bind group layout entry for " +
GetShaderDeclarationString(group, bindingNumber) +
" is not visible for the shader stage");
}
switch (layoutInfo.type) {
case wgpu::BindingType::SampledTexture: {
if (layoutInfo.textureComponentType != shaderInfo.textureComponentType) {
return DAWN_VALIDATION_ERROR(
"The textureComponentType of the bind group layout entry is "
"different from " +
GetShaderDeclarationString(group, bindingNumber));
}
if (layoutInfo.viewDimension != shaderInfo.viewDimension) {
return DAWN_VALIDATION_ERROR(
"The viewDimension of the bind group layout entry is different "
"from " +
GetShaderDeclarationString(group, bindingNumber));
}
break;
}
case wgpu::BindingType::ReadonlyStorageTexture:
case wgpu::BindingType::WriteonlyStorageTexture: {
ASSERT(layoutInfo.storageTextureFormat != wgpu::TextureFormat::Undefined);
ASSERT(shaderInfo.storageTextureFormat != wgpu::TextureFormat::Undefined);
if (layoutInfo.storageTextureFormat != shaderInfo.storageTextureFormat) {
return DAWN_VALIDATION_ERROR(
"The storageTextureFormat of the bind group layout entry is "
"different from " +
GetShaderDeclarationString(group, bindingNumber));
}
if (layoutInfo.viewDimension != shaderInfo.viewDimension) {
return DAWN_VALIDATION_ERROR(
"The viewDimension of the bind group layout entry is different "
"from " +
GetShaderDeclarationString(group, bindingNumber));
}
break;
}
case wgpu::BindingType::UniformBuffer:
case wgpu::BindingType::ReadonlyStorageBuffer:
case wgpu::BindingType::StorageBuffer: {
if (layoutInfo.minBufferBindingSize != 0 &&
shaderInfo.minBufferBindingSize > layoutInfo.minBufferBindingSize) {
return DAWN_VALIDATION_ERROR(
"The minimum buffer size of the bind group layout entry is smaller "
"than " +
GetShaderDeclarationString(group, bindingNumber));
}
break;
}
case wgpu::BindingType::Sampler:
case wgpu::BindingType::ComparisonSampler:
break;
case wgpu::BindingType::StorageTexture:
default:
UNREACHABLE();
return DAWN_VALIDATION_ERROR("Unsupported binding type");
}
}
return {};
}
} // anonymous namespace
MaybeError ValidateShaderModuleDescriptor(DeviceBase* device, MaybeError ValidateShaderModuleDescriptor(DeviceBase* device,
const ShaderModuleDescriptor* descriptor) { const ShaderModuleDescriptor* descriptor) {
const ChainedStruct* chainedDescriptor = descriptor->nextInChain; const ChainedStruct* chainedDescriptor = descriptor->nextInChain;
@ -582,7 +730,45 @@ namespace dawn_native {
} }
return {}; return {};
} // namespace }
RequiredBufferSizes ComputeRequiredBufferSizesForLayout(
const ShaderModuleBase::EntryPointMetadata& entryPoint,
const PipelineLayoutBase* layout) {
RequiredBufferSizes bufferSizes;
for (BindGroupIndex group : IterateBitSet(layout->GetBindGroupLayoutsMask())) {
bufferSizes[group] = GetBindGroupMinBufferSizes(entryPoint.bindings[group],
layout->GetBindGroupLayout(group));
}
return bufferSizes;
}
MaybeError ValidateCompatibilityWithPipelineLayout(
const ShaderModuleBase::EntryPointMetadata& entryPoint,
const PipelineLayoutBase* layout) {
for (BindGroupIndex group : IterateBitSet(layout->GetBindGroupLayoutsMask())) {
DAWN_TRY(ValidateCompatibilityWithBindGroupLayout(group, entryPoint,
layout->GetBindGroupLayout(group)));
}
for (BindGroupIndex group : IterateBitSet(~layout->GetBindGroupLayoutsMask())) {
if (entryPoint.bindings[group].size() > 0) {
std::ostringstream ostream;
ostream << "No bind group layout entry matches the declaration set "
<< static_cast<uint32_t>(group) << " in the shader module";
return DAWN_VALIDATION_ERROR(ostream.str());
}
}
return {};
}
// EntryPointMetadata
ShaderModuleBase::EntryPointMetadata::EntryPointMetadata() {
fragmentOutputFormatBaseTypes.fill(Format::Type::Other);
}
// ShaderModuleBase // ShaderModuleBase
@ -608,7 +794,6 @@ namespace dawn_native {
UNREACHABLE(); UNREACHABLE();
} }
mFragmentOutputFormatBaseTypes.fill(Format::Type::Other);
if (GetDevice()->IsToggleEnabled(Toggle::UseSpvcParser)) { if (GetDevice()->IsToggleEnabled(Toggle::UseSpvcParser)) {
mSpvcContext.SetUseSpvcParser(true); mSpvcContext.SetUseSpvcParser(true);
} }
@ -632,18 +817,22 @@ namespace dawn_native {
MaybeError ShaderModuleBase::ExtractSpirvInfo(const spirv_cross::Compiler& compiler) { MaybeError ShaderModuleBase::ExtractSpirvInfo(const spirv_cross::Compiler& compiler) {
ASSERT(!IsError()); ASSERT(!IsError());
if (GetDevice()->IsToggleEnabled(Toggle::UseSpvc)) { if (GetDevice()->IsToggleEnabled(Toggle::UseSpvc)) {
DAWN_TRY(ExtractSpirvInfoWithSpvc()); DAWN_TRY_ASSIGN(mMainEntryPoint, ExtractSpirvInfoWithSpvc());
} else { } else {
DAWN_TRY(ExtractSpirvInfoWithSpirvCross(compiler)); DAWN_TRY_ASSIGN(mMainEntryPoint, ExtractSpirvInfoWithSpirvCross(compiler));
} }
return {}; return {};
} }
MaybeError ShaderModuleBase::ExtractSpirvInfoWithSpvc() { ResultOrError<std::unique_ptr<ShaderModuleBase::EntryPointMetadata>>
ShaderModuleBase::ExtractSpirvInfoWithSpvc() {
DeviceBase* device = GetDevice();
std::unique_ptr<EntryPointMetadata> metadata = std::make_unique<EntryPointMetadata>();
shaderc_spvc_execution_model execution_model; shaderc_spvc_execution_model execution_model;
DAWN_TRY(CheckSpvcSuccess(mSpvcContext.GetExecutionModel(&execution_model), DAWN_TRY(CheckSpvcSuccess(mSpvcContext.GetExecutionModel(&execution_model),
"Unable to get execution model for shader.")); "Unable to get execution model for shader."));
mExecutionModel = ToSingleShaderStage(execution_model); metadata->stage = ToSingleShaderStage(execution_model);
size_t push_constant_buffers_count; size_t push_constant_buffers_count;
DAWN_TRY( DAWN_TRY(
@ -658,15 +847,16 @@ namespace dawn_native {
// Fill in bindingInfo with the SPIRV bindings // Fill in bindingInfo with the SPIRV bindings
auto ExtractResourcesBinding = auto ExtractResourcesBinding =
[this](std::vector<shaderc_spvc_binding_info> bindings) -> MaybeError { [](const DeviceBase* device, const std::vector<shaderc_spvc_binding_info>& spvcBindings,
for (const auto& binding : bindings) { ModuleBindingInfo* metadataBindings) -> MaybeError {
for (const shaderc_spvc_binding_info& binding : spvcBindings) {
BindGroupIndex bindGroupIndex(binding.set); BindGroupIndex bindGroupIndex(binding.set);
if (bindGroupIndex >= kMaxBindGroupsTyped) { if (bindGroupIndex >= kMaxBindGroupsTyped) {
return DAWN_VALIDATION_ERROR("Bind group index over limits in the SPIRV"); return DAWN_VALIDATION_ERROR("Bind group index over limits in the SPIRV");
} }
const auto& it = mBindingInfo[bindGroupIndex].emplace( const auto& it = (*metadataBindings)[bindGroupIndex].emplace(
BindingNumber(binding.binding), ShaderBindingInfo{}); BindingNumber(binding.binding), ShaderBindingInfo{});
if (!it.second) { if (!it.second) {
return DAWN_VALIDATION_ERROR("Shader has duplicate bindings"); return DAWN_VALIDATION_ERROR("Shader has duplicate bindings");
@ -694,8 +884,7 @@ namespace dawn_native {
return DAWN_VALIDATION_ERROR( return DAWN_VALIDATION_ERROR(
"Invalid image format declaration on storage image"); "Invalid image format declaration on storage image");
} }
const Format& format = const Format& format = device->GetValidInternalFormat(storageTextureFormat);
GetDevice()->GetValidInternalFormat(storageTextureFormat);
if (!format.supportsStorageUsage) { if (!format.supportsStorageUsage) {
return DAWN_VALIDATION_ERROR( return DAWN_VALIDATION_ERROR(
"The storage texture format is not supported"); "The storage texture format is not supported");
@ -722,45 +911,45 @@ namespace dawn_native {
shaderc_spvc_shader_resource_uniform_buffers, shaderc_spvc_shader_resource_uniform_buffers,
shaderc_spvc_binding_type_uniform_buffer, &resource_bindings), shaderc_spvc_binding_type_uniform_buffer, &resource_bindings),
"Unable to get binding info for uniform buffers from shader")); "Unable to get binding info for uniform buffers from shader"));
DAWN_TRY(ExtractResourcesBinding(resource_bindings)); DAWN_TRY(ExtractResourcesBinding(device, resource_bindings, &metadata->bindings));
DAWN_TRY(CheckSpvcSuccess( DAWN_TRY(CheckSpvcSuccess(
mSpvcContext.GetBindingInfo(shaderc_spvc_shader_resource_separate_images, mSpvcContext.GetBindingInfo(shaderc_spvc_shader_resource_separate_images,
shaderc_spvc_binding_type_sampled_texture, shaderc_spvc_binding_type_sampled_texture,
&resource_bindings), &resource_bindings),
"Unable to get binding info for sampled textures from shader")); "Unable to get binding info for sampled textures from shader"));
DAWN_TRY(ExtractResourcesBinding(resource_bindings)); DAWN_TRY(ExtractResourcesBinding(device, resource_bindings, &metadata->bindings));
DAWN_TRY(CheckSpvcSuccess( DAWN_TRY(CheckSpvcSuccess(
mSpvcContext.GetBindingInfo(shaderc_spvc_shader_resource_separate_samplers, mSpvcContext.GetBindingInfo(shaderc_spvc_shader_resource_separate_samplers,
shaderc_spvc_binding_type_sampler, &resource_bindings), shaderc_spvc_binding_type_sampler, &resource_bindings),
"Unable to get binding info for samples from shader")); "Unable to get binding info for samples from shader"));
DAWN_TRY(ExtractResourcesBinding(resource_bindings)); DAWN_TRY(ExtractResourcesBinding(device, resource_bindings, &metadata->bindings));
DAWN_TRY(CheckSpvcSuccess(mSpvcContext.GetBindingInfo( DAWN_TRY(CheckSpvcSuccess(mSpvcContext.GetBindingInfo(
shaderc_spvc_shader_resource_storage_buffers, shaderc_spvc_shader_resource_storage_buffers,
shaderc_spvc_binding_type_storage_buffer, &resource_bindings), shaderc_spvc_binding_type_storage_buffer, &resource_bindings),
"Unable to get binding info for storage buffers from shader")); "Unable to get binding info for storage buffers from shader"));
DAWN_TRY(ExtractResourcesBinding(resource_bindings)); DAWN_TRY(ExtractResourcesBinding(device, resource_bindings, &metadata->bindings));
DAWN_TRY(CheckSpvcSuccess( DAWN_TRY(CheckSpvcSuccess(
mSpvcContext.GetBindingInfo(shaderc_spvc_shader_resource_storage_images, mSpvcContext.GetBindingInfo(shaderc_spvc_shader_resource_storage_images,
shaderc_spvc_binding_type_storage_texture, shaderc_spvc_binding_type_storage_texture,
&resource_bindings), &resource_bindings),
"Unable to get binding info for storage textures from shader")); "Unable to get binding info for storage textures from shader"));
DAWN_TRY(ExtractResourcesBinding(resource_bindings)); DAWN_TRY(ExtractResourcesBinding(device, resource_bindings, &metadata->bindings));
std::vector<shaderc_spvc_resource_location_info> input_stage_locations; std::vector<shaderc_spvc_resource_location_info> input_stage_locations;
DAWN_TRY(CheckSpvcSuccess(mSpvcContext.GetInputStageLocationInfo(&input_stage_locations), DAWN_TRY(CheckSpvcSuccess(mSpvcContext.GetInputStageLocationInfo(&input_stage_locations),
"Unable to get input stage location information from shader")); "Unable to get input stage location information from shader"));
for (const auto& input : input_stage_locations) { for (const auto& input : input_stage_locations) {
if (mExecutionModel == SingleShaderStage::Vertex) { if (metadata->stage == SingleShaderStage::Vertex) {
if (input.location >= kMaxVertexAttributes) { if (input.location >= kMaxVertexAttributes) {
return DAWN_VALIDATION_ERROR("Attribute location over limits in the SPIRV"); return DAWN_VALIDATION_ERROR("Attribute location over limits in the SPIRV");
} }
mUsedVertexAttributes.set(input.location); metadata->usedVertexAttributes.set(input.location);
} else if (mExecutionModel == SingleShaderStage::Fragment) { } else if (metadata->stage == SingleShaderStage::Fragment) {
// Without a location qualifier on vertex inputs, spirv_cross::CompilerMSL gives // Without a location qualifier on vertex inputs, spirv_cross::CompilerMSL gives
// them all the location 0, causing a compile error. // them all the location 0, causing a compile error.
if (!input.has_location) { if (!input.has_location) {
@ -774,13 +963,13 @@ namespace dawn_native {
"Unable to get output stage location information from shader")); "Unable to get output stage location information from shader"));
for (const auto& output : output_stage_locations) { for (const auto& output : output_stage_locations) {
if (mExecutionModel == SingleShaderStage::Vertex) { if (metadata->stage == SingleShaderStage::Vertex) {
// Without a location qualifier on vertex outputs, spirv_cross::CompilerMSL // Without a location qualifier on vertex outputs, spirv_cross::CompilerMSL
// gives them all the location 0, causing a compile error. // gives them all the location 0, causing a compile error.
if (!output.has_location) { if (!output.has_location) {
return DAWN_VALIDATION_ERROR("Need location qualifier on vertex output"); return DAWN_VALIDATION_ERROR("Need location qualifier on vertex output");
} }
} else if (mExecutionModel == SingleShaderStage::Fragment) { } else if (metadata->stage == SingleShaderStage::Fragment) {
if (output.location >= kMaxColorAttachments) { if (output.location >= kMaxColorAttachments) {
return DAWN_VALIDATION_ERROR( return DAWN_VALIDATION_ERROR(
"Fragment output location over limits in the SPIRV"); "Fragment output location over limits in the SPIRV");
@ -788,7 +977,7 @@ namespace dawn_native {
} }
} }
if (mExecutionModel == SingleShaderStage::Fragment) { if (metadata->stage == SingleShaderStage::Fragment) {
std::vector<shaderc_spvc_resource_type_info> output_types; std::vector<shaderc_spvc_resource_type_info> output_types;
DAWN_TRY(CheckSpvcSuccess(mSpvcContext.GetOutputStageTypeInfo(&output_types), DAWN_TRY(CheckSpvcSuccess(mSpvcContext.GetOutputStageTypeInfo(&output_types),
"Unable to get output stage type information from shader")); "Unable to get output stage type information from shader"));
@ -797,27 +986,32 @@ namespace dawn_native {
if (output.type == shaderc_spvc_texture_format_type_other) { if (output.type == shaderc_spvc_texture_format_type_other) {
return DAWN_VALIDATION_ERROR("Unexpected Fragment output type"); return DAWN_VALIDATION_ERROR("Unexpected Fragment output type");
} }
mFragmentOutputFormatBaseTypes[output.location] = ToDawnFormatType(output.type); metadata->fragmentOutputFormatBaseTypes[output.location] =
ToDawnFormatType(output.type);
} }
} }
return {};
}
MaybeError ShaderModuleBase::ExtractSpirvInfoWithSpirvCross( return {std::move(metadata)};
const spirv_cross::Compiler& compiler) { }
ResultOrError<std::unique_ptr<ShaderModuleBase::EntryPointMetadata>>
ShaderModuleBase::ExtractSpirvInfoWithSpirvCross(const spirv_cross::Compiler& compiler) {
DeviceBase* device = GetDevice();
std::unique_ptr<EntryPointMetadata> metadata = std::make_unique<EntryPointMetadata>();
// TODO(cwallez@chromium.org): make errors here creation errors // TODO(cwallez@chromium.org): make errors here creation errors
// currently errors here do not prevent the shadermodule from being used // currently errors here do not prevent the shadermodule from being used
const auto& resources = compiler.get_shader_resources(); const auto& resources = compiler.get_shader_resources();
switch (compiler.get_execution_model()) { switch (compiler.get_execution_model()) {
case spv::ExecutionModelVertex: case spv::ExecutionModelVertex:
mExecutionModel = SingleShaderStage::Vertex; metadata->stage = SingleShaderStage::Vertex;
break; break;
case spv::ExecutionModelFragment: case spv::ExecutionModelFragment:
mExecutionModel = SingleShaderStage::Fragment; metadata->stage = SingleShaderStage::Fragment;
break; break;
case spv::ExecutionModelGLCompute: case spv::ExecutionModelGLCompute:
mExecutionModel = SingleShaderStage::Compute; metadata->stage = SingleShaderStage::Compute;
break; break;
default: default:
UNREACHABLE(); UNREACHABLE();
@ -834,9 +1028,10 @@ namespace dawn_native {
// Fill in bindingInfo with the SPIRV bindings // Fill in bindingInfo with the SPIRV bindings
auto ExtractResourcesBinding = auto ExtractResourcesBinding =
[this](const spirv_cross::SmallVector<spirv_cross::Resource>& resources, [](const DeviceBase* device,
const spirv_cross::Compiler& compiler, const spirv_cross::SmallVector<spirv_cross::Resource>& resources,
wgpu::BindingType bindingType) -> MaybeError { const spirv_cross::Compiler& compiler, wgpu::BindingType bindingType,
ModuleBindingInfo* metadataBindings) -> MaybeError {
for (const auto& resource : resources) { for (const auto& resource : resources) {
if (!compiler.get_decoration_bitset(resource.id).get(spv::DecorationBinding)) { if (!compiler.get_decoration_bitset(resource.id).get(spv::DecorationBinding)) {
return DAWN_VALIDATION_ERROR("No Binding decoration set for resource"); return DAWN_VALIDATION_ERROR("No Binding decoration set for resource");
@ -857,7 +1052,7 @@ namespace dawn_native {
} }
const auto& it = const auto& it =
mBindingInfo[bindGroupIndex].emplace(bindingNumber, ShaderBindingInfo{}); (*metadataBindings)[bindGroupIndex].emplace(bindingNumber, ShaderBindingInfo{});
if (!it.second) { if (!it.second) {
return DAWN_VALIDATION_ERROR("Shader has duplicate bindings"); return DAWN_VALIDATION_ERROR("Shader has duplicate bindings");
} }
@ -919,8 +1114,7 @@ namespace dawn_native {
return DAWN_VALIDATION_ERROR( return DAWN_VALIDATION_ERROR(
"Invalid image format declaration on storage image"); "Invalid image format declaration on storage image");
} }
const Format& format = const Format& format = device->GetValidInternalFormat(storageTextureFormat);
GetDevice()->GetValidInternalFormat(storageTextureFormat);
if (!format.supportsStorageUsage) { if (!format.supportsStorageUsage) {
return DAWN_VALIDATION_ERROR( return DAWN_VALIDATION_ERROR(
"The storage texture format is not supported"); "The storage texture format is not supported");
@ -938,19 +1132,19 @@ namespace dawn_native {
return {}; return {};
}; };
DAWN_TRY(ExtractResourcesBinding(resources.uniform_buffers, compiler, DAWN_TRY(ExtractResourcesBinding(device, resources.uniform_buffers, compiler,
wgpu::BindingType::UniformBuffer)); wgpu::BindingType::UniformBuffer, &metadata->bindings));
DAWN_TRY(ExtractResourcesBinding(resources.separate_images, compiler, DAWN_TRY(ExtractResourcesBinding(device, resources.separate_images, compiler,
wgpu::BindingType::SampledTexture)); wgpu::BindingType::SampledTexture, &metadata->bindings));
DAWN_TRY(ExtractResourcesBinding(resources.separate_samplers, compiler, DAWN_TRY(ExtractResourcesBinding(device, resources.separate_samplers, compiler,
wgpu::BindingType::Sampler)); wgpu::BindingType::Sampler, &metadata->bindings));
DAWN_TRY(ExtractResourcesBinding(resources.storage_buffers, compiler, DAWN_TRY(ExtractResourcesBinding(device, resources.storage_buffers, compiler,
wgpu::BindingType::StorageBuffer)); wgpu::BindingType::StorageBuffer, &metadata->bindings));
DAWN_TRY(ExtractResourcesBinding(resources.storage_images, compiler, DAWN_TRY(ExtractResourcesBinding(device, resources.storage_images, compiler,
wgpu::BindingType::StorageTexture)); wgpu::BindingType::StorageTexture, &metadata->bindings));
// Extract the vertex attributes // Extract the vertex attributes
if (mExecutionModel == SingleShaderStage::Vertex) { if (metadata->stage == SingleShaderStage::Vertex) {
for (const auto& attrib : resources.stage_inputs) { for (const auto& attrib : resources.stage_inputs) {
if (!(compiler.get_decoration_bitset(attrib.id).get(spv::DecorationLocation))) { if (!(compiler.get_decoration_bitset(attrib.id).get(spv::DecorationLocation))) {
return DAWN_VALIDATION_ERROR( return DAWN_VALIDATION_ERROR(
@ -962,7 +1156,7 @@ namespace dawn_native {
return DAWN_VALIDATION_ERROR("Attribute location over limits in the SPIRV"); return DAWN_VALIDATION_ERROR("Attribute location over limits in the SPIRV");
} }
mUsedVertexAttributes.set(location); metadata->usedVertexAttributes.set(location);
} }
// Without a location qualifier on vertex outputs, spirv_cross::CompilerMSL gives // Without a location qualifier on vertex outputs, spirv_cross::CompilerMSL gives
@ -974,7 +1168,7 @@ namespace dawn_native {
} }
} }
if (mExecutionModel == SingleShaderStage::Fragment) { if (metadata->stage == SingleShaderStage::Fragment) {
// Without a location qualifier on vertex inputs, spirv_cross::CompilerMSL gives // Without a location qualifier on vertex inputs, spirv_cross::CompilerMSL gives
// them all the location 0, causing a compile error. // them all the location 0, causing a compile error.
for (const auto& attrib : resources.stage_inputs) { for (const auto& attrib : resources.stage_inputs) {
@ -1003,209 +1197,44 @@ namespace dawn_native {
if (formatType == Format::Type::Other) { if (formatType == Format::Type::Other) {
return DAWN_VALIDATION_ERROR("Unexpected Fragment output type"); return DAWN_VALIDATION_ERROR("Unexpected Fragment output type");
} }
mFragmentOutputFormatBaseTypes[location] = formatType; metadata->fragmentOutputFormatBaseTypes[location] = formatType;
} }
} }
return {};
return {std::move(metadata)};
} }
const ShaderModuleBase::ModuleBindingInfo& ShaderModuleBase::GetBindingInfo() const { const ShaderModuleBase::ModuleBindingInfo& ShaderModuleBase::GetBindingInfo() const {
ASSERT(!IsError()); ASSERT(!IsError());
return mBindingInfo; return mMainEntryPoint->bindings;
} }
const std::bitset<kMaxVertexAttributes>& ShaderModuleBase::GetUsedVertexAttributes() const { const std::bitset<kMaxVertexAttributes>& ShaderModuleBase::GetUsedVertexAttributes() const {
ASSERT(!IsError()); ASSERT(!IsError());
return mUsedVertexAttributes; return mMainEntryPoint->usedVertexAttributes;
} }
const ShaderModuleBase::FragmentOutputBaseTypes& ShaderModuleBase::GetFragmentOutputBaseTypes() const ShaderModuleBase::FragmentOutputBaseTypes& ShaderModuleBase::GetFragmentOutputBaseTypes()
const { const {
ASSERT(!IsError()); ASSERT(!IsError());
return mFragmentOutputFormatBaseTypes; return mMainEntryPoint->fragmentOutputFormatBaseTypes;
} }
SingleShaderStage ShaderModuleBase::GetExecutionModel() const { SingleShaderStage ShaderModuleBase::GetExecutionModel() const {
ASSERT(!IsError()); ASSERT(!IsError());
return mExecutionModel; return mMainEntryPoint->stage;
} }
RequiredBufferSizes ShaderModuleBase::ComputeRequiredBufferSizesForLayout( RequiredBufferSizes ShaderModuleBase::ComputeRequiredBufferSizesForLayout(
const PipelineLayoutBase* layout) const { const PipelineLayoutBase* layout) const {
RequiredBufferSizes bufferSizes; ASSERT(!IsError());
for (BindGroupIndex group : IterateBitSet(layout->GetBindGroupLayoutsMask())) { return ::dawn_native::ComputeRequiredBufferSizesForLayout(*mMainEntryPoint, layout);
bufferSizes[group] =
GetBindGroupMinBufferSizes(mBindingInfo[group], layout->GetBindGroupLayout(group));
}
return bufferSizes;
}
std::vector<uint64_t> ShaderModuleBase::GetBindGroupMinBufferSizes(
const BindingInfoMap& shaderMap,
const BindGroupLayoutBase* layout) const {
std::vector<uint64_t> requiredBufferSizes(layout->GetUnverifiedBufferCount());
uint32_t packedIdx = 0;
for (BindingIndex bindingIndex{0}; bindingIndex < layout->GetBufferCount();
++bindingIndex) {
const BindingInfo& bindingInfo = layout->GetBindingInfo(bindingIndex);
if (bindingInfo.minBufferBindingSize != 0) {
// Skip bindings that have minimum buffer size set in the layout
continue;
}
ASSERT(packedIdx < requiredBufferSizes.size());
const auto& shaderInfo = shaderMap.find(bindingInfo.binding);
if (shaderInfo != shaderMap.end()) {
requiredBufferSizes[packedIdx] = shaderInfo->second.minBufferBindingSize;
} else {
// We have to include buffers if they are included in the bind group's
// packed vector. We don't actually need to check these at draw time, so
// if this is a problem in the future we can optimize it further.
requiredBufferSizes[packedIdx] = 0;
}
++packedIdx;
}
return requiredBufferSizes;
} }
MaybeError ShaderModuleBase::ValidateCompatibilityWithPipelineLayout( MaybeError ShaderModuleBase::ValidateCompatibilityWithPipelineLayout(
const PipelineLayoutBase* layout) const { const PipelineLayoutBase* layout) const {
ASSERT(!IsError()); ASSERT(!IsError());
return ::dawn_native::ValidateCompatibilityWithPipelineLayout(*mMainEntryPoint, layout);
for (BindGroupIndex group : IterateBitSet(layout->GetBindGroupLayoutsMask())) {
DAWN_TRY(
ValidateCompatibilityWithBindGroupLayout(group, layout->GetBindGroupLayout(group)));
}
for (BindGroupIndex group : IterateBitSet(~layout->GetBindGroupLayoutsMask())) {
if (mBindingInfo[group].size() > 0) {
std::ostringstream ostream;
ostream << "No bind group layout entry matches the declaration set "
<< static_cast<uint32_t>(group) << " in the shader module";
return DAWN_VALIDATION_ERROR(ostream.str());
}
}
return {};
}
MaybeError ShaderModuleBase::ValidateCompatibilityWithBindGroupLayout(
BindGroupIndex group,
const BindGroupLayoutBase* layout) const {
ASSERT(!IsError());
const BindGroupLayoutBase::BindingMap& bindingMap = layout->GetBindingMap();
// Iterate over all bindings used by this group in the shader, and find the
// corresponding binding in the BindGroupLayout, if it exists.
for (const auto& it : mBindingInfo[group]) {
BindingNumber bindingNumber = it.first;
const ShaderBindingInfo& moduleInfo = it.second;
const auto& bindingIt = bindingMap.find(bindingNumber);
if (bindingIt == bindingMap.end()) {
return DAWN_VALIDATION_ERROR("Missing bind group layout entry for " +
GetShaderDeclarationString(group, bindingNumber));
}
BindingIndex bindingIndex(bindingIt->second);
const BindingInfo& bindingInfo = layout->GetBindingInfo(bindingIndex);
if (bindingInfo.type != moduleInfo.type) {
// Binding mismatch between shader and bind group is invalid. For example, a
// writable binding in the shader with a readonly storage buffer in the bind group
// layout is invalid. However, a readonly binding in the shader with a writable
// storage buffer in the bind group layout is valid.
bool validBindingConversion =
bindingInfo.type == wgpu::BindingType::StorageBuffer &&
moduleInfo.type == wgpu::BindingType::ReadonlyStorageBuffer;
// TODO(crbug.com/dawn/367): Temporarily allow using either a sampler or a
// comparison sampler until we can perform the proper shader analysis of what type
// is used in the shader module.
validBindingConversion |= (bindingInfo.type == wgpu::BindingType::Sampler &&
moduleInfo.type == wgpu::BindingType::ComparisonSampler);
validBindingConversion |=
(bindingInfo.type == wgpu::BindingType::ComparisonSampler &&
moduleInfo.type == wgpu::BindingType::Sampler);
if (!validBindingConversion) {
return DAWN_VALIDATION_ERROR(
"The binding type of the bind group layout entry conflicts " +
GetShaderDeclarationString(group, bindingNumber));
}
}
if ((bindingInfo.visibility & StageBit(mExecutionModel)) == 0) {
return DAWN_VALIDATION_ERROR("The bind group layout entry for " +
GetShaderDeclarationString(group, bindingNumber) +
" is not visible for the shader stage");
}
switch (bindingInfo.type) {
case wgpu::BindingType::SampledTexture: {
if (bindingInfo.textureComponentType != moduleInfo.textureComponentType) {
return DAWN_VALIDATION_ERROR(
"The textureComponentType of the bind group layout entry is different "
"from " +
GetShaderDeclarationString(group, bindingNumber));
}
if (bindingInfo.viewDimension != moduleInfo.viewDimension) {
return DAWN_VALIDATION_ERROR(
"The viewDimension of the bind group layout entry is different "
"from " +
GetShaderDeclarationString(group, bindingNumber));
}
break;
}
case wgpu::BindingType::ReadonlyStorageTexture:
case wgpu::BindingType::WriteonlyStorageTexture: {
ASSERT(bindingInfo.storageTextureFormat != wgpu::TextureFormat::Undefined);
ASSERT(moduleInfo.storageTextureFormat != wgpu::TextureFormat::Undefined);
if (bindingInfo.storageTextureFormat != moduleInfo.storageTextureFormat) {
return DAWN_VALIDATION_ERROR(
"The storageTextureFormat of the bind group layout entry is different "
"from " +
GetShaderDeclarationString(group, bindingNumber));
}
if (bindingInfo.viewDimension != moduleInfo.viewDimension) {
return DAWN_VALIDATION_ERROR(
"The viewDimension of the bind group layout entry is different "
"from " +
GetShaderDeclarationString(group, bindingNumber));
}
break;
}
case wgpu::BindingType::UniformBuffer:
case wgpu::BindingType::ReadonlyStorageBuffer:
case wgpu::BindingType::StorageBuffer: {
if (bindingInfo.minBufferBindingSize != 0 &&
moduleInfo.minBufferBindingSize > bindingInfo.minBufferBindingSize) {
return DAWN_VALIDATION_ERROR(
"The minimum buffer size of the bind group layout entry is smaller "
"than " +
GetShaderDeclarationString(group, bindingNumber));
}
break;
}
case wgpu::BindingType::Sampler:
case wgpu::BindingType::ComparisonSampler:
break;
case wgpu::BindingType::StorageTexture:
default:
UNREACHABLE();
return DAWN_VALIDATION_ERROR("Unsupported binding type");
}
}
return {};
} }
size_t ShaderModuleBase::HashFunc::operator()(const ShaderModuleBase* module) const { size_t ShaderModuleBase::HashFunc::operator()(const ShaderModuleBase* module) const {

View File

@ -43,8 +43,6 @@ namespace dawn_native {
class ShaderModuleBase : public CachedObject { class ShaderModuleBase : public CachedObject {
public: public:
enum class Type { Undefined, Spirv, Wgsl };
ShaderModuleBase(DeviceBase* device, const ShaderModuleDescriptor* descriptor); ShaderModuleBase(DeviceBase* device, const ShaderModuleDescriptor* descriptor);
~ShaderModuleBase() override; ~ShaderModuleBase() override;
@ -98,6 +96,15 @@ namespace dawn_native {
uint32_t pullingBufferBindingSet) const; uint32_t pullingBufferBindingSet) const;
#endif #endif
struct EntryPointMetadata {
EntryPointMetadata();
ModuleBindingInfo bindings;
std::bitset<kMaxVertexAttributes> usedVertexAttributes;
SingleShaderStage stage;
FragmentOutputBaseTypes fragmentOutputFormatBaseTypes;
};
protected: protected:
static MaybeError CheckSpvcSuccess(shaderc_spvc_status status, const char* error_msg); static MaybeError CheckSpvcSuccess(shaderc_spvc_status status, const char* error_msg);
shaderc_spvc::CompileOptions GetCompileOptions() const; shaderc_spvc::CompileOptions GetCompileOptions() const;
@ -108,27 +115,18 @@ namespace dawn_native {
private: private:
ShaderModuleBase(DeviceBase* device, ObjectBase::ErrorTag tag); ShaderModuleBase(DeviceBase* device, ObjectBase::ErrorTag tag);
MaybeError ValidateCompatibilityWithBindGroupLayout(
BindGroupIndex group,
const BindGroupLayoutBase* layout) const;
std::vector<uint64_t> GetBindGroupMinBufferSizes(const BindingInfoMap& shaderMap,
const BindGroupLayoutBase* layout) const;
// Different implementations reflection into the shader depending on // Different implementations reflection into the shader depending on
// whether using spvc, or directly accessing spirv-cross. // whether using spvc, or directly accessing spirv-cross.
MaybeError ExtractSpirvInfoWithSpvc(); ResultOrError<std::unique_ptr<EntryPointMetadata>> ExtractSpirvInfoWithSpvc();
MaybeError ExtractSpirvInfoWithSpirvCross(const spirv_cross::Compiler& compiler); ResultOrError<std::unique_ptr<EntryPointMetadata>> ExtractSpirvInfoWithSpirvCross(
const spirv_cross::Compiler& compiler);
enum class Type { Undefined, Spirv, Wgsl };
Type mType; Type mType;
std::vector<uint32_t> mSpirv; std::vector<uint32_t> mSpirv;
std::string mWgsl; std::string mWgsl;
ModuleBindingInfo mBindingInfo; std::unique_ptr<EntryPointMetadata> mMainEntryPoint;
std::bitset<kMaxVertexAttributes> mUsedVertexAttributes;
SingleShaderStage mExecutionModel;
FragmentOutputBaseTypes mFragmentOutputFormatBaseTypes;
}; };
} // namespace dawn_native } // namespace dawn_native