blob: 5a105eae428a10c2edc9f1dbb34650c34f040881 [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 <fbl/algorithm.h>
#include <lib/zx/event.h>
#include <lib/zx/vmar.h>
#include <lib/zx/vmo.h>
#include <stdio.h>
#include <string.h>
#include <zircon/device/display-controller.h>
#include <zircon/process.h>
#include <zircon/syscalls.h>
#include "fuchsia/display/c/fidl.h"
#include "image.h"
#include "utils.h"
static constexpr uint32_t kRenderPeriod = 120;
Image::Image(uint32_t width, uint32_t height, int32_t stride,
zx_pixel_format_t format, zx_handle_t vmo, void* buf, uint32_t fg_color)
: width_(width), height_(height), stride_(stride), format_(format),
vmo_(vmo), buf_(buf), fg_color_(fg_color) {}
Image* Image::Create(zx_handle_t dc_handle,
uint32_t width, uint32_t height, zx_pixel_format_t format, uint32_t fg_color) {
fuchsia_display_ControllerComputeLinearImageStrideRequest stride_msg;
stride_msg.hdr.ordinal = fuchsia_display_ControllerComputeLinearImageStrideOrdinal;
stride_msg.width = width;
stride_msg.pixel_format = format;
fuchsia_display_ControllerComputeLinearImageStrideResponse stride_rsp;
zx_channel_call_args_t stride_call = {};
stride_call.wr_bytes = &stride_msg;
stride_call.rd_bytes = &stride_rsp;
stride_call.wr_num_bytes = sizeof(stride_msg);
stride_call.rd_num_bytes = sizeof(stride_rsp);
uint32_t actual_bytes, actual_handles;
zx_status_t read_status;
if (zx_channel_call(dc_handle, 0, ZX_TIME_INFINITE,
&stride_call, &actual_bytes, &actual_handles, &read_status) != ZX_OK) {
printf("Failed to make stride call\n");
return nullptr;
}
if (stride_rsp.stride < width) {
printf("Invalid stride\n");
return nullptr;
}
zx::vmo vmo;
fuchsia_display_ControllerAllocateVmoRequest alloc_msg;
alloc_msg.hdr.ordinal = fuchsia_display_ControllerAllocateVmoOrdinal;
alloc_msg.size = stride_rsp.stride * height * ZX_PIXEL_FORMAT_BYTES(format);
fuchsia_display_ControllerAllocateVmoResponse alloc_rsp;
zx_channel_call_args_t call_args = {};
call_args.wr_bytes = &alloc_msg;
call_args.rd_bytes = &alloc_rsp;
call_args.rd_handles = vmo.reset_and_get_address();
call_args.wr_num_bytes = sizeof(alloc_msg);
call_args.rd_num_bytes = sizeof(alloc_rsp);
call_args.rd_num_handles = 1;
if (zx_channel_call(dc_handle, 0, ZX_TIME_INFINITE, &call_args,
&actual_bytes, &actual_handles, &read_status) != ZX_OK) {
printf("Vmo alloc call failed\n");
return nullptr;
}
if (alloc_rsp.res != ZX_OK) {
printf("Failed to alloc vmo %d\n", alloc_rsp.res);
return nullptr;
}
uintptr_t addr;
uint32_t len = stride_rsp.stride * height * ZX_PIXEL_FORMAT_BYTES(format);
uint32_t perms = ZX_VM_FLAG_PERM_READ | ZX_VM_FLAG_PERM_WRITE;
if (zx::vmar::root_self().map(0, vmo, 0, len, perms, &addr) != ZX_OK) {
printf("Failed to map vmar\n");
return nullptr;
}
void* ptr = reinterpret_cast<void*>(addr);
memset(ptr, 0xff, len);
zx_cache_flush(ptr, len, ZX_CACHE_FLUSH_DATA);
return new Image(width, height, stride_rsp.stride, format, vmo.release(), ptr, fg_color);
}
#define STRIPE_SIZE 37 // prime to make movement more interesting
void Image::Render(int32_t prev_step, int32_t step_num) {
uint32_t start, end;
bool draw_stripe;
if (step_num < 0) {
start = 0;
end = height_;
draw_stripe = true;
} else {
uint32_t prev = interpolate(height_, prev_step, kRenderPeriod);
uint32_t cur = interpolate(height_, step_num, kRenderPeriod);
start = fbl::min(cur, prev);
end = fbl::max(cur, prev);
draw_stripe = cur > prev;
}
for (unsigned y = start; y < end; y++) {
for (unsigned x = 0; x < width_; x++) {
int32_t in_stripe = draw_stripe && ((x / STRIPE_SIZE % 2) != (y / STRIPE_SIZE % 2));
int32_t color = in_stripe ? fg_color_: 0xffffffff;
*(static_cast<int32_t*>(buf_) + (y * stride_) + x) = color;
}
}
uint32_t byte_stride = stride_ * ZX_PIXEL_FORMAT_BYTES(format_);
zx_cache_flush(reinterpret_cast<uint8_t*>(buf_) + (byte_stride * start),
byte_stride * (end - start), ZX_CACHE_FLUSH_DATA);
}
bool Image::Import(zx_handle_t dc_handle, image_import_t* info_out) {
for (int i = 0; i < 3; i++) {
static int event_id = INVALID_ID + 1;
zx_handle_t e1, e2;
if (zx_event_create(0, &e1) != ZX_OK
|| zx_handle_duplicate(e1, ZX_RIGHT_SAME_RIGHTS, &e2) != ZX_OK) {
printf("Failed to create event\n");
return false;
}
fuchsia_display_ControllerImportEventRequest import_evt_msg;
import_evt_msg.hdr.ordinal = fuchsia_display_ControllerImportEventOrdinal;
import_evt_msg.id = event_id++;
import_evt_msg.event = FIDL_HANDLE_PRESENT;
if (zx_channel_write(dc_handle, 0, &import_evt_msg,
sizeof(import_evt_msg), &e2, 1) != ZX_OK) {
printf("Failed to send import message\n");
return false;
}
if (i != WAIT_EVENT) {
zx_object_signal(e1, 0, ZX_EVENT_SIGNALED);
}
info_out->events[i] = e1;
info_out->event_ids[i] = import_evt_msg.id;
}
fuchsia_display_ControllerImportVmoImageRequest import_msg;
import_msg.hdr.ordinal = fuchsia_display_ControllerImportVmoImageOrdinal;
import_msg.image_config.height = height_;
import_msg.image_config.width = width_;
import_msg.image_config.pixel_format = format_;
import_msg.image_config.type = IMAGE_TYPE_SIMPLE;
import_msg.vmo = FIDL_HANDLE_PRESENT;
import_msg.offset = 0;
zx_handle_t vmo_dup;
if (zx_handle_duplicate(vmo_, ZX_RIGHT_SAME_RIGHTS, &vmo_dup) != ZX_OK) {
printf("Failed to dup handle\n");
return false;
}
fuchsia_display_ControllerImportVmoImageResponse import_rsp;
zx_channel_call_args_t import_call = {};
import_call.wr_bytes = &import_msg;
import_call.wr_handles = &vmo_dup;
import_call.rd_bytes = &import_rsp;
import_call.wr_num_bytes = sizeof(import_msg);
import_call.wr_num_handles = 1;
import_call.rd_num_bytes = sizeof(import_rsp);
uint32_t actual_bytes, actual_handles;
zx_status_t read_status;
if (zx_channel_call(dc_handle, 0, ZX_TIME_INFINITE, &import_call,
&actual_bytes, &actual_handles, &read_status) != ZX_OK) {
printf("Failed to make import call\n");
return false;
}
if (import_rsp.res != ZX_OK) {
printf("Failed to import vmo\n");
return false;
}
info_out->id = import_rsp.image_id;
return true;
}