| // 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 |