Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
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
11#include "fml/closure.h"
16
17namespace impeller {
18
20 std::shared_ptr<ReactorGLES> reactor,
21 std::shared_ptr<fml::BasicTaskRunner> io_task_runner)
22 : reactor_(std::move(reactor)),
23 compile_queue_(
24 PipelineCompileQueueGLES::Create(std::move(io_task_runner))) {}
25
26static std::string GetShaderInfoLog(const ProcTableGLES& gl, GLuint shader) {
27 GLint log_length = 0;
28 gl.GetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length);
29 if (log_length == 0) {
30 return "";
31 }
32 auto log_buffer =
33 reinterpret_cast<char*>(std::calloc(log_length, sizeof(char)));
34 gl.GetShaderInfoLog(shader, log_length, &log_length, log_buffer);
35 auto log_string = std::string(log_buffer, log_length);
36 std::free(log_buffer);
37 return log_string;
38}
39
40static std::string GetShaderSource(const ProcTableGLES& gl, GLuint shader) {
41 // Arbitrarily chosen size that should be larger than most shaders.
42 // Since this only fires on compilation errors the performance shouldn't
43 // matter.
44 auto data = static_cast<char*>(malloc(10240));
45 GLsizei length;
46 gl.GetShaderSource(shader, 10240, &length, data);
47
48 auto result = std::string{data, static_cast<size_t>(length)};
49 free(data);
50 return result;
51}
52
54 GLuint shader,
55 std::string_view name,
56 const fml::Mapping& source_mapping,
57 ShaderStage stage) {
58 std::stringstream stream;
59 stream << "Failed to compile ";
60 switch (stage) {
61 case ShaderStage::kUnknown:
62 stream << "unknown";
63 break;
64 case ShaderStage::kVertex:
65 stream << "vertex";
66 break;
67 case ShaderStage::kFragment:
68 stream << "fragment";
69 break;
70 case ShaderStage::kCompute:
71 stream << "compute";
72 break;
73 }
74 stream << " shader for '" << name << "' with error:" << std::endl;
75 stream << GetShaderInfoLog(gl, shader) << std::endl;
76 stream << "Shader source was: " << std::endl;
77 stream << GetShaderSource(gl, shader) << std::endl;
78 VALIDATION_LOG << stream.str();
79}
80
81static bool LinkProgram(
82 const ReactorGLES& reactor,
83 const std::shared_ptr<PipelineGLES>& pipeline,
84 const std::shared_ptr<const ShaderFunction>& vert_function,
85 const std::shared_ptr<const ShaderFunction>& frag_function) {
86 TRACE_EVENT0("impeller", __FUNCTION__);
87
88 const auto& descriptor = pipeline->GetDescriptor();
89
90 auto vert_mapping =
91 ShaderFunctionGLES::Cast(*vert_function).GetSourceMapping();
92 auto frag_mapping =
93 ShaderFunctionGLES::Cast(*frag_function).GetSourceMapping();
94
95 const auto& gl = reactor.GetProcTable();
96
97 auto vert_shader = gl.CreateShader(GL_VERTEX_SHADER);
98 auto frag_shader = gl.CreateShader(GL_FRAGMENT_SHADER);
99
100 if (vert_shader == 0 || frag_shader == 0) {
101 VALIDATION_LOG << "Could not create shader handles.";
102 return false;
103 }
104
105 gl.SetDebugLabel(DebugResourceType::kShader, vert_shader,
106 std::format("{} Vertex Shader", descriptor.GetLabel()));
107 gl.SetDebugLabel(DebugResourceType::kShader, frag_shader,
108 std::format("{} Fragment Shader", descriptor.GetLabel()));
109
110 fml::ScopedCleanupClosure delete_vert_shader(
111 [&gl, vert_shader]() { gl.DeleteShader(vert_shader); });
112 fml::ScopedCleanupClosure delete_frag_shader(
113 [&gl, frag_shader]() { gl.DeleteShader(frag_shader); });
114
115 gl.ShaderSourceMapping(vert_shader, *vert_mapping,
116 descriptor.GetSpecializationConstants());
117 gl.ShaderSourceMapping(frag_shader, *frag_mapping,
118 descriptor.GetSpecializationConstants());
119
120 gl.CompileShader(vert_shader);
121 gl.CompileShader(frag_shader);
122
123 GLint vert_status = GL_FALSE;
124 GLint frag_status = GL_FALSE;
125
126 gl.GetShaderiv(vert_shader, GL_COMPILE_STATUS, &vert_status);
127 gl.GetShaderiv(frag_shader, GL_COMPILE_STATUS, &frag_status);
128
129 if (vert_status != GL_TRUE) {
130 LogShaderCompilationFailure(gl, vert_shader, descriptor.GetLabel(),
131 *vert_mapping, ShaderStage::kVertex);
132 return false;
133 }
134
135 if (frag_status != GL_TRUE) {
136 LogShaderCompilationFailure(gl, frag_shader, descriptor.GetLabel(),
137 *frag_mapping, ShaderStage::kFragment);
138 return false;
139 }
140
141 auto program = reactor.GetGLHandle(pipeline->GetProgramHandle());
142 if (!program.has_value()) {
143 VALIDATION_LOG << "Could not get program handle from reactor.";
144 return false;
145 }
146
147 gl.AttachShader(*program, vert_shader);
148 gl.AttachShader(*program, frag_shader);
149
150 fml::ScopedCleanupClosure detach_vert_shader(
151 [&gl, program = *program, vert_shader]() {
152 gl.DetachShader(program, vert_shader);
153 });
154 fml::ScopedCleanupClosure detach_frag_shader(
155 [&gl, program = *program, frag_shader]() {
156 gl.DetachShader(program, frag_shader);
157 });
158
159 for (const auto& stage_input :
160 descriptor.GetVertexDescriptor()->GetStageInputs()) {
161 gl.BindAttribLocation(*program, //
162 static_cast<GLuint>(stage_input.location), //
163 stage_input.name //
164 );
165 }
166
167 gl.LinkProgram(*program);
168
169 GLint link_status = GL_FALSE;
170 gl.GetProgramiv(*program, GL_LINK_STATUS, &link_status);
171
172 if (link_status != GL_TRUE) {
173 VALIDATION_LOG << "Could not link shader program: "
174 << gl.GetProgramInfoLogString(*program)
175 << "\nVertex Shader:\n"
176 << GetShaderSource(gl, vert_shader) << "\nFragment Shader:\n"
177 << GetShaderSource(gl, frag_shader);
178 return false;
179 }
180 return true;
181}
182
183// |PipelineLibrary|
184bool PipelineLibraryGLES::IsValid() const {
185 return reactor_ != nullptr;
186}
187
188std::shared_ptr<PipelineGLES> PipelineLibraryGLES::CreatePipeline(
189 const std::weak_ptr<PipelineLibrary>& weak_library,
190 const PipelineDescriptor& desc,
191 const std::shared_ptr<const ShaderFunction>& vert_function,
192 const std::shared_ptr<const ShaderFunction>& frag_function,
193 bool threadsafe) {
194 auto strong_library = weak_library.lock();
195
196 if (!strong_library) {
197 VALIDATION_LOG << "Library was collected before a pending pipeline "
198 "creation could finish.";
199 return nullptr;
200 }
201
202 auto& library = PipelineLibraryGLES::Cast(*strong_library);
203
204 const auto& reactor = library.GetReactor();
205
206 if (!reactor) {
207 return nullptr;
208 }
209
210 auto program_key = ProgramKey{vert_function, frag_function,
211 desc.GetSpecializationConstants()};
212
213 auto cached_program = library.GetProgramForKey(program_key);
214
215 const auto has_cached_program = !!cached_program;
216
217 std::shared_ptr<UniqueHandleGLES> program_handle = nullptr;
218 if (has_cached_program) {
219 program_handle = std::move(cached_program);
220 } else {
221 program_handle = threadsafe ? std::make_shared<UniqueHandleGLES>(
222 reactor, HandleType::kProgram)
223 : std::make_shared<UniqueHandleGLES>(
224 UniqueHandleGLES::MakeUntracked(
226 }
227
228 auto pipeline = std::shared_ptr<PipelineGLES>(
229 new PipelineGLES(reactor, //
230 weak_library, //
231 desc, //
232 std::move(program_handle)));
233
234 auto program = reactor->GetGLHandle(pipeline->GetProgramHandle());
235
236 if (!program.has_value()) {
237 VALIDATION_LOG << "Could not obtain program handle.";
238 return nullptr;
239 }
240
241 const auto link_result = !has_cached_program ? LinkProgram(*reactor, //
242 pipeline, //
243 vert_function, //
244 frag_function //
245 )
246 : true;
247
248 if (!link_result) {
249 VALIDATION_LOG << "Could not link pipeline program.";
250 return nullptr;
251 }
252
253 if (!pipeline->BuildVertexDescriptor(reactor->GetProcTable(),
254 program.value())) {
255 VALIDATION_LOG << "Could not build pipeline vertex descriptors.";
256 return nullptr;
257 }
258
259 if (!pipeline->IsValid()) {
260 VALIDATION_LOG << "Pipeline validation checks failed.";
261 return nullptr;
262 }
263
264 if (!has_cached_program) {
265 library.SetProgramForKey(program_key, pipeline->GetSharedHandle());
266 }
267
268 return pipeline;
269}
270
271// |PipelineLibrary|
272PipelineFuture<PipelineDescriptor> PipelineLibraryGLES::GetPipeline(
273 PipelineDescriptor descriptor,
274 bool async,
275 bool threadsafe) {
276 if (auto found = pipelines_.find(descriptor); found != pipelines_.end()) {
277 return found->second;
278 }
279
280 if (!reactor_) {
281 return {
282 descriptor,
283 RealizedFuture<std::shared_ptr<Pipeline<PipelineDescriptor>>>(nullptr)};
284 }
285
286 auto vert_function = descriptor.GetEntrypointForStage(ShaderStage::kVertex);
287 auto frag_function = descriptor.GetEntrypointForStage(ShaderStage::kFragment);
288
289 if (!vert_function || !frag_function) {
291 << "Could not find stage entrypoint functions in pipeline descriptor.";
292 return {
293 descriptor,
294 RealizedFuture<std::shared_ptr<Pipeline<PipelineDescriptor>>>(nullptr)};
295 }
296
297 auto promise = std::make_shared<
298 std::promise<std::shared_ptr<Pipeline<PipelineDescriptor>>>>();
299 auto pipeline_future =
300 PipelineFuture<PipelineDescriptor>{descriptor, promise->get_future()};
301 pipelines_[descriptor] = pipeline_future;
302
303 std::weak_ptr<PipelineLibrary> weak_this = weak_from_this();
304 std::shared_ptr<ReactorGLES> reactor = reactor_;
305 auto generation_task = [promise, weak_this, descriptor, vert_function,
306 frag_function, threadsafe, reactor]() {
307 auto thiz = weak_this.lock();
308 if (!thiz) {
309 promise->set_value(nullptr);
310 return;
311 }
312 const bool result = reactor->AddOperation([promise, //
313 weak_this, //
314 descriptor, //
315 vert_function, //
316 frag_function, //
317 threadsafe //
318 ](const ReactorGLES& reactor) {
319 promise->set_value(CreatePipeline(weak_this, descriptor, vert_function,
320 frag_function, threadsafe));
321 });
322 FML_CHECK(result);
323 };
324
325 if (async && compile_queue_) {
326 compile_queue_->PostJobForDescriptor(descriptor,
327 std::move(generation_task));
328 } else {
329 generation_task();
330 }
331
332 return pipeline_future;
333}
334
335// |PipelineLibrary|
336PipelineFuture<ComputePipelineDescriptor> PipelineLibraryGLES::GetPipeline(
337 ComputePipelineDescriptor descriptor,
338 bool async) {
339 auto promise = std::make_shared<
340 std::promise<std::shared_ptr<Pipeline<ComputePipelineDescriptor>>>>();
341 promise->set_value(nullptr);
342 return {descriptor, promise->get_future()};
343}
344
345// |PipelineLibrary|
346bool PipelineLibraryGLES::HasPipeline(const PipelineDescriptor& descriptor) {
347 return pipelines_.find(descriptor) != pipelines_.end();
348}
349
350// |PipelineLibrary|
351void PipelineLibraryGLES::RemovePipelinesWithEntryPoint(
352 std::shared_ptr<const ShaderFunction> function) {
353 Lock lock(programs_mutex_);
354
355 PipelineMap::iterator it = pipelines_.begin();
356 while (it != pipelines_.end()) {
357 const PipelineDescriptor& desc = it->first;
358 if (desc.GetEntrypointForStage(function->GetStage())->IsEqual(*function)) {
359 const std::shared_ptr<const ShaderFunction>& vert_function =
360 desc.GetEntrypointForStage(ShaderStage::kVertex);
361 const std::shared_ptr<const ShaderFunction>& frag_function =
362 desc.GetEntrypointForStage(ShaderStage::kFragment);
363 ProgramKey program_key{vert_function, frag_function,
364 desc.GetSpecializationConstants()};
365 programs_.erase(program_key);
366 it = pipelines_.erase(it);
367 } else {
368 it++;
369 }
370 }
371}
372
373// |PipelineLibrary|
374PipelineLibraryGLES::~PipelineLibraryGLES() = default;
375
376const std::shared_ptr<ReactorGLES>& PipelineLibraryGLES::GetReactor() const {
377 return reactor_;
378}
379
380std::shared_ptr<UniqueHandleGLES> PipelineLibraryGLES::GetProgramForKey(
381 const ProgramKey& key) {
382 Lock lock(programs_mutex_);
383 auto found = programs_.find(key);
384 if (found != programs_.end()) {
385 return found->second;
386 }
387 return nullptr;
388}
389
390void PipelineLibraryGLES::SetProgramForKey(
391 const ProgramKey& key,
392 std::shared_ptr<UniqueHandleGLES> program) {
393 Lock lock(programs_mutex_);
394 programs_[key] = std::move(program);
395}
396
397PipelineCompileQueue* PipelineLibraryGLES::GetPipelineCompileQueue() const {
398 return compile_queue_.get();
399}
400
401} // namespace impeller
Wraps a closure that is invoked in the destructor unless released by the caller.
Definition closure.h:32
PipelineLibraryGLES(const PipelineLibraryGLES &)=delete
The reactor attempts to make thread-safe usage of OpenGL ES easier to reason about.
std::vector< std::pair< uint64_t, std::unique_ptr< GenericRenderPipelineHandle > > > pipelines_
#define FML_CHECK(condition)
Definition logging.h:104
Dart_NativeFunction function
Definition fuchsia.cc:51
const char * name
Definition fuchsia.cc:50
size_t length
ScopedObject< Object > Create(CtorArgs &&... args)
Definition object.h:161
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 ref_ptr.h:261
std::shared_ptr< ReactorGLES > reactor
std::shared_ptr< PipelineGLES > pipeline
#define TRACE_EVENT0(category_group, name)
#define VALIDATION_LOG
Definition validation.h:91