743 lines
28 KiB
C++
743 lines
28 KiB
C++
#include <mbgl/gl/context.hpp>
|
|
|
|
#include <mbgl/gfx/shader_registry.hpp>
|
|
#include <mbgl/gl/command_encoder.hpp>
|
|
#include <mbgl/gl/defines.hpp>
|
|
#include <mbgl/gl/drawable_gl.hpp>
|
|
#include <mbgl/gl/drawable_gl_builder.hpp>
|
|
#include <mbgl/gl/drawable_gl_tweaker.hpp>
|
|
#include <mbgl/gl/draw_scope_resource.hpp>
|
|
#include <mbgl/gl/enum.hpp>
|
|
#include <mbgl/gl/layer_group_gl.hpp>
|
|
#include <mbgl/gl/renderer_backend.hpp>
|
|
#include <mbgl/gl/renderbuffer_resource.hpp>
|
|
#include <mbgl/gl/uniform_buffer_gl.hpp>
|
|
#include <mbgl/gl/texture_resource.hpp>
|
|
#include <mbgl/gl/texture.hpp>
|
|
#include <mbgl/gl/offscreen_texture.hpp>
|
|
#include <mbgl/gl/debugging_extension.hpp>
|
|
#include <mbgl/renderer/paint_parameters.hpp>
|
|
#include <mbgl/shaders/gl/shader_program_gl.hpp>
|
|
#include <mbgl/util/traits.hpp>
|
|
#include <mbgl/util/std.hpp>
|
|
#include <mbgl/util/logging.hpp>
|
|
|
|
#include <cstring>
|
|
#include <iterator>
|
|
|
|
namespace mbgl {
|
|
namespace gl {
|
|
|
|
using namespace platform;
|
|
|
|
static_assert(underlying_type(ShaderType::Vertex) == GL_VERTEX_SHADER, "OpenGL type mismatch");
|
|
static_assert(underlying_type(ShaderType::Fragment) == GL_FRAGMENT_SHADER, "OpenGL type mismatch");
|
|
|
|
static_assert(std::is_same_v<ProgramID, GLuint>, "OpenGL type mismatch");
|
|
static_assert(std::is_same_v<ShaderID, GLuint>, "OpenGL type mismatch");
|
|
static_assert(std::is_same_v<BufferID, GLuint>, "OpenGL type mismatch");
|
|
static_assert(std::is_same_v<TextureID, GLuint>, "OpenGL type mismatch");
|
|
static_assert(std::is_same_v<VertexArrayID, GLuint>, "OpenGL type mismatch");
|
|
static_assert(std::is_same_v<FramebufferID, GLuint>, "OpenGL type mismatch");
|
|
static_assert(std::is_same_v<RenderbufferID, GLuint>, "OpenGL type mismatch");
|
|
|
|
static_assert(underlying_type(UniformDataType::Float) == GL_FLOAT, "OpenGL type mismatch");
|
|
static_assert(underlying_type(UniformDataType::FloatVec2) == GL_FLOAT_VEC2, "OpenGL type mismatch");
|
|
static_assert(underlying_type(UniformDataType::FloatVec3) == GL_FLOAT_VEC3, "OpenGL type mismatch");
|
|
static_assert(underlying_type(UniformDataType::FloatVec4) == GL_FLOAT_VEC4, "OpenGL type mismatch");
|
|
static_assert(underlying_type(UniformDataType::Int) == GL_INT, "OpenGL type mismatch");
|
|
static_assert(underlying_type(UniformDataType::IntVec2) == GL_INT_VEC2, "OpenGL type mismatch");
|
|
static_assert(underlying_type(UniformDataType::IntVec3) == GL_INT_VEC3, "OpenGL type mismatch");
|
|
static_assert(underlying_type(UniformDataType::IntVec4) == GL_INT_VEC4, "OpenGL type mismatch");
|
|
static_assert(underlying_type(UniformDataType::Bool) == GL_BOOL, "OpenGL type mismatch");
|
|
static_assert(underlying_type(UniformDataType::BoolVec2) == GL_BOOL_VEC2, "OpenGL type mismatch");
|
|
static_assert(underlying_type(UniformDataType::BoolVec3) == GL_BOOL_VEC3, "OpenGL type mismatch");
|
|
static_assert(underlying_type(UniformDataType::BoolVec4) == GL_BOOL_VEC4, "OpenGL type mismatch");
|
|
static_assert(underlying_type(UniformDataType::FloatMat2) == GL_FLOAT_MAT2, "OpenGL type mismatch");
|
|
static_assert(underlying_type(UniformDataType::FloatMat3) == GL_FLOAT_MAT3, "OpenGL type mismatch");
|
|
static_assert(underlying_type(UniformDataType::FloatMat4) == GL_FLOAT_MAT4, "OpenGL type mismatch");
|
|
static_assert(underlying_type(UniformDataType::Sampler2D) == GL_SAMPLER_2D, "OpenGL type mismatch");
|
|
static_assert(underlying_type(UniformDataType::SamplerCube) == GL_SAMPLER_CUBE, "OpenGL type mismatch");
|
|
|
|
Context::Context(RendererBackend& backend_)
|
|
: gfx::Context([] {
|
|
GLint value;
|
|
MBGL_CHECK_ERROR(glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &value));
|
|
return value;
|
|
}()),
|
|
backend(backend_),
|
|
stats() {}
|
|
|
|
Context::~Context() noexcept {
|
|
if (cleanupOnDestruction) {
|
|
reset();
|
|
assert(stats.isZero());
|
|
}
|
|
}
|
|
|
|
void Context::initializeExtensions(const std::function<gl::ProcAddress(const char*)>& getProcAddress) {
|
|
if (const auto* extensions = reinterpret_cast<const char*>(MBGL_CHECK_ERROR(glGetString(GL_EXTENSIONS)))) {
|
|
auto fn = [&](std::initializer_list<std::pair<const char*, const char*>> probes) -> ProcAddress {
|
|
for (auto probe : probes) {
|
|
if (strstr(extensions, probe.first) != nullptr) {
|
|
if (ProcAddress ptr = getProcAddress(probe.second)) {
|
|
return ptr;
|
|
}
|
|
}
|
|
}
|
|
return nullptr;
|
|
};
|
|
|
|
static const std::string renderer = []() {
|
|
std::string r = reinterpret_cast<const char*>(MBGL_CHECK_ERROR(glGetString(GL_RENDERER)));
|
|
Log::Info(Event::General, "GPU Identifier: " + r);
|
|
return r;
|
|
}();
|
|
|
|
// Block ANGLE on Direct3D since the debugging extension is causing crashes
|
|
if (!(renderer.find("ANGLE") != std::string::npos && renderer.find("Direct3D") != std::string::npos)) {
|
|
debugging = std::make_unique<extension::Debugging>(fn);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Context::enableDebugging() {
|
|
if (!debugging || !debugging->debugMessageControl || !debugging->debugMessageCallback) {
|
|
return;
|
|
}
|
|
|
|
// This will enable all messages including performance hints
|
|
// MBGL_CHECK_ERROR(debugging->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(
|
|
debugging->debugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_HIGH, 0, nullptr, GL_TRUE));
|
|
MBGL_CHECK_ERROR(
|
|
debugging->debugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_MEDIUM, 0, nullptr, GL_TRUE));
|
|
MBGL_CHECK_ERROR(debugging->debugMessageControl(
|
|
GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_NOTIFICATION, 0, nullptr, GL_FALSE));
|
|
|
|
MBGL_CHECK_ERROR(debugging->debugMessageCallback(extension::Debugging::DebugCallback, nullptr));
|
|
}
|
|
|
|
UniqueShader Context::createShader(ShaderType type, const std::initializer_list<const char*>& sources) {
|
|
UniqueShader result{MBGL_CHECK_ERROR(glCreateShader(static_cast<GLenum>(type))), {this}};
|
|
|
|
MBGL_CHECK_ERROR(glShaderSource(result, static_cast<GLsizei>(sources.size()), sources.begin(), nullptr));
|
|
MBGL_CHECK_ERROR(glCompileShader(result));
|
|
|
|
GLint status = 0;
|
|
MBGL_CHECK_ERROR(glGetShaderiv(result, GL_COMPILE_STATUS, &status));
|
|
if (status != 0) {
|
|
return result;
|
|
}
|
|
|
|
GLint logLength;
|
|
MBGL_CHECK_ERROR(glGetShaderiv(result, GL_INFO_LOG_LENGTH, &logLength));
|
|
if (logLength > 0) {
|
|
const auto log = std::make_unique<GLchar[]>(logLength);
|
|
MBGL_CHECK_ERROR(glGetShaderInfoLog(result, logLength, &logLength, log.get()));
|
|
Log::Error(Event::Shader, std::string("Shader failed to compile: ") + log.get());
|
|
}
|
|
|
|
throw std::runtime_error("shader failed to compile");
|
|
}
|
|
|
|
UniqueProgram Context::createProgram(ShaderID vertexShader, ShaderID fragmentShader, const char* location0AttribName) {
|
|
UniqueProgram result{MBGL_CHECK_ERROR(glCreateProgram()), {this}};
|
|
|
|
MBGL_CHECK_ERROR(glAttachShader(result, vertexShader));
|
|
MBGL_CHECK_ERROR(glAttachShader(result, fragmentShader));
|
|
|
|
// It is important to have attribute at position 0 enabled: conveniently,
|
|
// position attribute is always first and always enabled. The integrity of
|
|
// this assumption is verified in AttributeLocations::queryLocations and
|
|
// AttributeLocations::getFirstAttribName.
|
|
MBGL_CHECK_ERROR(glBindAttribLocation(result, 0, location0AttribName));
|
|
|
|
linkProgram(result);
|
|
|
|
return result;
|
|
}
|
|
|
|
void Context::linkProgram(ProgramID program_) {
|
|
MBGL_CHECK_ERROR(glLinkProgram(program_));
|
|
verifyProgramLinkage(program_);
|
|
}
|
|
|
|
void Context::verifyProgramLinkage(ProgramID program_) {
|
|
GLint status;
|
|
MBGL_CHECK_ERROR(glGetProgramiv(program_, GL_LINK_STATUS, &status));
|
|
if (status == GL_TRUE) {
|
|
return;
|
|
}
|
|
|
|
GLint logLength;
|
|
MBGL_CHECK_ERROR(glGetProgramiv(program_, GL_INFO_LOG_LENGTH, &logLength));
|
|
const auto log = std::make_unique<GLchar[]>(logLength);
|
|
if (logLength > 0) {
|
|
MBGL_CHECK_ERROR(glGetProgramInfoLog(program_, logLength, &logLength, log.get()));
|
|
Log::Error(Event::Shader, std::string("Program failed to link: ") + log.get());
|
|
}
|
|
|
|
throw std::runtime_error("program failed to link");
|
|
}
|
|
|
|
UniqueTexture Context::createUniqueTexture() {
|
|
if (pooledTextures.empty()) {
|
|
pooledTextures.resize(TextureMax);
|
|
MBGL_CHECK_ERROR(glGenTextures(TextureMax, pooledTextures.data()));
|
|
stats.numCreatedTextures += TextureMax;
|
|
}
|
|
|
|
TextureID id = pooledTextures.back();
|
|
pooledTextures.pop_back();
|
|
stats.numActiveTextures++;
|
|
// NOLINTNEXTLINE(performance-move-const-arg)
|
|
return UniqueTexture{std::move(id), {this}};
|
|
}
|
|
|
|
VertexArray Context::createVertexArray() {
|
|
VertexArrayID id = 0;
|
|
MBGL_CHECK_ERROR(glGenVertexArrays(1, &id));
|
|
// NOLINTNEXTLINE(performance-move-const-arg)
|
|
UniqueVertexArray vao(std::move(id), {this});
|
|
return {UniqueVertexArrayState(new VertexArrayState(std::move(vao)), VertexArrayStateDeleter{true})};
|
|
}
|
|
|
|
UniqueFramebuffer Context::createFramebuffer() {
|
|
FramebufferID id = 0;
|
|
MBGL_CHECK_ERROR(glGenFramebuffers(1, &id));
|
|
stats.numFrameBuffers++;
|
|
// NOLINTNEXTLINE(performance-move-const-arg)
|
|
return UniqueFramebuffer{std::move(id), {this}};
|
|
}
|
|
|
|
std::unique_ptr<gfx::TextureResource> Context::createTextureResource(const Size size,
|
|
const gfx::TexturePixelType format,
|
|
const gfx::TextureChannelDataType type) {
|
|
auto obj = createUniqueTexture();
|
|
int textureByteSize = gl::TextureResource::getStorageSize(size, format, type);
|
|
stats.memTextures += textureByteSize;
|
|
std::unique_ptr<gfx::TextureResource> resource = std::make_unique<gl::TextureResource>(std::move(obj),
|
|
textureByteSize);
|
|
|
|
// Always use texture unit 0 for manipulating it.
|
|
activeTextureUnit = 0;
|
|
texture[0] = static_cast<gl::TextureResource&>(*resource).texture;
|
|
|
|
// Creates an empty texture with the specified size and format.
|
|
MBGL_CHECK_ERROR(glTexImage2D(GL_TEXTURE_2D,
|
|
0,
|
|
Enum<gfx::TexturePixelType>::sizedFor(format, type),
|
|
size.width,
|
|
size.height,
|
|
0,
|
|
Enum<gfx::TexturePixelType>::to(format),
|
|
Enum<gfx::TextureChannelDataType>::to(type),
|
|
nullptr));
|
|
|
|
// We are using clamp to edge here since OpenGL ES doesn't allow GL_REPEAT
|
|
// on NPOT textures. We use those when the pixelRatio isn't a power of two,
|
|
// e.g. on iPhone 6 Plus.
|
|
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_MIN_FILTER, GL_NEAREST));
|
|
MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST));
|
|
|
|
return resource;
|
|
}
|
|
|
|
std::unique_ptr<gfx::RenderbufferResource> Context::createRenderbufferResource(const gfx::RenderbufferPixelType type,
|
|
const Size size) {
|
|
RenderbufferID id = 0;
|
|
MBGL_CHECK_ERROR(glGenRenderbuffers(1, &id));
|
|
// NOLINTNEXTLINE(performance-move-const-arg)
|
|
UniqueRenderbuffer renderbuffer{std::move(id), {this}};
|
|
|
|
bindRenderbuffer = renderbuffer;
|
|
MBGL_CHECK_ERROR(
|
|
glRenderbufferStorage(GL_RENDERBUFFER, Enum<gfx::RenderbufferPixelType>::to(type), size.width, size.height));
|
|
bindRenderbuffer = 0;
|
|
return std::make_unique<gl::RenderbufferResource>(std::move(renderbuffer));
|
|
}
|
|
|
|
std::unique_ptr<uint8_t[]> Context::readFramebuffer(const Size size,
|
|
const gfx::TexturePixelType format,
|
|
const bool flip) {
|
|
const size_t stride = size.width * (format == gfx::TexturePixelType::RGBA ? 4 : 1);
|
|
auto data = std::make_unique<uint8_t[]>(stride * size.height);
|
|
|
|
// When reading data from the framebuffer, make sure that we are storing the
|
|
// values tightly packed into the buffer to avoid buffer overruns.
|
|
pixelStorePack = {1};
|
|
|
|
MBGL_CHECK_ERROR(glReadPixels(
|
|
0, 0, size.width, size.height, Enum<gfx::TexturePixelType>::to(format), GL_UNSIGNED_BYTE, data.get()));
|
|
|
|
if (flip) {
|
|
auto tmp = std::make_unique<uint8_t[]>(stride);
|
|
uint8_t* rgba = data.get();
|
|
for (int i = 0, j = size.height - 1; i < j; i++, j--) {
|
|
std::memcpy(tmp.get(), rgba + i * stride, stride);
|
|
std::memcpy(rgba + i * stride, rgba + j * stride, stride);
|
|
std::memcpy(rgba + j * stride, tmp.get(), stride);
|
|
}
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
namespace {
|
|
|
|
void checkFramebuffer() {
|
|
GLenum status = MBGL_CHECK_ERROR(glCheckFramebufferStatus(GL_FRAMEBUFFER));
|
|
if (status != GL_FRAMEBUFFER_COMPLETE) {
|
|
switch (status) {
|
|
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
|
|
throw std::runtime_error("Couldn't create framebuffer: incomplete attachment");
|
|
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
|
|
throw std::runtime_error(
|
|
"Couldn't create framebuffer: incomplete missing "
|
|
"attachment");
|
|
#ifdef GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER
|
|
case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
|
|
throw std::runtime_error("Couldn't create framebuffer: incomplete draw buffer");
|
|
#endif
|
|
#ifdef GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER
|
|
case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
|
|
throw std::runtime_error("Couldn't create framebuffer: incomplete read buffer");
|
|
#endif
|
|
#ifdef GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS
|
|
case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
|
|
throw std::runtime_error("Couldn't create framebuffer: incomplete dimensions");
|
|
#endif
|
|
|
|
case GL_FRAMEBUFFER_UNSUPPORTED:
|
|
throw std::runtime_error("Couldn't create framebuffer: unsupported");
|
|
default:
|
|
throw std::runtime_error("Couldn't create framebuffer: other");
|
|
}
|
|
}
|
|
}
|
|
|
|
void bindDepthStencilRenderbuffer(const gfx::Renderbuffer<gfx::RenderbufferPixelType::DepthStencil>& depthStencil) {
|
|
auto& depthStencilResource = depthStencil.getResource<gl::RenderbufferResource>();
|
|
#ifdef GL_DEPTH_STENCIL_ATTACHMENT
|
|
MBGL_CHECK_ERROR(glFramebufferRenderbuffer(
|
|
GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, depthStencilResource.renderbuffer));
|
|
#else
|
|
MBGL_CHECK_ERROR(glFramebufferRenderbuffer(
|
|
GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthStencilResource.renderbuffer));
|
|
MBGL_CHECK_ERROR(glFramebufferRenderbuffer(
|
|
GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, depthStencilResource.renderbuffer));
|
|
#endif
|
|
}
|
|
|
|
} // namespace
|
|
|
|
Framebuffer Context::createFramebuffer(
|
|
const gfx::Renderbuffer<gfx::RenderbufferPixelType::RGBA>& color,
|
|
const gfx::Renderbuffer<gfx::RenderbufferPixelType::DepthStencil>& depthStencil) {
|
|
if (color.getSize() != depthStencil.getSize()) {
|
|
throw std::runtime_error("Renderbuffer size mismatch");
|
|
}
|
|
auto fbo = createFramebuffer();
|
|
bindFramebuffer = fbo;
|
|
|
|
auto& colorResource = color.getResource<gl::RenderbufferResource>();
|
|
MBGL_CHECK_ERROR(
|
|
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorResource.renderbuffer));
|
|
bindDepthStencilRenderbuffer(depthStencil);
|
|
checkFramebuffer();
|
|
return {color.getSize(), std::move(fbo)};
|
|
}
|
|
|
|
Framebuffer Context::createFramebuffer(const gfx::Renderbuffer<gfx::RenderbufferPixelType::RGBA>& color) {
|
|
auto fbo = createFramebuffer();
|
|
bindFramebuffer = fbo;
|
|
auto& colorResource = color.getResource<gl::RenderbufferResource>();
|
|
MBGL_CHECK_ERROR(
|
|
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorResource.renderbuffer));
|
|
checkFramebuffer();
|
|
return {color.getSize(), std::move(fbo)};
|
|
}
|
|
|
|
Framebuffer Context::createFramebuffer(
|
|
const gfx::Texture& color, const gfx::Renderbuffer<gfx::RenderbufferPixelType::DepthStencil>& depthStencil) {
|
|
if (color.size != depthStencil.getSize()) {
|
|
throw std::runtime_error("Renderbuffer size mismatch");
|
|
}
|
|
auto fbo = createFramebuffer();
|
|
bindFramebuffer = fbo;
|
|
MBGL_CHECK_ERROR(glFramebufferTexture2D(
|
|
GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color.getResource<gl::TextureResource>().texture, 0));
|
|
bindDepthStencilRenderbuffer(depthStencil);
|
|
checkFramebuffer();
|
|
return {color.size, std::move(fbo)};
|
|
}
|
|
|
|
Framebuffer Context::createFramebuffer(const gfx::Texture& color) {
|
|
auto fbo = createFramebuffer();
|
|
bindFramebuffer = fbo;
|
|
MBGL_CHECK_ERROR(glFramebufferTexture2D(
|
|
GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color.getResource<gl::TextureResource>().texture, 0));
|
|
checkFramebuffer();
|
|
return {color.size, std::move(fbo)};
|
|
}
|
|
|
|
Framebuffer Context::createFramebuffer(const gfx::Texture& color,
|
|
const gfx::Renderbuffer<gfx::RenderbufferPixelType::Depth>& depth) {
|
|
if (color.size != depth.getSize()) {
|
|
throw std::runtime_error("Renderbuffer size mismatch");
|
|
}
|
|
auto fbo = createFramebuffer();
|
|
bindFramebuffer = fbo;
|
|
MBGL_CHECK_ERROR(glFramebufferTexture2D(
|
|
GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color.getResource<gl::TextureResource>().texture, 0));
|
|
|
|
auto& depthResource = depth.getResource<gl::RenderbufferResource>();
|
|
MBGL_CHECK_ERROR(
|
|
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthResource.renderbuffer));
|
|
checkFramebuffer();
|
|
return {depth.getSize(), std::move(fbo)};
|
|
}
|
|
|
|
std::unique_ptr<gfx::OffscreenTexture> Context::createOffscreenTexture(const Size size,
|
|
const gfx::TextureChannelDataType type) {
|
|
return std::make_unique<gl::OffscreenTexture>(*this, size, type);
|
|
}
|
|
|
|
std::unique_ptr<gfx::DrawScopeResource> Context::createDrawScopeResource() {
|
|
return std::make_unique<gl::DrawScopeResource>(createVertexArray());
|
|
}
|
|
|
|
void Context::reset() {
|
|
std::copy(pooledTextures.begin(), pooledTextures.end(), std::back_inserter(abandonedTextures));
|
|
pooledTextures.resize(0);
|
|
performCleanup();
|
|
}
|
|
|
|
void Context::setDirtyState() {
|
|
// Note: does not set viewport/scissorTest/bindFramebuffer to dirty
|
|
// since they are handled separately in the view object.
|
|
stencilFunc.setDirty();
|
|
stencilMask.setDirty();
|
|
stencilTest.setDirty();
|
|
stencilOp.setDirty();
|
|
depthRange.setDirty();
|
|
depthMask.setDirty();
|
|
depthTest.setDirty();
|
|
depthFunc.setDirty();
|
|
blend.setDirty();
|
|
blendEquation.setDirty();
|
|
blendFunc.setDirty();
|
|
blendColor.setDirty();
|
|
colorMask.setDirty();
|
|
clearDepth.setDirty();
|
|
clearColor.setDirty();
|
|
clearStencil.setDirty();
|
|
cullFace.setDirty();
|
|
cullFaceSide.setDirty();
|
|
cullFaceWinding.setDirty();
|
|
program.setDirty();
|
|
lineWidth.setDirty();
|
|
activeTextureUnit.setDirty();
|
|
pixelStorePack.setDirty();
|
|
pixelStoreUnpack.setDirty();
|
|
for (auto& tex : texture) {
|
|
tex.setDirty();
|
|
}
|
|
vertexBuffer.setDirty();
|
|
bindVertexArray.setDirty();
|
|
globalVertexArrayState.setDirty();
|
|
}
|
|
|
|
bool Context::setupDraw(const PaintParameters& parameters, const gfx::Drawable& drawable) {
|
|
if (const auto& shader = drawable.getShader()) {
|
|
const auto& shaderGL = static_cast<const ShaderProgramGL&>(*shader);
|
|
if (shaderGL.getGLProgramID() != program.getCurrentValue()) {
|
|
program = shaderGL.getGLProgramID();
|
|
}
|
|
} else if (program != value::Program::Default) {
|
|
program = value::Program::Default;
|
|
}
|
|
|
|
setDepthMode(parameters.depthModeForSublayer(drawable.getSubLayerIndex(), drawable.getDepthType()));
|
|
|
|
// force disable depth test for debugging
|
|
// setDepthMode({gfx::DepthFunctionType::Always, gfx::DepthMaskType::ReadOnly, {0,1}});
|
|
|
|
if (auto tileID = drawable.getTileID()) {
|
|
// Doesn't work until the clipping masks are generated
|
|
// parameters.stencilModeForClipping(tileID->toUnwrapped());
|
|
setStencilMode(gfx::StencilMode::disabled());
|
|
} else {
|
|
setStencilMode(gfx::StencilMode::disabled());
|
|
}
|
|
|
|
setColorMode(gfx::ColorMode::alphaBlended());
|
|
setCullFaceMode(gfx::CullFaceMode::disabled());
|
|
|
|
auto& drawableGL = static_cast<const DrawableGL&>(drawable);
|
|
auto& vao = drawableGL.getVertexArray();
|
|
if (vao.isValid()) {
|
|
bindVertexArray = vao.getID();
|
|
return true;
|
|
} else {
|
|
bindVertexArray = value::BindVertexArray::Default;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
gfx::UniqueDrawableBuilder Context::createDrawableBuilder(std::string name) {
|
|
return std::make_unique<gl::DrawableGLBuilder>(std::move(name));
|
|
}
|
|
|
|
gfx::DrawableTweakerPtr Context::createDrawableTweaker() {
|
|
return std::make_shared<gl::DrawableGLTweaker>();
|
|
}
|
|
|
|
gfx::UniformBufferPtr Context::createUniformBuffer(const void* data, std::size_t size) {
|
|
return std::make_shared<gl::UniformBufferGL>(data, size);
|
|
}
|
|
|
|
gfx::ShaderProgramBasePtr Context::getGenericShader(gfx::ShaderRegistry& shaders, const std::string& name) {
|
|
return shaders.get<gl::ShaderProgramGL>(name);
|
|
}
|
|
|
|
TileLayerGroupPtr Context::createTileLayerGroup(int32_t layerIndex, std::size_t initialCapacity) {
|
|
return std::make_shared<TileLayerGroupGL>(layerIndex, initialCapacity);
|
|
}
|
|
|
|
void Context::clear(std::optional<mbgl::Color> color, std::optional<float> depth, std::optional<int32_t> stencil) {
|
|
GLbitfield mask = 0;
|
|
|
|
if (color) {
|
|
mask |= GL_COLOR_BUFFER_BIT;
|
|
clearColor = *color;
|
|
colorMask = value::ColorMask::Default;
|
|
}
|
|
|
|
if (depth) {
|
|
mask |= GL_DEPTH_BUFFER_BIT;
|
|
clearDepth = *depth;
|
|
depthMask = value::DepthMask::Default;
|
|
}
|
|
|
|
if (stencil) {
|
|
mask |= GL_STENCIL_BUFFER_BIT;
|
|
clearStencil = *stencil;
|
|
stencilMask = value::StencilMask::Default;
|
|
}
|
|
|
|
MBGL_CHECK_ERROR(glClear(mask));
|
|
|
|
stats.numDrawCalls = 0;
|
|
}
|
|
|
|
void Context::setCullFaceMode(const gfx::CullFaceMode& mode) {
|
|
cullFace = mode.enabled;
|
|
|
|
// These shouldn't need to be updated when face culling is disabled, but we
|
|
// might end up having the same isssues with Adreno 2xx GPUs as noted in
|
|
// Context::setDepthMode.
|
|
cullFaceSide = mode.side;
|
|
cullFaceWinding = mode.winding;
|
|
}
|
|
|
|
void Context::setDepthMode(const gfx::DepthMode& depth) {
|
|
if (depth.func == gfx::DepthFunctionType::Always && depth.mask != gfx::DepthMaskType::ReadWrite) {
|
|
depthTest = false;
|
|
|
|
// Workaround for rendering errors on Adreno 2xx GPUs. Depth-related
|
|
// state should not matter when the depth test is disabled, but on these
|
|
// GPUs it apparently does.
|
|
// https://github.com/mapbox/mapbox-gl-native/issues/9164
|
|
depthFunc = depth.func;
|
|
depthMask = depth.mask;
|
|
depthRange = depth.range;
|
|
} else {
|
|
depthTest = true;
|
|
depthFunc = depth.func;
|
|
depthMask = depth.mask;
|
|
depthRange = depth.range;
|
|
}
|
|
}
|
|
|
|
void Context::setStencilMode(const gfx::StencilMode& stencil) {
|
|
if (stencil.test.is<gfx::StencilMode::Always>() && !stencil.mask) {
|
|
stencilTest = false;
|
|
} else {
|
|
stencilTest = true;
|
|
stencilMask = stencil.mask;
|
|
stencilOp = {stencil.fail, stencil.depthFail, stencil.pass};
|
|
apply_visitor([&](const auto& test) { stencilFunc = {test.func, stencil.ref, test.mask}; }, stencil.test);
|
|
}
|
|
}
|
|
|
|
void Context::setColorMode(const gfx::ColorMode& color) {
|
|
if (color.blendFunction.is<gfx::ColorMode::Replace>()) {
|
|
blend = false;
|
|
} else {
|
|
blend = true;
|
|
blendColor = color.blendColor;
|
|
apply_visitor(
|
|
[&](const auto& blendFunction) {
|
|
blendEquation = gfx::ColorBlendEquationType(blendFunction.equation);
|
|
blendFunc = {blendFunction.srcFactor, blendFunction.dstFactor};
|
|
},
|
|
color.blendFunction);
|
|
}
|
|
|
|
colorMask = color.mask;
|
|
}
|
|
|
|
std::unique_ptr<gfx::CommandEncoder> Context::createCommandEncoder() {
|
|
backend.updateAssumedState();
|
|
if (backend.contextIsShared()) {
|
|
setDirtyState();
|
|
}
|
|
return std::make_unique<gl::CommandEncoder>(*this);
|
|
}
|
|
|
|
gfx::RenderingStats& Context::renderingStats() {
|
|
return stats;
|
|
}
|
|
|
|
const gfx::RenderingStats& Context::renderingStats() const {
|
|
return stats;
|
|
}
|
|
|
|
void Context::finish() {
|
|
MBGL_CHECK_ERROR(glFinish());
|
|
}
|
|
|
|
void Context::draw(const gfx::DrawMode& drawMode, std::size_t indexOffset, std::size_t indexLength) {
|
|
switch (drawMode.type) {
|
|
case gfx::DrawModeType::Points:
|
|
break;
|
|
case gfx::DrawModeType::Lines:
|
|
case gfx::DrawModeType::LineLoop:
|
|
case gfx::DrawModeType::LineStrip:
|
|
lineWidth = drawMode.size;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
MBGL_CHECK_ERROR(glDrawElements(Enum<gfx::DrawModeType>::to(drawMode.type),
|
|
static_cast<GLsizei>(indexLength),
|
|
GL_UNSIGNED_SHORT,
|
|
reinterpret_cast<GLvoid*>(sizeof(uint16_t) * indexOffset)));
|
|
|
|
stats.numDrawCalls++;
|
|
}
|
|
|
|
void Context::performCleanup() {
|
|
// TODO: Find a better way to unbind VAOs after we're done with them without
|
|
// introducing unnecessary bind(0)/bind(N) sequences.
|
|
{
|
|
activeTextureUnit = 1;
|
|
texture[1] = 0;
|
|
activeTextureUnit = 0;
|
|
texture[0] = 0;
|
|
|
|
bindVertexArray = 0;
|
|
}
|
|
|
|
for (auto id : abandonedPrograms) {
|
|
if (program == id) {
|
|
program.setDirty();
|
|
}
|
|
MBGL_CHECK_ERROR(glDeleteProgram(id));
|
|
}
|
|
abandonedPrograms.clear();
|
|
|
|
for (auto id : abandonedShaders) {
|
|
MBGL_CHECK_ERROR(glDeleteShader(id));
|
|
}
|
|
abandonedShaders.clear();
|
|
|
|
if (!abandonedBuffers.empty()) {
|
|
for (const auto id : abandonedBuffers) {
|
|
if (vertexBuffer == id) {
|
|
vertexBuffer.setDirty();
|
|
} else if (globalVertexArrayState.indexBuffer == id) {
|
|
globalVertexArrayState.indexBuffer.setDirty();
|
|
}
|
|
}
|
|
MBGL_CHECK_ERROR(glDeleteBuffers(int(abandonedBuffers.size()), abandonedBuffers.data()));
|
|
stats.numBuffers -= int(abandonedBuffers.size());
|
|
abandonedBuffers.clear();
|
|
}
|
|
|
|
if (!abandonedTextures.empty()) {
|
|
for (const auto id : abandonedTextures) {
|
|
for (auto& binding : texture) {
|
|
if (binding == id) {
|
|
binding.setDirty();
|
|
}
|
|
}
|
|
}
|
|
MBGL_CHECK_ERROR(glDeleteTextures(int(abandonedTextures.size()), abandonedTextures.data()));
|
|
stats.numCreatedTextures -= int(abandonedTextures.size());
|
|
assert(stats.numCreatedTextures >= 0);
|
|
abandonedTextures.clear();
|
|
}
|
|
|
|
if (!abandonedVertexArrays.empty()) {
|
|
for (const auto id : abandonedVertexArrays) {
|
|
if (bindVertexArray == id) {
|
|
bindVertexArray.setDirty();
|
|
}
|
|
}
|
|
MBGL_CHECK_ERROR(glDeleteVertexArrays(int(abandonedVertexArrays.size()), abandonedVertexArrays.data()));
|
|
abandonedVertexArrays.clear();
|
|
}
|
|
|
|
if (!abandonedFramebuffers.empty()) {
|
|
for (const auto id : abandonedFramebuffers) {
|
|
if (bindFramebuffer == id) {
|
|
bindFramebuffer.setDirty();
|
|
}
|
|
}
|
|
MBGL_CHECK_ERROR(glDeleteFramebuffers(int(abandonedFramebuffers.size()), abandonedFramebuffers.data()));
|
|
stats.numFrameBuffers -= int(abandonedFramebuffers.size());
|
|
assert(stats.numFrameBuffers >= 0);
|
|
abandonedFramebuffers.clear();
|
|
}
|
|
|
|
if (!abandonedRenderbuffers.empty()) {
|
|
MBGL_CHECK_ERROR(glDeleteRenderbuffers(int(abandonedRenderbuffers.size()), abandonedRenderbuffers.data()));
|
|
abandonedRenderbuffers.clear();
|
|
}
|
|
}
|
|
|
|
void Context::reduceMemoryUsage() {
|
|
performCleanup();
|
|
|
|
// Ensure that all pending actions are executed to ensure that they happen
|
|
// before the app goes to the background.
|
|
MBGL_CHECK_ERROR(glFinish());
|
|
}
|
|
|
|
#if !defined(NDEBUG)
|
|
void Context::visualizeStencilBuffer() {
|
|
throw std::runtime_error("Not yet implemented");
|
|
}
|
|
|
|
void Context::visualizeDepthBuffer([[maybe_unused]] const float depthRangeSize) {
|
|
throw std::runtime_error("Not yet implemented");
|
|
}
|
|
#endif
|
|
|
|
void Context::clearStencilBuffer(const int32_t bits) {
|
|
MBGL_CHECK_ERROR(glClearStencil(bits));
|
|
MBGL_CHECK_ERROR(glClear(GL_STENCIL_BUFFER_BIT));
|
|
}
|
|
|
|
} // namespace gl
|
|
} // namespace mbgl
|