Flutter Impeller
pipeline_library_gles.cc
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
6 
7 #include <sstream>
8 #include <string>
9 
10 #include "flutter/fml/container.h"
11 #include "flutter/fml/trace_event.h"
12 #include "fml/closure.h"
13 #include "impeller/base/promise.h"
16 
17 namespace impeller {
18 
20  : reactor_(std::move(reactor)) {}
21 
22 static std::string GetShaderInfoLog(const ProcTableGLES& gl, GLuint shader) {
23  GLint log_length = 0;
24  gl.GetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length);
25  if (log_length == 0) {
26  return "";
27  }
28  auto log_buffer =
29  reinterpret_cast<char*>(std::calloc(log_length, sizeof(char)));
30  gl.GetShaderInfoLog(shader, log_length, &log_length, log_buffer);
31  auto log_string = std::string(log_buffer, log_length);
32  std::free(log_buffer);
33  return log_string;
34 }
35 
36 static std::string GetShaderSource(const ProcTableGLES& gl, GLuint shader) {
37  // Arbitrarily chosen size that should be larger than most shaders.
38  // Since this only fires on compilation errors the performance shouldn't
39  // matter.
40  auto data = static_cast<char*>(malloc(10240));
41  GLsizei length;
42  gl.GetShaderSource(shader, 10240, &length, data);
43 
44  auto result = std::string{data, static_cast<size_t>(length)};
45  free(data);
46  return result;
47 }
48 
50  GLuint shader,
51  const std::string& name,
52  const fml::Mapping& source_mapping,
53  ShaderStage stage) {
54  std::stringstream stream;
55  stream << "Failed to compile ";
56  switch (stage) {
58  stream << "unknown";
59  break;
61  stream << "vertex";
62  break;
64  stream << "fragment";
65  break;
67  stream << "compute";
68  break;
69  }
70  stream << " shader for '" << name << "' with error:" << std::endl;
71  stream << GetShaderInfoLog(gl, shader) << std::endl;
72  stream << "Shader source was: " << std::endl;
73  stream << GetShaderSource(gl, shader) << std::endl;
74  VALIDATION_LOG << stream.str();
75 }
76 
77 static bool LinkProgram(
78  const ReactorGLES& reactor,
79  const std::shared_ptr<PipelineGLES>& pipeline,
80  const std::shared_ptr<const ShaderFunction>& vert_function,
81  const std::shared_ptr<const ShaderFunction>& frag_function) {
82  TRACE_EVENT0("impeller", __FUNCTION__);
83 
84  const auto& descriptor = pipeline->GetDescriptor();
85 
86  auto vert_mapping =
88  auto frag_mapping =
90 
91  const auto& gl = reactor.GetProcTable();
92 
93  auto vert_shader = gl.CreateShader(GL_VERTEX_SHADER);
94  auto frag_shader = gl.CreateShader(GL_FRAGMENT_SHADER);
95 
96  if (vert_shader == 0 || frag_shader == 0) {
97  VALIDATION_LOG << "Could not create shader handles.";
98  return false;
99  }
100 
101  gl.SetDebugLabel(DebugResourceType::kShader, vert_shader,
102  SPrintF("%s Vertex Shader", descriptor.GetLabel().c_str()));
103  gl.SetDebugLabel(
104  DebugResourceType::kShader, frag_shader,
105  SPrintF("%s Fragment Shader", descriptor.GetLabel().c_str()));
106 
107  fml::ScopedCleanupClosure delete_vert_shader(
108  [&gl, vert_shader]() { gl.DeleteShader(vert_shader); });
109  fml::ScopedCleanupClosure delete_frag_shader(
110  [&gl, frag_shader]() { gl.DeleteShader(frag_shader); });
111 
112  gl.ShaderSourceMapping(vert_shader, *vert_mapping,
113  descriptor.GetSpecializationConstants());
114  gl.ShaderSourceMapping(frag_shader, *frag_mapping,
115  descriptor.GetSpecializationConstants());
116 
117  gl.CompileShader(vert_shader);
118  gl.CompileShader(frag_shader);
119 
120  GLint vert_status = GL_FALSE;
121  GLint frag_status = GL_FALSE;
122 
123  gl.GetShaderiv(vert_shader, GL_COMPILE_STATUS, &vert_status);
124  gl.GetShaderiv(frag_shader, GL_COMPILE_STATUS, &frag_status);
125 
126  if (vert_status != GL_TRUE) {
127  LogShaderCompilationFailure(gl, vert_shader, descriptor.GetLabel(),
128  *vert_mapping, ShaderStage::kVertex);
129  return false;
130  }
131 
132  if (frag_status != GL_TRUE) {
133  LogShaderCompilationFailure(gl, frag_shader, descriptor.GetLabel(),
134  *frag_mapping, ShaderStage::kFragment);
135  return false;
136  }
137 
138  auto program = reactor.GetGLHandle(pipeline->GetProgramHandle());
139  if (!program.has_value()) {
140  VALIDATION_LOG << "Could not get program handle from reactor.";
141  return false;
142  }
143 
144  gl.AttachShader(*program, vert_shader);
145  gl.AttachShader(*program, frag_shader);
146 
147  fml::ScopedCleanupClosure detach_vert_shader(
148  [&gl, program = *program, vert_shader]() {
149  gl.DetachShader(program, vert_shader);
150  });
151  fml::ScopedCleanupClosure detach_frag_shader(
152  [&gl, program = *program, frag_shader]() {
153  gl.DetachShader(program, frag_shader);
154  });
155 
156  for (const auto& stage_input :
157  descriptor.GetVertexDescriptor()->GetStageInputs()) {
158  gl.BindAttribLocation(*program, //
159  static_cast<GLuint>(stage_input.location), //
160  stage_input.name //
161  );
162  }
163 
164  gl.LinkProgram(*program);
165 
166  GLint link_status = GL_FALSE;
167  gl.GetProgramiv(*program, GL_LINK_STATUS, &link_status);
168 
169  if (link_status != GL_TRUE) {
170  VALIDATION_LOG << "Could not link shader program: "
171  << gl.GetProgramInfoLogString(*program);
172  return false;
173  }
174  return true;
175 }
176 
177 // |PipelineLibrary|
178 bool PipelineLibraryGLES::IsValid() const {
179  return reactor_ != nullptr;
180 }
181 
182 std::shared_ptr<PipelineGLES> PipelineLibraryGLES::CreatePipeline(
183  const std::weak_ptr<PipelineLibrary>& weak_library,
184  const PipelineDescriptor& desc,
185  const std::shared_ptr<const ShaderFunction>& vert_function,
186  const std::shared_ptr<const ShaderFunction>& frag_function) {
187  auto strong_library = weak_library.lock();
188 
189  if (!strong_library) {
190  VALIDATION_LOG << "Library was collected before a pending pipeline "
191  "creation could finish.";
192  return nullptr;
193  }
194 
195  auto& library = PipelineLibraryGLES::Cast(*strong_library);
196 
197  const auto& reactor = library.GetReactor();
198 
199  if (!reactor) {
200  return nullptr;
201  }
202 
203  auto program_key = ProgramKey{vert_function, frag_function,
204  desc.GetSpecializationConstants()};
205 
206  auto cached_program = library.GetProgramForKey(program_key);
207 
208  const auto has_cached_program = !!cached_program;
209 
210  auto pipeline = std::shared_ptr<PipelineGLES>(new PipelineGLES(
211  reactor, //
212  weak_library, //
213  desc, //
214  has_cached_program
215  ? std::move(cached_program)
216  : std::make_shared<UniqueHandleGLES>(reactor, HandleType::kProgram)));
217 
218  auto program = reactor->GetGLHandle(pipeline->GetProgramHandle());
219 
220  if (!program.has_value()) {
221  VALIDATION_LOG << "Could not obtain program handle.";
222  return nullptr;
223  }
224 
225  const auto link_result = !has_cached_program ? LinkProgram(*reactor, //
226  pipeline, //
227  vert_function, //
228  frag_function //
229  )
230  : true;
231 
232  if (!link_result) {
233  VALIDATION_LOG << "Could not link pipeline program.";
234  return nullptr;
235  }
236 
237  if (!pipeline->BuildVertexDescriptor(reactor->GetProcTable(),
238  program.value())) {
239  VALIDATION_LOG << "Could not build pipeline vertex descriptors.";
240  return nullptr;
241  }
242 
243  if (!pipeline->IsValid()) {
244  VALIDATION_LOG << "Pipeline validation checks failed.";
245  return nullptr;
246  }
247 
248  if (!has_cached_program) {
249  library.SetProgramForKey(program_key, pipeline->GetSharedHandle());
250  }
251 
252  return pipeline;
253 }
254 
255 // |PipelineLibrary|
256 PipelineFuture<PipelineDescriptor> PipelineLibraryGLES::GetPipeline(
257  PipelineDescriptor descriptor,
258  bool async) {
259  if (auto found = pipelines_.find(descriptor); found != pipelines_.end()) {
260  return found->second;
261  }
262 
263  if (!reactor_) {
264  return {
265  descriptor,
266  RealizedFuture<std::shared_ptr<Pipeline<PipelineDescriptor>>>(nullptr)};
267  }
268 
269  auto vert_function = descriptor.GetEntrypointForStage(ShaderStage::kVertex);
270  auto frag_function = descriptor.GetEntrypointForStage(ShaderStage::kFragment);
271 
272  if (!vert_function || !frag_function) {
274  << "Could not find stage entrypoint functions in pipeline descriptor.";
275  return {
276  descriptor,
277  RealizedFuture<std::shared_ptr<Pipeline<PipelineDescriptor>>>(nullptr)};
278  }
279 
280  auto promise = std::make_shared<
281  std::promise<std::shared_ptr<Pipeline<PipelineDescriptor>>>>();
282  auto pipeline_future =
283  PipelineFuture<PipelineDescriptor>{descriptor, promise->get_future()};
284  pipelines_[descriptor] = pipeline_future;
285 
286  const auto result = reactor_->AddOperation([promise, //
287  weak_this = weak_from_this(), //
288  descriptor, //
289  vert_function, //
290  frag_function //
291  ](const ReactorGLES& reactor) {
292  promise->set_value(
293  CreatePipeline(weak_this, descriptor, vert_function, frag_function));
294  });
295  FML_CHECK(result);
296 
297  return pipeline_future;
298 }
299 
300 // |PipelineLibrary|
301 PipelineFuture<ComputePipelineDescriptor> PipelineLibraryGLES::GetPipeline(
302  ComputePipelineDescriptor descriptor,
303  bool async) {
304  auto promise = std::make_shared<
305  std::promise<std::shared_ptr<Pipeline<ComputePipelineDescriptor>>>>();
306  promise->set_value(nullptr);
307  return {descriptor, promise->get_future()};
308 }
309 
310 // |PipelineLibrary|
311 bool PipelineLibraryGLES::HasPipeline(const PipelineDescriptor& descriptor) {
312  return pipelines_.find(descriptor) != pipelines_.end();
313 }
314 
315 // |PipelineLibrary|
316 void PipelineLibraryGLES::RemovePipelinesWithEntryPoint(
317  std::shared_ptr<const ShaderFunction> function) {
318  fml::erase_if(pipelines_, [&](auto item) {
319  return item->first.GetEntrypointForStage(function->GetStage())
320  ->IsEqual(*function);
321  });
322 }
323 
324 // |PipelineLibrary|
326 
327 const ReactorGLES::Ref& PipelineLibraryGLES::GetReactor() const {
328  return reactor_;
329 }
330 
331 std::shared_ptr<UniqueHandleGLES> PipelineLibraryGLES::GetProgramForKey(
332  const ProgramKey& key) {
333  Lock lock(programs_mutex_);
334  auto found = programs_.find(key);
335  if (found != programs_.end()) {
336  return found->second;
337  }
338  return nullptr;
339 }
340 
341 void PipelineLibraryGLES::SetProgramForKey(
342  const ProgramKey& key,
343  std::shared_ptr<UniqueHandleGLES> program) {
344  Lock lock(programs_mutex_);
345  programs_[key] = std::move(program);
346 }
347 
348 } // namespace impeller
impeller::ReactorGLES::GetProcTable
const ProcTableGLES & GetProcTable() const
Get the OpenGL proc. table the reactor uses to manage handles.
Definition: reactor_gles.cc:48
impeller::ShaderStage::kUnknown
@ kUnknown
data
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63
impeller::ReactorGLES::Ref
std::shared_ptr< ReactorGLES > Ref
Definition: reactor_gles.h:86
impeller::LinkProgram
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)
Definition: pipeline_library_gles.cc:77
impeller::ShaderStage
ShaderStage
Definition: shader_types.h:22
impeller::LogShaderCompilationFailure
static void LogShaderCompilationFailure(const ProcTableGLES &gl, GLuint shader, const std::string &name, const fml::Mapping &source_mapping, ShaderStage stage)
Definition: pipeline_library_gles.cc:49
impeller::GetShaderSource
static std::string GetShaderSource(const ProcTableGLES &gl, GLuint shader)
Definition: pipeline_library_gles.cc:36
impeller::ShaderFunctionGLES::GetSourceMapping
const std::shared_ptr< const fml::Mapping > & GetSourceMapping() const
Definition: shader_function_gles.cc:20
impeller::PipelineLibraryGLES::PipelineLibraryGLES
PipelineLibraryGLES(const PipelineLibraryGLES &)=delete
impeller::SPrintF
std::string SPrintF(const char *format,...)
Definition: strings.cc:12
impeller::ProcTableGLES
Definition: proc_table_gles.h:226
impeller::ShaderStage::kFragment
@ kFragment
impeller::DebugResourceType::kShader
@ kShader
impeller::HandleType::kProgram
@ kProgram
shader_function_gles.h
VALIDATION_LOG
#define VALIDATION_LOG
Definition: validation.h:91
pipeline_library_gles.h
pipeline_gles.h
promise.h
std
Definition: comparable.h:95
impeller::ShaderStage::kVertex
@ kVertex
impeller::BackendCast< ShaderFunctionGLES, ShaderFunction >::Cast
static ShaderFunctionGLES & Cast(ShaderFunction &base)
Definition: backend_cast.h:13
impeller::ReactorGLES
The reactor attempts to make thread-safe usage of OpenGL ES easier to reason about.
Definition: reactor_gles.h:55
impeller::ShaderStage::kCompute
@ kCompute
impeller::GetShaderInfoLog
static std::string GetShaderInfoLog(const ProcTableGLES &gl, GLuint shader)
Definition: pipeline_library_gles.cc:22
impeller::PipelineLibraryGLES::~PipelineLibraryGLES
~PipelineLibraryGLES() override
impeller
Definition: allocation.cc:12
impeller::ReactorGLES::GetGLHandle
std::optional< GLuint > GetGLHandle(const HandleGLES &handle) const
Returns the OpenGL handle for a reactor handle if one is available. This is typically only safe to ca...
Definition: reactor_gles.cc:53