mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
179 lines
5.5 KiB
C++
179 lines
5.5 KiB
C++
// 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_FLOW_SKIA_GPU_OBJECT_H_
|
|
#define FLUTTER_FLOW_SKIA_GPU_OBJECT_H_
|
|
|
|
#include <mutex>
|
|
#include <queue>
|
|
|
|
#include "flutter/fml/memory/ref_counted.h"
|
|
#include "flutter/fml/memory/weak_ptr.h"
|
|
#include "flutter/fml/task_runner.h"
|
|
#include "flutter/fml/trace_event.h"
|
|
#include "third_party/skia/include/core/SkRefCnt.h"
|
|
#include "third_party/skia/include/gpu/GrBackendSurface.h"
|
|
#include "third_party/skia/include/gpu/GrDirectContext.h"
|
|
|
|
namespace flutter {
|
|
|
|
// A queue that holds Skia objects that must be destructed on the given task
|
|
// runner.
|
|
template <class T>
|
|
class UnrefQueue : public fml::RefCountedThreadSafe<UnrefQueue<T>> {
|
|
public:
|
|
using ResourceContext = T;
|
|
|
|
void Unref(SkRefCnt* object) {
|
|
if (drain_immediate_) {
|
|
object->unref();
|
|
return;
|
|
}
|
|
std::scoped_lock lock(mutex_);
|
|
objects_.push_back(object);
|
|
if (!drain_pending_) {
|
|
drain_pending_ = true;
|
|
task_runner_->PostDelayedTask(
|
|
[strong = fml::Ref(this)]() { strong->Drain(); }, drain_delay_);
|
|
}
|
|
}
|
|
|
|
void DeleteTexture(GrBackendTexture texture) {
|
|
// drain_immediate_ should only be used on Impeller.
|
|
FML_DCHECK(!drain_immediate_);
|
|
std::scoped_lock lock(mutex_);
|
|
textures_.push_back(texture);
|
|
if (!drain_pending_) {
|
|
drain_pending_ = true;
|
|
task_runner_->PostDelayedTask(
|
|
[strong = fml::Ref(this)]() { strong->Drain(); }, drain_delay_);
|
|
}
|
|
}
|
|
|
|
// Usually, the drain is called automatically. However, during IO manager
|
|
// shutdown (when the platform side reference to the OpenGL context is about
|
|
// to go away), we may need to pre-emptively drain the unref queue. It is the
|
|
// responsibility of the caller to ensure that no further unrefs are queued
|
|
// after this call.
|
|
void Drain() {
|
|
TRACE_EVENT0("flutter", "SkiaUnrefQueue::Drain");
|
|
std::deque<SkRefCnt*> skia_objects;
|
|
std::deque<GrBackendTexture> textures;
|
|
{
|
|
std::scoped_lock lock(mutex_);
|
|
objects_.swap(skia_objects);
|
|
textures_.swap(textures);
|
|
drain_pending_ = false;
|
|
}
|
|
DoDrain(skia_objects, textures, context_);
|
|
}
|
|
|
|
void UpdateResourceContext(sk_sp<ResourceContext> context) {
|
|
context_ = context;
|
|
}
|
|
|
|
private:
|
|
const fml::RefPtr<fml::TaskRunner> task_runner_;
|
|
const fml::TimeDelta drain_delay_;
|
|
std::mutex mutex_;
|
|
std::deque<SkRefCnt*> objects_;
|
|
std::deque<GrBackendTexture> textures_;
|
|
bool drain_pending_;
|
|
sk_sp<ResourceContext> context_;
|
|
// Enabled when there is an impeller context, which removes the usage of
|
|
// the queue altogether.
|
|
bool drain_immediate_;
|
|
|
|
// The `GrDirectContext* context` is only used for signaling Skia to
|
|
// performDeferredCleanup. It can be nullptr when such signaling is not needed
|
|
// (e.g., in unit tests).
|
|
UnrefQueue(fml::RefPtr<fml::TaskRunner> task_runner,
|
|
fml::TimeDelta delay,
|
|
sk_sp<ResourceContext> context = nullptr,
|
|
bool drain_immediate = false)
|
|
: task_runner_(std::move(task_runner)),
|
|
drain_delay_(delay),
|
|
drain_pending_(false),
|
|
context_(context),
|
|
drain_immediate_(drain_immediate) {}
|
|
|
|
~UnrefQueue() {
|
|
// The ResourceContext must be deleted on the task runner thread.
|
|
// Transfer ownership of the UnrefQueue's ResourceContext reference
|
|
// into a task queued to that thread.
|
|
ResourceContext* raw_context = context_.release();
|
|
fml::TaskRunner::RunNowOrPostTask(
|
|
task_runner_, [objects = std::move(objects_),
|
|
textures = std::move(textures_), raw_context]() mutable {
|
|
sk_sp<ResourceContext> context(raw_context);
|
|
DoDrain(objects, textures, context);
|
|
context.reset();
|
|
});
|
|
}
|
|
|
|
// static
|
|
static void DoDrain(const std::deque<SkRefCnt*>& skia_objects,
|
|
const std::deque<GrBackendTexture>& textures,
|
|
sk_sp<ResourceContext> context) {
|
|
for (SkRefCnt* skia_object : skia_objects) {
|
|
skia_object->unref();
|
|
}
|
|
|
|
if (context) {
|
|
for (GrBackendTexture texture : textures) {
|
|
context->deleteBackendTexture(texture);
|
|
}
|
|
|
|
if (!skia_objects.empty()) {
|
|
context->performDeferredCleanup(std::chrono::milliseconds(0));
|
|
}
|
|
}
|
|
}
|
|
|
|
FML_FRIEND_REF_COUNTED_THREAD_SAFE(UnrefQueue);
|
|
FML_FRIEND_MAKE_REF_COUNTED(UnrefQueue);
|
|
FML_DISALLOW_COPY_AND_ASSIGN(UnrefQueue);
|
|
};
|
|
|
|
using SkiaUnrefQueue = UnrefQueue<GrDirectContext>;
|
|
|
|
/// An object whose deallocation needs to be performed on an specific unref
|
|
/// queue. The template argument U need to have a call operator that returns
|
|
/// that unref queue.
|
|
template <class T>
|
|
class SkiaGPUObject {
|
|
public:
|
|
using SkiaObjectType = T;
|
|
|
|
SkiaGPUObject() = default;
|
|
SkiaGPUObject(sk_sp<SkiaObjectType> object, fml::RefPtr<SkiaUnrefQueue> queue)
|
|
: object_(std::move(object)), queue_(std::move(queue)) {
|
|
FML_DCHECK(object_);
|
|
}
|
|
SkiaGPUObject(SkiaGPUObject&&) = default;
|
|
~SkiaGPUObject() { reset(); }
|
|
|
|
SkiaGPUObject& operator=(SkiaGPUObject&&) = default;
|
|
|
|
sk_sp<SkiaObjectType> skia_object() const { return object_; }
|
|
|
|
void reset() {
|
|
if (object_ && queue_) {
|
|
queue_->Unref(object_.release());
|
|
}
|
|
queue_ = nullptr;
|
|
FML_DCHECK(object_ == nullptr);
|
|
}
|
|
|
|
private:
|
|
sk_sp<SkiaObjectType> object_;
|
|
fml::RefPtr<SkiaUnrefQueue> queue_;
|
|
|
|
FML_DISALLOW_COPY_AND_ASSIGN(SkiaGPUObject);
|
|
};
|
|
|
|
} // namespace flutter
|
|
|
|
#endif // FLUTTER_FLOW_SKIA_GPU_OBJECT_H_
|