• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2019 Google
3  * SPDX-License-Identifier: MIT
4  */
5 #pragma once
6 
7 #include "ring_buffer.h"
8 
9 #include <functional>
10 #include <cstddef>
11 
12 // This file defines common types for address space graphics and provides
13 // documentation.
14 
15 // Address space graphics======================================================
16 //
17 // Basic idea
18 //
19 // Address space graphics (ASG) is a subdevice of the address space device that
20 // provides a way to run graphics commands and data with fewer VM exits by
21 // leveraging shared memory ring buffers.
22 //
23 // Each GL/Vk thread in the guest is associated with a context (asg_context).
24 // asg_context consists of pointers into the shared memory that view it as a
25 // collection of ring buffers and a common write buffer.
26 //
27 // Consumer concept
28 //
29 // ASG does not assume a particular rendering backend (though we will use
30 // RenderThread's). This is for ease of coding/testing and flexibility; the
31 // implementation is not coupled to emugl/libOpenglRender.
32 //
33 // Instead, there is the concept of a "Consumer" of ASG that will do something
34 // with the data arriving from the shared memory region, and possibly reply
35 // back to the guest. We register functions to construct and deconstruct
36 // Consumers as part of emulator init (setConsumer).
37 //
38 // Guest workflow
39 //
40 // 1. Open address space device
41 //
42 // 2. Create the graphics context as the subdevice
43 //
44 // 3. ping(ASG_GET_RING) to get the offset/size of the ring buffer admin. info
45 //
46 // 4. ping(ASG_GET_BUFFER) to get the offset/size of the shared transfer buffer.
47 //
48 // 5. ioctl(CLAIM_SHARED) and mmap on those two offset/size pairs to get a
49 // guest-side mapping.
50 //
51 // 6. call asg_context_create on the ring and buffer pointers to create the asg_context.
52 //
53 // 7. Now the guest and host share asg_context pts and can communicate.
54 //
55 // 8. But usually the guest will sometimes need to ping(ASG_NOTIFY_AVAILABLE)
56 // so that the host side (which is usually a separate thread that we don't want
57 // to spin too much) wakes up and processes data.
58 
59 namespace gfxstream {
60 namespace guest {
61 
62 class Stream;
63 
64 } // namespace base
65 } // namespace android
66 
67 #define ADDRESS_SPACE_GRAPHICS_DEVICE_ID 0
68 #define ADDRESS_SPACE_GRAPHICS_PAGE_SIZE 4096
69 #define ADDRESS_SPACE_GRAPHICS_BLOCK_SIZE (16ULL * 1048576ULL)
70 
71 // AddressSpaceGraphicsContext shares memory with
72 // the guest via the following layout:
73 extern "C" {
74 
75 struct asg_ring_storage { // directly shared with guest
76     char to_host[ADDRESS_SPACE_GRAPHICS_PAGE_SIZE];
77     char to_host_large_xfer[ADDRESS_SPACE_GRAPHICS_PAGE_SIZE];
78     char from_host_large_xfer[ADDRESS_SPACE_GRAPHICS_PAGE_SIZE];
79 };
80 
81 // Set by the address space graphics device to notify the guest that the host
82 // has slept or is able to consume something, or we are exiting, or there is an
83 // error.
84 enum asg_host_state {
85     // The host renderthread is asleep and needs to be woken up.
86     ASG_HOST_STATE_NEED_NOTIFY = 0,
87 
88     // The host renderthread is active and can consume new data
89     // without notification.
90     ASG_HOST_STATE_CAN_CONSUME = 1,
91 
92     // Normal exit
93     ASG_HOST_STATE_EXIT = 2,
94 
95     // Error: Something weird happened and we need to exit.
96     ASG_HOST_STATE_ERROR = 3,
97 
98     // Host is rendering
99     ASG_HOST_STATE_RENDERING = 4,
100 };
101 
102 struct asg_ring_config;
103 
104 // Each context has a pair of ring buffers for communication
105 // to and from the host. There is another ring buffer for large xfers
106 // to the host (all xfers from the host are already considered "large").
107 //
108 // Each context also comes with _one_ auxiliary buffer to hold both its own
109 // commands and to perform private DMA transfers.
110 struct asg_context { // ptrs into RingStorage
111     struct ring_buffer* to_host;
112     char* buffer;
113     asg_host_state* host_state;
114     asg_ring_config* ring_config;
115     struct ring_buffer_with_view to_host_large_xfer;
116     struct ring_buffer_with_view from_host_large_xfer;
117 };
118 
119 // Helper function that will be common between guest and host:
120 // Given ring storage and a write buffer, returns asg_context that
121 // is the correct view into it.
asg_context_create(char * ring_storage,char * buffer,uint32_t buffer_size)122 static inline struct asg_context asg_context_create(
123     char* ring_storage,
124     char* buffer,
125     uint32_t buffer_size) {
126 
127     struct asg_context res;
128 
129     res.to_host =
130         reinterpret_cast<struct ring_buffer*>(
131             ring_storage +
132             offsetof(struct asg_ring_storage, to_host));
133     res.to_host_large_xfer.ring =
134         reinterpret_cast<struct ring_buffer*>(
135             ring_storage +
136             offsetof(struct asg_ring_storage, to_host_large_xfer));
137     res.from_host_large_xfer.ring =
138         reinterpret_cast<struct ring_buffer*>(
139             ring_storage +
140             offsetof(struct asg_ring_storage, from_host_large_xfer));
141 
142     ring_buffer_init(res.to_host);
143 
144     res.buffer = buffer;
145     res.host_state =
146         reinterpret_cast<asg_host_state*>(
147             &res.to_host->state);
148     res.ring_config =
149         reinterpret_cast<asg_ring_config*>(
150             res.to_host->config);
151 
152     ring_buffer_view_init(
153         res.to_host_large_xfer.ring,
154         &res.to_host_large_xfer.view,
155         (uint8_t*)res.buffer, buffer_size);
156 
157     ring_buffer_view_init(
158         res.from_host_large_xfer.ring,
159         &res.from_host_large_xfer.view,
160         (uint8_t*)res.buffer, buffer_size);
161 
162     return res;
163 }
164 
165 // During operation, the guest sends commands and data over the auxiliary
166 // buffer while using the |to_host| ring to communicate what parts of the auxiliary
167 // buffer is outstanding traffic needing to be consumed by the host.
168 // After a transfer completes to the host, the host may write back data.
169 // The guest then reads the results on the same auxiliary buffer
170 // while being notified of which parts to read via the |from_host| ring.
171 //
172 // The size of the auxiliary buffer and flush interval is defined by
173 // the following config.ini android_hw setting:
174 //
175 // 1) android_hw->hw_gltransport_asg_writeBufferSize
176 // 2) android_hw->hw_gltransport_asg_writeStepSize
177 //
178 // 1) the size for the auxiliary buffer
179 // 2) the step size over which commands are flushed to the host
180 //
181 // When transferring commands, command data is built up in writeStepSize
182 // chunks and flushed to the host when either writeStepSize is reached or
183 // the guest flushes explicitly.
184 //
185 // Command vs. Data Modes
186 //
187 // For command data larger than writeStepSize or when transferring data, we
188 // fall back to using a different mode where the entire auxiliary buffer is
189 // used to perform the transfer, |asg_writeBufferSize| steps at a time. The
190 // host is also notified of the total transport size.
191 //
192 // When writing back to the guest, it is assumed that the write buffer will
193 // be completely empty as the guest has already flushed and the host has
194 // already consumed all commands/data, and is writing back. In this case,
195 // the full auxiliary buffer is used at the same time for writing back to
196 // the guest.
197 //
198 // Larger / Shared transfers
199 //
200 // Each of |to_host| and |from_host| can contain elements of type 1, 2, or 3:
201 // Type 1: 8 bytes: 4 bytes offset, 4 bytes size. Relative to write buffer.
202 struct __attribute__((__packed__)) asg_type1_xfer {
203     uint32_t offset;
204     uint32_t size;
205 };
206 // Type 2: 16 bytes: 16 bytes offset into address space PCI space, 8 bytes
207 // size.
208 struct __attribute__((__packed__)) asg_type2_xfer {
209     uint64_t physAddr;
210     uint64_t size;
211 };
212 // Type 3: There is a large transfer of known size and the entire write buffer
213 // will be used to send it over.
214 //
215 // For type 1 transfers, we get the corresponding host virtual address by
216 // adding the offset to the beginning of the write buffer.  For type 2
217 // transfers, we need to calculate the guest physical address and then call
218 // addressspacecontrolops.gethostptr, which is slower since it goes through
219 // a data structure map of existing mappings.
220 //
221 // The rings never contain a mix of type 1 and 2 elements. For to_host,
222 // the guest initiates changes between type 1 and 2.
223 //
224 // The config fields:
225 //
226 struct asg_ring_config {
227     // config[0]: size of the auxiliary buffer
228     uint32_t buffer_size;
229 
230     // config[1]: flush interval for the auxiliary buffer
231     uint32_t flush_interval;
232 
233     // the position of the interval in the auxiliary buffer
234     // that the host has read so far
235     uint32_t host_consumed_pos;
236 
237     // the start of the places the guest might write to next
238     uint32_t guest_write_pos;
239 
240     // 1 if transfers are of type 1, 2 if transfers of type 2,
241     // 3 if the overall transfer size is known and we are sending something large.
242     uint32_t transfer_mode;
243 
244     // the size of the transfer, used if transfer size is known.
245     // Set before setting config[2] to 3.
246     uint32_t transfer_size;
247 
248     // error state
249     uint32_t in_error;
250 };
251 
252 // State/config changes may only occur if the ring is empty, or the state
253 // is transitioning to Error. That way, the host and guest have a chance to
254 // synchronize on the same state.
255 //
256 // Thus far we've established how commands and data are transferred
257 // to and from the host. Next, let's discuss how AddressSpaceGraphicsContext
258 // talks to the code that actually does something with the commands
259 // and sends data back.
260 
261 } // extern "C"
262 
263 namespace android {
264 namespace emulation {
265 namespace asg {
266 
267 // Consumer Concept
268 //
269 // AddressSpaceGraphicsContext's are each associated with a consumer that
270 // takes data off the auxiliary buffer and to_host, while sending back data
271 // over the auxiliary buffer / from_host.
272 //
273 // will read the commands and write back data.
274 //
275 // The consumer type is fixed at startup. The interface is as follows:
276 
277 // Called by the consumer, implemented in AddressSpaceGraphicsContext:
278 //
279 // Called when the consumer doesn't find anything to
280 // read in to_host. Will make the consumer sleep
281 // until another Ping(NotifyAvailable).
282 using OnUnavailableReadCallback =
283     std::function<int()>;
284 
285 // Unpacks a type 2 transfer into host pointer and size.
286 using GetPtrCallback =
287     std::function<char*(uint64_t)>;
288 
289 struct ConsumerCallbacks {
290     OnUnavailableReadCallback onUnavailableRead;
291     GetPtrCallback getPtr;
292 };
293 
294 using ConsumerCreateCallback =
295     std::function<void* (struct asg_context, ConsumerCallbacks)>;
296 using ConsumerDestroyCallback =
297     std::function<void(void*)>;
298 using ConsumerSaveCallback =
299     std::function<void(void*, gfxstream::guest::Stream*)>;
300 using ConsumerLoadCallback =
301     std::function<void(void*, gfxstream::guest::Stream*)>;
302 
303 struct ConsumerInterface {
304     ConsumerCreateCallback create;
305     ConsumerDestroyCallback destroy;
306     ConsumerSaveCallback save;
307     ConsumerLoadCallback load;
308 };
309 
310 } // namespace asg
311 } // namespace emulation
312 } // namespace android
313 
314 // The interface for the guest:
315 
316 extern "C" {
317 // Handled outside in address_space_device.cpp:
318 //
319 // Ping(device id): Create the device. On the host, the two rings and
320 // auxiliary buffer are allocated. The two rings are allocated up front.
321 // Both the auxiliary buffers and the rings are allocated from blocks of
322 // rings and auxiliary buffers. New blocks are created if we run out either
323 // way.
324 enum asg_command {
325     // Ping(get_ring): Returns, in the fields:
326     // metadata: offset to give to claimShared and mmap() in the guest
327     // size: size to give to claimShared and mmap() in the guest
328     ASG_GET_RING = 0,
329 
330     // Ping(get_buffer): Returns, in the fields:
331     // metadata: offset to give to claimShared and mmap() in the guest
332     // size: size to give to claimShared and mmap() in the guest
333     ASG_GET_BUFFER = 1,
334 
335     // Ping(set_version): Run after the guest reads and negotiates its
336     // version of the device with the host. The host now knows the guest's
337     // version and can proceed with a protocol that works for both.
338     // size (in): the version of the guest
339     // size (out): the version of the host
340     // metadata (out): hostmem id
341     // After this command runs, the consumer is
342     // implicitly created.
343     ASG_SET_VERSION = 2,
344 
345     // Ping(notiy_available): Wakes up the consumer from sleep so it
346     // can read data via toHost
347     ASG_NOTIFY_AVAILABLE = 3,
348 
349     // Retrieve the host config
350     ASG_GET_CONFIG = 4,
351 };
352 
353 } // extern "C"
354