bool OsmAnd::AtlasMapRenderer_OpenGL::doInitializeRendering() { GL_CHECK_PRESENT(glClearColor); GL_CHECK_PRESENT(glClearDepthf); bool ok; ok = AtlasMapRenderer::doInitializeRendering(); if(!ok) return false; glClearColor(0.0f, 0.0f, 0.0f, 1.0f); GL_CHECK_RESULT; glClearDepthf(1.0f); GL_CHECK_RESULT; _skyStage.initialize(); _rasterMapStage.initialize(); _symbolsStage.initialize(); #if OSMAND_DEBUG _debugStage.initialize(); #endif return true; }
bool OsmAnd::GPUAPI_OpenGL_Common::releaseResourceInGPU( const ResourceInGPU::Type type, const RefInGPU& refInGPU ) { switch (type) { case ResourceInGPU::Type::Texture: { GL_CHECK_PRESENT(glDeleteTextures); GLuint texture = static_cast<GLuint>(reinterpret_cast<intptr_t>(refInGPU)); glDeleteTextures(1, &texture); GL_CHECK_RESULT; return true; } case ResourceInGPU::Type::ArrayBuffer: { GL_CHECK_PRESENT(glDeleteBuffers); GLuint arrayBuffer = static_cast<GLuint>(reinterpret_cast<intptr_t>(refInGPU)); glDeleteBuffers(1, &arrayBuffer); GL_CHECK_RESULT; return true; } } return false; }
bool OsmAnd::AtlasMapRendererDebugStage_OpenGL::releaseQuads3D() { const auto gpuAPI = getGPUAPI(); GL_CHECK_PRESENT(glDeleteBuffers); GL_CHECK_PRESENT(glDeleteProgram); if (_vaoQuad3D.isValid()) { gpuAPI->releaseVAO(_vaoQuad3D); _vaoQuad3D.reset(); } if (_iboQuad3D.isValid()) { glDeleteBuffers(1, &_iboQuad3D); GL_CHECK_RESULT; _iboQuad3D.reset(); } if (_vboQuad3D.isValid()) { glDeleteBuffers(1, &_vboQuad3D); GL_CHECK_RESULT; _vboQuad3D.reset(); } if (_programQuad3D.id.isValid()) { glDeleteProgram(_programQuad3D.id); GL_CHECK_RESULT; _programQuad3D = ProgramQuad3D(); } return true; }
void OsmAnd::AtlasMapRendererDebugStage_OpenGL::releaseQuads3D() { const auto gpuAPI = getGPUAPI(); GL_CHECK_PRESENT(glDeleteBuffers); GL_CHECK_PRESENT(glDeleteProgram); if(_iboQuad3D) { glDeleteBuffers(1, &_iboQuad3D); GL_CHECK_RESULT; _iboQuad3D.reset(); } if(_vboQuad3D) { glDeleteBuffers(1, &_vboQuad3D); GL_CHECK_RESULT; _vboQuad3D.reset(); } if(_vaoQuad3D) { gpuAPI->glDeleteVertexArrays_wrapper(1, &_vaoQuad3D); GL_CHECK_RESULT; _vaoQuad3D.reset(); } if(_programQuad3D.id) { glDeleteProgram(_programQuad3D.id); GL_CHECK_RESULT; _programQuad3D = ProgramQuad3D(); } }
void OsmAnd::GPUAPI_OpenGLES2::uploadDataToTexture2D( GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, const GLvoid *data, GLsizei dataRowLengthInElements, GLsizei elementSize, const SourceFormat sourceFormat) { GL_CHECK_PRESENT(glTexSubImage2D); // Try to use glPixelStorei to unpack if (isSupported_EXT_unpack_subimage) { GL_CHECK_PRESENT(glPixelStorei); glPixelStorei(GL_UNPACK_ROW_LENGTH, dataRowLengthInElements); GL_CHECK_RESULT; glTexSubImage2D(target, level, xoffset, yoffset, width, height, static_cast<GLenum>(sourceFormat.format), static_cast<GLenum>(sourceFormat.type), data); GL_CHECK_RESULT; return; } // Otherwise fallback to manual unpacking // In case our row length is 0 or equals to image width (has no extra stride, just load as-is) if (dataRowLengthInElements == 0 || dataRowLengthInElements == width) { // Upload data glTexSubImage2D(target, level, xoffset, yoffset, width, height, static_cast<GLenum>(sourceFormat.format), static_cast<GLenum>(sourceFormat.type), data); GL_CHECK_RESULT; return; } // Otherwise we need to or load row by row auto pRow = reinterpret_cast<const uint8_t*>(data); for (auto rowIdx = 0; rowIdx < height; rowIdx++) { glTexSubImage2D(target, level, xoffset, yoffset + rowIdx, width, 1, static_cast<GLenum>(sourceFormat.format), static_cast<GLenum>(sourceFormat.type), pRow); GL_CHECK_RESULT; pRow += dataRowLengthInElements*elementSize; } }
void OsmAnd::AtlasMapRendererSkyStage_OpenGL::release() { const auto gpuAPI = getGPUAPI(); GL_CHECK_PRESENT(glDeleteBuffers); if(_skyplaneIBO) { glDeleteBuffers(1, &_skyplaneIBO); GL_CHECK_RESULT; _skyplaneIBO.reset(); } if(_skyplaneVBO) { glDeleteBuffers(1, &_skyplaneVBO); GL_CHECK_RESULT; _skyplaneVBO.reset(); } if(_skyplaneVAO) { gpuAPI->glDeleteVertexArrays_wrapper(1, &_skyplaneVAO); GL_CHECK_RESULT; _skyplaneVAO.reset(); } if(_program.id) { glDeleteProgram(_program.id); GL_CHECK_RESULT; _program = Program(); } }
void OsmAnd::GPUAPI_OpenGL_Common::findVariableLocation( GLuint program, GLint& location, const QString& name, const GLShaderVariableType& type ) { GL_CHECK_PRESENT(glGetAttribLocation); GL_CHECK_PRESENT(glGetUniformLocation); if(type == GLShaderVariableType::In) location = glGetAttribLocation(program, qPrintable(name)); else if(type == GLShaderVariableType::Uniform) location = glGetUniformLocation(program, qPrintable(name)); GL_CHECK_RESULT; if(location == -1) LogPrintf(LogSeverityLevel::Error, "Variable '%s' (%s) was not found in GLSL program %d", qPrintable(name), type == GLShaderVariableType::In ? "In" : "Uniform", program); assert(location != -1); assert(!_programVariables[program].contains(type, location)); _programVariables[program].insert(type, location); }
void OsmAnd::GPUAPI_OpenGLES2::glBindVertexArray_wrapper(GLuint array) { assert(isSupported_vertex_array_object); GL_CHECK_PRESENT(glBindVertexArrayOES); glBindVertexArrayOES(array); }
void OsmAnd::GPUAPI_OpenGLES2::glDeleteVertexArrays_wrapper(GLsizei n, const GLuint* arrays) { assert(isSupported_vertex_array_object); GL_CHECK_PRESENT(glDeleteVertexArraysOES); glDeleteVertexArraysOES(n, arrays); }
bool OsmAnd::AtlasMapRendererDebugStage_OpenGL::releaseLines3D(const bool gpuContextLost) { const auto gpuAPI = getGPUAPI(); GL_CHECK_PRESENT(glDeleteBuffers); GL_CHECK_PRESENT(glDeleteProgram); if (_vaoLine3D.isValid()) { gpuAPI->releaseVAO(_vaoLine3D, gpuContextLost); _vaoLine3D.reset(); } if (_iboLine3D.isValid()) { if (!gpuContextLost) { glDeleteBuffers(1, &_iboLine3D); GL_CHECK_RESULT; } _iboLine3D.reset(); } if (_vboLine3D.isValid()) { if (!gpuContextLost) { glDeleteBuffers(1, &_vboLine3D); GL_CHECK_RESULT; } _vboLine3D.reset(); } if (_programLine3D.id.isValid()) { if (!gpuContextLost) { glDeleteProgram(_programLine3D.id); GL_CHECK_RESULT; } _programLine3D = ProgramLine3D(); } return true; }
void OsmAnd::AtlasMapRendererDebugStage_OpenGL::renderLines3D() { const auto gpuAPI = getGPUAPI(); GL_CHECK_PRESENT(glUseProgram); GL_CHECK_PRESENT(glUniformMatrix4fv); GL_CHECK_PRESENT(glUniform1f); GL_CHECK_PRESENT(glUniform2f); GL_CHECK_PRESENT(glUniform3f); GL_CHECK_PRESENT(glDrawElements); gpuAPI->glBindVertexArray_wrapper(_vaoLine3D); GL_CHECK_RESULT; // Activate program glUseProgram(_programLine3D.id); GL_CHECK_RESULT; // Set projection*view*model matrix: const auto& mProjectionView = internalState.mPerspectiveProjection * internalState.mCameraView; glUniformMatrix4fv(_programLine3D.vs.param.mProjectionViewModel, 1, GL_FALSE, glm::value_ptr(mProjectionView)); GL_CHECK_RESULT; for(const auto& primitive : constOf(_lines3D)) { const auto& line = primitive.first; const auto& color = primitive.second; // Set line color glUniform4f(_programLine3D.fs.param.color, SkColorGetR(color) / 255.0f, SkColorGetG(color) / 255.0f, SkColorGetB(color) / 255.0f, SkColorGetA(color) / 255.0f); GL_CHECK_RESULT; // Iterate over pairs of points auto itV0 = line.cbegin(); auto itV1 = itV0 + 1; for(const auto itEnd = line.cend(); itV1 != itEnd; itV0 = itV1, ++itV1) { const auto& v0 = *itV0; const auto& v1 = *itV1; // Set line coordinates glUniform4f(_programLine3D.vs.param.v0, v0.x, v0.y, v0.z, 1.0f); GL_CHECK_RESULT; glUniform4f(_programLine3D.vs.param.v1, v1.x, v1.y, v1.z, 1.0f); GL_CHECK_RESULT; // Draw the line actually glDrawElements(GL_LINES, 2, GL_UNSIGNED_SHORT, nullptr); GL_CHECK_RESULT; } } // Deactivate program glUseProgram(0); GL_CHECK_RESULT; // Deselect VAO gpuAPI->glBindVertexArray_wrapper(0); GL_CHECK_RESULT; }
void OsmAnd::GPUAPI_OpenGLES2::setMipMapLevelsLimit(GLenum target, const uint32_t mipmapLevelsCount) { #if defined(OSMAND_TARGET_OS_ios) // Limit MipMap max level if possible if (isSupported_APPLE_texture_max_level) { GL_CHECK_PRESENT(glTexParameteri); glTexParameteri(target, GL_TEXTURE_MAX_LEVEL_APPLE, mipmapLevelsCount); GL_CHECK_RESULT; } #endif //OSMAND_TARGET_OS_ios }
bool OsmAnd::AtlasMapRendererDebugStage_OpenGL::renderLines2D() { const auto gpuAPI = getGPUAPI(); const auto& internalState = getInternalState(); GL_CHECK_PRESENT(glUseProgram); GL_CHECK_PRESENT(glUniformMatrix4fv); GL_CHECK_PRESENT(glUniform1f); GL_CHECK_PRESENT(glUniform2f); GL_CHECK_PRESENT(glUniform3f); GL_CHECK_PRESENT(glDrawElements); gpuAPI->useVAO(_vaoLine2D); // Activate program glUseProgram(_programLine2D.id); GL_CHECK_RESULT; // Set projection*view*model matrix: glUniformMatrix4fv(_programLine2D.vs.param.mProjectionViewModel, 1, GL_FALSE, glm::value_ptr(internalState.mOrthographicProjection)); GL_CHECK_RESULT; for(const auto& primitive : constOf(_lines2D)) { const auto& line = primitive.first; const auto& color = primitive.second; // Set line color glUniform4f(_programLine2D.fs.param.color, SkColorGetR(color) / 255.0f, SkColorGetG(color) / 255.0f, SkColorGetB(color) / 255.0f, SkColorGetA(color) / 255.0f); GL_CHECK_RESULT; // Iterate over pairs of points auto itV0 = line.cbegin(); auto itV1 = itV0 + 1; for(const auto itEnd = line.cend(); itV1 != itEnd; itV0 = itV1, ++itV1) { const auto& v0 = *itV0; const auto& v1 = *itV1; // Set line coordinates glUniform2f(_programLine2D.vs.param.v0, v0.x, currentState.windowSize.y - v0.y); GL_CHECK_RESULT; glUniform2f(_programLine2D.vs.param.v1, v1.x, currentState.windowSize.y - v1.y); GL_CHECK_RESULT; // Draw the line actually glDrawElements(GL_LINES, 2, GL_UNSIGNED_SHORT, nullptr); GL_CHECK_RESULT; } } // Deactivate program glUseProgram(0); GL_CHECK_RESULT; gpuAPI->unuseVAO(); return true; }
void OsmAnd::AtlasMapRendererDebugStage_OpenGL::renderQuads3D() { const auto gpuAPI = getGPUAPI(); GL_CHECK_PRESENT(glUseProgram); GL_CHECK_PRESENT(glUniformMatrix4fv); GL_CHECK_PRESENT(glUniform1f); GL_CHECK_PRESENT(glUniform2f); GL_CHECK_PRESENT(glUniform3f); GL_CHECK_PRESENT(glDrawElements); gpuAPI->glBindVertexArray_wrapper(_vaoQuad3D); GL_CHECK_RESULT; // Activate program glUseProgram(_programQuad3D.id); GL_CHECK_RESULT; // Set projection*view*model matrix: const auto& mProjectionView = internalState.mPerspectiveProjection * internalState.mCameraView; glUniformMatrix4fv(_programQuad3D.vs.param.mProjectionViewModel, 1, GL_FALSE, glm::value_ptr(mProjectionView)); GL_CHECK_RESULT; for(const auto& primitive : constOf(_quads3D)) { const auto& p0 = std::get<0>(primitive); const auto& p1 = std::get<1>(primitive); const auto& p2 = std::get<2>(primitive); const auto& p3 = std::get<3>(primitive); const auto& color = std::get<4>(primitive); // Set quad color glUniform4f(_programQuad3D.fs.param.color, SkColorGetR(color) / 255.0f, SkColorGetG(color) / 255.0f, SkColorGetB(color) / 255.0f, SkColorGetA(color) / 255.0f); GL_CHECK_RESULT; // Set points glUniform4f(_programQuad3D.vs.param.v0, p0.x, p0.y, p0.z, 1.0f); GL_CHECK_RESULT; glUniform4f(_programQuad3D.vs.param.v1, p1.x, p1.y, p1.z, 1.0f); GL_CHECK_RESULT; glUniform4f(_programQuad3D.vs.param.v2, p2.x, p2.y, p2.z, 1.0f); GL_CHECK_RESULT; glUniform4f(_programQuad3D.vs.param.v3, p3.x, p3.y, p3.z, 1.0f); GL_CHECK_RESULT; // Draw the quad actually glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, nullptr); GL_CHECK_RESULT; } // Deactivate program glUseProgram(0); GL_CHECK_RESULT; // Deselect VAO gpuAPI->glBindVertexArray_wrapper(0); GL_CHECK_RESULT; }
bool OsmAnd::GPUAPI_OpenGLES2::isShaderPrecisionFormatSupported(GLenum shaderType, GLenum precisionType) { GL_CHECK_PRESENT(glGetShaderPrecisionFormat); GLint range[] = { 0, 0 }; GLint precision = 0; glGetShaderPrecisionFormat(shaderType, precisionType, range, &precision); GL_CHECK_RESULT; if (range[0] == 0 && range[1] == 0) return false; return true; }
bool OsmAnd::GPUAPI_OpenGL_Common::uploadTileAsArrayBufferToGPU(const std::shared_ptr< const MapTile >& tile, std::shared_ptr< const ResourceInGPU >& resourceInGPU) { GL_CHECK_PRESENT(glBindBuffer); GL_CHECK_PRESENT(glBufferData); if(tile->dataType != MapTileDataType::ElevationData) { assert(false); return false; } const auto elevationTile = std::static_pointer_cast<const MapElevationDataTile>(tile); // Create array buffer GLuint buffer; glGenBuffers(1, &buffer); GL_CHECK_RESULT; // Bind it glBindBuffer(GL_ARRAY_BUFFER, buffer); GL_CHECK_RESULT; // Upload data const auto itemsCount = tile->size*tile->size; assert(tile->size*sizeof(float) == tile->rowLength); glBufferData(GL_ARRAY_BUFFER, itemsCount*sizeof(float), tile->data, GL_STATIC_DRAW); GL_CHECK_RESULT; // Unbind it glBindBuffer(GL_ARRAY_BUFFER, 0); GL_CHECK_RESULT; auto arrayBufferInGPU = new ArrayBufferInGPU(this, reinterpret_cast<RefInGPU>(buffer), itemsCount); resourceInGPU.reset(static_cast<ResourceInGPU*>(arrayBufferInGPU)); return true; }
void OsmAnd::GPUAPI_OpenGLES2::allocateTexture2D(GLenum target, GLsizei levels, GLsizei width, GLsizei height, const TextureFormat format) { // Use glTexStorage2D if possible if (isSupported_EXT_texture_storage) { GL_CHECK_PRESENT(glTexStorage2DEXT); glTexStorage2DEXT(target, levels, static_cast<GLenum>(format), width, height); GL_CHECK_RESULT; return; } // Fallback to dumb allocation GPUAPI_OpenGL::allocateTexture2D(target, levels, width, height, format); }
void OsmAnd::AtlasMapRendererDebugStage_OpenGL::renderRects2D() { const auto gpuAPI = getGPUAPI(); GL_CHECK_PRESENT(glUseProgram); GL_CHECK_PRESENT(glUniformMatrix4fv); GL_CHECK_PRESENT(glUniform1f); GL_CHECK_PRESENT(glUniform2f); GL_CHECK_PRESENT(glUniform3f); GL_CHECK_PRESENT(glDrawElements); gpuAPI->glBindVertexArray_wrapper(_vaoRect2D); GL_CHECK_RESULT; // Activate program glUseProgram(_programRect2D.id); GL_CHECK_RESULT; // Set projection*view*model matrix: glUniformMatrix4fv(_programRect2D.vs.param.mProjectionViewModel, 1, GL_FALSE, glm::value_ptr(internalState.mOrthographicProjection)); GL_CHECK_RESULT; for(const auto& primitive : constOf(_rects2D)) { const auto& rect = std::get<0>(primitive); const auto& color = std::get<1>(primitive); const auto& angle = std::get<2>(primitive); // Set rectangle coordinates const auto center = rect.center(); glUniform4f(_programRect2D.vs.param.rect, currentState.windowSize.y - center.y, center.x, rect.height(), rect.width()); GL_CHECK_RESULT; // Set rotation angle glUniform1f(_programRect2D.vs.param.angle, angle); GL_CHECK_RESULT; // Set rectangle color glUniform4f(_programRect2D.fs.param.color, SkColorGetR(color) / 255.0f, SkColorGetG(color) / 255.0f, SkColorGetB(color) / 255.0f, SkColorGetA(color) / 255.0f); GL_CHECK_RESULT; // Draw the rectangle actually glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, nullptr); GL_CHECK_RESULT; } // Deactivate program glUseProgram(0); GL_CHECK_RESULT; // Deselect VAO gpuAPI->glBindVertexArray_wrapper(0); GL_CHECK_RESULT; }
void OsmAnd::AtlasMapRendererSkyStage_OpenGL::render() { const auto gpuAPI = getGPUAPI(); GL_PUSH_GROUP_MARKER(QLatin1String("sky")); GL_CHECK_PRESENT(glUseProgram); GL_CHECK_PRESENT(glUniformMatrix4fv); GL_CHECK_PRESENT(glUniform1f); GL_CHECK_PRESENT(glUniform2f); GL_CHECK_PRESENT(glUniform3f); GL_CHECK_PRESENT(glDrawElements); // Set sky plane VAO gpuAPI->glBindVertexArray_wrapper(_skyplaneVAO); GL_CHECK_RESULT; // Activate program glUseProgram(_program.id); GL_CHECK_RESULT; // Set projection*view*model matrix: const auto mFogTranslate = glm::translate(glm::vec3(0.0f, 0.0f, -internalState.correctedFogDistance)); const auto mModel = internalState.mAzimuthInv * mFogTranslate; const auto mProjectionViewModel = internalState.mPerspectiveProjection * internalState.mCameraView * mModel; glUniformMatrix4fv(_program.vs.param.mProjectionViewModel, 1, GL_FALSE, glm::value_ptr(mProjectionViewModel)); GL_CHECK_RESULT; // Set size of the skyplane glUniform2f(_program.vs.param.planeSize, internalState.skyplaneSize.x, internalState.skyplaneSize.y); GL_CHECK_RESULT; // Set sky parameters glUniform4f(_program.fs.param.skyColor, currentState.skyColor.r, currentState.skyColor.g, currentState.skyColor.b, 1.0f); GL_CHECK_RESULT; // Draw the skyplane actually glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, nullptr); GL_CHECK_RESULT; // Deactivate program glUseProgram(0); GL_CHECK_RESULT; // Deselect VAO gpuAPI->glBindVertexArray_wrapper(0); GL_CHECK_RESULT; GL_POP_GROUP_MARKER; }
GLuint OsmAnd::GPUAPI_OpenGL_Common::compileShader( GLenum shaderType, const char* source ) { GL_CHECK_PRESENT(glCreateShader); GL_CHECK_PRESENT(glShaderSource); GL_CHECK_PRESENT(glCompileShader); GL_CHECK_PRESENT(glGetShaderiv); GL_CHECK_PRESENT(glGetShaderInfoLog); GL_CHECK_PRESENT(glDeleteShader); GLuint shader; shader = glCreateShader(shaderType); GL_CHECK_RESULT; const GLint sourceLen = static_cast<GLint>(strlen(source)); glShaderSource(shader, 1, &source, &sourceLen); GL_CHECK_RESULT; glCompileShader(shader); GL_CHECK_RESULT; // Check if compiled GLint didCompile; glGetShaderiv(shader, GL_COMPILE_STATUS, &didCompile); GL_CHECK_RESULT; if(didCompile != GL_TRUE) { GLint logBufferLen = 0; GLsizei logLen = 0; glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logBufferLen); GL_CHECK_RESULT; if (logBufferLen > 1) { GLchar* log = (GLchar*)malloc(logBufferLen); glGetShaderInfoLog(shader, logBufferLen, &logLen, log); GL_CHECK_RESULT; assert(logLen + 1 == logBufferLen); LogPrintf(LogSeverityLevel::Error, "Failed to compile GLSL shader:\n%s\nSources:\n%s", log, source); free(log); } glDeleteShader(shader); shader = 0; return shader; } return shader; }
void OsmAnd::AtlasMapRendererDebugStage_OpenGL::initializeQuads3D() { const auto gpuAPI = getGPUAPI(); GL_CHECK_PRESENT(glGenBuffers); GL_CHECK_PRESENT(glBindBuffer); GL_CHECK_PRESENT(glBufferData); GL_CHECK_PRESENT(glEnableVertexAttribArray); GL_CHECK_PRESENT(glVertexAttribPointer); // Compile vertex shader const QString vertexShader = QLatin1String( // Input data // (1.0, 0.0, 0.0, 0.0) for first point // (0.0, 1.0, 0.0, 0.0) for second point // (0.0, 0.0, 1.0, 0.0) for third point // (0.0, 0.0, 0.0, 1.0) for fourth point "INPUT vec4 in_vs_vertexPosition; ""\n" " ""\n" // Parameters: common data "uniform mat4 param_vs_mProjectionViewModel; ""\n" "uniform vec4 param_vs_v0; ""\n" "uniform vec4 param_vs_v1; ""\n" "uniform vec4 param_vs_v2; ""\n" "uniform vec4 param_vs_v3; ""\n" " ""\n" "void main() ""\n" "{ ""\n" " vec4 v = ""\n" " in_vs_vertexPosition.x*param_vs_v0 + ""\n" " in_vs_vertexPosition.y*param_vs_v1 + ""\n" " in_vs_vertexPosition.z*param_vs_v2 + ""\n" " in_vs_vertexPosition.w*param_vs_v3; ""\n" " ""\n" " gl_Position = param_vs_mProjectionViewModel * v; ""\n" "} ""\n"); auto preprocessedVertexShader = vertexShader; gpuAPI->preprocessVertexShader(preprocessedVertexShader); gpuAPI->optimizeVertexShader(preprocessedVertexShader); const auto vsId = gpuAPI->compileShader(GL_VERTEX_SHADER, qPrintable(preprocessedVertexShader)); assert(vsId != 0); // Compile fragment shader const QString fragmentShader = QLatin1String( // Parameters: common data "uniform lowp vec4 param_fs_color; ""\n" " ""\n" "void main() ""\n" "{ ""\n" " FRAGMENT_COLOR_OUTPUT = param_fs_color; ""\n" "} ""\n"); auto preprocessedFragmentShader = fragmentShader; QString preprocessedFragmentShader_UnrolledPerLayerProcessingCode; gpuAPI->preprocessFragmentShader(preprocessedFragmentShader); gpuAPI->optimizeFragmentShader(preprocessedFragmentShader); const auto fsId = gpuAPI->compileShader(GL_FRAGMENT_SHADER, qPrintable(preprocessedFragmentShader)); assert(fsId != 0); // Link everything into program object GLuint shaders[] = { vsId, fsId }; _programQuad3D.id = gpuAPI->linkProgram(2, shaders); assert(_programQuad3D.id); const auto& lookup = gpuAPI->obtainVariablesLookupContext(_programQuad3D.id); lookup->lookupLocation(_programQuad3D.vs.in.vertexPosition, "in_vs_vertexPosition", GLShaderVariableType::In); lookup->lookupLocation(_programQuad3D.vs.param.mProjectionViewModel, "param_vs_mProjectionViewModel", GLShaderVariableType::Uniform); lookup->lookupLocation(_programQuad3D.vs.param.v0, "param_vs_v0", GLShaderVariableType::Uniform); lookup->lookupLocation(_programQuad3D.vs.param.v1, "param_vs_v1", GLShaderVariableType::Uniform); lookup->lookupLocation(_programQuad3D.vs.param.v2, "param_vs_v2", GLShaderVariableType::Uniform); lookup->lookupLocation(_programQuad3D.vs.param.v3, "param_vs_v3", GLShaderVariableType::Uniform); lookup->lookupLocation(_programQuad3D.fs.param.color, "param_fs_color", GLShaderVariableType::Uniform); // Vertex data (x,y,z,w) float vertices[4][4] = { { 1.0f, 0.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 1.0f, 0.0f }, { 0.0f, 0.0f, 0.0f, 1.0f } }; const auto verticesCount = 4; // Index data GLushort indices[6] = { 0, 1, 2, 0, 2, 3 }; const auto indicesCount = 6; // Create Vertex Array Object gpuAPI->glGenVertexArrays_wrapper(1, &_vaoQuad3D); GL_CHECK_RESULT; gpuAPI->glBindVertexArray_wrapper(_vaoQuad3D); GL_CHECK_RESULT; // Create vertex buffer and associate it with VAO glGenBuffers(1, &_vboQuad3D); GL_CHECK_RESULT; glBindBuffer(GL_ARRAY_BUFFER, _vboQuad3D); GL_CHECK_RESULT; glBufferData(GL_ARRAY_BUFFER, verticesCount * sizeof(float)*4, vertices, GL_STATIC_DRAW); GL_CHECK_RESULT; glEnableVertexAttribArray(*_programQuad3D.vs.in.vertexPosition); GL_CHECK_RESULT; glVertexAttribPointer(*_programQuad3D.vs.in.vertexPosition, 4, GL_FLOAT, GL_FALSE, sizeof(float)*4, nullptr); GL_CHECK_RESULT; // Create index buffer and associate it with VAO glGenBuffers(1, &_iboQuad3D); GL_CHECK_RESULT; glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _iboQuad3D); GL_CHECK_RESULT; glBufferData(GL_ELEMENT_ARRAY_BUFFER, indicesCount * sizeof(GLushort), indices, GL_STATIC_DRAW); GL_CHECK_RESULT; gpuAPI->glBindVertexArray_wrapper(0); GL_CHECK_RESULT; }
void OsmAnd::AtlasMapRendererDebugStage_OpenGL::initializeRects2D() { const auto gpuAPI = getGPUAPI(); GL_CHECK_PRESENT(glGenBuffers); GL_CHECK_PRESENT(glBindBuffer); GL_CHECK_PRESENT(glBufferData); GL_CHECK_PRESENT(glEnableVertexAttribArray); GL_CHECK_PRESENT(glVertexAttribPointer); // Compile vertex shader const QString vertexShader = QLatin1String( // Input data "INPUT vec2 in_vs_vertexPosition; ""\n" " ""\n" // Parameters: common data "uniform mat4 param_vs_mProjectionViewModel; ""\n" "uniform vec4 param_vs_rect; ""\n" "uniform float param_vs_angle; ""\n" " ""\n" "void main() ""\n" "{ ""\n" " vec2 rectCenter = param_vs_rect.yx; ""\n" " vec2 p = in_vs_vertexPosition*param_vs_rect.wz + rectCenter; ""\n" " vec4 v; ""\n" " float cos_a = cos(param_vs_angle); ""\n" " float sin_a = sin(param_vs_angle); ""\n" " p -= rectCenter; ""\n" " v.x = rectCenter.x + p.x*cos_a - p.y*sin_a; ""\n" " v.y = rectCenter.y + p.x*sin_a + p.y*cos_a; ""\n" " v.z = -1.0; ""\n" " v.w = 1.0; ""\n" " ""\n" " gl_Position = param_vs_mProjectionViewModel * v; ""\n" "} ""\n"); auto preprocessedVertexShader = vertexShader; gpuAPI->preprocessVertexShader(preprocessedVertexShader); gpuAPI->optimizeVertexShader(preprocessedVertexShader); const auto vsId = gpuAPI->compileShader(GL_VERTEX_SHADER, qPrintable(preprocessedVertexShader)); assert(vsId != 0); // Compile fragment shader const QString fragmentShader = QLatin1String( // Parameters: common data "uniform lowp vec4 param_fs_color; ""\n" " ""\n" "void main() ""\n" "{ ""\n" " FRAGMENT_COLOR_OUTPUT = param_fs_color; ""\n" "} ""\n"); auto preprocessedFragmentShader = fragmentShader; QString preprocessedFragmentShader_UnrolledPerLayerProcessingCode; gpuAPI->preprocessFragmentShader(preprocessedFragmentShader); gpuAPI->optimizeFragmentShader(preprocessedFragmentShader); const auto fsId = gpuAPI->compileShader(GL_FRAGMENT_SHADER, qPrintable(preprocessedFragmentShader)); assert(fsId != 0); // Link everything into program object GLuint shaders[] = { vsId, fsId }; _programRect2D.id = gpuAPI->linkProgram(2, shaders); assert(_programRect2D.id); const auto& lookup = gpuAPI->obtainVariablesLookupContext(_programRect2D.id); lookup->lookupLocation(_programRect2D.vs.in.vertexPosition, "in_vs_vertexPosition", GLShaderVariableType::In); lookup->lookupLocation(_programRect2D.vs.param.mProjectionViewModel, "param_vs_mProjectionViewModel", GLShaderVariableType::Uniform); lookup->lookupLocation(_programRect2D.vs.param.rect, "param_vs_rect", GLShaderVariableType::Uniform); lookup->lookupLocation(_programRect2D.vs.param.angle, "param_vs_angle", GLShaderVariableType::Uniform); lookup->lookupLocation(_programRect2D.fs.param.color, "param_fs_color", GLShaderVariableType::Uniform); // Vertex data (x,y) float vertices[4][2] = { { -0.5f, -0.5f }, { -0.5f, 0.5f }, { 0.5f, 0.5f }, { 0.5f, -0.5f } }; const auto verticesCount = 4; // Index data GLushort indices[6] = { 0, 1, 2, 0, 2, 3 }; const auto indicesCount = 6; // Create Vertex Array Object gpuAPI->glGenVertexArrays_wrapper(1, &_vaoRect2D); GL_CHECK_RESULT; gpuAPI->glBindVertexArray_wrapper(_vaoRect2D); GL_CHECK_RESULT; // Create vertex buffer and associate it with VAO glGenBuffers(1, &_vboRect2D); GL_CHECK_RESULT; glBindBuffer(GL_ARRAY_BUFFER, _vboRect2D); GL_CHECK_RESULT; glBufferData(GL_ARRAY_BUFFER, verticesCount * sizeof(float)* 2, vertices, GL_STATIC_DRAW); GL_CHECK_RESULT; glEnableVertexAttribArray(*_programRect2D.vs.in.vertexPosition); GL_CHECK_RESULT; glVertexAttribPointer(*_programRect2D.vs.in.vertexPosition, 2, GL_FLOAT, GL_FALSE, sizeof(float)* 2, nullptr); GL_CHECK_RESULT; // Create index buffer and associate it with VAO glGenBuffers(1, &_iboRect2D); GL_CHECK_RESULT; glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _iboRect2D); GL_CHECK_RESULT; glBufferData(GL_ELEMENT_ARRAY_BUFFER, indicesCount * sizeof(GLushort), indices, GL_STATIC_DRAW); GL_CHECK_RESULT; gpuAPI->glBindVertexArray_wrapper(0); GL_CHECK_RESULT; }
bool OsmAnd::AtlasMapRenderer_OpenGL::doRenderFrame() { GL_PUSH_GROUP_MARKER(QLatin1String("OsmAndCore")); GL_CHECK_PRESENT(glViewport); GL_CHECK_PRESENT(glEnable); GL_CHECK_PRESENT(glDisable); GL_CHECK_PRESENT(glBlendFunc); GL_CHECK_PRESENT(glClear); #if OSMAND_DEBUG _debugStage.clear(); #endif // Setup viewport glViewport( currentState.viewport.left, currentState.windowSize.y - currentState.viewport.bottom, currentState.viewport.width(), currentState.viewport.height()); GL_CHECK_RESULT; // Clear buffers glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); GL_CHECK_RESULT; // Turn off blending for sky and map stages glDisable(GL_BLEND); GL_CHECK_RESULT; // Turn on depth testing for sky stage (since with disabled depth test, write to depth buffer is blocked), // but since sky is on top of everything, accept all fragments glEnable(GL_DEPTH_TEST); GL_CHECK_RESULT; glDepthFunc(GL_ALWAYS); GL_CHECK_RESULT; // Render the sky _skyStage.render(); // Change depth test function prior to raster map stage and further stages glDepthFunc(GL_LEQUAL); GL_CHECK_RESULT; // Raster map stage is rendered without blending, since it's done in fragment shader _rasterMapStage.render(); // Turn on blending since now objects with transparency are going to be rendered glEnable(GL_BLEND); GL_CHECK_RESULT; glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); GL_CHECK_RESULT; // Render map symbols without writing depth buffer, since symbols use own sorting and intersection checking //NOTE: Currently map symbols are incompatible with height-maps glDepthMask(GL_FALSE); GL_CHECK_RESULT; _symbolsStage.render(); glDepthMask(GL_TRUE); GL_CHECK_RESULT; //TODO: render special fog object some day #if OSMAND_DEBUG glDisable(GL_DEPTH_TEST); GL_CHECK_RESULT; _debugStage.render(); glEnable(GL_DEPTH_TEST); GL_CHECK_RESULT; #endif GL_POP_GROUP_MARKER; return true; }
GLuint OsmAnd::GPUAPI_OpenGL_Common::linkProgram( GLuint shadersCount, GLuint *shaders ) { GL_CHECK_PRESENT(glCreateProgram); GL_CHECK_PRESENT(glAttachShader); GL_CHECK_PRESENT(glLinkProgram); GL_CHECK_PRESENT(glGetProgramiv); GL_CHECK_PRESENT(glGetProgramInfoLog); GL_CHECK_PRESENT(glDeleteProgram); GLuint program = 0; program = glCreateProgram(); GL_CHECK_RESULT; for(auto shaderIdx = 0u; shaderIdx < shadersCount; shaderIdx++) { glAttachShader(program, shaders[shaderIdx]); GL_CHECK_RESULT; } glLinkProgram(program); GL_CHECK_RESULT; GLint linkSuccessful; glGetProgramiv(program, GL_LINK_STATUS, &linkSuccessful); GL_CHECK_RESULT; if(linkSuccessful != GL_TRUE) { GLint logBufferLen = 0; GLsizei logLen = 0; glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logBufferLen); GL_CHECK_RESULT; if (logBufferLen > 1) { GLchar* log = (GLchar*)malloc(logBufferLen); glGetProgramInfoLog(program, logBufferLen, &logLen, log); GL_CHECK_RESULT; assert(logLen + 1 == logBufferLen); LogPrintf(LogSeverityLevel::Error, "Failed to link GLSL program:\n%s", log); free(log); } glDeleteProgram(program); GL_CHECK_RESULT; program = 0; return program; } // Show some info GLint attributesCount; glGetProgramiv(program, GL_ACTIVE_ATTRIBUTES, &attributesCount); GL_CHECK_RESULT; LogPrintf(LogSeverityLevel::Info, "GLSL program %d has %d input variable(s)", program, attributesCount); GLint uniformsCount; glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &uniformsCount); GL_CHECK_RESULT; LogPrintf(LogSeverityLevel::Info, "GLSL program %d has %d parameter variable(s)", program, uniformsCount); return program; }
void OsmAnd::GPUAPI_OpenGLES2::glClearDepth_wrapper(const float depth) { GL_CHECK_PRESENT(glClearDepthf); glClearDepthf(depth); }
void OsmAnd::GPUAPI_OpenGLES2::glPopGroupMarkerEXT_wrapper() { GL_CHECK_PRESENT(glPopGroupMarkerEXT); glPopGroupMarkerEXT(); }
void OsmAnd::GPUAPI_OpenGLES2::glPushGroupMarkerEXT_wrapper(GLsizei length, const GLchar* marker) { GL_CHECK_PRESENT(glPushGroupMarkerEXT); glPushGroupMarkerEXT(length, marker); }
bool OsmAnd::GPUAPI_OpenGL_Common::uploadSymbolAsTextureToGPU(const std::shared_ptr< const MapSymbol >& symbol, std::shared_ptr< const ResourceInGPU >& resourceInGPU) { GL_CHECK_PRESENT(glGenTextures); GL_CHECK_PRESENT(glBindTexture); GL_CHECK_PRESENT(glGenerateMipmap); GL_CHECK_PRESENT(glTexParameteri); // Determine texture properties: GLsizei sourcePixelByteSize = 0; bool symbolUsesPalette = false; switch(symbol->bitmap->getConfig()) { case SkBitmap::Config::kARGB_8888_Config: sourcePixelByteSize = 4; break; case SkBitmap::Config::kARGB_4444_Config: case SkBitmap::Config::kRGB_565_Config: sourcePixelByteSize = 2; break; case SkBitmap::Config::kIndex8_Config: sourcePixelByteSize = 1; symbolUsesPalette = true; break; default: assert(false); return false; } const auto textureFormat = getTextureFormat(symbol); // Symbols don't use mipmapping, so there is no difference between POT vs NPOT size of texture. // In OpenGLES 2.0 and OpenGL 3.0+, NPOT textures are supported in general. // OpenGLES 2.0 has some limitations without isSupported_texturesNPOT: // - no mipmaps // - only LINEAR or NEAREST minification filter. // Create texture id GLuint texture; glGenTextures(1, &texture); GL_CHECK_RESULT; assert(texture != 0); // Activate texture glBindTexture(GL_TEXTURE_2D, texture); GL_CHECK_RESULT; if(!symbolUsesPalette) { // Allocate square 2D texture allocateTexture2D(GL_TEXTURE_2D, 1, symbol->bitmap->width(), symbol->bitmap->height(), textureFormat); // Upload data uploadDataToTexture2D(GL_TEXTURE_2D, 0, 0, 0, (GLsizei)symbol->bitmap->width(), (GLsizei)symbol->bitmap->height(), symbol->bitmap->getPixels(), symbol->bitmap->rowBytes() / sourcePixelByteSize, sourcePixelByteSize, getSourceFormat(symbol)); // Set maximal mipmap level to 0 setMipMapLevelsLimit(GL_TEXTURE_2D, 0); } else { //TODO: palettes are not yet supported assert(false); } // Deselect atlas as active texture glBindTexture(GL_TEXTURE_2D, 0); GL_CHECK_RESULT; // Create resource-in-GPU descriptor const auto textureInGPU = new TextureInGPU(this, reinterpret_cast<RefInGPU>(texture), symbol->bitmap->width(), symbol->bitmap->height(), 1); resourceInGPU.reset(static_cast<ResourceInGPU*>(textureInGPU)); return true; }
void OsmAnd::GPUAPI_OpenGLES2::applyTextureBlockToTexture(const GLenum texture, const GLenum textureBlock) { GL_CHECK_PRESENT(glTexParameteri); const auto samplerType = _textureBlocksSamplers[textureBlock]; if (samplerType == SamplerType::ElevationDataTile) { glTexParameteri(texture, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); GL_CHECK_RESULT; glTexParameteri(texture, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); GL_CHECK_RESULT; glTexParameteri(texture, GL_TEXTURE_MIN_FILTER, GL_NEAREST); GL_CHECK_RESULT; glTexParameteri(texture, GL_TEXTURE_MAG_FILTER, GL_NEAREST); GL_CHECK_RESULT; } else if (samplerType == SamplerType::BitmapTile_Bilinear) { glTexParameteri(texture, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); GL_CHECK_RESULT; glTexParameteri(texture, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); GL_CHECK_RESULT; glTexParameteri(texture, GL_TEXTURE_MIN_FILTER, GL_LINEAR); GL_CHECK_RESULT; glTexParameteri(texture, GL_TEXTURE_MAG_FILTER, GL_LINEAR); GL_CHECK_RESULT; } else if (samplerType == SamplerType::BitmapTile_BilinearMipmap) { glTexParameteri(texture, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); GL_CHECK_RESULT; glTexParameteri(texture, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); GL_CHECK_RESULT; glTexParameteri(texture, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); GL_CHECK_RESULT; glTexParameteri(texture, GL_TEXTURE_MAG_FILTER, GL_LINEAR); GL_CHECK_RESULT; } else if (samplerType == SamplerType::BitmapTile_TrilinearMipmap) { glTexParameteri(texture, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); GL_CHECK_RESULT; glTexParameteri(texture, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); GL_CHECK_RESULT; glTexParameteri(texture, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); GL_CHECK_RESULT; glTexParameteri(texture, GL_TEXTURE_MAG_FILTER, GL_LINEAR); GL_CHECK_RESULT; } else if (samplerType == SamplerType::Symbol) { glTexParameteri(texture, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); GL_CHECK_RESULT; glTexParameteri(texture, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); GL_CHECK_RESULT; glTexParameteri(texture, GL_TEXTURE_MIN_FILTER, GL_LINEAR); GL_CHECK_RESULT; glTexParameteri(texture, GL_TEXTURE_MAG_FILTER, GL_LINEAR); GL_CHECK_RESULT; } }
bool OsmAnd::AtlasMapRendererDebugStage_OpenGL::initializeLines3D() { const auto gpuAPI = getGPUAPI(); GL_CHECK_PRESENT(glGenBuffers); GL_CHECK_PRESENT(glBindBuffer); GL_CHECK_PRESENT(glBufferData); GL_CHECK_PRESENT(glEnableVertexAttribArray); GL_CHECK_PRESENT(glVertexAttribPointer); GL_CHECK_PRESENT(glDeleteShader); GL_CHECK_PRESENT(glDeleteProgram); // Compile vertex shader const QString vertexShader = QLatin1String( // Input data "INPUT vec2 in_vs_vertexPosition; // (1.0, 0.0) for first point, (0.0, 1.0) for second ""\n" " ""\n" // Parameters: common data "uniform mat4 param_vs_mProjectionViewModel; ""\n" "uniform vec4 param_vs_v0; ""\n" "uniform vec4 param_vs_v1; ""\n" " ""\n" "void main() ""\n" "{ ""\n" " vec4 v; ""\n" " v = in_vs_vertexPosition.x*param_vs_v0 + in_vs_vertexPosition.y*param_vs_v1; ""\n" " ""\n" " gl_Position = param_vs_mProjectionViewModel * v; ""\n" "} ""\n"); auto preprocessedVertexShader = vertexShader; gpuAPI->preprocessVertexShader(preprocessedVertexShader); gpuAPI->optimizeVertexShader(preprocessedVertexShader); const auto vsId = gpuAPI->compileShader(GL_VERTEX_SHADER, qPrintable(preprocessedVertexShader)); if (vsId == 0) { LogPrintf(LogSeverityLevel::Error, "Failed to compile AtlasMapRendererDebugStage_OpenGL vertex shader"); return false; } // Compile fragment shader const QString fragmentShader = QLatin1String( // Parameters: common data "uniform lowp vec4 param_fs_color; ""\n" " ""\n" "void main() ""\n" "{ ""\n" " FRAGMENT_COLOR_OUTPUT = param_fs_color; ""\n" "} ""\n"); auto preprocessedFragmentShader = fragmentShader; QString preprocessedFragmentShader_UnrolledPerLayerProcessingCode; gpuAPI->preprocessFragmentShader(preprocessedFragmentShader); gpuAPI->optimizeFragmentShader(preprocessedFragmentShader); const auto fsId = gpuAPI->compileShader(GL_FRAGMENT_SHADER, qPrintable(preprocessedFragmentShader)); if (fsId == 0) { glDeleteShader(vsId); GL_CHECK_RESULT; LogPrintf(LogSeverityLevel::Error, "Failed to compile AtlasMapRendererDebugStage_OpenGL fragment shader"); return false; } // Link everything into program object GLuint shaders[] = { vsId, fsId }; QHash< QString, GPUAPI_OpenGL::GlslProgramVariable > variablesMap; _programLine3D.id = gpuAPI->linkProgram(2, shaders, true, &variablesMap); if (!_programLine3D.id.isValid()) { LogPrintf(LogSeverityLevel::Error, "Failed to link AtlasMapRendererDebugStage_OpenGL program"); return false; } bool ok = true; const auto& lookup = gpuAPI->obtainVariablesLookupContext(_programLine3D.id, variablesMap); ok = ok && lookup->lookupLocation(_programLine3D.vs.in.vertexPosition, "in_vs_vertexPosition", GlslVariableType::In); ok = ok && lookup->lookupLocation(_programLine3D.vs.param.mProjectionViewModel, "param_vs_mProjectionViewModel", GlslVariableType::Uniform); ok = ok && lookup->lookupLocation(_programLine3D.vs.param.v0, "param_vs_v0", GlslVariableType::Uniform); ok = ok && lookup->lookupLocation(_programLine3D.vs.param.v1, "param_vs_v1", GlslVariableType::Uniform); ok = ok && lookup->lookupLocation(_programLine3D.fs.param.color, "param_fs_color", GlslVariableType::Uniform); if (!ok) { glDeleteProgram(_programLine3D.id); GL_CHECK_RESULT; _programLine3D.id.reset(); return false; } // Vertex data (x,y) float vertices[2][2] = { { 1.0f, 0.0f }, { 0.0f, 1.0f } }; const auto verticesCount = 2; // Index data GLushort indices[2] = { 0, 1 }; const auto indicesCount = 2; _vaoLine3D = gpuAPI->allocateUninitializedVAO(); // Create vertex buffer and associate it with VAO glGenBuffers(1, &_vboLine3D); GL_CHECK_RESULT; glBindBuffer(GL_ARRAY_BUFFER, _vboLine3D); GL_CHECK_RESULT; glBufferData(GL_ARRAY_BUFFER, verticesCount * sizeof(float)* 2, vertices, GL_STATIC_DRAW); GL_CHECK_RESULT; glEnableVertexAttribArray(*_programLine3D.vs.in.vertexPosition); GL_CHECK_RESULT; glVertexAttribPointer(*_programLine3D.vs.in.vertexPosition, 2, GL_FLOAT, GL_FALSE, sizeof(float)* 2, nullptr); GL_CHECK_RESULT; // Create index buffer and associate it with VAO glGenBuffers(1, &_iboLine3D); GL_CHECK_RESULT; glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _iboLine3D); GL_CHECK_RESULT; glBufferData(GL_ELEMENT_ARRAY_BUFFER, indicesCount * sizeof(GLushort), indices, GL_STATIC_DRAW); GL_CHECK_RESULT; gpuAPI->initializeVAO(_vaoLine3D); glBindBuffer(GL_ARRAY_BUFFER, 0); GL_CHECK_RESULT; glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); GL_CHECK_RESULT; return true; }