feat: add continuous retained render ranges
Some checks failed
validate-scripts / validate-scripts (push) Has been cancelled
Some checks failed
validate-scripts / validate-scripts (push) Has been cancelled
This commit is contained in:
@@ -58,6 +58,7 @@ public:
|
||||
|
||||
/** Enables or disables the map-scoped retained static underlay optimization. */
|
||||
void setRetainedUnderlayEnabled(bool);
|
||||
bool setContinuousLayerRange(std::string startLayerId, std::string endLayerId);
|
||||
bool isRetainedUnderlayEnabled() const;
|
||||
RetainedUnderlayDiagnostics getRetainedUnderlayDiagnostics() const;
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
namespace mbgl {
|
||||
@@ -12,6 +13,44 @@ struct RetainedUnderlayDiagnostics {
|
||||
uint64_t fallback = 0;
|
||||
uint64_t invalidation = 0;
|
||||
std::string lastReason;
|
||||
bool rangeConfigured = false;
|
||||
bool rangeValid = false;
|
||||
std::string startLayerId;
|
||||
std::string endLayerId;
|
||||
int32_t startIndex = -1;
|
||||
int32_t endIndex = -1;
|
||||
bool bottomPresent = false;
|
||||
uint64_t bottomBuild = 0;
|
||||
uint64_t bottomReuse = 0;
|
||||
uint64_t bottomInvalidation = 0;
|
||||
bool topPresent = false;
|
||||
uint64_t topBuild = 0;
|
||||
uint64_t topReuse = 0;
|
||||
uint64_t topInvalidation = 0;
|
||||
bool rangeFallback = false;
|
||||
};
|
||||
|
||||
struct ContinuousLayerRangeConfig {
|
||||
bool configured = false;
|
||||
std::string startLayerId;
|
||||
std::string endLayerId;
|
||||
uint64_t revision = 0;
|
||||
};
|
||||
|
||||
struct ContinuousLayerRangeResolution {
|
||||
bool configured = false;
|
||||
bool valid = false;
|
||||
std::string startLayerId;
|
||||
std::string endLayerId;
|
||||
int32_t startIndex = -1;
|
||||
int32_t endIndex = -1;
|
||||
uint64_t configRevision = 0;
|
||||
std::string reason{"not-configured"};
|
||||
|
||||
int32_t begin() const noexcept { return startIndex; }
|
||||
int32_t endExclusive() const noexcept { return valid ? endIndex + 1 : -1; }
|
||||
bool bottomPresent() const noexcept { return valid && startIndex > 0; }
|
||||
bool topPresent(int32_t layerCount) const noexcept { return valid && endExclusive() < layerCount; }
|
||||
};
|
||||
|
||||
} // namespace mbgl
|
||||
|
||||
@@ -67,6 +67,9 @@ class WeatherRuntimeBridgeModule(private val context: ReactApplicationContext) :
|
||||
map.setRetainedUnderlayEnabled(enabled)
|
||||
promise.resolve("ACCEPTED")
|
||||
}
|
||||
@ReactMethod override fun setContinuousLayerRange(reactTag: Double, startLayerId: String, endLayerId: String, promise: Promise) = withMap(reactTag, promise) { map ->
|
||||
promise.resolve(if (map.setContinuousLayerRange(startLayerId, endLayerId)) "ACCEPTED" else "REJECTED")
|
||||
}
|
||||
@ReactMethod override fun getRetainedUnderlayDiagnostics(reactTag: Double, promise: Promise) = withMap(reactTag, promise) { map ->
|
||||
val value = map.retainedUnderlayDiagnostics
|
||||
promise.resolve(Arguments.createMap().apply {
|
||||
@@ -76,6 +79,21 @@ class WeatherRuntimeBridgeModule(private val context: ReactApplicationContext) :
|
||||
putDouble("fallback", value.fallback.toDouble())
|
||||
putDouble("invalidation", value.invalidation.toDouble())
|
||||
putString("lastReason", value.lastReason)
|
||||
putBoolean("rangeConfigured", value.rangeConfigured)
|
||||
putBoolean("rangeValid", value.rangeValid)
|
||||
putString("startLayerId", value.startLayerId)
|
||||
putString("endLayerId", value.endLayerId)
|
||||
putInt("startIndex", value.startIndex)
|
||||
putInt("endIndex", value.endIndex)
|
||||
putBoolean("bottomPresent", value.bottomPresent)
|
||||
putDouble("bottomBuild", value.bottomBuild.toDouble())
|
||||
putDouble("bottomReuse", value.bottomReuse.toDouble())
|
||||
putDouble("bottomInvalidation", value.bottomInvalidation.toDouble())
|
||||
putBoolean("topPresent", value.topPresent)
|
||||
putDouble("topBuild", value.topBuild.toDouble())
|
||||
putDouble("topReuse", value.topReuse.toDouble())
|
||||
putDouble("topInvalidation", value.topInvalidation.toDouble())
|
||||
putBoolean("fallback", value.fallback)
|
||||
})
|
||||
}
|
||||
@ReactMethod override fun clearWindFields(reactTag: Double, promise: Promise) = command(reactTag, promise) { it.clearWindFields() }
|
||||
|
||||
@@ -164,12 +164,23 @@ RCT_EXPORT_MODULE(WeatherRuntimeBridge)
|
||||
resolve(@"ACCEPTED");
|
||||
}];
|
||||
}
|
||||
- (void)setContinuousLayerRange:(double)reactTag startLayerId:(NSString *)startLayerId endLayerId:(NSString *)endLayerId resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
|
||||
[self withMapForTag:reactTag requireRuntime:NO resolve:resolve reject:reject action:^(MLNMapView *map, MLNParticleFieldRuntime *runtime) {
|
||||
resolve([map setContinuousLayerRangeFromLayerIdentifier:startLayerId toLayerIdentifier:endLayerId] ? @"ACCEPTED" : @"REJECTED");
|
||||
}];
|
||||
}
|
||||
- (void)getRetainedUnderlayDiagnostics:(double)reactTag resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
|
||||
[self withMapForTag:reactTag requireRuntime:NO resolve:resolve reject:reject action:^(MLNMapView *map, MLNParticleFieldRuntime *runtime) {
|
||||
MLNRetainedUnderlayDiagnostics *value = map.retainedUnderlayDiagnostics;
|
||||
resolve(@{ @"enabled": @(value.enabled), @"build": @(value.build), @"reuse": @(value.reuse),
|
||||
@"fallback": @(value.fallback), @"invalidation": @(value.invalidation),
|
||||
@"lastReason": value.lastReason });
|
||||
@"lastReason": value.lastReason, @"rangeConfigured": @(value.rangeConfigured),
|
||||
@"rangeValid": @(value.rangeValid), @"startLayerId": value.startLayerId,
|
||||
@"endLayerId": value.endLayerId, @"startIndex": @(value.startIndex), @"endIndex": @(value.endIndex),
|
||||
@"bottomPresent": @(value.bottomPresent), @"bottomBuild": @(value.bottomBuild),
|
||||
@"bottomReuse": @(value.bottomReuse), @"bottomInvalidation": @(value.bottomInvalidation),
|
||||
@"topPresent": @(value.topPresent), @"topBuild": @(value.topBuild),
|
||||
@"topReuse": @(value.topReuse), @"topInvalidation": @(value.topInvalidation) });
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
@@ -9,12 +9,18 @@ type FieldSubmitResult = "REJECTED_SOURCE_MODE" | "REJECTED_ADAPTER" | "REJECTED
|
||||
type EncodedFieldSubmitResult = "ACCEPTED_INSERTED" | "ACCEPTED_REPLACED_REVISION" | "DUPLICATE" | "REJECTED_DECODE" | "REJECTED_CONTRACT" | "REJECTED_INVALID_FIELD_KEY" | "REJECTED_IDENTITY_CONFLICT" | "REJECTED_STALE_REVISION" | "REJECTED_INCOMPATIBLE" | "REJECTED_CAPACITY";
|
||||
type ColorStop = { speedMps: number; red: number; green: number; blue: number; alpha: number };
|
||||
type RuntimeSnapshot = { state: RuntimeState; playbackState: PlaybackState; targetFps: TargetFps; disposed: boolean };
|
||||
type RetainedUnderlaySnapshot = { enabled: boolean; build: number; reuse: number; fallback: number; invalidation: number; lastReason: string };
|
||||
type RetainedUnderlaySnapshot = {
|
||||
enabled: boolean; build: number; reuse: number; fallback: number; invalidation: number; lastReason: string;
|
||||
rangeConfigured: boolean; rangeValid: boolean; startLayerId: string; endLayerId: string;
|
||||
startIndex: number; endIndex: number; bottomPresent: boolean; bottomBuild: number; bottomReuse: number;
|
||||
bottomInvalidation: number; topPresent: boolean; topBuild: number; topReuse: number; topInvalidation: number;
|
||||
};
|
||||
export interface Spec extends TurboModule {
|
||||
attach(reactTag: number): Promise<RuntimeSnapshot>;
|
||||
detach(reactTag: number): Promise<void>;
|
||||
getState(reactTag: number): Promise<RuntimeSnapshot>;
|
||||
setRetainedUnderlayEnabled(reactTag: number, enabled: boolean): Promise<CommandResult>;
|
||||
setContinuousLayerRange(reactTag: number, startLayerId: string, endLayerId: string): Promise<CommandResult>;
|
||||
getRetainedUnderlayDiagnostics(reactTag: number): Promise<RetainedUnderlaySnapshot>;
|
||||
clearWindFields(reactTag: number): Promise<CommandResult>;
|
||||
submitWindField(reactTag: number, metadataJson: string, payloadFileUri: string): Promise<FieldSubmitResult>;
|
||||
|
||||
@@ -13,6 +13,10 @@ function nativeBridge() {
|
||||
export const RendererRuntime = {
|
||||
setRetainedUnderlayEnabled: (reactTag: number, enabled: boolean): Promise<CommandResult> =>
|
||||
nativeBridge().setRetainedUnderlayEnabled(reactTag, enabled),
|
||||
setContinuousLayerRange: (reactTag: number, startLayerId: string, endLayerId: string): Promise<CommandResult> => {
|
||||
if (startLayerId.length === 0 || endLayerId.length === 0) return Promise.resolve("REJECTED");
|
||||
return nativeBridge().setContinuousLayerRange(reactTag, startLayerId, endLayerId);
|
||||
},
|
||||
getRetainedUnderlayDiagnostics: (reactTag: number): Promise<RetainedUnderlayDiagnostics> =>
|
||||
nativeBridge().getRetainedUnderlayDiagnostics(reactTag),
|
||||
} as const;
|
||||
|
||||
@@ -28,4 +28,18 @@ export type RetainedUnderlayDiagnostics = Readonly<{
|
||||
fallback: number;
|
||||
invalidation: number;
|
||||
lastReason: string;
|
||||
rangeConfigured: boolean;
|
||||
rangeValid: boolean;
|
||||
startLayerId: string;
|
||||
endLayerId: string;
|
||||
startIndex: number;
|
||||
endIndex: number;
|
||||
bottomPresent: boolean;
|
||||
bottomBuild: number;
|
||||
bottomReuse: number;
|
||||
bottomInvalidation: number;
|
||||
topPresent: boolean;
|
||||
topBuild: number;
|
||||
topReuse: number;
|
||||
topInvalidation: number;
|
||||
}>;
|
||||
|
||||
@@ -720,15 +720,36 @@ void NativeMapView::setRetainedUnderlayEnabled(JNIEnv&, jni::jboolean enabled) {
|
||||
map->setRetainedUnderlayEnabled(enabled);
|
||||
}
|
||||
|
||||
jni::jboolean NativeMapView::setContinuousLayerRange(JNIEnv& env,
|
||||
const jni::String& startLayerId,
|
||||
const jni::String& endLayerId) {
|
||||
return map->setContinuousLayerRange(jni::Make<std::string>(env, startLayerId),
|
||||
jni::Make<std::string>(env, endLayerId));
|
||||
}
|
||||
|
||||
jni::Local<jni::Array<jni::String>> NativeMapView::getRetainedUnderlayDiagnostics(JNIEnv& env) {
|
||||
const auto snapshot = map->getRetainedUnderlayDiagnostics();
|
||||
const std::array<std::string, 6> values = {
|
||||
const std::array<std::string, 20> values = {
|
||||
snapshot.enabled ? "true" : "false",
|
||||
std::to_string(snapshot.build),
|
||||
std::to_string(snapshot.reuse),
|
||||
std::to_string(snapshot.fallback),
|
||||
std::to_string(snapshot.invalidation),
|
||||
snapshot.lastReason,
|
||||
snapshot.rangeConfigured ? "true" : "false",
|
||||
snapshot.rangeValid ? "true" : "false",
|
||||
snapshot.startLayerId,
|
||||
snapshot.endLayerId,
|
||||
std::to_string(snapshot.startIndex),
|
||||
std::to_string(snapshot.endIndex),
|
||||
snapshot.bottomPresent ? "true" : "false",
|
||||
std::to_string(snapshot.bottomBuild),
|
||||
std::to_string(snapshot.bottomReuse),
|
||||
std::to_string(snapshot.bottomInvalidation),
|
||||
snapshot.topPresent ? "true" : "false",
|
||||
std::to_string(snapshot.topBuild),
|
||||
std::to_string(snapshot.topReuse),
|
||||
std::to_string(snapshot.topInvalidation),
|
||||
};
|
||||
auto result = jni::Array<jni::String>::New(env, values.size());
|
||||
for (std::size_t index = 0; index < values.size(); ++index) {
|
||||
@@ -1817,6 +1838,7 @@ void NativeMapView::registerNative(jni::JNIEnv& env) {
|
||||
METHOD(&NativeMapView::setDebug, "nativeSetDebug"),
|
||||
METHOD(&NativeMapView::getDebug, "nativeGetDebug"),
|
||||
METHOD(&NativeMapView::setRetainedUnderlayEnabled, "nativeSetRetainedUnderlayEnabled"),
|
||||
METHOD(&NativeMapView::setContinuousLayerRange, "nativeSetContinuousLayerRange"),
|
||||
METHOD(&NativeMapView::getRetainedUnderlayDiagnostics, "nativeGetRetainedUnderlayDiagnostics"),
|
||||
METHOD(&NativeMapView::getActionJournalLogFiles, "nativeGetActionJournalLogFiles"),
|
||||
METHOD(&NativeMapView::getActionJournalLog, "nativeGetActionJournalLog"),
|
||||
|
||||
@@ -203,6 +203,8 @@ public:
|
||||
|
||||
void setRetainedUnderlayEnabled(JNIEnv&, jni::jboolean);
|
||||
|
||||
jni::jboolean setContinuousLayerRange(JNIEnv&, const jni::String&, const jni::String&);
|
||||
|
||||
jni::Local<jni::Array<jni::String>> getRetainedUnderlayDiagnostics(JNIEnv&);
|
||||
|
||||
jni::Local<jni::Array<jni::String>> getActionJournalLogFiles(JNIEnv&);
|
||||
|
||||
@@ -116,6 +116,11 @@ public final class MapLibreMap {
|
||||
nativeMapView.setRetainedUnderlayEnabled(enabled);
|
||||
}
|
||||
|
||||
/** Configures the inclusive Style-layer interval rendered continuously. */
|
||||
public boolean setContinuousLayerRange(@NonNull String startLayerId, @NonNull String endLayerId) {
|
||||
return nativeMapView.setContinuousLayerRange(startLayerId, endLayerId);
|
||||
}
|
||||
|
||||
/** Returns whether the retained static underlay optimization is enabled for this map. */
|
||||
public boolean isRetainedUnderlayEnabled() {
|
||||
return nativeMapView.getRetainedUnderlayDiagnostics().enabled;
|
||||
|
||||
@@ -288,6 +288,8 @@ interface NativeMap {
|
||||
|
||||
void setRetainedUnderlayEnabled(boolean enabled);
|
||||
|
||||
boolean setContinuousLayerRange(String startLayerId, String endLayerId);
|
||||
|
||||
RetainedUnderlayDiagnostics getRetainedUnderlayDiagnostics();
|
||||
|
||||
String[] getActionJournalLogFiles();
|
||||
|
||||
@@ -661,10 +661,16 @@ final class NativeMapView implements NativeMap {
|
||||
nativeSetRetainedUnderlayEnabled(enabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setContinuousLayerRange(@NonNull String startLayerId, @NonNull String endLayerId) {
|
||||
if (checkState("setContinuousLayerRange")) return false;
|
||||
return nativeSetContinuousLayerRange(startLayerId, endLayerId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RetainedUnderlayDiagnostics getRetainedUnderlayDiagnostics() {
|
||||
if (checkState("getRetainedUnderlayDiagnostics")) {
|
||||
return new RetainedUnderlayDiagnostics(new String[] {"true", "0", "0", "0", "0", "unavailable"});
|
||||
return RetainedUnderlayDiagnostics.unavailable();
|
||||
}
|
||||
return new RetainedUnderlayDiagnostics(nativeGetRetainedUnderlayDiagnostics());
|
||||
}
|
||||
@@ -1721,6 +1727,9 @@ final class NativeMapView implements NativeMap {
|
||||
@Keep
|
||||
private native void nativeSetRetainedUnderlayEnabled(boolean enabled);
|
||||
|
||||
@Keep
|
||||
private native boolean nativeSetContinuousLayerRange(String startLayerId, String endLayerId);
|
||||
|
||||
@Keep
|
||||
private native String[] nativeGetRetainedUnderlayDiagnostics();
|
||||
|
||||
|
||||
@@ -12,10 +12,24 @@ public final class RetainedUnderlayDiagnostics {
|
||||
public final long fallback;
|
||||
public final long invalidation;
|
||||
@NonNull public final String lastReason;
|
||||
public final boolean rangeConfigured;
|
||||
public final boolean rangeValid;
|
||||
@NonNull public final String startLayerId;
|
||||
@NonNull public final String endLayerId;
|
||||
public final int startIndex;
|
||||
public final int endIndex;
|
||||
public final boolean bottomPresent;
|
||||
public final long bottomBuild;
|
||||
public final long bottomReuse;
|
||||
public final long bottomInvalidation;
|
||||
public final boolean topPresent;
|
||||
public final long topBuild;
|
||||
public final long topReuse;
|
||||
public final long topInvalidation;
|
||||
|
||||
RetainedUnderlayDiagnostics(@NonNull String[] values) {
|
||||
if (values.length != 6) {
|
||||
throw new IllegalArgumentException("Retained-underlay diagnostics require six values");
|
||||
if (values.length != 20) {
|
||||
throw new IllegalArgumentException("Retained-range diagnostics require 20 values");
|
||||
}
|
||||
enabled = Boolean.parseBoolean(values[0]);
|
||||
build = Long.parseLong(values[1]);
|
||||
@@ -23,5 +37,26 @@ public final class RetainedUnderlayDiagnostics {
|
||||
fallback = Long.parseLong(values[3]);
|
||||
invalidation = Long.parseLong(values[4]);
|
||||
lastReason = values[5];
|
||||
rangeConfigured = Boolean.parseBoolean(values[6]);
|
||||
rangeValid = Boolean.parseBoolean(values[7]);
|
||||
startLayerId = values[8];
|
||||
endLayerId = values[9];
|
||||
startIndex = Integer.parseInt(values[10]);
|
||||
endIndex = Integer.parseInt(values[11]);
|
||||
bottomPresent = Boolean.parseBoolean(values[12]);
|
||||
bottomBuild = Long.parseLong(values[13]);
|
||||
bottomReuse = Long.parseLong(values[14]);
|
||||
bottomInvalidation = Long.parseLong(values[15]);
|
||||
topPresent = Boolean.parseBoolean(values[16]);
|
||||
topBuild = Long.parseLong(values[17]);
|
||||
topReuse = Long.parseLong(values[18]);
|
||||
topInvalidation = Long.parseLong(values[19]);
|
||||
}
|
||||
|
||||
static RetainedUnderlayDiagnostics unavailable() {
|
||||
return new RetainedUnderlayDiagnostics(new String[] {
|
||||
"true", "0", "0", "0", "0", "unavailable", "false", "false", "", "", "-1", "-1",
|
||||
"false", "0", "0", "0", "false", "0", "0", "0"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,20 @@ MLN_EXPORT
|
||||
@property (nonatomic, readonly) uint64_t fallback;
|
||||
@property (nonatomic, readonly) uint64_t invalidation;
|
||||
@property (nonatomic, copy, readonly) NSString *lastReason;
|
||||
@property (nonatomic, readonly) BOOL rangeConfigured;
|
||||
@property (nonatomic, readonly) BOOL rangeValid;
|
||||
@property (nonatomic, copy, readonly) NSString *startLayerId;
|
||||
@property (nonatomic, copy, readonly) NSString *endLayerId;
|
||||
@property (nonatomic, readonly) NSInteger startIndex;
|
||||
@property (nonatomic, readonly) NSInteger endIndex;
|
||||
@property (nonatomic, readonly) BOOL bottomPresent;
|
||||
@property (nonatomic, readonly) uint64_t bottomBuild;
|
||||
@property (nonatomic, readonly) uint64_t bottomReuse;
|
||||
@property (nonatomic, readonly) uint64_t bottomInvalidation;
|
||||
@property (nonatomic, readonly) BOOL topPresent;
|
||||
@property (nonatomic, readonly) uint64_t topBuild;
|
||||
@property (nonatomic, readonly) uint64_t topReuse;
|
||||
@property (nonatomic, readonly) uint64_t topInvalidation;
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
@@ -2284,6 +2298,10 @@ of north, the map will automatically snap to exact north.
|
||||
/** Enables or disables the map-scoped retained static underlay optimization. */
|
||||
@property (nonatomic, getter=isRetainedUnderlayEnabled) BOOL retainedUnderlayEnabled;
|
||||
|
||||
/** Configures the inclusive Style-layer interval rendered continuously. Empty identifiers are rejected. */
|
||||
- (BOOL)setContinuousLayerRangeFromLayerIdentifier:(NSString *)startLayerId
|
||||
toLayerIdentifier:(NSString *)endLayerId;
|
||||
|
||||
/** Returns a thread-safe retained-underlay diagnostics snapshot without enabling periodic events. */
|
||||
@property (nonatomic, readonly) MLNRetainedUnderlayDiagnostics *retainedUnderlayDiagnostics;
|
||||
|
||||
|
||||
@@ -246,6 +246,7 @@ int processIsTranslated() {
|
||||
fallback:(uint64_t)fallback
|
||||
invalidation:(uint64_t)invalidation
|
||||
lastReason:(NSString *)lastReason;
|
||||
- (void)applyRangeSnapshot:(const mbgl::RetainedUnderlayDiagnostics&)snapshot;
|
||||
@end
|
||||
|
||||
@implementation MLNRetainedUnderlayDiagnostics
|
||||
@@ -262,9 +263,30 @@ int processIsTranslated() {
|
||||
_fallback = fallback;
|
||||
_invalidation = invalidation;
|
||||
_lastReason = [lastReason copy];
|
||||
_startLayerId = @"";
|
||||
_endLayerId = @"";
|
||||
_startIndex = -1;
|
||||
_endIndex = -1;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)applyRangeSnapshot:(const mbgl::RetainedUnderlayDiagnostics&)snapshot {
|
||||
_rangeConfigured = snapshot.rangeConfigured;
|
||||
_rangeValid = snapshot.rangeValid;
|
||||
_startLayerId = [NSString stringWithUTF8String:snapshot.startLayerId.c_str()];
|
||||
_endLayerId = [NSString stringWithUTF8String:snapshot.endLayerId.c_str()];
|
||||
_startIndex = snapshot.startIndex;
|
||||
_endIndex = snapshot.endIndex;
|
||||
_bottomPresent = snapshot.bottomPresent;
|
||||
_bottomBuild = snapshot.bottomBuild;
|
||||
_bottomReuse = snapshot.bottomReuse;
|
||||
_bottomInvalidation = snapshot.bottomInvalidation;
|
||||
_topPresent = snapshot.topPresent;
|
||||
_topBuild = snapshot.topBuild;
|
||||
_topReuse = snapshot.topReuse;
|
||||
_topInvalidation = snapshot.topInvalidation;
|
||||
}
|
||||
@end
|
||||
|
||||
class MLNAnnotationContext;
|
||||
@@ -3106,6 +3128,12 @@ static void *windowScreenContext = &windowScreenContext;
|
||||
if (_mbglMap) self.mbglMap.setRetainedUnderlayEnabled(enabled);
|
||||
}
|
||||
|
||||
- (BOOL)setContinuousLayerRangeFromLayerIdentifier:(NSString *)startLayerId
|
||||
toLayerIdentifier:(NSString *)endLayerId {
|
||||
if (!_mbglMap || startLayerId.length == 0 || endLayerId.length == 0) return NO;
|
||||
return self.mbglMap.setContinuousLayerRange(startLayerId.UTF8String, endLayerId.UTF8String);
|
||||
}
|
||||
|
||||
- (MLNRetainedUnderlayDiagnostics *)retainedUnderlayDiagnostics {
|
||||
if (!_mbglMap) {
|
||||
return [[MLNRetainedUnderlayDiagnostics alloc] initWithEnabled:YES
|
||||
@@ -3116,12 +3144,14 @@ static void *windowScreenContext = &windowScreenContext;
|
||||
lastReason:@"unavailable"];
|
||||
}
|
||||
const auto snapshot = self.mbglMap.getRetainedUnderlayDiagnostics();
|
||||
return [[MLNRetainedUnderlayDiagnostics alloc] initWithEnabled:snapshot.enabled
|
||||
MLNRetainedUnderlayDiagnostics *result = [[MLNRetainedUnderlayDiagnostics alloc] initWithEnabled:snapshot.enabled
|
||||
build:snapshot.build
|
||||
reuse:snapshot.reuse
|
||||
fallback:snapshot.fallback
|
||||
invalidation:snapshot.invalidation
|
||||
lastReason:[NSString stringWithUTF8String:snapshot.lastReason.c_str()]];
|
||||
[result applyRangeSnapshot:snapshot];
|
||||
return result;
|
||||
}
|
||||
|
||||
- (void)resetNorth {
|
||||
|
||||
@@ -100,6 +100,14 @@ void Map::setRetainedUnderlayEnabled(bool enabled) {
|
||||
}
|
||||
}
|
||||
|
||||
bool Map::setContinuousLayerRange(std::string startLayerId, std::string endLayerId) {
|
||||
if (!impl->retainedUnderlayState->setContinuousLayerRange(std::move(startLayerId), std::move(endLayerId))) {
|
||||
return false;
|
||||
}
|
||||
impl->onUpdate();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Map::isRetainedUnderlayEnabled() const {
|
||||
return impl->retainedUnderlayState->isEnabled();
|
||||
}
|
||||
|
||||
80
src/mbgl/renderer/continuous_layer_range.hpp
Normal file
80
src/mbgl/renderer/continuous_layer_range.hpp
Normal file
@@ -0,0 +1,80 @@
|
||||
#pragma once
|
||||
|
||||
#include <mbgl/renderer/retained_underlay.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
struct ContinuousRenderRanges {
|
||||
bool explicitConfigured = false;
|
||||
bool explicitValid = false;
|
||||
int32_t layerCount = 0;
|
||||
int32_t continuousBegin = 0;
|
||||
int32_t continuousEndExclusive = 0;
|
||||
uint64_t configRevision = 0;
|
||||
|
||||
bool retainedMode() const noexcept {
|
||||
return explicitValid || (!explicitConfigured && continuousBegin > 0);
|
||||
}
|
||||
bool bottomPresent() const noexcept { return retainedMode() && continuousBegin > 0; }
|
||||
bool topPresent() const noexcept {
|
||||
return explicitValid && continuousEndExclusive < layerCount;
|
||||
}
|
||||
};
|
||||
|
||||
inline ContinuousRenderRanges selectContinuousRenderRanges(
|
||||
const ContinuousLayerRangeResolution& explicitRange,
|
||||
std::optional<int32_t> legacyBoundary,
|
||||
int32_t layerCount) {
|
||||
ContinuousRenderRanges ranges;
|
||||
ranges.explicitConfigured = explicitRange.configured;
|
||||
ranges.explicitValid = explicitRange.valid;
|
||||
ranges.layerCount = layerCount;
|
||||
ranges.continuousEndExclusive = layerCount;
|
||||
ranges.configRevision = explicitRange.configRevision;
|
||||
if (explicitRange.valid) {
|
||||
ranges.continuousBegin = explicitRange.begin();
|
||||
ranges.continuousEndExclusive = explicitRange.endExclusive();
|
||||
} else if (!explicitRange.configured && legacyBoundary) {
|
||||
ranges.continuousBegin = *legacyBoundary;
|
||||
}
|
||||
return ranges;
|
||||
}
|
||||
|
||||
inline ContinuousLayerRangeResolution resolveContinuousLayerRange(
|
||||
const ContinuousLayerRangeConfig& config,
|
||||
const std::vector<std::string>& orderedLayerIds) {
|
||||
ContinuousLayerRangeResolution result;
|
||||
result.configured = config.configured;
|
||||
result.startLayerId = config.startLayerId;
|
||||
result.endLayerId = config.endLayerId;
|
||||
result.configRevision = config.revision;
|
||||
if (!config.configured) {
|
||||
result.reason = "not-configured";
|
||||
return result;
|
||||
}
|
||||
if (orderedLayerIds.empty()) {
|
||||
result.reason = "style-not-ready";
|
||||
return result;
|
||||
}
|
||||
for (std::size_t index = 0; index < orderedLayerIds.size(); ++index) {
|
||||
if (orderedLayerIds[index] == config.startLayerId) result.startIndex = static_cast<int32_t>(index);
|
||||
if (orderedLayerIds[index] == config.endLayerId) result.endIndex = static_cast<int32_t>(index);
|
||||
}
|
||||
if (result.startIndex < 0) {
|
||||
result.reason = "start-layer-missing";
|
||||
} else if (result.endIndex < 0) {
|
||||
result.reason = "end-layer-missing";
|
||||
} else if (result.startIndex > result.endIndex) {
|
||||
result.reason = "range-order-invalid";
|
||||
} else {
|
||||
result.valid = true;
|
||||
result.reason = "none";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace mbgl
|
||||
@@ -1,4 +1,5 @@
|
||||
#include <mbgl/renderer/render_orchestrator.hpp>
|
||||
#include <mbgl/renderer/continuous_layer_range.hpp>
|
||||
|
||||
#include <mbgl/annotation/annotation_manager.hpp>
|
||||
#include <mbgl/layermanager/layer_manager.hpp>
|
||||
@@ -406,6 +407,26 @@ std::unique_ptr<RenderTree> RenderOrchestrator::createRenderTree(
|
||||
orderedLayers[index].get().startsRetainedDynamicRange());
|
||||
}
|
||||
retainedDynamicBoundary = boundaryTracker.value();
|
||||
std::vector<std::string> orderedLayerIds;
|
||||
orderedLayerIds.reserve(orderedLayers.size());
|
||||
for (const auto& layer : orderedLayers) orderedLayerIds.push_back(layer.get().getID());
|
||||
continuousLayerRange = resolveContinuousLayerRange(
|
||||
updateParameters->retainedUnderlayState
|
||||
? updateParameters->retainedUnderlayState->continuousLayerRangeConfig()
|
||||
: ContinuousLayerRangeConfig{},
|
||||
orderedLayerIds);
|
||||
if (continuousLayerRange.configured && !updateParameters->styleLoaded) {
|
||||
continuousLayerRange.valid = false;
|
||||
continuousLayerRange.startIndex = -1;
|
||||
continuousLayerRange.endIndex = -1;
|
||||
continuousLayerRange.reason = "style-not-ready";
|
||||
}
|
||||
if (updateParameters->retainedUnderlayState) {
|
||||
updateParameters->retainedUnderlayState->recordRangeResolution(
|
||||
continuousLayerRange,
|
||||
continuousLayerRange.bottomPresent(),
|
||||
continuousLayerRange.topPresent(static_cast<int32_t>(orderedLayers.size())));
|
||||
}
|
||||
|
||||
// Update all sources and initialize renderItems.
|
||||
for (const auto& sourceImpl : *sourceImpls) {
|
||||
@@ -894,21 +915,29 @@ bool RenderOrchestrator::hasTransitions(TimePoint timePoint) const {
|
||||
}
|
||||
|
||||
bool RenderOrchestrator::retainedUnderlayUsesPlacement(int32_t boundary) const {
|
||||
const auto staticLayerCount = std::min<std::size_t>(
|
||||
boundary > 0 ? static_cast<std::size_t>(boundary) : 0, orderedLayers.size());
|
||||
for (std::size_t index = 0; index < staticLayerCount; ++index) {
|
||||
return retainedRangeUsesPlacement(0, boundary);
|
||||
}
|
||||
|
||||
bool RenderOrchestrator::retainedRangeUsesPlacement(int32_t begin, int32_t endExclusive) const {
|
||||
const auto first = std::clamp<int32_t>(begin, 0, static_cast<int32_t>(orderedLayers.size()));
|
||||
const auto last = std::clamp<int32_t>(endExclusive, first, static_cast<int32_t>(orderedLayers.size()));
|
||||
for (int32_t index = first; index < last; ++index) {
|
||||
if (orderedLayers[index].get().needsPlacement()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool RenderOrchestrator::retainedUnderlayIsStable(TimePoint timePoint, int32_t boundary) const {
|
||||
if (renderLight.hasTransition()) return false;
|
||||
if (retainedUnderlayUsesPlacement(boundary) && placementController.hasTransitions(timePoint)) return false;
|
||||
return retainedRangeIsStable(timePoint, 0, boundary);
|
||||
}
|
||||
|
||||
const auto staticLayerCount = std::min<std::size_t>(
|
||||
boundary > 0 ? static_cast<std::size_t>(boundary) : 0, orderedLayers.size());
|
||||
for (std::size_t index = 0; index < staticLayerCount; ++index) {
|
||||
bool RenderOrchestrator::retainedRangeIsStable(TimePoint timePoint, int32_t begin, int32_t endExclusive) const {
|
||||
if (renderLight.hasTransition()) return false;
|
||||
if (retainedRangeUsesPlacement(begin, endExclusive) && placementController.hasTransitions(timePoint)) return false;
|
||||
|
||||
const auto first = std::clamp<int32_t>(begin, 0, static_cast<int32_t>(orderedLayers.size()));
|
||||
const auto last = std::clamp<int32_t>(endExclusive, first, static_cast<int32_t>(orderedLayers.size()));
|
||||
for (int32_t index = first; index < last; ++index) {
|
||||
const auto& layer = orderedLayers[index].get();
|
||||
if (layer.hasTransition() || layer.needsRepaint()) return false;
|
||||
}
|
||||
@@ -920,15 +949,31 @@ bool RenderOrchestrator::retainedUnderlayIsStable(TimePoint timePoint, int32_t b
|
||||
}
|
||||
|
||||
bool RenderOrchestrator::retainedUnderlayHasUnsupportedLayer(int32_t boundary) const {
|
||||
const auto staticLayerCount = std::min<std::size_t>(
|
||||
boundary > 0 ? static_cast<std::size_t>(boundary) : 0, orderedLayers.size());
|
||||
for (std::size_t index = 0; index < staticLayerCount; ++index) {
|
||||
return retainedRangeHasUnsupportedLayer(0, boundary);
|
||||
}
|
||||
|
||||
bool RenderOrchestrator::retainedRangeHasUnsupportedLayer(int32_t begin, int32_t endExclusive) const {
|
||||
const auto first = std::clamp<int32_t>(begin, 0, static_cast<int32_t>(orderedLayers.size()));
|
||||
const auto last = std::clamp<int32_t>(endExclusive, first, static_cast<int32_t>(orderedLayers.size()));
|
||||
for (int32_t index = first; index < last; ++index) {
|
||||
const std::string_view type = orderedLayers[index].get().baseImpl->getTypeInfo()->type;
|
||||
if (type == "custom" || type == "custom-drawable") return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool RenderOrchestrator::continuousRangeSupportsParticleFastPath(int32_t begin, int32_t endExclusive) const {
|
||||
const auto first = std::clamp<int32_t>(begin, 0, static_cast<int32_t>(orderedLayers.size()));
|
||||
const auto last = std::clamp<int32_t>(endExclusive, first, static_cast<int32_t>(orderedLayers.size()));
|
||||
bool particle = false;
|
||||
for (int32_t index = first; index < last; ++index) {
|
||||
const auto& layer = orderedLayers[index].get();
|
||||
if (!layer.startsRetainedDynamicRange()) return false;
|
||||
particle = particle || layer.isParticleAnimationLayer();
|
||||
}
|
||||
return particle;
|
||||
}
|
||||
|
||||
ParticleFastPathDynamicState RenderOrchestrator::particleFastPathDynamicState(TimePoint timePoint) const {
|
||||
ParticleFastPathDynamicState result;
|
||||
result.blockingTransition = renderLight.hasTransition();
|
||||
|
||||
@@ -119,9 +119,15 @@ public:
|
||||
bool removeLayerGroup(const LayerGroupBasePtr&);
|
||||
size_t numLayerGroups() const noexcept;
|
||||
std::optional<int32_t> getRetainedDynamicBoundary() const noexcept { return retainedDynamicBoundary; }
|
||||
const ContinuousLayerRangeResolution& getContinuousLayerRange() const noexcept { return continuousLayerRange; }
|
||||
int32_t getOrderedLayerCount() const noexcept { return static_cast<int32_t>(orderedLayers.size()); }
|
||||
bool retainedUnderlayUsesPlacement(int32_t boundary) const;
|
||||
bool retainedUnderlayIsStable(TimePoint, int32_t boundary) const;
|
||||
bool retainedUnderlayHasUnsupportedLayer(int32_t boundary) const;
|
||||
bool retainedRangeUsesPlacement(int32_t begin, int32_t endExclusive) const;
|
||||
bool retainedRangeIsStable(TimePoint, int32_t begin, int32_t endExclusive) const;
|
||||
bool retainedRangeHasUnsupportedLayer(int32_t begin, int32_t endExclusive) const;
|
||||
bool continuousRangeSupportsParticleFastPath(int32_t begin, int32_t endExclusive) const;
|
||||
ParticleFastPathDynamicState particleFastPathDynamicState(TimePoint) const;
|
||||
uint64_t getRetainedUnderlayContentRevision() const noexcept {
|
||||
return retainedUnderlayContentRevision.load(std::memory_order_acquire);
|
||||
@@ -144,6 +150,32 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Func /* void(LayerGroupBase&, size_t originalOrdinal) */>
|
||||
void visitLayerGroupsInRange(int32_t begin, int32_t endExclusive, Func f) {
|
||||
size_t ordinal = static_cast<size_t>(std::distance(layerGroupsByLayerIndex.begin(),
|
||||
layerGroupsByLayerIndex.lower_bound(begin)));
|
||||
for (auto it = layerGroupsByLayerIndex.lower_bound(begin);
|
||||
it != layerGroupsByLayerIndex.end() && it->first < endExclusive; ++it, ++ordinal) {
|
||||
if (it->second) {
|
||||
try { f(*it->second, ordinal); } catch (...) { observer->onRenderError(std::current_exception()); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Func /* void(LayerGroupBase&, size_t originalOrdinal) */>
|
||||
void visitLayerGroupsInRangeReversed(int32_t begin, int32_t endExclusive, Func f) {
|
||||
auto first = layerGroupsByLayerIndex.lower_bound(begin);
|
||||
auto last = layerGroupsByLayerIndex.lower_bound(endExclusive);
|
||||
size_t ordinal = static_cast<size_t>(std::distance(layerGroupsByLayerIndex.begin(), last));
|
||||
while (last != first) {
|
||||
--last;
|
||||
--ordinal;
|
||||
if (last->second) {
|
||||
try { f(*last->second, ordinal); } catch (...) { observer->onRenderError(std::current_exception()); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Func /* void(LayerGroupBase&, size_t originalOrdinal) */>
|
||||
void visitLayerGroupsBefore(int32_t boundary, Func f) {
|
||||
size_t ordinal = 0;
|
||||
@@ -343,6 +375,7 @@ private:
|
||||
using LayerGroupMap = std::multimap<int32_t, LayerGroupBasePtr>;
|
||||
LayerGroupMap layerGroupsByLayerIndex;
|
||||
std::optional<int32_t> retainedDynamicBoundary;
|
||||
ContinuousLayerRangeResolution continuousLayerRange;
|
||||
|
||||
std::vector<RenderTargetPtr> renderTargets;
|
||||
RenderItem::DebugLayerGroupMap debugLayerGroups;
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include <mbgl/renderer/layer_tweaker.hpp>
|
||||
#include <mbgl/renderer/render_target.hpp>
|
||||
#include <mbgl/renderer/retained_underlay_cache.hpp>
|
||||
#include <mbgl/renderer/continuous_layer_range.hpp>
|
||||
#include <mbgl/particle/renderer_debug_counters.hpp>
|
||||
|
||||
#if MLN_RENDER_BACKEND_METAL
|
||||
@@ -51,6 +52,12 @@ using namespace style;
|
||||
|
||||
namespace {
|
||||
|
||||
ContinuousRenderRanges activeRenderRanges(const RenderOrchestrator& orchestrator) {
|
||||
return selectContinuousRenderRanges(orchestrator.getContinuousLayerRange(),
|
||||
orchestrator.getRetainedDynamicBoundary(),
|
||||
orchestrator.getOrderedLayerCount());
|
||||
}
|
||||
|
||||
RendererObserver& nullObserver() {
|
||||
static RendererObserver observer;
|
||||
return observer;
|
||||
@@ -98,7 +105,8 @@ Renderer::Impl::Impl(gfx::RendererBackend& backend_,
|
||||
backend(backend_),
|
||||
observer(&nullObserver()),
|
||||
pixelRatio(pixelRatio_),
|
||||
retainedUnderlayCache(std::make_unique<RetainedUnderlayCache>()) {}
|
||||
retainedUnderlayCache(std::make_unique<RetainedUnderlayCache>(RetainedRangeRole::Bottom)),
|
||||
retainedTopCache(std::make_unique<RetainedUnderlayCache>(RetainedRangeRole::Top)) {}
|
||||
|
||||
Renderer::Impl::~Impl() {
|
||||
assert(gfx::BackendScope::exists());
|
||||
@@ -133,6 +141,7 @@ void Renderer::Impl::setObserver(RendererObserver* observer_) {
|
||||
void Renderer::Impl::synchronizeRetainedUnderlayState(
|
||||
const std::shared_ptr<RetainedUnderlayRuntimeState>& state) {
|
||||
retainedUnderlayCache->synchronizeRuntimeState(state);
|
||||
retainedTopCache->synchronizeRuntimeState(state);
|
||||
}
|
||||
|
||||
Renderer::Impl::ParticleFastPathUpdateSignature Renderer::Impl::makeParticleFastPathUpdateSignature(
|
||||
@@ -192,28 +201,48 @@ bool Renderer::Impl::tryRenderParticleFastFrame(const std::shared_ptr<UpdatePara
|
||||
}
|
||||
|
||||
const auto retained = retainedUnderlayCache->getDiagnostics();
|
||||
const auto currentBoundary = orchestrator.getRetainedDynamicBoundary();
|
||||
const auto ranges = activeRenderRanges(orchestrator);
|
||||
const auto liveRangeConfig = updateParameters->retainedUnderlayState
|
||||
? updateParameters->retainedUnderlayState->continuousLayerRangeConfig()
|
||||
: ContinuousLayerRangeConfig{};
|
||||
const std::optional<int32_t> currentBoundary =
|
||||
(ranges.explicitValid || !ranges.explicitConfigured) ? std::optional<int32_t>(ranges.continuousBegin)
|
||||
: std::nullopt;
|
||||
const bool hasSignature = particlePreparedSignature.has_value();
|
||||
const bool viewportMatches = hasSignature && particlePreparedSignature->viewport == signature.viewport &&
|
||||
particlePreparedBackendSize == backend.getDefaultRenderable().getSize();
|
||||
const bool cameraMatches = hasSignature && particlePreparedSignature->cameraMatrix == signature.cameraMatrix;
|
||||
const bool updateParametersMatch =
|
||||
hasSignature && particleFastPathNonCameraInputsMatch(*particlePreparedSignature, signature);
|
||||
const bool revisionMatches =
|
||||
retainedUnderlayCache->getStaticRevision() == retainedUnderlayRevisionTracker.current() &&
|
||||
retainedUnderlayCache->getBuildRevision() == retainedUnderlayRevisionTracker.current() &&
|
||||
particlePreparedStaticRevision == retainedUnderlayRevisionTracker.current();
|
||||
const bool boundaryMatches = particlePreparedBoundary == currentBoundary;
|
||||
const bool bottomRevisionMatches = !ranges.bottomPresent() ||
|
||||
(retainedUnderlayCache->getStaticRevision() == retainedUnderlayRevisionTracker.current() &&
|
||||
retainedUnderlayCache->getBuildRevision() == retainedUnderlayRevisionTracker.current());
|
||||
const bool topRevisionMatches = !ranges.topPresent() ||
|
||||
(retainedTopCache->getStaticRevision() == retainedUnderlayRevisionTracker.current() &&
|
||||
retainedTopCache->getBuildRevision() == retainedUnderlayRevisionTracker.current());
|
||||
const bool revisionMatches = bottomRevisionMatches && topRevisionMatches &&
|
||||
particlePreparedStaticRevision == retainedUnderlayRevisionTracker.current();
|
||||
const bool boundaryMatches = particlePreparedBoundary == currentBoundary &&
|
||||
particlePreparedContinuousEnd == std::optional<int32_t>(ranges.continuousEndExclusive) &&
|
||||
particlePreparedRangeConfigRevision == ranges.configRevision &&
|
||||
liveRangeConfig.revision == ranges.configRevision &&
|
||||
liveRangeConfig.configured == ranges.explicitConfigured &&
|
||||
particlePreparedBottomPresent == ranges.bottomPresent() &&
|
||||
particlePreparedTopPresent == ranges.topPresent();
|
||||
const bool topologyMatches = particlePreparedTopologyRevision == retainedUnderlayTopologyTracker.current();
|
||||
const bool contentRevisionMatches =
|
||||
particlePreparedContentRevision == orchestrator.getRetainedUnderlayContentRevision();
|
||||
const bool staticStable =
|
||||
currentBoundary && orchestrator.retainedUnderlayIsStable(updateParameters->timePoint, *currentBoundary);
|
||||
const bool staticStable = (ranges.explicitValid || (!ranges.explicitConfigured && ranges.bottomPresent())) &&
|
||||
(!ranges.bottomPresent() || orchestrator.retainedRangeIsStable(
|
||||
updateParameters->timePoint, 0, ranges.continuousBegin)) &&
|
||||
(!ranges.topPresent() || orchestrator.retainedRangeIsStable(
|
||||
updateParameters->timePoint, ranges.continuousEndExclusive, ranges.layerCount));
|
||||
|
||||
const ParticleFastPathGateInput input{
|
||||
.hasPreparedTree = !particlePreparedTree.empty(),
|
||||
.retainedEnabled = retained.enabled,
|
||||
.cacheValid = retainedUnderlayCache->isValid(),
|
||||
.cacheValid = (!ranges.bottomPresent() || retainedUnderlayCache->isValid()) &&
|
||||
(!ranges.topPresent() || retainedTopCache->isValid()),
|
||||
.revisionMatches = revisionMatches,
|
||||
.contentRevisionMatches = contentRevisionMatches,
|
||||
.cameraMatches = cameraMatches,
|
||||
@@ -254,11 +283,18 @@ bool Renderer::Impl::tryRenderParticleFastFrame(const std::shared_ptr<UpdatePara
|
||||
|
||||
void Renderer::Impl::commitPreparedRenderTree(std::unique_ptr<RenderTree> renderTree,
|
||||
const std::shared_ptr<UpdateParameters>& updateParameters) {
|
||||
const auto ranges = activeRenderRanges(orchestrator);
|
||||
particlePreparedSignature = makeParticleFastPathUpdateSignature(*updateParameters);
|
||||
particlePreparedContentRevision = orchestrator.getRetainedUnderlayContentRevision();
|
||||
particlePreparedTopologyRevision = retainedUnderlayTopologyTracker.current();
|
||||
particlePreparedStaticRevision = retainedUnderlayRevisionTracker.current();
|
||||
particlePreparedBoundary = orchestrator.getRetainedDynamicBoundary();
|
||||
particlePreparedBoundary = (ranges.explicitValid || !ranges.explicitConfigured)
|
||||
? std::optional<int32_t>(ranges.continuousBegin)
|
||||
: std::nullopt;
|
||||
particlePreparedContinuousEnd = ranges.continuousEndExclusive;
|
||||
particlePreparedRangeConfigRevision = ranges.configRevision;
|
||||
particlePreparedBottomPresent = ranges.bottomPresent();
|
||||
particlePreparedTopPresent = ranges.topPresent();
|
||||
particlePreparedBackendSize = backend.getDefaultRenderable().getSize();
|
||||
particlePreparedTree.replace(std::move(renderTree));
|
||||
}
|
||||
@@ -270,6 +306,10 @@ void Renderer::Impl::clearPreparedRenderTree() {
|
||||
particlePreparedTopologyRevision = 0;
|
||||
particlePreparedStaticRevision = 0;
|
||||
particlePreparedBoundary.reset();
|
||||
particlePreparedContinuousEnd.reset();
|
||||
particlePreparedRangeConfigRevision = 0;
|
||||
particlePreparedBottomPresent = false;
|
||||
particlePreparedTopPresent = false;
|
||||
particlePreparedBackendSize = {};
|
||||
particlePreparedSceneSupported = false;
|
||||
}
|
||||
@@ -455,46 +495,78 @@ void Renderer::Impl::render(const RenderTree& renderTree,
|
||||
|
||||
orchestrator.processChanges();
|
||||
|
||||
std::optional<RetainedUnderlayKey> retainedKey;
|
||||
const auto retainedBoundary = orchestrator.getRetainedDynamicBoundary();
|
||||
RetainedUnderlayTopologySnapshot retainedTopology{.boundary = retainedBoundary, .staticPrefix = {}};
|
||||
const auto ranges = activeRenderRanges(orchestrator);
|
||||
const bool rangeMode = ranges.explicitValid;
|
||||
const bool retainedMode = rangeMode || (!ranges.explicitConfigured && ranges.bottomPresent());
|
||||
const std::optional<int32_t> retainedBoundary = retainedMode
|
||||
? std::optional<int32_t>(ranges.continuousBegin) : std::nullopt;
|
||||
std::optional<RetainedUnderlayKey> bottomKey;
|
||||
std::optional<RetainedUnderlayKey> topKey;
|
||||
RetainedUnderlayTopologySnapshot retainedTopology{
|
||||
.boundary = retainedBoundary,
|
||||
.staticPrefix = {},
|
||||
.continuousEndExclusive = retainedMode ? std::optional<int32_t>(ranges.continuousEndExclusive) : std::nullopt,
|
||||
.staticSuffix = {}};
|
||||
mat4 retainedCameraMatrix;
|
||||
state.getProjMatrix(retainedCameraMatrix);
|
||||
if (retainedBoundary) {
|
||||
orchestrator.visitLayerGroupsBefore(*retainedBoundary, [&](LayerGroupBase& layerGroup, size_t) {
|
||||
if (retainedMode && ranges.bottomPresent()) {
|
||||
orchestrator.visitLayerGroupsInRange(0, ranges.continuousBegin, [&](LayerGroupBase& layerGroup, size_t) {
|
||||
retainedTopology.staticPrefix.push_back({.layerIndex = layerGroup.getLayerIndex(),
|
||||
.groupName = layerGroup.getName(),
|
||||
.groupType = static_cast<uint8_t>(layerGroup.getType())});
|
||||
});
|
||||
retainedKey = RetainedUnderlayKey{size, *retainedBoundary, retainedCameraMatrix};
|
||||
bottomKey = RetainedUnderlayKey{size, 0, ranges.continuousBegin, retainedCameraMatrix};
|
||||
}
|
||||
if (rangeMode && ranges.topPresent()) {
|
||||
orchestrator.visitLayerGroupsInRange(
|
||||
ranges.continuousEndExclusive, ranges.layerCount, [&](LayerGroupBase& layerGroup, size_t) {
|
||||
retainedTopology.staticSuffix.push_back({.layerIndex = layerGroup.getLayerIndex(),
|
||||
.groupName = layerGroup.getName(),
|
||||
.groupType = static_cast<uint8_t>(layerGroup.getType())});
|
||||
});
|
||||
topKey = RetainedUnderlayKey{
|
||||
size, ranges.continuousEndExclusive, ranges.layerCount, retainedCameraMatrix};
|
||||
}
|
||||
const auto retainedTopologyRevision = particleFastFrame ? retainedUnderlayTopologyTracker.current()
|
||||
: retainedUnderlayTopologyTracker.observe(retainedTopology);
|
||||
const auto retainedStaticGroupCount = retainedTopology.staticPrefix.size();
|
||||
bool legacyBelowBoundary = false;
|
||||
if (retainedBoundary) {
|
||||
const auto bottomGroupCount = retainedTopology.staticPrefix.size();
|
||||
bool legacyInRetainedRange = false;
|
||||
if (retainedMode) {
|
||||
for (const auto& item : layerRenderItems) {
|
||||
if (item.index < static_cast<uint32_t>(*retainedBoundary) && item.layer.get().usesLegacyRendering()) {
|
||||
legacyBelowBoundary = true;
|
||||
const auto index = static_cast<int32_t>(item.index);
|
||||
if ((index < ranges.continuousBegin || (rangeMode && index >= ranges.continuousEndExclusive)) &&
|
||||
item.layer.get().usesLegacyRendering()) {
|
||||
legacyInRetainedRange = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
const bool retainedStaticUsesPlacement =
|
||||
retainedBoundary && orchestrator.retainedUnderlayUsesPlacement(*retainedBoundary);
|
||||
const bool retainedStaticStable =
|
||||
retainedBoundary && orchestrator.retainedUnderlayIsStable(renderTreeParameters.timePoint, *retainedBoundary);
|
||||
const bool retainedStaticUsesPlacement = retainedMode &&
|
||||
((ranges.bottomPresent() && orchestrator.retainedRangeUsesPlacement(0, ranges.continuousBegin)) ||
|
||||
(ranges.topPresent() && orchestrator.retainedRangeUsesPlacement(
|
||||
ranges.continuousEndExclusive, ranges.layerCount)));
|
||||
const bool retainedStaticStable = retainedMode &&
|
||||
(!ranges.bottomPresent() || orchestrator.retainedRangeIsStable(
|
||||
renderTreeParameters.timePoint, 0, ranges.continuousBegin)) &&
|
||||
(!ranges.topPresent() || orchestrator.retainedRangeIsStable(
|
||||
renderTreeParameters.timePoint, ranges.continuousEndExclusive, ranges.layerCount));
|
||||
const bool retainedPlacementStable =
|
||||
!retainedStaticUsesPlacement ||
|
||||
(!renderTreeParameters.placementChanged && renderTreeParameters.symbolFadeChange == 0.0f);
|
||||
const bool retainedUnsupportedStaticLayer =
|
||||
retainedBoundary && orchestrator.retainedUnderlayHasUnsupportedLayer(*retainedBoundary);
|
||||
const bool retainedUnsupportedStaticLayer = retainedMode &&
|
||||
((ranges.bottomPresent() && orchestrator.retainedRangeHasUnsupportedLayer(0, ranges.continuousBegin)) ||
|
||||
(ranges.topPresent() && orchestrator.retainedRangeHasUnsupportedLayer(
|
||||
ranges.continuousEndExclusive, ranges.layerCount)));
|
||||
const auto retainedContentRevision = orchestrator.getRetainedUnderlayContentRevision();
|
||||
const auto retainedContentReason = orchestrator.getRetainedUnderlayContentReason();
|
||||
const RetainedUnderlayRevisionState retainedRevisionState{
|
||||
.viewport = size,
|
||||
.cameraMatrix = retainedCameraMatrix,
|
||||
.boundary = retainedBoundary,
|
||||
.continuousEndExclusive = retainedMode
|
||||
? std::optional<int32_t>(ranges.continuousEndExclusive)
|
||||
: std::nullopt,
|
||||
.rangeConfigRevision = ranges.configRevision,
|
||||
.pixelRatio = updateParameters->pixelRatio,
|
||||
.contentRevision = retainedContentRevision,
|
||||
.topologyRevision = retainedTopologyRevision,
|
||||
@@ -509,38 +581,67 @@ void Renderer::Impl::render(const RenderTree& renderTree,
|
||||
};
|
||||
const auto retainedStaticRevision = retainedUnderlayRevisionTracker.observe(
|
||||
retainedRevisionState, retainedUnderlayReasonText(retainedContentReason));
|
||||
const bool retainedSupported = retainedBoundary && retainedStaticGroupCount > 0 &&
|
||||
const bool retainedSupported = retainedMode &&
|
||||
(rangeMode || !ranges.bottomPresent() || bottomGroupCount > 0) &&
|
||||
renderTreeParameters.loaded && !renderTreeParameters.has3D &&
|
||||
renderTreeParameters.debugOptions == MapDebugOptions::NoDebug &&
|
||||
retainedStaticStable && retainedPlacementStable &&
|
||||
!legacyBelowBoundary && !retainedUnsupportedStaticLayer;
|
||||
if (!particleFastFrame) particlePreparedSceneSupported = retainedSupported;
|
||||
const char* retainedFallbackReason = !retainedBoundary ? "no-boundary"
|
||||
: retainedStaticGroupCount == 0 ? "empty-static-prefix"
|
||||
: !renderTreeParameters.loaded ? "style-not-loaded"
|
||||
: renderTreeParameters.has3D ? "3d"
|
||||
: renderTreeParameters.debugOptions != MapDebugOptions::NoDebug
|
||||
? "debug-options"
|
||||
: !retainedStaticStable
|
||||
? "static-transition"
|
||||
: !retainedPlacementStable
|
||||
? "static-placement"
|
||||
: legacyBelowBoundary ? "legacy-ordering"
|
||||
: retainedUnsupportedStaticLayer ? "unsupported"
|
||||
: nullptr;
|
||||
auto retainedDecision = retainedUnderlayCache->evaluate(retainedKey,
|
||||
retainedStaticRevision,
|
||||
retainedSupported,
|
||||
retainedFallbackReason,
|
||||
retainedUnderlayRevisionTracker.reason().c_str());
|
||||
if ((retainedDecision == RetainedUnderlayDecision::Build ||
|
||||
retainedDecision == RetainedUnderlayDecision::Reuse) &&
|
||||
(!staticData->shaders || !retainedUnderlayCache->prepareResources(context, *staticData->shaders, *retainedKey))) {
|
||||
retainedUnderlayCache->markBuildFailed("resource-or-compositor");
|
||||
retainedDecision = RetainedUnderlayDecision::Fallback;
|
||||
!legacyInRetainedRange && !retainedUnsupportedStaticLayer;
|
||||
const bool fastContinuousSupported = rangeMode
|
||||
? orchestrator.continuousRangeSupportsParticleFastPath(
|
||||
ranges.continuousBegin, ranges.continuousEndExclusive)
|
||||
: retainedSupported;
|
||||
if (!particleFastFrame) particlePreparedSceneSupported = retainedSupported && fastContinuousSupported;
|
||||
const char* retainedFallbackReason = nullptr;
|
||||
if (!retainedMode) {
|
||||
retainedFallbackReason = "no-boundary";
|
||||
} else if (!rangeMode && ranges.bottomPresent() && bottomGroupCount == 0) {
|
||||
retainedFallbackReason = "empty-static-prefix";
|
||||
} else if (!renderTreeParameters.loaded) {
|
||||
retainedFallbackReason = "style-not-loaded";
|
||||
} else if (renderTreeParameters.has3D) {
|
||||
retainedFallbackReason = "3d";
|
||||
} else if (renderTreeParameters.debugOptions != MapDebugOptions::NoDebug) {
|
||||
retainedFallbackReason = "debug-options";
|
||||
} else if (!retainedStaticStable) {
|
||||
retainedFallbackReason = "static-transition";
|
||||
} else if (!retainedPlacementStable) {
|
||||
retainedFallbackReason = "static-placement";
|
||||
} else if (legacyInRetainedRange) {
|
||||
retainedFallbackReason = "legacy-ordering";
|
||||
} else if (retainedUnsupportedStaticLayer) {
|
||||
retainedFallbackReason = "unsupported";
|
||||
}
|
||||
const bool useRetainedUnderlay = retainedDecision == RetainedUnderlayDecision::Build ||
|
||||
retainedDecision == RetainedUnderlayDecision::Reuse;
|
||||
auto bottomDecision = retainedUnderlayCache->evaluate(bottomKey,
|
||||
retainedStaticRevision,
|
||||
retainedSupported,
|
||||
retainedFallbackReason,
|
||||
retainedUnderlayRevisionTracker.reason().c_str());
|
||||
auto topDecision = retainedTopCache->evaluate(topKey,
|
||||
retainedStaticRevision,
|
||||
retainedSupported,
|
||||
retainedFallbackReason,
|
||||
retainedUnderlayRevisionTracker.reason().c_str());
|
||||
const auto wantsResources = [](RetainedUnderlayDecision decision) {
|
||||
return decision == RetainedUnderlayDecision::Build || decision == RetainedUnderlayDecision::Reuse;
|
||||
};
|
||||
bool resourcesReady = true;
|
||||
if (wantsResources(bottomDecision)) {
|
||||
resourcesReady = staticData->shaders &&
|
||||
retainedUnderlayCache->prepareResources(context, *staticData->shaders, *bottomKey);
|
||||
}
|
||||
if (resourcesReady && wantsResources(topDecision)) {
|
||||
resourcesReady = staticData->shaders &&
|
||||
retainedTopCache->prepareResources(context, *staticData->shaders, *topKey);
|
||||
}
|
||||
if (!resourcesReady) {
|
||||
if (wantsResources(bottomDecision)) retainedUnderlayCache->markBuildFailed("resource-or-compositor");
|
||||
if (wantsResources(topDecision)) retainedTopCache->markBuildFailed("resource-or-compositor");
|
||||
bottomDecision = topDecision = RetainedUnderlayDecision::Fallback;
|
||||
}
|
||||
const bool useRetainedRanges = retainedSupported &&
|
||||
(!ranges.bottomPresent() || wantsResources(bottomDecision)) &&
|
||||
(!ranges.topPresent() || wantsResources(topDecision));
|
||||
|
||||
// Upload layer groups
|
||||
{
|
||||
@@ -556,11 +657,13 @@ void Renderer::Impl::render(const RenderTree& renderTree,
|
||||
|
||||
// Tweakers are run in the upload pass so they can set up uniforms.
|
||||
parameters.currentLayer = 0;
|
||||
if (particleFastFrame && retainedBoundary) {
|
||||
orchestrator.visitLayerGroupsFrom(*retainedBoundary, [&](LayerGroupBase& layerGroup, size_t ordinal) {
|
||||
parameters.currentLayer = static_cast<uint32_t>(ordinal);
|
||||
layerGroup.runTweakers(renderTree, parameters);
|
||||
});
|
||||
if (particleFastFrame && retainedMode) {
|
||||
orchestrator.visitLayerGroupsInRange(
|
||||
ranges.continuousBegin, ranges.continuousEndExclusive,
|
||||
[&](LayerGroupBase& layerGroup, size_t ordinal) {
|
||||
parameters.currentLayer = static_cast<uint32_t>(ordinal);
|
||||
layerGroup.runTweakers(renderTree, parameters);
|
||||
});
|
||||
} else {
|
||||
orchestrator.visitLayerGroups([&](LayerGroupBase& layerGroup) {
|
||||
layerGroup.runTweakers(renderTree, parameters);
|
||||
@@ -574,9 +677,10 @@ void Renderer::Impl::render(const RenderTree& renderTree,
|
||||
}
|
||||
|
||||
// Give the layers a chance to upload
|
||||
if (particleFastFrame && retainedBoundary) {
|
||||
orchestrator.visitLayerGroupsFrom(
|
||||
*retainedBoundary, [&](LayerGroupBase& layerGroup, size_t) { layerGroup.upload(*uploadPass); });
|
||||
if (particleFastFrame && retainedMode) {
|
||||
orchestrator.visitLayerGroupsInRange(
|
||||
ranges.continuousBegin, ranges.continuousEndExclusive,
|
||||
[&](LayerGroupBase& layerGroup, size_t) { layerGroup.upload(*uploadPass); });
|
||||
} else {
|
||||
orchestrator.visitLayerGroups([&](LayerGroupBase& layerGroup) { layerGroup.upload(*uploadPass); });
|
||||
}
|
||||
@@ -588,7 +692,8 @@ void Renderer::Impl::render(const RenderTree& renderTree,
|
||||
if (!particleFastFrame) {
|
||||
orchestrator.visitDebugLayerGroups([&](LayerGroupBase& layerGroup) { layerGroup.upload(*uploadPass); });
|
||||
}
|
||||
if (useRetainedUnderlay) retainedUnderlayCache->upload(*uploadPass);
|
||||
if (useRetainedRanges && ranges.bottomPresent()) retainedUnderlayCache->upload(*uploadPass);
|
||||
if (useRetainedRanges && ranges.topPresent()) retainedTopCache->upload(*uploadPass);
|
||||
}
|
||||
|
||||
const Size atlasSize = parameters.patternAtlas.getPixelSize();
|
||||
@@ -652,30 +757,33 @@ void Renderer::Impl::render(const RenderTree& renderTree,
|
||||
[&](RenderTarget& renderTarget) { renderTarget.render(orchestrator, renderTree, parameters); });
|
||||
};
|
||||
|
||||
const auto buildRetainedUnderlay = [&] {
|
||||
if (retainedDecision != RetainedUnderlayDecision::Build || !retainedBoundary) return;
|
||||
retainedUnderlayCache->beginBuild(parameters);
|
||||
const auto buildRetainedRange = [&](RetainedUnderlayCache& cache,
|
||||
RetainedUnderlayDecision decision,
|
||||
int32_t begin,
|
||||
int32_t endExclusive) {
|
||||
if (!useRetainedRanges || decision != RetainedUnderlayDecision::Build || begin >= endExclusive) return;
|
||||
cache.beginBuild(parameters);
|
||||
context.bindGlobalUniformBuffers(*parameters.renderPass);
|
||||
parameters.depthRangeSize = 1 - (orchestrator.numLayerGroups() + 2) * PaintParameters::numSublayers *
|
||||
PaintParameters::depthEpsilon;
|
||||
parameters.pass = RenderPass::Opaque;
|
||||
orchestrator.visitLayerGroupsBeforeReversed(*retainedBoundary,
|
||||
[&](LayerGroupBase& layerGroup, size_t ordinal) {
|
||||
orchestrator.visitLayerGroupsInRangeReversed(begin, endExclusive,
|
||||
[&](LayerGroupBase& layerGroup, size_t ordinal) {
|
||||
parameters.currentLayer = static_cast<uint32_t>(orchestrator.numLayerGroups() - 1 - ordinal);
|
||||
layerGroup.render(orchestrator, parameters);
|
||||
particle::incrementRendererDebugCounter(
|
||||
particle::RendererDebugCounter::RetainedUnderlayStaticGroupDraw);
|
||||
});
|
||||
parameters.pass = RenderPass::Translucent;
|
||||
orchestrator.visitLayerGroupsBefore(*retainedBoundary, [&](LayerGroupBase& layerGroup, size_t ordinal) {
|
||||
orchestrator.visitLayerGroupsInRange(begin, endExclusive, [&](LayerGroupBase& layerGroup, size_t ordinal) {
|
||||
parameters.currentLayer = static_cast<uint32_t>(orchestrator.numLayerGroups() - 1 - ordinal);
|
||||
layerGroup.render(orchestrator, parameters);
|
||||
particle::incrementRendererDebugCounter(
|
||||
particle::RendererDebugCounter::RetainedUnderlayStaticGroupDraw);
|
||||
});
|
||||
context.unbindGlobalUniformBuffers(*parameters.renderPass);
|
||||
retainedUnderlayCache->finishBuild(parameters);
|
||||
retainedUnderlayCache->markBuildSucceeded();
|
||||
cache.finishBuild(parameters);
|
||||
cache.markBuildSucceeded();
|
||||
};
|
||||
|
||||
const auto commonClearPass = [&] {
|
||||
@@ -711,9 +819,10 @@ void Renderer::Impl::render(const RenderTree& renderTree,
|
||||
PaintParameters::depthEpsilon;
|
||||
|
||||
// draw layer groups, opaque pass
|
||||
if (useRetainedUnderlay && retainedBoundary) {
|
||||
orchestrator.visitLayerGroupsFromReversed(*retainedBoundary,
|
||||
[&](LayerGroupBase& layerGroup, size_t ordinal) {
|
||||
if (useRetainedRanges && retainedMode) {
|
||||
orchestrator.visitLayerGroupsInRangeReversed(
|
||||
ranges.continuousBegin, ranges.continuousEndExclusive,
|
||||
[&](LayerGroupBase& layerGroup, size_t ordinal) {
|
||||
parameters.currentLayer = static_cast<uint32_t>(orchestrator.numLayerGroups() - 1 - ordinal);
|
||||
layerGroup.render(orchestrator, parameters);
|
||||
particle::incrementRendererDebugCounter(particle::RendererDebugCounter::RetainedDynamicGroupDraw);
|
||||
@@ -734,8 +843,10 @@ void Renderer::Impl::render(const RenderTree& renderTree,
|
||||
PaintParameters::depthEpsilon;
|
||||
|
||||
// draw layer groups, translucent pass
|
||||
if (useRetainedUnderlay && retainedBoundary) {
|
||||
orchestrator.visitLayerGroupsFrom(*retainedBoundary, [&](LayerGroupBase& layerGroup, size_t ordinal) {
|
||||
if (useRetainedRanges && retainedMode) {
|
||||
orchestrator.visitLayerGroupsInRange(
|
||||
ranges.continuousBegin, ranges.continuousEndExclusive,
|
||||
[&](LayerGroupBase& layerGroup, size_t ordinal) {
|
||||
parameters.currentLayer = static_cast<uint32_t>(orchestrator.numLayerGroups() - 1 - ordinal);
|
||||
layerGroup.render(orchestrator, parameters);
|
||||
particle::incrementRendererDebugCounter(particle::RendererDebugCounter::RetainedDynamicGroupDraw);
|
||||
@@ -758,7 +869,10 @@ void Renderer::Impl::render(const RenderTree& renderTree,
|
||||
for (auto it = layerRenderItems.begin(); it != layerRenderItems.end() && i >= 0; ++it, --i) {
|
||||
parameters.currentLayer = i;
|
||||
const RenderItem& item = *it;
|
||||
if (item.hasRenderPass(parameters.pass)) {
|
||||
const bool itemInLiveRange = !useRetainedRanges || !retainedMode ||
|
||||
(static_cast<int32_t>(item.index) >= ranges.continuousBegin &&
|
||||
static_cast<int32_t>(item.index) < ranges.continuousEndExclusive);
|
||||
if (itemInLiveRange && item.hasRenderPass(parameters.pass)) {
|
||||
item.render(parameters);
|
||||
}
|
||||
}
|
||||
@@ -782,19 +896,23 @@ void Renderer::Impl::render(const RenderTree& renderTree,
|
||||
drawable3DPass();
|
||||
}
|
||||
drawableTargetsPass();
|
||||
buildRetainedUnderlay();
|
||||
buildRetainedRange(*retainedUnderlayCache, bottomDecision, 0, ranges.continuousBegin);
|
||||
buildRetainedRange(*retainedTopCache, topDecision, ranges.continuousEndExclusive, ranges.layerCount);
|
||||
commonClearPass();
|
||||
context.bindGlobalUniformBuffers(*parameters.renderPass);
|
||||
if (useRetainedUnderlay) retainedUnderlayCache->composite(orchestrator, parameters);
|
||||
if (useRetainedRanges && ranges.bottomPresent()) retainedUnderlayCache->composite(orchestrator, parameters);
|
||||
drawableOpaquePass();
|
||||
drawableTranslucentPass();
|
||||
if (useRetainedRanges && ranges.topPresent()) retainedTopCache->composite(orchestrator, parameters);
|
||||
drawableDebugOverlays();
|
||||
|
||||
// Give the layers a chance to do cleanup
|
||||
if (particleFastFrame && retainedBoundary) {
|
||||
orchestrator.visitLayerGroupsFrom(*retainedBoundary, [&](LayerGroupBase& layerGroup, size_t) {
|
||||
layerGroup.postRender(orchestrator, parameters);
|
||||
});
|
||||
if (particleFastFrame && retainedMode) {
|
||||
orchestrator.visitLayerGroupsInRange(
|
||||
ranges.continuousBegin, ranges.continuousEndExclusive,
|
||||
[&](LayerGroupBase& layerGroup, size_t) {
|
||||
layerGroup.postRender(orchestrator, parameters);
|
||||
});
|
||||
} else {
|
||||
orchestrator.visitLayerGroups(
|
||||
[&](LayerGroupBase& layerGroup) { layerGroup.postRender(orchestrator, parameters); });
|
||||
|
||||
@@ -62,6 +62,7 @@ private:
|
||||
const float pixelRatio;
|
||||
std::unique_ptr<RenderStaticData> staticData;
|
||||
std::unique_ptr<RetainedUnderlayCache> retainedUnderlayCache;
|
||||
std::unique_ptr<RetainedUnderlayCache> retainedTopCache;
|
||||
RetainedUnderlayRevisionTracker retainedUnderlayRevisionTracker;
|
||||
RetainedUnderlayTopologyTracker retainedUnderlayTopologyTracker;
|
||||
ParticleFastPathPreparedTree<RenderTree> particlePreparedTree;
|
||||
@@ -107,6 +108,10 @@ private:
|
||||
uint64_t particlePreparedTopologyRevision = 0;
|
||||
uint64_t particlePreparedStaticRevision = 0;
|
||||
std::optional<int32_t> particlePreparedBoundary;
|
||||
std::optional<int32_t> particlePreparedContinuousEnd;
|
||||
uint64_t particlePreparedRangeConfigRevision = 0;
|
||||
bool particlePreparedBottomPresent = false;
|
||||
bool particlePreparedTopPresent = false;
|
||||
Size particlePreparedBackendSize;
|
||||
bool particlePreparedSceneSupported = false;
|
||||
ParticleFastPathMissReason particleFastPathLastReason = ParticleFastPathMissReason::NoPreparedTree;
|
||||
|
||||
@@ -77,7 +77,9 @@ uint64_t RetainedUnderlayRevisionTracker::observe(const RetainedUnderlayRevision
|
||||
lastReason = "viewport";
|
||||
} else if (previous.pixelRatio != next.pixelRatio) {
|
||||
lastReason = "pixel-ratio";
|
||||
} else if (previous.cameraMatrix != next.cameraMatrix || previous.boundary != next.boundary) {
|
||||
} else if (previous.cameraMatrix != next.cameraMatrix || previous.boundary != next.boundary ||
|
||||
previous.continuousEndExclusive != next.continuousEndExclusive ||
|
||||
previous.rangeConfigRevision != next.rangeConfigRevision) {
|
||||
lastReason = "camera-or-boundary";
|
||||
} else if (previous.topologyRevision != next.topologyRevision) {
|
||||
lastReason = "layer-group-mutation";
|
||||
@@ -106,8 +108,8 @@ uint64_t RetainedUnderlayRevisionTracker::observe(const RetainedUnderlayRevision
|
||||
return revision;
|
||||
}
|
||||
|
||||
RetainedUnderlayCache::RetainedUnderlayCache()
|
||||
: runtimeState(std::make_shared<RetainedUnderlayRuntimeState>()) {}
|
||||
RetainedUnderlayCache::RetainedUnderlayCache(RetainedRangeRole role_)
|
||||
: runtimeState(std::make_shared<RetainedUnderlayRuntimeState>()), role(role_) {}
|
||||
RetainedUnderlayCache::~RetainedUnderlayCache() = default;
|
||||
|
||||
void RetainedUnderlayCache::synchronizeRuntimeState(
|
||||
@@ -123,6 +125,7 @@ void RetainedUnderlayCache::synchronizeRuntimeState(
|
||||
observedControlRevision = revision;
|
||||
// GPU resources are owned and released on the renderer thread. The public setter changes
|
||||
// the shared enabled bit first and schedules this synchronization through Map::onUpdate().
|
||||
if (valid || key) invalidate(runtimeState->isEnabled() ? "camera-or-boundary" : "runtime-disabled");
|
||||
releaseResources();
|
||||
if (!runtimeState->isEnabled()) {
|
||||
lastInvalidationReason = "runtime-disabled";
|
||||
@@ -178,6 +181,7 @@ RetainedUnderlayDecision RetainedUnderlayCache::evaluate(const std::optional<Ret
|
||||
if (valid) {
|
||||
++reuseCount;
|
||||
runtimeState->recordReuse();
|
||||
runtimeState->recordRangeReuse(role == RetainedRangeRole::Top);
|
||||
particle::incrementRendererDebugCounter(particle::RendererDebugCounter::RetainedUnderlayReuse);
|
||||
return RetainedUnderlayDecision::Reuse;
|
||||
}
|
||||
@@ -202,7 +206,8 @@ bool RetainedUnderlayCache::prepareResources(gfx::Context& context,
|
||||
|
||||
bool RetainedUnderlayCache::rebuildComposite(gfx::Context& context, gfx::ShaderRegistry& shaders) {
|
||||
auto shader = context.getGenericShader(shaders, CompositeShaderName);
|
||||
auto group = context.createLayerGroup(0, 1, "retained-underlay-composite");
|
||||
auto group = context.createLayerGroup(
|
||||
0, 1, role == RetainedRangeRole::Top ? "retained-top-composite" : "retained-bottom-composite");
|
||||
if (!shader || !group || !offscreenTexture) return false;
|
||||
|
||||
auto vertices = std::make_shared<gfx::VertexVector<HeatmapTextureLayoutVertex>>(
|
||||
@@ -242,7 +247,7 @@ void RetainedUnderlayCache::upload(gfx::UploadPass& uploadPass) {
|
||||
|
||||
void RetainedUnderlayCache::beginBuild(PaintParameters& parameters) {
|
||||
parameters.renderPass = parameters.encoder->createRenderPass(
|
||||
"retained static underlay",
|
||||
role == RetainedRangeRole::Top ? "retained top range" : "retained bottom range",
|
||||
{.renderable = *offscreenTexture,
|
||||
.clearColor = Color{0.0f, 0.0f, 0.0f, 0.0f},
|
||||
.clearDepth = 1.0f,
|
||||
@@ -269,6 +274,7 @@ void RetainedUnderlayCache::markBuildSucceeded() {
|
||||
buildRevision = observedStaticRevision;
|
||||
++buildCount;
|
||||
runtimeState->recordBuild();
|
||||
runtimeState->recordRangeBuild(role == RetainedRangeRole::Top);
|
||||
particle::incrementRendererDebugCounter(particle::RendererDebugCounter::RetainedUnderlayBuild);
|
||||
}
|
||||
|
||||
@@ -284,6 +290,7 @@ void RetainedUnderlayCache::invalidate(const char* reason) {
|
||||
valid = false;
|
||||
lastInvalidationReason = reason ? reason : "unknown";
|
||||
runtimeState->recordInvalidation(retainedReason(reason));
|
||||
runtimeState->recordRangeInvalidation(role == RetainedRangeRole::Top);
|
||||
particle::incrementRendererDebugCounter(particle::RendererDebugCounter::RetainedUnderlayInvalidation);
|
||||
}
|
||||
|
||||
|
||||
@@ -25,12 +25,15 @@ class UploadPass;
|
||||
|
||||
struct RetainedUnderlayKey {
|
||||
Size viewport;
|
||||
int32_t boundary = 0;
|
||||
int32_t begin = 0;
|
||||
int32_t endExclusive = 0;
|
||||
mat4 cameraMatrix{};
|
||||
|
||||
bool operator==(const RetainedUnderlayKey&) const = default;
|
||||
};
|
||||
|
||||
enum class RetainedRangeRole : uint8_t { Bottom, Top };
|
||||
|
||||
struct RetainedUnderlayTopologyEntry {
|
||||
int32_t layerIndex = 0;
|
||||
std::string groupName;
|
||||
@@ -42,6 +45,8 @@ struct RetainedUnderlayTopologyEntry {
|
||||
struct RetainedUnderlayTopologySnapshot {
|
||||
std::optional<int32_t> boundary;
|
||||
std::vector<RetainedUnderlayTopologyEntry> staticPrefix;
|
||||
std::optional<int32_t> continuousEndExclusive;
|
||||
std::vector<RetainedUnderlayTopologyEntry> staticSuffix;
|
||||
|
||||
bool operator==(const RetainedUnderlayTopologySnapshot&) const = default;
|
||||
};
|
||||
@@ -60,6 +65,8 @@ struct RetainedUnderlayRevisionState {
|
||||
Size viewport;
|
||||
mat4 cameraMatrix{};
|
||||
std::optional<int32_t> boundary;
|
||||
std::optional<int32_t> continuousEndExclusive;
|
||||
uint64_t rangeConfigRevision = 0;
|
||||
float pixelRatio = 1.0f;
|
||||
uint64_t contentRevision = 0;
|
||||
uint64_t topologyRevision = 0;
|
||||
@@ -107,7 +114,7 @@ inline bool retainedLayerGroupInRange(int32_t layerIndex, int32_t boundary, Reta
|
||||
|
||||
class RetainedUnderlayCache {
|
||||
public:
|
||||
RetainedUnderlayCache();
|
||||
explicit RetainedUnderlayCache(RetainedRangeRole = RetainedRangeRole::Bottom);
|
||||
~RetainedUnderlayCache();
|
||||
|
||||
RetainedUnderlayDecision evaluate(const std::optional<RetainedUnderlayKey>&,
|
||||
@@ -150,6 +157,7 @@ private:
|
||||
std::string lastInvalidationReason;
|
||||
std::shared_ptr<RetainedUnderlayRuntimeState> runtimeState;
|
||||
uint64_t observedControlRevision = 0;
|
||||
RetainedRangeRole role;
|
||||
};
|
||||
|
||||
} // namespace mbgl
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
#include <mbgl/renderer/retained_underlay.hpp>
|
||||
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include <utility>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
@@ -83,15 +85,81 @@ public:
|
||||
bool isEnabled() const noexcept { return enabled.load(std::memory_order_acquire); }
|
||||
uint64_t getControlRevision() const noexcept { return controlRevision.load(std::memory_order_acquire); }
|
||||
|
||||
bool setContinuousLayerRange(std::string startLayerId, std::string endLayerId) {
|
||||
if (startLayerId.empty() || endLayerId.empty()) return false;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(rangeMutex);
|
||||
if (rangeConfig.configured && rangeConfig.startLayerId == startLayerId &&
|
||||
rangeConfig.endLayerId == endLayerId) {
|
||||
return true;
|
||||
}
|
||||
rangeConfig.configured = true;
|
||||
rangeConfig.startLayerId = std::move(startLayerId);
|
||||
rangeConfig.endLayerId = std::move(endLayerId);
|
||||
rangeConfig.revision++;
|
||||
rangeResolution = {};
|
||||
rangeResolution.configured = true;
|
||||
rangeResolution.startLayerId = rangeConfig.startLayerId;
|
||||
rangeResolution.endLayerId = rangeConfig.endLayerId;
|
||||
rangeResolution.configRevision = rangeConfig.revision;
|
||||
rangeResolution.reason = "style-not-ready";
|
||||
fallback.fetch_add(1, std::memory_order_relaxed);
|
||||
}
|
||||
controlRevision.fetch_add(1, std::memory_order_release);
|
||||
return true;
|
||||
}
|
||||
|
||||
ContinuousLayerRangeConfig continuousLayerRangeConfig() const {
|
||||
std::lock_guard<std::mutex> lock(rangeMutex);
|
||||
return rangeConfig;
|
||||
}
|
||||
|
||||
void recordRangeResolution(const ContinuousLayerRangeResolution& value, bool bottom, bool top) {
|
||||
std::lock_guard<std::mutex> lock(rangeMutex);
|
||||
if (value.configRevision != rangeConfig.revision) return;
|
||||
const bool changed = rangeResolution.configRevision != value.configRevision ||
|
||||
rangeResolution.valid != value.valid ||
|
||||
rangeResolution.startIndex != value.startIndex ||
|
||||
rangeResolution.endIndex != value.endIndex ||
|
||||
rangeResolution.reason != value.reason;
|
||||
rangeResolution = value;
|
||||
bottomRangePresent = bottom;
|
||||
topRangePresent = top;
|
||||
if (changed && value.configured && !value.valid) {
|
||||
fallback.fetch_add(1, std::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
RetainedUnderlayDiagnostics snapshot() const {
|
||||
return {
|
||||
isEnabled(),
|
||||
build.load(std::memory_order_relaxed),
|
||||
reuse.load(std::memory_order_relaxed),
|
||||
fallback.load(std::memory_order_relaxed),
|
||||
invalidation.load(std::memory_order_relaxed),
|
||||
retainedUnderlayReasonText(lastReason.load(std::memory_order_acquire)),
|
||||
};
|
||||
RetainedUnderlayDiagnostics result;
|
||||
result.enabled = isEnabled();
|
||||
result.build = build.load(std::memory_order_relaxed);
|
||||
result.reuse = reuse.load(std::memory_order_relaxed);
|
||||
result.fallback = fallback.load(std::memory_order_relaxed);
|
||||
result.invalidation = invalidation.load(std::memory_order_relaxed);
|
||||
result.lastReason = retainedUnderlayReasonText(lastReason.load(std::memory_order_acquire));
|
||||
result.bottomBuild = bottomBuild.load(std::memory_order_relaxed);
|
||||
result.bottomReuse = bottomReuse.load(std::memory_order_relaxed);
|
||||
result.bottomInvalidation = bottomInvalidation.load(std::memory_order_relaxed);
|
||||
result.topBuild = topBuild.load(std::memory_order_relaxed);
|
||||
result.topReuse = topReuse.load(std::memory_order_relaxed);
|
||||
result.topInvalidation = topInvalidation.load(std::memory_order_relaxed);
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(rangeMutex);
|
||||
result.rangeConfigured = rangeResolution.configured;
|
||||
result.rangeValid = rangeResolution.valid;
|
||||
result.startLayerId = rangeResolution.startLayerId;
|
||||
result.endLayerId = rangeResolution.endLayerId;
|
||||
result.startIndex = rangeResolution.startIndex;
|
||||
result.endIndex = rangeResolution.endIndex;
|
||||
result.bottomPresent = bottomRangePresent;
|
||||
result.topPresent = topRangePresent;
|
||||
result.rangeFallback = rangeResolution.configured && !rangeResolution.valid;
|
||||
if (result.rangeFallback) {
|
||||
result.lastReason = rangeResolution.reason;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void recordBuild() noexcept { build.fetch_add(1, std::memory_order_relaxed); }
|
||||
@@ -101,6 +169,15 @@ public:
|
||||
lastReason.store(reason, std::memory_order_release);
|
||||
invalidation.fetch_add(1, std::memory_order_relaxed);
|
||||
}
|
||||
void recordRangeBuild(bool top) noexcept {
|
||||
(top ? topBuild : bottomBuild).fetch_add(1, std::memory_order_relaxed);
|
||||
}
|
||||
void recordRangeReuse(bool top) noexcept {
|
||||
(top ? topReuse : bottomReuse).fetch_add(1, std::memory_order_relaxed);
|
||||
}
|
||||
void recordRangeInvalidation(bool top) noexcept {
|
||||
(top ? topInvalidation : bottomInvalidation).fetch_add(1, std::memory_order_relaxed);
|
||||
}
|
||||
private:
|
||||
std::atomic<bool> enabled{true};
|
||||
std::atomic<uint64_t> controlRevision{0};
|
||||
@@ -109,6 +186,17 @@ private:
|
||||
std::atomic<uint64_t> fallback{0};
|
||||
std::atomic<uint64_t> invalidation{0};
|
||||
std::atomic<RetainedUnderlayReason> lastReason{RetainedUnderlayReason::None};
|
||||
std::atomic<uint64_t> bottomBuild{0};
|
||||
std::atomic<uint64_t> bottomReuse{0};
|
||||
std::atomic<uint64_t> bottomInvalidation{0};
|
||||
std::atomic<uint64_t> topBuild{0};
|
||||
std::atomic<uint64_t> topReuse{0};
|
||||
std::atomic<uint64_t> topInvalidation{0};
|
||||
mutable std::mutex rangeMutex;
|
||||
ContinuousLayerRangeConfig rangeConfig;
|
||||
ContinuousLayerRangeResolution rangeResolution;
|
||||
bool bottomRangePresent = false;
|
||||
bool topRangePresent = false;
|
||||
};
|
||||
|
||||
} // namespace mbgl
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <mbgl/renderer/retained_underlay_cache.hpp>
|
||||
#include <mbgl/renderer/particle_fast_path.hpp>
|
||||
#include <mbgl/renderer/continuous_layer_range.hpp>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
@@ -10,7 +11,7 @@ using namespace mbgl;
|
||||
|
||||
namespace {
|
||||
RetainedUnderlayKey key(Size size = {640, 480}, int32_t boundary = 4) {
|
||||
RetainedUnderlayKey result{size, boundary, {}};
|
||||
RetainedUnderlayKey result{size, 0, boundary, {}};
|
||||
result.cameraMatrix[0] = 1.0;
|
||||
result.cameraMatrix[5] = 1.0;
|
||||
result.cameraMatrix[10] = 1.0;
|
||||
@@ -128,7 +129,7 @@ TEST(RetainedUnderlayRevision, MonotonicAcrossCameraViewportStyleAndSourceChange
|
||||
RetainedUnderlayCache cache;
|
||||
auto state = revisionState();
|
||||
const auto currentKey = [&] {
|
||||
return RetainedUnderlayKey{state.viewport, *state.boundary, state.cameraMatrix};
|
||||
return RetainedUnderlayKey{state.viewport, 0, *state.boundary, state.cameraMatrix};
|
||||
};
|
||||
EXPECT_EQ(tracker.observe(state), 1u);
|
||||
EXPECT_EQ(tracker.observe(state), 1u);
|
||||
@@ -191,6 +192,134 @@ TEST(RetainedUnderlayRevision, MonotonicAcrossCameraViewportStyleAndSourceChange
|
||||
RetainedUnderlayDecision::Build);
|
||||
}
|
||||
|
||||
TEST(ContinuousLayerRange, ResolvesInclusiveSingleAndMultiLayerRanges) {
|
||||
const std::vector<std::string> layers{"bottom", "hidden-start", "middle", "hidden-end", "top"};
|
||||
auto single = resolveContinuousLayerRange({true, "middle", "middle", 1}, layers);
|
||||
ASSERT_TRUE(single.valid);
|
||||
EXPECT_EQ(single.startIndex, 2);
|
||||
EXPECT_EQ(single.endIndex, 2);
|
||||
EXPECT_EQ(single.endExclusive(), 3);
|
||||
|
||||
auto multi = resolveContinuousLayerRange({true, "hidden-start", "hidden-end", 2}, layers);
|
||||
ASSERT_TRUE(multi.valid);
|
||||
EXPECT_EQ(multi.startIndex, 1);
|
||||
EXPECT_EQ(multi.endIndex, 3);
|
||||
EXPECT_TRUE(multi.bottomPresent());
|
||||
EXPECT_TRUE(multi.topPresent(static_cast<int32_t>(layers.size())));
|
||||
}
|
||||
|
||||
TEST(ContinuousLayerRange, RejectsMissingAndReversedEndpointsWithoutStaleIndices) {
|
||||
const std::vector<std::string> layers{"a", "b", "c"};
|
||||
const auto reversed = resolveContinuousLayerRange({true, "c", "a", 1}, layers);
|
||||
EXPECT_FALSE(reversed.valid);
|
||||
EXPECT_EQ(reversed.reason, "range-order-invalid");
|
||||
|
||||
const auto missingStart = resolveContinuousLayerRange({true, "missing", "c", 2}, layers);
|
||||
EXPECT_FALSE(missingStart.valid);
|
||||
EXPECT_EQ(missingStart.startIndex, -1);
|
||||
EXPECT_EQ(missingStart.reason, "start-layer-missing");
|
||||
|
||||
const auto missingEnd = resolveContinuousLayerRange({true, "a", "missing", 3}, layers);
|
||||
EXPECT_FALSE(missingEnd.valid);
|
||||
EXPECT_EQ(missingEnd.endIndex, -1);
|
||||
EXPECT_EQ(missingEnd.reason, "end-layer-missing");
|
||||
}
|
||||
|
||||
TEST(ContinuousLayerRange, SupportsAllFourDegradedTopologies) {
|
||||
const std::vector<std::string> layers{"a", "b", "c", "d"};
|
||||
const auto three = resolveContinuousLayerRange({true, "b", "c", 1}, layers);
|
||||
EXPECT_TRUE(three.bottomPresent());
|
||||
EXPECT_TRUE(three.topPresent(4));
|
||||
|
||||
const auto bottomOnly = resolveContinuousLayerRange({true, "b", "d", 2}, layers);
|
||||
EXPECT_TRUE(bottomOnly.bottomPresent());
|
||||
EXPECT_FALSE(bottomOnly.topPresent(4));
|
||||
|
||||
const auto topOnly = resolveContinuousLayerRange({true, "a", "c", 3}, layers);
|
||||
EXPECT_FALSE(topOnly.bottomPresent());
|
||||
EXPECT_TRUE(topOnly.topPresent(4));
|
||||
|
||||
const auto continuousOnly = resolveContinuousLayerRange({true, "a", "d", 4}, layers);
|
||||
EXPECT_TRUE(continuousOnly.valid);
|
||||
EXPECT_FALSE(continuousOnly.bottomPresent());
|
||||
EXPECT_FALSE(continuousOnly.topPresent(4));
|
||||
}
|
||||
|
||||
TEST(ContinuousLayerRange, OnlyActuallyPresentRetainedRangesRequestCacheBuilds) {
|
||||
RetainedUnderlayCache bottom(RetainedRangeRole::Bottom);
|
||||
RetainedUnderlayCache top(RetainedRangeRole::Top);
|
||||
const auto countBuilds = [&](bool bottomPresent, bool topPresent) {
|
||||
const auto bottomDecision = bottom.evaluate(bottomPresent ? std::optional<RetainedUnderlayKey>(key())
|
||||
: std::nullopt,
|
||||
1, true);
|
||||
const auto topDecision = top.evaluate(topPresent ? std::optional<RetainedUnderlayKey>(key())
|
||||
: std::nullopt,
|
||||
1, true);
|
||||
return static_cast<int>(bottomDecision == RetainedUnderlayDecision::Build) +
|
||||
static_cast<int>(topDecision == RetainedUnderlayDecision::Build);
|
||||
};
|
||||
EXPECT_EQ(countBuilds(true, true), 2);
|
||||
EXPECT_EQ(countBuilds(true, false), 1);
|
||||
EXPECT_EQ(countBuilds(false, true), 1);
|
||||
EXPECT_EQ(countBuilds(false, false), 0);
|
||||
}
|
||||
|
||||
TEST(ContinuousLayerRange, ReorderAndEndpointDeletionAreResolvedFromCurrentStyle) {
|
||||
const ContinuousLayerRangeConfig config{true, "b", "c", 7};
|
||||
EXPECT_TRUE(resolveContinuousLayerRange(config, {"a", "b", "c"}).valid);
|
||||
EXPECT_FALSE(resolveContinuousLayerRange(config, {"a", "c", "b"}).valid);
|
||||
const auto deleted = resolveContinuousLayerRange(config, {"a", "b"});
|
||||
EXPECT_FALSE(deleted.valid);
|
||||
EXPECT_EQ(deleted.reason, "end-layer-missing");
|
||||
}
|
||||
|
||||
TEST(ContinuousLayerRange, RuntimeConfigurationRejectsEmptyIdsAndAdvancesMonotonically) {
|
||||
RetainedUnderlayRuntimeState state;
|
||||
EXPECT_FALSE(state.setContinuousLayerRange("", "end"));
|
||||
EXPECT_EQ(state.continuousLayerRangeConfig().revision, 0u);
|
||||
EXPECT_TRUE(state.setContinuousLayerRange("start", "end"));
|
||||
EXPECT_EQ(state.continuousLayerRangeConfig().revision, 1u);
|
||||
EXPECT_TRUE(state.setContinuousLayerRange("start", "end"));
|
||||
EXPECT_EQ(state.continuousLayerRangeConfig().revision, 1u);
|
||||
EXPECT_TRUE(state.setContinuousLayerRange("same", "same"));
|
||||
const auto config = state.continuousLayerRangeConfig();
|
||||
EXPECT_EQ(config.revision, 2u);
|
||||
EXPECT_EQ(config.startLayerId, config.endLayerId);
|
||||
}
|
||||
|
||||
TEST(ContinuousLayerRange, ExplicitConfigurationNeverMixesWithLegacyBoundary) {
|
||||
ContinuousLayerRangeResolution valid;
|
||||
valid.configured = true;
|
||||
valid.valid = true;
|
||||
valid.startIndex = 0;
|
||||
valid.endIndex = 1;
|
||||
valid.configRevision = 4;
|
||||
const auto explicitPlan = selectContinuousRenderRanges(valid, 7, 10);
|
||||
EXPECT_EQ(explicitPlan.continuousBegin, 0);
|
||||
EXPECT_EQ(explicitPlan.continuousEndExclusive, 2);
|
||||
EXPECT_FALSE(explicitPlan.bottomPresent());
|
||||
EXPECT_TRUE(explicitPlan.topPresent());
|
||||
|
||||
valid.valid = false;
|
||||
valid.reason = "start-layer-missing";
|
||||
const auto invalidPlan = selectContinuousRenderRanges(valid, 7, 10);
|
||||
EXPECT_FALSE(invalidPlan.retainedMode());
|
||||
EXPECT_FALSE(invalidPlan.bottomPresent());
|
||||
EXPECT_FALSE(invalidPlan.topPresent());
|
||||
|
||||
const ContinuousLayerRangeResolution unconfigured;
|
||||
const auto legacyPlan = selectContinuousRenderRanges(unconfigured, 7, 10);
|
||||
EXPECT_TRUE(legacyPlan.retainedMode());
|
||||
EXPECT_EQ(legacyPlan.continuousBegin, 7);
|
||||
EXPECT_FALSE(legacyPlan.topPresent());
|
||||
}
|
||||
|
||||
TEST(ContinuousLayerRange, AbsentRetainedRangesDoNotCauseFastPathMiss) {
|
||||
auto input = fastPathInput();
|
||||
input.cacheValid = true; // all zero actually-present retained ranges are valid vacuously.
|
||||
EXPECT_TRUE(evaluateParticleFastPath(input).candidate);
|
||||
}
|
||||
|
||||
TEST(RetainedUnderlayRevision, RepeatedFramesWithoutSemanticMutationDoNotAdvanceRevision) {
|
||||
RetainedUnderlayRevisionTracker tracker;
|
||||
auto state = revisionState();
|
||||
|
||||
Reference in New Issue
Block a user