summaryrefslogtreecommitdiff
path: root/host/libs/virglrenderer/ProtocolUtils.h
blob: 7c323c979c9b4c53a4bb232803fa216984a4c776 (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
#pragma once

#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

namespace emugl {

// A helper template to extract values form the wire protocol stream
// and convert them to appropriate host values.
//
// The wire protocol uses 32-bit exclusively when transferring
// GLintptr or GLsizei values, as well as opaque handles like GLeglImage,
// from the guest (even when the guest is 64-bit).
//
// The corresponding host definitions depend on the host bitness. For
// example, GLintptr is 64-bit on linux-x86_64. The following is a set
// of templates that can simplify the conversion of protocol values
// into host ones.
//
// The most important one is:
//
//     unpack<HOST_TYPE,SIZE_TYPE>(const void* ptr)
//
// Which reads bytes from |ptr|, using |SIZE_TYPE| as the underlying
// sized-integer specifier (e.g. 'uint32_t'), and converting the result
// into a |HOST_TYPE| value. For example:
//
//     unpack<EGLImage,uint32_t>(ptr + 12);
//
// will read a 4-byte value from |ptr + 12| and convert it into
// an EGLImage, which is a host void*. The template detects host
// pointer types to perform proper type casting.
//
// TODO(digit): Add custom unpackers to handle generic opaque void* values.
//              and map them to unique 32-bit values.

template <typename T, typename S>
struct UnpackerT {
    static T unpack(const void* ptr) {
        static_assert(sizeof(T) == sizeof(S),
                      "Bad input arguments, have to be of the same size");
        return *(const T*)ptr;
    }
};

template <typename T, typename S>
struct UnpackerT<T*, S> {
    static T* unpack(const void* ptr) {
        return (T*)(uintptr_t)(*(const S*)ptr);
    }
};

template <>
struct UnpackerT<ssize_t, uint32_t> {
    static ssize_t unpack(const void* ptr) {
        return (ssize_t)*(const int32_t*)ptr;
    }
};

template <typename T, typename S>
inline T Unpack(const void* ptr) {
    return UnpackerT<T, S>::unpack(ptr);
}

// Helper classes GenericInputBuffer and GenericOutputBuffer used to ensure
// input and output buffers passed to EGL/GL functions are properly aligned
// (preventing crashes with some backends).
//
// Usage example:
//
//    GenericInputBuffer<> inputBuffer(ptrIn, sizeIn);
//    GenericOutputBuffer<> outputBuffer(ptrOut, sizeOut);
//    glDoGetStuff(inputBuffer.get(), outputBuffer.get());
//    outputBuffer.flush();
//
// get() will return the original value of |ptr| if it was aligned on the
// configured boundary (8 bytes by default). Otherwise, it will return the
// address of an aligned copy of the original |size| bytes starting from |ptr|.
//
// Allowed alignment values are 1, 2, 4, 8.
//
// outputBuffer.flush() copies the content of the copy back to |ptr| explictly,
// if needed. It is a no-op if |ptr| was aligned.
//
// Both classes try to minimize heap usage as much as possible - the first
// template argument defines the size of an internal array Generic*Buffer-s use
// if the |ptr|'s |size| is small enough. If it doesn't fit into the internal
// array, an aligned copy is allocated on the heap and freed in the dtor.

template <size_t StackSize = 1024, size_t Align = 8>
class GenericInputBuffer {
    static_assert(Align == 1 || Align == 2 || Align == 4 || Align == 8,
                  "Bad alignment parameter");

public:
    GenericInputBuffer(const void* input, size_t size) : mOrigBuff(input) {
        if (((uintptr_t)input & (Align - 1U)) == 0) {
            mPtr = const_cast<void*>(input);
        } else {
            if (size <= StackSize) {
                mPtr = &mArray[0];
            } else {
                mPtr = malloc(size);
            }
            memcpy(mPtr, input, size);
        }
    }

    ~GenericInputBuffer() {
        if (mPtr != mOrigBuff && mPtr != &mArray[0]) {
            free(mPtr);
        }
    }

    const void* get() const { return mPtr; }

private:
    // A pointer to the aligned buffer, might point either to mOrgBuf, to mArray
    // start or to a heap-allocated chunk of data.
    void* mPtr;
    // Original buffer.
    const void* mOrigBuff;
    // Inplace aligned array for small enough buffers.
    char __attribute__((__aligned__(Align))) mArray[StackSize];
};

template <size_t StackSize = 1024, size_t Align = 8>
class GenericOutputBuffer {
    static_assert(Align == 1 || Align == 2 || Align == 4 || Align == 8,
                  "Bad alignment parameter");

public:
    GenericOutputBuffer(unsigned char* ptr, size_t size) :
            mOrigBuff(ptr), mSize(size) {
        if (((uintptr_t)ptr & (Align - 1U)) == 0) {
            mPtr = ptr;
        } else {
            if (size <= StackSize) {
                mPtr = &mArray[0];
            } else {
                mPtr = calloc(1, size);
            }
        }
    }

    ~GenericOutputBuffer() {
        if (mPtr != mOrigBuff && mPtr != &mArray[0]) {
            free(mPtr);
        }
    }

    void* get() const { return mPtr; }

    void flush() {
        if (mPtr != mOrigBuff) {
            memcpy(mOrigBuff, mPtr, mSize);
        }
    }

private:
    // A pointer to the aligned buffer, might point either to mOrgBuf, to mArray
    // start or to a heap-allocated chunk of data.
    void* mPtr;
    // Original buffer.
    unsigned char* mOrigBuff;
    // Original buffer size.
    size_t mSize;
    // Inplace array for small enough buffers.
    unsigned char __attribute__((__aligned__(Align))) mArray[StackSize];
};

// Pin the defaults for the commonly used type names
using InputBuffer = GenericInputBuffer<>;
using OutputBuffer = GenericOutputBuffer<>;

}  // namespace emugl