Handle allocation failure [Core feature] (#4178)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
Alex Cristici
2026-04-22 14:03:58 +03:00
committed by GitHub
parent 9f8667cf44
commit 14a81ec831
44 changed files with 295 additions and 85 deletions

View File

@@ -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

View File

@@ -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 <typename Image>
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

View File

@@ -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:

View File

@@ -43,11 +43,11 @@ public:
std::unique_ptr<RenderLayer> createRenderLayer(Immutable<style::Layer::Impl>) noexcept;
/// Returns a new Bucket instance on success call; returns `nullptr` otherwise.
std::unique_ptr<Bucket> createBucket(const BucketParameters&,
const std::vector<Immutable<style::LayerProperties>>&) noexcept;
const std::vector<Immutable<style::LayerProperties>>&);
/// Returns a new Layout instance on success call; returns `nullptr` otherwise.
std::unique_ptr<Layout> createLayout(const LayoutParameters&,
std::unique_ptr<GeometryTileLayer>,
const std::vector<Immutable<style::LayerProperties>>&) noexcept;
const std::vector<Immutable<style::LayerProperties>>&);
/**
* @brief a build-time flag to enable/disable annotations in

View File

@@ -89,6 +89,9 @@ public:
virtual void onSpriteLoaded(const std::optional<style::Sprite>&) {}
virtual void onSpriteError(const std::optional<style::Sprite>&, std::exception_ptr) {}
virtual void onSpriteRequested(const std::optional<style::Sprite>&) {}
// Renderer
virtual void onRenderError(std::exception_ptr) {}
};
} // namespace mbgl

View File

@@ -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()); }

View File

@@ -68,15 +68,15 @@ protected:
// For testing only.
Drawable(std::unique_ptr<Impl>);
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> impl;

View File

@@ -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;

View File

@@ -1,6 +1,7 @@
#pragma once
#include <mbgl/gfx/drawable.hpp>
#include <mbgl/gfx/context.hpp>
#include <mbgl/renderer/render_pass.hpp>
#include <mbgl/tile/tile_id.hpp>
#include <mbgl/util/identity.hpp>
@@ -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<LayerTweakerWeakPtr> layerTweakers;
std::string name;
gfx::ContextObserver* observer;
};
/**
@@ -132,8 +137,17 @@ public:
template <typename Func /* void(gfx::Drawable&) */>
std::size_t visitDrawables(Func f) {
assert(drawablesByTile.size() == sortedDrawables.size());
unordered_set<gfx::Drawable*> drawablesFailed;
for (auto* drawable : sortedDrawables) {
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 <typename Func /* void(gfx::Drawable&) */>
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<gfx::Drawable*> 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 <typename Func /* void(gfx::Drawable&) */>
std::size_t visitDrawables(Func f) {
for (const auto& item : drawables) {
unordered_set<gfx::Drawable*> drawablesFailed;
for (auto& item : drawables) {
if (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();
}

View File

@@ -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

View File

@@ -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> impl;

View File

@@ -64,18 +64,18 @@ public:
bool isDirty() const { return samplerStateDirty || textureDirty; }
bool isModifiedAfter(const std::chrono::duration<double>& 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; };

View File

@@ -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> mailbox;
ActorRef<RendererObserver> delegate;

View File

@@ -1656,5 +1656,17 @@ void NativeMapView::onSpriteRequested(const std::optional<style::Sprite>& sprite
}
}
void NativeMapView::onRenderError(std::exception_ptr) {
assert(vm != nullptr);
android::UniqueEnv _env = android::AttachEnv();
static auto& javaClass = jni::Class<NativeMapView>::Singleton(*_env);
static auto onRenderError = javaClass.GetMethod<void()>(*_env, "onRenderError");
auto weakReference = javaPeer.get(*_env);
if (weakReference) {
weakReference.Call(*_env, onRenderError);
}
}
} // namespace android
} // namespace mbgl

View File

@@ -346,6 +346,9 @@ public:
void onSpriteError(const std::optional<mbgl::style::Sprite>&, std::exception_ptr) override;
void onSpriteRequested(const std::optional<mbgl::style::Sprite>&) override;
// Renderer
void onRenderError(std::exception_ptr) override;
private:
std::shared_ptr<AndroidRendererFrontend> rendererFrontend;

View File

@@ -58,6 +58,8 @@ class MapChangeReceiver implements NativeMapView.StateCallback {
= new CopyOnWriteArrayList<>();
private final List<MapView.OnSpriteRequestedListener> onSpriteRequestedList
= new CopyOnWriteArrayList<>();
private final List<MapView.OnRenderErrorListener> 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();
}
}

View File

@@ -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.
* <p>
@@ -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.
* <p>
* {@link MapView#addOnRenderErrorListener(OnRenderErrorListener)}
* </p>
*/
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.
*

View File

@@ -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();
}
}

View File

@@ -87,6 +87,7 @@ public:
void onSpriteLoaded(const std::optional<mbgl::style::Sprite>&) override;
void onSpriteError(const std::optional<mbgl::style::Sprite>&, std::exception_ptr) override;
void onSpriteRequested(const std::optional<mbgl::style::Sprite>&) override;
void onRenderError(std::exception_ptr) override;
protected:
/// Cocoa map view that this adapter bridges to.

View File

@@ -222,3 +222,5 @@ void MLNMapViewImpl::onSpriteRequested(const std::optional<mbgl::style::Sprite>&
[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]; }

View File

@@ -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];
}

View File

