| // 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 "flutter/shell/platform/linux/public/flutter_linux/fl_texture_registrar.h" |
| #include "flutter/shell/platform/linux/fl_texture_registrar_private.h" |
| #include "flutter/shell/platform/linux/public/flutter_linux/fl_pixel_buffer_texture.h" |
| #include "flutter/shell/platform/linux/public/flutter_linux/fl_texture_gl.h" |
| #include "flutter/shell/platform/linux/testing/fl_test.h" |
| #include "flutter/shell/platform/linux/testing/mock_texture_registrar.h" |
| #include "gtest/gtest.h" |
| |
| #include <epoxy/gl.h> |
| |
| #include <gmodule.h> |
| #include <pthread.h> |
| |
| static constexpr uint32_t kBufferWidth = 4u; |
| static constexpr uint32_t kBufferHeight = 4u; |
| static constexpr uint32_t kRealBufferWidth = 2u; |
| static constexpr uint32_t kRealBufferHeight = 2u; |
| static constexpr uint64_t kThreadCount = 16u; |
| |
| G_DECLARE_FINAL_TYPE(FlTestRegistrarTexture, |
| fl_test_registrar_texture, |
| FL, |
| TEST_REGISTRAR_TEXTURE, |
| FlTextureGL) |
| |
| /// A simple texture. |
| struct _FlTestRegistrarTexture { |
| FlTextureGL parent_instance; |
| }; |
| |
| G_DEFINE_TYPE(FlTestRegistrarTexture, |
| fl_test_registrar_texture, |
| fl_texture_gl_get_type()) |
| |
| static gboolean fl_test_registrar_texture_populate(FlTextureGL* texture, |
| uint32_t* target, |
| uint32_t* format, |
| uint32_t* width, |
| uint32_t* height, |
| GError** error) { |
| EXPECT_TRUE(FL_IS_TEST_REGISTRAR_TEXTURE(texture)); |
| |
| EXPECT_EQ(*width, kBufferWidth); |
| EXPECT_EQ(*height, kBufferHeight); |
| *target = GL_TEXTURE_2D; |
| *format = GL_R8; |
| *width = kRealBufferWidth; |
| *height = kRealBufferHeight; |
| |
| return TRUE; |
| } |
| |
| static void fl_test_registrar_texture_class_init( |
| FlTestRegistrarTextureClass* klass) { |
| FL_TEXTURE_GL_CLASS(klass)->populate = fl_test_registrar_texture_populate; |
| } |
| |
| static void fl_test_registrar_texture_init(FlTestRegistrarTexture* self) {} |
| |
| static FlTestRegistrarTexture* fl_test_registrar_texture_new() { |
| return FL_TEST_REGISTRAR_TEXTURE( |
| g_object_new(fl_test_registrar_texture_get_type(), nullptr)); |
| } |
| |
| static void* add_mock_texture_to_registrar(void* pointer) { |
| g_return_val_if_fail(FL_TEXTURE_REGISTRAR(pointer), ((void*)NULL)); |
| FlTextureRegistrar* registrar = FL_TEXTURE_REGISTRAR(pointer); |
| g_autoptr(FlTexture) texture = FL_TEXTURE(fl_test_registrar_texture_new()); |
| fl_texture_registrar_register_texture(registrar, texture); |
| int64_t* id = static_cast<int64_t*>(malloc(sizeof(int64_t))); |
| id[0] = fl_texture_get_id(texture); |
| pthread_exit(id); |
| } |
| |
| // Checks can make a mock registrar. |
| TEST(FlTextureRegistrarTest, MockRegistrar) { |
| g_autoptr(FlTexture) texture = FL_TEXTURE(fl_test_registrar_texture_new()); |
| g_autoptr(FlMockTextureRegistrar) registrar = fl_mock_texture_registrar_new(); |
| EXPECT_TRUE(FL_IS_MOCK_TEXTURE_REGISTRAR(registrar)); |
| |
| EXPECT_TRUE(fl_texture_registrar_register_texture( |
| FL_TEXTURE_REGISTRAR(registrar), texture)); |
| EXPECT_EQ(fl_mock_texture_registrar_get_texture(registrar), texture); |
| EXPECT_TRUE(fl_texture_registrar_mark_texture_frame_available( |
| FL_TEXTURE_REGISTRAR(registrar), texture)); |
| EXPECT_TRUE(fl_mock_texture_registrar_get_frame_available(registrar)); |
| EXPECT_TRUE(fl_texture_registrar_unregister_texture( |
| FL_TEXTURE_REGISTRAR(registrar), texture)); |
| EXPECT_EQ(fl_mock_texture_registrar_get_texture(registrar), nullptr); |
| } |
| |
| // Test that registering a texture works. |
| TEST(FlTextureRegistrarTest, RegisterTexture) { |
| g_autoptr(FlEngine) engine = make_mock_engine(); |
| g_autoptr(FlTextureRegistrar) registrar = fl_texture_registrar_new(engine); |
| g_autoptr(FlTexture) texture = FL_TEXTURE(fl_test_registrar_texture_new()); |
| |
| EXPECT_FALSE(fl_texture_registrar_unregister_texture(registrar, texture)); |
| EXPECT_TRUE(fl_texture_registrar_register_texture(registrar, texture)); |
| EXPECT_TRUE(fl_texture_registrar_unregister_texture(registrar, texture)); |
| } |
| |
| // Test that marking a texture frame available works. |
| TEST(FlTextureRegistrarTest, MarkTextureFrameAvailable) { |
| g_autoptr(FlEngine) engine = make_mock_engine(); |
| g_autoptr(FlTextureRegistrar) registrar = fl_texture_registrar_new(engine); |
| g_autoptr(FlTexture) texture = FL_TEXTURE(fl_test_registrar_texture_new()); |
| |
| EXPECT_FALSE( |
| fl_texture_registrar_mark_texture_frame_available(registrar, texture)); |
| EXPECT_TRUE(fl_texture_registrar_register_texture(registrar, texture)); |
| EXPECT_TRUE( |
| fl_texture_registrar_mark_texture_frame_available(registrar, texture)); |
| } |
| |
| // Test the textures can be accessed via multiple threads without |
| // synchronization issues. |
| // TODO(robert-ancell): Re-enable when no longer flaky |
| // https://github.com/flutter/flutter/issues/138197 |
| TEST(FlTextureRegistrarTest, |
| DISABLED_RegistrarRegisterTextureInMultipleThreads) { |
| g_autoptr(FlEngine) engine = make_mock_engine(); |
| g_autoptr(FlTextureRegistrar) registrar = fl_texture_registrar_new(engine); |
| pthread_t threads[kThreadCount]; |
| int64_t ids[kThreadCount]; |
| |
| for (uint64_t t = 0; t < kThreadCount; t++) { |
| EXPECT_EQ(pthread_create(&threads[t], NULL, add_mock_texture_to_registrar, |
| (void*)registrar), |
| 0); |
| } |
| for (uint64_t t = 0; t < kThreadCount; t++) { |
| void* id; |
| pthread_join(threads[t], &id); |
| ids[t] = static_cast<int64_t*>(id)[0]; |
| free(id); |
| }; |
| // Check all the textures were created. |
| for (uint64_t t = 0; t < kThreadCount; t++) { |
| EXPECT_TRUE(fl_texture_registrar_lookup_texture(registrar, ids[t]) != NULL); |
| }; |
| } |