blob: b03db3cb94df5289ee0770c86164c9ffddd853c2 [file] [log] [blame]
Lingfeng Yang0f12dc52020-10-28 09:25:37 -07001#pragma once
2
Joshua Duongef2bbc22022-10-05 11:59:15 -07003#include "aemu/base/Tracing.h"
Lingfeng Yang0f12dc52020-10-28 09:25:37 -07004
5#include <assert.h>
6#include <stddef.h>
7#include <stdint.h>
8#include <stdlib.h>
9#include <string.h>
10
Jason Macnaked0c9e62023-03-30 15:58:24 -070011namespace gfxstream {
Lingfeng Yang0f12dc52020-10-28 09:25:37 -070012
13// A helper template to extract values form the wire protocol stream
14// and convert them to appropriate host values.
15//
16// The wire protocol uses 32-bit exclusively when transferring
17// GLintptr or GLsizei values, as well as opaque handles like GLeglImage,
18// from the guest (even when the guest is 64-bit).
19//
20// The corresponding host definitions depend on the host bitness. For
21// example, GLintptr is 64-bit on linux-x86_64. The following is a set
22// of templates that can simplify the conversion of protocol values
23// into host ones.
24//
25// The most important one is:
26//
27// unpack<HOST_TYPE,SIZE_TYPE>(const void* ptr)
28//
29// Which reads bytes from |ptr|, using |SIZE_TYPE| as the underlying
30// sized-integer specifier (e.g. 'uint32_t'), and converting the result
31// into a |HOST_TYPE| value. For example:
32//
33// unpack<EGLImage,uint32_t>(ptr + 12);
34//
35// will read a 4-byte value from |ptr + 12| and convert it into
36// an EGLImage, which is a host void*. The template detects host
37// pointer types to perform proper type casting.
38//
39// TODO(digit): Add custom unpackers to handle generic opaque void* values.
40// and map them to unique 32-bit values.
41
42template <typename T, typename S>
43struct UnpackerT {
44 static T unpack(const void* ptr) {
45 static_assert(sizeof(T) == sizeof(S),
46 "Bad input arguments, have to be of the same size");
47 return *(const T*)ptr;
48 }
49};
50
51template <typename T, typename S>
52struct UnpackerT<T*, S> {
53 static T* unpack(const void* ptr) {
54 return (T*)(uintptr_t)(*(const S*)ptr);
55 }
56};
57
58template <>
59struct UnpackerT<ssize_t, uint32_t> {
60 static ssize_t unpack(const void* ptr) {
61 return (ssize_t)*(const int32_t*)ptr;
62 }
63};
64
65template <typename T, typename S>
66inline T Unpack(const void* ptr) {
67 return UnpackerT<T, S>::unpack(ptr);
68}
69
70// Helper classes GenericInputBuffer and GenericOutputBuffer used to ensure
71// input and output buffers passed to EGL/GL functions are properly aligned
72// (preventing crashes with some backends).
73//
74// Usage example:
75//
76// GenericInputBuffer<> inputBuffer(ptrIn, sizeIn);
77// GenericOutputBuffer<> outputBuffer(ptrOut, sizeOut);
78// glDoGetStuff(inputBuffer.get(), outputBuffer.get());
79// outputBuffer.flush();
80//
81// get() will return the original value of |ptr| if it was aligned on the
82// configured boundary (8 bytes by default). Otherwise, it will return the
83// address of an aligned copy of the original |size| bytes starting from |ptr|.
84//
85// Allowed alignment values are 1, 2, 4, 8.
86//
87// outputBuffer.flush() copies the content of the copy back to |ptr| explictly,
88// if needed. It is a no-op if |ptr| was aligned.
89//
90// Both classes try to minimize heap usage as much as possible - the first
91// template argument defines the size of an internal array Generic*Buffer-s use
92// if the |ptr|'s |size| is small enough. If it doesn't fit into the internal
93// array, an aligned copy is allocated on the heap and freed in the dtor.
94
95template <size_t StackSize = 1024, size_t Align = 8>
96class GenericInputBuffer {
97 static_assert(Align == 1 || Align == 2 || Align == 4 || Align == 8,
98 "Bad alignment parameter");
99
100public:
101 GenericInputBuffer(const void* input, size_t size) : mOrigBuff(input) {
102 if (((uintptr_t)input & (Align - 1U)) == 0) {
103 mPtr = const_cast<void*>(input);
104 } else {
105 if (size <= StackSize) {
106 mPtr = &mArray[0];
107 } else {
108 mPtr = malloc(size);
109 }
110 memcpy(mPtr, input, size);
111 }
112 }
113
114 ~GenericInputBuffer() {
115 if (mPtr != mOrigBuff && mPtr != &mArray[0]) {
116 free(mPtr);
117 }
118 }
119
120 const void* get() const { return mPtr; }
121
122private:
123 // A pointer to the aligned buffer, might point either to mOrgBuf, to mArray
124 // start or to a heap-allocated chunk of data.
125 void* mPtr;
126 // Original buffer.
127 const void* mOrigBuff;
128 // Inplace aligned array for small enough buffers.
129 char __attribute__((__aligned__(Align))) mArray[StackSize];
130};
131
132template <size_t StackSize = 1024, size_t Align = 8>
133class GenericOutputBuffer {
134 static_assert(Align == 1 || Align == 2 || Align == 4 || Align == 8,
135 "Bad alignment parameter");
136
137public:
138 GenericOutputBuffer(unsigned char* ptr, size_t size) :
139 mOrigBuff(ptr), mSize(size) {
140 if (((uintptr_t)ptr & (Align - 1U)) == 0) {
141 mPtr = ptr;
142 } else {
143 if (size <= StackSize) {
144 mPtr = &mArray[0];
145 } else {
146 mPtr = calloc(1, size);
147 }
148 }
149 }
150
151 ~GenericOutputBuffer() {
152 if (mPtr != mOrigBuff && mPtr != &mArray[0]) {
153 free(mPtr);
154 }
155 }
156
157 void* get() const { return mPtr; }
158
159 void flush() {
160 if (mPtr != mOrigBuff) {
161 memcpy(mOrigBuff, mPtr, mSize);
162 }
163 }
164
165private:
166 // A pointer to the aligned buffer, might point either to mOrgBuf, to mArray
167 // start or to a heap-allocated chunk of data.
168 void* mPtr;
169 // Original buffer.
170 unsigned char* mOrigBuff;
171 // Original buffer size.
172 size_t mSize;
173 // Inplace array for small enough buffers.
174 unsigned char __attribute__((__aligned__(Align))) mArray[StackSize];
175};
176
177// Pin the defaults for the commonly used type names
178using InputBuffer = GenericInputBuffer<>;
179using OutputBuffer = GenericOutputBuffer<>;
180
Jason Macnaked0c9e62023-03-30 15:58:24 -0700181} // namespace gfxstream