blob: 14e395404efa5920c8ee60a3ab7fceba1e9a86a2 [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "impeller/renderer/backend/gles/pipeline_library_gles.h"
#include <sstream>
#include <string>
#include "flutter/fml/container.h"
#include "flutter/fml/trace_event.h"
#include "fml/closure.h"
#include "impeller/base/promise.h"
#include "impeller/renderer/backend/gles/pipeline_gles.h"
#include "impeller/renderer/backend/gles/shader_function_gles.h"
namespace impeller {
PipelineLibraryGLES::PipelineLibraryGLES(ReactorGLES::Ref reactor)
: reactor_(std::move(reactor)) {}
static std::string GetShaderInfoLog(const ProcTableGLES& gl, GLuint shader) {
GLint log_length = 0;
gl.GetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length);
if (log_length == 0) {
return "";
}
auto log_buffer =
reinterpret_cast<char*>(std::calloc(log_length, sizeof(char)));
gl.GetShaderInfoLog(shader, log_length, &log_length, log_buffer);
auto log_string = std::string(log_buffer, log_length);
std::free(log_buffer);
return log_string;
}
static std::string GetShaderSource(const ProcTableGLES& gl, GLuint shader) {
// Arbitrarily chosen size that should be larger than most shaders.
// Since this only fires on compilation errors the performance shouldn't
// matter.
auto data = static_cast<char*>(malloc(10240));
GLsizei length;
gl.GetShaderSource(shader, 10240, &length, data);
auto result = std::string{data, static_cast<size_t>(length)};
free(data);
return result;
}
static void LogShaderCompilationFailure(const ProcTableGLES& gl,
GLuint shader,
const std::string& name,
const fml::Mapping& source_mapping,
ShaderStage stage) {
std::stringstream stream;
stream << "Failed to compile ";
switch (stage) {
case ShaderStage::kUnknown:
stream << "unknown";
break;
case ShaderStage::kVertex:
stream << "vertex";
break;
case ShaderStage::kFragment:
stream << "fragment";
break;
case ShaderStage::kCompute:
stream << "compute";
break;
}
stream << " shader for '" << name << "' with error:" << std::endl;
stream << GetShaderInfoLog(gl, shader) << std::endl;
stream << "Shader source was: " << std::endl;
stream << GetShaderSource(gl, shader) << std::endl;
VALIDATION_LOG << stream.str();
}
static bool LinkProgram(
const ReactorGLES& reactor,
const std::shared_ptr<PipelineGLES>& pipeline,
const std::shared_ptr<const ShaderFunction>& vert_function,
const std::shared_ptr<const ShaderFunction>& frag_function) {
TRACE_EVENT0("impeller", __FUNCTION__);
const auto& descriptor = pipeline->GetDescriptor();
auto vert_mapping =
ShaderFunctionGLES::Cast(*vert_function).GetSourceMapping();
auto frag_mapping =
ShaderFunctionGLES::Cast(*frag_function).GetSourceMapping();
const auto& gl = reactor.GetProcTable();
auto vert_shader = gl.CreateShader(GL_VERTEX_SHADER);
auto frag_shader = gl.CreateShader(GL_FRAGMENT_SHADER);
if (vert_shader == 0 || frag_shader == 0) {
VALIDATION_LOG << "Could not create shader handles.";
return false;
}
gl.SetDebugLabel(DebugResourceType::kShader, vert_shader,
SPrintF("%s Vertex Shader", descriptor.GetLabel().c_str()));
gl.SetDebugLabel(
DebugResourceType::kShader, frag_shader,
SPrintF("%s Fragment Shader", descriptor.GetLabel().c_str()));
fml::ScopedCleanupClosure delete_vert_shader(
[&gl, vert_shader]() { gl.DeleteShader(vert_shader); });
fml::ScopedCleanupClosure delete_frag_shader(
[&gl, frag_shader]() { gl.DeleteShader(frag_shader); });
gl.ShaderSourceMapping(vert_shader, *vert_mapping,
descriptor.GetSpecializationConstants());
gl.ShaderSourceMapping(frag_shader, *frag_mapping,
descriptor.GetSpecializationConstants());
gl.CompileShader(vert_shader);
gl.CompileShader(frag_shader);
GLint vert_status = GL_FALSE;
GLint frag_status = GL_FALSE;
gl.GetShaderiv(vert_shader, GL_COMPILE_STATUS, &vert_status);
gl.GetShaderiv(frag_shader, GL_COMPILE_STATUS, &frag_status);
if (vert_status != GL_TRUE) {
LogShaderCompilationFailure(gl, vert_shader, descriptor.GetLabel(),
*vert_mapping, ShaderStage::kVertex);
return false;
}
if (frag_status != GL_TRUE) {
LogShaderCompilationFailure(gl, frag_shader, descriptor.GetLabel(),
*frag_mapping, ShaderStage::kFragment);
return false;
}
auto program = reactor.GetGLHandle(pipeline->GetProgramHandle());
if (!program.has_value()) {
VALIDATION_LOG << "Could not get program handle from reactor.";
return false;
}
gl.AttachShader(*program, vert_shader);
gl.AttachShader(*program, frag_shader);
fml::ScopedCleanupClosure detach_vert_shader(
[&gl, program = *program, vert_shader]() {
gl.DetachShader(program, vert_shader);
});
fml::ScopedCleanupClosure detach_frag_shader(
[&gl, program = *program, frag_shader]() {
gl.DetachShader(program, frag_shader);
});
for (const auto& stage_input :
descriptor.GetVertexDescriptor()->GetStageInputs()) {
gl.BindAttribLocation(*program, //
static_cast<GLuint>(stage_input.location), //
stage_input.name //
);
}
gl.LinkProgram(*program);
GLint link_status = GL_FALSE;
gl.GetProgramiv(*program, GL_LINK_STATUS, &link_status);
if (link_status != GL_TRUE) {
VALIDATION_LOG << "Could not link shader program: "
<< gl.GetProgramInfoLogString(*program);
return false;
}
return true;
}
// |PipelineLibrary|
bool PipelineLibraryGLES::IsValid() const {
return reactor_ != nullptr;
}
// |PipelineLibrary|
PipelineFuture<PipelineDescriptor> PipelineLibraryGLES::GetPipeline(
PipelineDescriptor descriptor) {
if (auto found = pipelines_.find(descriptor); found != pipelines_.end()) {
return found->second;
}
if (!reactor_) {
return {
descriptor,
RealizedFuture<std::shared_ptr<Pipeline<PipelineDescriptor>>>(nullptr)};
}
auto vert_function = descriptor.GetEntrypointForStage(ShaderStage::kVertex);
auto frag_function = descriptor.GetEntrypointForStage(ShaderStage::kFragment);
if (!vert_function || !frag_function) {
VALIDATION_LOG
<< "Could not find stage entrypoint functions in pipeline descriptor.";
return {
descriptor,
RealizedFuture<std::shared_ptr<Pipeline<PipelineDescriptor>>>(nullptr)};
}
auto promise = std::make_shared<
std::promise<std::shared_ptr<Pipeline<PipelineDescriptor>>>>();
auto pipeline_future =
PipelineFuture<PipelineDescriptor>{descriptor, promise->get_future()};
pipelines_[descriptor] = pipeline_future;
auto weak_this = weak_from_this();
auto result = reactor_->AddOperation(
[promise, weak_this, reactor_ptr = reactor_, descriptor, vert_function,
frag_function](const ReactorGLES& reactor) {
auto strong_this = weak_this.lock();
if (!strong_this) {
promise->set_value(nullptr);
VALIDATION_LOG << "Library was collected before a pending pipeline "
"creation could finish.";
return;
}
auto pipeline = std::shared_ptr<PipelineGLES>(
new PipelineGLES(reactor_ptr, strong_this, descriptor));
auto program = reactor.GetGLHandle(pipeline->GetProgramHandle());
if (!program.has_value()) {
promise->set_value(nullptr);
VALIDATION_LOG << "Could not obtain program handle.";
return;
}
const auto link_result = LinkProgram(reactor, //
pipeline, //
vert_function, //
frag_function //
);
if (!link_result) {
promise->set_value(nullptr);
VALIDATION_LOG << "Could not link pipeline program.";
return;
}
if (!pipeline->BuildVertexDescriptor(reactor.GetProcTable(),
program.value())) {
promise->set_value(nullptr);
VALIDATION_LOG << "Could not build pipeline vertex descriptors.";
return;
}
if (!pipeline->IsValid()) {
promise->set_value(nullptr);
VALIDATION_LOG << "Pipeline validation checks failed.";
return;
}
promise->set_value(std::move(pipeline));
});
FML_CHECK(result);
return pipeline_future;
}
// |PipelineLibrary|
PipelineFuture<ComputePipelineDescriptor> PipelineLibraryGLES::GetPipeline(
ComputePipelineDescriptor descriptor) {
auto promise = std::make_shared<
std::promise<std::shared_ptr<Pipeline<ComputePipelineDescriptor>>>>();
// TODO(dnfield): implement compute for GLES.
promise->set_value(nullptr);
return {descriptor, promise->get_future()};
}
// |PipelineLibrary|
void PipelineLibraryGLES::RemovePipelinesWithEntryPoint(
std::shared_ptr<const ShaderFunction> function) {
fml::erase_if(pipelines_, [&](auto item) {
return item->first.GetEntrypointForStage(function->GetStage())
->IsEqual(*function);
});
}
// |PipelineLibrary|
PipelineLibraryGLES::~PipelineLibraryGLES() = default;
} // namespace impeller