aboutsummaryrefslogtreecommitdiff
path: root/src/gfxstream/guest/GoldfishAddressSpace/include/address_space_graphics_types.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/gfxstream/guest/GoldfishAddressSpace/include/address_space_graphics_types.h')
-rw-r--r--src/gfxstream/guest/GoldfishAddressSpace/include/address_space_graphics_types.h362
1 files changed, 362 insertions, 0 deletions
diff --git a/src/gfxstream/guest/GoldfishAddressSpace/include/address_space_graphics_types.h b/src/gfxstream/guest/GoldfishAddressSpace/include/address_space_graphics_types.h
new file mode 100644
index 00000000000..e52c8fe22d4
--- /dev/null
+++ b/src/gfxstream/guest/GoldfishAddressSpace/include/address_space_graphics_types.h
@@ -0,0 +1,362 @@
+// Copyright 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#pragma once
+
+#include "aemu/base/ring_buffer.h"
+
+#include <functional>
+#include <cstddef>
+
+// This file defines common types for address space graphics and provides
+// documentation.
+
+// Address space graphics======================================================
+//
+// Basic idea
+//
+// Address space graphics (ASG) is a subdevice of the address space device that
+// provides a way to run graphics commands and data with fewer VM exits by
+// leveraging shared memory ring buffers.
+//
+// Each GL/Vk thread in the guest is associated with a context (asg_context).
+// asg_context consists of pointers into the shared memory that view it as a
+// collection of ring buffers and a common write buffer.
+//
+// Consumer concept
+//
+// ASG does not assume a particular rendering backend (though we will use
+// RenderThread's). This is for ease of coding/testing and flexibility; the
+// implementation is not coupled to emugl/libOpenglRender.
+//
+// Instead, there is the concept of a "Consumer" of ASG that will do something
+// with the data arriving from the shared memory region, and possibly reply
+// back to the guest. We register functions to construct and deconstruct
+// Consumers as part of emulator init (setConsumer).
+//
+// Guest workflow
+//
+// 1. Open address space device
+//
+// 2. Create the graphics context as the subdevice
+//
+// 3. ping(ASG_GET_RING) to get the offset/size of the ring buffer admin. info
+//
+// 4. ping(ASG_GET_BUFFER) to get the offset/size of the shared transfer buffer.
+//
+// 5. ioctl(CLAIM_SHARED) and mmap on those two offset/size pairs to get a
+// guest-side mapping.
+//
+// 6. call asg_context_create on the ring and buffer pointers to create the asg_context.
+//
+// 7. Now the guest and host share asg_context pts and can communicate.
+//
+// 8. But usually the guest will sometimes need to ping(ASG_NOTIFY_AVAILABLE)
+// so that the host side (which is usually a separate thread that we don't want
+// to spin too much) wakes up and processes data.
+
+namespace gfxstream {
+namespace guest {
+
+class Stream;
+
+} // namespace base
+} // namespace android
+
+#define ADDRESS_SPACE_GRAPHICS_DEVICE_ID 0
+#define ADDRESS_SPACE_GRAPHICS_PAGE_SIZE 4096
+#define ADDRESS_SPACE_GRAPHICS_BLOCK_SIZE (16ULL * 1048576ULL)
+
+// AddressSpaceGraphicsContext shares memory with
+// the guest via the following layout:
+extern "C" {
+
+struct asg_ring_storage { // directly shared with guest
+ char to_host[ADDRESS_SPACE_GRAPHICS_PAGE_SIZE];
+ char to_host_large_xfer[ADDRESS_SPACE_GRAPHICS_PAGE_SIZE];
+ char from_host_large_xfer[ADDRESS_SPACE_GRAPHICS_PAGE_SIZE];
+};
+
+// Set by the address space graphics device to notify the guest that the host
+// has slept or is able to consume something, or we are exiting, or there is an
+// error.
+enum asg_host_state {
+ // The host renderthread is asleep and needs to be woken up.
+ ASG_HOST_STATE_NEED_NOTIFY = 0,
+
+ // The host renderthread is active and can consume new data
+ // without notification.
+ ASG_HOST_STATE_CAN_CONSUME = 1,
+
+ // Normal exit
+ ASG_HOST_STATE_EXIT = 2,
+
+ // Error: Something weird happened and we need to exit.
+ ASG_HOST_STATE_ERROR = 3,
+
+ // Host is rendering
+ ASG_HOST_STATE_RENDERING = 4,
+};
+
+struct asg_ring_config;
+
+// Each context has a pair of ring buffers for communication
+// to and from the host. There is another ring buffer for large xfers
+// to the host (all xfers from the host are already considered "large").
+//
+// Each context also comes with _one_ auxiliary buffer to hold both its own
+// commands and to perform private DMA transfers.
+struct asg_context { // ptrs into RingStorage
+ struct ring_buffer* to_host;
+ char* buffer;
+ asg_host_state* host_state;
+ asg_ring_config* ring_config;
+ struct ring_buffer_with_view to_host_large_xfer;
+ struct ring_buffer_with_view from_host_large_xfer;
+};
+
+// Helper function that will be common between guest and host:
+// Given ring storage and a write buffer, returns asg_context that
+// is the correct view into it.
+static inline struct asg_context asg_context_create(
+ char* ring_storage,
+ char* buffer,
+ uint32_t buffer_size) {
+
+ struct asg_context res;
+
+ res.to_host =
+ reinterpret_cast<struct ring_buffer*>(
+ ring_storage +
+ offsetof(struct asg_ring_storage, to_host));
+ res.to_host_large_xfer.ring =
+ reinterpret_cast<struct ring_buffer*>(
+ ring_storage +
+ offsetof(struct asg_ring_storage, to_host_large_xfer));
+ res.from_host_large_xfer.ring =
+ reinterpret_cast<struct ring_buffer*>(
+ ring_storage +
+ offsetof(struct asg_ring_storage, from_host_large_xfer));
+
+ ring_buffer_init(res.to_host);
+
+ res.buffer = buffer;
+ res.host_state =
+ reinterpret_cast<asg_host_state*>(
+ &res.to_host->state);
+ res.ring_config =
+ reinterpret_cast<asg_ring_config*>(
+ res.to_host->config);
+
+ ring_buffer_view_init(
+ res.to_host_large_xfer.ring,
+ &res.to_host_large_xfer.view,
+ (uint8_t*)res.buffer, buffer_size);
+
+ ring_buffer_view_init(
+ res.from_host_large_xfer.ring,
+ &res.from_host_large_xfer.view,
+ (uint8_t*)res.buffer, buffer_size);
+
+ return res;
+}
+
+// During operation, the guest sends commands and data over the auxiliary
+// buffer while using the |to_host| ring to communicate what parts of the auxiliary
+// buffer is outstanding traffic needing to be consumed by the host.
+// After a transfer completes to the host, the host may write back data.
+// The guest then reads the results on the same auxiliary buffer
+// while being notified of which parts to read via the |from_host| ring.
+//
+// The size of the auxiliary buffer and flush interval is defined by
+// the following config.ini android_hw setting:
+//
+// 1) android_hw->hw_gltransport_asg_writeBufferSize
+// 2) android_hw->hw_gltransport_asg_writeStepSize
+//
+// 1) the size for the auxiliary buffer
+// 2) the step size over which commands are flushed to the host
+//
+// When transferring commands, command data is built up in writeStepSize
+// chunks and flushed to the host when either writeStepSize is reached or
+// the guest flushes explicitly.
+//
+// Command vs. Data Modes
+//
+// For command data larger than writeStepSize or when transferring data, we
+// fall back to using a different mode where the entire auxiliary buffer is
+// used to perform the transfer, |asg_writeBufferSize| steps at a time. The
+// host is also notified of the total transport size.
+//
+// When writing back to the guest, it is assumed that the write buffer will
+// be completely empty as the guest has already flushed and the host has
+// already consumed all commands/data, and is writing back. In this case,
+// the full auxiliary buffer is used at the same time for writing back to
+// the guest.
+//
+// Larger / Shared transfers
+//
+// Each of |to_host| and |from_host| can contain elements of type 1, 2, or 3:
+// Type 1: 8 bytes: 4 bytes offset, 4 bytes size. Relative to write buffer.
+struct __attribute__((__packed__)) asg_type1_xfer {
+ uint32_t offset;
+ uint32_t size;
+};
+// Type 2: 16 bytes: 16 bytes offset into address space PCI space, 8 bytes
+// size.
+struct __attribute__((__packed__)) asg_type2_xfer {
+ uint64_t physAddr;
+ uint64_t size;
+};
+// Type 3: There is a large transfer of known size and the entire write buffer
+// will be used to send it over.
+//
+// For type 1 transfers, we get the corresponding host virtual address by
+// adding the offset to the beginning of the write buffer. For type 2
+// transfers, we need to calculate the guest physical address and then call
+// addressspacecontrolops.gethostptr, which is slower since it goes through
+// a data structure map of existing mappings.
+//
+// The rings never contain a mix of type 1 and 2 elements. For to_host,
+// the guest initiates changes between type 1 and 2.
+//
+// The config fields:
+//
+struct asg_ring_config {
+ // config[0]: size of the auxiliary buffer
+ uint32_t buffer_size;
+
+ // config[1]: flush interval for the auxiliary buffer
+ uint32_t flush_interval;
+
+ // the position of the interval in the auxiliary buffer
+ // that the host has read so far
+ uint32_t host_consumed_pos;
+
+ // the start of the places the guest might write to next
+ uint32_t guest_write_pos;
+
+ // 1 if transfers are of type 1, 2 if transfers of type 2,
+ // 3 if the overall transfer size is known and we are sending something large.
+ uint32_t transfer_mode;
+
+ // the size of the transfer, used if transfer size is known.
+ // Set before setting config[2] to 3.
+ uint32_t transfer_size;
+
+ // error state
+ uint32_t in_error;
+};
+
+// State/config changes may only occur if the ring is empty, or the state
+// is transitioning to Error. That way, the host and guest have a chance to
+// synchronize on the same state.
+//
+// Thus far we've established how commands and data are transferred
+// to and from the host. Next, let's discuss how AddressSpaceGraphicsContext
+// talks to the code that actually does something with the commands
+// and sends data back.
+
+} // extern "C"
+
+namespace android {
+namespace emulation {
+namespace asg {
+
+// Consumer Concept
+//
+// AddressSpaceGraphicsContext's are each associated with a consumer that
+// takes data off the auxiliary buffer and to_host, while sending back data
+// over the auxiliary buffer / from_host.
+//
+// will read the commands and write back data.
+//
+// The consumer type is fixed at startup. The interface is as follows:
+
+// Called by the consumer, implemented in AddressSpaceGraphicsContext:
+//
+// Called when the consumer doesn't find anything to
+// read in to_host. Will make the consumer sleep
+// until another Ping(NotifyAvailable).
+using OnUnavailableReadCallback =
+ std::function<int()>;
+
+// Unpacks a type 2 transfer into host pointer and size.
+using GetPtrCallback =
+ std::function<char*(uint64_t)>;
+
+struct ConsumerCallbacks {
+ OnUnavailableReadCallback onUnavailableRead;
+ GetPtrCallback getPtr;
+};
+
+using ConsumerCreateCallback =
+ std::function<void* (struct asg_context, ConsumerCallbacks)>;
+using ConsumerDestroyCallback =
+ std::function<void(void*)>;
+using ConsumerSaveCallback =
+ std::function<void(void*, gfxstream::guest::Stream*)>;
+using ConsumerLoadCallback =
+ std::function<void(void*, gfxstream::guest::Stream*)>;
+
+struct ConsumerInterface {
+ ConsumerCreateCallback create;
+ ConsumerDestroyCallback destroy;
+ ConsumerSaveCallback save;
+ ConsumerLoadCallback load;
+};
+
+} // namespace asg
+} // namespace emulation
+} // namespace android
+
+// The interface for the guest:
+
+extern "C" {
+// Handled outside in address_space_device.cpp:
+//
+// Ping(device id): Create the device. On the host, the two rings and
+// auxiliary buffer are allocated. The two rings are allocated up front.
+// Both the auxiliary buffers and the rings are allocated from blocks of
+// rings and auxiliary buffers. New blocks are created if we run out either
+// way.
+enum asg_command {
+ // Ping(get_ring): Returns, in the fields:
+ // metadata: offset to give to claimShared and mmap() in the guest
+ // size: size to give to claimShared and mmap() in the guest
+ ASG_GET_RING = 0,
+
+ // Ping(get_buffer): Returns, in the fields:
+ // metadata: offset to give to claimShared and mmap() in the guest
+ // size: size to give to claimShared and mmap() in the guest
+ ASG_GET_BUFFER = 1,
+
+ // Ping(set_version): Run after the guest reads and negotiates its
+ // version of the device with the host. The host now knows the guest's
+ // version and can proceed with a protocol that works for both.
+ // size (in): the version of the guest
+ // size (out): the version of the host
+ // metadata (out): hostmem id
+ // After this command runs, the consumer is
+ // implicitly created.
+ ASG_SET_VERSION = 2,
+
+ // Ping(notiy_available): Wakes up the consumer from sleep so it
+ // can read data via toHost
+ ASG_NOTIFY_AVAILABLE = 3,
+
+ // Retrieve the host config
+ ASG_GET_CONFIG = 4,
+};
+
+} // extern "C"