diff --git a/cmake/core-files.cmake b/cmake/core-files.cmake index 2fc17af76df..32410927ad8 100644 --- a/cmake/core-files.cmake +++ b/cmake/core-files.cmake @@ -87,6 +87,7 @@ set(MBGL_CORE_FILES src/mbgl/gl/object.cpp src/mbgl/gl/object.hpp src/mbgl/gl/state.hpp + src/mbgl/gl/texture.hpp src/mbgl/gl/types.hpp src/mbgl/gl/value.cpp src/mbgl/gl/value.hpp @@ -229,6 +230,7 @@ set(MBGL_CORE_FILES include/mbgl/storage/response.hpp src/mbgl/storage/asset_file_source.hpp src/mbgl/storage/http_file_source.hpp + src/mbgl/storage/local_file_source.hpp src/mbgl/storage/network_status.cpp src/mbgl/storage/resource.cpp src/mbgl/storage/response.cpp @@ -468,8 +470,6 @@ set(MBGL_CORE_FILES src/mbgl/util/premultiply.cpp src/mbgl/util/premultiply.hpp src/mbgl/util/rapidjson.hpp - src/mbgl/util/raster.cpp - src/mbgl/util/raster.hpp src/mbgl/util/rect.hpp src/mbgl/util/std.hpp src/mbgl/util/stopwatch.cpp diff --git a/src/mbgl/gl/context.cpp b/src/mbgl/gl/context.cpp index ae50e64cbc2..ded7936feb9 100644 --- a/src/mbgl/gl/context.cpp +++ b/src/mbgl/gl/context.cpp @@ -124,6 +124,44 @@ void Context::uploadBuffer(BufferType type, size_t size, void* data) { MBGL_CHECK_ERROR(glBufferData(static_cast(type), size, data, GL_STATIC_DRAW)); } +UniqueTexture +Context::createTexture(uint16_t width, uint16_t height, const void* data, TextureUnit unit) { + auto obj = createTexture(); + activeTexture = unit; + texture[unit] = obj; + 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)); + MBGL_CHECK_ERROR( + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data)); + return obj; +} + +void Context::bindTexture(Texture& obj, + TextureUnit unit, + TextureFilter filter, + TextureMipMap mipmap) { + if (filter != obj.filter || mipmap != obj.mipmap) { + activeTexture = unit; + texture[unit] = obj.texture; + MBGL_CHECK_ERROR(glTexParameteri( + GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, + filter == TextureFilter::Linear + ? (mipmap == TextureMipMap::Yes ? GL_LINEAR_MIPMAP_NEAREST : GL_LINEAR) + : (mipmap == TextureMipMap::Yes ? GL_NEAREST_MIPMAP_NEAREST : GL_NEAREST))); + MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, + filter == TextureFilter::Linear ? GL_LINEAR : GL_NEAREST)); + obj.filter = filter; + obj.mipmap = mipmap; + } else if (texture[unit] != obj.texture) { + // We are checking first to avoid setting the active texture without a subsequent + // texture bind. + activeTexture = unit; + texture[unit] = obj.texture; + } +} + void Context::reset() { std::copy(pooledTextures.begin(), pooledTextures.end(), std::back_inserter(abandonedTextures)); pooledTextures.resize(0); diff --git a/src/mbgl/gl/context.hpp b/src/mbgl/gl/context.hpp index 04089c2000b..0ec846033a5 100644 --- a/src/mbgl/gl/context.hpp +++ b/src/mbgl/gl/context.hpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -28,6 +29,23 @@ class Context : private util::noncopyable { void uploadBuffer(BufferType, size_t, void*); + // Create a texture from an image with data. + template + Texture createTexture(const Image& image, TextureUnit unit = 0) { + return { {{ image.width, image.height }}, + createTexture(image.width, image.height, image.data.get(), unit) }; + } + + // Creates an empty texture with the specified dimensions. + Texture createTexture(const std::array& size, TextureUnit unit = 0) { + return { size, createTexture(size[0], size[1], nullptr, unit) }; + } + + void bindTexture(Texture&, + TextureUnit = 0, + TextureFilter = TextureFilter::Nearest, + TextureMipMap = TextureMipMap::No); + // 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(); @@ -79,6 +97,9 @@ class Context : private util::noncopyable { State elementBuffer; State vertexArrayObject; +private: + UniqueTexture createTexture(uint16_t width, uint16_t height, const void* data, TextureUnit); + private: friend detail::ProgramDeleter; friend detail::ShaderDeleter; diff --git a/src/mbgl/gl/texture.hpp b/src/mbgl/gl/texture.hpp new file mode 100644 index 00000000000..49e1323095f --- /dev/null +++ b/src/mbgl/gl/texture.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include + +#include + +namespace mbgl { +namespace gl { + +class Texture { +public: + std::array size; + UniqueTexture texture; + TextureFilter filter = TextureFilter::Nearest; + TextureMipMap mipmap = TextureMipMap::No; +}; + +} // namespace gl +} // namespace mbgl diff --git a/src/mbgl/gl/types.hpp b/src/mbgl/gl/types.hpp index 8578a11f66b..e9d14e48073 100644 --- a/src/mbgl/gl/types.hpp +++ b/src/mbgl/gl/types.hpp @@ -27,6 +27,9 @@ enum class BufferType : uint32_t { Element = 0x8893 }; +enum class TextureMipMap : bool { No = false, Yes = true }; +enum class TextureFilter : bool { Nearest = false, Linear = true }; + enum class StencilTestFunction : uint32_t { Never = 0x0200, Less = 0x0201, diff --git a/src/mbgl/renderer/raster_bucket.cpp b/src/mbgl/renderer/raster_bucket.cpp index 0f902880de7..3edbe3e0422 100644 --- a/src/mbgl/renderer/raster_bucket.cpp +++ b/src/mbgl/renderer/raster_bucket.cpp @@ -9,11 +9,13 @@ namespace mbgl { using namespace style; +RasterBucket::RasterBucket(PremultipliedImage&& image_) : image(std::move(image_)) { +} + void RasterBucket::upload(gl::Context& context) { - if (hasData()) { - raster.upload(context, 0); - uploaded = true; - } + texture = context.createTexture(image); + image = {}; + uploaded = true; } void RasterBucket::render(Painter& painter, @@ -23,22 +25,19 @@ void RasterBucket::render(Painter& painter, painter.renderRaster(parameters, *this, *layer.as(), tile); } -void RasterBucket::setImage(PremultipliedImage image) { - raster.load(std::move(image)); -} - void RasterBucket::drawRaster(RasterShader& shader, StaticRasterVertexBuffer& vertices, VertexArrayObject& array, gl::Context& context) { - raster.bind(context, 0, Raster::Scaling::Linear); - raster.bind(context, 1, Raster::Scaling::Linear); + assert(texture); + context.bindTexture(*texture, 0, gl::TextureFilter::Linear); + context.bindTexture(*texture, 1, gl::TextureFilter::Linear); array.bind(shader, vertices, BUFFER_OFFSET_0, context); MBGL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, (GLsizei)vertices.index())); } bool RasterBucket::hasData() const { - return raster.isLoaded(); + return true; } bool RasterBucket::needsClipping() const { diff --git a/src/mbgl/renderer/raster_bucket.hpp b/src/mbgl/renderer/raster_bucket.hpp index 24f3f8864ad..db8138b875c 100644 --- a/src/mbgl/renderer/raster_bucket.hpp +++ b/src/mbgl/renderer/raster_bucket.hpp @@ -1,7 +1,7 @@ #pragma once #include -#include +#include #include namespace mbgl { @@ -12,16 +12,18 @@ class VertexArrayObject; class RasterBucket : public Bucket { public: + RasterBucket(PremultipliedImage&&); + void upload(gl::Context&) override; void render(Painter&, PaintParameters&, const style::Layer&, const RenderTile&) override; bool hasData() const override; bool needsClipping() const override; - void setImage(PremultipliedImage); - void drawRaster(RasterShader&, StaticRasterVertexBuffer&, VertexArrayObject&, gl::Context&); - Raster raster; +private: + PremultipliedImage image; + optional texture; }; } // namespace mbgl diff --git a/src/mbgl/tile/raster_tile_worker.cpp b/src/mbgl/tile/raster_tile_worker.cpp index 838737d5b83..d7621e35b8a 100644 --- a/src/mbgl/tile/raster_tile_worker.cpp +++ b/src/mbgl/tile/raster_tile_worker.cpp @@ -16,8 +16,7 @@ void RasterTileWorker::parse(std::shared_ptr data) { } try { - auto bucket = std::make_unique(); - bucket->setImage(decodeImage(*data)); + auto bucket = std::make_unique(decodeImage(*data)); parent.invoke(&RasterTile::onParsed, std::move(bucket)); } catch (...) { parent.invoke(&RasterTile::setError, std::current_exception()); diff --git a/src/mbgl/util/offscreen_texture.cpp b/src/mbgl/util/offscreen_texture.cpp index 194527d1b0d..51049f82828 100644 --- a/src/mbgl/util/offscreen_texture.cpp +++ b/src/mbgl/util/offscreen_texture.cpp @@ -10,16 +10,15 @@ void OffscreenTexture::bind(gl::Context& context, std::array size) { assert(size[0] > 0 && size[1] > 0); - if (raster.getSize() != size) { - raster.load(PremultipliedImage(size[0], size[1], nullptr)); - raster.upload(context, 0); + if (!texture || texture->size != size) { + texture = context.createTexture(size); } if (!framebuffer) { framebuffer = context.createFramebuffer(); context.bindFramebuffer = *framebuffer; MBGL_CHECK_ERROR(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - raster.getID(), 0)); + texture->texture, 0)); GLenum status = MBGL_CHECK_ERROR(glCheckFramebufferStatus(GL_FRAMEBUFFER)); if (status != GL_FRAMEBUFFER_COMPLETE) { @@ -55,12 +54,17 @@ void OffscreenTexture::bind(gl::Context& context, context.viewport = { 0, 0, size[0], size[1] }; } -Raster& OffscreenTexture::getTexture() { - return raster; +gl::Texture& OffscreenTexture::getTexture() { + return *texture; } std::array OffscreenTexture::getSize() const { - return raster.getSize(); + if (texture) { + // Use explicit dereference instead of -> due to clang 3.5 bug + return (*texture).size; + } else { + return {{ 0, 0 }}; + } } } // namespace mbgl diff --git a/src/mbgl/util/offscreen_texture.hpp b/src/mbgl/util/offscreen_texture.hpp index 2f5fa75d6c1..c71c0e51d90 100644 --- a/src/mbgl/util/offscreen_texture.hpp +++ b/src/mbgl/util/offscreen_texture.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include namespace mbgl { @@ -12,12 +12,12 @@ class OffscreenTexture { public: void bind(gl::Context&, std::array size); - Raster& getTexture(); + gl::Texture& getTexture(); std::array getSize() const; private: - mbgl::optional framebuffer; - Raster raster; + optional framebuffer; + optional texture; }; } // namespace mbgl diff --git a/src/mbgl/util/raster.cpp b/src/mbgl/util/raster.cpp deleted file mode 100644 index 885230c7ec3..00000000000 --- a/src/mbgl/util/raster.cpp +++ /dev/null @@ -1,96 +0,0 @@ -#include -#include -#include -#include - -#include - -#include -#include - -namespace mbgl { - -bool Raster::isLoaded() const { - return loaded; -} - -gl::TextureID Raster::getID() const { - return texture ? *texture : 0; -} - -std::array Raster::getSize() const { - return size; -} - -void Raster::load(PremultipliedImage image, uint32_t mipmapLevel) { - if (images.size() <= mipmapLevel) { - images.resize(mipmapLevel + 1); - } - images.at(mipmapLevel) = std::move(image); - - loaded = true; -} - -void Raster::bind(gl::Context& context, - uint32_t unit, - Scaling newFilter, - MipMap newMipMap) { - bool filterNeedsUpdate = false; - - if (!texture) { - if (images.empty()) { - Log::Error(Event::OpenGL, "trying to bind texture without images"); - return; - } else { - upload(context, unit); - filterNeedsUpdate = true; - } - } else { - if (context.texture[unit] != *texture) { - context.activeTexture = unit; - context.texture[unit] = *texture; - } - filterNeedsUpdate = (filter != newFilter || mipmap != newMipMap); - } - - if (filterNeedsUpdate) { - filter = newFilter; - mipmap = newMipMap; - updateFilter(); - } -} - -void Raster::updateFilter() { - MBGL_CHECK_ERROR(glTexParameteri( - GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, - filter == Scaling::Linear - ? (mipmap == MipMap::Yes ? GL_LINEAR_MIPMAP_NEAREST : GL_LINEAR) - : (mipmap == MipMap::Yes ? GL_NEAREST_MIPMAP_NEAREST : GL_NEAREST))); - MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, - filter == Scaling::Linear ? GL_LINEAR : GL_NEAREST)); -} - -void Raster::upload(gl::Context& context, uint32_t unit) { - if (!images.empty() && !texture) { - texture = context.createTexture(); - context.activeTexture = unit; - context.texture[unit] = *texture; - updateFilter(); -#if not MBGL_USE_GLES2 - MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, images.size())); -#endif // MBGL_USE_GLES2 - MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); - MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); - GLint level = 0; - for (auto& img : images) { - MBGL_CHECK_ERROR(glTexImage2D( - GL_TEXTURE_2D, level++, GL_RGBA, static_cast(img.width), - static_cast(img.height), 0, GL_RGBA, GL_UNSIGNED_BYTE, img.data.get())); - } - size = { { images.front().width, images.front().height } }; - images.clear(); - images.shrink_to_fit(); - } -} - -} // namespace mbgl diff --git a/src/mbgl/util/raster.hpp b/src/mbgl/util/raster.hpp deleted file mode 100644 index 606839050de..00000000000 --- a/src/mbgl/util/raster.hpp +++ /dev/null @@ -1,62 +0,0 @@ -#pragma once - -#include -#include -#include - -#include -#include -#include - -namespace mbgl { - -namespace gl { -class Context; -} // namespace gl - -class Raster { -public: - enum class MipMap : bool { No = false, Yes = true }; - enum class Scaling : bool { Nearest = false, Linear = true }; - - // load image data - void load(PremultipliedImage, uint32_t mipmapLevel = 0); - - // bind current texture - void bind(gl::Context&, - uint32_t unit, - Scaling = Scaling::Nearest, - MipMap = MipMap::No); - - // uploads the texture if it hasn't been uploaded yet. - void upload(gl::Context&, uint32_t unit); - - // loaded status - bool isLoaded() const; - - // returns the OpenGL texture ID - gl::TextureID getID() const; - - // size of uploaded image. - std::array getSize() const; - -private: - void updateFilter(); - -private: - // raw pixels have been loaded - std::atomic loaded { false }; - std::array size = {{ 0, 0 }}; - - // filters - Scaling filter = Scaling::Nearest; - MipMap mipmap = MipMap::No; - - // the raw pixels - std::vector images; - - // GL buffer object handle. - mbgl::optional texture; -}; - -} // namespace mbgl diff --git a/test/util/offscreen_texture.test.cpp b/test/util/offscreen_texture.test.cpp index 2c80c04c413..301fc2c2501 100644 --- a/test/util/offscreen_texture.test.cpp +++ b/test/util/offscreen_texture.test.cpp @@ -4,7 +4,6 @@ #include #include -#include using namespace mbgl; @@ -137,7 +136,7 @@ void main() { // Now, composite the Framebuffer texture we've rendered to onto the main FBO. context.program = compositeShader.program; - texture.getTexture().bind(context, 0, Raster::Scaling::Linear); + context.bindTexture(texture.getTexture(), 0, gl::TextureFilter::Linear); MBGL_CHECK_ERROR(glUniform1i(u_texture, 0)); MBGL_CHECK_ERROR(glBindBuffer(GL_ARRAY_BUFFER, viewportBuffer.buffer)); MBGL_CHECK_ERROR(glEnableVertexAttribArray(compositeShader.a_pos));