Correct and revise OpenGL texture pooling (#2709)

This commit is contained in:
alasram
2024-08-29 14:13:26 -07:00
committed by GitHub
parent 290bf927f5
commit b850c257a3
79 changed files with 817 additions and 247 deletions

View File

@@ -88,7 +88,7 @@ void Mailbox::push(std::unique_ptr<Message> message) {
}};
{
MLN_TRACE_ZONE(push_lock);
MLN_TRACE_ZONE(push lock);
std::lock_guard<std::mutex> pushingLock(pushingMutex);
if (closed) {
@@ -98,7 +98,7 @@ void Mailbox::push(std::unique_ptr<Message> message) {
bool wasEmpty = false;
{
MLN_TRACE_ZONE(queue_lock);
MLN_TRACE_ZONE(queue lock);
std::lock_guard<std::mutex> queueLock(queueMutex);
wasEmpty = queue.empty();
queue.push(std::move(message));
@@ -168,4 +168,4 @@ void Mailbox::scheduleToRecieve(const std::optional<util::SimpleIdentity>& tag)
}
}
} // namespace mbgl
} // namespace mbgl

View File

@@ -98,6 +98,8 @@ Context::Context(RendererBackend& backend_)
#if MLN_DRAWABLE_RENDERER
uboAllocator = std::make_unique<gl::UniformBufferAllocator>();
#endif
texturePool = std::make_unique<Texture2DPool>(this);
}
Context::~Context() noexcept {
@@ -105,6 +107,10 @@ Context::~Context() noexcept {
backend.getThreadPool().runRenderJobs(true /* closeQueue */);
reset();
// Delete all pooled resources while the context is still valid
texturePool.reset();
#if !defined(NDEBUG)
Log::Debug(Event::General, "Rendering Stats:\n" + stats.toString("\n"));
#endif
@@ -265,18 +271,14 @@ void Context::verifyProgramLinkage(ProgramID program_) {
throw std::runtime_error("program failed to link");
}
UniqueTexture Context::createUniqueTexture() {
UniqueTexture Context::createUniqueTexture(const Size& size,
gfx::TexturePixelType format,
gfx::TextureChannelDataType type) {
MLN_TRACE_FUNC();
if (pooledTextures.empty()) {
pooledTextures.resize(TextureMax);
MBGL_CHECK_ERROR(glGenTextures(TextureMax, pooledTextures.data()));
stats.numCreatedTextures += TextureMax;
}
assert(texturePool);
TextureID id = texturePool->alloc(size, format, type);
TextureID id = pooledTextures.back();
pooledTextures.pop_back();
stats.numActiveTextures++;
// NOLINTNEXTLINE(performance-move-const-arg)
return UniqueTexture{std::move(id), {this}};
}
@@ -306,27 +308,13 @@ std::unique_ptr<gfx::TextureResource> Context::createTextureResource(const Size
const gfx::TextureChannelDataType type) {
MLN_TRACE_FUNC();
auto obj = createUniqueTexture();
int textureByteSize = gl::TextureResource::getStorageSize(size, format, type);
stats.memTextures += textureByteSize;
std::unique_ptr<gfx::TextureResource> resource = std::make_unique<gl::TextureResource>(std::move(obj),
textureByteSize);
auto obj = createUniqueTexture(size, format, type);
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>::sizedFor(format, type),
size.width,
size.height,
0,
Enum<gfx::TexturePixelType>::to(format),
Enum<gfx::TextureChannelDataType>::to(type),
nullptr));
// We are using clamp to edge here since OpenGL ES doesn't allow GL_REPEAT
// on NPOT textures. We use those when the pixelRatio isn't a power of two,
// e.g. on iPhone 6 Plus.
@@ -530,9 +518,10 @@ std::unique_ptr<gfx::DrawScopeResource> Context::createDrawScopeResource() {
void Context::reset() {
MLN_TRACE_FUNC();
std::copy(pooledTextures.begin(), pooledTextures.end(), std::back_inserter(abandonedTextures));
pooledTextures.resize(0);
performCleanup();
// Delete all unused pooled resources
texturePool->shrink();
}
#if MLN_DRAWABLE_RENDERER
@@ -883,10 +872,8 @@ void Context::performCleanup() {
binding.setDirty();
}
}
texturePool->release(id);
}
MBGL_CHECK_ERROR(glDeleteTextures(int(abandonedTextures.size()), abandonedTextures.data()));
stats.numCreatedTextures -= int(abandonedTextures.size());
assert(stats.numCreatedTextures >= 0);
abandonedTextures.clear();
}
@@ -923,6 +910,8 @@ void Context::reduceMemoryUsage() {
MLN_TRACE_FUNC_GL();
performCleanup();
assert(texturePool);
texturePool->shrink();
// Ensure that all pending actions are executed to ensure that they happen
// before the app goes to the background.
@@ -949,5 +938,10 @@ void Context::clearStencilBuffer(const int32_t bits) {
stats.stencilClears++;
}
Texture2DPool& Context::getTexturePool() {
assert(texturePool);
return *texturePool;
}
} // namespace gl
} // namespace mbgl

