1 #pragma once 2 3 #include "aemu/base/Tracing.h" 4 5 #include <assert.h> 6 #include <stddef.h> 7 #include <stdint.h> 8 #include <stdlib.h> 9 #include <string.h> 10 11 namespace gfxstream { 12 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 42 template <typename T, typename S> 43 struct UnpackerT { unpackUnpackerT44 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 51 template <typename T, typename S> 52 struct UnpackerT<T*, S> { 53 static T* unpack(const void* ptr) { 54 return (T*)(uintptr_t)(*(const S*)ptr); 55 } 56 }; 57 58 template <> 59 struct UnpackerT<ssize_t, uint32_t> { 60 static ssize_t unpack(const void* ptr) { 61 return (ssize_t)*(const int32_t*)ptr; 62 } 63 }; 64 65 template <typename T, typename S> 66 inline 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 95 template <size_t StackSize = 1024, size_t Align = 8> 96 class GenericInputBuffer { 97 static_assert(Align == 1 || Align == 2 || Align == 4 || Align == 8, 98 "Bad alignment parameter"); 99 100 public: 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 122 private: 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 132 template <size_t StackSize = 1024, size_t Align = 8> 133 class GenericOutputBuffer { 134 static_assert(Align == 1 || Align == 2 || Align == 4 || Align == 8, 135 "Bad alignment parameter"); 136 137 public: 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 165 private: 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 178 using InputBuffer = GenericInputBuffer<>; 179 using OutputBuffer = GenericOutputBuffer<>; 180 181 } // namespace gfxstream 182