| // Copyright 2017 The Dawn Authors | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| #include "dawn_native/Pipeline.h" | |
| #include "dawn_native/BindGroupLayout.h" | |
| #include "dawn_native/Device.h" | |
| #include "dawn_native/ObjectBase.h" | |
| #include "dawn_native/ObjectContentHasher.h" | |
| #include "dawn_native/PipelineLayout.h" | |
| #include "dawn_native/ShaderModule.h" | |
| namespace dawn::native { | |
| MaybeError ValidateProgrammableStage(DeviceBase* device, | |
| const ShaderModuleBase* module, | |
| const std::string& entryPoint, | |
| uint32_t constantCount, | |
| const ConstantEntry* constants, | |
| const PipelineLayoutBase* layout, | |
| SingleShaderStage stage) { | |
| DAWN_TRY(device->ValidateObject(module)); | |
| DAWN_INVALID_IF(!module->HasEntryPoint(entryPoint), | |
| "Entry point \"%s\" doesn't exist in the shader module %s.", entryPoint, | |
| module); | |
| const EntryPointMetadata& metadata = module->GetEntryPoint(entryPoint); | |
| DAWN_INVALID_IF(metadata.stage != stage, | |
| "The stage (%s) of the entry point \"%s\" isn't the expected one (%s).", | |
| metadata.stage, entryPoint, stage); | |
| if (layout != nullptr) { | |
| DAWN_TRY(ValidateCompatibilityWithPipelineLayout(device, metadata, layout)); | |
| } | |
| if (constantCount > 0u && device->IsToggleEnabled(Toggle::DisallowUnsafeAPIs)) { | |
| return DAWN_VALIDATION_ERROR( | |
| "Pipeline overridable constants are disallowed because they are partially " | |
| "implemented."); | |
| } | |
| // Validate if overridable constants exist in shader module | |
| // pipelineBase is not yet constructed at this moment so iterate constants from descriptor | |
| size_t numUninitializedConstants = metadata.uninitializedOverridableConstants.size(); | |
| // Keep an initialized constants sets to handle duplicate initialization cases | |
| std::unordered_set<std::string> stageInitializedConstantIdentifiers; | |
| for (uint32_t i = 0; i < constantCount; i++) { | |
| DAWN_INVALID_IF(metadata.overridableConstants.count(constants[i].key) == 0, | |
| "Pipeline overridable constant \"%s\" not found in %s.", | |
| constants[i].key, module); | |
| if (stageInitializedConstantIdentifiers.count(constants[i].key) == 0) { | |
| if (metadata.uninitializedOverridableConstants.count(constants[i].key) > 0) { | |
| numUninitializedConstants--; | |
| } | |
| stageInitializedConstantIdentifiers.insert(constants[i].key); | |
| } else { | |
| // There are duplicate initializations | |
| return DAWN_FORMAT_VALIDATION_ERROR( | |
| "Pipeline overridable constants \"%s\" is set more than once in %s", | |
| constants[i].key, module); | |
| } | |
| } | |
| // Validate if any overridable constant is left uninitialized | |
| if (DAWN_UNLIKELY(numUninitializedConstants > 0)) { | |
| std::string uninitializedConstantsArray; | |
| bool isFirst = true; | |
| for (std::string identifier : metadata.uninitializedOverridableConstants) { | |
| if (stageInitializedConstantIdentifiers.count(identifier) > 0) { | |
| continue; | |
| } | |
| if (isFirst) { | |
| isFirst = false; | |
| } else { | |
| uninitializedConstantsArray.append(", "); | |
| } | |
| uninitializedConstantsArray.append(identifier); | |
| } | |
| return DAWN_FORMAT_VALIDATION_ERROR( | |
| "There are uninitialized pipeline overridable constants in shader module %s, their " | |
| "identifiers:[%s]", | |
| module, uninitializedConstantsArray); | |
| } | |
| return {}; | |
| } | |
| // PipelineBase | |
| PipelineBase::PipelineBase(DeviceBase* device, | |
| PipelineLayoutBase* layout, | |
| const char* label, | |
| std::vector<StageAndDescriptor> stages) | |
| : ApiObjectBase(device, label), mLayout(layout) { | |
| ASSERT(!stages.empty()); | |
| for (const StageAndDescriptor& stage : stages) { | |
| // Extract argument for this stage. | |
| SingleShaderStage shaderStage = stage.shaderStage; | |
| ShaderModuleBase* module = stage.module; | |
| const char* entryPointName = stage.entryPoint.c_str(); | |
| const EntryPointMetadata& metadata = module->GetEntryPoint(entryPointName); | |
| ASSERT(metadata.stage == shaderStage); | |
| // Record them internally. | |
| bool isFirstStage = mStageMask == wgpu::ShaderStage::None; | |
| mStageMask |= StageBit(shaderStage); | |
| mStages[shaderStage] = {module, entryPointName, &metadata, {}}; | |
| auto& constants = mStages[shaderStage].constants; | |
| for (uint32_t i = 0; i < stage.constantCount; i++) { | |
| constants.emplace(stage.constants[i].key, stage.constants[i].value); | |
| } | |
| // Compute the max() of all minBufferSizes across all stages. | |
| RequiredBufferSizes stageMinBufferSizes = | |
| ComputeRequiredBufferSizesForLayout(metadata, layout); | |
| if (isFirstStage) { | |
| mMinBufferSizes = std::move(stageMinBufferSizes); | |
| } else { | |
| for (BindGroupIndex group(0); group < mMinBufferSizes.size(); ++group) { | |
| ASSERT(stageMinBufferSizes[group].size() == mMinBufferSizes[group].size()); | |
| for (size_t i = 0; i < stageMinBufferSizes[group].size(); ++i) { | |
| mMinBufferSizes[group][i] = | |
| std::max(mMinBufferSizes[group][i], stageMinBufferSizes[group][i]); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| PipelineBase::PipelineBase(DeviceBase* device) : ApiObjectBase(device, kLabelNotImplemented) { | |
| } | |
| PipelineBase::PipelineBase(DeviceBase* device, ObjectBase::ErrorTag tag) | |
| : ApiObjectBase(device, tag) { | |
| } | |
| PipelineBase::~PipelineBase() = default; | |
| PipelineLayoutBase* PipelineBase::GetLayout() { | |
| ASSERT(!IsError()); | |
| return mLayout.Get(); | |
| } | |
| const PipelineLayoutBase* PipelineBase::GetLayout() const { | |
| ASSERT(!IsError()); | |
| return mLayout.Get(); | |
| } | |
| const RequiredBufferSizes& PipelineBase::GetMinBufferSizes() const { | |
| ASSERT(!IsError()); | |
| return mMinBufferSizes; | |
| } | |
| const ProgrammableStage& PipelineBase::GetStage(SingleShaderStage stage) const { | |
| ASSERT(!IsError()); | |
| return mStages[stage]; | |
| } | |
| const PerStage<ProgrammableStage>& PipelineBase::GetAllStages() const { | |
| return mStages; | |
| } | |
| wgpu::ShaderStage PipelineBase::GetStageMask() const { | |
| return mStageMask; | |
| } | |
| MaybeError PipelineBase::ValidateGetBindGroupLayout(uint32_t groupIndex) { | |
| DAWN_TRY(GetDevice()->ValidateIsAlive()); | |
| DAWN_TRY(GetDevice()->ValidateObject(this)); | |
| DAWN_TRY(GetDevice()->ValidateObject(mLayout.Get())); | |
| DAWN_INVALID_IF( | |
| groupIndex >= kMaxBindGroups, | |
| "Bind group layout index (%u) exceeds the maximum number of bind groups (%u).", | |
| groupIndex, kMaxBindGroups); | |
| return {}; | |
| } | |
| ResultOrError<Ref<BindGroupLayoutBase>> PipelineBase::GetBindGroupLayout( | |
| uint32_t groupIndexIn) { | |
| DAWN_TRY(ValidateGetBindGroupLayout(groupIndexIn)); | |
| BindGroupIndex groupIndex(groupIndexIn); | |
| if (!mLayout->GetBindGroupLayoutsMask()[groupIndex]) { | |
| return Ref<BindGroupLayoutBase>(GetDevice()->GetEmptyBindGroupLayout()); | |
| } else { | |
| return Ref<BindGroupLayoutBase>(mLayout->GetBindGroupLayout(groupIndex)); | |
| } | |
| } | |
| BindGroupLayoutBase* PipelineBase::APIGetBindGroupLayout(uint32_t groupIndexIn) { | |
| Ref<BindGroupLayoutBase> result; | |
| if (GetDevice()->ConsumedError(GetBindGroupLayout(groupIndexIn), &result, | |
| "Validating GetBindGroupLayout (%u) on %s", groupIndexIn, | |
| this)) { | |
| return BindGroupLayoutBase::MakeError(GetDevice()); | |
| } | |
| return result.Detach(); | |
| } | |
| size_t PipelineBase::ComputeContentHash() { | |
| ObjectContentHasher recorder; | |
| recorder.Record(mLayout->GetContentHash()); | |
| recorder.Record(mStageMask); | |
| for (SingleShaderStage stage : IterateStages(mStageMask)) { | |
| recorder.Record(mStages[stage].module->GetContentHash()); | |
| recorder.Record(mStages[stage].entryPoint); | |
| } | |
| return recorder.GetContentHash(); | |
| } | |
| // static | |
| bool PipelineBase::EqualForCache(const PipelineBase* a, const PipelineBase* b) { | |
| // The layout is deduplicated so it can be compared by pointer. | |
| if (a->mLayout.Get() != b->mLayout.Get() || a->mStageMask != b->mStageMask) { | |
| return false; | |
| } | |
| for (SingleShaderStage stage : IterateStages(a->mStageMask)) { | |
| // The module is deduplicated so it can be compared by pointer. | |
| if (a->mStages[stage].module.Get() != b->mStages[stage].module.Get() || | |
| a->mStages[stage].entryPoint != b->mStages[stage].entryPoint) { | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| } // namespace dawn::native |