blob: f0575aa6aa2532e939c7cf0a4c96784d9f58c9bb [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/lib/machina/gpu_resource.h"
#include <string.h>
#include "garnet/lib/machina/gpu_scanout.h"
#include "lib/fxl/logging.h"
static zx_pixel_format_t pixel_format(uint32_t virtio_format) {
switch (virtio_format) {
case VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM:
return ZX_PIXEL_FORMAT_ARGB_8888;
case VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM:
return ZX_PIXEL_FORMAT_RGB_x888;
default:
return ZX_PIXEL_FORMAT_NONE;
}
}
namespace machina {
fbl::unique_ptr<GpuResource> GpuResource::Create(
const virtio_gpu_resource_create_2d_t* request, VirtioGpu* gpu) {
zx_pixel_format_t format = pixel_format(request->format);
if (format == ZX_PIXEL_FORMAT_NONE) {
FXL_LOG(INFO) << "Unsupported GPU format " << request->format;
return nullptr;
}
GpuBitmap bitmap(request->width, request->height, format);
return fbl::make_unique<GpuResource>(gpu, request->resource_id,
fbl::move(bitmap));
}
GpuResource::GpuResource(VirtioGpu* gpu, ResourceId id, GpuBitmap bitmap)
: gpu_(gpu), res_id_(id), bitmap_(fbl::move(bitmap)) {}
virtio_gpu_ctrl_type GpuResource::AttachBacking(
const virtio_gpu_mem_entry_t* mem_entries, uint32_t num_entries) {
const size_t required_bytes =
bitmap_.width() * bitmap_.height() * bitmap_.pixelsize();
size_t backing_size = 0;
for (int i = num_entries - 1; i >= 0; --i) {
const virtio_gpu_mem_entry_t* entry = &mem_entries[i];
backing_.push_front(
fbl::make_unique<BackingPages>(entry->addr, entry->length));
backing_size += entry->length;
}
if (backing_size < required_bytes) {
FXL_LOG(ERROR) << "Attach backing command provided buffer is too small";
backing_.clear();
return VIRTIO_GPU_RESP_ERR_UNSPEC;
}
return VIRTIO_GPU_RESP_OK_NODATA;
}
virtio_gpu_ctrl_type GpuResource::DetachBacking() {
backing_.clear();
return VIRTIO_GPU_RESP_OK_NODATA;
}
virtio_gpu_ctrl_type GpuResource::TransferToHost2D(
const virtio_gpu_transfer_to_host_2d_t* request) {
if (bitmap_.buffer() == nullptr) {
return VIRTIO_GPU_RESP_ERR_UNSPEC;
}
if (backing_.is_empty()) {
return VIRTIO_GPU_RESP_ERR_UNSPEC;
}
// Optimize for copying a contiguous region.
uint8_t* pixel_ref = bitmap_.buffer();
uint32_t stride = bitmap_.width() * bitmap_.pixelsize();
if (request->offset == 0 && request->r.x == 0 && request->r.y == 0 &&
request->r.width == static_cast<uint32_t>(bitmap_.width())) {
CopyBytes(0, pixel_ref, stride * bitmap_.height());
return VIRTIO_GPU_RESP_OK_NODATA;
}
// line-by-line copy.
uint32_t linesize = request->r.width * 4;
for (uint32_t line = 0; line < request->r.height; ++line) {
uint64_t src_offset = request->offset + stride * line;
size_t size =
((request->r.y + line) * stride) + (request->r.x * bitmap_.pixelsize());
CopyBytes(src_offset, pixel_ref + size, linesize);
}
return VIRTIO_GPU_RESP_OK_NODATA;
}
virtio_gpu_ctrl_type GpuResource::Flush(
const virtio_gpu_resource_flush_t* request) {
GpuScanout* scanout = scanout_;
if (scanout == nullptr) {
return VIRTIO_GPU_RESP_OK_NODATA;
}
scanout->DrawScanoutResource(request->r);
return VIRTIO_GPU_RESP_OK_NODATA;
}
void GpuResource::CopyBytes(uint64_t offset, uint8_t* dest, size_t size) {
size_t base = 0;
for (const auto& entry : backing_) {
if (size == 0) {
break;
}
if (base + entry.length > offset) {
size_t len = (entry.length + base) - offset;
len = len > size ? size : len;
zx_vaddr_t src_vaddr = entry.addr + offset - base;
memcpy(dest, gpu_->phys_mem().as<void>(src_vaddr, len), len);
dest += len;
offset += len;
size -= len;
}
base += entry.length;
}
}
} // namespace machina