void showDebugImage(std::string name, const char *data, size_t width, size_t height) { glfwInit(); static GLFWwindow *debugWindow = nullptr; if (!debugWindow) { debugWindow = glfwCreateWindow(width, height, name.c_str(), nullptr, nullptr); if (!debugWindow) { glfwTerminate(); fprintf(stderr, "Failed to initialize window\n"); exit(1); } } GLFWwindow *currentWindow = glfwGetCurrentContext(); glfwSetWindowSize(debugWindow, width, height); glfwMakeContextCurrent(debugWindow); int fbWidth, fbHeight; glfwGetFramebufferSize(debugWindow, &fbWidth, &fbHeight); float scale = static_cast<float>(fbWidth) / static_cast<float>(width); { gl::PreservePixelZoom pixelZoom; gl::PreserveRasterPos rasterPos; MBGL_CHECK_ERROR(glPixelZoom(scale, -scale)); MBGL_CHECK_ERROR(glRasterPos2f(-1.0f, 1.0f)); MBGL_CHECK_ERROR(glDrawPixels(width, height, GL_LUMINANCE, GL_UNSIGNED_BYTE, data)); } glfwSwapBuffers(debugWindow); glfwMakeContextCurrent(currentWindow); }
inline static Type Get() { GLint sfail, dpfail, dppass; MBGL_CHECK_ERROR(glGetIntegerv(GL_STENCIL_FAIL, &sfail)); MBGL_CHECK_ERROR(glGetIntegerv(GL_STENCIL_PASS_DEPTH_FAIL, &dpfail)); MBGL_CHECK_ERROR(glGetIntegerv(GL_STENCIL_PASS_DEPTH_PASS, &dppass)); return { static_cast<GLenum>(sfail), static_cast<GLenum>(dpfail), static_cast<GLuint>(dppass) }; }
void LinepatternShader::bind(GLbyte* offset) { MBGL_CHECK_ERROR(glEnableVertexAttribArray(a_pos)); MBGL_CHECK_ERROR(glVertexAttribPointer(a_pos, 2, GL_SHORT, false, 8, offset + 0)); MBGL_CHECK_ERROR(glEnableVertexAttribArray(a_data)); MBGL_CHECK_ERROR(glVertexAttribPointer(a_data, 4, GL_UNSIGNED_BYTE, false, 8, offset + 4)); }
inline static Type Get() { GLint func, ref, mask; MBGL_CHECK_ERROR(glGetIntegerv(GL_STENCIL_FUNC, &func)); MBGL_CHECK_ERROR(glGetIntegerv(GL_STENCIL_REF, &ref)); MBGL_CHECK_ERROR(glGetIntegerv(GL_STENCIL_VALUE_MASK, &mask)); return { static_cast<GLenum>(func), ref, static_cast<GLuint>(mask) }; }
void Painter::renderClipMasks() { config.stencilTest = GL_FALSE; config.depthTest = GL_FALSE; config.program = 0; config.colorMask = { GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE }; #ifndef GL_ES_VERSION_2_0 config.pixelZoom = { 1, 1 }; config.rasterPos = {{ -1, -1, 0, 0 }}; // Read the stencil buffer const auto& fbSize = frame.framebufferSize; auto pixels = std::make_unique<uint8_t[]>(fbSize[0] * fbSize[1]); MBGL_CHECK_ERROR(glReadPixels( 0, // GLint x 0, // GLint y fbSize[0], // GLsizei width fbSize[1], // GLsizei height GL_STENCIL_INDEX, // GLenum format GL_UNSIGNED_BYTE, // GLenum type pixels.get() // GLvoid * data )); // Scale the Stencil buffer to cover the entire color space. auto it = pixels.get(); auto end = it + fbSize[0] * fbSize[1]; const auto factor = 255.0f / *std::max_element(it, end); for (; it != end; ++it) { *it *= factor; } MBGL_CHECK_ERROR(glWindowPos2i(0, 0)); MBGL_CHECK_ERROR(glDrawPixels(fbSize[0], fbSize[1], GL_LUMINANCE, GL_UNSIGNED_BYTE, pixels.get())); #endif // GL_ES_VERSION_2_0 }
void ObjectStore::performCleanup() { for (GLuint id : abandonedPrograms) { MBGL_CHECK_ERROR(glDeleteProgram(id)); } abandonedPrograms.clear(); for (GLuint id : abandonedShaders) { MBGL_CHECK_ERROR(glDeleteShader(id)); } abandonedShaders.clear(); if (!abandonedBuffers.empty()) { MBGL_CHECK_ERROR(glDeleteBuffers(int(abandonedBuffers.size()), abandonedBuffers.data())); abandonedBuffers.clear(); } if (!abandonedTextures.empty()) { MBGL_CHECK_ERROR(glDeleteTextures(int(abandonedTextures.size()), abandonedTextures.data())); abandonedTextures.clear(); } if (!abandonedVAOs.empty()) { MBGL_CHECK_ERROR(gl::DeleteVertexArrays(int(abandonedVAOs.size()), abandonedVAOs.data())); abandonedVAOs.clear(); } }
void Painter::renderDepthBuffer() { context.stencilTest = GL_FALSE; context.depthTest = GL_FALSE; context.program = 0; context.colorMask = { GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE }; #ifndef GL_ES_VERSION_2_0 context.pixelZoom = { 1, 1 }; context.rasterPos = {{ -1, -1, 0, 0 }}; // Read the stencil buffer const auto& fbSize = frame.framebufferSize; auto pixels = std::make_unique<GLubyte[]>(fbSize[0] * fbSize[1]); const double base = 1.0 / (1.0 - depthRangeSize); glPixelTransferf(GL_DEPTH_SCALE, base); glPixelTransferf(GL_DEPTH_BIAS, 1.0 - base); MBGL_CHECK_ERROR(glReadPixels( 0, // GLint x 0, // GLint y fbSize[0], // GLsizei width fbSize[1], // GLsizei height GL_DEPTH_COMPONENT, // GLenum format GL_UNSIGNED_BYTE, // GLenum type pixels.get() // GLvoid * data )); MBGL_CHECK_ERROR(glWindowPos2i(0, 0)); MBGL_CHECK_ERROR(glDrawPixels(fbSize[0], fbSize[1], GL_LUMINANCE, GL_UNSIGNED_BYTE, pixels.get())); #endif // GL_ES_VERSION_2_0 }
inline void end_group() { if (gl::PopDebugGroup != nullptr) { MBGL_CHECK_ERROR(gl::PopDebugGroup()); } else if (gl::PopGroupMarkerEXT != nullptr) { MBGL_CHECK_ERROR(gl::PopGroupMarkerEXT()); } // indent--; }
void Raster::updateFilter() { MBGL_CHECK_ERROR(glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter == Scaling::Linear ? (mipmap == MipMap::Yes ? GL_LINEAR_MIPMAP_NEAREST : GL_LINEAR) : (mipmap == MipMap::Yes ? GL_NEAREST_MIPMAP_NEAREST : GL_NEAREST))); MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter == Scaling::Linear ? GL_LINEAR : GL_NEAREST)); }
// static int indent = 0; inline void start_group(const std::string &str) { if (gl::PushDebugGroup != nullptr) { MBGL_CHECK_ERROR(gl::PushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, GLsizei(str.size()), str.c_str())); } else if (gl::PushGroupMarkerEXT != nullptr) { MBGL_CHECK_ERROR(gl::PushGroupMarkerEXT(GLsizei(str.size() + 1), str.c_str())); } // fprintf(stderr, "%s%s\n", std::string(indent * 4, ' ').c_str(), str.c_str()); // indent++; }
void enable() { if (!DebugMessageControl || !DebugMessageCallback) { return; } // This will enable all messages including performance hints //MBGL_CHECK_ERROR(DebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, GL_TRUE)); // This will only enable high and medium severity messages MBGL_CHECK_ERROR(DebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_HIGH, 0, nullptr, GL_TRUE)); MBGL_CHECK_ERROR(DebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_MEDIUM, 0, nullptr, GL_TRUE)); MBGL_CHECK_ERROR(DebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_NOTIFICATION, 0, nullptr, GL_FALSE)); MBGL_CHECK_ERROR(DebugMessageCallback(debugCallback, nullptr)); }
void VertexArrayObject::bindVertexArrayObject() { if (!GenVertexArrays || !BindVertexArray) { static bool reported = false; if (!reported) { Log::Warning(Event::OpenGL, "Not using Vertex Array Objects"); reported = true; } return; } if (!vao) { MBGL_CHECK_ERROR(GenVertexArrays(1, &vao)); } MBGL_CHECK_ERROR(BindVertexArray(vao)); }
void Painter::drawClippingMasks(PaintParameters& parameters, const std::map<UnwrappedTileID, ClipID>& stencils) { MBGL_DEBUG_GROUP("clipping masks"); auto& plainShader = parameters.shaders.plain; auto& arrayCoveringPlain = parameters.shaders.coveringPlainArray; mat4 matrix; const GLuint mask = 0b11111111; config.program = plainShader.getID(); config.stencilOp.reset(); config.stencilTest = GL_TRUE; config.depthTest = GL_FALSE; config.depthMask = GL_FALSE; config.colorMask = { GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE }; config.stencilMask = mask; arrayCoveringPlain.bind(plainShader, tileStencilBuffer, BUFFER_OFFSET_0, store); for (const auto& stencil : stencils) { const auto& id = stencil.first; const auto& clip = stencil.second; MBGL_DEBUG_GROUP(std::string{ "mask: " } + util::toString(id)); state.matrixFor(matrix, id); matrix::multiply(matrix, projMatrix, matrix); plainShader.u_matrix = matrix; const GLint ref = (GLint)(clip.reference.to_ulong()); config.stencilFunc = { GL_ALWAYS, ref, mask }; MBGL_CHECK_ERROR(glDrawArrays(GL_TRIANGLES, 0, (GLsizei)tileStencilBuffer.index())); } }
void AttributeBinding<T, N>::bind(Context& context, AttributeLocation location, std::size_t vertexOffset) const { // FillProgram will attempt to bind at location -1 because it includes // a fill-outline-color paint property but does not use it or have an // a_outline_color shader attribute - in this case, we have nothing to bind. if (location == -1) return; context.vertexBuffer = vertexBuffer; MBGL_CHECK_ERROR(glEnableVertexAttribArray(location)); MBGL_CHECK_ERROR(glVertexAttribPointer( location, static_cast<GLint>(attributeSize), static_cast<GLenum>(DataTypeOf<T>), static_cast<GLboolean>(false), static_cast<GLsizei>(vertexSize), reinterpret_cast<GLvoid*>(attributeOffset + (vertexSize * vertexOffset)))); }
// Transfers this buffer to the GPU and binds the buffer to the GL context. void bind() { if (buffer) { MBGL_CHECK_ERROR(glBindBuffer(bufferType, buffer)); } else { MBGL_CHECK_ERROR(glGenBuffers(1, &buffer)); MBGL_CHECK_ERROR(glBindBuffer(bufferType, buffer)); if (array == nullptr) { Log::Debug(Event::OpenGL, "Buffer doesn't contain elements"); pos = 0; } MBGL_CHECK_ERROR(glBufferData(bufferType, pos, array, GL_STATIC_DRAW)); if (!retainAfterUpload) { cleanup(); } } }
// Transfers this buffer to the GPU and binds the buffer to the GL context. void bind(gl::ObjectStore& store) { if (buffer) { MBGL_CHECK_ERROR(glBindBuffer(bufferType, *buffer)); } else { buffer = store.createBuffer(); MBGL_CHECK_ERROR(glBindBuffer(bufferType, *buffer)); if (array == nullptr) { Log::Debug(Event::OpenGL, "Buffer doesn't contain elements"); pos = 0; } MBGL_CHECK_ERROR(glBufferData(bufferType, pos, array, GL_STATIC_DRAW)); if (!retainAfterUpload) { cleanup(); } } }
void FrameHistory::bind(gl::ObjectStore& store, gl::Config& config, uint32_t unit) { if (!texture) { texture = store.createTexture(); config.activeTexture = unit; config.texture[unit] = *texture; #ifndef GL_ES_VERSION_2_0 MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0)); #endif MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)); MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)); } else if (config.texture[unit] != *texture) { config.activeTexture = unit; config.texture[unit] = *texture; } }
void SymbolBucket::drawCollisionBoxes(CollisionBoxShader &shader) { GLbyte *vertex_index = BUFFER_OFFSET_0; auto& collisionBox = renderData->collisionBox; for (auto &group : collisionBox.groups) { group->array[0].bind(shader, collisionBox.vertices, vertex_index); MBGL_CHECK_ERROR(glDrawArrays(GL_LINES, 0, group->vertex_length)); } }
void showColorDebugImage(std::string name, const char *data, size_t logicalWidth, size_t logicalHeight, size_t width, size_t height) { glfwInit(); static GLFWwindow *debugWindow = nullptr; if (!debugWindow) { debugWindow = glfwCreateWindow(logicalWidth, logicalHeight, name.c_str(), nullptr, nullptr); if (!debugWindow) { glfwTerminate(); fprintf(stderr, "Failed to initialize window\n"); exit(1); } } GLFWwindow *currentWindow = glfwGetCurrentContext(); glfwSetWindowSize(debugWindow, logicalWidth, logicalHeight); glfwMakeContextCurrent(debugWindow); int fbWidth, fbHeight; glfwGetFramebufferSize(debugWindow, &fbWidth, &fbHeight); float xScale = static_cast<float>(fbWidth) / static_cast<float>(width); float yScale = static_cast<float>(fbHeight) / static_cast<float>(height); MBGL_CHECK_ERROR(glClearColor(0.8, 0.8, 0.8, 1)); MBGL_CHECK_ERROR(glClear(GL_COLOR_BUFFER_BIT)); MBGL_CHECK_ERROR(glEnable(GL_BLEND)); MBGL_CHECK_ERROR(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); MBGL_CHECK_ERROR(glPixelZoom(xScale, -yScale)); MBGL_CHECK_ERROR(glRasterPos2f(-1.0f, 1.0f)); MBGL_CHECK_ERROR(glDrawPixels(width, height, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, data)); glfwSwapBuffers(debugWindow); glfwMakeContextCurrent(currentWindow); }
void SymbolBucket::drawIcons(IconShader &shader) { char *vertex_index = BUFFER_OFFSET(0); char *elements_index = BUFFER_OFFSET(0); for (auto &group : icon.groups) { assert(group); group->array[1].bind(shader, icon.vertices, icon.triangles, vertex_index); MBGL_CHECK_ERROR(glDrawElements(GL_TRIANGLES, group->elements_length * 3, GL_UNSIGNED_SHORT, elements_index)); vertex_index += group->vertex_length * icon.vertices.itemSize; elements_index += group->elements_length * icon.triangles.itemSize; } }
void HeadlessView::clearBuffers() { assert(isActive()); MBGL_CHECK_ERROR(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0)); if (fbo) { MBGL_CHECK_ERROR(glDeleteFramebuffersEXT(1, &fbo)); fbo = 0; } if (fboColor) { MBGL_CHECK_ERROR(glDeleteTextures(1, &fboColor)); fboColor = 0; } if (fboDepthStencil) { MBGL_CHECK_ERROR(glDeleteRenderbuffersEXT(1, &fboDepthStencil)); fboDepthStencil = 0; } }
void MapContext::pause() { MBGL_CHECK_ERROR(glFinish()); view.deactivate(); std::unique_lock<std::mutex> lockPause(data.mutexPause); data.condPaused.notify_all(); data.condResume.wait(lockPause); view.activate(); }
std::string getAttributeName(ProgramID id, int32_t maxLength, AttributeLocation location) { std::string attributeName; attributeName.resize(maxLength); GLsizei actualLength; GLint size; GLenum type; MBGL_CHECK_ERROR(glGetActiveAttrib(id, static_cast<GLuint>(location), static_cast<GLsizei>(maxLength), &actualLength, &size, &type, const_cast<char*>(attributeName.data()))); attributeName.resize(actualLength); return attributeName; }
void SymbolBucket::drawIcons(SDFShader &shader) { GLbyte *vertex_index = BUFFER_OFFSET_0; GLbyte *elements_index = BUFFER_OFFSET_0; auto& icon = renderData->icon; for (auto &group : icon.groups) { assert(group); group->array[0].bind(shader, icon.vertices, icon.triangles, vertex_index); MBGL_CHECK_ERROR(glDrawElements(GL_TRIANGLES, group->elements_length * 3, GL_UNSIGNED_SHORT, elements_index)); vertex_index += group->vertex_length * icon.vertices.itemSize; elements_index += group->elements_length * icon.triangles.itemSize; } }
void SymbolBucket::drawGlyphs(SDFShader& shader, gl::GLObjectStore& glObjectStore) { GLbyte *vertex_index = BUFFER_OFFSET_0; GLbyte *elements_index = BUFFER_OFFSET_0; auto& text = renderData->text; for (auto &group : text.groups) { assert(group); group->array[0].bind(shader, text.vertices, text.triangles, vertex_index, glObjectStore); MBGL_CHECK_ERROR(glDrawElements(GL_TRIANGLES, group->elements_length * 3, GL_UNSIGNED_SHORT, elements_index)); vertex_index += group->vertex_length * text.vertices.itemSize; elements_index += group->elements_length * text.triangles.itemSize; } }
void GLObjectStore::performCleanup() { assert(ThreadContext::currentlyOn(ThreadType::Map)); if (!abandonedVAOs.empty()) { MBGL_CHECK_ERROR(VertexArrayObject::Delete(static_cast<GLsizei>(abandonedVAOs.size()), abandonedVAOs.data())); abandonedVAOs.clear(); } if (!abandonedTextures.empty()) { MBGL_CHECK_ERROR(glDeleteTextures(static_cast<GLsizei>(abandonedTextures.size()), abandonedTextures.data())); abandonedTextures.clear(); } if (!abandonedBuffers.empty()) { MBGL_CHECK_ERROR(glDeleteBuffers(static_cast<GLsizei>(abandonedBuffers.size()), abandonedBuffers.data())); abandonedBuffers.clear(); } }
bool Shader::compileShader(gl::UniqueShader& shader, const GLchar *source) { GLint status = 0; const GLsizei lengths = static_cast<GLsizei>(std::strlen(source)); MBGL_CHECK_ERROR(glShaderSource(shader.get(), 1, &source, &lengths)); MBGL_CHECK_ERROR(glCompileShader(shader.get())); MBGL_CHECK_ERROR(glGetShaderiv(shader.get(), GL_COMPILE_STATUS, &status)); if (status == 0) { GLint logLength; MBGL_CHECK_ERROR(glGetShaderiv(shader.get(), GL_INFO_LOG_LENGTH, &logLength)); if (logLength > 0) { const auto log = std::make_unique<GLchar[]>(logLength); MBGL_CHECK_ERROR(glGetShaderInfoLog(shader.get(), logLength, &logLength, log.get())); Log::Error(Event::Shader, "Shader failed to compile: %s", log.get()); } return false; } MBGL_CHECK_ERROR(glGetShaderiv(shader.get(), GL_COMPILE_STATUS, &status)); if (status == GL_FALSE) { Log::Error(Event::Shader, "Shader %s failed to compile.", name); return false; } return true; }
void Raster::upload(gl::Context& context, uint32_t unit) { if (!images.empty() && !texture) { texture = context.createTexture(); context.activeTexture = unit; context.texture[unit] = *texture; updateFilter(); #ifndef GL_ES_VERSION_2_0 MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, images.size())); #endif MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); GLint level = 0; for (auto& img : images) { MBGL_CHECK_ERROR(glTexImage2D( GL_TEXTURE_2D, level++, GL_RGBA, static_cast<GLsizei>(img.width), static_cast<GLsizei>(img.height), 0, GL_RGBA, GL_UNSIGNED_BYTE, img.data.get())); } size = { { images.front().width, images.front().height } }; images.clear(); images.shrink_to_fit(); } }
void MapContext::pause() { MBGL_CHECK_ERROR(glFinish()); view.deactivate(); std::unique_lock<std::mutex> lockPause(data.mutexPause); data.paused = true; data.condPause.notify_all(); data.condPause.wait(lockPause, [&]{ return !data.paused; }); view.activate(); asyncInvalidate->send(); }
void Context::performCleanup() { for (GLuint id : abandonedPrograms) { if (program == id) { program.setDirty(); } MBGL_CHECK_ERROR(glDeleteProgram(id)); } abandonedPrograms.clear(); for (GLuint id : abandonedShaders) { MBGL_CHECK_ERROR(glDeleteShader(id)); } abandonedShaders.clear(); if (!abandonedBuffers.empty()) { for (const auto id : abandonedBuffers) { if (vertexBuffer == id) { vertexBuffer.setDirty(); } else if (elementBuffer == id) { elementBuffer.setDirty(); } } MBGL_CHECK_ERROR(glDeleteBuffers(int(abandonedBuffers.size()), abandonedBuffers.data())); abandonedBuffers.clear(); } if (!abandonedTextures.empty()) { for (const auto id : abandonedTextures) { if (activeTexture == id) { activeTexture.setDirty(); } } MBGL_CHECK_ERROR(glDeleteTextures(int(abandonedTextures.size()), abandonedTextures.data())); abandonedTextures.clear(); } if (!abandonedVAOs.empty()) { for (const auto id : abandonedVAOs) { if (vertexArrayObject == id) { vertexArrayObject.setDirty(); } } MBGL_CHECK_ERROR(gl::DeleteVertexArrays(int(abandonedVAOs.size()), abandonedVAOs.data())); abandonedVAOs.clear(); } if (!abandonedFBOs.empty()) { for (const auto id : abandonedFBOs) { if (bindFramebuffer == id) { bindFramebuffer.setDirty(); } } MBGL_CHECK_ERROR(glDeleteFramebuffers(int(abandonedFBOs.size()), abandonedFBOs.data())); abandonedFBOs.clear(); } }