blob: 9db75d2ee813cd2e28412f7c8926f2522b38a26a [file] [log] [blame]
// Copyright 2021 The Fuchsia 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 "screen_capture_buffer_collection_importer.h"
#include <lib/async/default.h>
#include <lib/trace/event.h>
#include <optional>
namespace {
// Image formats supported by Scenic in a priority order.
const vk::Format kSupportedImageFormats[] = {vk::Format::eR8G8B8A8Srgb, vk::Format::eB8G8R8A8Srgb};
} // anonymous namespace
namespace screen_capture {
ScreenCaptureBufferCollectionImporter::ScreenCaptureBufferCollectionImporter(
std::shared_ptr<flatland::Renderer> renderer)
: dispatcher_(async_get_default_dispatcher()), renderer_(renderer) {}
ScreenCaptureBufferCollectionImporter::~ScreenCaptureBufferCollectionImporter() {
for (auto& id : buffer_collections_) {
renderer_->ReleaseBufferCollection(id);
}
buffer_collections_.clear();
}
bool ScreenCaptureBufferCollectionImporter::ImportBufferCollection(
allocation::GlobalBufferCollectionId collection_id,
fuchsia::sysmem::Allocator_Sync* sysmem_allocator,
fidl::InterfaceHandle<fuchsia::sysmem::BufferCollectionToken> token) {
TRACE_DURATION("gfx", "ScreenCaptureBufferCollectionImporter::ImportBufferCollection");
if (!token.is_valid()) {
FX_LOGS(WARNING) << "ImportBufferCollection called with invalid token";
return false;
}
if (buffer_collections_.find(collection_id) != buffer_collections_.end()) {
FX_LOGS(WARNING) << __func__ << " failed, called with pre-existing collection_id "
<< collection_id << ".";
return false;
}
// Tie the |buffer_collection_info| to the |collection_id| using the |local_token|.
fuchsia::sysmem::BufferCollectionSyncPtr buffer_collection;
fuchsia::sysmem::BufferCollectionTokenSyncPtr local_token = token.BindSync();
fuchsia::sysmem::BufferCollectionTokenSyncPtr vulkan_token;
zx_status_t status =
local_token->Duplicate(std::numeric_limits<uint32_t>::max(), vulkan_token.NewRequest());
if (status != ZX_OK) {
FX_LOGS(WARNING) << __func__ << "failed, could not duplicate token: " << status;
return false;
}
sysmem_allocator->BindSharedCollection(std::move(local_token), buffer_collection.NewRequest());
status = buffer_collection->Sync();
if (status != ZX_OK) {
FX_LOGS(WARNING) << __func__ << " failed, could not bind buffer collection: " << status;
return false;
}
status = buffer_collection->SetConstraints(false, fuchsia::sysmem::BufferCollectionConstraints());
if (status != ZX_OK) {
FX_LOGS(WARNING) << __func__ << " failed, could not set constraints: " << status;
return false;
}
buffer_collection_sync_ptrs_[collection_id] = std::move(buffer_collection);
const bool success = renderer_->RegisterRenderTargetCollection(collection_id, sysmem_allocator,
std::move(vulkan_token));
if (!success) {
ReleaseBufferCollection(collection_id);
FX_LOGS(WARNING) << __func__ << " failed, could not register with Renderer";
return false;
}
buffer_collections_.insert(collection_id);
return true;
}
void ScreenCaptureBufferCollectionImporter::ReleaseBufferCollection(
allocation::GlobalBufferCollectionId collection_id) {
TRACE_DURATION("gfx", "ScreenCaptureBufferCollectionImporter::ReleaseBufferCollection");
auto collection_itr = buffer_collections_.find(collection_id);
// If the collection is not in the map, then there's nothing to do.
if (collection_itr == buffer_collections_.end()) {
FX_LOGS(WARNING) << "Attempting to release a non-existent buffer collection.";
return;
}
buffer_collections_.erase(collection_id);
if (buffer_collection_sync_ptrs_.find(collection_id) != buffer_collection_sync_ptrs_.end()) {
fuchsia::sysmem::BufferCollectionSyncPtr buffer_collection;
buffer_collection_sync_ptrs_.erase(collection_id);
};
if (buffer_collection_buffer_counts_.find(collection_id) !=
buffer_collection_buffer_counts_.end()) {
buffer_collection_buffer_counts_.erase(collection_id);
};
renderer_->DeregisterRenderTargetCollection(collection_id);
}
bool ScreenCaptureBufferCollectionImporter::ImportBufferImage(
const allocation::ImageMetadata& metadata) {
// The metadata can't have an invalid |collection_id|.
if (metadata.collection_id == allocation::kInvalidId) {
FX_LOGS(WARNING) << "Image has invalid collection id.";
return false;
}
// The metadata can't have an invalid identifier.
if (metadata.identifier == allocation::kInvalidImageId) {
FX_LOGS(WARNING) << "Image has invalid identifier.";
return false;
}
// Check for valid dimensions.
if (metadata.width == 0 || metadata.height == 0) {
FX_LOGS(WARNING) << "Image has invalid dimensions: "
<< "(" << metadata.width << ", " << metadata.height << ").";
return false;
}
// Make sure that the collection that will back this image's memory
// is actually registered.
auto collection_itr = buffer_collections_.find(metadata.collection_id);
if (collection_itr == buffer_collections_.end()) {
FX_LOGS(WARNING) << "Collection with id " << metadata.collection_id << " does not exist.";
return false;
}
const std::optional<uint32_t> buffer_count =
GetBufferCollectionBufferCount(metadata.collection_id);
if (!buffer_count.value()) {
FX_LOGS(WARNING) << __func__ << " failed, buffer_count invalid";
return false;
}
if (metadata.vmo_index >= buffer_count) {
FX_LOGS(WARNING) << __func__ << " failed, vmo_index " << metadata.vmo_index << " is invalid";
return false;
}
const bool success = renderer_->ImportBufferImage(metadata);
if (!success) {
FX_LOGS(WARNING) << __func__ << " failed, could not import to Renderer";
return false;
}
return true;
}
void ScreenCaptureBufferCollectionImporter::ReleaseBufferImage(allocation::GlobalImageId image_id) {
renderer_->ReleaseBufferImage(image_id);
}
std::optional<uint32_t> ScreenCaptureBufferCollectionImporter::GetBufferCollectionBufferCount(
allocation::GlobalBufferCollectionId collection_id) {
// If the collection info has not been retrieved before, wait for the buffers to be allocated and
// populate the map/delete the reference to the |collection_id| from |collection_id_sync_ptrs_|.
if (buffer_collection_buffer_counts_.find(collection_id) ==
buffer_collection_buffer_counts_.end()) {
fuchsia::sysmem::BufferCollectionSyncPtr buffer_collection;
zx_status_t allocation_status = ZX_OK;
if (buffer_collection_sync_ptrs_.find(collection_id) == buffer_collection_sync_ptrs_.end()) {
FX_LOGS(WARNING) << "Collection with id " << collection_id << " does not exist.";
return std::nullopt;
}
buffer_collection = std::move(buffer_collection_sync_ptrs_[collection_id]);
buffer_collection->CheckBuffersAllocated(&allocation_status);
if (allocation_status != ZX_OK) {
FX_LOGS(WARNING) << __func__ << " failed, no buffers allocated: " << allocation_status;
return std::nullopt;
}
fuchsia::sysmem::BufferCollectionInfo_2 buffer_collection_info = {};
buffer_collection->WaitForBuffersAllocated(&allocation_status, &buffer_collection_info);
if (allocation_status != ZX_OK) {
FX_LOGS(WARNING) << __func__
<< " failed, waiting on no buffers allocated: " << allocation_status;
return std::nullopt;
}
buffer_collection_sync_ptrs_.erase(collection_id);
buffer_collection->Close();
buffer_collection_buffer_counts_[collection_id] = buffer_collection_info.buffer_count;
}
return buffer_collection_buffer_counts_[collection_id];
}
} // namespace screen_capture