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