| // 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 <utility> |
| |
| #include "absl/container/flat_hash_map.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/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>; |
| |
| #define GLSL_COMPILATION_REQUEST_MEMBERS(X) \ |
| X(const tint::Program*, inputProgram) \ |
| X(std::string, entryPointName) \ |
| X(SingleShaderStage, stage) \ |
| X(std::optional<tint::ast::transform::SubstituteOverride::Config>, substituteOverrideConfig) \ |
| X(LimitsForCompilationRequest, limits) \ |
| X(bool, disableSymbolRenaming) \ |
| X(std::vector<InterstageLocationAndName>, interstageVariables) \ |
| X(std::vector<std::string>, bufferBindingVariables) \ |
| 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) |
| |
| DAWN_SERIALIZABLE(struct, GLSLCompilation, GLSL_COMPILATION_MEMBERS){}; |
| #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(tint::inspector::Inspector& inspector, |
| const std::string& entryPoint, |
| tint::glsl::writer::Bindings& bindings, |
| BindingMap externalTextureExpansionMap, |
| bool* needsPlaceholderSampler |
| |
| ) { |
| auto uses = |
| inspector.GetSamplerTextureUses(entryPoint, bindings.placeholder_sampler_bind_point); |
| CombinedSamplerInfo combinedSamplerInfo; |
| for (const auto& use : uses) { |
| tint::BindingPoint samplerBindPoint = use.sampler_binding_point; |
| tint::BindingPoint texBindPoint = use.texture_binding_point; |
| |
| CombinedSampler* info = AppendCombinedSampler( |
| &combinedSamplerInfo, use.texture_binding_point, use.sampler_binding_point, |
| 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(use.texture_binding_point); |
| if (it != externalTextureExpansionMap.end()) { |
| CombinedSampler* plane1Info = |
| AppendCombinedSampler(&combinedSamplerInfo, it->second, use.sampler_binding_point, |
| bindings.placeholder_sampler_bind_point); |
| |
| tint::BindingPoint plane1TexBindPoint = it->second; |
| auto dstIt = bindings.external_texture.find(use.texture_binding_point); |
| 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(tint::inspector::Inspector& inspector, |
| const std::string& entryPoint, |
| const PipelineLayout* layout, |
| BindingPointToFunctionAndOffset* bindingPointToData, |
| tint::glsl::writer::Bindings& bindings) { |
| auto textureBuiltinsFromUniformData = inspector.GetTextureQueries(entryPoint); |
| |
| 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 tint::inspector::Inspector::TextureQueryType::kTextureNumLevels: |
| type = BindPointFunction::kTextureNumLevels; |
| break; |
| case tint::inspector::Inspector::TextureQueryType::kTextureNumSamples: |
| 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; |
| } |
| |
| } // 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, |
| 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, |
| OwnedCompilationMessages* compilationMessages) { |
| ScopedTintICEHandler scopedICEHandler(GetDevice()); |
| |
| 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 : IterateBitSet(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) { |
| // For buffer bindings that can be shareable across stages, we need to rename them |
| // to avoid GL program link failures due to block naming issues. |
| if (stage != SingleShaderStage::Compute) { |
| req.bufferBindingVariables.emplace_back(shaderBindingInfo.name); |
| } |
| |
| 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: |
| bindings.storage.emplace( |
| srcBindingPoint, |
| tint::glsl::writer::binding::Storage{dstBindingPoint.binding}); |
| break; |
| 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, |
| CombinedSamplerInfo* combinedSamplers, |
| const PipelineLayout* layout, |
| bool* needsPlaceholderSampler, |
| bool* needsTextureBuiltinUniformBuffer, |
| BindingPointToFunctionAndOffset* bindingPointToData) const { |
| TRACE_EVENT0(GetDevice()->GetPlatform(), General, "TranslateToGLSL"); |
| |
| const OpenGLVersion& version = ToBackend(GetDevice())->GetGL().GetVersion(); |
| |
| GLSLCompilationRequest req = {}; |
| |
| auto tintProgram = GetTintProgram(); |
| req.inputProgram = &(tintProgram->program); |
| |
| tint::inspector::Inspector inspector(*req.inputProgram); |
| |
| // 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()), |
| // GetSamplerTextureUses() will return this sentinel value. |
| bindings.placeholder_sampler_bind_point = {static_cast<uint32_t>(kMaxBindGroupsTyped), 0}; |
| |
| // 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->GetInternalUniformBinding()}); |
| |
| *needsPlaceholderSampler = false; |
| CombinedSamplerInfo combinedSamplerInfo = |
| generateCombinedSamplerInfo(inspector, programmableStage.entryPoint, bindings, |
| externalTextureExpansionMap, needsPlaceholderSampler); |
| |
| bool needsInternalUBO = generateTextureBuiltinFromUniformData( |
| inspector, programmableStage.entryPoint, layout, bindingPointToData, bindings); |
| |
| std::optional<tint::ast::transform::SubstituteOverride::Config> substituteOverrideConfig; |
| if (!programmableStage.metadata->overrides.empty()) { |
| substituteOverrideConfig = BuildSubstituteOverridesTransformConfig(programmableStage); |
| } |
| |
| const CombinedLimits& limits = GetDevice()->GetLimits(); |
| |
| req.stage = stage; |
| req.entryPointName = programmableStage.entryPoint; |
| req.substituteOverrideConfig = std::move(substituteOverrideConfig); |
| req.limits = LimitsForCompilationRequest::Create(limits.v1); |
| 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::PushConstantLocation::FirstVertex; |
| } |
| |
| if (usesInstanceIndex) { |
| req.tintOptions.first_instance_offset = |
| 4 * PipelineLayout::PushConstantLocation::FirstInstance; |
| } |
| |
| if (usesFragDepth) { |
| req.tintOptions.depth_range_offsets = {4 * PipelineLayout::PushConstantLocation::MinDepth, |
| 4 * PipelineLayout::PushConstantLocation::MaxDepth}; |
| } |
| |
| req.disableSymbolRenaming = 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::FromBlob, |
| [](GLSLCompilationRequest r) -> ResultOrError<GLSLCompilation> { |
| tint::ast::transform::Manager transformManager; |
| tint::ast::transform::DataMap transformInputs; |
| |
| transformManager.Add<tint::ast::transform::SingleEntryPoint>(); |
| transformInputs.Add<tint::ast::transform::SingleEntryPoint::Config>(r.entryPointName); |
| |
| { |
| tint::ast::transform::Renamer::Remappings assignedRenamings = {}; |
| |
| // Give explicit renaming mappings for interstage variables |
| // Because GLSL requires interstage IO names to match. |
| for (const auto& it : r.interstageVariables) { |
| assignedRenamings.emplace( |
| it.second, "dawn_interstage_location_" + std::to_string(it.first)); |
| } |
| |
| // Prepend v_ or f_ to buffer binding variable names in order to avoid collisions in |
| // renamed interface blocks. The AddBlockAttribute transform in the Tint GLSL |
| // printer will always generate wrapper structs from such bindings. |
| for (const auto& variableName : r.bufferBindingVariables) { |
| assignedRenamings.emplace( |
| variableName, |
| (r.stage == SingleShaderStage::Vertex ? "v_" : "f_") + variableName); |
| } |
| |
| // Needs to run early so that they can use builtin names safely. |
| // TODO(dawn:2180): move this transform into Tint. |
| transformManager.Add<tint::ast::transform::Renamer>(); |
| transformInputs.Add<tint::ast::transform::Renamer::Config>( |
| r.disableSymbolRenaming ? tint::ast::transform::Renamer::Target::kGlslKeywords |
| : tint::ast::transform::Renamer::Target::kAll, |
| false, std::move(assignedRenamings)); |
| } |
| |
| if (r.substituteOverrideConfig) { |
| // This needs to run after SingleEntryPoint transform which removes unused overrides |
| // for current entry point. |
| transformManager.Add<tint::ast::transform::SubstituteOverride>(); |
| transformInputs.Add<tint::ast::transform::SubstituteOverride::Config>( |
| std::move(r.substituteOverrideConfig).value()); |
| } |
| |
| tint::Program program; |
| tint::ast::transform::DataMap transformOutputs; |
| DAWN_TRY_ASSIGN(program, RunTransforms(&transformManager, r.inputProgram, |
| transformInputs, &transformOutputs, nullptr)); |
| |
| // TODO(dawn:2180): refactor out. |
| // Get the entry point name after the renamer pass. |
| // In the case of the entry-point name being a reserved GLSL keyword |
| // (including `main`) the entry-point would have been renamed |
| // regardless of the `disableSymbolRenaming` flag. Always check the |
| // rename map, and if the name was changed, get the new one. |
| auto* data = transformOutputs.Get<tint::ast::transform::Renamer::Data>(); |
| DAWN_ASSERT(data != nullptr); |
| auto it = data->remappings.find(r.entryPointName.data()); |
| std::string remappedEntryPoint; |
| if (it != data->remappings.end()) { |
| remappedEntryPoint = it->second; |
| } else { |
| remappedEntryPoint = r.entryPointName; |
| } |
| DAWN_ASSERT(remappedEntryPoint != ""); |
| |
| if (r.stage == SingleShaderStage::Compute) { |
| // Validate workgroup size after program runs transforms. |
| Extent3D _; |
| DAWN_TRY_ASSIGN(_, ValidateComputeStageWorkgroupSize( |
| program, remappedEntryPoint.c_str(), r.limits, |
| /* fullSubgroups */ {})); |
| } |
| |
| auto result = tint::glsl::writer::Generate(program, r.tintOptions, remappedEntryPoint); |
| DAWN_INVALID_IF(result != tint::Success, "An error occurred while generating GLSL:\n%s", |
| result.Failure().reason.Str()); |
| |
| 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 = gl.CreateShader(GLShaderType(stage)); |
| const char* source = compilationResult->glsl.c_str(); |
| gl.ShaderSource(shader, 1, &source, nullptr); |
| gl.CompileShader(shader); |
| |
| GLint compileStatus = GL_FALSE; |
| gl.GetShaderiv(shader, GL_COMPILE_STATUS, &compileStatus); |
| if (compileStatus == GL_FALSE) { |
| GLint infoLogLength = 0; |
| gl.GetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogLength); |
| |
| if (infoLogLength > 1) { |
| std::vector<char> buffer(infoLogLength); |
| gl.GetShaderInfoLog(shader, infoLogLength, nullptr, &buffer[0]); |
| gl.DeleteShader(shader); |
| return DAWN_VALIDATION_ERROR("%s\nProgram compilation failed:\n%s", source, |
| buffer.data()); |
| } |
| } |
| |
| GetDevice()->GetBlobCache()->EnsureStored(compilationResult); |
| *needsTextureBuiltinUniformBuffer = needsInternalUBO; |
| |
| *combinedSamplers = std::move(combinedSamplerInfo); |
| return shader; |
| } |
| |
| } // namespace dawn::native::opengl |