blob: 1b93a6d08dbc808811140c9d22fc584017e7aebd [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/Enumerator.h"
#include "dawn/common/MatchVariant.h"
#include "dawn/native/Adapter.h"
#include "dawn/native/BindGroupLayoutInternal.h"
#include "dawn/native/CacheRequest.h"
#include "dawn/native/ImmediateConstantsLayout.h"
#include "dawn/native/Pipeline.h"
#include "dawn/native/TintUtils.h"
#include "dawn/native/opengl/BindGroupLayoutGL.h"
#include "dawn/native/opengl/DeviceGL.h"
#include "dawn/native/opengl/PipelineGL.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 "tint/tint.h"
namespace dawn::native::opengl {
namespace {
using InterstageLocationAndName = std::pair<uint32_t, std::string>;
#define GLSL_COMPILATION_REQUEST_MEMBERS(X) \
X(ShaderModuleBase::ShaderModuleHash, shaderModuleHash) \
X(UnsafeUnserializedValue<ShaderModuleBase::ScopedUseTintProgram>, inputProgram) \
X(SingleShaderStage, stage) \
X(LimitsForCompilationRequest, limits) \
X(UnsafeUnserializedValue<LimitsForCompilationRequest>, adapterSupportedLimits) \
X(bool, disableSymbolRenaming) \
X(std::vector<InterstageLocationAndName>, interstageVariables) \
X(tint::glsl::writer::Options, tintOptions) \
X(UnsafeUnserializedValue<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(Extent3D, workgroupSize)
DAWN_SERIALIZABLE(struct, GLSLCompilation, GLSL_COMPILATION_MEMBERS) {
static ResultOrError<GLSLCompilation> FromValidatedBlob(Blob blob);
};
#undef GLSL_COMPILATION_MEMBERS
// Separated from the class definition above to fix a clang-format over-indentation.
// static
ResultOrError<GLSLCompilation> 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;
}
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 OpenGLVersion::Standard::Desktop:
return tint::glsl::writer::Version::Standard::kDesktop;
case OpenGLVersion::Standard::ES:
return tint::glsl::writer::Version::Standard::kES;
}
DAWN_UNREACHABLE();
}
// Returns information about the texture/sampler pairs used by the entry point. This is necessary
// because GL uses combined texture/sampler bindings while WGSL allows mixing and matching textures
// and samplers in the shader. GL also uses a placeholder sampler to use with textures when they
// aren't combined with a sampler in the WGSL.
//
// Another subtlety is that Dawn uses pre-remapping BindingPoints when referring to bindings while
// the Tint GLSL writer uses post-remapping BindingPoints.
void GenerateCombinedSamplerInfo(
const EntryPointMetadata& metadata,
const tint::Bindings& bindings,
const PipelineLayout* layout,
std::vector<CombinedSampler>* combinedSamplers,
tint::glsl::writer::CombinedTextureSamplerInfo* samplerTextureToName,
tint::BindingPoint* placeholder_sampler_bind_point) {
// Helper to avoid duplicated logic for when a CombinedSampler is determined. It takes a bunch
// of information for both the texture and the sampler and translate to what Dawn/Tint need.
struct CombinedBindingInfo {
// Dawn takes BindGroupIndex + BindingIndex.
BindGroupIndex group;
BindingIndex index;
BindingIndex shaderArraySize = BindingIndex(1);
// Tint takes the post-remapping binding point.
tint::BindingPoint remappedBinding;
};
auto AddCombinedSampler = [&](CombinedBindingInfo texture,
std::optional<CombinedBindingInfo> sampler,
bool isPlane1 = false) {
// Reflect to the pipeline the combination with BindGroupIndex + BindingIndex in that BGL.
CombinedSampler combinedSampler = {{
.samplerLocation = std::nullopt,
.textureLocation = {{
.group = texture.group,
.index = texture.index,
.shaderArraySize = texture.shaderArraySize,
}},
}};
if (sampler.has_value()) {
combinedSampler.samplerLocation = {{{
.group = sampler->group,
.index = sampler->index,
.shaderArraySize = sampler->shaderArraySize,
}}};
}
combinedSamplers->push_back(combinedSampler);
// Let Tint know to generate a new GLSL sampler for this combination.
tint::BindingPoint samplerRemapped = *placeholder_sampler_bind_point;
if (sampler.has_value()) {
samplerRemapped = {0, sampler->remappedBinding.binding};
}
samplerTextureToName->emplace(
tint::glsl::writer::CombinedTextureSamplerPair{
{0, texture.remappedBinding.binding}, samplerRemapped, isPlane1},
combinedSampler.GetName());
};
for (const auto& use : metadata.samplerAndNonSamplerTexturePairs) {
// Replace uses of the placeholder sampler with its actual binding point.
std::optional<CombinedBindingInfo> sampler = std::nullopt;
if (use.sampler != EntryPointMetadata::nonSamplerBindingPoint) {
const BindGroupLayoutInternalBase* bgl = layout->GetBindGroupLayout(use.sampler.group);
sampler = {
.group = use.sampler.group,
.index = bgl->AsBindingIndex(bgl->GetBindingMap().at(use.sampler.binding)),
.remappedBinding = bindings.sampler.at(ToTint(use.sampler)),
};
}
// Tint reflection returns information about uses of both regular textures and sampled
// textures so we need to differentiate both cases here.
const BindGroupLayoutInternalBase* bgl = layout->GetBindGroupLayout(use.texture.group);
APIBindingIndex textureAPIIndex = bgl->GetBindingMap().at(use.texture.binding);
const auto& bindingInfo = bgl->GetAPIBindingInfo(textureAPIIndex);
// The easy case is when a regular texture is being handled.
if (std::holds_alternative<TextureBindingInfo>(bindingInfo.bindingLayout)) {
CombinedBindingInfo texture = {
.group = use.texture.group,
.index = bgl->AsBindingIndex(textureAPIIndex),
.shaderArraySize =
metadata.bindings.at(use.texture.group).at(use.texture.binding).arraySize,
.remappedBinding = bindings.texture.at(ToTint(use.texture)),
};
AddCombinedSampler(texture, sampler);
continue;
}
// This is an external texture, add planes individually.
const auto& bindingLayout = std::get<ExternalTextureBindingInfo>(bindingInfo.bindingLayout);
auto& tint_data = bindings.external_texture.at(ToTint(use.texture));
DAWN_ASSERT(std::holds_alternative<tint::ExternalMultiplanarTexture>(tint_data));
tint::ExternalMultiplanarTexture mp_data =
std::get<tint::ExternalMultiplanarTexture>(tint_data);
CombinedBindingInfo plane0 = {
.group = use.texture.group,
.index = bindingLayout.plane0,
.remappedBinding = mp_data.plane0,
};
AddCombinedSampler(plane0, sampler, false);
CombinedBindingInfo plane1 = {
.group = use.texture.group,
.index = bindingLayout.plane1,
.remappedBinding = mp_data.plane1,
};
AddCombinedSampler(plane1, sampler, true);
}
}
// Returns whether the stage uses any texture builtin metadata.
void GenerateTextureBuiltinFromUniformData(
const EntryPointMetadata& metadata,
const PipelineLayout* layout,
const tint::Bindings& bindings,
EmulatedTextureBuiltinRegistrar* emulatedTextureBuiltins,
tint::glsl::writer::TextureBuiltinsFromUniformOptions* textureBuiltinsFromUniform) {
// Tell Tint where the uniform containing the builtin data will be (in post-remapping space),
// only when this shader stage uses some builtin metadata.
if (!metadata.textureQueries.empty()) {
textureBuiltinsFromUniform->ubo_binding = {
.group = 0,
.binding = uint32_t(layout->GetInternalTextureBuiltinsUniformBinding()),
};
}
for (auto [i, query] : Enumerate(metadata.textureQueries)) {
BindGroupIndex group = BindGroupIndex(query.group);
const auto* bgl = layout->GetBindGroupLayout(group);
BindingIndex binding =
bgl->AsBindingIndex(bgl->GetAPIBindingIndex(BindingNumber{query.binding}));
// Register that the query needs to be emulated and get the offset in the UBO where the data
// will be passed.
TextureQuery textureQuery;
switch (query.type) {
case EntryPointMetadata::TextureMetadataQuery::TextureQueryType::TextureNumLevels:
textureQuery = TextureQuery::NumLevels;
break;
case EntryPointMetadata::TextureMetadataQuery::TextureQueryType::TextureNumSamples:
textureQuery = TextureQuery::NumSamples;
break;
}
uint32_t offset = emulatedTextureBuiltins->Register(group, binding, textureQuery);
// Tint uses post-remapping binding points for textureBuiltinFromUniform options.
tint::BindingPoint wgslBindPoint = {.group = query.group, .binding = query.binding};
tint::BindingPoint remappedBinding;
if (bindings.texture.contains(wgslBindPoint)) {
remappedBinding = bindings.texture.at(wgslBindPoint);
} else {
remappedBinding = bindings.storage_texture.at(wgslBindPoint);
}
textureBuiltinsFromUniform->ubo_contents.push_back({
.offset = offset,
.count = 1,
.binding = remappedBinding,
});
}
}
bool GenerateArrayLengthFromuniformData(
const BindingInfoArray& moduleBindingInfo,
const PipelineLayout* layout,
tint::glsl::writer::ArrayLengthFromUniformOptions& options) {
const PipelineLayout::BindingIndexInfo& indexInfo = layout->GetBindingIndexInfo();
for (BindGroupIndex group : layout->GetBindGroupLayoutsMask()) {
const BindGroupLayoutInternalBase* bgl = layout->GetBindGroupLayout(group);
for (BindingIndex binding : bgl->GetBufferIndices()) {
const BindingInfo& bindingInfo = bgl->GetBindingInfo(binding);
switch (std::get<BufferBindingInfo>(bindingInfo.bindingLayout).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 = {uint32_t(group),
uint32_t(bindingInfo.binding)};
FlatBindingIndex ssboIndex = indexInfo[group][binding];
options.bindpoint_to_size_index.emplace(srcBindingPoint, uint32_t(ssboIndex));
break;
}
default:
break;
}
}
}
return options.bindpoint_to_size_index.size() > 0;
}
} // namespace
bool operator<(const CombinedSamplerElement& a, const CombinedSamplerElement& b) {
return std::tie(a.group, a.index, a.shaderArraySize) <
std::tie(b.group, b.index, b.shaderArraySize);
}
bool operator<(const CombinedSampler& a, const CombinedSampler& b) {
return std::tie(a.samplerLocation, a.textureLocation) <
std::tie(b.samplerLocation, b.textureLocation);
}
std::string CombinedSampler::GetName() const {
std::ostringstream o;
o << "dawn_combined";
if (!samplerLocation) {
o << "_placeholder_sampler";
} else {
o << "_" << static_cast<uint32_t>(samplerLocation->group) << "_"
<< static_cast<uint32_t>(samplerLocation->index);
}
o << "_with_" << static_cast<uint32_t>(textureLocation.group) << "_"
<< static_cast<uint32_t>(textureLocation.index);
return o.str();
}
// static
ResultOrError<Ref<ShaderModule>> ShaderModule::Create(
Device* device,
const UnpackedPtr<ShaderModuleDescriptor>& descriptor,
const std::vector<tint::wgsl::Extension>& internalExtensions) {
Ref<ShaderModule> shader = AcquireRef(new ShaderModule(device, descriptor, internalExtensions));
shader->Initialize();
return shader;
}
ShaderModule::ShaderModule(Device* device,
const UnpackedPtr<ShaderModuleDescriptor>& descriptor,
std::vector<tint::wgsl::Extension> internalExtensions)
: ShaderModuleBase(device, descriptor, std::move(internalExtensions)) {}
ResultOrError<GLuint> ShaderModule::CompileShader(
const OpenGLFunctions& gl,
const ProgrammableStage& programmableStage,
SingleShaderStage stage,
const ImmediateConstantMask& pipelineImmediateMask,
VertexAttributeMask bgraSwizzleAttributes,
std::vector<CombinedSampler>* combinedSamplersOut,
const PipelineLayout* layout,
EmulatedTextureBuiltinRegistrar* emulatedTextureBuiltins,
bool* needsSSBOLengthUniformBuffer,
Extent3D* workgroupSize) {
TRACE_EVENT0(GetDevice()->GetPlatform(), General, "TranslateToGLSL");
const OpenGLVersion& version = gl.GetVersion();
GLSLCompilationRequest req = {};
req.shaderModuleHash = GetHash();
req.inputProgram = UnsafeUnserializedValue(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;
tint::Bindings bindings =
GenerateBindingRemapping(layout, stage, [&](BindGroupIndex group, BindingIndex index) {
return tint::BindingPoint{
.group = 0,
.binding = uint32_t(layout->GetBindingIndexInfo()[group][index]),
};
});
// When textures are accessed without a sampler (e.g., textureLoad()), returned
// CombinedSamplerInfo should use this sentinel value as sampler binding point.
req.tintOptions.placeholder_sampler_bind_point = {
.group = static_cast<uint32_t>(kMaxBindGroupsTyped),
.binding = 0,
};
// Compute the metadata necessary for translating to GL's combined textures and samplers, both
// for Dawn and for the Tint translation to GLSL.
{
std::vector<CombinedSampler> combinedSamplers;
GenerateCombinedSamplerInfo(entryPointMetaData, bindings, layout, &combinedSamplers,
&(req.tintOptions.sampler_texture_to_name),
&(req.tintOptions.placeholder_sampler_bind_point));
*combinedSamplersOut = std::move(combinedSamplers);
}
// Compute the metadata necessary to emulate some of the texture "getter" builtins not present
// in GLSL, both for Dawn and for the Tint translation to GLSL.
GenerateTextureBuiltinFromUniformData(entryPointMetaData, layout, bindings,
emulatedTextureBuiltins,
&(req.tintOptions.texture_builtins_from_uniform));
req.stage = stage;
req.limits = LimitsForCompilationRequest::Create(GetDevice()->GetLimits().v1);
req.adapterSupportedLimits = UnsafeUnserializedValue(
LimitsForCompilationRequest::Create(GetDevice()->GetAdapter()->GetLimits().v1));
if (GetDevice()->IsToggleEnabled(Toggle::GLUseArrayLengthFromUniform)) {
*needsSSBOLengthUniformBuffer = GenerateArrayLengthFromuniformData(
moduleBindingInfo, layout, req.tintOptions.array_length_from_uniform);
if (*needsSSBOLengthUniformBuffer) {
req.tintOptions.use_array_length_from_uniform = true;
req.tintOptions.array_length_from_uniform.ubo_binding = {
.group = kMaxBindGroups + 2,
.binding = 0,
};
bindings.uniform.emplace(
req.tintOptions.array_length_from_uniform.ubo_binding,
tint::BindingPoint{
.group = 0,
.binding = uint32_t(layout->GetInternalArrayLengthUniformBinding()),
});
}
}
req.platform = UnsafeUnserializedValue(GetDevice()->GetPlatform());
req.tintOptions.entry_point_name = programmableStage.entryPoint;
req.tintOptions.version = tint::glsl::writer::Version(ToTintGLStandard(version.GetStandard()),
version.GetMajor(), version.GetMinor());
req.tintOptions.substitute_overrides_config = {
.map = BuildSubstituteOverridesTransformConfig(programmableStage),
};
req.tintOptions.disable_robustness = !GetDevice()->IsRobustnessEnabled();
req.tintOptions.disable_workgroup_init =
GetDevice()->IsToggleEnabled(Toggle::DisableWorkgroupInit);
// If the size or alignment of the vertex and fragment stage immediate variables differ (e.g.,
// the vertex shader immediates contain a vec4 and the fragment shader do not), the generated
// structs may have differing alignment or size, and GLSL will give an error at link time. Count
// the actual used slots, round up to the widest possible alignment (4 u32s), multiply by the
// element byte size and pass that to Tint.
auto immediateCount = RoundUp(pipelineImmediateMask.count(), 4u);
req.tintOptions.minimum_immediate_size = immediateCount * kImmediateConstantElementByteSize;
if (HasImmediateConstants(&RenderImmediateConstants::firstVertex, pipelineImmediateMask)) {
req.tintOptions.first_vertex_offset = GetImmediateByteOffsetInPipelineIfAny(
&RenderImmediateConstants::firstVertex, pipelineImmediateMask);
}
if (HasImmediateConstants(&RenderImmediateConstants::firstInstance, pipelineImmediateMask)) {
req.tintOptions.first_instance_offset = GetImmediateByteOffsetInPipelineIfAny(
&RenderImmediateConstants::firstInstance, pipelineImmediateMask);
}
if (HasImmediateConstants(&RenderImmediateConstants::clampFragDepth, pipelineImmediateMask)) {
uint32_t offsetStartBytes = GetImmediateByteOffsetInPipeline(
&RenderImmediateConstants::clampFragDepth, pipelineImmediateMask);
req.tintOptions.depth_range_offsets = {
offsetStartBytes, offsetStartBytes + kImmediateConstantElementByteSize};
}
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);
req.tintOptions.disable_integer_range_analysis =
!GetDevice()->IsToggleEnabled(Toggle::EnableIntegerRangeAnalysisInRobustness);
req.tintOptions.use_uniform_buffers =
!GetDevice()->IsToggleEnabled(Toggle::DecomposeUniformBuffers);
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);
}
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);
}
GLSLCompilation compResult{{.glsl = std::move(result->glsl)}};
// 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.
// Subgroups are not supported on OpenGL backend.
Extent3D workgroupSize;
DAWN_TRY_ASSIGN(workgroupSize, ValidateComputeStageWorkgroupSize(
result->workgroup_info,
/*usesSubgroupMatrix=*/false,
/*maxSubgroupSize=*/0, r.limits,
r.adapterSupportedLimits.UnsafeGetValue()));
compResult.workgroupSize = workgroupSize;
}
return compResult;
},
"OpenGL.CompileShaderToGLSL");
*workgroupSize = compilationResult->workgroupSize;
if (GetDevice()->IsToggleEnabled(Toggle::DumpShaders)) {
std::ostringstream dumpedMsg;
dumpedMsg << "/* Dumped generated GLSL */\n" << compilationResult->glsl;
GetDevice()->EmitLog(wgpu::LoggingType::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);
return shader;
}
} // namespace dawn::native::opengl