Remove support for multiple entrypoints with the same name

Previsouly having a ShaderModule with multiple entrypoints with the same
name and different stages was valid in Dawn. However it is disallowed by
the WGSL specification so change Dawn to index the ShaderModule's
entrypoints only by their name (instead of name and stage).

Bug: dawn:216
Change-Id: Id6fc80a03436b008c2f057bd30c70fdf240919e8
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/31665
Reviewed-by: dan sinclair <dsinclair@chromium.org>
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
This commit is contained in:
Corentin Wallez 2020-11-05 13:25:16 +00:00 committed by Commit Bot service account
parent e87a8c466f
commit d42713de7a
9 changed files with 34 additions and 83 deletions

View File

@ -29,15 +29,20 @@ namespace dawn_native {
const ShaderModuleBase* module = descriptor->module;
DAWN_TRY(device->ValidateObject(module));
if (!module->HasEntryPoint(descriptor->entryPoint, stage)) {
if (!module->HasEntryPoint(descriptor->entryPoint)) {
return DAWN_VALIDATION_ERROR("Entry point doesn't exist in the module");
}
const EntryPointMetadata& metadata = module->GetEntryPoint(descriptor->entryPoint);
if (metadata.stage != stage) {
return DAWN_VALIDATION_ERROR("Entry point isn't for the correct stage");
}
if (layout != nullptr) {
const EntryPointMetadata& metadata =
module->GetEntryPoint(descriptor->entryPoint, stage);
DAWN_TRY(ValidateCompatibilityWithPipelineLayout(device, metadata, layout));
}
return {};
}
@ -54,7 +59,9 @@ namespace dawn_native {
SingleShaderStage shaderStage = stage.first;
ShaderModuleBase* module = stage.second->module;
const char* entryPointName = stage.second->entryPoint;
const EntryPointMetadata& metadata = module->GetEntryPoint(entryPointName, shaderStage);
const EntryPointMetadata& metadata = module->GetEntryPoint(entryPointName);
ASSERT(metadata.stage == shaderStage);
// Record them internally.
bool isFirstStage = mStageMask == wgpu::ShaderStage::None;

View File

@ -148,9 +148,8 @@ namespace dawn_native {
// Loops over all the reflected BindGroupLayoutEntries from shaders.
for (const StageAndDescriptor& stage : stages) {
SingleShaderStage shaderStage = stage.first;
const EntryPointMetadata::BindingInfo& info =
stage.second->module->GetEntryPoint(stage.second->entryPoint, shaderStage).bindings;
stage.second->module->GetEntryPoint(stage.second->entryPoint).bindings;
for (BindGroupIndex group(0); group < info.size(); ++group) {
for (const auto& bindingIt : info[group]) {
@ -160,7 +159,7 @@ namespace dawn_native {
// Create the BindGroupLayoutEntry
BindGroupLayoutEntry entry = ConvertMetadataToEntry(shaderBinding);
entry.binding = static_cast<uint32_t>(bindingNumber);
entry.visibility = StageBit(shaderStage);
entry.visibility = StageBit(stage.first);
// Add it to our map of all entries, if there is an existing entry, then we
// need to merge, if we can.
@ -206,7 +205,7 @@ namespace dawn_native {
// Sanity check in debug that the pipeline layout is compatible with the current pipeline.
for (const StageAndDescriptor& stage : stages) {
const EntryPointMetadata& metadata =
stage.second->module->GetEntryPoint(stage.second->entryPoint, stage.first);
stage.second->module->GetEntryPoint(stage.second->entryPoint);
ASSERT(ValidateCompatibilityWithPipelineLayout(device, metadata, pipelineLayout)
.IsSuccess());
}

View File

@ -330,8 +330,8 @@ namespace dawn_native {
DAWN_TRY(ValidateRasterizationStateDescriptor(descriptor->rasterizationState));
}
const EntryPointMetadata& vertexMetadata = descriptor->vertexStage.module->GetEntryPoint(
descriptor->vertexStage.entryPoint, SingleShaderStage::Vertex);
const EntryPointMetadata& vertexMetadata =
descriptor->vertexStage.module->GetEntryPoint(descriptor->vertexStage.entryPoint);
if ((vertexMetadata.usedVertexAttributes & ~attributesSetMask).any()) {
return DAWN_VALIDATION_ERROR(
"Pipeline vertex stage uses vertex buffers not in the vertex state");
@ -352,8 +352,7 @@ namespace dawn_native {
ASSERT(descriptor->fragmentStage != nullptr);
const EntryPointMetadata& fragmentMetadata =
descriptor->fragmentStage->module->GetEntryPoint(descriptor->fragmentStage->entryPoint,
SingleShaderStage::Fragment);
descriptor->fragmentStage->module->GetEntryPoint(descriptor->fragmentStage->entryPoint);
for (ColorAttachmentIndex i(uint8_t(0));
i < ColorAttachmentIndex(static_cast<uint8_t>(descriptor->colorStateCount)); ++i) {
DAWN_TRY(ValidateColorStateDescriptor(

View File

@ -844,19 +844,13 @@ namespace dawn_native {
return new ShaderModuleBase(device, ObjectBase::kError);
}
bool ShaderModuleBase::HasEntryPoint(const std::string& entryPoint,
SingleShaderStage stage) const {
auto entryPointsForNameIt = mEntryPoints.find(entryPoint);
if (entryPointsForNameIt == mEntryPoints.end()) {
return false;
}
return entryPointsForNameIt->second[stage] != nullptr;
bool ShaderModuleBase::HasEntryPoint(const std::string& entryPoint) const {
return mEntryPoints.count(entryPoint) > 0;
}
const EntryPointMetadata& ShaderModuleBase::GetEntryPoint(const std::string& entryPoint,
SingleShaderStage stage) const {
ASSERT(HasEntryPoint(entryPoint, stage));
return *mEntryPoints.at(entryPoint)[stage];
const EntryPointMetadata& ShaderModuleBase::GetEntryPoint(const std::string& entryPoint) const {
ASSERT(HasEntryPoint(entryPoint));
return *mEntryPoints.at(entryPoint);
}
size_t ShaderModuleBase::HashFunc::operator()(const ShaderModuleBase* module) const {
@ -921,13 +915,15 @@ namespace dawn_native {
spirv_cross::Compiler compiler(mSpirv);
for (const spirv_cross::EntryPoint& entryPoint : compiler.get_entry_points_and_stages()) {
ASSERT(mEntryPoints.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(GetDevice(), compiler, entryPoint.name, stage));
mEntryPoints[entryPoint.name][stage] = std::move(metadata);
mEntryPoints[entryPoint.name] = std::move(metadata);
}
return {};

View File

@ -94,13 +94,12 @@ namespace dawn_native {
static ShaderModuleBase* MakeError(DeviceBase* device);
// Return true iff the module has an entrypoint called `entryPoint` for stage `stage`.
bool HasEntryPoint(const std::string& entryPoint, SingleShaderStage stage) const;
// Return true iff the module has an entrypoint called `entryPoint`.
bool HasEntryPoint(const std::string& entryPoint) const;
// Returns the metadata for the given `entryPoint` and `stage`. HasEntryPoint with the same
// arguments must be true.
const EntryPointMetadata& GetEntryPoint(const std::string& entryPoint,
SingleShaderStage stage) const;
// Returns the metadata for the given `entryPoint`. HasEntryPoint with the same argument
// must be true.
const EntryPointMetadata& GetEntryPoint(const std::string& entryPoint) const;
// Functors necessary for the unordered_set<ShaderModuleBase*>-based cache.
struct HashFunc {
@ -132,7 +131,7 @@ namespace dawn_native {
std::string mWgsl;
// A map from [name, stage] to EntryPointMetadata.
std::unordered_map<std::string, PerStage<std::unique_ptr<EntryPointMetadata>>> mEntryPoints;
std::unordered_map<std::string, std::unique_ptr<EntryPointMetadata>> mEntryPoints;
};
} // namespace dawn_native

View File

@ -209,7 +209,7 @@ namespace dawn_native { namespace d3d12 {
compiler.set_entry_point(entryPointName, ShaderStageToExecutionModel(stage));
const EntryPointMetadata::BindingInfo& moduleBindingInfo =
GetEntryPoint(entryPointName, stage).bindings;
GetEntryPoint(entryPointName).bindings;
for (BindGroupIndex group : IterateBitSet(layout->GetBindGroupLayoutsMask())) {
const BindGroupLayout* bgl = ToBackend(layout->GetBindGroupLayout(group));

View File

@ -182,7 +182,7 @@ namespace dawn_native { namespace metal {
out->needsStorageBufferLength = compiler.needs_buffer_size_buffer();
if (GetDevice()->IsToggleEnabled(Toggle::MetalEnableVertexPulling) &&
GetEntryPoint(entryPointName, stage).usedVertexAttributes.any()) {
GetEntryPoint(entryPointName).usedVertexAttributes.any()) {
out->needsStorageBufferLength = true;
}

View File

@ -108,8 +108,7 @@ namespace dawn_native { namespace opengl {
compiler.set_name(combined.combined_id, info->GetName());
}
const EntryPointMetadata::BindingInfo& bindingInfo =
GetEntryPoint(entryPointName, stage).bindings;
const EntryPointMetadata::BindingInfo& bindingInfo = GetEntryPoint(entryPointName).bindings;
// Change binding names to be "dawn_binding_<group>_<binding>".
// Also unsets the SPIRV "Binding" decoration as it outputs "layout(binding=)" which

View File

@ -67,54 +67,6 @@ TEST_P(EntryPointTests, FragAndVertexSameModule) {
EXPECT_PIXEL_RGBA8_EQ(RGBA8::kRed, renderPass.color, 0, 0);
}
// Test creating a render pipeline from two entryPoints in the same module with the same name.
TEST_P(EntryPointTests, FragAndVertexSameModuleSameName) {
// TODO: Reenable once Tint is able to produce Vulkan 1.0 / 1.1 SPIR-V.
DAWN_SKIP_TEST_IF(IsVulkan());
wgpu::ShaderModule module = utils::CreateShaderModuleFromWGSL(device, R"(
[[builtin(position)]] var<out> Position : vec4<f32>;
[[stage(vertex)]]
fn main() -> void {
Position = vec4<f32>(0.0, 0.0, 0.0, 1.0);
return;
}
[[location(0)]] var<out> outColor : vec4<f32>;
[[stage(fragment)]]
fn main() -> void {
outColor = vec4<f32>(1.0, 0.0, 0.0, 1.0);
return;
}
)");
// Create a point pipeline from the module.
utils::ComboRenderPipelineDescriptor desc(device);
desc.vertexStage.module = module;
desc.vertexStage.entryPoint = "main";
desc.cFragmentStage.module = module;
desc.cFragmentStage.entryPoint = "main";
desc.cColorStates[0].format = wgpu::TextureFormat::RGBA8Unorm;
desc.primitiveTopology = wgpu::PrimitiveTopology::PointList;
wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&desc);
// Render the point and check that it was rendered.
utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, 1, 1);
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
{
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
pass.SetPipeline(pipeline);
pass.Draw(1);
pass.EndPass();
}
wgpu::CommandBuffer commands = encoder.Finish();
queue.Submit(1, &commands);
EXPECT_PIXEL_RGBA8_EQ(RGBA8::kRed, renderPass.color, 0, 0);
}
// Test creating two compute pipelines from the same module.
TEST_P(EntryPointTests, TwoComputeInModule) {
// TODO: Reenable once Tint is able to produce Vulkan 1.0 / 1.1 SPIR-V.