From 7068f9900d136312318ff430aef588b14e0c87ad Mon Sep 17 00:00:00 2001 From: Anthony Barbier Date: Thu, 26 Oct 2017 15:23:08 +0100 Subject: COMPMID-631: Merge branches/gles_compute branch Last commit: commit b25c5f68042b0c81bf611d59a1bb8535e1c42497 Author: Xinghang Zhou 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 Tested-by: Kaizen --- src/core/GLES_COMPUTE/GCKernelLibrary.cpp | 716 ++++++++++++++++++++++++++++++ 1 file changed, 716 insertions(+) create mode 100644 src/core/GLES_COMPUTE/GCKernelLibrary.cpp (limited to 'src/core/GLES_COMPUTE/GCKernelLibrary.cpp') diff --git a/src/core/GLES_COMPUTE/GCKernelLibrary.cpp b/src/core/GLES_COMPUTE/GCKernelLibrary.cpp new file mode 100644 index 0000000000..fd362f1665 --- /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 +#include +#include +#include +#include +#include + +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 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 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 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 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 param_list; + const std::string content; + }; + + // Found macro definitions so far + std::map macro_definitions; + + // Define a GLES compute shader parser function + std::function 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(static_cast(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 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(match.str(1), info)); + dst.append(match.str()); + break; + } + case ParserStage::EXPAND_MACRO_USES: + { + ptrdiff_t args_str_length = 0; + std::vector 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 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 ¶m_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(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; +} -- cgit v1.2.1