• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2017-2019 Arm Limited.
3  *
4  * SPDX-License-Identifier: MIT
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to
8  * deal in the Software without restriction, including without limitation the
9  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10  * sell copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in all
14  * copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22  * SOFTWARE.
23  */
24 #include "arm_compute/core/GLES_COMPUTE/GCKernelLibrary.h"
25 
26 #include "arm_compute/core/Error.h"
27 #include "arm_compute/core/Utils.h"
28 
29 #include <fstream>
30 #include <iomanip>
31 #include <iostream>
32 #include <regex>
33 #include <utility>
34 #include <vector>
35 
36 using namespace arm_compute;
37 
GCProgram()38 GCProgram::GCProgram()
39     : _name(), _source()
40 {
41 }
42 
GCProgram(std::string name,std::string source)43 GCProgram::GCProgram(std::string name, std::string source)
44     : _name(std::move(name)), _source(std::move(source))
45 {
46 }
47 
link_program(GLuint shader)48 GLuint GCProgram::link_program(GLuint shader)
49 {
50     GLuint program = ARM_COMPUTE_GL_CHECK(glCreateProgram());
51 
52     GLint   rvalue;
53     GLsizei length;
54 
55     ARM_COMPUTE_GL_CHECK(glAttachShader(program, shader));
56     ARM_COMPUTE_GL_CHECK(glLinkProgram(program));
57     ARM_COMPUTE_GL_CHECK(glDetachShader(program, shader));
58     ARM_COMPUTE_GL_CHECK(glDeleteShader(shader));
59 
60     // Check if there were some issues when linking the shader.
61     ARM_COMPUTE_GL_CHECK(glGetProgramiv(program, GL_LINK_STATUS, &rvalue));
62 
63     if(rvalue == 0)
64     {
65         ARM_COMPUTE_GL_CHECK(glGetProgramiv(program, GL_INFO_LOG_LENGTH, &length));
66 
67         std::vector<GLchar> log(length);
68         ARM_COMPUTE_GL_CHECK(glGetProgramInfoLog(program, length, nullptr, log.data()));
69         ARM_COMPUTE_ERROR_VAR("Error: Linker log:\n%s\n", log.data());
70 
71         return 0;
72     }
73 
74     ARM_COMPUTE_GL_CHECK(glUseProgram(program));
75 
76     return program;
77 }
78 
compile_shader(const std::string & build_options)79 GLuint GCProgram::compile_shader(const std::string &build_options)
80 {
81     GLuint shader = ARM_COMPUTE_GL_CHECK(glCreateShader(GL_COMPUTE_SHADER));
82 
83     const char *src[]
84     {
85         "#version 310 es\n",
86         build_options.c_str(),
87         _source.c_str()
88     };
89 
90     ARM_COMPUTE_GL_CHECK(glShaderSource(shader, sizeof(src) / sizeof(src[0]), src, nullptr));
91 
92     ARM_COMPUTE_GL_CHECK(glCompileShader(shader));
93 
94     // Check if there were any issues when compiling the shader
95     GLint   rvalue;
96     GLsizei length;
97 
98     ARM_COMPUTE_GL_CHECK(glGetShaderiv(shader, GL_COMPILE_STATUS, &rvalue));
99 
100     if(rvalue == 0)
101     {
102         ARM_COMPUTE_GL_CHECK(glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &length));
103 
104         std::vector<GLchar> log(length);
105         ARM_COMPUTE_GL_CHECK(glGetShaderInfoLog(shader, length, nullptr, log.data()));
106 
107 #ifdef ARM_COMPUTE_DEBUG_ENABLED
108         std::istringstream ss(_source);
109         std::stringstream  output_stream;
110         std::string        line;
111         size_t             line_num = 1;
112 
113         ARM_COMPUTE_LOG_INFO_MSG_WITH_FORMAT_CORE("GLES Shader build options:\n%s\n", build_options.c_str());
114         while(std::getline(ss, line, '\n'))
115         {
116             output_stream << std::setw(6) << line_num << ": " << line << std::endl;
117             line_num++;
118         }
119         ARM_COMPUTE_LOG_INFO_STREAM_CORE("GLES Shader source code:\n"
120                                          << output_stream.rdbuf());
121 #endif /* ARM_COMPUTE_DEBUG_ENABLED */
122 
123         ARM_COMPUTE_ERROR_VAR("Error: Compiler log:\n%s\n", log.data());
124 
125         return 0;
126     }
127 
128     return shader;
129 }
130 
GCKernel()131 GCKernel::GCKernel()
132     : _name(), _program(), _shader_arguments(), _shader_params_ubo_name(), _shader_params_binding_point(), _shader_params_index(), _shader_params_size()
133 {
134 }
135 
136 // Add a default destructor in cpp file to workaround the free unallocated value issue on Android
~GCKernel()137 GCKernel::~GCKernel() // NOLINT
138 {
139 }
140 
GCKernel(std::string name,GLuint program)141 GCKernel::GCKernel(std::string name, GLuint program)
142     : _name(std::move(name)),
143       _program(program),
144       _shader_arguments(),
145       _shader_params_ubo_name(0),
146       _shader_params_binding_point(0),
147       _shader_params_index(0),
148       _shader_params_size(0)
149 {
150     _shader_arguments.clear();
151 
152     ARM_COMPUTE_GL_CHECK(glGenBuffers(1, &_shader_params_ubo_name));
153 
154     _shader_params_index = ARM_COMPUTE_GL_CHECK(glGetUniformBlockIndex(_program, _shader_params_name));
155     ARM_COMPUTE_ERROR_ON_MSG_VAR(_shader_params_index == GL_INVALID_INDEX, "Failed to get index of %s", _shader_params_name);
156     ARM_COMPUTE_GL_CHECK(glGetActiveUniformBlockiv(_program, _shader_params_index, GL_UNIFORM_BLOCK_DATA_SIZE, &_shader_params_size));
157     ARM_COMPUTE_ERROR_ON_MSG_VAR(_shader_params_size == 0, "Failed to get size of %s", _shader_params_name);
158 }
159 
cleanup()160 void GCKernel::cleanup()
161 {
162     ARM_COMPUTE_GL_CHECK(glDeleteBuffers(1, &_shader_params_ubo_name));
163     ARM_COMPUTE_GL_CHECK(glBindBuffer(GL_UNIFORM_BUFFER, 0));
164     ARM_COMPUTE_GL_CHECK(glDeleteProgram(_program));
165     ARM_COMPUTE_GL_CHECK(glUseProgram(0));
166 }
167 
use()168 void GCKernel::use()
169 {
170     ARM_COMPUTE_GL_CHECK(glUseProgram(_program));
171 }
172 
unuse()173 void GCKernel::unuse()
174 {
175     ARM_COMPUTE_GL_CHECK(glUseProgram(0));
176 }
177 
update_shader_params()178 void GCKernel::update_shader_params()
179 {
180     ARM_COMPUTE_ERROR_ON_MSG_VAR((_shader_params_size != (int)(_shader_arguments.size() * sizeof(_shader_arguments[0]))), "Arguments size (%zu) is not equal to shader params block size (%d)",
181                                  _shader_arguments.size() * sizeof(_shader_arguments[0]), _shader_params_size);
182 
183     ARM_COMPUTE_GL_CHECK(glUniformBlockBinding(_program, _shader_params_index, _shader_params_binding_point));
184     ARM_COMPUTE_GL_CHECK(glBindBufferBase(GL_UNIFORM_BUFFER, _shader_params_binding_point, _shader_params_ubo_name));
185     ARM_COMPUTE_GL_CHECK(glBindBuffer(GL_UNIFORM_BUFFER, _shader_params_ubo_name));
186     ARM_COMPUTE_GL_CHECK(glBufferData(GL_UNIFORM_BUFFER, _shader_params_size, _shader_arguments.data(), GL_DYNAMIC_DRAW));
187     ARM_COMPUTE_GL_CHECK(glBindBuffer(GL_UNIFORM_BUFFER, 0));
188 }
189 
190 const std::map<std::string, std::string> GCKernelLibrary::_shader_program_map =
191 {
192     { "absdiff", "absdiff.cs" },
193     { "tensorshift", "tensor_shift.cs" },
194     { "direct_convolution1x1", "direct_convolution1x1.cs" },
195     { "direct_convolution3x3", "direct_convolution3x3.cs" },
196     { "direct_convolution5x5", "direct_convolution5x5.cs" },
197     { "pooling_layer_2", "pooling_layer.cs" },
198     { "pooling_layer_3", "pooling_layer.cs" },
199     { "pooling_layer_7", "pooling_layer.cs" },
200     { "pooling_layer_3_optimized", "pooling_layer.cs" },
201     { "pooling_layer_n", "pooling_layer.cs" },
202     { "fill_image_borders_replicate", "fill_border.cs" },
203     { "fill_image_borders_constant", "fill_border.cs" },
204     { "gemm_accumulate_biases", "gemm.cs" },
205     { "gemm_interleave4x4", "gemm.cs" },
206     { "gemm_ma", "gemm.cs" },
207     { "gemm_mm_interleaved_transposed", "gemm.cs" },
208     { "gemm_mm_floating_point", "gemm.cs" },
209     { "gemm_transpose1x4", "gemm.cs" },
210     { "reshape_to_columns", "convolution_layer.cs" },
211     { "im2col_kernel3x3_padx0_pady0", "convolution_layer.cs" },
212     { "im2col_generic", "convolution_layer.cs" },
213     { "im2col_reduced", "convolution_layer.cs" },
214     { "col2im", "convolution_layer.cs" },
215     { "transpose", "transpose.cs" },
216     { "activation_layer", "activation_layer.cs" },
217     { "softmax_layer_max", "softmax_layer.cs" },
218     { "softmax_layer_shift_exp_sum", "softmax_layer.cs" },
219     { "softmax_layer_norm", "softmax_layer.cs" },
220     { "pixelwise_mul_float", "pixelwise_mul_float.cs" },
221     { "normalization_layer", "normalization_layer.cs" },
222     { "batchnormalization_layer", "batchnormalization_layer.cs" },
223     { "concatenate_depth", "concatenate.cs" },
224     { "dropout", "dropout.cs" },
225     { "normalize_planar_yuv_layer", "normalize_planar_yuv_layer.cs" },
226     { "scale_nearest_neighbour", "scale.cs" },
227     { "arithmetic_add", "arithmetic_add.cs" },
228     { "depthwise_convolution_3x3", "depthwise_convolution3x3.cs" },
229 };
230 
231 const std::map<std::string, std::string> GCKernelLibrary::_program_source_map =
232 {
233 #ifdef EMBEDDED_KERNELS
234     {
235         "helpers_cs.h",
236 #include "./cs_shaders/helpers_cs.hembed"
237     },
238     {
239         "activation_layer_helpers_cs.h",
240 #include "./cs_shaders/activation_layer_helpers_cs.hembed"
241     },
242     {
243         "absdiff.cs",
244 #include "./cs_shaders/absdiff.csembed"
245     },
246     {
247         "tensor_shift.cs",
248 #include "./cs_shaders/tensor_shift.csembed"
249     },
250     {
251         "convolution_layer.cs",
252 #include "./cs_shaders/convolution_layer.csembed"
253     },
254     {
255         "direct_convolution1x1.cs",
256 #include "./cs_shaders/direct_convolution1x1.csembed"
257     },
258     {
259         "direct_convolution3x3.cs",
260 #include "./cs_shaders/direct_convolution3x3.csembed"
261     },
262     {
263         "direct_convolution5x5.cs",
264 #include "./cs_shaders/direct_convolution5x5.csembed"
265     },
266     {
267         "pooling_layer.cs",
268 #include "./cs_shaders/pooling_layer.csembed"
269     },
270     {
271         "fill_border.cs",
272 #include "./cs_shaders/fill_border.csembed"
273     },
274     {
275         "gemm.cs",
276 #include "./cs_shaders/gemm.csembed"
277     },
278     {
279         "transpose.cs",
280 #include "./cs_shaders/transpose.csembed"
281     },
282     {
283         "activation_layer.cs",
284 #include "./cs_shaders/activation_layer.csembed"
285     },
286     {
287         "softmax_layer.cs",
288 #include "./cs_shaders/softmax_layer.csembed"
289     },
290     {
291         "pixelwise_mul_float.cs",
292 #include "./cs_shaders/pixelwise_mul_float.csembed"
293     },
294     {
295         "normalization_layer.cs",
296 #include "./cs_shaders/normalization_layer.csembed"
297     },
298     {
299         "batchnormalization_layer.cs",
300 #include "./cs_shaders/batchnormalization_layer.csembed"
301     },
302     {
303         "concatenate.cs",
304 #include "./cs_shaders/concatenate.csembed"
305     },
306     {
307         "dropout.cs",
308 #include "./cs_shaders/dropout.csembed"
309     },
310     {
311         "normalize_planar_yuv_layer.cs",
312 #include "./cs_shaders/normalize_planar_yuv_layer.csembed"
313     },
314     {
315         "scale.cs",
316 #include "./cs_shaders/scale.csembed"
317     },
318     {
319         "arithmetic_add.cs",
320 #include "./cs_shaders/arithmetic_add.csembed"
321     },
322     {
323         "depthwise_convolution3x3.cs",
324 #include "./cs_shaders/depthwise_convolution3x3.csembed"
325     },
326 #endif /* EMBEDDED_KERNELS */
327 };
328 
GCKernelLibrary()329 GCKernelLibrary::GCKernelLibrary()
330     : _display(EGL_NO_DISPLAY), _context(EGL_NO_CONTEXT), _frame_buffer(0), _tex_rt(0), _shader_path("./"), _programs_map(), _built_programs_map()
331 {
332 }
333 
get()334 GCKernelLibrary &GCKernelLibrary::get()
335 {
336     static GCKernelLibrary _kernel_library;
337     return _kernel_library;
338 }
339 
init(std::string shader_path,EGLDisplay dpy,EGLContext ctx)340 void GCKernelLibrary::init(std::string shader_path, EGLDisplay dpy, EGLContext ctx)
341 {
342     //TODO: deal with old display and context.
343     _shader_path = std::move(shader_path);
344 
345     _display = dpy;
346     _context = ctx;
347 
348     eglMakeCurrent(_display, EGL_NO_SURFACE, EGL_NO_SURFACE, _context);
349     setup_dummy_fbo();
350 }
351 
set_shader_path(const std::string & shader_path)352 void GCKernelLibrary::set_shader_path(const std::string &shader_path)
353 {
354     _shader_path = shader_path;
355 }
356 
set_context(EGLDisplay dpy,EGLContext ctx)357 void GCKernelLibrary::set_context(EGLDisplay dpy, EGLContext ctx)
358 {
359     //TODO: deal with old display and context.
360     _display = dpy;
361     _context = ctx;
362 
363     eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, ctx);
364     setup_dummy_fbo();
365 }
366 
create_kernel(const std::string & shader_name,const StringSet & build_options_set) const367 GCKernel GCKernelLibrary::create_kernel(const std::string &shader_name, const StringSet &build_options_set) const
368 {
369     // Find which program contains the kernel
370     auto shader_program_it = _shader_program_map.find(shader_name);
371 
372     if(_shader_program_map.end() == shader_program_it)
373     {
374         ARM_COMPUTE_ERROR_VAR("Shader %s not found in the GCKernelLibrary", shader_name.c_str());
375     }
376 
377     // Check if the program has been built before with same build options.
378     const std::string program_name       = shader_program_it->second;
379     const std::string build_options      = stringify_set(build_options_set);
380     const std::string built_program_name = program_name + "_" + build_options;
381     auto              built_program_it   = _built_programs_map.find(built_program_name);
382 
383     GCKernel kernel;
384 
385     if(_built_programs_map.end() != built_program_it)
386     {
387         // If program has been built, retrieve to create kernel from it
388         kernel = built_program_it->second;
389     }
390     else
391     {
392         GCProgram program = load_program(program_name);
393 
394         std::string source_name = _shader_path + shader_program_it->second;
395 
396         // load shader
397         GLuint shader = program.compile_shader(build_options);
398 
399         // Build program
400         GLuint gles_program = program.link_program(shader);
401 
402         // Create GCKernel
403         kernel = GCKernel(shader_name, gles_program);
404 
405         // Add built program to internal map
406         _built_programs_map.emplace(built_program_name, kernel);
407     }
408 
409     kernel.use();
410     kernel.clear_arguments();
411     // set shader params binding point
412     kernel.set_shader_params_binding_point(0);
413 
414     return kernel;
415 }
416 
preprocess_shader(const std::string & shader_source) const417 std::string GCKernelLibrary::preprocess_shader(const std::string &shader_source) const
418 {
419     enum class ParserStage
420     {
421         FIRST,
422         SKIP_COMMENTS = FIRST,
423         RESOLVE_INCLUDES,
424         LAST
425     };
426 
427     // Define a GLES compute shader parser function
428     std::function<std::string(const std::string &, ParserStage, int)> cs_parser;
429     cs_parser = [&](const std::string & src, ParserStage stage, int) -> std::string
430     {
431         std::string dst;
432 
433         if(stage == ParserStage::LAST || std::regex_match(src, std::regex(R"(\s*)")))
434         {
435             return src;
436         }
437         auto next_stage = static_cast<ParserStage>(static_cast<int>(stage) + 1);
438 
439         std::string search_pattern;
440         switch(stage)
441         {
442             case ParserStage::SKIP_COMMENTS:
443                 search_pattern = R"((/\*([^*]|\n|(\*+([^*/]|\n)))*\*+/)|(//.*))";
444                 break;
445             case ParserStage::RESOLVE_INCLUDES:
446                 search_pattern = R"rgx((?:^|\n)[ \t]*#include "(.*)")rgx";
447                 break;
448             default:
449                 break;
450         }
451 
452         std::regex  search_regex(search_pattern);
453         std::smatch match;
454         ptrdiff_t   parsed_pos = 0;
455         if(std::regex_search(src, match, search_regex))
456         {
457             // Pass the content before the match to the next stage
458             dst.append(cs_parser(src.substr(0, match.position()), next_stage, 0));
459             parsed_pos = match.position() + match.length();
460 
461             // Deal with the matched content
462             switch(stage)
463             {
464                 case ParserStage::RESOLVE_INCLUDES:
465                 {
466                     // Replace with the included file contents
467                     // And parse the content from the first stage
468                     const std::string source_name = _shader_path + match.str(1);
469                     dst.append(cs_parser(read_file(source_name, false), ParserStage::FIRST, 0));
470                     break;
471                 }
472                 case ParserStage::SKIP_COMMENTS:
473                 default:
474                     dst.append(match.str());
475                     break;
476             }
477             next_stage = stage;
478         }
479         dst.append(cs_parser(src.substr(parsed_pos, src.length() - parsed_pos), next_stage, 0));
480 
481         return dst;
482     };
483 
484     return cs_parser(shader_source, ParserStage::FIRST, 0);
485 }
486 
load_program(const std::string & program_name) const487 const GCProgram &GCKernelLibrary::load_program(const std::string &program_name) const
488 {
489     const auto program_it = _programs_map.find(program_name);
490 
491     if(program_it != _programs_map.end())
492     {
493         return program_it->second;
494     }
495 
496     GCProgram program;
497 
498 #ifdef EMBEDDED_KERNELS
499     const auto program_source_it = _program_source_map.find(program_name);
500 
501     if(_program_source_map.end() == program_source_it)
502     {
503         ARM_COMPUTE_ERROR_VAR("Embedded program for %s does not exist.", program_name.c_str());
504     }
505 
506     program = GCProgram(program_name, program_source_it->second);
507 #else  /* EMBEDDED_KERNELS */
508     // Check for binary
509     std::string source_name = _shader_path + program_name;
510     if(std::ifstream(source_name).is_open())
511     {
512         program = GCProgram(program_name, preprocess_shader(read_file(source_name, false)));
513     }
514     else
515     {
516         ARM_COMPUTE_ERROR_VAR("Shader file %s does not exist.", source_name.c_str());
517     }
518 #endif /* EMBEDDED_KERNELS */
519 
520     // Insert program to program map
521     const auto new_program = _programs_map.emplace(program_name, std::move(program));
522 
523     return new_program.first->second;
524 }
525 
setup_dummy_fbo()526 void GCKernelLibrary::setup_dummy_fbo()
527 {
528     ARM_COMPUTE_GL_CHECK(glGenFramebuffers(1, &_frame_buffer));
529     ARM_COMPUTE_GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, _frame_buffer));
530     ARM_COMPUTE_GL_CHECK(glGenTextures(1, &_tex_rt));
531     ARM_COMPUTE_GL_CHECK(glBindTexture(GL_TEXTURE_2D, _tex_rt));
532     ARM_COMPUTE_GL_CHECK(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr));
533     ARM_COMPUTE_GL_CHECK(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _tex_rt, 0));
534 }
535 
~GCKernelLibrary()536 GCKernelLibrary::~GCKernelLibrary()
537 {
538     for(auto &program : _built_programs_map)
539     {
540         static_cast<GCKernel>(program.second).cleanup();
541     }
542 
543     ARM_COMPUTE_GL_CHECK(glBindTexture(GL_TEXTURE_2D, 0));
544     ARM_COMPUTE_GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, 0));
545     ARM_COMPUTE_GL_CHECK(glDeleteTextures(1, &_tex_rt));
546     ARM_COMPUTE_GL_CHECK(glDeleteFramebuffers(1, &_frame_buffer));
547 }
548 
stringify_set(const StringSet & s) const549 std::string GCKernelLibrary::stringify_set(const StringSet &s) const
550 {
551     std::string concat_set;
552 
553     // Concatenate set
554     for(const auto &el : s)
555     {
556         concat_set += el + "\n";
557     }
558 
559     return concat_set;
560 }
561