View File

@@ -10,6 +10,7 @@
#include <mbgl/gl/state.hpp>
#include <mbgl/gl/value.hpp>
#include <mbgl/gl/framebuffer.hpp>
#include <mbgl/gl/resource_pool.hpp>
#include <mbgl/gl/vertex_array.hpp>
#include <mbgl/gl/types.hpp>
#include <mbgl/platform/gl_functions.hpp>
@@ -29,7 +30,6 @@
namespace mbgl {
namespace gl {
constexpr size_t TextureMax = 64;
using ProcAddress = void (*)();
class RendererBackend;
@@ -58,7 +58,7 @@ public:
UniqueProgram createProgram(ShaderID vertexShader, ShaderID fragmentShader, const char* location0AttribName);
void verifyProgramLinkage(ProgramID);
void linkProgram(ProgramID);
UniqueTexture createUniqueTexture();
UniqueTexture createUniqueTexture(const Size& size, gfx::TexturePixelType format, gfx::TextureChannelDataType type);
Framebuffer createFramebuffer(const gfx::Renderbuffer<gfx::RenderbufferPixelType::RGBA>&,
const gfx::Renderbuffer<gfx::RenderbufferPixelType::DepthStencil>&);
@@ -93,18 +93,23 @@ public:
// Actually remove the objects we marked as abandoned with the above methods.
// Only call this while the OpenGL context is exclusive to this thread.
// Pooled textures are retained
void performCleanup() override;
// Called when the app receives a memory warning and before it goes to the background.
// Calls performCleanup and destroy all pooled resources
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();
// Returns whether there are any objects that need to be cleaned up.
// If considerPool is true, it will also check if resources are still pooled.
bool empty(bool considerPool = false) const {
return abandonedPrograms.empty() && abandonedShaders.empty() && abandonedBuffers.empty() &&
abandonedTextures.empty() && abandonedVertexArrays.empty() && abandonedFramebuffers.empty() &&
(considerPool ? texturePool->empty() : true);
}
extension::Debugging* getDebuggingExtension() const { return debugging.get(); }
@@ -151,6 +156,8 @@ public:
void setDirtyState() override;
Texture2DPool& getTexturePool();
private:
RendererBackend& backend;
bool cleanupOnDestruction = true;
@@ -230,8 +237,6 @@ private:
friend detail::FramebufferDeleter;
friend detail::RenderbufferDeleter;
std::vector<TextureID> pooledTextures;
std::vector<ProgramID> abandonedPrograms;
std::vector<ShaderID> abandonedShaders;
std::vector<BufferID> abandonedBuffers;
@@ -240,6 +245,8 @@ private:
std::vector<FramebufferID> abandonedFramebuffers;
std::vector<RenderbufferID> abandonedRenderbuffers;
std::unique_ptr<Texture2DPool> texturePool;
public:
#if !defined(NDEBUG)
public:

View File

@@ -23,13 +23,10 @@ void BufferDeleter::operator()(BufferID id) const {
void TextureDeleter::operator()(TextureID id) const {
assert(context);
if (context->pooledTextures.size() >= TextureMax) {
context->abandonedTextures.push_back(id);
} else {
context->pooledTextures.push_back(id);
}
context->abandonedTextures.push_back(id);
context->renderingStats().numActiveTextures--;
assert(context->renderingStats().numActiveTextures >= 0);
assert(context->renderingStats().numActiveTextures < context->renderingStats().numCreatedTextures);
}
void VertexArrayDeleter::operator()(VertexArrayID id) const {

View File

@@ -0,0 +1,251 @@
#include <mbgl/gl/resource_pool.hpp>
#include <cassert>
#include <type_traits>
#include <mbgl/gl/context.hpp>
#include <mbgl/gl/defines.hpp>
#include <mbgl/gl/enum.hpp>
#include <mbgl/gl/texture_resource.hpp>
#include <mbgl/util/hash.hpp>
#include <mbgl/util/instrumentation.hpp>
#include <mbgl/util/logging.hpp>
namespace mbgl {
namespace gl {
using namespace platform;
namespace {
size_t storageSize(const Texture2DDesc& desc) {
return static_cast<size_t>(TextureResource::getStorageSize(desc.size, desc.pixelFormat, desc.channelType));
}
#if !defined(NDEBUG)
void logDebugMessage(const std::string& message) {
Log::Debug(Event::General, "Texture2DPool: " + message);
}
#else
void logDebugMessage(const std::string&) {}
#endif
} // namespace
std::size_t Texture2DDescHash::operator()(const Texture2DDesc& desc) const {
std::size_t seed = 0;
util::hash_combine(seed, desc.size.width);
util::hash_combine(seed, desc.size.height);
util::hash_combine(seed, std::underlying_type_t<gfx::TexturePixelType>(desc.pixelFormat));
util::hash_combine(seed, std::underlying_type_t<gfx::TextureChannelDataType>(desc.channelType));
return seed;
}
Texture2DPool::Texture2DPool(Context* context_, size_t maxPoolStorage_)
: context(context_),
maxPoolStorage(maxPoolStorage_) {
assert(context);
}
Texture2DPool::~Texture2DPool() {
assert(usedStorage() == 0);
assert(unusedStorage() == poolStorage);
shrink();
assert(usedStorage() == 0);
assert(unusedStorage() == 0);
assert(poolStorage == 0);
assert(lru_cache.empty());
assert(descriptions.empty());
assert(pool.empty());
logDebugMessage("total allocations " + util::toString(allocCount));
logDebugMessage("total reused allocations " + util::toString(reuseCount));
logDebugMessage("total textures released " + util::toString(releaseCount));
logDebugMessage("total textures released and deallocated " + util::toString(freedCount));
}
TextureID Texture2DPool::alloc(const Texture2DDesc& desc) {
MLN_TRACE_FUNC();
context->renderingStats().numActiveTextures++;
// Check if the texture is already allocated
auto set_it = pool.find(desc);
if (set_it != pool.end() && !set_it->second.free.empty()) {
// Reuse an existing texture
TextureID id = *set_it->second.free.begin();
set_it->second.free.erase(id);
set_it->second.used.insert(id);
assert(lru_cache.isHit(id));
lru_cache.remove(id);
++reuseCount;
return id;
}
// Allocate a new texture
++allocCount;
return allocateGLMemory(desc);
}
void Texture2DPool::release(TextureID id) {
MLN_TRACE_FUNC();
assert(isUsed(id));
auto desc = descriptions.at(id);
auto set_it = pool.find(desc);
set_it->second.used.erase(id);
set_it->second.free.insert(id);
lru_cache.touch(id);
// Evict old textures if necessary
evict();
++releaseCount;
}
TextureID Texture2DPool::allocateGLMemory(const Texture2DDesc& desc) {
MLN_TRACE_FUNC();
// Create handle
TextureID id = 0;
MBGL_CHECK_ERROR(glGenTextures(1, &id));
assert(id != 0);
// Allocate memory
// Bind to TU 0 and upload
context->activeTextureUnit = 0;
context->texture[0] = id;
MBGL_CHECK_ERROR(glTexImage2D(GL_TEXTURE_2D,
0,
Enum<gfx::TexturePixelType>::sizedFor(desc.pixelFormat, desc.channelType),
desc.size.width,
desc.size.height,
0,
Enum<gfx::TexturePixelType>::to(desc.pixelFormat),
Enum<gfx::TextureChannelDataType>::to(desc.channelType),
nullptr));
// Update stats
context->renderingStats().numCreatedTextures++;
// Add to descriptions
descriptions.emplace(id, desc);
// Add to pool
pool[desc].used.insert(id);
// Update poolStorage
auto storage = storageSize(desc);
poolStorage += storage;
context->renderingStats().memTextures += storage;
MLN_TRACE_ALLOC_TEXTURE(id, storage);
// Evict old textures if necessary
evict();
return id;
}
void Texture2DPool::freeAllocatedGLMemory(TextureID id) {
MLN_TRACE_FUNC();
// Remove from descriptions
auto desc_it = descriptions.find(id);
assert(desc_it != descriptions.end());
auto desc = desc_it->second;
descriptions.erase(desc_it);
// Remove from descriptions lru_cache if it exists
lru_cache.remove(id);
// Remove from pool
auto set_it = pool.find(desc);
assert(set_it != pool.end());
auto free_it = set_it->second.free.find(id);
assert(free_it != set_it->second.free.end());
set_it->second.free.erase(free_it);
if (set_it->second.free.empty() && set_it->second.used.empty()) {
pool.erase(set_it);
}
// Update poolStorage
auto storage = storageSize(desc);
poolStorage -= storage;
context->renderingStats().memTextures -= storage;
assert(context->renderingStats().memTextures >= 0);
MLN_TRACE_FREE_TEXTURE(id);
// Delete the texture
MBGL_CHECK_ERROR(glDeleteTextures(1, &id));
// Update stats
context->renderingStats().numCreatedTextures--;
++freedCount;
}
void Texture2DPool::evict() {
while (poolStorage > maxPoolStorage && !lru_cache.empty()) {
TextureID id = lru_cache.evict();
freeAllocatedGLMemory(id);
}
}
void Texture2DPool::shrink() {
MLN_TRACE_FUNC();
while (!lru_cache.empty()) {
TextureID id = lru_cache.evict();
freeAllocatedGLMemory(id);
}
}
bool Texture2DPool::isPooled(TextureID id) const {
return descriptions.find(id) != descriptions.end();
}
bool Texture2DPool::isUsed(TextureID id) const {
auto desc_it = descriptions.find(id);
if (desc_it == descriptions.end()) {
return false;
}
const auto& desc = desc_it->second;
const auto& used_set = pool.at(desc).used;
return used_set.find(id) != used_set.end();
}
bool Texture2DPool::isUnused(TextureID id) const {
auto desc_it = descriptions.find(id);
if (desc_it == descriptions.end()) {
return false;
}
const auto& desc = desc_it->second;
const auto& free_set = pool.at(desc).free;
return free_set.find(id) != free_set.end();
}
size_t Texture2DPool::usedStorage() const {
size_t total = 0;
for (const auto& set : pool) {
total += storageSize(set.first) * set.second.used.size();
}
return total;
}
size_t Texture2DPool::unusedStorage() const {
size_t total = 0;
for (const auto& set : pool) {
total += storageSize(set.first) * set.second.free.size();
}
return total;
}
const Texture2DDesc& Texture2DPool::desc(TextureID id) const {
assert(descriptions.find(id) != descriptions.end());
return descriptions.at(id);
}
size_t Texture2DPool::storage(TextureID id) const {
assert(descriptions.find(id) != descriptions.end());
return storageSize(descriptions.at(id));
}
} // namespace gl
} // namespace mbgl

View File

@@ -0,0 +1,118 @@
#pragma once
#include <cstddef>
#include <functional>
#include <unordered_map>
#include <unordered_set>
#include <mbgl/gfx/types.hpp>
#include <mbgl/gl/types.hpp>
#include <mbgl/util/lru_cache.hpp>
#include <mbgl/util/size.hpp>
namespace mbgl {
namespace gl {
class Context;
struct Texture2DDesc {
Size size;
gfx::TexturePixelType pixelFormat;
gfx::TextureChannelDataType channelType;
};
struct Texture2DDescHash {
std::size_t operator()(const Texture2DDesc& desc) const;
};
inline bool operator==(const Texture2DDesc& desc1, const Texture2DDesc& desc2) {
return desc1.size == desc2.size && desc1.pixelFormat == desc2.pixelFormat && desc1.channelType == desc2.channelType;
}
inline bool operator!=(const Texture2DDesc& desc1, const Texture2DDesc& desc2) {
return !(desc1 == desc2);
}
class Texture2DPool {
public:
// Create a texture pool with a maximum storage size maxPoolStorage in bytes
// If maxPoolStorage is 0, pooling will disabled
Texture2DPool(Context* context, size_t maxPoolStorage = 16 * 1024 * 1024);
// Deallocate all storage
~Texture2DPool();
// Allocate a texture with the given size, pixel format, and channel type
// If the texture is already allocated in the pool, the existing texture ID will be returned
TextureID alloc(const Size& size, gfx::TexturePixelType pixelFormat, gfx::TextureChannelDataType channelType) {
return alloc({size, pixelFormat, channelType});
}
TextureID alloc(const Texture2DDesc& desc);
// Release the texture but keep it in the pool
void release(TextureID id);
// Free all unused textures
void shrink();
// Get the description of a texture
const Texture2DDesc& desc(TextureID id) const;
// Get the storage size in byte of a texture
size_t storage(TextureID id) const;
// Get the storage size in byte of all textures being used
size_t usedStorage() const;
// Get the storage size in byte of all unused textures
size_t unusedStorage() const;
// Get the storage size in byte of all textures in the pool
size_t storage() const { return poolStorage; }
// Get the maximum storage size before starting to evict unused textures
size_t maxStorage() const { return maxPoolStorage; }
bool empty() const { return poolStorage == 0; }
// Check if a texture is pooled
bool isPooled(TextureID id) const;
// Check if a texture is pooled and being used
bool isUsed(TextureID id) const;
// Check if a texture is pooled but released
bool isUnused(TextureID id) const;
private:
struct TextureSet {
std::unordered_set<TextureID> free;
std::unordered_set<TextureID> used;
};
// Effectively glGenTextures and glTexImage2D
TextureID allocateGLMemory(const Texture2DDesc& desc);
// Effectively call glDeleteTextures
void freeAllocatedGLMemory(TextureID id);
// Evict the least recently used textures until the pool storage is below maxPoolStorage
void evict();
private:
Context* context = nullptr;
size_t maxPoolStorage = 0;
std::size_t poolStorage = 0;
std::unordered_map<Texture2DDesc, TextureSet, Texture2DDescHash> pool;
std::unordered_map<TextureID, Texture2DDesc> descriptions;
LRU<TextureID> lru_cache;
// stats
int64_t allocCount = 0; // total allocations
int64_t reuseCount = 0; // total reused allocations
int64_t releaseCount = 0; // total textures released
int64_t freedCount = 0; // total textures released and deallocated
};
} // namespace gl
} // namespace mbgl

View File

@@ -3,6 +3,7 @@
#include <mbgl/gl/defines.hpp>
#include <mbgl/gl/enum.hpp>
#include <mbgl/platform/gl_functions.hpp>
#include <mbgl/util/instrumentation.hpp>
#include <mbgl/gl/texture_resource.hpp>
#include <mbgl/gfx/texture.hpp>
@@ -70,46 +71,30 @@ size_t Texture2D::numChannels() const noexcept {
}
}
void Texture2D::createObject() noexcept {
void Texture2D::allocateTexture() noexcept {
MLN_TRACE_FUNC();
// Create a new texture object
assert(!textureResource);
auto obj = context.createUniqueTexture();
const auto storageSize = gl::TextureResource::getStorageSize(size, pixelFormat, channelType);
context.renderingStats().memTextures += storageSize;
auto obj = context.createUniqueTexture(size, pixelFormat, channelType);
// @TODO: TextureResource is still needed while we have legacy rendering pathways
textureResource = std::make_unique<gl::TextureResource>(std::move(obj), storageSize);
textureResource = std::make_unique<gl::TextureResource>(std::move(obj));
}
void Texture2D::createStorage(const void* data) noexcept {
assert(textureResource);
void Texture2D::updateTextureData(const void* data) noexcept {
MLN_TRACE_FUNC();
if (data) {
uploadSubRegion(data, size, 0, 0);
}
// Create backing storage for our texture object
using namespace platform;
// Bind to TU 0 and upload
context.activeTextureUnit = 0;
context.texture[0] = getTextureID();
context.pixelStoreUnpack = {1};
MBGL_CHECK_ERROR(glTexImage2D(GL_TEXTURE_2D,
0,
Enum<gfx::TexturePixelType>::sizedFor(pixelFormat, channelType),
size.width,
size.height,
0,
Enum<gfx::TexturePixelType>::to(pixelFormat),
Enum<gfx::TextureChannelDataType>::to(channelType),
data));
storageDirty = false;
updateSamplerConfiguration();
}
void Texture2D::create() noexcept {
if (!textureResource) {
createObject();
}
allocateTexture();
if (storageDirty) {
createStorage();
updateTextureData();
}
}
@@ -184,19 +169,10 @@ void Texture2D::upload(const void* pixelData, const Size& size_) noexcept {
if (!textureResource || storageDirty || size_ == Size{0, 0} || size_ != size) {
size = size_;
// Create the texture object if we don't already have one
if (!textureResource) {
createObject();
}
createStorage(pixelData);
} else {
if (pixelData) {
// Upload to existing memory
uploadSubRegion(pixelData, size, 0, 0);
}
// Create the texture object if we don't already have one or if storage is dirty
allocateTexture();
}
updateTextureData(pixelData);
}
void Texture2D::uploadSubRegion(const void* pixelData, const Size& size_, uint16_t xOffset, uint16_t yOffset) noexcept {

View File

@@ -32,18 +32,10 @@ static int channelStorageSize(gfx::TextureChannelDataType type) {
}
}
TextureResource::TextureResource(UniqueTexture&& texture_, int byteSize_)
: texture(std::move(texture_)),
byteSize(byteSize_) {
MLN_TRACE_ALLOC_TEXTURE(texture.get(), byteSize);
}
TextureResource::TextureResource(UniqueTexture&& texture_)
: texture(std::move(texture_)) {}
TextureResource::~TextureResource() noexcept {
auto& stats = texture.get_deleter().context->renderingStats();
MLN_TRACE_FREE_TEXTURE(texture.get());
stats.memTextures -= byteSize;
assert(stats.memTextures >= 0);
}
TextureResource::~TextureResource() noexcept {}
int TextureResource::getStorageSize(const Size& size, gfx::TexturePixelType format, gfx::TextureChannelDataType type) {
return size.width * size.height * channelCount(format) * channelStorageSize(type);

View File

@@ -8,7 +8,7 @@ namespace gl {
class TextureResource : public gfx::TextureResource {
public:
TextureResource(UniqueTexture&& texture_, int byteSize_);
TextureResource(UniqueTexture&& texture_);
~TextureResource() noexcept override;
static int getStorageSize(const Size& size, gfx::TexturePixelType format, gfx::TextureChannelDataType type);
@@ -18,7 +18,6 @@ public:
gfx::TextureMipMapType mipmap = gfx::TextureMipMapType::No;
gfx::TextureWrapType wrapX = gfx::TextureWrapType::Clamp;
gfx::TextureWrapType wrapY = gfx::TextureWrapType::Clamp;
int byteSize;
};
} // namespace gl

View File

@@ -76,12 +76,12 @@ std::unique_ptr<gfx::TextureResource> UploadPass::createTextureResource(const Si
const void* data,
gfx::TexturePixelType format,
gfx::TextureChannelDataType type) {
auto obj = commandEncoder.context.createUniqueTexture();
const int textureByteSize = gl::TextureResource::getStorageSize(size, format, type);
commandEncoder.context.renderingStats().memTextures += textureByteSize;
auto resource = std::make_unique<gl::TextureResource>(std::move(obj), textureByteSize);
MLN_TRACE_FUNC();
auto obj = commandEncoder.context.createUniqueTexture(size, format, type);
auto resource = std::make_unique<gl::TextureResource>(std::move(obj));
commandEncoder.context.pixelStoreUnpack = {1};
updateTextureResource(*resource, size, data, format, type);
updateTextureResourceSub(*resource, 0, 0, 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.
@@ -97,18 +97,9 @@ void UploadPass::updateTextureResource(gfx::TextureResource& resource,
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));
MLN_TRACE_FUNC();
updateTextureResourceSub(resource, 0, 0, size, data, format, type);
}
void UploadPass::updateTextureResourceSub(gfx::TextureResource& resource,
@@ -118,9 +109,20 @@ void UploadPass::updateTextureResourceSub(gfx::TextureResource& resource,
const void* data,
gfx::TexturePixelType format,
gfx::TextureChannelDataType type) {
MLN_TRACE_FUNC();
auto& ctx = commandEncoder.context;
assert(ctx.getTexturePool().isUsed(static_cast<gl::TextureResource&>(resource).texture));
assert(ctx.getTexturePool().desc(static_cast<gl::TextureResource&>(resource).texture).channelType == type);
assert(ctx.getTexturePool().desc(static_cast<gl::TextureResource&>(resource).texture).pixelFormat == format);
assert(ctx.getTexturePool().desc(static_cast<gl::TextureResource&>(resource).texture).size.width >=
xOffset + size.width);
assert(ctx.getTexturePool().desc(static_cast<gl::TextureResource&>(resource).texture).size.height >=
yOffset + size.height);
// Always use texture unit 0 for manipulating it.
commandEncoder.context.activeTextureUnit = 0;
commandEncoder.context.texture[0] = static_cast<const gl::TextureResource&>(resource).texture;
ctx.activeTextureUnit = 0;
ctx.texture[0] = static_cast<const gl::TextureResource&>(resource).texture;
MBGL_CHECK_ERROR(glTexSubImage2D(GL_TEXTURE_2D,
0,
xOffset,