1// Copyright 2018 The Fuchsia Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <fbl/algorithm.h> 6#include <lib/zx/event.h> 7#include <lib/zx/vmar.h> 8#include <lib/zx/vmo.h> 9#include <stdio.h> 10#include <string.h> 11#include <zircon/device/display-controller.h> 12#include <zircon/process.h> 13#include <zircon/syscalls.h> 14 15#include "fuchsia/display/c/fidl.h" 16#include "image.h" 17#include "utils.h" 18 19static constexpr uint32_t kRenderPeriod = 120; 20 21Image::Image(uint32_t width, uint32_t height, int32_t stride, 22 zx_pixel_format_t format, zx_handle_t vmo, void* buf, 23 uint32_t fg_color, uint32_t bg_color, bool cursor) 24 : width_(width), height_(height), stride_(stride), format_(format), 25 vmo_(vmo), buf_(buf), fg_color_(fg_color), bg_color_(bg_color), cursor_(cursor) {} 26 27Image* Image::Create(zx_handle_t dc_handle, 28 uint32_t width, uint32_t height, zx_pixel_format_t format, 29 uint32_t fg_color, uint32_t bg_color, bool cursor) { 30 fuchsia_display_ControllerComputeLinearImageStrideRequest stride_msg; 31 stride_msg.hdr.ordinal = fuchsia_display_ControllerComputeLinearImageStrideOrdinal; 32 stride_msg.width = width; 33 stride_msg.pixel_format = format; 34 35 fuchsia_display_ControllerComputeLinearImageStrideResponse stride_rsp; 36 zx_channel_call_args_t stride_call = {}; 37 stride_call.wr_bytes = &stride_msg; 38 stride_call.rd_bytes = &stride_rsp; 39 stride_call.wr_num_bytes = sizeof(stride_msg); 40 stride_call.rd_num_bytes = sizeof(stride_rsp); 41 uint32_t actual_bytes, actual_handles; 42 if (zx_channel_call(dc_handle, 0, ZX_TIME_INFINITE, 43 &stride_call, &actual_bytes, &actual_handles) != ZX_OK) { 44 printf("Failed to make stride call\n"); 45 return nullptr; 46 } 47 48 if (stride_rsp.stride < width) { 49 printf("Invalid stride\n"); 50 return nullptr; 51 } 52 53 zx::vmo vmo; 54 fuchsia_display_ControllerAllocateVmoRequest alloc_msg; 55 alloc_msg.hdr.ordinal = fuchsia_display_ControllerAllocateVmoOrdinal; 56 if (format == ZX_PIXEL_FORMAT_NV12) { 57 alloc_msg.size = stride_rsp.stride * height * ZX_PIXEL_FORMAT_BYTES(format) * 3 / 2; 58 } else if (!USE_INTEL_Y_TILING || cursor) { 59 alloc_msg.size = stride_rsp.stride * height * ZX_PIXEL_FORMAT_BYTES(format); 60 } else { 61 ZX_ASSERT(ZX_PIXEL_FORMAT_BYTES(format) == TILE_BYTES_PER_PIXEL); 62 alloc_msg.size = fbl::round_up(width, TILE_PIXEL_WIDTH) * 63 fbl::round_up(height, TILE_PIXEL_HEIGHT) * TILE_BYTES_PER_PIXEL; 64 } 65 66 fuchsia_display_ControllerAllocateVmoResponse alloc_rsp; 67 zx_channel_call_args_t call_args = {}; 68 call_args.wr_bytes = &alloc_msg; 69 call_args.rd_bytes = &alloc_rsp; 70 call_args.rd_handles = vmo.reset_and_get_address(); 71 call_args.wr_num_bytes = sizeof(alloc_msg); 72 call_args.rd_num_bytes = sizeof(alloc_rsp); 73 call_args.rd_num_handles = 1; 74 if (zx_channel_call(dc_handle, 0, ZX_TIME_INFINITE, &call_args, 75 &actual_bytes, &actual_handles) != ZX_OK) { 76 printf("Vmo alloc call failed\n"); 77 return nullptr; 78 } 79 if (alloc_rsp.res != ZX_OK) { 80 printf("Failed to alloc vmo %d\n", alloc_rsp.res); 81 return nullptr; 82 } 83 84 uintptr_t addr; 85 uint32_t perms = ZX_VM_PERM_READ | ZX_VM_PERM_WRITE; 86 if (zx::vmar::root_self()->map(0, vmo, 0, alloc_msg.size, perms, &addr) != ZX_OK) { 87 printf("Failed to map vmar\n"); 88 return nullptr; 89 } 90 91 uint32_t* ptr = reinterpret_cast<uint32_t*>(addr); 92 for (unsigned i = 0; i < alloc_msg.size / sizeof(uint32_t); i++) { 93 ptr[i] = bg_color; 94 } 95 zx_cache_flush(ptr, alloc_msg.size, ZX_CACHE_FLUSH_DATA); 96 97 return new Image(width, height, stride_rsp.stride, format, 98 vmo.release(), ptr, fg_color, bg_color, cursor); 99} 100 101#define STRIPE_SIZE 37 // prime to make movement more interesting 102 103void Image::Render(int32_t prev_step, int32_t step_num) { 104 if (format_ == ZX_PIXEL_FORMAT_NV12) { 105 uint32_t byte_stride = stride_ * ZX_PIXEL_FORMAT_BYTES(format_); 106 uint32_t real_height = height_; 107 for (uint32_t y = 0; y < real_height; y++) { 108 uint8_t* buf = static_cast<uint8_t*>(buf_) + y * stride_; 109 memset(buf, 128, stride_); 110 } 111 112 for (uint32_t y = 0; y < real_height / 2; y++) { 113 for (uint32_t x = 0; x < width_ / 2; x++) { 114 uint8_t* buf = 115 static_cast<uint8_t*>(buf_) + real_height * stride_ + y * stride_ + x * 2; 116 int32_t in_stripe = (((x * 2) / STRIPE_SIZE % 2) != ((y * 2) / STRIPE_SIZE % 2)); 117 if (in_stripe) { 118 buf[0] = 16; 119 buf[1] = 256 - 16; 120 } else { 121 buf[0] = 256 - 16; 122 buf[1] = 16; 123 } 124 } 125 } 126 zx_cache_flush(reinterpret_cast<uint8_t*>(buf_), byte_stride * height_ * 3 / 2, 127 ZX_CACHE_FLUSH_DATA); 128 } else { 129 uint32_t start, end; 130 bool draw_stripe; 131 if (step_num < 0) { 132 start = 0; 133 end = height_; 134 draw_stripe = true; 135 } else { 136 uint32_t prev = interpolate(height_, prev_step, kRenderPeriod); 137 uint32_t cur = interpolate(height_, step_num, kRenderPeriod); 138 start = fbl::min(cur, prev); 139 end = fbl::max(cur, prev); 140 draw_stripe = cur > prev; 141 } 142 143 for (unsigned y = start; y < end; y++) { 144 for (unsigned x = 0; x < width_; x++) { 145 int32_t in_stripe = draw_stripe && ((x / STRIPE_SIZE % 2) != (y / STRIPE_SIZE % 2)); 146 int32_t color = in_stripe ? fg_color_ : bg_color_; 147 148 uint32_t* ptr = static_cast<uint32_t*>(buf_); 149 if (!USE_INTEL_Y_TILING || cursor_) { 150 ptr += (y * stride_) + x; 151 } else { 152 // Add the offset to the pixel's tile 153 uint32_t width_in_tiles = (width_ + TILE_PIXEL_WIDTH - 1) / TILE_PIXEL_WIDTH; 154 uint32_t tile_idx = 155 (y / TILE_PIXEL_HEIGHT) * width_in_tiles + (x / TILE_PIXEL_WIDTH); 156 ptr += (TILE_NUM_PIXELS * tile_idx); 157 // Add the offset within the pixel's tile 158 uint32_t subtile_column_offset = 159 ((x % TILE_PIXEL_WIDTH) / SUBTILE_COLUMN_WIDTH) * TILE_PIXEL_HEIGHT; 160 uint32_t subtile_line_offset = 161 (subtile_column_offset + (y % TILE_PIXEL_HEIGHT)) * SUBTILE_COLUMN_WIDTH; 162 ptr += subtile_line_offset + (x % SUBTILE_COLUMN_WIDTH); 163 } 164 *ptr = color; 165 } 166 } 167 168 if (!USE_INTEL_Y_TILING || cursor_) { 169 uint32_t byte_stride = stride_ * ZX_PIXEL_FORMAT_BYTES(format_); 170 zx_cache_flush(reinterpret_cast<uint8_t*>(buf_) + (byte_stride * start), 171 byte_stride * (end - start), ZX_CACHE_FLUSH_DATA); 172 } else { 173 uint8_t* buf = static_cast<uint8_t*>(buf_); 174 uint32_t width_in_tiles = (width_ + TILE_PIXEL_WIDTH - 1) / TILE_PIXEL_WIDTH; 175 uint32_t y_start_tile = start / TILE_PIXEL_HEIGHT; 176 uint32_t y_end_tile = (end + TILE_PIXEL_HEIGHT - 1) / TILE_PIXEL_HEIGHT; 177 for (unsigned i = 0; i < width_in_tiles; i++) { 178 for (unsigned j = y_start_tile; j < y_end_tile; j++) { 179 unsigned offset = (TILE_NUM_BYTES * (j * width_in_tiles + i)); 180 zx_cache_flush(buf + offset, TILE_NUM_BYTES, ZX_CACHE_FLUSH_DATA); 181 } 182 } 183 } 184 } 185} 186 187void Image::GetConfig(fuchsia_display_ImageConfig* config_out) { 188 config_out->height = height_; 189 config_out->width = width_; 190 config_out->pixel_format = format_; 191 if (!USE_INTEL_Y_TILING || cursor_) { 192 config_out->type = IMAGE_TYPE_SIMPLE; 193 } else { 194 config_out->type = 2; // IMAGE_TYPE_Y_LEGACY 195 } 196 memset(config_out->planes, 0, sizeof(config_out->planes)); 197 config_out->planes[0].byte_offset = 0; 198 config_out->planes[0].bytes_per_row = stride_ * ZX_PIXEL_FORMAT_BYTES(format_); 199 if (config_out->pixel_format == ZX_PIXEL_FORMAT_NV12) { 200 config_out->planes[1].byte_offset = stride_ * height_; 201 config_out->planes[1].bytes_per_row = stride_ * ZX_PIXEL_FORMAT_BYTES(format_); 202 } 203} 204 205bool Image::Import(zx_handle_t dc_handle, image_import_t* info_out) { 206 for (int i = 0; i < 2; i++) { 207 static int event_id = INVALID_ID + 1; 208 zx_handle_t e1, e2; 209 if (zx_event_create(0, &e1) != ZX_OK 210 || zx_handle_duplicate(e1, ZX_RIGHT_SAME_RIGHTS, &e2) != ZX_OK) { 211 printf("Failed to create event\n"); 212 return false; 213 } 214 215 fuchsia_display_ControllerImportEventRequest import_evt_msg; 216 import_evt_msg.hdr.ordinal = fuchsia_display_ControllerImportEventOrdinal; 217 import_evt_msg.id = event_id++; 218 import_evt_msg.event = FIDL_HANDLE_PRESENT; 219 220 if (zx_channel_write(dc_handle, 0, &import_evt_msg, 221 sizeof(import_evt_msg), &e2, 1) != ZX_OK) { 222 printf("Failed to send import message\n"); 223 return false; 224 } 225 226 if (i != WAIT_EVENT) { 227 zx_object_signal(e1, 0, ZX_EVENT_SIGNALED); 228 } 229 230 info_out->events[i] = e1; 231 info_out->event_ids[i] = import_evt_msg.id; 232 } 233 234 fuchsia_display_ControllerImportVmoImageRequest import_msg; 235 import_msg.hdr.ordinal = fuchsia_display_ControllerImportVmoImageOrdinal; 236 GetConfig(&import_msg.image_config); 237 import_msg.vmo = FIDL_HANDLE_PRESENT; 238 import_msg.offset = 0; 239 zx_handle_t vmo_dup; 240 if (zx_handle_duplicate(vmo_, ZX_RIGHT_SAME_RIGHTS, &vmo_dup) != ZX_OK) { 241 printf("Failed to dup handle\n"); 242 return false; 243 } 244 245 fuchsia_display_ControllerImportVmoImageResponse import_rsp; 246 zx_channel_call_args_t import_call = {}; 247 import_call.wr_bytes = &import_msg; 248 import_call.wr_handles = &vmo_dup; 249 import_call.rd_bytes = &import_rsp; 250 import_call.wr_num_bytes = sizeof(import_msg); 251 import_call.wr_num_handles = 1; 252 import_call.rd_num_bytes = sizeof(import_rsp); 253 uint32_t actual_bytes, actual_handles; 254 if (zx_channel_call(dc_handle, 0, ZX_TIME_INFINITE, &import_call, 255 &actual_bytes, &actual_handles) != ZX_OK) { 256 printf("Failed to make import call\n"); 257 return false; 258 } 259 260 if (import_rsp.res != ZX_OK) { 261 printf("Failed to import vmo\n"); 262 return false; 263 } 264 265 info_out->id = import_rsp.image_id; 266 267 return true; 268} 269