aboutsummaryrefslogtreecommitdiff
path: root/src/gfxstream/guest/GoldfishAddressSpace/include/address_space_graphics_types.h
blob: e52c8fe22d4458df9eb43ccff56d7dd478778cf2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
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"