summaryrefslogtreecommitdiff
path: root/src/mbgl/gl/gl.cpp
blob: c57717aa87d6d4a32385942408f8b7f3f60c3804 (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
#include <mbgl/gl/gl.hpp>
#include <mbgl/util/string.hpp>
#include <mbgl/platform/log.hpp>

#include <cassert>
#include <iostream>
#include <map>
#include <mutex>

namespace mbgl {
namespace gl {

std::vector<ExtensionFunctionBase*>& ExtensionFunctionBase::functions() {
    static std::vector<ExtensionFunctionBase*> functions;
    return functions;
}

static std::once_flag initializeExtensionsOnce;

void InitializeExtensions(glProc (*getProcAddress)(const char *)) {
    std::call_once(initializeExtensionsOnce, [getProcAddress] {
        const char * extensionsPtr = reinterpret_cast<const char *>(
            MBGL_CHECK_ERROR(glGetString(GL_EXTENSIONS)));

        if (!extensionsPtr)
            return;

        const std::string extensions = extensionsPtr;
        for (auto fn : ExtensionFunctionBase::functions()) {
            for (auto probe : fn->probes) {
                if (extensions.find(probe.first) != std::string::npos) {
#ifdef GL_TRACK
                    fn->foundName = probe.second;
#endif
                    fn->ptr = getProcAddress(probe.second);
                    break;
                }
            }
        }
    });
}

void checkError(const char *cmd, const char *file, int line) {
    const GLenum err = glGetError();
    if (err != GL_NO_ERROR) {
        const char *error = nullptr;
        switch (err) {
            case GL_INVALID_ENUM: error = "INVALID_ENUM"; break;
            case GL_INVALID_VALUE: error = "INVALID_VALUE"; break;
            case GL_INVALID_OPERATION: error = "INVALID_OPERATION"; break;
            case GL_INVALID_FRAMEBUFFER_OPERATION:  error = "INVALID_FRAMEBUFFER_OPERATION";  break;
            case GL_OUT_OF_MEMORY: error = "OUT_OF_MEMORY"; break;
#ifdef GL_STACK_UNDERFLOW
            case GL_STACK_UNDERFLOW:  error = "STACK_UNDERFLOW";  break;
#endif
#ifdef GL_STACK_OVERFLOW
            case GL_STACK_OVERFLOW:  error = "STACK_OVERFLOW";  break;
#endif
            default: error = "(unknown)"; break;
        }

        throw ::mbgl::gl::Error(err, std::string(cmd) + ": Error GL_" + error + " - " + file + ":" + util::toString(line));
    }
}
} // namespace gl
} // namespace mbgl

#ifdef GL_TRACK
#undef glBindTexture
#undef glDeleteTextures
#undef glTexImage2D
#undef glClear
#undef glShaderSource
#undef glBufferData
#undef glBindBuffer
#undef glDeleteBuffers
#undef glBufferData
static unsigned int currentUsedBytes = 0;
static GLint currentBoundTexture = 0;
static std::map<GLint, unsigned int> bindingToSizeMap;

static GLuint currentArrayBuffer = 0;
static GLuint currentElementArrayBuffer = 0;
static std::map<GLint, GLsizeiptr> bufferBindingToSizeMap;
static unsigned int currentUsedBufferBytes = 0;
static unsigned int largestAmountUsedSoFar = 0;

static std::map<GLuint, GLuint> vertexArrayToArrayBufferMap;
static GLuint currentVertexArray = 0;

static std::mutex gDebugMutex;

namespace mbgl {
    namespace gl {
        void mbx_trapExtension(const char *) { }
        void mbx_trapExtension(const char *, GLint, const char *) { }
        void mbx_trapExtension(const char *, GLsizei, GLuint *) { }
        void mbx_trapExtension(const char *, GLsizei, const GLuint *) { }
        void mbx_trapExtension(const char *, GLenum, GLenum, GLenum, GLsizei, const GLuint *, GLboolean) { }
        void mbx_trapExtension(const char *, GLenum, GLuint, GLsizei, const GLchar *) { }
        void mbx_trapExtension(const char *, GLDEBUGPROC, const void *) { }
        void mbx_trapExtension(const char *, GLuint, GLuint, GLuint, GLuint, GLint, const char *, const void*) { }
        
        void mbx_trapExtension(const char *name, GLuint array) {
            if(strncasecmp(name, "glBindVertexArray", 17) == 0) {
                currentVertexArray = array;
                std::cout << name << ": " << array << std::endl;
            }
        }
    }
}

void mbx_glBindBuffer(GLenum target,
                      GLuint buffer) {
    std::unique_lock<std::mutex> lock(gDebugMutex);
    if (target == GL_ARRAY_BUFFER) {
        currentArrayBuffer = buffer;
        if (currentVertexArray != 0) {
            if (vertexArrayToArrayBufferMap.find(currentVertexArray) != vertexArrayToArrayBufferMap.end()) {
                if (vertexArrayToArrayBufferMap[currentVertexArray] != currentArrayBuffer) {
                    std::cout << "glBindBuffer: ERROR: You are re-binding a VAO to point to a new array buffer.  This is almost certainly unintended." << std::endl;
                }
            }
            std::cout << "glBindBuffer: binding VAO " << currentVertexArray << " to array buffer " << currentArrayBuffer << std::endl;
            vertexArrayToArrayBufferMap[currentVertexArray] = currentArrayBuffer;
        }
    } else if (target == GL_ELEMENT_ARRAY_BUFFER) {
        currentElementArrayBuffer = buffer;
    }
    lock.unlock();
    glBindBuffer(target, buffer);
}

void mbx_glDeleteBuffers(GLsizei n,
                     const GLuint * buffers) {
    std::unique_lock<std::mutex> lock(gDebugMutex);
    for (int i = 0; i < n; ++i) {
        if (bufferBindingToSizeMap.find(buffers[i]) != bufferBindingToSizeMap.end()) {
            currentUsedBufferBytes -= bufferBindingToSizeMap[buffers[i]];
            std::cout << "GL glDeleteBuffers: " << buffers[i] << " freeing " << bufferBindingToSizeMap[buffers[i]] << " bytes current total " << currentUsedBufferBytes << "\n";
            bufferBindingToSizeMap.erase(buffers[i]);
        }
    }
    lock.unlock();
    glDeleteBuffers(n, buffers);
}

void mbx_glBufferData(GLenum target,
                      GLsizeiptr size,
                      const GLvoid * data,
                      GLenum usage) {
    std::unique_lock<std::mutex> lock(gDebugMutex);
    GLuint currentBinding = 0;
    if (target == GL_ARRAY_BUFFER) {
        currentBinding = currentArrayBuffer;
    } else if (target == GL_ELEMENT_ARRAY_BUFFER) {
        currentBinding = currentElementArrayBuffer;
    }
    if (bufferBindingToSizeMap.find(currentBinding) != bufferBindingToSizeMap.end()) {
        currentUsedBufferBytes -= bufferBindingToSizeMap[currentBinding];
        std::cout << "GL glBufferData: " << currentBinding << " freeing " << bufferBindingToSizeMap[currentBinding] << " bytes current total " << currentUsedBufferBytes << "\n";
    }
    bufferBindingToSizeMap[currentBinding] = size;
    currentUsedBufferBytes += size;
    if (currentUsedBufferBytes > largestAmountUsedSoFar) {
        largestAmountUsedSoFar = currentUsedBufferBytes;
    }
    std::cout << "GL glBufferData: " << currentBinding << " using " << bufferBindingToSizeMap[currentBinding] << " bytes current total " << currentUsedBufferBytes << " high water mark " << largestAmountUsedSoFar << "\n";
    lock.unlock();
    
    glBufferData(target, size, data, usage);
}


void mbx_glShaderSource(GLuint shader,
                        GLsizei count,
                        const GLchar * const *string,
                        const GLint *length) {
    //std::cout << "Calling glShaderSource: " << *string << std::endl;
    glShaderSource(shader, count, const_cast<const GLchar **>(string), length);
}

void mbx_glClear(GLbitfield mask) {
    //std::cout << "Calling glClear" << std::endl;
    glClear(mask);
}

void mbx_glBindTexture(	GLenum target,
                       GLuint texture) {
    std::unique_lock<std::mutex> lock(gDebugMutex);
    if (target == GL_TEXTURE_2D) {
        currentBoundTexture = texture;
    }
    lock.unlock();
    glBindTexture(target, texture);
}

void mbx_glDeleteTextures(GLsizei n,
                          const GLuint * textures) {
    std::unique_lock<std::mutex> lock(gDebugMutex);
    for (int i = 0; i < n; ++i) {
        if (bindingToSizeMap.find(textures[i]) != bindingToSizeMap.end()) {
            std::cout << "GL deleteTexture:" << textures[i] << "freeing " << bindingToSizeMap[textures[i]] << " bytes current total " << currentUsedBytes << "\n";
            currentUsedBytes -= bindingToSizeMap[textures[i]];
            bindingToSizeMap.erase(textures[i]);
        }
    }
    lock.unlock();
    glDeleteTextures(n, textures);
}

void mbx_glTexImage2D(GLenum target,
                      GLint level,
                      GLint internalformat,
                      GLsizei width,
                      GLsizei height,
                      GLint border,
                      GLenum format,
                      GLenum type,
                      const GLvoid * data) {
    std::unique_lock<std::mutex> lock(gDebugMutex);
    if (internalformat == GL_RGBA &&
        type == GL_UNSIGNED_BYTE) {
        if (bindingToSizeMap.find(currentBoundTexture) != bindingToSizeMap.end()) {
            currentUsedBytes -= bindingToSizeMap[currentBoundTexture];
            std::cout << "GL glTexImage2D: " << currentBoundTexture << " freeing " << bindingToSizeMap[currentBoundTexture] << " bytes current total " << currentUsedBytes << "\n";
        }
        bindingToSizeMap[currentBoundTexture] = width * height * 4;
        currentUsedBytes += bindingToSizeMap[currentBoundTexture];
        std::cout << "GL glTexImage2D: " << currentBoundTexture << " freeing " << bindingToSizeMap[currentBoundTexture] << " bytes current total " << currentUsedBytes << "\n";
    }
    lock.unlock();
    glTexImage2D(target, level, internalformat, width, height, border, format, type, data);
}
#endif