@@ -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
/**

View File

@@ -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;

View File

@@ -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) {}
}

View File

@@ -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<TileLayerGroupGL>(layerIndex, initialCapacity, std::move(name));
auto tileLayerGroup = std::make_shared<TileLayerGroupGL>(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<LayerGroupGL>(layerIndex, initialCapacity, std::move(name));
auto layerGroup = std::make_shared<LayerGroupGL>(layerIndex, initialCapacity, std::move(name));
layerGroup->setObserver(observer);
return layerGroup;
}
gfx::Texture2DPtr Context::createTexture2D() {

View File

@@ -154,6 +154,9 @@ TextureID Texture2DPool::allocateGLMemory(const Texture2DDesc& desc) {
Enum<gfx::TexturePixelType>::to(desc.pixelFormat),
Enum<gfx::TextureChannelDataType>::to(desc.channelType),
nullptr));
if (glGetError()) {
throw std::bad_alloc();
}
// Update stats
context->renderingStats().numCreatedTextures++;

View File

@@ -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<size_t>(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);

View File

@@ -36,6 +36,9 @@ std::unique_ptr<gfx::VertexBufferResource> UploadPass::createVertexBufferResourc
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)));
if (glGetError()) {
throw std::bad_alloc();
}
return std::make_unique<gl::VertexBufferResource>(std::move(result), static_cast<int>(size));
}
@@ -61,6 +64,9 @@ std::unique_ptr<gfx::IndexBufferResource> UploadPass::createIndexBufferResource(
commandEncoder.context.bindVertexArray = 0;
commandEncoder.context.globalVertexArrayState.indexBuffer = result;
MBGL_CHECK_ERROR(glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, data, Enum<gfx::BufferUsageType>::to(usage)));
if (glGetError()) {
throw std::bad_alloc();
}
return std::make_unique<gl::IndexBufferResource>(std::move(result), static_cast<int>(size));
}

View File

@@ -31,8 +31,8 @@ std::unique_ptr<style::Layer> LayerManager::createLayer(const std::string& type,
return nullptr;
}
std::unique_ptr<Bucket> LayerManager::createBucket(
const BucketParameters& parameters, const std::vector<Immutable<style::LayerProperties>>& layers) noexcept {
std::unique_ptr<Bucket> LayerManager::createBucket(const BucketParameters& parameters,
const std::vector<Immutable<style::LayerProperties>>& layers) {
assert(!layers.empty());
assert(parameters.layerType->layout == style::LayerTypeInfo::Layout::NotRequired);
LayerFactory* factory = getFactory(parameters.layerType);
@@ -40,10 +40,9 @@ std::unique_ptr<Bucket> LayerManager::createBucket(
return factory->createBucket(parameters, layers);
}
std::unique_ptr<Layout> LayerManager::createLayout(
const LayoutParameters& parameters,
std::unique_ptr<Layout> LayerManager::createLayout(const LayoutParameters& parameters,
std::unique_ptr<GeometryTileLayer> tileLayer,
const std::vector<Immutable<style::LayerProperties>>& layers) noexcept {
const std::vector<Immutable<style::LayerProperties>>& layers) {
assert(!layers.empty());
assert(parameters.bucketParameters.layerType->layout == style::LayerTypeInfo::Layout::Required);
LayerFactory* factory = getFactory(parameters.bucketParameters.layerType);

View File

@@ -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

View File

@@ -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&);

View File

@@ -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;
}

View File

@@ -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<TileLayerGroup>(layerIndex, initialCapacity, std::move(name));
auto tileLayerGroup = std::make_shared<TileLayerGroup>(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<LayerGroup>(layerIndex, initialCapacity, name);
auto layerGroup = std::make_shared<LayerGroup>(layerIndex, initialCapacity, name);
layerGroup->setObserver(observer);
return layerGroup;
}
gfx::Texture2DPtr Context::createTexture2D() {

View File

@@ -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<const mtl::VertexBufferResource*>(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();

View File

@@ -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);

View File

@@ -977,7 +977,11 @@ void RenderOrchestrator::updateLayers(gfx::ShaderRegistry& shaders,
renderLayer.removeAllDrawables();
}
#endif
try {
renderLayer.update(shaders, context, state, updateParameters, renderTree, changes);
} catch (...) {
observer->onRenderError(std::current_exception());
}
}
addChanges(changes);
}

View File

@@ -112,7 +112,11 @@ public:
void visitLayerGroups(Func f) {
for (auto& pair : layerGroupsByLayerIndex) {
if (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) {
try {
f(*rit->second);
} catch (...) {
observer->onRenderError(std::current_exception());
}
}
}
}

View File

@@ -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();
}

View File

@@ -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;

View File

@@ -95,7 +95,7 @@ BufferResource::BufferResource(
bufferAllocation = std::make_shared<BufferAllocation>(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);

View File

@@ -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<TileLayerGroup>(layerIndex, initialCapacity, std::move(name));
auto tileLayerGroup = std::make_shared<TileLayerGroup>(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<LayerGroup>(layerIndex, initialCapacity, name);
auto layerGroup = std::make_shared<LayerGroup>(layerIndex, initialCapacity, name);
layerGroup->setObserver(observer);
return layerGroup;
}
bool Context::emplaceOrUpdateUniformBuffer(gfx::UniformBufferPtr& buffer,

View File

@@ -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) {

View File

@@ -137,7 +137,7 @@ void DynamicTexture::uploadImage(const uint8_t* pixelData, gfx::TextureHandle& t
UniqueBufferAllocation bufferAllocation = std::make_unique<BufferAllocation>(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);

View File

@@ -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<BufferAllocation>(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<ImageAllocation>(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();