| // 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/opengl/ShaderModuleGL.h" |
| |
| #include <sstream> |
| #include <utility> |
| |
| #include "dawn/native/BindGroupLayout.h" |
| #include "dawn/native/CacheRequest.h" |
| #include "dawn/native/Pipeline.h" |
| #include "dawn/native/TintUtils.h" |
| #include "dawn/native/opengl/DeviceGL.h" |
| #include "dawn/native/opengl/PipelineLayoutGL.h" |
| #include "dawn/native/stream/BlobSource.h" |
| #include "dawn/native/stream/ByteVectorSink.h" |
| #include "dawn/platform/DawnPlatform.h" |
| #include "dawn/platform/metrics/HistogramMacros.h" |
| #include "dawn/platform/tracing/TraceEvent.h" |
| |
| #include "tint/tint.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; |
| } |
| 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; |
| } |
| UNREACHABLE(); |
| } |
| |
| using BindingMap = std::unordered_map<tint::BindingPoint, tint::BindingPoint>; |
| |
| #define GLSL_COMPILATION_REQUEST_MEMBERS(X) \ |
| X(const tint::Program*, inputProgram) \ |
| X(std::string, entryPointName) \ |
| X(SingleShaderStage, stage) \ |
| X(tint::ExternalTextureOptions, externalTextureOptions) \ |
| X(BindingMap, glBindings) \ |
| X(std::optional<tint::ast::transform::SubstituteOverride::Config>, substituteOverrideConfig) \ |
| X(LimitsForCompilationRequest, limits) \ |
| X(opengl::OpenGLVersion::Standard, glVersionStandard) \ |
| X(uint32_t, glVersionMajor) \ |
| X(uint32_t, glVersionMinor) \ |
| 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) \ |
| X(bool, needsPlaceholderSampler) \ |
| X(opengl::CombinedSamplerInfo, combinedSamplerInfo) |
| |
| DAWN_SERIALIZABLE(struct, GLSLCompilation, GLSL_COMPILATION_MEMBERS){}; |
| #undef GLSL_COMPILATION_MEMBERS |
| |
| } // namespace |
| } // namespace dawn::native |
| |
| namespace dawn::native::opengl { |
| |
| 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 ShaderModuleDescriptor* descriptor, |
| ShaderModuleParseResult* parseResult, |
| OwnedCompilationMessages* compilationMessages) { |
| Ref<ShaderModule> module = AcquireRef(new ShaderModule(device, descriptor)); |
| DAWN_TRY(module->Initialize(parseResult, compilationMessages)); |
| return module; |
| } |
| |
| ShaderModule::ShaderModule(Device* device, const ShaderModuleDescriptor* descriptor) |
| : ShaderModuleBase(device, descriptor) {} |
| |
| MaybeError ShaderModule::Initialize(ShaderModuleParseResult* parseResult, |
| OwnedCompilationMessages* compilationMessages) { |
| ScopedTintICEHandler scopedICEHandler(GetDevice()); |
| |
| DAWN_TRY(InitializeBase(parseResult, compilationMessages)); |
| |
| return {}; |
| } |
| |
| ResultOrError<GLuint> ShaderModule::CompileShader(const OpenGLFunctions& gl, |
| const ProgrammableStage& programmableStage, |
| SingleShaderStage stage, |
| CombinedSamplerInfo* combinedSamplers, |
| const PipelineLayout* layout, |
| bool* needsPlaceholderSampler) const { |
| TRACE_EVENT0(GetDevice()->GetPlatform(), General, "TranslateToGLSL"); |
| |
| const OpenGLVersion& version = ToBackend(GetDevice())->GetGL().GetVersion(); |
| |
| using tint::BindingPoint; |
| // 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 BindingInfoArray& moduleBindingInfo = |
| GetEntryPoint(programmableStage.entryPoint).bindings; |
| std::unordered_map<BindingPoint, BindingPoint> glBindings; |
| for (BindGroupIndex group : IterateBitSet(layout->GetBindGroupLayoutsMask())) { |
| const BindGroupLayoutBase* bgl = layout->GetBindGroupLayout(group); |
| const auto& groupBindingInfo = moduleBindingInfo[group]; |
| for (const auto& [bindingNumber, bindingInfo] : groupBindingInfo) { |
| BindingIndex bindingIndex = bgl->GetBindingIndex(bindingNumber); |
| GLuint shaderIndex = layout->GetBindingIndexInfo()[group][bindingIndex]; |
| BindingPoint srcBindingPoint{static_cast<uint32_t>(group), |
| static_cast<uint32_t>(bindingNumber)}; |
| BindingPoint dstBindingPoint{0, shaderIndex}; |
| if (srcBindingPoint != dstBindingPoint) { |
| glBindings.emplace(srcBindingPoint, dstBindingPoint); |
| } |
| } |
| } |
| |
| std::optional<tint::ast::transform::SubstituteOverride::Config> substituteOverrideConfig; |
| if (!programmableStage.metadata->overrides.empty()) { |
| substituteOverrideConfig = BuildSubstituteOverridesTransformConfig(programmableStage); |
| } |
| |
| const CombinedLimits& limits = GetDevice()->GetLimits(); |
| |
| GLSLCompilationRequest req = {}; |
| req.inputProgram = GetTintProgram(); |
| req.stage = stage; |
| req.entryPointName = programmableStage.entryPoint; |
| req.externalTextureOptions = BuildExternalTextureTransformBindings(layout); |
| req.glBindings = std::move(glBindings); |
| req.substituteOverrideConfig = std::move(substituteOverrideConfig); |
| req.limits = LimitsForCompilationRequest::Create(limits.v1); |
| req.glVersionStandard = version.GetStandard(); |
| req.glVersionMajor = version.GetMajor(); |
| req.glVersionMinor = version.GetMinor(); |
| req.platform = UnsafeUnkeyedValue(GetDevice()->GetPlatform()); |
| |
| 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; |
| |
| if (r.substituteOverrideConfig) { |
| transformManager.Add<tint::ast::transform::SingleEntryPoint>(); |
| transformInputs.Add<tint::ast::transform::SingleEntryPoint::Config>( |
| r.entryPointName); |
| // 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; |
| DAWN_TRY_ASSIGN(program, RunTransforms(&transformManager, r.inputProgram, |
| transformInputs, nullptr, nullptr)); |
| |
| if (r.stage == SingleShaderStage::Compute) { |
| // Validate workgroup size after program runs transforms. |
| Extent3D _; |
| DAWN_TRY_ASSIGN(_, ValidateComputeStageWorkgroupSize( |
| program, r.entryPointName.c_str(), r.limits)); |
| } |
| |
| tint::glsl::writer::Options tintOptions; |
| tintOptions.version = tint::glsl::writer::Version(ToTintGLStandard(r.glVersionStandard), |
| r.glVersionMajor, r.glVersionMinor); |
| |
| // TODO(crbug.com/dawn/1686): Robustness causes shader compilation failures. |
| tintOptions.disable_robustness = true; |
| |
| tintOptions.external_texture_options = r.externalTextureOptions; |
| |
| // When textures are accessed without a sampler (e.g., textureLoad()), |
| // GetSamplerTextureUses() will return this sentinel value. |
| BindingPoint placeholderBindingPoint{static_cast<uint32_t>(kMaxBindGroupsTyped), 0}; |
| |
| bool needsPlaceholderSampler = false; |
| tint::inspector::Inspector inspector(&program); |
| // 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). |
| auto uses = inspector.GetSamplerTextureUses(r.entryPointName, placeholderBindingPoint); |
| CombinedSamplerInfo combinedSamplerInfo; |
| for (const auto& use : uses) { |
| combinedSamplerInfo.emplace_back(); |
| |
| CombinedSampler* info = &combinedSamplerInfo.back(); |
| if (use.sampler_binding_point == placeholderBindingPoint) { |
| info->usePlaceholderSampler = true; |
| needsPlaceholderSampler = true; |
| tintOptions.placeholder_binding_point = placeholderBindingPoint; |
| } else { |
| info->usePlaceholderSampler = false; |
| } |
| info->samplerLocation.group = BindGroupIndex(use.sampler_binding_point.group); |
| info->samplerLocation.binding = BindingNumber(use.sampler_binding_point.binding); |
| info->textureLocation.group = BindGroupIndex(use.texture_binding_point.group); |
| info->textureLocation.binding = BindingNumber(use.texture_binding_point.binding); |
| tintOptions.binding_map[use] = info->GetName(); |
| } |
| tintOptions.binding_points = std::move(r.glBindings); |
| tintOptions.allow_collisions = true; |
| |
| auto result = tint::glsl::writer::Generate(&program, tintOptions, r.entryPointName); |
| DAWN_INVALID_IF(!result.success, "An error occured while generating GLSL: %s.", |
| result.error); |
| |
| return GLSLCompilation{ |
| {std::move(result.glsl), needsPlaceholderSampler, std::move(combinedSamplerInfo)}}; |
| }, |
| "OpenGL.CompileShaderToGLSL"); |
| |
| if (GetDevice()->IsToggleEnabled(Toggle::DumpShaders)) { |
| std::ostringstream dumpedMsg; |
| dumpedMsg << "/* Dumped generated GLSL */" << std::endl << 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); |
| *needsPlaceholderSampler = compilationResult->needsPlaceholderSampler; |
| *combinedSamplers = std::move(compilationResult->combinedSamplerInfo); |
| return shader; |
| } |
| |
| } // namespace dawn::native::opengl |