Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions ios/maps/MCMapView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,11 @@ extension MCMapView: MTKViewDelegate {
invalidate()
}

public func draw(in view: MTKView) {
public func draw(in view: MTKView) {
draw(in: view, present: true)
}

public func draw(in view: MTKView, present: Bool) {
guard !backgroundDisable else {
isPaused = true
mapInterface.resetIsInvalidated()
Expand Down Expand Up @@ -282,7 +286,7 @@ extension MCMapView: MTKViewDelegate {
if self.renderToImage {
commandBuffer.commit()
commandBuffer.waitUntilCompleted()
} else {
} else if present {

Comment on lines 286 to 290

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Commit the command buffer even when not presenting

When present is false and renderToImage is also false, the command buffer is never committed, so the completion handler never fires and renderSemaphore is never signaled. This makes the frame a no‑op and can deadlock the next draw(in:) call (e.g., MCMapViewMapReadyCallbacks calls draw(in:present:false) before renderToImageResult(), so the subsequent render waits forever on the semaphore). You should still commit() (without presenting) in the non‑present path to ensure the GPU work runs and the semaphore is released.

Useful? React with 👍 / 👎.

guard let drawable = view.currentDrawable else {
self.renderSemaphore.signal()
Expand Down Expand Up @@ -312,6 +316,9 @@ extension MCMapView: MTKViewDelegate {
renderToImageQueue.async { @Sendable [weak mapInterface] in
DispatchQueue.main.sync {
MainActor.assumeIsolated {
// also need to set size
self.frame.size = size

// set the drawable size to get correctly sized texture
self.drawableSize = size
}
Expand Down Expand Up @@ -512,7 +519,7 @@ private final class MCMapViewMapReadyCallbacks:
Task { @MainActor in
guard let delegate = self.delegate else { return }

delegate.draw(in: delegate)
delegate.draw(in: delegate, present: false)

callbackQueue?
.async {
Expand Down
19 changes: 19 additions & 0 deletions shared/src/map/camera/MapCamera2d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,17 @@ void MapCamera2d::viewportSizeChanged() {
zoom = std::clamp(zoom, zoomMax, zoomMin);
}

// Apply pending bounding box if viewport size is now known
if (pendingBoundingBox.has_value() && viewportSize.x > 0 && viewportSize.y > 0) {
auto pending = pendingBoundingBox.value();
bool frozenBefore = cameraFrozen;
cameraFrozen = false;
moveToBoundingBox(pending.boundingBox, pending.paddingPc, pending.animated,
pending.minZoom, pending.maxZoom);
cameraFrozen = frozenBefore;
pendingBoundingBox = std::nullopt;
}

notifyListeners(ListenerType::BOUNDS);
}

Expand Down Expand Up @@ -183,9 +194,14 @@ void MapCamera2d::moveToBoundingBox(const RectCoord &boundingBox, float paddingP

Vec2I viewSize = mapInterface->getRenderingContext()->getViewportSize();
if (viewSize.x == 0 && viewSize.y == 0) {
// Viewport size not yet known, store parameters for later
pendingBoundingBox = PendingBoundingBox(boundingBox, paddingPc, animated, minZoom, maxZoom);
return;
}

// Clear any pending bounding box since we're applying it now
pendingBoundingBox = std::nullopt;

RectCoord mapSystemBBox = conversionHelper->convertRect(mapCoordinateSystem.identifier, boundingBox);
float newLeft = mapSystemBBox.topLeft.x + paddingPc * (mapSystemBBox.topLeft.x - mapSystemBBox.bottomRight.x);
float newRight = mapSystemBBox.bottomRight.x + paddingPc * (mapSystemBBox.bottomRight.x - mapSystemBBox.topLeft.x);
Expand Down Expand Up @@ -576,6 +592,9 @@ void MapCamera2d::notifyListeners(const int &listenerType) {
(listenerType & ListenerType::BOUNDS) ? std::optional<RectCoord>(getVisibleRect()) : std::nullopt;
double angle = this->angle;
double zoom = this->zoom;
if (pendingBoundingBox) {
return;
}
std::lock_guard<std::recursive_mutex> lock(listenerMutex);
for (auto listener : listeners) {
if (listenerType & ListenerType::BOUNDS) {
Expand Down
14 changes: 14 additions & 0 deletions shared/src/map/camera/MapCamera2d.h
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,20 @@ class MapCamera2d : public MapCameraInterface,

RectCoord bounds;

struct PendingBoundingBox {
RectCoord boundingBox;
float paddingPc;
bool animated;
std::optional<double> minZoom;
std::optional<double> maxZoom;

PendingBoundingBox(const RectCoord &bbox, float padding, bool anim,
std::optional<double> minZ, std::optional<double> maxZ)
: boundingBox(bbox), paddingPc(padding), animated(anim),
minZoom(minZ), maxZoom(maxZ) {}
};
std::optional<PendingBoundingBox> pendingBoundingBox = std::nullopt;

std::recursive_mutex vpDataMutex;
std::optional<RectCoord> lastVpBounds = std::nullopt;
std::optional<double> lastVpRotation = std::nullopt;
Expand Down
17 changes: 17 additions & 0 deletions shared/src/map/camera/MapCamera3d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,14 @@ void MapCamera3d::viewportSizeChanged() {
updateZoom(zoom);
}

// Apply pending bounding box if viewport size is now known
if (pendingBoundingBox.has_value() && viewportSize.x > 0 && viewportSize.y > 0) {
auto pending = pendingBoundingBox.value();
pendingBoundingBox = std::nullopt;
moveToBoundingBox(pending.boundingBox, pending.paddingPc, pending.animated,
pending.minZoom, pending.maxZoom);
}

updateMatrices();

notifyListeners(ListenerType::BOUNDS);
Expand Down Expand Up @@ -241,6 +249,15 @@ void MapCamera3d::moveToBoundingBox(const RectCoord &boundingBox, float paddingP
assert(boundingBox.topLeft.systemIdentifier == 4326);

Vec2I sizeViewport = mapInterface->getRenderingContext()->getViewportSize();

if (sizeViewport.x == 0 && sizeViewport.y == 0) {
// Viewport size not yet known, store parameters for later
pendingBoundingBox = PendingBoundingBox(boundingBox, paddingPc, animated, minZoom, maxZoom);
return;
}

// Clear any pending bounding box since we're applying it now
pendingBoundingBox = std::nullopt;

auto distance =
calculateDistance(boundingBox.topLeft.y, boundingBox.topLeft.x, boundingBox.bottomRight.y, boundingBox.bottomRight.x);
Expand Down
14 changes: 14 additions & 0 deletions shared/src/map/camera/MapCamera3d.h
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,20 @@ class MapCamera3d : public MapCameraInterface,

RectCoord bounds;

struct PendingBoundingBox {
RectCoord boundingBox;
float paddingPc;
bool animated;
std::optional<double> minZoom;
std::optional<double> maxZoom;

PendingBoundingBox(const RectCoord &bbox, float padding, bool anim,
std::optional<double> minZ, std::optional<double> maxZ)
: boundingBox(bbox), paddingPc(padding), animated(anim),
minZoom(minZ), maxZoom(maxZ) {}
};
std::optional<PendingBoundingBox> pendingBoundingBox = std::nullopt;

std::optional<RectCoord> lastVpBounds = std::nullopt;
std::optional<double> lastVpRotation = std::nullopt;
std::optional<double> lastVpZoom = std::nullopt;
Expand Down