diff options
Diffstat (limited to 'platform/gfx/gl/src/mbgl/gl')
39 files changed, 4321 insertions, 0 deletions
diff --git a/platform/gfx/gl/src/mbgl/gl/attribute.cpp b/platform/gfx/gl/src/mbgl/gl/attribute.cpp new file mode 100644 index 0000000000..d5240b5b7a --- /dev/null +++ b/platform/gfx/gl/src/mbgl/gl/attribute.cpp @@ -0,0 +1,19 @@ +#include <mbgl/gl/attribute.hpp> +#include <mbgl/gl/context.hpp> + +namespace mbgl { +namespace gl { + +using namespace platform; + +optional<AttributeLocation> queryLocation(ProgramID id, const char* name) { + GLint attributeLocation = MBGL_CHECK_ERROR(glGetAttribLocation(id, name)); + if (attributeLocation != -1) { + return attributeLocation; + } else { + return {}; + } +} + +} // namespace gl +} // namespace mbgl diff --git a/platform/gfx/gl/src/mbgl/gl/attribute.hpp b/platform/gfx/gl/src/mbgl/gl/attribute.hpp new file mode 100644 index 0000000000..d578bdb361 --- /dev/null +++ b/platform/gfx/gl/src/mbgl/gl/attribute.hpp @@ -0,0 +1,115 @@ +#pragma once + +#include <mbgl/gfx/attribute.hpp> +#include <mbgl/gl/types.hpp> +#include <mbgl/programs/attributes.hpp> +#include <mbgl/util/literal.hpp> +#include <mbgl/util/optional.hpp> + +#include <vector> +#include <string> + +namespace mbgl { +namespace gl { + +using AttributeBindingArray = std::vector<optional<gfx::AttributeBinding>>; +using NamedAttributeLocations = std::vector<std::pair<const std::string, AttributeLocation>>; + +optional<AttributeLocation> queryLocation(ProgramID id, const char* name); + +template <class> +class AttributeLocations; + +template <class... As> +class AttributeLocations<TypeList<As...>> final { +private: + using Locations = + IndexedTuple<TypeList<As...>, TypeList<ExpandToType<As, optional<AttributeLocation>>...>>; + + Locations locations; + +public: + AttributeLocations() = default; + + void queryLocations(const ProgramID& id) { + locations = Locations{ + queryLocation(id, concat_literals<&string_literal<'a', '_'>::value, &As::name>::value())... }; + using TypeOfFirst = typename std::tuple_element<0, std::tuple<As...>>::type; + auto first = locations.template get<TypeOfFirst>(); + assert(first && first.value() == 0); + } + + static constexpr const char* getFirstAttribName() { + // Static assert that attribute list starts with position: we bind it on location 0. + using TypeOfFirst = typename std::tuple_element<0, std::tuple<As...>>::type; + static_assert(std::is_same<attributes::pos, TypeOfFirst>::value || std::is_same<attributes::pos_offset, TypeOfFirst>::value || + std::is_same<attributes::pos_normal, TypeOfFirst>::value, "Program must start with position related attribute."); + return concat_literals<&string_literal<'a', '_'>::value, TypeOfFirst::name>::value(); + } + + NamedAttributeLocations getNamedLocations() const { + NamedAttributeLocations result; + + auto maybeAddLocation = [&] (const std::string& name, const optional<AttributeLocation>& location) { + if (location) { + result.emplace_back(name, *location); + } + }; + + util::ignore({ (maybeAddLocation(concat_literals<&string_literal<'a', '_'>::value, &As::name>::value(), + locations.template get<As>()), + 0)... }); + + return result; + } + + AttributeBindingArray toBindingArray(const gfx::AttributeBindings<TypeList<As...>>& bindings) const { + AttributeBindingArray result; + result.resize(sizeof...(As)); + + auto maybeAddBinding = [&] (const optional<AttributeLocation>& location, + const optional<gfx::AttributeBinding>& binding) { + if (location) { + result.at(*location) = binding; + } + }; + + util::ignore({ (maybeAddBinding(locations.template get<As>(), bindings.template get<As>()), 0)... }); + + return result; + } +}; + +template <class> +class AttributeKey; + +constexpr auto attributeDefinePrefix() { + return "#define HAS_UNIFORM_u_"; +} + +template <class... As> +class AttributeKey<TypeList<As...>> final { +public: + static_assert(sizeof...(As) <= 32, "attribute count exceeds 32"); + + static uint32_t compute(const gfx::AttributeBindings<TypeList<As...>>& bindings) { + uint32_t value = 0; + util::ignore( + { (bindings.template get<As>() ? (void)(value |= 1 << TypeIndex<As, As...>::value) + : (void)0, + 0)... }); + return value; + } + + static std::string defines(const gfx::AttributeBindings<TypeList<As...>>& bindings) { + std::string result; + util::ignore({ (!bindings.template get<As>() + ? (void)(result += concat_literals<&attributeDefinePrefix, &As::name, &string_literal<'\n'>::value>::value()) + : (void)0, + 0)... }); + return result; + } +}; + +} // namespace gl +} // namespace mbgl diff --git a/platform/gfx/gl/src/mbgl/gl/command_encoder.cpp b/platform/gfx/gl/src/mbgl/gl/command_encoder.cpp new file mode 100644 index 0000000000..a7df14df09 --- /dev/null +++ b/platform/gfx/gl/src/mbgl/gl/command_encoder.cpp @@ -0,0 +1,60 @@ +#include <mbgl/gl/command_encoder.hpp> +#include <mbgl/gl/upload_pass.hpp> +#include <mbgl/gl/render_pass.hpp> +#include <mbgl/gl/context.hpp> +#include <mbgl/gl/renderable_resource.hpp> +#include <mbgl/gl/debugging_extension.hpp> +#include <mbgl/platform/gl_functions.hpp> + +#include <cstring> + +namespace mbgl { +namespace gl { + +CommandEncoder::~CommandEncoder() { + const auto debugGroup(createDebugGroup("cleanup")); + context.performCleanup(); +} + +std::unique_ptr<gfx::UploadPass> +CommandEncoder::createUploadPass(const char* name) { + return std::make_unique<gl::UploadPass>(*this, name); +} + +std::unique_ptr<gfx::RenderPass> +CommandEncoder::createRenderPass(const char* name, const gfx::RenderPassDescriptor& descriptor) { + return std::make_unique<gl::RenderPass>(*this, name, descriptor); +} + +void CommandEncoder::present(gfx::Renderable& renderable) { + renderable.getResource<gl::RenderableResource>().swap(); +} + +void CommandEncoder::pushDebugGroup(const char* name) { + (void)name; +#ifndef NDEBUG + if (auto debugging = context.getDebuggingExtension()) { + if (debugging->pushDebugGroup) { + MBGL_CHECK_ERROR(debugging->pushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, + platform::GLsizei(strlen(name)), name)); + } else if (debugging->pushGroupMarkerEXT) { + MBGL_CHECK_ERROR(debugging->pushGroupMarkerEXT(platform::GLsizei(strlen(name) + 1), name)); + } + } +#endif +} + +void CommandEncoder::popDebugGroup() { +#ifndef NDEBUG + if (auto debugging = context.getDebuggingExtension()) { + if (debugging->popDebugGroup) { + MBGL_CHECK_ERROR(debugging->popDebugGroup()); + } else if (debugging->popGroupMarkerEXT) { + MBGL_CHECK_ERROR(debugging->popGroupMarkerEXT()); + } + } +#endif +} + +} // namespace gl +} // namespace mbgl diff --git a/platform/gfx/gl/src/mbgl/gl/command_encoder.hpp b/platform/gfx/gl/src/mbgl/gl/command_encoder.hpp new file mode 100644 index 0000000000..f47aad4946 --- /dev/null +++ b/platform/gfx/gl/src/mbgl/gl/command_encoder.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include <mbgl/gfx/command_encoder.hpp> + +namespace mbgl { +namespace gl { + +class Context; + +class CommandEncoder final : public gfx::CommandEncoder { +public: + explicit CommandEncoder(gl::Context& context_) : context(context_) { + } + + ~CommandEncoder() override; + + friend class UploadPass; + friend class RenderPass; + + std::unique_ptr<gfx::UploadPass> createUploadPass(const char* name) override; + std::unique_ptr<gfx::RenderPass> createRenderPass(const char* name, const gfx::RenderPassDescriptor&) override; + void present(gfx::Renderable&) override; + +private: + void pushDebugGroup(const char* name) override; + void popDebugGroup() override; + +public: + gl::Context& context; +}; + +} // namespace gl +} // namespace mbgl diff --git a/platform/gfx/gl/src/mbgl/gl/context.cpp b/platform/gfx/gl/src/mbgl/gl/context.cpp new file mode 100644 index 0000000000..4d9c2055de --- /dev/null +++ b/platform/gfx/gl/src/mbgl/gl/context.cpp @@ -0,0 +1,755 @@ +#include <mbgl/gl/context.hpp> +#include <mbgl/gl/enum.hpp> +#include <mbgl/gl/renderer_backend.hpp> +#include <mbgl/gl/texture_resource.hpp> +#include <mbgl/gl/renderbuffer_resource.hpp> +#include <mbgl/gl/draw_scope_resource.hpp> +#include <mbgl/gl/texture.hpp> +#include <mbgl/gl/offscreen_texture.hpp> +#include <mbgl/gl/command_encoder.hpp> +#include <mbgl/gl/debugging_extension.hpp> +#include <mbgl/gl/vertex_array_extension.hpp> +#include <mbgl/util/traits.hpp> +#include <mbgl/util/std.hpp> +#include <mbgl/util/logging.hpp> + +#include <cstring> + +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<ProgramID, GLuint>::value, "OpenGL type mismatch"); +static_assert(std::is_same<ShaderID, GLuint>::value, "OpenGL type mismatch"); +static_assert(std::is_same<BufferID, GLuint>::value, "OpenGL type mismatch"); +static_assert(std::is_same<TextureID, GLuint>::value, "OpenGL type mismatch"); +static_assert(std::is_same<VertexArrayID, GLuint>::value, "OpenGL type mismatch"); +static_assert(std::is_same<FramebufferID, GLuint>::value, "OpenGL type mismatch"); +static_assert(std::is_same<RenderbufferID, GLuint>::value, "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_) { +} + +Context::~Context() { + if (cleanupOnDestruction) { + reset(); + } +} + +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: %s", r.c_str()); + 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); + } + + // Block Adreno 2xx, 3xx as it crashes on glBuffer(Sub)Data + // Block ARM Mali-T720 (in some MT8163 chipsets) as it crashes on glBindVertexArray + // Block ANGLE on Direct3D as the combination of Qt + Windows + ANGLE leads to crashes + if (renderer.find("Adreno (TM) 2") == std::string::npos + && renderer.find("Adreno (TM) 3") == std::string::npos + && (!(renderer.find("ANGLE") != std::string::npos + && renderer.find("Direct3D") != std::string::npos)) + && renderer.find("Mali-T720") == std::string::npos + && renderer.find("Sapphire 650") == std::string::npos + && !disableVAOExtension) { + vertexArray = std::make_unique<extension::VertexArray>(fn); + } + +#if MBGL_USE_GLES2 + constexpr const char* halfFloatExtensionName = "OES_texture_half_float"; + constexpr const char* halfFloatColorBufferExtensionName = "EXT_color_buffer_half_float"; +#else + constexpr const char* halfFloatExtensionName = "ARB_half_float_pixel"; + constexpr const char* halfFloatColorBufferExtensionName = "ARB_color_buffer_float"; +#endif + if (strstr(extensions, halfFloatExtensionName) != nullptr && + strstr(extensions, halfFloatColorBufferExtensionName) != nullptr) { + + supportsHalfFloatTextures = true; + } + + if (!supportsVertexArrays()) { + Log::Warning(Event::OpenGL, "Not using Vertex Array Objects"); + } + } +} + +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, "Shader failed to compile: %s", 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, "Program failed to link: %s", 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())); + } + + TextureID id = pooledTextures.back(); + pooledTextures.pop_back(); + return UniqueTexture{ std::move(id), { this } }; +} + +bool Context::supportsVertexArrays() const { + return vertexArray && + vertexArray->genVertexArrays && + vertexArray->bindVertexArray && + vertexArray->deleteVertexArrays; +} + +VertexArray Context::createVertexArray() { + if (supportsVertexArrays()) { + VertexArrayID id = 0; + MBGL_CHECK_ERROR(vertexArray->genVertexArrays(1, &id)); + UniqueVertexArray vao(std::move(id), { this }); + return { UniqueVertexArrayState(new VertexArrayState(std::move(vao)), VertexArrayStateDeleter { true })}; + } else { + // On GL implementations which do not support vertex arrays, attribute bindings are global state. + // So return a VertexArray which shares our global state tracking and whose deleter is a no-op. + return { UniqueVertexArrayState(&globalVertexArrayState, VertexArrayStateDeleter { false }) }; + } +} + +UniqueFramebuffer Context::createFramebuffer() { + FramebufferID id = 0; + MBGL_CHECK_ERROR(glGenFramebuffers(1, &id)); + 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(); + std::unique_ptr<gfx::TextureResource> resource = + std::make_unique<gl::TextureResource>(std::move(obj)); + + // 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>::to(format), + 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)); + 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; +} + +#if not MBGL_USE_GLES2 +void Context::drawPixels(const Size size, const void* data, gfx::TexturePixelType format) { + pixelStoreUnpack = { 1 }; + // TODO + if (format != gfx::TexturePixelType::RGBA) { + format = gfx::TexturePixelType::Luminance; + } + MBGL_CHECK_ERROR(glDrawPixels(size.width, size.height, Enum<gfx::TexturePixelType>::to(format), + GL_UNSIGNED_BYTE, data)); +} +#endif // MBGL_USE_GLES2 + +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(); +#if not MBGL_USE_GLES2 + pointSize.setDirty(); + pixelZoom.setDirty(); + rasterPos.setDirty(); + pixelTransferDepth.setDirty(); + pixelTransferStencil.setDirty(); +#endif // MBGL_USE_GLES2 + for (auto& tex : texture) { + tex.setDirty(); + } + vertexBuffer.setDirty(); + bindVertexArray.setDirty(); + globalVertexArrayState.setDirty(); +} + +void Context::clear(optional<mbgl::Color> color, + optional<float> depth, + 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)); +} + +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); +} + +void Context::draw(const gfx::DrawMode& drawMode, + std::size_t indexOffset, + std::size_t indexLength) { + switch (drawMode.type) { + case gfx::DrawModeType::Points: +#if not MBGL_USE_GLES2 + // In OpenGL ES 2, the point size is set in the vertex shader. + pointSize = drawMode.size; +#endif // MBGL_USE_GLES2 + 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))); +} + +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())); + 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())); + abandonedTextures.clear(); + } + + if (!abandonedVertexArrays.empty()) { + assert(supportsVertexArrays()); + for (const auto id : abandonedVertexArrays) { + if (bindVertexArray == id) { + bindVertexArray.setDirty(); + } + } + MBGL_CHECK_ERROR(vertexArray->deleteVertexArrays(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())); + 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 not defined(NDEBUG) +void Context::visualizeStencilBuffer() { +#if not MBGL_USE_GLES2 + setStencilMode(gfx::StencilMode::disabled()); + setDepthMode(gfx::DepthMode::disabled()); + setColorMode(gfx::ColorMode::unblended()); + program = 0; + + // Reset the value in case someone else changed it, or it's dirty. + pixelTransferStencil = gl::value::PixelTransferStencil::Default; + + // Read the stencil buffer + const auto viewportValue = viewport.getCurrentValue(); + auto image = readFramebuffer<AlphaImage, gfx::TexturePixelType::Stencil>(viewportValue.size, false); + + // Scale the Stencil buffer to cover the entire color space. + auto it = image.data.get(); + auto end = it + viewportValue.size.width * viewportValue.size.height; + const auto factor = 255.0f / *std::max_element(it, end); + for (; it != end; ++it) { + *it *= factor; + } + + pixelZoom = { 1, 1 }; + rasterPos = { -1, -1, 0, 1 }; + drawPixels(image); +#endif +} + +void Context::visualizeDepthBuffer(const float depthRangeSize) { + (void)depthRangeSize; +#if not MBGL_USE_GLES2 + setStencilMode(gfx::StencilMode::disabled()); + setDepthMode(gfx::DepthMode::disabled()); + setColorMode(gfx::ColorMode::unblended()); + program = 0; + + // Scales the values in the depth buffer so that they cover the entire grayscale range. This + // makes it easier to spot tiny differences. + const float base = 1.0f / (1.0f - depthRangeSize); + pixelTransferDepth = { base, 1.0f - base }; + + // Read the stencil buffer + auto viewportValue = viewport.getCurrentValue(); + auto image = readFramebuffer<AlphaImage, gfx::TexturePixelType::Depth>(viewportValue.size, false); + + pixelZoom = { 1, 1 }; + rasterPos = { -1, -1, 0, 1 }; + drawPixels(image); +#endif +} + +#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 diff --git a/platform/gfx/gl/src/mbgl/gl/context.hpp b/platform/gfx/gl/src/mbgl/gl/context.hpp new file mode 100644 index 0000000000..70f12e5a8d --- /dev/null +++ b/platform/gfx/gl/src/mbgl/gl/context.hpp @@ -0,0 +1,235 @@ +#pragma once + +#include <mbgl/gfx/context.hpp> +#include <mbgl/gl/object.hpp> +#include <mbgl/gl/state.hpp> +#include <mbgl/gl/value.hpp> +#include <mbgl/gl/framebuffer.hpp> +#include <mbgl/gl/vertex_array.hpp> +#include <mbgl/gl/types.hpp> +#include <mbgl/gfx/texture.hpp> +#include <mbgl/gfx/draw_mode.hpp> +#include <mbgl/gfx/depth_mode.hpp> +#include <mbgl/gfx/stencil_mode.hpp> +#include <mbgl/gfx/color_mode.hpp> +#include <mbgl/platform/gl_functions.hpp> +#include <mbgl/util/noncopyable.hpp> + + +#include <functional> +#include <memory> +#include <vector> +#include <array> +#include <string> + +namespace mbgl { +namespace gl { + +constexpr size_t TextureMax = 64; +using ProcAddress = void (*)(); +class RendererBackend; + +namespace extension { +class VertexArray; +class Debugging; +} // namespace extension + +class Context final : public gfx::Context { +public: + Context(RendererBackend&); + ~Context() override; + Context(const Context&) = delete; + Context& operator=(const Context& other) = delete; + + std::unique_ptr<gfx::CommandEncoder> createCommandEncoder() override; + + void initializeExtensions(const std::function<gl::ProcAddress(const char*)>&); + + void enableDebugging(); + + UniqueShader createShader(ShaderType type, const std::initializer_list<const char*>& sources); + UniqueProgram createProgram(ShaderID vertexShader, ShaderID fragmentShader, const char* location0AttribName); + void verifyProgramLinkage(ProgramID); + void linkProgram(ProgramID); + UniqueTexture createUniqueTexture(); + + Framebuffer createFramebuffer(const gfx::Renderbuffer<gfx::RenderbufferPixelType::RGBA>&, + const gfx::Renderbuffer<gfx::RenderbufferPixelType::DepthStencil>&); + Framebuffer createFramebuffer(const gfx::Renderbuffer<gfx::RenderbufferPixelType::RGBA>&); + Framebuffer createFramebuffer(const gfx::Texture&, + const gfx::Renderbuffer<gfx::RenderbufferPixelType::DepthStencil>&); + Framebuffer createFramebuffer(const gfx::Texture&); + Framebuffer createFramebuffer(const gfx::Texture&, + const gfx::Renderbuffer<gfx::RenderbufferPixelType::Depth>&); + + template <typename Image, + gfx::TexturePixelType format = Image::channels == 4 ? gfx::TexturePixelType::RGBA + : gfx::TexturePixelType::Alpha> + Image readFramebuffer(const Size size, bool flip = true) { + static_assert(Image::channels == (format == gfx::TexturePixelType::RGBA ? 4 : 1), + "image format mismatch"); + return { size, readFramebuffer(size, format, flip) }; + } + +#if not MBGL_USE_GLES2 + template <typename Image> + void drawPixels(const Image& image) { + auto format = image.channels == 4 ? gfx::TexturePixelType::RGBA : gfx::TexturePixelType::Alpha; + drawPixels(image.size, image.data.get(), format); + } +#endif // MBGL_USE_GLES2 + + void clear(optional<mbgl::Color> color, + optional<float> depth, + optional<int32_t> stencil); + + void setDepthMode(const gfx::DepthMode&); + void setStencilMode(const gfx::StencilMode&); + void setColorMode(const gfx::ColorMode&); + void setCullFaceMode(const gfx::CullFaceMode&); + + void draw(const gfx::DrawMode&, + std::size_t indexOffset, + std::size_t indexLength); + + // Actually remove the objects we marked as abandoned with the above methods. + // Only call this while the OpenGL context is exclusive to this thread. + void performCleanup() override; + + void reduceMemoryUsage() override; + + // Drain pools and remove abandoned objects, in preparation for destroying the store. + // Only call this while the OpenGL context is exclusive to this thread. + void reset(); + + bool empty() const { + return pooledTextures.empty() + && abandonedPrograms.empty() + && abandonedShaders.empty() + && abandonedBuffers.empty() + && abandonedTextures.empty() + && abandonedVertexArrays.empty() + && abandonedFramebuffers.empty(); + } + + void setDirtyState(); + + extension::Debugging* getDebuggingExtension() const { + return debugging.get(); + } + + extension::VertexArray* getVertexArrayExtension() const { + return vertexArray.get(); + } + + void setCleanupOnDestruction(bool cleanup) { + cleanupOnDestruction = cleanup; + } + +private: + RendererBackend& backend; + bool cleanupOnDestruction = true; + + std::unique_ptr<extension::Debugging> debugging; + std::unique_ptr<extension::VertexArray> vertexArray; + +public: + State<value::ActiveTextureUnit> activeTextureUnit; + State<value::BindFramebuffer> bindFramebuffer; + State<value::Viewport> viewport; + State<value::ScissorTest> scissorTest; + std::array<State<value::BindTexture>, 2> texture; + State<value::Program> program; + State<value::BindVertexBuffer> vertexBuffer; + + State<value::BindVertexArray, const Context&> bindVertexArray { *this }; + VertexArrayState globalVertexArrayState { UniqueVertexArray(0, { this }) }; + + State<value::PixelStorePack> pixelStorePack; + State<value::PixelStoreUnpack> pixelStoreUnpack; + +#if not MBGL_USE_GLES2 + State<value::PixelZoom> pixelZoom; + State<value::RasterPos> rasterPos; + State<value::PixelTransferDepth> pixelTransferDepth; + State<value::PixelTransferStencil> pixelTransferStencil; +#endif // MBGL_USE_GLES2 + +private: + State<value::StencilFunc> stencilFunc; + State<value::StencilMask> stencilMask; + State<value::StencilTest> stencilTest; + State<value::StencilOp> stencilOp; + State<value::DepthRange> depthRange; + State<value::DepthMask> depthMask; + State<value::DepthTest> depthTest; + State<value::DepthFunc> depthFunc; + State<value::Blend> blend; + State<value::BlendEquation> blendEquation; + State<value::BlendFunc> blendFunc; + State<value::BlendColor> blendColor; + State<value::ColorMask> colorMask; + State<value::ClearDepth> clearDepth; + State<value::ClearColor> clearColor; + State<value::ClearStencil> clearStencil; + State<value::LineWidth> lineWidth; + State<value::BindRenderbuffer> bindRenderbuffer; + State<value::CullFace> cullFace; + State<value::CullFaceSide> cullFaceSide; + State<value::CullFaceWinding> cullFaceWinding; +#if not MBGL_USE_GLES2 + State<value::PointSize> pointSize; +#endif // MBGL_USE_GLES2 + + std::unique_ptr<gfx::OffscreenTexture> createOffscreenTexture( + Size, gfx::TextureChannelDataType = gfx::TextureChannelDataType::UnsignedByte) override; + + std::unique_ptr<gfx::TextureResource> + createTextureResource(Size, gfx::TexturePixelType, gfx::TextureChannelDataType) override; + + std::unique_ptr<gfx::RenderbufferResource> createRenderbufferResource(gfx::RenderbufferPixelType, Size size) override; + + std::unique_ptr<gfx::DrawScopeResource> createDrawScopeResource() override; + + UniqueFramebuffer createFramebuffer(); + std::unique_ptr<uint8_t[]> readFramebuffer(Size, gfx::TexturePixelType, bool flip); +#if not MBGL_USE_GLES2 + void drawPixels(Size size, const void* data, gfx::TexturePixelType); +#endif // MBGL_USE_GLES2 + + VertexArray createVertexArray(); + bool supportsVertexArrays() const; + + friend detail::ProgramDeleter; + friend detail::ShaderDeleter; + friend detail::BufferDeleter; + friend detail::TextureDeleter; + friend detail::VertexArrayDeleter; + friend detail::FramebufferDeleter; + friend detail::RenderbufferDeleter; + + std::vector<TextureID> pooledTextures; + + std::vector<ProgramID> abandonedPrograms; + std::vector<ShaderID> abandonedShaders; + std::vector<BufferID> abandonedBuffers; + std::vector<TextureID> abandonedTextures; + std::vector<VertexArrayID> abandonedVertexArrays; + std::vector<FramebufferID> abandonedFramebuffers; + std::vector<RenderbufferID> abandonedRenderbuffers; + +public: + // For testing + bool disableVAOExtension = false; + +#if not defined(NDEBUG) +public: + void visualizeStencilBuffer() override; + void visualizeDepthBuffer(float depthRangeSize) override; +#endif + + void clearStencilBuffer(int32_t) override; +}; + +} // namespace gl +} // namespace mbgl diff --git a/platform/gfx/gl/src/mbgl/gl/debugging_extension.cpp b/platform/gfx/gl/src/mbgl/gl/debugging_extension.cpp new file mode 100644 index 0000000000..5afa4f50fc --- /dev/null +++ b/platform/gfx/gl/src/mbgl/gl/debugging_extension.cpp @@ -0,0 +1,55 @@ +#include <mbgl/gl/debugging_extension.hpp> +#include <mbgl/util/logging.hpp> + +namespace mbgl { +namespace gl { +namespace extension { + +void Debugging::DebugCallback(GLenum source, + GLenum type, + GLuint id, + GLenum severity, + GLsizei /* length */, + const GLchar* message, + const void* /* userParam */) { + std::string strSource; + switch (source) { + case GL_DEBUG_SOURCE_API: strSource = "DEBUG_SOURCE_API"; break; + case GL_DEBUG_SOURCE_WINDOW_SYSTEM: strSource = "DEBUG_SOURCE_WINDOW_SYSTEM"; break; + case GL_DEBUG_SOURCE_SHADER_COMPILER: strSource = "DEBUG_SOURCE_SHADER_COMPILER"; break; + case GL_DEBUG_SOURCE_THIRD_PARTY: strSource = "DEBUG_SOURCE_THIRD_PARTY"; break; + case GL_DEBUG_SOURCE_APPLICATION: strSource = "DEBUG_SOURCE_APPLICATION"; break; + case GL_DEBUG_SOURCE_OTHER: strSource = "DEBUG_SOURCE_OTHER"; break; + default: strSource = "(unknown)"; break; + } + + std::string strType; + switch (type) { + case GL_DEBUG_TYPE_ERROR: strType = "DEBUG_TYPE_ERROR"; break; + case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: strType = "DEBUG_TYPE_DEPRECATED_BEHAVIOR"; break; + case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: strType = "DEBUG_TYPE_UNDEFINED_BEHAVIOR"; break; + case GL_DEBUG_TYPE_PERFORMANCE: strType = "DEBUG_TYPE_PERFORMANCE"; break; + case GL_DEBUG_TYPE_PORTABILITY: strType = "DEBUG_TYPE_PORTABILITY"; break; + case GL_DEBUG_TYPE_OTHER: strType = "DEBUG_TYPE_OTHER"; break; + case GL_DEBUG_TYPE_MARKER: strType = "DEBUG_TYPE_MARKER"; break; + case GL_DEBUG_TYPE_PUSH_GROUP: strType = "DEBUG_TYPE_OTHER"; break; + case GL_DEBUG_TYPE_POP_GROUP: strType = "DEBUG_TYPE_POP_GROUP"; break; + default: strSource = "(unknown)"; break; + } + + std::string strSeverity; + mbgl::EventSeverity evtSeverity; + switch (severity) { + case GL_DEBUG_SEVERITY_HIGH: strSeverity = "DEBUG_SEVERITY_HIGH"; evtSeverity = mbgl::EventSeverity::Error; break; + case GL_DEBUG_SEVERITY_MEDIUM: strSeverity = "DEBUG_SEVERITY_MEDIUM"; evtSeverity = mbgl::EventSeverity::Warning; break; + case GL_DEBUG_SEVERITY_LOW: strSeverity = "DEBUG_SEVERITY_LOW"; evtSeverity = mbgl::EventSeverity::Info; break; + case GL_DEBUG_SEVERITY_NOTIFICATION: strSeverity = "DEBUG_SEVERITY_NOTIFICATION"; evtSeverity = mbgl::EventSeverity::Debug; break; + default: strSource = "(unknown)"; evtSeverity = mbgl::EventSeverity::Debug; break; + } + + mbgl::Log::Record(evtSeverity, mbgl::Event::OpenGL, "GL_%s GL_%s %u GL_%s - %s", strSource.c_str(), strType.c_str(), id, strSeverity.c_str(), message); +} + +} // namespace extension +} // namespace gl +} // namespace mbgl diff --git a/platform/gfx/gl/src/mbgl/gl/debugging_extension.hpp b/platform/gfx/gl/src/mbgl/gl/debugging_extension.hpp new file mode 100644 index 0000000000..0fa5a7b424 --- /dev/null +++ b/platform/gfx/gl/src/mbgl/gl/debugging_extension.hpp @@ -0,0 +1,122 @@ +#pragma once + +#include <mbgl/gl/extension.hpp> +#include <mbgl/platform/gl_functions.hpp> + +#define GL_DEBUG_OUTPUT_SYNCHRONOUS 0x8242 +#define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH 0x8243 +#define GL_DEBUG_CALLBACK_FUNCTION 0x8244 +#define GL_DEBUG_CALLBACK_USER_PARAM 0x8245 +#define GL_DEBUG_SOURCE_API 0x8246 +#define GL_DEBUG_SOURCE_WINDOW_SYSTEM 0x8247 +#define GL_DEBUG_SOURCE_SHADER_COMPILER 0x8248 +#define GL_DEBUG_SOURCE_THIRD_PARTY 0x8249 +#define GL_DEBUG_SOURCE_APPLICATION 0x824A +#define GL_DEBUG_SOURCE_OTHER 0x824B +#define GL_DEBUG_TYPE_ERROR 0x824C +#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR 0x824D +#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR 0x824E +#define GL_DEBUG_TYPE_PORTABILITY 0x824F +#define GL_DEBUG_TYPE_PERFORMANCE 0x8250 +#define GL_DEBUG_TYPE_OTHER 0x8251 +#define GL_MAX_DEBUG_MESSAGE_LENGTH 0x9143 +#define GL_MAX_DEBUG_LOGGED_MESSAGES 0x9144 +#define GL_DEBUG_LOGGED_MESSAGES 0x9145 +#define GL_DEBUG_SEVERITY_HIGH 0x9146 +#define GL_DEBUG_SEVERITY_MEDIUM 0x9147 +#define GL_DEBUG_SEVERITY_LOW 0x9148 +#define GL_DEBUG_TYPE_MARKER 0x8268 +#define GL_DEBUG_TYPE_PUSH_GROUP 0x8269 +#define GL_DEBUG_TYPE_POP_GROUP 0x826A +#define GL_DEBUG_SEVERITY_NOTIFICATION 0x826B +#define GL_MAX_DEBUG_GROUP_STACK_DEPTH 0x826C +#define GL_DEBUG_GROUP_STACK_DEPTH 0x826D +#define GL_BUFFER 0x82E0 +#define GL_SHADER 0x82E1 +#define GL_PROGRAM 0x82E2 +#define GL_QUERY 0x82E3 +#define GL_PROGRAM_PIPELINE 0x82E4 +#define GL_SAMPLER 0x82E6 +#define GL_MAX_LABEL_LENGTH 0x82E8 +#define GL_DEBUG_OUTPUT 0x92E0 +#define GL_CONTEXT_FLAG_DEBUG_BIT 0x00000002 +#define GL_DISPLAY_LIST 0x82E7 +#define GL_VERTEX_ARRAY 0x8074 +#define GL_TRANSFORM_FEEDBACK 0x8E22 +#define GL_TEXTURE 0x1702 +#define GL_RENDERBUFFER 0x8D41 +#define GL_FRAMEBUFFER 0x8D40 + +namespace mbgl { +namespace gl { +namespace extension { + +using namespace platform; + +class Debugging { +public: + using Callback = void (*)(GLenum source, + GLenum type, + GLuint id, + GLenum severity, + GLsizei length, + const GLchar* message, + const void* userParam); + + static void DebugCallback(GLenum source, + GLenum type, + GLuint id, + GLenum severity, + GLsizei /* length */, + const GLchar* message, + const void* /* userParam */); + + template <typename Fn> + Debugging(const Fn& loadExtension) + : +#ifndef NDEBUG + pushDebugGroup( + loadExtension({ { "GL_KHR_debug", "glPushDebugGroup" } })), + popDebugGroup( + loadExtension({ { "GL_KHR_debug", "glPopDebugGroup" } })), + pushGroupMarkerEXT( + loadExtension({ { "GL_EXT_debug_marker", "glPushGroupMarkerEXT" } })), + popGroupMarkerEXT( + loadExtension({ { "GL_EXT_debug_marker", "glPopGroupMarkerEXT" } })), +#endif + debugMessageControl( + loadExtension({ { "GL_KHR_debug", "glDebugMessageControl" }, + { "GL_ARB_debug_output", "glDebugMessageControlARB" } })), + debugMessageCallback( + loadExtension({ { "GL_KHR_debug", "glDebugMessageCallback" }, + { "GL_ARB_debug_output", "glDebugMessageCallbackARB" } })) { + } + +#ifndef NDEBUG + const ExtensionFunction<void(GLenum source, + GLuint id, + GLsizei length, + const GLchar* message)> pushDebugGroup; + + const ExtensionFunction<void()> popDebugGroup; + + const ExtensionFunction<void(GLsizei length, + const GLchar* marker)> pushGroupMarkerEXT; + + const ExtensionFunction<void()> popGroupMarkerEXT; +#endif + + const ExtensionFunction<void(GLenum source, + GLenum type, + GLenum severity, + GLsizei count, + const GLuint* ids, + GLboolean enabled)> debugMessageControl; + + const ExtensionFunction<void(Callback callback, + const void* userParam)> debugMessageCallback; +}; + +} // namespace extension +} // namespace gl +} // namespace mbgl diff --git a/platform/gfx/gl/src/mbgl/gl/defines.hpp b/platform/gfx/gl/src/mbgl/gl/defines.hpp new file mode 100644 index 0000000000..75325dfb75 --- /dev/null +++ b/platform/gfx/gl/src/mbgl/gl/defines.hpp @@ -0,0 +1,179 @@ +#pragma once + +#define GL_ACTIVE_ATTRIBUTE_MAX_LENGTH 0x8B8A +#define GL_ACTIVE_ATTRIBUTES 0x8B89 +#define GL_ACTIVE_TEXTURE 0x84E0 +#define GL_ACTIVE_UNIFORM_MAX_LENGTH 0x8B87 +#define GL_ACTIVE_UNIFORMS 0x8B86 +#define GL_ALPHA 0x1906 +#define GL_ALWAYS 0x0207 +#define GL_ARRAY_BUFFER 0x8892 +#define GL_ARRAY_BUFFER_BINDING 0x8894 +#define GL_BACK 0x0405 +#define GL_BLEND 0x0BE2 +#define GL_BLEND_COLOR 0x8005 +#define GL_BLEND_DST_ALPHA 0x80CA +#define GL_BLEND_EQUATION_RGB 0x8009 +#define GL_BLEND_SRC_ALPHA 0x80CB +#define GL_BOOL 0x8B56 +#define GL_BOOL_VEC2 0x8B57 +#define GL_BOOL_VEC3 0x8B58 +#define GL_BOOL_VEC4 0x8B59 +#define GL_BYTE 0x1400 +#define GL_CCW 0x0901 +#define GL_CLAMP_TO_EDGE 0x812F +#define GL_COLOR_ATTACHMENT0 0x8CE0 +#define GL_COLOR_BUFFER_BIT 0x00004000 +#define GL_COLOR_CLEAR_VALUE 0x0C22 +#define GL_COLOR_WRITEMASK 0x0C23 +#define GL_COMPILE_STATUS 0x8B81 +#define GL_CONSTANT_ALPHA 0x8003 +#define GL_CONSTANT_COLOR 0x8001 +#define GL_CULL_FACE 0x0B44 +#define GL_CULL_FACE_MODE 0x0B45 +#define GL_CURRENT_PROGRAM 0x8B8D +#define GL_CW 0x0900 +#define GL_DECR 0x1E03 +#define GL_DECR_WRAP 0x8508 +#define GL_DEPTH24_STENCIL8_OES 0x88F0 +#define GL_DEPTH_ATTACHMENT 0x8D00 +#define GL_DEPTH_BUFFER_BIT 0x00000100 +#define GL_DEPTH_CLEAR_VALUE 0x0B73 +#define GL_DEPTH_COMPONENT 0x1902 +#define GL_DEPTH_COMPONENT16 0x81A5 +#define GL_DEPTH_FUNC 0x0B74 +#define GL_DEPTH_RANGE 0x0B70 +#define GL_DEPTH_TEST 0x0B71 +#define GL_DEPTH_WRITEMASK 0x0B72 +#define GL_DONT_CARE 0x1100 +#define GL_DST_ALPHA 0x0304 +#define GL_DST_COLOR 0x0306 +#define GL_DYNAMIC_DRAW 0x88E8 +#define GL_ELEMENT_ARRAY_BUFFER 0x8893 +#define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895 +#define GL_EQUAL 0x0202 +#define GL_EXTENSIONS 0x1F03 +#define GL_FALSE 0 +#define GL_FLOAT 0x1406 +#define GL_FLOAT_MAT2 0x8B5A +#define GL_FLOAT_MAT3 0x8B5B +#define GL_FLOAT_MAT4 0x8B5C +#define GL_FLOAT_VEC2 0x8B50 +#define GL_FLOAT_VEC3 0x8B51 +#define GL_FLOAT_VEC4 0x8B52 +#define GL_FRAGMENT_SHADER 0x8B30 +#define GL_FRAMEBUFFER 0x8D40 +#define GL_FRAMEBUFFER_BINDING 0x8CA6 +#define GL_FRAMEBUFFER_COMPLETE 0x8CD5 +#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6 +#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7 +#define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD +#define GL_FRONT 0x0404 +#define GL_FRONT_AND_BACK 0x0408 +#define GL_FRONT_FACE 0x0B46 +#define GL_FUNC_ADD 0x8006 +#define GL_FUNC_REVERSE_SUBTRACT 0x800B +#define GL_FUNC_SUBTRACT 0x800A +#define GL_GEQUAL 0x0206 +#define GL_GREATER 0x0204 +#define GL_INCR 0x1E02 +#define GL_INCR_WRAP 0x8507 +#define GL_INFO_LOG_LENGTH 0x8B84 +#define GL_INT 0x1404 +#define GL_INT_VEC2 0x8B53 +#define GL_INT_VEC3 0x8B54 +#define GL_INT_VEC4 0x8B55 +#define GL_INVALID_ENUM 0x0500 +#define GL_INVALID_FRAMEBUFFER_OPERATION 0x0506 +#define GL_INVALID_OPERATION 0x0502 +#define GL_INVALID_VALUE 0x0501 +#define GL_INVERT 0x150A +#define GL_KEEP 0x1E00 +#define GL_LEQUAL 0x0203 +#define GL_LESS 0x0201 +#define GL_LINEAR 0x2601 +#define GL_LINEAR_MIPMAP_NEAREST 0x2701 +#define GL_LINE_LOOP 0x0002 +#define GL_LINES 0x0001 +#define GL_LINE_STRIP 0x0003 +#define GL_LINE_WIDTH 0x0B21 +#define GL_LINK_STATUS 0x8B82 +#define GL_LUMINANCE 0x1909 +#define GL_MAX_VERTEX_ATTRIBS 0x8869 +#define GL_NEAREST 0x2600 +#define GL_NEAREST_MIPMAP_NEAREST 0x2700 +#define GL_NEVER 0x0200 +#define GL_NO_ERROR 0 +#define GL_NOTEQUAL 0x0205 +#define GL_ONE 1 +#define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004 +#define GL_ONE_MINUS_CONSTANT_COLOR 0x8002 +#define GL_ONE_MINUS_DST_ALPHA 0x0305 +#define GL_ONE_MINUS_DST_COLOR 0x0307 +#define GL_ONE_MINUS_SRC_ALPHA 0x0303 +#define GL_ONE_MINUS_SRC_COLOR 0x0301 +#define GL_OUT_OF_MEMORY 0x0505 +#define GL_PACK_ALIGNMENT 0x0D05 +#define GL_POINTS 0x0000 +#define GL_RENDERBUFFER 0x8D41 +#define GL_RENDERBUFFER_BINDING 0x8CA7 +#define GL_RENDERER 0x1F01 +#define GL_REPEAT 0x2901 +#define GL_REPLACE 0x1E01 +#define GL_RGBA 0x1908 +#define GL_RGBA8_OES 0x8058 +#define GL_SAMPLER_2D 0x8B5E +#define GL_SAMPLER_CUBE 0x8B60 +#define GL_SCISSOR_TEST 0x0C11 +#define GL_SHORT 0x1402 +#define GL_SRC_ALPHA 0x0302 +#define GL_SRC_ALPHA_SATURATE 0x0308 +#define GL_SRC_COLOR 0x0300 +#define GL_STATIC_DRAW 0x88E4 +#define GL_STENCIL_ATTACHMENT 0x8D20 +#define GL_STENCIL_BUFFER_BIT 0x00000400 +#define GL_STENCIL_CLEAR_VALUE 0x0B91 +#define GL_STENCIL_FAIL 0x0B94 +#define GL_STENCIL_FUNC 0x0B92 +#define GL_STENCIL_INDEX 0x1901 +#define GL_STENCIL_PASS_DEPTH_FAIL 0x0B95 +#define GL_STENCIL_PASS_DEPTH_PASS 0x0B96 +#define GL_STENCIL_REF 0x0B97 +#define GL_STENCIL_TEST 0x0B90 +#define GL_STENCIL_VALUE_MASK 0x0B93 +#define GL_STENCIL_WRITEMASK 0x0B98 +#define GL_STREAM_DRAW 0x88E0 +#define GL_TEXTURE0 0x84C0 +#define GL_TEXTURE_2D 0x0DE1 +#define GL_TEXTURE_BINDING_2D 0x8069 +#define GL_TEXTURE_MAG_FILTER 0x2800 +#define GL_TEXTURE_MIN_FILTER 0x2801 +#define GL_TEXTURE_WRAP_S 0x2802 +#define GL_TEXTURE_WRAP_T 0x2803 +#define GL_TRIANGLE_FAN 0x0006 +#define GL_TRIANGLES 0x0004 +#define GL_TRIANGLE_STRIP 0x0005 +#define GL_TRUE 1 +#define GL_UNPACK_ALIGNMENT 0x0CF5 +#define GL_UNSIGNED_BYTE 0x1401 +#define GL_UNSIGNED_INT 0x1405 +#define GL_UNSIGNED_SHORT 0x1403 +#define GL_VERTEX_SHADER 0x8B31 +#define GL_VIEWPORT 0x0BA2 +#define GL_ZERO 0 + +#ifdef MBGL_USE_GLES2 +#define GL_HALF_FLOAT 0x8D61 +#else +#define GL_CURRENT_RASTER_POSITION 0x0B07 +#define GL_DEPTH24_STENCIL8 0x88F0 +#define GL_DEPTH_BIAS 0x0D1F +#define GL_DEPTH_SCALE 0x0D1E +#define GL_HALF_FLOAT 0x140B +#define GL_INDEX_OFFSET 0x0D13 +#define GL_INDEX_SHIFT 0x0D12 +#define GL_POINT_SIZE 0x0B11 +#define GL_RGBA8 0x8058 +#define GL_ZOOM_X 0x0D16 +#define GL_ZOOM_Y 0x0D17 +#endif
\ No newline at end of file diff --git a/platform/gfx/gl/src/mbgl/gl/draw_scope_resource.hpp b/platform/gfx/gl/src/mbgl/gl/draw_scope_resource.hpp new file mode 100644 index 0000000000..4c446cd4fe --- /dev/null +++ b/platform/gfx/gl/src/mbgl/gl/draw_scope_resource.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include <mbgl/gfx/draw_scope.hpp> +#include <mbgl/gl/vertex_array.hpp> + +namespace mbgl { +namespace gl { + +class DrawScopeResource : public gfx::DrawScopeResource { +public: + DrawScopeResource(VertexArray&& vertexArray_) : vertexArray(std::move(vertexArray_)) { + } + + VertexArray vertexArray; +}; + +} // namespace gl +} // namespace mbgl diff --git a/platform/gfx/gl/src/mbgl/gl/enum.cpp b/platform/gfx/gl/src/mbgl/gl/enum.cpp new file mode 100644 index 0000000000..5f8cd77ad6 --- /dev/null +++ b/platform/gfx/gl/src/mbgl/gl/enum.cpp @@ -0,0 +1,319 @@ +#include <mbgl/gl/enum.hpp> +#include <mbgl/gfx/types.hpp> +#include <mbgl/gl/defines.hpp> + +namespace mbgl { +namespace gl { + +template <> +platform::GLenum Enum<gfx::DrawModeType>::to(const gfx::DrawModeType value) { + switch (value) { + case gfx::DrawModeType::Points: return GL_POINTS; + case gfx::DrawModeType::Lines: return GL_LINES; + case gfx::DrawModeType::LineLoop: return GL_LINE_LOOP; + case gfx::DrawModeType::LineStrip: return GL_LINE_STRIP; + case gfx::DrawModeType::Triangles: return GL_TRIANGLES; + case gfx::DrawModeType::TriangleStrip: return GL_TRIANGLE_STRIP; + case gfx::DrawModeType::TriangleFan: return GL_TRIANGLE_FAN; + } + return GL_INVALID_ENUM; +} + +template <> +gfx::ColorBlendEquationType Enum<gfx::ColorBlendEquationType>::from(const platform::GLint value) { + switch (value) { + case GL_FUNC_ADD: return gfx::ColorBlendEquationType::Add; + case GL_FUNC_SUBTRACT: return gfx::ColorBlendEquationType::Subtract; + case GL_FUNC_REVERSE_SUBTRACT: return gfx::ColorBlendEquationType::ReverseSubtract; + } + return {}; +} + +template <> +platform::GLenum Enum<gfx::ColorBlendEquationType>::to(const gfx::ColorBlendEquationType value) { + switch (value) { + case gfx::ColorBlendEquationType::Add: return GL_FUNC_ADD; + case gfx::ColorBlendEquationType::Subtract: return GL_FUNC_SUBTRACT; + case gfx::ColorBlendEquationType::ReverseSubtract: return GL_FUNC_REVERSE_SUBTRACT; + } + return GL_INVALID_ENUM; +} + +template <> +gfx::ColorBlendFactorType Enum<gfx::ColorBlendFactorType>::from(const platform::GLint value) { + switch (value) { + case GL_ZERO: return gfx::ColorBlendFactorType::Zero; + case GL_ONE: return gfx::ColorBlendFactorType::One; + case GL_SRC_COLOR: return gfx::ColorBlendFactorType::SrcColor; + case GL_ONE_MINUS_SRC_COLOR: return gfx::ColorBlendFactorType::OneMinusSrcColor; + case GL_DST_COLOR: return gfx::ColorBlendFactorType::DstColor; + case GL_ONE_MINUS_DST_COLOR: return gfx::ColorBlendFactorType::OneMinusDstColor; + case GL_SRC_ALPHA: return gfx::ColorBlendFactorType::SrcAlpha; + case GL_ONE_MINUS_SRC_ALPHA: return gfx::ColorBlendFactorType::OneMinusSrcAlpha; + case GL_DST_ALPHA: return gfx::ColorBlendFactorType::DstAlpha; + case GL_ONE_MINUS_DST_ALPHA: return gfx::ColorBlendFactorType::OneMinusDstAlpha; + case GL_CONSTANT_COLOR: return gfx::ColorBlendFactorType::ConstantColor; + case GL_ONE_MINUS_CONSTANT_COLOR: return gfx::ColorBlendFactorType::OneMinusConstantColor; + case GL_CONSTANT_ALPHA: return gfx::ColorBlendFactorType::ConstantAlpha; + case GL_ONE_MINUS_CONSTANT_ALPHA: return gfx::ColorBlendFactorType::OneMinusConstantAlpha; + case GL_SRC_ALPHA_SATURATE: return gfx::ColorBlendFactorType::SrcAlphaSaturate; + } + return {}; +} + +template <> +platform::GLenum Enum<gfx::ColorBlendFactorType>::to(const gfx::ColorBlendFactorType value) { + switch (value) { + case gfx::ColorBlendFactorType::Zero: return GL_ZERO; + case gfx::ColorBlendFactorType::One: return GL_ONE; + case gfx::ColorBlendFactorType::SrcColor: return GL_SRC_COLOR; + case gfx::ColorBlendFactorType::OneMinusSrcColor: return GL_ONE_MINUS_SRC_COLOR; + case gfx::ColorBlendFactorType::DstColor: return GL_DST_COLOR; + case gfx::ColorBlendFactorType::OneMinusDstColor: return GL_ONE_MINUS_DST_COLOR; + case gfx::ColorBlendFactorType::SrcAlpha: return GL_SRC_ALPHA; + case gfx::ColorBlendFactorType::OneMinusSrcAlpha: return GL_ONE_MINUS_SRC_ALPHA; + case gfx::ColorBlendFactorType::DstAlpha: return GL_DST_ALPHA; + case gfx::ColorBlendFactorType::OneMinusDstAlpha: return GL_ONE_MINUS_DST_ALPHA; + case gfx::ColorBlendFactorType::ConstantColor: return GL_CONSTANT_COLOR; + case gfx::ColorBlendFactorType::OneMinusConstantColor: return GL_ONE_MINUS_CONSTANT_COLOR; + case gfx::ColorBlendFactorType::ConstantAlpha: return GL_CONSTANT_ALPHA; + case gfx::ColorBlendFactorType::OneMinusConstantAlpha: return GL_ONE_MINUS_CONSTANT_ALPHA; + case gfx::ColorBlendFactorType::SrcAlphaSaturate: return GL_SRC_ALPHA_SATURATE; + } + return GL_INVALID_ENUM; +} + +template <> +gfx::DepthFunctionType Enum<gfx::DepthFunctionType>::from(const platform::GLint value) { + switch (value) { + case GL_NEVER: return gfx::DepthFunctionType::Never; + case GL_LESS: return gfx::DepthFunctionType::Less; + case GL_EQUAL: return gfx::DepthFunctionType::Equal; + case GL_LEQUAL: return gfx::DepthFunctionType::LessEqual; + case GL_GREATER: return gfx::DepthFunctionType::Greater; + case GL_NOTEQUAL: return gfx::DepthFunctionType::NotEqual; + case GL_GEQUAL: return gfx::DepthFunctionType::GreaterEqual; + case GL_ALWAYS: return gfx::DepthFunctionType::Always; + } + return {}; +} + +template <> +platform::GLenum Enum<gfx::DepthFunctionType>::to(const gfx::DepthFunctionType value) { + switch (value) { + case gfx::DepthFunctionType::Never: return GL_NEVER; + case gfx::DepthFunctionType::Less: return GL_LESS; + case gfx::DepthFunctionType::Equal: return GL_EQUAL; + case gfx::DepthFunctionType::LessEqual: return GL_LEQUAL; + case gfx::DepthFunctionType::Greater: return GL_GREATER; + case gfx::DepthFunctionType::NotEqual: return GL_NOTEQUAL; + case gfx::DepthFunctionType::GreaterEqual: return GL_GEQUAL; + case gfx::DepthFunctionType::Always: return GL_ALWAYS; + } + return GL_INVALID_ENUM; +} + +template <> +gfx::DepthMaskType Enum<gfx::DepthMaskType>::from(const platform::GLboolean value) { + return value ? gfx::DepthMaskType::ReadWrite : gfx::DepthMaskType::ReadOnly; +} + +template <> +platform::GLboolean Enum<gfx::DepthMaskType>::to(const gfx::DepthMaskType value) { + return value == gfx::DepthMaskType::ReadWrite ? GL_TRUE : GL_FALSE; +} + +template <> +gfx::StencilFunctionType Enum<gfx::StencilFunctionType>::from(const platform::GLint value) { + switch (value) { + case GL_NEVER: return gfx::StencilFunctionType::Never; + case GL_LESS: return gfx::StencilFunctionType::Less; + case GL_EQUAL: return gfx::StencilFunctionType::Equal; + case GL_LEQUAL: return gfx::StencilFunctionType::LessEqual; + case GL_GREATER: return gfx::StencilFunctionType::Greater; + case GL_NOTEQUAL: return gfx::StencilFunctionType::NotEqual; + case GL_GEQUAL: return gfx::StencilFunctionType::GreaterEqual; + case GL_ALWAYS: return gfx::StencilFunctionType::Always; + } + return {}; +} + +template <> +platform::GLenum Enum<gfx::StencilFunctionType>::to(const gfx::StencilFunctionType value) { + switch (value) { + case gfx::StencilFunctionType::Never: return GL_NEVER; + case gfx::StencilFunctionType::Less: return GL_LESS; + case gfx::StencilFunctionType::Equal: return GL_EQUAL; + case gfx::StencilFunctionType::LessEqual: return GL_LEQUAL; + case gfx::StencilFunctionType::Greater: return GL_GREATER; + case gfx::StencilFunctionType::NotEqual: return GL_NOTEQUAL; + case gfx::StencilFunctionType::GreaterEqual: return GL_GEQUAL; + case gfx::StencilFunctionType::Always: return GL_ALWAYS; + } + return GL_INVALID_ENUM; +} + +template <> +gfx::StencilOpType Enum<gfx::StencilOpType>::from(const platform::GLint value) { + switch (value) { + case GL_KEEP: return gfx::StencilOpType::Keep; + case GL_ZERO: return gfx::StencilOpType::Zero; + case GL_REPLACE: return gfx::StencilOpType::Replace; + case GL_INCR: return gfx::StencilOpType::Increment; + case GL_INCR_WRAP: return gfx::StencilOpType::IncrementWrap; + case GL_DECR: return gfx::StencilOpType::Decrement; + case GL_DECR_WRAP: return gfx::StencilOpType::DecrementWrap; + case GL_INVERT: return gfx::StencilOpType::Invert; + } + return {}; +} + +template <> +platform::GLenum Enum<gfx::StencilOpType>::to(const gfx::StencilOpType value) { + switch (value) { + case gfx::StencilOpType::Keep: return GL_KEEP; + case gfx::StencilOpType::Zero: return GL_ZERO; + case gfx::StencilOpType::Replace: return GL_REPLACE; + case gfx::StencilOpType::Increment: return GL_INCR; + case gfx::StencilOpType::IncrementWrap: return GL_INCR_WRAP; + case gfx::StencilOpType::Decrement: return GL_DECR; + case gfx::StencilOpType::DecrementWrap: return GL_DECR_WRAP; + case gfx::StencilOpType::Invert: return GL_INVERT; + } + return GL_INVALID_ENUM; +} + +template <> +gfx::CullFaceSideType Enum<gfx::CullFaceSideType>::from(const platform::GLint value) { + switch (value) { + case GL_FRONT: return gfx::CullFaceSideType::Front; + case GL_BACK: return gfx::CullFaceSideType::Back; + case GL_FRONT_AND_BACK: return gfx::CullFaceSideType::FrontAndBack; + } + return {}; +} + +template <> +platform::GLenum Enum<gfx::CullFaceSideType>::to(const gfx::CullFaceSideType value) { + switch (value) { + case gfx::CullFaceSideType::Front: return GL_FRONT; + case gfx::CullFaceSideType::Back: return GL_BACK; + case gfx::CullFaceSideType::FrontAndBack: return GL_FRONT_AND_BACK; + } + return GL_INVALID_ENUM; +} + +template <> +gfx::CullFaceWindingType Enum<gfx::CullFaceWindingType>::from(const platform::GLint value) { + switch (value) { + case GL_CW: return gfx::CullFaceWindingType::Clockwise; + case GL_CCW: return gfx::CullFaceWindingType::CounterClockwise; + } + return {}; +} + +template <> +platform::GLenum Enum<gfx::CullFaceWindingType>::to(const gfx::CullFaceWindingType value) { + switch (value) { + case gfx::CullFaceWindingType::Clockwise: return GL_CW; + case gfx::CullFaceWindingType::CounterClockwise: return GL_CCW; + } + return GL_INVALID_ENUM; +} + +template <> +gfx::BufferUsageType Enum<gfx::BufferUsageType>::from(const platform::GLint value) { + switch (value) { + case GL_STREAM_DRAW: return gfx::BufferUsageType::StreamDraw; + case GL_STATIC_DRAW: return gfx::BufferUsageType::StaticDraw; + case GL_DYNAMIC_DRAW: return gfx::BufferUsageType::DynamicDraw; + } + return {}; +} + +template <> +platform::GLenum Enum<gfx::BufferUsageType>::to(const gfx::BufferUsageType value) { + switch (value) { + case gfx::BufferUsageType::StreamDraw: return GL_STREAM_DRAW; + case gfx::BufferUsageType::StaticDraw: return GL_STATIC_DRAW; + case gfx::BufferUsageType::DynamicDraw: return GL_DYNAMIC_DRAW; + } + return GL_INVALID_ENUM; +} + +template <> +gfx::TexturePixelType Enum<gfx::TexturePixelType>::from(const platform::GLint value) { + switch (value) { + case GL_RGBA: return gfx::TexturePixelType::RGBA; + case GL_ALPHA: return gfx::TexturePixelType::Alpha; + case GL_STENCIL_INDEX: return gfx::TexturePixelType::Stencil; + case GL_DEPTH_COMPONENT: return gfx::TexturePixelType::Depth; + case GL_LUMINANCE: return gfx::TexturePixelType::Luminance; + } + return {}; +} + +template <> +platform::GLenum Enum<gfx::TexturePixelType>::to(const gfx::TexturePixelType value) { + switch (value) { + case gfx::TexturePixelType::RGBA: return GL_RGBA; + case gfx::TexturePixelType::Alpha: return GL_ALPHA; + case gfx::TexturePixelType::Stencil: return GL_STENCIL_INDEX; + case gfx::TexturePixelType::Depth: return GL_DEPTH_COMPONENT; + case gfx::TexturePixelType::Luminance: return GL_LUMINANCE; + } + return GL_INVALID_ENUM; +} + +template <> +gfx::TextureChannelDataType Enum<gfx::TextureChannelDataType>::from(const platform::GLint value) { + switch (value) { + case GL_UNSIGNED_BYTE: return gfx::TextureChannelDataType::UnsignedByte; + case GL_HALF_FLOAT: return gfx::TextureChannelDataType::HalfFloat; + } + return {}; +} + +template <> +platform::GLenum Enum<gfx::TextureChannelDataType>::to(const gfx::TextureChannelDataType value) { + switch (value) { + case gfx::TextureChannelDataType::UnsignedByte: return GL_UNSIGNED_BYTE; + case gfx::TextureChannelDataType::HalfFloat: return GL_HALF_FLOAT; + } + return GL_INVALID_ENUM; +} + +template <> +gfx::RenderbufferPixelType Enum<gfx::RenderbufferPixelType>::from(const platform::GLint value) { + switch (value) { +#if not MBGL_USE_GLES2 + case GL_RGBA8: return gfx::RenderbufferPixelType::RGBA; + case GL_DEPTH_COMPONENT: return gfx::RenderbufferPixelType::Depth; + case GL_DEPTH24_STENCIL8: return gfx::RenderbufferPixelType::DepthStencil; +#else + case GL_RGBA8_OES: return gfx::RenderbufferPixelType::RGBA; + case GL_DEPTH_COMPONENT16: return gfx::RenderbufferPixelType::Depth; + case GL_DEPTH24_STENCIL8_OES: return gfx::RenderbufferPixelType::DepthStencil; +#endif + } + return {}; +} + +template <> +platform::GLenum Enum<gfx::RenderbufferPixelType>::to(const gfx::RenderbufferPixelType value) { + switch (value) { +#if not MBGL_USE_GLES2 + case gfx::RenderbufferPixelType::RGBA: return GL_RGBA8; + case gfx::RenderbufferPixelType::Depth: return GL_DEPTH_COMPONENT; + case gfx::RenderbufferPixelType::DepthStencil: return GL_DEPTH24_STENCIL8; +#else + case gfx::RenderbufferPixelType::RGBA: return GL_RGBA8_OES; + case gfx::RenderbufferPixelType::Depth: return GL_DEPTH_COMPONENT16; + case gfx::RenderbufferPixelType::DepthStencil: return GL_DEPTH24_STENCIL8_OES; +#endif + } + return GL_INVALID_ENUM; +} + +} // namespace gl +} // namespace mbgl diff --git a/platform/gfx/gl/src/mbgl/gl/enum.hpp b/platform/gfx/gl/src/mbgl/gl/enum.hpp new file mode 100644 index 0000000000..6aa29efdb1 --- /dev/null +++ b/platform/gfx/gl/src/mbgl/gl/enum.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include <mbgl/platform/gl_functions.hpp> + +#include <type_traits> + +namespace mbgl { +namespace gl { + +template <typename T> +class Enum { +public: + using InType = std::conditional_t<std::is_same<std::underlying_type_t<T>, bool>::value, platform::GLboolean, platform::GLint>; + using OutType = std::conditional_t<std::is_same<std::underlying_type_t<T>, bool>::value, platform::GLboolean, platform::GLenum>; + + static T from(const InType); + static OutType to(T); +}; + +} // namespace gl +} // namespace mbgl diff --git a/platform/gfx/gl/src/mbgl/gl/extension.hpp b/platform/gfx/gl/src/mbgl/gl/extension.hpp new file mode 100644 index 0000000000..8710314ef2 --- /dev/null +++ b/platform/gfx/gl/src/mbgl/gl/extension.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include <initializer_list> +#include <utility> +#include <functional> + +namespace mbgl { +namespace gl { + +using ProcAddress = void (*)(); + +template <class> +class ExtensionFunction; + +template <class R, class... Args> +class ExtensionFunction<R(Args...)> { +public: + ExtensionFunction(const ProcAddress ptr_) : ptr(ptr_) { + } + + explicit operator bool() const { + return ptr; + } + + R operator()(Args... args) const { + return (*reinterpret_cast<R (*)(Args...)>(ptr))(std::forward<Args>(args)...); + } + +private: + const ProcAddress ptr; +}; + +} // namespace gl +} // namespace mbgl diff --git a/platform/gfx/gl/src/mbgl/gl/framebuffer.hpp b/platform/gfx/gl/src/mbgl/gl/framebuffer.hpp new file mode 100644 index 0000000000..91ed467b40 --- /dev/null +++ b/platform/gfx/gl/src/mbgl/gl/framebuffer.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include <mbgl/gl/object.hpp> +#include <mbgl/util/size.hpp> + +namespace mbgl { +namespace gl { + +class Framebuffer { +public: + Size size; + gl::UniqueFramebuffer framebuffer; +}; + +} // namespace gl +} // namespace mbgl diff --git a/platform/gfx/gl/src/mbgl/gl/index_buffer_resource.hpp b/platform/gfx/gl/src/mbgl/gl/index_buffer_resource.hpp new file mode 100644 index 0000000000..2da25fdb96 --- /dev/null +++ b/platform/gfx/gl/src/mbgl/gl/index_buffer_resource.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include <mbgl/gfx/index_buffer.hpp> +#include <mbgl/gl/object.hpp> + +namespace mbgl { +namespace gl { + +class IndexBufferResource : public gfx::IndexBufferResource { +public: + IndexBufferResource(UniqueBuffer&& buffer_) : buffer(std::move(buffer_)) { + } + + UniqueBuffer buffer; +}; + +} // namespace gl +} // namespace mbgl diff --git a/platform/gfx/gl/src/mbgl/gl/object.cpp b/platform/gfx/gl/src/mbgl/gl/object.cpp new file mode 100644 index 0000000000..ec2998a27d --- /dev/null +++ b/platform/gfx/gl/src/mbgl/gl/object.cpp @@ -0,0 +1,52 @@ +#include <mbgl/gl/object.hpp> +#include <mbgl/gl/context.hpp> + +#include <cassert> + +namespace mbgl { +namespace gl { +namespace detail { + +void ProgramDeleter::operator()(ProgramID id) const { + assert(context); + context->abandonedPrograms.push_back(id); +} + +void ShaderDeleter::operator()(ShaderID id) const { + assert(context); + context->abandonedShaders.push_back(id); +} + +void BufferDeleter::operator()(BufferID id) const { + context.abandonedBuffers.push_back(id); +} + +void TextureDeleter::operator()(TextureID id) const { + assert(context); + if (context->pooledTextures.size() >= TextureMax) { + context->abandonedTextures.push_back(id); + } else { + context->pooledTextures.push_back(id); + } +} + +void VertexArrayDeleter::operator()(VertexArrayID id) const { + assert(context); + if (id != 0) { + context->abandonedVertexArrays.push_back(id); + } +} + +void FramebufferDeleter::operator()(FramebufferID id) const { + assert(context); + context->abandonedFramebuffers.push_back(id); +} + +void RenderbufferDeleter::operator()(RenderbufferID id) const { + assert(context); + context->abandonedRenderbuffers.push_back(id); +} + +} // namespace detail +} // namespace gl +} // namespace mbgl diff --git a/platform/gfx/gl/src/mbgl/gl/object.hpp b/platform/gfx/gl/src/mbgl/gl/object.hpp new file mode 100644 index 0000000000..9598e0c59e --- /dev/null +++ b/platform/gfx/gl/src/mbgl/gl/object.hpp @@ -0,0 +1,60 @@ +#pragma once + +#include <mbgl/gl/types.hpp> + +#include <unique_resource.hpp> + +namespace mbgl { +namespace gl { + +class Context; + +namespace detail { + +struct ProgramDeleter { + Context* context; + void operator()(ProgramID) const; +}; + +struct ShaderDeleter { + Context* context; + void operator()(ShaderID) const; +}; + +struct BufferDeleter { + Context& context; + void operator()(BufferID) const; +}; + +struct TextureDeleter { + Context* context; + void operator()(TextureID) const; +}; + +struct VertexArrayDeleter { + Context* context; + void operator()(VertexArrayID) const; +}; + +struct FramebufferDeleter { + Context* context; + void operator()(FramebufferID) const; +}; + +struct RenderbufferDeleter { + Context* context; + void operator()(RenderbufferID) const; +}; + +} // namespace detail + +using UniqueProgram = std_experimental::unique_resource<ProgramID, detail::ProgramDeleter>; +using UniqueShader = std_experimental::unique_resource<ShaderID, detail::ShaderDeleter>; +using UniqueBuffer = std_experimental::unique_resource<BufferID, detail::BufferDeleter>; +using UniqueTexture = std_experimental::unique_resource<TextureID, detail::TextureDeleter>; +using UniqueVertexArray = std_experimental::unique_resource<VertexArrayID, detail::VertexArrayDeleter>; +using UniqueFramebuffer = std_experimental::unique_resource<FramebufferID, detail::FramebufferDeleter>; +using UniqueRenderbuffer = std_experimental::unique_resource<RenderbufferID, detail::RenderbufferDeleter>; + +} // namespace gl +} // namespace mbgl diff --git a/platform/gfx/gl/src/mbgl/gl/offscreen_texture.cpp b/platform/gfx/gl/src/mbgl/gl/offscreen_texture.cpp new file mode 100644 index 0000000000..92f80a87b4 --- /dev/null +++ b/platform/gfx/gl/src/mbgl/gl/offscreen_texture.cpp @@ -0,0 +1,75 @@ +#include <mbgl/gl/offscreen_texture.hpp> +#include <mbgl/gl/renderable_resource.hpp> +#include <mbgl/gl/context.hpp> +#include <mbgl/gl/framebuffer.hpp> + +namespace mbgl { +namespace gl { + +class OffscreenTextureResource final : public gl::RenderableResource { +public: + OffscreenTextureResource(gl::Context& context_, + const Size size_, + const gfx::TextureChannelDataType type_) + : context(context_), size(size_), type(type_) { + assert(!size.isEmpty()); + } + + void bind() override { + if (!framebuffer) { + assert(!texture); + texture = context.createTexture(size, gfx::TexturePixelType::RGBA, type); + framebuffer = context.createFramebuffer(*texture); + } else { + context.bindFramebuffer = framebuffer->framebuffer; + } + + context.activeTextureUnit = 0; + context.scissorTest = false; + context.viewport = { 0, 0, size }; + } + + PremultipliedImage readStillImage() { + assert(framebuffer); + context.bindFramebuffer = framebuffer->framebuffer; + return context.readFramebuffer<PremultipliedImage>(size); + } + + gfx::Texture& getTexture() { + assert(texture); + return *texture; + } + +private: + gl::Context& context; + const Size size; + optional<gfx::Texture> texture; + const gfx::TextureChannelDataType type; + optional<gl::Framebuffer> framebuffer; +}; + +OffscreenTexture::OffscreenTexture(gl::Context& context, + const Size size_, + const gfx::TextureChannelDataType type) + : gfx::OffscreenTexture(size, std::make_unique<OffscreenTextureResource>(context, size_, type)) { +} + +bool OffscreenTexture::isRenderable() { + try { + getResource<OffscreenTextureResource>().bind(); + return true; + } catch (const std::runtime_error& ex) { + return false; + } +} + +PremultipliedImage OffscreenTexture::readStillImage() { + return getResource<OffscreenTextureResource>().readStillImage(); +} + +gfx::Texture& OffscreenTexture::getTexture() { + return getResource<OffscreenTextureResource>().getTexture(); +} + +} // namespace gl +} // namespace mbgl diff --git a/platform/gfx/gl/src/mbgl/gl/offscreen_texture.hpp b/platform/gfx/gl/src/mbgl/gl/offscreen_texture.hpp new file mode 100644 index 0000000000..5f3863d3e9 --- /dev/null +++ b/platform/gfx/gl/src/mbgl/gl/offscreen_texture.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include <mbgl/gfx/offscreen_texture.hpp> +#include <mbgl/gfx/types.hpp> + +namespace mbgl { +namespace gl { + +class Context; + +class OffscreenTexture final : public gfx::OffscreenTexture { +public: + OffscreenTexture(gl::Context&, + Size size, + gfx::TextureChannelDataType type = gfx::TextureChannelDataType::UnsignedByte); + + bool isRenderable() override; + + PremultipliedImage readStillImage() override; + gfx::Texture& getTexture() override; +}; + +} // namespace gl +} // namespace mbgl diff --git a/platform/gfx/gl/src/mbgl/gl/program.hpp b/platform/gfx/gl/src/mbgl/gl/program.hpp new file mode 100644 index 0000000000..6cfe05bf54 --- /dev/null +++ b/platform/gfx/gl/src/mbgl/gl/program.hpp @@ -0,0 +1,141 @@ +#pragma once + +#include <mbgl/gfx/program.hpp> +#include <mbgl/gl/types.hpp> +#include <mbgl/gl/object.hpp> +#include <mbgl/gl/context.hpp> +#include <mbgl/gl/draw_scope_resource.hpp> +#include <mbgl/gfx/vertex_buffer.hpp> +#include <mbgl/gfx/index_buffer.hpp> +#include <mbgl/gfx/uniform.hpp> +#include <mbgl/gl/vertex_array.hpp> +#include <mbgl/gl/attribute.hpp> +#include <mbgl/gl/uniform.hpp> +#include <mbgl/gl/texture.hpp> +#include <mbgl/util/io.hpp> + +#include <mbgl/util/logging.hpp> +#include <mbgl/programs/program_parameters.hpp> +#include <mbgl/programs/gl/shader_source.hpp> +#include <mbgl/programs/gl/shaders.hpp> + +#include <string> + +namespace mbgl { +namespace gl { + +template <class Name> +class Program final : public gfx::Program<Name> { +public: + using AttributeList = typename Name::AttributeList; + using UniformList = typename Name::UniformList; + using TextureList = typename Name::TextureList; + + Program(ProgramParameters programParameters_) + : programParameters(std::move(programParameters_)) { + } + + const ProgramParameters programParameters; + + static constexpr const auto vertexOffset = programs::gl::ShaderSource<Name>::vertexOffset; + static constexpr const auto fragmentOffset = programs::gl::ShaderSource<Name>::fragmentOffset; + + class Instance { + public: + Instance(Context& context, + const std::initializer_list<const char*>& vertexSource, + const std::initializer_list<const char*>& fragmentSource) + : program(context.createProgram( + context.createShader(ShaderType::Vertex, vertexSource), + context.createShader(ShaderType::Fragment, fragmentSource), + attributeLocations.getFirstAttribName())) { + attributeLocations.queryLocations(program); + uniformStates.queryLocations(program); + // Texture units are specified via uniforms as well, so we need query their locations + textureStates.queryLocations(program); + } + + static std::unique_ptr<Instance> + createInstance(gl::Context& context, + const ProgramParameters& programParameters, + const std::string& additionalDefines) { + // Compile the shader + const std::initializer_list<const char*> vertexSource = { + programParameters.getDefines().c_str(), + additionalDefines.c_str(), + (programs::gl::shaderSource() + programs::gl::vertexPreludeOffset), + (programs::gl::shaderSource() + vertexOffset) + }; + const std::initializer_list<const char*> fragmentSource = { + programParameters.getDefines().c_str(), + additionalDefines.c_str(), + (programs::gl::shaderSource() + programs::gl::fragmentPreludeOffset), + (programs::gl::shaderSource() + fragmentOffset) + }; + auto result = std::make_unique<Instance>(context, vertexSource, fragmentSource); + + return std::move(result); + } + + UniqueProgram program; + gl::AttributeLocations<AttributeList> attributeLocations; + gl::UniformStates<UniformList> uniformStates; + gl::TextureStates<TextureList> textureStates; + }; + + void draw(gfx::Context& genericContext, + gfx::RenderPass&, + const gfx::DrawMode& drawMode, + const gfx::DepthMode& depthMode, + const gfx::StencilMode& stencilMode, + const gfx::ColorMode& colorMode, + const gfx::CullFaceMode& cullFaceMode, + const gfx::UniformValues<UniformList>& uniformValues, + gfx::DrawScope& drawScope, + const gfx::AttributeBindings<AttributeList>& attributeBindings, + const gfx::TextureBindings<TextureList>& textureBindings, + const gfx::IndexBuffer& indexBuffer, + std::size_t indexOffset, + std::size_t indexLength) override { + auto& context = static_cast<gl::Context&>(genericContext); + + context.setDepthMode(depthMode); + context.setStencilMode(stencilMode); + context.setColorMode(colorMode); + context.setCullFaceMode(cullFaceMode); + + const uint32_t key = gl::AttributeKey<AttributeList>::compute(attributeBindings); + auto it = instances.find(key); + if (it == instances.end()) { + it = instances + .emplace(key, + Instance::createInstance( + context, + programParameters, + gl::AttributeKey<AttributeList>::defines(attributeBindings))) + .first; + } + + auto& instance = *it->second; + context.program = instance.program; + + instance.uniformStates.bind(uniformValues); + + instance.textureStates.bind(context, textureBindings); + + auto& vertexArray = drawScope.getResource<gl::DrawScopeResource>().vertexArray; + vertexArray.bind(context, + indexBuffer, + instance.attributeLocations.toBindingArray(attributeBindings)); + + context.draw(drawMode, + indexOffset, + indexLength); + } + +private: + std::map<uint32_t, std::unique_ptr<Instance>> instances; +}; + +} // namespace gl +} // namespace mbgl diff --git a/platform/gfx/gl/src/mbgl/gl/render_pass.cpp b/platform/gfx/gl/src/mbgl/gl/render_pass.cpp new file mode 100644 index 0000000000..b327f7954f --- /dev/null +++ b/platform/gfx/gl/src/mbgl/gl/render_pass.cpp @@ -0,0 +1,28 @@ +#include <mbgl/gl/render_pass.hpp> +#include <mbgl/gl/command_encoder.hpp> +#include <mbgl/gl/renderable_resource.hpp> +#include <mbgl/gl/context.hpp> + +namespace mbgl { +namespace gl { + +RenderPass::RenderPass(gl::CommandEncoder& commandEncoder_, + const char* name, + const gfx::RenderPassDescriptor& descriptor) + : commandEncoder(commandEncoder_), debugGroup(commandEncoder.createDebugGroup(name)) { + descriptor.renderable.getResource<gl::RenderableResource>().bind(); + const auto clearDebugGroup(commandEncoder.createDebugGroup("clear")); + commandEncoder.context.clear(descriptor.clearColor, descriptor.clearDepth, + descriptor.clearStencil); +} + +void RenderPass::pushDebugGroup(const char* name) { + commandEncoder.pushDebugGroup(name); +} + +void RenderPass::popDebugGroup() { + commandEncoder.popDebugGroup(); +} + +} // namespace gl +} // namespace mbgl diff --git a/platform/gfx/gl/src/mbgl/gl/render_pass.hpp b/platform/gfx/gl/src/mbgl/gl/render_pass.hpp new file mode 100644 index 0000000000..85a56243a6 --- /dev/null +++ b/platform/gfx/gl/src/mbgl/gl/render_pass.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include <mbgl/gfx/render_pass.hpp> + +namespace mbgl { +namespace gfx { + +class CommandEncoder; + +} // namespace gfx + +namespace gl { + +class CommandEncoder; +class Context; + +class RenderPass final : public gfx::RenderPass { +public: + RenderPass(gl::CommandEncoder&, const char* name, const gfx::RenderPassDescriptor&); + +private: + void pushDebugGroup(const char* name) override; + void popDebugGroup() override; + +private: + gl::CommandEncoder& commandEncoder; + const gfx::DebugGroup<gfx::CommandEncoder> debugGroup; +}; + +} // namespace gl +} // namespace mbgl diff --git a/platform/gfx/gl/src/mbgl/gl/renderbuffer_resource.hpp b/platform/gfx/gl/src/mbgl/gl/renderbuffer_resource.hpp new file mode 100644 index 0000000000..52865b42f7 --- /dev/null +++ b/platform/gfx/gl/src/mbgl/gl/renderbuffer_resource.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include <mbgl/gfx/renderbuffer.hpp> +#include <mbgl/gl/object.hpp> + +namespace mbgl { +namespace gl { + +class RenderbufferResource final : public gfx::RenderbufferResource { +public: + explicit RenderbufferResource(UniqueRenderbuffer renderbuffer_) + : renderbuffer(std::move(renderbuffer_)) { + } + + UniqueRenderbuffer renderbuffer; +}; + +} // namespace gl +} // namespace mbgl diff --git a/platform/gfx/gl/src/mbgl/gl/renderer_backend.cpp b/platform/gfx/gl/src/mbgl/gl/renderer_backend.cpp new file mode 100644 index 0000000000..fe0ca4b5b2 --- /dev/null +++ b/platform/gfx/gl/src/mbgl/gl/renderer_backend.cpp @@ -0,0 +1,69 @@ +#include <mbgl/gl/renderer_backend.hpp> +#include <mbgl/gfx/backend_scope.hpp> +#include <mbgl/gl/context.hpp> +#include <mbgl/gl/extension.hpp> + +#include <cassert> + +namespace mbgl { +namespace gl { + +RendererBackend::RendererBackend(const gfx::ContextMode contextMode_) + : gfx::RendererBackend(contextMode_) { +} + +std::unique_ptr<gfx::Context> RendererBackend::createContext() { + auto result = std::make_unique<gl::Context>(*this); + result->enableDebugging(); + result->initializeExtensions( + std::bind(&RendererBackend::getExtensionFunctionPointer, this, std::placeholders::_1)); + // Needs move to placate GCC 4.9 + return std::move(result); +} + +PremultipliedImage RendererBackend::readFramebuffer(const Size& size) { + return getContext<gl::Context>().readFramebuffer<PremultipliedImage>(size); +} + +void RendererBackend::assumeFramebufferBinding(const gl::FramebufferID fbo) { + getContext<gl::Context>().bindFramebuffer.setCurrentValue(fbo); + if (fbo != ImplicitFramebufferBinding) { + assert(gl::value::BindFramebuffer::Get() == getContext<gl::Context>().bindFramebuffer.getCurrentValue()); + } +} + +void RendererBackend::assumeViewport(int32_t x, int32_t y, const Size& size) { + getContext<gl::Context>().viewport.setCurrentValue({ x, y, size }); + assert(gl::value::Viewport::Get() == getContext<gl::Context>().viewport.getCurrentValue()); +} + +void RendererBackend::assumeScissorTest(bool enabled) { + getContext<gl::Context>().scissorTest.setCurrentValue(enabled); + assert(gl::value::ScissorTest::Get() == getContext<gl::Context>().scissorTest.getCurrentValue()); +} + +bool RendererBackend::implicitFramebufferBound() { + return getContext<gl::Context>().bindFramebuffer.getCurrentValue() == ImplicitFramebufferBinding; +} + +void RendererBackend::setFramebufferBinding(const gl::FramebufferID fbo) { + getContext<gl::Context>().bindFramebuffer = fbo; + if (fbo != ImplicitFramebufferBinding) { + assert(gl::value::BindFramebuffer::Get() == getContext<gl::Context>().bindFramebuffer.getCurrentValue()); + } +} + +void RendererBackend::setViewport(int32_t x, int32_t y, const Size& size) { + getContext<gl::Context>().viewport = { x, y, size }; + assert(gl::value::Viewport::Get() == getContext<gl::Context>().viewport.getCurrentValue()); +} + +void RendererBackend::setScissorTest(bool enabled) { + getContext<gl::Context>().scissorTest = enabled; + assert(gl::value::ScissorTest::Get() == getContext<gl::Context>().scissorTest.getCurrentValue()); +} + +RendererBackend::~RendererBackend() = default; + +} // namespace gl +} // namespace mbgl diff --git a/platform/gfx/gl/src/mbgl/gl/state.hpp b/platform/gfx/gl/src/mbgl/gl/state.hpp new file mode 100644 index 0000000000..17503cc043 --- /dev/null +++ b/platform/gfx/gl/src/mbgl/gl/state.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include <tuple> + +namespace mbgl { +namespace gl { + +// Wraps a piece of OpenGL state and remember its value to avoid redundant state calls. +// Wrapped types need to implement to the Value class interface: +// +// class Value { +// using Type = ...; +// static const constexpr Type Default = ...; +// static void Set(const Type& value); +// static Type Get(); +// }; +template <typename T, typename... Args> +class State { +public: + State(Args&&... args) : params(std::forward_as_tuple(::std::forward<Args>(args)...)) { + } + + void operator=(const typename T::Type& value) { + if (*this != value) { + setCurrentValue(value); + set(std::index_sequence_for<Args...>{}); + } + } + + bool operator==(const typename T::Type& value) const { + return !(*this != value); + } + + bool operator!=(const typename T::Type& value) const { + return dirty || currentValue != value; + } + + void setCurrentValue(const typename T::Type& value) { + dirty = false; + currentValue = value; + } + + // Mark the state as dirty. This means that the next time we are assigning a value to this + // piece of OpenGL state will always result in an actual OpenGL call. + void setDirty() { + dirty = true; + } + + typename T::Type getCurrentValue() const { + return currentValue; + } + + bool isDirty() const { + return dirty; + } + +private: + template <std::size_t... I> + void set(std::index_sequence<I...>) { + T::Set(currentValue, std::get<I>(params)...); + } + +private: + typename T::Type currentValue = T::Default; + bool dirty = true; + const std::tuple<Args...> params; +}; + +} // namespace gl +} // namespace mbgl diff --git a/platform/gfx/gl/src/mbgl/gl/texture.cpp b/platform/gfx/gl/src/mbgl/gl/texture.cpp new file mode 100644 index 0000000000..ef4d083677 --- /dev/null +++ b/platform/gfx/gl/src/mbgl/gl/texture.cpp @@ -0,0 +1,54 @@ +#include <mbgl/gl/texture.hpp> +#include <mbgl/gl/context.hpp> +#include <mbgl/gl/texture_resource.hpp> +#include <mbgl/gl/defines.hpp> + +namespace mbgl { +namespace gl { + +using namespace platform; + +void bindTexture(gl::Context& context, const uint8_t unit, const gfx::TextureBinding& binding) { + auto& resource = static_cast<gl::TextureResource&>(*binding.resource); + if (binding.filter != resource.filter || binding.mipmap != resource.mipmap || + binding.wrapX != resource.wrapX || binding.wrapY != resource.wrapY) { + context.activeTextureUnit = unit; + context.texture[unit] = resource.texture; + + if (binding.filter != resource.filter || binding.mipmap != resource.mipmap) { + MBGL_CHECK_ERROR(glTexParameteri( + GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, + binding.filter == gfx::TextureFilterType::Linear + ? (binding.mipmap == gfx::TextureMipMapType::Yes ? GL_LINEAR_MIPMAP_NEAREST + : GL_LINEAR) + : (binding.mipmap == gfx::TextureMipMapType::Yes ? GL_NEAREST_MIPMAP_NEAREST + : GL_NEAREST))); + MBGL_CHECK_ERROR(glTexParameteri( + GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, + binding.filter == gfx::TextureFilterType::Linear ? GL_LINEAR : GL_NEAREST)); + resource.filter = binding.filter; + resource.mipmap = binding.mipmap; + } + if (binding.wrapX != resource.wrapX) { + + MBGL_CHECK_ERROR(glTexParameteri( + GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, + binding.wrapX == gfx::TextureWrapType::Clamp ? GL_CLAMP_TO_EDGE : GL_REPEAT)); + resource.wrapX = binding.wrapX; + } + if (binding.wrapY != resource.wrapY) { + MBGL_CHECK_ERROR(glTexParameteri( + GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, + binding.wrapY == gfx::TextureWrapType::Clamp ? GL_CLAMP_TO_EDGE : GL_REPEAT)); + resource.wrapY = binding.wrapY; + } + } else if (context.texture[unit] != resource.texture) { + // We are checking first to avoid setting the active texture without a subsequent + // texture bind. + context.activeTextureUnit = unit; + context.texture[unit] = resource.texture; + } +} + +} // namespace gl +} // namespace mbgl diff --git a/platform/gfx/gl/src/mbgl/gl/texture.hpp b/platform/gfx/gl/src/mbgl/gl/texture.hpp new file mode 100644 index 0000000000..74a314d2a7 --- /dev/null +++ b/platform/gfx/gl/src/mbgl/gl/texture.hpp @@ -0,0 +1,49 @@ +#pragma once + +#include <mbgl/gfx/texture.hpp> +#include <mbgl/gl/uniform.hpp> +#include <mbgl/util/literal.hpp> +#include <mbgl/util/ignore.hpp> + +#include <vector> +#include <string> + +namespace mbgl { +namespace gl { + +class Context; + +void bindTexture(gl::Context&, uint8_t unit, const gfx::TextureBinding&); + +template <class> +class TextureStates; + +template <class... Ts> +class TextureStates<TypeList<Ts...>> { +private: + using State = + IndexedTuple<TypeList<Ts...>, TypeList<ExpandToType<Ts, gl::UniformState<uint8_t>>...>>; + + State state; + +public: + void queryLocations(const ProgramID& id) { + state = State{ gl::uniformLocation(id, + concat_literals<&string_literal<'u', '_'>::value, &Ts::name>::value())... }; + } + + NamedUniformLocations getNamedLocations() const { + return NamedUniformLocations{ { concat_literals<&string_literal<'u', '_'>::value, &Ts::name>::value(), + state.template get<Ts>().location }... }; + } + + void bind(gl::Context& context, const gfx::TextureBindings<TypeList<Ts...>>& bindings) { + util::ignore( + { (state.template get<Ts>() = TypeIndex<Ts, Ts...>::value, + gl::bindTexture(context, TypeIndex<Ts, Ts...>::value, bindings.template get<Ts>()), + 0)... }); + } +}; + +} // namespace gl +} // namespace mbgl diff --git a/platform/gfx/gl/src/mbgl/gl/texture_resource.hpp b/platform/gfx/gl/src/mbgl/gl/texture_resource.hpp new file mode 100644 index 0000000000..ed742e75b7 --- /dev/null +++ b/platform/gfx/gl/src/mbgl/gl/texture_resource.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include <mbgl/gfx/texture.hpp> +#include <mbgl/gl/object.hpp> + +namespace mbgl { +namespace gl { + +class TextureResource : public gfx::TextureResource { +public: + TextureResource(UniqueTexture&& texture_) : texture(std::move(texture_)) { + } + + UniqueTexture texture; + gfx::TextureFilterType filter = gfx::TextureFilterType::Nearest; + gfx::TextureMipMapType mipmap = gfx::TextureMipMapType::No; + gfx::TextureWrapType wrapX = gfx::TextureWrapType::Clamp; + gfx::TextureWrapType wrapY = gfx::TextureWrapType::Clamp; +}; + +} // namespace gl +} // namespace mbgl diff --git a/platform/gfx/gl/src/mbgl/gl/types.hpp b/platform/gfx/gl/src/mbgl/gl/types.hpp new file mode 100644 index 0000000000..e679d7646b --- /dev/null +++ b/platform/gfx/gl/src/mbgl/gl/types.hpp @@ -0,0 +1,62 @@ +#pragma once + +#include <cstdint> +#include <type_traits> + +namespace mbgl { +namespace gl { + +// Mapping based on https://www.opengl.org/wiki/OpenGL_Type +using ProgramID = uint32_t; +using ShaderID = uint32_t; +using BufferID = uint32_t; +using TextureID = uint32_t; +using VertexArrayID = uint32_t; +using FramebufferID = uint32_t; +using RenderbufferID = uint32_t; + +// OpenGL does not formally define a type for attribute locations, but most APIs use +// GLuint. The exception is glGetAttribLocation, which returns GLint so that -1 can +// be used as an error indicator. +using AttributeLocation = uint32_t; + +// OpenGL does not formally define a type for uniform locations, but all APIs use GLint. +// The value -1 is special, typically used as a placeholder for an unused uniform and +// "silently ignored". +using UniformLocation = int32_t; + +enum class ShaderType : uint32_t { + Vertex = 0x8B31, + Fragment = 0x8B30 +}; + +struct PixelStorageType { + int32_t alignment; +}; + +constexpr bool operator!=(const PixelStorageType& a, const PixelStorageType& b) { + return a.alignment != b.alignment; +} + +enum class UniformDataType : uint32_t { + Float = 0x1406, + FloatVec2 = 0x8B50, + FloatVec3 = 0x8B51, + FloatVec4 = 0x8B52, + Int = 0x1404, + IntVec2 = 0x8B53, + IntVec3 = 0x8B54, + IntVec4 = 0x8B55, + Bool = 0x8B56, + BoolVec2 = 0x8B57, + BoolVec3 = 0x8B58, + BoolVec4 = 0x8B59, + FloatMat2 = 0x8B5A, + FloatMat3 = 0x8B5B, + FloatMat4 = 0x8B5C, + Sampler2D = 0x8B5E, + SamplerCube = 0x8B60, +}; + +} // namespace gl +} // namespace mbgl diff --git a/platform/gfx/gl/src/mbgl/gl/uniform.cpp b/platform/gfx/gl/src/mbgl/gl/uniform.cpp new file mode 100644 index 0000000000..8d99d4b467 --- /dev/null +++ b/platform/gfx/gl/src/mbgl/gl/uniform.cpp @@ -0,0 +1,202 @@ +#include <mbgl/gl/uniform.hpp> +#include <mbgl/gl/defines.hpp> +#include <mbgl/platform/gl_functions.hpp> +#include <mbgl/util/color.hpp> +#include <mbgl/util/size.hpp> +#include <mbgl/util/convert.hpp> + +#include <memory> + +namespace mbgl { +namespace gl { + +using namespace platform; + +UniformLocation uniformLocation(ProgramID id, const char* name) { + return MBGL_CHECK_ERROR(glGetUniformLocation(id, name)); +} + +template <> +void bindUniform<float>(UniformLocation location, const float& t) { + MBGL_CHECK_ERROR(glUniform1f(location, t)); +} + +template <> +void bindUniform<int32_t>(UniformLocation location, const int32_t& t) { + MBGL_CHECK_ERROR(glUniform1i(location, t)); +} + +template <> +void bindUniform<std::array<float, 2>>(UniformLocation location, const std::array<float, 2>& t) { + MBGL_CHECK_ERROR(glUniform2fv(location, 1, t.data())); +} + +template <> +void bindUniform<std::array<float, 3>>(UniformLocation location, const std::array<float, 3>& t) { + MBGL_CHECK_ERROR(glUniform3fv(location, 1, t.data())); +} + +template <> +void bindUniform<std::array<float, 4>>(UniformLocation location, const std::array<float, 4>& t) { + MBGL_CHECK_ERROR(glUniform4fv(location, 1, t.data())); +} + +template <> +void bindUniform<std::array<double, 4>>(UniformLocation location, const std::array<double, 4>& t) { + MBGL_CHECK_ERROR(glUniformMatrix2fv(location, 1, GL_FALSE, util::convert<float>(t).data())); +} + +template <> +void bindUniform<std::array<double, 9>>(UniformLocation location, const std::array<double, 9>& t) { + MBGL_CHECK_ERROR(glUniformMatrix3fv(location, 1, GL_FALSE, util::convert<float>(t).data())); +} + +template <> +void bindUniform<std::array<double, 16>>(UniformLocation location, const std::array<double, 16>& t) { + MBGL_CHECK_ERROR(glUniformMatrix4fv(location, 1, GL_FALSE, util::convert<float>(t).data())); +} + + +template <> +void bindUniform<bool>(UniformLocation location, const bool& t) { + return bindUniform(location, int32_t(t)); +} + +template <> +void bindUniform<uint32_t>(UniformLocation location, const uint32_t& t) { + bindUniform(location, int32_t(t)); +} + +template <> +void bindUniform<uint8_t>(UniformLocation location, const uint8_t& t) { + bindUniform(location, int32_t(t)); +} + +template <> +void bindUniform<Color>(UniformLocation location, const Color& t) { + bindUniform(location, std::array<float, 4> {{ t.r, t.g, t.b, t.a }}); +} + +template <> +void bindUniform<Size>(UniformLocation location, const Size& t) { + bindUniform(location, util::convert<float>(std::array<uint32_t, 2> {{ t.width, t.height }})); +} + +template <> +void bindUniform<std::array<uint16_t, 2>>(UniformLocation location, const std::array<uint16_t, 2>& t) { + bindUniform(location, util::convert<float>(t)); +} + +template <> +void bindUniform<std::array<uint16_t, 4>>(UniformLocation location, const std::array<uint16_t, 4>& t) { + bindUniform(location, util::convert<float>(t)); +} + +// Add more as needed. + +#ifndef NDEBUG + +ActiveUniforms activeUniforms(ProgramID id) { + ActiveUniforms active; + + GLint count; + GLint maxLength; + MBGL_CHECK_ERROR(glGetProgramiv(id, GL_ACTIVE_UNIFORMS, &count)); + MBGL_CHECK_ERROR(glGetProgramiv(id, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxLength)); + + auto name = std::make_unique<GLchar[]>(maxLength); + GLsizei length; + GLint size; + GLenum type; + for (GLint index = 0; index < count; index++) { + MBGL_CHECK_ERROR(glGetActiveUniform(id, index, maxLength, &length, &size, &type, name.get())); + active.emplace( + std::string{ name.get(), static_cast<size_t>(length) }, + ActiveUniform{ static_cast<size_t>(size), static_cast<UniformDataType>(type) }); + } + + return active; +} + +template <> +bool verifyUniform<float>(const ActiveUniform& uniform) { + assert(uniform.size == 1 && uniform.type == UniformDataType::Float); + return true; +} + +template <> +bool verifyUniform<std::array<float, 2>>(const ActiveUniform& uniform) { + assert(uniform.size == 1 && uniform.type == UniformDataType::FloatVec2); + return true; +} + +template <> +bool verifyUniform<std::array<float, 3>>(const ActiveUniform& uniform) { + assert(uniform.size == 1 && uniform.type == UniformDataType::FloatVec3); + return true; +} + +template <> +bool verifyUniform<std::array<float, 4>>(const ActiveUniform& uniform) { + assert(uniform.size == 1 && uniform.type == UniformDataType::FloatVec4); + return true; +} + +template <> +bool verifyUniform<std::array<double, 16>>(const ActiveUniform& uniform) { + assert(uniform.size == 1 && uniform.type == UniformDataType::FloatMat4); + return true; +} + +template <> +bool verifyUniform<bool>(const ActiveUniform& uniform) { + assert(uniform.size == 1 && + (uniform.type == UniformDataType::Bool || + uniform.type == UniformDataType::Int || + uniform.type == UniformDataType::Float)); + return true; +} + +template <> +bool verifyUniform<uint32_t>(const ActiveUniform& uniform) { + assert(uniform.size == 1 && + (uniform.type == UniformDataType::Int || + uniform.type == UniformDataType::Float || + uniform.type == UniformDataType::Sampler2D)); + return true; +} + +template <> +bool verifyUniform<Color>(const ActiveUniform& uniform) { + assert(uniform.size == 1 && uniform.type == UniformDataType::FloatVec4); + return true; +} + +template <> +bool verifyUniform<Size>(const ActiveUniform& uniform) { + assert(uniform.size == 1 && uniform.type == UniformDataType::FloatVec2); + return true; +} + +template <> +bool verifyUniform<std::array<uint16_t, 2>>(const ActiveUniform& uniform) { + assert(uniform.size == 1 && + (uniform.type == UniformDataType::IntVec2 || + uniform.type == UniformDataType::FloatVec2)); + return true; +} + +template <> +bool verifyUniform<std::array<uint16_t, 4>>(const ActiveUniform& uniform) { + assert(uniform.size == 1 && + (uniform.type == UniformDataType::IntVec4 || + uniform.type == UniformDataType::FloatVec4)); + return true; +} + +// Add more as needed. + +#endif + +} // namespace gl +} // namespace mbgl diff --git a/platform/gfx/gl/src/mbgl/gl/uniform.hpp b/platform/gfx/gl/src/mbgl/gl/uniform.hpp new file mode 100644 index 0000000000..10501036cb --- /dev/null +++ b/platform/gfx/gl/src/mbgl/gl/uniform.hpp @@ -0,0 +1,95 @@ +#pragma once + +#include <mbgl/gfx/uniform.hpp> +#include <mbgl/gl/types.hpp> +#include <mbgl/util/optional.hpp> +#include <mbgl/util/literal.hpp> +#include <mbgl/util/ignore.hpp> +#include <mbgl/util/indexed_tuple.hpp> + +#include <array> +#include <vector> +#include <map> +#include <functional> + +namespace mbgl { +namespace gl { + +template <class T> +void bindUniform(UniformLocation, const T&); + +class ActiveUniform { +public: + std::size_t size; + UniformDataType type; +}; + +#ifndef NDEBUG + +template <class T> +bool verifyUniform(const ActiveUniform&); + +using ActiveUniforms = std::map<std::string, ActiveUniform>; +ActiveUniforms activeUniforms(ProgramID); + +#endif + +template <class Value> +class UniformState { +public: + UniformState(UniformLocation location_ = -1) : location(std::move(location_)) { + } + + void operator=(const Value& value) { + if (location >= 0 && (!current || *current != value)) { + current = value; + bindUniform(location, value); + } + } + + UniformLocation location; + optional<Value> current = {}; +}; + +UniformLocation uniformLocation(ProgramID, const char* name); + +using NamedUniformLocations = std::vector<std::pair<const std::string, UniformLocation>>; + +template <class> +class UniformStates; + +template <class... Us> +class UniformStates<TypeList<Us...>> final { +private: + using State = IndexedTuple<TypeList<Us...>, TypeList<UniformState<typename Us::Value>...>>; + + State state; + +public: + void queryLocations(const ProgramID& id) { +#ifndef NDEBUG + // Verify active uniform types match the enum + const auto active = gl::activeUniforms(id); + + util::ignore( + { // Some shader programs have uniforms declared, but not used, so they're not active. + // Therefore, we'll only verify them when they are indeed active. + (active.find(concat_literals<&string_literal<'u', '_'>::value, &Us::name>::value()) != active.end() + ? verifyUniform<typename Us::Value>(active.at(concat_literals<&string_literal<'u', '_'>::value, &Us::name>::value())) + : false)... }); +#endif + + state = State{ gl::uniformLocation(id, concat_literals<&string_literal<'u', '_'>::value, &Us::name>::value())... }; + } + + NamedUniformLocations getNamedLocations() const { + return NamedUniformLocations{ { concat_literals<&string_literal<'u', '_'>::value, &Us::name>::value(), state.template get<Us>().location }... }; + } + + void bind(const gfx::UniformValues<TypeList<Us...>>& values) { + util::ignore({ (state.template get<Us>() = values.template get<Us>(), 0)... }); + } +}; + +} // namespace gl +} // namespace mbgl diff --git a/platform/gfx/gl/src/mbgl/gl/upload_pass.cpp b/platform/gfx/gl/src/mbgl/gl/upload_pass.cpp new file mode 100644 index 0000000000..358f1a7203 --- /dev/null +++ b/platform/gfx/gl/src/mbgl/gl/upload_pass.cpp @@ -0,0 +1,117 @@ +#include <mbgl/gl/upload_pass.hpp> +#include <mbgl/gl/context.hpp> +#include <mbgl/gl/enum.hpp> +#include <mbgl/gl/defines.hpp> +#include <mbgl/gl/command_encoder.hpp> +#include <mbgl/gl/vertex_buffer_resource.hpp> +#include <mbgl/gl/index_buffer_resource.hpp> +#include <mbgl/gl/texture_resource.hpp> + +namespace mbgl { +namespace gl { + +using namespace platform; + +UploadPass::UploadPass(gl::CommandEncoder& commandEncoder_, const char* name) + : commandEncoder(commandEncoder_), debugGroup(commandEncoder.createDebugGroup(name)) { +} + +std::unique_ptr<gfx::VertexBufferResource> UploadPass::createVertexBufferResource( + const void* data, std::size_t size, const gfx::BufferUsageType usage) { + BufferID id = 0; + MBGL_CHECK_ERROR(glGenBuffers(1, &id)); + UniqueBuffer result{ std::move(id), { commandEncoder.context } }; + commandEncoder.context.vertexBuffer = result; + MBGL_CHECK_ERROR( + glBufferData(GL_ARRAY_BUFFER, size, data, Enum<gfx::BufferUsageType>::to(usage))); + return std::make_unique<gl::VertexBufferResource>(std::move(result)); +} + +void UploadPass::updateVertexBufferResource(gfx::VertexBufferResource& resource, + const void* data, + std::size_t size) { + commandEncoder.context.vertexBuffer = static_cast<gl::VertexBufferResource&>(resource).buffer; + MBGL_CHECK_ERROR(glBufferSubData(GL_ARRAY_BUFFER, 0, size, data)); +} + +std::unique_ptr<gfx::IndexBufferResource> UploadPass::createIndexBufferResource( + const void* data, std::size_t size, const gfx::BufferUsageType usage) { + BufferID id = 0; + MBGL_CHECK_ERROR(glGenBuffers(1, &id)); + UniqueBuffer result{ std::move(id), { commandEncoder.context } }; + commandEncoder.context.bindVertexArray = 0; + commandEncoder.context.globalVertexArrayState.indexBuffer = result; + MBGL_CHECK_ERROR( + glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, data, Enum<gfx::BufferUsageType>::to(usage))); + return std::make_unique<gl::IndexBufferResource>(std::move(result)); +} + +void UploadPass::updateIndexBufferResource(gfx::IndexBufferResource& resource, + const void* data, + std::size_t size) { + // Be sure to unbind any existing vertex array object before binding the index buffer + // so that we don't mess up another VAO + commandEncoder.context.bindVertexArray = 0; + commandEncoder.context.globalVertexArrayState.indexBuffer = + static_cast<gl::IndexBufferResource&>(resource).buffer; + MBGL_CHECK_ERROR(glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, size, data)); +} + +std::unique_ptr<gfx::TextureResource> +UploadPass::createTextureResource(const Size size, + const void* data, + gfx::TexturePixelType format, + gfx::TextureChannelDataType type) { + auto obj = commandEncoder.context.createUniqueTexture(); + std::unique_ptr<gfx::TextureResource> resource = + std::make_unique<gl::TextureResource>(std::move(obj)); + commandEncoder.context.pixelStoreUnpack = { 1 }; + updateTextureResource(*resource, size, data, format, type); + // 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; +} + +void UploadPass::updateTextureResource(gfx::TextureResource& resource, + const Size size, + const void* data, + gfx::TexturePixelType format, + gfx::TextureChannelDataType type) { + // Always use texture unit 0 for manipulating it. + commandEncoder.context.activeTextureUnit = 0; + commandEncoder.context.texture[0] = static_cast<gl::TextureResource&>(resource).texture; + MBGL_CHECK_ERROR(glTexImage2D(GL_TEXTURE_2D, 0, Enum<gfx::TexturePixelType>::to(format), + size.width, size.height, 0, + Enum<gfx::TexturePixelType>::to(format), + Enum<gfx::TextureChannelDataType>::to(type), data)); +} + +void UploadPass::updateTextureResourceSub(gfx::TextureResource& resource, + const uint16_t xOffset, + const uint16_t yOffset, + const Size size, + const void* data, + gfx::TexturePixelType format, + gfx::TextureChannelDataType type) { + // Always use texture unit 0 for manipulating it. + commandEncoder.context.activeTextureUnit = 0; + commandEncoder.context.texture[0] = static_cast<const gl::TextureResource&>(resource).texture; + MBGL_CHECK_ERROR(glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, size.width, size.height, + Enum<gfx::TexturePixelType>::to(format), + Enum<gfx::TextureChannelDataType>::to(type), data)); +} + +void UploadPass::pushDebugGroup(const char* name) { + commandEncoder.pushDebugGroup(name); +} + +void UploadPass::popDebugGroup() { + commandEncoder.popDebugGroup(); +} + +} // namespace gl +} // namespace mbgl diff --git a/platform/gfx/gl/src/mbgl/gl/upload_pass.hpp b/platform/gfx/gl/src/mbgl/gl/upload_pass.hpp new file mode 100644 index 0000000000..8b4f71b88d --- /dev/null +++ b/platform/gfx/gl/src/mbgl/gl/upload_pass.hpp @@ -0,0 +1,42 @@ +#pragma once + +#include <mbgl/gfx/upload_pass.hpp> + +namespace mbgl { +namespace gfx { + +class CommandEncoder; + +} // namespace gfx + +namespace gl { + +class CommandEncoder; +class Context; + +class UploadPass final : public gfx::UploadPass { +public: + UploadPass(gl::CommandEncoder&, const char* name); + +private: + void pushDebugGroup(const char* name) override; + void popDebugGroup() override; + +public: + std::unique_ptr<gfx::VertexBufferResource> createVertexBufferResource(const void* data, std::size_t size, const gfx::BufferUsageType) override; + void updateVertexBufferResource(gfx::VertexBufferResource&, const void* data, std::size_t size) override; + std::unique_ptr<gfx::IndexBufferResource> createIndexBufferResource(const void* data, std::size_t size, const gfx::BufferUsageType) override; + void updateIndexBufferResource(gfx::IndexBufferResource&, const void* data, std::size_t size) override; + +public: + std::unique_ptr<gfx::TextureResource> createTextureResource(Size, const void* data, gfx::TexturePixelType, gfx::TextureChannelDataType) override; + void updateTextureResource(gfx::TextureResource&, Size, const void* data, gfx::TexturePixelType, gfx::TextureChannelDataType) override; + void updateTextureResourceSub(gfx::TextureResource&, const uint16_t xOffset, const uint16_t yOffset, Size, const void* data, gfx::TexturePixelType, gfx::TextureChannelDataType) override; + +private: + gl::CommandEncoder& commandEncoder; + const gfx::DebugGroup<gfx::CommandEncoder> debugGroup; +}; + +} // namespace gl +} // namespace mbgl diff --git a/platform/gfx/gl/src/mbgl/gl/value.cpp b/platform/gfx/gl/src/mbgl/gl/value.cpp new file mode 100644 index 0000000000..bd08ac48fc --- /dev/null +++ b/platform/gfx/gl/src/mbgl/gl/value.cpp @@ -0,0 +1,602 @@ +#include <mbgl/gl/value.hpp> +#include <mbgl/gl/context.hpp> +#include <mbgl/gl/vertex_buffer_resource.hpp> +#include <mbgl/gl/vertex_array_extension.hpp> +#include <mbgl/gl/enum.hpp> + +namespace mbgl { +namespace gl { +namespace value { + +using namespace platform; + +const constexpr ClearDepth::Type ClearDepth::Default; + +void ClearDepth::Set(const Type& value) { + MBGL_CHECK_ERROR(glClearDepthf(value)); +} + +ClearDepth::Type ClearDepth::Get() { + GLfloat clearDepth; + MBGL_CHECK_ERROR(glGetFloatv(GL_DEPTH_CLEAR_VALUE, &clearDepth)); + return clearDepth; +} + +const ClearColor::Type ClearColor::Default { 0, 0, 0, 0 }; + +void ClearColor::Set(const Type& value) { + MBGL_CHECK_ERROR(glClearColor(value.r, value.g, value.b, value.a)); +} + +ClearColor::Type ClearColor::Get() { + GLfloat clearColor[4]; + MBGL_CHECK_ERROR(glGetFloatv(GL_COLOR_CLEAR_VALUE, clearColor)); + return { clearColor[0], clearColor[1], clearColor[2], clearColor[3] }; +} + +const constexpr ClearStencil::Type ClearStencil::Default; + +void ClearStencil::Set(const Type& value) { + MBGL_CHECK_ERROR(glClearStencil(value)); +} + +ClearStencil::Type ClearStencil::Get() { + GLint clearStencil; + MBGL_CHECK_ERROR(glGetIntegerv(GL_STENCIL_CLEAR_VALUE, &clearStencil)); + return clearStencil; +} + +const constexpr StencilMask::Type StencilMask::Default; + +void StencilMask::Set(const Type& value) { + MBGL_CHECK_ERROR(glStencilMask(value)); +} + +StencilMask::Type StencilMask::Get() { + GLint stencilMask; + MBGL_CHECK_ERROR(glGetIntegerv(GL_STENCIL_WRITEMASK, &stencilMask)); + return stencilMask; +} + +const constexpr DepthMask::Type DepthMask::Default; + +void DepthMask::Set(const Type& value) { + MBGL_CHECK_ERROR(glDepthMask(Enum<gfx::DepthMaskType>::to(value))); +} + +DepthMask::Type DepthMask::Get() { + GLboolean depthMask; + MBGL_CHECK_ERROR(glGetBooleanv(GL_DEPTH_WRITEMASK, &depthMask)); + return Enum<gfx::DepthMaskType>::from(depthMask); +} + +const constexpr ColorMask::Type ColorMask::Default; + +void ColorMask::Set(const Type& value) { + MBGL_CHECK_ERROR(glColorMask(value.r, value.g, value.b, value.a)); +} + +ColorMask::Type ColorMask::Get() { + GLboolean bools[4]; + MBGL_CHECK_ERROR(glGetBooleanv(GL_COLOR_WRITEMASK, bools)); + return { static_cast<bool>(bools[0]), static_cast<bool>(bools[1]), static_cast<bool>(bools[2]), + static_cast<bool>(bools[3]) }; +} + +const constexpr StencilFunc::Type StencilFunc::Default; + +void StencilFunc::Set(const Type& value) { + MBGL_CHECK_ERROR(glStencilFunc(Enum<gfx::StencilFunctionType>::to(value.func), value.ref, value.mask)); +} + +StencilFunc::Type StencilFunc::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 { Enum<gfx::StencilFunctionType>::from(func), ref, static_cast<uint32_t>(mask) }; +} + +const constexpr StencilTest::Type StencilTest::Default; + +void StencilTest::Set(const Type& value) { + MBGL_CHECK_ERROR(value ? glEnable(GL_STENCIL_TEST) : glDisable(GL_STENCIL_TEST)); +} + +StencilTest::Type StencilTest::Get() { + Type stencilTest; + MBGL_CHECK_ERROR(stencilTest = glIsEnabled(GL_STENCIL_TEST)); + return stencilTest; +} + +const constexpr StencilOp::Type StencilOp::Default; + +void StencilOp::Set(const Type& value) { + MBGL_CHECK_ERROR(glStencilOp(Enum<gfx::StencilOpType>::to(value.sfail), + Enum<gfx::StencilOpType>::to(value.dpfail), + Enum<gfx::StencilOpType>::to(value.dppass))); +} + +StencilOp::Type StencilOp::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 { Enum<gfx::StencilOpType>::from(sfail), + Enum<gfx::StencilOpType>::from(dpfail), + Enum<gfx::StencilOpType>::from(dppass) }; +} + +const constexpr DepthRange::Type DepthRange::Default; + +void DepthRange::Set(const Type& value) { + MBGL_CHECK_ERROR(glDepthRangef(value.min, value.max)); +} + +DepthRange::Type DepthRange::Get() { + GLfloat floats[2]; + MBGL_CHECK_ERROR(glGetFloatv(GL_DEPTH_RANGE, floats)); + return { floats[0], floats[1] }; +} + +const constexpr DepthTest::Type DepthTest::Default; + +void DepthTest::Set(const Type& value) { + MBGL_CHECK_ERROR(value ? glEnable(GL_DEPTH_TEST) : glDisable(GL_DEPTH_TEST)); +} + +DepthTest::Type DepthTest::Get() { + Type depthTest; + MBGL_CHECK_ERROR(depthTest = glIsEnabled(GL_DEPTH_TEST)); + return depthTest; +} + +const constexpr DepthFunc::Type DepthFunc::Default; + +void DepthFunc::Set(const DepthFunc::Type& value) { + MBGL_CHECK_ERROR(glDepthFunc(Enum<gfx::DepthFunctionType>::to(value))); +} + +DepthFunc::Type DepthFunc::Get() { + GLint depthFunc; + MBGL_CHECK_ERROR(glGetIntegerv(GL_DEPTH_FUNC, &depthFunc)); + return Enum<gfx::DepthFunctionType>::from(depthFunc); +} + +const constexpr Blend::Type Blend::Default; + +void Blend::Set(const Type& value) { + MBGL_CHECK_ERROR(value ? glEnable(GL_BLEND) : glDisable(GL_BLEND)); +} + +Blend::Type Blend::Get() { + Type blend; + MBGL_CHECK_ERROR(blend = glIsEnabled(GL_BLEND)); + return blend; +} + +const constexpr BlendEquation::Type BlendEquation::Default; + +void BlendEquation::Set(const Type& value) { + MBGL_CHECK_ERROR(glBlendEquation(Enum<gfx::ColorBlendEquationType>::to(value))); +} + +BlendEquation::Type BlendEquation::Get() { + GLint blend; + MBGL_CHECK_ERROR(glGetIntegerv(GL_BLEND_EQUATION_RGB, &blend)); + return Enum<gfx::ColorBlendEquationType>::from(blend); +} + +const constexpr BlendFunc::Type BlendFunc::Default; + +void BlendFunc::Set(const Type& value) { + MBGL_CHECK_ERROR(glBlendFunc(Enum<gfx::ColorBlendFactorType>::to(value.sfactor), + Enum<gfx::ColorBlendFactorType>::to(value.dfactor))); +} + +BlendFunc::Type BlendFunc::Get() { + GLint sfactor, dfactor; + MBGL_CHECK_ERROR(glGetIntegerv(GL_BLEND_SRC_ALPHA, &sfactor)); + MBGL_CHECK_ERROR(glGetIntegerv(GL_BLEND_DST_ALPHA, &dfactor)); + return { Enum<gfx::ColorBlendFactorType>::from(sfactor), + Enum<gfx::ColorBlendFactorType>::from(dfactor) }; +} + +const BlendColor::Type BlendColor::Default { 0, 0, 0, 0 }; + +void BlendColor::Set(const Type& value) { + MBGL_CHECK_ERROR(glBlendColor(value.r, value.g, value.b, value.a)); +} + +BlendColor::Type BlendColor::Get() { + GLfloat floats[4]; + MBGL_CHECK_ERROR(glGetFloatv(GL_BLEND_COLOR, floats)); + return { floats[0], floats[1], floats[2], floats[3] }; +} + +const constexpr Program::Type Program::Default; + +void Program::Set(const Type& value) { + MBGL_CHECK_ERROR(glUseProgram(value)); +} + +Program::Type Program::Get() { + GLint program; + MBGL_CHECK_ERROR(glGetIntegerv(GL_CURRENT_PROGRAM, &program)); + return program; +} + +const constexpr LineWidth::Type LineWidth::Default; + +void LineWidth::Set(const Type& value) { + MBGL_CHECK_ERROR(glLineWidth(value)); +} + +LineWidth::Type LineWidth::Get() { + GLfloat lineWidth; + MBGL_CHECK_ERROR(glGetFloatv(GL_LINE_WIDTH, &lineWidth)); + return lineWidth; +} + +const constexpr ActiveTextureUnit::Type ActiveTextureUnit::Default; + +void ActiveTextureUnit::Set(const Type& value) { + MBGL_CHECK_ERROR(glActiveTexture(GL_TEXTURE0 + value)); +} + +ActiveTextureUnit::Type ActiveTextureUnit::Get() { + GLint activeTexture; + MBGL_CHECK_ERROR(glGetIntegerv(GL_ACTIVE_TEXTURE, &activeTexture)); + return static_cast<Type>(activeTexture - GL_TEXTURE0); +} + +const constexpr Viewport::Type Viewport::Default; + +void Viewport::Set(const Type& value) { + MBGL_CHECK_ERROR(glViewport(value.x, value.y, value.size.width, value.size.height)); +} + +Viewport::Type Viewport::Get() { + GLint viewport[4]; + MBGL_CHECK_ERROR(glGetIntegerv(GL_VIEWPORT, viewport)); + return { static_cast<int32_t>(viewport[0]), static_cast<int32_t>(viewport[1]), + { static_cast<uint32_t>(viewport[2]), static_cast<uint32_t>(viewport[3]) } }; +} + +const constexpr ScissorTest::Type ScissorTest::Default; + +void ScissorTest::Set(const Type& value) { + MBGL_CHECK_ERROR(value ? glEnable(GL_SCISSOR_TEST) : glDisable(GL_SCISSOR_TEST)); +} + +ScissorTest::Type ScissorTest::Get() { + Type scissorTest; + MBGL_CHECK_ERROR(scissorTest = glIsEnabled(GL_SCISSOR_TEST)); + return scissorTest; +} + +const constexpr BindFramebuffer::Type BindFramebuffer::Default; + +void BindFramebuffer::Set(const Type& value) { + MBGL_CHECK_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, value)); +} + +BindFramebuffer::Type BindFramebuffer::Get() { + GLint binding; + MBGL_CHECK_ERROR(glGetIntegerv(GL_FRAMEBUFFER_BINDING, &binding)); + return binding; +} + +const constexpr BindRenderbuffer::Type BindRenderbuffer::Default; + +void BindRenderbuffer::Set(const Type& value) { + MBGL_CHECK_ERROR(glBindRenderbuffer(GL_RENDERBUFFER, value)); +} + +BindRenderbuffer::Type BindRenderbuffer::Get() { + GLint binding; + MBGL_CHECK_ERROR(glGetIntegerv(GL_RENDERBUFFER_BINDING, &binding)); + return binding; +} + +const constexpr CullFace::Type CullFace::Default; + +void CullFace::Set(const Type& value) { + MBGL_CHECK_ERROR(value ? glEnable(GL_CULL_FACE) : glDisable(GL_CULL_FACE)); +} + +CullFace::Type CullFace::Get() { + GLboolean cullFace; + MBGL_CHECK_ERROR(cullFace = glIsEnabled(GL_CULL_FACE)); + return cullFace; +} + +const constexpr CullFaceSide::Type CullFaceSide::Default; + +void CullFaceSide::Set(const Type& value) { + MBGL_CHECK_ERROR(glCullFace(Enum<gfx::CullFaceSideType>::to(value))); +} + +CullFaceSide::Type CullFaceSide::Get() { + GLint cullFaceMode; + MBGL_CHECK_ERROR(glGetIntegerv(GL_CULL_FACE_MODE, &cullFaceMode)); + return Enum<gfx::CullFaceSideType>::from(cullFaceMode); +} + +const constexpr CullFaceWinding::Type CullFaceWinding::Default; + +void CullFaceWinding::Set(const Type& value) { + MBGL_CHECK_ERROR(glFrontFace(Enum<gfx::CullFaceWindingType>::to(value))); +} + +CullFaceWinding::Type CullFaceWinding::Get() { + GLint frontFace; + MBGL_CHECK_ERROR(glGetIntegerv(GL_FRONT_FACE, &frontFace)); + return Enum<gfx::CullFaceWindingType>::from(frontFace); +} + +const constexpr BindTexture::Type BindTexture::Default; + +void BindTexture::Set(const Type& value) { + MBGL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, value)); +} + +BindTexture::Type BindTexture::Get() { + GLint binding; + MBGL_CHECK_ERROR(glGetIntegerv(GL_TEXTURE_BINDING_2D, &binding)); + return binding; +} + +const constexpr BindVertexBuffer::Type BindVertexBuffer::Default; + +void BindVertexBuffer::Set(const Type& value) { + MBGL_CHECK_ERROR(glBindBuffer(GL_ARRAY_BUFFER, value)); +} + +BindVertexBuffer::Type BindVertexBuffer::Get() { + GLint binding; + MBGL_CHECK_ERROR(glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &binding)); + return binding; +} + +const constexpr BindElementBuffer::Type BindElementBuffer::Default; + +void BindElementBuffer::Set(const Type& value) { + MBGL_CHECK_ERROR(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, value)); +} + +BindElementBuffer::Type BindElementBuffer::Get() { + GLint binding; + MBGL_CHECK_ERROR(glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &binding)); + return binding; +} + +const constexpr BindVertexArray::Type BindVertexArray::Default; + +void BindVertexArray::Set(const Type& value, const Context& context) { + if (auto vertexArray = context.getVertexArrayExtension()) { + if (vertexArray->bindVertexArray) { + MBGL_CHECK_ERROR(vertexArray->bindVertexArray(value)); + } + } +} + +BindVertexArray::Type BindVertexArray::Get(const Context& context) { + GLint binding = 0; + if (context.getVertexArrayExtension()) { +#ifdef GL_VERTEX_ARRAY_BINDING + MBGL_CHECK_ERROR(glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &binding)); +#elif GL_VERTEX_ARRAY_BINDING_OES + MBGL_CHECK_ERROR(glGetIntegerv(GL_VERTEX_ARRAY_BINDING_OES, &binding)); +#elif GL_VERTEX_ARRAY_BINDING_ARB + MBGL_CHECK_ERROR(glGetIntegerv(GL_VERTEX_ARRAY_BINDING_ARB, &binding)); +#elif GLVERTEX_ARRAY_BINDING_APPLE + MBGL_CHECK_ERROR(glGetIntegerv(GLVERTEX_ARRAY_BINDING_APPLE, &binding)); +#endif + } + return binding; +} + +const VertexAttribute::Type VertexAttribute::Default {}; + +namespace { + +GLenum vertexType(const gfx::AttributeDataType type) { + switch (type) { + case gfx::AttributeDataType::Byte: + case gfx::AttributeDataType::Byte2: + case gfx::AttributeDataType::Byte3: + case gfx::AttributeDataType::Byte4: + return GL_BYTE; + case gfx::AttributeDataType::UByte: + case gfx::AttributeDataType::UByte2: + case gfx::AttributeDataType::UByte3: + case gfx::AttributeDataType::UByte4: + return GL_UNSIGNED_BYTE; + case gfx::AttributeDataType::Short: + case gfx::AttributeDataType::Short2: + case gfx::AttributeDataType::Short3: + case gfx::AttributeDataType::Short4: + return GL_SHORT; + case gfx::AttributeDataType::UShort: + case gfx::AttributeDataType::UShort2: + case gfx::AttributeDataType::UShort3: + case gfx::AttributeDataType::UShort4: + return GL_UNSIGNED_SHORT; + case gfx::AttributeDataType::Int: + case gfx::AttributeDataType::Int2: + case gfx::AttributeDataType::Int3: + case gfx::AttributeDataType::Int4: + return GL_INT; + case gfx::AttributeDataType::UInt: + case gfx::AttributeDataType::UInt2: + case gfx::AttributeDataType::UInt3: + case gfx::AttributeDataType::UInt4: + return GL_UNSIGNED_INT; + case gfx::AttributeDataType::Float: + case gfx::AttributeDataType::Float2: + case gfx::AttributeDataType::Float3: + case gfx::AttributeDataType::Float4: + return GL_FLOAT; + default: + return GL_FLOAT; + } +} + +GLint components(const gfx::AttributeDataType type) { + switch (type) { + case gfx::AttributeDataType::Byte: + case gfx::AttributeDataType::UByte: + case gfx::AttributeDataType::Short: + case gfx::AttributeDataType::UShort: + case gfx::AttributeDataType::Int: + case gfx::AttributeDataType::UInt: + case gfx::AttributeDataType::Float: + return 1; + case gfx::AttributeDataType::Byte2: + case gfx::AttributeDataType::UByte2: + case gfx::AttributeDataType::Short2: + case gfx::AttributeDataType::UShort2: + case gfx::AttributeDataType::Int2: + case gfx::AttributeDataType::UInt2: + case gfx::AttributeDataType::Float2: + return 2; + case gfx::AttributeDataType::Byte3: + case gfx::AttributeDataType::UByte3: + case gfx::AttributeDataType::Short3: + case gfx::AttributeDataType::UShort3: + case gfx::AttributeDataType::Int3: + case gfx::AttributeDataType::UInt3: + case gfx::AttributeDataType::Float3: + return 3; + case gfx::AttributeDataType::Byte4: + case gfx::AttributeDataType::UByte4: + case gfx::AttributeDataType::Short4: + case gfx::AttributeDataType::UShort4: + case gfx::AttributeDataType::Int4: + case gfx::AttributeDataType::UInt4: + case gfx::AttributeDataType::Float4: + return 4; + default: + return 0; + } +} + +} // namespace + +void VertexAttribute::Set(const Type& binding, Context& context, AttributeLocation location) { + if (binding) { + context.vertexBuffer = reinterpret_cast<const gl::VertexBufferResource&>(*binding->vertexBufferResource).buffer; + MBGL_CHECK_ERROR(glEnableVertexAttribArray(location)); + MBGL_CHECK_ERROR(glVertexAttribPointer( + location, + components(binding->attribute.dataType), + vertexType(binding->attribute.dataType), + static_cast<GLboolean>(false), + static_cast<GLsizei>(binding->vertexStride), + reinterpret_cast<GLvoid*>(binding->attribute.offset + (binding->vertexStride * binding->vertexOffset)))); + } else { + MBGL_CHECK_ERROR(glDisableVertexAttribArray(location)); + } +} + +const constexpr PixelStorePack::Type PixelStorePack::Default; + +void PixelStorePack::Set(const Type& value) { + assert(value.alignment == 1 || value.alignment == 2 || value.alignment == 4 || + value.alignment == 8); + MBGL_CHECK_ERROR(glPixelStorei(GL_PACK_ALIGNMENT, value.alignment)); +} + +PixelStorePack::Type PixelStorePack::Get() { + Type value; + MBGL_CHECK_ERROR(glGetIntegerv(GL_PACK_ALIGNMENT, &value.alignment)); + return value; +} + +const constexpr PixelStoreUnpack::Type PixelStoreUnpack::Default; + +void PixelStoreUnpack::Set(const Type& value) { + assert(value.alignment == 1 || value.alignment == 2 || value.alignment == 4 || + value.alignment == 8); + MBGL_CHECK_ERROR(glPixelStorei(GL_UNPACK_ALIGNMENT, value.alignment)); +} + +PixelStoreUnpack::Type PixelStoreUnpack::Get() { + Type value; + MBGL_CHECK_ERROR(glGetIntegerv(GL_UNPACK_ALIGNMENT, &value.alignment)); + return value; +} + +#if not MBGL_USE_GLES2 + +const constexpr PointSize::Type PointSize::Default; + +void PointSize::Set(const Type& value) { + MBGL_CHECK_ERROR(glPointSize(value)); +} + +PointSize::Type PointSize::Get() { + GLfloat pointSize; + MBGL_CHECK_ERROR(glGetFloatv(GL_POINT_SIZE, &pointSize)); + return pointSize; +} + +const constexpr PixelZoom::Type PixelZoom::Default; + +void PixelZoom::Set(const Type& value) { + MBGL_CHECK_ERROR(glPixelZoom(value.xfactor, value.yfactor)); +} + +PixelZoom::Type PixelZoom::Get() { + GLfloat xfactor, yfactor; + MBGL_CHECK_ERROR(glGetFloatv(GL_ZOOM_X, &xfactor)); + MBGL_CHECK_ERROR(glGetFloatv(GL_ZOOM_Y, &yfactor)); + return { xfactor, yfactor }; +} + +const constexpr RasterPos::Type RasterPos::Default; + +void RasterPos::Set(const Type& value) { + MBGL_CHECK_ERROR(glRasterPos4d(value.x, value.y, value.z, value.w)); +} + +RasterPos::Type RasterPos::Get() { + GLdouble pos[4]; + MBGL_CHECK_ERROR(glGetDoublev(GL_CURRENT_RASTER_POSITION, pos)); + return { pos[0], pos[1], pos[2], pos[3] }; +} + +const constexpr PixelTransferDepth::Type PixelTransferDepth::Default; + +void PixelTransferDepth::Set(const Type& value) { + MBGL_CHECK_ERROR(glPixelTransferf(GL_DEPTH_SCALE, value.scale)); + MBGL_CHECK_ERROR(glPixelTransferf(GL_DEPTH_BIAS, value.bias)); +} + +PixelTransferDepth::Type PixelTransferDepth::Get() { + Type value; + MBGL_CHECK_ERROR(glGetFloatv(GL_DEPTH_SCALE, &value.scale)); + MBGL_CHECK_ERROR(glGetFloatv(GL_DEPTH_BIAS, &value.bias)); + return value; +} + +const constexpr PixelTransferStencil::Type PixelTransferStencil::Default; + +void PixelTransferStencil::Set(const Type& value) { + MBGL_CHECK_ERROR(glPixelTransferf(GL_INDEX_SHIFT, value.shift)); + MBGL_CHECK_ERROR(glPixelTransferf(GL_INDEX_OFFSET, value.offset)); +} + +PixelTransferStencil::Type PixelTransferStencil::Get() { + Type value; + MBGL_CHECK_ERROR(glGetIntegerv(GL_INDEX_SHIFT, &value.shift)); + MBGL_CHECK_ERROR(glGetIntegerv(GL_INDEX_OFFSET, &value.offset)); + return value; +} + +#endif // MBGL_USE_GLES2 + +} // namespace value +} // namespace gl +} // namespace mbgl diff --git a/platform/gfx/gl/src/mbgl/gl/value.hpp b/platform/gfx/gl/src/mbgl/gl/value.hpp new file mode 100644 index 0000000000..5bd2132ecf --- /dev/null +++ b/platform/gfx/gl/src/mbgl/gl/value.hpp @@ -0,0 +1,357 @@ +#pragma once + +#include <mbgl/gl/types.hpp> +#include <mbgl/gfx/depth_mode.hpp> +#include <mbgl/gfx/stencil_mode.hpp> +#include <mbgl/gfx/color_mode.hpp> +#include <mbgl/gfx/cull_face_mode.hpp> +#include <mbgl/gl/attribute.hpp> +#include <mbgl/platform/gl_functions.hpp> +#include <mbgl/util/color.hpp> +#include <mbgl/util/size.hpp> +#include <mbgl/util/range.hpp> + +namespace mbgl { +namespace gl { + +class Context; + +namespace value { + +struct ClearDepth { + using Type = float; + static const constexpr Type Default = 1; + static void Set(const Type&); + static Type Get(); +}; + +struct ClearColor { + using Type = Color; + static const Type Default; + static void Set(const Type&); + static Type Get(); +}; + +struct ClearStencil { + using Type = int32_t; + static const constexpr Type Default = 0; + static void Set(const Type&); + static Type Get(); +}; + +struct StencilMask { + using Type = uint32_t; + static const constexpr Type Default = ~0u; + static void Set(const Type&); + static Type Get(); +}; + +struct DepthMask { + using Type = gfx::DepthMaskType; + static const constexpr Type Default = gfx::DepthMaskType::ReadWrite; + static void Set(const Type&); + static Type Get(); +}; + +struct ColorMask { + using Type = gfx::ColorMode::Mask; + static const constexpr Type Default = { true, true, true, true }; + static void Set(const Type&); + static Type Get(); +}; + +struct StencilFunc { + struct Type { + gfx::StencilFunctionType func; + int32_t ref; + uint32_t mask; + }; + static const constexpr Type Default = { gfx::StencilMode::Always::func, 0, ~0u }; + static void Set(const Type&); + static Type Get(); +}; + +constexpr bool operator!=(const StencilFunc::Type& a, const StencilFunc::Type& b) { + return a.func != b.func || a.ref != b.ref || a.mask != b.mask; +} + +struct StencilTest { + using Type = bool; + static const constexpr Type Default = false; + static void Set(const Type&); + static Type Get(); +}; + +struct StencilOp { + struct Type { + gfx::StencilOpType sfail; + gfx::StencilOpType dpfail; + gfx::StencilOpType dppass; + }; + static const constexpr Type Default = { gfx::StencilOpType::Keep, gfx::StencilOpType::Keep, gfx::StencilOpType::Keep }; + static void Set(const Type&); + static Type Get(); +}; + +constexpr bool operator!=(const StencilOp::Type& a, const StencilOp::Type& b) { + return a.sfail != b.sfail || a.dpfail != b.dpfail || a.dppass != b.dppass; +} + +struct DepthRange { + using Type = Range<float>; + static const constexpr Type Default = { 0, 1 }; + static void Set(const Type&); + static Type Get(); +}; + +struct DepthTest { + using Type = bool; + static const constexpr Type Default = false; + static void Set(const Type&); + static Type Get(); +}; + +struct DepthFunc { + using Type = gfx::DepthFunctionType; + static const constexpr Type Default = gfx::DepthFunctionType::Less; + static void Set(const Type&); + static Type Get(); +}; + +struct Blend { + using Type = bool; + static const constexpr Type Default = true; + static void Set(const Type&); + static Type Get(); +}; + +struct BlendEquation { + using Type = gfx::ColorBlendEquationType; + static const constexpr Type Default = gfx::ColorBlendEquationType::Add; + static void Set(const Type&); + static Type Get(); +}; + +struct BlendFunc { + struct Type { + gfx::ColorBlendFactorType sfactor; + gfx::ColorBlendFactorType dfactor; + }; + static const constexpr Type Default = { gfx::ColorBlendFactorType::One, gfx::ColorBlendFactorType::Zero }; + static void Set(const Type&); + static Type Get(); +}; + +constexpr bool operator!=(const BlendFunc::Type& a, const BlendFunc::Type& b) { + return a.sfactor != b.sfactor || a.dfactor != b.dfactor; +} + +struct BlendColor { + using Type = Color; + static const Type Default; + static void Set(const Type&); + static Type Get(); +}; + +struct Program { + using Type = gl::ProgramID; + static const constexpr Type Default = 0; + static void Set(const Type&); + static Type Get(); +}; + +struct LineWidth { + using Type = float; + static const constexpr Type Default = 1; + static void Set(const Type&); + static Type Get(); +}; + +struct ActiveTextureUnit { + using Type = uint8_t; + static const constexpr Type Default = 0; + static void Set(const Type&); + static Type Get(); +}; + +struct Viewport { + struct Type { + int32_t x; + int32_t y; + Size size; + }; + static const constexpr Type Default = { 0, 0, { 0, 0 } }; + static void Set(const Type&); + static Type Get(); +}; + +struct ScissorTest { + using Type = bool; + static const constexpr Type Default = false; + static void Set(const Type&); + static Type Get(); +}; + +constexpr bool operator!=(const Viewport::Type& a, const Viewport::Type& b) { + return a.x != b.x || a.y != b.y || a.size != b.size; +} + +constexpr bool operator==(const Viewport::Type& a, const Viewport::Type& b) { + return !(a != b); +} + +struct BindFramebuffer { + using Type = FramebufferID; + static const constexpr Type Default = 0; + static void Set(const Type&); + static Type Get(); +}; + +struct BindRenderbuffer { + using Type = RenderbufferID; + static const constexpr Type Default = 0; + static void Set(const Type&); + static Type Get(); +}; + +struct CullFace { + using Type = bool; + static const constexpr Type Default = false; + static void Set(const Type&); + static Type Get(); +}; + +struct CullFaceSide { + using Type = gfx::CullFaceSideType; + static const constexpr Type Default = gfx::CullFaceSideType::Back; + static void Set(const Type&); + static Type Get(); +}; + +struct CullFaceWinding { + using Type = gfx::CullFaceWindingType; + static const constexpr Type Default = gfx::CullFaceWindingType::CounterClockwise; + static void Set(const Type&); + static Type Get(); +}; + +struct BindTexture { + using Type = gl::TextureID; + static const constexpr Type Default = 0; + static void Set(const Type&); + static Type Get(); +}; + +struct BindVertexBuffer { + using Type = gl::BufferID; + static const constexpr Type Default = 0; + static void Set(const Type&); + static Type Get(); +}; + +struct BindElementBuffer { + using Type = gl::BufferID; + static const constexpr Type Default = 0; + static void Set(const Type&); + static Type Get(); +}; + +struct BindVertexArray { + using Type = gl::VertexArrayID; + static const constexpr Type Default = 0; + static void Set(const Type&, const Context&); + static Type Get(const Context&); +}; + +struct VertexAttribute { + using Type = optional<gfx::AttributeBinding>; + static const Type Default; + static void Set(const Type&, Context&, AttributeLocation); +}; + +struct PixelStorePack { + using Type = PixelStorageType; + static const constexpr Type Default = { 4 }; + static void Set(const Type&); + static Type Get(); +}; + +struct PixelStoreUnpack { + using Type = PixelStorageType; + static const constexpr Type Default = { 4 }; + static void Set(const Type&); + static Type Get(); +}; + +#if not MBGL_USE_GLES2 + +struct PointSize { + using Type = float; + static const constexpr Type Default = 1; + static void Set(const Type&); + static Type Get(); +}; + +struct PixelZoom { + struct Type { + float xfactor; + float yfactor; + }; + static const constexpr Type Default = { 1, 1 }; + static void Set(const Type&); + static Type Get(); +}; + +constexpr bool operator!=(const PixelZoom::Type& a, const PixelZoom::Type& b) { + return a.xfactor != b.xfactor || a.yfactor != b.yfactor; +} + +struct RasterPos { + struct Type { + double x; + double y; + double z; + double w; + }; + static const constexpr Type Default = { 0, 0, 0, 1 }; + static void Set(const Type&); + static Type Get(); +}; + +constexpr bool operator!=(const RasterPos::Type& a, const RasterPos::Type& b) { + return a.x != b.x || a.y != b.y || a.z != b.z || a.w != b.w; +} + +struct PixelTransferDepth { + struct Type { + float scale; + float bias; + }; + static const constexpr Type Default = { 1, 0 }; + static void Set(const Type&); + static Type Get(); +}; + +constexpr bool operator!=(const PixelTransferDepth::Type& a, const PixelTransferDepth::Type& b) { + return a.scale != b.scale || a.bias != b.bias; +} + +struct PixelTransferStencil { + struct Type { + int32_t shift; + int32_t offset; + }; + static const constexpr Type Default = { 0, 0 }; + static void Set(const Type&); + static Type Get(); +}; + +constexpr bool operator!=(const PixelTransferStencil::Type& a, const PixelTransferStencil::Type& b) { + return a.shift != b.shift || a.offset != b.offset; +} + +#endif // MBGL_USE_GLES2 + +} // namespace value +} // namespace gl +} // namespace mbgl diff --git a/platform/gfx/gl/src/mbgl/gl/vertex_array.cpp b/platform/gfx/gl/src/mbgl/gl/vertex_array.cpp new file mode 100644 index 0000000000..831b118fce --- /dev/null +++ b/platform/gfx/gl/src/mbgl/gl/vertex_array.cpp @@ -0,0 +1,24 @@ +#include <mbgl/gl/vertex_array.hpp> +#include <mbgl/gl/index_buffer_resource.hpp> +#include <mbgl/gl/context.hpp> + +namespace mbgl { +namespace gl { + +void VertexArray::bind(Context& context, + const gfx::IndexBuffer& indexBuffer, + const AttributeBindingArray& bindings) { + context.bindVertexArray = state->vertexArray; + state->indexBuffer = indexBuffer.getResource<gl::IndexBufferResource>().buffer; + + state->bindings.reserve(bindings.size()); + for (AttributeLocation location = 0; location < bindings.size(); ++location) { + if (state->bindings.size() <= location) { + state->bindings.emplace_back(context, AttributeLocation(location)); + } + state->bindings[location] = bindings[location]; + } +} + +} // namespace gl +} // namespace mbgl diff --git a/platform/gfx/gl/src/mbgl/gl/vertex_array.hpp b/platform/gfx/gl/src/mbgl/gl/vertex_array.hpp new file mode 100644 index 0000000000..70413050b2 --- /dev/null +++ b/platform/gfx/gl/src/mbgl/gl/vertex_array.hpp @@ -0,0 +1,71 @@ +#pragma once + +#include <mbgl/gl/object.hpp> +#include <mbgl/gl/attribute.hpp> +#include <mbgl/gl/state.hpp> +#include <mbgl/gl/value.hpp> + +#include <array> +#include <memory> + +namespace mbgl { + +namespace gfx { +class IndexBuffer; +} // namespace gfx + +namespace gl { + +class Context; + +class VertexArrayState { +public: + VertexArrayState(UniqueVertexArray vertexArray_) + : vertexArray(std::move(vertexArray_)) { + } + + void setDirty() { + indexBuffer.setDirty(); + for (auto& binding : bindings) { + binding.setDirty(); + } + } + + UniqueVertexArray vertexArray; + State<value::BindElementBuffer> indexBuffer; + + using AttributeState = State<value::VertexAttribute, Context&, AttributeLocation>; + std::vector<AttributeState> bindings; +}; + +class VertexArrayStateDeleter { +public: + VertexArrayStateDeleter(bool destroy_) + : destroy(destroy_) {} + + void operator()(VertexArrayState* ptr) const { + if (destroy) { + delete ptr; + } + } + +private: + bool destroy; +}; + +using UniqueVertexArrayState = std::unique_ptr<VertexArrayState, VertexArrayStateDeleter>; + +class VertexArray { +public: + VertexArray(UniqueVertexArrayState state_) + : state(std::move(state_)) { + } + + void bind(Context&, const gfx::IndexBuffer&, const AttributeBindingArray&); + +private: + UniqueVertexArrayState state; +}; + +} // namespace gl +} // namespace mbgl diff --git a/platform/gfx/gl/src/mbgl/gl/vertex_array_extension.hpp b/platform/gfx/gl/src/mbgl/gl/vertex_array_extension.hpp new file mode 100644 index 0000000000..967d99af32 --- /dev/null +++ b/platform/gfx/gl/src/mbgl/gl/vertex_array_extension.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include <mbgl/gl/extension.hpp> +#include <mbgl/gl/defines.hpp> +#include <mbgl/platform/gl_functions.hpp> + +namespace mbgl { +namespace gl { +namespace extension { + +class VertexArray { +public: + template <typename Fn> + VertexArray(const Fn& loadExtension) + : bindVertexArray( + loadExtension({ { "GL_ARB_vertex_array_object", "glBindVertexArray" }, + { "GL_OES_vertex_array_object", "glBindVertexArrayOES" }, + { "GL_APPLE_vertex_array_object", "glBindVertexArrayAPPLE" } })), + deleteVertexArrays( + loadExtension({ { "GL_ARB_vertex_array_object", "glDeleteVertexArrays" }, + { "GL_OES_vertex_array_object", "glDeleteVertexArraysOES" }, + { "GL_APPLE_vertex_array_object", "glDeleteVertexArraysAPPLE" } })), + genVertexArrays( + loadExtension({ { "GL_ARB_vertex_array_object", "glGenVertexArrays" }, + { "GL_OES_vertex_array_object", "glGenVertexArraysOES" }, + { "GL_APPLE_vertex_array_object", "glGenVertexArraysAPPLE" } })) { + } + + const ExtensionFunction<void(platform::GLuint array)> bindVertexArray; + + const ExtensionFunction<void(platform::GLsizei n, const platform::GLuint* arrays)> deleteVertexArrays; + + const ExtensionFunction<void(platform::GLsizei n, platform::GLuint* arrays)> genVertexArrays; +}; + +} // namespace extension +} // namespace gl +} // namespace mbgl diff --git a/platform/gfx/gl/src/mbgl/gl/vertex_buffer_resource.hpp b/platform/gfx/gl/src/mbgl/gl/vertex_buffer_resource.hpp new file mode 100644 index 0000000000..95e5e75d45 --- /dev/null +++ b/platform/gfx/gl/src/mbgl/gl/vertex_buffer_resource.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include <mbgl/gfx/vertex_buffer.hpp> +#include <mbgl/gl/object.hpp> + +namespace mbgl { +namespace gl { + +class VertexBufferResource : public gfx::VertexBufferResource { +public: + VertexBufferResource(UniqueBuffer&& buffer_) : buffer(std::move(buffer_)) { + } + + UniqueBuffer buffer; +}; + +} // namespace gl +} // namespace mbgl |