blob: fcf8b3e53b0f9bf2987a6230932ba713868a61ef [file] [log] [blame]
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGPU_GPU_EXTERNAL_TEXTURE_H_
#define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGPU_GPU_EXTERNAL_TEXTURE_H_
#include <atomic>
#include "base/task/single_thread_task_runner.h"
#include "media/base/video_frame.h"
#include "third_party/blink/renderer/modules/webgpu/dawn_object.h"
#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
#include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_map.h"
#include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_set.h"
#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
namespace media {
class PaintCanvasVideoRenderer;
} // namespace media
namespace blink {
class ExceptionState;
class GPUExternalTexture;
class GPUExternalTextureDescriptor;
class WebGPUMailboxTexture;
class HTMLVideoElement;
class VideoFrame;
// GPUExternalTexture uses auto expiry mechanism
// (https://www.w3.org/TR/webgpu/#-automatic-expiry-task-source). The
// mechanism requires webgpu to expire GPUExternalTexture when current task
// scope finished by posting expiration task. The expired GPUExternalTexture
// is invalid to submit and needs to call importExternalTexture() to get the
// refreshed GPUExternalTexture object. In implementation side,
// importExternalTexture() also wraps GPUExternalTexture with underly video
// frames. It is possible that multiple importExternalTexture() call with the
// same source tries to wrap the same underly video frames. So a cache system
// has been integrated to avoid re-creating the GPUExternalTexture again and
// again with the same video frame. So importExternalTexture() tries to do:
// - Search cache to see any hit. if not, create a new GPUExternalTexture and
// insert it into cache.
// - Refresh the external texture to un-expire it.
// - Post a task to expire this external texture after finishing current task.
// More details refers to
// https://www.w3.org/TR/webgpu/#external-texture-creation
class ExternalTextureCache : public GarbageCollected<ExternalTextureCache> {
public:
explicit ExternalTextureCache(GPUDevice* device);
ExternalTextureCache(const ExternalTextureCache&) = delete;
ExternalTextureCache& operator=(const ExternalTextureCache&) = delete;
// Implement importExternalTexture() auto expiry mechanism.
GPUExternalTexture* Import(const GPUExternalTextureDescriptor* descriptor,
ExceptionState& exception_state);
// Destroy all cached GPUExternalTexture and clear all lists.
void Destroy();
void Add(HTMLVideoElement* video, GPUExternalTexture* external_texture);
void Remove(HTMLVideoElement* video);
void Add(VideoFrame* frame, GPUExternalTexture* external_texture);
void Remove(VideoFrame* frame);
void ReferenceUntilGPUIsFinished(
scoped_refptr<WebGPUMailboxTexture> mailbox_texture);
void Trace(Visitor* visitor) const;
GPUDevice* device() const;
private:
void ExpireAtEndOfTask(GPUExternalTexture* external_texture);
void ExpireTask();
// Keep a list of all active GPUExternalTexture. Eagerly destroy them
// when the device is destroyed (via .destroy) to free the memory.
HeapHashMap<WeakMember<HTMLVideoElement>, WeakMember<GPUExternalTexture>>
from_html_video_element_;
HeapHashMap<WeakMember<VideoFrame>, WeakMember<GPUExternalTexture>>
from_video_frame_;
bool expire_task_scheduled_ = false;
HeapHashSet<Member<GPUExternalTexture>> expire_set_;
Member<GPUDevice> device_;
};
class GPUExternalTexture : public DawnObject<wgpu::ExternalTexture> {
DEFINE_WRAPPERTYPEINFO();
public:
static GPUExternalTexture* CreateExpired(
ExternalTextureCache* cache,
const GPUExternalTextureDescriptor* webgpu_desc,
ExceptionState& exception_state);
static GPUExternalTexture* FromHTMLVideoElement(
ExternalTextureCache* cache,
HTMLVideoElement* video,
const GPUExternalTextureDescriptor* webgpu_desc,
ExceptionState& exception_state);
static GPUExternalTexture* FromVideoFrame(
ExternalTextureCache* cache,
VideoFrame* frame,
const GPUExternalTextureDescriptor* webgpu_desc,
ExceptionState& exception_state);
explicit GPUExternalTexture(
ExternalTextureCache* cache,
wgpu::ExternalTexture external_texture,
scoped_refptr<WebGPUMailboxTexture> mailbox_texture,
bool is_zero_copy,
bool read_lock_fences_enabled,
std::optional<media::VideoFrame::ID> media_video_frame_unique_id,
const String& label);
GPUExternalTexture(const GPUExternalTexture&) = delete;
GPUExternalTexture& operator=(const GPUExternalTexture&) = delete;
// gpu_external_texture.idl {{{
bool isZeroCopy() const;
// }}} End of WebIDL binding implementation.
bool IsReadLockFenceEnabled() const;
void Destroy();
void Expire();
void Refresh();
void SetVideo(HTMLVideoElement* video);
// Returns true iff the video frame is still available
bool ListenToVideoFrame(VideoFrame* frame);
// Check whether current VideoFrame is outdated from HTMLVideoElement. Pure
// video playback might not trigger any script animation work. Check video
// frame states in import to ensure cache refresh.
bool NeedsToUpdate();
// GPUExternalTexture from VideoFrame expires when VideoFrame is closed. Note
// that all back resources destroyed needs to happen on the thread that
// GPUExternalTexture is created.
// In multithread situation, the callback should change the state of external
// texture to State::Expired and post a task to issue the destroy.
void OnVideoFrameClosed();
void Trace(Visitor* visitor) const override;
private:
// [1] [2]
// Creation -> [Active] --> [Expired] --> [Destroyed]
// ^ |
// |-------------
// [3]
//
// [1] Happens when the current task finishes: the GPUExternalTexture cannot
// be used util it is refreshed [2] Happens when the source changes frames,
// the texture can no longer be refreshed. [3] Happens when the texture is
// refreshed by being re-imported.
enum class Status { Active, Expired, Destroyed };
static GPUExternalTexture* CreateImpl(
ExternalTextureCache* cache,
const GPUExternalTextureDescriptor* webgpu_desc,
scoped_refptr<media::VideoFrame> media_video_frame,
media::PaintCanvasVideoRenderer* video_renderer,
std::optional<media::VideoFrame::ID> media_video_frame_unique_id,
ExceptionState& exception_state);
void SetLabelImpl(const String& value) override {
std::string utf8_label = value.Utf8();
GetHandle().SetLabel(utf8_label.c_str());
}
bool IsCurrentFrameFromHTMLVideoElementValid();
// This is the function to push a task to destroy the external texture when
// the imported video frame in GPUDevice cache is outdated. The function is
// used as callback function and be registered to the imported
// Blink::VideoFrame or HTMLVideoElement.
void OnSourceInvalidated();
// GPUDevice holds cache for GPUExternalTextures to handling import same
// frame multiple time cases.
void RemoveFromCache();
bool IsActive() const;
bool IsExpired() const;
bool IsDestroyed() const;
scoped_refptr<WebGPUMailboxTexture> mailbox_texture_;
bool is_zero_copy_ = false;
bool remove_from_cache_task_scheduled_ = false;
// read_lock_fences_enabled_ comes from media::VideoFrame metadata.
// VideoFrame set this metadata as a hint to ensure all previous gpu
// execution complete before returning video frame to producer.
bool read_lock_fences_enabled_ = false;
std::optional<media::VideoFrame::ID> media_video_frame_unique_id_;
WeakMember<HTMLVideoElement> video_;
WeakMember<VideoFrame> frame_;
WeakMember<ExternalTextureCache> cache_;
scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
std::atomic<Status> status_ = Status::Active;
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGPU_GPU_EXTERNAL_TEXTURE_H_