blob: 6ac7e253d1a2eeac306f486f7c29dc7be782270c [file] [log] [blame]
// Copyright 2017 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 "garnet/bin/guest/vmm/device/gpu_scanout.h"
#include <zx/vmar.h>
#include "garnet/bin/guest/vmm/device/gpu_resource.h"
void GpuScanout::SetConfigChangedHandler(fit::closure config_changed_handler) {
config_changed_handler_ = std::move(config_changed_handler);
}
void GpuScanout::SetUpdateSourceHandler(
fit::function<void(uint32_t, uint32_t)> update_source_handler) {
update_source_handler_ = std::move(update_source_handler);
}
void GpuScanout::SetFlushHandler(
fit::function<void(virtio_gpu_rect_t)> flush_handler) {
flush_handler_ = std::move(flush_handler);
}
zx_status_t GpuScanout::SetFlushTarget(zx::vmo vmo, uint64_t size,
uint32_t width, uint32_t height,
uint32_t stride) {
// Bind the target and map its memory into our process.
target_vmo_ = std::move(vmo);
target_size_ = size;
target_width_ = width;
target_height_ = height;
target_stride_ = stride;
zx_status_t status = zx::vmar::root_self()->map(
0, target_vmo_, 0, target_size_,
ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, &target_vmo_addr_);
if (status != ZX_OK) {
return status;
}
// Notify the client of the current guest source dimensions, in case this is
// the first time it has attached.
if (update_source_handler_) {
update_source_handler_(extents_.width, extents_.height);
}
// Update the scanout extents to match the target.
extents_.width = width;
extents_.height = height;
config_changed_handler_();
// Force a flush of the entire source region to populate the new target.
if (source_resource_) {
OnResourceFlush(source_resource_, source_rect_);
}
return ZX_OK;
}
void GpuScanout::OnSetScanout(const GpuResource* source_resource,
const virtio_gpu_rect_t& source_rect) {
source_resource_ = source_resource;
source_rect_ = source_rect;
if (update_source_handler_) {
update_source_handler_(source_rect.width, source_rect.height);
}
}
static bool overlaps(virtio_gpu_rect_t a, virtio_gpu_rect_t b) {
if (a.x > (b.x + b.width) || b.x > (a.x + a.width)) {
return false;
} else if (a.y > (b.y + b.height) || b.y > (a.y + a.height)) {
return false;
} else {
return true;
}
}
static virtio_gpu_rect_t clip(virtio_gpu_rect_t rect, virtio_gpu_rect_t clip) {
if (rect.x < clip.x) {
rect.width -= clip.x - rect.x;
rect.x = clip.x;
}
if (rect.y < clip.y) {
rect.height -= clip.y - rect.y;
rect.y = clip.y;
}
if (rect.x + rect.width > clip.x + clip.width) {
rect.width = clip.x + clip.width - rect.x;
}
if (rect.y + rect.height > clip.y + clip.height) {
rect.height = clip.y + clip.height - rect.y;
}
return rect;
}
void GpuScanout::OnResourceFlush(const GpuResource* resource,
const virtio_gpu_rect_t& rect) {
if (resource != source_resource_ || !overlaps(rect, source_rect_)) {
return;
}
virtio_gpu_rect_t flush_rect = clip(rect, extents_);
if (target_vmo_) {
// Copy the flushed region to the target.
uint32_t row_begin = flush_rect.y;
uint32_t row_end =
std::min(flush_rect.y + flush_rect.height, target_height_);
uint32_t row_bytes =
std::min(flush_rect.width, target_width_ - flush_rect.x) *
resource->pixel_size();
for (uint32_t row = row_begin; row < row_end; ++row) {
uint8_t* dest = reinterpret_cast<uint8_t*>(target_vmo_addr_) +
target_stride_ * row +
flush_rect.x * resource->pixel_size();
const uint8_t* src = source_resource_->data() +
source_resource_->stride() * row +
flush_rect.x * resource->pixel_size();
memcpy(dest, src, row_bytes);
}
}
if (flush_handler_) {
flush_handler_(flush_rect);
}
}
void GpuScanout::OnUpdateCursor(const GpuResource* cursor_resource,
uint32_t hot_x, uint32_t hot_y) {
cursor_resource_ = cursor_resource;
cursor_hot_x_ = hot_x;
cursor_hot_y_ = hot_y;
}
void GpuScanout::OnMoveCursor(uint32_t x, uint32_t y) {
cursor_x_ = x;
cursor_y_ = y;
}