blob: a6ca4fd6d851d8c3996a34b224ba82ff3ba13d14 [file] [log] [blame]
// Copyright 2019 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 "src/camera/drivers/controller/memory_allocation.h"
#include <lib/syslog/cpp/macros.h>
#include <lib/trace/event.h>
#include <zircon/errors.h>
#include <sstream>
#include "src/lib/fsl/handles/object_info.h"
namespace camera {
constexpr auto kTag = "camera_controller";
ControllerMemoryAllocator::ControllerMemoryAllocator(const ddk::SysmemProtocolClient& sysmem)
: sysmem_(sysmem) {
sysmem_.Connect(sysmem_allocator_.NewRequest().TakeChannel());
}
template <typename T>
static std::string FormatMinMax(const T& v_min, const T& v_max) {
if (v_min == v_max) {
return std::to_string(v_min);
}
bool lowest_min = v_min == std::numeric_limits<T>::min();
bool highest_max = v_max == std::numeric_limits<T>::max();
if (lowest_min && highest_max) {
return "any";
}
if (lowest_min) {
return "≤ " + std::to_string(v_max);
}
if (highest_max) {
return "≥ " + std::to_string(v_min);
}
return std::to_string(v_min) + " - " + std::to_string(v_max);
}
static std::string FormatConstraints(
const std::vector<fuchsia::sysmem::BufferCollectionConstraints>& constraints) {
std::stringstream ss;
for (uint32_t i = 0; i < constraints.size(); ++i) {
const auto& element = constraints[i];
ss << " [" << i << "] " << element.min_buffer_count_for_camping << " camping, "
<< element.min_buffer_count << " total\n";
for (uint32_t j = 0; j < element.image_format_constraints_count; ++j) {
const auto& format = element.image_format_constraints[j];
ss << " [" << j << "] ("
<< FormatMinMax(format.required_min_coded_width, format.required_max_coded_width)
<< ") x ("
<< FormatMinMax(format.required_min_coded_height, format.required_max_coded_height)
<< ")\n";
}
}
return ss.str();
}
zx_status_t ControllerMemoryAllocator::AllocateSharedMemory(
const std::vector<fuchsia::sysmem::BufferCollectionConstraints>& constraints,
BufferCollection& out_buffer_collection, const std::string& name) const {
TRACE_DURATION("camera", "ControllerMemoryAllocator::AllocateSharedMemory");
FX_LOGST(INFO, kTag) << "AllocateSharedMemory (name = " << name
<< ") with the following constraints:\n"
<< FormatConstraints(constraints);
auto num_constraints = constraints.size();
if (!num_constraints) {
return ZX_ERR_INVALID_ARGS;
}
// Create tokens which we'll hold on to to get our buffer_collection.
std::vector<fuchsia::sysmem::BufferCollectionTokenSyncPtr> tokens(num_constraints);
// Start the allocation process.
auto status = sysmem_allocator_->AllocateSharedCollection(tokens[0].NewRequest());
if (status != ZX_OK) {
FX_LOGST(ERROR, kTag) << "Failed to create token";
return status;
}
// Duplicate the tokens.
for (uint32_t i = 1; i < num_constraints; i++) {
status = tokens[0]->Duplicate(std::numeric_limits<uint32_t>::max(), tokens[i].NewRequest());
if (status != ZX_OK) {
FX_LOGST(ERROR, kTag) << "Failed to duplicate token";
return status;
}
}
// Now convert into a Logical BufferCollection:
std::vector<fuchsia::sysmem::BufferCollectionSyncPtr> buffer_collections(num_constraints);
status = sysmem_allocator_->BindSharedCollection(std::move(tokens[0]),
buffer_collections[0].NewRequest());
if (status != ZX_OK) {
FX_LOGST(ERROR, kTag) << "Failed to create logical buffer collection";
return status;
}
status = buffer_collections[0]->Sync();
if (status != ZX_OK) {
FX_LOGST(ERROR, kTag) << "Failed to sync";
return status;
}
constexpr uint32_t kNamePriority = 10u;
buffer_collections[0]->SetName(kNamePriority, name);
// Create rest of the logical buffer collections
for (uint32_t i = 1; i < num_constraints; i++) {
status = sysmem_allocator_->BindSharedCollection(std::move(tokens[i]),
buffer_collections[i].NewRequest());
if (status != ZX_OK) {
FX_LOGST(ERROR, kTag) << "Failed to create logical buffer collection";
return status;
}
}
// Set constraints
for (uint32_t i = 0; i < num_constraints; i++) {
status = buffer_collections[i]->SetConstraints(true, constraints[i]);
if (status != ZX_OK) {
FX_LOGST(ERROR, kTag) << "Failed to set buffer collection constraints";
return status;
}
}
zx_status_t allocation_status;
status = buffer_collections[0]->WaitForBuffersAllocated(&allocation_status,
&out_buffer_collection.buffers);
if (status != ZX_OK) {
FX_LOGST(ERROR, kTag) << "Failed to wait for buffer collection info.";
return status;
}
if (allocation_status != ZX_OK) {
FX_LOGST(ERROR, kTag) << "Failed to allocate buffer collection.";
return allocation_status;
}
// Leave first collection handle open to return
out_buffer_collection.ptr = buffer_collections[0].Unbind().Bind();
for (uint32_t i = 1; i < num_constraints; i++) {
status = buffer_collections[i]->Close();
if (status != ZX_OK) {
FX_LOGST(ERROR, kTag) << "Failed to close producer buffer collection";
return status;
}
}
return ZX_OK;
}
fuchsia::sysmem::BufferCollectionHandle ControllerMemoryAllocator::AttachObserverCollection(
fuchsia::sysmem::BufferCollectionTokenHandle& token) {
// Temporarily bind the provided token so it can be duplicated.
auto ptr = token.BindSync();
fuchsia::sysmem::BufferCollectionTokenHandle observer;
ZX_ASSERT(ptr->Duplicate(ZX_RIGHT_SAME_RIGHTS, observer.NewRequest()) == ZX_OK);
ZX_ASSERT(ptr->Sync() == ZX_OK);
// Return the channel to the provided token.
token = ptr.Unbind();
// Bind the new token to a collection, set constraints, and return the client end to the caller.
fuchsia::sysmem::BufferCollectionSyncPtr collection;
ZX_ASSERT(sysmem_allocator_->BindSharedCollection(std::move(observer), collection.NewRequest()) ==
ZX_OK);
ZX_ASSERT(collection->SetConstraints(true, {.usage{.none = fuchsia::sysmem::noneUsage}}) ==
ZX_OK);
ZX_ASSERT(collection->Sync() == ZX_OK);
return collection.Unbind();
}
} // namespace camera