blob: eccfa0fca78a97f0d169b0c70ec2da53df987cf0 [file] [log] [blame]
// 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