diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index e01e277e8fda0..07202c2899ce0 100755 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -1215,6 +1215,9 @@ FILE: ../../../flutter/shell/platform/glfw/platform_handler.h FILE: ../../../flutter/shell/platform/glfw/public/flutter_glfw.h FILE: ../../../flutter/shell/platform/glfw/text_input_plugin.cc FILE: ../../../flutter/shell/platform/glfw/text_input_plugin.h +FILE: ../../../flutter/shell/platform/linux/egl_utils.cc +FILE: ../../../flutter/shell/platform/linux/egl_utils.h +FILE: ../../../flutter/shell/platform/linux/egl_utils_test.cc FILE: ../../../flutter/shell/platform/linux/fl_basic_message_channel.cc FILE: ../../../flutter/shell/platform/linux/fl_basic_message_channel_test.cc FILE: ../../../flutter/shell/platform/linux/fl_binary_codec.cc diff --git a/shell/platform/linux/BUILD.gn b/shell/platform/linux/BUILD.gn index 08b81bb6a10c5..3029ba6e8eaba 100644 --- a/shell/platform/linux/BUILD.gn +++ b/shell/platform/linux/BUILD.gn @@ -76,6 +76,7 @@ source_set("flutter_linux_sources") { configs += [ "//flutter/shell/platform/linux/config:gtk" ] sources = [ + "egl_utils.cc", "fl_basic_message_channel.cc", "fl_binary_codec.cc", "fl_binary_messenger.cc", @@ -138,6 +139,7 @@ executable("flutter_linux_unittests") { testonly = true sources = [ + "egl_utils_test.cc", "fl_basic_message_channel_test.cc", "fl_binary_codec_test.cc", "fl_binary_messenger_test.cc", diff --git a/shell/platform/linux/egl_utils.cc b/shell/platform/linux/egl_utils.cc new file mode 100644 index 0000000000000..f859fb55c5e7c --- /dev/null +++ b/shell/platform/linux/egl_utils.cc @@ -0,0 +1,282 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "egl_utils.h" + +#include + +// Converts an EGL decimal value to a string. +static gchar* egl_decimal_to_string(EGLint value) { + return g_strdup_printf("%d", value); +} + +// Converts an EGL hexadecimal value to a string. +static gchar* egl_hexadecimal_to_string(EGLint value) { + return g_strdup_printf("0x%x", value); +} + +// Converts an EGL enumerated value to a string. +static gchar* egl_enum_to_string(EGLint value) { + if (value == EGL_FALSE) + return g_strdup("EGL_FALSE"); + else if (value == EGL_LUMINANCE_BUFFER) + return g_strdup("EGL_LUMINANCE_BUFFER"); + else if (value == EGL_NONE) + return g_strdup("EGL_NONE"); + else if (value == EGL_NON_CONFORMANT_CONFIG) + return g_strdup("EGL_NON_CONFORMANT_CONFIG"); + else if (value == EGL_RGB_BUFFER) + return g_strdup("EGL_RGB_BUFFER"); + else if (value == EGL_SLOW_CONFIG) + return g_strdup("EGL_SLOW_CONFIG"); + else if (value == EGL_TRANSPARENT_RGB) + return g_strdup("EGL_TRANSPARENT_RGB"); + else if (value == EGL_TRUE) + return g_strdup("EGL_TRUE"); + else + return nullptr; +} + +// Ensures the given bit is not set in a bitfield. Returns TRUE if that bit was +// cleared. +static gboolean clear_bit(EGLint* field, EGLint bit) { + if ((*field & bit) == 0) + return FALSE; + + *field ^= bit; + return TRUE; +} + +// Converts an EGL renderable type bitfield to a string. +static gchar* egl_renderable_type_to_string(EGLint value) { + EGLint v = value; + g_autoptr(GPtrArray) strings = g_ptr_array_new_with_free_func(g_free); + if (clear_bit(&v, EGL_OPENGL_ES_BIT)) + g_ptr_array_add(strings, g_strdup("EGL_OPENGL_ES_BIT")); + if (clear_bit(&v, EGL_OPENVG_BIT)) + g_ptr_array_add(strings, g_strdup("EGL_OPENVG_BIT")); + if (clear_bit(&v, EGL_OPENGL_ES2_BIT)) + g_ptr_array_add(strings, g_strdup("EGL_OPENGL_ES2_BIT")); + if (clear_bit(&v, EGL_OPENGL_BIT)) + g_ptr_array_add(strings, g_strdup("EGL_OPENGL_BIT")); + if (clear_bit(&v, EGL_OPENGL_ES3_BIT)) + g_ptr_array_add(strings, g_strdup("EGL_OPENGL_ES3_BIT")); + if (v != 0) + g_ptr_array_add(strings, egl_hexadecimal_to_string(v)); + g_ptr_array_add(strings, nullptr); + + return g_strjoinv("|", reinterpret_cast(strings->pdata)); +} + +// Converts an EGL surface type bitfield to a string. +static gchar* egl_surface_type_to_string(EGLint value) { + EGLint v = value; + g_autoptr(GPtrArray) strings = g_ptr_array_new_with_free_func(g_free); + if (clear_bit(&v, EGL_PBUFFER_BIT)) + g_ptr_array_add(strings, g_strdup("EGL_PBUFFER_BIT")); + if (clear_bit(&v, EGL_PIXMAP_BIT)) + g_ptr_array_add(strings, g_strdup("EGL_PIXMAP_BIT")); + if (clear_bit(&v, EGL_WINDOW_BIT)) + g_ptr_array_add(strings, g_strdup("EGL_WINDOW_BIT")); + if (v != 0) + g_ptr_array_add(strings, egl_hexadecimal_to_string(v)); + g_ptr_array_add(strings, nullptr); + + return g_strjoinv("|", reinterpret_cast(strings->pdata)); +} + +const gchar* egl_error_to_string(EGLint error) { + switch (error) { + case EGL_SUCCESS: + return "Success"; + case EGL_NOT_INITIALIZED: + return "Not Initialized"; + case EGL_BAD_ACCESS: + return "Bad Access"; + case EGL_BAD_ALLOC: + return "Bad Allocation"; + case EGL_BAD_ATTRIBUTE: + return "Bad Attribute"; + case EGL_BAD_CONTEXT: + return "Bad Context"; + case EGL_BAD_CONFIG: + return "Bad Configuration"; + case EGL_BAD_CURRENT_SURFACE: + return "Bad Current Surface"; + case EGL_BAD_DISPLAY: + return "Bad Display"; + case EGL_BAD_SURFACE: + return "Bad Surface"; + case EGL_BAD_MATCH: + return "Bad Match"; + case EGL_BAD_PARAMETER: + return "Bad Parameter"; + case EGL_BAD_NATIVE_PIXMAP: + return "Bad Native Pixmap"; + case EGL_BAD_NATIVE_WINDOW: + return "Bad Native Window"; + case EGL_CONTEXT_LOST: + return "Context Lost"; + default: + return "Unknown Error"; + } +} + +gchar* egl_config_to_string(EGLDisplay display, EGLConfig config) { + struct { + EGLint attribute; + const gchar* name; + gchar* (*to_string)(EGLint value); + } config_items[] = {{ + EGL_CONFIG_ID, + "EGL_CONFIG_ID", + egl_decimal_to_string, + }, + { + EGL_BUFFER_SIZE, + "EGL_BUFFER_SIZE", + egl_decimal_to_string, + }, + { + EGL_COLOR_BUFFER_TYPE, + "EGL_COLOR_BUFFER_TYPE", + egl_enum_to_string, + }, + { + EGL_TRANSPARENT_TYPE, + "EGL_TRANSPARENT_TYPE", + egl_enum_to_string, + }, + { + EGL_LEVEL, + "EGL_LEVEL", + egl_decimal_to_string, + }, + { + EGL_RED_SIZE, + "EGL_RED_SIZE", + egl_decimal_to_string, + }, + { + EGL_GREEN_SIZE, + "EGL_GREEN_SIZE", + egl_decimal_to_string, + }, + { + EGL_BLUE_SIZE, + "EGL_BLUE_SIZE", + egl_decimal_to_string, + }, + { + EGL_ALPHA_SIZE, + "EGL_ALPHA_SIZE", + egl_decimal_to_string, + }, + { + EGL_DEPTH_SIZE, + "EGL_DEPTH_SIZE", + egl_decimal_to_string, + }, + { + EGL_STENCIL_SIZE, + "EGL_STENCIL_SIZE", + egl_decimal_to_string, + }, + { + EGL_SAMPLES, + "EGL_SAMPLES", + egl_decimal_to_string, + }, + { + EGL_SAMPLE_BUFFERS, + "EGL_SAMPLE_BUFFERS", + egl_decimal_to_string, + }, + { + EGL_NATIVE_VISUAL_ID, + "EGL_NATIVE_VISUAL_ID", + egl_hexadecimal_to_string, + }, + { + EGL_NATIVE_VISUAL_TYPE, + "EGL_NATIVE_VISUAL_TYPE", + egl_hexadecimal_to_string, + }, + { + EGL_NATIVE_RENDERABLE, + "EGL_NATIVE_RENDERABLE", + egl_enum_to_string, + }, + { + EGL_CONFIG_CAVEAT, + "EGL_CONFIG_CAVEAT", + egl_enum_to_string, + }, + { + EGL_BIND_TO_TEXTURE_RGB, + "EGL_BIND_TO_TEXTURE_RGB", + egl_enum_to_string, + }, + { + EGL_BIND_TO_TEXTURE_RGBA, + "EGL_BIND_TO_TEXTURE_RGBA", + egl_enum_to_string, + }, + { + EGL_RENDERABLE_TYPE, + "EGL_RENDERABLE_TYPE", + egl_renderable_type_to_string, + }, + { + EGL_CONFORMANT, + "EGL_CONFORMANT", + egl_renderable_type_to_string, + }, + { + EGL_SURFACE_TYPE, + "EGL_SURFACE_TYPE", + egl_surface_type_to_string, + }, + { + EGL_MAX_PBUFFER_WIDTH, + "EGL_MAX_PBUFFER_WIDTH", + egl_decimal_to_string, + }, + { + EGL_MAX_PBUFFER_HEIGHT, + "EGL_MAX_PBUFFER_HEIGHT", + egl_decimal_to_string, + }, + { + EGL_MAX_PBUFFER_PIXELS, + "EGL_MAX_PBUFFER_PIXELS", + egl_decimal_to_string, + }, + { + EGL_MIN_SWAP_INTERVAL, + "EGL_MIN_SWAP_INTERVAL", + egl_decimal_to_string, + }, + { + EGL_MAX_SWAP_INTERVAL, + "EGL_MAX_SWAP_INTERVAL", + egl_decimal_to_string, + }, + {EGL_NONE, nullptr, nullptr}}; + + g_autoptr(GPtrArray) strings = g_ptr_array_new_with_free_func(g_free); + for (int i = 0; config_items[i].attribute != EGL_NONE; i++) { + EGLint value; + if (!eglGetConfigAttrib(display, config, config_items[i].attribute, &value)) + continue; + g_autofree gchar* value_string = config_items[i].to_string(value); + if (value_string == nullptr) + value_string = egl_hexadecimal_to_string(value); + g_ptr_array_add( + strings, g_strdup_printf("%s=%s", config_items[i].name, value_string)); + } + g_ptr_array_add(strings, nullptr); + + return g_strjoinv(" ", reinterpret_cast(strings->pdata)); +} diff --git a/shell/platform/linux/egl_utils.h b/shell/platform/linux/egl_utils.h new file mode 100644 index 0000000000000..cc97174ea3714 --- /dev/null +++ b/shell/platform/linux/egl_utils.h @@ -0,0 +1,38 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_PLATFORM_LINUX_EGL_UTILS_H_ +#define FLUTTER_SHELL_PLATFORM_LINUX_EGL_UTILS_H_ + +#include + +#include + +G_BEGIN_DECLS + +/** + * egl_error_to_string: + * @error: an EGL error code. + * + * Converts an egl error code to a human readable string. e.g. "Bad Match". + * + * Returns: an error description. + */ +const gchar* egl_error_to_string(EGLint error); + +/** + * egl_config_to_string: + * @display: an EGL display. + * @config: an EGL configuration. + * + * Converts an EGL configuration to a human readable string. e.g. + * "EGL_CONFIG_ID=1 EGL_RED_SIZE=8...". + * + * Returns: a configuration description. + */ +gchar* egl_config_to_string(EGLDisplay display, EGLConfig config); + +G_END_DECLS + +#endif // FLUTTER_SHELL_PLATFORM_LINUX_EGL_UTILS_H_ diff --git a/shell/platform/linux/egl_utils_test.cc b/shell/platform/linux/egl_utils_test.cc new file mode 100644 index 0000000000000..ba6ae8e7b1286 --- /dev/null +++ b/shell/platform/linux/egl_utils_test.cc @@ -0,0 +1,58 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "gtest/gtest.h" + +#include "flutter/shell/platform/linux/egl_utils.h" + +TEST(EGLUtils, ErrorToString) { + const gchar* error_string = egl_error_to_string(EGL_SUCCESS); + EXPECT_STREQ(error_string, "Success"); +} + +TEST(EGLUtils, ErrorToStringUnknown) { + const gchar* error_string = egl_error_to_string(0xffffffff); + EXPECT_STREQ(error_string, "Unknown Error"); +} + +TEST(EGLUtils, ErrorToStringNegative) { + const gchar* error_string = egl_error_to_string(-1); + EXPECT_STREQ(error_string, "Unknown Error"); +} + +TEST(EGLUtils, ConfigToString) { + EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + eglInitialize(display, nullptr, nullptr); + EGLConfig config; + eglChooseConfig(display, nullptr, &config, 1, nullptr); + g_autofree gchar* config_string = egl_config_to_string(display, config); + EXPECT_STREQ( + config_string, + "EGL_CONFIG_ID=1 EGL_BUFFER_SIZE=32 EGL_COLOR_BUFFER_TYPE=EGL_RGB_BUFFER " + "EGL_TRANSPARENT_TYPE=EGL_NONE EGL_LEVEL=1 EGL_RED_SIZE=8 " + "EGL_GREEN_SIZE=8 EGL_BLUE_SIZE=8 EGL_ALPHA_SIZE=0 EGL_DEPTH_SIZE=0 " + "EGL_STENCIL_SIZE=0 EGL_SAMPLES=0 EGL_SAMPLE_BUFFERS=0 " + "EGL_NATIVE_VISUAL_ID=0x1 EGL_NATIVE_VISUAL_TYPE=0x0 " + "EGL_NATIVE_RENDERABLE=EGL_TRUE EGL_CONFIG_CAVEAT=EGL_NONE " + "EGL_BIND_TO_TEXTURE_RGB=EGL_TRUE EGL_BIND_TO_TEXTURE_RGBA=EGL_FALSE " + "EGL_RENDERABLE_TYPE=EGL_OPENGL_ES2_BIT " + "EGL_CONFORMANT=EGL_OPENGL_ES2_BIT " + "EGL_SURFACE_TYPE=EGL_PBUFFER_BIT|EGL_WINDOW_BIT " + "EGL_MAX_PBUFFER_WIDTH=1024 EGL_MAX_PBUFFER_HEIGHT=1024 " + "EGL_MAX_PBUFFER_PIXELS=1048576 EGL_MIN_SWAP_INTERVAL=0 " + "EGL_MAX_SWAP_INTERVAL=1000"); +} + +TEST(EGLUtils, ConfigToStringNullptr) { + EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + eglInitialize(display, nullptr, nullptr); + EGLConfig config; + eglChooseConfig(display, nullptr, &config, 1, nullptr); + g_autofree gchar* config_string1 = egl_config_to_string(nullptr, config); + EXPECT_STREQ(config_string1, ""); + g_autofree gchar* config_string2 = egl_config_to_string(display, nullptr); + EXPECT_STREQ(config_string2, ""); + g_autofree gchar* config_string3 = egl_config_to_string(nullptr, nullptr); + EXPECT_STREQ(config_string3, ""); +} diff --git a/shell/platform/linux/fl_renderer.cc b/shell/platform/linux/fl_renderer.cc index 6f8d4b61fe99a..c326952d531fc 100644 --- a/shell/platform/linux/fl_renderer.cc +++ b/shell/platform/linux/fl_renderer.cc @@ -5,6 +5,7 @@ #include "fl_renderer.h" #include "flutter/shell/platform/embedder/embedder.h" +#include "flutter/shell/platform/linux/egl_utils.h" G_DEFINE_QUARK(fl_renderer_error_quark, fl_renderer_error) @@ -19,45 +20,6 @@ typedef struct { G_DEFINE_TYPE_WITH_PRIVATE(FlRenderer, fl_renderer, G_TYPE_OBJECT) -// Gets a string representation of the last EGL error. -static const gchar* get_egl_error() { - EGLint error = eglGetError(); - switch (error) { - case EGL_SUCCESS: - return "Success"; - case EGL_NOT_INITIALIZED: - return "Not Initialized"; - case EGL_BAD_ACCESS: - return "Bad Access"; - case EGL_BAD_ALLOC: - return "Bad Allocation"; - case EGL_BAD_ATTRIBUTE: - return "Bad Attribute"; - case EGL_BAD_CONTEXT: - return "Bad Context"; - case EGL_BAD_CONFIG: - return "Bad Configuration"; - case EGL_BAD_CURRENT_SURFACE: - return "Bad Current Surface"; - case EGL_BAD_DISPLAY: - return "Bad Display"; - case EGL_BAD_SURFACE: - return "Bad Surface"; - case EGL_BAD_MATCH: - return "Bad Match"; - case EGL_BAD_PARAMETER: - return "Bad Parameter"; - case EGL_BAD_NATIVE_PIXMAP: - return "Bad Native Pixmap"; - case EGL_BAD_NATIVE_WINDOW: - return "Bad Native Window"; - case EGL_CONTEXT_LOST: - return "Context Lost"; - default: - return "Unknown Error"; - } -} - // Creates a resource surface. static void create_resource_surface(FlRenderer* self, EGLConfig config) { FlRendererPrivate* priv = @@ -69,14 +31,16 @@ static void create_resource_surface(FlRenderer* self, EGLConfig config) { priv->resource_surface = eglCreatePbufferSurface(priv->egl_display, config, resource_context_attribs); if (priv->resource_surface == nullptr) { - g_warning("Failed to create EGL resource surface: %s", get_egl_error()); + g_warning("Failed to create EGL resource surface: %s", + egl_error_to_string(eglGetError())); return; } priv->resource_context = eglCreateContext( priv->egl_display, config, priv->egl_context, context_attributes); if (priv->resource_context == nullptr) - g_warning("Failed to create EGL resource context: %s", get_egl_error()); + g_warning("Failed to create EGL resource context: %s", + egl_error_to_string(eglGetError())); } // Default implementation for the start virtual method. @@ -114,33 +78,44 @@ static gboolean fl_renderer_real_start(FlRenderer* self, GError** error) { if (!eglChooseConfig(priv->egl_display, attributes, &egl_config, 1, &n_config)) { g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED, - "Failed to choose EGL config: %s", get_egl_error()); + "Failed to choose EGL config: %s", + egl_error_to_string(eglGetError())); return FALSE; } if (n_config == 0) { g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED, - "Failed to find appropriate EGL config: %s", get_egl_error()); + "Failed to find appropriate EGL config: %s", + egl_error_to_string(eglGetError())); return FALSE; } if (!eglBindAPI(EGL_OPENGL_ES_API)) { + g_autofree gchar* config_string = + egl_config_to_string(priv->egl_display, egl_config); g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED, - "Failed to bind EGL OpenGL ES API: %s", get_egl_error()); + "Failed to bind EGL OpenGL ES API using configuration (%s): %s", + config_string, egl_error_to_string(eglGetError())); return FALSE; } priv->egl_surface = FL_RENDERER_GET_CLASS(self)->create_surface( self, priv->egl_display, egl_config); if (priv->egl_surface == nullptr) { + g_autofree gchar* config_string = + egl_config_to_string(priv->egl_display, egl_config); g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED, - "Failed to create EGL surface: %s", get_egl_error()); + "Failed to create EGL surface using configuration (%s): %s", + config_string, egl_error_to_string(eglGetError())); return FALSE; } EGLint context_attributes[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE}; priv->egl_context = eglCreateContext(priv->egl_display, egl_config, EGL_NO_CONTEXT, context_attributes); if (priv->egl_context == nullptr) { + g_autofree gchar* config_string = + egl_config_to_string(priv->egl_display, egl_config); g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED, - "Failed to create EGL context: %s", get_egl_error()); + "Failed to create EGL context using configuration (%s): %s", + config_string, egl_error_to_string(eglGetError())); return FALSE; } @@ -174,7 +149,8 @@ gboolean fl_renderer_make_current(FlRenderer* self, GError** error) { if (!eglMakeCurrent(priv->egl_display, priv->egl_surface, priv->egl_surface, priv->egl_context)) { g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED, - "Failed to make EGL context current: %s", get_egl_error()); + "Failed to make EGL context current: %s", + egl_error_to_string(eglGetError())); return FALSE; } @@ -191,7 +167,8 @@ gboolean fl_renderer_make_resource_current(FlRenderer* self, GError** error) { if (!eglMakeCurrent(priv->egl_display, priv->resource_surface, priv->resource_surface, priv->resource_context)) { g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED, - "Failed to make EGL context current: %s", get_egl_error()); + "Failed to make EGL context current: %s", + egl_error_to_string(eglGetError())); return FALSE; } @@ -205,7 +182,8 @@ gboolean fl_renderer_clear_current(FlRenderer* self, GError** error) { if (!eglMakeCurrent(priv->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) { g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED, - "Failed to clear EGL context: %s", get_egl_error()); + "Failed to clear EGL context: %s", + egl_error_to_string(eglGetError())); return FALSE; } @@ -223,7 +201,8 @@ gboolean fl_renderer_present(FlRenderer* self, GError** error) { if (!eglSwapBuffers(priv->egl_display, priv->egl_surface)) { g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED, - "Failed to swap EGL buffers: %s", get_egl_error()); + "Failed to swap EGL buffers: %s", + egl_error_to_string(eglGetError())); return FALSE; } diff --git a/shell/platform/linux/testing/mock_egl.cc b/shell/platform/linux/testing/mock_egl.cc index ab28fc9068492..555a3af61f5e7 100644 --- a/shell/platform/linux/testing/mock_egl.cc +++ b/shell/platform/linux/testing/mock_egl.cc @@ -4,72 +4,315 @@ #include -EGLBoolean eglBindAPI(EGLenum api) { +typedef struct { + EGLint config_id; + EGLint buffer_size; + EGLint color_buffer_type; + EGLint transparent_type; + EGLint level; + EGLint red_size; + EGLint green_size; + EGLint blue_size; + EGLint alpha_size; + EGLint depth_size; + EGLint stencil_size; + EGLint samples; + EGLint sample_buffers; + EGLint native_visual_id; + EGLint native_visual_type; + EGLint native_renderable; + EGLint config_caveat; + EGLint bind_to_texture_rgb; + EGLint bind_to_texture_rgba; + EGLint renderable_type; + EGLint conformant; + EGLint surface_type; + EGLint max_pbuffer_width; + EGLint max_pbuffer_height; + EGLint max_pbuffer_pixels; + EGLint min_swap_interval; + EGLint max_swap_interval; +} MockConfig; + +typedef struct { + bool initialized; + MockConfig config; +} MockDisplay; + +static MockDisplay mock_display; + +static EGLint mock_error = EGL_SUCCESS; + +static bool check_display(EGLDisplay dpy) { + if (dpy == nullptr) { + mock_error = EGL_BAD_DISPLAY; + return false; + } + + MockDisplay* display = static_cast(dpy); + if (!display->initialized) { + mock_error = EGL_NOT_INITIALIZED; + return false; + } + + return true; +} + +static bool check_config(EGLConfig config) { + if (config == nullptr) { + mock_error = EGL_BAD_CONFIG; + return false; + } + + return true; +} + +static EGLBoolean bool_success() { + mock_error = EGL_SUCCESS; return EGL_TRUE; } +static EGLBoolean bool_failure(EGLint error) { + mock_error = error; + return EGL_FALSE; +} + +EGLBoolean eglBindAPI(EGLenum api) { + return bool_success(); +} + EGLBoolean eglChooseConfig(EGLDisplay dpy, const EGLint* attrib_list, EGLConfig* configs, EGLint config_size, EGLint* num_config) { - return EGL_TRUE; + MockDisplay* display = static_cast(dpy); + + if (configs == nullptr) { + if (num_config != nullptr) + *num_config = 1; + return bool_success(); + } + + EGLint n_returned = 0; + if (config_size >= 1) { + configs[0] = &display->config; + } + + if (num_config != nullptr) + *num_config = n_returned; + + return bool_success(); } EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config, EGLContext share_context, const EGLint* attrib_list) { - return nullptr; + if (!check_display(dpy) || !check_config(config)) + return EGL_NO_CONTEXT; + + mock_error = EGL_SUCCESS; + return EGL_NO_CONTEXT; } EGLSurface eglCreatePbufferSurface(EGLDisplay dpy, EGLConfig config, const EGLint* attrib_list) { - return nullptr; + if (!check_display(dpy) || !check_config(config)) + return EGL_NO_SURFACE; + + mock_error = EGL_SUCCESS; + return EGL_NO_SURFACE; } EGLSurface eglCreateWindowSurface(EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win, const EGLint* attrib_list) { - return nullptr; + if (!check_display(dpy) || !check_config(config)) + return EGL_NO_SURFACE; + + mock_error = EGL_SUCCESS; + return EGL_NO_SURFACE; +} + +EGLBoolean eglGetConfigAttrib(EGLDisplay dpy, + EGLConfig config, + EGLint attribute, + EGLint* value) { + if (!check_display(dpy) || !check_config(config)) + return EGL_FALSE; + + MockConfig* c = static_cast(config); + switch (attribute) { + case EGL_CONFIG_ID: + *value = c->config_id; + return bool_success(); + case EGL_BUFFER_SIZE: + *value = c->buffer_size; + return bool_success(); + case EGL_COLOR_BUFFER_TYPE: + *value = c->color_buffer_type; + return bool_success(); + case EGL_TRANSPARENT_TYPE: + *value = c->transparent_type; + return bool_success(); + case EGL_LEVEL: + *value = c->level; + return bool_success(); + case EGL_RED_SIZE: + *value = c->red_size; + return bool_success(); + case EGL_GREEN_SIZE: + *value = c->green_size; + return bool_success(); + case EGL_BLUE_SIZE: + *value = c->blue_size; + return bool_success(); + case EGL_ALPHA_SIZE: + *value = c->alpha_size; + return bool_success(); + case EGL_DEPTH_SIZE: + *value = c->depth_size; + return bool_success(); + case EGL_STENCIL_SIZE: + *value = c->stencil_size; + return bool_success(); + case EGL_SAMPLES: + *value = c->samples; + return bool_success(); + case EGL_SAMPLE_BUFFERS: + *value = c->sample_buffers; + return bool_success(); + case EGL_NATIVE_VISUAL_ID: + *value = c->native_visual_id; + return bool_success(); + case EGL_NATIVE_VISUAL_TYPE: + *value = c->native_visual_type; + return bool_success(); + case EGL_NATIVE_RENDERABLE: + *value = c->native_renderable; + return bool_success(); + case EGL_CONFIG_CAVEAT: + *value = c->config_caveat; + return bool_success(); + case EGL_BIND_TO_TEXTURE_RGB: + *value = c->bind_to_texture_rgb; + return bool_success(); + case EGL_BIND_TO_TEXTURE_RGBA: + *value = c->bind_to_texture_rgba; + return bool_success(); + case EGL_RENDERABLE_TYPE: + *value = c->renderable_type; + return bool_success(); + case EGL_CONFORMANT: + *value = c->conformant; + return bool_success(); + case EGL_SURFACE_TYPE: + *value = c->surface_type; + return bool_success(); + case EGL_MAX_PBUFFER_WIDTH: + *value = c->max_pbuffer_width; + return bool_success(); + case EGL_MAX_PBUFFER_HEIGHT: + *value = c->max_pbuffer_height; + return bool_success(); + case EGL_MAX_PBUFFER_PIXELS: + *value = c->max_pbuffer_pixels; + return bool_success(); + case EGL_MIN_SWAP_INTERVAL: + *value = c->min_swap_interval; + return bool_success(); + case EGL_MAX_SWAP_INTERVAL: + *value = c->max_swap_interval; + return bool_success(); + default: + return bool_failure(EGL_BAD_ATTRIBUTE); + } } EGLDisplay eglGetDisplay(EGLNativeDisplayType display_id) { - return nullptr; + return &mock_display; } EGLint eglGetError() { - return EGL_SUCCESS; + EGLint error = mock_error; + mock_error = EGL_SUCCESS; + return error; } void (*eglGetProcAddress(const char* procname))(void) { + mock_error = EGL_SUCCESS; return nullptr; } EGLBoolean eglInitialize(EGLDisplay dpy, EGLint* major, EGLint* minor) { + MockDisplay* display = static_cast(dpy); + + if (!display->initialized) { + MockConfig* c = &display->config; + c->config_id = 1; + c->buffer_size = 32; + c->color_buffer_type = EGL_RGB_BUFFER; + c->transparent_type = EGL_NONE; + c->level = 1; + c->red_size = 8; + c->green_size = 8; + c->blue_size = 8; + c->alpha_size = 0; + c->depth_size = 0; + c->stencil_size = 0; + c->samples = 0; + c->sample_buffers = 0; + c->native_visual_id = 1; + c->native_visual_type = 0; + c->native_renderable = EGL_TRUE; + c->config_caveat = EGL_NONE; + c->bind_to_texture_rgb = EGL_TRUE; + c->bind_to_texture_rgba = EGL_FALSE; + c->renderable_type = EGL_OPENGL_ES2_BIT; + c->conformant = EGL_OPENGL_ES2_BIT; + c->surface_type = EGL_WINDOW_BIT | EGL_PBUFFER_BIT; + c->max_pbuffer_width = 1024; + c->max_pbuffer_height = 1024; + c->max_pbuffer_pixels = 1024 * 1024; + c->min_swap_interval = 0; + c->max_swap_interval = 1000; + display->initialized = true; + } + if (major != nullptr) *major = 1; if (minor != nullptr) *major = 5; - return EGL_TRUE; + + return bool_success(); } EGLBoolean eglMakeCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx) { - return EGL_TRUE; + if (!check_display(dpy)) + return EGL_FALSE; + + return bool_success(); } EGLBoolean eglQueryContext(EGLDisplay dpy, EGLContext ctx, EGLint attribute, EGLint* value) { - return EGL_TRUE; + if (!check_display(dpy)) + return EGL_FALSE; + + return bool_success(); } EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface surface) { - return EGL_TRUE; + if (!check_display(dpy)) + return EGL_FALSE; + + return bool_success(); }