From 14a81ec831674276c93b407688e375ace5a6d9f7 Mon Sep 17 00:00:00 2001 From: Alex Cristici Date: Wed, 22 Apr 2026 14:03:58 +0300 Subject: [PATCH] Handle allocation failure [Core feature] (#4178) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- include/mbgl/gfx/context_observer.hpp | 1 + include/mbgl/gfx/texture2d.hpp | 13 +++--- include/mbgl/gl/texture2d.hpp | 8 ++-- include/mbgl/layermanager/layer_manager.hpp | 4 +- include/mbgl/map/map_observer.hpp | 3 ++ include/mbgl/mtl/buffer_resource.hpp | 2 +- include/mbgl/mtl/drawable.hpp | 8 ++-- include/mbgl/mtl/texture2d.hpp | 12 ++--- include/mbgl/renderer/layer_group.hpp | 45 ++++++++++++++++--- include/mbgl/renderer/renderer_observer.hpp | 3 ++ include/mbgl/vulkan/drawable.hpp | 8 ++-- include/mbgl/vulkan/texture2d.hpp | 10 ++--- .../src/cpp/android_renderer_frontend.cpp | 2 + .../src/cpp/native_map_view.cpp | 12 +++++ .../src/cpp/native_map_view.hpp | 3 ++ .../android/maps/MapChangeReceiver.java | 25 +++++++++++ .../org/maplibre/android/maps/MapView.java | 40 ++++++++++++++++- .../maplibre/android/maps/NativeMapView.java | 9 ++++ platform/ios/src/MLNMapView+Impl.h | 1 + platform/ios/src/MLNMapView+Impl.mm | 2 + platform/ios/src/MLNMapView.mm | 10 +++++ platform/ios/src/MLNMapViewDelegate.h | 11 +++++ platform/ios/src/MLNMapView_Private.h | 1 + .../MLNMapViewDelegateIntegrationTests.swift | 1 + src/mbgl/gl/context.cpp | 8 +++- src/mbgl/gl/resource_pool.cpp | 3 ++ src/mbgl/gl/texture2d.cpp | 8 ++-- src/mbgl/gl/upload_pass.cpp | 6 +++ src/mbgl/layermanager/layer_manager.cpp | 11 +++-- src/mbgl/map/map_impl.cpp | 4 ++ src/mbgl/map/map_impl.hpp | 1 + src/mbgl/mtl/buffer_resource.cpp | 11 ++++- src/mbgl/mtl/context.cpp | 8 +++- src/mbgl/mtl/drawable.cpp | 8 ++-- src/mbgl/mtl/texture2d.cpp | 18 +++++--- src/mbgl/renderer/render_orchestrator.cpp | 6 ++- src/mbgl/renderer/render_orchestrator.hpp | 12 ++++- src/mbgl/renderer/renderer_impl.cpp | 4 ++ src/mbgl/renderer/renderer_impl.hpp | 1 + src/mbgl/vulkan/buffer_resource.cpp | 2 +- src/mbgl/vulkan/context.cpp | 8 +++- src/mbgl/vulkan/drawable.cpp | 8 ++-- src/mbgl/vulkan/dynamic_texture.cpp | 2 +- src/mbgl/vulkan/texture2d.cpp | 17 ++++--- 44 files changed, 295 insertions(+), 85 deletions(-) diff --git a/include/mbgl/gfx/context_observer.hpp b/include/mbgl/gfx/context_observer.hpp index 5e0ca2eb9..5c33f7971 100644 --- a/include/mbgl/gfx/context_observer.hpp +++ b/include/mbgl/gfx/context_observer.hpp @@ -13,6 +13,7 @@ public: virtual void onPreCompileShader(shaders::BuiltIn, gfx::Backend::Type, const std::string&) {} virtual void onPostCompileShader(shaders::BuiltIn, gfx::Backend::Type, const std::string&) {} virtual void onShaderCompileFailed(shaders::BuiltIn, gfx::Backend::Type, const std::string&) {} + virtual void onRenderError(std::exception_ptr) {} }; } // namespace gfx diff --git a/include/mbgl/gfx/texture2d.hpp b/include/mbgl/gfx/texture2d.hpp index 5b4d73256..da3b14f9d 100644 --- a/include/mbgl/gfx/texture2d.hpp +++ b/include/mbgl/gfx/texture2d.hpp @@ -78,17 +78,17 @@ public: /// @brief Create the texture with default initialized memory. /// @note Be sure to configure a valid size and format. - virtual void create() noexcept = 0; + virtual void create() = 0; /// @brief Upload image data to the texture resource /// @param pixelData Image data to transfer - virtual void upload(const void* pixelData, const Size& size_) noexcept = 0; + virtual void upload(const void* pixelData, const Size& size_) = 0; /// @brief Upload image data to the texture resource /// @tparam Image Image object type /// @param img Image to transfer template - void upload(const Image& img) noexcept { + void upload(const Image& img) { setFormat(Image::channels == 1 ? gfx::TexturePixelType::Alpha : gfx::TexturePixelType::RGBA, gfx::TextureChannelDataType::UnsignedByte); upload(img.data ? img.data.get() : nullptr, img.size); @@ -99,10 +99,7 @@ public: /// @param size image dimensions /// @param xOffset destination x coordinate /// @param yOffset destination y coordinate - virtual void uploadSubRegion(const void* pixelData, - const Size& size, - uint16_t xOffset, - uint16_t yOffset) noexcept = 0; + virtual void uploadSubRegion(const void* pixelData, const Size& size, uint16_t xOffset, uint16_t yOffset) = 0; /// @brief Upload a subregion of the texture resource /// @tparam Image Image object type @@ -120,7 +117,7 @@ public: /// @brief Upload staged image data if present and required. /// @see needsUpload - virtual void upload() noexcept = 0; + virtual void upload() = 0; /// @brief Check whether the texture needs upload /// @return bool diff --git a/include/mbgl/gl/texture2d.hpp b/include/mbgl/gl/texture2d.hpp index 6cce3b3a1..f1984b45f 100644 --- a/include/mbgl/gl/texture2d.hpp +++ b/include/mbgl/gl/texture2d.hpp @@ -37,11 +37,11 @@ public: // gfx::Texture2D size_t numChannels() const noexcept override; - void create() noexcept override; + void create() override; - void upload(const void* pixelData, const Size& size_) noexcept override; + void upload(const void* pixelData, const Size& size_) override; void uploadSubRegion(const void* pixelData, const Size& size, uint16_t xOffset, uint16_t yOffset) noexcept override; - void upload() noexcept override; + void upload() override; bool needsUpload() const noexcept override { return !!image; }; @@ -62,7 +62,7 @@ public: void unbind() noexcept; private: - void allocateTexture() noexcept; + void allocateTexture(); void updateTextureData(const void* data = nullptr) noexcept; private: diff --git a/include/mbgl/layermanager/layer_manager.hpp b/include/mbgl/layermanager/layer_manager.hpp index f3bd94e6a..d29eeb9f7 100644 --- a/include/mbgl/layermanager/layer_manager.hpp +++ b/include/mbgl/layermanager/layer_manager.hpp @@ -43,11 +43,11 @@ public: std::unique_ptr createRenderLayer(Immutable) noexcept; /// Returns a new Bucket instance on success call; returns `nullptr` otherwise. std::unique_ptr createBucket(const BucketParameters&, - const std::vector>&) noexcept; + const std::vector>&); /// Returns a new Layout instance on success call; returns `nullptr` otherwise. std::unique_ptr createLayout(const LayoutParameters&, std::unique_ptr, - const std::vector>&) noexcept; + const std::vector>&); /** * @brief a build-time flag to enable/disable annotations in diff --git a/include/mbgl/map/map_observer.hpp b/include/mbgl/map/map_observer.hpp index f6055571d..d398c47f5 100644 --- a/include/mbgl/map/map_observer.hpp +++ b/include/mbgl/map/map_observer.hpp @@ -89,6 +89,9 @@ public: virtual void onSpriteLoaded(const std::optional&) {} virtual void onSpriteError(const std::optional&, std::exception_ptr) {} virtual void onSpriteRequested(const std::optional&) {} + + // Renderer + virtual void onRenderError(std::exception_ptr) {} }; } // namespace mbgl diff --git a/include/mbgl/mtl/buffer_resource.hpp b/include/mbgl/mtl/buffer_resource.hpp index d519666f1..98348a706 100644 --- a/include/mbgl/mtl/buffer_resource.hpp +++ b/include/mbgl/mtl/buffer_resource.hpp @@ -37,7 +37,7 @@ public: BufferResource clone() const; - void update(const void* data, std::size_t size, std::size_t offset) noexcept; + void update(const void* data, std::size_t size, std::size_t offset); std::size_t getSizeInBytes() const noexcept { return size; } const void* contents() const noexcept { return buffer ? buffer->contents() : (raw.empty() ? nullptr : raw.data()); } diff --git a/include/mbgl/mtl/drawable.hpp b/include/mbgl/mtl/drawable.hpp index d98e62d11..577f9fbe1 100644 --- a/include/mbgl/mtl/drawable.hpp +++ b/include/mbgl/mtl/drawable.hpp @@ -68,15 +68,15 @@ protected: // For testing only. Drawable(std::unique_ptr); - void bindAttributes(RenderPass&) const noexcept; + void bindAttributes(RenderPass&) const; void unbindAttributes(RenderPass&) const noexcept {} - void bindInstanceAttributes(RenderPass&) const noexcept; + void bindInstanceAttributes(RenderPass&) const; - void bindTextures(RenderPass&) const noexcept; + void bindTextures(RenderPass&) const; void unbindTextures(RenderPass&) const noexcept; - void uploadTextures(UploadPass&) const noexcept; + void uploadTextures(UploadPass&) const; class Impl; const std::unique_ptr impl; diff --git a/include/mbgl/mtl/texture2d.hpp b/include/mbgl/mtl/texture2d.hpp index e2024c996..ddc9e62d0 100644 --- a/include/mbgl/mtl/texture2d.hpp +++ b/include/mbgl/mtl/texture2d.hpp @@ -39,11 +39,11 @@ public: size_t numChannels() const noexcept override; - void create() noexcept override; + void create() override; - void upload(const void* pixelData, const Size& size_) noexcept override; + void upload(const void* pixelData, const Size& size_) override; void uploadSubRegion(const void* pixelData, const Size& size, uint16_t xOffset, uint16_t yOffset) noexcept override; - void upload() noexcept override; + void upload() override; bool needsUpload() const noexcept override { return !!image; }; @@ -51,12 +51,12 @@ public: MTL::Texture* getMetalTexture() const noexcept; - void updateSamplerConfiguration() noexcept; + void updateSamplerConfiguration(); /// @brief Bind this texture to the specified location /// @param renderPass Render pass on which the texture will be assign /// @param location Location index of texture sampler in a shader - void bind(RenderPass& renderPass, int32_t location) noexcept; + void bind(RenderPass& renderPass, int32_t location); /// @brief Unbind the texture, if it was bound /// @param renderPass Render pass from which the texture will be removed @@ -65,7 +65,7 @@ public: private: MTL::PixelFormat getMetalPixelFormat() const noexcept; - void createMetalTexture() noexcept; + void createMetalTexture(); void destroyMetalTexture() noexcept; Context& context; diff --git a/include/mbgl/renderer/layer_group.hpp b/include/mbgl/renderer/layer_group.hpp index b369adacd..a5f90a18f 100644 --- a/include/mbgl/renderer/layer_group.hpp +++ b/include/mbgl/renderer/layer_group.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include @@ -107,12 +108,16 @@ public: /// Get the mutable uniform buffer array attached to this layer group virtual gfx::UniformBufferArray& mutableUniformBuffers() = 0; + /// Set observer + void setObserver(gfx::ContextObserver* observer_) { observer = observer_ ? observer_ : &gfx::nullObserver; } + protected: const Type type; bool enabled = true; int32_t layerIndex; std::vector layerTweakers; std::string name; + gfx::ContextObserver* observer; }; /** @@ -132,8 +137,17 @@ public: template std::size_t visitDrawables(Func f) { assert(drawablesByTile.size() == sortedDrawables.size()); + unordered_set drawablesFailed; for (auto* drawable : sortedDrawables) { - f(*drawable); + try { + f(*drawable); + } catch (...) { + drawablesFailed.insert(drawable); + observer->onRenderError(std::current_exception()); + } + } + if (drawablesFailed.size() > 0) { + removeDrawablesIf([&](gfx::Drawable& drawable) { return drawablesFailed.contains(&drawable); }); } return sortedDrawables.size(); } @@ -160,8 +174,20 @@ public: template std::size_t visitDrawables(mbgl::RenderPass pass, const OverscaledTileID& tileID, Func f) { assert(drawablesByTile.size() == sortedDrawables.size()); - const auto range = drawablesByTile.equal_range({pass, tileID}); - std::for_each(range.first, range.second, [&f](const auto& pair) { f(*pair.second); }); + unordered_set drawablesFailed; + auto range = drawablesByTile.equal_range({pass, tileID}); + std::for_each(range.first, range.second, [&f, &drawablesFailed, this](const auto& pair) { + try { + f(*pair.second); + } catch (...) { + drawablesFailed.insert(pair.second.get()); + observer->onRenderError(std::current_exception()); + } + }); + if (drawablesFailed.size() > 0) { + removeDrawablesIf([&](gfx::Drawable& drawable) { return drawablesFailed.contains(&drawable); }); + range = drawablesByTile.equal_range({pass, tileID}); + } return std::distance(range.first, range.second); } @@ -213,11 +239,20 @@ public: template std::size_t visitDrawables(Func f) { - for (const auto& item : drawables) { + unordered_set drawablesFailed; + for (auto& item : drawables) { if (item) { - f(*item); + try { + f(*item); + } catch (...) { + drawablesFailed.insert(item.get()); + observer->onRenderError(std::current_exception()); + } } } + if (drawablesFailed.size() > 0) { + removeDrawablesIf([&](gfx::Drawable& drawable) { return drawablesFailed.contains(&drawable); }); + } return drawables.size(); } diff --git a/include/mbgl/renderer/renderer_observer.hpp b/include/mbgl/renderer/renderer_observer.hpp index 5fac1a2a1..fad2a7bf5 100644 --- a/include/mbgl/renderer/renderer_observer.hpp +++ b/include/mbgl/renderer/renderer_observer.hpp @@ -80,6 +80,9 @@ public: // Tile loading virtual void onTileAction(TileOperation, const OverscaledTileID&, const std::string&) {} + + /// Render layer or drawable failed + virtual void onRenderError(std::exception_ptr) {} }; } // namespace mbgl diff --git a/include/mbgl/vulkan/drawable.hpp b/include/mbgl/vulkan/drawable.hpp index 9ed2c2982..df2cebe7c 100644 --- a/include/mbgl/vulkan/drawable.hpp +++ b/include/mbgl/vulkan/drawable.hpp @@ -52,12 +52,12 @@ public: std::size_t segmentCount) override; protected: - void buildVulkanInputBindings() noexcept; + void buildVulkanInputBindings(); - bool bindAttributes(CommandEncoder&) const noexcept; - bool bindDescriptors(CommandEncoder&) const noexcept; + bool bindAttributes(CommandEncoder&) const; + bool bindDescriptors(CommandEncoder&) const; - void uploadTextures(UploadPass&) const noexcept; + void uploadTextures(UploadPass&) const; class Impl; const std::unique_ptr impl; diff --git a/include/mbgl/vulkan/texture2d.hpp b/include/mbgl/vulkan/texture2d.hpp index fb3230a9d..d30a9c2a8 100644 --- a/include/mbgl/vulkan/texture2d.hpp +++ b/include/mbgl/vulkan/texture2d.hpp @@ -64,18 +64,18 @@ public: bool isDirty() const { return samplerStateDirty || textureDirty; } bool isModifiedAfter(const std::chrono::duration& t) const { return t < lastModified; } - void create() noexcept override; + void create() override; void destroy(bool deferred = true) noexcept; - void upload() noexcept override; - void upload(const void* pixelData, const Size& size_) noexcept override; - void uploadSubRegion(const void* pixelData, const Size& size, uint16_t xOffset, uint16_t yOffset) noexcept override; + void upload() override; + void upload(const void* pixelData, const Size& size_) override; + void uploadSubRegion(const void* pixelData, const Size& size, uint16_t xOffset, uint16_t yOffset) override; void uploadSubRegion(const void* pixelData, const Size& size, uint16_t xOffset, uint16_t yOffset, const vk::UniqueCommandBuffer& buffer, - bool submit) noexcept; + bool submit); bool needsUpload() const noexcept override { return !!imageData; }; diff --git a/platform/android/MapLibreAndroid/src/cpp/android_renderer_frontend.cpp b/platform/android/MapLibreAndroid/src/cpp/android_renderer_frontend.cpp index 32d22c85d..aa4ce26eb 100644 --- a/platform/android/MapLibreAndroid/src/cpp/android_renderer_frontend.cpp +++ b/platform/android/MapLibreAndroid/src/cpp/android_renderer_frontend.cpp @@ -88,6 +88,8 @@ public: delegate.invoke(&RendererObserver::onTileAction, op, id, sourceID); } + void onRenderError(std::exception_ptr err) override { delegate.invoke(&RendererObserver::onRenderError, err); } + private: std::shared_ptr mailbox; ActorRef delegate; diff --git a/platform/android/MapLibreAndroid/src/cpp/native_map_view.cpp b/platform/android/MapLibreAndroid/src/cpp/native_map_view.cpp index 4e71d68f1..d817e23dd 100644 --- a/platform/android/MapLibreAndroid/src/cpp/native_map_view.cpp +++ b/platform/android/MapLibreAndroid/src/cpp/native_map_view.cpp @@ -1656,5 +1656,17 @@ void NativeMapView::onSpriteRequested(const std::optional& sprite } } +void NativeMapView::onRenderError(std::exception_ptr) { + assert(vm != nullptr); + + android::UniqueEnv _env = android::AttachEnv(); + static auto& javaClass = jni::Class::Singleton(*_env); + static auto onRenderError = javaClass.GetMethod(*_env, "onRenderError"); + auto weakReference = javaPeer.get(*_env); + if (weakReference) { + weakReference.Call(*_env, onRenderError); + } +} + } // namespace android } // namespace mbgl diff --git a/platform/android/MapLibreAndroid/src/cpp/native_map_view.hpp b/platform/android/MapLibreAndroid/src/cpp/native_map_view.hpp index 2914c96d1..41a9ba98c 100644 --- a/platform/android/MapLibreAndroid/src/cpp/native_map_view.hpp +++ b/platform/android/MapLibreAndroid/src/cpp/native_map_view.hpp @@ -346,6 +346,9 @@ public: void onSpriteError(const std::optional&, std::exception_ptr) override; void onSpriteRequested(const std::optional&) override; + // Renderer + void onRenderError(std::exception_ptr) override; + private: std::shared_ptr rendererFrontend; diff --git a/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/MapChangeReceiver.java b/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/MapChangeReceiver.java index c92d50520..15d667a7e 100644 --- a/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/MapChangeReceiver.java +++ b/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/MapChangeReceiver.java @@ -58,6 +58,8 @@ class MapChangeReceiver implements NativeMapView.StateCallback { = new CopyOnWriteArrayList<>(); private final List onSpriteRequestedList = new CopyOnWriteArrayList<>(); + private final List onRenderErrorList + = new CopyOnWriteArrayList<>(); @Override public void onCameraWillChange(boolean animated) { @@ -424,6 +426,20 @@ class MapChangeReceiver implements NativeMapView.StateCallback { } } + @Override + public void onRenderError() { + try { + if (!onRenderErrorList.isEmpty()) { + for (MapView.OnRenderErrorListener listener : onRenderErrorList) { + listener.onRenderError(); + } + } + } catch (Throwable err) { + Logger.e(TAG, "Exception in onRenderError", err); + throw err; + } + } + void addOnCameraWillChangeListener(MapView.OnCameraWillChangeListener listener) { onCameraWillChangeListenerList.add(listener); } @@ -592,6 +608,10 @@ class MapChangeReceiver implements NativeMapView.StateCallback { onSpriteRequestedList.add(callback); } + public void addOnRenderErrorListener(MapView.OnRenderErrorListener callback) { + onRenderErrorList.add(callback); + } + public void removeOnPreCompileShaderListener(MapView.OnPreCompileShaderListener callback) { onPreCompileShaderList.remove(callback); } @@ -632,6 +652,10 @@ class MapChangeReceiver implements NativeMapView.StateCallback { onSpriteRequestedList.remove(callback); } + public void removeOnRenderErrorListener(MapView.OnRenderErrorListener callback) { + onRenderErrorList.remove(callback); + } + void clear() { onCameraWillChangeListenerList.clear(); onCameraIsChangingListenerList.clear(); @@ -658,5 +682,6 @@ class MapChangeReceiver implements NativeMapView.StateCallback { onSpriteLoadedList.clear(); onSpriteErrorList.clear(); onSpriteRequestedList.clear(); + onRenderErrorList.clear(); } } diff --git a/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/MapView.java b/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/MapView.java index d0828b142..77f959f6a 100644 --- a/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/MapView.java +++ b/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/MapView.java @@ -1064,7 +1064,7 @@ public class MapView extends FrameLayout implements NativeMapView.ViewCallback { } /** - * Set a callback that's invoked after a sprite fails to laod. + * Set a callback that's invoked after a sprite fails to load. * * @param listener The callback that's invoked after a sprite fails to load */ @@ -1073,7 +1073,7 @@ public class MapView extends FrameLayout implements NativeMapView.ViewCallback { } /** - * Removes a callback that's invoked after a sprite fails to laod. + * Removes a callback that's invoked after a sprite fails to load. * * @param listener The callback that's invoked after a sprite fails to load */ @@ -1099,6 +1099,28 @@ public class MapView extends FrameLayout implements NativeMapView.ViewCallback { mapChangeReceiver.removeOnSpriteRequestedListener(callback); } + /** + * Set a callback that's invoked after an error occurs + * while trying to render a layer or drawable. + * + * @param listener The callback that's invoked after an error occurs + * while trying to render a layer or drawable. + */ + public void addOnRenderErrorListener(MapView.OnRenderErrorListener callback) { + mapChangeReceiver.addOnRenderErrorListener(callback); + } + + /** + * Removes a callback that's invoked after an error occurs + * while trying to render a layer or drawable. + * + * @param listener The callback that's invoked after an error occurs + * while trying to render a layer or drawable. + */ + public void removeOnRenderErrorListener(MapView.OnRenderErrorListener callback) { + mapChangeReceiver.removeOnRenderErrorListener(callback); + } + /** * Interface definition for a callback to be invoked when the camera will change. *

@@ -1513,6 +1535,20 @@ public class MapView extends FrameLayout implements NativeMapView.ViewCallback { void onSpriteRequested(@NonNull String id, @NonNull String url); } + /** + * Interface definition for a callback to be invoked after an error occurs + * while trying to render a layer or drawable. + *

+ * {@link MapView#addOnRenderErrorListener(OnRenderErrorListener)} + *

+ */ + public interface OnRenderErrorListener { + /** + * Called when an error occurs while trying to render a layer or drawable. + */ + void onRenderError(); + } + /** * Sets a callback object which will be triggered when the {@link MapLibreMap} instance is ready to be used. * diff --git a/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/NativeMapView.java b/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/NativeMapView.java index 67228a468..721de41d3 100755 --- a/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/NativeMapView.java +++ b/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/NativeMapView.java @@ -1379,6 +1379,13 @@ final class NativeMapView implements NativeMap { } } + @Keep + private void onRenderError() { + if (stateCallback != null) { + stateCallback.onRenderError(); + } + } + // // JNI methods // @@ -1867,5 +1874,7 @@ final class NativeMapView implements NativeMap { void onSpriteError(String id, String url); void onSpriteRequested(String id, String url); + + void onRenderError(); } } diff --git a/platform/ios/src/MLNMapView+Impl.h b/platform/ios/src/MLNMapView+Impl.h index 5367ede6f..7c61d9fef 100644 --- a/platform/ios/src/MLNMapView+Impl.h +++ b/platform/ios/src/MLNMapView+Impl.h @@ -87,6 +87,7 @@ public: void onSpriteLoaded(const std::optional&) override; void onSpriteError(const std::optional&, std::exception_ptr) override; void onSpriteRequested(const std::optional&) override; + void onRenderError(std::exception_ptr) override; protected: /// Cocoa map view that this adapter bridges to. diff --git a/platform/ios/src/MLNMapView+Impl.mm b/platform/ios/src/MLNMapView+Impl.mm index 2102bc007..aa99aab87 100644 --- a/platform/ios/src/MLNMapView+Impl.mm +++ b/platform/ios/src/MLNMapView+Impl.mm @@ -222,3 +222,5 @@ void MLNMapViewImpl::onSpriteRequested(const std::optional& [mapView spriteWillLoad:[NSString stringWithUTF8String:spriteID.value().id.c_str()] url:[NSString stringWithUTF8String:spriteID.value().spriteURL.c_str()]]; } + +void MLNMapViewImpl::onRenderError(std::exception_ptr error) { [mapView rendererDidError]; } diff --git a/platform/ios/src/MLNMapView.mm b/platform/ios/src/MLNMapView.mm index 21859a610..4744948dd 100644 --- a/platform/ios/src/MLNMapView.mm +++ b/platform/ios/src/MLNMapView.mm @@ -7106,6 +7106,16 @@ static void *windowScreenContext = &windowScreenContext; } } +- (void)rendererDidError { + if (!_mbglMap) { + return; + } + + if ([self.delegate respondsToSelector:@selector(mapViewRendererDidError:)]) { + [self.delegate mapViewRendererDidError:self]; + } +} + - (void)updateUserLocationAnnotationView { [self updateUserLocationAnnotationViewAnimatedWithDuration:0]; } diff --git a/platform/ios/src/MLNMapViewDelegate.h b/platform/ios/src/MLNMapViewDelegate.h index ec9dd6e54..535058985 100644 --- a/platform/ios/src/MLNMapViewDelegate.h +++ b/platform/ios/src/MLNMapViewDelegate.h @@ -503,6 +503,17 @@ Called when an error occurs while loading a sprite. */ - (void)mapView:(MLNMapView *)mapView spriteDidError:(NSString *)id url:(NSString *)url; +// MARK: - Renderer + +/** +Called when an error occurs while trying to render a layer or drawable. + +@param mapView The ``MLNMapView`` instance invoking this delegate method. + +> Warning: This method is not thread-safe. +*/ +- (void)mapViewRendererDidError:(MLNMapView *)mapView; + // MARK: Tracking User Location /** diff --git a/platform/ios/src/MLNMapView_Private.h b/platform/ios/src/MLNMapView_Private.h index db918f1aa..f4b312000 100644 --- a/platform/ios/src/MLNMapView_Private.h +++ b/platform/ios/src/MLNMapView_Private.h @@ -73,6 +73,7 @@ FOUNDATION_EXTERN MLN_EXPORT MLNExceptionName const _Nonnull MLNUnderlyingMapUna - (void)spriteWillLoad:(nullable NSString *)id url:(nullable NSString *)url; - (void)spriteDidLoad:(nullable NSString *)id url:(nullable NSString *)url; - (void)spriteDidError:(nullable NSString *)id url:(nullable NSString *)url; +- (void)rendererDidError; - (CLLocationDistance)metersPerPointAtLatitude:(CLLocationDegrees)latitude zoomLevel:(double)zoomLevel; diff --git a/platform/ios/test/MLNMapViewDelegateIntegrationTests.swift b/platform/ios/test/MLNMapViewDelegateIntegrationTests.swift index 6f7e69b9d..ed7fd16d4 100644 --- a/platform/ios/test/MLNMapViewDelegateIntegrationTests.swift +++ b/platform/ios/test/MLNMapViewDelegateIntegrationTests.swift @@ -127,4 +127,5 @@ extension MLNMapViewDelegateIntegrationTests: MLNMapViewDelegate { func mapView(_: MLNMapView, spriteWillLoad _: String, url _: String) {} func mapView(_: MLNMapView, spriteDidLoad _: String, url _: String) {} func mapView(_: MLNMapView, spriteDidError _: String, url _: String) {} + func mapViewRendererDidError(_: MLNMapView) {} } diff --git a/src/mbgl/gl/context.cpp b/src/mbgl/gl/context.cpp index f8415d24f..ff6a159d4 100644 --- a/src/mbgl/gl/context.cpp +++ b/src/mbgl/gl/context.cpp @@ -566,13 +566,17 @@ gfx::ShaderProgramBasePtr Context::getGenericShader(gfx::ShaderRegistry& shaders TileLayerGroupPtr Context::createTileLayerGroup(int32_t layerIndex, std::size_t initialCapacity, std::string name) { MLN_TRACE_FUNC(); - return std::make_shared(layerIndex, initialCapacity, std::move(name)); + auto tileLayerGroup = std::make_shared(layerIndex, initialCapacity, std::move(name)); + tileLayerGroup->setObserver(observer); + return tileLayerGroup; } LayerGroupPtr Context::createLayerGroup(int32_t layerIndex, std::size_t initialCapacity, std::string name) { MLN_TRACE_FUNC(); - return std::make_shared(layerIndex, initialCapacity, std::move(name)); + auto layerGroup = std::make_shared(layerIndex, initialCapacity, std::move(name)); + layerGroup->setObserver(observer); + return layerGroup; } gfx::Texture2DPtr Context::createTexture2D() { diff --git a/src/mbgl/gl/resource_pool.cpp b/src/mbgl/gl/resource_pool.cpp index 21903b612..86ea1b780 100644 --- a/src/mbgl/gl/resource_pool.cpp +++ b/src/mbgl/gl/resource_pool.cpp @@ -154,6 +154,9 @@ TextureID Texture2DPool::allocateGLMemory(const Texture2DDesc& desc) { Enum::to(desc.pixelFormat), Enum::to(desc.channelType), nullptr)); + if (glGetError()) { + throw std::bad_alloc(); + } // Update stats context->renderingStats().numCreatedTextures++; diff --git a/src/mbgl/gl/texture2d.cpp b/src/mbgl/gl/texture2d.cpp index 72270b79d..8378b6fb8 100644 --- a/src/mbgl/gl/texture2d.cpp +++ b/src/mbgl/gl/texture2d.cpp @@ -70,7 +70,7 @@ size_t Texture2D::numChannels() const noexcept { } } -void Texture2D::allocateTexture() noexcept { +void Texture2D::allocateTexture() { MLN_TRACE_FUNC(); // Create a new texture object @@ -88,7 +88,7 @@ void Texture2D::updateTextureData(const void* data) noexcept { updateSamplerConfiguration(); } -void Texture2D::create() noexcept { +void Texture2D::create() { allocateTexture(); if (storageDirty) { updateTextureData(); @@ -156,7 +156,7 @@ void Texture2D::unbind() noexcept { } } -void Texture2D::upload(const void* pixelData, const Size& size_) noexcept { +void Texture2D::upload(const void* pixelData, const Size& size_) { if (!texture || storageDirty || size_ == Size{0, 0} || size_ != size) { size = size_; @@ -194,7 +194,7 @@ void Texture2D::uploadSubRegion(const void* pixelData, const Size& size_, uint16 context.renderingStats().textureUpdateBytes += static_cast(getPixelStride() * size_.width * size_.height); } -void Texture2D::upload() noexcept { +void Texture2D::upload() { if (image && image->valid()) { setFormat(gfx::TexturePixelType::RGBA, gfx::TextureChannelDataType::UnsignedByte); upload(image->data.get(), image->size); diff --git a/src/mbgl/gl/upload_pass.cpp b/src/mbgl/gl/upload_pass.cpp index 7f4ad62e9..9c2dbcb56 100644 --- a/src/mbgl/gl/upload_pass.cpp +++ b/src/mbgl/gl/upload_pass.cpp @@ -36,6 +36,9 @@ std::unique_ptr UploadPass::createVertexBufferResourc UniqueBuffer result{std::move(id), {commandEncoder.context}}; commandEncoder.context.vertexBuffer = result; MBGL_CHECK_ERROR(glBufferData(GL_ARRAY_BUFFER, size, data, Enum::to(usage))); + if (glGetError()) { + throw std::bad_alloc(); + } return std::make_unique(std::move(result), static_cast(size)); } @@ -61,6 +64,9 @@ std::unique_ptr UploadPass::createIndexBufferResource( commandEncoder.context.bindVertexArray = 0; commandEncoder.context.globalVertexArrayState.indexBuffer = result; MBGL_CHECK_ERROR(glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, data, Enum::to(usage))); + if (glGetError()) { + throw std::bad_alloc(); + } return std::make_unique(std::move(result), static_cast(size)); } diff --git a/src/mbgl/layermanager/layer_manager.cpp b/src/mbgl/layermanager/layer_manager.cpp index 5dfeda299..36f9a3342 100644 --- a/src/mbgl/layermanager/layer_manager.cpp +++ b/src/mbgl/layermanager/layer_manager.cpp @@ -31,8 +31,8 @@ std::unique_ptr LayerManager::createLayer(const std::string& type, return nullptr; } -std::unique_ptr LayerManager::createBucket( - const BucketParameters& parameters, const std::vector>& layers) noexcept { +std::unique_ptr LayerManager::createBucket(const BucketParameters& parameters, + const std::vector>& layers) { assert(!layers.empty()); assert(parameters.layerType->layout == style::LayerTypeInfo::Layout::NotRequired); LayerFactory* factory = getFactory(parameters.layerType); @@ -40,10 +40,9 @@ std::unique_ptr LayerManager::createBucket( return factory->createBucket(parameters, layers); } -std::unique_ptr LayerManager::createLayout( - const LayoutParameters& parameters, - std::unique_ptr tileLayer, - const std::vector>& layers) noexcept { +std::unique_ptr LayerManager::createLayout(const LayoutParameters& parameters, + std::unique_ptr tileLayer, + const std::vector>& layers) { assert(!layers.empty()); assert(parameters.bucketParameters.layerType->layout == style::LayerTypeInfo::Layout::Required); LayerFactory* factory = getFactory(parameters.bucketParameters.layerType); diff --git a/src/mbgl/map/map_impl.cpp b/src/mbgl/map/map_impl.cpp index 6bb533f10..9e556a03c 100644 --- a/src/mbgl/map/map_impl.cpp +++ b/src/mbgl/map/map_impl.cpp @@ -428,4 +428,8 @@ void Map::Impl::onTileAction(TileOperation op, const OverscaledTileID& id, const } } +void Map::Impl::onRenderError(std::exception_ptr error) { + observer.onRenderError(error); +} + } // namespace mbgl diff --git a/src/mbgl/map/map_impl.hpp b/src/mbgl/map/map_impl.hpp index e231b3182..5ef9ffa43 100644 --- a/src/mbgl/map/map_impl.hpp +++ b/src/mbgl/map/map_impl.hpp @@ -74,6 +74,7 @@ public: void onGlyphsError(const FontStack&, const GlyphRange&, std::exception_ptr) final; void onGlyphsRequested(const FontStack&, const GlyphRange&) final; void onTileAction(TileOperation op, const OverscaledTileID&, const std::string&) final; + void onRenderError(std::exception_ptr) final; // Map void jumpTo(const CameraOptions&); diff --git a/src/mbgl/mtl/buffer_resource.cpp b/src/mbgl/mtl/buffer_resource.cpp index 6e7b47161..89b1d0b64 100644 --- a/src/mbgl/mtl/buffer_resource.cpp +++ b/src/mbgl/mtl/buffer_resource.cpp @@ -42,6 +42,10 @@ BufferResource::BufferResource(Context& context_, auto& device = context.getBackend().getDevice(); buffer = NS::TransferPtr((data && size) ? device->newBuffer(data, size, usage) : device->newBuffer(size, usage)); + + if (!buffer) { + throw std::bad_alloc(); + } } if (isValid()) { @@ -91,7 +95,7 @@ BufferResource& BufferResource::operator=(BufferResource&& other) noexcept { return *this; } -void BufferResource::update(const void* newData, std::size_t updateSize, std::size_t offset) noexcept { +void BufferResource::update(const void* newData, std::size_t updateSize, std::size_t offset) { assert(size >= 0 && updateSize + offset <= size); updateSize = std::min(updateSize, size - offset); if (updateSize <= 0) { @@ -135,6 +139,9 @@ void BufferResource::update(const void* newData, std::size_t updateSize, std::si if (newBufferSource) { auto newBuffer = NS::TransferPtr(device->newBuffer(newBufferSource, size, usage)); + if (!newBuffer) { + throw std::bad_alloc(); + } assert(newBuffer); if (newBuffer) { buffer = std::move(newBuffer); @@ -144,7 +151,7 @@ void BufferResource::update(const void* newData, std::size_t updateSize, std::si stats.bufferUpdateBytes += updateSize; } } - } else { + } else if (raw.size() > 0) { std::memcpy(raw.data() + offset, newData, updateSize); stats.bufferUpdateBytes += updateSize; } diff --git a/src/mbgl/mtl/context.cpp b/src/mbgl/mtl/context.cpp index 7c4f44af4..9a82d411c 100644 --- a/src/mbgl/mtl/context.cpp +++ b/src/mbgl/mtl/context.cpp @@ -219,11 +219,15 @@ gfx::ShaderProgramBasePtr Context::getGenericShader(gfx::ShaderRegistry& shaders } TileLayerGroupPtr Context::createTileLayerGroup(int32_t layerIndex, std::size_t initialCapacity, std::string name) { - return std::make_shared(layerIndex, initialCapacity, std::move(name)); + auto tileLayerGroup = std::make_shared(layerIndex, initialCapacity, std::move(name)); + tileLayerGroup->setObserver(observer); + return tileLayerGroup; } LayerGroupPtr Context::createLayerGroup(int32_t layerIndex, std::size_t initialCapacity, std::string name) { - return std::make_shared(layerIndex, initialCapacity, name); + auto layerGroup = std::make_shared(layerIndex, initialCapacity, name); + layerGroup->setObserver(observer); + return layerGroup; } gfx::Texture2DPtr Context::createTexture2D() { diff --git a/src/mbgl/mtl/drawable.cpp b/src/mbgl/mtl/drawable.cpp index 9603a3bc6..8219aeda9 100644 --- a/src/mbgl/mtl/drawable.cpp +++ b/src/mbgl/mtl/drawable.cpp @@ -397,7 +397,7 @@ void Drawable::setVertexAttrId(const size_t id) { impl->vertexAttrId = id; } -void Drawable::bindAttributes(RenderPass& renderPass) const noexcept { +void Drawable::bindAttributes(RenderPass& renderPass) const { NS::UInteger attributeIndex = 0; for (const auto& binding : impl->attributeBindings) { const auto* buffer = static_cast(binding ? binding->vertexBufferResource @@ -410,7 +410,7 @@ void Drawable::bindAttributes(RenderPass& renderPass) const noexcept { } } -void Drawable::bindInstanceAttributes(RenderPass& renderPass) const noexcept { +void Drawable::bindInstanceAttributes(RenderPass& renderPass) const { NS::UInteger attributeIndex = 0; for (const auto& binding : impl->instanceBindings) { if (binding.has_value()) { @@ -423,7 +423,7 @@ void Drawable::bindInstanceAttributes(RenderPass& renderPass) const noexcept { } } -void Drawable::bindTextures(RenderPass& renderPass) const noexcept { +void Drawable::bindTextures(RenderPass& renderPass) const { for (size_t id = 0; id < textures.size(); id++) { if (const auto& texture = textures[id]) { if (const auto& location = shader->getSamplerLocation(id)) { @@ -443,7 +443,7 @@ void Drawable::unbindTextures(RenderPass& renderPass) const noexcept { } } -void Drawable::uploadTextures(UploadPass&) const noexcept { +void Drawable::uploadTextures(UploadPass&) const { for (const auto& texture : textures) { if (texture) { texture->upload(); diff --git a/src/mbgl/mtl/texture2d.cpp b/src/mbgl/mtl/texture2d.cpp index f95bdcf2f..90dbd5bd4 100644 --- a/src/mbgl/mtl/texture2d.cpp +++ b/src/mbgl/mtl/texture2d.cpp @@ -145,7 +145,7 @@ MTL::PixelFormat Texture2D::getMetalPixelFormat() const noexcept { } } -void Texture2D::createMetalTexture() noexcept { +void Texture2D::createMetalTexture() { if (size == Size{0, 0}) { return; } @@ -180,6 +180,9 @@ void Texture2D::createMetalTexture() noexcept { } #endif metalTexture = context.createMetalTexture(std::move(textureDescriptor)); + if (!metalTexture) { + throw std::bad_alloc(); + } } if (metalTexture) { @@ -192,7 +195,7 @@ void Texture2D::createMetalTexture() noexcept { } } -void Texture2D::create() noexcept { +void Texture2D::create() { if (textureDirty) { createMetalTexture(); } @@ -224,7 +227,7 @@ MTL::Texture* Texture2D::getMetalTexture() const noexcept { return metalTexture.get(); } -void Texture2D::updateSamplerConfiguration() noexcept { +void Texture2D::updateSamplerConfiguration() { auto samplerDescriptor = NS::TransferPtr(MTL::SamplerDescriptor::alloc()->init()); samplerDescriptor->setMinFilter(samplerState.filter == gfx::TextureFilterType::Nearest ? MTL::SamplerMinMagFilterNearest @@ -239,11 +242,14 @@ void Texture2D::updateSamplerConfiguration() noexcept { ? MTL::SamplerAddressModeClampToEdge : MTL::SamplerAddressModeRepeat); metalSamplerState = context.createMetalSamplerState(samplerDescriptor); + if (!metalSamplerState) { + throw std::bad_alloc(); + } samplerStateDirty = false; } -void Texture2D::bind(RenderPass& renderPass, int32_t location) noexcept { +void Texture2D::bind(RenderPass& renderPass, int32_t location) { assert(!textureDirty); // Update the sampler state if it was changed after resource creation @@ -261,7 +267,7 @@ void Texture2D::unbind(RenderPass&, int32_t /*location*/) noexcept { context.threadSafeAccessRenderingStats([&](gfx::RenderingStats& stats) { stats.numTextureBindings--; }); } -void Texture2D::upload(const void* pixelData, const Size& size_) noexcept { +void Texture2D::upload(const void* pixelData, const Size& size_) { setSize(size_); if (textureDirty) { createMetalTexture(); @@ -287,7 +293,7 @@ void Texture2D::uploadSubRegion(const void* pixelData, const Size& size_, uint16 }); } -void Texture2D::upload() noexcept { +void Texture2D::upload() { if (image && image->valid()) { setFormat(gfx::TexturePixelType::RGBA, gfx::TextureChannelDataType::UnsignedByte); upload(image->data.get(), image->size); diff --git a/src/mbgl/renderer/render_orchestrator.cpp b/src/mbgl/renderer/render_orchestrator.cpp index 3c3419bf8..3e6f8089f 100644 --- a/src/mbgl/renderer/render_orchestrator.cpp +++ b/src/mbgl/renderer/render_orchestrator.cpp @@ -977,7 +977,11 @@ void RenderOrchestrator::updateLayers(gfx::ShaderRegistry& shaders, renderLayer.removeAllDrawables(); } #endif - renderLayer.update(shaders, context, state, updateParameters, renderTree, changes); + try { + renderLayer.update(shaders, context, state, updateParameters, renderTree, changes); + } catch (...) { + observer->onRenderError(std::current_exception()); + } } addChanges(changes); } diff --git a/src/mbgl/renderer/render_orchestrator.hpp b/src/mbgl/renderer/render_orchestrator.hpp index 05b67f3e6..c439204cf 100644 --- a/src/mbgl/renderer/render_orchestrator.hpp +++ b/src/mbgl/renderer/render_orchestrator.hpp @@ -112,7 +112,11 @@ public: void visitLayerGroups(Func f) { for (auto& pair : layerGroupsByLayerIndex) { if (pair.second) { - f(*pair.second); + try { + f(*pair.second); + } catch (...) { + observer->onRenderError(std::current_exception()); + } } } } @@ -121,7 +125,11 @@ public: void visitLayerGroupsReversed(Func f) { for (auto rit = layerGroupsByLayerIndex.rbegin(); rit != layerGroupsByLayerIndex.rend(); ++rit) { if (rit->second) { - f(*rit->second); + try { + f(*rit->second); + } catch (...) { + observer->onRenderError(std::current_exception()); + } } } } diff --git a/src/mbgl/renderer/renderer_impl.cpp b/src/mbgl/renderer/renderer_impl.cpp index 1ea642f9c..5c1b3ad1f 100644 --- a/src/mbgl/renderer/renderer_impl.cpp +++ b/src/mbgl/renderer/renderer_impl.cpp @@ -81,6 +81,10 @@ void Renderer::Impl::onShaderCompileFailed(shaders::BuiltIn shaderID, observer->onShaderCompileFailed(shaderID, type, additionalDefines); } +void Renderer::Impl::onRenderError(std::exception_ptr error) { + observer->onRenderError(error); +} + void Renderer::Impl::setObserver(RendererObserver* observer_) { observer = observer_ ? observer_ : &nullObserver(); } diff --git a/src/mbgl/renderer/renderer_impl.hpp b/src/mbgl/renderer/renderer_impl.hpp index b1137d267..9e27dabf6 100644 --- a/src/mbgl/renderer/renderer_impl.hpp +++ b/src/mbgl/renderer/renderer_impl.hpp @@ -33,6 +33,7 @@ public: void onPreCompileShader(shaders::BuiltIn, gfx::Backend::Type, const std::string&) override; void onPostCompileShader(shaders::BuiltIn, gfx::Backend::Type, const std::string&) override; void onShaderCompileFailed(shaders::BuiltIn, gfx::Backend::Type, const std::string&) override; + void onRenderError(std::exception_ptr) override; private: friend class Renderer; diff --git a/src/mbgl/vulkan/buffer_resource.cpp b/src/mbgl/vulkan/buffer_resource.cpp index a7a3478f3..eca0d8f5b 100644 --- a/src/mbgl/vulkan/buffer_resource.cpp +++ b/src/mbgl/vulkan/buffer_resource.cpp @@ -95,7 +95,7 @@ BufferResource::BufferResource( bufferAllocation = std::make_shared(allocator); if (!bufferAllocation->create(allocationInfo, bufferInfo)) { mbgl::Log::Error(mbgl::Event::Render, "Vulkan buffer allocation failed"); - return; + throw std::bad_alloc(); } vmaMapMemory(allocator, bufferAllocation->allocation, &bufferAllocation->mappedBuffer); diff --git a/src/mbgl/vulkan/context.cpp b/src/mbgl/vulkan/context.cpp index 2695c376c..d56181f45 100644 --- a/src/mbgl/vulkan/context.cpp +++ b/src/mbgl/vulkan/context.cpp @@ -441,11 +441,15 @@ gfx::ShaderProgramBasePtr Context::getGenericShader(gfx::ShaderRegistry& shaders } TileLayerGroupPtr Context::createTileLayerGroup(int32_t layerIndex, std::size_t initialCapacity, std::string name) { - return std::make_shared(layerIndex, initialCapacity, std::move(name)); + auto tileLayerGroup = std::make_shared(layerIndex, initialCapacity, std::move(name)); + tileLayerGroup->setObserver(observer); + return tileLayerGroup; } LayerGroupPtr Context::createLayerGroup(int32_t layerIndex, std::size_t initialCapacity, std::string name) { - return std::make_shared(layerIndex, initialCapacity, name); + auto layerGroup = std::make_shared(layerIndex, initialCapacity, name); + layerGroup->setObserver(observer); + return layerGroup; } bool Context::emplaceOrUpdateUniformBuffer(gfx::UniformBufferPtr& buffer, diff --git a/src/mbgl/vulkan/drawable.cpp b/src/mbgl/vulkan/drawable.cpp index e2eb2d498..507b8ac84 100644 --- a/src/mbgl/vulkan/drawable.cpp +++ b/src/mbgl/vulkan/drawable.cpp @@ -358,7 +358,7 @@ gfx::UniformBufferArray& Drawable::mutableUniformBuffers() { return impl->uniformBuffers; } -void Drawable::buildVulkanInputBindings() noexcept { +void Drawable::buildVulkanInputBindings() { MLN_TRACE_FUNC(); impl->vulkanVertexBuffers.clear(); @@ -412,7 +412,7 @@ void Drawable::buildVulkanInputBindings() noexcept { impl->pipelineInfo.updateVertexInputHash(); } -bool Drawable::bindAttributes(CommandEncoder& encoder) const noexcept { +bool Drawable::bindAttributes(CommandEncoder& encoder) const { MLN_TRACE_FUNC(); if (impl->vulkanVertexBuffers.empty()) return false; @@ -433,7 +433,7 @@ bool Drawable::bindAttributes(CommandEncoder& encoder) const noexcept { return true; } -bool Drawable::bindDescriptors(CommandEncoder& encoder) const noexcept { +bool Drawable::bindDescriptors(CommandEncoder& encoder) const { MLN_TRACE_FUNC(); if (!shader) return false; @@ -464,7 +464,7 @@ bool Drawable::bindDescriptors(CommandEncoder& encoder) const noexcept { return true; } -void Drawable::uploadTextures(UploadPass&) const noexcept { +void Drawable::uploadTextures(UploadPass&) const { MLN_TRACE_FUNC(); for (const auto& texture : textures) { if (texture) { diff --git a/src/mbgl/vulkan/dynamic_texture.cpp b/src/mbgl/vulkan/dynamic_texture.cpp index e0c4c602b..025125ef7 100644 --- a/src/mbgl/vulkan/dynamic_texture.cpp +++ b/src/mbgl/vulkan/dynamic_texture.cpp @@ -137,7 +137,7 @@ void DynamicTexture::uploadImage(const uint8_t* pixelData, gfx::TextureHandle& t UniqueBufferAllocation bufferAllocation = std::make_unique(allocator); if (!bufferAllocation->create(allocationInfo, bufferInfo)) { mbgl::Log::Error(mbgl::Event::Render, "Vulkan texture buffer allocation failed"); - return; + throw std::bad_alloc(); } vmaMapMemory(allocator, bufferAllocation->allocation, &bufferAllocation->mappedBuffer); diff --git a/src/mbgl/vulkan/texture2d.cpp b/src/mbgl/vulkan/texture2d.cpp index 4143cff93..bc5c5d4c6 100644 --- a/src/mbgl/vulkan/texture2d.cpp +++ b/src/mbgl/vulkan/texture2d.cpp @@ -125,7 +125,7 @@ size_t Texture2D::numChannels() const noexcept { } } -void Texture2D::create() noexcept { +void Texture2D::create() { if (textureDirty) { createTexture(); } @@ -136,7 +136,7 @@ void Texture2D::destroy(bool deferred) noexcept { destroySampler(deferred); } -void Texture2D::upload() noexcept { +void Texture2D::upload() { if (!imageData) return; upload(imageData->data.get(), imageData->size); @@ -144,12 +144,12 @@ void Texture2D::upload() noexcept { imageData.reset(); } -void Texture2D::upload(const void* pixelData, const Size& size_) noexcept { +void Texture2D::upload(const void* pixelData, const Size& size_) { setSize(size_); uploadSubRegion(pixelData, size_, 0, 0); } -void Texture2D::uploadSubRegion(const void* pixelData, const Size& size_, uint16_t xOffset, uint16_t yOffset) noexcept { +void Texture2D::uploadSubRegion(const void* pixelData, const Size& size_, uint16_t xOffset, uint16_t yOffset) { if (!pixelData || size_.width == 0 || size_.height == 0) return; const auto& encoder = context.createCommandEncoder(); @@ -163,7 +163,7 @@ void Texture2D::uploadSubRegion(const void* pixelData, uint16_t xOffset, uint16_t yOffset, const vk::UniqueCommandBuffer& commandBuffer, - bool submit) noexcept { + bool submit) { if (!pixelData || size_.width == 0 || size_.height == 0) return; create(); @@ -187,7 +187,7 @@ void Texture2D::uploadSubRegion(const void* pixelData, SharedBufferAllocation bufferAllocation = std::make_shared(allocator); if (!bufferAllocation->create(allocationInfo, bufferInfo)) { mbgl::Log::Error(mbgl::Event::Render, "Vulkan texture buffer allocation failed"); - return; + throw std::bad_alloc(); } vmaMapMemory(allocator, bufferAllocation->allocation, &bufferAllocation->mappedBuffer); @@ -348,7 +348,7 @@ void Texture2D::createTexture() { imageAllocation = std::make_shared(backend.getAllocator()); if (!imageAllocation->create(allocCreateInfo, imageCreateInfo)) { mbgl::Log::Error(mbgl::Event::Render, "Vulkan texture allocation failed"); - return; + throw std::bad_alloc(); } // defaults to eIdentity @@ -421,6 +421,9 @@ void Texture2D::createSampler() { } sampler = backend.getDevice()->createSampler(samplerCreateInfo, nullptr, backend.getDispatcher()); + if (!sampler) { + throw std::bad_alloc(); + } samplerStateDirty = false; lastModified = util::MonotonicTimer::now();