| // Copyright 2017 The Dawn & Tint Authors | |
| // | |
| // Redistribution and use in source and binary forms, with or without | |
| // modification, are permitted provided that the following conditions are met: | |
| // | |
| // 1. Redistributions of source code must retain the above copyright notice, this | |
| // list of conditions and the following disclaimer. | |
| // | |
| // 2. Redistributions in binary form must reproduce the above copyright notice, | |
| // this list of conditions and the following disclaimer in the documentation | |
| // and/or other materials provided with the distribution. | |
| // | |
| // 3. Neither the name of the copyright holder nor the names of its | |
| // contributors may be used to endorse or promote products derived from | |
| // this software without specific prior written permission. | |
| // | |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
| // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
| // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | |
| // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
| // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |
| // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | |
| // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |
| // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| #ifndef SRC_DAWN_NATIVE_SHADERMODULE_H_ | |
| #define SRC_DAWN_NATIVE_SHADERMODULE_H_ | |
| #include <bitset> | |
| #include <limits> | |
| #include <map> | |
| #include <memory> | |
| #include <string> | |
| #include <utility> | |
| #include <variant> | |
| #include <vector> | |
| #include "absl/container/flat_hash_map.h" | |
| #include "absl/container/flat_hash_set.h" | |
| #include "absl/strings/string_view.h" | |
| #include "dawn/common/Constants.h" | |
| #include "dawn/common/ContentLessObjectCacheable.h" | |
| #include "dawn/common/MutexProtected.h" | |
| #include "dawn/common/RefCountedWithExternalCount.h" | |
| #include "dawn/common/Sha3.h" | |
| #include "dawn/common/ityp_array.h" | |
| #include "dawn/native/BindingInfo.h" | |
| #include "dawn/native/CachedObject.h" | |
| #include "dawn/native/CompilationMessages.h" | |
| #include "dawn/native/Error.h" | |
| #include "dawn/native/Format.h" | |
| #include "dawn/native/Forward.h" | |
| #include "dawn/native/IntegerTypes.h" | |
| #include "dawn/native/Limits.h" | |
| #include "dawn/native/ObjectBase.h" | |
| #include "dawn/native/PerStage.h" | |
| #include "dawn/native/Serializable.h" | |
| #include "dawn/native/dawn_platform.h" | |
| #include "tint/tint.h" | |
| namespace tint { | |
| class Program; | |
| } // namespace tint | |
| namespace dawn::native { | |
| struct EntryPointMetadata; | |
| // Base component type of an inter-stage variable | |
| enum class InterStageComponentType { | |
| I32, | |
| U32, | |
| F32, | |
| F16, | |
| }; | |
| enum class InterpolationType { | |
| Perspective, | |
| Linear, | |
| Flat, | |
| }; | |
| enum class InterpolationSampling { | |
| None, | |
| Center, | |
| Centroid, | |
| Sample, | |
| First, | |
| Either, | |
| }; | |
| enum class PixelLocalMemberType { | |
| I32, | |
| U32, | |
| F32, | |
| }; | |
| // Use map to make sure constant keys are sorted for creating shader cache keys | |
| using PipelineConstantEntries = std::map<std::string, double>; | |
| // A map from name to EntryPointMetadata. | |
| using EntryPointMetadataTable = | |
| absl::flat_hash_map<std::string, std::unique_ptr<EntryPointMetadata>>; | |
| struct TintProgram : public RefCounted { | |
| TintProgram(tint::Program program, std::unique_ptr<tint::Source::File> file) | |
| : program(std::move(program)), file(std::move(file)) {} | |
| const tint::Program program; | |
| const std::unique_ptr<tint::Source::File> file; // Keep the tint::Source::File alive | |
| }; | |
| struct ShaderModuleParseResult { | |
| ShaderModuleParseResult(); | |
| ~ShaderModuleParseResult(); | |
| ShaderModuleParseResult(ShaderModuleParseResult&& rhs); | |
| ShaderModuleParseResult& operator=(ShaderModuleParseResult&& rhs); | |
| bool HasParsedShader() const; | |
| Ref<TintProgram> tintProgram; | |
| }; | |
| struct ShaderModuleEntryPoint { | |
| bool defaulted; | |
| std::string name; | |
| }; | |
| // Parse a shader module from a validated ShaderModuleDescriptor. Errors are generated only if the | |
| // shader code itself is invalid. | |
| MaybeError ParseShaderModule(DeviceBase* device, | |
| const UnpackedPtr<ShaderModuleDescriptor>& descriptor, | |
| const std::vector<tint::wgsl::Extension>& internalExtensions, | |
| ShaderModuleParseResult* parseResult, | |
| OwnedCompilationMessages* outMessages); | |
| MaybeError ValidateCompatibilityWithPipelineLayout(DeviceBase* device, | |
| const EntryPointMetadata& entryPoint, | |
| const PipelineLayoutBase* layout); | |
| // Return extent3D with workgroup size dimension info if it is valid. | |
| // width = x, height = y, depthOrArrayLength = z. | |
| ResultOrError<Extent3D> ValidateComputeStageWorkgroupSize( | |
| const tint::Program& program, | |
| const char* entryPointName, | |
| bool usesSubgroupMatrix, | |
| uint32_t maxSubgroupSize, | |
| const LimitsForCompilationRequest& limits, | |
| const LimitsForCompilationRequest& adaterSupportedlimits); | |
| // Return extent3D with workgroup size dimension info if it is valid. | |
| // width = x, height = y, depthOrArrayLength = z. | |
| ResultOrError<Extent3D> ValidateComputeStageWorkgroupSize( | |
| uint32_t x, | |
| uint32_t y, | |
| uint32_t z, | |
| size_t workgroupStorageSize, | |
| bool usesSubgroupMatrix, | |
| uint32_t maxSubgroupSize, | |
| const LimitsForCompilationRequest& limits, | |
| const LimitsForCompilationRequest& adaterSupportedlimits); | |
| RequiredBufferSizes ComputeRequiredBufferSizesForLayout(const EntryPointMetadata& entryPoint, | |
| const PipelineLayoutBase* layout); | |
| // Shader metadata for a binding, very similar to information contained in a pipeline layout. | |
| using ShaderBindingInfoVariant = std::variant<BufferBindingInfo, | |
| SamplerBindingInfo, | |
| TextureBindingInfo, | |
| StorageTextureBindingInfo, | |
| ExternalTextureBindingInfo, | |
| InputAttachmentBindingInfo>; | |
| #define SHADER_BINDING_INFO_MEMBER(X) \ | |
| X(BindingNumber, binding) \ | |
| X(BindingIndex, arraySize) \ | |
| /*The variable name of the binding resource.*/ \ | |
| X(std::string, name) \ | |
| X(ShaderBindingInfoVariant, bindingInfo) | |
| DAWN_SERIALIZABLE(struct, ShaderBindingInfo, SHADER_BINDING_INFO_MEMBER){}; | |
| #undef SHADER_BINDING_INFO_MEMBER | |
| using BindingGroupInfoMap = absl::flat_hash_map<BindingNumber, ShaderBindingInfo>; | |
| using BindingInfoArray = ityp::array<BindGroupIndex, BindingGroupInfoMap, kMaxBindGroups>; | |
| // Define types for the shader reflection data structures in detail namespaces to prevent messing | |
| // up dawn::native namespace. These types can be exposed within EntryPointMetadata if needed. | |
| namespace detail { | |
| #define SAMPLER_TEXTURE_PAIR_MEMBER(X) \ | |
| X(BindingSlot, sampler) \ | |
| X(BindingSlot, texture) | |
| DAWN_SERIALIZABLE(struct, SamplerTexturePair, SAMPLER_TEXTURE_PAIR_MEMBER){}; | |
| #undef SAMPLER_TEXTURE_PAIR_MEMBER | |
| /// Match tint::inspector::Inspector::LevelSampleInfo | |
| enum class TextureQueryType : uint8_t { TextureNumLevels, TextureNumSamples }; | |
| #define TEXTURE_METADATE_QUERY_MEMBER(X) \ | |
| X(TextureQueryType, type) \ | |
| X(uint32_t, group) \ | |
| X(uint32_t, binding) | |
| DAWN_SERIALIZABLE(struct, TextureMetadataQuery, TEXTURE_METADATE_QUERY_MEMBER) { | |
| using TextureQueryType = TextureQueryType; | |
| }; | |
| #undef TEXTURE_METADATE_QUERY_MEMBER | |
| // Structure to record the basic types (float, int and uint) of the fragment shader framebuffer | |
| // input/outputs (inputs being "framebuffer fetch"). | |
| #define FRAGMENT_RENDER_ATTACHMENT_INFO_MEMBER(X) \ | |
| X(TextureComponentType, baseType) \ | |
| X(uint8_t, componentCount) \ | |
| X(uint8_t, blendSrc) | |
| DAWN_SERIALIZABLE(struct, FragmentRenderAttachmentInfo, FRAGMENT_RENDER_ATTACHMENT_INFO_MEMBER){}; | |
| #undef FRAGMENT_RENDER_ATTACHMENT_INFO_MEMBER | |
| #define INTER_STAGE_VARIABLE_INFO_MEMBER(X) \ | |
| X(std::string, name) \ | |
| X(InterStageComponentType, baseType) \ | |
| X(uint32_t, componentCount) \ | |
| X(InterpolationType, interpolationType) \ | |
| X(InterpolationSampling, interpolationSampling) | |
| DAWN_SERIALIZABLE(struct, InterStageVariableInfo, INTER_STAGE_VARIABLE_INFO_MEMBER){}; | |
| #undef INTER_STAGE_VARIABLE_INFO_MEMBER | |
| // Match tint::OverrideId | |
| #define OVERRIDE_ID_MEMBER(X) X(uint16_t, value) | |
| DAWN_SERIALIZABLE(struct, OverrideId, OVERRIDE_ID_MEMBER){}; | |
| #undef OVERRIDE_ID_MEMBER | |
| enum class OverrideType { Boolean, Float32, Uint32, Int32, Float16 }; | |
| #define OVERRIDE_MEMBER(X) \ | |
| X(OverrideId, id) \ | |
| X(OverrideType, type) \ | |
| X(bool, isInitialized) \ | |
| X(bool, isUsed) | |
| DAWN_SERIALIZABLE(struct, Override, OVERRIDE_MEMBER) { | |
| using Type = OverrideType; | |
| }; | |
| #undef OVERRIDE_MEMBER | |
| using OverridesMap = absl::flat_hash_map<std::string, Override>; | |
| } // namespace detail | |
| // Contains all the reflection data for a valid (ShaderModule, entryPoint, stage). This structure is | |
| // serializable and doesn't depend on the shader program, thus it can outlive the shader program and | |
| // get cached on disk. They are stored in the ShaderModuleBase so pointers to EntryPointMetadata are | |
| // safe to store as long as you also keep a Ref to the ShaderModuleBase. | |
| #define ENTRY_POINT_METADATA_MEMBER(X) \ | |
| /* It is valid for a shader to contain entry points that go over limits. To keep this */ \ | |
| /* structure with packed arrays and bitsets, we still validate against limits when doing */ \ | |
| /* reflection, but store the errors in this vector, for later use if the application tries */ \ | |
| /* to use the entry point. */ \ | |
| X(std::vector<std::string>, infringedLimitErrors) \ | |
| /* bindings[G][B] is the reflection data for the binding defined with @group(G) @binding(B)*/ \ | |
| X(BindingInfoArray, bindings) \ | |
| /* Contains the reflection information of all sampler and non-sampler texture (storage */ \ | |
| /* texture not included) usage in the entry point. For non-sampler usage, */ \ | |
| /* nonSamplerBindingPoint is used for sampler slot. */ \ | |
| X(std::vector<detail::SamplerTexturePair>, samplerAndNonSamplerTexturePairs) \ | |
| X(std::vector<detail::TextureMetadataQuery>, textureQueries) \ | |
| /* The set of vertex attributes this entryPoint uses.*/ \ | |
| X(PerVertexAttribute<VertexFormatBaseType>, vertexInputBaseTypes) \ | |
| X(VertexAttributeMask, usedVertexInputs) \ | |
| /* An array to record the basic types of the fragment shader framebuffer input/outputs.*/ \ | |
| X(PerColorAttachment<detail::FragmentRenderAttachmentInfo>, fragmentOutputVariables) \ | |
| X(ColorAttachmentMask, fragmentOutputMask) \ | |
| X(PerColorAttachment<detail::FragmentRenderAttachmentInfo>, fragmentInputVariables) \ | |
| X(ColorAttachmentMask, fragmentInputMask) \ | |
| /* Now that we only support vertex and fragment stages, there can't be both inter-stage */ \ | |
| /* inputs and outputs in one shader stage. */ \ | |
| X(std::vector<bool>, usedInterStageVariables) \ | |
| X(std::vector<detail::InterStageVariableInfo>, interStageVariables) \ | |
| X(uint32_t, totalInterStageShaderVariables) \ | |
| /* The shader stage for this entry point.*/ \ | |
| X(SingleShaderStage, stage) \ | |
| /* Map identifier to override variable. */ \ | |
| /* Identifier is unique: either the variable name or the numeric ID if specified */ \ | |
| X(detail::OverridesMap, overrides) \ | |
| /* Override variables that are not initialized in shaders. They need value initialization */ \ | |
| /* from pipeline stage or it is a validation error */ \ | |
| X(absl::flat_hash_set<std::string>, uninitializedOverrides) \ | |
| /* Store constants with shader initialized values as well. */ \ | |
| /* This is used by metal backend to set values with default initializers that are not */ \ | |
| /* overridden. */ \ | |
| X(absl::flat_hash_set<std::string>, initializedOverrides) \ | |
| /* Reflection information about potential `pixel_local` variable use. */ \ | |
| X(bool, usesPixelLocal) \ | |
| X(size_t, pixelLocalBlockSize) \ | |
| X(std::vector<PixelLocalMemberType>, pixelLocalMembers) \ | |
| X(bool, usesFragDepth) \ | |
| X(bool, usesInstanceIndex) \ | |
| X(bool, usesNumWorkgroups) \ | |
| X(bool, usesSampleMaskOutput) \ | |
| X(bool, usesSampleIndex) \ | |
| X(bool, usesVertexIndex) \ | |
| X(bool, usesTextureLoadWithDepthTexture) \ | |
| X(bool, usesDepthTextureWithNonComparisonSampler) \ | |
| X(bool, usesSubgroupMatrix) \ | |
| /* Immediate Data block byte size */ \ | |
| X(uint32_t, immediateDataRangeByteSize) \ | |
| /* Number of texture+sampler combinations, computed as 1 for every texture+sampler */ \ | |
| /* combination + 1 for every texture used without a sampler that wasn't previously counted.*/ \ | |
| /* Note: this is only set in compatibility mode. */ \ | |
| X(uint32_t, numTextureSamplerCombinations) | |
| DAWN_SERIALIZABLE(struct, EntryPointMetadata, ENTRY_POINT_METADATA_MEMBER) { | |
| using SamplerTexturePair = detail::SamplerTexturePair; | |
| // TODO(crbug.com/409438000): Remove the hack of sampler placeholders for non-sampler texture. | |
| static constexpr const BindingSlot nonSamplerBindingPoint{ | |
| {BindGroupIndex{std::numeric_limits<uint32_t>::max()}, | |
| BindingNumber{std::numeric_limits<uint32_t>::max()}}}; | |
| using TextureMetadataQuery = detail::TextureMetadataQuery; | |
| using FragmentRenderAttachmentInfo = detail::FragmentRenderAttachmentInfo; | |
| using InterStageVariableInfo = detail::InterStageVariableInfo; | |
| using OverrideId = detail::OverrideId; | |
| using Override = detail::Override; | |
| }; | |
| #undef ENTRY_POINT_METADATA_MEMBER | |
| // The WebGPU override variables only support these scalar types | |
| union OverrideScalar { | |
| // Use int32_t for boolean to initialize the full 32bit | |
| int32_t b; | |
| float f32; | |
| int32_t i32; | |
| uint32_t u32; | |
| }; | |
| class ShaderModuleBase : public RefCountedWithExternalCount<ApiObjectBase>, | |
| public CachedObject, | |
| public ContentLessObjectCacheable<ShaderModuleBase> { | |
| public: | |
| using Base = RefCountedWithExternalCount<ApiObjectBase>; | |
| ShaderModuleBase(DeviceBase* device, | |
| const UnpackedPtr<ShaderModuleDescriptor>& descriptor, | |
| std::vector<tint::wgsl::Extension> internalExtensions, | |
| ApiObjectBase::UntrackedByDeviceTag tag); | |
| ShaderModuleBase(DeviceBase* device, | |
| const UnpackedPtr<ShaderModuleDescriptor>& descriptor, | |
| std::vector<tint::wgsl::Extension> internalExtensions); | |
| ~ShaderModuleBase() override; | |
| static Ref<ShaderModuleBase> MakeError( | |
| DeviceBase* device, | |
| StringView label, | |
| std::unique_ptr<OwnedCompilationMessages> compilationMessages); | |
| ObjectType GetType() const override; | |
| // Return true iff the program has an entrypoint called `entryPoint`. | |
| bool HasEntryPoint(absl::string_view entryPoint) const; | |
| // Return the number of entry points for a stage. | |
| size_t GetEntryPointCount(SingleShaderStage stage) const { return mEntryPointCounts[stage]; } | |
| // Return the entry point for a stage. If no entry point name, returns the default one. | |
| ShaderModuleEntryPoint ReifyEntryPointName(StringView entryPointName, | |
| SingleShaderStage stage) const; | |
| // Return the metadata for the given `entryPoint`. HasEntryPoint with the same argument | |
| // must be true. | |
| const EntryPointMetadata& GetEntryPoint(absl::string_view entryPoint) const; | |
| // Functions necessary for the unordered_set<ShaderModuleBase*>-based cache. | |
| size_t ComputeContentHash() override; | |
| struct EqualityFunc { | |
| bool operator()(const ShaderModuleBase* a, const ShaderModuleBase* b) const; | |
| }; | |
| std::optional<bool> GetStrictMath() const; | |
| using ShaderModuleHasher = Sha3_512; | |
| using ShaderModuleHash = ShaderModuleHasher::Output; | |
| const ShaderModuleHash& GetHash() const; | |
| using ScopedUseTintProgram = APIRef<ShaderModuleBase>; | |
| ScopedUseTintProgram UseTintProgram(); | |
| // Get tintProgram, (re)create it if necessary. | |
| Ref<TintProgram> GetTintProgram(); | |
| Future APIGetCompilationInfo(const WGPUCompilationInfoCallbackInfo& callbackInfo); | |
| OwnedCompilationMessages* GetCompilationMessages() const; | |
| std::string GetCompilationLog() const; | |
| // Return nullable tintProgram directly without any recreation, can be used for testing the | |
| // releasing/recreation behaviors. | |
| Ref<TintProgram> GetNullableTintProgramForTesting() const; | |
| int GetTintProgramRecreateCountForTesting() const; | |
| protected: | |
| void DestroyImpl() override; | |
| MaybeError InitializeBase(ShaderModuleParseResult* parseResult, | |
| std::unique_ptr<OwnedCompilationMessages>* compilationMessages); | |
| private: | |
| ShaderModuleBase(DeviceBase* device, | |
| ObjectBase::ErrorTag tag, | |
| StringView label, | |
| std::unique_ptr<OwnedCompilationMessages> compilationMessages); | |
| void WillDropLastExternalRef() override; | |
| // The original data in the descriptor for caching. | |
| enum class Type { Undefined, Spirv, Wgsl }; | |
| Type mType; | |
| std::vector<uint32_t> mOriginalSpirv; | |
| std::string mWgsl; | |
| // Secure hash computed from shader code and other metadata to be used as a cache key | |
| // representing the shader module. | |
| ShaderModuleHash mHash; | |
| // TODO(dawn:2503): Remove the optional when Dawn can has a consistent default across backends. | |
| // Right now D3D uses strictness by default, and Vulkan/Metal use fast math by default. | |
| std::optional<bool> mStrictMath; | |
| EntryPointMetadataTable mEntryPoints; | |
| PerStage<std::string> mDefaultEntryPointNames; | |
| PerStage<size_t> mEntryPointCounts; | |
| struct TintData { | |
| // tintProgram is nullable so that it can be lazily (re)generated right before actual using. | |
| Ref<TintProgram> tintProgram = nullptr; | |
| int tintProgramRecreateCount = 0; | |
| }; | |
| MutexProtected<TintData> mTintData; | |
| std::unique_ptr<OwnedCompilationMessages> mCompilationMessages; | |
| const std::vector<tint::wgsl::Extension> mInternalExtensions; | |
| }; | |
| } // namespace dawn::native | |
| #endif // SRC_DAWN_NATIVE_SHADERMODULE_H_ |