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