| 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
5 #include "impeller/renderer/backend/gles/pipeline_library_gles.h"
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"
13 #include "impeller/renderer/backend/gles/pipeline_gles.h"
14 #include "impeller/renderer/backend/gles/shader_function_gles.h"
15 #include "impeller/renderer/pipeline_descriptor.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
49 static void LogShaderCompilationFailure(const ProcTableGLES& gl,
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) {
286 VALIDATION_LOG
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
impeller::PipelineLibraryGLES::PipelineLibraryGLES
PipelineLibraryGLES(const PipelineLibraryGLES &)=delete
impeller::ProcTableGLES
Definition: proc_table_gles.h:283
impeller::ReactorGLES
The reactor attempts to make thread-safe usage of OpenGL ES easier to reason about.
Definition: reactor_gles.h:57
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:151
impeller::ReactorGLES::GetProcTable
const ProcTableGLES & GetProcTable() const
Get the OpenGL proc. table the reactor uses to manage handles.
Definition: reactor_gles.cc:122
pipelines_
std::vector< std::pair< uint64_t, std::unique_ptr< GenericRenderPipelineHandle > > > pipelines_
Definition: content_context.cc:80
impeller
Definition: allocation.cc:12
impeller::LogShaderCompilationFailure
static void LogShaderCompilationFailure(const ProcTableGLES &gl, GLuint shader, std::string_view 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::ShaderStage
ShaderStage
Definition: shader_types.h:22
impeller::GetShaderInfoLog
static std::string GetShaderInfoLog(const ProcTableGLES &gl, GLuint shader)
Definition: pipeline_library_gles.cc:22
impeller::HandleType
HandleType
Definition: handle_gles.h:18
impeller::HandleType::kProgram
@ kProgram
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
std
Definition: comparable.h:95
pipeline_descriptor.h
pipeline_gles.h
pipeline_library_gles.h
promise.h
shader_function_gles.h
data
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:69
VALIDATION_LOG
#define VALIDATION_LOG
Definition: validation.h:91