| // 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. |
| |
| #include "dawn/native/opengl/ShaderModuleGL.h" |
| |
| #include <sstream> |
| #include <unordered_map> |
| #include <utility> |
| |
| #include "absl/container/flat_hash_map.h" |
| #include "dawn/common/MatchVariant.h" |
| #include "dawn/native/Adapter.h" |
| #include "dawn/native/BindGroupLayoutInternal.h" |
| #include "dawn/native/CacheRequest.h" |
| #include "dawn/native/Pipeline.h" |
| #include "dawn/native/TintUtils.h" |
| #include "dawn/native/opengl/BindGroupLayoutGL.h" |
| #include "dawn/native/opengl/BindingPoint.h" |
| #include "dawn/native/opengl/DeviceGL.h" |
| #include "dawn/native/opengl/PipelineLayoutGL.h" |
| #include "dawn/native/opengl/UtilsGL.h" |
| #include "dawn/platform/DawnPlatform.h" |
| #include "dawn/platform/tracing/TraceEvent.h" |
| |
| #include "src/tint/api/common/binding_point.h" |
| |
| namespace dawn::native { |
| namespace { |
| |
| GLenum GLShaderType(SingleShaderStage stage) { |
| switch (stage) { |
| case SingleShaderStage::Vertex: |
| return GL_VERTEX_SHADER; |
| case SingleShaderStage::Fragment: |
| return GL_FRAGMENT_SHADER; |
| case SingleShaderStage::Compute: |
| return GL_COMPUTE_SHADER; |
| } |
| DAWN_UNREACHABLE(); |
| } |
| |
| tint::glsl::writer::Version::Standard ToTintGLStandard(opengl::OpenGLVersion::Standard standard) { |
| switch (standard) { |
| case opengl::OpenGLVersion::Standard::Desktop: |
| return tint::glsl::writer::Version::Standard::kDesktop; |
| case opengl::OpenGLVersion::Standard::ES: |
| return tint::glsl::writer::Version::Standard::kES; |
| } |
| DAWN_UNREACHABLE(); |
| } |
| |
| using BindingMap = absl::flat_hash_map<tint::BindingPoint, tint::BindingPoint>; |
| |
| opengl::CombinedSampler* AppendCombinedSampler(opengl::CombinedSamplerInfo* info, |
| tint::BindingPoint texture, |
| tint::BindingPoint sampler, |
| tint::BindingPoint placeholderBindingPoint) { |
| info->emplace_back(); |
| opengl::CombinedSampler* combinedSampler = &info->back(); |
| combinedSampler->usePlaceholderSampler = sampler == placeholderBindingPoint; |
| combinedSampler->samplerLocation.group = BindGroupIndex(sampler.group); |
| combinedSampler->samplerLocation.binding = BindingNumber(sampler.binding); |
| combinedSampler->textureLocation.group = BindGroupIndex(texture.group); |
| combinedSampler->textureLocation.binding = BindingNumber(texture.binding); |
| return combinedSampler; |
| } |
| |
| using InterstageLocationAndName = std::pair<uint32_t, std::string>; |
| using SubstituteOverrideConfig = std::unordered_map<tint::OverrideId, double>; |
| |
| #define GLSL_COMPILATION_REQUEST_MEMBERS(X) \ |
| X(ShaderModuleBase::ShaderModuleHash, shaderModuleHash) \ |
| X(CacheKey::UnsafeUnkeyedValue<ShaderModuleBase::ScopedUseTintProgram>, inputProgram) \ |
| X(std::string, entryPointName) \ |
| X(SingleShaderStage, stage) \ |
| X(SubstituteOverrideConfig, substituteOverrideConfig) \ |
| X(LimitsForCompilationRequest, limits) \ |
| X(CacheKey::UnsafeUnkeyedValue<LimitsForCompilationRequest>, adapterSupportedLimits) \ |
| X(bool, disableSymbolRenaming) \ |
| X(std::vector<InterstageLocationAndName>, interstageVariables) \ |
| X(tint::glsl::writer::Options, tintOptions) \ |
| X(CacheKey::UnsafeUnkeyedValue<dawn::platform::Platform*>, platform) |
| |
| DAWN_MAKE_CACHE_REQUEST(GLSLCompilationRequest, GLSL_COMPILATION_REQUEST_MEMBERS); |
| #undef GLSL_COMPILATION_REQUEST_MEMBERS |
| |
| #define GLSL_COMPILATION_MEMBERS(X) X(std::string, glsl) |
| |
| // clang-format off |
| DAWN_SERIALIZABLE(struct, GLSLCompilation, GLSL_COMPILATION_MEMBERS) { |
| static ResultOrError<GLSLCompilation> FromValidatedBlob(Blob blob) { |
| GLSLCompilation result; |
| DAWN_TRY_ASSIGN(result, FromBlob(std::move(blob))); |
| DAWN_INVALID_IF(result.glsl.empty(), "Cached GLSLCompilation result has no GLSL"); |
| return result; |
| } |
| }; |
| // clang-format on |
| #undef GLSL_COMPILATION_MEMBERS |
| |
| } // namespace |
| } // namespace dawn::native |
| |
| namespace dawn::native::opengl { |
| namespace { |
| |
| // Find all the sampler/texture pairs for this entry point, and create CombinedSamplers for them. |
| // CombinedSampler records the binding points of the original texture and sampler, and generates a |
| // unique name. The corresponding uniforms will be retrieved by these generated names in PipelineGL. |
| // Any texture-only references will have "usePlaceholderSampler" set to true, and only the texture |
| // binding point will be used in naming them. In addition, Dawn will bind a non-filtering sampler |
| // for them (see PipelineGL). |
| CombinedSamplerInfo GenerateCombinedSamplerInfo(const EntryPointMetadata& metadata, |
| tint::glsl::writer::Bindings& bindings, |
| BindingMap externalTextureExpansionMap, |
| bool* needsPlaceholderSampler |
| |
| ) { |
| auto samplerAndNonSamplerTextureUses = metadata.samplerAndNonSamplerTexturePairs; |
| |
| CombinedSamplerInfo combinedSamplerInfo; |
| for (const auto& use : samplerAndNonSamplerTextureUses) { |
| // Convert BindingSlot in metadata back to tint::BindingPoint |
| tint::BindingPoint samplerBindPoint = |
| use.sampler == EntryPointMetadata::nonSamplerBindingPoint |
| ? bindings.placeholder_sampler_bind_point |
| : ToTint(use.sampler); |
| tint::BindingPoint texBindPoint = ToTint(use.texture); |
| |
| CombinedSampler* info = |
| AppendCombinedSampler(&combinedSamplerInfo, ToTint(use.texture), |
| use.sampler == EntryPointMetadata::nonSamplerBindingPoint |
| ? bindings.placeholder_sampler_bind_point |
| : ToTint(use.sampler), |
| bindings.placeholder_sampler_bind_point); |
| |
| if (info->usePlaceholderSampler) { |
| *needsPlaceholderSampler = true; |
| } |
| |
| // Note, the rest of Dawn is expecting to use the un-modified WGSL binding points when |
| // looking up information on the combined samplers. Tint is expecting Dawn to provide |
| // the final expected values for those entry points. So, we end up using the original |
| // values for the AppendCombinedSampler calls and the remapped binding points when we |
| // put things in the tint bindings structure. |
| |
| { |
| auto texIt = bindings.texture.find(texBindPoint); |
| if (texIt != bindings.texture.end()) { |
| texBindPoint.group = 0; |
| texBindPoint.binding = texIt->second.binding; |
| } else { |
| // The plane0 texture will be in external_textures, not textures, so we have to set |
| // the `sampler_texture_to_name` based on the external_texture value. |
| auto exIt = bindings.external_texture.find(texBindPoint); |
| if (exIt != bindings.external_texture.end()) { |
| texBindPoint.group = 0; |
| texBindPoint.binding = exIt->second.plane0.binding; |
| } |
| } |
| } |
| { |
| auto it = bindings.sampler.find(samplerBindPoint); |
| if (it != bindings.sampler.end()) { |
| samplerBindPoint.group = 0; |
| samplerBindPoint.binding = it->second.binding; |
| } |
| } |
| |
| bindings.sampler_texture_to_name.emplace( |
| tint::glsl::writer::binding::CombinedTextureSamplerPair{texBindPoint, samplerBindPoint, |
| false}, |
| info->GetName()); |
| |
| // If the texture has an associated plane1 texture (ie., it's an external texture), |
| // append a new combined sampler with the same sampler and the plane1 texture. |
| auto it = externalTextureExpansionMap.find(ToTint(use.texture)); |
| if (it != externalTextureExpansionMap.end()) { |
| CombinedSampler* plane1Info = |
| AppendCombinedSampler(&combinedSamplerInfo, it->second, |
| use.sampler == EntryPointMetadata::nonSamplerBindingPoint |
| ? bindings.placeholder_sampler_bind_point |
| : ToTint(use.sampler), |
| bindings.placeholder_sampler_bind_point); |
| |
| tint::BindingPoint plane1TexBindPoint = it->second; |
| auto dstIt = bindings.external_texture.find(ToTint(use.texture)); |
| if (dstIt != bindings.external_texture.end()) { |
| plane1TexBindPoint.group = 0; |
| plane1TexBindPoint.binding = dstIt->second.plane1.binding; |
| } |
| |
| bindings.sampler_texture_to_name.emplace( |
| tint::glsl::writer::binding::CombinedTextureSamplerPair{plane1TexBindPoint, |
| samplerBindPoint, true}, |
| plane1Info->GetName()); |
| } |
| } |
| return combinedSamplerInfo; |
| } |
| |
| bool GenerateTextureBuiltinFromUniformData(const EntryPointMetadata& metadata, |
| const PipelineLayout* layout, |
| BindingPointToFunctionAndOffset* bindingPointToData, |
| tint::glsl::writer::Bindings& bindings) { |
| auto textureBuiltinsFromUniformData = metadata.textureQueries; |
| |
| if (textureBuiltinsFromUniformData.empty()) { |
| return false; |
| } |
| |
| for (size_t i = 0; i < textureBuiltinsFromUniformData.size(); ++i) { |
| const auto& info = textureBuiltinsFromUniformData[i]; |
| |
| // This is the unmodified binding point from the WGSL shader. |
| tint::BindingPoint srcBindingPoint{info.group, info.binding}; |
| bindings.texture_builtins_from_uniform.ubo_bindingpoint_ordering.emplace_back( |
| srcBindingPoint); |
| |
| // The remapped binding point is inserted into the Dawn data structure. |
| const BindGroupLayoutInternalBase* bgl = |
| layout->GetBindGroupLayout(BindGroupIndex{info.group}); |
| tint::BindingPoint dstBindingPoint = tint::BindingPoint{ |
| info.group, static_cast<uint32_t>(bgl->GetBindingIndex(BindingNumber{info.binding}))}; |
| |
| BindPointFunction type = BindPointFunction::kTextureNumLevels; |
| switch (info.type) { |
| case EntryPointMetadata::TextureMetadataQuery::TextureQueryType::TextureNumLevels: |
| type = BindPointFunction::kTextureNumLevels; |
| break; |
| case EntryPointMetadata::TextureMetadataQuery::TextureQueryType::TextureNumSamples: |
| type = BindPointFunction::kTextureNumSamples; |
| break; |
| } |
| |
| // Note, the `sizeof(uint32_t)` has to match up with the data type created by the |
| // `TextureBuiltinsFromUniform` when it creates the UBO structure. |
| bindingPointToData->emplace(dstBindingPoint, |
| std::pair{type, static_cast<uint32_t>(i * sizeof(uint32_t))}); |
| } |
| |
| return true; |
| } |
| |
| bool GenerateArrayLengthFromuniformData(const BindingInfoArray& moduleBindingInfo, |
| const PipelineLayout* layout, |
| tint::glsl::writer::Bindings& bindings) { |
| const PipelineLayout::BindingIndexInfo& indexInfo = layout->GetBindingIndexInfo(); |
| |
| for (BindGroupIndex group : layout->GetBindGroupLayoutsMask()) { |
| const BindGroupLayoutInternalBase* bgl = layout->GetBindGroupLayout(group); |
| for (const auto& [binding, shaderBindingInfo] : moduleBindingInfo[group]) { |
| BindingIndex bindingIndex = bgl->GetBindingIndex(binding); |
| const BindingInfo& bindingInfo = bgl->GetBindingInfo(bindingIndex); |
| |
| // TODO(crbug.com/408010433): capturing binding directly in lambda is C++20 |
| // extension in cmake |
| uint32_t capturedBindingNumber = static_cast<uint32_t>(binding); |
| |
| MatchVariant( |
| bindingInfo.bindingLayout, |
| [&](const BufferBindingInfo& bufferBinding) { |
| switch (bufferBinding.type) { |
| case wgpu::BufferBindingType::Storage: |
| case kInternalStorageBufferBinding: |
| case wgpu::BufferBindingType::ReadOnlyStorage: |
| case kInternalReadOnlyStorageBufferBinding: { |
| // Use ssbo index as the indices for the buffer size lookups |
| // in the array length from uniform transform. |
| tint::BindingPoint srcBindingPoint = {static_cast<uint32_t>(group), |
| capturedBindingNumber}; |
| uint32_t ssboIndex = indexInfo[group][bindingIndex]; |
| bindings.array_length_from_uniform.bindpoint_to_size_index.emplace( |
| srcBindingPoint, ssboIndex); |
| break; |
| } |
| default: |
| break; |
| } |
| }, |
| [](const StaticSamplerBindingInfo&) {}, [](const SamplerBindingInfo&) {}, |
| [](const TextureBindingInfo&) {}, [](const StorageTextureBindingInfo&) {}, |
| [](const InputAttachmentBindingInfo&) {}); |
| } |
| } |
| |
| return bindings.array_length_from_uniform.bindpoint_to_size_index.size() > 0; |
| } |
| |
| } // namespace |
| |
| std::string GetBindingName(BindGroupIndex group, BindingNumber bindingNumber) { |
| std::ostringstream o; |
| o << "dawn_binding_" << static_cast<uint32_t>(group) << "_" |
| << static_cast<uint32_t>(bindingNumber); |
| return o.str(); |
| } |
| |
| bool operator<(const BindingLocation& a, const BindingLocation& b) { |
| return std::tie(a.group, a.binding) < std::tie(b.group, b.binding); |
| } |
| |
| bool operator<(const CombinedSampler& a, const CombinedSampler& b) { |
| return std::tie(a.usePlaceholderSampler, a.samplerLocation, a.textureLocation) < |
| std::tie(b.usePlaceholderSampler, a.samplerLocation, b.textureLocation); |
| } |
| |
| std::string CombinedSampler::GetName() const { |
| std::ostringstream o; |
| o << "dawn_combined"; |
| if (usePlaceholderSampler) { |
| o << "_placeholder_sampler"; |
| } else { |
| o << "_" << static_cast<uint32_t>(samplerLocation.group) << "_" |
| << static_cast<uint32_t>(samplerLocation.binding); |
| } |
| o << "_with_" << static_cast<uint32_t>(textureLocation.group) << "_" |
| << static_cast<uint32_t>(textureLocation.binding); |
| return o.str(); |
| } |
| |
| // static |
| ResultOrError<Ref<ShaderModule>> ShaderModule::Create( |
| Device* device, |
| const UnpackedPtr<ShaderModuleDescriptor>& descriptor, |
| const std::vector<tint::wgsl::Extension>& internalExtensions, |
| ShaderModuleParseResult* parseResult, |
| std::unique_ptr<OwnedCompilationMessages>* compilationMessages) { |
| Ref<ShaderModule> module = AcquireRef(new ShaderModule(device, descriptor, internalExtensions)); |
| DAWN_TRY(module->Initialize(parseResult, compilationMessages)); |
| return module; |
| } |
| |
| ShaderModule::ShaderModule(Device* device, |
| const UnpackedPtr<ShaderModuleDescriptor>& descriptor, |
| std::vector<tint::wgsl::Extension> internalExtensions) |
| : ShaderModuleBase(device, descriptor, std::move(internalExtensions)) {} |
| |
| MaybeError ShaderModule::Initialize( |
| ShaderModuleParseResult* parseResult, |
| std::unique_ptr<OwnedCompilationMessages>* compilationMessages) { |
| DAWN_TRY(InitializeBase(parseResult, compilationMessages)); |
| |
| return {}; |
| } |
| |
| std::pair<tint::glsl::writer::Bindings, BindingMap> GenerateBindingInfo( |
| SingleShaderStage stage, |
| const PipelineLayout* layout, |
| const BindingInfoArray& moduleBindingInfo, |
| GLSLCompilationRequest& req) { |
| // Because of the way the rest of the backend uses the binding information, we need to pass |
| // through the original WGSL values in the combined shader map. That means, we need to store |
| // that data for the external texture, otherwise it ends up getting lost. |
| BindingMap externalTextureExpansionMap; |
| |
| tint::glsl::writer::Bindings bindings; |
| |
| for (BindGroupIndex group : layout->GetBindGroupLayoutsMask()) { |
| const BindGroupLayout* bgl = ToBackend(layout->GetBindGroupLayout(group)); |
| |
| for (const auto& [binding, shaderBindingInfo] : moduleBindingInfo[group]) { |
| tint::BindingPoint srcBindingPoint{static_cast<uint32_t>(group), |
| static_cast<uint32_t>(binding)}; |
| |
| BindingIndex bindingIndex = bgl->GetBindingIndex(binding); |
| auto& bindingIndexInfo = layout->GetBindingIndexInfo()[group]; |
| uint32_t shaderIndex = bindingIndexInfo[bindingIndex]; |
| tint::BindingPoint dstBindingPoint{0, shaderIndex}; |
| |
| auto* const bufferBindingInfo = |
| std::get_if<BufferBindingInfo>(&shaderBindingInfo.bindingInfo); |
| |
| if (bufferBindingInfo) { |
| switch (bufferBindingInfo->type) { |
| case wgpu::BufferBindingType::Uniform: |
| bindings.uniform.emplace( |
| srcBindingPoint, |
| tint::glsl::writer::binding::Uniform{dstBindingPoint.binding}); |
| break; |
| case kInternalStorageBufferBinding: |
| case wgpu::BufferBindingType::Storage: |
| case wgpu::BufferBindingType::ReadOnlyStorage: |
| case kInternalReadOnlyStorageBufferBinding: |
| bindings.storage.emplace( |
| srcBindingPoint, |
| tint::glsl::writer::binding::Storage{dstBindingPoint.binding}); |
| break; |
| case wgpu::BufferBindingType::BindingNotUsed: |
| case wgpu::BufferBindingType::Undefined: |
| DAWN_UNREACHABLE(); |
| break; |
| } |
| } else if (std::holds_alternative<SamplerBindingInfo>(shaderBindingInfo.bindingInfo)) { |
| bindings.sampler.emplace( |
| srcBindingPoint, tint::glsl::writer::binding::Sampler{dstBindingPoint.binding}); |
| } else if (std::holds_alternative<TextureBindingInfo>(shaderBindingInfo.bindingInfo)) { |
| bindings.texture.emplace( |
| srcBindingPoint, tint::glsl::writer::binding::Texture{dstBindingPoint.binding}); |
| } else if (std::holds_alternative<StorageTextureBindingInfo>( |
| shaderBindingInfo.bindingInfo)) { |
| bindings.storage_texture.emplace( |
| srcBindingPoint, |
| tint::glsl::writer::binding::StorageTexture{dstBindingPoint.binding}); |
| } else if (std::holds_alternative<ExternalTextureBindingInfo>( |
| shaderBindingInfo.bindingInfo)) { |
| const auto& etBindingMap = bgl->GetExternalTextureBindingExpansionMap(); |
| const auto& expansion = etBindingMap.find(binding); |
| DAWN_ASSERT(expansion != etBindingMap.end()); |
| |
| using BindingInfo = tint::glsl::writer::binding::BindingInfo; |
| |
| const auto& bindingExpansion = expansion->second; |
| const BindingInfo plane0{ |
| bindingIndexInfo[bgl->GetBindingIndex(bindingExpansion.plane0)]}; |
| const BindingInfo plane1{ |
| bindingIndexInfo[bgl->GetBindingIndex(bindingExpansion.plane1)]}; |
| const BindingInfo metadata{ |
| bindingIndexInfo[bgl->GetBindingIndex(bindingExpansion.params)]}; |
| |
| tint::BindingPoint plane1WGSLBindingPoint{ |
| static_cast<uint32_t>(group), static_cast<uint32_t>(bindingExpansion.plane1)}; |
| externalTextureExpansionMap[srcBindingPoint] = plane1WGSLBindingPoint; |
| |
| bindings.external_texture.emplace( |
| srcBindingPoint, |
| tint::glsl::writer::binding::ExternalTexture{metadata, plane0, plane1}); |
| } |
| } |
| } |
| return {bindings, externalTextureExpansionMap}; |
| } |
| |
| ResultOrError<GLuint> ShaderModule::CompileShader( |
| const OpenGLFunctions& gl, |
| const ProgrammableStage& programmableStage, |
| SingleShaderStage stage, |
| bool usesVertexIndex, |
| bool usesInstanceIndex, |
| bool usesFragDepth, |
| VertexAttributeMask bgraSwizzleAttributes, |
| CombinedSamplerInfo* combinedSamplers, |
| const PipelineLayout* layout, |
| bool* needsPlaceholderSampler, |
| BindingPointToFunctionAndOffset* bindingPointToTextureBuiltinData, |
| bool* needsSSBOLengthUniformBuffer) { |
| TRACE_EVENT0(GetDevice()->GetPlatform(), General, "TranslateToGLSL"); |
| |
| const OpenGLVersion& version = ToBackend(GetDevice())->GetGL().GetVersion(); |
| |
| GLSLCompilationRequest req = {}; |
| |
| req.shaderModuleHash = GetHash(); |
| req.inputProgram = UseTintProgram(); |
| |
| // Since (non-Vulkan) GLSL does not support descriptor sets, generate a |
| // mapping from the original group/binding pair to a binding-only |
| // value. This mapping will be used by Tint to remap all global |
| // variables to the 1D space. |
| const EntryPointMetadata& entryPointMetaData = GetEntryPoint(programmableStage.entryPoint); |
| const BindingInfoArray& moduleBindingInfo = entryPointMetaData.bindings; |
| |
| auto [bindings, externalTextureExpansionMap] = |
| GenerateBindingInfo(stage, layout, moduleBindingInfo, req); |
| |
| // When textures are accessed without a sampler (e.g., textureLoad()), returned |
| // CombinedSamplerInfo should use this sentinel value as sampler binding point. |
| bindings.placeholder_sampler_bind_point = {static_cast<uint32_t>(kMaxBindGroupsTyped), 0}; |
| |
| CombinedSamplerInfo combinedSamplerInfo = GenerateCombinedSamplerInfo( |
| entryPointMetaData, bindings, externalTextureExpansionMap, needsPlaceholderSampler); |
| |
| bool needsInternalUBO = GenerateTextureBuiltinFromUniformData( |
| entryPointMetaData, layout, bindingPointToTextureBuiltinData, bindings); |
| if (needsInternalUBO) { |
| DAWN_ASSERT(!bindingPointToTextureBuiltinData->empty()); |
| // Some texture builtin functions are unsupported on GLSL ES. These are emulated with |
| // internal uniforms. |
| bindings.texture_builtins_from_uniform.ubo_binding = {kMaxBindGroups + 1, 0}; |
| |
| // Remap the internal ubo binding as well. |
| bindings.uniform.emplace(bindings.texture_builtins_from_uniform.ubo_binding, |
| tint::glsl::writer::binding::Uniform{ |
| layout->GetInternalTextureBuiltinsUniformBinding()}); |
| } |
| |
| req.stage = stage; |
| req.entryPointName = programmableStage.entryPoint; |
| req.substituteOverrideConfig = BuildSubstituteOverridesTransformConfig(programmableStage); |
| req.limits = LimitsForCompilationRequest::Create(GetDevice()->GetLimits().v1); |
| req.adapterSupportedLimits = |
| LimitsForCompilationRequest::Create(GetDevice()->GetAdapter()->GetLimits().v1); |
| |
| if (GetDevice()->IsToggleEnabled(Toggle::GLUseArrayLengthFromUniform)) { |
| *needsSSBOLengthUniformBuffer = |
| GenerateArrayLengthFromuniformData(moduleBindingInfo, layout, bindings); |
| if (*needsSSBOLengthUniformBuffer) { |
| req.tintOptions.use_array_length_from_uniform = true; |
| bindings.array_length_from_uniform.ubo_binding = {kMaxBindGroups + 2, 0}; |
| bindings.uniform.emplace(bindings.array_length_from_uniform.ubo_binding, |
| tint::glsl::writer::binding::Uniform{ |
| layout->GetInternalArrayLengthUniformBinding()}); |
| } |
| } |
| |
| req.platform = UnsafeUnkeyedValue(GetDevice()->GetPlatform()); |
| |
| req.tintOptions.version = tint::glsl::writer::Version(ToTintGLStandard(version.GetStandard()), |
| version.GetMajor(), version.GetMinor()); |
| |
| req.tintOptions.disable_robustness = false; |
| |
| if (usesVertexIndex) { |
| req.tintOptions.first_vertex_offset = 4 * PipelineLayout::ImmediateLocation::FirstVertex; |
| } |
| |
| if (usesInstanceIndex) { |
| req.tintOptions.first_instance_offset = |
| 4 * PipelineLayout::ImmediateLocation::FirstInstance; |
| } |
| |
| if (usesFragDepth) { |
| req.tintOptions.depth_range_offsets = {4 * PipelineLayout::ImmediateLocation::MinDepth, |
| 4 * PipelineLayout::ImmediateLocation::MaxDepth}; |
| } |
| |
| if (stage == SingleShaderStage::Vertex) { |
| for (VertexAttributeLocation i : bgraSwizzleAttributes) { |
| req.tintOptions.bgra_swizzle_locations.insert(static_cast<uint8_t>(i)); |
| } |
| } |
| |
| req.tintOptions.strip_all_names = !GetDevice()->IsToggleEnabled(Toggle::DisableSymbolRenaming); |
| |
| req.interstageVariables = {}; |
| for (size_t i = 0; i < entryPointMetaData.interStageVariables.size(); i++) { |
| if (entryPointMetaData.usedInterStageVariables[i]) { |
| req.interstageVariables.emplace_back(static_cast<uint32_t>(i), |
| entryPointMetaData.interStageVariables[i].name); |
| } |
| } |
| |
| req.tintOptions.bindings = std::move(bindings); |
| req.tintOptions.disable_polyfill_integer_div_mod = |
| GetDevice()->IsToggleEnabled(Toggle::DisablePolyfillsOnIntegerDivisonAndModulo); |
| |
| CacheResult<GLSLCompilation> compilationResult; |
| DAWN_TRY_LOAD_OR_RUN( |
| compilationResult, GetDevice(), std::move(req), GLSLCompilation::FromValidatedBlob, |
| [](GLSLCompilationRequest r) -> ResultOrError<GLSLCompilation> { |
| // Requires Tint Program here right before actual using. |
| auto inputProgram = r.inputProgram.UnsafeGetValue()->GetTintProgram(); |
| const tint::Program* tintInputProgram = &(inputProgram->program); |
| // Convert the AST program to an IR module. |
| tint::Result<tint::core::ir::Module> ir; |
| { |
| SCOPED_DAWN_HISTOGRAM_TIMER_MICROS(r.platform.UnsafeGetValue(), |
| "ShaderModuleProgramToIR"); |
| ir = tint::wgsl::reader::ProgramToLoweredIR(*tintInputProgram); |
| DAWN_INVALID_IF(ir != tint::Success, |
| "An error occurred while generating Tint IR\n%s", |
| ir.Failure().reason); |
| } |
| |
| { |
| SCOPED_DAWN_HISTOGRAM_TIMER_MICROS(r.platform.UnsafeGetValue(), |
| "ShaderModuleSingleEntryPoint"); |
| auto singleEntryPointResult = |
| tint::core::ir::transform::SingleEntryPoint(ir.Get(), r.entryPointName); |
| DAWN_INVALID_IF(singleEntryPointResult != tint::Success, |
| "Pipeline single entry point (IR) failed:\n%s", |
| singleEntryPointResult.Failure().reason); |
| } |
| |
| // this needs to run after SingleEntryPoint transform which removes unused |
| // overrides for the current entry point. |
| |
| { |
| SCOPED_DAWN_HISTOGRAM_TIMER_MICROS(r.platform.UnsafeGetValue(), |
| "ShaderModuleSubstituteOverrides"); |
| tint::core::ir::transform::SubstituteOverridesConfig cfg; |
| cfg.map = std::move(r.substituteOverrideConfig); |
| auto substituteOverridesResult = |
| tint::core::ir::transform::SubstituteOverrides(ir.Get(), cfg); |
| DAWN_INVALID_IF(substituteOverridesResult != tint::Success, |
| "Pipeline override substitution (IR) failed:\n%s", |
| substituteOverridesResult.Failure().reason); |
| } |
| |
| tint::Result<tint::glsl::writer::Output> result; |
| { |
| SCOPED_DAWN_HISTOGRAM_TIMER_MICROS(r.platform.UnsafeGetValue(), |
| "ShaderModuleGenerateGLSL"); |
| // Generate GLSL from Tint IR. |
| result = tint::glsl::writer::Generate(ir.Get(), r.tintOptions); |
| DAWN_INVALID_IF(result != tint::Success, |
| "An error occurred while generating GLSL:\n%s", |
| result.Failure().reason); |
| } |
| |
| // Workgroup validation has to come after `Generate` because it may require |
| // overrides to have been substituted. |
| if (r.stage == SingleShaderStage::Compute) { |
| // Validate workgroup size after program runs transforms. |
| Extent3D _; |
| DAWN_TRY_ASSIGN(_, |
| ValidateComputeStageWorkgroupSize( |
| result->workgroup_info.x, result->workgroup_info.y, |
| result->workgroup_info.z, result->workgroup_info.storage_size, |
| /* usesSubgroupMatrix */ false, |
| /* maxSubgroupSize, GL backend not support */ 0, r.limits, |
| r.adapterSupportedLimits.UnsafeGetValue())); |
| } |
| |
| return GLSLCompilation{{std::move(result->glsl)}}; |
| }, |
| "OpenGL.CompileShaderToGLSL"); |
| |
| if (GetDevice()->IsToggleEnabled(Toggle::DumpShaders)) { |
| std::ostringstream dumpedMsg; |
| dumpedMsg << "/* Dumped generated GLSL */\n" << compilationResult->glsl; |
| |
| GetDevice()->EmitLog(WGPULoggingType_Info, dumpedMsg.str().c_str()); |
| } |
| |
| GLuint shader = DAWN_GL_TRY(gl, CreateShader(GLShaderType(stage))); |
| const char* source = compilationResult->glsl.c_str(); |
| { |
| SCOPED_DAWN_HISTOGRAM_TIMER_MICROS(GetDevice()->GetPlatform(), "GLSL.CompileShader"); |
| |
| DAWN_GL_TRY(gl, ShaderSource(shader, 1, &source, nullptr)); |
| DAWN_GL_TRY(gl, CompileShader(shader)); |
| } |
| |
| GLint compileStatus = GL_FALSE; |
| DAWN_GL_TRY(gl, GetShaderiv(shader, GL_COMPILE_STATUS, &compileStatus)); |
| if (compileStatus == GL_FALSE) { |
| GLint infoLogLength = 0; |
| DAWN_GL_TRY(gl, GetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogLength)); |
| |
| if (infoLogLength > 1) { |
| std::vector<char> buffer(infoLogLength); |
| DAWN_GL_TRY(gl, GetShaderInfoLog(shader, infoLogLength, nullptr, &buffer[0])); |
| DAWN_GL_TRY(gl, DeleteShader(shader)); |
| return DAWN_VALIDATION_ERROR("%s\nProgram compilation failed:\n%s", source, |
| buffer.data()); |
| } |
| } |
| |
| GetDevice()->GetBlobCache()->EnsureStored(compilationResult); |
| |
| *combinedSamplers = std::move(combinedSamplerInfo); |
| return shader; |
| } |
| |
| } // namespace dawn::native::opengl |