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/trace_event.h"
11 #include "fml/closure.h"
12 #include "impeller/base/promise.h"
16 
17 namespace impeller {
18 
19 PipelineLibraryGLES::PipelineLibraryGLES(std::shared_ptr<ReactorGLES> reactor)
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  std::string_view name,
52  const fml::Mapping& source_mapping,
53  ShaderStage stage) {
54  std::stringstream stream;
55  stream << "Failed to compile ";
56  switch (stage) {
57  case ShaderStage::kUnknown:
58  stream << "unknown";
59  break;
60  case ShaderStage::kVertex:
61  stream << "vertex";
62  break;
63  case ShaderStage::kFragment:
64  stream << "fragment";
65  break;
66  case ShaderStage::kCompute:
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 =
87  ShaderFunctionGLES::Cast(*vert_function).GetSourceMapping();
88  auto frag_mapping =
89  ShaderFunctionGLES::Cast(*frag_function).GetSourceMapping();
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  std::format("{} Vertex Shader", descriptor.GetLabel()));
103  gl.SetDebugLabel(DebugResourceType::kShader, frag_shader,
104  std::format("{} Fragment Shader", descriptor.GetLabel()));
105 
106  fml::ScopedCleanupClosure delete_vert_shader(
107  [&gl, vert_shader]() { gl.DeleteShader(vert_shader); });
108  fml::ScopedCleanupClosure delete_frag_shader(
109  [&gl, frag_shader]() { gl.DeleteShader(frag_shader); });
110 
111  gl.ShaderSourceMapping(vert_shader, *vert_mapping,
112  descriptor.GetSpecializationConstants());
113  gl.ShaderSourceMapping(frag_shader, *frag_mapping,
114  descriptor.GetSpecializationConstants());
115 
116  gl.CompileShader(vert_shader);
117  gl.CompileShader(frag_shader);
118 
119  GLint vert_status = GL_FALSE;
120  GLint frag_status = GL_FALSE;
121 
122  gl.GetShaderiv(vert_shader, GL_COMPILE_STATUS, &vert_status);
123  gl.GetShaderiv(frag_shader, GL_COMPILE_STATUS, &frag_status);
124 
125  if (vert_status != GL_TRUE) {
126  LogShaderCompilationFailure(gl, vert_shader, descriptor.GetLabel(),
127  *vert_mapping, ShaderStage::kVertex);
128  return false;
129  }
130 
131  if (frag_status != GL_TRUE) {
132  LogShaderCompilationFailure(gl, frag_shader, descriptor.GetLabel(),
133  *frag_mapping, ShaderStage::kFragment);
134  return false;
135  }
136 
137  auto program = reactor.GetGLHandle(pipeline->GetProgramHandle());
138  if (!program.has_value()) {
139  VALIDATION_LOG << "Could not get program handle from reactor.";
140  return false;
141  }
142 
143  gl.AttachShader(*program, vert_shader);
144  gl.AttachShader(*program, frag_shader);
145 
146  fml::ScopedCleanupClosure detach_vert_shader(
147  [&gl, program = *program, vert_shader]() {
148  gl.DetachShader(program, vert_shader);
149  });
150  fml::ScopedCleanupClosure detach_frag_shader(
151  [&gl, program = *program, frag_shader]() {
152  gl.DetachShader(program, frag_shader);
153  });
154 
155  for (const auto& stage_input :
156  descriptor.GetVertexDescriptor()->GetStageInputs()) {
157  gl.BindAttribLocation(*program, //
158  static_cast<GLuint>(stage_input.location), //
159  stage_input.name //
160  );
161  }
162 
163  gl.LinkProgram(*program);
164 
165  GLint link_status = GL_FALSE;
166  gl.GetProgramiv(*program, GL_LINK_STATUS, &link_status);
167 
168  if (link_status != GL_TRUE) {
169  VALIDATION_LOG << "Could not link shader program: "
170  << gl.GetProgramInfoLogString(*program)
171  << "\nVertex Shader:\n"
172  << GetShaderSource(gl, vert_shader) << "\nFragment Shader:\n"
173  << GetShaderSource(gl, frag_shader);
174  return false;
175  }
176  return true;
177 }
178 
179 // |PipelineLibrary|
180 bool PipelineLibraryGLES::IsValid() const {
181  return reactor_ != nullptr;
182 }
183 
184 std::shared_ptr<PipelineGLES> PipelineLibraryGLES::CreatePipeline(
185  const std::weak_ptr<PipelineLibrary>& weak_library,
186  const PipelineDescriptor& desc,
187  const std::shared_ptr<const ShaderFunction>& vert_function,
188  const std::shared_ptr<const ShaderFunction>& frag_function,
189  bool threadsafe) {
190  auto strong_library = weak_library.lock();
191 
192  if (!strong_library) {
193  VALIDATION_LOG << "Library was collected before a pending pipeline "
194  "creation could finish.";
195  return nullptr;
196  }
197 
198  auto& library = PipelineLibraryGLES::Cast(*strong_library);
199 
200  const auto& reactor = library.GetReactor();
201 
202  if (!reactor) {
203  return nullptr;
204  }
205 
206  auto program_key = ProgramKey{vert_function, frag_function,
207  desc.GetSpecializationConstants()};
208 
209  auto cached_program = library.GetProgramForKey(program_key);
210 
211  const auto has_cached_program = !!cached_program;
212 
213  std::shared_ptr<UniqueHandleGLES> program_handle = nullptr;
214  if (has_cached_program) {
215  program_handle = std::move(cached_program);
216  } else {
217  program_handle = threadsafe ? std::make_shared<UniqueHandleGLES>(
218  reactor, HandleType::kProgram)
219  : std::make_shared<UniqueHandleGLES>(
220  UniqueHandleGLES::MakeUntracked(
221  reactor, HandleType::kProgram));
222  }
223 
224  auto pipeline = std::shared_ptr<PipelineGLES>(
225  new PipelineGLES(reactor, //
226  weak_library, //
227  desc, //
228  std::move(program_handle)));
229 
230  auto program = reactor->GetGLHandle(pipeline->GetProgramHandle());
231 
232  if (!program.has_value()) {
233  VALIDATION_LOG << "Could not obtain program handle.";
234  return nullptr;
235  }
236 
237  const auto link_result = !has_cached_program ? LinkProgram(*reactor, //
238  pipeline, //
239  vert_function, //
240  frag_function //
241  )
242  : true;
243 
244  if (!link_result) {
245  VALIDATION_LOG << "Could not link pipeline program.";
246  return nullptr;
247  }
248 
249  if (!pipeline->BuildVertexDescriptor(reactor->GetProcTable(),
250  program.value())) {
251  VALIDATION_LOG << "Could not build pipeline vertex descriptors.";
252  return nullptr;
253  }
254 
255  if (!pipeline->IsValid()) {
256  VALIDATION_LOG << "Pipeline validation checks failed.";
257  return nullptr;
258  }
259 
260  if (!has_cached_program) {
261  library.SetProgramForKey(program_key, pipeline->GetSharedHandle());
262  }
263 
264  return pipeline;
265 }
266 
267 // |PipelineLibrary|
268 PipelineFuture<PipelineDescriptor> PipelineLibraryGLES::GetPipeline(
269  PipelineDescriptor descriptor,
270  bool async,
271  bool threadsafe) {
272  if (auto found = pipelines_.find(descriptor); found != pipelines_.end()) {
273  return found->second;
274  }
275 
276  if (!reactor_) {
277  return {
278  descriptor,
279  RealizedFuture<std::shared_ptr<Pipeline<PipelineDescriptor>>>(nullptr)};
280  }
281 
282  auto vert_function = descriptor.GetEntrypointForStage(ShaderStage::kVertex);
283  auto frag_function = descriptor.GetEntrypointForStage(ShaderStage::kFragment);
284 
285  if (!vert_function || !frag_function) {
287  << "Could not find stage entrypoint functions in pipeline descriptor.";
288  return {
289  descriptor,
290  RealizedFuture<std::shared_ptr<Pipeline<PipelineDescriptor>>>(nullptr)};
291  }
292 
293  auto promise = std::make_shared<
294  std::promise<std::shared_ptr<Pipeline<PipelineDescriptor>>>>();
295  auto pipeline_future =
296  PipelineFuture<PipelineDescriptor>{descriptor, promise->get_future()};
297  pipelines_[descriptor] = pipeline_future;
298 
299  const auto result = reactor_->AddOperation([promise, //
300  weak_this = weak_from_this(), //
301  descriptor, //
302  vert_function, //
303  frag_function, //
304  threadsafe //
305  ](const ReactorGLES& reactor) {
306  promise->set_value(CreatePipeline(weak_this, descriptor, vert_function,
307  frag_function, threadsafe));
308  });
309  FML_CHECK(result);
310 
311  return pipeline_future;
312 }
313 
314 // |PipelineLibrary|
315 PipelineFuture<ComputePipelineDescriptor> PipelineLibraryGLES::GetPipeline(
316  ComputePipelineDescriptor descriptor,
317  bool async) {
318  auto promise = std::make_shared<
319  std::promise<std::shared_ptr<Pipeline<ComputePipelineDescriptor>>>>();
320  promise->set_value(nullptr);
321  return {descriptor, promise->get_future()};
322 }
323 
324 // |PipelineLibrary|
325 bool PipelineLibraryGLES::HasPipeline(const PipelineDescriptor& descriptor) {
326  return pipelines_.find(descriptor) != pipelines_.end();
327 }
328 
329 // |PipelineLibrary|
330 void PipelineLibraryGLES::RemovePipelinesWithEntryPoint(
331  std::shared_ptr<const ShaderFunction> function) {
332  Lock lock(programs_mutex_);
333 
334  PipelineMap::iterator it = pipelines_.begin();
335  while (it != pipelines_.end()) {
336  const PipelineDescriptor& desc = it->first;
337  if (desc.GetEntrypointForStage(function->GetStage())->IsEqual(*function)) {
338  const std::shared_ptr<const ShaderFunction>& vert_function =
339  desc.GetEntrypointForStage(ShaderStage::kVertex);
340  const std::shared_ptr<const ShaderFunction>& frag_function =
341  desc.GetEntrypointForStage(ShaderStage::kFragment);
342  ProgramKey program_key{vert_function, frag_function,
343  desc.GetSpecializationConstants()};
344  programs_.erase(program_key);
345  it = pipelines_.erase(it);
346  } else {
347  it++;
348  }
349  }
350 }
351 
352 // |PipelineLibrary|
353 PipelineLibraryGLES::~PipelineLibraryGLES() = default;
354 
355 const std::shared_ptr<ReactorGLES>& PipelineLibraryGLES::GetReactor() const {
356  return reactor_;
357 }
358 
359 std::shared_ptr<UniqueHandleGLES> PipelineLibraryGLES::GetProgramForKey(
360  const ProgramKey& key) {
361  Lock lock(programs_mutex_);
362  auto found = programs_.find(key);
363  if (found != programs_.end()) {
364  return found->second;
365  }
366  return nullptr;
367 }
368 
369 void PipelineLibraryGLES::SetProgramForKey(
370  const ProgramKey& key,
371  std::shared_ptr<UniqueHandleGLES> program) {
372  Lock lock(programs_mutex_);
373  programs_[key] = std::move(program);
374 }
375 
376 } // namespace impeller
PipelineLibraryGLES(const PipelineLibraryGLES &)=delete
The reactor attempts to make thread-safe usage of OpenGL ES easier to reason about.
Definition: reactor_gles.h:57
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...
const ProcTableGLES & GetProcTable() const
Get the OpenGL proc. table the reactor uses to manage handles.
std::vector< std::pair< uint64_t, std::unique_ptr< GenericRenderPipelineHandle > > > pipelines_
static void LogShaderCompilationFailure(const ProcTableGLES &gl, GLuint shader, std::string_view name, const fml::Mapping &source_mapping, ShaderStage stage)
static std::string GetShaderSource(const ProcTableGLES &gl, GLuint shader)
static std::string GetShaderInfoLog(const ProcTableGLES &gl, GLuint shader)
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: comparable.h:95
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:68
#define VALIDATION_LOG
Definition: validation.h:91