| // 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. |
| |
| #include "media/fuchsia/common/sysmem_client.h" |
| |
| #include <lib/sys/cpp/component_context.h> |
| #include <zircon/rights.h> |
| |
| #include <algorithm> |
| #include <string_view> |
| |
| #include "base/fuchsia/fuchsia_logging.h" |
| #include "base/fuchsia/process_context.h" |
| #include "base/functional/bind.h" |
| #include "base/process/process_handle.h" |
| #include "media/fuchsia/common/vmo_buffer.h" |
| |
| namespace media { |
| |
| SysmemCollectionClient::SysmemCollectionClient( |
| fuchsia::sysmem2::Allocator* allocator, |
| fuchsia::sysmem2::BufferCollectionTokenPtr collection_token) |
| : allocator_(allocator), collection_token_(std::move(collection_token)) { |
| DCHECK(allocator_); |
| DCHECK(collection_token_); |
| } |
| |
| SysmemCollectionClient::~SysmemCollectionClient() { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| if (collection_) |
| collection_->Release(); |
| } |
| |
| void SysmemCollectionClient::Initialize( |
| fuchsia::sysmem2::BufferCollectionConstraints constraints, |
| std::string_view name, |
| uint32_t name_priority) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| |
| uint32_t cpu = 0; |
| if (constraints.has_usage() && constraints.usage().has_cpu()) { |
| cpu = constraints.usage().cpu(); |
| } |
| writable_ = (cpu & fuchsia::sysmem2::CPU_USAGE_WRITE) == |
| fuchsia::sysmem2::CPU_USAGE_WRITE; |
| |
| allocator_->BindSharedCollection( |
| std::move(fuchsia::sysmem2::AllocatorBindSharedCollectionRequest{} |
| .set_token(std::move(collection_token_)) |
| .set_buffer_collection_request(collection_.NewRequest()))); |
| |
| collection_.set_error_handler( |
| fit::bind_member(this, &SysmemCollectionClient::OnError)); |
| collection_->SetName(std::move(fuchsia::sysmem2::NodeSetNameRequest{} |
| .set_priority(name_priority) |
| .set_name(std::string(name)))); |
| |
| // We may need to send a Sync to ensure previously-started CreateSharedToken() |
| // calls can complete. The Sync completion is how we know that sysmem knows |
| // about the existence of the tokens created by the CreateSharedToken() calls, |
| // which is needed before we can send the token to a different participant. |
| // |
| // CreateSharedToken can complete as soon as this Sync is done. |
| if (!shared_token_ready_closures_.empty()) { |
| collection_->Sync( |
| fit::bind_member(this, &SysmemCollectionClient::OnSyncComplete)); |
| } |
| |
| collection_->SetConstraints(std::move( |
| fuchsia::sysmem2::BufferCollectionSetConstraintsRequest{}.set_constraints( |
| std::move(constraints)))); |
| } |
| |
| void SysmemCollectionClient::CreateSharedToken( |
| GetSharedTokenCB cb, |
| std::string_view debug_client_name, |
| uint64_t debug_client_id) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| DCHECK(collection_token_); |
| |
| fuchsia::sysmem2::BufferCollectionTokenPtr token; |
| collection_token_->Duplicate( |
| std::move(fuchsia::sysmem2::BufferCollectionTokenDuplicateRequest{} |
| .set_rights_attenuation_mask(ZX_RIGHT_SAME_RIGHTS) |
| .set_token_request(token.NewRequest()))); |
| |
| if (!debug_client_name.empty()) { |
| token->SetDebugClientInfo( |
| std::move(fuchsia::sysmem2::NodeSetDebugClientInfoRequest{} |
| .set_name(std::string(debug_client_name)) |
| .set_id(debug_client_id))); |
| } |
| |
| shared_token_ready_closures_.push_back( |
| base::BindOnce(std::move(cb), std::move(token))); |
| } |
| |
| void SysmemCollectionClient::AcquireBuffers(AcquireBuffersCB cb) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| DCHECK(!collection_token_); |
| |
| if (!collection_) { |
| std::move(cb).Run({}, {}); |
| return; |
| } |
| |
| acquire_buffers_cb_ = std::move(cb); |
| collection_->WaitForAllBuffersAllocated( |
| fit::bind_member(this, &SysmemCollectionClient::OnBuffersAllocated)); |
| } |
| |
| void SysmemCollectionClient::OnSyncComplete( |
| fuchsia::sysmem2::Node_Sync_Result sync_result) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| |
| std::vector<base::OnceClosure> sync_closures = |
| std::move(shared_token_ready_closures_); |
| for (auto& cb : sync_closures) { |
| std::move(cb).Run(); |
| } |
| } |
| |
| void SysmemCollectionClient::OnBuffersAllocated( |
| fuchsia::sysmem2::BufferCollection_WaitForAllBuffersAllocated_Result |
| wait_result) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| |
| if (!wait_result.is_response()) { |
| zx_status_t error_status; |
| if (wait_result.is_framework_err()) { |
| ZX_LOG(ERROR, fidl::ToUnderlying(wait_result.framework_err())) |
| << "Failed to allocate sysmem buffers (framework_err)."; |
| error_status = ZX_ERR_INTERNAL; |
| } else { |
| ZX_LOG(ERROR, static_cast<uint32_t>(wait_result.err())) |
| << "Failed to allocate sysmem buffers (err)."; |
| // no real need to translate from sysmem2::Error to zx_status_t here |
| error_status = ZX_ERR_INTERNAL; |
| } |
| OnError(error_status); |
| return; |
| } |
| auto buffer_collection_info = |
| std::move(*wait_result.response().mutable_buffer_collection_info()); |
| |
| if (acquire_buffers_cb_) { |
| auto buffers = VmoBuffer::CreateBuffersFromSysmemCollection( |
| &buffer_collection_info, writable_); |
| std::move(acquire_buffers_cb_) |
| .Run(std::move(buffers), buffer_collection_info.settings()); |
| } |
| } |
| |
| void SysmemCollectionClient::OnError(zx_status_t status) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| ZX_DLOG(ERROR, status) << "Connection to BufferCollection was disconnected."; |
| collection_.Unbind(); |
| if (acquire_buffers_cb_) |
| std::move(acquire_buffers_cb_).Run({}, {}); |
| } |
| |
| SysmemAllocatorClient::SysmemAllocatorClient(std::string_view client_name) { |
| allocator_ = base::ComponentContextForProcess() |
| ->svc() |
| ->Connect<fuchsia::sysmem2::Allocator>(); |
| |
| allocator_->SetDebugClientInfo( |
| std::move(fuchsia::sysmem2::AllocatorSetDebugClientInfoRequest{} |
| .set_name(std::string(client_name)) |
| .set_id(base::GetCurrentProcId()))); |
| |
| allocator_.set_error_handler([](zx_status_t status) { |
| // Just log a warning. We will handle BufferCollection the failure when |
| // trying to create a new BufferCollection. |
| ZX_DLOG(WARNING, status) |
| << "The fuchsia.sysmem.Allocator channel was disconnected."; |
| }); |
| } |
| |
| SysmemAllocatorClient::~SysmemAllocatorClient() = default; |
| |
| fuchsia::sysmem2::BufferCollectionTokenPtr |
| SysmemAllocatorClient::CreateNewToken() { |
| fuchsia::sysmem2::BufferCollectionTokenPtr collection_token; |
| allocator_->AllocateSharedCollection( |
| std::move(fuchsia::sysmem2::AllocatorAllocateSharedCollectionRequest{} |
| .set_token_request(collection_token.NewRequest()))); |
| return collection_token; |
| } |
| |
| std::unique_ptr<SysmemCollectionClient> |
| SysmemAllocatorClient::AllocateNewCollection() { |
| return std::make_unique<SysmemCollectionClient>(allocator_.get(), |
| CreateNewToken()); |
| } |
| |
| std::unique_ptr<SysmemCollectionClient> |
| SysmemAllocatorClient::BindSharedCollection( |
| fuchsia::sysmem2::BufferCollectionTokenPtr token) { |
| return std::make_unique<SysmemCollectionClient>(allocator_.get(), |
| std::move(token)); |
| } |
| |
| } // namespace media |