blob: 0be10cb63b98f91840d9c60728882b378459bddd [file] [log] [blame]
// 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 <gmodule.h>
#include "flutter/shell/platform/embedder/embedder.h"
#include "flutter/shell/platform/linux/fl_engine_private.h"
#include "flutter/shell/platform/linux/fl_pixel_buffer_texture_private.h"
#include "flutter/shell/platform/linux/fl_texture_gl_private.h"
#include "flutter/shell/platform/linux/fl_texture_private.h"
#include "flutter/shell/platform/linux/fl_texture_registrar_private.h"
G_DECLARE_FINAL_TYPE(FlTextureRegistrarImpl,
fl_texture_registrar_impl,
FL,
TEXTURE_REGISTRAR_IMPL,
GObject)
struct _FlTextureRegistrarImpl {
GObject parent_instance;
// Weak reference to the engine this texture registrar is created for.
FlEngine* engine;
// ID to assign to the next new texture.
int64_t next_id;
// Internal record for registered textures.
//
// It is a map from Flutter texture ID to #FlTexture instance created by
// plugins. The keys are directly stored int64s. The values are stored
// pointer to #FlTexture. This table is freed by the responder.
GHashTable* textures;
// The mutex guard to make `textures` thread-safe.
GMutex textures_mutex;
};
static void fl_texture_registrar_impl_iface_init(
FlTextureRegistrarInterface* iface);
G_DEFINE_INTERFACE(FlTextureRegistrar, fl_texture_registrar, G_TYPE_OBJECT)
G_DEFINE_TYPE_WITH_CODE(
FlTextureRegistrarImpl,
fl_texture_registrar_impl,
G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE(fl_texture_registrar_get_type(),
fl_texture_registrar_impl_iface_init))
static void fl_texture_registrar_default_init(
FlTextureRegistrarInterface* iface) {}
static void engine_weak_notify_cb(gpointer user_data,
GObject* where_the_object_was) {
FlTextureRegistrarImpl* self = FL_TEXTURE_REGISTRAR_IMPL(user_data);
self->engine = nullptr;
// Unregister any textures.
g_mutex_lock(&self->textures_mutex);
g_autoptr(GHashTable) textures = self->textures;
self->textures = g_hash_table_new_full(g_direct_hash, g_direct_equal, nullptr,
g_object_unref);
g_hash_table_remove_all(textures);
g_mutex_unlock(&self->textures_mutex);
}
static void fl_texture_registrar_impl_dispose(GObject* object) {
FlTextureRegistrarImpl* self = FL_TEXTURE_REGISTRAR_IMPL(object);
g_mutex_lock(&self->textures_mutex);
g_clear_pointer(&self->textures, g_hash_table_unref);
g_mutex_unlock(&self->textures_mutex);
if (self->engine != nullptr) {
g_object_weak_unref(G_OBJECT(self->engine), engine_weak_notify_cb, self);
self->engine = nullptr;
}
g_mutex_clear(&self->textures_mutex);
G_OBJECT_CLASS(fl_texture_registrar_impl_parent_class)->dispose(object);
}
static void fl_texture_registrar_impl_class_init(
FlTextureRegistrarImplClass* klass) {
G_OBJECT_CLASS(klass)->dispose = fl_texture_registrar_impl_dispose;
}
static gboolean register_texture(FlTextureRegistrar* registrar,
FlTexture* texture) {
FlTextureRegistrarImpl* self = FL_TEXTURE_REGISTRAR_IMPL(registrar);
if (FL_IS_TEXTURE_GL(texture) || FL_IS_PIXEL_BUFFER_TEXTURE(texture)) {
if (self->engine == nullptr) {
return FALSE;
}
// We ideally would use numeric IDs, but for backwards compatibility with
// existing code use the address of the texture. Once all code uses
// fl_texture_get_id we can re-enable this method. See
// https://github.com/flutter/flutter/issues/124009 int64_t id =
// self->next_id++;
int64_t id = reinterpret_cast<int64_t>(texture);
if (fl_engine_register_external_texture(self->engine, id)) {
fl_texture_set_id(texture, id);
g_mutex_lock(&self->textures_mutex);
g_hash_table_insert(self->textures, GINT_TO_POINTER(id),
g_object_ref(texture));
g_mutex_unlock(&self->textures_mutex);
return TRUE;
} else {
return FALSE;
}
} else {
// We currently only support #FlTextureGL and #FlPixelBufferTexture.
return FALSE;
}
}
static FlTexture* lookup_texture(FlTextureRegistrar* registrar,
int64_t texture_id) {
FlTextureRegistrarImpl* self = FL_TEXTURE_REGISTRAR_IMPL(registrar);
g_mutex_lock(&self->textures_mutex);
FlTexture* texture = reinterpret_cast<FlTexture*>(
g_hash_table_lookup(self->textures, GINT_TO_POINTER(texture_id)));
g_mutex_unlock(&self->textures_mutex);
return texture;
}
static gboolean mark_texture_frame_available(FlTextureRegistrar* registrar,
FlTexture* texture) {
FlTextureRegistrarImpl* self = FL_TEXTURE_REGISTRAR_IMPL(registrar);
if (self->engine == nullptr) {
return FALSE;
}
return fl_engine_mark_texture_frame_available(self->engine,
fl_texture_get_id(texture));
}
static gboolean unregister_texture(FlTextureRegistrar* registrar,
FlTexture* texture) {
FlTextureRegistrarImpl* self = FL_TEXTURE_REGISTRAR_IMPL(registrar);
if (self->engine == nullptr) {
return FALSE;
}
gboolean result = fl_engine_unregister_external_texture(
self->engine, fl_texture_get_id(texture));
g_mutex_lock(&self->textures_mutex);
if (!g_hash_table_remove(self->textures,
GINT_TO_POINTER(fl_texture_get_id(texture)))) {
g_warning("Unregistering a non-existent texture %p", texture);
}
g_mutex_unlock(&self->textures_mutex);
return result;
}
static void fl_texture_registrar_impl_iface_init(
FlTextureRegistrarInterface* iface) {
iface->register_texture = register_texture;
iface->lookup_texture = lookup_texture;
iface->mark_texture_frame_available = mark_texture_frame_available;
iface->unregister_texture = unregister_texture;
}
static void fl_texture_registrar_impl_init(FlTextureRegistrarImpl* self) {
self->next_id = 1;
self->textures = g_hash_table_new_full(g_direct_hash, g_direct_equal, nullptr,
g_object_unref);
// Initialize the mutex for textures.
g_mutex_init(&self->textures_mutex);
}
G_MODULE_EXPORT gboolean
fl_texture_registrar_register_texture(FlTextureRegistrar* self,
FlTexture* texture) {
g_return_val_if_fail(FL_IS_TEXTURE_REGISTRAR(self), FALSE);
g_return_val_if_fail(FL_IS_TEXTURE(texture), FALSE);
return FL_TEXTURE_REGISTRAR_GET_IFACE(self)->register_texture(self, texture);
}
FlTexture* fl_texture_registrar_lookup_texture(FlTextureRegistrar* self,
int64_t texture_id) {
g_return_val_if_fail(FL_IS_TEXTURE_REGISTRAR(self), NULL);
return FL_TEXTURE_REGISTRAR_GET_IFACE(self)->lookup_texture(self, texture_id);
}
G_MODULE_EXPORT gboolean
fl_texture_registrar_mark_texture_frame_available(FlTextureRegistrar* self,
FlTexture* texture) {
g_return_val_if_fail(FL_IS_TEXTURE_REGISTRAR(self), FALSE);
return FL_TEXTURE_REGISTRAR_GET_IFACE(self)->mark_texture_frame_available(
self, texture);
}
G_MODULE_EXPORT gboolean
fl_texture_registrar_unregister_texture(FlTextureRegistrar* self,
FlTexture* texture) {
g_return_val_if_fail(FL_IS_TEXTURE_REGISTRAR(self), FALSE);
return FL_TEXTURE_REGISTRAR_GET_IFACE(self)->unregister_texture(self,
texture);
}
FlTextureRegistrar* fl_texture_registrar_new(FlEngine* engine) {
FlTextureRegistrarImpl* self = FL_TEXTURE_REGISTRAR_IMPL(
g_object_new(fl_texture_registrar_impl_get_type(), nullptr));
// Added to stop compiler complaining about an unused function.
FL_IS_TEXTURE_REGISTRAR_IMPL(self);
self->engine = engine;
g_object_weak_ref(G_OBJECT(engine), engine_weak_notify_cb, self);
return FL_TEXTURE_REGISTRAR(self);
}