COMPMID-631: Merge branches/gles_compute branch

Last commit:
commit b25c5f68042b0c81bf611d59a1bb8535e1c42497
Author: Xinghang Zhou <xinghang.zhou@arm.com>
Date:   Wed Oct 25 18:48:10 2017 +0800

    Synced validation's tolerances of GCSoftmax from cl side

Change-Id: Ibe72054205c1c8721845d679a31af7ed0a7c5cf6
Reviewed-on: http://mpd-gerrit.cambridge.arm.com/93283
Reviewed-by: Anthony Barbier <anthony.barbier@arm.com>
Tested-by: Kaizen <jeremy.johnson+kaizengerrit@arm.com>
diff --git a/src/core/GLES_COMPUTE/GCKernelLibrary.cpp b/src/core/GLES_COMPUTE/GCKernelLibrary.cpp
new file mode 100644
index 0000000..fd362f1
--- /dev/null
+++ b/src/core/GLES_COMPUTE/GCKernelLibrary.cpp
@@ -0,0 +1,716 @@
+/*
+ * Copyright (c) 2017 ARM Limited.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include "arm_compute/core/GLES_COMPUTE/GCKernelLibrary.h"
+
+#include "arm_compute/core/Error.h"
+#include "arm_compute/core/Utils.h"
+
+#include <fstream>
+#include <iomanip>
+#include <iostream>
+#include <regex>
+#include <utility>
+#include <vector>
+
+using namespace arm_compute;
+
+GCProgram::GCProgram()
+    : _name(), _source()
+{
+}
+
+GCProgram::GCProgram(std::string name, std::string source)
+    : _name(std::move(name)), _source(std::move(source))
+{
+}
+
+GLuint GCProgram::link_program(GLuint shader)
+{
+    GLuint program = ARM_COMPUTE_GL_CHECK(glCreateProgram());
+
+    GLint   rvalue;
+    GLsizei length;
+
+    ARM_COMPUTE_GL_CHECK(glAttachShader(program, shader));
+    ARM_COMPUTE_GL_CHECK(glLinkProgram(program));
+    ARM_COMPUTE_GL_CHECK(glDetachShader(program, shader));
+    ARM_COMPUTE_GL_CHECK(glDeleteShader(shader));
+
+    // Check if there were some issues when linking the shader.
+    ARM_COMPUTE_GL_CHECK(glGetProgramiv(program, GL_LINK_STATUS, &rvalue));
+
+    if(rvalue == 0)
+    {
+        ARM_COMPUTE_GL_CHECK(glGetProgramiv(program, GL_INFO_LOG_LENGTH, &length));
+
+        std::vector<GLchar> log(length);
+        ARM_COMPUTE_GL_CHECK(glGetProgramInfoLog(program, length, nullptr, log.data()));
+        ARM_COMPUTE_ERROR("Error: Linker log:\n%s\n", log.data());
+
+        return 0;
+    }
+
+    ARM_COMPUTE_GL_CHECK(glUseProgram(program));
+
+    return program;
+}
+
+GLuint GCProgram::compile_shader(const std::string &build_options)
+{
+    GLuint shader = ARM_COMPUTE_GL_CHECK(glCreateShader(GL_COMPUTE_SHADER));
+
+    const char *src[]
+    {
+        "#version 310 es\n",
+        build_options.c_str(),
+        _source.c_str()
+    };
+
+    ARM_COMPUTE_GL_CHECK(glShaderSource(shader, sizeof(src) / sizeof(src[0]), src, nullptr));
+
+    ARM_COMPUTE_GL_CHECK(glCompileShader(shader));
+
+    // Check if there were any issues when compiling the shader
+    GLint   rvalue;
+    GLsizei length;
+
+    ARM_COMPUTE_GL_CHECK(glGetShaderiv(shader, GL_COMPILE_STATUS, &rvalue));
+
+    if(rvalue == 0)
+    {
+        ARM_COMPUTE_GL_CHECK(glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &length));
+
+        std::vector<GLchar> log(length);
+        ARM_COMPUTE_GL_CHECK(glGetShaderInfoLog(shader, length, nullptr, log.data()));
+
+#ifdef ARM_COMPUTE_DEBUG_ENABLED
+        std::istringstream ss(_source);
+        std::stringstream  output_stream;
+        std::string        line;
+        size_t             line_num = 1;
+
+        ARM_COMPUTE_LOG_INFO_MSG_WITH_FORMAT_CORE("GLES Shader build options:\n%s\n", build_options.c_str());
+        while(std::getline(ss, line, '\n'))
+        {
+            output_stream << std::setw(6) << line_num << ": " << line << std::endl;
+            line_num++;
+        }
+        ARM_COMPUTE_LOG_INFO_STREAM_CORE("GLES Shader source code:" << output_stream.rdbuf());
+#endif /* ARM_COMPUTE_DEBUG_ENABLED */
+
+        ARM_COMPUTE_ERROR("Error: Compiler log:\n%s\n", log.data());
+
+        return 0;
+    }
+
+    return shader;
+}
+
+GCKernel::GCKernel()
+    : _name(), _program(), _params(), _shader_params(), _shader_params_binding_point(), _shader_params_index(), _shader_params_size()
+{
+}
+
+GCKernel::GCKernel(std::string name, GLuint program)
+    : _name(std::move(name)),
+      _program(program),
+      _params(),
+      _shader_params(0),
+      _shader_params_binding_point(0),
+      _shader_params_index(0),
+      _shader_params_size(0)
+{
+    _params.clear();
+
+    ARM_COMPUTE_GL_CHECK(glGenBuffers(1, &_shader_params));
+
+    _shader_params_index = ARM_COMPUTE_GL_CHECK(glGetUniformBlockIndex(_program, _shader_params_name));
+    ARM_COMPUTE_ERROR_ON_MSG((_shader_params_index == GL_INVALID_INDEX), "Failed to get index of %s", _shader_params_name);
+    ARM_COMPUTE_GL_CHECK(glGetActiveUniformBlockiv(_program, _shader_params_index, GL_UNIFORM_BLOCK_DATA_SIZE, &_shader_params_size));
+    ARM_COMPUTE_ERROR_ON_MSG((_shader_params_size == 0), "Failed to get size of %s", _shader_params_name);
+}
+
+void GCKernel::cleanup()
+{
+    ARM_COMPUTE_GL_CHECK(glDeleteBuffers(1, &_shader_params));
+    ARM_COMPUTE_GL_CHECK(glBindBuffer(GL_UNIFORM_BUFFER, 0));
+    ARM_COMPUTE_GL_CHECK(glDeleteProgram(_program));
+    ARM_COMPUTE_GL_CHECK(glUseProgram(0));
+}
+
+void GCKernel::use()
+{
+    ARM_COMPUTE_GL_CHECK(glUseProgram(_program));
+}
+
+void GCKernel::unuse()
+{
+    ARM_COMPUTE_GL_CHECK(glUseProgram(0));
+}
+
+void GCKernel::update_shader_params()
+{
+    ARM_COMPUTE_ERROR_ON_MSG((_shader_params_size != (int)(_params.size() * sizeof(_params[0]))), "Params size (%d) is not equal to shader params block size (%d)", _params.size() * sizeof(_params[0]),
+                             _shader_params_size);
+
+    ARM_COMPUTE_GL_CHECK(glUniformBlockBinding(_program, _shader_params_index, _shader_params_binding_point));
+    ARM_COMPUTE_GL_CHECK(glBindBufferBase(GL_UNIFORM_BUFFER, _shader_params_binding_point, _shader_params));
+    ARM_COMPUTE_GL_CHECK(glBindBuffer(GL_UNIFORM_BUFFER, _shader_params));
+    ARM_COMPUTE_GL_CHECK(glBufferData(GL_UNIFORM_BUFFER, _shader_params_size, _params.data(), GL_DYNAMIC_DRAW));
+    ARM_COMPUTE_GL_CHECK(glBindBuffer(GL_UNIFORM_BUFFER, 0));
+}
+
+const std::map<std::string, std::string> GCKernelLibrary::_shader_program_map =
+{
+    { "absdiff", "absdiff.cs" },
+    { "col2im", "convolution_layer.cs" },
+    { "direct_convolution1x1", "direct_convolution1x1.cs" },
+    { "direct_convolution3x3", "direct_convolution3x3.cs" },
+    { "direct_convolution5x5", "direct_convolution5x5.cs" },
+    { "pooling_layer_2", "pooling_layer.cs" },
+    { "pooling_layer_3", "pooling_layer.cs" },
+    { "pooling_layer_7", "pooling_layer.cs" },
+    { "pooling_layer_3_optimized", "pooling_layer.cs" },
+    { "pooling_layer_n", "pooling_layer.cs" },
+    { "fill_image_borders_replicate", "fill_border.cs" },
+    { "fill_image_borders_constant", "fill_border.cs" },
+    { "gemm_accumulate_biases", "gemm.cs" },
+    { "gemm_interleave4x4", "gemm.cs" },
+    { "gemm_ma", "gemm.cs" },
+    { "gemm_mm_interleaved_transposed", "gemm.cs" },
+    { "gemm_mm_floating_point", "gemm.cs" },
+    { "gemm_transpose1x4", "gemm.cs" },
+    { "im2col_kernel3x3_padx0_pady0", "convolution_layer.cs" },
+    { "im2col_generic", "convolution_layer.cs" },
+    { "im2col_reduced", "convolution_layer.cs" },
+    { "transpose", "transpose.cs" },
+    { "activation_layer", "activation_layer.cs" },
+    { "softmax_layer_max", "softmax_layer.cs" },
+    { "softmax_layer_shift_exp_sum", "softmax_layer.cs" },
+    { "softmax_layer_norm", "softmax_layer.cs" },
+    { "pixelwise_mul_float", "pixelwise_mul_float.cs" },
+    { "normalization_layer", "normalization_layer.cs" },
+    { "batchnormalization_layer", "batchnormalization_layer.cs" },
+    { "concatenate_depth", "concatenate.cs" },
+    { "dropout", "dropout.cs" },
+};
+
+const std::map<std::string, std::string> GCKernelLibrary::_program_source_map =
+{
+#ifdef EMBEDDED_KERNELS
+    {
+        "absdiff.cs",
+#include "./cs_shaders/absdiff.csembed"
+    },
+    {
+        "convolution_layer.cs",
+#include "./cs_shaders/convolution_layer.csembed"
+    },
+    {
+        "direct_convolution1x1.cs",
+#include "./cs_shaders/direct_convolution1x1.csembed"
+    },
+    {
+        "direct_convolution3x3.cs",
+#include "./cs_shaders/direct_convolution3x3.csembed"
+    },
+    {
+        "direct_convolution5x5.cs",
+#include "./cs_shaders/direct_convolution5x5.csembed"
+    },
+    {
+        "pooling_layer.cs",
+#include "./cs_shaders/pooling_layer.csembed"
+    },
+    {
+        "fill_border.cs",
+#include "./cs_shaders/fill_border.csembed"
+    },
+    {
+        "gemm.cs",
+#include "./cs_shaders/gemm.csembed"
+    },
+    {
+        "transpose.cs",
+#include "./cs_shaders/transpose.csembed"
+    },
+    {
+        "activation_layer.cs",
+#include "./cs_shaders/activation_layer.csembed"
+    },
+    {
+        "softmax_layer.cs",
+#include "./cs_shaders/softmax_layer.csembed"
+    },
+    {
+        "pixelwise_mul_float.cs",
+#include "./cs_shaders/pixelwise_mul_float.csembed"
+    },
+    {
+        "normalization_layer.cs",
+#include "./cs_shaders/normalization_layer.csembed"
+    },
+    {
+        "batchnormalization_layer.cs",
+#include "./cs_shaders/batchnormalization_layer.csembed"
+    },
+    {
+        "concatenate.cs",
+#include "./cs_shaders/concatenate.csembed"
+    },
+    {
+        "dropout.cs",
+#include "./cs_shaders/dropout.csembed"
+    },
+#endif /* EMBEDDED_KERNELS */
+};
+
+GCKernelLibrary::GCKernelLibrary()
+    : _display(EGL_NO_DISPLAY), _context(EGL_NO_CONTEXT), _frame_buffer(0), _tex_rt(0), _own_context(false), _shader_path("./"), _programs_map(), _built_programs_map()
+{
+}
+
+GCKernelLibrary &GCKernelLibrary::get()
+{
+    static GCKernelLibrary _kernel_library;
+    return _kernel_library;
+}
+
+GCKernel GCKernelLibrary::create_kernel(const std::string &shader_name, const StringSet &build_options_set) const
+{
+    // Find which program contains the kernel
+    auto shader_program_it = _shader_program_map.find(shader_name);
+
+    if(_shader_program_map.end() == shader_program_it)
+    {
+        ARM_COMPUTE_ERROR("Shader %s not found in the GCKernelLibrary", shader_name.c_str());
+    }
+
+    // Check if the program has been built before with same build options.
+    const std::string program_name       = shader_program_it->second;
+    const std::string build_options      = stringify_set(build_options_set);
+    const std::string built_program_name = program_name + "_" + build_options;
+    auto              built_program_it   = _built_programs_map.find(built_program_name);
+
+    GCKernel kernel;
+
+    if(_built_programs_map.end() != built_program_it)
+    {
+        // If program has been built, retrieve to create kernel from it
+        kernel = built_program_it->second;
+        kernel.use();
+    }
+    else
+    {
+        GCProgram program = load_program(program_name);
+
+        std::string source_name = _shader_path + shader_program_it->second;
+
+        // load shader
+        GLuint shader = program.compile_shader(build_options);
+
+        // Build program
+        GLuint gles_program = program.link_program(shader);
+
+        // Create GCKernel
+        kernel = GCKernel(shader_name, gles_program);
+
+        // Add built program to internal map
+        _built_programs_map.emplace(built_program_name, kernel);
+    }
+
+    return kernel;
+}
+
+const std::string GCKernelLibrary::preprocess_shader(const std::string &shader_source) const
+{
+    enum class ParserStage
+    {
+        FIRST,
+        SKIP_COMMENTS = FIRST,
+        RESOLVE_INCLUDES,
+        SKIP_PREPROCESSOR_DIRECTIVES,
+        SEARCH_MACRO_DEFINITIONS,
+        EXPAND_MACRO_USES,
+        LAST
+    };
+
+    struct MacroDefinitionInfo
+    {
+        const std::vector<std::string> param_list;
+        const std::string              content;
+    };
+
+    // Found macro definitions so far
+    std::map<const std::string, const MacroDefinitionInfo> macro_definitions;
+
+    // Define a GLES compute shader parser function
+    std::function<std::string(const std::string &, ParserStage, int)> cs_parser;
+    cs_parser = [&](const std::string & src, ParserStage stage, int nested_level) -> std::string
+    {
+        std::string dst;
+
+        if(stage == ParserStage::LAST || std::regex_match(src, std::regex(R"(\s*)")))
+        {
+            return src;
+        }
+        auto next_stage = static_cast<ParserStage>(static_cast<int>(stage) + 1);
+
+        std::string search_pattern;
+        switch(stage)
+        {
+            case ParserStage::SKIP_COMMENTS:
+                search_pattern = R"((/\*([^*]|\n|(\*+([^*/]|\n)))*\*+/)|(//.*))";
+                break;
+            case ParserStage::RESOLVE_INCLUDES:
+                search_pattern = R"rgx((?:^|\n)[ \t]*#include "(.*)")rgx";
+                break;
+            case ParserStage::SKIP_PREPROCESSOR_DIRECTIVES:
+                search_pattern = R"((^|\n)[ \t]*(#ifdef|#ifndef|#if)[^\n]+)";
+                break;
+            case ParserStage::SEARCH_MACRO_DEFINITIONS:
+                search_pattern = R"((?:^|\n)[ \t]*#define[ \t]+(\w+)(?:\((\w+(?:[ \t]*,[ \t]*\w+)*)\))?(?: |\t|\\\n)*((?:(?:[^\\\n]|\\[^\n])*\\+\n)*(?:[ \t]*[^ \t\n]+)*)[ \t]*)";
+                break;
+            case ParserStage::EXPAND_MACRO_USES:
+            {
+                if(macro_definitions.empty())
+                {
+                    // Nothing to expand
+                    return src;
+                }
+                int i = 0;
+                for(auto &def : macro_definitions)
+                {
+                    if(i == 0)
+                    {
+                        search_pattern = R"((\b)" + def.first;
+                    }
+                    else
+                    {
+                        search_pattern += R"(\b|\b)" + def.first;
+                    }
+                    i++;
+                }
+                search_pattern += R"(\b))";
+                break;
+            }
+            default:
+                break;
+        }
+
+        std::regex  search_regex(search_pattern);
+        std::smatch match;
+        ptrdiff_t   parsed_pos = 0;
+        if(std::regex_search(src, match, search_regex))
+        {
+            // Pass the content before the match to the next stage
+            dst.append(cs_parser(src.substr(0, match.position()), next_stage, 0));
+            parsed_pos = match.position() + match.length();
+
+            // Deal with the matched content
+            switch(stage)
+            {
+                case ParserStage::RESOLVE_INCLUDES:
+                {
+                    // Replace with the included file contents
+                    // And parse the content from the first stage
+                    const std::string source_name = _shader_path + match.str(1);
+                    dst.append(cs_parser(read_file(source_name, false), ParserStage::FIRST, 0));
+                    break;
+                }
+                case ParserStage::SEARCH_MACRO_DEFINITIONS:
+                {
+                    std::regex                     params_regex(R"(\b\w+\b)");
+                    const std::string              macro_param_str = match.str(2);
+                    const std::vector<std::string> macro_param_list(
+                        std::sregex_token_iterator(macro_param_str.begin(),
+                                                   macro_param_str.end(),
+                                                   params_regex),
+                        std::sregex_token_iterator());
+
+                    const MacroDefinitionInfo info =
+                    {
+                        macro_param_list,
+                        match.str(3)
+                    };
+                    // Collect the macro definition data and not change the shader source
+                    macro_definitions.insert(std::pair<const std::string, const MacroDefinitionInfo>(match.str(1), info));
+                    dst.append(match.str());
+                    break;
+                }
+                case ParserStage::EXPAND_MACRO_USES:
+                {
+                    ptrdiff_t                args_str_length = 0;
+                    std::vector<std::string> args_list;
+
+                    // Walk through argument list, because the regular expression does NOT support nested parentheses
+                    size_t cur_args_str_pos = match.position() + match.length();
+                    if(src[cur_args_str_pos++] == '(')
+                    {
+                        int       nested_parentheses = 0;
+                        ptrdiff_t cur_arg_pos        = cur_args_str_pos;
+                        ptrdiff_t cur_arg_length     = 0;
+
+                        args_str_length++;
+                        while(src[cur_args_str_pos] != ')' || nested_parentheses != 0)
+                        {
+                            switch(src[cur_args_str_pos++])
+                            {
+                                case '(':
+                                    nested_parentheses++;
+                                    cur_arg_length++;
+                                    break;
+                                case ',':
+                                    if(nested_parentheses == 0)
+                                    {
+                                        args_list.push_back(src.substr(cur_arg_pos, cur_arg_length));
+                                        cur_arg_pos    = cur_args_str_pos;
+                                        cur_arg_length = 0;
+                                    }
+                                    else
+                                    {
+                                        cur_arg_length++;
+                                    }
+                                    break;
+                                case ' ':
+                                case '\t':
+                                    if(cur_arg_length == 0)
+                                    {
+                                        cur_arg_pos++;
+                                    }
+                                    else
+                                    {
+                                        cur_arg_length++;
+                                    }
+                                    break;
+                                case ')':
+                                    nested_parentheses--;
+                                // no break here!
+                                default:
+                                    cur_arg_length++;
+                                    break;
+                            }
+                            args_str_length++;
+                        }
+                        if(src[cur_args_str_pos] == ')' && nested_parentheses == 0)
+                        {
+                            args_list.push_back(src.substr(cur_arg_pos, cur_arg_length));
+                        }
+                        args_str_length++;
+                    }
+
+                    std::string                    expanded_content = match.str();
+                    const std::vector<std::string> macro_param_list = macro_definitions.at(match.str()).param_list;
+
+                    if((nested_level != 0 || !macro_param_list.empty()) && macro_param_list.size() == args_list.size())
+                    {
+                        parsed_pos += args_str_length;
+                        expanded_content = macro_definitions.at(match.str()).content;
+                        size_t i         = 0;
+                        for(auto &param_name : macro_param_list)
+                        {
+                            std::regex params_regex(R"(\b)" + param_name + R"(\b)");
+                            expanded_content.assign(std::regex_replace(expanded_content, params_regex, args_list[i]));
+                            ++i;
+                        }
+                        // Expand macro recursively
+                        expanded_content = cs_parser(expanded_content, stage, nested_level + 1);
+
+                        if(nested_level == 0)
+                        {
+                            const std::regex token_pasting_rgx = std::regex(R"(\b##\b)");
+                            if(std::regex_search(expanded_content, token_pasting_rgx))
+                            {
+                                // Remove token pasting operator "##"
+                                expanded_content.assign(std::regex_replace(expanded_content, std::regex(token_pasting_rgx), ""));
+                                // Trim trailing whitespace
+                                expanded_content.assign(std::regex_replace(expanded_content, std::regex(R"([ \t]*\\\n)"), "\n"));
+                            }
+                            else
+                            {
+                                // Do not expand the macro if the result does not have token pasting operator "##"
+                                expanded_content = src.substr(match.position(), match.length() + args_str_length);
+                            }
+                        }
+                    }
+                    dst.append(expanded_content);
+                    break;
+                }
+                case ParserStage::SKIP_COMMENTS:
+                case ParserStage::SKIP_PREPROCESSOR_DIRECTIVES:
+                default:
+                    dst.append(match.str());
+                    break;
+            }
+            next_stage = stage;
+        }
+        dst.append(cs_parser(src.substr(parsed_pos, src.length() - parsed_pos), next_stage, 0));
+
+        return dst;
+    };
+
+    return cs_parser(shader_source, ParserStage::FIRST, 0);
+}
+
+const GCProgram &GCKernelLibrary::load_program(const std::string &program_name) const
+{
+    const auto program_it = _programs_map.find(program_name);
+
+    if(program_it != _programs_map.end())
+    {
+        return program_it->second;
+    }
+
+    GCProgram program;
+
+#ifdef EMBEDDED_KERNELS
+    const auto program_source_it = _program_source_map.find(program_name);
+
+    if(_program_source_map.end() == program_source_it)
+    {
+        ARM_COMPUTE_ERROR("Embedded program for %s does not exist.", program_name.c_str());
+    }
+
+    // TODO(APPBROWSER-298): Do not call shader preprocessor here
+    //       We should do the preprocess at compile time
+    //       The preprocess_shader function is used for support "#include" directive and token pasting operator "##".
+    //       This job could be done at compile time by using a python script in order to get better performance at runtime.
+    //       BTW: We usually defined EMBEDDED_KERNELS in release build.
+    program = GCProgram(program_name, preprocess_shader(program_source_it->second));
+#else  /* EMBEDDED_KERNELS */
+    // Check for binary
+    std::string source_name = _shader_path + program_name;
+    if(std::ifstream(source_name).is_open())
+    {
+        program = GCProgram(program_name, preprocess_shader(read_file(source_name, false)));
+    }
+    else
+    {
+        ARM_COMPUTE_ERROR("Shader file %s does not exist.", source_name.c_str());
+    }
+#endif /* EMBEDDED_KERNELS */
+
+    // Insert program to program map
+    const auto new_program = _programs_map.emplace(program_name, std::move(program));
+
+    return new_program.first->second;
+}
+
+void GCKernelLibrary::setup_context()
+{
+    EGLBoolean res;
+    _display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+
+    ARM_COMPUTE_ERROR_ON_MSG(_display == EGL_NO_DISPLAY, "Failed to get display: 0x%x.", eglGetError());
+
+    res = eglInitialize(_display, nullptr, nullptr);
+
+    ARM_COMPUTE_ERROR_ON_MSG(res == EGL_FALSE, "Failed to initialize egl: 0x%x.", eglGetError());
+    ARM_COMPUTE_UNUSED(res);
+
+    const char *egl_extension_st = eglQueryString(_display, EGL_EXTENSIONS);
+    ARM_COMPUTE_ERROR_ON_MSG((strstr(egl_extension_st, "EGL_KHR_create_context") == nullptr), "Failed to query EGL_KHR_create_context");
+    ARM_COMPUTE_ERROR_ON_MSG((strstr(egl_extension_st, "EGL_KHR_surfaceless_context") == nullptr), "Failed to query EGL_KHR_surfaceless_context");
+    ARM_COMPUTE_UNUSED(egl_extension_st);
+
+    const EGLint config_attribs[] =
+    {
+        EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT_KHR,
+        EGL_NONE
+    };
+    EGLConfig cfg;
+    EGLint    count;
+
+    res = eglChooseConfig(_display, config_attribs, &cfg, 1, &count);
+
+    ARM_COMPUTE_ERROR_ON_MSG(res == EGL_FALSE, "Failed to choose config: 0x%x.", eglGetError());
+    ARM_COMPUTE_UNUSED(res);
+
+    res = eglBindAPI(EGL_OPENGL_ES_API);
+
+    ARM_COMPUTE_ERROR_ON_MSG(res == EGL_FALSE, "Failed to bind api: 0x%x.", eglGetError());
+
+    const EGLint attribs[] =
+    {
+        EGL_CONTEXT_CLIENT_VERSION, 3,
+        EGL_NONE
+    };
+    _context = eglCreateContext(_display,
+                                cfg,
+                                EGL_NO_CONTEXT,
+                                attribs);
+
+    ARM_COMPUTE_ERROR_ON_MSG(_context == EGL_NO_CONTEXT, "Failed to create context: 0x%x.", eglGetError());
+    ARM_COMPUTE_UNUSED(res);
+
+    res = eglMakeCurrent(_display, EGL_NO_SURFACE, EGL_NO_SURFACE, _context);
+
+    ARM_COMPUTE_ERROR_ON_MSG(res == EGL_FALSE, "Failed to make current: 0x%x.", eglGetError());
+    ARM_COMPUTE_UNUSED(res);
+}
+
+void GCKernelLibrary::setup_dummy_fbo()
+{
+    ARM_COMPUTE_GL_CHECK(glGenFramebuffers(1, &_frame_buffer));
+    ARM_COMPUTE_GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, _frame_buffer));
+    ARM_COMPUTE_GL_CHECK(glGenTextures(1, &_tex_rt));
+    ARM_COMPUTE_GL_CHECK(glBindTexture(GL_TEXTURE_2D, _tex_rt));
+    ARM_COMPUTE_GL_CHECK(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr));
+    ARM_COMPUTE_GL_CHECK(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _tex_rt, 0));
+}
+
+GCKernelLibrary::~GCKernelLibrary()
+{
+    for(auto &program : _built_programs_map)
+    {
+        static_cast<GCKernel>(program.second).cleanup();
+    }
+
+    ARM_COMPUTE_GL_CHECK(glBindTexture(GL_TEXTURE_2D, 0));
+    ARM_COMPUTE_GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, 0));
+    ARM_COMPUTE_GL_CHECK(glDeleteTextures(1, &_tex_rt));
+    ARM_COMPUTE_GL_CHECK(glDeleteFramebuffers(1, &_frame_buffer));
+
+    if(_own_context)
+    {
+        eglDestroyContext(_display, _context);
+        eglTerminate(_display);
+
+        _context = EGL_NO_CONTEXT;
+        _display = EGL_NO_DISPLAY;
+    }
+}
+
+std::string GCKernelLibrary::stringify_set(const StringSet &s) const
+{
+    std::string concat_set;
+
+    // Concatenate set
+    for(const auto &el : s)
+    {
+        concat_set += el + "\n";
+    }
+
+    return concat_set;
+}