| // Copyright 2023 The Chromium Authors | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "gpu/command_buffer/client/client_shared_image.h" | |
| #include <GLES2/gl2.h> | |
| #include <GLES2/gl2extchromium.h> | |
| #include <optional> | |
| #include <utility> | |
| #include "base/check_is_test.h" | |
| #include "base/containers/to_vector.h" | |
| #include "base/debug/crash_logging.h" | |
| #include "base/feature_list.h" | |
| #include "base/logging.h" | |
| #include "base/notreached.h" | |
| #include "base/strings/string_util.h" | |
| #include "base/task/bind_post_task.h" | |
| #include "base/task/single_thread_task_runner.h" | |
| #include "base/task/thread_pool.h" | |
| #include "base/trace_event/process_memory_dump.h" | |
| #include "base/trace_event/trace_event.h" | |
| #include "components/viz/common/resources/shared_image_format_utils.h" | |
| #include "gpu/command_buffer/client/context_support.h" | |
| #include "gpu/command_buffer/client/gles2_interface.h" | |
| #include "gpu/command_buffer/client/internal/mappable_buffer.h" | |
| #include "gpu/command_buffer/client/internal/mappable_buffer_shared_memory.h" | |
| #include "gpu/command_buffer/client/shared_image_interface.h" | |
| #include "gpu/command_buffer/client/webgpu_interface.h" | |
| #include "gpu/command_buffer/common/mailbox.h" | |
| #include "gpu/command_buffer/common/shared_image_capabilities.h" | |
| #include "gpu/command_buffer/common/shared_image_usage.h" | |
| #include "gpu/command_buffer/common/sync_token.h" | |
| #include "gpu/config/gpu_finch_features.h" | |
| #include "mojo/public/cpp/bindings/callback_helpers.h" | |
| #include "third_party/dawn/include/dawn/wire/client/webgpu_cpp.h" | |
| #include "third_party/perfetto/include/perfetto/tracing/track.h" | |
| #include "ui/gfx/buffer_types.h" | |
| #include "ui/gfx/buffer_usage_util.h" | |
| #include "ui/gfx/gpu_fence.h" | |
| #if BUILDFLAG(IS_APPLE) | |
| #include "gpu/command_buffer/client/internal/mappable_buffer_io_surface.h" | |
| #endif | |
| #if BUILDFLAG(IS_OZONE) | |
| #include "gpu/command_buffer/client/internal/mappable_buffer_native_pixmap.h" | |
| #include "ui/ozone/public/client_native_pixmap_factory_ozone.h" | |
| #include "ui/ozone/public/ozone_platform.h" | |
| #endif | |
| #if BUILDFLAG(IS_WIN) | |
| #include "gpu/command_buffer/client/internal/mappable_buffer_dxgi.h" | |
| #endif | |
| #if BUILDFLAG(IS_ANDROID) | |
| #include "gpu/command_buffer/client/internal/mappable_buffer_ahb.h" | |
| #endif | |
| namespace gpu { | |
| namespace { | |
| #if BUILDFLAG(IS_OZONE) || BUILDFLAG(IS_ANDROID) | |
| bool GMBIsNative(gfx::GpuMemoryBufferType gmb_type) { | |
| return gmb_type != gfx::EMPTY_BUFFER && gmb_type != gfx::SHARED_MEMORY_BUFFER; | |
| } | |
| #endif | |
| // Computes the texture target to use for a SharedImage that was created with | |
| // `metadata` and the given type of GpuMemoryBuffer(Handle) supplied by the | |
| // client (which will be gfx::EmptyBuffer if the client did not supply a | |
| // GMB/GMBHandle). Conceptually: | |
| // * On Mac the native buffer target is required if either (1) the client | |
| // gave a native buffer or (2) the usages require a native buffer. And this | |
| // matters only when running on ANGLE OpenGL/CGL - in all other cases we use | |
| // GL_TEXTURE_2D including with Graphite and on iOS (EAGL instead of CGL). | |
| // * On Ozone the native buffer target is required iff external sampling is | |
| // being used, which is dictated by the format of the SharedImage. Note | |
| // * Fuchsia does not support import of external images to GL for usage with | |
| // external sampling. The ClientSharedImage's texture target must be 0 in | |
| // the case where external sampling would be used to signal this lack of | |
| // support to the //media code, which detects the lack of support *based on* | |
| // on the texture target being 0. | |
| // * On all other platforms GL_TEXTURE_2D is always used (external sampling is | |
| // supported in Chromium only on Ozone). | |
| uint32_t ComputeTextureTargetForSharedImage( | |
| SharedImageMetadata metadata, | |
| gfx::GpuMemoryBufferType client_gmb_type) { | |
| CHECK_GE(metadata.array_layers, 1u); | |
| #if !BUILDFLAG(IS_ANDROID) | |
| CHECK_EQ(metadata.array_layers, 1u); | |
| #endif | |
| #if !BUILDFLAG(IS_OZONE) && !BUILDFLAG(IS_ANDROID) | |
| return GL_TEXTURE_2D; | |
| #else // Ozone or Android | |
| #if BUILDFLAG(IS_ANDROID) | |
| if (metadata.array_layers > 1) { | |
| CHECK(GMBIsNative(client_gmb_type)); | |
| constexpr gpu::SharedImageUsageSet kSupportedUsages = | |
| gpu::SHARED_IMAGE_USAGE_GLES2_READ | | |
| gpu::SHARED_IMAGE_USAGE_GLES2_WRITE; | |
| CHECK(kSupportedUsages.HasAll(metadata.usage)); | |
| CHECK(metadata.format.is_single_plane()); | |
| return GL_TEXTURE_2D_ARRAY; | |
| } | |
| #endif | |
| // Check for external sampling being used. | |
| if (!metadata.format.PrefersExternalSampler()) { | |
| return GL_TEXTURE_2D; | |
| } | |
| // The client should configure an SI to use external sampling only if they | |
| // have provided a native buffer to back that SI. | |
| CHECK(GMBIsNative(client_gmb_type)); | |
| // See the note at the top of this function wrt Fuchsia. | |
| #if BUILDFLAG(IS_FUCHSIA) | |
| return 0; | |
| #else | |
| return GL_TEXTURE_EXTERNAL_OES; | |
| #endif // BUILDFLAG(IS_FUCHSIA) | |
| #endif // !BUILDFLAG(IS_OZONE) && !BUILDFLAG(IS_ANDROID) | |
| } | |
| } // namespace | |
| SharedImageExportResult::SharedImageExportResult() = default; | |
| SharedImageExportResult::~SharedImageExportResult() = default; | |
| SharedImageExportResult::SharedImageExportResult(SharedImageExportResult&&) = | |
| default; | |
| SharedImageExportResult& SharedImageExportResult::operator=( | |
| SharedImageExportResult&&) = default; | |
| SharedImageExportResult::SharedImageExportResult(const SyncToken& sync_token) | |
| : sync_tokens_{sync_token} {} | |
| SharedImageExportResult::SharedImageExportResult( | |
| std::vector<SyncToken> sync_tokens) | |
| : sync_tokens_(std::move(sync_tokens)) { | |
| CHECK(!sync_tokens_.empty()); | |
| } | |
| bool SharedImageExportResult::HasData() const { | |
| for (const auto& sync_token : sync_tokens_) { | |
| if (sync_token.HasData()) { | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| std::string SharedImageExportResult::ToDebugString() const { | |
| if (sync_tokens_.empty()) { | |
| return "empty"; | |
| } | |
| std::vector<std::string> token_strings = base::ToVector( | |
| sync_tokens_, [](const auto& token) { return token.ToDebugString(); }); | |
| return base::JoinString(token_strings, ","); | |
| } | |
| ClientSharedImage::ScopedMapping::ScopedMapping(const gfx::Size& size, | |
| viz::SharedImageFormat format) | |
| : size_(size), format_(format) {} | |
| ClientSharedImage::ScopedMapping::~ScopedMapping() { | |
| if (buffer_) { | |
| buffer_->Unmap(); | |
| } | |
| } | |
| base::span<uint8_t> ClientSharedImage::ScopedMapping::GetMemoryForPlane( | |
| const uint32_t plane_index) { | |
| CHECK(buffer_); | |
| size_t height_in_pixels = format_.GetPlaneSize(plane_index, Size()).height(); | |
| size_t row_size_in_bytes = viz::SharedMemoryRowSizeForSharedImageFormat( | |
| format_, plane_index, Size().width()) | |
| .value(); | |
| CHECK(height_in_pixels); | |
| CHECK(row_size_in_bytes); | |
| base::span<uint8_t> memory = buffer_->memory(plane_index); | |
| #if BUILDFLAG(IS_OZONE) | |
| // We are currently prevented from doing this tightening for | |
| // NativePixmap-backed MappableBuffers by the fact that | |
| // VideoFrame requires that the buffer returned from this method be of size | |
| // that is equal to the size in its internal layout, which for NativePixmap is | |
| // overridden to be the size of the plane stored in the GMB handle | |
| // (https://source.chromium.org/chromium/chromium/src/+/main:media/base/video_frame.cc;drc=21e6d1583d1b5683f21556f6125b340d25a6b937;l=527). | |
| // TODO(crbug.com/404905709): Eliminate that VideoFrame override and do | |
| // tightening here for NativePixmap. | |
| return memory; | |
| #else | |
| // Note that the stride might be larger than the row size. | |
| // For all rows other than the last, this is legal data for the client to | |
| // access as it's part of the buffer. However, the final row is not | |
| // guaranteed to have padding (it's a system-dependent internal detail). | |
| // Thus, the data that is legal for the client to access should *not* | |
| // include any bytes beyond the actual end of the final row. | |
| size_t span_length = | |
| Stride(plane_index) * (height_in_pixels - 1) + row_size_in_bytes; | |
| return memory.first(span_length); | |
| #endif | |
| } | |
| size_t ClientSharedImage::ScopedMapping::Stride(const uint32_t plane_index) { | |
| CHECK(buffer_); | |
| return buffer_->stride(plane_index); | |
| } | |
| gfx::Size ClientSharedImage::ScopedMapping::Size() { | |
| return size_; | |
| } | |
| bool ClientSharedImage::ScopedMapping::IsSharedMemory() { | |
| CHECK(buffer_); | |
| return buffer_->GetType() == gfx::GpuMemoryBufferType::SHARED_MEMORY_BUFFER; | |
| } | |
| bool ClientSharedImage::ScopedMapping::Init(MappableBuffer* mappable_buffer, | |
| bool is_already_mapped) { | |
| if (!mappable_buffer) { | |
| LOG(ERROR) << "No MappableBuffer."; | |
| return false; | |
| } | |
| if (!is_already_mapped && !mappable_buffer->Map()) { | |
| LOG(ERROR) << "Failed to map the buffer."; | |
| return false; | |
| } | |
| buffer_ = mappable_buffer; | |
| return true; | |
| } | |
| std::unique_ptr<MappableBuffer> | |
| ClientSharedImage::CreateMappableBufferFromHandle( | |
| gfx::GpuMemoryBufferHandle handle, | |
| const gfx::Size& size, | |
| viz::SharedImageFormat format, | |
| gfx::BufferUsage usage, | |
| gpu::SharedImageUsageSet si_usage, | |
| scoped_refptr<base::UnsafeSharedMemoryPool> pool) { | |
| auto copy_native_buffer_to_shmem_callback = | |
| base::BindRepeating(&ClientSharedImage::CopyNativeGmbToSharedMemoryAsync, | |
| base::Unretained(this)); | |
| switch (handle.type) { | |
| case gfx::EMPTY_BUFFER: | |
| NOTREACHED(); | |
| case gfx::SHARED_MEMORY_BUFFER: | |
| return MappableBufferSharedMemory::CreateFromHandle(std::move(handle), | |
| size, format); | |
| #if BUILDFLAG(IS_APPLE) | |
| case gfx::IO_SURFACE_BUFFER: { | |
| bool is_read_only_cpu_usage = | |
| si_usage.Has(SHARED_IMAGE_USAGE_CPU_READ) && | |
| !si_usage.Has(SHARED_IMAGE_USAGE_CPU_WRITE_ONLY); | |
| return MappableBufferIOSurface::CreateFromHandle( | |
| std::move(handle), size, format, is_read_only_cpu_usage); | |
| } | |
| #endif | |
| #if BUILDFLAG(IS_OZONE) | |
| case gfx::NATIVE_PIXMAP: { | |
| // NOTE: This is not used beyond the lifetime of CreateFromHandle(). | |
| auto client_native_pixmap_factory = | |
| ui::CreateClientNativePixmapFactoryOzone(); | |
| return MappableBufferNativePixmap::CreateFromHandle( | |
| client_native_pixmap_factory.get(), std::move(handle), size, format, | |
| usage); | |
| } | |
| #endif | |
| #if BUILDFLAG(IS_WIN) | |
| case gfx::DXGI_SHARED_HANDLE: { | |
| // DXGI handles require GPU roundtrip for mapping, so they will wait | |
| // for event to trigger in async callback. | |
| // So the copy callback must execute in internal thread otherwise there | |
| // will be a deadlock: the waiting thread would be used to process the | |
| // callback reply. | |
| auto wrapped_callback = base::BindRepeating( | |
| &ClientSharedImage::RunOnTaskRunner, base::Unretained(this), | |
| copy_native_buffer_to_shmem_callback); | |
| return MappableBufferDXGI::CreateFromHandle( | |
| std::move(handle), size, format, std::move(wrapped_callback), | |
| std::move(pool)); | |
| } | |
| #endif | |
| #if BUILDFLAG(IS_ANDROID) | |
| case gfx::ANDROID_HARDWARE_BUFFER: { | |
| // ANDROID_HARDWARE_BUFFER handles require GPU roundtrip for mapping, so | |
| // they will wait for event to trigger in async callback. | |
| // So the copy callback must execute in internal thread otherwise there | |
| // will be a deadlock: the waiting thread would be used to process the | |
| // callback reply. | |
| auto wrapped_callback = base::BindRepeating( | |
| &ClientSharedImage::RunOnTaskRunner, base::Unretained(this), | |
| copy_native_buffer_to_shmem_callback); | |
| return MappableBufferAHB::CreateFromHandle( | |
| std::move(handle), size, format, std::move(wrapped_callback), | |
| std::move(pool)); | |
| } | |
| #endif | |
| } | |
| } | |
| // static | |
| std::unique_ptr<ClientSharedImage::ScopedMapping> | |
| ClientSharedImage::ScopedMapping::Create(SharedImageMetadata metadata, | |
| MappableBuffer* mappable_buffer, | |
| bool is_already_mapped) { | |
| auto scoped_mapping = | |
| base::WrapUnique(new ScopedMapping(metadata.size, metadata.format)); | |
| if (!scoped_mapping->Init(mappable_buffer, is_already_mapped)) { | |
| LOG(ERROR) << "ScopedMapping init failed."; | |
| return nullptr; | |
| } | |
| return scoped_mapping; | |
| } | |
| // static | |
| void ClientSharedImage::ScopedMapping::StartCreateAsync( | |
| SharedImageMetadata metadata, | |
| MappableBuffer* mappable_buffer, | |
| base::OnceCallback<void(std::unique_ptr<ScopedMapping>)> result_cb) { | |
| uint64_t track_id = reinterpret_cast<uintptr_t>(mappable_buffer); | |
| TRACE_EVENT_BEGIN( | |
| "gpu", "ClientSharedImage::MapAsync", | |
| perfetto::NamedTrack("ClientSharedImage::MapAsync", track_id), "format", | |
| metadata.format.ToString(), "usage", metadata.usage.ToString(), "size", | |
| metadata.size.ToString()); | |
| mappable_buffer->MapAsync( | |
| base::BindOnce(&ClientSharedImage::ScopedMapping::FinishCreateAsync, | |
| metadata, mappable_buffer, std::move(result_cb))); | |
| } | |
| // static | |
| void ClientSharedImage::ScopedMapping::FinishCreateAsync( | |
| SharedImageMetadata metadata, | |
| MappableBuffer* mappable_buffer, | |
| base::OnceCallback<void(std::unique_ptr<ScopedMapping>)> result_cb, | |
| bool success) { | |
| uint64_t track_id = reinterpret_cast<uintptr_t>(mappable_buffer); | |
| TRACE_EVENT_END( | |
| "gpu", perfetto::NamedTrack("ClientSharedImage::MapAsync", track_id)); | |
| std::unique_ptr<ClientSharedImage::ScopedMapping> mapping; | |
| if (success) { | |
| mapping = ClientSharedImage::ScopedMapping::Create( | |
| metadata, mappable_buffer, /*is_already_mapped=*/true); | |
| } | |
| std::move(result_cb).Run(std::move(mapping)); | |
| } | |
| SkPixmap ClientSharedImage::ScopedMapping::GetSkPixmapForPlane( | |
| const uint32_t plane_index, | |
| SkImageInfo sk_image_info) { | |
| return SkPixmap(sk_image_info, GetMemoryForPlane(plane_index).data(), | |
| Stride(plane_index)); | |
| } | |
| ClientSharedImage::ClientSharedImage( | |
| const Mailbox& mailbox, | |
| const SharedImageInfo& info, | |
| const SyncToken& sync_token, | |
| scoped_refptr<SharedImageInterfaceHolder> sii_holder, | |
| gfx::GpuMemoryBufferType gmb_type) | |
| : mailbox_(mailbox), | |
| metadata_(info), | |
| debug_label_(info.debug_label), | |
| creation_sync_token_(sync_token), | |
| sii_holder_(std::move(sii_holder)), | |
| sii_(base::FeatureList::IsEnabled( | |
| features::kUseStrongRefToSharedImageInterface) | |
| ? sii_holder_->Get() | |
| : nullptr) { | |
| CHECK(!mailbox.IsZero()); | |
| CHECK(sii_holder_); | |
| texture_target_ = ComputeTextureTargetForSharedImage(metadata_, gmb_type); | |
| StoreSyncTokenInternal(sync_token); | |
| } | |
| ClientSharedImage::ClientSharedImage( | |
| const Mailbox& mailbox, | |
| const SharedImageInfo& info, | |
| const SyncToken& sync_token, | |
| scoped_refptr<SharedImageInterfaceHolder> sii_holder, | |
| base::WritableSharedMemoryMapping mapping) | |
| : ClientSharedImage(mailbox, | |
| info, | |
| sync_token, | |
| sii_holder, | |
| gfx::SHARED_MEMORY_BUFFER) { | |
| mappable_buffer_ = MappableBufferSharedMemory::CreateFromMapping( | |
| std::move(mapping), metadata_.size, metadata_.format); | |
| is_software_ = true; | |
| } | |
| ClientSharedImage::ClientSharedImage( | |
| const Mailbox& mailbox, | |
| const SharedImageInfo& info, | |
| const SyncToken& sync_token, | |
| scoped_refptr<SharedImageInterfaceHolder> sii_holder, | |
| uint32_t texture_target) | |
| : mailbox_(mailbox), | |
| metadata_(info), | |
| debug_label_(info.debug_label), | |
| creation_sync_token_(sync_token), | |
| sii_holder_(std::move(sii_holder)), | |
| sii_(base::FeatureList::IsEnabled( | |
| features::kUseStrongRefToSharedImageInterface) | |
| ? sii_holder_->Get() | |
| : nullptr), | |
| texture_target_(texture_target) { | |
| // TODO(crbug.com/391788839): Create GpuMemoryBuffer from handle. | |
| CHECK(!mailbox.IsZero()); | |
| CHECK(sii_holder_); | |
| #if !BUILDFLAG(IS_FUCHSIA) | |
| CHECK(texture_target); | |
| #endif | |
| StoreSyncTokenInternal(sync_token); | |
| } | |
| ClientSharedImage::ClientSharedImage( | |
| ExportedSharedImage exported_si, | |
| scoped_refptr<SharedImageInterfaceHolder> sii_holder) | |
| : ClientSharedImage(std::move(exported_si)) { | |
| sii_holder_ = std::move(sii_holder); | |
| CHECK(sii_holder_); | |
| sii_ = base::FeatureList::IsEnabled( | |
| features::kUseStrongRefToSharedImageInterface) | |
| ? sii_holder_->Get() | |
| : nullptr; | |
| } | |
| ClientSharedImage::ClientSharedImage(ExportedSharedImage exported_si) | |
| : mailbox_(exported_si.mailbox_), | |
| metadata_(exported_si.metadata_), | |
| debug_label_(exported_si.debug_label_), | |
| creation_sync_token_(exported_si.creation_sync_token_), | |
| buffer_usage_(exported_si.buffer_usage_), | |
| texture_target_(exported_si.texture_target_), | |
| is_software_(exported_si.is_software_) { | |
| if (exported_si.buffer_handle_) { | |
| mappable_buffer_ = CreateMappableBufferFromHandle( | |
| std::move(exported_si.buffer_handle_.value()), metadata_.size, | |
| metadata_.format, exported_si.buffer_usage_.value(), metadata_.usage); | |
| } | |
| CHECK(!mailbox_.IsZero()); | |
| #if !BUILDFLAG(IS_FUCHSIA) | |
| CHECK(texture_target_); | |
| #endif | |
| StoreSyncTokenVectorInternal(exported_si.managed_sync_tokens_); | |
| StoreSyncTokenInternal(exported_si.creation_sync_token_); | |
| } | |
| ClientSharedImage::ClientSharedImage( | |
| const Mailbox& mailbox, | |
| const SharedImageInfo& info, | |
| const SyncToken& sync_token, | |
| GpuMemoryBufferHandleInfo handle_info, | |
| scoped_refptr<SharedImageInterfaceHolder> sii_holder, | |
| scoped_refptr<base::UnsafeSharedMemoryPool> shared_memory_pool) | |
| : mailbox_(mailbox), | |
| metadata_(info), | |
| debug_label_(info.debug_label), | |
| creation_sync_token_(sync_token), | |
| mappable_buffer_( | |
| CreateMappableBufferFromHandle(std::move(handle_info.handle), | |
| metadata_.size, | |
| metadata_.format, | |
| handle_info.buffer_usage, | |
| info.usage, | |
| std::move(shared_memory_pool))), | |
| buffer_usage_(handle_info.buffer_usage), | |
| sii_holder_(std::move(sii_holder)), | |
| sii_(base::FeatureList::IsEnabled( | |
| features::kUseStrongRefToSharedImageInterface) | |
| ? sii_holder_->Get() | |
| : nullptr) { | |
| CHECK(!mailbox.IsZero()); | |
| CHECK(sii_holder_); | |
| CHECK(mappable_buffer_); | |
| texture_target_ = ComputeTextureTargetForSharedImage( | |
| metadata_, mappable_buffer_->GetType()); | |
| StoreSyncTokenInternal(sync_token); | |
| } | |
| ClientSharedImage::ClientSharedImage(const Mailbox& mailbox, | |
| const SharedImageInfo& info) | |
| : mailbox_(mailbox), metadata_(info), debug_label_(info.debug_label) { | |
| CHECK(!mailbox.IsZero()); | |
| texture_target_ = GL_TEXTURE_2D; | |
| } | |
| ClientSharedImage::~ClientSharedImage() { | |
| if (!HasHolder()) { | |
| return; | |
| } | |
| auto sii = GetSharedImageInterface(); | |
| if (sii) { | |
| std::vector<SyncToken> sync_tokens; | |
| if (base::FeatureList::IsEnabled( | |
| features::kUseAutomaticSyncTokenManagement)) { | |
| sync_tokens = CollectSyncTokens(); | |
| } else if (destruction_sync_token_.HasData()) { | |
| sync_tokens.push_back(destruction_sync_token_); | |
| } | |
| sii->DestroySharedImage(std::move(sync_tokens), mailbox_); | |
| } | |
| } | |
| size_t ClientSharedImage::GetStrideForVideoFrame(uint32_t plane_index) const { | |
| if (async_map_invoked_callback_for_testing_) { | |
| return viz::SharedMemoryRowSizeForSharedImageFormat(format(), plane_index, | |
| size().width()) | |
| .value(); | |
| } | |
| CHECK(mappable_buffer_); | |
| return mappable_buffer_->stride(plane_index); | |
| } | |
| // Returns whether the underlying resource is shared memory without needing to | |
| // Map() the shared image. This method is supposed to be used by VideoFrame | |
| // temporarily as mentioned above in ::GetStrideForVideoFrame(). | |
| bool ClientSharedImage::IsSharedMemoryForVideoFrame() const { | |
| if (async_map_invoked_callback_for_testing_) { | |
| return true; | |
| } | |
| CHECK(mappable_buffer_); | |
| return mappable_buffer_->GetType() == | |
| gfx::GpuMemoryBufferType::SHARED_MEMORY_BUFFER; | |
| } | |
| bool ClientSharedImage::AsyncMappingIsNonBlocking() const { | |
| if (async_map_invoked_callback_for_testing_) { | |
| return true; | |
| } | |
| CHECK(mappable_buffer_); | |
| return mappable_buffer_->AsyncMappingIsNonBlocking(); | |
| } | |
| uint64_t ClientSharedImage::SignalLatestSyncToken( | |
| std::vector<scoped_refptr<ClientSharedImage>> shared_images, | |
| std::vector<SyncToken> sync_tokens, | |
| base::OnceClosure callback, | |
| SharedImageInterface* sii, | |
| ContextSupport* context_support, | |
| uint64_t pending_callback_id) { | |
| CHECK(sii); | |
| gpu::SyncToken latest_sync_token; | |
| if (base::FeatureList::IsEnabled( | |
| features::kUseAutomaticSyncTokenManagement)) { | |
| SyncPointClientId client_id = context_support->GetSyncPointClientId(); | |
| for (const auto& shared_image : shared_images) { | |
| if (!shared_image) { | |
| continue; | |
| } | |
| base::AutoLock auto_lock(shared_image->lock_); | |
| auto it = shared_image->sync_token_map_.find(client_id); | |
| if (it != shared_image->sync_token_map_.end() && | |
| it->second.release_count() > latest_sync_token.release_count()) { | |
| latest_sync_token = it->second; | |
| } | |
| } | |
| } else { | |
| for (const auto& sync_token : sync_tokens) { | |
| if (sync_token.release_count() > latest_sync_token.release_count()) { | |
| latest_sync_token = sync_token; | |
| } | |
| } | |
| } | |
| uint64_t callback_id = latest_sync_token.release_count(); | |
| if (callback_id == 0u) { | |
| // Run callback immediately if all sync tokens are invalid. | |
| std::move(callback).Run(); | |
| } else if (callback_id != pending_callback_id) { | |
| // If the callback is different from the one the caller is already waiting | |
| // on, pass the callback through to SignalSyncToken. Otherwise the request | |
| // is redundant. | |
| sii->SignalSyncToken({latest_sync_token}, std::move(callback)); | |
| } | |
| return callback_id; | |
| } | |
| void ClientSharedImage::SignalLatestSyncToken( | |
| std::vector<scoped_refptr<ClientSharedImage>> shared_images, | |
| std::vector<SyncToken> sync_tokens, | |
| base::OnceClosure callback, | |
| SharedImageInterface* sii) { | |
| CHECK(sii); | |
| bool has_valid_token = false; | |
| if (base::FeatureList::IsEnabled( | |
| features::kUseAutomaticSyncTokenManagement)) { | |
| sync_tokens.clear(); | |
| for (const auto& shared_image : shared_images) { | |
| if (!shared_image) { | |
| continue; | |
| } | |
| std::vector<SyncToken> image_tokens = shared_image->CollectSyncTokens(); | |
| sync_tokens.insert(sync_tokens.end(), image_tokens.begin(), | |
| image_tokens.end()); | |
| } | |
| has_valid_token = !sync_tokens.empty(); | |
| } else { | |
| for (const auto& sync_token : sync_tokens) { | |
| if (sync_token.HasData()) { | |
| has_valid_token = true; | |
| break; | |
| } | |
| } | |
| } | |
| if (!has_valid_token) { | |
| std::move(callback).Run(); | |
| } else { | |
| sii->SignalSyncToken(std::move(sync_tokens), std::move(callback)); | |
| } | |
| } | |
| bool ClientSharedImage::IsSyncTokenSignaled( | |
| ContextSupport* context_support, | |
| const SyncToken& resource_sync_token) { | |
| CHECK(context_support); | |
| if (base::FeatureList::IsEnabled( | |
| features::kUseAutomaticSyncTokenManagement)) { | |
| base::AutoLock auto_lock(lock_); | |
| SyncPointClientId client_id = context_support->GetSyncPointClientId(); | |
| auto it = sync_token_map_.find(client_id); | |
| if (it != sync_token_map_.end()) { | |
| return context_support->IsSyncTokenSignaled(it->second); | |
| } | |
| return true; | |
| } | |
| // This SyncToken should have been set by calling OrderingBarrier() before | |
| // calling this. | |
| DCHECK(resource_sync_token.HasData()); | |
| // IsSyncTokenSignaled is thread-safe, no need for worker context lock. | |
| return context_support->IsSyncTokenSignaled(resource_sync_token); | |
| } | |
| std::vector<SyncToken> ClientSharedImage::GetSyncTokensForDisplayCompositor( | |
| const SyncToken& sync_token) { | |
| if (base::FeatureList::IsEnabled( | |
| features::kUseAutomaticSyncTokenManagement)) { | |
| return CollectSyncTokens(); | |
| } else { | |
| if (sync_token.HasData()) { | |
| return {sync_token}; | |
| } else { | |
| return {}; | |
| } | |
| } | |
| } | |
| void ClientSharedImage::EndDisplayCompositorAccess( | |
| const SyncToken& sync_token) { | |
| if (base::FeatureList::IsEnabled( | |
| features::kUseAutomaticSyncTokenManagement)) { | |
| StoreSyncTokenInternal(sync_token); | |
| } | |
| } | |
| std::unique_ptr<ClientSharedImage::ScopedMapping> ClientSharedImage::Map() { | |
| TRACE_EVENT("gpu", "ClientSharedImage::Map", "format", | |
| metadata_.format.ToString(), "usage", metadata_.usage.ToString(), | |
| "size", metadata_.size.ToString()); | |
| std::unique_ptr<ClientSharedImage::ScopedMapping> scoped_mapping = | |
| ScopedMapping::Create(metadata_, mappable_buffer_.get(), | |
| /*is_already_mapped=*/false); | |
| if (!scoped_mapping) { | |
| std::ostringstream s; | |
| s << " shared_image Info: {" | |
| << " format: " << format().ToString() << " usage: " << usage().ToString() | |
| << " label: " << debug_label() << " }"; | |
| LOG(ERROR) << "Unable to create ScopedMapping. " << s.str(); | |
| } | |
| return scoped_mapping; | |
| } | |
| void ClientSharedImage::FinishMapAsyncForTests( | |
| base::OnceCallback<void(std::unique_ptr<ScopedMapping>)> result_cb, | |
| bool success) { | |
| std::unique_ptr<ScopedMapping> mapping; | |
| if (success) { | |
| mapping = Map(); | |
| } | |
| std::move(result_cb).Run(std::move(mapping)); | |
| } | |
| void ClientSharedImage::MapAsync( | |
| base::OnceCallback<void(std::unique_ptr<ScopedMapping>)> result_cb) { | |
| if (async_map_invoked_callback_for_testing_) { | |
| if (premapped_for_testing_) { | |
| FinishMapAsyncForTests(std::move(result_cb), true); | |
| } else { | |
| async_map_invoked_callback_for_testing_.Run( | |
| base::BindOnce(&ClientSharedImage::FinishMapAsyncForTests, | |
| base::Unretained(this), std::move(result_cb))); | |
| } | |
| return; | |
| } | |
| ScopedMapping::StartCreateAsync(metadata_, mappable_buffer_.get(), | |
| std::move(result_cb)); | |
| } | |
| gfx::GpuMemoryBufferType ClientSharedImage::GetGpuMemoryBufferType() const { | |
| return mappable_buffer_ ? mappable_buffer_->GetType() : gfx::EMPTY_BUFFER; | |
| } | |
| bool ClientSharedImage::SupportsZeroCopyWebGPUImport() const { | |
| return mappable_buffer_ && mappable_buffer_->SupportsZeroCopyWebGPUImport(); | |
| } | |
| gfx::GpuMemoryBufferHandle ClientSharedImage::CloneGpuMemoryBufferHandle() | |
| const { | |
| // Supported only if this ClientSI is backed by a MappableBuffer that is | |
| // itself backed by a GMBHandle. | |
| CHECK(mappable_buffer_ && !is_software_); | |
| return mappable_buffer_->CloneHandle(); | |
| } | |
| uint32_t ClientSharedImage::GetTextureTarget() { | |
| #if !BUILDFLAG(IS_FUCHSIA) | |
| // Check that `texture_target_` has been initialized (note that on Fuchsia it | |
| // is possible for `texture_target_` to be initialized to 0: Fuchsia does not | |
| // support import of external images to GL for usage with external sampling. | |
| // SetTextureTarget() sets the texture target to 0 in the case where external | |
| // sampling would be used to signal this lack of support to the //media code, | |
| // which detects the lack of support *based on* on the texture target being | |
| // 0). | |
| CHECK(texture_target_); | |
| #endif | |
| return texture_target_; | |
| } | |
| scoped_refptr<ClientSharedImage> ClientSharedImage::MakeUnowned() { | |
| return ClientSharedImage::ImportUnowned(Export()); | |
| } | |
| ExportedSharedImage ClientSharedImage::Export(bool with_buffer_handle) { | |
| if (creation_sync_token_.HasData() && | |
| !creation_sync_token_.verified_flush()) { | |
| auto sii = GetSharedImageInterface(); | |
| // TODO(crbug.com/40286368): We should let ClientSharedImage hold a strong | |
| // SharedImageInterface reference to ensure `sii` is always valid. | |
| if (sii) { | |
| sii->VerifySyncToken(creation_sync_token_); | |
| } else { | |
| creation_sync_token_ = gpu::SyncToken(); | |
| } | |
| } | |
| std::optional<gfx::GpuMemoryBufferHandle> buffer_handle; | |
| std::optional<gfx::BufferUsage> buffer_usage; | |
| if (with_buffer_handle && mappable_buffer_) { | |
| buffer_handle = mappable_buffer_->CloneHandle(); | |
| buffer_usage = buffer_usage_.value(); | |
| } | |
| return ExportedSharedImage(mailbox_, metadata_, creation_sync_token_, | |
| VerifyAndCollectSyncTokens(), debug_label_, | |
| std::move(buffer_handle), buffer_usage, | |
| texture_target_, is_software_); | |
| } | |
| scoped_refptr<ClientSharedImage> ClientSharedImage::ImportUnowned( | |
| ExportedSharedImage exported_shared_image) { | |
| return base::WrapRefCounted<ClientSharedImage>( | |
| new ClientSharedImage(std::move(exported_shared_image))); | |
| } | |
| void ClientSharedImage::CreateGpuFenceForSyncTokens( | |
| std::vector<scoped_refptr<ClientSharedImage>> shared_images, | |
| std::vector<SyncToken> sync_tokens, | |
| gles2::GLES2Interface* gl, | |
| ContextSupport* context_support, | |
| base::OnceCallback<void(std::unique_ptr<gfx::GpuFence>)> callback) { | |
| CHECK(gl && context_support); | |
| if (base::FeatureList::IsEnabled( | |
| features::kUseAutomaticSyncTokenManagement)) { | |
| // Ignore the the input `sync_tokens`. | |
| SyncToken dummy_sync_token; | |
| for (auto& shared_image : shared_images) { | |
| shared_image->WaitSyncTokenInternal(gl, dummy_sync_token); | |
| } | |
| } else { | |
| for (auto& sync_token : sync_tokens) { | |
| gl->WaitSyncTokenCHROMIUM(sync_token.GetConstData()); | |
| } | |
| } | |
| GLuint id = gl->CreateGpuFenceCHROMIUM(); | |
| context_support->GetGpuFence(id, std::move(callback)); | |
| gl->DestroyGpuFenceCHROMIUM(id); | |
| } | |
| gpu::SyncToken ClientSharedImage::BackingWasExternallyUpdated( | |
| const gpu::SyncToken& sync_token) { | |
| CHECK(sii_holder_); | |
| auto sii = GetSharedImageInterface(); | |
| if (!sii) { | |
| return gpu::SyncToken(); | |
| } | |
| sii->UpdateSharedImage(sync_token, mailbox()); | |
| return StoreSyncTokenInternal(sii->GenUnverifiedSyncToken()); | |
| } | |
| gpu::SyncToken ClientSharedImage::BackingWasExternallyUpdated( | |
| std::unique_ptr<gfx::GpuFence> acquire_fence) { | |
| CHECK(sii_holder_); | |
| auto sii = GetSharedImageInterface(); | |
| if (!sii) { | |
| return gpu::SyncToken(); | |
| } | |
| sii->UpdateSharedImage(SyncToken(), std::move(acquire_fence), mailbox()); | |
| return StoreSyncTokenInternal(sii->GenUnverifiedSyncToken()); | |
| } | |
| void ClientSharedImage::OnMemoryDump( | |
| base::trace_event::ProcessMemoryDump* pmd, | |
| const base::trace_event::MemoryAllocatorDumpGuid& buffer_dump_guid, | |
| int importance) { | |
| auto tracing_guid = GetGUIDForTracing(); | |
| pmd->CreateSharedGlobalAllocatorDump(tracing_guid); | |
| pmd->AddOwnershipEdge(buffer_dump_guid, tracing_guid, importance); | |
| } | |
| scoped_refptr<SharedImageInterface> | |
| ClientSharedImage::GetSharedImageInterface() { | |
| if (base::FeatureList::IsEnabled( | |
| features::kUseStrongRefToSharedImageInterface)) { | |
| return sii_; | |
| } else { | |
| return sii_holder_ ? sii_holder_->Get() : nullptr; | |
| } | |
| } | |
| void ClientSharedImage::BeginAccess(bool readonly) { | |
| base::AutoLock lock(lock_); | |
| if (readonly) { | |
| CHECK(!has_writer_ || | |
| usage().Has(SHARED_IMAGE_USAGE_CONCURRENT_READ_WRITE)); | |
| num_readers_++; | |
| } else { | |
| CHECK(!has_writer_); | |
| CHECK(num_readers_ == 0 || | |
| usage().Has(SHARED_IMAGE_USAGE_CONCURRENT_READ_WRITE)); | |
| has_writer_ = true; | |
| } | |
| } | |
| void ClientSharedImage::EndAccess(bool readonly) { | |
| base::AutoLock lock(lock_); | |
| if (readonly) { | |
| CHECK(num_readers_ > 0); | |
| num_readers_--; | |
| } else { | |
| CHECK(has_writer_); | |
| has_writer_ = false; | |
| } | |
| } | |
| std::unique_ptr<SharedImageTexture> ClientSharedImage::CreateGLTexture( | |
| gles2::GLES2Interface* gl) { | |
| CHECK(metadata_.usage.Has(SHARED_IMAGE_USAGE_GLES2_READ) || | |
| metadata_.usage.Has(SHARED_IMAGE_USAGE_GLES2_WRITE)); | |
| return base::WrapUnique(new SharedImageTexture(gl, this)); | |
| } | |
| std::unique_ptr<RasterScopedAccess> ClientSharedImage::BeginRasterAccess( | |
| InterfaceBase* raster_interface, | |
| const SyncToken& sync_token, | |
| bool readonly) { | |
| bool has_raster_usage = | |
| metadata_.usage.Has(SHARED_IMAGE_USAGE_RASTER_READ) || | |
| metadata_.usage.Has(SHARED_IMAGE_USAGE_RASTER_WRITE) || | |
| metadata_.usage.Has(SHARED_IMAGE_USAGE_RASTER_COPY_SOURCE); | |
| CHECK(has_raster_usage); | |
| return base::WrapUnique( | |
| new RasterScopedAccess(raster_interface, this, sync_token, readonly)); | |
| } | |
| std::unique_ptr<RasterScopedAccess> | |
| ClientSharedImage::BeginGLAccessForCopySharedImage(InterfaceBase* gl_interface, | |
| const SyncToken& sync_token, | |
| bool readonly) { | |
| return BeginRasterAccess(gl_interface, sync_token, readonly); | |
| } | |
| #if BUILDFLAG(IS_WIN) | |
| void ClientSharedImage::SetUsePreMappedMemory(bool use_premapped_memory) { | |
| CHECK(mappable_buffer_); | |
| mappable_buffer_->SetUsePreMappedMemory(use_premapped_memory); | |
| } | |
| #endif | |
| // static | |
| scoped_refptr<ClientSharedImage> ClientSharedImage::CreateForTesting() { | |
| return CreateForTesting(viz::SinglePlaneFormat::kRGBA_8888, GL_TEXTURE_2D); | |
| } | |
| // static | |
| scoped_refptr<ClientSharedImage> ClientSharedImage::CreateSoftwareForTesting() { | |
| auto shared_image = CreateForTesting(); // IN-TEST | |
| shared_image->is_software_ = true; | |
| return shared_image; | |
| } | |
| // static | |
| scoped_refptr<ClientSharedImage> ClientSharedImage::CreateForTesting( | |
| const SharedImageMetadata& metadata) { | |
| return CreateForTesting(metadata, GL_TEXTURE_2D); // IN-TEST | |
| } | |
| // static | |
| scoped_refptr<ClientSharedImage> ClientSharedImage::CreateForTesting( | |
| viz::SharedImageFormat format, | |
| uint32_t texture_target) { | |
| SharedImageMetadata metadata; | |
| metadata.format = format; | |
| metadata.size = gfx::Size(64, 64); | |
| metadata.color_space = gfx::ColorSpace::CreateSRGB(); | |
| metadata.surface_origin = kTopLeft_GrSurfaceOrigin; | |
| metadata.alpha_type = kOpaque_SkAlphaType; | |
| metadata.usage = gpu::SharedImageUsageSet(); | |
| return CreateForTesting(metadata, texture_target); | |
| } | |
| // static | |
| scoped_refptr<ClientSharedImage> ClientSharedImage::CreateForTesting( | |
| const gfx::ColorSpace& color_space) { | |
| SharedImageMetadata metadata; | |
| metadata.format = viz::SinglePlaneFormat::kRGBA_8888; | |
| metadata.size = gfx::Size(64, 64); | |
| metadata.color_space = color_space; | |
| metadata.surface_origin = kTopLeft_GrSurfaceOrigin; | |
| metadata.alpha_type = kOpaque_SkAlphaType; | |
| metadata.usage = gpu::SharedImageUsageSet(); | |
| return CreateForTesting(metadata, GL_TEXTURE_2D); // IN-TEST | |
| } | |
| // static | |
| scoped_refptr<ClientSharedImage> ClientSharedImage::CreateForTesting( | |
| SharedImageUsageSet usage) { | |
| SharedImageMetadata metadata; | |
| metadata.format = viz::SinglePlaneFormat::kRGBA_8888; | |
| metadata.size = gfx::Size(64, 64); | |
| metadata.color_space = gfx::ColorSpace::CreateSRGB(); | |
| metadata.surface_origin = kTopLeft_GrSurfaceOrigin; | |
| metadata.alpha_type = kOpaque_SkAlphaType; | |
| metadata.usage = usage; | |
| return CreateForTesting(metadata, GL_TEXTURE_2D); | |
| } | |
| // static | |
| scoped_refptr<ClientSharedImage> ClientSharedImage::CreateForTesting( | |
| const SharedImageMetadata& metadata, | |
| uint32_t texture_target) { | |
| return CreateForTesting(Mailbox::Generate(), metadata, // IN-TEST | |
| SyncToken(), texture_target); | |
| } | |
| // static | |
| scoped_refptr<ClientSharedImage> | |
| ClientSharedImage::CreateForTesting( // IN-TEST | |
| const Mailbox& mailbox, | |
| const SharedImageMetadata& metadata, | |
| const SyncToken& sync_token, | |
| uint32_t texture_target, | |
| bool is_software) { | |
| gpu::ExportedSharedImage exported_shared_image = gpu::ExportedSharedImage( | |
| mailbox, metadata, sync_token, /*managed_sync_tokens=*/{}, | |
| "CSICreateForTesting", | |
| /*buffer_handle=*/std::nullopt, /*buffer_usage=*/std::nullopt, | |
| texture_target, is_software); | |
| auto shared_image = | |
| gpu::ClientSharedImage::ImportUnowned(std::move(exported_shared_image)); | |
| shared_image->is_software_ = is_software; | |
| return shared_image; | |
| } | |
| // static | |
| scoped_refptr<ClientSharedImage> ClientSharedImage::CreateForTesting( | |
| const Mailbox& mailbox, | |
| const SharedImageMetadata& metadata, | |
| const SyncToken& sync_token, | |
| bool premapped, | |
| const AsyncMapInvokedCallback& callback, | |
| gfx::BufferUsage buffer_usage, | |
| scoped_refptr<SharedImageInterfaceHolder> sii_holder) { | |
| SharedImageInfo info(metadata, "CSICreateForTesting"); | |
| gfx::GpuMemoryBufferHandle handle; | |
| MappableBufferSharedMemory::AllocateForTesting(info.size, info.format, | |
| buffer_usage, &handle); | |
| auto mappable_buffer = MappableBufferSharedMemory::CreateFromHandle( | |
| std::move(handle), info.size, info.format); | |
| // Since the |mappable_buffer| here is always a shared memory, clear the | |
| // external sampler prefs if it is already set by client. | |
| // https://issues.chromium.org/339546249. | |
| if (info.format.PrefersExternalSampler()) { | |
| info.format.ClearPrefersExternalSampler(); | |
| if (info.alpha_type == kPremul_SkAlphaType) { | |
| info.alpha_type = kUnpremul_SkAlphaType; | |
| } | |
| } | |
| auto client_si = base::MakeRefCounted<ClientSharedImage>( | |
| mailbox, info, sync_token, sii_holder, gfx::SHARED_MEMORY_BUFFER); | |
| client_si->mappable_buffer_ = std::move(mappable_buffer); | |
| client_si->async_map_invoked_callback_for_testing_ = callback; | |
| client_si->premapped_for_testing_ = premapped; | |
| client_si->buffer_usage_ = buffer_usage; | |
| return client_si; | |
| } | |
| void ClientSharedImage::CopyNativeGmbToSharedMemoryAsync( | |
| gfx::GpuMemoryBufferHandle buffer_handle, | |
| base::UnsafeSharedMemoryRegion memory_region, | |
| base::OnceCallback<void(bool)> callback) { | |
| auto sii = GetSharedImageInterface(); | |
| if (!sii) { | |
| DLOG(WARNING) << "No SharedImageInterface."; | |
| std::move(callback).Run(false); | |
| return; | |
| } | |
| sii->CopyNativeGmbToSharedMemoryAsync( | |
| std::move(buffer_handle), std::move(memory_region), | |
| mojo::WrapCallbackWithDefaultInvokeIfNotRun(std::move(callback), | |
| /*result=*/false)); | |
| } | |
| void ClientSharedImage::RunOnTaskRunner( | |
| MappableBuffer::CopyNativeBufferToShMemCallback callback, | |
| gfx::GpuMemoryBufferHandle buffer_handle, | |
| base::UnsafeSharedMemoryRegion memory_region, | |
| base::OnceCallback<void(bool)> result_cb) { | |
| // Lazily create the |task_runner_|. | |
| if (!copy_native_buffer_to_shmem_task_runner_) { | |
| copy_native_buffer_to_shmem_task_runner_ = | |
| base::ThreadPool::CreateSingleThreadTaskRunner({base::MayBlock()}); | |
| CHECK(copy_native_buffer_to_shmem_task_runner_); | |
| } | |
| if (!copy_native_buffer_to_shmem_task_runner_->BelongsToCurrentThread()) { | |
| copy_native_buffer_to_shmem_task_runner_->PostTask( | |
| FROM_HERE, | |
| base::BindOnce(callback, std::move(buffer_handle), | |
| std::move(memory_region), std::move(result_cb))); | |
| return; | |
| } | |
| callback.Run(std::move(buffer_handle), std::move(memory_region), | |
| std::move(result_cb)); | |
| } | |
| void ClientSharedImage::WaitSyncTokenInternal(InterfaceBase* ib, | |
| const SyncToken& sync_token) { | |
| if (base::FeatureList::IsEnabled( | |
| features::kUseAutomaticSyncTokenManagement)) { | |
| // Ignore the input `sync_token` since all SyncTokens that we need to wait | |
| // upon is already inside this ClientSharedImage instance. | |
| base::AutoLock auto_lock(lock_); | |
| for (const auto& [_, token] : sync_token_map_) { | |
| ib->WaitSyncTokenCHROMIUM(token.GetConstData()); | |
| } | |
| } else { | |
| ib->WaitSyncTokenCHROMIUM(sync_token.GetConstData()); | |
| } | |
| } | |
| void ClientSharedImage::StoreSyncTokenLocked(const SyncToken& sync_token) { | |
| if (!sync_token.HasData()) { | |
| return; | |
| } | |
| gpu::SyncPointClientId client_id = sync_token.GetClientId(); | |
| auto [it, inserted] = sync_token_map_.try_emplace(client_id, sync_token); | |
| if (!inserted) { | |
| // Entry already exists for this client sequence. Only update if the new | |
| // SyncToken is newer or has updated flush state. | |
| if (sync_token.release_count() > it->second.release_count() || | |
| (sync_token.release_count() == it->second.release_count() && | |
| sync_token.verified_flush())) { | |
| it->second = sync_token; | |
| } | |
| } | |
| } | |
| SyncToken ClientSharedImage::StoreSyncTokenInternal( | |
| const SyncToken& sync_token) { | |
| if (base::FeatureList::IsEnabled( | |
| features::kUseAutomaticSyncTokenManagement)) { | |
| base::AutoLock auto_lock(lock_); | |
| StoreSyncTokenLocked(sync_token); | |
| return SyncToken(); | |
| } else { | |
| return sync_token; | |
| } | |
| } | |
| std::vector<SyncToken> ClientSharedImage::StoreSyncTokenVectorInternal( | |
| const std::vector<SyncToken>& sync_tokens) { | |
| if (base::FeatureList::IsEnabled( | |
| features::kUseAutomaticSyncTokenManagement)) { | |
| base::AutoLock auto_lock(lock_); | |
| for (const auto& sync_token : sync_tokens) { | |
| StoreSyncTokenLocked(sync_token); | |
| } | |
| return std::vector<SyncToken>(); | |
| } else { | |
| return sync_tokens; | |
| } | |
| } | |
| SyncToken ClientSharedImage::GenSyncTokenInternal(InterfaceBase* ib) { | |
| SyncToken sync_token; | |
| if (base::FeatureList::IsEnabled( | |
| features::kUseAutomaticSyncTokenManagement)) { | |
| if (GetSharedImageInterface()) { | |
| ib->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData()); | |
| } else { | |
| // Without a valid `sii_holder_`, we cannot verify the SyncToken later, | |
| // so the SyncToken must be verified at generation time. | |
| ib->GenSyncTokenCHROMIUM(sync_token.GetData()); | |
| } | |
| } else { | |
| ib->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData()); | |
| } | |
| return StoreSyncTokenInternal(sync_token); | |
| } | |
| std::unique_ptr<WebGPUTextureScopedAccess> | |
| ClientSharedImage::BeginWebGPUTextureAccess( | |
| webgpu::WebGPUInterface* webgpu, | |
| const SyncToken& sync_token, | |
| const wgpu::dawn::wire::client::Device& device, | |
| const wgpu::dawn::wire::client::TextureDescriptor& desc, | |
| uint64_t usage, | |
| webgpu::MailboxFlags mailbox_flags) { | |
| CHECK(metadata_.usage.Has(SHARED_IMAGE_USAGE_WEBGPU_READ) || | |
| metadata_.usage.Has(SHARED_IMAGE_USAGE_WEBGPU_WRITE)); | |
| return base::WrapUnique(new WebGPUTextureScopedAccess( | |
| webgpu, this, sync_token, device, desc, usage, mailbox_flags)); | |
| } | |
| std::vector<SyncToken> ClientSharedImage::CollectSyncTokensLocked() { | |
| std::vector<SyncToken> sync_tokens; | |
| sync_tokens.reserve(sync_token_map_.size()); | |
| for (const auto& [_, sync_token] : sync_token_map_) { | |
| sync_tokens.push_back(sync_token); | |
| } | |
| return sync_tokens; | |
| } | |
| std::vector<SyncToken> ClientSharedImage::CollectSyncTokens() { | |
| base::AutoLock auto_lock(lock_); | |
| return CollectSyncTokensLocked(); | |
| } | |
| std::vector<SyncToken> ClientSharedImage::VerifyAndCollectSyncTokens() { | |
| base::AutoLock auto_lock(lock_); | |
| auto sii = GetSharedImageInterface(); | |
| if (sii) { | |
| sii->VerifySyncTokens(sync_token_map_, [](auto& entry) -> gpu::SyncToken& { | |
| return entry.second; | |
| }); | |
| } | |
| return CollectSyncTokensLocked(); | |
| } | |
| SharedImageExportResult ClientSharedImage::EndImport( | |
| const SyncToken& sync_token) { | |
| if (base::FeatureList::IsEnabled( | |
| features::kUseAutomaticSyncTokenManagement)) { | |
| // Ignore the input `sync_token` since it must have been already generated | |
| // and stored in this ClientSharedImage. | |
| std::vector<SyncToken> verified_sync_tokens = VerifyAndCollectSyncTokens(); | |
| if (verified_sync_tokens.empty()) { | |
| return SharedImageExportResult{SyncToken()}; | |
| } | |
| return SharedImageExportResult{std::move(verified_sync_tokens)}; | |
| } else { | |
| return SharedImageExportResult{sync_token}; | |
| } | |
| } | |
| SharedImageExportResult ClientSharedImage::EndImport( | |
| const std::vector<SyncToken>& sync_tokens) { | |
| if (base::FeatureList::IsEnabled( | |
| features::kUseAutomaticSyncTokenManagement)) { | |
| // Ignore the input `sync_tokens` since it must have been already generated | |
| // and stored in this ClientSharedImage. | |
| std::vector<SyncToken> verified_sync_tokens = VerifyAndCollectSyncTokens(); | |
| if (verified_sync_tokens.empty()) { | |
| return SharedImageExportResult{SyncToken()}; | |
| } | |
| return SharedImageExportResult{std::move(verified_sync_tokens)}; | |
| } else { | |
| if (sync_tokens.empty()) { | |
| return SharedImageExportResult{SyncToken()}; | |
| } else { | |
| return SharedImageExportResult{sync_tokens}; | |
| } | |
| } | |
| } | |
| // Unpack the SharedImageExportResult. | |
| // This version expects an empty or a single-SyncToken export result. | |
| SyncToken ClientSharedImage::EndExport(SharedImageExportResult&& result) { | |
| if (result.sync_tokens_.empty()) { | |
| return SyncToken(); | |
| } | |
| if (base::FeatureList::IsEnabled( | |
| features::kUseAutomaticSyncTokenManagement)) { | |
| auto tokens = StoreSyncTokenVectorInternal(std::move(result.sync_tokens_)); | |
| return tokens.empty() ? SyncToken() : tokens.back(); | |
| } else { | |
| CHECK(result.sync_tokens_.size() == 1); | |
| return StoreSyncTokenInternal(result.sync_tokens_[0]); | |
| } | |
| } | |
| std::vector<SyncToken> ClientSharedImage::EndExportAsVector( | |
| SharedImageExportResult&& result) { | |
| return StoreSyncTokenVectorInternal(std::move(result.sync_tokens_)); | |
| } | |
| ExportedSharedImage::ExportedSharedImage() = default; | |
| ExportedSharedImage::~ExportedSharedImage() = default; | |
| ExportedSharedImage::ExportedSharedImage(ExportedSharedImage&& other) = default; | |
| ExportedSharedImage& ExportedSharedImage::operator=( | |
| ExportedSharedImage&& other) = default; | |
| ExportedSharedImage::ExportedSharedImage( | |
| const Mailbox& mailbox, | |
| const SharedImageMetadata& metadata, | |
| const SyncToken& creation_sync_token, | |
| const std::vector<SyncToken>& managed_sync_tokens, | |
| std::string debug_label, | |
| std::optional<gfx::GpuMemoryBufferHandle> buffer_handle, | |
| std::optional<gfx::BufferUsage> buffer_usage, | |
| uint32_t texture_target, | |
| bool is_software) | |
| : mailbox_(mailbox), | |
| metadata_(metadata), | |
| creation_sync_token_(creation_sync_token), | |
| managed_sync_tokens_(managed_sync_tokens), | |
| debug_label_(debug_label), | |
| buffer_handle_(std::move(buffer_handle)), | |
| buffer_usage_(buffer_usage), | |
| texture_target_(texture_target), | |
| is_software_(is_software) {} | |
| ExportedSharedImage ExportedSharedImage::Clone() const { | |
| std::optional<gfx::GpuMemoryBufferHandle> handle = std::nullopt; | |
| if (buffer_handle_.has_value()) { | |
| handle = buffer_handle_->Clone(); | |
| } | |
| return ExportedSharedImage(mailbox_, metadata_, creation_sync_token_, | |
| managed_sync_tokens_, debug_label_, | |
| std::move(handle), buffer_usage_, texture_target_, | |
| is_software_); | |
| } | |
| SharedImageTexture::ScopedAccess::ScopedAccess(SharedImageTexture* texture, | |
| const SyncToken& sync_token, | |
| bool readonly) | |
| : texture_(texture), readonly_(readonly) { | |
| texture_->shared_image_->WaitSyncTokenInternal(texture_->gl_, sync_token); | |
| texture_->gl_->BeginSharedImageAccessDirectCHROMIUM( | |
| texture->id(), (readonly_) | |
| ? GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM | |
| : GL_SHARED_IMAGE_ACCESS_MODE_READWRITE_CHROMIUM); | |
| } | |
| SharedImageTexture::ScopedAccess::~ScopedAccess() { | |
| CHECK(is_access_ended_); | |
| } | |
| void SharedImageTexture::ScopedAccess::DidEndAccess() { | |
| is_access_ended_ = true; | |
| texture_->DidEndAccess(readonly_); | |
| } | |
| // static | |
| SyncToken SharedImageTexture::ScopedAccess::EndAccess( | |
| std::unique_ptr<SharedImageTexture::ScopedAccess> scoped_shared_image) { | |
| gles2::GLES2Interface* gl = scoped_shared_image->texture_->gl_; | |
| gl->EndSharedImageAccessDirectCHROMIUM(scoped_shared_image->texture_->id()); | |
| scoped_shared_image->DidEndAccess(); | |
| return scoped_shared_image->texture_->shared_image_->GenSyncTokenInternal(gl); | |
| } | |
| SharedImageTexture::SharedImageTexture(gles2::GLES2Interface* gl, | |
| ClientSharedImage* shared_image) | |
| : gl_(gl), shared_image_(shared_image) { | |
| CHECK(gl_); | |
| CHECK(shared_image_); | |
| shared_image_->WaitSyncTokenInternal(gl_, | |
| shared_image_->creation_sync_token()); | |
| id_ = gl_->CreateAndTexStorage2DSharedImageCHROMIUM( | |
| shared_image_->mailbox().name); | |
| } | |
| SharedImageTexture::~SharedImageTexture() { | |
| CHECK(!has_active_access_); | |
| gl_->DeleteTextures(1, &id_); | |
| } | |
| std::unique_ptr<SharedImageTexture::ScopedAccess> | |
| SharedImageTexture::BeginAccess(const SyncToken& sync_token, bool readonly) { | |
| CHECK(!has_active_access_); | |
| if (readonly) { | |
| CHECK(shared_image_->usage().Has(SHARED_IMAGE_USAGE_GLES2_READ)); | |
| } else { | |
| CHECK(shared_image_->usage().Has(SHARED_IMAGE_USAGE_GLES2_WRITE)); | |
| } | |
| has_active_access_ = true; | |
| shared_image_->BeginAccess(readonly); | |
| return base::WrapUnique( | |
| new SharedImageTexture::ScopedAccess(this, sync_token, readonly)); | |
| } | |
| void SharedImageTexture::DidEndAccess(bool readonly) { | |
| has_active_access_ = false; | |
| shared_image_->EndAccess(readonly); | |
| } | |
| RasterScopedAccess::RasterScopedAccess(InterfaceBase* raster_interface, | |
| ClientSharedImage* shared_image, | |
| const SyncToken& sync_token, | |
| bool readonly) | |
| : raster_interface_(raster_interface), | |
| shared_image_(shared_image), | |
| readonly_(readonly) { | |
| CHECK(raster_interface_); | |
| shared_image_->BeginAccess(readonly); | |
| shared_image_->WaitSyncTokenInternal(raster_interface_, sync_token); | |
| if (readonly) { | |
| bool has_read_usage = | |
| shared_image_->usage().Has(SHARED_IMAGE_USAGE_RASTER_READ) || | |
| shared_image_->usage().Has(SHARED_IMAGE_USAGE_RASTER_COPY_SOURCE); | |
| CHECK(has_read_usage); | |
| } else { | |
| bool has_write_usage = | |
| shared_image_->usage().Has(SHARED_IMAGE_USAGE_RASTER_WRITE); | |
| CHECK(has_write_usage); | |
| } | |
| } | |
| // static | |
| SyncToken RasterScopedAccess::EndAccess( | |
| std::unique_ptr<RasterScopedAccess> scoped_access) { | |
| InterfaceBase* raster_interface = scoped_access->raster_interface_; | |
| scoped_access->shared_image_->EndAccess(scoped_access->readonly_); | |
| return scoped_access->shared_image_->GenSyncTokenInternal(raster_interface); | |
| } | |
| WebGPUTextureScopedAccess::WebGPUTextureScopedAccess( | |
| webgpu::WebGPUInterface* webgpu, | |
| ClientSharedImage* shared_image, | |
| const SyncToken& sync_token, | |
| const wgpu::dawn::wire::client::Device& device, | |
| const wgpu::dawn::wire::client::TextureDescriptor& desc, | |
| uint64_t usage, | |
| webgpu::MailboxFlags mailbox_flags) | |
| : webgpu_(webgpu), shared_image_(shared_image) { | |
| // Wait on any work using the image. | |
| shared_image_->WaitSyncTokenInternal(webgpu_, sync_token); | |
| // Produce and inject image to WebGPU texture | |
| webgpu::ReservedTexture reservation = webgpu_->ReserveTexture( | |
| device.Get(), &static_cast<const WGPUTextureDescriptor&>(desc)); | |
| DCHECK(reservation.texture); | |
| // If either |desc.usage| or |usage| contains the following flags, the access | |
| // is not read-only. | |
| const wgpu::TextureUsage write_flags = wgpu::TextureUsage::CopyDst | | |
| wgpu::TextureUsage::RenderAttachment | | |
| wgpu::TextureUsage::StorageBinding; | |
| readonly_ = !((desc.usage | wgpu::TextureUsage{usage}) & write_flags); | |
| if (readonly_) { | |
| CHECK(shared_image_->usage().Has(SHARED_IMAGE_USAGE_WEBGPU_READ)); | |
| } else { | |
| CHECK(shared_image_->usage().Has(SHARED_IMAGE_USAGE_WEBGPU_WRITE)); | |
| } | |
| shared_image_->BeginAccess(readonly_); | |
| texture_ = base::WrapUnique( | |
| new wgpu::Texture(wgpu::Texture::Acquire(reservation.texture))); | |
| device_id_ = reservation.deviceId; | |
| device_generation_ = reservation.deviceGeneration; | |
| texture_id_ = reservation.id; | |
| texture_generation_ = reservation.generation; | |
| // This may fail because gl_backing resource cannot produce dawn | |
| // representation. | |
| webgpu_->AssociateMailbox( | |
| device_id_, device_generation_, texture_id_, texture_generation_, | |
| static_cast<uint64_t>(desc.usage), static_cast<uint64_t>(usage), | |
| reinterpret_cast<const WGPUTextureFormat*>(desc.viewFormats), | |
| base::checked_cast<GLuint>(desc.viewFormatCount), mailbox_flags, | |
| shared_image->mailbox()); | |
| } | |
| WebGPUTextureScopedAccess::~WebGPUTextureScopedAccess() = default; | |
| SyncToken WebGPUTextureScopedAccess::EndAccess( | |
| std::unique_ptr<WebGPUTextureScopedAccess> scoped_access) { | |
| webgpu::WebGPUInterface* webgpu = scoped_access->webgpu_; | |
| if (scoped_access->needs_present_) { | |
| webgpu->DissociateMailboxForPresent( | |
| scoped_access->device_id_, scoped_access->device_generation_, | |
| scoped_access->texture_id_, scoped_access->texture_generation_); | |
| } else { | |
| webgpu->DissociateMailbox(scoped_access->texture_id_, | |
| scoped_access->texture_generation_); | |
| } | |
| scoped_access->shared_image_->EndAccess(scoped_access->readonly_); | |
| return scoped_access->shared_image_->GenSyncTokenInternal(webgpu); | |
| } | |
| const wgpu::dawn::wire::client::Texture& WebGPUTextureScopedAccess::texture() { | |
| return *texture_.get(); | |
| } | |
| void WebGPUTextureScopedAccess::ClearContext() { | |
| webgpu_ = nullptr; | |
| } | |
| void WebGPUTextureScopedAccess::SetNeedsPresent(bool needs_present) { | |
| needs_present_ = needs_present; | |
| } | |
| std::unique_ptr<WebGPUBufferScopedAccess> | |
| ClientSharedImage::BeginWebGPUBufferAccess( | |
| webgpu::WebGPUInterface* webgpu, | |
| const SyncToken& sync_token, | |
| const wgpu::dawn::wire::client::Device& device, | |
| const wgpu::dawn::wire::client::BufferDescriptor& desc, | |
| uint64_t usage, | |
| webgpu::MailboxFlags mailbox_flags) { | |
| CHECK(metadata_.usage.Has(SHARED_IMAGE_USAGE_WEBGPU_SHARED_BUFFER)); | |
| return base::WrapUnique(new WebGPUBufferScopedAccess( | |
| webgpu, this, sync_token, device, desc, usage, mailbox_flags)); | |
| } | |
| WebGPUBufferScopedAccess::WebGPUBufferScopedAccess( | |
| webgpu::WebGPUInterface* webgpu, | |
| ClientSharedImage* shared_image, | |
| const SyncToken& sync_token, | |
| const wgpu::dawn::wire::client::Device& device, | |
| const wgpu::dawn::wire::client::BufferDescriptor& desc, | |
| uint64_t usage, | |
| webgpu::MailboxFlags mailbox_flags) | |
| : webgpu_(webgpu), shared_image_(shared_image) { | |
| // Wait on any work using the buffer. | |
| shared_image_->WaitSyncTokenInternal(webgpu_, sync_token); | |
| webgpu::ReservedBuffer reservation = webgpu_->ReserveBuffer( | |
| device.Get(), &static_cast<const WGPUBufferDescriptor&>(desc)); | |
| DCHECK(reservation.buffer); | |
| buffer_id_ = reservation.id; | |
| buffer_generation_ = reservation.generation; | |
| // We currently only use storage buffers. Which are always read-write. | |
| shared_image_->BeginAccess(false); | |
| buffer_ = base::WrapUnique( | |
| new wgpu::Buffer(wgpu::Buffer::Acquire(reservation.buffer))); | |
| webgpu_->AssociateMailboxForBuffer( | |
| reservation.deviceId, reservation.deviceGeneration, buffer_id_, | |
| buffer_generation_, static_cast<uint64_t>(desc.usage), | |
| shared_image_->mailbox()); | |
| } | |
| WebGPUBufferScopedAccess::~WebGPUBufferScopedAccess() = default; | |
| SyncToken WebGPUBufferScopedAccess::EndAccess( | |
| std::unique_ptr<WebGPUBufferScopedAccess> scoped_access) { | |
| webgpu::WebGPUInterface* webgpu = scoped_access->webgpu_; | |
| SyncToken finished_access_token; | |
| webgpu->DissociateMailboxForBuffer(scoped_access->buffer_id_, | |
| scoped_access->buffer_generation_); | |
| scoped_access->shared_image_->EndAccess(false); | |
| // SyncToken must be verified to allow use from another pipe. | |
| webgpu->GenSyncTokenCHROMIUM(finished_access_token.GetData()); | |
| return finished_access_token; | |
| } | |
| const wgpu::Buffer& WebGPUBufferScopedAccess::buffer() { | |
| return *buffer_.get(); | |
| } | |
| } // namespace gpu |