blob: 781eb740b38bca28b94cd4703f6553e5beb3087e [file] [log] [blame]
// Copyright 2018 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 <lib/sysmem/sysmem.h>
#include <fbl/algorithm.h>
#include <fuchsia/sysmem/c/fidl.h>
#include <lib/fidl/bind.h>
#include <ddk/debug.h>
#include <lib/zx/vmo.h>
#include <string.h>
#include <zircon/assert.h>
#include <zircon/syscalls.h>
zx_status_t PickImageFormat(const fuchsia_sysmem_BufferSpec &spec,
fuchsia_sysmem_ImageFormat *format,
size_t *buffer_size) {
// For the simple case, just use whatever format was given.
*format_out = {
.width = spec.image.min_width;
.height = spec.image.min_height;
.layers = spec.image.layers;
.pixel_format = spec.image.pixel_format;
.color_space = spec.image.color_space;
}
// Need to choose bytes_per_row, which depends on pixel_format:
// (More generally, it also depends on color space and BufferUsage,
// but this is a simplified version.)
switch (spec.image.pixel_format) {
case fuchsia_sysmem_PixelFormatType_R8G8B8A8:
case fuchsia_sysmem_PixelFormatType_BGRA32:
format->layers[0].bytes_per_row = 4 * format->width;
*buffer_size = fbl::round_up(format->image.height * format->image.planes[0].bytes_per_row, ZX_PAGE_SIZE);
break;
case fuchsia_sysmem_PixelFormatType_I420: // An NxN Y plane and (N/2)x(N/2) U and V planes.
format->layers[0].bytes_per_row = format->width;
format->layers[1].bytes_per_row = format->width / 2;
format->layers[1].byte_offset = format->layers[0].bytes_per_row * format->height;
format->layers[2].bytes_per_row = format->width / 2;
format->layers[2].byte_offset = format->layers[1].byte_offset + format->layers[1].bytes_per_row * format->height / 2;
*buffer_size = fbl::round_up(ormat->layers[2].byte_offset + format->layers[2].bytes_per_row * format->height / 2, ZX_PAGE_SIZE);
break;
}
// This should be called with a fully specified BufferFormat, after the allocator has
// decided what format to use:
size_t GetBufferSize(const fuchsia_sysmem_BufferFormat& format) {
// Simple GetBufferSize. Only valid for simple formats:
return fbl::round_up(format.image.height * format.image.planes[0].bytes_per_row, ZX_PAGE_SIZE);
}
static zx_status_t Allocator_AllocateCollection(void* ctx,
uint32_t buffer_count,
const fuchsia_sysmem_BufferSpec* spec,
const fuchsia_sysmem_BufferUsage* usage,
fidl_txn_t* txn) {
fuchsia_sysmem_BufferCollectionInfo info;
memset(&info, 0, sizeof(info));
// Most basic usage of the allocator: create vmos with no special vendor format:
// 1) Pick which format gets used. For the simple case, just use whatever format was given.
// We also assume here that the format is an ImageFormat
ZX_ASSERT(info.format.tag == fuchsia_sysmem_BufferFormatTagimage);
info.format = *format;
// 2) Determine the size of the buffer from the format.
info.vmo_size = GetBufferSize(info.format);
// 3) Allocate the buffers. This will be specialized for different formats.
info.buffer_count = buffer_count;
zx_status_t status;
for (uint32_t i = 0; i < buffer_count; ++i) {
status = zx_vmo_create(info.vmo_size, 0, &info.vmos[i]);
if (status != ZX_OK) {
// Close the handles we created already. We do not support partial allocations.
for (uint32_t j = 0; j < i; ++j) {
zx_handle_close(info.vmos[j]);
}
info.buffer_count = 0;
zxlogf(ERROR, "Failed to allocate Buffer Collection\n");
return fuchsia_sysmem_AllocatorAllocateCollection_reply(txn, ZX_ERR_NO_MEMORY, &info);
}
}
// If everything is happy and allocated, can give ZX_OK:
return fuchsia_sysmem_AllocatorAllocateCollection_reply(txn, ZX_OK, &info);
}
static zx_status_t Allocator_AllocateSharedCollection(void* ctx,
uint32_t buffer_count,
const fuchsia_sysmem_BufferSpec* spec,
zx_handle_t token_peer,
fidl_txn_t* txn) {
return fuchsia_sysmem_AllocatorAllocateSharedCollection_reply(txn, ZX_ERR_NOT_SUPPORTED);
}
static zx_status_t Allocator_BindSharedCollection(void* ctx,
const fuchsia_sysmem_BufferUsage* usage,
zx_handle_t token,
fidl_txn_t* txn) {
fuchsia_sysmem_BufferCollectionInfo info;
memset(&info, 0, sizeof(info));
return fuchsia_sysmem_AllocatorBindSharedCollection_reply(txn, ZX_ERR_NOT_SUPPORTED, &info);
}
static constexpr const fuchsia_sysmem_Allocator_ops_t allocator_ops = {
.AllocateCollection = Allocator_AllocateCollection,
.AllocateSharedCollection = Allocator_AllocateSharedCollection,
.BindSharedCollection = Allocator_BindSharedCollection,
};
static zx_status_t connect(void* ctx, async_dispatcher_t* dispatcher,
const char* service_name, zx_handle_t request) {
if (!strcmp(service_name, fuchsia_sysmem_Allocator_Name)) {
return fidl_bind(dispatcher, request,
(fidl_dispatch_t*)fuchsia_sysmem_Allocator_dispatch,
ctx, &allocator_ops);
}
zx_handle_close(request);
return ZX_ERR_NOT_SUPPORTED;
}
static constexpr const char* sysmem_services[] = {
fuchsia_sysmem_Allocator_Name,
nullptr,
};
static constexpr zx_service_ops_t sysmem_ops = {
.init = nullptr,
.connect = connect,
.release = nullptr,
};
static constexpr zx_service_provider_t sysmem_service_provider = {
.version = SERVICE_PROVIDER_VERSION,
.services = sysmem_services,
.ops = &sysmem_ops,
};
const zx_service_provider_t* sysmem_get_service_provider() {
return &sysmem_service_provider;
}