From 27cd95337156b1cd6268165273d6147535e50244 Mon Sep 17 00:00:00 2001 From: Bart Louwers Date: Thu, 7 Dec 2023 12:36:58 +0100 Subject: [PATCH 01/18] Android benchmark on CI (#1905) --- .github/workflows/android-ci.yml | 16 ++ .../android/MapboxGLAndroidSDK/build.gradle | 15 +- .../MapboxGLAndroidSDKLint/build.gradle | 1 - .../MapboxGLAndroidSDKTestApp/build.gradle | 22 +- .../maplibre/android/benchmark/Benchmark.kt | 22 ++ .../src/main/AndroidManifest.xml | 6 +- .../activity/benchmark/BenchmarkActivity.kt | 196 +++++++++++++----- .../android/testapp/utils/BenchmarkUtils.kt | 44 ++++ .../view/LockableBottomSheetBehavior.kt | 20 +- .../test-proguard-rules.pro | 2 + platform/android/build.gradle | 5 +- platform/android/gradle/dependencies.gradle | 5 +- platform/android/gradle/native-build.gradle | 2 - test/android/app/build.gradle | 2 + test/android/build.gradle | 2 +- 15 files changed, 285 insertions(+), 75 deletions(-) create mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/org/maplibre/android/benchmark/Benchmark.kt create mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/org/maplibre/android/testapp/utils/BenchmarkUtils.kt create mode 100644 platform/android/MapboxGLAndroidSDKTestApp/test-proguard-rules.pro diff --git a/.github/workflows/android-ci.yml b/.github/workflows/android-ci.yml index e062923caa1..2dd28d5e9c5 100644 --- a/.github/workflows/android-ci.yml +++ b/.github/workflows/android-ci.yml @@ -118,7 +118,22 @@ jobs: echo "No secrets.MAPLIBRE_DEVELOPER_CONFIG_XML variable set, not copying..." fi + - name: Build Benchmark + if: github.ref != 'refs/heads/main' + run: | + ./gradlew assembleDrawableRelease assembleDrawableReleaseAndroidTest + + - uses: actions/upload-artifact@v3 + if: github.ref != 'refs/heads/main' + with: + if-no-files-found: error + name: benchmarkAPKs + path: | + platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk/drawable/release/MapboxGLAndroidSDKTestApp-drawable-release.apk + platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk/androidTest/drawable/release/MapboxGLAndroidSDKTestApp-drawable-release-androidTest.apk + - name: Build UI tests + if: github.ref == 'refs/heads/main' run: make android-ui-test-arm-v8 - name: Configure AWS Credentials @@ -230,6 +245,7 @@ jobs: project_arn: ${{ vars.AWS_DEVICE_FARM_PROJECT_ARN }} device_pool_arn: ${{ vars.AWS_DEVICE_FARM_DEVICE_POOL_ARN }} app_arn: ${{ env.ANDROID_APP_ARN }} + test_spec_arn: ${{ vars.AWS_DEVICE_FARM_TEST_SPEC_ARN_ANDROID_UI_TESTS }} app_type: ANDROID_APP test_type: INSTRUMENTATION test_package_arn: ${{ env.ANDROID_TEST_ARN }} diff --git a/platform/android/MapboxGLAndroidSDK/build.gradle b/platform/android/MapboxGLAndroidSDK/build.gradle index 10ffec123c1..4904f8ace93 100644 --- a/platform/android/MapboxGLAndroidSDK/build.gradle +++ b/platform/android/MapboxGLAndroidSDK/build.gradle @@ -26,7 +26,6 @@ dependencies { testImplementation dependenciesList.mockito testImplementation dependenciesList.mockk testImplementation dependenciesList.robolectric - testImplementation dependenciesList.kotlinLib testImplementation dependenciesList.commonsIO testImplementation dependenciesList.assertjcore @@ -53,6 +52,7 @@ android { minSdkVersion androidVersions.minSdkVersion targetSdkVersion androidVersions.targetSdkVersion buildConfigField "String", "GIT_REVISION_SHORT", String.format("\"%s\"", getGitRevision()) + buildConfigField "String", "GIT_REVISION", String.format("\"%s\"", getGitRevision(false)) buildConfigField "String", "MAPBOX_SDK_IDENTIFIER", String.format("\"%s\"", "mapbox-maps-android") buildConfigField "String", "MAPBOX_SDK_VERSION", String.format("\"%s\"", project.VERSION_NAME) buildConfigField "String", "MAPBOX_VERSION_STRING", String.format("\"Mapbox/%s\"", project.VERSION_NAME) @@ -128,6 +128,15 @@ android { disable 'MissingTranslation', 'TypographyQuotes', 'ObsoleteLintCustomCheck', 'MissingPermission', 'WrongThreadInterprocedural' warningsAsErrors false } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = "1.8" + } } licenseReport { @@ -137,8 +146,8 @@ licenseReport { copyJsonReportToAssets = false } -def static getGitRevision() { - def cmd = "git rev-parse --short HEAD" +def static getGitRevision(shortRev = true) { + def cmd = shortRev ? "git rev-parse --short HEAD" : "git rev-parse HEAD" def proc = cmd.execute() def ref = proc.text.trim() return ref diff --git a/platform/android/MapboxGLAndroidSDKLint/build.gradle b/platform/android/MapboxGLAndroidSDKLint/build.gradle index 1e0952d91e9..5b74b737df0 100644 --- a/platform/android/MapboxGLAndroidSDKLint/build.gradle +++ b/platform/android/MapboxGLAndroidSDKLint/build.gradle @@ -7,7 +7,6 @@ apply plugin: 'kotlin' dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) - compileOnly dependenciesList.kotlinLib compileOnly dependenciesList.lint compileOnly dependenciesList.lintApi diff --git a/platform/android/MapboxGLAndroidSDKTestApp/build.gradle b/platform/android/MapboxGLAndroidSDKTestApp/build.gradle index 7b2fc628e50..139154ce61b 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/build.gradle +++ b/platform/android/MapboxGLAndroidSDKTestApp/build.gradle @@ -1,6 +1,8 @@ plugins { id 'org.jmailen.kotlinter' id 'com.android.application' + id 'org.jetbrains.kotlin.android' + id 'org.jetbrains.kotlin.plugin.serialization' version '1.9.21' } apply from: "${rootDir}/gradle/native-build.gradle" @@ -23,6 +25,10 @@ android { targetCompatibility JavaVersion.VERSION_1_8 } + kotlinOptions { + jvmTarget = "1.8" + } + nativeBuild(["example-custom-layer"]) packagingOptions { resources { @@ -44,10 +50,13 @@ android { minifyEnabled true shrinkResources true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + testProguardFiles 'test-proguard-rules.pro' signingConfig signingConfigs.debug } } + testBuildType "release" // for benchmark + flavorDimensions += "renderer" productFlavors { create("legacy") { @@ -74,8 +83,6 @@ android { } dependencies { - implementation dependenciesList.kotlinLib - implementation project(':MapboxGLAndroidSDK') implementation dependenciesList.mapboxJavaTurf @@ -83,6 +90,7 @@ dependencies { implementation dependenciesList.supportRecyclerView implementation dependenciesList.supportDesign implementation dependenciesList.supportConstraintLayout + implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0' implementation dependenciesList.multidex @@ -100,14 +108,12 @@ dependencies { androidTestImplementation dependenciesList.testEspressoContrib androidTestImplementation dependenciesList.testUiAutomator androidTestImplementation dependenciesList.appCenter - androidTestImplementation 'androidx.test.ext:junit:1.1.4' + androidTestImplementation 'androidx.test.ext:junit:1.1.5' + androidTestImplementation 'androidx.test:core-ktx:1.5.0' + } apply from: "${rootDir}/gradle/gradle-make.gradle" apply from: "${rootDir}/gradle/gradle-config.gradle" apply from: "${rootDir}/gradle/gradle-checkstyle.gradle" -apply from: "${rootDir}/gradle/gradle-lint.gradle" - - - -apply plugin: 'kotlin-android' +apply from: "${rootDir}/gradle/gradle-lint.gradle" \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/org/maplibre/android/benchmark/Benchmark.kt b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/org/maplibre/android/benchmark/Benchmark.kt new file mode 100644 index 00000000000..b6a057bdad9 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/org/maplibre/android/benchmark/Benchmark.kt @@ -0,0 +1,22 @@ +package org.maplibre.android.benchmark + +import android.app.Activity +import androidx.lifecycle.Lifecycle +import androidx.test.core.app.ActivityScenario +import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner +import org.junit.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith +import org.maplibre.android.testapp.activity.benchmark.BenchmarkActivity + +@RunWith(AndroidJUnit4ClassRunner::class) +class Benchmark { + @Test + fun worldTourBenchmark() { + val scenario = ActivityScenario.launchActivityForResult(BenchmarkActivity::class.java) + while (scenario.state !== Lifecycle.State.DESTROYED) { + Thread.sleep(1000) + } + assertEquals(scenario.result.resultCode, Activity.RESULT_OK) + } +} \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml index 494b37eeefb..ee0bffdca26 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml @@ -1,5 +1,6 @@ - + @@ -1184,10 +1185,11 @@ + tools:ignore="LockedOrientationActivity"> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/org/maplibre/android/testapp/activity/benchmark/BenchmarkActivity.kt b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/org/maplibre/android/testapp/activity/benchmark/BenchmarkActivity.kt index 3d800363179..da8f53aa8a7 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/org/maplibre/android/testapp/activity/benchmark/BenchmarkActivity.kt +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/org/maplibre/android/testapp/activity/benchmark/BenchmarkActivity.kt @@ -1,47 +1,70 @@ package org.maplibre.android.testapp.activity.benchmark import android.annotation.SuppressLint +import android.os.Build import android.os.Bundle +import android.os.Environment import android.os.Handler import android.view.View +import android.view.WindowManager import androidx.appcompat.app.AppCompatActivity +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonObject +import kotlinx.serialization.json.JsonPrimitive +import kotlinx.serialization.json.buildJsonObject +import kotlinx.serialization.json.jsonArray +import kotlinx.serialization.json.jsonObject +import kotlinx.serialization.json.jsonPrimitive +import kotlinx.serialization.json.putJsonObject +import okhttp3.MediaType.Companion.toMediaType +import okhttp3.OkHttpClient +import okhttp3.Request +import okhttp3.RequestBody.Companion.toRequestBody +import org.maplibre.android.BuildConfig.GIT_REVISION import org.maplibre.android.camera.CameraUpdateFactory import org.maplibre.android.geometry.LatLng +import org.maplibre.android.log.Logger import org.maplibre.android.maps.* import org.maplibre.android.maps.MapLibreMap.CancelableCallback +import org.maplibre.android.testapp.BuildConfig import org.maplibre.android.testapp.R -import java.util.* - -class FpsStore { - private val fpsValues = ArrayList(100000) - - fun add(fps: Double) { - fpsValues.add(fps) - } - - fun reset() { - fpsValues.clear() - } +import org.maplibre.android.testapp.utils.FpsStore +import org.maplibre.android.testapp.utils.BenchmarkResults +import java.io.File - fun low1p(): Double { - fpsValues.sort() - return fpsValues.slice(0..(fpsValues.size / 100)).average() - } +import java.util.* - fun average(): Double { - return fpsValues.average() +data class BenchmarkInputData( + val styleNames: List, + val styleURLs: List, + val resultsAPI: String = "" +) { + init { + if (styleNames.size != styleURLs.size) + throw Error("Different size: styleNames=$styleNames, styleURLs=$styleURLs") } } -data class Result(val average: Double, val low1p: Double) - -data class Results( - var map: MutableMap> = mutableMapOf>().withDefault { emptyList() }) - { - - fun addResult(styleName: String, fpsStore: FpsStore) { - val newResults = map.getValue(styleName).plus(Result(fpsStore.average(), fpsStore.low1p())) - map[styleName] = newResults +/** + * Prepares JSON payload that is sent to the API that collects benchmark results. + * See https://github.com/maplibre/ci-runners + */ +fun jsonPayload(results: BenchmarkResults): JsonObject { + return buildJsonObject { + putJsonObject("resultsPerStyle") { + for ((styleName, result) in results.resultsPerStyle) { + putJsonObject(styleName) { + put("avgFps", JsonPrimitive(result.map { it.average }.average())) + put("low1p", JsonPrimitive(result.map { it.low1p }.average())) + } + } + } + put("deviceManufacturer", JsonPrimitive(Build.MANUFACTURER)) + put("model", JsonPrimitive(Build.MODEL)) + put("renderer", JsonPrimitive(BuildConfig.FLAVOR)) + put("debugBuild", JsonPrimitive(BuildConfig.DEBUG)) + put("gitRevision", JsonPrimitive(GIT_REVISION)) } } @@ -49,12 +72,14 @@ data class Results( * Benchmark using a [android.view.TextureView] */ class BenchmarkActivity : AppCompatActivity() { + private val TAG = "BenchmarkActivity" + private lateinit var mapView: MapView private lateinit var maplibreMap: MapLibreMap private var handler: Handler? = null private var delayed: Runnable? = null private var fpsStore = FpsStore() - private var results = Results() + private var results = BenchmarkResults() private var runsLeft = 5 // the styles used for the benchmark @@ -67,27 +92,79 @@ class BenchmarkActivity : AppCompatActivity() { // https://zelonewolf.github.io/openstreetmap-americana/style.json // // ``` - private var styles = listOf(Pair("Demotiles", "https://demotiles.maplibre.org/style.json")) + private lateinit var inputData: BenchmarkInputData @SuppressLint("DiscouragedApi") + private fun getStringFromResources(name: String): String { + return try { + resources.getString(applicationContext.resources.getIdentifier( + name, + "string", + applicationContext.packageName)) + } catch (e: Throwable) { + "" + } + } + + @SuppressLint("DiscouragedApi") + private fun getArrayFromResources(name: String): Array { + return try { + resources.getStringArray(applicationContext.resources.getIdentifier( + name, + "array", + applicationContext.packageName)) + } catch (e: Throwable) { + emptyArray() + } + } + + private fun getBenchmarkInputData(): BenchmarkInputData { + // read input for benchmark from JSON file (on CI) + val jsonFile = File("${Environment.getExternalStorageDirectory()}/benchmark-input.json") + if (jsonFile.isFile) { + val jsonFileContents = jsonFile.readText() + val jsonElement = Json.parseToJsonElement(jsonFileContents) + val styleNames = jsonElement.jsonObject["styleNames"]?.jsonArray?.map { it.jsonPrimitive.content } + val styleURLs = jsonElement.jsonObject["styleURLs"]?.jsonArray?.map { it.jsonPrimitive.content } + val resultsAPI = jsonElement.jsonObject["resultsAPI"]?.jsonPrimitive?.content + if (styleNames == null || styleURLs == null || resultsAPI == null) { + throw Error("${jsonFile.name} is missing elements") + } + return BenchmarkInputData( + styleNames = styleNames.toList(), + styleURLs = styleURLs.toList(), + resultsAPI = resultsAPI + ) + } else { + Logger.i(TAG, "${jsonFile.name} not found, reading from developer-config.xml") + } + + // try to read from developer-config.xml instead (for development) + val styleNames = getArrayFromResources("benchmark_style_names") + val styleURLs = getArrayFromResources("benchmark_style_urls") + if (styleNames.isNotEmpty() && styleURLs.isNotEmpty()) { + return BenchmarkInputData( + styleNames = styleNames.toList(), + styleURLs = styleURLs.toList() + ) + } + + // return default + return BenchmarkInputData( + styleNames = listOf("MapLibre Demotiles"), + styleURLs = listOf("https://demotiles.maplibre.org/style.json") + ) + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_benchmark) + window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) + handler = Handler(mainLooper) setupToolbar() + inputData = getBenchmarkInputData() setupMapView(savedInstanceState) - - val styleNames = resources.getStringArray(applicationContext.resources.getIdentifier( - "benchmark_style_names", - "array", - applicationContext.packageName)) - val styleURLs = resources.getStringArray(applicationContext.resources.getIdentifier( - "benchmark_style_urls", - "array", - applicationContext.packageName)) - if (styleNames.isNotEmpty() && styleNames.size == styleURLs.size) { - styles = styleNames.zip(styleURLs) - } } private fun setupToolbar() { @@ -102,7 +179,7 @@ class BenchmarkActivity : AppCompatActivity() { mapView = findViewById(R.id.mapView) as MapView mapView.getMapAsync { maplibreMap: MapLibreMap -> this@BenchmarkActivity.maplibreMap = maplibreMap - maplibreMap.setStyle(styles[0].second) + maplibreMap.setStyle(inputData.styleURLs[0]) setFpsView(maplibreMap) // Start an animation on the map as well @@ -127,20 +204,20 @@ class BenchmarkActivity : AppCompatActivity() { override fun onFinish() { if (place == PLACES.size - 1) { // done with tour - results.addResult(styles[style].first, fpsStore) + results.addResult(inputData.styleNames[style], fpsStore) fpsStore.reset() println("FPS results $results") - if (style < styles.size - 1) { // continue with next style - maplibreMap.setStyle(styles[style + 1].second) + if (style < inputData.styleURLs.size - 1) { // continue with next style + maplibreMap.setStyle(inputData.styleURLs[style + 1]) flyTo(maplibreMap, 0, style + 1, zoom) } else if (runsLeft > 0) { // start over --runsLeft - maplibreMap.setStyle(styles[0].second) + maplibreMap.setStyle(inputData.styleURLs[0]) flyTo(maplibreMap, 0, 0, zoom) } else { - finish() + benchmarkDone() } return } @@ -196,6 +273,31 @@ class BenchmarkActivity : AppCompatActivity() { mapView.onLowMemory() } + private fun sendResults() { + val api = inputData.resultsAPI + if (api.isEmpty()) { + Logger.i(TAG, "Not sending results to API") + return + } + + val client = OkHttpClient() + + val payload = jsonPayload(results) + Logger.i(TAG, "Sending JSON payload to API: $payload") + + val request = Request.Builder() + .url(api) + .post( + Json.encodeToString(payload).toRequestBody("application/json".toMediaType())) + .build() + client.newCall(request).execute() + } + + private fun benchmarkDone() { + sendResults() + finish() + } + companion object { private val PLACES = arrayOf( LatLng(37.7749, -122.4194), // SF diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/org/maplibre/android/testapp/utils/BenchmarkUtils.kt b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/org/maplibre/android/testapp/utils/BenchmarkUtils.kt new file mode 100644 index 00000000000..ef0b820c1d4 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/org/maplibre/android/testapp/utils/BenchmarkUtils.kt @@ -0,0 +1,44 @@ +package org.maplibre.android.testapp.utils + +import java.util.ArrayList + +class FpsStore { + private val fpsValues = ArrayList(100000) + + fun add(fps: Double) { + fpsValues.add(fps) + } + + fun reset() { + fpsValues.clear() + } + + fun low1p(): Double { + fpsValues.sort() + return fpsValues.slice(0..(fpsValues.size / 100)).average() + } + + fun average(): Double { + return fpsValues.average() + } +} + +/** + * Result of single benchmark run + */ +data class BenchmarkResult(val average: Double, val low1p: Double) + +data class BenchmarkResults( + var resultsPerStyle: MutableMap> = mutableMapOf>().withDefault { emptyList() }) +{ + + fun addResult(styleName: String, fpsStore: FpsStore) { + val newResults = resultsPerStyle.getValue(styleName).plus( + BenchmarkResult( + fpsStore.average(), + fpsStore.low1p() + ) + ) + resultsPerStyle[styleName] = newResults + } +} \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/org/maplibre/android/testapp/view/LockableBottomSheetBehavior.kt b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/org/maplibre/android/testapp/view/LockableBottomSheetBehavior.kt index 716945aeb75..e783ab5fe5e 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/org/maplibre/android/testapp/view/LockableBottomSheetBehavior.kt +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/org/maplibre/android/testapp/view/LockableBottomSheetBehavior.kt @@ -16,7 +16,7 @@ class LockableBottomSheetBehavior(context: Context, attrs: AttributeS override fun onInterceptTouchEvent( parent: CoordinatorLayout, - child: V, + child: V & Any, event: MotionEvent ): Boolean { var handled = false @@ -26,7 +26,11 @@ class LockableBottomSheetBehavior(context: Context, attrs: AttributeS return handled } - override fun onTouchEvent(parent: CoordinatorLayout, child: V, event: MotionEvent): Boolean { + override fun onTouchEvent( + parent: CoordinatorLayout, + child: V & Any, + event: MotionEvent + ): Boolean { var handled = false if (!locked) { handled = super.onTouchEvent(parent, child, event) @@ -36,7 +40,7 @@ class LockableBottomSheetBehavior(context: Context, attrs: AttributeS override fun onStartNestedScroll( coordinatorLayout: CoordinatorLayout, - child: V, + child: V & Any, directTargetChild: View, target: View, nestedScrollAxes: Int @@ -56,7 +60,7 @@ class LockableBottomSheetBehavior(context: Context, attrs: AttributeS override fun onNestedPreScroll( coordinatorLayout: CoordinatorLayout, - child: V, + child: V & Any, target: View, dx: Int, dy: Int, @@ -67,7 +71,11 @@ class LockableBottomSheetBehavior(context: Context, attrs: AttributeS } } - override fun onStopNestedScroll(coordinatorLayout: CoordinatorLayout, child: V, target: View) { + override fun onStopNestedScroll( + coordinatorLayout: CoordinatorLayout, + child: V & Any, + target: View + ) { if (!locked) { super.onStopNestedScroll(coordinatorLayout, child, target) } @@ -75,7 +83,7 @@ class LockableBottomSheetBehavior(context: Context, attrs: AttributeS override fun onNestedPreFling( coordinatorLayout: CoordinatorLayout, - child: V, + child: V & Any, target: View, velocityX: Float, velocityY: Float diff --git a/platform/android/MapboxGLAndroidSDKTestApp/test-proguard-rules.pro b/platform/android/MapboxGLAndroidSDKTestApp/test-proguard-rules.pro new file mode 100644 index 00000000000..93181ceaa4e --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/test-proguard-rules.pro @@ -0,0 +1,2 @@ +-dontwarn java.lang.ClassValue +-dontwarn javax.lang.model.element.Modifier \ No newline at end of file diff --git a/platform/android/build.gradle b/platform/android/build.gradle index 3107bea5013..3db385e00f4 100644 --- a/platform/android/build.gradle +++ b/platform/android/build.gradle @@ -7,17 +7,18 @@ buildscript { google() } dependencies { - classpath 'com.android.tools.build:gradle:8.0.2' + classpath 'com.android.tools.build:gradle:8.1.4' classpath dependenciesList.licensesPlugin - classpath dependenciesList.kotlinPlugin // classpath dependenciesList.jacocoPlugin classpath dependenciesList.gradleNexus + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.21" } } plugins { id("io.github.gradle-nexus.publish-plugin") version "1.1.0" id("org.jmailen.kotlinter") version "3.13.0" apply false + id 'org.jetbrains.kotlin.android' version '1.9.21' apply false } allprojects { diff --git a/platform/android/gradle/dependencies.gradle b/platform/android/gradle/dependencies.gradle index 0d93a002b14..d9eacb7d049 100644 --- a/platform/android/gradle/dependencies.gradle +++ b/platform/android/gradle/dependencies.gradle @@ -7,7 +7,7 @@ ext { ] androidSdkVersions = [ - buildToolsVersion: '31.0.0', + buildToolsVersion: '33.0.1', ndkVersion : '25.1.8937393', cmakeVersion : '3.18.1+', ] @@ -34,7 +34,7 @@ ext { robolectric : '4.9.1', timber : '5.0.1', okhttp : '4.11.0', - kotlin : '1.7.20', + kotlin : '1.9.21', licenses : '0.8.80', lint : '30.3.1', // jacoco : '0.8.5', @@ -78,7 +78,6 @@ ext { okhttp3 : "com.squareup.okhttp3:okhttp:${versions.okhttp}", leakCanary : "com.squareup.leakcanary:leakcanary-android:${versions.leakCanary}", - kotlinLib : "org.jetbrains.kotlin:kotlin-stdlib-jdk8:${versions.kotlin}", kotlinPlugin : "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}", licensesPlugin : "com.jaredsburrows:gradle-license-plugin:${versions.licenses}", // jacocoPlugin : "org.jacoco:org.jacoco.core:${versions.jacoco}", diff --git a/platform/android/gradle/native-build.gradle b/platform/android/gradle/native-build.gradle index 5c66f466fb1..cf27a1cc4cc 100644 --- a/platform/android/gradle/native-build.gradle +++ b/platform/android/gradle/native-build.gradle @@ -3,8 +3,6 @@ ext.nativeBuild = { nativeTargets -> ndkVersion androidSdkVersions.ndkVersion buildToolsVersion androidSdkVersions.buildToolsVersion - defaultPublishConfig project.hasProperty("mapbox.buildtype") ? project.getProperty("mapbox.buildtype") : "debug" - // We sometimes want to invoke Gradle without building a native dependency, e.g. when we just want // to invoke the Java tests. When we explicitly specify an ABI of 'none', no native dependencies are // added. When another ABI is specified explicitly, we're just going to build that ABI. In all other diff --git a/test/android/app/build.gradle b/test/android/app/build.gradle index 76118615177..7a873df205f 100644 --- a/test/android/app/build.gradle +++ b/test/android/app/build.gradle @@ -1,6 +1,8 @@ apply plugin: 'com.android.application' android { + namespace "android.app" + compileSdkVersion 33 defaultConfig { diff --git a/test/android/build.gradle b/test/android/build.gradle index de7242684e1..547d12de1bf 100644 --- a/test/android/build.gradle +++ b/test/android/build.gradle @@ -5,7 +5,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.5.3' + classpath 'com.android.tools.build:gradle:8.1.1' } } From 7ccc5de6b14fe7258be5476c940f60a3ea4aae0d Mon Sep 17 00:00:00 2001 From: Bart Louwers Date: Thu, 7 Dec 2023 16:16:06 +0100 Subject: [PATCH 02/18] Android Benchmark on AWS Device Farm (#1925) --- .../actions/aws-device-farm-run/action.yml | 41 ++++++++++++++----- .github/workflows/android-device-test.yml | 14 +++++++ .../activity/benchmark/BenchmarkActivity.kt | 2 + 3 files changed, 47 insertions(+), 10 deletions(-) diff --git a/.github/actions/aws-device-farm-run/action.yml b/.github/actions/aws-device-farm-run/action.yml index 049876a61ce..6555664220e 100644 --- a/.github/actions/aws-device-farm-run/action.yml +++ b/.github/actions/aws-device-farm-run/action.yml @@ -19,6 +19,12 @@ inputs: testType: description: 'e.g. INSTRUMENTATION, XCTEST' required: true + testFilter: + description: 'Filter tests' + required: false + externalData: + description: 'ARN of external data package' + required: false AWS_ACCESS_KEY_ID: description: "AWS_ACCESS_KEY_ID" required: true @@ -98,13 +104,28 @@ runs: done - name: Schedule test run - uses: realm/aws-devicefarm/test-application@master - with: - name: MapLibre Native ${{ matrix.test.name }} - project_arn: ${{ inputs.AWS_DEVICE_FARM_PROJECT_ARN }} - device_pool_arn: ${{ inputs.AWS_DEVICE_FARM_DEVICE_POOL_ARN }} - app_arn: ${{ env.app_arn }} - app_type: ${{ inputs.appType }} - test_type: ${{ inputs.testType }} - test_package_arn: ${{ env.test_package_arn }} - timeout: 28800 + shell: bash + run: | + arn="$(aws devicefarm schedule-run \ + --project-arn ${{ inputs.AWS_DEVICE_FARM_PROJECT_ARN }} \ + --name "MapLibre Native ${{ matrix.test.name }}" \ + --app-arn ${{ env.app_arn }} \ + --device-pool-arn ${{ inputs.AWS_DEVICE_FARM_DEVICE_POOL_ARN }} \ + --test type=${{ inputs.testType }},testPackageArn=${{ env.test_package_arn }}${{ inputs.testFilter && ",filter=" }}${{ inputs.testFilter }} \ + --execution-configuration jobTimeoutMinutes=28800,videoCapture=false \ + ${{ inputs.externalData && "--configuration extraDataPackageArn=" }}${{ inputs.externalData }} \ + --output text --query "run.arn")" + + # wait until result is not PENDING + # https://awscli.amazonaws.com/v2/documentation/api/latest/reference/devicefarm/get-run.html#output + while true; do + sleep 30 + result="$(aws get-run --arn "$arn" --output text --query "run.result")" + case $result in + FAILED|ERRORED|STOPPED) echo "Run $result" && exit 1 ;; + SKIPPED|PASSED) echo "Run $result" && exit 0 ;; + PENDING) continue ;; + *) echo "Unexpected run result $result" && exit 1 ;; + esac + done + diff --git a/.github/workflows/android-device-test.yml b/.github/workflows/android-device-test.yml index f5db0a0c383..6b7d1d65fd6 100644 --- a/.github/workflows/android-device-test.yml +++ b/.github/workflows/android-device-test.yml @@ -12,6 +12,19 @@ jobs: matrix: test: [ {artifactName: android-render-tests, testFile: RenderTests.apk, appFile: RenderTestsApp.apk, name: "Android Render Tests"}, + { + artifactName: benchmarkAPKs, + testFile: "androidTest/drawable/release/MapboxGLAndroidSDKTestApp-drawable-release-androidTest.apk", + appFile: "drawable/release/MapboxGLAndroidSDKTestApp-drawable-release.apk", + name: "Android Benchmark", + testFilter: "org.maplibre.android.benchmark.Benchmark", + # echo '{"styleNames": [...], "styleURLs": [...], "resultsAPI: "..." }' > benchmark-input.json + # zip benchmark-input.zip benchmark-input.json + # aws devicefarm create-upload --project-arn --type EXTERNAL_DATA --name benchmark-input.zip + # curl -T benchmark-input.zip + # aws devicefarm get-upload + externalData: "arn:aws:devicefarm:us-west-2:373521797162:upload:20687d72-0e46-403e-8f03-0941850665bc/c27174c2-63f4-4cdb-9af9-68957d75ebed" + } ] runs-on: ubuntu-latest steps: @@ -57,6 +70,7 @@ jobs: AWS_ROLE_TO_ASSUME: ${{ vars.AWS_ROLE_TO_ASSUME }} AWS_DEVICE_FARM_PROJECT_ARN: ${{ vars.AWS_DEVICE_FARM_PROJECT_ARN }} AWS_DEVICE_FARM_DEVICE_POOL_ARN: ${{ vars.AWS_DEVICE_FARM_DEVICE_POOL_ARN }} + externalData: ${{ matrix.test.externalData }} - uses: LouisBrunner/checks-action@v1.6.2 if: always() diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/org/maplibre/android/testapp/activity/benchmark/BenchmarkActivity.kt b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/org/maplibre/android/testapp/activity/benchmark/BenchmarkActivity.kt index da8f53aa8a7..f8c6e3b2c5e 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/org/maplibre/android/testapp/activity/benchmark/BenchmarkActivity.kt +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/org/maplibre/android/testapp/activity/benchmark/BenchmarkActivity.kt @@ -1,6 +1,7 @@ package org.maplibre.android.testapp.activity.benchmark import android.annotation.SuppressLint +import android.app.Activity import android.os.Build import android.os.Bundle import android.os.Environment @@ -295,6 +296,7 @@ class BenchmarkActivity : AppCompatActivity() { private fun benchmarkDone() { sendResults() + setResult(Activity.RESULT_OK) finish() } From 18e6802973571ab8442e7a466597f888d2dd18c1 Mon Sep 17 00:00:00 2001 From: Tim Sylvester Date: Thu, 7 Dec 2023 08:21:24 -0800 Subject: [PATCH 03/18] Metal permutations 2 (#1923) --- include/mbgl/renderer/layer_tweaker.hpp | 41 ----- include/mbgl/shaders/background_layer_ubo.hpp | 9 +- include/mbgl/shaders/circle_layer_ubo.hpp | 15 -- .../mbgl/shaders/fill_extrusion_layer_ubo.hpp | 13 -- include/mbgl/shaders/fill_layer_ubo.hpp | 42 ----- include/mbgl/shaders/heatmap_layer_ubo.hpp | 10 -- .../shaders/heatmap_texture_layer_ubo.hpp | 4 +- include/mbgl/shaders/hillshade_layer_ubo.hpp | 6 +- .../shaders/hillshade_prepare_layer_ubo.hpp | 4 - include/mbgl/shaders/layer_ubo.hpp | 13 -- include/mbgl/shaders/line_layer_ubo.hpp | 17 -- include/mbgl/shaders/mtl/background.hpp | 9 +- .../mbgl/shaders/mtl/background_pattern.hpp | 35 ++-- include/mbgl/shaders/mtl/circle.hpp | 64 +++---- include/mbgl/shaders/mtl/clipping_mask.hpp | 1 - include/mbgl/shaders/mtl/collision_box.hpp | 1 - include/mbgl/shaders/mtl/collision_circle.hpp | 1 - include/mbgl/shaders/mtl/common.hpp | 102 ------------ include/mbgl/shaders/mtl/debug.hpp | 1 - include/mbgl/shaders/mtl/fill.hpp | 142 ++++++---------- include/mbgl/shaders/mtl/fill_extrusion.hpp | 60 +++---- .../shaders/mtl/fill_extrusion_pattern.hpp | 156 ++++++++++-------- include/mbgl/shaders/mtl/heatmap.hpp | 62 ++++--- include/mbgl/shaders/mtl/heatmap_texture.hpp | 7 +- include/mbgl/shaders/mtl/hillshade.hpp | 11 +- .../mbgl/shaders/mtl/hillshade_prepare.hpp | 12 +- include/mbgl/shaders/mtl/line.hpp | 115 ++++++------- include/mbgl/shaders/mtl/line_gradient.hpp | 86 +++++++--- include/mbgl/shaders/mtl/raster.hpp | 8 +- include/mbgl/shaders/mtl/shader_group.hpp | 34 ++-- include/mbgl/shaders/mtl/shader_program.hpp | 4 - include/mbgl/shaders/mtl/symbol_icon.hpp | 43 ++--- include/mbgl/shaders/mtl/symbol_sdf.hpp | 73 ++++---- .../mbgl/shaders/mtl/symbol_text_and_icon.hpp | 68 ++++---- include/mbgl/shaders/raster_layer_ubo.hpp | 4 +- include/mbgl/shaders/symbol_layer_ubo.hpp | 12 -- .../drawable.hillshade_prepare.fragment.glsl | 4 - .../drawable.hillshade_prepare.vertex.glsl | 4 - src/mbgl/mtl/drawable.cpp | 8 +- src/mbgl/programs/program_parameters.cpp | 3 +- src/mbgl/programs/program_parameters.hpp | 4 + src/mbgl/renderer/layer_tweaker.cpp | 45 ----- .../layers/background_layer_tweaker.cpp | 8 +- .../renderer/layers/circle_layer_tweaker.cpp | 38 ----- .../renderer/layers/circle_layer_tweaker.hpp | 5 - .../layers/fill_extrusion_layer_tweaker.cpp | 37 ----- .../renderer/layers/fill_layer_tweaker.cpp | 108 ------------ .../renderer/layers/fill_layer_tweaker.hpp | 8 - .../renderer/layers/heatmap_layer_tweaker.cpp | 34 ---- .../layers/heatmap_texture_layer_tweaker.cpp | 4 +- .../layers/hillshade_layer_tweaker.cpp | 9 +- .../hillshade_prepare_layer_tweaker.cpp | 9 +- .../renderer/layers/line_layer_tweaker.cpp | 121 -------------- .../renderer/layers/raster_layer_tweaker.cpp | 3 - .../layers/render_background_layer.cpp | 3 +- .../renderer/layers/render_circle_layer.cpp | 14 +- .../layers/render_fill_extrusion_layer.cpp | 14 +- .../renderer/layers/render_fill_layer.cpp | 7 +- .../renderer/layers/render_heatmap_layer.cpp | 13 +- .../layers/render_hillshade_layer.cpp | 3 +- .../renderer/layers/render_line_layer.cpp | 25 +-- .../renderer/layers/render_raster_layer.cpp | 3 +- .../renderer/layers/render_symbol_layer.cpp | 3 +- .../renderer/layers/symbol_layer_tweaker.cpp | 38 +---- .../renderer/sources/render_tile_source.cpp | 22 --- src/mbgl/shaders/mtl/circle.cpp | 4 +- src/mbgl/shaders/mtl/fill.cpp | 16 +- src/mbgl/shaders/mtl/fill_extrusion.cpp | 4 +- .../shaders/mtl/fill_extrusion_pattern.cpp | 14 +- src/mbgl/shaders/mtl/heatmap.cpp | 4 +- src/mbgl/shaders/mtl/line.cpp | 12 +- src/mbgl/shaders/mtl/line_gradient.cpp | 6 +- src/mbgl/shaders/mtl/symbol_icon.cpp | 4 +- src/mbgl/shaders/mtl/symbol_sdf.cpp | 4 +- src/mbgl/shaders/mtl/symbol_text_and_icon.cpp | 4 +- .../style/layers/custom_drawable_layer.cpp | 45 ----- 76 files changed, 525 insertions(+), 1474 deletions(-) diff --git a/include/mbgl/renderer/layer_tweaker.hpp b/include/mbgl/renderer/layer_tweaker.hpp index 339785cf348..7107de38861 100644 --- a/include/mbgl/renderer/layer_tweaker.hpp +++ b/include/mbgl/renderer/layer_tweaker.hpp @@ -17,11 +17,6 @@ class UniformBuffer; using UniformBufferPtr = std::shared_ptr; } // namespace gfx -namespace shaders { -enum class AttributeSource : int32_t; -struct ExpressionInputsUBO; -} // namespace shaders - namespace style { class LayerProperties; enum class TranslateAnchorType : bool; @@ -49,31 +44,6 @@ class LayerTweaker { const std::string& getID() const { return id; } -#if MLN_RENDER_BACKEND_METAL - /// Build the common expression inupts UBO - static shaders::ExpressionInputsUBO buildExpressionUBO(double zoom, uint64_t frameCount); - - /// @brief Check whether a property name exists within the previously set collection. - shaders::AttributeSource getAttributeSource(const StringIdentity id) { - return propertiesAsUniforms.count(id) ? shaders::AttributeSource::Constant - : shaders::AttributeSource::PerVertex; - } - - template - shaders::AttributeSource getAttributeSource(size_t index) { - using ShaderClass = shaders::ShaderSource; - return getAttributeSource(ShaderClass::attributes[index].nameID); - } -#endif // MLN_RENDER_BACKEND_METAL - - /// @brief Set the collection of attribute names which will be provided at uniform values rather than per-vertex - /// attributes. - /// @details These values should not have "a_" prefixes, as produced by `readDataDrivenPaintProperties`. - void setPropertiesAsUniforms(const mbgl::unordered_set&); - const mbgl::unordered_set& getPropertiesAsUniforms() const; - - void enableOverdrawInspector(bool); - virtual void execute(LayerGroupBase&, const PaintParameters&) = 0; void updateProperties(Immutable); @@ -96,19 +66,8 @@ class LayerTweaker { std::string id; Immutable evaluatedProperties; -#if MLN_RENDER_BACKEND_METAL - // For Metal, whether a property is provided through attribtues or uniforms is specified in - // a uniform buffer rather than by a shader compiled with different preprocessor definitions. - mbgl::unordered_set propertiesAsUniforms; -#endif // MLN_RENDER_BACKEND_METAL - // Indicates that the evaluated properties have changed bool propertiesUpdated = true; - - // Indicates that the properties-as-uniforms has changed - bool permutationUpdated = true; - - bool overdrawInspector = false; }; } // namespace mbgl diff --git a/include/mbgl/shaders/background_layer_ubo.hpp b/include/mbgl/shaders/background_layer_ubo.hpp index 3004e769a21..a7a5c6203fe 100644 --- a/include/mbgl/shaders/background_layer_ubo.hpp +++ b/include/mbgl/shaders/background_layer_ubo.hpp @@ -15,10 +15,7 @@ static_assert(sizeof(BackgroundDrawableUBO) % 16 == 0); struct alignas(16) BackgroundLayerUBO { /* 0 */ Color color; /* 16 */ float opacity; - // overdrawInspector is used only in Metal, while in GL this is a 16 bytes empty padding. - /* 20 */ bool overdrawInspector; - /* 21 */ uint8_t pad1, pad2, pad3; - /* 24 */ float pad4, pad5; + /* 24 */ float pad1, pad2, pad3; /* 32 */ }; static_assert(sizeof(BackgroundLayerUBO) == 32); @@ -38,9 +35,7 @@ struct alignas(16) BackgroundPatternLayerUBO { /* 80 */ float scale_b; /* 84 */ float mix; /* 88 */ float opacity; - // overdrawInspector is used only in Metal, while in GL this is a 4 bytes empty padding. - /* 92 */ bool overdrawInspector; - /* 93 */ uint8_t pad1, pad2, pad3; + /* 92 */ float pad1; /* 96 */ }; static_assert(sizeof(BackgroundPatternLayerUBO) == 96); diff --git a/include/mbgl/shaders/circle_layer_ubo.hpp b/include/mbgl/shaders/circle_layer_ubo.hpp index b223882ecc0..f98dfe89ebc 100644 --- a/include/mbgl/shaders/circle_layer_ubo.hpp +++ b/include/mbgl/shaders/circle_layer_ubo.hpp @@ -47,20 +47,5 @@ struct alignas(16) CircleInterpolateUBO { }; static_assert(sizeof(CircleInterpolateUBO) % 16 == 0); -struct alignas(16) CirclePermutationUBO { - /* 0 */ Attribute color; - /* 8 */ Attribute radius; - /* 16 */ Attribute blur; - /* 24 */ Attribute opacity; - /* 32 */ Attribute stroke_color; - /* 40 */ Attribute stroke_width; - /* 48 */ Attribute stroke_opacity; - /* 56 */ bool overdrawInspector; - /* 57 */ uint8_t pad1, pad2, pad3; - /* 60 */ float pad4; - /* 64 */ -}; -static_assert(sizeof(CirclePermutationUBO) == 4 * 16); - } // namespace shaders } // namespace mbgl diff --git a/include/mbgl/shaders/fill_extrusion_layer_ubo.hpp b/include/mbgl/shaders/fill_extrusion_layer_ubo.hpp index b416302814f..7861a3bcc2f 100644 --- a/include/mbgl/shaders/fill_extrusion_layer_ubo.hpp +++ b/include/mbgl/shaders/fill_extrusion_layer_ubo.hpp @@ -56,18 +56,5 @@ struct alignas(16) FillExtrusionDrawablePropsUBO { }; static_assert(sizeof(FillExtrusionDrawablePropsUBO) == 5 * 16); -struct alignas(16) FillExtrusionPermutationUBO { - /* 0 */ Attribute color; - /* 8 */ Attribute base; - /* 16 */ Attribute height; - /* 24 */ Attribute pattern_from; - /* 32 */ Attribute pattern_to; - /* 40 */ bool overdrawInspector; - /* 41 */ uint8_t pad1, pad2, pad3; - /* 44 */ float pad4; - /* 48 */ -}; -static_assert(sizeof(FillExtrusionPermutationUBO) == 3 * 16, "unexpected padding"); - } // namespace shaders } // namespace mbgl diff --git a/include/mbgl/shaders/fill_layer_ubo.hpp b/include/mbgl/shaders/fill_layer_ubo.hpp index cb320afc158..789d1fa68e3 100644 --- a/include/mbgl/shaders/fill_layer_ubo.hpp +++ b/include/mbgl/shaders/fill_layer_ubo.hpp @@ -27,16 +27,6 @@ struct alignas(16) FillInterpolateUBO { }; static_assert(sizeof(FillInterpolateUBO) % 16 == 0); -struct alignas(16) FillPermutationUBO { - /* 0 */ Attribute color; - /* 8 */ Attribute opacity; - /* 16 */ bool overdrawInspector; - /* 17 */ bool pad1, pad2, pad3; - /* 20 */ float pad4, pad5, pad6; - /* 32 */ -}; -static_assert(sizeof(FillPermutationUBO) == 2 * 16); - // // Fill outline @@ -62,16 +52,6 @@ struct alignas(16) FillOutlineInterpolateUBO { }; static_assert(sizeof(FillOutlineInterpolateUBO) == 1 * 16); -struct alignas(16) FillOutlinePermutationUBO { - /* 0 */ Attribute outline_color; - /* 8 */ Attribute opacity; - /* 16 */ bool overdrawInspector; - /* 17 */ bool pad1, pad2, pad3; - /* 20 */ float pad4, pad5, pad6; - /* 32 */ -}; -static_assert(sizeof(FillOutlinePermutationUBO) == 2 * 16); - // // Fill Pattern @@ -107,17 +87,6 @@ struct alignas(16) FillPatternInterpolateUBO { }; static_assert(sizeof(FillPatternInterpolateUBO) == 1 * 16); -struct alignas(16) FillPatternPermutationUBO { - /* 0 */ Attribute pattern_from; - /* 8 */ Attribute pattern_to; - /* 16 */ Attribute opacity; - /* 24 */ bool overdrawInspector; - /* 25 */ bool pad1, pad2, pad3; - /* 28 */ float pad4; - /* 32 */ -}; -static_assert(sizeof(FillPatternPermutationUBO) == 2 * 16); - // // Fill pattern outline @@ -152,16 +121,5 @@ struct alignas(16) FillOutlinePatternInterpolateUBO { }; static_assert(sizeof(FillOutlinePatternInterpolateUBO) == 1 * 16); -struct alignas(16) FillOutlinePatternPermutationUBO { - /* 0 */ Attribute pattern_from; - /* 8 */ Attribute pattern_to; - /* 16 */ Attribute opacity; - /* 24 */ bool overdrawInspector; - /* 17 */ bool pad1, pad2, pad3; - /* 20 */ float pad4; - /* 32 */ -}; -static_assert(sizeof(FillOutlinePatternPermutationUBO) == 2 * 16); - } // namespace shaders } // namespace mbgl diff --git a/include/mbgl/shaders/heatmap_layer_ubo.hpp b/include/mbgl/shaders/heatmap_layer_ubo.hpp index 534a9750aab..68ac43ea8c9 100644 --- a/include/mbgl/shaders/heatmap_layer_ubo.hpp +++ b/include/mbgl/shaders/heatmap_layer_ubo.hpp @@ -27,15 +27,5 @@ struct alignas(16) HeatmapInterpolateUBO { }; static_assert(sizeof(HeatmapInterpolateUBO) % 16 == 0); -struct alignas(16) HeatmapPermutationUBO { - /* 0 */ Attribute weight; - /* 8 */ Attribute radius; - /* 16 */ bool overdrawInspector; - /* 17 */ uint8_t pad1, pad2, pad3; - /* 20 */ float pad4, pad5, pad6; - /* 32 */ -}; -static_assert(sizeof(HeatmapPermutationUBO) == 2 * 16); - } // namespace shaders } // namespace mbgl diff --git a/include/mbgl/shaders/heatmap_texture_layer_ubo.hpp b/include/mbgl/shaders/heatmap_texture_layer_ubo.hpp index 278223c5141..73d4f89bce1 100644 --- a/include/mbgl/shaders/heatmap_texture_layer_ubo.hpp +++ b/include/mbgl/shaders/heatmap_texture_layer_ubo.hpp @@ -9,9 +9,7 @@ struct alignas(16) HeatmapTextureDrawableUBO { std::array matrix; std::array world; float opacity; - // overdrawInspector is used only in Metal, while in GL this is a 4 bytes empty padding. - bool overdrawInspector; - uint8_t pad1, pad2, pad3; + float pad1; }; static_assert(sizeof(HeatmapTextureDrawableUBO) % 16 == 0); diff --git a/include/mbgl/shaders/hillshade_layer_ubo.hpp b/include/mbgl/shaders/hillshade_layer_ubo.hpp index bf9fa455f39..77fd1a993ee 100644 --- a/include/mbgl/shaders/hillshade_layer_ubo.hpp +++ b/include/mbgl/shaders/hillshade_layer_ubo.hpp @@ -9,12 +9,8 @@ struct alignas(16) HillshadeDrawableUBO { std::array matrix; std::array latrange; std::array light; - // overdrawInspector is used only in Metal, while in GL this is a 16 bytes empty padding. - bool overdrawInspector; - uint8_t pad1, pad2, pad3; - float pad4, pad5, pad6; }; -static_assert(sizeof(HillshadeDrawableUBO) % 16 == 0); +static_assert(sizeof(HillshadeDrawableUBO) == 16 * 5); struct alignas(16) HillshadeEvaluatedPropsUBO { Color highlight; diff --git a/include/mbgl/shaders/hillshade_prepare_layer_ubo.hpp b/include/mbgl/shaders/hillshade_prepare_layer_ubo.hpp index 68af948a485..57ac65f3e97 100644 --- a/include/mbgl/shaders/hillshade_prepare_layer_ubo.hpp +++ b/include/mbgl/shaders/hillshade_prepare_layer_ubo.hpp @@ -11,10 +11,6 @@ struct alignas(16) HillshadePrepareDrawableUBO { std::array dimension; float zoom; float maxzoom; - // overdrawInspector is used only in Metal, while in GL this is a 16 bytes empty padding. - bool overdrawInspector; - uint8_t pad1, pad2, pad3; - float pad4, pad5, pad6; }; static_assert(sizeof(HillshadePrepareDrawableUBO) % 16 == 0); diff --git a/include/mbgl/shaders/layer_ubo.hpp b/include/mbgl/shaders/layer_ubo.hpp index ea69aa070c6..52d85880a62 100644 --- a/include/mbgl/shaders/layer_ubo.hpp +++ b/include/mbgl/shaders/layer_ubo.hpp @@ -33,18 +33,5 @@ struct Attribute { }; static_assert(sizeof(Attribute) == 8); -struct alignas(16) ExpressionInputsUBO { - // These can use uint64_t in later versions of Metal - /* 0 */ uint32_t time_lo; /// Current scene time (nanoseconds) - /* 4 */ uint32_t time_hi; - /* 8 */ uint32_t frame_lo; /// Current frame count - /* 12 */ uint32_t frame_hi; - /* 16 */ float zoom; /// Current zoom level - /* 20 */ float zoom_frac; /// double precision zoom - /* 24 */ float pad1, pad2; - /* 32 */ -}; -static_assert(sizeof(ExpressionInputsUBO) == 2 * 16); - } // namespace shaders } // namespace mbgl diff --git a/include/mbgl/shaders/line_layer_ubo.hpp b/include/mbgl/shaders/line_layer_ubo.hpp index 2ee31542642..e1959e97fd3 100644 --- a/include/mbgl/shaders/line_layer_ubo.hpp +++ b/include/mbgl/shaders/line_layer_ubo.hpp @@ -147,22 +147,5 @@ struct alignas(16) LinePatternTilePropertiesUBO { }; static_assert(sizeof(LinePatternTilePropertiesUBO) % 16 == 0); -struct alignas(16) LinePermutationUBO { - /* 0 */ Attribute color; - /* 8 */ Attribute blur; - /* 16 */ Attribute opacity; - /* 24 */ Attribute gapwidth; - /* 32 */ Attribute offset; - /* 40 */ Attribute width; - /* 48 */ Attribute floorwidth; - /* 56 */ Attribute pattern_from; - /* 64 */ Attribute pattern_to; - /* 72 */ bool overdrawInspector; - /* 73 */ uint8_t pad1, pad2, pad3; - /* 76 */ float pad4; - /* 80 */ -}; -static_assert(sizeof(LinePermutationUBO) == 5 * 16); - } // namespace shaders } // namespace mbgl diff --git a/include/mbgl/shaders/mtl/background.hpp b/include/mbgl/shaders/mtl/background.hpp index 192a3990f80..051f2bd4b9f 100644 --- a/include/mbgl/shaders/mtl/background.hpp +++ b/include/mbgl/shaders/mtl/background.hpp @@ -13,7 +13,6 @@ struct ShaderSource { static constexpr auto name = "BackgroundShader"; static constexpr auto vertexMainFunction = "vertexMain"; static constexpr auto fragmentMainFunction = "fragmentMain"; - static constexpr auto hasPermutations = false; static const std::array attributes; static const std::array uniforms; @@ -51,10 +50,10 @@ FragmentStage vertex vertexMain(VertexStage in [[stage_in]], half4 fragment fragmentMain(FragmentStage in [[stage_in]], device const BackgroundLayerUBO& layerUBO [[buffer(2)]]) { - if (layerUBO.overdrawInspector) { - return half4(0.0); - } - +#if defined(OVERDRAW_INSPECTOR) + return half4(1.0); +#endif + return half4(layerUBO.color * layerUBO.opacity); } )"; diff --git a/include/mbgl/shaders/mtl/background_pattern.hpp b/include/mbgl/shaders/mtl/background_pattern.hpp index 2b46b11c363..0c44ca41cdb 100644 --- a/include/mbgl/shaders/mtl/background_pattern.hpp +++ b/include/mbgl/shaders/mtl/background_pattern.hpp @@ -10,7 +10,6 @@ struct ShaderSource static constexpr auto name = "BackgroundPatternShader"; static constexpr auto vertexMainFunction = "vertexMain"; static constexpr auto fragmentMainFunction = "fragmentMain"; - static constexpr auto hasPermutations = false; static const std::array attributes; static const std::array uniforms; @@ -56,13 +55,20 @@ FragmentStage vertex vertexMain(VertexStage in [[stage_in]], device const BackgroundDrawableUBO& drawableUBO [[buffer(1)]], device const BackgroundLayerUBO& layerUBO [[buffer(2)]]) { const float2 pos = float2(in.position); - float2 pos_a = get_pattern_pos(layerUBO.pixel_coord_upper, layerUBO.pixel_coord_lower, layerUBO.scale_a * layerUBO.pattern_size_a, layerUBO.tile_units_to_pixels, pos); - float2 pos_b = get_pattern_pos(layerUBO.pixel_coord_upper, layerUBO.pixel_coord_lower, layerUBO.scale_b * layerUBO.pattern_size_b, layerUBO.tile_units_to_pixels, pos); - + const float2 pos_a = get_pattern_pos(layerUBO.pixel_coord_upper, + layerUBO.pixel_coord_lower, + layerUBO.scale_a * layerUBO.pattern_size_a, + layerUBO.tile_units_to_pixels, + pos); + const float2 pos_b = get_pattern_pos(layerUBO.pixel_coord_upper, + layerUBO.pixel_coord_lower, + layerUBO.scale_b * layerUBO.pattern_size_b, + layerUBO.tile_units_to_pixels, + pos); return { .position = drawableUBO.matrix * float4(float2(in.position.xy), 0, 1), .pos_a = pos_a, - .pos_b = pos_b + .pos_b = pos_b, }; } @@ -70,17 +76,16 @@ half4 fragment fragmentMain(FragmentStage in [[stage_in]], device const BackgroundLayerUBO& layerUBO [[buffer(2)]], texture2d image [[texture(0)]], sampler image_sampler [[sampler(0)]]) { - if (layerUBO.overdrawInspector) { - return half4(0.0); - } - - float2 imagecoord = glMod(in.pos_a, 1.0); - float2 pos = mix(layerUBO.pattern_tl_a / layerUBO.texsize, layerUBO.pattern_br_a / layerUBO.texsize, imagecoord); - float4 color1 = image.sample(image_sampler, pos); +#if defined(OVERDRAW_INSPECTOR) + return half4(1.0); +#endif - float2 imagecoord_b = glMod(in.pos_b, 1.0); - float2 pos2 = mix(layerUBO.pattern_tl_b / layerUBO.texsize, layerUBO.pattern_br_b / layerUBO.texsize, imagecoord_b); - float4 color2 = image.sample(image_sampler, pos2); + const float2 imagecoord = glMod(float2(in.pos_a), 1.0); + const float2 pos = mix(layerUBO.pattern_tl_a / layerUBO.texsize, layerUBO.pattern_br_a / layerUBO.texsize, imagecoord); + const float4 color1 = image.sample(image_sampler, pos); + const float2 imagecoord_b = glMod(float2(in.pos_b), 1.0); + const float2 pos2 = mix(layerUBO.pattern_tl_b / layerUBO.texsize, layerUBO.pattern_br_b / layerUBO.texsize, imagecoord_b); + const float4 color2 = image.sample(image_sampler, pos2); return half4(mix(color1, color2, layerUBO.mix) * layerUBO.opacity); } diff --git a/include/mbgl/shaders/mtl/circle.hpp b/include/mbgl/shaders/mtl/circle.hpp index 738edb54749..9bf0b1b2571 100644 --- a/include/mbgl/shaders/mtl/circle.hpp +++ b/include/mbgl/shaders/mtl/circle.hpp @@ -13,14 +13,12 @@ struct ShaderSource { static constexpr auto name = "CircleShader"; static constexpr auto vertexMainFunction = "vertexMain"; static constexpr auto fragmentMainFunction = "fragmentMain"; - static constexpr auto hasPermutations = true; static const std::array attributes; - static const std::array uniforms; + static const std::array uniforms; static const std::array textures; static constexpr auto source = R"( - struct VertexStage { short2 position [[attribute(0)]]; @@ -50,7 +48,7 @@ struct VertexStage { struct FragmentStage { float4 position [[position, invariant]]; float2 extrude; - half antialiasblur; + float antialiasblur; #if !defined(HAS_UNIFORM_u_color) half4 color; @@ -109,35 +107,22 @@ struct alignas(16) CircleInterpolateUBO { float pad1_; }; -struct alignas(16) CirclePermutationUBO { - Attribute color; - Attribute radius; - Attribute blur; - Attribute opacity; - Attribute stroke_color; - Attribute stroke_width; - Attribute stroke_opacity; - bool overdrawInspector; -}; - FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], device const CircleDrawableUBO& drawable [[buffer(8)]], device const CirclePaintParamsUBO& params [[buffer(9)]], device const CircleEvaluatedPropsUBO& props [[buffer(10)]], - device const CircleInterpolateUBO& interp [[buffer(11)]], - device const CirclePermutationUBO& permutation [[buffer(12)]], - device const ExpressionInputsUBO& expr [[buffer(13)]]) { + device const CircleInterpolateUBO& interp [[buffer(11)]]) { #if defined(HAS_UNIFORM_u_radius) const auto radius = props.radius; #else - const auto radius = valueFor(permutation.radius, props.radius, vertx.radius, interp.radius_t, expr); + const auto radius = unpack_mix_float(vertx.radius, interp.radius_t); #endif #if defined(HAS_UNIFORM_u_stroke_width) const auto stroke_width = props.stroke_width; #else - const auto stroke_width = valueFor(permutation.stroke_width, props.stroke_width, vertx.stroke_width, interp.stroke_width_t, expr); + const auto stroke_width = unpack_mix_float(vertx.stroke_width, interp.stroke_width_t); #endif // unencode the extrusion vector that we snuck into the a_pos vector @@ -156,7 +141,7 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], // Pitching the circle with the map effectively scales it with the map // To counteract the effect for pitch-scale: viewport, we rescale the // whole circle based on the pitch scaling effect at its central point - float4 projected_center = drawable.matrix * float4(circle_center, 0, 1); + const float4 projected_center = drawable.matrix * float4(circle_center, 0, 1); corner_position += scaled_extrude * (radius + stroke_width) * (projected_center.w / params.camera_to_center_distance); } @@ -180,36 +165,35 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], .antialiasblur = antialiasblur, #if !defined(HAS_UNIFORM_u_color) - .color = half4(colorFor(permutation.color, props.color, vertx.color, interp.color_t, expr)), + .color = half4(unpack_mix_color(vertx.color, interp.color_t)), #endif #if !defined(HAS_UNIFORM_u_radius) - .radius = half(radius), + .radius = radius, #endif #if !defined(HAS_UNIFORM_u_blur) - .blur = half(valueFor(permutation.blur, props.blur, vertx.blur, interp.blur_t, expr)), + .blur = half(unpack_mix_float(vertx.blur, interp.blur_t)), #endif #if !defined(HAS_UNIFORM_u_opacity) - .opacity = half(valueFor(permutation.opacity, props.opacity, vertx.opacity, interp.opacity_t, expr)), + .opacity = half(unpack_mix_float(vertx.opacity, interp.opacity_t)), #endif #if !defined(HAS_UNIFORM_u_stroke_color) - .stroke_color = half4(colorFor(permutation.stroke_color, props.stroke_color, vertx.stroke_color, interp.stroke_color_t, expr)), + .stroke_color = half4(unpack_mix_color(vertx.stroke_color, interp.stroke_color_t)), #endif #if !defined(HAS_UNIFORM_u_stroke_width) .stroke_width = half(stroke_width), #endif #if !defined(HAS_UNIFORM_u_stroke_opacity) - .stroke_opacity = half(valueFor(permutation.stroke_opacity, props.stroke_opacity, vertx.stroke_opacity, interp.stroke_opacity_t, expr)), + .stroke_opacity = half(unpack_mix_float(vertx.stroke_opacity, interp.stroke_opacity_t)), #endif }; } half4 fragment fragmentMain(FragmentStage in [[stage_in]], device const CirclePaintParamsUBO& params [[buffer(9)]], - device const CircleEvaluatedPropsUBO& props [[buffer(10)]], - device const CirclePermutationUBO& permutation [[buffer(12)]]) { - if (permutation.overdrawInspector) { - return half4(1.0); - } + device const CircleEvaluatedPropsUBO& props [[buffer(10)]]) { +#if defined(OVERDRAW_INSPECTOR) + return half4(1.0); +#endif #if defined(HAS_UNIFORM_u_color) const half4 color = half4(props.color); @@ -222,14 +206,14 @@ half4 fragment fragmentMain(FragmentStage in [[stage_in]], const float radius = in.radius; #endif #if defined(HAS_UNIFORM_u_blur) - const half blur = props.blur; + const float blur = props.blur; #else - const half blur = in.blur; + const float blur = in.blur; #endif #if defined(HAS_UNIFORM_u_opacity) - const half opacity = props.opacity; + const float opacity = props.opacity; #else - const half opacity = in.opacity; + const float opacity = in.opacity; #endif #if defined(HAS_UNIFORM_u_stroke_color) const half4 stroke_color = half4(props.stroke_color); @@ -237,14 +221,14 @@ half4 fragment fragmentMain(FragmentStage in [[stage_in]], const half4 stroke_color = in.stroke_color; #endif #if defined(HAS_UNIFORM_u_stroke_width) - const half stroke_width = props.stroke_width; + const float stroke_width = props.stroke_width; #else - const half stroke_width = in.stroke_width; + const float stroke_width = in.stroke_width; #endif #if defined(HAS_UNIFORM_u_stroke_opacity) - const half stroke_opacity = props.stroke_opacity; + const float stroke_opacity = props.stroke_opacity; #else - const half stroke_opacity = in.stroke_opacity; + const float stroke_opacity = in.stroke_opacity; #endif const float extrude_length = length(in.extrude); diff --git a/include/mbgl/shaders/mtl/clipping_mask.hpp b/include/mbgl/shaders/mtl/clipping_mask.hpp index 596ea618ee0..197f5c4f4b8 100644 --- a/include/mbgl/shaders/mtl/clipping_mask.hpp +++ b/include/mbgl/shaders/mtl/clipping_mask.hpp @@ -21,7 +21,6 @@ struct ShaderSource { static constexpr auto name = "ClippingMaskProgram"; static constexpr auto vertexMainFunction = "vertexMain"; static constexpr auto fragmentMainFunction = "fragmentMain"; - static constexpr auto hasPermutations = false; static const std::array attributes; static const std::array uniforms; diff --git a/include/mbgl/shaders/mtl/collision_box.hpp b/include/mbgl/shaders/mtl/collision_box.hpp index aa55feacde4..6f87dfce96b 100644 --- a/include/mbgl/shaders/mtl/collision_box.hpp +++ b/include/mbgl/shaders/mtl/collision_box.hpp @@ -13,7 +13,6 @@ struct ShaderSource { static constexpr auto name = "CollisionBoxShader"; static constexpr auto vertexMainFunction = "vertexMain"; static constexpr auto fragmentMainFunction = "fragmentMain"; - static constexpr auto hasPermutations = false; static const std::array attributes; static const std::array uniforms; diff --git a/include/mbgl/shaders/mtl/collision_circle.hpp b/include/mbgl/shaders/mtl/collision_circle.hpp index 7b6b3d120f4..52a9b2e24f1 100644 --- a/include/mbgl/shaders/mtl/collision_circle.hpp +++ b/include/mbgl/shaders/mtl/collision_circle.hpp @@ -13,7 +13,6 @@ struct ShaderSource { static constexpr auto name = "CollisionCircleShader"; static constexpr auto vertexMainFunction = "vertexMain"; static constexpr auto fragmentMainFunction = "fragmentMain"; - static constexpr auto hasPermutations = false; static const std::array attributes; static const std::array uniforms; diff --git a/include/mbgl/shaders/mtl/common.hpp b/include/mbgl/shaders/mtl/common.hpp index c7f20a26562..8a3c2586ec0 100644 --- a/include/mbgl/shaders/mtl/common.hpp +++ b/include/mbgl/shaders/mtl/common.hpp @@ -27,30 +27,6 @@ float radians(float degrees) { return M_PI_F * degrees / 180.0; } -enum class AttributeSource : int32_t { - Constant, - PerVertex, - Computed, -}; - -struct Expression {}; - -struct Attribute { - AttributeSource source; - Expression expression; -}; - -struct alignas(16) ExpressionInputsUBO { - // These can use uint64_t in later versions of Metal - /* 0 */ uint32_t time_lo; // Current scene time (nanoseconds) - /* 4 */ uint32_t time_hi; - /* 8 */ uint32_t frame_lo; // Current frame count - /* 12 */ uint32_t frame_hi; - /* 16 */ float zoom; // Current zoom level - /* 20 */ float pad1, pad2, pad3; - /* 32 */ -}; - // Unpack a pair of values that have been packed into a single float. // The packed values are assumed to be 8-bit unsigned integers, and are // packed like so: packedValue = floor(input[0]) * 256 + input[1], @@ -78,44 +54,6 @@ float4 unpack_mix_color(const float4 packedColors, const float t) { decode_color(float2(packedColors[2], packedColors[3])), t); } -float valueFor(device const Attribute& attrib, - const float constValue, - thread const float2& vertexValue, - const float t, - device const ExpressionInputsUBO&) { - switch (attrib.source) { - case AttributeSource::PerVertex: return unpack_mix_float(vertexValue, t); - case AttributeSource::Computed: // TODO - default: - case AttributeSource::Constant: return constValue; - } -} -// single packed color -float4 colorFor(device const Attribute& attrib, - device const float4& constValue, - thread const float2& vertexValue, - device const ExpressionInputsUBO&) { - switch (attrib.source) { - case AttributeSource::PerVertex: return decode_color(float2(vertexValue[0], vertexValue[1])); - case AttributeSource::Computed: // TODO - default: - case AttributeSource::Constant: return constValue; - } -} -// interpolated packed colors -float4 colorFor(device const Attribute& attrib, - device const float4& constValue, - const float4 vertexValue, - const float t, - device const ExpressionInputsUBO&) { - switch (attrib.source) { - case AttributeSource::PerVertex: return unpack_mix_color(vertexValue, t); - case AttributeSource::Computed: // TODO - default: - case AttributeSource::Constant: return constValue; - } -} - struct alignas(16) LineUBO { float4x4 matrix; float2 units_to_pixels; @@ -163,21 +101,6 @@ struct alignas(16) LineGradientPropertiesUBO { float pad1, pad2, pad3; }; -struct alignas(16) LinePermutationUBO { - Attribute color; - Attribute blur; - Attribute opacity; - Attribute gapwidth; - Attribute offset; - Attribute width; - Attribute floorwidth; - Attribute pattern_from; - Attribute pattern_to; - bool overdrawInspector; - uint8_t pad1, pad2, pad3; - float pad4; -}; - struct alignas(16) LineInterpolationUBO { float color_t; float blur_t; @@ -251,31 +174,6 @@ struct alignas(16) SymbolDrawablePaintUBO { }; static_assert(sizeof(SymbolDrawablePaintUBO) == 3 * 16, "unexpected padding"); -struct alignas(16) SymbolPermutationUBO { - Attribute fill_color; - Attribute halo_color; - Attribute opacity; - Attribute halo_width; - Attribute halo_blur; - int32_t /*bool*/ overdrawInspector; - float pad1, pad2, pad3; -}; -static_assert(sizeof(SymbolPermutationUBO) == 4 * 16, "unexpected padding"); - - -float4 patternFor(device const Attribute& attrib, - device const float4& constValue, - thread const ushort4& vertexValue, - device const float& , - device const ExpressionInputsUBO&) { - switch (attrib.source) { - case AttributeSource::PerVertex: return float4(vertexValue); - case AttributeSource::Computed: // TODO - default: - case AttributeSource::Constant: return constValue; - } -} - // unpack pattern position inline float2 get_pattern_pos(const float2 pixel_coord_upper, const float2 pixel_coord_lower, const float2 pattern_size, const float tile_units_to_pixels, const float2 pos) { diff --git a/include/mbgl/shaders/mtl/debug.hpp b/include/mbgl/shaders/mtl/debug.hpp index 72ae1b39942..b59dc284f86 100644 --- a/include/mbgl/shaders/mtl/debug.hpp +++ b/include/mbgl/shaders/mtl/debug.hpp @@ -13,7 +13,6 @@ struct ShaderSource { static constexpr auto name = "DebugShader"; static constexpr auto vertexMainFunction = "vertexMain"; static constexpr auto fragmentMainFunction = "fragmentMain"; - static constexpr auto hasPermutations = false; static const std::array attributes; static const std::array uniforms; diff --git a/include/mbgl/shaders/mtl/fill.hpp b/include/mbgl/shaders/mtl/fill.hpp index 2f5051e8a47..d69c3476f52 100644 --- a/include/mbgl/shaders/mtl/fill.hpp +++ b/include/mbgl/shaders/mtl/fill.hpp @@ -13,10 +13,9 @@ struct ShaderSource { static constexpr auto name = "FillShader"; static constexpr auto vertexMainFunction = "vertexMain"; static constexpr auto fragmentMainFunction = "fragmentMain"; - static constexpr auto hasPermutations = true; static const std::array attributes; - static const std::array uniforms; + static const std::array uniforms; static const std::array textures; static constexpr auto source = R"( @@ -57,35 +56,26 @@ struct alignas(16) FillInterpolateUBO { float opacity_t; }; -struct alignas(16) FillPermutationUBO { - Attribute color; - Attribute opacity; - bool overdrawInspector; -}; - FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], device const FillDrawableUBO& drawable [[buffer(3)]], device const FillEvaluatedPropsUBO& props [[buffer(4)]], - device const FillInterpolateUBO& interp [[buffer(5)]], - device const FillPermutationUBO& permutation [[buffer(6)]], - device const ExpressionInputsUBO& expr [[buffer(7)]]) { + device const FillInterpolateUBO& interp [[buffer(5)]]) { return { .position = drawable.matrix * float4(float2(vertx.position), 0.0f, 1.0f), #if !defined(HAS_UNIFORM_u_color) - .color = half4(colorFor(permutation.color, props.color, vertx.color, interp.color_t, expr)), + .color = half4(unpack_mix_color(vertx.color, interp.color_t)), #endif #if !defined(HAS_UNIFORM_u_opacity) - .opacity = half(valueFor(permutation.opacity, props.opacity, vertx.opacity, interp.opacity_t, expr)), + .opacity = half(unpack_mix_float(vertx.opacity, interp.opacity_t)), #endif }; } half4 fragment fragmentMain(FragmentStage in [[stage_in]], - device const FillEvaluatedPropsUBO& props [[buffer(4)]], - device const FillPermutationUBO& permutation [[buffer(6)]]) { - if (permutation.overdrawInspector) { - return half4(1.0); - } + device const FillEvaluatedPropsUBO& props [[buffer(4)]]) { +#if defined(OVERDRAW_INSPECTOR) + return half4(1.0); +#endif #if defined(HAS_UNIFORM_u_color) const half4 color = half4(props.color); @@ -109,10 +99,9 @@ struct ShaderSource { static constexpr auto name = "FillOutlineShader"; static constexpr auto vertexMainFunction = "vertexMain"; static constexpr auto fragmentMainFunction = "fragmentMain"; - static constexpr auto hasPermutations = true; static const std::array attributes; - static const std::array uniforms; + static const std::array uniforms; static const std::array textures; static constexpr auto source = R"( @@ -149,37 +138,28 @@ struct alignas(16) FillOutlineInterpolateUBO { float opacity_t; }; -struct alignas(16) FillOutlinePermutationUBO { - Attribute outline_color; - Attribute opacity; - bool overdrawInspector; -}; - FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], device const FillOutlineDrawableUBO& drawable [[buffer(3)]], device const FillOutlineEvaluatedPropsUBO& props [[buffer(4)]], - device const FillOutlineInterpolateUBO& interp [[buffer(5)]], - device const FillOutlinePermutationUBO& permutation [[buffer(6)]], - device const ExpressionInputsUBO& expr [[buffer(7)]]) { + device const FillOutlineInterpolateUBO& interp [[buffer(5)]]) { const float4 position = drawable.matrix * float4(float2(vertx.position), 0.0f, 1.0f); return { .position = position, .pos = (position.xy / position.w + 1.0) / 2.0 * drawable.world, #if !defined(HAS_UNIFORM_u_outline_color) - .outline_color = half4(colorFor(permutation.outline_color, props.outline_color, vertx.outline_color, interp.outline_color_t, expr)), + .outline_color = half4(unpack_mix_color(vertx.outline_color, interp.outline_color_t)), #endif #if !defined(HAS_UNIFORM_u_opacity) - .opacity = half( valueFor(permutation.opacity, props.opacity, vertx.opacity, interp.opacity_t, expr)), + .opacity = half(unpack_mix_float(vertx.opacity, interp.opacity_t)), #endif }; } half4 fragment fragmentMain(FragmentStage in [[stage_in]], - device const FillOutlineEvaluatedPropsUBO& props [[buffer(4)]], - device const FillOutlinePermutationUBO& permutation [[buffer(6)]]) { - if (permutation.overdrawInspector) { - return half4(1.0); - } + device const FillOutlineEvaluatedPropsUBO& props [[buffer(4)]]) { +#if defined(OVERDRAW_INSPECTOR) + return half4(1.0); +#endif // TODO: Cause metal line primitive only support draw 1 pixel width line // use alpha to provide edge antialiased is no point @@ -209,10 +189,9 @@ struct ShaderSource { static constexpr auto name = "FillPatternShader"; static constexpr auto vertexMainFunction = "vertexMain"; static constexpr auto fragmentMainFunction = "fragmentMain"; - static constexpr auto hasPermutations = true; static const std::array attributes; - static const std::array uniforms; + static const std::array uniforms; static const std::array textures; static constexpr auto source = R"( @@ -236,10 +215,10 @@ struct FragmentStage { float2 v_pos_b; #if !defined(HAS_UNIFORM_u_pattern_from) - float4 pattern_from; + half4 pattern_from; #endif #if !defined(HAS_UNIFORM_u_pattern_to) - float4 pattern_to; + half4 pattern_to; #endif #if !defined(HAS_UNIFORM_u_opacity) half opacity; @@ -270,30 +249,21 @@ struct alignas(16) FillPatternInterpolateUBO { float opacity_t; }; -struct alignas(16) FillPatternPermutationUBO { - Attribute pattern_from; - Attribute pattern_to; - Attribute opacity; - bool overdrawInspector; -}; - FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], device const FillPatternDrawableUBO& drawable [[buffer(4)]], device const FillPatternTilePropsUBO& tileProps [[buffer(5)]], device const FillPatternEvaluatedPropsUBO& props [[buffer(6)]], - device const FillPatternInterpolateUBO& interp [[buffer(7)]], - device const FillPatternPermutationUBO& permutation [[buffer(8)]], - device const ExpressionInputsUBO& expr [[buffer(9)]]) { + device const FillPatternInterpolateUBO& interp [[buffer(7)]]) { #if defined(HAS_UNIFORM_u_pattern_from) - const auto pattern_from = tileProps.pattern_from; + const auto pattern_from = float4(tileProps.pattern_from); #else - const auto pattern_from = patternFor(permutation.pattern_from, tileProps.pattern_from, vertx.pattern_from, interp.pattern_from_t, expr); + const auto pattern_from = float4(vertx.pattern_from); #endif #if defined(HAS_UNIFORM_u_pattern_to) - const auto pattern_to = tileProps.pattern_to; + const auto pattern_to = float4(tileProps.pattern_to); #else - const auto pattern_to = patternFor(permutation.pattern_to, tileProps.pattern_to, vertx.pattern_to, interp.pattern_to_t, expr); + const auto pattern_to = float4(vertx.pattern_to); #endif const float2 pattern_tl_a = pattern_from.xy; @@ -315,13 +285,13 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], .v_pos_a = get_pattern_pos(drawable.pixel_coord_upper, drawable.pixel_coord_lower, fromScale * display_size_a, tileZoomRatio, postion), .v_pos_b = get_pattern_pos(drawable.pixel_coord_upper, drawable.pixel_coord_lower, toScale * display_size_b, tileZoomRatio, postion), #if !defined(HAS_UNIFORM_u_pattern_from) - .pattern_from = pattern_from, + .pattern_from = half4(pattern_from), #endif #if !defined(HAS_UNIFORM_u_pattern_to) - .pattern_to = pattern_to, + .pattern_to = half4(pattern_to), #endif #if !defined(HAS_UNIFORM_u_opacity) - .opacity = half(valueFor(permutation.opacity, props.opacity, vertx.opacity, interp.opacity_t, expr)), + .opacity = half(unpack_mix_float(vertx.opacity, interp.opacity_t)), #endif }; } @@ -330,23 +300,22 @@ half4 fragment fragmentMain(FragmentStage in [[stage_in]], device const FillPatternDrawableUBO& drawable [[buffer(4)]], device const FillPatternTilePropsUBO& tileProps [[buffer(5)]], device const FillPatternEvaluatedPropsUBO& props [[buffer(6)]], - device const FillPatternPermutationUBO& permutation [[buffer(8)]], texture2d image0 [[texture(0)]], sampler image0_sampler [[sampler(0)]]) { - if (permutation.overdrawInspector) { - return half4(1.0); - } +#if defined(OVERDRAW_INSPECTOR) + return half4(1.0); +#endif #if defined(HAS_UNIFORM_u_pattern_from) - const auto pattern_from = tileProps.pattern_from; + const auto pattern_from = float4(tileProps.pattern_from); #else - const auto pattern_from = in.pattern_from; + const auto pattern_from = float4(in.pattern_from); #endif #if defined(HAS_UNIFORM_u_pattern_to) - const auto pattern_to = tileProps.pattern_to; + const auto pattern_to = float4(tileProps.pattern_to); #else - const auto pattern_to = in.pattern_to; + const auto pattern_to = float4(in.pattern_to); #endif #if defined(HAS_UNIFORM_u_opacity) @@ -378,10 +347,9 @@ struct ShaderSource attributes; - static const std::array uniforms; + static const std::array uniforms; static const std::array textures; static constexpr auto source = R"( @@ -407,10 +375,10 @@ struct FragmentStage { float2 v_pos; #if !defined(HAS_UNIFORM_u_pattern_from) - float4 pattern_from; + half4 pattern_from; #endif #if !defined(HAS_UNIFORM_u_pattern_to) - float4 pattern_to; + half4 pattern_to; #endif #if !defined(HAS_UNIFORM_u_opacity) half opacity; @@ -442,34 +410,25 @@ struct alignas(16) FillOutlinePatternInterpolateUBO { float opacity_t; }; -struct alignas(16) FillOutlinePatternPermutationUBO { - Attribute pattern_from; - Attribute pattern_to; - Attribute opacity; - bool overdrawInspector; -}; - FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], device const FillOutlinePatternDrawableUBO& drawable [[buffer(4)]], device const FillOutlinePatternTilePropsUBO& tileProps [[buffer(5)]], device const FillOutlinePatternEvaluatedPropsUBO& props [[buffer(6)]], - device const FillOutlinePatternInterpolateUBO& interp [[buffer(7)]], - device const FillOutlinePatternPermutationUBO& permutation [[buffer(8)]], - device const ExpressionInputsUBO& expr [[buffer(9)]]) { + device const FillOutlinePatternInterpolateUBO& interp [[buffer(7)]]) { #if defined(HAS_UNIFORM_u_pattern_from) const auto pattern_from = tileProps.pattern_from; #else - const auto pattern_from = patternFor(permutation.pattern_from, tileProps.pattern_from, vertx.pattern_from, interp.pattern_from_t, expr); + const auto pattern_from = float4(vertx.pattern_from); #endif #if defined(HAS_UNIFORM_u_pattern_to) const auto pattern_to = tileProps.pattern_to; #else - const auto pattern_to = patternFor(permutation.pattern_to, tileProps.pattern_to, vertx.pattern_to, interp.pattern_to_t, expr); + const auto pattern_to = float4(vertx.pattern_to); #endif #if !defined(HAS_UNIFORM_u_opacity) - const auto opacity = valueFor(permutation.opacity, props.opacity, vertx.opacity, interp.opacity_t, expr); + const auto opacity = unpack_mix_float(vertx.opacity, interp.opacity_t); #endif const float2 pattern_tl_a = pattern_from.xy; @@ -494,10 +453,10 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], .v_pos = (position.xy / position.w + 1.0) / 2.0 * drawable.world, #if !defined(HAS_UNIFORM_u_pattern_from) - .pattern_from = pattern_from, + .pattern_from = half4(pattern_from), #endif #if !defined(HAS_UNIFORM_u_pattern_to) - .pattern_to = pattern_to, + .pattern_to = half4(pattern_to), #endif #if !defined(HAS_UNIFORM_u_opacity) .opacity = half(opacity), @@ -509,23 +468,22 @@ half4 fragment fragmentMain(FragmentStage in [[stage_in]], device const FillOutlinePatternDrawableUBO& drawable [[buffer(4)]], device const FillOutlinePatternTilePropsUBO& tileProps [[buffer(5)]], device const FillOutlinePatternEvaluatedPropsUBO& props [[buffer(6)]], - device const FillOutlinePatternPermutationUBO& permutation [[buffer(8)]], texture2d image0 [[texture(0)]], sampler image0_sampler [[sampler(0)]]) { - if (permutation.overdrawInspector) { - return half4(1.0); - } +#if defined(OVERDRAW_INSPECTOR) + return half4(1.0); +#endif #if defined(HAS_UNIFORM_u_pattern_from) - const auto pattern_from = tileProps.pattern_from; + const auto pattern_from = float4(tileProps.pattern_from); #else - const auto pattern_from = in.pattern_from; + const auto pattern_from = float4(in.pattern_from); #endif #if defined(HAS_UNIFORM_u_pattern_to) - const auto pattern_to = tileProps.pattern_to; + const auto pattern_to = float4(tileProps.pattern_to); #else - const auto pattern_to = in.pattern_to; + const auto pattern_to = float4(in.pattern_to); #endif #if defined(HAS_UNIFORM_u_opacity) diff --git a/include/mbgl/shaders/mtl/fill_extrusion.hpp b/include/mbgl/shaders/mtl/fill_extrusion.hpp index b202740876c..e94ea3db6be 100644 --- a/include/mbgl/shaders/mtl/fill_extrusion.hpp +++ b/include/mbgl/shaders/mtl/fill_extrusion.hpp @@ -13,10 +13,9 @@ struct ShaderSource { static constexpr auto name = "FillExtrusionShader"; static constexpr auto vertexMainFunction = "vertexMain"; static constexpr auto fragmentMainFunction = "fragmentMain"; - static constexpr auto hasPermutations = false; static const std::array attributes; - static const std::array uniforms; + static const std::array uniforms; static const std::array textures; static constexpr auto source = R"( @@ -57,25 +56,19 @@ struct alignas(16) FillExtrusionDrawablePropsUBO { }; static_assert(sizeof(FillExtrusionDrawablePropsUBO) == 5 * 16, "unexpected padding"); -struct alignas(16) FillExtrusionPermutationUBO { - /* 0 */ Attribute color; - /* 8 */ Attribute base; - /* 16 */ Attribute height; - /* 24 */ Attribute pattern_from; - /* 32 */ Attribute pattern_to; - /* 40 */ bool overdrawInspector; - /* 41 */ uint8_t pad1, pad2, pad3; - /* 44 */ float pad4; - /* 48 */ -}; -static_assert(sizeof(FillExtrusionPermutationUBO) == 3 * 16, "unexpected padding"); - struct VertexStage { short2 pos [[attribute(0)]]; short4 normal_ed [[attribute(1)]]; + +#if !defined(HAS_UNIFORM_u_color) float4 color [[attribute(2)]]; +#endif +#if !defined(HAS_UNIFORM_u_base) float base [[attribute(3)]]; +#endif +#if !defined(HAS_UNIFORM_u_height) float height [[attribute(4)]]; +#endif }; struct FragmentStage { @@ -91,27 +84,36 @@ struct FragmentOutput { FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], device const FillExtrusionDrawableUBO& fill [[buffer(5)]], device const FillExtrusionDrawablePropsUBO& props [[buffer(6)]], - device const FillExtrusionInterpolateUBO& interp [[buffer(7)]], - device const FillExtrusionPermutationUBO& permutation [[buffer(8)]], - device const ExpressionInputsUBO& expr [[buffer(9)]]) { - - const float u_base = props.light_position_base.w; - const auto base = max(valueFor(permutation.base, u_base, vertx.base, interp.base_t, expr), 0.0); - const auto height = max(valueFor(permutation.height, props.height, vertx.height, interp.height_t, expr), 0.0); + device const FillExtrusionInterpolateUBO& interp [[buffer(7)]]) { + +#if defined(HAS_UNIFORM_u_base) + const auto base = props.light_position_base.w; +#else + const auto base = max(unpack_mix_float(vertx.base, interp.base_t), 0.0); +#endif +#if defined(HAS_UNIFORM_u_height) + const auto height = props.height; +#else + const auto height = max(unpack_mix_float(vertx.height, interp.height_t), 0.0); +#endif const float3 normal = float3(vertx.normal_ed.xyz); const float t = glMod(normal.x, 2.0); const float z = (t != 0.0) ? height : base; // TODO: This would come out wrong on GL for negative values, check it... const float4 position = fill.matrix * float4(float2(vertx.pos), z, 1); - if (permutation.overdrawInspector) { - return { - .position = position, - .color = half4(1.0), - }; - } +#if defined(OVERDRAW_INSPECTOR) + return { + .position = position, + .color = half4(1.0), + }; +#endif - auto color = colorFor(permutation.color, props.color, vertx.color, interp.color_t, expr); +#if defined(HAS_UNIFORM_u_color) + auto color = props.color; +#else + auto color = unpack_mix_color(vertx.color, interp.color_t); +#endif // Relative luminance (how dark/bright is the surface color?) const float luminance = color.r * 0.2126 + color.g * 0.7152 + color.b * 0.0722; diff --git a/include/mbgl/shaders/mtl/fill_extrusion_pattern.hpp b/include/mbgl/shaders/mtl/fill_extrusion_pattern.hpp index 0ef4f28b474..6dcda60261a 100644 --- a/include/mbgl/shaders/mtl/fill_extrusion_pattern.hpp +++ b/include/mbgl/shaders/mtl/fill_extrusion_pattern.hpp @@ -13,10 +13,9 @@ struct ShaderSource attributes; - static const std::array uniforms; + static const std::array uniforms; static const std::array textures; static constexpr auto source = R"( @@ -63,35 +62,36 @@ struct alignas(16) FillExtrusionDrawablePropsUBO { }; static_assert(sizeof(FillExtrusionDrawablePropsUBO) == 5 * 16, "unexpected padding"); -struct alignas(16) FillExtrusionPermutationUBO { - /* 0 */ Attribute color; - /* 8 */ Attribute base; - /* 16 */ Attribute height; - /* 24 */ Attribute pattern_from; - /* 32 */ Attribute pattern_to; - /* 40 */ bool overdrawInspector; - /* 41 */ uint8_t pad1, pad2, pad3; - /* 44 */ float pad4; - /* 48 */ -}; -static_assert(sizeof(FillExtrusionPermutationUBO) == 3 * 16, "unexpected padding"); - struct VertexStage { short2 pos [[attribute(0)]]; short4 normal_ed [[attribute(1)]]; + +#if !defined(HAS_UNIFORM_u_base) float base [[attribute(2)]]; +#endif +#if !defined(HAS_UNIFORM_u_height) float height [[attribute(3)]]; +#endif +#if !defined(HAS_UNIFORM_u_pattern_from) ushort4 pattern_from [[attribute(4)]]; +#endif +#if !defined(HAS_UNIFORM_u_pattern_to) ushort4 pattern_to [[attribute(5)]]; +#endif }; struct FragmentStage { float4 position [[position, invariant]]; float4 lighting; - float4 pattern_from; - float4 pattern_to; float2 pos_a; float2 pos_b; + +#if !defined(HAS_UNIFORM_u_pattern_from) + half4 pattern_from; +#endif +#if !defined(HAS_UNIFORM_u_pattern_to) + half4 pattern_to; +#endif }; struct FragmentOutput { @@ -103,13 +103,18 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], device const FillExtrusionDrawableUBO& fill [[buffer(6)]], device const FillExtrusionDrawablePropsUBO& props [[buffer(7)]], device const FillExtrusionDrawableTilePropsUBO& tileProps [[buffer(8)]], - device const FillExtrusionInterpolateUBO& interp [[buffer(9)]], - device const FillExtrusionPermutationUBO& permutation [[buffer(10)]], - device const ExpressionInputsUBO& expr [[buffer(11)]]) { - - const float u_base = props.light_position_base.w; - const auto base = max(valueFor(permutation.base, u_base, vertx.base, interp.base_t, expr), 0.0); - const auto height = max(valueFor(permutation.height, props.height, vertx.height, interp.height_t, expr), 0.0); + device const FillExtrusionInterpolateUBO& interp [[buffer(9)]]) { + +#if defined(HAS_UNIFORM_u_base) + const auto base = props.light_position_base.w; +#else + const auto base = max(unpack_mix_float(vertx.base, interp.base_t), 0.0); +#endif +#if defined(HAS_UNIFORM_u_height) + const auto height = props.height; +#else + const auto height = max(unpack_mix_float(vertx.height, interp.height_t), 0.0); +#endif const float3 normal = float3(vertx.normal_ed.xyz); const float edgedistance = vertx.normal_ed.w; @@ -117,34 +122,42 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], const float z = (t != 0.0) ? height : base; // TODO: This would come out wrong on GL for negative values, check it... const float4 position = fill.matrix * float4(float2(vertx.pos), z, 1); - const auto pattern_from = patternFor(permutation.pattern_from, tileProps.pattern_from, vertx.pattern_from, interp.pattern_from_t, expr); - const auto pattern_to = patternFor(permutation.pattern_to, tileProps.pattern_to, vertx.pattern_to, interp.pattern_to_t, expr); - - if (permutation.overdrawInspector) { - return { - .position = position, - .lighting = float4(1.0), - .pattern_from = float4(1.0), - .pattern_to = float4(1.0), - .pos_a = float2(1.0), - .pos_b = float2(1.0), - }; - } - - float2 pattern_tl_a = pattern_from.xy; - float2 pattern_br_a = pattern_from.zw; - float2 pattern_tl_b = pattern_to.xy; - float2 pattern_br_b = pattern_to.zw; - - float pixelRatio = fill.scale.x; - float tileZoomRatio = fill.scale.y; - float fromScale = fill.scale.z; - float toScale = fill.scale.w; - - float2 display_size_a = float2((pattern_br_a.x - pattern_tl_a.x) / pixelRatio, (pattern_br_a.y - pattern_tl_a.y) / pixelRatio); - float2 display_size_b = float2((pattern_br_b.x - pattern_tl_b.x) / pixelRatio, (pattern_br_b.y - pattern_tl_b.y) / pixelRatio); - - float2 pos = normal.x == 1.0 && normal.y == 0.0 && normal.z == 16384.0 +#if defined(OVERDRAW_INSPECTOR) + return { + .position = position, + .lighting = float4(1.0), + .pattern_from = float4(1.0), + .pattern_to = float4(1.0), + .pos_a = float2(1.0), + .pos_b = float2(1.0), + }; +#endif + +#if defined(HAS_UNIFORM_u_pattern_from) + const auto pattern_from = tileProps.pattern_from; +#else + const auto pattern_from = float4(vertx.pattern_from); +#endif +#if defined(HAS_UNIFORM_u_pattern_to) + const auto pattern_to = tileProps.pattern_to; +#else + const auto pattern_to = float4(vertx.pattern_to); +#endif + + const float2 pattern_tl_a = pattern_from.xy; + const float2 pattern_br_a = pattern_from.zw; + const float2 pattern_tl_b = pattern_to.xy; + const float2 pattern_br_b = pattern_to.zw; + + const float pixelRatio = fill.scale.x; + const float tileZoomRatio = fill.scale.y; + const float fromScale = fill.scale.z; + const float toScale = fill.scale.w; + + const float2 display_size_a = float2((pattern_br_a.x - pattern_tl_a.x) / pixelRatio, (pattern_br_a.y - pattern_tl_a.y) / pixelRatio); + const float2 display_size_b = float2((pattern_br_b.x - pattern_tl_b.x) / pixelRatio, (pattern_br_b.y - pattern_tl_b.y) / pixelRatio); + + const float2 pos = normal.x == 1.0 && normal.y == 0.0 && normal.z == 16384.0 ? float2(vertx.pos) // extrusion top : float2(edgedistance, z * fill.height_factor); // extrusion side @@ -166,8 +179,12 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], return { .position = position, .lighting = lighting, - .pattern_from = pattern_from, - .pattern_to = pattern_to, +#if !defined(HAS_UNIFORM_u_pattern_from) + .pattern_from = half4(pattern_from), +#endif +#if !defined(HAS_UNIFORM_u_pattern_from) + .pattern_to = half4(pattern_to), +#endif .pos_a = get_pattern_pos(fill.pixel_coord_upper, fill.pixel_coord_lower, fromScale * display_size_a, tileZoomRatio, pos), .pos_b = get_pattern_pos(fill.pixel_coord_upper, fill.pixel_coord_lower, toScale * display_size_b, tileZoomRatio, pos), }; @@ -176,19 +193,28 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], fragment FragmentOutput fragmentMain(FragmentStage in [[stage_in]], device const FillExtrusionDrawableUBO& fill [[buffer(6)]], device const FillExtrusionDrawablePropsUBO& props [[buffer(7)]], - device const FillExtrusionPermutationUBO& permutation [[buffer(10)]], + device const FillExtrusionDrawableTilePropsUBO& tileProps [[buffer(8)]], texture2d image0 [[texture(0)]], sampler image0_sampler [[sampler(0)]]) { - if (permutation.overdrawInspector) { - return {half4(1.0)/*, in.position.z*/}; - } - - - const float2 pattern_tl_a = in.pattern_from.xy; - const float2 pattern_br_a = in.pattern_from.zw; - const float2 pattern_tl_b = in.pattern_to.xy; - const float2 pattern_br_b = in.pattern_to.zw; - +#if defined(OVERDRAW_INSPECTOR) + return {half4(1.0)/*, in.position.z*/}; +#endif + +#if defined(HAS_UNIFORM_u_pattern_from) + const auto pattern_from = float4(tileProps.pattern_from); +#else + const auto pattern_from = float4(in.pattern_from); +#endif +#if defined(HAS_UNIFORM_u_pattern_to) + const auto pattern_to = float4(tileProps.pattern_to); +#else + const auto pattern_to = float4(in.pattern_to); +#endif + + const float2 pattern_tl_a = pattern_from.xy; + const float2 pattern_br_a = pattern_from.zw; + const float2 pattern_tl_b = pattern_to.xy; + const float2 pattern_br_b = pattern_to.zw; const float2 imagecoord = glMod(in.pos_a, 1.0); const float2 pos = mix(pattern_tl_a / fill.texsize, pattern_br_a / fill.texsize, imagecoord); diff --git a/include/mbgl/shaders/mtl/heatmap.hpp b/include/mbgl/shaders/mtl/heatmap.hpp index c0aef6c0cae..5d90268446f 100644 --- a/include/mbgl/shaders/mtl/heatmap.hpp +++ b/include/mbgl/shaders/mtl/heatmap.hpp @@ -13,18 +13,22 @@ struct ShaderSource { static constexpr auto name = "HeatmapShader"; static constexpr auto vertexMainFunction = "vertexMain"; static constexpr auto fragmentMainFunction = "fragmentMain"; - static constexpr auto hasPermutations = false; static const std::array attributes; - static const std::array uniforms; + static const std::array uniforms; static const std::array textures; static constexpr auto source = R"( struct VertexStage { short2 pos [[attribute(0)]]; + +#if !defined(HAS_UNIFORM_u_weight) float2 weight [[attribute(1)]]; +#endif +#if !defined(HAS_UNIFORM_u_radius) float2 radius [[attribute(2)]]; +#endif }; struct FragmentStage { @@ -53,14 +57,6 @@ struct alignas(16) HeatmapInterpolateUBO { float2 pad1; }; -struct alignas(16) HeatmapPermutationUBO { - Attribute weight; - Attribute radius; - bool overdrawInspector; - uint8_t pad1, pad2, pad3; - float pad4, pad5, pad6; -}; - // Effective "0" in the kernel density texture to adjust the kernel size to; // this empirically chosen number minimizes artifacts on overlapping kernels // for typical heatmap cases (assuming clustered source) @@ -72,15 +68,21 @@ constant const float ZERO = 1.0 / 255.0 / 16.0; FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], device const HeatmapDrawableUBO& drawable [[buffer(3)]], device const HeatmapEvaluatedPropsUBO& props [[buffer(4)]], - device const HeatmapInterpolateUBO& interp [[buffer(5)]], - device const HeatmapPermutationUBO& permutation [[buffer(6)]], - device const ExpressionInputsUBO& expr [[buffer(7)]]) { + device const HeatmapInterpolateUBO& interp [[buffer(5)]]) { + +#if defined(HAS_UNIFORM_u_weight) + const auto weight = props.weight; +#else + const auto weight = unpack_mix_float(vertx.weight, interp.weight_t); +#endif +#if defined(HAS_UNIFORM_u_radius) + const auto radius = props.radius; +#else + const auto radius = unpack_mix_float(vertx.radius, interp.radius_t); +#endif - const auto weight = valueFor(permutation.weight, props.weight, vertx.weight, interp.weight_t, expr); - const auto radius = valueFor(permutation.radius, props.radius, vertx.radius, interp.radius_t, expr); - // unencode the extrusion vector that we snuck into the a_pos vector - float2 unscaled_extrude = float2(glMod(float2(vertx.pos), 2.0) * 2.0 - 1.0); + const float2 unscaled_extrude = float2(glMod(float2(vertx.pos), 2.0) * 2.0 - 1.0); // This 'extrude' comes in ranging from [-1, -1], to [1, 1]. We'll use // it to produce the vertices of a square mesh framing the point feature @@ -93,39 +95,35 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], // weight * u_intensity * GAUSS_COEF * exp(-0.5 * 3.0^2 * S^2) == ZERO // Which solves to: // S = sqrt(-2.0 * log(ZERO / (weight * u_intensity * GAUSS_COEF))) / 3.0 - float S = sqrt(-2.0 * log(ZERO / weight / props.intensity / GAUSS_COEF)) / 3.0; + const float S = sqrt(-2.0 * log(ZERO / weight / props.intensity / GAUSS_COEF)) / 3.0; // Pass the varying in units of radius - float2 extrude = S * unscaled_extrude; + const float2 extrude = S * unscaled_extrude; // Scale by radius and the zoom-based scale factor to produce actual // mesh position - float2 scaled_extrude = extrude * radius * drawable.extrude_scale; + const float2 scaled_extrude = extrude * radius * drawable.extrude_scale; // multiply a_pos by 0.5, since we had it * 2 in order to sneak // in extrusion data - float4 pos = float4(floor(float2(vertx.pos) * 0.5) + scaled_extrude, 0, 1); + const float4 pos = float4(floor(float2(vertx.pos) * 0.5) + scaled_extrude, 0, 1); - float4 position = drawable.matrix * pos; - return { - .position = position, + .position = drawable.matrix * pos, .weight = weight, .extrude = extrude, }; } half4 fragment fragmentMain(FragmentStage in [[stage_in]], - device const HeatmapEvaluatedPropsUBO& props [[buffer(4)]], - device const HeatmapPermutationUBO& permutation [[buffer(6)]]) { - - if (permutation.overdrawInspector) { - return half4(1.0); - } + device const HeatmapEvaluatedPropsUBO& props [[buffer(4)]]) { +#if defined(OVERDRAW_INSPECTOR) + return half4(1.0); +#endif // Kernel density estimation with a Gaussian kernel of size 5x5 - float d = -0.5 * 3.0 * 3.0 * dot(in.extrude, in.extrude); - float val = in.weight * props.intensity * GAUSS_COEF * exp(d); + const float d = -0.5 * 3.0 * 3.0 * dot(in.extrude, in.extrude); + const float val = in.weight * props.intensity * GAUSS_COEF * exp(d); return half4(val, 1.0, 1.0, 1.0); } diff --git a/include/mbgl/shaders/mtl/heatmap_texture.hpp b/include/mbgl/shaders/mtl/heatmap_texture.hpp index 793bef27ec7..f1f143c4006 100644 --- a/include/mbgl/shaders/mtl/heatmap_texture.hpp +++ b/include/mbgl/shaders/mtl/heatmap_texture.hpp @@ -13,7 +13,6 @@ struct ShaderSource { static constexpr auto name = "HeatmapTextureShader"; static constexpr auto vertexMainFunction = "vertexMain"; static constexpr auto fragmentMainFunction = "fragmentMain"; - static constexpr auto hasPermutations = false; static const std::array attributes; static const std::array uniforms; @@ -57,9 +56,9 @@ half4 fragment fragmentMain(FragmentStage in [[stage_in]], sampler image_sampler [[sampler(0)]], sampler color_ramp_sampler [[sampler(1)]]) { - if (drawable.overdrawInspector) { - return half4(0.0); - } +#if defined(OVERDRAW_INSPECTOR) + return half4(1.0); +#endif float t = image.sample(image_sampler, in.pos).r; float4 color = color_ramp.sample(color_ramp_sampler, float2(t, 0.5)); diff --git a/include/mbgl/shaders/mtl/hillshade.hpp b/include/mbgl/shaders/mtl/hillshade.hpp index 852af4f4a17..77b35d65ef9 100644 --- a/include/mbgl/shaders/mtl/hillshade.hpp +++ b/include/mbgl/shaders/mtl/hillshade.hpp @@ -13,7 +13,6 @@ struct ShaderSource { static constexpr auto name = "HillshadeShader"; static constexpr auto vertexMainFunction = "vertexMain"; static constexpr auto fragmentMainFunction = "fragmentMain"; - static constexpr auto hasPermutations = false; static const std::array attributes; static const std::array uniforms; @@ -35,9 +34,6 @@ struct alignas(16) HillshadeDrawableUBO { float4x4 matrix; float2 latrange; float2 light; - bool overdrawInspector; - uint8_t pad1, pad2, pad3; - float pad4, pad5, pad6; }; struct alignas(16) HillshadeEvaluatedPropsUBO { @@ -64,10 +60,9 @@ half4 fragment fragmentMain(FragmentStage in [[stage_in]], device const HillshadeEvaluatedPropsUBO& props [[buffer(3)]], texture2d image [[texture(0)]], sampler image_sampler [[sampler(0)]]) { - - if (drawable.overdrawInspector) { - return half4(1.0); - } +#if defined(OVERDRAW_INSPECTOR) + return half4(1.0); +#endif float4 pixel = image.sample(image_sampler, in.pos); diff --git a/include/mbgl/shaders/mtl/hillshade_prepare.hpp b/include/mbgl/shaders/mtl/hillshade_prepare.hpp index c4c9617f96e..f3d58649129 100644 --- a/include/mbgl/shaders/mtl/hillshade_prepare.hpp +++ b/include/mbgl/shaders/mtl/hillshade_prepare.hpp @@ -13,14 +13,12 @@ struct ShaderSource static constexpr auto name = "HillshadePrepareShader"; static constexpr auto vertexMainFunction = "vertexMain"; static constexpr auto fragmentMainFunction = "fragmentMain"; - static constexpr auto hasPermutations = false; static const std::array attributes; static const std::array uniforms; static const std::array textures; static constexpr auto source = R"( - struct VertexStage { short2 pos [[attribute(0)]]; short2 texture_pos [[attribute(1)]]; @@ -37,9 +35,6 @@ struct alignas(16) HillshadePrepareDrawableUBO { float2 dimension; float zoom; float maxzoom; - bool overdrawInspector; - uint8_t pad1, pad2, pad3; - float pad4, pad5, pad6; }; FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], @@ -68,10 +63,9 @@ half4 fragment fragmentMain(FragmentStage in [[stage_in]], device const HillshadePrepareDrawableUBO& drawable [[buffer(2)]], texture2d image [[texture(0)]], sampler image_sampler [[sampler(0)]]) { - - if (drawable.overdrawInspector) { - return half4(1.0); - } +#if defined(OVERDRAW_INSPECTOR) + return half4(1.0); +#endif float2 epsilon = 1.0 / drawable.dimension; diff --git a/include/mbgl/shaders/mtl/line.hpp b/include/mbgl/shaders/mtl/line.hpp index 6e057fabe51..09bae7178a5 100644 --- a/include/mbgl/shaders/mtl/line.hpp +++ b/include/mbgl/shaders/mtl/line.hpp @@ -13,10 +13,9 @@ struct ShaderSource { static constexpr auto name = "LineShader"; static constexpr auto vertexMainFunction = "vertexMain"; static constexpr auto fragmentMainFunction = "fragmentMain"; - static constexpr auto hasPermutations = true; static const std::array attributes; - static const std::array uniforms; + static const std::array uniforms; static const std::array textures; static constexpr auto source = R"( @@ -64,24 +63,22 @@ struct FragmentStage { FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], device const LineUBO& line [[buffer(8)]], device const LinePropertiesUBO& props [[buffer(9)]], - device const LineInterpolationUBO& interp [[buffer(10)]], - device const LinePermutationUBO& permutation [[buffer(11)]], - device const ExpressionInputsUBO& expr [[buffer(12)]]) { + device const LineInterpolationUBO& interp [[buffer(10)]]) { #if defined(HAS_UNIFORM_u_gapwidth) const auto gapwidth = props.gapwidth / 2; #else - const auto gapwidth = valueFor(permutation.gapwidth, props.gapwidth, vertx.gapwidth, interp.gapwidth_t, expr) / 2; + const auto gapwidth = unpack_mix_float(vertx.gapwidth, interp.gapwidth_t) / 2; #endif #if defined(HAS_UNIFORM_u_offset) const auto offset = props.offset * -1; #else - const auto offset = valueFor(permutation.offset, props.offset, vertx.offset, interp.offset_t, expr) * -1; + const auto offset = unpack_mix_float(vertx.offset, interp.offset_t) * -1; #endif #if defined(HAS_UNIFORM_u_width) const auto width = props.width; #else - const auto width = valueFor(permutation.width, props.width, vertx.width, interp.width_t, expr); + const auto width = unpack_mix_float(vertx.width, interp.width_t); #endif // the distance over which the line edge fades out. @@ -127,24 +124,23 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], .gamma_scale = half(extrude_length_without_perspective / extrude_length_with_perspective), #if !defined(HAS_UNIFORM_u_color) - .color = colorFor(permutation.color, props.color, vertx.color, interp.color_t, expr), + .color = unpack_mix_color(vertx.color, interp.color_t), #endif #if !defined(HAS_UNIFORM_u_blur) - .blur = valueFor(permutation.blur, props.blur, vertx.blur, interp.blur_t, expr), + .blur = unpack_mix_float(vertx.blur, interp.blur_t), #endif #if !defined(HAS_UNIFORM_u_opacity) - .opacity = valueFor(permutation.opacity, props.opacity, vertx.opacity, interp.opacity_t, expr), + .opacity = unpack_mix_float(vertx.opacity, interp.opacity_t), #endif }; } half4 fragment fragmentMain(FragmentStage in [[stage_in]], device const LineUBO& line [[buffer(8)]], - device const LinePropertiesUBO& props [[buffer(9)]], - device const LinePermutationUBO& permutation [[buffer(11)]]) { - if (permutation.overdrawInspector) { - return half4(1.0); - } + device const LinePropertiesUBO& props [[buffer(9)]]) { +#if defined(OVERDRAW_INSPECTOR) + return half4(1.0); +#endif #if defined(HAS_UNIFORM_u_color) const float4 color = props.color; @@ -180,10 +176,9 @@ struct ShaderSource { static constexpr auto name = "LinePatternShader"; static constexpr auto vertexMainFunction = "vertexMain"; static constexpr auto fragmentMainFunction = "fragmentMain"; - static constexpr auto hasPermutations = false; static const std::array attributes; - static const std::array uniforms; + static const std::array uniforms; static const std::array textures; static constexpr auto source = R"( @@ -217,21 +212,21 @@ struct VertexStage { struct FragmentStage { float4 position [[position, invariant]]; float2 width2; - float2 normal; - half gamma_scale; float linesofar; + half2 normal; + half gamma_scale; #if !defined(HAS_UNIFORM_u_blur) - float blur; + half blur; #endif #if !defined(HAS_UNIFORM_u_opacity) - float opacity; + half opacity; #endif #if !defined(HAS_UNIFORM_u_pattern_from) - float4 pattern_from; + half4 pattern_from; #endif #if !defined(HAS_UNIFORM_u_pattern_to) - float4 pattern_to; + half4 pattern_to; #endif }; @@ -275,24 +270,22 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], device const LinePatternUBO& line [[buffer(9)]], device const LinePatternPropertiesUBO& props [[buffer(10)]], device const LinePatternInterpolationUBO& interp [[buffer(11)]], - device const LinePatternTilePropertiesUBO& tileProps [[buffer(12)]], - device const LinePermutationUBO& permutation [[buffer(13)]], - device const ExpressionInputsUBO& expr [[buffer(14)]]) { + device const LinePatternTilePropertiesUBO& tileProps [[buffer(12)]]) { #if defined(HAS_UNIFORM_u_gapwidth) const auto gapwidth = props.gapwidth / 2; #else - const auto gapwidth = valueFor(permutation.gapwidth, props.gapwidth, vertx.gapwidth, interp.gapwidth_t, expr) / 2; + const auto gapwidth = unpack_mix_float(vertx.gapwidth, interp.gapwidth_t) / 2; #endif #if defined(HAS_UNIFORM_u_offset) const auto offset = props.offset * -1; #else - const auto offset = valueFor(permutation.offset, props.offset, vertx.offset, interp.offset_t, expr) * -1; + const auto offset = unpack_mix_float(vertx.offset, interp.offset_t) * -1; #endif #if defined(HAS_UNIFORM_u_width) const auto width = props.width; #else - const auto width = valueFor(permutation.width, props.width, vertx.width, interp.width_t, expr); + const auto width = unpack_mix_float(vertx.width, interp.width_t); #endif // the distance over which the line edge fades out. @@ -336,21 +329,21 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], return { .position = position, .width2 = float2(outset, inset), - .normal = v_normal, + .normal = half2(v_normal), .gamma_scale = half(extrude_length_without_perspective / extrude_length_with_perspective), .linesofar = linesofar, #if !defined(HAS_UNIFORM_u_blur) - .blur = valueFor(permutation.blur, props.blur, vertx.blur, interp.blur_t, expr), + .blur = half(unpack_mix_float(vertx.blur, interp.blur_t)), #endif #if !defined(HAS_UNIFORM_u_opacity) - .opacity = valueFor(permutation.opacity, props.opacity, vertx.opacity, interp.opacity_t, expr), + .opacity = half(unpack_mix_float(vertx.opacity, interp.opacity_t)), #endif #if !defined(HAS_UNIFORM_u_pattern_from) - .pattern_from = patternFor(permutation.pattern_from, tileProps.pattern_from, vertx.pattern_from, interp.pattern_from_t, expr), + .pattern_from = half4(vertx.pattern_from), #endif #if !defined(HAS_UNIFORM_u_pattern_to) - .pattern_to = patternFor(permutation.pattern_to, tileProps.pattern_to, vertx.pattern_to, interp.pattern_to_t, expr), + .pattern_to = half4(vertx.pattern_to), #endif }; } @@ -359,32 +352,31 @@ half4 fragment fragmentMain(FragmentStage in [[stage_in]], device const LinePatternUBO& line [[buffer(9)]], device const LinePatternPropertiesUBO& props [[buffer(10)]], device const LinePatternTilePropertiesUBO& tileProps [[buffer(12)]], - device const LinePermutationUBO& permutation [[buffer(13)]], texture2d image0 [[texture(0)]], sampler image0_sampler [[sampler(0)]]) { - if (permutation.overdrawInspector) { - return half4(1.0); - } +#if defined(OVERDRAW_INSPECTOR) + return half4(1.0); +#endif #if defined(HAS_UNIFORM_u_blur) - const auto blur = props.blur; + const half blur = props.blur; #else - const auto blur = in.blur; + const half blur = in.blur; #endif #if defined(HAS_UNIFORM_u_opacity) - const auto opacity = props.opacity; + const half opacity = props.opacity; #else - const auto opacity = in.opacity; + const half opacity = in.opacity; #endif #if defined(HAS_UNIFORM_u_pattern_from) - const auto pattern_from = tileProps.pattern_from; + const auto pattern_from = float4(tileProps.pattern_from); #else - const auto pattern_from = in.pattern_from; + const auto pattern_from = float4(in.pattern_from); #endif #if defined(HAS_UNIFORM_u_pattern_to) - const auto pattern_to = tileProps.pattern_to; + const auto pattern_to = float4(tileProps.pattern_to); #else - const auto pattern_to = in.pattern_to; + const auto pattern_to = float4(in.pattern_to); #endif const float2 pattern_tl_a = pattern_from.xy; @@ -437,10 +429,9 @@ struct ShaderSource { static constexpr auto name = "LineSDFShader"; static constexpr auto vertexMainFunction = "vertexMain"; static constexpr auto fragmentMainFunction = "fragmentMain"; - static constexpr auto hasPermutations = true; static const std::array attributes; - static const std::array uniforms; + static const std::array uniforms; static const std::array textures; static constexpr auto source = R"( @@ -531,29 +522,27 @@ struct alignas(16) LineSDFInterpolationUBO { FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], device const LineSDFUBO& line [[buffer(9)]], device const LineSDFPropertiesUBO& props [[buffer(10)]], - device const LineSDFInterpolationUBO& interp [[buffer(11)]], - device const LinePermutationUBO& permutation [[buffer(12)]], - device const ExpressionInputsUBO& expr [[buffer(13)]]) { + device const LineSDFInterpolationUBO& interp [[buffer(11)]]) { #if defined(HAS_UNIFORM_u_gapwidth) const auto gapwidth = props.gapwidth / 2; #else - const auto gapwidth = valueFor(permutation.gapwidth, props.gapwidth, vertx.gapwidth, interp.gapwidth_t, expr) / 2; + const auto gapwidth = unpack_mix_float(vertx.gapwidth, interp.gapwidth_t) / 2; #endif #if defined(HAS_UNIFORM_u_offset) const auto offset = props.offset * -1; #else - const auto offset = valueFor(permutation.offset, props.offset, vertx.offset, interp.offset_t, expr) * -1; + const auto offset = unpack_mix_float(vertx.offset, interp.offset_t) * -1; #endif #if defined(HAS_UNIFORM_u_width) const auto width = props.width; #else - const auto width = valueFor(permutation.width, props.width, vertx.width, interp.width_t, expr); + const auto width = unpack_mix_float(vertx.width, interp.width_t); #endif #if defined(HAS_UNIFORM_u_floorwidth) const auto floorwidth = props.floorwidth; #else - const auto floorwidth = valueFor(permutation.floorwidth, props.floorwidth, vertx.floorwidth, interp.floorwidth_t, expr); + const auto floorwidth = unpack_mix_float(vertx.floorwidth, interp.floorwidth_t); #endif // the distance over which the line edge fades out. @@ -603,13 +592,13 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], .tex_b = float2(linesofar * line.patternscale_b.x / floorwidth, (normal.y * line.patternscale_b.y + line.tex_y_b) * 2.0), #if !defined(HAS_UNIFORM_u_color) - .color = colorFor(permutation.color, props.color, vertx.color, interp.color_t, expr), + .color = unpack_mix_color(vertx.color, interp.color_t), #endif #if !defined(HAS_UNIFORM_u_blur) - .blur = valueFor(permutation.blur, props.blur, vertx.blur, interp.blur_t, expr), + .blur = unpack_mix_float(vertx.blur, interp.blur_t), #endif #if !defined(HAS_UNIFORM_u_opacity) - .opacity = valueFor(permutation.opacity, props.opacity, vertx.opacity, interp.opacity_t, expr), + .opacity = unpack_mix_float(vertx.opacity, interp.opacity_t), #endif #if !defined(HAS_UNIFORM_u_floorwidth) .floorwidth = floorwidth, @@ -620,12 +609,11 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], half4 fragment fragmentMain(FragmentStage in [[stage_in]], device const LineSDFUBO& line [[buffer(9)]], device const LineSDFPropertiesUBO& props [[buffer(10)]], - device const LinePermutationUBO& permutation [[buffer(12)]], texture2d image0 [[texture(0)]], sampler image0_sampler [[sampler(0)]]) { - if (permutation.overdrawInspector) { - return half4(1.0); - } +#if defined(OVERDRAW_INSPECTOR) + return half4(1.0); +#endif #if defined(HAS_UNIFORM_u_color) const float4 color = props.color; @@ -669,7 +657,6 @@ struct ShaderSource { static constexpr auto name = "LineBasicShader"; static constexpr auto vertexMainFunction = "vertexMain"; static constexpr auto fragmentMainFunction = "fragmentMain"; - static constexpr auto hasPermutations = true; static const std::array attributes; static const std::array uniforms; diff --git a/include/mbgl/shaders/mtl/line_gradient.hpp b/include/mbgl/shaders/mtl/line_gradient.hpp index 7a0d1e09e4c..9c0eb3b8414 100644 --- a/include/mbgl/shaders/mtl/line_gradient.hpp +++ b/include/mbgl/shaders/mtl/line_gradient.hpp @@ -13,10 +13,9 @@ struct ShaderSource { static constexpr auto name = "LineGradientShader"; static constexpr auto vertexMainFunction = "vertexMain"; static constexpr auto fragmentMainFunction = "fragmentMain"; - static constexpr auto hasPermutations = false; static const std::array attributes; - static const std::array uniforms; + static const std::array uniforms; static const std::array textures; static constexpr auto source = R"( @@ -33,26 +32,44 @@ struct VertexStage { struct FragmentStage { float4 position [[position, invariant]]; float2 width2; - float2 gapwidth; - float2 normal; - float gamma_scale; - float blur; - float opacity; + half2 normal; + half gamma_scale; float lineprogress; + +#if !defined(HAS_UNIFORM_u_blur) + half blur; +#endif +#if !defined(HAS_UNIFORM_u_opacity) + half opacity; +#endif }; FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], device const LineGradientUBO& line [[buffer(7)]], device const LineGradientPropertiesUBO& props [[buffer(8)]], - device const LineGradientInterpolationUBO& interp [[buffer(9)]], - device const LinePermutationUBO& permutation [[buffer(10)]], - device const ExpressionInputsUBO& expr [[buffer(11)]]) { - - const auto blur = valueFor(permutation.blur, props.blur, vertx.blur, interp.blur_t, expr); - const auto opacity = valueFor(permutation.opacity, props.opacity, vertx.opacity, interp.opacity_t, expr); - const auto gapwidth = valueFor(permutation.gapwidth, props.gapwidth, vertx.gapwidth, interp.gapwidth_t, expr) / 2; - const auto offset = valueFor(permutation.offset, props.offset, vertx.offset, interp.offset_t, expr) * -1; - const auto width = valueFor(permutation.width, props.width, vertx.width, interp.width_t, expr); + device const LineGradientInterpolationUBO& interp [[buffer(9)]]) { + +#if !defined(HAS_UNIFORM_u_blur) + const auto blur = unpack_mix_float(vertx.blur, interp.blur_t); +#endif +#if !defined(HAS_UNIFORM_u_opacity) + const auto opacity = unpack_mix_float(vertx.opacity, interp.opacity_t); +#endif +#if defined(HAS_UNIFORM_u_gapwidth) + const auto gapwidth = props.gapwidth; +#else + const auto gapwidth = unpack_mix_float(vertx.gapwidth, interp.gapwidth_t) / 2; +#endif +#if defined(HAS_UNIFORM_u_offset) + const auto offset = props.offset; +#else + const auto offset = unpack_mix_float(vertx.offset, interp.offset_t) * -1; +#endif +#if defined(HAS_UNIFORM_u_width) + const auto width = props.width; +#else + const auto width = unpack_mix_float(vertx.width, interp.width_t); +#endif // the distance over which the line edge fades out. // Retina devices need a smaller distance to avoid aliasing. @@ -94,29 +111,44 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], return { .position = position, .width2 = float2(outset, inset), - .gapwidth = gapwidth, - .normal = v_normal, - .gamma_scale = extrude_length_without_perspective / extrude_length_with_perspective, - .blur = blur, - .opacity = opacity, + .normal = half2(v_normal), + .gamma_scale = half(extrude_length_without_perspective / extrude_length_with_perspective), .lineprogress = v_lineprogress, + +#if !defined(HAS_UNIFORM_u_blur) + .blur = half(blur), +#endif +#if !defined(HAS_UNIFORM_u_opacity) + .opacity = half(opacity), +#endif }; } half4 fragment fragmentMain(FragmentStage in [[stage_in]], device const LineGradientUBO& line [[buffer(7)]], - device const LinePermutationUBO& permutation [[buffer(10)]], + device const LineGradientPropertiesUBO& props [[buffer(8)]], texture2d gradientTexture [[texture(0)]]) { - if (permutation.overdrawInspector) { - return half4(1.0); - } +#if defined(OVERDRAW_INSPECTOR) + return half4(1.0); +#endif + +#if defined(HAS_UNIFORM_u_blur) + const float blur = props.blur; +#else + const float blur = float(in.blur); +#endif +#if defined(HAS_UNIFORM_u_opacity) + const float opacity = props.opacity; +#else + const float opacity = float(in.opacity); +#endif // Calculate the distance of the pixel from the line in pixels. const float dist = length(in.normal) * in.width2.x; // Calculate the antialiasing fade factor. This is either when fading in the // line in case of an offset line (v_width2.y) or when fading out (v_width2.x) - const float blur2 = (in.blur + 1.0 / line.device_pixel_ratio) * in.gamma_scale; + const float blur2 = (blur + 1.0 / line.device_pixel_ratio) * in.gamma_scale; const float alpha = clamp(min(dist - (in.width2.y - blur2), in.width2.x - dist) / blur2, 0.0, 1.0); // For gradient lines, v_lineprogress is the ratio along the entire line, @@ -124,7 +156,7 @@ half4 fragment fragmentMain(FragmentStage in [[stage_in]], constexpr sampler sampler2d(coord::normalized, filter::linear); const float4 color = gradientTexture.sample(sampler2d, float2(in.lineprogress, 0.5)); - return half4(color * (alpha * in.opacity)); + return half4(color * (alpha * opacity)); } )"; }; diff --git a/include/mbgl/shaders/mtl/raster.hpp b/include/mbgl/shaders/mtl/raster.hpp index 630d4c2d36e..15b032ed24d 100644 --- a/include/mbgl/shaders/mtl/raster.hpp +++ b/include/mbgl/shaders/mtl/raster.hpp @@ -13,7 +13,6 @@ struct ShaderSource { static constexpr auto name = "RasterShader"; static constexpr auto vertexMainFunction = "vertexMain"; static constexpr auto fragmentMainFunction = "fragmentMain"; - static constexpr auto hasPermutations = false; static const std::array attributes; static const std::array uniforms; @@ -75,10 +74,9 @@ half4 fragment fragmentMain(FragmentStage in [[stage_in]], texture2d image1 [[texture(1)]], sampler image0_sampler [[sampler(0)]], sampler image1_sampler [[sampler(1)]]) { - - if (drawable.overdrawInspector) { - return half4(1.0); - } +#if defined(OVERDRAW_INSPECTOR) + return half4(1.0); +#endif // read and cross-fade colors from the main and parent tiles float4 color0 = image0.sample(image0_sampler, in.pos0); diff --git a/include/mbgl/shaders/mtl/shader_group.hpp b/include/mbgl/shaders/mtl/shader_group.hpp index e55f00af6b6..06c987ed186 100644 --- a/include/mbgl/shaders/mtl/shader_group.hpp +++ b/include/mbgl/shaders/mtl/shader_group.hpp @@ -20,26 +20,35 @@ class ShaderGroupBase : public gfx::ShaderGroup { using StringIDSet = mbgl::unordered_set; protected: + ShaderGroupBase(const ProgramParameters& parameters_) + : programParameters(parameters_) {} + using DefinesMap = mbgl::unordered_map; void addAdditionalDefines(const StringIDSet& propertiesAsUniforms, DefinesMap& additionalDefines) { - const std::string uniformPrefix = "HAS_UNIFORM_u_"; additionalDefines.reserve(propertiesAsUniforms.size() + 1); - additionalDefines.insert(std::make_pair("HAS_PERMUTATIONS", std::string())); + if (programParameters.getOverdrawInspectorEnabled()) { + additionalDefines.insert(std::make_pair(overdrawName, std::string())); + } for (const auto nameID : propertiesAsUniforms) { // We expect the names to be prefixed by "a_", but we need just the base here. const auto name = stringIndexer().get(nameID); const auto* base = (name[0] == 'a' && name[1] == '_') ? &name[2] : name.data(); - additionalDefines.insert(std::make_pair(uniformPrefix + base, std::string())); + additionalDefines.insert(std::make_pair(std::string(uniformPrefix) + base, std::string())); } } + + ProgramParameters programParameters; + +private: + static constexpr auto uniformPrefix = "HAS_UNIFORM_u_"; + static constexpr auto overdrawName = "OVERDRAW_INSPECTOR"; }; template class ShaderGroup final : public ShaderGroupBase { public: ShaderGroup(const ProgramParameters& programParameters_) - : ShaderGroupBase(), - programParameters(programParameters_) {} + : ShaderGroupBase(programParameters_) {} ~ShaderGroup() noexcept override = default; gfx::ShaderPtr getOrCreateShader(gfx::Context& gfxContext, @@ -50,17 +59,13 @@ class ShaderGroup final : public ShaderGroupBase { constexpr auto& source = ShaderSource::source; constexpr auto& vertMain = ShaderSource::vertexMainFunction; constexpr auto& fragMain = ShaderSource::fragmentMainFunction; - constexpr auto permutations = shaders::ShaderSource::hasPermutations; - const PropertyHashType key = permutations ? propertyHash(propertiesAsUniforms) : 0; - const std::string shaderName = permutations ? getShaderName(name, key) : name; + const std::string shaderName = getShaderName(name, propertyHash(propertiesAsUniforms)); auto shader = get(shaderName); if (!shader) { DefinesMap additionalDefines; - if (permutations) { - addAdditionalDefines(propertiesAsUniforms, additionalDefines); - } + addAdditionalDefines(propertiesAsUniforms, additionalDefines); auto& context = static_cast(gfxContext); const auto shaderSource = std::string(shaders::prelude) + source; @@ -72,11 +77,9 @@ class ShaderGroup final : public ShaderGroupBase { throw std::runtime_error("Failed to register " + shaderName + " with shader group!"); } - shader->setBindMissingAttributes(!permutations); - using ShaderClass = shaders::ShaderSource; for (const auto& attrib : ShaderClass::attributes) { - if (!permutations || !propertiesAsUniforms.count(attrib.nameID)) { + if (!propertiesAsUniforms.count(attrib.nameID)) { shader->initAttribute(attrib); } } @@ -89,9 +92,6 @@ class ShaderGroup final : public ShaderGroupBase { } return shader; } - -private: - ProgramParameters programParameters; }; } // namespace mtl diff --git a/include/mbgl/shaders/mtl/shader_program.hpp b/include/mbgl/shaders/mtl/shader_program.hpp index 390000586e5..357a32549f8 100644 --- a/include/mbgl/shaders/mtl/shader_program.hpp +++ b/include/mbgl/shaders/mtl/shader_program.hpp @@ -69,9 +69,6 @@ class ShaderProgram final : public gfx::ShaderProgramBase { void initUniformBlock(const shaders::UniformBlockInfo&); void initTexture(const shaders::TextureInfo&); - bool getBindMissingAttributes() const { return bindMissingAttributes; } - void setBindMissingAttributes(bool value) { bindMissingAttributes = value; } - protected: std::string shaderName; RendererBackend& backend; @@ -80,7 +77,6 @@ class ShaderProgram final : public gfx::ShaderProgramBase { UniformBlockArray uniformBlocks; VertexAttributeArray vertexAttributes; std::unordered_map textureBindings; - bool bindMissingAttributes = true; }; } // namespace mtl diff --git a/include/mbgl/shaders/mtl/symbol_icon.hpp b/include/mbgl/shaders/mtl/symbol_icon.hpp index 49d463890fb..896cc55b57f 100644 --- a/include/mbgl/shaders/mtl/symbol_icon.hpp +++ b/include/mbgl/shaders/mtl/symbol_icon.hpp @@ -13,10 +13,9 @@ struct ShaderSource { static constexpr auto name = "SymbolIconShader"; static constexpr auto vertexMainFunction = "vertexMain"; static constexpr auto fragmentMainFunction = "fragmentMain"; - static constexpr auto hasPermutations = true; static const std::array attributes; - static const std::array uniforms; + static const std::array uniforms; static const std::array textures; static constexpr auto source = R"( @@ -34,10 +33,13 @@ struct VertexStage { struct FragmentStage { float4 position [[position, invariant]]; - float2 tex; - half fade_opacity; + half2 tex; -#if !defined(HAS_UNIFORM_u_opacity) +#if defined(HAS_UNIFORM_u_opacity) + // We only need to pass `fade_opacity` separately if opacity is a + // uniform, otherwise it's multiplied into fragment opacity, below. + half fade_opacity; +#else half opacity; #endif }; @@ -47,9 +49,7 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], device const SymbolDynamicUBO& dynamic [[buffer(9)]], device const SymbolDrawablePaintUBO& paint [[buffer(10)]], device const SymbolDrawableTilePropsUBO& props [[buffer(11)]], - device const SymbolDrawableInterpolateUBO& interp [[buffer(12)]], - device const SymbolPermutationUBO& permutation [[buffer(13)]], - device const ExpressionInputsUBO& expr [[buffer(14)]]) { + device const SymbolDrawableInterpolateUBO& interp [[buffer(12)]]) { const float2 a_pos = vertx.pos_offset.xy; const float2 a_offset = vertx.pos_offset.zw; @@ -106,15 +106,17 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], const float2 posOffset = a_offset * max(a_minFontScale, fontScale) / 32.0 + a_pxoffset / 16.0; const float4 position = drawable.coord_matrix * float4(pos0 + rotation_matrix * posOffset, 0.0, 1.0); - const float2 fade_opacity = unpack_opacity(vertx.fade_opacity); - const float fade_change = fade_opacity[1] > 0.5 ? dynamic.fade_change : -dynamic.fade_change; + const float2 raw_fade_opacity = unpack_opacity(vertx.fade_opacity); + const float fade_change = raw_fade_opacity[1] > 0.5 ? dynamic.fade_change : -dynamic.fade_change; + const float fade_opacity = max(0.0, min(1.0, raw_fade_opacity[0] + fade_change)); return { .position = position, - .tex = a_tex / drawable.texsize, - .fade_opacity = half(max(0.0, min(1.0, fade_opacity[0] + fade_change))), -#if !defined(HAS_UNIFORM_u_opacity) - .opacity = half(valueFor(permutation.opacity, paint.opacity, vertx.opacity, interp.opacity_t, expr)), + .tex = half2(a_tex / drawable.texsize), +#if defined(HAS_UNIFORM_u_opacity) + .fade_opacity = half(fade_opacity), +#else + .opacity = half(unpack_mix_float(vertx.opacity, interp.opacity_t) * fade_opacity), #endif }; } @@ -122,20 +124,19 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], half4 fragment fragmentMain(FragmentStage in [[stage_in]], device const SymbolDrawableUBO& drawable [[buffer(8)]], device const SymbolDrawablePaintUBO& paint [[buffer(10)]], - device const SymbolPermutationUBO& permutation [[buffer(13)]], texture2d image [[texture(0)]], sampler image_sampler [[sampler(0)]]) { - if (permutation.overdrawInspector) { - return half4(1.0); - } +#if defined(OVERDRAW_INSPECTOR) + return half4(1.0); +#endif #if defined(HAS_UNIFORM_u_opacity) - const half opacity = half(paint.opacity); + const float opacity = paint.opacity * in.fade_opacity; #else - const half opacity = in.opacity; + const float opacity = in.opacity; // fade_opacity is baked in for this case #endif - return half4(image.sample(image_sampler, in.tex) * (opacity * in.fade_opacity)); + return half4(image.sample(image_sampler, float2(in.tex)) * opacity); } )"; }; diff --git a/include/mbgl/shaders/mtl/symbol_sdf.hpp b/include/mbgl/shaders/mtl/symbol_sdf.hpp index a2d540f9145..b080edd9687 100644 --- a/include/mbgl/shaders/mtl/symbol_sdf.hpp +++ b/include/mbgl/shaders/mtl/symbol_sdf.hpp @@ -13,10 +13,9 @@ struct ShaderSource { static constexpr auto name = "SymbolSDFIconShader"; static constexpr auto vertexMainFunction = "vertexMain"; static constexpr auto fragmentMainFunction = "fragmentMain"; - static constexpr auto hasPermutations = true; static const std::array attributes; - static const std::array uniforms; + static const std::array uniforms; static const std::array textures; static constexpr auto source = R"( @@ -46,8 +45,6 @@ struct VertexStage { struct FragmentStage { float4 position [[position, invariant]]; - float2 data0; - float3 data1; #if !defined(HAS_UNIFORM_u_fill_color) half4 fill_color; @@ -55,6 +52,12 @@ struct FragmentStage { #if !defined(HAS_UNIFORM_u_halo_color) half4 halo_color; #endif + + half2 tex; + half gamma_scale; + half fontScale; + half fade_opacity; + #if !defined(HAS_UNIFORM_u_opacity) half opacity; #endif @@ -71,9 +74,7 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], device const SymbolDynamicUBO& dynamic [[buffer(11)]], device const SymbolDrawablePaintUBO& paint [[buffer(12)]], device const SymbolDrawableTilePropsUBO& props [[buffer(13)]], - device const SymbolDrawableInterpolateUBO& interp [[buffer(14)]], - device const SymbolPermutationUBO& permutation [[buffer(15)]], - device const ExpressionInputsUBO& expr [[buffer(16)]]) { + device const SymbolDrawableInterpolateUBO& interp [[buffer(14)]]) { const float2 a_pos = vertx.pos_offset.xy; const float2 a_offset = vertx.pos_offset.zw; @@ -130,37 +131,35 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], const float angle_sin = sin(segment_angle + symbol_rotation); const float angle_cos = cos(segment_angle + symbol_rotation); - const float2x2 rotation_matrix = float2x2(angle_cos, -1.0 * angle_sin, angle_sin, angle_cos); - + const auto rotation_matrix = float2x2(angle_cos, -1.0 * angle_sin, angle_sin, angle_cos); const float4 projected_pos = drawable.label_plane_matrix * float4(vertx.projected_pos.xy, 0.0, 1.0); const float2 pos_rot = a_offset / 32.0 * fontScale + a_pxoffset; const float2 pos0 = projected_pos.xy / projected_pos.w + rotation_matrix * pos_rot; const float4 position = drawable.coord_matrix * float4(pos0, 0.0, 1.0); - const float gamma_scale = position.w; - const float2 fade_opacity = unpack_opacity(vertx.fade_opacity); const float fade_change = (fade_opacity[1] > 0.5) ? dynamic.fade_change : -dynamic.fade_change; - const float interpolated_fade_opacity = max(0.0, min(1.0, fade_opacity[0] + fade_change)); return { - .position = position, + .position = position, #if !defined(HAS_UNIFORM_u_fill_color) - .fill_color = half4(colorFor(permutation.fill_color, paint.fill_color, vertx.fill_color, interp.fill_color_t, expr)), + .fill_color = half4(unpack_mix_color(vertx.fill_color, interp.fill_color_t)), #endif #if !defined(HAS_UNIFORM_u_halo_color) - .halo_color = half4(colorFor(permutation.halo_color, paint.halo_color, vertx.halo_color, interp.halo_color_t, expr)), + .halo_color = half4(unpack_mix_color(vertx.halo_color, interp.halo_color_t)), #endif #if !defined(HAS_UNIFORM_u_halo_width) - .halo_width = half(valueFor(permutation.halo_width, paint.halo_width, vertx.halo_width, interp.halo_width_t, expr)), + .halo_width = half(unpack_mix_float(vertx.halo_width, interp.halo_width_t)), #endif #if !defined(HAS_UNIFORM_u_halo_blur) - .halo_blur = half(valueFor(permutation.halo_blur, paint.halo_blur, vertx.halo_blur, interp.halo_blur_t, expr)), + .halo_blur = half(unpack_mix_float(vertx.halo_blur, interp.halo_blur_t)), #endif #if !defined(HAS_UNIFORM_u_opacity) - .opacity = half(valueFor(permutation.opacity, paint.opacity, vertx.opacity, interp.opacity_t, expr)), + .opacity = half(unpack_mix_float(vertx.opacity, interp.opacity_t)), #endif - .data0 = a_tex / drawable.texsize, - .data1 = float3(gamma_scale, size, interpolated_fade_opacity), + .tex = half2(a_tex / drawable.texsize), + .gamma_scale = half(position.w), + .fontScale = half(fontScale), + .fade_opacity = half(max(0.0, min(1.0, fade_opacity[0] + fade_change))), }; } @@ -169,12 +168,11 @@ half4 fragment fragmentMain(FragmentStage in [[stage_in]], device const SymbolDynamicUBO& dynamic [[buffer(11)]], device const SymbolDrawablePaintUBO& paint [[buffer(12)]], device const SymbolDrawableTilePropsUBO& props [[buffer(13)]], - device const SymbolPermutationUBO& permutation [[buffer(15)]], texture2d image [[texture(0)]], sampler image_sampler [[sampler(0)]]) { - if (permutation.overdrawInspector) { - return half4(1.0); - } +#if defined(OVERDRAW_INSPECTOR) + return half4(1.0); +#endif #if defined(HAS_UNIFORM_u_fill_color) const half4 fill_color = half4(paint.fill_color); @@ -187,36 +185,31 @@ half4 fragment fragmentMain(FragmentStage in [[stage_in]], const half4 halo_color = in.halo_color; #endif #if defined(HAS_UNIFORM_u_opacity) - const half opacity = half(paint.opacity); + const float opacity = paint.opacity; #else - const half opacity = in.opacity; + const float opacity = in.opacity; #endif #if defined(HAS_UNIFORM_u_halo_width) - const half halo_width = half(paint.halo_width); + const float halo_width = paint.halo_width; #else - const half halo_width = in.halo_width; + const float halo_width = in.halo_width; #endif #if defined(HAS_UNIFORM_u_halo_blur) - const half halo_blur = half(paint.halo_blur); + const float halo_blur = paint.halo_blur; #else - const half halo_blur = in.halo_blur; + const float halo_blur = in.halo_blur; #endif const float EDGE_GAMMA = 0.105 / dynamic.device_pixel_ratio; - const float2 tex = in.data0.xy; - const float gamma_scale = in.data1.x; - const float size = in.data1.y; - const float fade_opacity = in.data1[2]; - const float fontScale = props.is_text ? size / 24.0 : size; - const float fontGamma = fontScale * drawable.gamma_scale; + const float fontGamma = in.fontScale * drawable.gamma_scale; const half4 color = props.is_halo ? halo_color : fill_color; const float gamma = ((props.is_halo ? (halo_blur * 1.19 / SDF_PX) : 0) + EDGE_GAMMA) / fontGamma; - const float buff = props.is_halo ? (6.0 - halo_width / fontScale) / SDF_PX : (256.0 - 64.0) / 256.0; - const float dist = image.sample(image_sampler, tex).a; - const float gamma_scaled = gamma * gamma_scale; + const float buff = props.is_halo ? (6.0 - halo_width / in.fontScale) / SDF_PX : (256.0 - 64.0) / 256.0; + const float dist = image.sample(image_sampler, float2(in.tex)).a; + const float gamma_scaled = gamma * in.gamma_scale; const float alpha = smoothstep(buff - gamma_scaled, buff + gamma_scaled, dist); - return half4(color * (alpha * opacity * fade_opacity)); + return half4(color * (alpha * opacity * in.fade_opacity)); } )"; }; diff --git a/include/mbgl/shaders/mtl/symbol_text_and_icon.hpp b/include/mbgl/shaders/mtl/symbol_text_and_icon.hpp index a57c1cb762d..f21d3386d62 100644 --- a/include/mbgl/shaders/mtl/symbol_text_and_icon.hpp +++ b/include/mbgl/shaders/mtl/symbol_text_and_icon.hpp @@ -13,10 +13,9 @@ struct ShaderSource static constexpr auto name = "SymbolTextAndIconShader"; static constexpr auto vertexMainFunction = "vertexMain"; static constexpr auto fragmentMainFunction = "fragmentMain"; - static constexpr auto hasPermutations = true; static const std::array attributes; - static const std::array uniforms; + static const std::array uniforms; static const std::array textures; static constexpr auto source = R"( @@ -48,8 +47,6 @@ struct VertexStage { struct FragmentStage { float4 position [[position, invariant]]; - float4 data0; - float4 data1; #if !defined(HAS_UNIFORM_u_fill_color) half4 fill_color; @@ -57,6 +54,9 @@ struct FragmentStage { #if !defined(HAS_UNIFORM_u_halo_color) half4 halo_color; #endif + + half2 tex; + #if !defined(HAS_UNIFORM_u_opacity) half opacity; #endif @@ -66,6 +66,11 @@ struct FragmentStage { #if !defined(HAS_UNIFORM_u_halo_blur) half halo_blur; #endif + + half gamma_scale; + half fontScale; + half fade_opacity; + bool is_icon; }; FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], @@ -73,9 +78,7 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], device const SymbolDynamicUBO& dynamic [[buffer(10)]], device const SymbolDrawablePaintUBO& paint [[buffer(11)]], device const SymbolDrawableTilePropsUBO& props [[buffer(12)]], - device const SymbolDrawableInterpolateUBO& interp [[buffer(13)]], - device const SymbolPermutationUBO& permutation [[buffer(14)]], - device const ExpressionInputsUBO& expr [[buffer(15)]]) { + device const SymbolDrawableInterpolateUBO& interp [[buffer(13)]]) { const float2 a_pos = vertx.pos_offset.xy; const float2 a_offset = vertx.pos_offset.zw; @@ -142,27 +145,30 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], const float2 fade_opacity = unpack_opacity(vertx.fade_opacity); const float fade_change = (fade_opacity[1] > 0.5) ? dynamic.fade_change : -dynamic.fade_change; - const float interpolated_fade_opacity = max(0.0, min(1.0, fade_opacity[0] + fade_change)); + const bool is_icon = (is_sdf == ICON); return { - .position = position, - .data0 = float4(a_tex / drawable.texsize, a_tex / drawable.texsize_icon), - .data1 = float4(gamma_scale, size, interpolated_fade_opacity, is_sdf), + .position = position, + .tex = half2(a_tex / (is_icon ? drawable.texsize_icon : drawable.texsize)), + .gamma_scale = half(gamma_scale), + .fontScale = half(fontScale), + .fade_opacity = half(max(0.0, min(1.0, fade_opacity[0] + fade_change))), + .is_icon = is_icon, #if !defined(HAS_UNIFORM_u_fill_color) - .fill_color = half(colorFor(permutation.fill_color, paint.fill_color, vertx.fill_color, interp.fill_color_t, expr)); + .fill_color = half(unpack_mix_color(vertx.fill_color, interp.fill_color_t)); #endif #if !defined(HAS_UNIFORM_u_halo_color) - .halo_color = half(colorFor(permutation.halo_color, paint.halo_color, vertx.halo_color, interp.halo_color_t, expr)); + .halo_color = half(unpack_mix_color(vertx.halo_color, interp.halo_color_t)); #endif #if !defined(HAS_UNIFORM_u_opacity) - .opacity = half(valueFor(permutation.opacity, paint.opacity, vertx.opacity, interp.opacity_t, expr)); + .opacity = half(unpack_mix_float(vertx.opacity, interp.opacity_t)); #endif #if !defined(HAS_UNIFORM_u_halo_width) - .halo_width = half(valueFor(permutation.halo_width, paint.halo_width, vertx.halo_width, interp.halo_width_t, expr)); + .halo_width = half(unpack_mix_float(vertx.halo_width, interp.halo_width_t)); #endif #if !defined(HAS_UNIFORM_u_halo_blur) - .halo_blur = half(valueFor(permutation.halo_blur, paint.halo_blur, vertx.halo_blur, interp.halo_blur_t, expr)); + .halo_blur = half(unpack_mix_float(vertx.halo_blur, interp.halo_blur_t)); #endif }; } @@ -172,14 +178,13 @@ half4 fragment fragmentMain(FragmentStage in [[stage_in]], device const SymbolDynamicUBO& dynamic [[buffer(10)]], device const SymbolDrawablePaintUBO& paint [[buffer(11)]], device const SymbolDrawableTilePropsUBO& props [[buffer(12)]], - device const SymbolPermutationUBO& permutation [[buffer(14)]], texture2d glyph_image [[texture(0)]], texture2d icon_image [[texture(1)]], sampler glyph_sampler [[sampler(0)]], sampler icon_sampler [[sampler(1)]]) { - if (permutation.overdrawInspector) { - return half4(1.0); - } +#if defined(OVERDRAW_INSPECTOR) + return half4(1.0); +#endif #if defined(HAS_UNIFORM_u_fill_color) const half4 fill_color = half4(paint.fill_color); @@ -207,28 +212,21 @@ half4 fragment fragmentMain(FragmentStage in [[stage_in]], const half halo_blur = in.halo_blur; #endif - const float fade_opacity = in.data1[2]; - - if (in.data1.w == ICON) { - const float2 tex_icon = in.data0.zw; - const float alpha = opacity * fade_opacity; - return half4(icon_image.sample(icon_sampler, tex_icon) * alpha); + if (in.is_icon) { + const float alpha = opacity * in.fade_opacity; + return half4(icon_image.sample(icon_sampler, float2(in.tex)) * alpha); } - const float2 tex = in.data0.xy; const float EDGE_GAMMA = 0.105 / dynamic.device_pixel_ratio; - const float gamma_scale = in.data1.x; - const float size = in.data1.y; - const float fontScale = size / 24.0; const half4 color = props.is_halo ? halo_color : fill_color; - const float fontGamma = fontScale * drawable.gamma_scale; + const float fontGamma = in.fontScale * drawable.gamma_scale; const float gamma = ((props.is_halo ? (halo_blur * 1.19 / SDF_PX) : 0) + EDGE_GAMMA) / fontGamma; - const float buff = props.is_halo ? (6.0 - halo_width / fontScale) / SDF_PX : (256.0 - 64.0) / 256.0; - const float dist = glyph_image.sample(glyph_sampler, tex).a; - const float gamma_scaled = gamma * gamma_scale; + const float buff = props.is_halo ? (6.0 - halo_width / in.fontScale) / SDF_PX : (256.0 - 64.0) / 256.0; + const float dist = glyph_image.sample(glyph_sampler, float2(in.tex)).a; + const float gamma_scaled = gamma * in.gamma_scale; const float alpha = smoothstep(buff - gamma_scaled, buff + gamma_scaled, dist); - return half4(color * (alpha * opacity * fade_opacity)); + return half4(color * (alpha * opacity * in.fade_opacity)); } )"; }; diff --git a/include/mbgl/shaders/raster_layer_ubo.hpp b/include/mbgl/shaders/raster_layer_ubo.hpp index 51da7c00694..f289db53dba 100644 --- a/include/mbgl/shaders/raster_layer_ubo.hpp +++ b/include/mbgl/shaders/raster_layer_ubo.hpp @@ -17,9 +17,7 @@ struct alignas(16) RasterDrawableUBO { float brightness_high; float saturation_factor; float contrast_factor; - bool overdrawInspector; - uint8_t pad1, pad2, pad3; - float pad4; + float pad1, pad2; }; static_assert(sizeof(RasterDrawableUBO) == 128); static_assert(sizeof(RasterDrawableUBO) % 16 == 0); diff --git a/include/mbgl/shaders/symbol_layer_ubo.hpp b/include/mbgl/shaders/symbol_layer_ubo.hpp index 352d12b7a50..d01868870b3 100644 --- a/include/mbgl/shaders/symbol_layer_ubo.hpp +++ b/include/mbgl/shaders/symbol_layer_ubo.hpp @@ -69,17 +69,5 @@ struct alignas(16) SymbolDrawablePaintUBO { }; static_assert(sizeof(SymbolDrawablePaintUBO) == 3 * 16); -struct alignas(16) SymbolPermutationUBO { - /* 0 */ Attribute fill_color; - /* 8 */ Attribute halo_color; - /* 16 */ Attribute opacity; - /* 24 */ Attribute halo_width; - /* 40 */ Attribute halo_blur; - /* 48 */ int32_t /*bool*/ overdrawInspector; - /* 52 */ float pad1, pad2, pad3; - /* 64 */ -}; -static_assert(sizeof(SymbolPermutationUBO) == 4 * 16); - } // namespace shaders } // namespace mbgl diff --git a/shaders/drawable.hillshade_prepare.fragment.glsl b/shaders/drawable.hillshade_prepare.fragment.glsl index 6782fa986f4..8cd8009055e 100644 --- a/shaders/drawable.hillshade_prepare.fragment.glsl +++ b/shaders/drawable.hillshade_prepare.fragment.glsl @@ -11,10 +11,6 @@ layout (std140) uniform HillshadePrepareDrawableUBO { highp vec2 u_dimension; highp float u_zoom; highp float u_maxzoom; - lowp float pad0_; - lowp float pad1_; - lowp float pad2_; - lowp float pad3_; }; float getElevation(vec2 coord, float bias) { diff --git a/shaders/drawable.hillshade_prepare.vertex.glsl b/shaders/drawable.hillshade_prepare.vertex.glsl index efee3fdc2e8..a3aaad8d065 100644 --- a/shaders/drawable.hillshade_prepare.vertex.glsl +++ b/shaders/drawable.hillshade_prepare.vertex.glsl @@ -7,10 +7,6 @@ layout (std140) uniform HillshadePrepareDrawableUBO { highp vec2 u_dimension; highp float u_zoom; highp float u_maxzoom; - lowp float pad0_; - lowp float pad1_; - lowp float pad2_; - lowp float pad3_; }; out vec2 v_pos; diff --git a/src/mbgl/mtl/drawable.cpp b/src/mbgl/mtl/drawable.cpp index a4ecd265494..ab8287e5964 100644 --- a/src/mbgl/mtl/drawable.cpp +++ b/src/mbgl/mtl/drawable.cpp @@ -346,12 +346,8 @@ void Drawable::bindAttributes(RenderPass& renderPass) const noexcept { renderPass.bindVertex(buffer->get(), /*offset=*/0, attributeIndex); } else { const auto* shaderMTL = static_cast(shader.get()); - if (impl->noBindingBuffer && shaderMTL && shaderMTL->getBindMissingAttributes()) { - renderPass.bindVertex(impl->noBindingBuffer->get(), /*offset=*/0, attributeIndex); - } else { - auto& context = renderPass.getCommandEncoder().getContext(); - renderPass.bindVertex(context.getEmptyBuffer(), /*offset=*/0, attributeIndex); - } + auto& context = renderPass.getCommandEncoder().getContext(); + renderPass.bindVertex(context.getEmptyBuffer(), /*offset=*/0, attributeIndex); } attributeIndex += 1; } diff --git a/src/mbgl/programs/program_parameters.cpp b/src/mbgl/programs/program_parameters.cpp index 7fefa770109..bacbfdb4872 100644 --- a/src/mbgl/programs/program_parameters.cpp +++ b/src/mbgl/programs/program_parameters.cpp @@ -8,7 +8,8 @@ namespace mbgl { ProgramParameters::ProgramParameters(const float pixelRatio, const bool overdraw) - : defines(2) { + : defines(2), + overdrawInspector(overdraw) { defines["DEVICE_PIXEL_RATIO"] = util::toString(pixelRatio, true); if (overdraw) { defines["OVERDRAW_INSPECTOR"] = std::string(); diff --git a/src/mbgl/programs/program_parameters.hpp b/src/mbgl/programs/program_parameters.hpp index 7004170335f..52d696d5d43 100644 --- a/src/mbgl/programs/program_parameters.hpp +++ b/src/mbgl/programs/program_parameters.hpp @@ -61,6 +61,8 @@ class ProgramParameters { /// @return Shader source string const std::string& fragmentSource(gfx::Backend::Type backend) const; + bool getOverdrawInspectorEnabled() const { return overdrawInspector; } + private: std::unordered_map defines; @@ -69,6 +71,8 @@ class ProgramParameters { std::array(gfx::Backend::Type::TYPE_MAX)> defaultSources; std::array(gfx::Backend::Type::TYPE_MAX)> userSources; + + bool overdrawInspector; }; } // namespace mbgl diff --git a/src/mbgl/renderer/layer_tweaker.cpp b/src/mbgl/renderer/layer_tweaker.cpp index 3e1315bc048..013d6ec886d 100644 --- a/src/mbgl/renderer/layer_tweaker.cpp +++ b/src/mbgl/renderer/layer_tweaker.cpp @@ -52,49 +52,4 @@ void LayerTweaker::updateProperties(Immutable newProps) propertiesUpdated = true; } -#if MLN_RENDER_BACKEND_METAL -shaders::ExpressionInputsUBO LayerTweaker::buildExpressionUBO(double zoom, uint64_t frameCount) { - const auto time = util::MonotonicTimer::now(); - const auto time_ns = std::chrono::duration_cast(time).count(); - return {/* .time_lo = */ static_cast(time_ns), - /* .time_hi = */ static_cast(time_ns >> 32), - /* .frame_lo = */ static_cast(frameCount), - /* .frame_hi = */ static_cast(frameCount >> 32), - /* .zoom = */ static_cast(zoom), - /* .zoom_frac = */ static_cast(zoom - static_cast(zoom)), - /* .pad = */ 0, - 0}; -} -#endif // MLN_RENDER_BACKEND_METAL - -void LayerTweaker::setPropertiesAsUniforms([[maybe_unused]] const mbgl::unordered_set& props) { -#if MLN_RENDER_BACKEND_METAL - if (props != propertiesAsUniforms) { - propertiesAsUniforms = props; - permutationUpdated = true; - } -#endif -} - -#if !MLN_RENDER_BACKEND_METAL -namespace { -const mbgl::unordered_set emptyIDSet; -} -#endif - -const mbgl::unordered_set& LayerTweaker::getPropertiesAsUniforms() const { -#if MLN_RENDER_BACKEND_METAL - return propertiesAsUniforms; -#else - return emptyIDSet; -#endif -} - -void LayerTweaker::enableOverdrawInspector(bool value) { - if (overdrawInspector != value) { - overdrawInspector = value; - permutationUpdated = true; - } -} - } // namespace mbgl diff --git a/src/mbgl/renderer/layers/background_layer_tweaker.cpp b/src/mbgl/renderer/layers/background_layer_tweaker.cpp index 384cfc742f8..7949be8ea5c 100644 --- a/src/mbgl/renderer/layers/background_layer_tweaker.cpp +++ b/src/mbgl/renderer/layers/background_layer_tweaker.cpp @@ -109,10 +109,7 @@ void BackgroundLayerTweaker::execute(LayerGroupBase& layerGroup, const PaintPara /* .scale_b = */ crossfade.toScale, /* .mix = */ crossfade.t, /* .opacity = */ evaluated.get(), - /* .overdrawInspector = */ overdrawInspector, - /* .pad1/2/3 = */ 0, - 0, - 0, + /* .pad1 = */ 0, }; uniforms.createOrUpdate(idBackgroundLayerUBOName, &layerUBO, context); } else { @@ -120,11 +117,8 @@ void BackgroundLayerTweaker::execute(LayerGroupBase& layerGroup, const PaintPara if (!backgroundLayerBuffer) { const BackgroundLayerUBO layerUBO = {/* .color = */ evaluated.get(), /* .opacity = */ evaluated.get(), - /* .overdrawInspector = */ overdrawInspector, /* .pad1/2/3 = */ 0, 0, - 0, - /* .pad4/5 = */ 0, 0}; backgroundLayerBuffer = context.createUniformBuffer(&layerUBO, sizeof(layerUBO)); } diff --git a/src/mbgl/renderer/layers/circle_layer_tweaker.cpp b/src/mbgl/renderer/layers/circle_layer_tweaker.cpp index ebeab47ecf4..17cee3ff8f5 100644 --- a/src/mbgl/renderer/layers/circle_layer_tweaker.cpp +++ b/src/mbgl/renderer/layers/circle_layer_tweaker.cpp @@ -26,9 +26,6 @@ static const StringIdentity idCircleDrawableUBOName = stringIndexer().get("Circl static const StringIdentity idCirclePaintParamsUBOName = stringIndexer().get("CirclePaintParamsUBO"); static const StringIdentity idCircleEvaluatedPropsUBOName = stringIndexer().get("CircleEvaluatedPropsUBO"); -static const StringIdentity idExpressionInputsUBOName = stringIndexer().get("ExpressionInputsUBO"); -static const StringIdentity idCirclePermutationUBOName = stringIndexer().get("CirclePermutationUBO"); - void CircleLayerTweaker::execute(LayerGroupBase& layerGroup, const PaintParameters& parameters) { auto& context = parameters.context; const auto& evaluated = static_cast(*evaluatedProperties).evaluated; @@ -56,37 +53,6 @@ void CircleLayerTweaker::execute(LayerGroupBase& layerGroup, const PaintParamete } const auto zoom = parameters.state.getZoom(); - -#if MLN_RENDER_BACKEND_METAL - if (permutationUpdated) { - const CirclePermutationUBO permutationUBO = { - /* .color = */ {/*.source=*/getAttributeSource(1), /*.expression=*/{}}, - /* .radius = */ {/*.source=*/getAttributeSource(2), /*.expression=*/{}}, - /* .blur = */ {/*.source=*/getAttributeSource(3), /*.expression=*/{}}, - /* .opacity = */ {/*.source=*/getAttributeSource(4), /*.expression=*/{}}, - /* .stroke_color = */ {/*.source=*/getAttributeSource(5), /*.expression=*/{}}, - /* .stroke_width = */ {/*.source=*/getAttributeSource(6), /*.expression=*/{}}, - /* .stroke_opacity = */ {/*.source=*/getAttributeSource(7), /*.expression=*/{}}, - /* .overdrawInspector = */ overdrawInspector, - /* .pad = */ 0, - 0, - 0, - 0}; - - if (permutationUniformBuffer) { - permutationUniformBuffer->update(&permutationUBO, sizeof(permutationUBO)); - } else { - permutationUniformBuffer = context.createUniformBuffer(&permutationUBO, sizeof(permutationUBO)); - } - - permutationUpdated = false; - } - if (!expressionUniformBuffer) { - const auto expressionUBO = buildExpressionUBO(zoom, parameters.frameCount); - expressionUniformBuffer = context.createUniformBuffer(&expressionUBO, sizeof(expressionUBO)); - } -#endif - const bool pitchWithMap = evaluated.get() == AlignmentType::Map; const bool scaleWithMap = evaluated.get() == CirclePitchScaleType::Map; @@ -133,10 +99,6 @@ void CircleLayerTweaker::execute(LayerGroupBase& layerGroup, const PaintParamete /* .padding = */ 0}; uniforms.createOrUpdate(idCircleDrawableUBOName, &drawableUBO, context); -#if MLN_RENDER_BACKEND_METAL - uniforms.addOrReplace(idExpressionInputsUBOName, expressionUniformBuffer); - uniforms.addOrReplace(idCirclePermutationUBOName, permutationUniformBuffer); -#endif // MLN_RENDER_BACKEND_METAL }); } diff --git a/src/mbgl/renderer/layers/circle_layer_tweaker.hpp b/src/mbgl/renderer/layers/circle_layer_tweaker.hpp index e78f30003be..bf58bb82d58 100644 --- a/src/mbgl/renderer/layers/circle_layer_tweaker.hpp +++ b/src/mbgl/renderer/layers/circle_layer_tweaker.hpp @@ -21,11 +21,6 @@ class CircleLayerTweaker : public LayerTweaker { protected: gfx::UniformBufferPtr paintParamsUniformBuffer; gfx::UniformBufferPtr evaluatedPropsUniformBuffer; - -#if MLN_RENDER_BACKEND_METAL - gfx::UniformBufferPtr permutationUniformBuffer; - gfx::UniformBufferPtr expressionUniformBuffer; -#endif // MLN_RENDER_BACKEND_METAL }; } // namespace mbgl diff --git a/src/mbgl/renderer/layers/fill_extrusion_layer_tweaker.cpp b/src/mbgl/renderer/layers/fill_extrusion_layer_tweaker.cpp index e9c4e51e2f6..30455c5950b 100644 --- a/src/mbgl/renderer/layers/fill_extrusion_layer_tweaker.cpp +++ b/src/mbgl/renderer/layers/fill_extrusion_layer_tweaker.cpp @@ -29,8 +29,6 @@ using namespace style; namespace { const StringIdentity idFillExtrusionDrawableUBOName = stringIndexer().get("FillExtrusionDrawableUBO"); const StringIdentity idFillExtrusionDrawablePropsUBOName = stringIndexer().get("FillExtrusionDrawablePropsUBO"); -const StringIdentity idExpressionInputsUBOName = stringIndexer().get("ExpressionInputsUBO"); -const StringIdentity idFillExtrusionPermutationUBOName = stringIndexer().get("FillExtrusionPermutationUBO"); const StringIdentity idTexImageName = stringIndexer().get("u_image"); } // namespace @@ -78,36 +76,6 @@ void FillExtrusionLayerTweaker::execute(LayerGroupBase& layerGroup, const PaintP propsBuffer->update(¶msUBO, sizeof(paramsUBO)); } -#if MLN_RENDER_BACKEND_METAL - const auto zoom = parameters.state.getZoom(); - if (permutationUpdated) { - const FillExtrusionPermutationUBO permutationUBO = { - /* .color = */ {/*.source=*/getAttributeSource(2), /*.expression=*/{}}, - /* .base = */ {/*.source=*/getAttributeSource(3), /*.expression=*/{}}, - /* .height = */ {/*.source=*/getAttributeSource(4), /*.expression=*/{}}, - /* .pattern_from = */ - {/*.source=*/getAttributeSource(4), /*.expression=*/{}}, - /* .pattern_to = */ - {/*.source=*/getAttributeSource(5), /*.expression=*/{}}, - /* .overdrawInspector = */ overdrawInspector, - /* .pad = */ 0, - 0, - 0, - 0}; - - if (permutationUniformBuffer) { - permutationUniformBuffer->update(&permutationUBO, sizeof(permutationUBO)); - } else { - permutationUniformBuffer = context.createUniformBuffer(&permutationUBO, sizeof(permutationUBO)); - } - permutationUpdated = false; - } - if (!expressionUniformBuffer) { - const auto expressionUBO = buildExpressionUBO(zoom, parameters.frameCount); - expressionUniformBuffer = context.createUniformBuffer(&expressionUBO, sizeof(expressionUBO)); - } -#endif - visitLayerGroupDrawables(layerGroup, [&](gfx::Drawable& drawable) { if (!drawable.getTileID() || !checkTweakDrawable(drawable)) { return; @@ -154,11 +122,6 @@ void FillExtrusionLayerTweaker::execute(LayerGroupBase& layerGroup, const PaintP /* .pad = */ 0}; uniforms.createOrUpdate(idFillExtrusionDrawableUBOName, &drawableUBO, context); - -#if MLN_RENDER_BACKEND_METAL - uniforms.addOrReplace(idExpressionInputsUBOName, expressionUniformBuffer); - uniforms.addOrReplace(idFillExtrusionPermutationUBOName, permutationUniformBuffer); -#endif // MLN_RENDER_BACKEND_METAL }); } diff --git a/src/mbgl/renderer/layers/fill_layer_tweaker.cpp b/src/mbgl/renderer/layers/fill_layer_tweaker.cpp index 64ff197ba80..f3f65a466c9 100644 --- a/src/mbgl/renderer/layers/fill_layer_tweaker.cpp +++ b/src/mbgl/renderer/layers/fill_layer_tweaker.cpp @@ -27,7 +27,6 @@ using namespace style; static const StringIdentity idFillDrawableUBOName = stringIndexer().get("FillDrawableUBO"); static const StringIdentity idFillDrawablePropsUBOName = stringIndexer().get("FillDrawablePropsUBO"); static const StringIdentity idFillEvaluatedPropsUBOName = stringIndexer().get("FillEvaluatedPropsUBO"); -static const StringIdentity idFillPermutationUBOName = stringIndexer().get("FillPermutationUBO"); const StringIdentity FillLayerTweaker::idFillTilePropsUBOName = stringIndexer().get("FillDrawableTilePropsUBO"); const StringIdentity FillLayerTweaker::idFillInterpolateUBOName = stringIndexer().get("FillInterpolateUBO"); @@ -36,19 +35,15 @@ const StringIdentity FillLayerTweaker::idFillOutlineInterpolateUBOName = stringI static const StringIdentity idFillOutlineDrawableUBOName = stringIndexer().get("FillOutlineDrawableUBO"); static const StringIdentity idFillOutlineEvaluatedPropsUBOName = stringIndexer().get("FillOutlineEvaluatedPropsUBO"); -static const StringIdentity idFillOutlinePermutationUBOName = stringIndexer().get("FillOutlinePermutationUBO"); static const StringIdentity idFillOutlineInterpolateUBOName = stringIndexer().get("FillOutlineInterpolateUBO"); static const StringIdentity idFillPatternDrawableUBOName = stringIndexer().get("FillPatternDrawableUBO"); -static const StringIdentity idFillPatternPermutationUBOName = stringIndexer().get("FillPatternPermutationUBO"); static const StringIdentity idFillPatternInterpolateUBOName = stringIndexer().get("FillPatternInterpolateUBO"); static const StringIdentity idFillPatternEvaluatedPropsUBOName = stringIndexer().get("FillPatternEvaluatedPropsUBO"); static const StringIdentity idFillPatternTilePropsUBOName = stringIndexer().get("FillPatternTilePropsUBO"); static const StringIdentity idFillOutlinePatternDrawableUBOName = stringIndexer().get("FillOutlinePatternDrawableUBO"); -static const StringIdentity idFillOutlinePatternPermutationUBOName = stringIndexer().get( - "FillOutlinePatternPermutationUBO"); static const StringIdentity idFillOutlinePatternInterpolateUBOName = stringIndexer().get( "FillOutlinePatternInterpolateUBO"); static const StringIdentity idFillOutlinePatternEvaluatedPropsUBOName = stringIndexer().get( @@ -56,8 +51,6 @@ static const StringIdentity idFillOutlinePatternEvaluatedPropsUBOName = stringIn static const StringIdentity idFillOutlinePatternTilePropsUBOName = stringIndexer().get( "FillOutlinePatternTilePropsUBO"); -static const StringIdentity idExpressionInputsUBOName = stringIndexer().get("ExpressionInputsUBO"); - static const StringIdentity idTexImageName = stringIndexer().get("u_image"); using namespace shaders; @@ -86,24 +79,6 @@ void FillLayerTweaker::execute(LayerGroupBase& layerGroup, const PaintParameters if (fillUniformBufferUpdated) return; fillUniformBufferUpdated = true; -#if MLN_RENDER_BACKEND_METAL - if (permutationUpdated || !fillPermutationUniformBuffer) { - const FillPermutationUBO permutationUBO = { - /* .color = */ {/*.source=*/getAttributeSource(1), /*.expression=*/{}}, - /* .opacity = */ {/*.source=*/getAttributeSource(2), /*.expression=*/{}}, - /* .overdrawInspector = */ overdrawInspector, - 0, - 0, - 0, - 0, - 0, - 0, - }; - - context.emplaceOrUpdateUniformBuffer(fillPermutationUniformBuffer, &permutationUBO); - } -#endif - if (!fillPropsUniformBuffer || propertiesUpdated) { const FillEvaluatedPropsUBO paramsUBO = { /* .color = */ evaluated.get().constantOr(FillColor::defaultValue()), @@ -120,24 +95,6 @@ void FillLayerTweaker::execute(LayerGroupBase& layerGroup, const PaintParameters if (fillOutlineUniformBufferUpdated) return; fillOutlineUniformBufferUpdated = true; -#if MLN_RENDER_BACKEND_METAL - if (permutationUpdated || !fillOutlinePermutationUniformBuffer) { - const FillOutlinePermutationUBO permutationUBO = { - /* .outline_color = */ {/*.source=*/getAttributeSource(1), - /*.expression=*/{}}, - /* .opacity = */ {/*.source=*/getAttributeSource(2), /*.expression=*/{}}, - /* .overdrawInspector = */ overdrawInspector, - 0, - 0, - 0, - 0, - 0, - 0, - }; - context.emplaceOrUpdateUniformBuffer(fillOutlinePermutationUniformBuffer, &permutationUBO); - } -#endif - if (!fillOutlinePropsUniformBuffer || propertiesUpdated) { const FillOutlineEvaluatedPropsUBO paramsUBO = { /* .outline_color = */ evaluated.get().constantOr(FillOutlineColor::defaultValue()), @@ -154,25 +111,6 @@ void FillLayerTweaker::execute(LayerGroupBase& layerGroup, const PaintParameters if (fillPatternUniformBufferUpdated) return; fillPatternUniformBufferUpdated = true; -#if MLN_RENDER_BACKEND_METAL - if (permutationUpdated || !fillPatternPermutationUniformBuffer) { - const FillPatternPermutationUBO permutationUBO = { - /* .pattern_from = */ {/*.source=*/getAttributeSource(1), - /*.expression=*/{}}, - /* .pattern_to = */ - {/*.source=*/getAttributeSource(2), /*.expression=*/{}}, - /* .opacity = */ - {/*.source=*/getAttributeSource(3), /*.expression=*/{}}, - /* .overdrawInspector = */ overdrawInspector, - 0, - 0, - 0, - 0, - }; - context.emplaceOrUpdateUniformBuffer(fillPatternPermutationUniformBuffer, &permutationUBO); - } -#endif - if (!fillPatternPropsUniformBuffer || propertiesUpdated) { const FillPatternEvaluatedPropsUBO paramsUBO = { /* .opacity = */ evaluated.get().constantOr(FillOpacity::defaultValue()), @@ -188,25 +126,6 @@ void FillLayerTweaker::execute(LayerGroupBase& layerGroup, const PaintParameters if (fillOutlinePatternUniformBufferUpdated) return; fillOutlinePatternUniformBufferUpdated = true; -#if MLN_RENDER_BACKEND_METAL - if (permutationUpdated || !fillOutlinePatternPermutationUniformBuffer) { - const FillOutlinePatternPermutationUBO permutationUBO = { - /* .pattern_from = */ {/*.source=*/getAttributeSource(1), - /*.expression=*/{}}, - /* .pattern_to = */ - {/*.source=*/getAttributeSource(2), /*.expression=*/{}}, - /* .opacity = */ - {/*.source=*/getAttributeSource(3), /*.expression=*/{}}, - /* .overdrawInspector = */ overdrawInspector, - 0, - 0, - 0, - 0, - }; - context.emplaceOrUpdateUniformBuffer(fillOutlinePatternPermutationUniformBuffer, &permutationUBO); - } -#endif - if (!fillOutlinePatternPropsUniformBuffer || propertiesUpdated) { const FillOutlinePatternEvaluatedPropsUBO paramsUBO = { /* .opacity = */ evaluated.get().constantOr(FillOpacity::defaultValue()), @@ -218,12 +137,6 @@ void FillLayerTweaker::execute(LayerGroupBase& layerGroup, const PaintParameters } }; -#if MLN_RENDER_BACKEND_METAL - const auto zoom = parameters.state.getZoom(); - const auto expressionUBO = buildExpressionUBO(zoom, parameters.frameCount); - context.emplaceOrUpdateUniformBuffer(expressionUniformBuffer, &expressionUBO); -#endif - const auto& translation = evaluated.get(); const auto anchor = evaluated.get(); @@ -268,10 +181,6 @@ void FillLayerTweaker::execute(LayerGroupBase& layerGroup, const PaintParameters const FillDrawableUBO drawableUBO = {/*.matrix=*/util::cast(matrix)}; uniforms.createOrUpdate(idFillDrawableUBOName, &drawableUBO, context); - -#if MLN_RENDER_BACKEND_METAL - uniforms.addOrReplace(idFillPermutationUBOName, fillPermutationUniformBuffer); -#endif // MLN_RENDER_BACKEND_METAL } else if (uniforms.get(idFillOutlineInterpolateUBOName)) { UpdateFillOutlineUniformBuffers(); @@ -283,10 +192,6 @@ void FillLayerTweaker::execute(LayerGroupBase& layerGroup, const PaintParameters /* pad1 */ 0, /* pad2 */ 0}; uniforms.createOrUpdate(idFillOutlineDrawableUBOName, &drawableUBO, context); - -#if MLN_RENDER_BACKEND_METAL - uniforms.addOrReplace(idFillOutlinePermutationUBOName, fillOutlinePermutationUniformBuffer); -#endif // MLN_RENDER_BACKEND_METAL } else if (uniforms.get(idFillPatternInterpolateUBOName)) { UpdateFillPatternUniformBuffers(); @@ -302,10 +207,6 @@ void FillLayerTweaker::execute(LayerGroupBase& layerGroup, const PaintParameters 0, }; uniforms.createOrUpdate(idFillPatternDrawableUBOName, &drawableUBO, context); - -#if MLN_RENDER_BACKEND_METAL - uniforms.addOrReplace(idFillPatternPermutationUBOName, fillPatternPermutationUniformBuffer); -#endif // MLN_RENDER_BACKEND_METAL } else if (uniforms.get(idFillOutlinePatternInterpolateUBOName)) { UpdateFillOutlinePatternUniformBuffers(); @@ -320,18 +221,9 @@ void FillLayerTweaker::execute(LayerGroupBase& layerGroup, const PaintParameters /*.texsize=*/{static_cast(textureSize.width), static_cast(textureSize.height)}, }; uniforms.createOrUpdate(idFillOutlinePatternDrawableUBOName, &drawableUBO, context); - -#if MLN_RENDER_BACKEND_METAL - uniforms.addOrReplace(idFillOutlinePatternPermutationUBOName, fillOutlinePatternPermutationUniformBuffer); -#endif // MLN_RENDER_BACKEND_METAL } - -#if MLN_RENDER_BACKEND_METAL - uniforms.addOrReplace(idExpressionInputsUBOName, expressionUniformBuffer); -#endif // MLN_RENDER_BACKEND_METAL }); - permutationUpdated = false; propertiesUpdated = false; } diff --git a/src/mbgl/renderer/layers/fill_layer_tweaker.hpp b/src/mbgl/renderer/layers/fill_layer_tweaker.hpp index cd11ba0fa87..f2fb7583f45 100644 --- a/src/mbgl/renderer/layers/fill_layer_tweaker.hpp +++ b/src/mbgl/renderer/layers/fill_layer_tweaker.hpp @@ -29,14 +29,6 @@ class FillLayerTweaker : public LayerTweaker { gfx::UniformBufferPtr fillOutlinePropsUniformBuffer; gfx::UniformBufferPtr fillPatternPropsUniformBuffer; gfx::UniformBufferPtr fillOutlinePatternPropsUniformBuffer; - - gfx::UniformBufferPtr fillPermutationUniformBuffer; - gfx::UniformBufferPtr fillOutlinePermutationUniformBuffer; - gfx::UniformBufferPtr fillPatternPermutationUniformBuffer; - gfx::UniformBufferPtr fillOutlinePatternPermutationUniformBuffer; -#if MLN_RENDER_BACKEND_METAL - gfx::UniformBufferPtr expressionUniformBuffer; -#endif // MLN_RENDER_BACKEND_METAL }; } // namespace mbgl diff --git a/src/mbgl/renderer/layers/heatmap_layer_tweaker.cpp b/src/mbgl/renderer/layers/heatmap_layer_tweaker.cpp index 7fd4e6b22eb..9042a589067 100644 --- a/src/mbgl/renderer/layers/heatmap_layer_tweaker.cpp +++ b/src/mbgl/renderer/layers/heatmap_layer_tweaker.cpp @@ -22,8 +22,6 @@ using namespace shaders; static const StringIdentity idHeatmapDrawableUBOName = stringIndexer().get("HeatmapDrawableUBO"); static const StringIdentity idHeatmapEvaluatedPropsUBOName = stringIndexer().get("HeatmapEvaluatedPropsUBO"); -static const StringIdentity idHeatmapPermutationUBOName = stringIndexer().get("HeatmapPermutationUBO"); -static const StringIdentity idExpressionInputsUBOName = stringIndexer().get("ExpressionInputsUBO"); void HeatmapLayerTweaker::execute(LayerGroupBase& layerGroup, const PaintParameters& parameters) { auto& context = parameters.context; @@ -40,33 +38,6 @@ void HeatmapLayerTweaker::execute(LayerGroupBase& layerGroup, const PaintParamet const auto zoom = parameters.state.getZoom(); -#if MLN_RENDER_BACKEND_METAL - if (permutationUpdated) { - const HeatmapPermutationUBO permutationUBO = { - /* .weight = */ {/*.source=*/getAttributeSource(1), /*.expression=*/{}}, - /* .radius = */ {/*.source=*/getAttributeSource(2), /*.expression=*/{}}, - /* .overdrawInspector = */ overdrawInspector, - /* .pad1/2/3 = */ 0, - 0, - 0, - /* .pad4/5/6 = */ 0, - 0, - 0}; - - if (permutationUniformBuffer) { - permutationUniformBuffer->update(&permutationUBO, sizeof(permutationUBO)); - } else { - permutationUniformBuffer = context.createUniformBuffer(&permutationUBO, sizeof(permutationUBO)); - } - - permutationUpdated = false; - } - if (!expressionUniformBuffer) { - const auto expressionUBO = buildExpressionUBO(zoom, parameters.frameCount); - expressionUniformBuffer = context.createUniformBuffer(&expressionUBO, sizeof(expressionUBO)); - } -#endif - if (!evaluatedPropsUniformBuffer) { const HeatmapEvaluatedPropsUBO evaluatedPropsUBO = { /* .weight = */ evaluated.get().constantOr(HeatmapWeight::defaultValue()), @@ -97,11 +68,6 @@ void HeatmapLayerTweaker::execute(LayerGroupBase& layerGroup, const PaintParamet /* .padding = */ {0}}; uniforms.createOrUpdate(idHeatmapDrawableUBOName, &drawableUBO, context); - -#if MLN_RENDER_BACKEND_METAL - uniforms.addOrReplace(idHeatmapPermutationUBOName, permutationUniformBuffer); - uniforms.addOrReplace(idExpressionInputsUBOName, expressionUniformBuffer); -#endif // MLN_RENDER_BACKEND_METAL }); } diff --git a/src/mbgl/renderer/layers/heatmap_texture_layer_tweaker.cpp b/src/mbgl/renderer/layers/heatmap_texture_layer_tweaker.cpp index 98752c45d49..3f622b90fcb 100644 --- a/src/mbgl/renderer/layers/heatmap_texture_layer_tweaker.cpp +++ b/src/mbgl/renderer/layers/heatmap_texture_layer_tweaker.cpp @@ -42,10 +42,8 @@ void HeatmapTextureLayerTweaker::execute(LayerGroupBase& layerGroup, const Paint /* .matrix = */ util::cast(viewportMat), /* .world = */ {static_cast(size.width), static_cast(size.height)}, /* .opacity = */ evaluated.get(), - /* .overdrawInspector = */ overdrawInspector, /* .pad1 = */ 0, - /* .pad2 = */ 0, - /* .pad3 = */ 0}; + }; drawable.mutableUniformBuffers().createOrUpdate( idHeatmapTextureDrawableUBOName, &drawableUBO, parameters.context); diff --git a/src/mbgl/renderer/layers/hillshade_layer_tweaker.cpp b/src/mbgl/renderer/layers/hillshade_layer_tweaker.cpp index e4e817c284f..f14787e6429 100644 --- a/src/mbgl/renderer/layers/hillshade_layer_tweaker.cpp +++ b/src/mbgl/renderer/layers/hillshade_layer_tweaker.cpp @@ -65,14 +65,7 @@ void HillshadeLayerTweaker::execute(LayerGroupBase& layerGroup, const PaintParam tileID, parameters, {0.f, 0.f}, TranslateAnchorType::Viewport, false, false, true); HillshadeDrawableUBO drawableUBO = {/* .matrix = */ util::cast(matrix), /* .latrange = */ getLatRange(tileID), - /* .light = */ getLight(parameters, evaluated), - /* .overdrawInspector = */ overdrawInspector, - /* .pad1/2/3 = */ 0, - 0, - 0, - /* .pad4/5/6 = */ 0, - 0, - 0}; + /* .light = */ getLight(parameters, evaluated)}; drawable.mutableUniformBuffers().createOrUpdate(idHillshadeDrawableUBOName, &drawableUBO, parameters.context); }); diff --git a/src/mbgl/renderer/layers/hillshade_prepare_layer_tweaker.cpp b/src/mbgl/renderer/layers/hillshade_prepare_layer_tweaker.cpp index d09ae4d47d9..80aa6ff29af 100644 --- a/src/mbgl/renderer/layers/hillshade_prepare_layer_tweaker.cpp +++ b/src/mbgl/renderer/layers/hillshade_prepare_layer_tweaker.cpp @@ -55,14 +55,7 @@ void HillshadePrepareLayerTweaker::execute(LayerGroupBase& layerGroup, const Pai /* .unpack = */ getUnpackVector(drawableData.encoding), /* .dimension = */ {static_cast(drawableData.stride), static_cast(drawableData.stride)}, /* .zoom = */ static_cast(tileID.canonical.z), - /* .maxzoom = */ static_cast(drawableData.maxzoom), - /* .overdrawInspector = */ overdrawInspector, - /* .pad1/2/3 = */ 0, - 0, - 0, - /* .pad4/5/6 = */ 0, - 0, - 0}; + /* .maxzoom = */ static_cast(drawableData.maxzoom)}; drawable.mutableUniformBuffers().createOrUpdate( idHillshadePrepareDrawableUBOName, &drawableUBO, parameters.context); diff --git a/src/mbgl/renderer/layers/line_layer_tweaker.cpp b/src/mbgl/renderer/layers/line_layer_tweaker.cpp index 6b8608cad74..3136dd8d9ae 100644 --- a/src/mbgl/renderer/layers/line_layer_tweaker.cpp +++ b/src/mbgl/renderer/layers/line_layer_tweaker.cpp @@ -35,9 +35,6 @@ static const StringIdentity idLineSDFUBOName = stringIndexer().get("LineSDFUBO") static const StringIdentity idLineSDFPropertiesUBOName = stringIndexer().get("LineSDFPropertiesUBO"); static const StringIdentity idTexImageName = stringIndexer().get("u_image"); -static const StringIdentity idExpressionInputsUBOName = stringIndexer().get("ExpressionInputsUBO"); -static const StringIdentity idLinePermutationUBOName = stringIndexer().get("LinePermutationUBO"); - void LineLayerTweaker::execute(LayerGroupBase& layerGroup, const PaintParameters& parameters) { auto& context = parameters.context; const auto& evaluated = static_cast(*evaluatedProperties).evaluated; @@ -110,13 +107,6 @@ void LineLayerTweaker::execute(LayerGroupBase& layerGroup, const PaintParameters return lineSDFPropertiesBuffer; }; -#if MLN_RENDER_BACKEND_METAL - if (!expressionUniformBuffer) { - const auto expressionUBO = buildExpressionUBO(zoom, parameters.frameCount); - expressionUniformBuffer = context.createUniformBuffer(&expressionUBO, sizeof(expressionUBO)); - } -#endif - visitLayerGroupDrawables(layerGroup, [&](gfx::Drawable& drawable) { const auto shader = drawable.getShader(); if (!drawable.getTileID() || !shader || !checkTweakDrawable(drawable)) { @@ -145,29 +135,6 @@ void LineLayerTweaker::execute(LayerGroupBase& layerGroup, const PaintParameters // properties UBO uniforms.addOrReplace(idLinePropertiesUBOName, getLinePropsBuffer()); - -#if MLN_RENDER_BACKEND_METAL - if (permutationUpdated) { - const LinePermutationUBO permutationUBO = { - /* .color = */ {/*.source=*/getAttributeSource(2), /*.expression=*/{}}, - /* .blur = */ {/*.source=*/getAttributeSource(3), /*.expression=*/{}}, - /* .opacity = */ {/*.source=*/getAttributeSource(4), /*.expression=*/{}}, - /* .gapwidth = */ {/*.source=*/getAttributeSource(5), /*.expression=*/{}}, - /* .offset = */ {/*.source=*/getAttributeSource(6), /*.expression=*/{}}, - /* .width = */ {/*.source=*/getAttributeSource(7), /*.expression=*/{}}, - /* .floorwidth = */ {/*.source=*/AttributeSource::Constant, /*.expression=*/{}}, - /* .pattern_from = */ {/*.source=*/AttributeSource::Constant, /*.expression=*/{}}, - /* .pattern_to = */ {/*.source=*/AttributeSource::Constant, /*.expression=*/{}}, - /* .overdrawInspector = */ overdrawInspector, - /* .pad = */ 0, - 0, - 0, - 0}; - - context.emplaceOrUpdateUniformBuffer(permutationUniformBuffer, &permutationUBO); - permutationUpdated = false; - } -#endif } break; case LineType::Gradient: { @@ -180,34 +147,6 @@ void LineLayerTweaker::execute(LayerGroupBase& layerGroup, const PaintParameters // properties UBO uniforms.addOrReplace(idLineGradientPropertiesUBOName, getLineGradientPropsBuffer()); - -#if MLN_RENDER_BACKEND_METAL - if (permutationUpdated) { - const LinePermutationUBO permutationUBO = { - /* .color = */ {/*.source=*/AttributeSource::Constant, /*.expression=*/{}}, - /* .blur = */ - {/*.source=*/getAttributeSource(2), /*.expression=*/{}}, - /* .opacity = */ - {/*.source=*/getAttributeSource(3), /*.expression=*/{}}, - /* .gapwidth = */ - {/*.source=*/getAttributeSource(4), /*.expression=*/{}}, - /* .offset = */ - {/*.source=*/getAttributeSource(5), /*.expression=*/{}}, - /* .width = */ - {/*.source=*/getAttributeSource(6), /*.expression=*/{}}, - /* .floorwidth = */ {/*.source=*/AttributeSource::Constant, /*.expression=*/{}}, - /* .pattern_from = */ {/*.source=*/AttributeSource::Constant, /*.expression=*/{}}, - /* .pattern_to = */ {/*.source=*/AttributeSource::Constant, /*.expression=*/{}}, - /* .overdrawInspector = */ overdrawInspector, - /* .pad = */ 0, - 0, - 0, - 0}; - - context.emplaceOrUpdateUniformBuffer(permutationUniformBuffer, &permutationUBO); - permutationUpdated = false; - } -#endif } break; case LineType::Pattern: { @@ -234,35 +173,6 @@ void LineLayerTweaker::execute(LayerGroupBase& layerGroup, const PaintParameters // properties UBO uniforms.addOrReplace(idLinePatternPropertiesUBOName, getLinePatternPropsBuffer()); -#if MLN_RENDER_BACKEND_METAL - if (permutationUpdated) { - const LinePermutationUBO permutationUBO = { - /* .color = */ {/*.source=*/AttributeSource::Constant, /*.expression=*/{}}, - /* .blur = */ - {/*.source=*/getAttributeSource(2), /*.expression=*/{}}, - /* .opacity = */ - {/*.source=*/getAttributeSource(3), /*.expression=*/{}}, - /* .gapwidth = */ - {/*.source=*/getAttributeSource(4), /*.expression=*/{}}, - /* .offset = */ - {/*.source=*/getAttributeSource(5), /*.expression=*/{}}, - /* .width = */ - {/*.source=*/getAttributeSource(6), /*.expression=*/{}}, - /* .floorwidth = */ {/*.source=*/AttributeSource::Constant, /*.expression=*/{}}, - /* .pattern_from = */ - {/*.source=*/getAttributeSource(7), /*.expression=*/{}}, - /* .pattern_to = */ - {/*.source=*/getAttributeSource(8), /*.expression=*/{}}, - /* .overdrawInspector = */ overdrawInspector, - /* .pad = */ 0, - 0, - 0, - 0}; - - context.emplaceOrUpdateUniformBuffer(permutationUniformBuffer, &permutationUBO); - permutationUpdated = false; - } -#endif } break; case LineType::SDF: { @@ -309,32 +219,6 @@ void LineLayerTweaker::execute(LayerGroupBase& layerGroup, const PaintParameters // properties UBO uniforms.addOrReplace(idLineSDFPropertiesUBOName, getLineSDFPropsBuffer()); } - -#if MLN_RENDER_BACKEND_METAL - if (permutationUpdated) { - const LinePermutationUBO permutationUBO = { - /* .color = */ {/*.source=*/getAttributeSource(2), /*.expression=*/{}}, - /* .blur = */ {/*.source=*/getAttributeSource(3), /*.expression=*/{}}, - /* .opacity = */ - {/*.source=*/getAttributeSource(4), /*.expression=*/{}}, - /* .gapwidth = */ - {/*.source=*/getAttributeSource(5), /*.expression=*/{}}, - /* .offset = */ {/*.source=*/getAttributeSource(6), /*.expression=*/{}}, - /* .width = */ {/*.source=*/getAttributeSource(7), /*.expression=*/{}}, - /* .floorwidth = */ - {/*.source=*/getAttributeSource(8), /*.expression=*/{}}, - /* .pattern_from = */ {/*.source=*/AttributeSource::Constant, /*.expression=*/{}}, - /* .pattern_to = */ {/*.source=*/AttributeSource::Constant, /*.expression=*/{}}, - /* .overdrawInspector = */ overdrawInspector, - /* .pad = */ 0, - 0, - 0, - 0}; - - context.emplaceOrUpdateUniformBuffer(permutationUniformBuffer, &permutationUBO); - permutationUpdated = false; - } -#endif } break; default: { @@ -343,11 +227,6 @@ void LineLayerTweaker::execute(LayerGroupBase& layerGroup, const PaintParameters "LineLayerTweaker: unknown line type: "s + std::to_string(mbgl::underlying_type(type))); } break; } - -#if MLN_RENDER_BACKEND_METAL - uniforms.addOrReplace(idExpressionInputsUBOName, expressionUniformBuffer); - uniforms.addOrReplace(idLinePermutationUBOName, permutationUniformBuffer); -#endif // MLN_RENDER_BACKEND_METAL }); } diff --git a/src/mbgl/renderer/layers/raster_layer_tweaker.cpp b/src/mbgl/renderer/layers/raster_layer_tweaker.cpp index 922d40047d9..80d0caed14f 100644 --- a/src/mbgl/renderer/layers/raster_layer_tweaker.cpp +++ b/src/mbgl/renderer/layers/raster_layer_tweaker.cpp @@ -80,9 +80,6 @@ void RasterLayerTweaker::execute([[maybe_unused]] LayerGroupBase& layerGroup, /*.brightness_high = */ evaluated.get(), /*.saturation_factor = */ saturationFactor(evaluated.get()), /*.contrast_factor = */ contrastFactor(evaluated.get()), - /*.overdrawInspector = */ overdrawInspector, - 0, - 0, 0, 0}; auto& uniforms = drawable.mutableUniformBuffers(); diff --git a/src/mbgl/renderer/layers/render_background_layer.cpp b/src/mbgl/renderer/layers/render_background_layer.cpp index 293ee2175e0..5fecbcd64d4 100644 --- a/src/mbgl/renderer/layers/render_background_layer.cpp +++ b/src/mbgl/renderer/layers/render_background_layer.cpp @@ -213,7 +213,7 @@ static constexpr std::string_view BackgroundPatternShaderName = "BackgroundPatte void RenderBackgroundLayer::update(gfx::ShaderRegistry& shaders, gfx::Context& context, const TransformState& state, - const std::shared_ptr& updateParameters, + const std::shared_ptr&, [[maybe_unused]] const RenderTree& renderTree, [[maybe_unused]] UniqueChangeRequestVec& changes) { std::unique_lock guard(mutex); @@ -258,7 +258,6 @@ void RenderBackgroundLayer::update(gfx::ShaderRegistry& shaders, layerTweaker = std::make_shared(getID(), evaluatedProperties); layerGroup->addLayerTweaker(layerTweaker); } - layerTweaker->enableOverdrawInspector(!!(updateParameters->debugOptions & MapDebugOptions::Overdraw)); if (!hasPattern && !plainShader) { plainShader = context.getGenericShader(shaders, std::string(BackgroundPlainShaderName)); diff --git a/src/mbgl/renderer/layers/render_circle_layer.cpp b/src/mbgl/renderer/layers/render_circle_layer.cpp index 955d4b9ab5c..b9a1a4371aa 100644 --- a/src/mbgl/renderer/layers/render_circle_layer.cpp +++ b/src/mbgl/renderer/layers/render_circle_layer.cpp @@ -88,12 +88,6 @@ void RenderCircleLayer::evaluate(const PropertyEvaluationParameters& parameters) #if MLN_DRAWABLE_RENDERER if (layerGroup) { auto newTweaker = std::make_shared(getID(), evaluatedProperties); - - // propertiesAsUniforms isn't recalculated every update, so carry it over - if (layerTweaker) { - newTweaker->setPropertiesAsUniforms(layerTweaker->getPropertiesAsUniforms()); - } - replaceTweaker(layerTweaker, std::move(newTweaker), {layerGroup}); } #endif // MLN_DRAWABLE_RENDERER @@ -281,7 +275,7 @@ using namespace shaders; void RenderCircleLayer::update(gfx::ShaderRegistry& shaders, gfx::Context& context, const TransformState& state, - [[maybe_unused]] const std::shared_ptr& updateParameters, + [[maybe_unused]] const std::shared_ptr&, [[maybe_unused]] const RenderTree& renderTree, UniqueChangeRequestVec& changes) { std::unique_lock guard(mutex); @@ -300,12 +294,10 @@ void RenderCircleLayer::update(gfx::ShaderRegistry& shaders, } } auto* tileLayerGroup = static_cast(layerGroup.get()); - if (!layerTweaker) { layerTweaker = std::make_shared(getID(), evaluatedProperties); layerGroup->addLayerTweaker(layerTweaker); } - layerTweaker->enableOverdrawInspector(!!(updateParameters->debugOptions & MapDebugOptions::Overdraw)); if (!circleShaderGroup) { circleShaderGroup = shaders.getShaderGroup(CircleShaderGroupName); @@ -398,10 +390,6 @@ void RenderCircleLayer::update(gfx::ShaderRegistry& shaders, continue; } - if (layerTweaker) { - layerTweaker->setPropertiesAsUniforms(propertiesAsUniforms); - } - if (const auto& attr = circleVertexAttrs->add(idVertexAttribName)) { attr->setSharedRawData(bucket.sharedVertices, offsetof(CircleLayoutVertex, a1), diff --git a/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp b/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp index 02ef9f418f8..f3aebbb2a15 100644 --- a/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp +++ b/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp @@ -77,12 +77,6 @@ void RenderFillExtrusionLayer::evaluate(const PropertyEvaluationParameters& para #if MLN_DRAWABLE_RENDERER if (layerGroup) { auto newTweaker = std::make_shared(getID(), evaluatedProperties); - - // propertiesAsUniforms isn't recalculated every update, so carry it over - if (layerTweaker) { - newTweaker->setPropertiesAsUniforms(layerTweaker->getPropertiesAsUniforms()); - } - replaceTweaker(layerTweaker, std::move(newTweaker), {layerGroup}); } #endif // MLN_DRAWABLE_RENDERER @@ -283,7 +277,7 @@ bool RenderFillExtrusionLayer::queryIntersectsFeature(const GeometryCoordinates& void RenderFillExtrusionLayer::update(gfx::ShaderRegistry& shaders, gfx::Context& context, const TransformState& state, - const std::shared_ptr& updateParameters, + const std::shared_ptr&, const RenderTree& /*renderTree*/, UniqueChangeRequestVec& changes) { if (!renderTiles || renderTiles->empty() || passes == RenderPass::None) { @@ -305,8 +299,6 @@ void RenderFillExtrusionLayer::update(gfx::ShaderRegistry& shaders, layerGroup->addLayerTweaker(layerTweaker); } - layerTweaker->enableOverdrawInspector(!!(updateParameters->debugOptions & MapDebugOptions::Overdraw)); - if (!fillExtrusionGroup) { fillExtrusionGroup = shaders.getShaderGroup("FillExtrusionShader"); } @@ -433,10 +425,6 @@ void RenderFillExtrusionLayer::update(gfx::ShaderRegistry& shaders, continue; } - if (layerTweaker) { - layerTweaker->setPropertiesAsUniforms(propertiesAsUniforms); - } - // The non-pattern path in `render()` only uses two-pass rendering if there's translucency. // The pattern path always uses two passes. const auto doDepthPass = (!opaque || hasPattern); diff --git a/src/mbgl/renderer/layers/render_fill_layer.cpp b/src/mbgl/renderer/layers/render_fill_layer.cpp index 7b07ffc13ec..8c61a5853e5 100644 --- a/src/mbgl/renderer/layers/render_fill_layer.cpp +++ b/src/mbgl/renderer/layers/render_fill_layer.cpp @@ -405,7 +405,7 @@ class OutlineDrawableTweaker : public gfx::DrawableTweaker { void RenderFillLayer::update(gfx::ShaderRegistry& shaders, gfx::Context& context, const TransformState& state, - const std::shared_ptr& updateParameters, + const std::shared_ptr&, [[maybe_unused]] const RenderTree& renderTree, [[maybe_unused]] UniqueChangeRequestVec& changes) { std::unique_lock guard(mutex); @@ -441,7 +441,6 @@ void RenderFillLayer::update(gfx::ShaderRegistry& shaders, layerTweaker = std::make_shared(getID(), evaluatedProperties); layerGroup->addLayerTweaker(layerTweaker); } - layerTweaker->enableOverdrawInspector(!!(updateParameters->debugOptions & MapDebugOptions::Overdraw)); if (!fillShaderGroup) { fillShaderGroup = shaders.getShaderGroup(std::string(FillShaderName)); @@ -627,10 +626,6 @@ void RenderFillLayer::update(gfx::ShaderRegistry& shaders, vertexAttrs->readDataDrivenPaintProperties( binders, evaluated, propertiesAsUniforms); - if (layerTweaker) { - layerTweaker->setPropertiesAsUniforms(propertiesAsUniforms); - } - const auto vertexCount = bucket.vertices.elements(); if (const auto& attr = vertexAttrs->add(idPosAttribName)) { attr->setSharedRawData(bucket.sharedVertices, diff --git a/src/mbgl/renderer/layers/render_heatmap_layer.cpp b/src/mbgl/renderer/layers/render_heatmap_layer.cpp index b62c5dd7a08..cc69d3e0994 100644 --- a/src/mbgl/renderer/layers/render_heatmap_layer.cpp +++ b/src/mbgl/renderer/layers/render_heatmap_layer.cpp @@ -71,11 +71,6 @@ void RenderHeatmapLayer::evaluate(const PropertyEvaluationParameters& parameters if (renderTarget) { if (auto tileLayerGroup = renderTarget->getLayerGroup(0)) { auto newTweaker = std::make_shared(getID(), evaluatedProperties); - - if (layerTweaker) { - newTweaker->setPropertiesAsUniforms(layerTweaker->getPropertiesAsUniforms()); - } - replaceTweaker(layerTweaker, std::move(newTweaker), {std::move(tileLayerGroup)}); } } @@ -290,7 +285,7 @@ using namespace shaders; void RenderHeatmapLayer::update(gfx::ShaderRegistry& shaders, gfx::Context& context, const TransformState& state, - const std::shared_ptr& updateParameters, + const std::shared_ptr&, [[maybe_unused]] const RenderTree& renderTree, UniqueChangeRequestVec& changes) { std::unique_lock guard(mutex); @@ -338,7 +333,6 @@ void RenderHeatmapLayer::update(gfx::ShaderRegistry& shaders, layerTweaker = std::make_shared(getID(), evaluatedProperties); tileLayerGroup->addLayerTweaker(layerTweaker); } - layerTweaker->enableOverdrawInspector(!!(updateParameters->debugOptions & MapDebugOptions::Overdraw)); std::unique_ptr heatmapBuilder; constexpr auto renderPass = RenderPass::Translucent; @@ -477,10 +471,6 @@ void RenderHeatmapLayer::update(gfx::ShaderRegistry& shaders, } } - if (layerTweaker && propertiesAsUniforms) { - layerTweaker->setPropertiesAsUniforms(*propertiesAsUniforms); - } - // Set up texture layer group if (!layerGroup) { if (auto layerGroup_ = context.createLayerGroup(layerIndex, /*initialCapacity=*/1, getID())) { @@ -496,7 +486,6 @@ void RenderHeatmapLayer::update(gfx::ShaderRegistry& shaders, textureTweaker = std::make_shared(getID(), evaluatedProperties); layerGroup->addLayerTweaker(textureTweaker); } - textureTweaker->enableOverdrawInspector(!!(updateParameters->debugOptions & MapDebugOptions::Overdraw)); if (!heatmapTextureShader) { heatmapTextureShader = context.getGenericShader(shaders, HeatmapTextureShaderGroupName); diff --git a/src/mbgl/renderer/layers/render_hillshade_layer.cpp b/src/mbgl/renderer/layers/render_hillshade_layer.cpp index b2853951648..a0ab12ac628 100644 --- a/src/mbgl/renderer/layers/render_hillshade_layer.cpp +++ b/src/mbgl/renderer/layers/render_hillshade_layer.cpp @@ -306,7 +306,7 @@ static const StringIdentity idTexImageName = stringIndexer().get("u_image"); void RenderHillshadeLayer::update(gfx::ShaderRegistry& shaders, gfx::Context& context, [[maybe_unused]] const TransformState& state, - const std::shared_ptr& updateParameters, + const std::shared_ptr&, [[maybe_unused]] const RenderTree& renderTree, UniqueChangeRequestVec& changes) { std::unique_lock guard(mutex); @@ -331,7 +331,6 @@ void RenderHillshadeLayer::update(gfx::ShaderRegistry& shaders, layerTweaker = std::make_shared(getID(), evaluatedProperties); layerGroup->addLayerTweaker(layerTweaker); } - layerTweaker->enableOverdrawInspector(!!(updateParameters->debugOptions & MapDebugOptions::Overdraw)); if (!hillshadePrepareShader) { hillshadePrepareShader = context.getGenericShader(shaders, HillshadePrepareShaderGroupName); diff --git a/src/mbgl/renderer/layers/render_line_layer.cpp b/src/mbgl/renderer/layers/render_line_layer.cpp index a3f939bff12..2975eb084b4 100644 --- a/src/mbgl/renderer/layers/render_line_layer.cpp +++ b/src/mbgl/renderer/layers/render_line_layer.cpp @@ -83,12 +83,6 @@ void RenderLineLayer::evaluate(const PropertyEvaluationParameters& parameters) { #if MLN_DRAWABLE_RENDERER if (layerGroup) { auto newTweaker = std::make_shared(getID(), evaluatedProperties); - - // propertiesAsUniforms isn't recalculated every update, so carry it over - if (layerTweaker) { - newTweaker->setPropertiesAsUniforms(layerTweaker->getPropertiesAsUniforms()); - } - replaceTweaker(layerTweaker, std::move(newTweaker), {layerGroup}); } #endif @@ -366,7 +360,7 @@ static const StringIdentity idLineImageUniformName = stringIndexer().get("u_imag void RenderLineLayer::update(gfx::ShaderRegistry& shaders, gfx::Context& context, const TransformState& state, - const std::shared_ptr& updateParameters, + const std::shared_ptr&, [[maybe_unused]] const RenderTree& renderTree, [[maybe_unused]] UniqueChangeRequestVec& changes) { std::unique_lock guard(mutex); @@ -390,7 +384,6 @@ void RenderLineLayer::update(gfx::ShaderRegistry& shaders, layerTweaker = std::make_shared(getID(), evaluatedProperties); layerGroup->addLayerTweaker(layerTweaker); } - layerTweaker->enableOverdrawInspector(!!(updateParameters->debugOptions & MapDebugOptions::Overdraw)); if (!lineShaderGroup) { lineShaderGroup = shaders.getShaderGroup("LineShader"); @@ -599,10 +592,6 @@ void RenderLineLayer::update(gfx::ShaderRegistry& shaders, continue; } - if (layerTweaker) { - layerTweaker->setPropertiesAsUniforms(propertiesAsUniforms); - } - auto builder = createLineBuilder("lineSDF", std::move(shader)); // vertices, attributes and segments @@ -647,10 +636,6 @@ void RenderLineLayer::update(gfx::ShaderRegistry& shaders, continue; } - if (layerTweaker) { - layerTweaker->setPropertiesAsUniforms(propertiesAsUniforms); - } - auto builder = createLineBuilder("linePattern", std::move(shader)); // vertices and attributes @@ -704,10 +689,6 @@ void RenderLineLayer::update(gfx::ShaderRegistry& shaders, continue; } - if (layerTweaker) { - layerTweaker->setPropertiesAsUniforms(propertiesAsUniforms); - } - auto builder = createLineBuilder("lineGradient", std::move(shader)); // vertices and attributes @@ -760,10 +741,6 @@ void RenderLineLayer::update(gfx::ShaderRegistry& shaders, continue; } - if (layerTweaker) { - layerTweaker->setPropertiesAsUniforms(propertiesAsUniforms); - } - auto builder = createLineBuilder("line", std::move(shader)); // vertices, attributes and segments diff --git a/src/mbgl/renderer/layers/render_raster_layer.cpp b/src/mbgl/renderer/layers/render_raster_layer.cpp index cd4053bf49e..e0831cddf44 100644 --- a/src/mbgl/renderer/layers/render_raster_layer.cpp +++ b/src/mbgl/renderer/layers/render_raster_layer.cpp @@ -254,7 +254,7 @@ static const StringIdentity idTexImage1Name = stringIndexer().get("u_image1"); void RenderRasterLayer::update(gfx::ShaderRegistry& shaders, gfx::Context& context, const TransformState& /*state*/, - const std::shared_ptr& updateParameters, + const std::shared_ptr&, [[maybe_unused]] const RenderTree& renderTree, [[maybe_unused]] UniqueChangeRequestVec& changes) { std::unique_lock guard(mutex); @@ -290,7 +290,6 @@ void RenderRasterLayer::update(gfx::ShaderRegistry& shaders, imageLayerGroup->addLayerTweaker(layerTweaker); } } - layerTweaker->enableOverdrawInspector(!!(updateParameters->debugOptions & MapDebugOptions::Overdraw)); if (!staticDataVertices) { staticDataVertices = std::make_shared(RenderStaticData::rasterVertices()); diff --git a/src/mbgl/renderer/layers/render_symbol_layer.cpp b/src/mbgl/renderer/layers/render_symbol_layer.cpp index 1c9f6ab37ea..0381216107a 100644 --- a/src/mbgl/renderer/layers/render_symbol_layer.cpp +++ b/src/mbgl/renderer/layers/render_symbol_layer.cpp @@ -974,7 +974,7 @@ std::size_t RenderSymbolLayer::removeAllDrawables() { void RenderSymbolLayer::update(gfx::ShaderRegistry& shaders, gfx::Context& context, const TransformState& state, - const std::shared_ptr& updateParameters, + const std::shared_ptr&, const RenderTree& /*renderTree*/, UniqueChangeRequestVec& changes) { if (!renderTiles || renderTiles->empty() || passes == RenderPass::None) { @@ -993,7 +993,6 @@ void RenderSymbolLayer::update(gfx::ShaderRegistry& shaders, layerTweaker = std::make_shared(getID(), evaluatedProperties); layerGroup->addLayerTweaker(layerTweaker); } - layerTweaker->enableOverdrawInspector(!!(updateParameters->debugOptions & MapDebugOptions::Overdraw)); const auto& getCollisionTileLayerGroup = [&] { if (!collisionTileLayerGroup) { diff --git a/src/mbgl/renderer/layers/symbol_layer_tweaker.cpp b/src/mbgl/renderer/layers/symbol_layer_tweaker.cpp index 9fa33381ad9..feee50e547a 100644 --- a/src/mbgl/renderer/layers/symbol_layer_tweaker.cpp +++ b/src/mbgl/renderer/layers/symbol_layer_tweaker.cpp @@ -46,8 +46,6 @@ std::array toArray(const Size& s) { const StringIdentity idTexUniformName = stringIndexer().get("u_texture"); const StringIdentity idTexIconUniformName = stringIndexer().get("u_texture_icon"); -const StringIdentity idExpressionInputsUBOName = stringIndexer().get("ExpressionInputsUBO"); -const StringIdentity idSymbolPermutationUBOName = stringIndexer().get("SymbolPermutationUBO"); SymbolDrawablePaintUBO buildPaintUBO(bool isText, const SymbolPaintProperties::PossiblyEvaluated& evaluated) { return { @@ -86,15 +84,6 @@ void SymbolLayerTweaker::execute(LayerGroupBase& layerGroup, const PaintParamete const auto debugGroup = parameters.encoder->createDebugGroup(label.c_str()); #endif - const auto zoom = parameters.state.getZoom(); - -#if MLN_RENDER_BACKEND_METAL - if (!expressionUniformBuffer) { - const auto expressionUBO = buildExpressionUBO(zoom, parameters.frameCount); - expressionUniformBuffer = context.createUniformBuffer(&expressionUBO, sizeof(expressionUBO)); - } -#endif - if (propertiesUpdated) { textPropertiesUpdated = true; iconPropertiesUpdated = true; @@ -142,7 +131,7 @@ void SymbolLayerTweaker::execute(LayerGroupBase& layerGroup, const PaintParamete const auto matrix = getTileMatrix(tileID, parameters, translate, anchor, nearClipped, inViewportPixelUnits); // from symbol_program, makeValues - const auto currentZoom = static_cast(zoom); + const auto currentZoom = static_cast(parameters.state.getZoom()); const float pixelsToTileUnits = tileID.pixelsToTileUnits(1.f, currentZoom); const bool pitchWithMap = symbolData.pitchAlignment == style::AlignmentType::Map; const bool rotateWithMap = symbolData.rotationAlignment == style::AlignmentType::Map; @@ -183,31 +172,6 @@ void SymbolLayerTweaker::execute(LayerGroupBase& layerGroup, const PaintParamete uniforms.addOrReplace(idSymbolDynamicUBOName, dynamicBuffer); uniforms.addOrReplace(idSymbolDrawablePaintUBOName, isText ? textPaintBuffer : iconPaintBuffer); - -#if MLN_RENDER_BACKEND_METAL - assert(propertiesAsUniforms.empty()); - - using namespace shaders; - using ShaderClass = ShaderSource; - const auto source = [&](int index) { - const auto nameID = ShaderClass::attributes[index].nameID; - const bool uniform = symbolData.propertiesAsUniforms.count(nameID); - return uniform ? AttributeSource::Constant : AttributeSource::PerVertex; - }; - - const SymbolPermutationUBO permutationUBO = {/* .fill_color = */ {/*.source=*/source(5), /*.expression=*/{}}, - /* .halo_color = */ {/*.source=*/source(6), /*.expression=*/{}}, - /* .opacity = */ {/*.source=*/source(7), /*.expression=*/{}}, - /* .halo_width = */ {/*.source=*/source(8), /*.expression=*/{}}, - /* .halo_blur = */ {/*.source=*/source(9), /*.expression=*/{}}, - /* .overdrawInspector = */ overdrawInspector, - /* .pad = */ 0, - 0, - 0}; - uniforms.createOrUpdate(idSymbolPermutationUBOName, &permutationUBO, context); - - uniforms.addOrReplace(idExpressionInputsUBOName, expressionUniformBuffer); -#endif // MLN_RENDER_BACKEND_METAL }); } diff --git a/src/mbgl/renderer/sources/render_tile_source.cpp b/src/mbgl/renderer/sources/render_tile_source.cpp index 84ae38bbea9..a0a8d9b5fb2 100644 --- a/src/mbgl/renderer/sources/render_tile_source.cpp +++ b/src/mbgl/renderer/sources/render_tile_source.cpp @@ -232,28 +232,6 @@ void TileSourceRenderItem::updateDebugDrawables(DebugLayerGroupMap& debugLayerGr uniforms.createOrUpdate(idLineUBOName, &lineUBO, parameters.context); uniforms.createOrUpdate(idLinePropertiesUBOName, &linePropertiesUBO, parameters.context); uniforms.createOrUpdate(idLineInterpolationUBOName, &lineInterpolationUBO, parameters.context); - - static const StringIdentity idExpressionInputsUBOName = stringIndexer().get("ExpressionInputsUBO"); - const auto expressionUBO = LayerTweaker::buildExpressionUBO(zoom, parameters.frameCount); - uniforms.createOrUpdate(idExpressionInputsUBOName, &expressionUBO, parameters.context); - - static const StringIdentity idLinePermutationUBOName = stringIndexer().get("LinePermutationUBO"); - const shaders::LinePermutationUBO permutationUBO = { - /* .color = */ {/*.source=*/shaders::AttributeSource::Constant, /*.expression=*/{}}, - /* .blur = */ {/*.source=*/shaders::AttributeSource::Constant, /*.expression=*/{}}, - /* .opacity = */ {/*.source=*/shaders::AttributeSource::Constant, /*.expression=*/{}}, - /* .gapwidth = */ {/*.source=*/shaders::AttributeSource::Constant, /*.expression=*/{}}, - /* .offset = */ {/*.source=*/shaders::AttributeSource::Constant, /*.expression=*/{}}, - /* .width = */ {/*.source=*/shaders::AttributeSource::Constant, /*.expression=*/{}}, - /* .floorwidth = */ {/*.source=*/shaders::AttributeSource::Constant, /*.expression=*/{}}, - /* .pattern_from = */ {/*.source=*/shaders::AttributeSource::Constant, /*.expression=*/{}}, - /* .pattern_to = */ {/*.source=*/shaders::AttributeSource::Constant, /*.expression=*/{}}, - /* .overdrawInspector = */ false, - /* .pad = */ 0, - 0, - 0, - 0}; - uniforms.createOrUpdate(idLinePermutationUBOName, &permutationUBO, parameters.context); }; private: diff --git a/src/mbgl/shaders/mtl/circle.cpp b/src/mbgl/shaders/mtl/circle.cpp index e472e4d84aa..757f001b338 100644 --- a/src/mbgl/shaders/mtl/circle.cpp +++ b/src/mbgl/shaders/mtl/circle.cpp @@ -13,13 +13,11 @@ const std::array ShaderSource ShaderSource::uniforms = { +const std::array ShaderSource::uniforms = { UniformBlockInfo{8, true, false, sizeof(CircleDrawableUBO), "CircleDrawableUBO"}, UniformBlockInfo{9, true, true, sizeof(CirclePaintParamsUBO), "CirclePaintParamsUBO"}, UniformBlockInfo{10, true, true, sizeof(CircleEvaluatedPropsUBO), "CircleEvaluatedPropsUBO"}, UniformBlockInfo{11, true, false, sizeof(CircleInterpolateUBO), "CircleInterpolateUBO"}, - UniformBlockInfo{12, true, true, sizeof(CirclePermutationUBO), "CirclePermutationUBO"}, - UniformBlockInfo{13, true, false, sizeof(ExpressionInputsUBO), "ExpressionInputsUBO"}, }; const std::array ShaderSource::textures = {}; diff --git a/src/mbgl/shaders/mtl/fill.cpp b/src/mbgl/shaders/mtl/fill.cpp index 487d5b6925e..2e656fb0c50 100644 --- a/src/mbgl/shaders/mtl/fill.cpp +++ b/src/mbgl/shaders/mtl/fill.cpp @@ -8,12 +8,10 @@ const std::array ShaderSource ShaderSource::uniforms = { +const std::array ShaderSource::uniforms = { UniformBlockInfo{3, true, false, sizeof(FillDrawableUBO), "FillDrawableUBO"}, UniformBlockInfo{4, true, true, sizeof(FillEvaluatedPropsUBO), "FillEvaluatedPropsUBO"}, UniformBlockInfo{5, true, false, sizeof(FillInterpolateUBO), "FillInterpolateUBO"}, - UniformBlockInfo{6, true, true, sizeof(FillPermutationUBO), "FillPermutationUBO"}, - UniformBlockInfo{7, true, false, sizeof(ExpressionInputsUBO), "ExpressionInputsUBO"}, }; const std::array ShaderSource::textures = {}; @@ -22,12 +20,10 @@ const std::array ShaderSource ShaderSource::uniforms = { +const std::array ShaderSource::uniforms = { UniformBlockInfo{3, true, false, sizeof(FillOutlineDrawableUBO), "FillOutlineDrawableUBO"}, UniformBlockInfo{4, true, true, sizeof(FillOutlineEvaluatedPropsUBO), "FillOutlineEvaluatedPropsUBO"}, UniformBlockInfo{5, true, false, sizeof(FillOutlineInterpolateUBO), "FillOutlineInterpolateUBO"}, - UniformBlockInfo{6, true, true, sizeof(FillOutlinePermutationUBO), "FillOutlinePermutationUBO"}, - UniformBlockInfo{7, true, false, sizeof(ExpressionInputsUBO), "ExpressionInputsUBO"}, }; const std::array ShaderSource::textures = {}; @@ -37,13 +33,11 @@ const std::array ShaderSource ShaderSource::uniforms = { +const std::array ShaderSource::uniforms = { UniformBlockInfo{4, true, true, sizeof(FillPatternDrawableUBO), "FillPatternDrawableUBO"}, UniformBlockInfo{5, true, true, sizeof(FillPatternTilePropsUBO), "FillPatternTilePropsUBO"}, UniformBlockInfo{6, true, true, sizeof(FillPatternEvaluatedPropsUBO), "FillPatternEvaluatedPropsUBO"}, UniformBlockInfo{7, true, false, sizeof(FillPatternInterpolateUBO), "FillPatternInterpolateUBO"}, - UniformBlockInfo{8, true, true, sizeof(FillPatternPermutationUBO), "FillPatternPermutationUBO"}, - UniformBlockInfo{9, true, false, sizeof(ExpressionInputsUBO), "ExpressionInputsUBO"}, }; const std::array ShaderSource::textures = { TextureInfo{0, "u_image"}, @@ -56,14 +50,12 @@ const std::array AttributeInfo{2, gfx::AttributeDataType::UShort4, 1, "a_pattern_to"}, AttributeInfo{3, gfx::AttributeDataType::Float2, 1, "a_opacity"}, }; -const std::array ShaderSource ShaderSource::uniforms = { UniformBlockInfo{4, true, true, sizeof(FillOutlinePatternDrawableUBO), "FillOutlinePatternDrawableUBO"}, UniformBlockInfo{5, true, true, sizeof(FillOutlinePatternTilePropsUBO), "FillOutlinePatternTilePropsUBO"}, UniformBlockInfo{6, true, true, sizeof(FillOutlinePatternEvaluatedPropsUBO), "FillOutlinePatternEvaluatedPropsUBO"}, UniformBlockInfo{7, true, false, sizeof(FillOutlinePatternInterpolateUBO), "FillOutlinePatternInterpolateUBO"}, - UniformBlockInfo{8, true, true, sizeof(FillOutlinePatternPermutationUBO), "FillOutlinePatternPermutationUBO"}, - UniformBlockInfo{9, true, false, sizeof(ExpressionInputsUBO), "ExpressionInputsUBO"}, }; const std::array ShaderSource::textures = { diff --git a/src/mbgl/shaders/mtl/fill_extrusion.cpp b/src/mbgl/shaders/mtl/fill_extrusion.cpp index a320f95e8ca..ba1c1671e47 100644 --- a/src/mbgl/shaders/mtl/fill_extrusion.cpp +++ b/src/mbgl/shaders/mtl/fill_extrusion.cpp @@ -10,13 +10,11 @@ const std::array ShaderSource ShaderSource::uniforms = +const std::array ShaderSource::uniforms = { UniformBlockInfo{5, true, false, sizeof(FillExtrusionDrawableUBO), "FillExtrusionDrawableUBO"}, UniformBlockInfo{6, true, false, sizeof(FillExtrusionDrawablePropsUBO), "FillExtrusionDrawablePropsUBO"}, UniformBlockInfo{7, true, false, sizeof(FillExtrusionInterpolateUBO), "FillExtrusionInterpolateUBO"}, - UniformBlockInfo{8, true, false, sizeof(FillExtrusionPermutationUBO), "FillExtrusionPermutationUBO"}, - UniformBlockInfo{9, true, false, sizeof(ExpressionInputsUBO), "ExpressionInputsUBO"}, }; const std::array ShaderSource::textures = {}; diff --git a/src/mbgl/shaders/mtl/fill_extrusion_pattern.cpp b/src/mbgl/shaders/mtl/fill_extrusion_pattern.cpp index b8e0d87e1d7..6f235b6085a 100644 --- a/src/mbgl/shaders/mtl/fill_extrusion_pattern.cpp +++ b/src/mbgl/shaders/mtl/fill_extrusion_pattern.cpp @@ -13,14 +13,12 @@ const std::array AttributeInfo{4, gfx::AttributeDataType::UShort4, 1, "a_pattern_from"}, AttributeInfo{5, gfx::AttributeDataType::UShort4, 1, "a_pattern_to"}, }; -const std::array ShaderSource::uniforms = { - UniformBlockInfo{6, true, true, sizeof(FillExtrusionDrawableUBO), "FillExtrusionDrawableUBO"}, - UniformBlockInfo{7, true, true, sizeof(FillExtrusionDrawablePropsUBO), "FillExtrusionDrawablePropsUBO"}, - UniformBlockInfo{8, true, false, sizeof(FillExtrusionDrawableTilePropsUBO), "FillExtrusionDrawableTilePropsUBO"}, - UniformBlockInfo{9, true, false, sizeof(FillExtrusionInterpolateUBO), "FillExtrusionInterpolateUBO"}, - UniformBlockInfo{10, true, true, sizeof(FillExtrusionPermutationUBO), "FillExtrusionPermutationUBO"}, - UniformBlockInfo{11, true, false, sizeof(ExpressionInputsUBO), "ExpressionInputsUBO"}, +const std::array + ShaderSource::uniforms = { + UniformBlockInfo{6, true, true, sizeof(FillExtrusionDrawableUBO), "FillExtrusionDrawableUBO"}, + UniformBlockInfo{7, true, true, sizeof(FillExtrusionDrawablePropsUBO), "FillExtrusionDrawablePropsUBO"}, + UniformBlockInfo{8, true, true, sizeof(FillExtrusionDrawableTilePropsUBO), "FillExtrusionDrawableTilePropsUBO"}, + UniformBlockInfo{9, true, false, sizeof(FillExtrusionInterpolateUBO), "FillExtrusionInterpolateUBO"}, }; const std::array ShaderSource::textures = { diff --git a/src/mbgl/shaders/mtl/heatmap.cpp b/src/mbgl/shaders/mtl/heatmap.cpp index b6a42f3f41a..a4590017806 100644 --- a/src/mbgl/shaders/mtl/heatmap.cpp +++ b/src/mbgl/shaders/mtl/heatmap.cpp @@ -8,12 +8,10 @@ const std::array ShaderSource ShaderSource::uniforms = { +const std::array ShaderSource::uniforms = { UniformBlockInfo{3, true, false, sizeof(HeatmapDrawableUBO), "HeatmapDrawableUBO"}, UniformBlockInfo{4, true, true, sizeof(HeatmapEvaluatedPropsUBO), "HeatmapEvaluatedPropsUBO"}, UniformBlockInfo{5, true, false, sizeof(HeatmapInterpolateUBO), "HeatmapInterpolateUBO"}, - UniformBlockInfo{6, true, true, sizeof(HeatmapPermutationUBO), "HeatmapPermutationUBO"}, - UniformBlockInfo{7, true, false, sizeof(ExpressionInputsUBO), "ExpressionInputsUBO"}, }; const std::array ShaderSource::textures = {}; diff --git a/src/mbgl/shaders/mtl/line.cpp b/src/mbgl/shaders/mtl/line.cpp index 901177b99c7..49bccbff4b1 100644 --- a/src/mbgl/shaders/mtl/line.cpp +++ b/src/mbgl/shaders/mtl/line.cpp @@ -13,12 +13,10 @@ const std::array ShaderSource ShaderSource::uniforms = { +const std::array ShaderSource::uniforms = { UniformBlockInfo{8, true, true, sizeof(LineUBO), "LineUBO"}, UniformBlockInfo{9, true, true, sizeof(LinePropertiesUBO), "LinePropertiesUBO"}, UniformBlockInfo{10, true, false, sizeof(LineInterpolationUBO), "LineInterpolationUBO"}, - UniformBlockInfo{11, true, true, sizeof(LinePermutationUBO), "LinePermutationUBO"}, - UniformBlockInfo{12, true, false, sizeof(ExpressionInputsUBO), "ExpressionInputsUBO"}, }; const std::array ShaderSource::textures = {}; @@ -33,13 +31,11 @@ const std::array ShaderSource ShaderSource::uniforms = { +const std::array ShaderSource::uniforms = { UniformBlockInfo{9, true, true, sizeof(LinePatternUBO), "LinePatternUBO"}, UniformBlockInfo{10, true, true, sizeof(LinePatternPropertiesUBO), "LinePatternPropertiesUBO"}, UniformBlockInfo{11, true, false, sizeof(LinePatternInterpolationUBO), "LinePatternInterpolationUBO"}, UniformBlockInfo{12, true, true, sizeof(LinePatternTilePropertiesUBO), "LinePatternTilePropertiesUBO"}, - UniformBlockInfo{13, true, true, sizeof(LinePermutationUBO), "LinePermutationUBO"}, - UniformBlockInfo{14, true, false, sizeof(ExpressionInputsUBO), "ExpressionInputsUBO"}, }; const std::array ShaderSource::textures = { TextureInfo{0, "u_image"}, @@ -56,12 +52,10 @@ const std::array ShaderSource ShaderSource::uniforms = { +const std::array ShaderSource::uniforms = { UniformBlockInfo{9, true, true, sizeof(LineSDFUBO), "LineSDFUBO"}, UniformBlockInfo{10, true, true, sizeof(LineSDFPropertiesUBO), "LineSDFPropertiesUBO"}, UniformBlockInfo{11, true, false, sizeof(LineSDFInterpolationUBO), "LineSDFInterpolationUBO"}, - UniformBlockInfo{12, true, true, sizeof(LinePermutationUBO), "LinePermutationUBO"}, - UniformBlockInfo{13, true, false, sizeof(ExpressionInputsUBO), "ExpressionInputsUBO"}, }; const std::array ShaderSource::textures = { TextureInfo{0, "u_image"}, diff --git a/src/mbgl/shaders/mtl/line_gradient.cpp b/src/mbgl/shaders/mtl/line_gradient.cpp index fc980003e27..66b0ba7ce1b 100644 --- a/src/mbgl/shaders/mtl/line_gradient.cpp +++ b/src/mbgl/shaders/mtl/line_gradient.cpp @@ -12,12 +12,10 @@ const std::array ShaderSource ShaderSource::uniforms = { +const std::array ShaderSource::uniforms = { UniformBlockInfo{7, true, true, sizeof(LineGradientUBO), "LineGradientUBO"}, - UniformBlockInfo{8, true, false, sizeof(LineGradientPropertiesUBO), "LineGradientPropertiesUBO"}, + UniformBlockInfo{8, true, true, sizeof(LineGradientPropertiesUBO), "LineGradientPropertiesUBO"}, UniformBlockInfo{9, true, false, sizeof(LineGradientInterpolationUBO), "LineGradientInterpolationUBO"}, - UniformBlockInfo{10, true, true, sizeof(LinePermutationUBO), "LinePermutationUBO"}, - UniformBlockInfo{11, true, false, sizeof(ExpressionInputsUBO), "ExpressionInputsUBO"}, }; const std::array ShaderSource::textures = { TextureInfo{0, "u_image"}, diff --git a/src/mbgl/shaders/mtl/symbol_icon.cpp b/src/mbgl/shaders/mtl/symbol_icon.cpp index 566f388870b..a84c029f641 100644 --- a/src/mbgl/shaders/mtl/symbol_icon.cpp +++ b/src/mbgl/shaders/mtl/symbol_icon.cpp @@ -14,14 +14,12 @@ const std::array ShaderSource ShaderSource::uniforms = { +const std::array ShaderSource::uniforms = { UniformBlockInfo{8, true, true, sizeof(SymbolDrawableUBO), "SymbolDrawableUBO"}, UniformBlockInfo{9, true, false, sizeof(SymbolDynamicUBO), "SymbolDynamicUBO"}, UniformBlockInfo{10, true, true, sizeof(SymbolDrawablePaintUBO), "SymbolDrawablePaintUBO"}, UniformBlockInfo{11, true, false, sizeof(SymbolDrawableTilePropsUBO), "SymbolDrawableTilePropsUBO"}, UniformBlockInfo{12, true, false, sizeof(SymbolDrawableInterpolateUBO), "SymbolDrawableInterpolateUBO"}, - UniformBlockInfo{13, true, true, sizeof(SymbolPermutationUBO), "SymbolPermutationUBO"}, - UniformBlockInfo{14, true, false, sizeof(ExpressionInputsUBO), "ExpressionInputsUBO"}, }; const std::array ShaderSource::textures = { TextureInfo{0, "u_texture"}, diff --git a/src/mbgl/shaders/mtl/symbol_sdf.cpp b/src/mbgl/shaders/mtl/symbol_sdf.cpp index 98d5e39a678..cd4b8a6d4b8 100644 --- a/src/mbgl/shaders/mtl/symbol_sdf.cpp +++ b/src/mbgl/shaders/mtl/symbol_sdf.cpp @@ -19,15 +19,13 @@ const std::array ShaderSource ShaderSource::uniforms = +const std::array ShaderSource::uniforms = { UniformBlockInfo{10, true, true, sizeof(SymbolDrawableUBO), "SymbolDrawableUBO"}, UniformBlockInfo{11, true, true, sizeof(SymbolDynamicUBO), "SymbolDynamicUBO"}, UniformBlockInfo{12, true, true, sizeof(SymbolDrawablePaintUBO), "SymbolDrawablePaintUBO"}, UniformBlockInfo{13, true, true, sizeof(SymbolDrawableTilePropsUBO), "SymbolDrawableTilePropsUBO"}, UniformBlockInfo{14, true, false, sizeof(SymbolDrawableInterpolateUBO), "SymbolDrawableInterpolateUBO"}, - UniformBlockInfo{15, true, true, sizeof(SymbolPermutationUBO), "SymbolPermutationUBO"}, - UniformBlockInfo{16, true, false, sizeof(ExpressionInputsUBO), "ExpressionInputsUBO"}, }; const std::array ShaderSource::textures = { TextureInfo{0, "u_texture"}, diff --git a/src/mbgl/shaders/mtl/symbol_text_and_icon.cpp b/src/mbgl/shaders/mtl/symbol_text_and_icon.cpp index 51b1a24df57..408a83033e9 100644 --- a/src/mbgl/shaders/mtl/symbol_text_and_icon.cpp +++ b/src/mbgl/shaders/mtl/symbol_text_and_icon.cpp @@ -18,15 +18,13 @@ const std::array AttributeInfo{7, gfx::AttributeDataType::Float, 1, "a_halo_width"}, AttributeInfo{8, gfx::AttributeDataType::Float, 1, "a_halo_blur"}, }; -const std::array +const std::array ShaderSource::uniforms = { UniformBlockInfo{9, true, true, sizeof(SymbolDrawableUBO), "SymbolDrawableUBO"}, UniformBlockInfo{10, true, true, sizeof(SymbolDynamicUBO), "SymbolDynamicUBO"}, UniformBlockInfo{11, true, true, sizeof(SymbolDrawablePaintUBO), "SymbolDrawablePaintUBO"}, UniformBlockInfo{12, true, true, sizeof(SymbolDrawableTilePropsUBO), "SymbolDrawableTilePropsUBO"}, UniformBlockInfo{13, true, false, sizeof(SymbolDrawableInterpolateUBO), "SymbolDrawableInterpolateUBO"}, - UniformBlockInfo{14, true, true, sizeof(SymbolPermutationUBO), "SymbolPermutationUBO"}, - UniformBlockInfo{15, true, false, sizeof(ExpressionInputsUBO), "ExpressionInputsUBO"}, }; const std::array ShaderSource::textures = { TextureInfo{0, "u_texture"}, diff --git a/src/mbgl/style/layers/custom_drawable_layer.cpp b/src/mbgl/style/layers/custom_drawable_layer.cpp index 39215e259c5..e4037550f1e 100644 --- a/src/mbgl/style/layers/custom_drawable_layer.cpp +++ b/src/mbgl/style/layers/custom_drawable_layer.cpp @@ -119,30 +119,6 @@ class LineDrawableTweaker : public gfx::DrawableTweaker { uniforms.createOrUpdate(idLineUBOName, &lineUBO, parameters.context); uniforms.createOrUpdate(idLinePropertiesUBOName, &linePropertiesUBO, parameters.context); uniforms.createOrUpdate(idLineInterpolationUBOName, &lineInterpolationUBO, parameters.context); - -#if MLN_RENDER_BACKEND_METAL - static const StringIdentity idExpressionInputsUBOName = stringIndexer().get("ExpressionInputsUBO"); - const auto expressionUBO = LayerTweaker::buildExpressionUBO(zoom, parameters.frameCount); - uniforms.createOrUpdate(idExpressionInputsUBOName, &expressionUBO, parameters.context); - - static const StringIdentity idLinePermutationUBOName = stringIndexer().get("LinePermutationUBO"); - const shaders::LinePermutationUBO permutationUBO = { - /* .color = */ {/*.source=*/shaders::AttributeSource::Constant, /*.expression=*/{}}, - /* .blur = */ {/*.source=*/shaders::AttributeSource::Constant, /*.expression=*/{}}, - /* .opacity = */ {/*.source=*/shaders::AttributeSource::Constant, /*.expression=*/{}}, - /* .gapwidth = */ {/*.source=*/shaders::AttributeSource::Constant, /*.expression=*/{}}, - /* .offset = */ {/*.source=*/shaders::AttributeSource::Constant, /*.expression=*/{}}, - /* .width = */ {/*.source=*/shaders::AttributeSource::Constant, /*.expression=*/{}}, - /* .floorwidth = */ {/*.source=*/shaders::AttributeSource::Constant, /*.expression=*/{}}, - /* .pattern_from = */ {/*.source=*/shaders::AttributeSource::Constant, /*.expression=*/{}}, - /* .pattern_to = */ {/*.source=*/shaders::AttributeSource::Constant, /*.expression=*/{}}, - /* .overdrawInspector = */ false, - /* .pad = */ 0, - 0, - 0, - 0}; - uniforms.createOrUpdate(idLinePermutationUBOName, &permutationUBO, parameters.context); -#endif // MLN_RENDER_BACKEND_METAL }; private: @@ -193,27 +169,6 @@ class FillDrawableTweaker : public gfx::DrawableTweaker { uniforms.createOrUpdate(idFillDrawableUBOName, &fillUBO, parameters.context); uniforms.createOrUpdate(idFillEvaluatedPropsUBOName, &fillPropertiesUBO, parameters.context); uniforms.createOrUpdate(idFillInterpolateUBOName, &fillInterpolateUBO, parameters.context); - -#if MLN_RENDER_BACKEND_METAL - const auto zoom = parameters.state.getZoom(); - static const StringIdentity idExpressionInputsUBOName = stringIndexer().get("ExpressionInputsUBO"); - const auto expressionUBO = LayerTweaker::buildExpressionUBO(zoom, parameters.frameCount); - uniforms.createOrUpdate(idExpressionInputsUBOName, &expressionUBO, parameters.context); - - static const StringIdentity idFillPermutationUBOName = stringIndexer().get("FillPermutationUBO"); - const shaders::FillPermutationUBO permutationUBO = { - /* .color = */ {/*.source=*/shaders::AttributeSource::Constant, /*.expression=*/{}}, - /* .opacity = */ {/*.source=*/shaders::AttributeSource::Constant, /*.expression=*/{}}, - /* .overdrawInspector = */ false, - 0, - 0, - 0, - 0, - 0, - 0, - }; - uniforms.createOrUpdate(idFillPermutationUBOName, &permutationUBO, parameters.context); -#endif // MLN_RENDER_BACKEND_METAL }; private: From edd478ee331b996abf56e0b6a458faca618794b8 Mon Sep 17 00:00:00 2001 From: Bart Louwers Date: Thu, 7 Dec 2023 19:56:29 +0100 Subject: [PATCH 04/18] Small CI fixes Android (#1927) --- .github/actions/aws-device-farm-run/action.yml | 4 ++-- .github/workflows/android-ci.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/actions/aws-device-farm-run/action.yml b/.github/actions/aws-device-farm-run/action.yml index 6555664220e..fc1d1814db0 100644 --- a/.github/actions/aws-device-farm-run/action.yml +++ b/.github/actions/aws-device-farm-run/action.yml @@ -111,9 +111,9 @@ runs: --name "MapLibre Native ${{ matrix.test.name }}" \ --app-arn ${{ env.app_arn }} \ --device-pool-arn ${{ inputs.AWS_DEVICE_FARM_DEVICE_POOL_ARN }} \ - --test type=${{ inputs.testType }},testPackageArn=${{ env.test_package_arn }}${{ inputs.testFilter && ",filter=" }}${{ inputs.testFilter }} \ + --test type=${{ inputs.testType }},testPackageArn=${{ env.test_package_arn }}${{ inputs.testFilter && ',filter=' }}${{ inputs.testFilter }} \ --execution-configuration jobTimeoutMinutes=28800,videoCapture=false \ - ${{ inputs.externalData && "--configuration extraDataPackageArn=" }}${{ inputs.externalData }} \ + ${{ inputs.externalData && '--configuration extraDataPackageArn=' }}${{ inputs.externalData }} \ --output text --query "run.arn")" # wait until result is not PENDING diff --git a/.github/workflows/android-ci.yml b/.github/workflows/android-ci.yml index 2dd28d5e9c5..b885990ac01 100644 --- a/.github/workflows/android-ci.yml +++ b/.github/workflows/android-ci.yml @@ -168,8 +168,8 @@ jobs: - name: Upload Android UI test if: github.ref == 'refs/heads/main' run: | - curl -T MapboxGLAndroidSDKTestApp/build/outputs/apk/legacy/debug/MapboxGLAndroidSDKTestApp-legacy-debug.apk '${{ steps.upload-android-app.outputs.url }}' - curl -T MapboxGLAndroidSDKTestApp/build/outputs/apk/androidTest/legacy/debug/MapboxGLAndroidSDKTestApp-legacy-debug-androidTest.apk '${{ steps.upload-android-test.outputs.url }}' + curl -T MapboxGLAndroidSDKTestApp/build/outputs/apk/legacy/release/MapboxGLAndroidSDKTestApp-legacy-debug.apk '${{ steps.upload-android-app.outputs.url }}' + curl -T MapboxGLAndroidSDKTestApp/build/outputs/apk/androidTest/legacy/release/MapboxGLAndroidSDKTestApp-legacy-debug-androidTest.apk '${{ steps.upload-android-test.outputs.url }}' - name: Write uploads.env if: github.ref == 'refs/heads/main' From ae4120d953372e29d34c9bb3b9b309503a80cbda Mon Sep 17 00:00:00 2001 From: Bart Louwers Date: Thu, 7 Dec 2023 21:43:55 +0100 Subject: [PATCH 05/18] Remove slashes from AWS Device Farm uploads (#1930) --- .github/actions/aws-device-farm-run/action.yml | 2 +- .github/workflows/android-ci.yml | 12 ++++++++---- .github/workflows/android-device-test.yml | 4 ++-- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/.github/actions/aws-device-farm-run/action.yml b/.github/actions/aws-device-farm-run/action.yml index fc1d1814db0..9edc0054cc9 100644 --- a/.github/actions/aws-device-farm-run/action.yml +++ b/.github/actions/aws-device-farm-run/action.yml @@ -112,7 +112,7 @@ runs: --app-arn ${{ env.app_arn }} \ --device-pool-arn ${{ inputs.AWS_DEVICE_FARM_DEVICE_POOL_ARN }} \ --test type=${{ inputs.testType }},testPackageArn=${{ env.test_package_arn }}${{ inputs.testFilter && ',filter=' }}${{ inputs.testFilter }} \ - --execution-configuration jobTimeoutMinutes=28800,videoCapture=false \ + --execution-configuration jobTimeoutMinutes=480,videoCapture=false \ ${{ inputs.externalData && '--configuration extraDataPackageArn=' }}${{ inputs.externalData }} \ --output text --query "run.arn")" diff --git a/.github/workflows/android-ci.yml b/.github/workflows/android-ci.yml index b885990ac01..724242f1f30 100644 --- a/.github/workflows/android-ci.yml +++ b/.github/workflows/android-ci.yml @@ -118,19 +118,23 @@ jobs: echo "No secrets.MAPLIBRE_DEVELOPER_CONFIG_XML variable set, not copying..." fi - - name: Build Benchmark + - name: Build Benchmark, copy to platform/android if: github.ref != 'refs/heads/main' run: | ./gradlew assembleDrawableRelease assembleDrawableReleaseAndroidTest + cp MapboxGLAndroidSDKTestApp/build/outputs/apk/drawable/release/MapboxGLAndroidSDKTestApp-drawable-release.apk . + cp MapboxGLAndroidSDKTestApp/build/outputs/apk/androidTest/drawable/release/MapboxGLAndroidSDKTestApp-drawable-release-androidTest.apk . - - uses: actions/upload-artifact@v3 + + - name: Create artifact for benchmark APKs + uses: actions/upload-artifact@v3 if: github.ref != 'refs/heads/main' with: if-no-files-found: error name: benchmarkAPKs path: | - platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk/drawable/release/MapboxGLAndroidSDKTestApp-drawable-release.apk - platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk/androidTest/drawable/release/MapboxGLAndroidSDKTestApp-drawable-release-androidTest.apk + platform/android/MapboxGLAndroidSDKTestApp-drawable-release.apk + platform/android/MapboxGLAndroidSDKTestApp-drawable-release-androidTest.apk - name: Build UI tests if: github.ref == 'refs/heads/main' diff --git a/.github/workflows/android-device-test.yml b/.github/workflows/android-device-test.yml index 6b7d1d65fd6..7f4c2ce7305 100644 --- a/.github/workflows/android-device-test.yml +++ b/.github/workflows/android-device-test.yml @@ -14,8 +14,8 @@ jobs: {artifactName: android-render-tests, testFile: RenderTests.apk, appFile: RenderTestsApp.apk, name: "Android Render Tests"}, { artifactName: benchmarkAPKs, - testFile: "androidTest/drawable/release/MapboxGLAndroidSDKTestApp-drawable-release-androidTest.apk", - appFile: "drawable/release/MapboxGLAndroidSDKTestApp-drawable-release.apk", + testFile: "MapboxGLAndroidSDKTestApp-drawable-release-androidTest.apk", + appFile: "MapboxGLAndroidSDKTestApp-drawable-release.apk", name: "Android Benchmark", testFilter: "org.maplibre.android.benchmark.Benchmark", # echo '{"styleNames": [...], "styleURLs": [...], "resultsAPI: "..." }' > benchmark-input.json From efa70a41ee9206dd800f0952d65bf54e681a45f7 Mon Sep 17 00:00:00 2001 From: Bart Louwers Date: Fri, 8 Dec 2023 10:12:05 +0100 Subject: [PATCH 06/18] Use default timeout AWS Device Farm (#1931) --- .github/actions/aws-device-farm-run/action.yml | 1 - .github/workflows/android-device-test.yml | 16 +++++++++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/.github/actions/aws-device-farm-run/action.yml b/.github/actions/aws-device-farm-run/action.yml index 9edc0054cc9..9d7a9888804 100644 --- a/.github/actions/aws-device-farm-run/action.yml +++ b/.github/actions/aws-device-farm-run/action.yml @@ -112,7 +112,6 @@ runs: --app-arn ${{ env.app_arn }} \ --device-pool-arn ${{ inputs.AWS_DEVICE_FARM_DEVICE_POOL_ARN }} \ --test type=${{ inputs.testType }},testPackageArn=${{ env.test_package_arn }}${{ inputs.testFilter && ',filter=' }}${{ inputs.testFilter }} \ - --execution-configuration jobTimeoutMinutes=480,videoCapture=false \ ${{ inputs.externalData && '--configuration extraDataPackageArn=' }}${{ inputs.externalData }} \ --output text --query "run.arn")" diff --git a/.github/workflows/android-device-test.yml b/.github/workflows/android-device-test.yml index 7f4c2ce7305..30595408f2b 100644 --- a/.github/workflows/android-device-test.yml +++ b/.github/workflows/android-device-test.yml @@ -9,9 +9,17 @@ on: jobs: create-check: strategy: + max-parallel: 2 matrix: test: [ - {artifactName: android-render-tests, testFile: RenderTests.apk, appFile: RenderTestsApp.apk, name: "Android Render Tests"}, + { + artifactName: android-render-tests, + testFile: RenderTests.apk, + appFile: RenderTestsApp.apk, + name: "Android Render Tests", + # Google Pixel 7 Pro + devicePool: "arn:aws:devicefarm:us-west-2:373521797162:devicepool:20687d72-0e46-403e-8f03-0941850665bc/9692fe7f-86a9-4ecc-908f-175600968564" + }, { artifactName: benchmarkAPKs, testFile: "MapboxGLAndroidSDKTestApp-drawable-release-androidTest.apk", @@ -23,7 +31,9 @@ jobs: # aws devicefarm create-upload --project-arn --type EXTERNAL_DATA --name benchmark-input.zip # curl -T benchmark-input.zip # aws devicefarm get-upload - externalData: "arn:aws:devicefarm:us-west-2:373521797162:upload:20687d72-0e46-403e-8f03-0941850665bc/c27174c2-63f4-4cdb-9af9-68957d75ebed" + externalData: "arn:aws:devicefarm:us-west-2:373521797162:upload:20687d72-0e46-403e-8f03-0941850665bc/c27174c2-63f4-4cdb-9af9-68957d75ebed", + # top devices, query with `aws list-device-pools --arn ` + devicePool: "arn:aws:devicefarm:us-west-2::devicepool:082d10e5-d7d7-48a5-ba5c-b33d66efa1f5" } ] runs-on: ubuntu-latest @@ -69,7 +79,7 @@ jobs: AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_ROLE_TO_ASSUME: ${{ vars.AWS_ROLE_TO_ASSUME }} AWS_DEVICE_FARM_PROJECT_ARN: ${{ vars.AWS_DEVICE_FARM_PROJECT_ARN }} - AWS_DEVICE_FARM_DEVICE_POOL_ARN: ${{ vars.AWS_DEVICE_FARM_DEVICE_POOL_ARN }} + AWS_DEVICE_FARM_DEVICE_POOL_ARN: ${{ matrix.test.devicePool }} externalData: ${{ matrix.test.externalData }} - uses: LouisBrunner/checks-action@v1.6.2 From 23c046bf983756f386a38ef3e475f34b88971762 Mon Sep 17 00:00:00 2001 From: Bart Louwers Date: Fri, 8 Dec 2023 16:28:50 +0100 Subject: [PATCH 07/18] Run benchmark on request (#1932) --- .github/workflows/android-ci.yml | 4 +++- .github/workflows/android-device-test.yml | 20 ++++++++++++++++-- CONTRIBUTING.md | 25 ++++++++--------------- 3 files changed, 30 insertions(+), 19 deletions(-) diff --git a/.github/workflows/android-ci.yml b/.github/workflows/android-ci.yml index 724242f1f30..81d789aeb37 100644 --- a/.github/workflows/android-ci.yml +++ b/.github/workflows/android-ci.yml @@ -125,7 +125,6 @@ jobs: cp MapboxGLAndroidSDKTestApp/build/outputs/apk/drawable/release/MapboxGLAndroidSDKTestApp-drawable-release.apk . cp MapboxGLAndroidSDKTestApp/build/outputs/apk/androidTest/drawable/release/MapboxGLAndroidSDKTestApp-drawable-release-androidTest.apk . - - name: Create artifact for benchmark APKs uses: actions/upload-artifact@v3 if: github.ref != 'refs/heads/main' @@ -136,6 +135,9 @@ jobs: platform/android/MapboxGLAndroidSDKTestApp-drawable-release.apk platform/android/MapboxGLAndroidSDKTestApp-drawable-release-androidTest.apk + - if: github.event_name == 'pull_request' + uses: ./.github/actions/save-pr-number + - name: Build UI tests if: github.ref == 'refs/heads/main' run: make android-ui-test-arm-v8 diff --git a/.github/workflows/android-device-test.yml b/.github/workflows/android-device-test.yml index 30595408f2b..b028cdd3fe7 100644 --- a/.github/workflows/android-device-test.yml +++ b/.github/workflows/android-device-test.yml @@ -47,7 +47,20 @@ jobs: app_id: ${{ secrets.MAPLIBRE_NATIVE_BOT_APP_ID }} private_key: ${{ secrets.MAPLIBRE_NATIVE_BOT_PRIVATE_KEY }} + - run: echo ${{ toJSON(github.event.workflow_run) }} + + - uses: ./.github/actions/get-pr-number + id: get-pr-number + + - name: Check if comment on PR contains '!benchmark android' + uses: peter-evans/find-comment@v2 + id: fc + with: + issue-number: ${{ steps.get-pr-number.outputs.pr-number }} + body-regex: '^!benchmark.*android.*$' + - uses: LouisBrunner/checks-action@v1.6.2 + if: matrix.test.name != 'Android Benchmark' || steps.fc.outputs.comment-id id: create_check with: token: ${{ steps.generate_token.outputs.token }} @@ -57,17 +70,19 @@ jobs: sha: ${{ github.event.workflow_run.head_sha }} - uses: ./.github/actions/download-workflow-run-artifact + if: matrix.test.name != 'Android Benchmark' || steps.fc.outputs.comment-id with: artifact-name: ${{ matrix.test.artifactName }} - name: Check if test files exist (otherwise the parent workflow was skipped) + if: matrix.test.name != 'Android Benchmark' || steps.fc.outputs.comment-id id: check_files uses: andstor/file-existence-action@v2.0.0 with: files: "${{ matrix.test.testFile }}, ${{ matrix.test.appFile }}" - uses: ./.github/actions/aws-device-farm-run - if: steps.check_files.outputs.files_exists == 'true' + if: steps.check_files.outputs.files_exists == 'true' && (matrix.test.name != 'Android Benchmark' || steps.fc.outputs.comment-id) with: name: ${{ matrix.test.name }} appType: ANDROID_APP @@ -75,6 +90,7 @@ jobs: testFile: ${{ matrix.test.testFile }} testPackageType: INSTRUMENTATION_TEST_PACKAGE testType: INSTRUMENTATION + testFilter: ${{ matrix.test.testFilter }} AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_ROLE_TO_ASSUME: ${{ vars.AWS_ROLE_TO_ASSUME }} @@ -83,7 +99,7 @@ jobs: externalData: ${{ matrix.test.externalData }} - uses: LouisBrunner/checks-action@v1.6.2 - if: always() + if: always() && (matrix.test.name != 'Android Benchmark' || steps.fc.outputs.comment-id) with: token: ${{ steps.generate_token.outputs.token }} check_id: ${{ steps.create_check.outputs.check_id }} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cc8f58475d5..34af96905f4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -11,19 +11,6 @@ git clone --recurse-submodules https://github.com/maplibre/maplibre-native.git ``` -## Building - -MapLibre Native shares a single C++ core library with all platforms. To build it, we utilize CMake. - -To build, run the following from the root directory -```bash -cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug -DMLN_WITH_CORE_ONLY=ON -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DMLN_WITH_COVERAGE=ON -``` - -`CMAKE_BUILD_TYPE=Debug` will build debug artifacts. You can opt to omit it if that is not necessary. -`MLN_WITH_CORE_ONLY=ON` will build only the core libraries. -Built artifacts should be available on `build` folder. - ## Guidelines @@ -33,12 +20,18 @@ If you want to contribute code: 1. Ensure that existing [pull requests](https://github.com/maplibre/maplibre-native/pulls) and [issues](https://github.com/maplibre/maplibre-native/issues) don’t already cover your contribution or question. -1. Pull requests are gladly accepted. If there are any changes that developers using one of the GL SDKs should be aware of, please update the **main** section of the relevant `CHANGELOG.md`. - -4. Prefix your commit messages with the platform(s) your changes affect, e.g. `[ios]`. +1. Pull requests are gladly accepted. If there are any changes that developers using one of the platforms should be aware of, please update the **main** section of the relevant `CHANGELOG.md`. Please note the special instructions for contributing new source code files, asset files, or user-facing strings to MapLibre Native for [iOS](platform/ios/CONTRIBUTING.md), [Android](platform/android/DEVELOPING.md) or [macOS](platform/macos/DEVELOPING.md). +## Pull Requests + +To run the benchmarks (for Android) include the following line on a PR comment: + +``` +!benchmark android +``` + ## Design Proposals If you would like to change MapLibre Native in a substantial way, we recommend that you write a Design Proposal. Examples for substantial changes could be if you would like to split the mono-repo or if you would like to introduce shaders written in Metal. From 8a6bfbae7e0207d465047826eb5e27d1d59a75ae Mon Sep 17 00:00:00 2001 From: Bart Louwers Date: Fri, 8 Dec 2023 19:56:47 +0100 Subject: [PATCH 08/18] Add missing quotes (#1933) --- .github/actions/aws-device-farm-run/action.yml | 2 +- .github/workflows/android-device-test.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/actions/aws-device-farm-run/action.yml b/.github/actions/aws-device-farm-run/action.yml index 9d7a9888804..76c1ef87b5a 100644 --- a/.github/actions/aws-device-farm-run/action.yml +++ b/.github/actions/aws-device-farm-run/action.yml @@ -119,7 +119,7 @@ runs: # https://awscli.amazonaws.com/v2/documentation/api/latest/reference/devicefarm/get-run.html#output while true; do sleep 30 - result="$(aws get-run --arn "$arn" --output text --query "run.result")" + result="$(aws devicefarm get-run --arn "$arn" --output text --query "run.result")" case $result in FAILED|ERRORED|STOPPED) echo "Run $result" && exit 1 ;; SKIPPED|PASSED) echo "Run $result" && exit 0 ;; diff --git a/.github/workflows/android-device-test.yml b/.github/workflows/android-device-test.yml index b028cdd3fe7..bf2eaf8e95c 100644 --- a/.github/workflows/android-device-test.yml +++ b/.github/workflows/android-device-test.yml @@ -47,7 +47,7 @@ jobs: app_id: ${{ secrets.MAPLIBRE_NATIVE_BOT_APP_ID }} private_key: ${{ secrets.MAPLIBRE_NATIVE_BOT_PRIVATE_KEY }} - - run: echo ${{ toJSON(github.event.workflow_run) }} + - run: echo "${{ toJSON(github.event.workflow_run) }}" - uses: ./.github/actions/get-pr-number id: get-pr-number From 1c2faae52e182e514d19e6537916de5dcacd1587 Mon Sep 17 00:00:00 2001 From: Alex Cristici Date: Sat, 9 Dec 2023 07:48:42 +0200 Subject: [PATCH 09/18] Hillshade unused padding removed (#1935) --- include/mbgl/shaders/gl/drawable_hillshade.hpp | 8 -------- include/mbgl/shaders/gl/drawable_hillshade_prepare.hpp | 8 -------- shaders/drawable.hillshade.fragment.glsl | 4 ---- shaders/drawable.hillshade.vertex.glsl | 4 ---- 4 files changed, 24 deletions(-) diff --git a/include/mbgl/shaders/gl/drawable_hillshade.hpp b/include/mbgl/shaders/gl/drawable_hillshade.hpp index 100ddc6e793..3d706de1a5f 100644 --- a/include/mbgl/shaders/gl/drawable_hillshade.hpp +++ b/include/mbgl/shaders/gl/drawable_hillshade.hpp @@ -15,10 +15,6 @@ layout (std140) uniform HillshadeDrawableUBO { highp mat4 u_matrix; highp vec2 u_latrange; highp vec2 u_light; - lowp float pad0_; - lowp float pad1_; - lowp float pad2_; - lowp float pad3_; }; out vec2 v_pos; @@ -35,10 +31,6 @@ layout (std140) uniform HillshadeDrawableUBO { highp mat4 u_matrix; highp vec2 u_latrange; highp vec2 u_light; - lowp float pad0_; - lowp float pad1_; - lowp float pad2_; - lowp float pad3_; }; layout (std140) uniform HillshadeEvaluatedPropsUBO { diff --git a/include/mbgl/shaders/gl/drawable_hillshade_prepare.hpp b/include/mbgl/shaders/gl/drawable_hillshade_prepare.hpp index bec7a55bae4..76335aed366 100644 --- a/include/mbgl/shaders/gl/drawable_hillshade_prepare.hpp +++ b/include/mbgl/shaders/gl/drawable_hillshade_prepare.hpp @@ -17,10 +17,6 @@ layout (std140) uniform HillshadePrepareDrawableUBO { highp vec2 u_dimension; highp float u_zoom; highp float u_maxzoom; - lowp float pad0_; - lowp float pad1_; - lowp float pad2_; - lowp float pad3_; }; out vec2 v_pos; @@ -46,10 +42,6 @@ layout (std140) uniform HillshadePrepareDrawableUBO { highp vec2 u_dimension; highp float u_zoom; highp float u_maxzoom; - lowp float pad0_; - lowp float pad1_; - lowp float pad2_; - lowp float pad3_; }; float getElevation(vec2 coord, float bias) { diff --git a/shaders/drawable.hillshade.fragment.glsl b/shaders/drawable.hillshade.fragment.glsl index 3e043b22fd8..ca3df92554f 100644 --- a/shaders/drawable.hillshade.fragment.glsl +++ b/shaders/drawable.hillshade.fragment.glsl @@ -5,10 +5,6 @@ layout (std140) uniform HillshadeDrawableUBO { highp mat4 u_matrix; highp vec2 u_latrange; highp vec2 u_light; - lowp float pad0_; - lowp float pad1_; - lowp float pad2_; - lowp float pad3_; }; layout (std140) uniform HillshadeEvaluatedPropsUBO { diff --git a/shaders/drawable.hillshade.vertex.glsl b/shaders/drawable.hillshade.vertex.glsl index 68df07fec24..20fadb2f04a 100644 --- a/shaders/drawable.hillshade.vertex.glsl +++ b/shaders/drawable.hillshade.vertex.glsl @@ -5,10 +5,6 @@ layout (std140) uniform HillshadeDrawableUBO { highp mat4 u_matrix; highp vec2 u_latrange; highp vec2 u_light; - lowp float pad0_; - lowp float pad1_; - lowp float pad2_; - lowp float pad3_; }; out vec2 v_pos; From ea73a57a093d6b417c6faf80dec50bd9003b6349 Mon Sep 17 00:00:00 2001 From: Alex Cristici Date: Sat, 9 Dec 2023 07:48:57 +0200 Subject: [PATCH 10/18] Device pixel ratio send as define instead of UBO field (#1936) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- include/mbgl/shaders/circle_layer_ubo.hpp | 3 +-- include/mbgl/shaders/gl/drawable_circle.hpp | 6 +++--- include/mbgl/shaders/gl/drawable_line.hpp | 8 ++++---- .../mbgl/shaders/gl/drawable_line_basic.hpp | 8 ++++---- .../shaders/gl/drawable_line_gradient.hpp | 8 ++++---- .../mbgl/shaders/gl/drawable_line_pattern.hpp | 8 ++++---- include/mbgl/shaders/gl/drawable_line_sdf.hpp | 10 ++++++---- .../mbgl/shaders/gl/drawable_symbol_icon.hpp | 2 +- .../mbgl/shaders/gl/drawable_symbol_sdf.hpp | 6 +++--- .../gl/drawable_symbol_text_and_icon.hpp | 6 +++--- include/mbgl/shaders/gl/shader_group_gl.hpp | 6 +++++- include/mbgl/shaders/line_layer_ubo.hpp | 7 +++---- include/mbgl/shaders/mtl/circle.hpp | 5 ++--- include/mbgl/shaders/mtl/common.hpp | 8 ++++---- include/mbgl/shaders/mtl/line.hpp | 19 +++++++++---------- include/mbgl/shaders/mtl/line_gradient.hpp | 4 ++-- include/mbgl/shaders/mtl/shader_group.hpp | 11 +++++------ include/mbgl/shaders/mtl/symbol_sdf.hpp | 2 +- .../mbgl/shaders/mtl/symbol_text_and_icon.hpp | 2 +- include/mbgl/shaders/symbol_layer_ubo.hpp | 4 ++-- shaders/drawable.circle.vertex.glsl | 6 +++--- shaders/drawable.line.fragment.glsl | 4 ++-- shaders/drawable.line.vertex.glsl | 4 ++-- shaders/drawable.line_basic.fragment.glsl | 4 ++-- shaders/drawable.line_basic.vertex.glsl | 4 ++-- shaders/drawable.line_gradient.fragment.glsl | 4 ++-- shaders/drawable.line_gradient.vertex.glsl | 4 ++-- shaders/drawable.line_pattern.fragment.glsl | 4 ++-- shaders/drawable.line_pattern.vertex.glsl | 4 ++-- shaders/drawable.line_sdf.fragment.glsl | 5 +++-- shaders/drawable.line_sdf.vertex.glsl | 5 +++-- shaders/drawable.symbol_icon.vertex.glsl | 2 +- shaders/drawable.symbol_sdf.fragment.glsl | 4 ++-- shaders/drawable.symbol_sdf.vertex.glsl | 2 +- ...rawable.symbol_text_and_icon.fragment.glsl | 4 ++-- .../drawable.symbol_text_and_icon.vertex.glsl | 2 +- src/mbgl/programs/program_parameters.cpp | 2 ++ src/mbgl/programs/program_parameters.hpp | 2 ++ .../renderer/layers/circle_layer_tweaker.cpp | 2 +- .../renderer/layers/line_layer_tweaker.cpp | 10 +++++----- .../renderer/layers/render_fill_layer.cpp | 2 +- .../renderer/layers/symbol_layer_tweaker.cpp | 4 ++-- .../renderer/sources/render_tile_source.cpp | 2 +- .../style/layers/custom_drawable_layer.cpp | 2 +- 44 files changed, 114 insertions(+), 107 deletions(-) diff --git a/include/mbgl/shaders/circle_layer_ubo.hpp b/include/mbgl/shaders/circle_layer_ubo.hpp index f98dfe89ebc..9c347b807e1 100644 --- a/include/mbgl/shaders/circle_layer_ubo.hpp +++ b/include/mbgl/shaders/circle_layer_ubo.hpp @@ -15,8 +15,7 @@ static_assert(sizeof(CircleDrawableUBO) == 5 * 16); struct alignas(16) CirclePaintParamsUBO { /* 0 */ float camera_to_center_distance; - /* 4 */ float device_pixel_ratio; - /* 8 */ float pad1, pad2; + /* 4 */ float pad1, pad2, pad3; /* 16 */ }; static_assert(sizeof(CirclePaintParamsUBO) == 1 * 16); diff --git a/include/mbgl/shaders/gl/drawable_circle.hpp b/include/mbgl/shaders/gl/drawable_circle.hpp index d55b883d029..169c5691d7f 100644 --- a/include/mbgl/shaders/gl/drawable_circle.hpp +++ b/include/mbgl/shaders/gl/drawable_circle.hpp @@ -19,8 +19,8 @@ layout (std140) uniform CircleDrawableUBO { layout (std140) uniform CirclePaintParamsUBO { highp float u_camera_to_center_distance; - lowp float u_device_pixel_ratio; - lowp vec2 pad3_; + lowp float pad3_; + lowp vec2 pad4_; }; layout (std140) uniform CircleEvaluatedPropsUBO { @@ -145,7 +145,7 @@ lowp float stroke_opacity = u_stroke_opacity; // This is a minimum blur distance that serves as a faux-antialiasing for // the circle. since blur is a ratio of the circle's size and the intent is // to keep the blur at roughly 1px, the two are inversely related. - lowp float antialiasblur = 1.0 / u_device_pixel_ratio / (radius + stroke_width); + lowp float antialiasblur = 1.0 / DEVICE_PIXEL_RATIO / (radius + stroke_width); v_data = vec3(extrude.x, extrude.y, antialiasblur); } diff --git a/include/mbgl/shaders/gl/drawable_line.hpp b/include/mbgl/shaders/gl/drawable_line.hpp index 186c394ca9b..6645eded1d8 100644 --- a/include/mbgl/shaders/gl/drawable_line.hpp +++ b/include/mbgl/shaders/gl/drawable_line.hpp @@ -23,7 +23,7 @@ layout (std140) uniform LineUBO { highp mat4 u_matrix; highp vec2 u_units_to_pixels; mediump float u_ratio; - lowp float u_device_pixel_ratio; + lowp float pad0; }; layout (std140) uniform LinePropertiesUBO { @@ -110,7 +110,7 @@ mediump float width = u_width; // the distance over which the line edge fades out. // Retina devices need a smaller distance to avoid aliasing. - float ANTIALIASING = 1.0 / u_device_pixel_ratio / 2.0; + float ANTIALIASING = 1.0 / DEVICE_PIXEL_RATIO / 2.0; vec2 a_extrude = a_data.xy - 128.0; float a_direction = mod(a_data.z, 4.0) - 1.0; @@ -162,7 +162,7 @@ mediump float width = u_width; highp mat4 u_matrix; highp vec2 u_units_to_pixels; mediump float u_ratio; - lowp float u_device_pixel_ratio; + lowp float pad0; }; layout (std140) uniform LinePropertiesUBO { @@ -219,7 +219,7 @@ lowp float opacity = u_opacity; // Calculate the antialiasing fade factor. This is either when fading in // the line in case of an offset line (v_width2.t) or when fading out // (v_width2.s) - float blur2 = (blur + 1.0 / u_device_pixel_ratio) * v_gamma_scale; + float blur2 = (blur + 1.0 / DEVICE_PIXEL_RATIO) * v_gamma_scale; float alpha = clamp(min(dist - (v_width2.t - blur2), v_width2.s - dist) / blur2, 0.0, 1.0); fragColor = color * (alpha * opacity); diff --git a/include/mbgl/shaders/gl/drawable_line_basic.hpp b/include/mbgl/shaders/gl/drawable_line_basic.hpp index e7f4e1256a8..acdbef93cfb 100644 --- a/include/mbgl/shaders/gl/drawable_line_basic.hpp +++ b/include/mbgl/shaders/gl/drawable_line_basic.hpp @@ -23,7 +23,7 @@ layout (std140) uniform LineBasicUBO { highp mat4 u_matrix; highp vec2 u_units_to_pixels; mediump float u_ratio; - lowp float u_device_pixel_ratio; + lowp float pad0; }; layout (std140) uniform LineBasicPropertiesUBO { @@ -42,7 +42,7 @@ out highp float v_linesofar; void main() { // the distance over which the line edge fades out. // Retina devices need a smaller distance to avoid aliasing. - float ANTIALIASING = 1.0 / u_device_pixel_ratio / 2.0; + float ANTIALIASING = 1.0 / DEVICE_PIXEL_RATIO / 2.0; vec2 a_extrude = a_data.xy - 128.0; @@ -81,7 +81,7 @@ void main() { highp mat4 u_matrix; highp vec2 u_units_to_pixels; mediump float u_ratio; - lowp float u_device_pixel_ratio; + lowp float pad0; }; layout (std140) uniform LineBasicPropertiesUBO { @@ -103,7 +103,7 @@ void main() { // Calculate the antialiasing fade factor. This is either when fading in // the line in case of an offset line (v_width2.t) or when fading out // (v_width2.s) - float blur2 = (1.0 / u_device_pixel_ratio) * v_gamma_scale; + float blur2 = (1.0 / DEVICE_PIXEL_RATIO) * v_gamma_scale; float alpha = clamp(min(dist + blur2, v_width - dist) / blur2, 0.0, 1.0); fragColor = u_color * (alpha * u_opacity); diff --git a/include/mbgl/shaders/gl/drawable_line_gradient.hpp b/include/mbgl/shaders/gl/drawable_line_gradient.hpp index c1c44aa5a40..a41f945e444 100644 --- a/include/mbgl/shaders/gl/drawable_line_gradient.hpp +++ b/include/mbgl/shaders/gl/drawable_line_gradient.hpp @@ -27,7 +27,7 @@ layout (std140) uniform LineGradientUBO { highp mat4 u_matrix; highp vec2 u_units_to_pixels; mediump float u_ratio; - lowp float u_device_pixel_ratio; + lowp float pad0; }; layout (std140) uniform LineGradientPropertiesUBO { @@ -104,7 +104,7 @@ mediump float width = u_width; // the distance over which the line edge fades out. // Retina devices need a smaller distance to avoid aliasing. - float ANTIALIASING = 1.0 / u_device_pixel_ratio / 2.0; + float ANTIALIASING = 1.0 / DEVICE_PIXEL_RATIO / 2.0; vec2 a_extrude = a_data.xy - 128.0; float a_direction = mod(a_data.z, 4.0) - 1.0; @@ -156,7 +156,7 @@ mediump float width = u_width; highp mat4 u_matrix; highp vec2 u_units_to_pixels; mediump float u_ratio; - lowp float u_device_pixel_ratio; + lowp float pad0; }; layout (std140) uniform LineGradientPropertiesUBO { @@ -209,7 +209,7 @@ lowp float opacity = u_opacity; // Calculate the antialiasing fade factor. This is either when fading in // the line in case of an offset line (v_width2.t) or when fading out // (v_width2.s) - float blur2 = (blur + 1.0 / u_device_pixel_ratio) * v_gamma_scale; + float blur2 = (blur + 1.0 / DEVICE_PIXEL_RATIO) * v_gamma_scale; float alpha = clamp(min(dist - (v_width2.t - blur2), v_width2.s - dist) / blur2, 0.0, 1.0); // For gradient lines, v_lineprogress is the ratio along the entire line, diff --git a/include/mbgl/shaders/gl/drawable_line_pattern.hpp b/include/mbgl/shaders/gl/drawable_line_pattern.hpp index 391df59c96c..6fe186c5beb 100644 --- a/include/mbgl/shaders/gl/drawable_line_pattern.hpp +++ b/include/mbgl/shaders/gl/drawable_line_pattern.hpp @@ -29,9 +29,9 @@ layout (std140) uniform LinePatternUBO { highp vec2 u_texsize; highp vec2 u_units_to_pixels; mediump float u_ratio; - lowp float u_device_pixel_ratio; highp float u_fade; + lowp float pad0; highp float pad1; }; @@ -133,7 +133,7 @@ mediump vec4 pattern_to = u_pattern_to; // the distance over which the line edge fades out. // Retina devices need a smaller distance to avoid aliasing. - float ANTIALIASING = 1.0 / u_device_pixel_ratio / 2.0; + float ANTIALIASING = 1.0 / DEVICE_PIXEL_RATIO / 2.0; vec2 a_extrude = a_data.xy - 128.0; float a_direction = mod(a_data.z, 4.0) - 1.0; @@ -187,9 +187,9 @@ mediump vec4 pattern_to = u_pattern_to; highp vec2 u_texsize; highp vec2 u_units_to_pixels; mediump float u_ratio; - lowp float u_device_pixel_ratio; highp float u_fade; + lowp float pad0; highp float pad1; }; @@ -278,7 +278,7 @@ lowp float opacity = u_opacity; // Calculate the antialiasing fade factor. This is either when fading in // the line in case of an offset line (v_width2.t) or when fading out // (v_width2.s) - float blur2 = (blur + 1.0 / u_device_pixel_ratio) * v_gamma_scale; + float blur2 = (blur + 1.0 / DEVICE_PIXEL_RATIO) * v_gamma_scale; float alpha = clamp(min(dist - (v_width2.t - blur2), v_width2.s - dist) / blur2, 0.0, 1.0); float x_a = mod(v_linesofar / pattern_size_a.x, 1.0); diff --git a/include/mbgl/shaders/gl/drawable_line_sdf.hpp b/include/mbgl/shaders/gl/drawable_line_sdf.hpp index 37c64108179..eecdcd5cc4f 100644 --- a/include/mbgl/shaders/gl/drawable_line_sdf.hpp +++ b/include/mbgl/shaders/gl/drawable_line_sdf.hpp @@ -29,11 +29,12 @@ layout (std140) uniform LineSDFUBO { highp vec2 u_patternscale_a; highp vec2 u_patternscale_b; mediump float u_ratio; - lowp float u_device_pixel_ratio; highp float u_tex_y_a; highp float u_tex_y_b; highp float u_sdfgamma; highp float u_mix; + + lowp float pad0; }; layout (std140) uniform LineSDFPropertiesUBO { @@ -132,7 +133,7 @@ lowp float floorwidth = u_floorwidth; // the distance over which the line edge fades out. // Retina devices need a smaller distance to avoid aliasing. - float ANTIALIASING = 1.0 / u_device_pixel_ratio / 2.0; + float ANTIALIASING = 1.0 / DEVICE_PIXEL_RATIO / 2.0; vec2 a_extrude = a_data.xy - 128.0; float a_direction = mod(a_data.z, 4.0) - 1.0; @@ -189,11 +190,12 @@ layout (std140) uniform LineSDFUBO { highp vec2 u_patternscale_a; highp vec2 u_patternscale_b; mediump float u_ratio; - lowp float u_device_pixel_ratio; highp float u_tex_y_a; highp float u_tex_y_b; highp float u_sdfgamma; highp float u_mix; + + lowp float pad0; }; layout (std140) uniform LineSDFPropertiesUBO { @@ -267,7 +269,7 @@ lowp float floorwidth = u_floorwidth; // Calculate the antialiasing fade factor. This is either when fading in // the line in case of an offset line (v_width2.t) or when fading out // (v_width2.s) - float blur2 = (blur + 1.0 / u_device_pixel_ratio) * v_gamma_scale; + float blur2 = (blur + 1.0 / DEVICE_PIXEL_RATIO) * v_gamma_scale; float alpha = clamp(min(dist - (v_width2.t - blur2), v_width2.s - dist) / blur2, 0.0, 1.0); float sdfdist_a = texture(u_image, v_tex_a).a; diff --git a/include/mbgl/shaders/gl/drawable_symbol_icon.hpp b/include/mbgl/shaders/gl/drawable_symbol_icon.hpp index 9c62bb798b3..fe648f6537f 100644 --- a/include/mbgl/shaders/gl/drawable_symbol_icon.hpp +++ b/include/mbgl/shaders/gl/drawable_symbol_icon.hpp @@ -30,8 +30,8 @@ layout (std140) uniform SymbolDrawableUBO { layout (std140) uniform SymbolDynamicUBO { highp float u_fade_change; highp float u_camera_to_center_distance; - highp float u_device_pixel_ratio; highp float u_aspect_ratio; + highp float pad0; }; layout (std140) uniform SymbolDrawablePaintUBO { diff --git a/include/mbgl/shaders/gl/drawable_symbol_sdf.hpp b/include/mbgl/shaders/gl/drawable_symbol_sdf.hpp index be2b56a399f..9a6b3d0a7b2 100644 --- a/include/mbgl/shaders/gl/drawable_symbol_sdf.hpp +++ b/include/mbgl/shaders/gl/drawable_symbol_sdf.hpp @@ -38,8 +38,8 @@ layout (std140) uniform SymbolDrawableUBO { layout (std140) uniform SymbolDynamicUBO { highp float u_fade_change; highp float u_camera_to_center_distance; - highp float u_device_pixel_ratio; highp float u_aspect_ratio; + highp float pad0; }; layout (std140) uniform SymbolDrawablePaintUBO { @@ -209,8 +209,8 @@ layout (std140) uniform SymbolDrawableUBO { layout (std140) uniform SymbolDynamicUBO { highp float u_fade_change; highp float u_camera_to_center_distance; - highp float u_device_pixel_ratio; highp float u_aspect_ratio; + highp float pad0; }; layout (std140) uniform SymbolDrawablePaintUBO { @@ -280,7 +280,7 @@ lowp float halo_width = u_halo_width; lowp float halo_blur = u_halo_blur; #endif - float EDGE_GAMMA = 0.105 / u_device_pixel_ratio; + float EDGE_GAMMA = 0.105 / DEVICE_PIXEL_RATIO; vec2 tex = v_data0.xy; float gamma_scale = v_data1.x; diff --git a/include/mbgl/shaders/gl/drawable_symbol_text_and_icon.hpp b/include/mbgl/shaders/gl/drawable_symbol_text_and_icon.hpp index 9589b057b85..7d0f6abe801 100644 --- a/include/mbgl/shaders/gl/drawable_symbol_text_and_icon.hpp +++ b/include/mbgl/shaders/gl/drawable_symbol_text_and_icon.hpp @@ -37,8 +37,8 @@ layout (std140) uniform SymbolDrawableUBO { layout (std140) uniform SymbolDynamicUBO { highp float u_fade_change; highp float u_camera_to_center_distance; - highp float u_device_pixel_ratio; highp float u_aspect_ratio; + highp float pad0; }; layout (std140) uniform SymbolDrawablePaintUBO { @@ -212,8 +212,8 @@ layout (std140) uniform SymbolDrawableUBO { layout (std140) uniform SymbolDynamicUBO { highp float u_fade_change; highp float u_camera_to_center_distance; - highp float u_device_pixel_ratio; highp float u_aspect_ratio; + highp float pad0; }; layout (std140) uniform SymbolDrawablePaintUBO { @@ -299,7 +299,7 @@ lowp float halo_blur = u_halo_blur; vec2 tex = v_data0.xy; - float EDGE_GAMMA = 0.105 / u_device_pixel_ratio; + float EDGE_GAMMA = 0.105 / DEVICE_PIXEL_RATIO; float gamma_scale = v_data1.x; float size = v_data1.y; diff --git a/include/mbgl/shaders/gl/shader_group_gl.hpp b/include/mbgl/shaders/gl/shader_group_gl.hpp index a1c2367df1a..8a517470d0c 100644 --- a/include/mbgl/shaders/gl/shader_group_gl.hpp +++ b/include/mbgl/shaders/gl/shader_group_gl.hpp @@ -26,7 +26,11 @@ class ShaderGroupGL final : public gfx::ShaderGroup { // We could cache these by key here to avoid creating a string key each time, but we // would need another mutex. We could also push string IDs down into `ShaderGroup`. - const std::string shaderName = getShaderName(name, propertyHash(propertiesAsUniforms)); + std::size_t seed = 0; + mbgl::util::hash_combine(seed, propertyHash(propertiesAsUniforms)); + mbgl::util::hash_combine(seed, programParameters.getDefinesHash()); + const std::string shaderName = getShaderName(name, seed); + auto shader = get(shaderName); if (shader) { return shader; diff --git a/include/mbgl/shaders/line_layer_ubo.hpp b/include/mbgl/shaders/line_layer_ubo.hpp index e1959e97fd3..43859adf7fc 100644 --- a/include/mbgl/shaders/line_layer_ubo.hpp +++ b/include/mbgl/shaders/line_layer_ubo.hpp @@ -9,7 +9,7 @@ struct alignas(16) LineUBO { std::array matrix; std::array units_to_pixels; float ratio; - float device_pixel_ratio; + float pad; }; static_assert(sizeof(LineUBO) % 16 == 0); @@ -42,9 +42,8 @@ struct alignas(16) LinePatternUBO { std::array texsize; std::array units_to_pixels; float ratio; - float device_pixel_ratio; float fade; - float pad1; + float pad1, pad2; }; static_assert(sizeof(LinePatternUBO) % 16 == 0); @@ -64,11 +63,11 @@ struct alignas(16) LineSDFUBO { std::array patternscale_a; std::array patternscale_b; float ratio; - float device_pixel_ratio; float tex_y_a; float tex_y_b; float sdfgamma; float mix; + float pad; }; static_assert(sizeof(LineSDFUBO) % 16 == 0); diff --git a/include/mbgl/shaders/mtl/circle.hpp b/include/mbgl/shaders/mtl/circle.hpp index 9bf0b1b2571..9f107985301 100644 --- a/include/mbgl/shaders/mtl/circle.hpp +++ b/include/mbgl/shaders/mtl/circle.hpp @@ -80,8 +80,7 @@ struct alignas(16) CircleDrawableUBO { }; struct alignas(16) CirclePaintParamsUBO { float camera_to_center_distance; - float device_pixel_ratio; - float pad1,pad2; + float pad1,pad2,pad3; }; struct alignas(16) CircleEvaluatedPropsUBO { float4 color; @@ -157,7 +156,7 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], // This is a minimum blur distance that serves as a faux-antialiasing for // the circle. since blur is a ratio of the circle's size and the intent is // to keep the blur at roughly 1px, the two are inversely related. - const half antialiasblur = 1.0 / params.device_pixel_ratio / (radius + stroke_width); + const half antialiasblur = 1.0 / DEVICE_PIXEL_RATIO / (radius + stroke_width); return { .position = position, diff --git a/include/mbgl/shaders/mtl/common.hpp b/include/mbgl/shaders/mtl/common.hpp index 8a3c2586ec0..da318e2cf6b 100644 --- a/include/mbgl/shaders/mtl/common.hpp +++ b/include/mbgl/shaders/mtl/common.hpp @@ -58,21 +58,21 @@ struct alignas(16) LineUBO { float4x4 matrix; float2 units_to_pixels; float ratio; - float device_pixel_ratio; + float pad; }; struct alignas(16) LineBasicUBO { float4x4 matrix; float2 units_to_pixels; float ratio; - float device_pixel_ratio; + float pad; }; struct alignas(16) LineGradientUBO { float4x4 matrix; float2 units_to_pixels; float ratio; - float device_pixel_ratio; + float pad; }; struct alignas(16) LinePropertiesUBO { @@ -159,8 +159,8 @@ static_assert(sizeof(SymbolDrawableUBO) == 14 * 16, "unexpected padding"); struct alignas(16) SymbolDynamicUBO { float fade_change; float camera_to_center_distance; - float device_pixel_ratio; float aspect_ratio; + float pad; }; static_assert(sizeof(SymbolDynamicUBO) == 16, "unexpected padding"); diff --git a/include/mbgl/shaders/mtl/line.hpp b/include/mbgl/shaders/mtl/line.hpp index 09bae7178a5..c96569cb793 100644 --- a/include/mbgl/shaders/mtl/line.hpp +++ b/include/mbgl/shaders/mtl/line.hpp @@ -83,7 +83,7 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], // the distance over which the line edge fades out. // Retina devices need a smaller distance to avoid aliasing. - const float ANTIALIASING = 1.0 / line.device_pixel_ratio / 2.0; + const float ANTIALIASING = 1.0 / DEVICE_PIXEL_RATIO / 2.0; const float2 a_extrude = float2(vertx.data.xy) - 128.0; const float a_direction = fmod(float(vertx.data.z), 4.0) - 1.0; @@ -163,7 +163,7 @@ half4 fragment fragmentMain(FragmentStage in [[stage_in]], // Calculate the antialiasing fade factor. This is either when fading in the // line in case of an offset line (v_width2.y) or when fading out (v_width2.x) - const float blur2 = (blur + 1.0 / line.device_pixel_ratio) * in.gamma_scale; + const float blur2 = (blur + 1.0 / DEVICE_PIXEL_RATIO) * in.gamma_scale; const float alpha = clamp(min(dist - (in.width2.y - blur2), in.width2.x - dist) / blur2, 0.0, 1.0); return half4(color * (alpha * opacity)); @@ -236,9 +236,8 @@ struct alignas(16) LinePatternUBO { float2 texsize; float2 units_to_pixels; float ratio; - float device_pixel_ratio; float fade; - float pad1; + float pad1, pad2; }; struct alignas(16) LinePatternPropertiesUBO { @@ -290,7 +289,7 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], // the distance over which the line edge fades out. // Retina devices need a smaller distance to avoid aliasing. - const float ANTIALIASING = 1.0 / line.device_pixel_ratio / 2.0; + const float ANTIALIASING = 1.0 / DEVICE_PIXEL_RATIO / 2.0; const float LINE_DISTANCE_SCALE = 2.0; const float2 a_extrude = float2(vertx.data.xy) - 128.0; @@ -490,11 +489,11 @@ struct alignas(16) LineSDFUBO { float2 patternscale_a; float2 patternscale_b; float ratio; - float device_pixel_ratio; float tex_y_a; float tex_y_b; float sdfgamma; float mix; + float pad; }; struct alignas(16) LineSDFPropertiesUBO { @@ -547,7 +546,7 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], // the distance over which the line edge fades out. // Retina devices need a smaller distance to avoid aliasing. - const float ANTIALIASING = 1.0 / line.device_pixel_ratio / 2.0; + const float ANTIALIASING = 1.0 / DEVICE_PIXEL_RATIO / 2.0; const float LINE_DISTANCE_SCALE = 2.0; const float2 a_extrude = float2(vertx.data.xy) - 128.0; @@ -638,7 +637,7 @@ half4 fragment fragmentMain(FragmentStage in [[stage_in]], // Calculate the antialiasing fade factor. This is either when fading in the // line in case of an offset line (`v_width2.y`) or when fading out (`v_width2.x`) - const float blur2 = (blur + 1.0 / line.device_pixel_ratio) * in.gamma_scale; + const float blur2 = (blur + 1.0 / DEVICE_PIXEL_RATIO) * in.gamma_scale; const float sdfdist_a = image0.sample(image0_sampler, in.tex_a).a; const float sdfdist_b = image0.sample(image0_sampler, in.tex_b).a; @@ -681,7 +680,7 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], // the distance over which the line edge fades out. // Retina devices need a smaller distance to avoid aliasing. - const float ANTIALIASING = 1.0 / line.device_pixel_ratio / 2.0; + const float ANTIALIASING = 1.0 / DEVICE_PIXEL_RATIO / 2.0; const float2 a_extrude = float2(vertx.data.xy) - 128.0; const float2 pos = floor(float2(vertx.pos_normal) * 0.5); @@ -722,7 +721,7 @@ half4 fragment fragmentMain(FragmentStage in [[stage_in]], // Calculate the antialiasing fade factor. This is either when fading in the // line in case of an offset line (`v_width2.y`) or when fading out (`v_width2.x`) - const float blur2 = (1.0 / line.device_pixel_ratio) * in.gamma_scale; + const float blur2 = (1.0 / DEVICE_PIXEL_RATIO) * in.gamma_scale; const float alpha = clamp(min(dist + blur2, in.width2 - dist) / blur2, 0.0, 1.0); return half4(props.color * (alpha * props.opacity)); diff --git a/include/mbgl/shaders/mtl/line_gradient.hpp b/include/mbgl/shaders/mtl/line_gradient.hpp index 9c0eb3b8414..b8cf813fc93 100644 --- a/include/mbgl/shaders/mtl/line_gradient.hpp +++ b/include/mbgl/shaders/mtl/line_gradient.hpp @@ -73,7 +73,7 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], // the distance over which the line edge fades out. // Retina devices need a smaller distance to avoid aliasing. - const float ANTIALIASING = 1.0 / line.device_pixel_ratio / 2.0; + const float ANTIALIASING = 1.0 / DEVICE_PIXEL_RATIO / 2.0; const float2 a_extrude = float2(vertx.data.xy) - 128.0; const float a_direction = fmod(float(vertx.data.z), 4.0) - 1.0; @@ -148,7 +148,7 @@ half4 fragment fragmentMain(FragmentStage in [[stage_in]], // Calculate the antialiasing fade factor. This is either when fading in the // line in case of an offset line (v_width2.y) or when fading out (v_width2.x) - const float blur2 = (blur + 1.0 / line.device_pixel_ratio) * in.gamma_scale; + const float blur2 = (blur + 1.0 / DEVICE_PIXEL_RATIO) * in.gamma_scale; const float alpha = clamp(min(dist - (in.width2.y - blur2), in.width2.x - dist) / blur2, 0.0, 1.0); // For gradient lines, v_lineprogress is the ratio along the entire line, diff --git a/include/mbgl/shaders/mtl/shader_group.hpp b/include/mbgl/shaders/mtl/shader_group.hpp index 06c987ed186..a3b8e990423 100644 --- a/include/mbgl/shaders/mtl/shader_group.hpp +++ b/include/mbgl/shaders/mtl/shader_group.hpp @@ -25,10 +25,7 @@ class ShaderGroupBase : public gfx::ShaderGroup { using DefinesMap = mbgl::unordered_map; void addAdditionalDefines(const StringIDSet& propertiesAsUniforms, DefinesMap& additionalDefines) { - additionalDefines.reserve(propertiesAsUniforms.size() + 1); - if (programParameters.getOverdrawInspectorEnabled()) { - additionalDefines.insert(std::make_pair(overdrawName, std::string())); - } + additionalDefines.reserve(propertiesAsUniforms.size()); for (const auto nameID : propertiesAsUniforms) { // We expect the names to be prefixed by "a_", but we need just the base here. const auto name = stringIndexer().get(nameID); @@ -41,7 +38,6 @@ class ShaderGroupBase : public gfx::ShaderGroup { private: static constexpr auto uniformPrefix = "HAS_UNIFORM_u_"; - static constexpr auto overdrawName = "OVERDRAW_INSPECTOR"; }; template @@ -60,7 +56,10 @@ class ShaderGroup final : public ShaderGroupBase { constexpr auto& vertMain = ShaderSource::vertexMainFunction; constexpr auto& fragMain = ShaderSource::fragmentMainFunction; - const std::string shaderName = getShaderName(name, propertyHash(propertiesAsUniforms)); + std::size_t seed = 0; + mbgl::util::hash_combine(seed, propertyHash(propertiesAsUniforms)); + mbgl::util::hash_combine(seed, programParameters.getDefinesHash()); + const std::string shaderName = getShaderName(name, seed); auto shader = get(shaderName); if (!shader) { diff --git a/include/mbgl/shaders/mtl/symbol_sdf.hpp b/include/mbgl/shaders/mtl/symbol_sdf.hpp index b080edd9687..1d5e5b8139a 100644 --- a/include/mbgl/shaders/mtl/symbol_sdf.hpp +++ b/include/mbgl/shaders/mtl/symbol_sdf.hpp @@ -200,7 +200,7 @@ half4 fragment fragmentMain(FragmentStage in [[stage_in]], const float halo_blur = in.halo_blur; #endif - const float EDGE_GAMMA = 0.105 / dynamic.device_pixel_ratio; + const float EDGE_GAMMA = 0.105 / DEVICE_PIXEL_RATIO; const float fontGamma = in.fontScale * drawable.gamma_scale; const half4 color = props.is_halo ? halo_color : fill_color; const float gamma = ((props.is_halo ? (halo_blur * 1.19 / SDF_PX) : 0) + EDGE_GAMMA) / fontGamma; diff --git a/include/mbgl/shaders/mtl/symbol_text_and_icon.hpp b/include/mbgl/shaders/mtl/symbol_text_and_icon.hpp index f21d3386d62..4fc259c3e07 100644 --- a/include/mbgl/shaders/mtl/symbol_text_and_icon.hpp +++ b/include/mbgl/shaders/mtl/symbol_text_and_icon.hpp @@ -217,7 +217,7 @@ half4 fragment fragmentMain(FragmentStage in [[stage_in]], return half4(icon_image.sample(icon_sampler, float2(in.tex)) * alpha); } - const float EDGE_GAMMA = 0.105 / dynamic.device_pixel_ratio; + const float EDGE_GAMMA = 0.105 / DEVICE_PIXEL_RATIO; const half4 color = props.is_halo ? halo_color : fill_color; const float fontGamma = in.fontScale * drawable.gamma_scale; const float gamma = ((props.is_halo ? (halo_blur * 1.19 / SDF_PX) : 0) + EDGE_GAMMA) / fontGamma; diff --git a/include/mbgl/shaders/symbol_layer_ubo.hpp b/include/mbgl/shaders/symbol_layer_ubo.hpp index d01868870b3..7c93fb56764 100644 --- a/include/mbgl/shaders/symbol_layer_ubo.hpp +++ b/include/mbgl/shaders/symbol_layer_ubo.hpp @@ -51,8 +51,8 @@ static_assert(sizeof(SymbolDrawableUBO) == 14 * 16); struct alignas(16) SymbolDynamicUBO { /* 0 */ float fade_change; /* 4 */ float camera_to_center_distance; - /* 8 */ float device_pixel_ratio; - /* 12 */ float aspect_ratio; + /* 8 */ float aspect_ratio; + /* 12 */ float pad; /* 16 */ }; static_assert(sizeof(SymbolDynamicUBO) == 16); diff --git a/shaders/drawable.circle.vertex.glsl b/shaders/drawable.circle.vertex.glsl index ac7612bb90c..5e6c784c620 100644 --- a/shaders/drawable.circle.vertex.glsl +++ b/shaders/drawable.circle.vertex.glsl @@ -9,8 +9,8 @@ layout (std140) uniform CircleDrawableUBO { layout (std140) uniform CirclePaintParamsUBO { highp float u_camera_to_center_distance; - lowp float u_device_pixel_ratio; - lowp vec2 pad3_; + lowp float pad3_; + lowp vec2 pad4_; }; layout (std140) uniform CircleEvaluatedPropsUBO { @@ -86,7 +86,7 @@ void main(void) { // This is a minimum blur distance that serves as a faux-antialiasing for // the circle. since blur is a ratio of the circle's size and the intent is // to keep the blur at roughly 1px, the two are inversely related. - lowp float antialiasblur = 1.0 / u_device_pixel_ratio / (radius + stroke_width); + lowp float antialiasblur = 1.0 / DEVICE_PIXEL_RATIO / (radius + stroke_width); v_data = vec3(extrude.x, extrude.y, antialiasblur); } diff --git a/shaders/drawable.line.fragment.glsl b/shaders/drawable.line.fragment.glsl index 88167104f87..a8e354c9627 100644 --- a/shaders/drawable.line.fragment.glsl +++ b/shaders/drawable.line.fragment.glsl @@ -2,7 +2,7 @@ layout (std140) uniform LineUBO { highp mat4 u_matrix; highp vec2 u_units_to_pixels; mediump float u_ratio; - lowp float u_device_pixel_ratio; + lowp float pad0; }; layout (std140) uniform LinePropertiesUBO { @@ -47,7 +47,7 @@ void main() { // Calculate the antialiasing fade factor. This is either when fading in // the line in case of an offset line (v_width2.t) or when fading out // (v_width2.s) - float blur2 = (blur + 1.0 / u_device_pixel_ratio) * v_gamma_scale; + float blur2 = (blur + 1.0 / DEVICE_PIXEL_RATIO) * v_gamma_scale; float alpha = clamp(min(dist - (v_width2.t - blur2), v_width2.s - dist) / blur2, 0.0, 1.0); fragColor = color * (alpha * opacity); diff --git a/shaders/drawable.line.vertex.glsl b/shaders/drawable.line.vertex.glsl index e68edd525ce..169564ab8db 100644 --- a/shaders/drawable.line.vertex.glsl +++ b/shaders/drawable.line.vertex.glsl @@ -13,7 +13,7 @@ layout (std140) uniform LineUBO { highp mat4 u_matrix; highp vec2 u_units_to_pixels; mediump float u_ratio; - lowp float u_device_pixel_ratio; + lowp float pad0; }; layout (std140) uniform LinePropertiesUBO { @@ -61,7 +61,7 @@ void main() { // the distance over which the line edge fades out. // Retina devices need a smaller distance to avoid aliasing. - float ANTIALIASING = 1.0 / u_device_pixel_ratio / 2.0; + float ANTIALIASING = 1.0 / DEVICE_PIXEL_RATIO / 2.0; vec2 a_extrude = a_data.xy - 128.0; float a_direction = mod(a_data.z, 4.0) - 1.0; diff --git a/shaders/drawable.line_basic.fragment.glsl b/shaders/drawable.line_basic.fragment.glsl index 681a5ed3c3e..a7b00637703 100644 --- a/shaders/drawable.line_basic.fragment.glsl +++ b/shaders/drawable.line_basic.fragment.glsl @@ -2,7 +2,7 @@ layout (std140) uniform LineBasicUBO { highp mat4 u_matrix; highp vec2 u_units_to_pixels; mediump float u_ratio; - lowp float u_device_pixel_ratio; + lowp float pad0; }; layout (std140) uniform LineBasicPropertiesUBO { @@ -24,7 +24,7 @@ void main() { // Calculate the antialiasing fade factor. This is either when fading in // the line in case of an offset line (v_width2.t) or when fading out // (v_width2.s) - float blur2 = (1.0 / u_device_pixel_ratio) * v_gamma_scale; + float blur2 = (1.0 / DEVICE_PIXEL_RATIO) * v_gamma_scale; float alpha = clamp(min(dist + blur2, v_width - dist) / blur2, 0.0, 1.0); fragColor = u_color * (alpha * u_opacity); diff --git a/shaders/drawable.line_basic.vertex.glsl b/shaders/drawable.line_basic.vertex.glsl index a73879f78df..74b4ca7e52f 100644 --- a/shaders/drawable.line_basic.vertex.glsl +++ b/shaders/drawable.line_basic.vertex.glsl @@ -13,7 +13,7 @@ layout (std140) uniform LineBasicUBO { highp mat4 u_matrix; highp vec2 u_units_to_pixels; mediump float u_ratio; - lowp float u_device_pixel_ratio; + lowp float pad0; }; layout (std140) uniform LineBasicPropertiesUBO { @@ -32,7 +32,7 @@ out highp float v_linesofar; void main() { // the distance over which the line edge fades out. // Retina devices need a smaller distance to avoid aliasing. - float ANTIALIASING = 1.0 / u_device_pixel_ratio / 2.0; + float ANTIALIASING = 1.0 / DEVICE_PIXEL_RATIO / 2.0; vec2 a_extrude = a_data.xy - 128.0; diff --git a/shaders/drawable.line_gradient.fragment.glsl b/shaders/drawable.line_gradient.fragment.glsl index 1f1465b9d0f..4e3c6856d4f 100644 --- a/shaders/drawable.line_gradient.fragment.glsl +++ b/shaders/drawable.line_gradient.fragment.glsl @@ -2,7 +2,7 @@ layout (std140) uniform LineGradientUBO { highp mat4 u_matrix; highp vec2 u_units_to_pixels; mediump float u_ratio; - lowp float u_device_pixel_ratio; + lowp float pad0; }; layout (std140) uniform LineGradientPropertiesUBO { @@ -47,7 +47,7 @@ void main() { // Calculate the antialiasing fade factor. This is either when fading in // the line in case of an offset line (v_width2.t) or when fading out // (v_width2.s) - float blur2 = (blur + 1.0 / u_device_pixel_ratio) * v_gamma_scale; + float blur2 = (blur + 1.0 / DEVICE_PIXEL_RATIO) * v_gamma_scale; float alpha = clamp(min(dist - (v_width2.t - blur2), v_width2.s - dist) / blur2, 0.0, 1.0); // For gradient lines, v_lineprogress is the ratio along the entire line, diff --git a/shaders/drawable.line_gradient.vertex.glsl b/shaders/drawable.line_gradient.vertex.glsl index d56c52a0e54..e41ca7901a5 100644 --- a/shaders/drawable.line_gradient.vertex.glsl +++ b/shaders/drawable.line_gradient.vertex.glsl @@ -17,7 +17,7 @@ layout (std140) uniform LineGradientUBO { highp mat4 u_matrix; highp vec2 u_units_to_pixels; mediump float u_ratio; - lowp float u_device_pixel_ratio; + lowp float pad0; }; layout (std140) uniform LineGradientPropertiesUBO { @@ -62,7 +62,7 @@ void main() { // the distance over which the line edge fades out. // Retina devices need a smaller distance to avoid aliasing. - float ANTIALIASING = 1.0 / u_device_pixel_ratio / 2.0; + float ANTIALIASING = 1.0 / DEVICE_PIXEL_RATIO / 2.0; vec2 a_extrude = a_data.xy - 128.0; float a_direction = mod(a_data.z, 4.0) - 1.0; diff --git a/shaders/drawable.line_pattern.fragment.glsl b/shaders/drawable.line_pattern.fragment.glsl index a6548afb9e0..30b5a7c0c22 100644 --- a/shaders/drawable.line_pattern.fragment.glsl +++ b/shaders/drawable.line_pattern.fragment.glsl @@ -4,9 +4,9 @@ layout (std140) uniform LinePatternUBO { highp vec2 u_texsize; highp vec2 u_units_to_pixels; mediump float u_ratio; - lowp float u_device_pixel_ratio; highp float u_fade; + lowp float pad0; highp float pad1; }; @@ -79,7 +79,7 @@ void main() { // Calculate the antialiasing fade factor. This is either when fading in // the line in case of an offset line (v_width2.t) or when fading out // (v_width2.s) - float blur2 = (blur + 1.0 / u_device_pixel_ratio) * v_gamma_scale; + float blur2 = (blur + 1.0 / DEVICE_PIXEL_RATIO) * v_gamma_scale; float alpha = clamp(min(dist - (v_width2.t - blur2), v_width2.s - dist) / blur2, 0.0, 1.0); float x_a = mod(v_linesofar / pattern_size_a.x, 1.0); diff --git a/shaders/drawable.line_pattern.vertex.glsl b/shaders/drawable.line_pattern.vertex.glsl index 21b2d3e003c..600e8f3af6e 100644 --- a/shaders/drawable.line_pattern.vertex.glsl +++ b/shaders/drawable.line_pattern.vertex.glsl @@ -19,9 +19,9 @@ layout (std140) uniform LinePatternUBO { highp vec2 u_texsize; highp vec2 u_units_to_pixels; mediump float u_ratio; - lowp float u_device_pixel_ratio; highp float u_fade; + lowp float pad0; highp float pad1; }; @@ -77,7 +77,7 @@ void main() { // the distance over which the line edge fades out. // Retina devices need a smaller distance to avoid aliasing. - float ANTIALIASING = 1.0 / u_device_pixel_ratio / 2.0; + float ANTIALIASING = 1.0 / DEVICE_PIXEL_RATIO / 2.0; vec2 a_extrude = a_data.xy - 128.0; float a_direction = mod(a_data.z, 4.0) - 1.0; diff --git a/shaders/drawable.line_sdf.fragment.glsl b/shaders/drawable.line_sdf.fragment.glsl index c154788850a..64b2c406db9 100644 --- a/shaders/drawable.line_sdf.fragment.glsl +++ b/shaders/drawable.line_sdf.fragment.glsl @@ -5,11 +5,12 @@ layout (std140) uniform LineSDFUBO { highp vec2 u_patternscale_a; highp vec2 u_patternscale_b; mediump float u_ratio; - lowp float u_device_pixel_ratio; highp float u_tex_y_a; highp float u_tex_y_b; highp float u_sdfgamma; highp float u_mix; + + lowp float pad0; }; layout (std140) uniform LineSDFPropertiesUBO { @@ -63,7 +64,7 @@ void main() { // Calculate the antialiasing fade factor. This is either when fading in // the line in case of an offset line (v_width2.t) or when fading out // (v_width2.s) - float blur2 = (blur + 1.0 / u_device_pixel_ratio) * v_gamma_scale; + float blur2 = (blur + 1.0 / DEVICE_PIXEL_RATIO) * v_gamma_scale; float alpha = clamp(min(dist - (v_width2.t - blur2), v_width2.s - dist) / blur2, 0.0, 1.0); float sdfdist_a = texture(u_image, v_tex_a).a; diff --git a/shaders/drawable.line_sdf.vertex.glsl b/shaders/drawable.line_sdf.vertex.glsl index 87985783086..cc41cf61be4 100644 --- a/shaders/drawable.line_sdf.vertex.glsl +++ b/shaders/drawable.line_sdf.vertex.glsl @@ -19,11 +19,12 @@ layout (std140) uniform LineSDFUBO { highp vec2 u_patternscale_a; highp vec2 u_patternscale_b; mediump float u_ratio; - lowp float u_device_pixel_ratio; highp float u_tex_y_a; highp float u_tex_y_b; highp float u_sdfgamma; highp float u_mix; + + lowp float pad0; }; layout (std140) uniform LineSDFPropertiesUBO { @@ -75,7 +76,7 @@ void main() { // the distance over which the line edge fades out. // Retina devices need a smaller distance to avoid aliasing. - float ANTIALIASING = 1.0 / u_device_pixel_ratio / 2.0; + float ANTIALIASING = 1.0 / DEVICE_PIXEL_RATIO / 2.0; vec2 a_extrude = a_data.xy - 128.0; float a_direction = mod(a_data.z, 4.0) - 1.0; diff --git a/shaders/drawable.symbol_icon.vertex.glsl b/shaders/drawable.symbol_icon.vertex.glsl index eebbe2c2776..bd902925f0e 100644 --- a/shaders/drawable.symbol_icon.vertex.glsl +++ b/shaders/drawable.symbol_icon.vertex.glsl @@ -20,8 +20,8 @@ layout (std140) uniform SymbolDrawableUBO { layout (std140) uniform SymbolDynamicUBO { highp float u_fade_change; highp float u_camera_to_center_distance; - highp float u_device_pixel_ratio; highp float u_aspect_ratio; + highp float pad0; }; layout (std140) uniform SymbolDrawablePaintUBO { diff --git a/shaders/drawable.symbol_sdf.fragment.glsl b/shaders/drawable.symbol_sdf.fragment.glsl index 980d3025192..a96bc3c250b 100644 --- a/shaders/drawable.symbol_sdf.fragment.glsl +++ b/shaders/drawable.symbol_sdf.fragment.glsl @@ -16,8 +16,8 @@ layout (std140) uniform SymbolDrawableUBO { layout (std140) uniform SymbolDynamicUBO { highp float u_fade_change; highp float u_camera_to_center_distance; - highp float u_device_pixel_ratio; highp float u_aspect_ratio; + highp float pad0; }; layout (std140) uniform SymbolDrawablePaintUBO { @@ -67,7 +67,7 @@ void main() { #pragma mapbox: initialize lowp float halo_width #pragma mapbox: initialize lowp float halo_blur - float EDGE_GAMMA = 0.105 / u_device_pixel_ratio; + float EDGE_GAMMA = 0.105 / DEVICE_PIXEL_RATIO; vec2 tex = v_data0.xy; float gamma_scale = v_data1.x; diff --git a/shaders/drawable.symbol_sdf.vertex.glsl b/shaders/drawable.symbol_sdf.vertex.glsl index 6c6287f15b3..e7a5d85c52c 100644 --- a/shaders/drawable.symbol_sdf.vertex.glsl +++ b/shaders/drawable.symbol_sdf.vertex.glsl @@ -28,8 +28,8 @@ layout (std140) uniform SymbolDrawableUBO { layout (std140) uniform SymbolDynamicUBO { highp float u_fade_change; highp float u_camera_to_center_distance; - highp float u_device_pixel_ratio; highp float u_aspect_ratio; + highp float pad0; }; layout (std140) uniform SymbolDrawablePaintUBO { diff --git a/shaders/drawable.symbol_text_and_icon.fragment.glsl b/shaders/drawable.symbol_text_and_icon.fragment.glsl index 44c4931d23b..c956d4daa84 100644 --- a/shaders/drawable.symbol_text_and_icon.fragment.glsl +++ b/shaders/drawable.symbol_text_and_icon.fragment.glsl @@ -19,8 +19,8 @@ layout (std140) uniform SymbolDrawableUBO { layout (std140) uniform SymbolDynamicUBO { highp float u_fade_change; highp float u_camera_to_center_distance; - highp float u_device_pixel_ratio; highp float u_aspect_ratio; + highp float pad0; }; layout (std140) uniform SymbolDrawablePaintUBO { @@ -86,7 +86,7 @@ void main() { vec2 tex = v_data0.xy; - float EDGE_GAMMA = 0.105 / u_device_pixel_ratio; + float EDGE_GAMMA = 0.105 / DEVICE_PIXEL_RATIO; float gamma_scale = v_data1.x; float size = v_data1.y; diff --git a/shaders/drawable.symbol_text_and_icon.vertex.glsl b/shaders/drawable.symbol_text_and_icon.vertex.glsl index 4c8156c4056..9390c584ab6 100644 --- a/shaders/drawable.symbol_text_and_icon.vertex.glsl +++ b/shaders/drawable.symbol_text_and_icon.vertex.glsl @@ -27,8 +27,8 @@ layout (std140) uniform SymbolDrawableUBO { layout (std140) uniform SymbolDynamicUBO { highp float u_fade_change; highp float u_camera_to_center_distance; - highp float u_device_pixel_ratio; highp float u_aspect_ratio; + highp float pad0; }; layout (std140) uniform SymbolDrawablePaintUBO { diff --git a/src/mbgl/programs/program_parameters.cpp b/src/mbgl/programs/program_parameters.cpp index bacbfdb4872..197f14e44bd 100644 --- a/src/mbgl/programs/program_parameters.cpp +++ b/src/mbgl/programs/program_parameters.cpp @@ -1,5 +1,6 @@ #include +#include #include #include @@ -14,6 +15,7 @@ ProgramParameters::ProgramParameters(const float pixelRatio, const bool overdraw if (overdraw) { defines["OVERDRAW_INSPECTOR"] = std::string(); } + definesHash = util::hash(getDefinesString()); } ProgramParameters ProgramParameters::withShaderSource(const ProgramSource& source) const noexcept { diff --git a/src/mbgl/programs/program_parameters.hpp b/src/mbgl/programs/program_parameters.hpp index 52d696d5d43..a4936a97229 100644 --- a/src/mbgl/programs/program_parameters.hpp +++ b/src/mbgl/programs/program_parameters.hpp @@ -62,9 +62,11 @@ class ProgramParameters { const std::string& fragmentSource(gfx::Backend::Type backend) const; bool getOverdrawInspectorEnabled() const { return overdrawInspector; } + std::uint64_t getDefinesHash() const { return definesHash; } private: std::unordered_map defines; + std::uint64_t definesHash; // cached value of `defines` converted to string format mutable std::string definesStr; diff --git a/src/mbgl/renderer/layers/circle_layer_tweaker.cpp b/src/mbgl/renderer/layers/circle_layer_tweaker.cpp index 17cee3ff8f5..2b450e31d7d 100644 --- a/src/mbgl/renderer/layers/circle_layer_tweaker.cpp +++ b/src/mbgl/renderer/layers/circle_layer_tweaker.cpp @@ -42,8 +42,8 @@ void CircleLayerTweaker::execute(LayerGroupBase& layerGroup, const PaintParamete // Updated every frame, but shared across drawables const CirclePaintParamsUBO paintParamsUBO = { /* .camera_to_center_distance = */ parameters.state.getCameraToCenterDistance(), - /* .device_pixel_ratio = */ parameters.pixelRatio, /* .padding = */ 0, + 0, 0}; if (!paintParamsUniformBuffer) { diff --git a/src/mbgl/renderer/layers/line_layer_tweaker.cpp b/src/mbgl/renderer/layers/line_layer_tweaker.cpp index 3136dd8d9ae..4f46e255e1f 100644 --- a/src/mbgl/renderer/layers/line_layer_tweaker.cpp +++ b/src/mbgl/renderer/layers/line_layer_tweaker.cpp @@ -130,7 +130,7 @@ void LineLayerTweaker::execute(LayerGroupBase& layerGroup, const PaintParameters /*matrix = */ util::cast(matrix), /*units_to_pixels = */ {1.0f / parameters.pixelsToGLUnits[0], 1.0f / parameters.pixelsToGLUnits[1]}, /*ratio = */ 1.0f / tileID.pixelsToTileUnits(1.0f, static_cast(zoom)), - /*device_pixel_ratio = */ parameters.pixelRatio}; + 0}; uniforms.createOrUpdate(idLineUBOName, &lineUBO, context); // properties UBO @@ -142,7 +142,7 @@ void LineLayerTweaker::execute(LayerGroupBase& layerGroup, const PaintParameters /*matrix = */ util::cast(matrix), /*units_to_pixels = */ {1.0f / parameters.pixelsToGLUnits[0], 1.0f / parameters.pixelsToGLUnits[1]}, /*ratio = */ 1.0f / tileID.pixelsToTileUnits(1.0f, static_cast(zoom)), - /*device_pixel_ratio = */ parameters.pixelRatio}; + 0}; uniforms.createOrUpdate(idLineGradientUBOName, &lineGradientUBO, context); // properties UBO @@ -165,8 +165,8 @@ void LineLayerTweaker::execute(LayerGroupBase& layerGroup, const PaintParameters /*texsize =*/{static_cast(textureSize.width), static_cast(textureSize.height)}, /*units_to_pixels =*/{1.0f / parameters.pixelsToGLUnits[0], 1.0f / parameters.pixelsToGLUnits[1]}, /*ratio =*/1.0f / tileID.pixelsToTileUnits(1.0f, static_cast(zoom)), - /*device_pixel_ratio =*/parameters.pixelRatio, /*fade =*/crossfade.t, + 0, 0}; uniforms.createOrUpdate(idLinePatternUBOName, &linePatternUBO, context); @@ -208,12 +208,12 @@ void LineLayerTweaker::execute(LayerGroupBase& layerGroup, const PaintParameters -posB.height / 2.0f}, /* ratio = */ 1.0f / tileID.pixelsToTileUnits(1.0f, static_cast(parameters.state.getZoom())), - /* device_pixel_ratio = */ parameters.pixelRatio, /* tex_y_a = */ posA.y, /* tex_y_b = */ posB.y, /* sdfgamma = */ static_cast(dashPatternTexture.getSize().width) / (std::min(widthA, widthB) * 256.0f * parameters.pixelRatio) / 2.0f, - /* mix = */ crossfade.t}; + /* mix = */ crossfade.t, + 0}; uniforms.createOrUpdate(idLineSDFUBOName, &lineSDFUBO, context); // properties UBO diff --git a/src/mbgl/renderer/layers/render_fill_layer.cpp b/src/mbgl/renderer/layers/render_fill_layer.cpp index 8c61a5853e5..d3bd88c6799 100644 --- a/src/mbgl/renderer/layers/render_fill_layer.cpp +++ b/src/mbgl/renderer/layers/render_fill_layer.cpp @@ -374,7 +374,7 @@ class OutlineDrawableTweaker : public gfx::DrawableTweaker { /*matrix = */ util::cast(matrix), /*units_to_pixels = */ {1.0f / parameters.pixelsToGLUnits[0], 1.0f / parameters.pixelsToGLUnits[1]}, /*ratio = */ 1.0f / tileID.pixelsToTileUnits(1.0f, zoom), - /*device_pixel_ratio = */ parameters.pixelRatio}; + 0}; parameters.context.emplaceOrUpdateUniformBuffer(lineUniformBuffer, &lineUBO); } uniforms.addOrReplace(idLineUBOName, lineUniformBuffer); diff --git a/src/mbgl/renderer/layers/symbol_layer_tweaker.cpp b/src/mbgl/renderer/layers/symbol_layer_tweaker.cpp index feee50e547a..715bc738a7e 100644 --- a/src/mbgl/renderer/layers/symbol_layer_tweaker.cpp +++ b/src/mbgl/renderer/layers/symbol_layer_tweaker.cpp @@ -94,8 +94,8 @@ void SymbolLayerTweaker::execute(LayerGroupBase& layerGroup, const PaintParamete const SymbolDynamicUBO dynamicUBO = {/*.fade_change=*/parameters.symbolFadeChange, /*.camera_to_center_distance=*/camDist, - /*.device_pixel_ratio=*/parameters.pixelRatio, - /*.aspect_ratio=*/state.getSize().aspectRatio()}; + /*.aspect_ratio=*/state.getSize().aspectRatio(), + 0}; if (!dynamicBuffer) { dynamicBuffer = parameters.context.createUniformBuffer(&dynamicUBO, sizeof(dynamicUBO)); diff --git a/src/mbgl/renderer/sources/render_tile_source.cpp b/src/mbgl/renderer/sources/render_tile_source.cpp index a0a8d9b5fb2..da7e2b2d6b0 100644 --- a/src/mbgl/renderer/sources/render_tile_source.cpp +++ b/src/mbgl/renderer/sources/render_tile_source.cpp @@ -215,7 +215,7 @@ void TileSourceRenderItem::updateDebugDrawables(DebugLayerGroupMap& debugLayerGr /*matrix = */ util::cast(matrix), /*units_to_pixels = */ {1.0f / parameters.pixelsToGLUnits[0], 1.0f / parameters.pixelsToGLUnits[1]}, /*ratio = */ 1.0f / tileID.pixelsToTileUnits(1.0f, zoom), - /*device_pixel_ratio = */ parameters.pixelRatio}; + 0}; static const StringIdentity idLinePropertiesUBOName = stringIndexer().get("LinePropertiesUBO"); diff --git a/src/mbgl/style/layers/custom_drawable_layer.cpp b/src/mbgl/style/layers/custom_drawable_layer.cpp index e4037550f1e..5984e209327 100644 --- a/src/mbgl/style/layers/custom_drawable_layer.cpp +++ b/src/mbgl/style/layers/custom_drawable_layer.cpp @@ -102,7 +102,7 @@ class LineDrawableTweaker : public gfx::DrawableTweaker { /*matrix = */ util::cast(matrix), /*units_to_pixels = */ {1.0f / parameters.pixelsToGLUnits[0], 1.0f / parameters.pixelsToGLUnits[1]}, /*ratio = */ 1.0f / tileID.pixelsToTileUnits(1.0f, zoom), - /*device_pixel_ratio = */ parameters.pixelRatio}; + 0}; static const StringIdentity idLinePropertiesUBOName = stringIndexer().get("LinePropertiesUBO"); From de1bcd1665800a3ff663a76e2f98c569407c0937 Mon Sep 17 00:00:00 2001 From: Tim Sylvester Date: Fri, 8 Dec 2023 22:22:43 -0800 Subject: [PATCH 11/18] Add explicit frame capture (#1934) --- include/mbgl/mtl/mtl_fwd.hpp | 2 + src/mbgl/renderer/renderer_impl.cpp | 85 ++++++++++++++++++++++++++++- src/mbgl/renderer/renderer_impl.hpp | 9 +++ 3 files changed, 95 insertions(+), 1 deletion(-) diff --git a/include/mbgl/mtl/mtl_fwd.hpp b/include/mbgl/mtl/mtl_fwd.hpp index 9f31d01c8c9..9a56a34f464 100644 --- a/include/mbgl/mtl/mtl_fwd.hpp +++ b/include/mbgl/mtl/mtl_fwd.hpp @@ -19,6 +19,7 @@ namespace MTL { class BlitCommandEncoder; class BlitPassDescriptor; class Buffer; +class CaptureScope; class CommandBuffer; class CommandQueue; class Device; @@ -43,6 +44,7 @@ using CAMetalDrawablePtr = NS::SharedPtr; using MTLBlitCommandEncoderPtr = NS::SharedPtr; using MTLBlitPassDescriptorPtr = NS::SharedPtr; using MTLBufferPtr = NS::SharedPtr; +using MTLCaptureScopePtr = NS::SharedPtr; using MTLCommandBufferPtr = NS::SharedPtr; using MTLCommandQueuePtr = NS::SharedPtr; using MTLDevicePtr = NS::SharedPtr; diff --git a/src/mbgl/renderer/renderer_impl.cpp b/src/mbgl/renderer/renderer_impl.cpp index e54245f884f..f74cf2e6e53 100644 --- a/src/mbgl/renderer/renderer_impl.cpp +++ b/src/mbgl/renderer/renderer_impl.cpp @@ -25,7 +25,16 @@ #include #endif // MLN_DRAWABLE_RENDERER -#if !MLN_RENDER_BACKEND_METAL +#if MLN_RENDER_BACKEND_METAL +#include +#include +#include +/// Enable programmatic Metal frame captures for specific frame numbers. +/// Requries iOS 13 +constexpr auto EnableMetalCapture = 0; +constexpr auto CaptureFrameStart = 0; // frames are 0-based +constexpr auto CaptureFrameCount = 1; +#else // !MLN_RENDER_BACKEND_METAL #include #if MLN_DRAWABLE_RENDERER #include @@ -64,6 +73,67 @@ void Renderer::Impl::setObserver(RendererObserver* observer_) { void Renderer::Impl::render(const RenderTree& renderTree, [[maybe_unused]] const std::shared_ptr& updateParameters) { auto& context = backend.getContext(); +#if MLN_RENDER_BACKEND_METAL + if constexpr (EnableMetalCapture) { + const auto& mtlBackend = static_cast(backend); + const auto& mtlDevice = mtlBackend.getDevice(); + + if (!commandCaptureScope) { + if (const auto& cmdQueue = mtlBackend.getCommandQueue()) { + if (const auto captureManager = NS::RetainPtr(MTL::CaptureManager::sharedCaptureManager())) { + if ((commandCaptureScope = NS::TransferPtr(captureManager->newCaptureScope(cmdQueue.get())))) { + const auto label = "Renderer::Impl frame=" + util::toString(frameCount); + commandCaptureScope->setLabel(NS::String::string(label.c_str(), NS::UTF8StringEncoding)); + captureManager->setDefaultCaptureScope(commandCaptureScope.get()); + } + } + } + } + + // "When you capture a frame programmatically, you can capture Metal commands that span multiple + // frames by using a custom capture scope. For example, by calling begin() at the start of frame + // 1 and end() after frame 3, the trace will contain command data from all the buffers that were + // committed in the three frames." + // https://developer.apple.com/documentation/metal/debugging_tools/capturing_gpu_command_data_programmatically + if constexpr (0 < CaptureFrameStart && 0 < CaptureFrameCount) { + if (commandCaptureScope) { + const auto captureManager = NS::RetainPtr(MTL::CaptureManager::sharedCaptureManager()); + if (frameCount == CaptureFrameStart) { + constexpr auto captureDest = MTL::CaptureDestination::CaptureDestinationDeveloperTools; + if (captureManager && !captureManager->isCapturing() && + captureManager->supportsDestination(captureDest)) { + if (auto captureDesc = NS::TransferPtr(MTL::CaptureDescriptor::alloc()->init())) { + captureDesc->setCaptureObject(mtlDevice.get()); + captureDesc->setDestination(captureDest); + NS::Error* errorPtr = nullptr; + if (captureManager->startCapture(captureDesc.get(), &errorPtr)) { + Log::Warning(Event::Render, "Capture Started"); + } else { + std::string errStr = ""; + if (auto error = NS::TransferPtr(errorPtr)) { + if (auto str = error->localizedDescription()) { + if (auto cstr = str->utf8String()) { + errStr = cstr; + } + } + } + Log::Warning(Event::Render, "Capture Failed: " + errStr); + } + } + } + } + } + } + if (commandCaptureScope) { + commandCaptureScope->beginScope(); + + const auto captureManager = NS::RetainPtr(MTL::CaptureManager::sharedCaptureManager()); + if (captureManager->isCapturing()) { + Log::Info(Event::Render, "Capturing frame " + util::toString(frameCount)); + } + } + } +#endif // MLN_RENDER_BACKEND_METAL // Blocks execution until the renderable is available. backend.getDefaultRenderable().wait(); @@ -402,6 +472,19 @@ void Renderer::Impl::render(const RenderTree& renderTree, // CommandEncoder destructor submits render commands. parameters.encoder.reset(); +#if MLN_RENDER_BACKEND_METAL + if constexpr (EnableMetalCapture) { + if (commandCaptureScope) { + commandCaptureScope->endScope(); + + const auto captureManager = NS::RetainPtr(MTL::CaptureManager::sharedCaptureManager()); + if (frameCount == CaptureFrameStart + CaptureFrameCount - 1 && captureManager->isCapturing()) { + captureManager->stopCapture(); + } + } + } +#endif // MLN_RENDER_BACKEND_METAL + const auto encodingTime = renderTree.getElapsedTime() - renderingTime; observer->onDidFinishRenderingFrame( diff --git a/src/mbgl/renderer/renderer_impl.hpp b/src/mbgl/renderer/renderer_impl.hpp index c869773dfba..e67c0cc528c 100644 --- a/src/mbgl/renderer/renderer_impl.hpp +++ b/src/mbgl/renderer/renderer_impl.hpp @@ -2,6 +2,11 @@ #include +#if MLN_RENDER_BACKEND_METAL +#include +#include +#endif // MLN_RENDER_BACKEND_METAL + #include #include @@ -49,6 +54,10 @@ class Renderer::Impl { RenderState renderState = RenderState::Never; uint64_t frameCount = 0; + +#if MLN_RENDER_BACKEND_METAL + mtl::MTLCaptureScopePtr commandCaptureScope; +#endif // MLN_RENDER_BACKEND_METAL }; } // namespace mbgl From 1439a6516aab1079ac61fe5b0a99645de2696aa6 Mon Sep 17 00:00:00 2001 From: Keith Smiley Date: Tue, 12 Dec 2023 04:48:12 -0800 Subject: [PATCH 12/18] [bazel] Switch to bzlmod (#1947) --- .bazelrc | 5 +++- .bazelversion | 2 +- MODULE.bazel | 9 ++++++++ WORKSPACE | 63 --------------------------------------------------- 4 files changed, 14 insertions(+), 65 deletions(-) create mode 100644 MODULE.bazel diff --git a/.bazelrc b/.bazelrc index 2a1c6ed1ff8..20b03e38887 100644 --- a/.bazelrc +++ b/.bazelrc @@ -1 +1,4 @@ -coverage --experimental_ui_max_stdouterr_bytes=10485760 \ No newline at end of file +# TODO: remove with bazel 7.x +common --enable_bzlmod + +coverage --experimental_ui_max_stdouterr_bytes=10485760 diff --git a/.bazelversion b/.bazelversion index 4ac4fded49f..19b860c1872 100644 --- a/.bazelversion +++ b/.bazelversion @@ -1 +1 @@ -6.2.0 \ No newline at end of file +6.4.0 diff --git a/MODULE.bazel b/MODULE.bazel new file mode 100644 index 00000000000..d05d4ad36e0 --- /dev/null +++ b/MODULE.bazel @@ -0,0 +1,9 @@ +module(name = "maplibre") + +bazel_dep(name = "bazel_skylib", version = "1.5.0") +bazel_dep(name = "rules_apple", version = "3.1.1", repo_name = "build_bazel_rules_apple") +bazel_dep(name = "rules_swift", version = "1.13.0", repo_name = "build_bazel_rules_swift") +bazel_dep(name = "rules_xcodeproj", version = "1.13.0") + +provisioning_profile_repository = use_extension("@build_bazel_rules_apple//apple:apple.bzl", "provisioning_profile_repository_extension") +provisioning_profile_repository.setup() diff --git a/WORKSPACE b/WORKSPACE index 640e8eda84a..e69de29bb2d 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -1,63 +0,0 @@ -workspace(name = "Maplibre") - -load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") - -http_archive( - name = "build_bazel_rules_apple", - sha256 = "34c41bfb59cdaea29ac2df5a2fa79e5add609c71bb303b2ebb10985f93fa20e7", - url = "https://github.com/bazelbuild/rules_apple/releases/download/3.1.1/rules_apple.3.1.1.tar.gz", -) - -http_archive( - name = "rules_xcodeproj", - sha256 = "f5c1f4bea9f00732ef9d54d333d9819d574de7020dbd9d081074232b93c10b2c", - url = "https://github.com/MobileNativeFoundation/rules_xcodeproj/releases/download/1.13.0/release.tar.gz", -) - -load( - "@rules_xcodeproj//xcodeproj:repositories.bzl", - "xcodeproj_rules_dependencies", -) - -xcodeproj_rules_dependencies() - -load("@bazel_features//:deps.bzl", "bazel_features_deps") - -bazel_features_deps() - -load( - "@build_bazel_rules_apple//apple:repositories.bzl", - "apple_rules_dependencies", -) - -apple_rules_dependencies() - -load( - "@build_bazel_rules_swift//swift:repositories.bzl", - "swift_rules_dependencies", -) - -swift_rules_dependencies() - -load( - "@build_bazel_rules_swift//swift:extras.bzl", - "swift_rules_extra_dependencies", -) - -swift_rules_extra_dependencies() - -load( - "@build_bazel_apple_support//lib:repositories.bzl", - "apple_support_dependencies", -) - -apple_support_dependencies() - -load( - "@build_bazel_rules_apple//apple:apple.bzl", - "provisioning_profile_repository" -) - -provisioning_profile_repository( - name = "local_provisioning_profiles" -) From dda4e4043cd492ea361f4565ebb1726128fea98c Mon Sep 17 00:00:00 2001 From: Bart Louwers Date: Tue, 12 Dec 2023 17:58:28 +0100 Subject: [PATCH 13/18] Use custom test spec for benchmark on AWS Device Farm (#1951) --- .github/actions/aws-device-farm-run/action.yml | 5 ++++- .github/workflows/android-device-test.yml | 5 ++++- .../MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml | 3 +-- .../android/testapp/activity/benchmark/BenchmarkActivity.kt | 3 ++- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/.github/actions/aws-device-farm-run/action.yml b/.github/actions/aws-device-farm-run/action.yml index 76c1ef87b5a..755d4f28885 100644 --- a/.github/actions/aws-device-farm-run/action.yml +++ b/.github/actions/aws-device-farm-run/action.yml @@ -40,6 +40,9 @@ inputs: AWS_DEVICE_FARM_DEVICE_POOL_ARN: description: "AWS_DEVICE_FARM_DEVICE_POOL_ARN" required: true + testSpecArn: + description: "ARN of test spec" + required: false runs: using: "composite" steps: @@ -111,7 +114,7 @@ runs: --name "MapLibre Native ${{ matrix.test.name }}" \ --app-arn ${{ env.app_arn }} \ --device-pool-arn ${{ inputs.AWS_DEVICE_FARM_DEVICE_POOL_ARN }} \ - --test type=${{ inputs.testType }},testPackageArn=${{ env.test_package_arn }}${{ inputs.testFilter && ',filter=' }}${{ inputs.testFilter }} \ + --test type=${{ inputs.testType }},testPackageArn=${{ env.test_package_arn }}${{ inputs.testFilter && ',filter=' }}${{ inputs.testFilter }}${{ inputs.testSpecArn && ',testSpecArn=' }}${{ inputs.testSpecArn }} \ ${{ inputs.externalData && '--configuration extraDataPackageArn=' }}${{ inputs.externalData }} \ --output text --query "run.arn")" diff --git a/.github/workflows/android-device-test.yml b/.github/workflows/android-device-test.yml index bf2eaf8e95c..1d7c063ece2 100644 --- a/.github/workflows/android-device-test.yml +++ b/.github/workflows/android-device-test.yml @@ -33,7 +33,10 @@ jobs: # aws devicefarm get-upload externalData: "arn:aws:devicefarm:us-west-2:373521797162:upload:20687d72-0e46-403e-8f03-0941850665bc/c27174c2-63f4-4cdb-9af9-68957d75ebed", # top devices, query with `aws list-device-pools --arn ` - devicePool: "arn:aws:devicefarm:us-west-2::devicepool:082d10e5-d7d7-48a5-ba5c-b33d66efa1f5" + devicePool: "arn:aws:devicefarm:us-west-2::devicepool:082d10e5-d7d7-48a5-ba5c-b33d66efa1f5", + # benchmark-android.yaml + # see https://github.com/maplibre/ci-runners/tree/main/aws-device-farm/custom-test-envs + "testSpecArn": "arn:aws:devicefarm:us-west-2:373521797162:upload:20687d72-0e46-403e-8f03-0941850665bc/14862afb-cf88-44aa-9f1e-5131cbb22f01" } ] runs-on: ubuntu-latest diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml index ee0bffdca26..54132f347b6 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml @@ -2,8 +2,7 @@ - - + = Build.VERSION_CODES.R && Environment.isExternalStorageManager() && jsonFile.isFile) { val jsonFileContents = jsonFile.readText() val jsonElement = Json.parseToJsonElement(jsonFileContents) val styleNames = jsonElement.jsonObject["styleNames"]?.jsonArray?.map { it.jsonPrimitive.content } From 9fbcb031f019048f21fdfcb57b80f4451cdecfd9 Mon Sep 17 00:00:00 2001 From: Alex Cristici Date: Tue, 12 Dec 2023 19:32:20 +0200 Subject: [PATCH 14/18] Line layer UBO improvements (#1946) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- include/mbgl/shaders/gl/drawable_line.hpp | 23 ++++---- .../shaders/gl/drawable_line_gradient.hpp | 27 +++++----- .../mbgl/shaders/gl/drawable_line_pattern.hpp | 13 ++--- include/mbgl/shaders/gl/drawable_line_sdf.hpp | 20 +++---- include/mbgl/shaders/line_layer_ubo.hpp | 23 +++++--- include/mbgl/shaders/mtl/common.hpp | 11 ++-- include/mbgl/shaders/mtl/line.hpp | 54 +++++++++---------- include/mbgl/shaders/mtl/line_gradient.hpp | 15 +++--- shaders/drawable.line.fragment.glsl | 9 ++-- shaders/drawable.line.vertex.glsl | 14 +++-- shaders/drawable.line_gradient.fragment.glsl | 11 ++-- shaders/drawable.line_gradient.vertex.glsl | 16 +++--- shaders/drawable.line_pattern.fragment.glsl | 4 -- shaders/drawable.line_pattern.vertex.glsl | 9 ++-- shaders/drawable.line_sdf.fragment.glsl | 7 ++- shaders/drawable.line_sdf.vertex.glsl | 13 +++-- .../renderer/layers/line_layer_tweaker.cpp | 39 +++++++++----- .../renderer/layers/line_layer_tweaker.hpp | 1 + .../renderer/sources/render_tile_source.cpp | 15 ++++-- src/mbgl/shaders/mtl/line.cpp | 29 +++++----- src/mbgl/shaders/mtl/line_gradient.cpp | 9 ++-- .../style/layers/custom_drawable_layer.cpp | 15 ++++-- 22 files changed, 214 insertions(+), 163 deletions(-) diff --git a/include/mbgl/shaders/gl/drawable_line.hpp b/include/mbgl/shaders/gl/drawable_line.hpp index 6645eded1d8..e5dcdabe457 100644 --- a/include/mbgl/shaders/gl/drawable_line.hpp +++ b/include/mbgl/shaders/gl/drawable_line.hpp @@ -19,11 +19,15 @@ struct ShaderSource { layout (location = 0) in vec2 a_pos_normal; layout (location = 1) in vec4 a_data; +layout (std140) uniform LineDynamicUBO { + highp vec2 u_units_to_pixels; + lowp float pad0, pad1; +}; + layout (std140) uniform LineUBO { highp mat4 u_matrix; - highp vec2 u_units_to_pixels; mediump float u_ratio; - lowp float pad0; + lowp float pad2, pad3, pad4; }; layout (std140) uniform LinePropertiesUBO { @@ -34,8 +38,8 @@ layout (std140) uniform LinePropertiesUBO { lowp float u_offset; mediump float u_width; - highp float pad1; - highp vec2 pad2; + highp float pad5; + highp vec2 pad6; }; layout (std140) uniform LineInterpolationUBO { @@ -46,7 +50,7 @@ layout (std140) uniform LineInterpolationUBO { lowp float u_offset_t; lowp float u_width_t; - highp vec2 pad3; + highp vec2 pad7; }; out vec2 v_normal; @@ -160,9 +164,8 @@ mediump float width = u_width; )"; static constexpr const char* fragment = R"(layout (std140) uniform LineUBO { highp mat4 u_matrix; - highp vec2 u_units_to_pixels; mediump float u_ratio; - lowp float pad0; + lowp float pad2, pad3, pad4; }; layout (std140) uniform LinePropertiesUBO { @@ -173,8 +176,8 @@ layout (std140) uniform LinePropertiesUBO { lowp float u_offset; mediump float u_width; - highp float pad1; - highp vec2 pad2; + highp float pad5; + highp vec2 pad6; }; layout (std140) uniform LineInterpolationUBO { @@ -185,7 +188,7 @@ layout (std140) uniform LineInterpolationUBO { lowp float u_offset_t; lowp float u_width_t; - highp vec2 pad3; + highp vec2 pad7; }; in vec2 v_width2; diff --git a/include/mbgl/shaders/gl/drawable_line_gradient.hpp b/include/mbgl/shaders/gl/drawable_line_gradient.hpp index a41f945e444..6ba9b50e17a 100644 --- a/include/mbgl/shaders/gl/drawable_line_gradient.hpp +++ b/include/mbgl/shaders/gl/drawable_line_gradient.hpp @@ -23,11 +23,15 @@ struct ShaderSource { layout (location = 0) in vec2 a_pos_normal; layout (location = 1) in vec4 a_data; +layout (std140) uniform LineDynamicUBO { + highp vec2 u_units_to_pixels; + lowp float pad0, pad1; +}; + layout (std140) uniform LineGradientUBO { highp mat4 u_matrix; - highp vec2 u_units_to_pixels; mediump float u_ratio; - lowp float pad0; + lowp float pad2, pad3, pad4; }; layout (std140) uniform LineGradientPropertiesUBO { @@ -37,8 +41,8 @@ layout (std140) uniform LineGradientPropertiesUBO { lowp float u_offset; mediump float u_width; - highp float pad1; - highp vec2 pad2; + highp float pad5; + highp vec2 pad6; }; layout (std140) uniform LineGradientInterpolationUBO { @@ -48,8 +52,8 @@ layout (std140) uniform LineGradientInterpolationUBO { lowp float u_offset_t; lowp float u_width_t; - highp float pad3; - highp vec2 pad4; + highp float pad7; + highp vec2 pad8; }; out vec2 v_normal; @@ -154,9 +158,8 @@ mediump float width = u_width; )"; static constexpr const char* fragment = R"(layout (std140) uniform LineGradientUBO { highp mat4 u_matrix; - highp vec2 u_units_to_pixels; mediump float u_ratio; - lowp float pad0; + lowp float pad2, pad3, pad4; }; layout (std140) uniform LineGradientPropertiesUBO { @@ -166,8 +169,8 @@ layout (std140) uniform LineGradientPropertiesUBO { lowp float u_offset; mediump float u_width; - highp float pad1; - highp vec2 pad2; + highp float pad5; + highp vec2 pad6; }; layout (std140) uniform LineGradientInterpolationUBO { @@ -177,8 +180,8 @@ layout (std140) uniform LineGradientInterpolationUBO { lowp float u_offset_t; lowp float u_width_t; - highp float pad3; - highp vec2 pad4; + highp float pad7; + highp vec2 pad8; }; uniform sampler2D u_image; diff --git a/include/mbgl/shaders/gl/drawable_line_pattern.hpp b/include/mbgl/shaders/gl/drawable_line_pattern.hpp index 6fe186c5beb..d2fdbe3a91f 100644 --- a/include/mbgl/shaders/gl/drawable_line_pattern.hpp +++ b/include/mbgl/shaders/gl/drawable_line_pattern.hpp @@ -23,16 +23,17 @@ struct ShaderSource { layout (location = 0) in vec2 a_pos_normal; layout (location = 1) in vec4 a_data; +layout (std140) uniform LineDynamicUBO { + highp vec2 u_units_to_pixels; + lowp float pad0, pad1; +}; + layout (std140) uniform LinePatternUBO { highp mat4 u_matrix; mediump vec4 u_scale; highp vec2 u_texsize; - highp vec2 u_units_to_pixels; mediump float u_ratio; highp float u_fade; - - lowp float pad0; - highp float pad1; }; layout (std140) uniform LinePatternPropertiesUBO { @@ -185,12 +186,8 @@ mediump vec4 pattern_to = u_pattern_to; highp mat4 u_matrix; mediump vec4 u_scale; highp vec2 u_texsize; - highp vec2 u_units_to_pixels; mediump float u_ratio; highp float u_fade; - - lowp float pad0; - highp float pad1; }; layout (std140) uniform LinePatternPropertiesUBO { diff --git a/include/mbgl/shaders/gl/drawable_line_sdf.hpp b/include/mbgl/shaders/gl/drawable_line_sdf.hpp index eecdcd5cc4f..52d39174dc0 100644 --- a/include/mbgl/shaders/gl/drawable_line_sdf.hpp +++ b/include/mbgl/shaders/gl/drawable_line_sdf.hpp @@ -23,9 +23,13 @@ struct ShaderSource { layout (location = 0) in vec2 a_pos_normal; layout (location = 1) in vec4 a_data; +layout (std140) uniform LineDynamicUBO { + highp vec2 u_units_to_pixels; + lowp float pad0, pad1; +}; + layout (std140) uniform LineSDFUBO { highp mat4 u_matrix; - highp vec2 u_units_to_pixels; highp vec2 u_patternscale_a; highp vec2 u_patternscale_b; mediump float u_ratio; @@ -33,8 +37,7 @@ layout (std140) uniform LineSDFUBO { highp float u_tex_y_b; highp float u_sdfgamma; highp float u_mix; - - lowp float pad0; + lowp float pad2, pad3, pad4; }; layout (std140) uniform LineSDFPropertiesUBO { @@ -46,7 +49,7 @@ layout (std140) uniform LineSDFPropertiesUBO { mediump float u_width; lowp float u_floorwidth; - highp vec2 pad1; + highp vec2 pad5; }; layout (std140) uniform LineSDFInterpolationUBO { @@ -58,7 +61,7 @@ layout (std140) uniform LineSDFInterpolationUBO { lowp float u_width_t; lowp float u_floorwidth_t; - highp float pad2; + highp float pad6; }; out vec2 v_normal; @@ -186,7 +189,6 @@ lowp float floorwidth = u_floorwidth; static constexpr const char* fragment = R"( layout (std140) uniform LineSDFUBO { highp mat4 u_matrix; - highp vec2 u_units_to_pixels; highp vec2 u_patternscale_a; highp vec2 u_patternscale_b; mediump float u_ratio; @@ -195,7 +197,7 @@ layout (std140) uniform LineSDFUBO { highp float u_sdfgamma; highp float u_mix; - lowp float pad0; + lowp float pad2, pad3, pad4; }; layout (std140) uniform LineSDFPropertiesUBO { @@ -207,7 +209,7 @@ layout (std140) uniform LineSDFPropertiesUBO { mediump float u_width; lowp float u_floorwidth; - highp vec2 pad1; + highp vec2 pad5; }; layout (std140) uniform LineSDFInterpolationUBO { @@ -219,7 +221,7 @@ layout (std140) uniform LineSDFInterpolationUBO { lowp float u_width_t; lowp float u_floorwidth_t; - highp float pad2; + highp float pad6; }; uniform sampler2D u_image; diff --git a/include/mbgl/shaders/line_layer_ubo.hpp b/include/mbgl/shaders/line_layer_ubo.hpp index 43859adf7fc..0b20b6d6e14 100644 --- a/include/mbgl/shaders/line_layer_ubo.hpp +++ b/include/mbgl/shaders/line_layer_ubo.hpp @@ -5,11 +5,17 @@ namespace mbgl { namespace shaders { +struct alignas(16) LineDynamicUBO { + /* 0 */ std::array units_to_pixels; + /* 8 */ float pad1, pad2; + /* 16 */ +}; +static_assert(sizeof(LineDynamicUBO) == 16); + struct alignas(16) LineUBO { std::array matrix; - std::array units_to_pixels; float ratio; - float pad; + float pad1, pad2, pad3; }; static_assert(sizeof(LineUBO) % 16 == 0); @@ -40,10 +46,8 @@ struct alignas(16) LinePatternUBO { std::array matrix; std::array scale; std::array texsize; - std::array units_to_pixels; float ratio; float fade; - float pad1, pad2; }; static_assert(sizeof(LinePatternUBO) % 16 == 0); @@ -59,7 +63,6 @@ static_assert(sizeof(LinePatternPropertiesUBO) % 16 == 0); struct alignas(16) LineSDFUBO { std::array matrix; - std::array units_to_pixels; std::array patternscale_a; std::array patternscale_b; float ratio; @@ -67,7 +70,7 @@ struct alignas(16) LineSDFUBO { float tex_y_b; float sdfgamma; float mix; - float pad; + float pad1, pad2, pad3; }; static_assert(sizeof(LineSDFUBO) % 16 == 0); @@ -83,7 +86,13 @@ struct alignas(16) LineSDFPropertiesUBO { }; static_assert(sizeof(LineSDFPropertiesUBO) % 16 == 0); -using LineBasicUBO = LineUBO; +struct alignas(16) LineBasicUBO { + std::array matrix; + std::array units_to_pixels; + float ratio; + float pad; +}; +static_assert(sizeof(LineBasicUBO) % 16 == 0); struct alignas(16) LineBasicPropertiesUBO { Color color; diff --git a/include/mbgl/shaders/mtl/common.hpp b/include/mbgl/shaders/mtl/common.hpp index da318e2cf6b..0f33b5b6176 100644 --- a/include/mbgl/shaders/mtl/common.hpp +++ b/include/mbgl/shaders/mtl/common.hpp @@ -54,11 +54,15 @@ float4 unpack_mix_color(const float4 packedColors, const float t) { decode_color(float2(packedColors[2], packedColors[3])), t); } +struct alignas(16) LineDynamicUBO { + float2 units_to_pixels; + float pad1, pad2; +}; + struct alignas(16) LineUBO { float4x4 matrix; - float2 units_to_pixels; float ratio; - float pad; + float pad1, pad2, pad3; }; struct alignas(16) LineBasicUBO { @@ -70,9 +74,8 @@ struct alignas(16) LineBasicUBO { struct alignas(16) LineGradientUBO { float4x4 matrix; - float2 units_to_pixels; float ratio; - float pad; + float pad1, pad2, pad3; }; struct alignas(16) LinePropertiesUBO { diff --git a/include/mbgl/shaders/mtl/line.hpp b/include/mbgl/shaders/mtl/line.hpp index c96569cb793..cbc63e1e3be 100644 --- a/include/mbgl/shaders/mtl/line.hpp +++ b/include/mbgl/shaders/mtl/line.hpp @@ -15,7 +15,7 @@ struct ShaderSource { static constexpr auto fragmentMainFunction = "fragmentMain"; static const std::array attributes; - static const std::array uniforms; + static const std::array uniforms; static const std::array textures; static constexpr auto source = R"( @@ -61,9 +61,10 @@ struct FragmentStage { }; FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], - device const LineUBO& line [[buffer(8)]], - device const LinePropertiesUBO& props [[buffer(9)]], - device const LineInterpolationUBO& interp [[buffer(10)]]) { + device const LineDynamicUBO& dynamic [[buffer(8)]], + device const LineUBO& line [[buffer(9)]], + device const LinePropertiesUBO& props [[buffer(10)]], + device const LineInterpolationUBO& interp [[buffer(11)]]) { #if defined(HAS_UNIFORM_u_gapwidth) const auto gapwidth = props.gapwidth / 2; @@ -115,7 +116,7 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], // calculate how much the perspective view squishes or stretches the extrude const float extrude_length_without_perspective = length(dist); - const float extrude_length_with_perspective = length(projected_extrude.xy / position.w * line.units_to_pixels); + const float extrude_length_with_perspective = length(projected_extrude.xy / position.w * dynamic.units_to_pixels); return { .position = position, @@ -136,8 +137,8 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], } half4 fragment fragmentMain(FragmentStage in [[stage_in]], - device const LineUBO& line [[buffer(8)]], - device const LinePropertiesUBO& props [[buffer(9)]]) { + device const LineUBO& line [[buffer(9)]], + device const LinePropertiesUBO& props [[buffer(10)]]) { #if defined(OVERDRAW_INSPECTOR) return half4(1.0); #endif @@ -178,7 +179,7 @@ struct ShaderSource { static constexpr auto fragmentMainFunction = "fragmentMain"; static const std::array attributes; - static const std::array uniforms; + static const std::array uniforms; static const std::array textures; static constexpr auto source = R"( @@ -234,10 +235,8 @@ struct alignas(16) LinePatternUBO { float4x4 matrix; float4 scale; float2 texsize; - float2 units_to_pixels; float ratio; float fade; - float pad1, pad2; }; struct alignas(16) LinePatternPropertiesUBO { @@ -266,10 +265,11 @@ struct alignas(16) LinePatternTilePropertiesUBO { }; FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], - device const LinePatternUBO& line [[buffer(9)]], - device const LinePatternPropertiesUBO& props [[buffer(10)]], - device const LinePatternInterpolationUBO& interp [[buffer(11)]], - device const LinePatternTilePropertiesUBO& tileProps [[buffer(12)]]) { + device const LineDynamicUBO& dynamic [[buffer(9)]], + device const LinePatternUBO& line [[buffer(10)]], + device const LinePatternPropertiesUBO& props [[buffer(11)]], + device const LinePatternInterpolationUBO& interp [[buffer(12)]], + device const LinePatternTilePropertiesUBO& tileProps [[buffer(13)]]) { #if defined(HAS_UNIFORM_u_gapwidth) const auto gapwidth = props.gapwidth / 2; @@ -323,7 +323,7 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], // calculate how much the perspective view squishes or stretches the extrude const float extrude_length_without_perspective = length(dist); - const float extrude_length_with_perspective = length(projected_extrude.xy / position.w * line.units_to_pixels); + const float extrude_length_with_perspective = length(projected_extrude.xy / position.w * dynamic.units_to_pixels); return { .position = position, @@ -348,9 +348,9 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], } half4 fragment fragmentMain(FragmentStage in [[stage_in]], - device const LinePatternUBO& line [[buffer(9)]], - device const LinePatternPropertiesUBO& props [[buffer(10)]], - device const LinePatternTilePropertiesUBO& tileProps [[buffer(12)]], + device const LinePatternUBO& line [[buffer(10)]], + device const LinePatternPropertiesUBO& props [[buffer(11)]], + device const LinePatternTilePropertiesUBO& tileProps [[buffer(13)]], texture2d image0 [[texture(0)]], sampler image0_sampler [[sampler(0)]]) { #if defined(OVERDRAW_INSPECTOR) @@ -430,7 +430,7 @@ struct ShaderSource { static constexpr auto fragmentMainFunction = "fragmentMain"; static const std::array attributes; - static const std::array uniforms; + static const std::array uniforms; static const std::array textures; static constexpr auto source = R"( @@ -485,7 +485,6 @@ struct FragmentStage { struct alignas(16) LineSDFUBO { float4x4 matrix; - float2 units_to_pixels; float2 patternscale_a; float2 patternscale_b; float ratio; @@ -493,7 +492,7 @@ struct alignas(16) LineSDFUBO { float tex_y_b; float sdfgamma; float mix; - float pad; + float pad1, pad2, pad3; }; struct alignas(16) LineSDFPropertiesUBO { @@ -519,9 +518,10 @@ struct alignas(16) LineSDFInterpolationUBO { }; FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], - device const LineSDFUBO& line [[buffer(9)]], - device const LineSDFPropertiesUBO& props [[buffer(10)]], - device const LineSDFInterpolationUBO& interp [[buffer(11)]]) { + device const LineDynamicUBO& dynamic [[buffer(9)]], + device const LineSDFUBO& line [[buffer(10)]], + device const LineSDFPropertiesUBO& props [[buffer(11)]], + device const LineSDFInterpolationUBO& interp [[buffer(12)]]) { #if defined(HAS_UNIFORM_u_gapwidth) const auto gapwidth = props.gapwidth / 2; @@ -580,7 +580,7 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], // calculate how much the perspective view squishes or stretches the extrude const float extrude_length_without_perspective = length(dist); - const float extrude_length_with_perspective = length(projected_extrude.xy / position.w * line.units_to_pixels); + const float extrude_length_with_perspective = length(projected_extrude.xy / position.w * dynamic.units_to_pixels); return { .position = position, @@ -606,8 +606,8 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], } half4 fragment fragmentMain(FragmentStage in [[stage_in]], - device const LineSDFUBO& line [[buffer(9)]], - device const LineSDFPropertiesUBO& props [[buffer(10)]], + device const LineSDFUBO& line [[buffer(10)]], + device const LineSDFPropertiesUBO& props [[buffer(11)]], texture2d image0 [[texture(0)]], sampler image0_sampler [[sampler(0)]]) { #if defined(OVERDRAW_INSPECTOR) diff --git a/include/mbgl/shaders/mtl/line_gradient.hpp b/include/mbgl/shaders/mtl/line_gradient.hpp index b8cf813fc93..28653583b8a 100644 --- a/include/mbgl/shaders/mtl/line_gradient.hpp +++ b/include/mbgl/shaders/mtl/line_gradient.hpp @@ -15,7 +15,7 @@ struct ShaderSource { static constexpr auto fragmentMainFunction = "fragmentMain"; static const std::array attributes; - static const std::array uniforms; + static const std::array uniforms; static const std::array textures; static constexpr auto source = R"( @@ -45,9 +45,10 @@ struct FragmentStage { }; FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], - device const LineGradientUBO& line [[buffer(7)]], - device const LineGradientPropertiesUBO& props [[buffer(8)]], - device const LineGradientInterpolationUBO& interp [[buffer(9)]]) { + device const LineDynamicUBO& dynamic [[buffer(7)]], + device const LineGradientUBO& line [[buffer(8)]], + device const LineGradientPropertiesUBO& props [[buffer(9)]], + device const LineGradientInterpolationUBO& interp [[buffer(10)]]) { #if !defined(HAS_UNIFORM_u_blur) const auto blur = unpack_mix_float(vertx.blur, interp.blur_t); @@ -106,7 +107,7 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], // calculate how much the perspective view squishes or stretches the extrude const float extrude_length_without_perspective = length(dist); - const float extrude_length_with_perspective = length(projected_extrude.xy / position.w * line.units_to_pixels); + const float extrude_length_with_perspective = length(projected_extrude.xy / position.w * dynamic.units_to_pixels); return { .position = position, @@ -125,8 +126,8 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], } half4 fragment fragmentMain(FragmentStage in [[stage_in]], - device const LineGradientUBO& line [[buffer(7)]], - device const LineGradientPropertiesUBO& props [[buffer(8)]], + device const LineGradientUBO& line [[buffer(8)]], + device const LineGradientPropertiesUBO& props [[buffer(9)]], texture2d gradientTexture [[texture(0)]]) { #if defined(OVERDRAW_INSPECTOR) return half4(1.0); diff --git a/shaders/drawable.line.fragment.glsl b/shaders/drawable.line.fragment.glsl index a8e354c9627..bb7e9dbb3c7 100644 --- a/shaders/drawable.line.fragment.glsl +++ b/shaders/drawable.line.fragment.glsl @@ -1,8 +1,7 @@ layout (std140) uniform LineUBO { highp mat4 u_matrix; - highp vec2 u_units_to_pixels; mediump float u_ratio; - lowp float pad0; + lowp float pad2, pad3, pad4; }; layout (std140) uniform LinePropertiesUBO { @@ -13,8 +12,8 @@ layout (std140) uniform LinePropertiesUBO { lowp float u_offset; mediump float u_width; - highp float pad1; - highp vec2 pad2; + highp float pad5; + highp vec2 pad6; }; layout (std140) uniform LineInterpolationUBO { @@ -25,7 +24,7 @@ layout (std140) uniform LineInterpolationUBO { lowp float u_offset_t; lowp float u_width_t; - highp vec2 pad3; + highp vec2 pad7; }; in vec2 v_width2; diff --git a/shaders/drawable.line.vertex.glsl b/shaders/drawable.line.vertex.glsl index 169564ab8db..b44a9bd3b04 100644 --- a/shaders/drawable.line.vertex.glsl +++ b/shaders/drawable.line.vertex.glsl @@ -9,11 +9,15 @@ layout (location = 0) in vec2 a_pos_normal; layout (location = 1) in vec4 a_data; +layout (std140) uniform LineDynamicUBO { + highp vec2 u_units_to_pixels; + lowp float pad0, pad1; +}; + layout (std140) uniform LineUBO { highp mat4 u_matrix; - highp vec2 u_units_to_pixels; mediump float u_ratio; - lowp float pad0; + lowp float pad2, pad3, pad4; }; layout (std140) uniform LinePropertiesUBO { @@ -24,8 +28,8 @@ layout (std140) uniform LinePropertiesUBO { lowp float u_offset; mediump float u_width; - highp float pad1; - highp vec2 pad2; + highp float pad5; + highp vec2 pad6; }; layout (std140) uniform LineInterpolationUBO { @@ -36,7 +40,7 @@ layout (std140) uniform LineInterpolationUBO { lowp float u_offset_t; lowp float u_width_t; - highp vec2 pad3; + highp vec2 pad7; }; out vec2 v_normal; diff --git a/shaders/drawable.line_gradient.fragment.glsl b/shaders/drawable.line_gradient.fragment.glsl index 4e3c6856d4f..03b3064017b 100644 --- a/shaders/drawable.line_gradient.fragment.glsl +++ b/shaders/drawable.line_gradient.fragment.glsl @@ -1,8 +1,7 @@ layout (std140) uniform LineGradientUBO { highp mat4 u_matrix; - highp vec2 u_units_to_pixels; mediump float u_ratio; - lowp float pad0; + lowp float pad2, pad3, pad4; }; layout (std140) uniform LineGradientPropertiesUBO { @@ -12,8 +11,8 @@ layout (std140) uniform LineGradientPropertiesUBO { lowp float u_offset; mediump float u_width; - highp float pad1; - highp vec2 pad2; + highp float pad5; + highp vec2 pad6; }; layout (std140) uniform LineGradientInterpolationUBO { @@ -23,8 +22,8 @@ layout (std140) uniform LineGradientInterpolationUBO { lowp float u_offset_t; lowp float u_width_t; - highp float pad3; - highp vec2 pad4; + highp float pad7; + highp vec2 pad8; }; uniform sampler2D u_image; diff --git a/shaders/drawable.line_gradient.vertex.glsl b/shaders/drawable.line_gradient.vertex.glsl index e41ca7901a5..5e212cd5921 100644 --- a/shaders/drawable.line_gradient.vertex.glsl +++ b/shaders/drawable.line_gradient.vertex.glsl @@ -13,11 +13,15 @@ layout (location = 0) in vec2 a_pos_normal; layout (location = 1) in vec4 a_data; +layout (std140) uniform LineDynamicUBO { + highp vec2 u_units_to_pixels; + lowp float pad0, pad1; +}; + layout (std140) uniform LineGradientUBO { highp mat4 u_matrix; - highp vec2 u_units_to_pixels; mediump float u_ratio; - lowp float pad0; + lowp float pad2, pad3, pad4; }; layout (std140) uniform LineGradientPropertiesUBO { @@ -27,8 +31,8 @@ layout (std140) uniform LineGradientPropertiesUBO { lowp float u_offset; mediump float u_width; - highp float pad1; - highp vec2 pad2; + highp float pad5; + highp vec2 pad6; }; layout (std140) uniform LineGradientInterpolationUBO { @@ -38,8 +42,8 @@ layout (std140) uniform LineGradientInterpolationUBO { lowp float u_offset_t; lowp float u_width_t; - highp float pad3; - highp vec2 pad4; + highp float pad7; + highp vec2 pad8; }; out vec2 v_normal; diff --git a/shaders/drawable.line_pattern.fragment.glsl b/shaders/drawable.line_pattern.fragment.glsl index 30b5a7c0c22..e049d70a483 100644 --- a/shaders/drawable.line_pattern.fragment.glsl +++ b/shaders/drawable.line_pattern.fragment.glsl @@ -2,12 +2,8 @@ layout (std140) uniform LinePatternUBO { highp mat4 u_matrix; mediump vec4 u_scale; highp vec2 u_texsize; - highp vec2 u_units_to_pixels; mediump float u_ratio; highp float u_fade; - - lowp float pad0; - highp float pad1; }; layout (std140) uniform LinePatternPropertiesUBO { diff --git a/shaders/drawable.line_pattern.vertex.glsl b/shaders/drawable.line_pattern.vertex.glsl index 600e8f3af6e..e69bdd4bce7 100644 --- a/shaders/drawable.line_pattern.vertex.glsl +++ b/shaders/drawable.line_pattern.vertex.glsl @@ -13,16 +13,17 @@ layout (location = 0) in vec2 a_pos_normal; layout (location = 1) in vec4 a_data; +layout (std140) uniform LineDynamicUBO { + highp vec2 u_units_to_pixels; + lowp float pad0, pad1; +}; + layout (std140) uniform LinePatternUBO { highp mat4 u_matrix; mediump vec4 u_scale; highp vec2 u_texsize; - highp vec2 u_units_to_pixels; mediump float u_ratio; highp float u_fade; - - lowp float pad0; - highp float pad1; }; layout (std140) uniform LinePatternPropertiesUBO { diff --git a/shaders/drawable.line_sdf.fragment.glsl b/shaders/drawable.line_sdf.fragment.glsl index 64b2c406db9..140b0c47269 100644 --- a/shaders/drawable.line_sdf.fragment.glsl +++ b/shaders/drawable.line_sdf.fragment.glsl @@ -1,7 +1,6 @@ layout (std140) uniform LineSDFUBO { highp mat4 u_matrix; - highp vec2 u_units_to_pixels; highp vec2 u_patternscale_a; highp vec2 u_patternscale_b; mediump float u_ratio; @@ -10,7 +9,7 @@ layout (std140) uniform LineSDFUBO { highp float u_sdfgamma; highp float u_mix; - lowp float pad0; + lowp float pad2, pad3, pad4; }; layout (std140) uniform LineSDFPropertiesUBO { @@ -22,7 +21,7 @@ layout (std140) uniform LineSDFPropertiesUBO { mediump float u_width; lowp float u_floorwidth; - highp vec2 pad1; + highp vec2 pad5; }; layout (std140) uniform LineSDFInterpolationUBO { @@ -34,7 +33,7 @@ layout (std140) uniform LineSDFInterpolationUBO { lowp float u_width_t; lowp float u_floorwidth_t; - highp float pad2; + highp float pad6; }; uniform sampler2D u_image; diff --git a/shaders/drawable.line_sdf.vertex.glsl b/shaders/drawable.line_sdf.vertex.glsl index cc41cf61be4..7008e79e211 100644 --- a/shaders/drawable.line_sdf.vertex.glsl +++ b/shaders/drawable.line_sdf.vertex.glsl @@ -13,9 +13,13 @@ layout (location = 0) in vec2 a_pos_normal; layout (location = 1) in vec4 a_data; +layout (std140) uniform LineDynamicUBO { + highp vec2 u_units_to_pixels; + lowp float pad0, pad1; +}; + layout (std140) uniform LineSDFUBO { highp mat4 u_matrix; - highp vec2 u_units_to_pixels; highp vec2 u_patternscale_a; highp vec2 u_patternscale_b; mediump float u_ratio; @@ -23,8 +27,7 @@ layout (std140) uniform LineSDFUBO { highp float u_tex_y_b; highp float u_sdfgamma; highp float u_mix; - - lowp float pad0; + lowp float pad2, pad3, pad4; }; layout (std140) uniform LineSDFPropertiesUBO { @@ -36,7 +39,7 @@ layout (std140) uniform LineSDFPropertiesUBO { mediump float u_width; lowp float u_floorwidth; - highp vec2 pad1; + highp vec2 pad5; }; layout (std140) uniform LineSDFInterpolationUBO { @@ -48,7 +51,7 @@ layout (std140) uniform LineSDFInterpolationUBO { lowp float u_width_t; lowp float u_floorwidth_t; - highp float pad2; + highp float pad6; }; out vec2 v_normal; diff --git a/src/mbgl/renderer/layers/line_layer_tweaker.cpp b/src/mbgl/renderer/layers/line_layer_tweaker.cpp index 4f46e255e1f..bcb96430044 100644 --- a/src/mbgl/renderer/layers/line_layer_tweaker.cpp +++ b/src/mbgl/renderer/layers/line_layer_tweaker.cpp @@ -33,6 +33,7 @@ static const StringIdentity idLinePatternUBOName = stringIndexer().get("LinePatt static const StringIdentity idLinePatternPropertiesUBOName = stringIndexer().get("LinePatternPropertiesUBO"); static const StringIdentity idLineSDFUBOName = stringIndexer().get("LineSDFUBO"); static const StringIdentity idLineSDFPropertiesUBOName = stringIndexer().get("LineSDFPropertiesUBO"); +static const StringIdentity idLineDynamicUBOName = stringIndexer().get("LineDynamicUBO"); static const StringIdentity idTexImageName = stringIndexer().get("u_image"); void LineLayerTweaker::execute(LayerGroupBase& layerGroup, const PaintParameters& parameters) { @@ -107,6 +108,15 @@ void LineLayerTweaker::execute(LayerGroupBase& layerGroup, const PaintParameters return lineSDFPropertiesBuffer; }; + const LineDynamicUBO dynamicUBO = { + /*units_to_pixels = */ {1.0f / parameters.pixelsToGLUnits[0], 1.0f / parameters.pixelsToGLUnits[1]}, 0, 0}; + + if (!dynamicBuffer) { + dynamicBuffer = parameters.context.createUniformBuffer(&dynamicUBO, sizeof(dynamicUBO)); + } else { + dynamicBuffer->update(&dynamicUBO, sizeof(dynamicUBO)); + } + visitLayerGroupDrawables(layerGroup, [&](gfx::Drawable& drawable) { const auto shader = drawable.getShader(); if (!drawable.getTileID() || !shader || !checkTweakDrawable(drawable)) { @@ -122,15 +132,16 @@ void LineLayerTweaker::execute(LayerGroupBase& layerGroup, const PaintParameters const auto matrix = getTileMatrix(tileID, parameters, translation, anchor, nearClipped, inViewportPixelUnits); - const LineType type = static_cast(drawable.getType()); + uniforms.addOrReplace(idLineDynamicUBOName, dynamicBuffer); + const LineType type = static_cast(drawable.getType()); switch (type) { case LineType::Simple: { - const LineUBO lineUBO{ - /*matrix = */ util::cast(matrix), - /*units_to_pixels = */ {1.0f / parameters.pixelsToGLUnits[0], 1.0f / parameters.pixelsToGLUnits[1]}, - /*ratio = */ 1.0f / tileID.pixelsToTileUnits(1.0f, static_cast(zoom)), - 0}; + const LineUBO lineUBO{/*matrix = */ util::cast(matrix), + /*ratio = */ 1.0f / tileID.pixelsToTileUnits(1.0f, static_cast(zoom)), + 0, + 0, + 0}; uniforms.createOrUpdate(idLineUBOName, &lineUBO, context); // properties UBO @@ -140,8 +151,9 @@ void LineLayerTweaker::execute(LayerGroupBase& layerGroup, const PaintParameters case LineType::Gradient: { const LineGradientUBO lineGradientUBO{ /*matrix = */ util::cast(matrix), - /*units_to_pixels = */ {1.0f / parameters.pixelsToGLUnits[0], 1.0f / parameters.pixelsToGLUnits[1]}, /*ratio = */ 1.0f / tileID.pixelsToTileUnits(1.0f, static_cast(zoom)), + 0, + 0, 0}; uniforms.createOrUpdate(idLineGradientUBOName, &lineGradientUBO, context); @@ -158,16 +170,14 @@ void LineLayerTweaker::execute(LayerGroupBase& layerGroup, const PaintParameters } const LinePatternUBO linePatternUBO{ /*matrix =*/util::cast(matrix), + /*scale =*/ {parameters.pixelRatio, 1 / tileID.pixelsToTileUnits(1, parameters.state.getIntegerZoom()), crossfade.fromScale, crossfade.toScale}, /*texsize =*/{static_cast(textureSize.width), static_cast(textureSize.height)}, - /*units_to_pixels =*/{1.0f / parameters.pixelsToGLUnits[0], 1.0f / parameters.pixelsToGLUnits[1]}, /*ratio =*/1.0f / tileID.pixelsToTileUnits(1.0f, static_cast(zoom)), - /*fade =*/crossfade.t, - 0, - 0}; + /*fade =*/crossfade.t}; uniforms.createOrUpdate(idLinePatternUBOName, &linePatternUBO, context); // properties UBO @@ -200,19 +210,20 @@ void LineLayerTweaker::execute(LayerGroupBase& layerGroup, const PaintParameters const float widthB = posB.width * crossfade.toScale; const LineSDFUBO lineSDFUBO{ /* matrix = */ util::cast(matrix), - {1.0f / parameters.pixelsToGLUnits[0], 1.0f / parameters.pixelsToGLUnits[1]}, + /* patternscale_a = */ {1.0f / tileID.pixelsToTileUnits(widthA, parameters.state.getIntegerZoom()), -posA.height / 2.0f}, /* patternscale_b = */ {1.0f / tileID.pixelsToTileUnits(widthB, parameters.state.getIntegerZoom()), -posB.height / 2.0f}, - /* ratio = */ 1.0f / - tileID.pixelsToTileUnits(1.0f, static_cast(parameters.state.getZoom())), + /* ratio = */ 1.0f / tileID.pixelsToTileUnits(1.0f, static_cast(zoom)), /* tex_y_a = */ posA.y, /* tex_y_b = */ posB.y, /* sdfgamma = */ static_cast(dashPatternTexture.getSize().width) / (std::min(widthA, widthB) * 256.0f * parameters.pixelRatio) / 2.0f, /* mix = */ crossfade.t, + 0, + 0, 0}; uniforms.createOrUpdate(idLineSDFUBOName, &lineSDFUBO, context); diff --git a/src/mbgl/renderer/layers/line_layer_tweaker.hpp b/src/mbgl/renderer/layers/line_layer_tweaker.hpp index 75a0b641802..724cc88f128 100644 --- a/src/mbgl/renderer/layers/line_layer_tweaker.hpp +++ b/src/mbgl/renderer/layers/line_layer_tweaker.hpp @@ -35,6 +35,7 @@ class LineLayerTweaker : public LayerTweaker { gfx::UniformBufferPtr lineGradientPropertiesBuffer; gfx::UniformBufferPtr linePatternPropertiesBuffer; gfx::UniformBufferPtr lineSDFPropertiesBuffer; + gfx::UniformBufferPtr dynamicBuffer; #if MLN_RENDER_BACKEND_METAL gfx::UniformBufferPtr permutationUniformBuffer; diff --git a/src/mbgl/renderer/sources/render_tile_source.cpp b/src/mbgl/renderer/sources/render_tile_source.cpp index da7e2b2d6b0..b05eb756754 100644 --- a/src/mbgl/renderer/sources/render_tile_source.cpp +++ b/src/mbgl/renderer/sources/render_tile_source.cpp @@ -210,13 +210,19 @@ void TileSourceRenderItem::updateDebugDrawables(DebugLayerGroupMap& debugLayerGr const auto matrix = LayerTweaker::getTileMatrix( tileID, parameters, {{0, 0}}, style::TranslateAnchorType::Viewport, false, false, false); - static const StringIdentity idLineUBOName = stringIndexer().get("LineUBO"); - const shaders::LineUBO lineUBO{ - /*matrix = */ util::cast(matrix), + static const StringIdentity idLineDynamicUBOName = stringIndexer().get("LineDynamicUBO"); + const shaders::LineDynamicUBO dynamicUBO = { /*units_to_pixels = */ {1.0f / parameters.pixelsToGLUnits[0], 1.0f / parameters.pixelsToGLUnits[1]}, - /*ratio = */ 1.0f / tileID.pixelsToTileUnits(1.0f, zoom), + 0, 0}; + static const StringIdentity idLineUBOName = stringIndexer().get("LineUBO"); + const shaders::LineUBO lineUBO{/*matrix = */ util::cast(matrix), + /*ratio = */ 1.0f / tileID.pixelsToTileUnits(1.0f, zoom), + 0, + 0, + 0}; + static const StringIdentity idLinePropertiesUBOName = stringIndexer().get("LinePropertiesUBO"); static const StringIdentity idLineInterpolationUBOName = stringIndexer().get("LineInterpolationUBO"); @@ -229,6 +235,7 @@ void TileSourceRenderItem::updateDebugDrawables(DebugLayerGroupMap& debugLayerGr 0, 0}; auto& uniforms = drawable.mutableUniformBuffers(); + uniforms.createOrUpdate(idLineDynamicUBOName, &dynamicUBO, parameters.context); uniforms.createOrUpdate(idLineUBOName, &lineUBO, parameters.context); uniforms.createOrUpdate(idLinePropertiesUBOName, &linePropertiesUBO, parameters.context); uniforms.createOrUpdate(idLineInterpolationUBOName, &lineInterpolationUBO, parameters.context); diff --git a/src/mbgl/shaders/mtl/line.cpp b/src/mbgl/shaders/mtl/line.cpp index 49bccbff4b1..d7412dd7e62 100644 --- a/src/mbgl/shaders/mtl/line.cpp +++ b/src/mbgl/shaders/mtl/line.cpp @@ -13,10 +13,11 @@ const std::array ShaderSource ShaderSource::uniforms = { - UniformBlockInfo{8, true, true, sizeof(LineUBO), "LineUBO"}, - UniformBlockInfo{9, true, true, sizeof(LinePropertiesUBO), "LinePropertiesUBO"}, - UniformBlockInfo{10, true, false, sizeof(LineInterpolationUBO), "LineInterpolationUBO"}, +const std::array ShaderSource::uniforms = { + UniformBlockInfo{8, true, false, sizeof(LineGradientUBO), "LineDynamicUBO"}, + UniformBlockInfo{9, true, true, sizeof(LineUBO), "LineUBO"}, + UniformBlockInfo{10, true, true, sizeof(LinePropertiesUBO), "LinePropertiesUBO"}, + UniformBlockInfo{11, true, false, sizeof(LineInterpolationUBO), "LineInterpolationUBO"}, }; const std::array ShaderSource::textures = {}; @@ -31,11 +32,12 @@ const std::array ShaderSource ShaderSource::uniforms = { - UniformBlockInfo{9, true, true, sizeof(LinePatternUBO), "LinePatternUBO"}, - UniformBlockInfo{10, true, true, sizeof(LinePatternPropertiesUBO), "LinePatternPropertiesUBO"}, - UniformBlockInfo{11, true, false, sizeof(LinePatternInterpolationUBO), "LinePatternInterpolationUBO"}, - UniformBlockInfo{12, true, true, sizeof(LinePatternTilePropertiesUBO), "LinePatternTilePropertiesUBO"}, +const std::array ShaderSource::uniforms = { + UniformBlockInfo{9, true, false, sizeof(LineGradientUBO), "LineDynamicUBO"}, + UniformBlockInfo{10, true, true, sizeof(LinePatternUBO), "LinePatternUBO"}, + UniformBlockInfo{11, true, true, sizeof(LinePatternPropertiesUBO), "LinePatternPropertiesUBO"}, + UniformBlockInfo{12, true, false, sizeof(LinePatternInterpolationUBO), "LinePatternInterpolationUBO"}, + UniformBlockInfo{13, true, true, sizeof(LinePatternTilePropertiesUBO), "LinePatternTilePropertiesUBO"}, }; const std::array ShaderSource::textures = { TextureInfo{0, "u_image"}, @@ -52,10 +54,11 @@ const std::array ShaderSource ShaderSource::uniforms = { - UniformBlockInfo{9, true, true, sizeof(LineSDFUBO), "LineSDFUBO"}, - UniformBlockInfo{10, true, true, sizeof(LineSDFPropertiesUBO), "LineSDFPropertiesUBO"}, - UniformBlockInfo{11, true, false, sizeof(LineSDFInterpolationUBO), "LineSDFInterpolationUBO"}, +const std::array ShaderSource::uniforms = { + UniformBlockInfo{9, true, false, sizeof(LineGradientUBO), "LineDynamicUBO"}, + UniformBlockInfo{10, true, true, sizeof(LineSDFUBO), "LineSDFUBO"}, + UniformBlockInfo{11, true, true, sizeof(LineSDFPropertiesUBO), "LineSDFPropertiesUBO"}, + UniformBlockInfo{12, true, false, sizeof(LineSDFInterpolationUBO), "LineSDFInterpolationUBO"}, }; const std::array ShaderSource::textures = { TextureInfo{0, "u_image"}, diff --git a/src/mbgl/shaders/mtl/line_gradient.cpp b/src/mbgl/shaders/mtl/line_gradient.cpp index 66b0ba7ce1b..9fcb5d65d29 100644 --- a/src/mbgl/shaders/mtl/line_gradient.cpp +++ b/src/mbgl/shaders/mtl/line_gradient.cpp @@ -12,10 +12,11 @@ const std::array ShaderSource ShaderSource::uniforms = { - UniformBlockInfo{7, true, true, sizeof(LineGradientUBO), "LineGradientUBO"}, - UniformBlockInfo{8, true, true, sizeof(LineGradientPropertiesUBO), "LineGradientPropertiesUBO"}, - UniformBlockInfo{9, true, false, sizeof(LineGradientInterpolationUBO), "LineGradientInterpolationUBO"}, +const std::array ShaderSource::uniforms = { + UniformBlockInfo{7, true, false, sizeof(LineGradientUBO), "LineDynamicUBO"}, + UniformBlockInfo{8, true, true, sizeof(LineGradientUBO), "LineGradientUBO"}, + UniformBlockInfo{9, true, true, sizeof(LineGradientPropertiesUBO), "LineGradientPropertiesUBO"}, + UniformBlockInfo{10, true, false, sizeof(LineGradientInterpolationUBO), "LineGradientInterpolationUBO"}, }; const std::array ShaderSource::textures = { TextureInfo{0, "u_image"}, diff --git a/src/mbgl/style/layers/custom_drawable_layer.cpp b/src/mbgl/style/layers/custom_drawable_layer.cpp index 5984e209327..37422228574 100644 --- a/src/mbgl/style/layers/custom_drawable_layer.cpp +++ b/src/mbgl/style/layers/custom_drawable_layer.cpp @@ -97,12 +97,16 @@ class LineDrawableTweaker : public gfx::DrawableTweaker { const auto matrix = LayerTweaker::getTileMatrix( tileID, parameters, {{0, 0}}, style::TranslateAnchorType::Viewport, false, false, false); + static const StringIdentity idLineDynamicUBOName = stringIndexer().get("LineDynamicUBO"); + const shaders::LineDynamicUBO dynamicUBO = { + /*units_to_pixels = */ {1.0f / parameters.pixelsToGLUnits[0], 1.0f / parameters.pixelsToGLUnits[1]}, 0, 0}; + static const StringIdentity idLineUBOName = stringIndexer().get("LineUBO"); - const shaders::LineUBO lineUBO{ - /*matrix = */ util::cast(matrix), - /*units_to_pixels = */ {1.0f / parameters.pixelsToGLUnits[0], 1.0f / parameters.pixelsToGLUnits[1]}, - /*ratio = */ 1.0f / tileID.pixelsToTileUnits(1.0f, zoom), - 0}; + const shaders::LineUBO lineUBO{/*matrix = */ util::cast(matrix), + /*ratio = */ 1.0f / tileID.pixelsToTileUnits(1.0f, zoom), + 0, + 0, + 0}; static const StringIdentity idLinePropertiesUBOName = stringIndexer().get("LinePropertiesUBO"); @@ -116,6 +120,7 @@ class LineDrawableTweaker : public gfx::DrawableTweaker { 0, 0}; auto& uniforms = drawable.mutableUniformBuffers(); + uniforms.createOrUpdate(idLineDynamicUBOName, &dynamicUBO, parameters.context); uniforms.createOrUpdate(idLineUBOName, &lineUBO, parameters.context); uniforms.createOrUpdate(idLinePropertiesUBOName, &linePropertiesUBO, parameters.context); uniforms.createOrUpdate(idLineInterpolationUBOName, &lineInterpolationUBO, parameters.context); From 8b2c748341912a4e5767205a55df670470ecb6a1 Mon Sep 17 00:00:00 2001 From: mwilsnd <53413200+mwilsnd@users.noreply.github.com> Date: Tue, 12 Dec 2023 12:39:34 -0500 Subject: [PATCH 15/18] OpenGL: Monotonic UBO allocator (#1911) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Bart Louwers --- CMakeLists.txt | 5 +- bazel/core.bzl | 4 + include/mbgl/gl/buffer_allocator.hpp | 177 ++++++ include/mbgl/gl/uniform_buffer_gl.hpp | 26 +- include/mbgl/mtl/context.hpp | 3 + .../mbgl/shaders/gl/drawable_fill_outline.hpp | 1 + src/mbgl/gfx/context.hpp | 3 + src/mbgl/gl/buffer_allocator.cpp | 518 ++++++++++++++++++ src/mbgl/gl/context.cpp | 40 +- src/mbgl/gl/context.hpp | 14 + src/mbgl/gl/drawable_gl.cpp | 2 + src/mbgl/gl/fence.cpp | 32 ++ src/mbgl/gl/fence.hpp | 22 + src/mbgl/gl/uniform_block_gl.cpp | 8 +- src/mbgl/gl/uniform_buffer_gl.cpp | 82 ++- src/mbgl/mtl/context.cpp | 3 + src/mbgl/renderer/renderer_impl.cpp | 2 + 17 files changed, 907 insertions(+), 35 deletions(-) create mode 100644 include/mbgl/gl/buffer_allocator.hpp create mode 100644 src/mbgl/gl/buffer_allocator.cpp create mode 100644 src/mbgl/gl/fence.cpp create mode 100644 src/mbgl/gl/fence.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 95610e26152..b8df248e3b3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -159,8 +159,8 @@ if(MLN_DRAWABLE_RENDERER) ${PROJECT_SOURCE_DIR}/include/mbgl/shaders/shader_program_base.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/util/identity.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/util/suppress_copies.hpp - ${PROJECT_SOURCE_DIR}/include/mbgl/shaders/gl/shader_program_gl.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/gl/buffer_allocator.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/gl/drawable_gl.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/gl/drawable_gl_builder.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/gl/layer_group_gl.hpp @@ -216,6 +216,7 @@ if(MLN_DRAWABLE_RENDERER) ${PROJECT_SOURCE_DIR}/src/mbgl/shaders/shader_program_base.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/util/identity.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/shaders/gl/shader_program_gl.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gl/buffer_allocator.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/gl/drawable_gl.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/gl/drawable_gl_builder.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/gl/drawable_gl_impl.hpp @@ -1067,6 +1068,8 @@ if(MLN_WITH_OPENGL) ${PROJECT_SOURCE_DIR}/src/mbgl/gl/command_encoder.hpp ${PROJECT_SOURCE_DIR}/src/mbgl/gl/context.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/gl/context.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gl/fence.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gl/fence.hpp ${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/custom_layer.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/layermanager/custom_layer_factory.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/custom_layer_impl.cpp diff --git a/bazel/core.bzl b/bazel/core.bzl index 8b5f2c4b5b9..21341a2da1c 100644 --- a/bazel/core.bzl +++ b/bazel/core.bzl @@ -854,6 +854,8 @@ MLN_OPENGL_SOURCE = [ "src/mbgl/gl/command_encoder.hpp", "src/mbgl/gl/context.cpp", "src/mbgl/gl/context.hpp", + "src/mbgl/gl/fence.cpp", + "src/mbgl/gl/fence.hpp", "src/mbgl/gl/debugging_extension.cpp", "src/mbgl/gl/debugging_extension.hpp", "src/mbgl/gl/defines.hpp", @@ -995,6 +997,7 @@ MLN_DRAWABLES_HEADERS = [ ] MLN_DRAWABLES_GL_SOURCE = [ + "src/mbgl/gl/buffer_allocator.cpp", "src/mbgl/gl/drawable_gl.cpp", "src/mbgl/gl/drawable_gl_builder.cpp", "src/mbgl/gl/drawable_gl_impl.hpp", @@ -1007,6 +1010,7 @@ MLN_DRAWABLES_GL_SOURCE = [ ] MLN_DRAWABLES_GL_HEADERS = [ + "include/mbgl/gl/buffer_allocator.hpp", "include/mbgl/gl/drawable_gl.hpp", "include/mbgl/gl/drawable_gl_builder.hpp", "include/mbgl/gl/layer_group_gl.hpp", diff --git a/include/mbgl/gl/buffer_allocator.hpp b/include/mbgl/gl/buffer_allocator.hpp new file mode 100644 index 00000000000..246a0f4fff7 --- /dev/null +++ b/include/mbgl/gl/buffer_allocator.hpp @@ -0,0 +1,177 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace mbgl { +namespace gl { + +class Context; +class Fence; + +class BufferRef { +public: + BufferRef(size_t pointer, size_t offset, size_t size_) + : bufferIndex(pointer), + bufferOffset(offset), + size(size_) {} + + // Size of our allocation + size_t getSize() const noexcept { return size; } + // Index into the array of buffers indicating our current buffer + size_t getBufferIndex() const noexcept { return bufferIndex; } + // Offset into the buffer where our allocation begins + intptr_t getBufferOffset() const noexcept { return bufferOffset; } + +private: + size_t bufferIndex = 0; + intptr_t bufferOffset = 0; + size_t size = 0; +}; + +/// @brief IBufferAllocator hides the underlying implementation of the buffer allocator scheme used. +class IBufferAllocator { +public: + virtual ~IBufferAllocator(){}; + + /// @brief Write data into a buffer managed by the allocator + /// @param data Pointer to data to write into the buffer + /// @param size Size in bytes of contents to copy from `data` + /// @param ref Buffer reference keeping the new allocation alive. + /// Note: The concrete type of BufferRef is assumed based on the class type of + /// IBufferAllocator. + /// @return False on allocation failure. + virtual bool write(const void* data, size_t size, BufferRef*& ref) noexcept = 0; + + /// @brief Release the allocation held by the given reference + /// @param ref Allocation reference to release + virtual void release(BufferRef* ref) noexcept = 0; + + /// Defragment the allocator's underlying buffer pool. + virtual void defragment(const std::shared_ptr& fence) noexcept = 0; + + /// Return the buffer page size used by the allocator. This is the maximum size a + /// single buffer can possible be. + virtual size_t pageSize() const noexcept = 0; + + /// Get the OpenGL object ID for the buffer at the given index. + virtual int32_t getBufferID(size_t bufferIndex) const noexcept = 0; +}; + +/// @brief A BufferRef holds a strong reference on a buffer sub-allocation, managed by a RelocatableBuffer. +/// @tparam OwnerClass The class type holding a reference on the buffer +template +class TypedBufferRef : public BufferRef { +public: + TypedBufferRef() = default; + TypedBufferRef(OwnerClass* buffer, size_t pointer, size_t offset, size_t size_) + : BufferRef(pointer, offset, size_), + ownerPtr(buffer) {} + TypedBufferRef(OwnerClass* buffer) + : ownerPtr(buffer) {} + + bool operator==(const TypedBufferRef& rhs) const noexcept { return ownerPtr == rhs.ownerPtr; } + + // The owner of this allocation + OwnerClass* getOwner() const noexcept { return ownerPtr; } + void setOwner(OwnerClass* owner) { ownerPtr = owner; } + +private: + OwnerClass* ownerPtr = nullptr; +}; + +/// A RelocatableBuffer is in essence a view onto an actual buffer. These actual buffers may move around +/// over time as fragmentation is managed and buffers are recycled. A RelocatableBuffer is aware of this +/// and actively participates in this memory relocation. +template +class RelocatableBuffer { +public: + RelocatableBuffer(IBufferAllocator& allocator_, OwnerClass* owner_) + : allocator(allocator_), + owner(owner_) { + assert(owner); + } + RelocatableBuffer(const RelocatableBuffer& rhs) + : allocator(rhs.allocator), + owner(rhs.owner), + contents(rhs.contents) { + allocate(contents.data(), contents.size()); + } + RelocatableBuffer(RelocatableBuffer&& rhs) noexcept + : allocator(rhs.allocator), + owner(rhs.owner), + ref(std::move(rhs.ref)), + contents(std::move(rhs.contents)) {} + ~RelocatableBuffer() { + if (ref) { + assert(ref->getOwner()); + allocator.release(ref); + } + } + + // As references are added to a buffer, reallocation of the underlying reference + // storage may occur. When that happens, we will be informed of the new memory location + // of our reference here. + void relocRef(TypedBufferRef* newRef) noexcept { ref = newRef; } + + // Get the current OpenGL buffer ID. Do not store this, bind it and discard after the active frame. + int32_t getBufferID() const noexcept { return ref ? allocator.getBufferID(ref->getBufferIndex()) : 0; } + + intptr_t getBindingOffset() const noexcept { return ref ? ref->getBufferOffset() : 0; } + + const std::vector& getContents() const noexcept { return contents; } + + void setOwner(OwnerClass* owner_) noexcept { owner = owner_; } + + /// Allocate buffer memory and copy `size` bytes into the allocation from `data` + void allocate(const void* data, size_t size) noexcept { + assert(owner); + + if (ref && ref->getOwner()) { + // If we're writing new data, we need to remove our ref from our old buffer. + allocator.release(ref); + ref = nullptr; + } + + BufferRef* reference = ref; + allocator.write(data, size, reference); + + ref = std::move(static_cast(reference)); + ref->setOwner(owner); + + contents.resize(size); + std::memcpy(contents.data(), data, size); + }; + + IBufferAllocator& allocator; + +private: + OwnerClass* owner = nullptr; + TypedBufferRef* ref = nullptr; // A strong reference to the active allocation backing this buffer + std::vector contents; // CPU-side buffer contents +}; + +class UniformBufferAllocator : public IBufferAllocator { +public: + UniformBufferAllocator(); + ~UniformBufferAllocator() override; + + bool write(const void* data, size_t size, BufferRef*& ref) noexcept override; + void release(BufferRef* ref) noexcept override; + void defragment(const std::shared_ptr& fence) noexcept override; + size_t pageSize() const noexcept override; + int32_t getBufferID(size_t bufferIndex) const noexcept override; + +private: + class Impl; + std::unique_ptr impl; +}; + +} // namespace gl +} // namespace mbgl diff --git a/include/mbgl/gl/uniform_buffer_gl.hpp b/include/mbgl/gl/uniform_buffer_gl.hpp index 579f5f523bf..9775dc9ad6c 100644 --- a/include/mbgl/gl/uniform_buffer_gl.hpp +++ b/include/mbgl/gl/uniform_buffer_gl.hpp @@ -2,6 +2,7 @@ #include #include +#include namespace mbgl { namespace gl { @@ -10,19 +11,28 @@ class UniformBufferGL final : public gfx::UniformBuffer { UniformBufferGL(const UniformBufferGL&); public: - UniformBufferGL(const void* data, std::size_t size_); - UniformBufferGL(UniformBufferGL&& other) - : UniformBuffer(std::move(other)) {} + UniformBufferGL(const void* data, std::size_t size_, IBufferAllocator& allocator); ~UniformBufferGL() override; - BufferID getID() const { return id; } - void update(const void* data, std::size_t size_) override; + UniformBufferGL(UniformBufferGL&& rhs) noexcept; + UniformBufferGL& operator=(const UniformBufferGL& rhs) = delete; + + BufferID getID() const; + gl::RelocatableBuffer& getManagedBuffer() noexcept { return managedBuffer; } + const gl::RelocatableBuffer& getManagedBuffer() const noexcept { return managedBuffer; } UniformBufferGL clone() const { return {*this}; } -protected: - BufferID id = 0; - uint32_t hash; + // gfx::UniformBuffer + void update(const void* data, std::size_t size_) override; + +private: + // If the requested UBO size is too large for the allocator, the UBO will manage its own allocation + bool isManagedAllocation = false; + BufferID localID; + gl::RelocatableBuffer managedBuffer; + + friend class UniformBufferArrayGL; }; /// Stores a collection of uniform buffers by name diff --git a/include/mbgl/mtl/context.hpp b/include/mbgl/mtl/context.hpp index 24d3b8e1611..83e73d3b5a7 100644 --- a/include/mbgl/mtl/context.hpp +++ b/include/mbgl/mtl/context.hpp @@ -50,6 +50,9 @@ class Context final : public gfx::Context { const RendererBackend& getBackend() const { return backend; } + void beginFrame() override; + void endFrame() override; + std::unique_ptr createCommandEncoder() override; /// Create a new buffer object diff --git a/include/mbgl/shaders/gl/drawable_fill_outline.hpp b/include/mbgl/shaders/gl/drawable_fill_outline.hpp index 2e90ca776e8..ebbd7034509 100644 --- a/include/mbgl/shaders/gl/drawable_fill_outline.hpp +++ b/include/mbgl/shaders/gl/drawable_fill_outline.hpp @@ -11,6 +11,7 @@ struct ShaderSource { static constexpr const char* vertex = R"(layout (std140) uniform FillOutlineDrawableUBO { highp mat4 u_matrix; highp vec2 u_world; + highp vec2 pad; }; layout (std140) uniform FillOutlineEvaluatedPropsUBO { highp vec4 u_outline_color; diff --git a/src/mbgl/gfx/context.hpp b/src/mbgl/gfx/context.hpp index 154d3590873..3ec22e7b766 100644 --- a/src/mbgl/gfx/context.hpp +++ b/src/mbgl/gfx/context.hpp @@ -65,6 +65,9 @@ class Context { Context& operator=(const Context& other) = delete; virtual ~Context() = default; + virtual void beginFrame() = 0; + virtual void endFrame() = 0; + /// Called at the end of a frame. virtual void performCleanup() = 0; diff --git a/src/mbgl/gl/buffer_allocator.cpp b/src/mbgl/gl/buffer_allocator.cpp new file mode 100644 index 00000000000..ced99efb4e2 --- /dev/null +++ b/src/mbgl/gl/buffer_allocator.cpp @@ -0,0 +1,518 @@ +#include +#include +#include +#include +#include + +namespace mbgl { + +using namespace platform; + +namespace gl { + +/// @brief BufferAllocator is a semi-generic allocation strategy for uniform buffer objects. +/// This allocator works by streaming buffer writes into sub-allocated sections of actual UBOs. +/// As UBOs fill up, they are marked 'in-flight' and placed in a waiting area for references to +/// be released. Once all references to a UBO are gone (Or the buffer is defragmented), the +/// buffer is recycled. +/// @tparam OwnerClass The class type having allocations managed by this allocator. +/// @tparam type GL_UNIFORM_BUFFER, no other type is currently valid. +/// @tparam PageSizeKB Size of underlying UBO allocations, in KB. 8KB has been observed to work well. +/// @tparam MaxFragmentationOccupancy Buffers under this percentage of occupancy are considered fragmented +/// and the defragmentation routine will attempt to relocate living references to them to other buffers. +/// @tparam MaxFreeBuffers When buffers are made free for recycling, buffers in excess of this amount are +/// released to reduce memory consumption. +/// @tparam InitialBufferSize Initial size of the reference list. 128 has been observed to work well. +template +class BufferAllocator : public IBufferAllocator { + static_assert(type == GL_UNIFORM_BUFFER); + +public: + static constexpr const size_t PageSize = 1024 * PageSizeKB; + static constexpr const size_t FragmentedOccupancyLevel = static_cast( + PageSize * (static_cast(MaxFragmentationOccupancy) / 100.0)); + using BufferTy = + BufferAllocator; + using RefTy = TypedBufferRef; + + ~BufferAllocator() override = default; + +private: + /// @brief A buffer in-flight is one that is being used by the GPU. Such buffers become free + /// once their `frameSync` fence becomes signaled. + struct InFlightBuffer { + size_t bufferIndex; // Index into buffers list + std::shared_ptr frameSync; + + InFlightBuffer(size_t buf, std::shared_ptr sync) + : bufferIndex(buf), + frameSync(std::move(sync)) {} + }; + +public: + /// @brief An allocated buffer the allocator manages. These buffers are sub-allocated + /// in monotonic fashion. Once a buffer's capacity has been reached, it is stored until + /// it can be recycled and a new buffer is acquired to replace it. + struct Buffer { + BufferTy& allocator; + + // The pointer points to the next unused region of memory in the buffer + ptrdiff_t pointer = 0; + + // References to buffer objects allocating from this block of memory. + // For performance reasons, we use a vector here. To deal with vector storage reallocation, + // vector resizes are handled explicitly and Refs are made aware of relocation events. + std::vector refs; + + // Backing GL object for this buffer + GLuint id = 0; + + // Tombstones indicating a removed ref. When a Ref is removed, a tombstone is added to this + // counter. This is much faster vs. erasing from our vector. This works because we enforce + // a monotonic allocation scheme on our buffers and recycle the whole buffer at once. + size_t tombstones = 0; + + // Used to drive defragmentation, occupancy is a measure of the number of bytes held by + // living references + size_t occupancyBytes = 0; + + // We need to know the index to ourselves in the main buffer list + size_t bufferIndex = 0; + + // If true, the buffer's memory has been released to compact memory + bool reclaimed = false; + + Buffer(BufferAllocator& allocator_) + : allocator(allocator_) { + refs.reserve(InitialBufferSize); + MBGL_CHECK_ERROR(glGenBuffers(1, &id)); + MBGL_CHECK_ERROR(glBindBuffer(type, id)); + MBGL_CHECK_ERROR(glBufferData(type, PageSize, nullptr, GL_DYNAMIC_DRAW)); + } + + ~Buffer() { + if (id != 0) { + glDeleteBuffers(1, &id); + id = 0; + } + } + + void reclaim() { + refs.clear(); + refs = decltype(refs)(); + + if (id != 0) { + glDeleteBuffers(1, &id); + id = 0; + } + } + + Buffer(const Buffer&) = delete; + Buffer(Buffer&& rhs) noexcept + : allocator(rhs.allocator), + pointer(rhs.pointer), + refs(std::move(rhs.refs)), + id(rhs.id), + tombstones(rhs.tombstones), + occupancyBytes(rhs.occupancyBytes), + bufferIndex(rhs.bufferIndex) { + rhs.id = 0; + } + + Buffer& operator=(const Buffer&) = delete; + Buffer& operator=(Buffer&& rhs) noexcept { + allocator = rhs.allocator; + pointer = rhs.pointer; + refs = std::move(rhs.refs); + id = rhs.id; + rhs.id = 0; + tombstones = rhs.tombstones; + occupancyBytes = rhs.occupancyBytes; + bufferIndex = rhs.bufferIndex; + return *this; + } + + void setBufferIndex(size_t index) noexcept { bufferIndex = index; } + + // Reset this buffer for a fresh recording session + void reset() { + assert(refs.size() - tombstones == 0); + pointer = tombstones = occupancyBytes = 0; + refs.clear(); + } + + /// @brief Once the buffer is full, we mark it 'in-flight', storing it in another location + /// in the allocator. The defragmentation routine will traverse this location for buffers + /// that can be recycled. + /// @param index The buffer index that maps to this buffer instance. + void markInFlight(size_t index) noexcept { allocator.inFlightBuffers.emplace_back(index); } + + /// @brief Number of living references to this buffer + size_t numRefs() const noexcept { return refs.size() - tombstones; } + + /// @brief Add a living reference to a sub-allocated region of this buffer + /// @param bufObj Managed instance that owns this sub-allocation + /// @param bufPtr Offset into the buffer the allocation begins at + /// @param size_ Size of the allocated region + /// @return BufferRef instance + RefTy* addRef(OwnerClass* bufObj, ptrdiff_t bufPtr, size_t size_) { + // Important: Check if our list needs to reallocate, if so we need to handle it manually + // so we can update the references with their new memory locations + if (refs.size() == refs.capacity()) { + auto oldList = std::exchange(refs, {}); + refs.reserve(oldList.size() * 2); + + for (size_t i = 0; i < oldList.size(); ++i) { + if (!oldList[i].getOwner()) { + // Released ref, skip it + continue; + } + + // Relocate the reference + refs.push_back(std::move(oldList[i])); + refs.back().getOwner()->getManagedBuffer().relocRef(&refs.back()); + } + + // Since we dropped released refs, we can clear the tombstone count + tombstones = 0; + } + + refs.emplace_back(bufObj, bufferIndex, bufPtr, size_); + occupancyBytes += size_; + return &refs.back(); + } + + /// @brief Remove a living reference from this buffer. + /// @note: Remember buffers are only permitted to grow monotonically. A buffer can only be + /// reused once all living references are removed or relocated to other buffers. + /// @param bufObj BufferRef to remove + void decRef(RefTy* bufObj) { +#ifndef NDEBUG + assert(bufObj->getOwner()); + // Sanity check this ref actually belongs + auto it = std::find(refs.begin(), refs.end(), *bufObj); + if (it != refs.end()) { + tombstones++; + } else { + assert(0); + } +#else + tombstones++; +#endif + bufObj->setOwner(nullptr); + + assert(occupancyBytes >= bufObj->getSize()); + occupancyBytes -= bufObj->getSize(); + } + + // Align a pointer on a set block size + static size_t align(size_t ptr, size_t alignment) { + if (ptr == alignment) { + return ptr; + } + return ((ptr + alignment) / alignment) * alignment; + } + }; + +public: + // Write a block of memory to the recording buffer + // Returns the base index of written memory in the buffer + bool write(const void* data, size_t size, BufferRef*& residentBuffer) noexcept override { + Buffer* buffer = nullptr; + if (buffers.size() == 0) { + // Query for the alignment we must use + int32_t alignment = 0; + MBGL_CHECK_ERROR(glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &alignment)); + + sharedBufferAlignment = static_cast(alignment); + recordingBuffer = newBuffer(); + } + buffer = &buffers[recordingBuffer]; + + const auto alignedSize = Buffer::align(size, sharedBufferAlignment); + + // Reached past the buffer range + if ((buffer->pointer + alignedSize) > PageSize) { + // Put the buffer in the pending list + buffer->markInFlight(recordingBuffer); + buffer = nullptr; + + // Look for a free buffer + const auto freeIndex = getFreeBuffer(); + if (freeIndex != -1) { + recordingBuffer = freeIndex; + } else { + // Otherwise, allocate a fresh buffer + recordingBuffer = newBuffer(); + } + buffer = &buffers[recordingBuffer]; + } + + assert(buffer); + if (!buffer) { + assert(0); + return false; + } + MBGL_CHECK_ERROR(glBindBuffer(type, buffer->id)); + + // Map the next available slice of memory + auto* buf = MBGL_CHECK_ERROR( + glMapBufferRange(type, + buffer->pointer, + alignedSize, + GL_MAP_INVALIDATE_RANGE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_WRITE_BIT)); + const auto writtenIndex = buffer->pointer; + + if (buf) { + std::memcpy(buf, data, size); + MBGL_CHECK_ERROR(glUnmapBuffer(type)); + + residentBuffer = buffer->addRef(nullptr, writtenIndex, size); + buffer->pointer += alignedSize; + } else { + assert(0); + return false; + } + +#ifndef NDEBUG + MBGL_CHECK_ERROR(glBindBuffer(type, 0)); +#endif + + return true; + } + + /// Release a living reference + void release(BufferRef* ref) noexcept override { + assert(ref); + if (!ref) { + return; + } + buffers[ref->getBufferIndex()].decRef(static_cast(ref)); + } + + // Look for buffers that either have zero living references or equal or under FragmentationThresh + // references. In the latter case, attempt to relocate these allocations to more utilized buffers. + void defragment(const std::shared_ptr& fence) noexcept override { + if (buffers.size() == 0) { + return; + } + intptr_t recycleIndex = recordingBuffer; + + // Phase one: Mark free buffers + updateInFlight(); + + // Phase two: Consolidate fragmentation + MBGL_CHECK_ERROR(glBindBuffer(type, buffers[recycleIndex].id)); + + for (auto it = inFlightBuffers.begin(); it != inFlightBuffers.end();) { + auto& fragBuffer = buffers[*it]; + if (fragBuffer.occupancyBytes > FragmentedOccupancyLevel) { + ++it; + continue; + } + + // This buffer is fragmented. Move allocations from this buffer and append to fresh ones + // so that this one may be recycled. + + // For each ref, append to new buffer and update that ref + for (auto refIt = fragBuffer.refs.begin(); refIt != fragBuffer.refs.end();) { + if (!refIt->getOwner()) { + ++refIt; + continue; // This is a released reference + } + + const auto alignedSize = Buffer::align(refIt->getOwner()->getSize(), sharedBufferAlignment); + + // 2.a: Get recycle buffer + if (recycleIndex == -1 || (buffers[recycleIndex].pointer + alignedSize) > PageSize) { + if (recycleIndex != -1) { + // We filled this one up, mark it in-flight and get a fresh one. + buffers[recycleIndex].markInFlight(recycleIndex); + } + + // We need to get a new buffer. + recycleIndex = recordingBuffer = getFreeBuffer(); + if (recycleIndex == -1) { + // No more buffers available, we're done. + break; + } + + MBGL_CHECK_ERROR(glBindBuffer(type, buffers[recycleIndex].id)); + } + + // 2.b: Copy into recycle buffer + if (!moveRef(*refIt, recycleIndex, alignedSize)) { + // Relocation failure, stop trying to defrag. +#ifndef NDEBUG + MBGL_CHECK_ERROR(glBindBuffer(GL_UNIFORM_BUFFER, 0)); +#endif + return; + } + } + + // All refs have relocated, clear this buffer. + if (fragBuffer.numRefs() == 0) { + fragBuffer.reset(); + // Store this buffer in a waiting area until the GPU is done using it + waitingFree.emplace_back(fragBuffer.bufferIndex, fence); + it = inFlightBuffers.erase(it); + } else { + // We only managed a partial (or no) defrag. Do nothing with this buffer. + ++it; + } + + // No more buffers to write into, stop. + if (recycleIndex == -1) { + break; + } + } + +#ifndef NDEBUG + MBGL_CHECK_ERROR(glBindBuffer(GL_UNIFORM_BUFFER, 0)); +#endif + + if (recordingBuffer == -1) { + recordingBuffer = newBuffer(); + } + } + + size_t pageSize() const noexcept override { return PageSize; } + + int32_t getBufferID(size_t bufferIndex) const noexcept override { return buffers[bufferIndex].id; } + +private: + size_t newBuffer() noexcept { + buffers.emplace_back(*this); + const auto index = recordingBuffer = buffers.size() - 1; + buffers[index].setBufferIndex(index); + return index; + } + + intptr_t getFreeBuffer() { + if (freeList.size() > 0) { + assert(buffers[freeList.back()].pointer == 0); + const auto index = freeList.back(); + freeList.pop_back(); + return index; + } else if (reclaimedList.size() > 0) { + const auto index = reclaimedList.back(); + reclaimedList.pop_back(); + new (&buffers[index]) Buffer(*this); + buffers[index].setBufferIndex(index); + return index; + } + return -1; + } + + // Check waiting in-flight buffers and move them to the free list + void updateInFlight() { + for (auto it = waitingFree.begin(); it != waitingFree.end();) { + if (it->frameSync->isSignaled()) { + freeList.push_back(it->bufferIndex); + it = waitingFree.erase(it); + } else { + ++it; + } + } + + // And check the free list for an excess of buffers, reclaim if we need to + while (freeList.size() > MaxFreeBuffers) { + reclaimedList.push_back(freeList.back()); + freeList.pop_back(); + buffers[reclaimedList.back()].reclaim(); + } + } + + // Move a buffer reference (An allocation from a UniformBufferGL instance) to a new buffer + bool moveRef(RefTy& ref, size_t toIndex, size_t alignedSize) { + auto& destBuffer = buffers[toIndex]; + const auto recycledWriteIndex = destBuffer.pointer; + + auto* buf = MBGL_CHECK_ERROR( + glMapBufferRange(type, + recycledWriteIndex, + alignedSize, + GL_MAP_INVALIDATE_RANGE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_WRITE_BIT)); + + if (buf) { + std::memcpy(buf, ref.getOwner()->getManagedBuffer().getContents().data(), ref.getOwner()->getSize()); + MBGL_CHECK_ERROR(glUnmapBuffer(type)); + destBuffer.pointer += alignedSize; + } else { + assert(0); + return false; + } + + // 2.c: Now the ref must be made aware of the relocation of its contents. + // Note that this means defragmentation can never run on refs that are + // expected to remain bound after defragmentation. They must be re-bound + // to become valid again. + auto owner = ref.getOwner(); + const auto refSize = ref.getSize(); + const auto oldIndex = ref.getBufferIndex(); + + const auto newRef = destBuffer.addRef(owner, recycledWriteIndex, refSize); + buffers[oldIndex].decRef(&ref); + owner->getManagedBuffer().relocRef(newRef); + return true; + } + +private: + // GL requires us to satisfy this alignment for sub-allocations in our buffers + size_t sharedBufferAlignment = 0; + // Our active recording buffer + intptr_t recordingBuffer = 0; + + // Buffers free for re-use + std::vector freeList; + // Buffers reclaimed that can be recreated if needed + std::vector reclaimedList; + // Buffers recorded and currently in-use + std::list inFlightBuffers; + // When we recycle a buffer, we copy from an in-flight buffer to a fresh one. + // Before we can convert the in-flight buffer to a free one however, we must wait + // after the copy operation(s) to ensure the GPU is done with it before we reuse it. + std::list waitingFree; + +public: + // All buffers allocated so far + std::vector buffers; + + friend Buffer; +}; + +class UniformBufferAllocator::Impl : public gl::BufferAllocator {}; + +UniformBufferAllocator::~UniformBufferAllocator() = default; + +UniformBufferAllocator::UniformBufferAllocator() { + impl = std::make_unique(); +} + +bool UniformBufferAllocator::write(const void* data, size_t size, BufferRef*& ref) noexcept { + return impl->write(data, size, ref); +} + +void UniformBufferAllocator::release(BufferRef* ref) noexcept { + impl->release(ref); +} + +void UniformBufferAllocator::defragment(const std::shared_ptr& fence) noexcept { + impl->defragment(fence); +} + +size_t UniformBufferAllocator::pageSize() const noexcept { + return impl->pageSize(); +} + +int32_t UniformBufferAllocator::getBufferID(size_t bufferIndex) const noexcept { + return impl->getBufferID(bufferIndex); +} + +} // namespace gl +} // namespace mbgl diff --git a/src/mbgl/gl/context.cpp b/src/mbgl/gl/context.cpp index 1ece3d10c8e..1fc9ccf58aa 100644 --- a/src/mbgl/gl/context.cpp +++ b/src/mbgl/gl/context.cpp @@ -73,7 +73,11 @@ GLint getMaxVertexAttribs() { Context::Context(RendererBackend& backend_) : gfx::Context(/*maximumVertexBindingCount=*/getMaxVertexAttribs()), - backend(backend_) {} + backend(backend_) { +#if MLN_DRAWABLE_RENDERER + uboAllocator = std::make_unique(); +#endif +} Context::~Context() noexcept { if (cleanupOnDestruction) { @@ -85,6 +89,32 @@ Context::~Context() noexcept { } } +void Context::beginFrame() { +#if MLN_DRAWABLE_RENDERER + frameInFlightFence = std::make_shared(); + + // Run allocator defragmentation on this frame interval. + constexpr auto defragFreq = 4; + + if (frameNum == defragFreq) { + uboAllocator->defragment(frameInFlightFence); + frameNum = 0; + } else { + frameNum++; + } +#endif +} + +void Context::endFrame() { +#if MLN_DRAWABLE_RENDERER + if (!frameInFlightFence) { + return; + } + + frameInFlightFence->insert(); +#endif +} + void Context::initializeExtensions(const std::function& getProcAddress) { if (const auto* extensions = reinterpret_cast(MBGL_CHECK_ERROR(glGetString(GL_EXTENSIONS)))) { auto fn = [&](std::initializer_list> probes) -> ProcAddress { @@ -494,7 +524,7 @@ gfx::UniqueDrawableBuilder Context::createDrawableBuilder(std::string name) { } gfx::UniformBufferPtr Context::createUniformBuffer(const void* data, std::size_t size, bool /*persistent*/) { - return std::make_shared(data, size); + return std::make_shared(data, size, *uboAllocator); } gfx::ShaderProgramBasePtr Context::getGenericShader(gfx::ShaderRegistry& shaders, const std::string& name) { @@ -634,6 +664,12 @@ void Context::finish() { MBGL_CHECK_ERROR(glFinish()); } +#if MLN_DRAWABLE_RENDERER +std::shared_ptr Context::getCurrentFrameFence() const { + return frameInFlightFence; +} +#endif + void Context::draw(const gfx::DrawMode& drawMode, std::size_t indexOffset, std::size_t indexLength) { switch (drawMode.type) { case gfx::DrawModeType::Points: diff --git a/src/mbgl/gl/context.hpp b/src/mbgl/gl/context.hpp index 810c9033c42..1c90020023f 100644 --- a/src/mbgl/gl/context.hpp +++ b/src/mbgl/gl/context.hpp @@ -16,6 +16,8 @@ #include #if MLN_DRAWABLE_RENDERER +#include +#include #include #endif @@ -44,6 +46,9 @@ class Context final : public gfx::Context { std::unique_ptr createCommandEncoder() override; + void beginFrame() override; + void endFrame() override; + void initializeExtensions(const std::function&); void enableDebugging(); @@ -81,6 +86,10 @@ class Context final : public gfx::Context { void finish(); +#if MLN_DRAWABLE_RENDERER + std::shared_ptr getCurrentFrameFence() const; +#endif + // Actually remove the objects we marked as abandoned with the above methods. // Only call this while the OpenGL context is exclusive to this thread. void performCleanup() override; @@ -134,6 +143,11 @@ class Context final : public gfx::Context { bool cleanupOnDestruction = true; std::unique_ptr debugging; +#if MLN_DRAWABLE_RENDERER + std::shared_ptr frameInFlightFence; + std::unique_ptr uboAllocator; + size_t frameNum = 0; +#endif public: State activeTextureUnit; diff --git a/src/mbgl/gl/drawable_gl.cpp b/src/mbgl/gl/drawable_gl.cpp index 7c368069a0f..f9b2728d338 100644 --- a/src/mbgl/gl/drawable_gl.cpp +++ b/src/mbgl/gl/drawable_gl.cpp @@ -71,10 +71,12 @@ void DrawableGL::draw(PaintParameters& parameters) const { } } +#ifndef NDEBUG context.bindVertexArray = value::BindVertexArray::Default; unbindTextures(); unbindUniformBuffers(); +#endif } void DrawableGL::setIndexData(gfx::IndexVectorBasePtr indexes, std::vector segments) { diff --git a/src/mbgl/gl/fence.cpp b/src/mbgl/gl/fence.cpp new file mode 100644 index 00000000000..eb8622da5e5 --- /dev/null +++ b/src/mbgl/gl/fence.cpp @@ -0,0 +1,32 @@ +#include +#include +#include + +namespace mbgl { +namespace gl { + +using namespace platform; + +Fence::Fence() {} + +Fence::~Fence() { + if (fence) { + glDeleteSync(fence); + } +} + +void Fence::insert() noexcept { + assert(!fence); + fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); +} + +bool Fence::isSignaled() const noexcept { + if (!fence) { + return false; + } + + return glClientWaitSync(fence, GL_SYNC_FLUSH_COMMANDS_BIT, 0); +} + +} // namespace gl +} // namespace mbgl diff --git a/src/mbgl/gl/fence.hpp b/src/mbgl/gl/fence.hpp new file mode 100644 index 00000000000..989cad8b257 --- /dev/null +++ b/src/mbgl/gl/fence.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include +#include + +namespace mbgl { +namespace gl { + +class Fence { +public: + Fence(); + ~Fence(); + + void insert() noexcept; + bool isSignaled() const noexcept; + +private: + platform::GLsync fence{nullptr}; +}; + +} // namespace gl +} // namespace mbgl diff --git a/src/mbgl/gl/uniform_block_gl.cpp b/src/mbgl/gl/uniform_block_gl.cpp index 8b93c87e050..c53a9cd8673 100644 --- a/src/mbgl/gl/uniform_block_gl.cpp +++ b/src/mbgl/gl/uniform_block_gl.cpp @@ -1,4 +1,4 @@ - +#include #include #include #include @@ -15,7 +15,11 @@ void UniformBlockGL::bindBuffer(const gfx::UniformBuffer& uniformBuffer) { assert(size == uniformBuffer.getSize()); GLint binding = index; const auto& uniformBufferGL = static_cast(uniformBuffer); - MBGL_CHECK_ERROR(glBindBufferBase(GL_UNIFORM_BUFFER, binding, uniformBufferGL.getID())); + MBGL_CHECK_ERROR(glBindBufferRange(GL_UNIFORM_BUFFER, + binding, + uniformBufferGL.getID(), + uniformBufferGL.getManagedBuffer().getBindingOffset(), + uniformBufferGL.getSize())); } void UniformBlockGL::unbindBuffer() { diff --git a/src/mbgl/gl/uniform_buffer_gl.cpp b/src/mbgl/gl/uniform_buffer_gl.cpp index 209ddc1ec18..6c6ab4adc79 100644 --- a/src/mbgl/gl/uniform_buffer_gl.cpp +++ b/src/mbgl/gl/uniform_buffer_gl.cpp @@ -1,9 +1,10 @@ +#include #include #include -#include #include #include +#include #include namespace mbgl { @@ -11,45 +12,82 @@ namespace gl { using namespace platform; -UniformBufferGL::UniformBufferGL(const void* data_, std::size_t size_) +UniformBufferGL::UniformBufferGL(const void* data_, std::size_t size_, IBufferAllocator& allocator_) : UniformBuffer(size_), - hash(util::crc32(data_, size_)) { - MBGL_CHECK_ERROR(glGenBuffers(1, &id)); - MBGL_CHECK_ERROR(glBindBuffer(GL_UNIFORM_BUFFER, id)); - MBGL_CHECK_ERROR(glBufferData(GL_UNIFORM_BUFFER, size, data_, GL_DYNAMIC_DRAW)); - MBGL_CHECK_ERROR(glBindBuffer(GL_UNIFORM_BUFFER, 0)); + managedBuffer(allocator_, this) { + if (size_ > managedBuffer.allocator.pageSize()) { + // Buffer is very large, won't fit in the provided allocator + MBGL_CHECK_ERROR(glGenBuffers(1, &localID)); + MBGL_CHECK_ERROR(glBindBuffer(GL_UNIFORM_BUFFER, localID)); + MBGL_CHECK_ERROR(glBufferData(GL_UNIFORM_BUFFER, size, data_, GL_DYNAMIC_DRAW)); + MBGL_CHECK_ERROR(glBindBuffer(GL_UNIFORM_BUFFER, 0)); + return; + } + + isManagedAllocation = true; + managedBuffer.allocate(data_, size_); +} + +UniformBufferGL::UniformBufferGL(UniformBufferGL&& rhs) noexcept + : UniformBuffer(rhs.size), + isManagedAllocation(rhs.isManagedAllocation), + localID(rhs.localID), + managedBuffer(std::move(rhs.managedBuffer)) { + managedBuffer.setOwner(this); } UniformBufferGL::UniformBufferGL(const UniformBufferGL& other) : UniformBuffer(other), - hash(other.hash) { - MBGL_CHECK_ERROR(glGenBuffers(1, &id)); - MBGL_CHECK_ERROR(glCopyBufferSubData(other.id, id, 0, 0, size)); - MBGL_CHECK_ERROR(glBindBuffer(GL_COPY_READ_BUFFER, other.id)); - MBGL_CHECK_ERROR(glBindBuffer(GL_COPY_WRITE_BUFFER, id)); - MBGL_CHECK_ERROR(glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0, size)); + managedBuffer(other.managedBuffer.allocator, this) { + managedBuffer.setOwner(this); + if (other.isManagedAllocation) { + managedBuffer.allocate(other.managedBuffer.getContents().data(), other.size); + } else { + MBGL_CHECK_ERROR(glGenBuffers(1, &localID)); + MBGL_CHECK_ERROR(glCopyBufferSubData(other.localID, localID, 0, 0, size)); + MBGL_CHECK_ERROR(glBindBuffer(GL_COPY_READ_BUFFER, other.localID)); + MBGL_CHECK_ERROR(glBindBuffer(GL_COPY_WRITE_BUFFER, localID)); + MBGL_CHECK_ERROR(glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0, size)); + } } UniformBufferGL::~UniformBufferGL() { - if (id) { - MBGL_CHECK_ERROR(glDeleteBuffers(1, &id)); - id = 0; + if (isManagedAllocation) { + return; + } + + if (localID) { + MBGL_CHECK_ERROR(glDeleteBuffers(1, &localID)); + localID = 0; + } +} + +BufferID UniformBufferGL::getID() const { + if (isManagedAllocation) { + return managedBuffer.getBufferID(); + } else { + return localID; } } void UniformBufferGL::update(const void* data_, std::size_t size_) { - assert(size == size_); - if (size != size_) { + assert(isManagedAllocation ? managedBuffer.getContents().size() == size_ : size == size_); + + if (size != size_ || (isManagedAllocation && managedBuffer.getContents().size() != size_)) { Log::Error( Event::General, "Mismatched size given to UBO update, expected " + std::to_string(size) + ", got " + std::to_string(size_)); return; } - const uint32_t newHash = util::crc32(data_, size_); - if (newHash != hash) { - hash = newHash; - MBGL_CHECK_ERROR(glBindBuffer(GL_UNIFORM_BUFFER, id)); + if (std::memcmp(data_, managedBuffer.getContents().data(), managedBuffer.getContents().size()) == 0) { + return; + } + + if (isManagedAllocation) { + managedBuffer.allocate(data_, size); + } else { + MBGL_CHECK_ERROR(glBindBuffer(GL_UNIFORM_BUFFER, localID)); MBGL_CHECK_ERROR(glBufferSubData(GL_UNIFORM_BUFFER, 0, size_, data_)); MBGL_CHECK_ERROR(glBindBuffer(GL_UNIFORM_BUFFER, 0)); } diff --git a/src/mbgl/mtl/context.cpp b/src/mbgl/mtl/context.cpp index bb075bfc738..ee4ad699503 100644 --- a/src/mbgl/mtl/context.cpp +++ b/src/mbgl/mtl/context.cpp @@ -60,6 +60,9 @@ Context::~Context() noexcept { } } +void Context::beginFrame() {} +void Context::endFrame() {} + std::unique_ptr Context::createCommandEncoder() { return std::make_unique(*this); } diff --git a/src/mbgl/renderer/renderer_impl.cpp b/src/mbgl/renderer/renderer_impl.cpp index f74cf2e6e53..22d9704761d 100644 --- a/src/mbgl/renderer/renderer_impl.cpp +++ b/src/mbgl/renderer/renderer_impl.cpp @@ -137,6 +137,7 @@ void Renderer::Impl::render(const RenderTree& renderTree, // Blocks execution until the renderable is available. backend.getDefaultRenderable().wait(); + context.beginFrame(); if (!staticData) { staticData = std::make_unique(pixelRatio, std::make_unique()); @@ -471,6 +472,7 @@ void Renderer::Impl::render(const RenderTree& renderTree, // CommandEncoder destructor submits render commands. parameters.encoder.reset(); + context.endFrame(); #if MLN_RENDER_BACKEND_METAL if constexpr (EnableMetalCapture) { From a29f18f32ae41f0f19cde49ac76a17471b40ec8c Mon Sep 17 00:00:00 2001 From: abebeos <149062843+abebeos@users.noreply.github.com> Date: Tue, 12 Dec 2023 19:48:08 +0200 Subject: [PATCH 16/18] Convert java to kotlin (androidTest). (#1952) Co-authored-by: abebeos <129396476+abebeos@users.noreply.github.com> --- .../android/maps/IconManagerResolver.java | 42 -- .../android/maps/IconManagerResolver.kt | 35 ++ .../android/maps/MapLibreMapTest.java | 480 ------------------ .../maplibre/android/maps/MapLibreMapTest.kt | 454 +++++++++++++++++ .../maplibre/android/maps/MapLibreTest.java | 68 --- .../org/maplibre/android/maps/MapLibreTest.kt | 62 +++ 6 files changed, 551 insertions(+), 590 deletions(-) delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/org/maplibre/android/maps/IconManagerResolver.java create mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/org/maplibre/android/maps/IconManagerResolver.kt delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/org/maplibre/android/maps/MapLibreMapTest.java create mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/org/maplibre/android/maps/MapLibreMapTest.kt delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/org/maplibre/android/maps/MapLibreTest.java create mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/org/maplibre/android/maps/MapLibreTest.kt diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/org/maplibre/android/maps/IconManagerResolver.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/org/maplibre/android/maps/IconManagerResolver.java deleted file mode 100644 index 5bf443bebb8..00000000000 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/org/maplibre/android/maps/IconManagerResolver.java +++ /dev/null @@ -1,42 +0,0 @@ -package org.maplibre.android.maps; - -import org.maplibre.android.annotations.Icon; - -import java.lang.reflect.Field; -import java.util.HashMap; -import java.util.Map; - -import timber.log.Timber; - -public class IconManagerResolver { - - private IconManager iconManager; - - public IconManagerResolver(MapLibreMap maplibreMap) { - try { - Field annotationManagerField = MapLibreMap.class.getDeclaredField("annotationManager"); - annotationManagerField.setAccessible(true); - AnnotationManager annotationManager = (AnnotationManager) annotationManagerField.get(maplibreMap); - - Field iconManagerField = AnnotationManager.class.getDeclaredField("iconManager"); - iconManagerField.setAccessible(true); - iconManager = (IconManager) iconManagerField.get(annotationManager); - } catch (Exception exception) { - Timber.e(exception, "Could not create IconManagerResolver, unable to reflect."); - } - } - - @SuppressWarnings("unchecked") - public Map getIconMap() { - try { - Field field = IconManager.class.getDeclaredField("iconMap"); - field.setAccessible(true); - return (Map) field.get(iconManager); - } catch (NoSuchFieldException exception) { - Timber.e(exception, "Could not getIconMap, unable to reflect."); - } catch (IllegalAccessException exception) { - Timber.e(exception, "Could not getIconMap, unable to reflect."); - } - return new HashMap<>(); - } -} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/org/maplibre/android/maps/IconManagerResolver.kt b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/org/maplibre/android/maps/IconManagerResolver.kt new file mode 100644 index 00000000000..df49b8cbfcf --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/org/maplibre/android/maps/IconManagerResolver.kt @@ -0,0 +1,35 @@ +package org.maplibre.android.maps + +import org.maplibre.android.annotations.Icon +import timber.log.Timber + +class IconManagerResolver(maplibreMap: MapLibreMap?) { + private var iconManager: IconManager? = null + + init { + try { + val annotationManagerField = MapLibreMap::class.java.getDeclaredField("annotationManager") + annotationManagerField.isAccessible = true + val annotationManager = annotationManagerField[maplibreMap] as AnnotationManager + val iconManagerField = AnnotationManager::class.java.getDeclaredField("iconManager") + iconManagerField.isAccessible = true + iconManager = iconManagerField[annotationManager] as IconManager + } catch (exception: Exception) { + Timber.e(exception, "Could not create IconManagerResolver, unable to reflect.") + } + } + + val iconMap: Map + get() { + try { + val field = IconManager::class.java.getDeclaredField("iconMap") + field.isAccessible = true + return field[iconManager] as Map + } catch (exception: NoSuchFieldException) { + Timber.e(exception, "Could not getIconMap, unable to reflect.") + } catch (exception: IllegalAccessException) { + Timber.e(exception, "Could not getIconMap, unable to reflect.") + } + return HashMap() + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/org/maplibre/android/maps/MapLibreMapTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/org/maplibre/android/maps/MapLibreMapTest.java deleted file mode 100644 index 0320c8771ae..00000000000 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/org/maplibre/android/maps/MapLibreMapTest.java +++ /dev/null @@ -1,480 +0,0 @@ -package org.maplibre.android.maps; - -import android.graphics.Color; -import android.view.View; - -import androidx.test.espresso.UiController; -import androidx.test.espresso.ViewAction; - -import org.maplibre.android.annotations.BaseMarkerOptions; -import org.maplibre.android.annotations.Marker; -import org.maplibre.android.annotations.MarkerOptions; -import org.maplibre.android.annotations.Polygon; -import org.maplibre.android.annotations.PolygonOptions; -import org.maplibre.android.annotations.Polyline; -import org.maplibre.android.annotations.PolylineOptions; -import org.maplibre.android.exceptions.InvalidMarkerPositionException; -import org.maplibre.android.geometry.LatLng; -import org.maplibre.android.testapp.R; -import org.maplibre.android.testapp.activity.EspressoTest; - -import org.hamcrest.Matcher; -import org.junit.Test; - -import java.util.ArrayList; -import java.util.List; - -import static androidx.test.espresso.Espresso.onView; -import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; -import static androidx.test.espresso.matcher.ViewMatchers.withId; -import static junit.framework.TestCase.assertFalse; -import static junit.framework.TestCase.assertNotNull; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -/** - * This test is responsible for testing the public API. - *

- * Methods executed on MapboxMap are called from a ViewAction to ensure correct synchronisation - * with the application UI-thread. - *

- * @deprecated remove this file when removing deprecated annotations - */ -@Deprecated -public class MapLibreMapTest extends EspressoTest { - - // - // InfoWindow - // - - @Test - public void testConcurrentInfoWindowEnabled() { - validateTestSetup(); - onView(withId(R.id.mapView)).perform(new MapLibreMapAction((uiController, view) -> { - maplibreMap.setAllowConcurrentMultipleOpenInfoWindows(true); - assertTrue("ConcurrentWindows should be true", maplibreMap.isAllowConcurrentMultipleOpenInfoWindows()); - })); - } - - @Test - public void testConcurrentInfoWindowDisabled() { - validateTestSetup(); - onView(withId(R.id.mapView)).perform(new MapLibreMapAction((uiController, view) -> { - maplibreMap.setAllowConcurrentMultipleOpenInfoWindows(false); - assertFalse("ConcurrentWindows should be false", maplibreMap.isAllowConcurrentMultipleOpenInfoWindows()); - })); - } - - @Test - public void testInfoWindowAdapter() { - validateTestSetup(); - onView(withId(R.id.mapView)).perform(new MapLibreMapAction((uiController, view) -> { - MapLibreMap.InfoWindowAdapter infoWindowAdapter = marker -> null; - maplibreMap.setInfoWindowAdapter(infoWindowAdapter); - assertEquals("InfoWindowAdpter should be the same", infoWindowAdapter, maplibreMap.getInfoWindowAdapter()); - })); - } - - // - // Annotations - // - - @Test - public void testAddMarker() { - validateTestSetup(); - onView(withId(R.id.mapView)).perform(new MapLibreMapAction((uiController, view) -> { - MarkerOptions markerOptions = new MarkerOptions().position(new LatLng()); - Marker marker = maplibreMap.addMarker(markerOptions); - assertTrue("Marker should be contained", maplibreMap.getMarkers().contains(marker)); - })); - } - - @Test(expected = InvalidMarkerPositionException.class) - public void testAddMarkerInvalidPosition() { - new MarkerOptions().getMarker(); - } - - @Test - public void testAddMarkers() { - validateTestSetup(); - onView(withId(R.id.mapView)).perform(new MapLibreMapAction((uiController, view) -> { - List markerList = new ArrayList<>(); - MarkerOptions markerOptions1 = new MarkerOptions().position(new LatLng()).title("a"); - MarkerOptions markerOptions2 = new MarkerOptions().position(new LatLng()).title("b"); - markerList.add(markerOptions1); - markerList.add(markerOptions2); - List markers = maplibreMap.addMarkers(markerList); - assertEquals("Markers size should be 2", 2, maplibreMap.getMarkers().size()); - assertTrue(maplibreMap.getMarkers().contains(markers.get(0))); - assertTrue(maplibreMap.getMarkers().contains(markers.get(1))); - })); - } - - @Test - public void testAddMarkersEmpty() { - validateTestSetup(); - onView(withId(R.id.mapView)).perform(new MapLibreMapAction((uiController, view) -> { - List markerList = new ArrayList<>(); - maplibreMap.addMarkers(markerList); - assertEquals("Markers size should be 0", 0, maplibreMap.getMarkers().size()); - })); - } - - @Test - public void testAddMarkersSingleMarker() { - validateTestSetup(); - onView(withId(R.id.mapView)).perform(new MapLibreMapAction((uiController, view) -> { - List markerList = new ArrayList<>(); - MarkerOptions markerOptions = new MarkerOptions().title("a").position(new LatLng()); - markerList.add(markerOptions); - List markers = maplibreMap.addMarkers(markerList); - assertEquals("Markers size should be 1", 1, maplibreMap.getMarkers().size()); - assertTrue(maplibreMap.getMarkers().contains(markers.get(0))); - })); - } - - @Test - public void testAddPolygon() { - validateTestSetup(); - onView(withId(R.id.mapView)).perform(new MapLibreMapAction((uiController, view) -> { - PolygonOptions polygonOptions = new PolygonOptions().add(new LatLng()); - Polygon polygon = maplibreMap.addPolygon(polygonOptions); - assertTrue("Polygon should be contained", maplibreMap.getPolygons().contains(polygon)); - })); - } - - @Test - public void testAddEmptyPolygon() { - validateTestSetup(); - onView(withId(R.id.mapView)).perform(new MapLibreMapAction((uiController, view) -> { - PolygonOptions polygonOptions = new PolygonOptions(); - Polygon polygon = maplibreMap.addPolygon(polygonOptions); - assertTrue("Polygon should be ignored", !maplibreMap.getPolygons().contains(polygon)); - })); - } - - @Test - public void testAddPolygons() { - validateTestSetup(); - onView(withId(R.id.mapView)).perform(new MapLibreMapAction((uiController, view) -> { - List polygonList = new ArrayList<>(); - PolygonOptions polygonOptions1 = new PolygonOptions().fillColor(Color.BLACK).add(new LatLng()); - PolygonOptions polygonOptions2 = new PolygonOptions().fillColor(Color.WHITE).add(new LatLng()); - PolygonOptions polygonOptions3 = new PolygonOptions(); - polygonList.add(polygonOptions1); - polygonList.add(polygonOptions2); - polygonList.add(polygonOptions3); - maplibreMap.addPolygons(polygonList); - assertEquals("Polygons size should be 2", 2, maplibreMap.getPolygons().size()); - assertTrue(maplibreMap.getPolygons().contains(polygonOptions1.getPolygon())); - assertTrue(maplibreMap.getPolygons().contains(polygonOptions2.getPolygon())); - assertTrue("Polygon should be ignored", !maplibreMap.getPolygons().contains(polygonOptions3.getPolygon())); - })); - } - - @Test - public void addPolygonsEmpty() { - validateTestSetup(); - onView(withId(R.id.mapView)).perform(new MapLibreMapAction((uiController, view) -> { - maplibreMap.addPolygons(new ArrayList()); - assertEquals("Polygons size should be 0", 0, maplibreMap.getPolygons().size()); - })); - } - - @Test - public void addPolygonsSingle() { - validateTestSetup(); - onView(withId(R.id.mapView)).perform(new MapLibreMapAction((uiController, view) -> { - List polygonList = new ArrayList<>(); - PolygonOptions polygonOptions = new PolygonOptions().fillColor(Color.BLACK).add(new LatLng()); - polygonList.add(polygonOptions); - maplibreMap.addPolygons(polygonList); - assertEquals("Polygons size should be 1", 1, maplibreMap.getPolygons().size()); - assertTrue(maplibreMap.getPolygons().contains(polygonOptions.getPolygon())); - })); - } - - @Test - public void testAddPolyline() { - validateTestSetup(); - onView(withId(R.id.mapView)).perform(new MapLibreMapAction((uiController, view) -> { - PolylineOptions polylineOptions = new PolylineOptions().add(new LatLng()); - Polyline polyline = maplibreMap.addPolyline(polylineOptions); - assertTrue("Polyline should be contained", maplibreMap.getPolylines().contains(polyline)); - })); - } - - @Test - public void testAddEmptyPolyline() { - validateTestSetup(); - onView(withId(R.id.mapView)).perform(new MapLibreMapAction((uiController, view) -> { - PolylineOptions polylineOptions = new PolylineOptions(); - Polyline polyline = maplibreMap.addPolyline(polylineOptions); - assertTrue("Polyline should be ignored", !maplibreMap.getPolylines().contains(polyline)); - })); - } - - @Test - public void testAddPolylines() { - validateTestSetup(); - onView(withId(R.id.mapView)).perform(new MapLibreMapAction((uiController, view) -> { - List polylineList = new ArrayList<>(); - PolylineOptions polygonOptions1 = new PolylineOptions().color(Color.BLACK).add(new LatLng()); - PolylineOptions polygonOptions2 = new PolylineOptions().color(Color.WHITE).add(new LatLng()); - PolylineOptions polygonOptions3 = new PolylineOptions(); - polylineList.add(polygonOptions1); - polylineList.add(polygonOptions2); - polylineList.add(polygonOptions3); - maplibreMap.addPolylines(polylineList); - assertEquals("Polygons size should be 2", 2, maplibreMap.getPolylines().size()); - assertTrue(maplibreMap.getPolylines().contains(polygonOptions1.getPolyline())); - assertTrue(maplibreMap.getPolylines().contains(polygonOptions2.getPolyline())); - assertTrue( - "Polyline should be ignored", !maplibreMap.getPolylines().contains(polygonOptions3.getPolyline()) - ); - })); - } - - @Test - public void testAddPolylinesEmpty() { - validateTestSetup(); - onView(withId(R.id.mapView)).perform(new MapLibreMapAction((uiController, view) -> { - maplibreMap.addPolylines(new ArrayList()); - assertEquals("Polygons size should be 0", 0, maplibreMap.getPolylines().size()); - })); - } - - @Test - public void testAddPolylinesSingle() { - validateTestSetup(); - onView(withId(R.id.mapView)).perform(new MapLibreMapAction((uiController, view) -> { - List polylineList = new ArrayList<>(); - PolylineOptions polygonOptions = new PolylineOptions().color(Color.BLACK).add(new LatLng()); - polylineList.add(polygonOptions); - maplibreMap.addPolylines(polylineList); - assertEquals("Polygons size should be 1", 1, maplibreMap.getPolylines().size()); - assertTrue(maplibreMap.getPolylines().contains(polygonOptions.getPolyline())); - })); - } - - @Test - public void testRemoveMarker() { - validateTestSetup(); - onView(withId(R.id.mapView)).perform(new MapLibreMapAction((uiController, view) -> { - MarkerOptions markerOptions = new MarkerOptions().position(new LatLng()); - Marker marker = maplibreMap.addMarker(markerOptions); - maplibreMap.removeMarker(marker); - assertTrue("Markers should be empty", maplibreMap.getMarkers().isEmpty()); - })); - } - - @Test - public void testRemovePolygon() { - validateTestSetup(); - onView(withId(R.id.mapView)).perform(new MapLibreMapAction((uiController, view) -> { - PolygonOptions polygonOptions = new PolygonOptions(); - Polygon polygon = maplibreMap.addPolygon(polygonOptions); - maplibreMap.removePolygon(polygon); - assertTrue("Polygons should be empty", maplibreMap.getPolylines().isEmpty()); - })); - } - - @Test - public void testRemovePolyline() { - validateTestSetup(); - onView(withId(R.id.mapView)).perform(new MapLibreMapAction((uiController, view) -> { - PolylineOptions polylineOptions = new PolylineOptions(); - Polyline polyline = maplibreMap.addPolyline(polylineOptions); - maplibreMap.removePolyline(polyline); - assertTrue("Polylines should be empty", maplibreMap.getPolylines().isEmpty()); - })); - } - - @Test - public void testRemoveAnnotation() { - validateTestSetup(); - onView(withId(R.id.mapView)).perform(new MapLibreMapAction((uiController, view) -> { - MarkerOptions markerOptions = new MarkerOptions().position(new LatLng()); - Marker marker = maplibreMap.addMarker(markerOptions); - maplibreMap.removeAnnotation(marker); - assertTrue("Annotations should be empty", maplibreMap.getAnnotations().isEmpty()); - })); - } - - @Test - public void testRemoveAnnotationById() { - validateTestSetup(); - onView(withId(R.id.mapView)).perform(new MapLibreMapAction((uiController, view) -> { - MarkerOptions markerOptions = new MarkerOptions().position(new LatLng()); - maplibreMap.addMarker(markerOptions); - // id will always be 0 in unit tests - maplibreMap.removeAnnotation(0); - assertTrue("Annotations should be empty", maplibreMap.getAnnotations().isEmpty()); - })); - } - - @Test - public void testRemoveAnnotations() { - validateTestSetup(); - onView(withId(R.id.mapView)).perform(new MapLibreMapAction((uiController, view) -> { - List markerList = new ArrayList<>(); - MarkerOptions markerOptions1 = new MarkerOptions().title("a").position(new LatLng()); - MarkerOptions markerOptions2 = new MarkerOptions().title("b").position(new LatLng()); - markerList.add(markerOptions1); - markerList.add(markerOptions2); - maplibreMap.addMarkers(markerList); - maplibreMap.removeAnnotations(); - assertTrue("Annotations should be empty", maplibreMap.getAnnotations().isEmpty()); - })); - } - - @Test - public void testClear() { - validateTestSetup(); - onView(withId(R.id.mapView)).perform(new MapLibreMapAction((uiController, view) -> { - List markerList = new ArrayList<>(); - MarkerOptions markerOptions1 = new MarkerOptions().title("a").position(new LatLng()); - MarkerOptions markerOptions2 = new MarkerOptions().title("b").position(new LatLng()); - markerList.add(markerOptions1); - markerList.add(markerOptions2); - maplibreMap.addMarkers(markerList); - maplibreMap.clear(); - assertTrue("Annotations should be empty", maplibreMap.getAnnotations().isEmpty()); - })); - } - - @Test - public void testRemoveAnnotationsByList() { - validateTestSetup(); - onView(withId(R.id.mapView)).perform(new MapLibreMapAction((uiController, view) -> { - List markerList = new ArrayList<>(); - MarkerOptions markerOptions1 = new MarkerOptions().title("a").position(new LatLng()); - MarkerOptions markerOptions2 = new MarkerOptions().title("b").position(new LatLng()); - markerList.add(markerOptions1); - markerList.add(markerOptions2); - List markers = maplibreMap.addMarkers(markerList); - Marker marker = maplibreMap.addMarker(new MarkerOptions().position(new LatLng()).title("c")); - maplibreMap.removeAnnotations(markers); - assertTrue("Annotations should not be empty", maplibreMap.getAnnotations().size() == 1); - assertTrue("Marker should be contained", maplibreMap.getAnnotations().contains(marker)); - })); - } - - @Test - public void testGetAnnotationById() { - validateTestSetup(); - onView(withId(R.id.mapView)).perform(new MapLibreMapAction((uiController, view) -> { - MarkerOptions markerOptions = new MarkerOptions().position(new LatLng()); - Marker initialMarker = maplibreMap.addMarker(markerOptions); - Marker retrievedMarker = (Marker) maplibreMap.getAnnotation(0); - assertEquals("Markers should match", initialMarker, retrievedMarker); - })); - } - - @Test - public void testGetAnnotations() { - validateTestSetup(); - onView(withId(R.id.mapView)).perform( - new MapLibreMapAction((uiController, view) -> - assertNotNull("Annotations should be non null", maplibreMap.getAnnotations())) - ); - } - - @Test - public void testGetMarkers() { - validateTestSetup(); - onView(withId(R.id.mapView)).perform( - new MapLibreMapAction((uiController, view) -> - assertNotNull("Markers should be non null", maplibreMap.getMarkers())) - ); - } - - @Test - public void testGetPolygons() { - validateTestSetup(); - onView(withId(R.id.mapView)).perform(new MapLibreMapAction((uiController, view) -> - assertNotNull("Polygons should be non null", maplibreMap.getPolygons())) - ); - } - - @Test - public void testGetPolylines() { - validateTestSetup(); - onView(withId(R.id.mapView)).perform(new MapLibreMapAction((uiController, view) -> - assertNotNull("Polylines should be non null", maplibreMap.getPolylines())) - ); - } - - @Test - public void testGetSelectedMarkers() { - validateTestSetup(); - onView(withId(R.id.mapView)).perform(new MapLibreMapAction((uiController, view) -> - assertNotNull("Selected markers should be non null", maplibreMap.getSelectedMarkers())) - ); - } - - @Test - public void testSelectMarker() { - validateTestSetup(); - onView(withId(R.id.mapView)).perform(new MapLibreMapAction((uiController, view) -> { - MarkerOptions markerOptions = new MarkerOptions().position(new LatLng()); - Marker marker = maplibreMap.addMarker(markerOptions); - maplibreMap.selectMarker(marker); - assertTrue("Marker should be contained", maplibreMap.getSelectedMarkers().contains(marker)); - })); - } - - @Test - public void testDeselectMarker() { - validateTestSetup(); - onView(withId(R.id.mapView)).perform(new MapLibreMapAction((uiController, view) -> { - MarkerOptions markerOptions = new MarkerOptions().position(new LatLng()); - Marker marker = maplibreMap.addMarker(markerOptions); - maplibreMap.selectMarker(marker); - maplibreMap.deselectMarker(marker); - assertTrue("Selected markers should be empty", maplibreMap.getSelectedMarkers().isEmpty()); - })); - } - - @Test - public void testDeselectMarkers() { - validateTestSetup(); - onView(withId(R.id.mapView)).perform(new MapLibreMapAction((uiController, view) -> { - MarkerOptions markerOptions = new MarkerOptions().position(new LatLng()); - Marker marker1 = maplibreMap.addMarker(markerOptions); - Marker marker2 = maplibreMap.addMarker(markerOptions); - maplibreMap.selectMarker(marker1); - maplibreMap.selectMarker(marker2); - maplibreMap.deselectMarkers(); - assertTrue("Selected markers should be empty", maplibreMap.getSelectedMarkers().isEmpty()); - })); - } - - public class MapLibreMapAction implements ViewAction { - - private InvokeViewAction invokeViewAction; - - MapLibreMapAction(InvokeViewAction invokeViewAction) { - this.invokeViewAction = invokeViewAction; - } - - @Override - public Matcher getConstraints() { - return isDisplayed(); - } - - @Override - public String getDescription() { - return getClass().getSimpleName(); - } - - @Override - public void perform(UiController uiController, View view) { - invokeViewAction.onViewAction(uiController, view); - } - } - - interface InvokeViewAction { - void onViewAction(UiController uiController, View view); - } -} \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/org/maplibre/android/maps/MapLibreMapTest.kt b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/org/maplibre/android/maps/MapLibreMapTest.kt new file mode 100644 index 00000000000..76b57c07be3 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/org/maplibre/android/maps/MapLibreMapTest.kt @@ -0,0 +1,454 @@ +package org.maplibre.android.maps + +import android.graphics.Color +import android.view.View +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.UiController +import androidx.test.espresso.ViewAction +import androidx.test.espresso.matcher.ViewMatchers +import junit.framework.TestCase +import org.hamcrest.Matcher +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Test +import org.maplibre.android.annotations.BaseMarkerOptions +import org.maplibre.android.annotations.Marker +import org.maplibre.android.annotations.MarkerOptions +import org.maplibre.android.annotations.PolygonOptions +import org.maplibre.android.annotations.PolylineOptions +import org.maplibre.android.exceptions.InvalidMarkerPositionException +import org.maplibre.android.geometry.LatLng +import org.maplibre.android.maps.MapLibreMap.InfoWindowAdapter +import org.maplibre.android.testapp.R +import org.maplibre.android.testapp.activity.EspressoTest + +// J2K: IDE suggest "Add 'fun' modifier + +/** + * This test is responsible for testing the public API. + * + * + * Methods executed on MapboxMap are called from a ViewAction to ensure correct synchronisation + * with the application UI-thread. + * + */ +@Deprecated("remove this file when removing deprecated annotations") +class MapLibreMapTest : EspressoTest() { + // + // InfoWindow + // + @Test + fun testConcurrentInfoWindowEnabled() { + validateTestSetup() + onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? -> + maplibreMap.isAllowConcurrentMultipleOpenInfoWindows = true + assertTrue("ConcurrentWindows should be true", maplibreMap.isAllowConcurrentMultipleOpenInfoWindows) + }) + } + + @Test + fun testConcurrentInfoWindowDisabled() { + validateTestSetup() + onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? -> + maplibreMap.isAllowConcurrentMultipleOpenInfoWindows = false + TestCase.assertFalse("ConcurrentWindows should be false", maplibreMap.isAllowConcurrentMultipleOpenInfoWindows) + }) + } + + @Test + fun testInfoWindowAdapter() { + validateTestSetup() + onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? -> + val infoWindowAdapter = InfoWindowAdapter { marker: Marker? -> null } + maplibreMap.infoWindowAdapter = infoWindowAdapter + assertEquals("InfoWindowAdpter should be the same", infoWindowAdapter, maplibreMap.infoWindowAdapter) + }) + } + + // + // Annotations + // + @Test + fun testAddMarker() { + validateTestSetup() + onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? -> + val markerOptions = MarkerOptions().position(LatLng()) + val marker = maplibreMap.addMarker(markerOptions) + assertTrue("Marker should be contained", maplibreMap.markers.contains(marker)) + }) + } + + @Test(expected = InvalidMarkerPositionException::class) + fun testAddMarkerInvalidPosition() { + MarkerOptions().marker + } + + @Test + fun testAddMarkers() { + validateTestSetup() + onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? -> + val markerList: MutableList> = ArrayList() + val markerOptions1 = MarkerOptions().position(LatLng()).title("a") + val markerOptions2 = MarkerOptions().position(LatLng()).title("b") + markerList.add(markerOptions1) + markerList.add(markerOptions2) + val markers = maplibreMap.addMarkers(markerList) + assertEquals("Markers size should be 2", 2, maplibreMap.markers.size.toLong()) + assertTrue(maplibreMap.markers.contains(markers[0])) + assertTrue(maplibreMap.markers.contains(markers[1])) + }) + } + + @Test + fun testAddMarkersEmpty() { + validateTestSetup() + onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? -> + val markerList: List> = ArrayList() + maplibreMap.addMarkers(markerList) + assertEquals("Markers size should be 0", 0, maplibreMap.markers.size.toLong()) + }) + } + + @Test + fun testAddMarkersSingleMarker() { + validateTestSetup() + onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? -> + val markerList: MutableList> = ArrayList() + val markerOptions = MarkerOptions().title("a").position(LatLng()) + markerList.add(markerOptions) + val markers = maplibreMap.addMarkers(markerList) + assertEquals("Markers size should be 1", 1, maplibreMap.markers.size.toLong()) + assertTrue(maplibreMap.markers.contains(markers[0])) + }) + } + + @Test + fun testAddPolygon() { + validateTestSetup() + onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? -> + val polygonOptions = PolygonOptions().add(LatLng()) + val polygon = maplibreMap.addPolygon(polygonOptions) + assertTrue("Polygon should be contained", maplibreMap.polygons.contains(polygon)) + }) + } + + @Test + fun testAddEmptyPolygon() { + validateTestSetup() + onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? -> + val polygonOptions = PolygonOptions() + val polygon = maplibreMap.addPolygon(polygonOptions) + assertTrue("Polygon should be ignored", !maplibreMap.polygons.contains(polygon)) + }) + } + + @Test + fun testAddPolygons() { + validateTestSetup() + onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? -> + val polygonList: MutableList = ArrayList() + val polygonOptions1 = PolygonOptions().fillColor(Color.BLACK).add(LatLng()) + val polygonOptions2 = PolygonOptions().fillColor(Color.WHITE).add(LatLng()) + val polygonOptions3 = PolygonOptions() + polygonList.add(polygonOptions1) + polygonList.add(polygonOptions2) + polygonList.add(polygonOptions3) + maplibreMap.addPolygons(polygonList) + assertEquals("Polygons size should be 2", 2, maplibreMap.polygons.size.toLong()) + assertTrue(maplibreMap.polygons.contains(polygonOptions1.polygon)) + assertTrue(maplibreMap.polygons.contains(polygonOptions2.polygon)) + assertTrue("Polygon should be ignored", !maplibreMap.polygons.contains(polygonOptions3.polygon)) + }) + } + + @Test + fun addPolygonsEmpty() { + validateTestSetup() + onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? -> + maplibreMap.addPolygons(ArrayList()) + assertEquals("Polygons size should be 0", 0, maplibreMap.polygons.size.toLong()) + }) + } + + @Test + fun addPolygonsSingle() { + validateTestSetup() + onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? -> + val polygonList: MutableList = ArrayList() + val polygonOptions = PolygonOptions().fillColor(Color.BLACK).add(LatLng()) + polygonList.add(polygonOptions) + maplibreMap.addPolygons(polygonList) + assertEquals("Polygons size should be 1", 1, maplibreMap.polygons.size.toLong()) + assertTrue(maplibreMap.polygons.contains(polygonOptions.polygon)) + }) + } + + @Test + fun testAddPolyline() { + validateTestSetup() + onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? -> + val polylineOptions = PolylineOptions().add(LatLng()) + val polyline = maplibreMap.addPolyline(polylineOptions) + assertTrue("Polyline should be contained", maplibreMap.polylines.contains(polyline)) + }) + } + + @Test + fun testAddEmptyPolyline() { + validateTestSetup() + onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? -> + val polylineOptions = PolylineOptions() + val polyline = maplibreMap.addPolyline(polylineOptions) + assertTrue("Polyline should be ignored", !maplibreMap.polylines.contains(polyline)) + }) + } + + @Test + fun testAddPolylines() { + validateTestSetup() + onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? -> + val polylineList: MutableList = ArrayList() + val polygonOptions1 = PolylineOptions().color(Color.BLACK).add(LatLng()) + val polygonOptions2 = PolylineOptions().color(Color.WHITE).add(LatLng()) + val polygonOptions3 = PolylineOptions() + polylineList.add(polygonOptions1) + polylineList.add(polygonOptions2) + polylineList.add(polygonOptions3) + maplibreMap.addPolylines(polylineList) + assertEquals("Polygons size should be 2", 2, maplibreMap.polylines.size.toLong()) + assertTrue(maplibreMap.polylines.contains(polygonOptions1.polyline)) + assertTrue(maplibreMap.polylines.contains(polygonOptions2.polyline)) + assertTrue( + "Polyline should be ignored", !maplibreMap.polylines.contains(polygonOptions3.polyline) + ) + }) + } + + @Test + fun testAddPolylinesEmpty() { + validateTestSetup() + onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? -> + maplibreMap.addPolylines(ArrayList()) + assertEquals("Polygons size should be 0", 0, maplibreMap.polylines.size.toLong()) + }) + } + + @Test + fun testAddPolylinesSingle() { + validateTestSetup() + onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? -> + val polylineList: MutableList = ArrayList() + val polygonOptions = PolylineOptions().color(Color.BLACK).add(LatLng()) + polylineList.add(polygonOptions) + maplibreMap.addPolylines(polylineList) + assertEquals("Polygons size should be 1", 1, maplibreMap.polylines.size.toLong()) + assertTrue(maplibreMap.polylines.contains(polygonOptions.polyline)) + }) + } + + @Test + fun testRemoveMarker() { + validateTestSetup() + onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? -> + val markerOptions = MarkerOptions().position(LatLng()) + val marker = maplibreMap.addMarker(markerOptions) + maplibreMap.removeMarker(marker) + assertTrue("Markers should be empty", maplibreMap.markers.isEmpty()) + }) + } + + @Test + fun testRemovePolygon() { + validateTestSetup() + onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? -> + val polygonOptions = PolygonOptions() + val polygon = maplibreMap.addPolygon(polygonOptions) + maplibreMap.removePolygon(polygon) + assertTrue("Polygons should be empty", maplibreMap.polylines.isEmpty()) + }) + } + + @Test + fun testRemovePolyline() { + validateTestSetup() + onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? -> + val polylineOptions = PolylineOptions() + val polyline = maplibreMap.addPolyline(polylineOptions) + maplibreMap.removePolyline(polyline) + assertTrue("Polylines should be empty", maplibreMap.polylines.isEmpty()) + }) + } + + @Test + fun testRemoveAnnotation() { + validateTestSetup() + onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? -> + val markerOptions = MarkerOptions().position(LatLng()) + val marker = maplibreMap.addMarker(markerOptions) + maplibreMap.removeAnnotation(marker) + assertTrue("Annotations should be empty", maplibreMap.annotations.isEmpty()) + }) + } + + @Test + fun testRemoveAnnotationById() { + validateTestSetup() + onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? -> + val markerOptions = MarkerOptions().position(LatLng()) + maplibreMap.addMarker(markerOptions) + // id will always be 0 in unit tests + maplibreMap.removeAnnotation(0) + assertTrue("Annotations should be empty", maplibreMap.annotations.isEmpty()) + }) + } + + @Test + fun testRemoveAnnotations() { + validateTestSetup() + onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? -> + val markerList: MutableList> = ArrayList() + val markerOptions1 = MarkerOptions().title("a").position(LatLng()) + val markerOptions2 = MarkerOptions().title("b").position(LatLng()) + markerList.add(markerOptions1) + markerList.add(markerOptions2) + maplibreMap.addMarkers(markerList) + maplibreMap.removeAnnotations() + assertTrue("Annotations should be empty", maplibreMap.annotations.isEmpty()) + }) + } + + @Test + fun testClear() { + validateTestSetup() + onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? -> + val markerList: MutableList> = ArrayList() + val markerOptions1 = MarkerOptions().title("a").position(LatLng()) + val markerOptions2 = MarkerOptions().title("b").position(LatLng()) + markerList.add(markerOptions1) + markerList.add(markerOptions2) + maplibreMap.addMarkers(markerList) + maplibreMap.clear() + assertTrue("Annotations should be empty", maplibreMap.annotations.isEmpty()) + }) + } + + @Test + fun testRemoveAnnotationsByList() { + validateTestSetup() + onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? -> + val markerList: MutableList> = ArrayList() + val markerOptions1 = MarkerOptions().title("a").position(LatLng()) + val markerOptions2 = MarkerOptions().title("b").position(LatLng()) + markerList.add(markerOptions1) + markerList.add(markerOptions2) + val markers = maplibreMap.addMarkers(markerList) + val marker = maplibreMap.addMarker(MarkerOptions().position(LatLng()).title("c")) + maplibreMap.removeAnnotations(markers) + assertTrue("Annotations should not be empty", maplibreMap.annotations.size == 1) + assertTrue("Marker should be contained", maplibreMap.annotations.contains(marker)) + }) + } + + @Test + fun testGetAnnotationById() { + validateTestSetup() + onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? -> + val markerOptions = MarkerOptions().position(LatLng()) + val initialMarker = maplibreMap.addMarker(markerOptions) + val retrievedMarker = maplibreMap.getAnnotation(0) as Marker? + assertEquals("Markers should match", initialMarker, retrievedMarker) + }) + } + + @Test + fun testGetAnnotations() { + validateTestSetup() + onView(ViewMatchers.withId(R.id.mapView)).perform( + MapLibreMapAction { uiController: UiController?, view: View? -> TestCase.assertNotNull("Annotations should be non null", maplibreMap.annotations) } + ) + } + + @Test + fun testGetMarkers() { + validateTestSetup() + onView(ViewMatchers.withId(R.id.mapView)).perform( + MapLibreMapAction { uiController: UiController?, view: View? -> TestCase.assertNotNull("Markers should be non null", maplibreMap.markers) } + ) + } + + @Test + fun testGetPolygons() { + validateTestSetup() + onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? -> TestCase.assertNotNull("Polygons should be non null", maplibreMap.polygons) } + ) + } + + @Test + fun testGetPolylines() { + validateTestSetup() + onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? -> TestCase.assertNotNull("Polylines should be non null", maplibreMap.polylines) } + ) + } + + @Test + fun testGetSelectedMarkers() { + validateTestSetup() + onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? -> TestCase.assertNotNull("Selected markers should be non null", maplibreMap.selectedMarkers) } + ) + } + + @Test + fun testSelectMarker() { + validateTestSetup() + onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? -> + val markerOptions = MarkerOptions().position(LatLng()) + val marker = maplibreMap.addMarker(markerOptions) + maplibreMap.selectMarker(marker) + assertTrue("Marker should be contained", maplibreMap.selectedMarkers.contains(marker)) + }) + } + + @Test + fun testDeselectMarker() { + validateTestSetup() + onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? -> + val markerOptions = MarkerOptions().position(LatLng()) + val marker = maplibreMap.addMarker(markerOptions) + maplibreMap.selectMarker(marker) + maplibreMap.deselectMarker(marker) + assertTrue("Selected markers should be empty", maplibreMap.selectedMarkers.isEmpty()) + }) + } + + @Test + fun testDeselectMarkers() { + validateTestSetup() + onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? -> + val markerOptions = MarkerOptions().position(LatLng()) + val marker1 = maplibreMap.addMarker(markerOptions) + val marker2 = maplibreMap.addMarker(markerOptions) + maplibreMap.selectMarker(marker1) + maplibreMap.selectMarker(marker2) + maplibreMap.deselectMarkers() + assertTrue("Selected markers should be empty", maplibreMap.selectedMarkers.isEmpty()) + }) + } + + inner class MapLibreMapAction internal constructor(private val invokeViewAction: InvokeViewAction) : ViewAction { + override fun getConstraints(): Matcher { + return ViewMatchers.isDisplayed() + } + + override fun getDescription(): String { + return javaClass.simpleName + } + + override fun perform(uiController: UiController, view: View) { + invokeViewAction.onViewAction(uiController, view) + } + } + + internal fun interface InvokeViewAction { + fun onViewAction(uiController: UiController?, view: View?) + } +} \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/org/maplibre/android/maps/MapLibreTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/org/maplibre/android/maps/MapLibreTest.java deleted file mode 100644 index 5784c50d71e..00000000000 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/org/maplibre/android/maps/MapLibreTest.java +++ /dev/null @@ -1,68 +0,0 @@ -package org.maplibre.android.maps; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertThrows; -import static org.junit.Assert.assertTrue; - -import androidx.test.annotation.UiThreadTest; -import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner; - -import org.maplibre.android.AppCenter; -import org.maplibre.android.MapLibre; -import org.maplibre.android.exceptions.MapLibreConfigurationException; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -@RunWith(AndroidJUnit4ClassRunner.class) -public class MapLibreTest extends AppCenter { - - private static final String API_KEY = "pk.0000000001"; - private static final String API_KEY_2 = "pk.0000000002"; - - private String realToken; - - @Before - public void setup() { - realToken = MapLibre.getApiKey(); - } - - @Test - @UiThreadTest - public void testConnected() { - assertTrue(MapLibre.isConnected()); - - // test manual connectivity - MapLibre.setConnected(true); - assertTrue(MapLibre.isConnected()); - MapLibre.setConnected(false); - assertFalse(MapLibre.isConnected()); - - // reset to Android connectivity - MapLibre.setConnected(null); - assertTrue(MapLibre.isConnected()); - } - - @Test - @UiThreadTest - public void setApiKey() { - MapLibre.setApiKey(API_KEY); - assertSame(API_KEY, MapLibre.getApiKey()); - MapLibre.setApiKey(API_KEY_2); - assertSame(API_KEY_2, MapLibre.getApiKey()); - } - - @Test - @UiThreadTest - public void setNullApiKey() { - assertThrows(MapLibreConfigurationException.class, () -> MapLibre.setApiKey(null)); - } - - @After - public void tearDown() { - MapLibre.setApiKey(realToken); - } -} \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/org/maplibre/android/maps/MapLibreTest.kt b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/org/maplibre/android/maps/MapLibreTest.kt new file mode 100644 index 00000000000..82a5ec869a9 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/org/maplibre/android/maps/MapLibreTest.kt @@ -0,0 +1,62 @@ +package org.maplibre.android.maps + +import androidx.test.annotation.UiThreadTest +import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner +import org.junit.After +import org.junit.Assert.* +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.maplibre.android.AppCenter +import org.maplibre.android.MapLibre.* +import org.maplibre.android.exceptions.MapLibreConfigurationException + +@RunWith(AndroidJUnit4ClassRunner::class) +class MapLibreTest : AppCenter() { + private var realToken: String? = null + @Before + fun setup() { + realToken = getApiKey() + } + + @Test + @UiThreadTest + fun testConnected() { + assertTrue(isConnected()) + + // test manual connectivity + setConnected(true) + assertTrue(isConnected()) + setConnected(false) + assertFalse(isConnected()) + + // reset to Android connectivity + setConnected(null) + assertTrue(isConnected()) + } + + @Test + @UiThreadTest + fun setApiKey() { + setApiKey(API_KEY) + assertSame(API_KEY, getApiKey()) + setApiKey(API_KEY_2) + assertSame(API_KEY_2, getApiKey()) + } + + @Test + @UiThreadTest + fun setNullApiKey() { + assertThrows(MapLibreConfigurationException::class.java) { setApiKey(null) } + } + + @After + fun tearDown() { + setApiKey(realToken) + } + + companion object { + private const val API_KEY = "pk.0000000001" + private const val API_KEY_2 = "pk.0000000002" + } +} \ No newline at end of file From 884af02462aaf05ccfd6bd7b3bacf41e2b41d375 Mon Sep 17 00:00:00 2001 From: Bart Louwers Date: Tue, 12 Dec 2023 20:42:34 +0100 Subject: [PATCH 17/18] Fix android-device-test.yml (#1953) --- .github/workflows/android-device-test.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/android-device-test.yml b/.github/workflows/android-device-test.yml index 1d7c063ece2..40858c3c2b1 100644 --- a/.github/workflows/android-device-test.yml +++ b/.github/workflows/android-device-test.yml @@ -36,7 +36,7 @@ jobs: devicePool: "arn:aws:devicefarm:us-west-2::devicepool:082d10e5-d7d7-48a5-ba5c-b33d66efa1f5", # benchmark-android.yaml # see https://github.com/maplibre/ci-runners/tree/main/aws-device-farm/custom-test-envs - "testSpecArn": "arn:aws:devicefarm:us-west-2:373521797162:upload:20687d72-0e46-403e-8f03-0941850665bc/14862afb-cf88-44aa-9f1e-5131cbb22f01" + testSpecArn: "arn:aws:devicefarm:us-west-2:373521797162:upload:20687d72-0e46-403e-8f03-0941850665bc/14862afb-cf88-44aa-9f1e-5131cbb22f01" } ] runs-on: ubuntu-latest @@ -100,6 +100,7 @@ jobs: AWS_DEVICE_FARM_PROJECT_ARN: ${{ vars.AWS_DEVICE_FARM_PROJECT_ARN }} AWS_DEVICE_FARM_DEVICE_POOL_ARN: ${{ matrix.test.devicePool }} externalData: ${{ matrix.test.externalData }} + testSpecArn: ${{ matrix.test.testSpecArn }} - uses: LouisBrunner/checks-action@v1.6.2 if: always() && (matrix.test.name != 'Android Benchmark' || steps.fc.outputs.comment-id) From f8e414a9dc83d753b8eb0a1bddedcefc492dc510 Mon Sep 17 00:00:00 2001 From: abebeos <149062843+abebeos@users.noreply.github.com> Date: Wed, 13 Dec 2023 00:13:37 +0200 Subject: [PATCH 18/18] convert java to kotlin (10 last files within 'AndroidSDK..test') (#1926) Co-authored-by: abebeos <129396476+abebeos@users.noreply.github.com> --- .../maplibre/android/MapLibreInjector.java | 36 --- .../org/maplibre/android/MapLibreInjector.kt | 32 +++ .../org/maplibre/android/MapLibreTest.java | 98 ------- .../java/org/maplibre/android/MapLibreTest.kt | 88 ++++++ .../engine/AndroidLocationEngineImplTest.java | 188 ------------ .../engine/AndroidLocationEngineImplTest.kt | 170 +++++++++++ .../engine/LocationEngineProxyTest.java | 105 ------- .../engine/LocationEngineProxyTest.kt | 88 ++++++ .../engine/LocationEngineRequestTest.java | 72 ----- .../engine/LocationEngineRequestTest.kt | 64 +++++ .../engine/LocationEngineResultTest.java | 133 --------- .../engine/LocationEngineResultTest.kt | 127 +++++++++ .../location/engine/LocationEngineTest.java | 143 ---------- .../location/engine/LocationEngineTest.kt | 127 +++++++++ ...usedLocationEngineImplAdditionalTest2.java | 153 ---------- ...eFusedLocationEngineImplAdditionalTest2.kt | 118 ++++++++ .../MapLibreFusedLocationEngineImplTest.java | 177 ------------ .../MapLibreFusedLocationEngineImplTest.kt | 166 +++++++++++ .../maplibre/android/utils/MockParcel.java | 268 ------------------ .../org/maplibre/android/utils/MockParcel.kt | 169 +++++++++++ 20 files changed, 1149 insertions(+), 1373 deletions(-) delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/MapLibreInjector.java create mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/MapLibreInjector.kt delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/MapLibreTest.java create mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/MapLibreTest.kt delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/location/engine/AndroidLocationEngineImplTest.java create mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/location/engine/AndroidLocationEngineImplTest.kt delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/location/engine/LocationEngineProxyTest.java create mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/location/engine/LocationEngineProxyTest.kt delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/location/engine/LocationEngineRequestTest.java create mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/location/engine/LocationEngineRequestTest.kt delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/location/engine/LocationEngineResultTest.java create mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/location/engine/LocationEngineResultTest.kt delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/location/engine/LocationEngineTest.java create mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/location/engine/LocationEngineTest.kt delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/location/engine/MapLibreFusedLocationEngineImplAdditionalTest2.java create mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/location/engine/MapLibreFusedLocationEngineImplAdditionalTest2.kt delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/location/engine/MapLibreFusedLocationEngineImplTest.java create mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/location/engine/MapLibreFusedLocationEngineImplTest.kt delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/utils/MockParcel.java create mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/utils/MockParcel.kt diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/MapLibreInjector.java b/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/MapLibreInjector.java deleted file mode 100644 index 94dbb8f4084..00000000000 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/MapLibreInjector.java +++ /dev/null @@ -1,36 +0,0 @@ -package org.maplibre.android; - -import android.content.Context; - -import androidx.annotation.NonNull; - -import org.maplibre.android.util.TileServerOptions; - -import java.lang.reflect.Field; - -public class MapLibreInjector { - - private static final String FIELD_INSTANCE = "INSTANCE"; - - public static void inject(@NonNull Context context, @NonNull String apiKey, - @NonNull TileServerOptions options) { - MapLibre maplibre = new MapLibre(context, apiKey, options); - try { - Field instance = MapLibre.class.getDeclaredField(FIELD_INSTANCE); - instance.setAccessible(true); - instance.set(maplibre, maplibre); - } catch (Exception exception) { - throw new AssertionError(); - } - } - - public static void clear() { - try { - Field field = MapLibre.class.getDeclaredField(FIELD_INSTANCE); - field.setAccessible(true); - field.set(field, null); - } catch (Exception exception) { - throw new AssertionError(); - } - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/MapLibreInjector.kt b/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/MapLibreInjector.kt new file mode 100644 index 00000000000..037f48bef2c --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/MapLibreInjector.kt @@ -0,0 +1,32 @@ +package org.maplibre.android + +import android.content.Context +import org.maplibre.android.MapLibre +import org.maplibre.android.util.TileServerOptions + +object MapLibreInjector { + private const val FIELD_INSTANCE = "INSTANCE" + @JvmStatic + fun inject(context: Context, apiKey: String, + options: TileServerOptions) { + val maplibre = MapLibre(context, apiKey, options) + try { + val instance = MapLibre::class.java.getDeclaredField(FIELD_INSTANCE) + instance.isAccessible = true + instance[maplibre] = maplibre + } catch (exception: Exception) { + throw AssertionError() + } + } + + @JvmStatic + fun clear() { + try { + val field = MapLibre::class.java.getDeclaredField(FIELD_INSTANCE) + field.isAccessible = true + field[field] = null + } catch (exception: Exception) { + throw AssertionError() + } + } +} \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/MapLibreTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/MapLibreTest.java deleted file mode 100644 index 2a7fe91a5e2..00000000000 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/MapLibreTest.java +++ /dev/null @@ -1,98 +0,0 @@ -package org.maplibre.android; - -import android.content.Context; -import android.content.res.Resources; -import android.content.res.TypedArray; -import android.util.AttributeSet; -import android.util.DisplayMetrics; - -import org.maplibre.android.exceptions.MapLibreConfigurationException; -import org.maplibre.android.maps.MapView; -import org.maplibre.android.utils.ConfigUtils; - -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.nullable; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class MapLibreTest { - - private Context context; - private Context appContext; - - @Rule - public ExpectedException expectedException = ExpectedException.none(); - - @Before - public void before() { - context = mock(Context.class); - appContext = mock(Context.class); - when(context.getApplicationContext()).thenReturn(appContext); - } - - @Test - public void testGetApiKey() { - final String apiKey = "pk.0000000001"; - MapLibreInjector.inject(context, apiKey, ConfigUtils.getMockedOptions()); - assertSame(apiKey, MapLibre.getApiKey()); - } - - @Test - public void testApplicationContext() { - MapLibreInjector.inject(context, "pk.0000000001", ConfigUtils.getMockedOptions()); - assertNotNull(MapLibre.getApplicationContext()); - assertNotEquals(context, appContext); - assertEquals(appContext, appContext); - } - - @Test - public void testPlainTokenValid() { - assertTrue(MapLibre.isApiKeyValid("apiKey")); - } - - @Test - public void testEmptyToken() { - assertFalse(MapLibre.isApiKeyValid("")); - } - - @Test - public void testNullToken() { - assertFalse(MapLibre.isApiKeyValid(null)); - } - - @Test - public void testNoInstance() { - DisplayMetrics displayMetrics = mock(DisplayMetrics.class); - Resources resources = mock(Resources.class); - when(resources.getDisplayMetrics()).thenReturn(displayMetrics); - when(context.getResources()).thenReturn(resources); - TypedArray typedArray = mock(TypedArray.class); - when(context.obtainStyledAttributes(nullable(AttributeSet.class), any(int[].class), anyInt(), anyInt())) - .thenReturn(typedArray); - - expectedException.expect(MapLibreConfigurationException.class); - expectedException.expectMessage( - "\nUsing MapView requires calling MapLibre.getInstance(Context context, String apiKey," - + " WellKnownTileServer wellKnownTileServer) before inflating or creating the view." - ); - new MapView(context); - } - - @After - public void after() { - MapLibreInjector.clear(); - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/MapLibreTest.kt b/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/MapLibreTest.kt new file mode 100644 index 00000000000..8dbe1f7e49b --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/MapLibreTest.kt @@ -0,0 +1,88 @@ +package org.maplibre.android + +import android.content.Context +import android.content.res.Resources +import android.content.res.TypedArray +import android.util.AttributeSet +import android.util.DisplayMetrics +import org.junit.After +import org.junit.Assert +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.rules.ExpectedException +import org.maplibre.android.MapLibreInjector.clear +import org.maplibre.android.MapLibreInjector.inject +import org.maplibre.android.exceptions.MapLibreConfigurationException +import org.maplibre.android.maps.MapView +import org.maplibre.android.utils.ConfigUtils.Companion.getMockedOptions +import org.mockito.ArgumentMatchers +import org.mockito.Mockito + +class MapLibreTest { + private var context: Context? = null + private var appContext: Context? = null + + @Rule + @JvmField // J2K: https://stackoverflow.com/a/33449455 + var expectedException = ExpectedException.none() + @Before + fun before() { + context = Mockito.mock(Context::class.java) + appContext = Mockito.mock(Context::class.java) + // J2K: https://www.baeldung.com/kotlin/smart-cast-to-type-is-impossible#2-using-the-safe-call-operator--and-a-scope-function + Mockito.`when`(context?.getApplicationContext()).thenReturn(appContext) + } + + @Test + fun testGetApiKey() { + val apiKey = "pk.0000000001" + inject(context!!, apiKey, getMockedOptions()) + Assert.assertSame(apiKey, MapLibre.getApiKey()) + } + + @Test + fun testApplicationContext() { + inject(context!!, "pk.0000000001", getMockedOptions()) + Assert.assertNotNull(MapLibre.getApplicationContext()) + Assert.assertNotEquals(context, appContext) + Assert.assertEquals(appContext, appContext) + } + + @Test + fun testPlainTokenValid() { + Assert.assertTrue(MapLibre.isApiKeyValid("apiKey")) + } + + @Test + fun testEmptyToken() { + Assert.assertFalse(MapLibre.isApiKeyValid("")) + } + + @Test + fun testNullToken() { + Assert.assertFalse(MapLibre.isApiKeyValid(null)) + } + + @Test + fun testNoInstance() { + val displayMetrics = Mockito.mock(DisplayMetrics::class.java) + val resources = Mockito.mock(Resources::class.java) + Mockito.`when`(resources.displayMetrics).thenReturn(displayMetrics) + Mockito.`when`(context!!.resources).thenReturn(resources) + val typedArray = Mockito.mock(TypedArray::class.java) + Mockito.`when`(context!!.obtainStyledAttributes(ArgumentMatchers.nullable(AttributeSet::class.java), ArgumentMatchers.any(IntArray::class.java), ArgumentMatchers.anyInt(), ArgumentMatchers.anyInt())) + .thenReturn(typedArray) + expectedException.expect(MapLibreConfigurationException::class.java) + expectedException.expectMessage(""" + + Using MapView requires calling MapLibre.getInstance(Context context, String apiKey, WellKnownTileServer wellKnownTileServer) before inflating or creating the view. + """.trimIndent()) + MapView(context!!) + } + + @After + fun after() { + clear() + } +} \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/location/engine/AndroidLocationEngineImplTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/location/engine/AndroidLocationEngineImplTest.java deleted file mode 100644 index beb5a4e726e..00000000000 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/location/engine/AndroidLocationEngineImplTest.java +++ /dev/null @@ -1,188 +0,0 @@ -package org.maplibre.android.location.engine; - -import android.app.PendingIntent; -import android.content.Context; -import android.location.Criteria; -import android.location.Location; -import android.location.LocationListener; -import android.location.LocationManager; -import android.os.Looper; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; - -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.atomic.AtomicReference; - -import static java.util.concurrent.TimeUnit.SECONDS; -import static junit.framework.TestCase.assertTrue; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -@RunWith(MockitoJUnitRunner.class) -public class AndroidLocationEngineImplTest { - private static final double LATITUDE = 37.7749; - private static final double LONGITUDE = 122.4194; - - @Mock - private LocationManager locationManagerMock; - - private LocationEngine engine; - private AndroidLocationEngineImpl androidLocationEngineImpl; - - @Before - public void setUp() { - Context context = mock(Context.class); - when(context.getSystemService(Context.LOCATION_SERVICE)).thenReturn(locationManagerMock); - androidLocationEngineImpl = new AndroidLocationEngineImpl(context); - engine = new LocationEngineProxy<>(androidLocationEngineImpl); - } - - @Test - public void getLastLocation() throws InterruptedException { - final CountDownLatch latch = new CountDownLatch(1); - final AtomicReference resultRef = new AtomicReference<>(); - - LocationEngineCallback callback = getCallback(resultRef, latch); - final Location location = getMockLocation(LATITUDE, LONGITUDE); - final LocationEngineResult expectedResult = getMockEngineResult(location); - - when(locationManagerMock.getLastKnownLocation(anyString())).thenReturn(location); - - engine.getLastLocation(callback); - assertTrue(latch.await(5, SECONDS)); - - LocationEngineResult result = resultRef.get(); - assertThat(result.getLastLocation()).isEqualTo(expectedResult.getLastLocation()); - } - - @Test - public void createListener() { - LocationEngineCallback callback = mock(LocationEngineCallback.class); - LocationListener locationListener = androidLocationEngineImpl.createListener(callback); - Location mockLocation = getMockLocation(LATITUDE, LONGITUDE); - locationListener.onLocationChanged(mockLocation); - ArgumentCaptor argument = ArgumentCaptor.forClass(LocationEngineResult.class); - verify(callback).onSuccess(argument.capture()); - - LocationEngineResult result = argument.getValue(); - assertThat(result.getLastLocation()).isSameAs(mockLocation); - } - - @Test - public void requestLocationUpdatesWithNoPower() { - LocationEngineRequest request = new LocationEngineRequest.Builder(10) - .setPriority(LocationEngineRequest.PRIORITY_NO_POWER).build(); - LocationEngineCallback callback = mock(LocationEngineCallback.class); - Looper looper = mock(Looper.class); - Criteria criteria = mock(Criteria.class); - - engine.requestLocationUpdates(request, callback, looper); - verify(locationManagerMock, never()).getBestProvider(criteria, true); - } - - @Test - public void requestLocationUpdatesBestProviderNull() { - LocationEngineRequest request = new LocationEngineRequest.Builder(10) - .setPriority(LocationEngineRequest.PRIORITY_HIGH_ACCURACY).build(); - LocationEngineCallback callback = mock(LocationEngineCallback.class); - Looper looper = mock(Looper.class); - when(locationManagerMock.getBestProvider(any(Criteria.class), anyBoolean())).thenReturn(null); - - engine.requestLocationUpdates(request, callback, looper); - assertThat(androidLocationEngineImpl.currentProvider).isEqualTo("passive"); - } - - @Test - public void requestLocationUpdatesWithPendingIntent() { - LocationEngineRequest request = new LocationEngineRequest.Builder(10) - .setPriority(LocationEngineRequest.PRIORITY_HIGH_ACCURACY).build(); - PendingIntent pendingIntent = mock(PendingIntent.class); - - when(locationManagerMock.getBestProvider(any(Criteria.class), anyBoolean())).thenReturn(null); - - engine.requestLocationUpdates(request, pendingIntent); - assertThat(androidLocationEngineImpl.currentProvider).isEqualTo("passive"); - } - - @Test - public void removeLocationUpdatesForInvalidListener() { - LocationEngineCallback callback = mock(LocationEngineCallback.class); - engine.removeLocationUpdates(callback); - verify(locationManagerMock, never()).removeUpdates(any(LocationListener.class)); - } - - @Test - public void removeLocationUpdatesForPendingIntent() { - PendingIntent pendingIntent = mock(PendingIntent.class); - engine.removeLocationUpdates(pendingIntent); - verify(locationManagerMock, times(1)).removeUpdates(any(PendingIntent.class)); - } - - @Test - public void removeLocationUpdatesForValidListener() { - LocationEngineCallback callback = mock(LocationEngineCallback.class); - LocationEngineRequest request = new LocationEngineRequest.Builder(10) - .setPriority(LocationEngineRequest.PRIORITY_HIGH_ACCURACY).build(); - engine.requestLocationUpdates(request, callback, mock(Looper.class)); - engine.removeLocationUpdates(callback); - verify(locationManagerMock, times(1)).removeUpdates(any(LocationListener.class)); - } - - @Test(expected = NullPointerException.class) - public void getLastLocationNullCallback() { - engine.getLastLocation(null); - } - - @Test(expected = NullPointerException.class) - public void requestLocationUpdatesNullCallback() { - engine.requestLocationUpdates(null, null, null); - } - - @After - public void tearDown() { - reset(locationManagerMock); - engine = null; - } - - private static LocationEngineCallback getCallback( - final AtomicReference resultRef, - final CountDownLatch latch) { - return new LocationEngineCallback() { - @Override - public void onSuccess(LocationEngineResult result) { - resultRef.set(result); - latch.countDown(); - } - - @Override - public void onFailure(Exception exception) { - exception.printStackTrace(); - } - }; - } - - private static LocationEngineResult getMockEngineResult(Location location) { - return LocationEngineResult.create(location); - } - - private static Location getMockLocation(double lat, double lon) { - Location location = mock(Location.class); - location.setLatitude(lat); - location.setLongitude(lon); - return location; - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/location/engine/AndroidLocationEngineImplTest.kt b/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/location/engine/AndroidLocationEngineImplTest.kt new file mode 100644 index 00000000000..5d0f8e47077 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/location/engine/AndroidLocationEngineImplTest.kt @@ -0,0 +1,170 @@ +package org.maplibre.android.location.engine + +import android.app.PendingIntent +import android.content.Context +import android.location.Criteria +import android.location.Location +import android.location.LocationListener +import android.location.LocationManager +import android.os.Looper +import junit.framework.TestCase +import org.assertj.core.api.Assertions.* +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.ArgumentMatchers +import org.mockito.Mock +import org.mockito.Mockito.* +import org.mockito.junit.MockitoJUnitRunner +import java.util.concurrent.CountDownLatch +import java.util.concurrent.TimeUnit +import java.util.concurrent.atomic.AtomicReference + +@RunWith(MockitoJUnitRunner::class) +class AndroidLocationEngineImplTest { + @Mock + private val locationManagerMock: LocationManager? = null + private var engine: LocationEngine? = null + private var androidLocationEngineImpl: AndroidLocationEngineImpl? = null + @Before + fun setUp() { + val context = mock(Context::class.java) + `when`(context.getSystemService(Context.LOCATION_SERVICE)).thenReturn(locationManagerMock) + androidLocationEngineImpl = AndroidLocationEngineImpl(context) + engine = LocationEngineProxy(androidLocationEngineImpl) + } + + //@get:Throws(InterruptedException::class) + @Test + fun getLastLocation() { + val latch = CountDownLatch(1) + val resultRef = AtomicReference() + val callback = getCallback(resultRef, latch) + val location = getMockLocation(LATITUDE, LONGITUDE) + val expectedResult = getMockEngineResult(location) + `when`(locationManagerMock!!.getLastKnownLocation(anyString())).thenReturn(location) + engine!!.getLastLocation(callback) + TestCase.assertTrue(latch.await(5, TimeUnit.SECONDS)) + val result = resultRef.get() + assertThat(result.lastLocation).isEqualTo(expectedResult.lastLocation) + } + + @Test + fun createListener() { + val callback: LocationEngineCallback = mock(LocationEngineCallback::class.java) as LocationEngineCallback + val locationListener = androidLocationEngineImpl!!.createListener(callback) + val mockLocation = getMockLocation(LATITUDE, LONGITUDE) + locationListener.onLocationChanged(mockLocation) + val argument = ArgumentCaptor.forClass(LocationEngineResult::class.java) + verify(callback).onSuccess(argument.capture()) + val result = argument.value + assertThat(result.lastLocation).isSameAs(mockLocation) + } + + @Test + fun requestLocationUpdatesWithNoPower() { + val request = LocationEngineRequest.Builder(10) + .setPriority(LocationEngineRequest.PRIORITY_NO_POWER).build() + val callback: LocationEngineCallback = mock(LocationEngineCallback::class.java) as LocationEngineCallback + val looper = mock(Looper::class.java) + val criteria = mock(Criteria::class.java) + engine!!.requestLocationUpdates(request, callback, looper) + verify(locationManagerMock, never())?.getBestProvider(criteria, true) + } + + @Test + fun requestLocationUpdatesBestProviderNull() { + val request = LocationEngineRequest.Builder(10) + .setPriority(LocationEngineRequest.PRIORITY_HIGH_ACCURACY).build() + val callback: LocationEngineCallback = mock(LocationEngineCallback::class.java) as LocationEngineCallback + val looper = mock(Looper::class.java) + `when`(locationManagerMock!!.getBestProvider(any(Criteria::class.java), anyBoolean())).thenReturn(null) + engine!!.requestLocationUpdates(request, callback, looper) + assertThat(androidLocationEngineImpl!!.currentProvider).isEqualTo("passive") + } + + @Test + fun requestLocationUpdatesWithPendingIntent() { + val request = LocationEngineRequest.Builder(10) + .setPriority(LocationEngineRequest.PRIORITY_HIGH_ACCURACY).build() + val pendingIntent = mock(PendingIntent::class.java) + `when`(locationManagerMock!!.getBestProvider(any(Criteria::class.java), anyBoolean())).thenReturn(null) + engine!!.requestLocationUpdates(request, pendingIntent) + assertThat(androidLocationEngineImpl!!.currentProvider).isEqualTo("passive") + } + + @Test + fun removeLocationUpdatesForInvalidListener() { + val callback: LocationEngineCallback = mock(LocationEngineCallback::class.java) as LocationEngineCallback + engine!!.removeLocationUpdates(callback) + verify(locationManagerMock, never())?.removeUpdates(ArgumentMatchers.any(LocationListener::class.java)) + } + + @Test + fun removeLocationUpdatesForPendingIntent() { + val pendingIntent = mock(PendingIntent::class.java) + engine!!.removeLocationUpdates(pendingIntent) + verify(locationManagerMock, times(1))?.removeUpdates(ArgumentMatchers.any(PendingIntent::class.java)) + } + + @Test + fun removeLocationUpdatesForValidListener() { + val callback: LocationEngineCallback = mock(LocationEngineCallback::class.java) as LocationEngineCallback + val request = LocationEngineRequest.Builder(10) + .setPriority(LocationEngineRequest.PRIORITY_HIGH_ACCURACY).build() + engine!!.requestLocationUpdates(request, callback, mock(Looper::class.java)) + engine!!.removeLocationUpdates(callback) + verify(locationManagerMock, times(1))?.removeUpdates(ArgumentMatchers.any(LocationListener::class.java)) + } + + // J2K: removed "get". TODO: validate necessity of this test + @Test(expected = NullPointerException::class) + fun lastLocationNullCallback() { + engine!!.getLastLocation(null!!) + } + + // J2K: add "!!". TODO: validate necessity of this test + @Test(expected = NullPointerException::class) + fun requestLocationUpdatesNullCallback() { + engine!!.requestLocationUpdates(null!!, null!!, null!!) + } + + @After + fun tearDown() { + reset(locationManagerMock) + engine = null + } + + companion object { + private const val LATITUDE = 37.7749 + private const val LONGITUDE = 122.4194 + private fun getCallback( + resultRef: AtomicReference, + latch: CountDownLatch): LocationEngineCallback { + // J2K: remove "?" from LocationEngineResult? + return object : LocationEngineCallback { + override fun onSuccess(result: LocationEngineResult) { + resultRef.set(result) + latch.countDown() + } + + override fun onFailure(exception: Exception) { + exception.printStackTrace() + } + } + } + + private fun getMockEngineResult(location: Location): LocationEngineResult { + return LocationEngineResult.create(location) + } + + private fun getMockLocation(lat: Double, lon: Double): Location { + val location = mock(Location::class.java) + location.latitude = lat + location.longitude = lon + return location + } + } +} diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/location/engine/LocationEngineProxyTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/location/engine/LocationEngineProxyTest.java deleted file mode 100644 index bc4090644fe..00000000000 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/location/engine/LocationEngineProxyTest.java +++ /dev/null @@ -1,105 +0,0 @@ -package org.maplibre.android.location.engine; - -import android.location.LocationListener; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -@RunWith(MockitoJUnitRunner.class) -public class LocationEngineProxyTest { - @Mock - private LocationEngineCallback callback; - - @Mock - private LocationEngineImpl engineImpl; - - private LocationEngineProxy locationEngineProxy; - - @Before - public void setUp() { - locationEngineProxy = new LocationEngineProxy<>(engineImpl); - } - - @Test - public void testAddListener() { - AndroidLocationEngineImpl.AndroidLocationEngineCallbackTransport transport = - new AndroidLocationEngineImpl.AndroidLocationEngineCallbackTransport(callback); - when(engineImpl.createListener(callback)).thenReturn(transport); - - LocationListener locationListener = locationEngineProxy.getListener(callback); - assertThat(locationListener).isSameAs(transport); - assertThat(locationEngineProxy.getListenersCount()).isEqualTo(1); - } - - @Test - public void testAddListenerTwice() { - AndroidLocationEngineImpl.AndroidLocationEngineCallbackTransport transport = - new AndroidLocationEngineImpl.AndroidLocationEngineCallbackTransport(callback); - when(engineImpl.createListener(callback)).thenReturn(transport); - - locationEngineProxy.getListener(callback); - locationEngineProxy.getListener(callback); - assertThat(locationEngineProxy.getListenersCount()).isEqualTo(1); - } - - @Test - public void testAddTwoListeners() { - AndroidLocationEngineImpl.AndroidLocationEngineCallbackTransport transport = - new AndroidLocationEngineImpl.AndroidLocationEngineCallbackTransport(callback); - when(engineImpl.createListener(callback)).thenReturn(transport); - locationEngineProxy.getListener(callback); - - LocationEngineCallback anotherCallback = mock(LocationEngineCallback.class); - AndroidLocationEngineImpl.AndroidLocationEngineCallbackTransport anotherTransport = - new AndroidLocationEngineImpl.AndroidLocationEngineCallbackTransport(anotherCallback); - when(engineImpl.createListener(anotherCallback)).thenReturn(anotherTransport); - locationEngineProxy.getListener(anotherCallback); - assertThat(locationEngineProxy.getListenersCount()).isEqualTo(2); - } - - @Test - public void testRemoveListener() { - AndroidLocationEngineImpl.AndroidLocationEngineCallbackTransport transport = - new AndroidLocationEngineImpl.AndroidLocationEngineCallbackTransport(callback); - when(engineImpl.createListener(callback)).thenReturn(transport); - locationEngineProxy.getListener(callback); - - locationEngineProxy.removeListener(callback); - assertThat(locationEngineProxy.getListenersCount()).isEqualTo(0); - } - - @Test - public void testCheckRemovedListener() { - AndroidLocationEngineImpl.AndroidLocationEngineCallbackTransport transport = - new AndroidLocationEngineImpl.AndroidLocationEngineCallbackTransport(callback); - when(engineImpl.createListener(callback)).thenReturn(transport); - locationEngineProxy.getListener(callback); - - LocationEngineCallback anotherCallback = mock(LocationEngineCallback.class); - AndroidLocationEngineImpl.AndroidLocationEngineCallbackTransport anotherTransport = - new AndroidLocationEngineImpl.AndroidLocationEngineCallbackTransport(anotherCallback); - when(engineImpl.createListener(anotherCallback)).thenReturn(anotherTransport); - locationEngineProxy.getListener(anotherCallback); - - assertThat(locationEngineProxy.removeListener(callback)).isSameAs(transport); - assertThat(locationEngineProxy.removeListener(anotherCallback)).isSameAs(anotherTransport); - } - - @Test - public void testRemoveListenerTwice() { - AndroidLocationEngineImpl.AndroidLocationEngineCallbackTransport transport = - new AndroidLocationEngineImpl.AndroidLocationEngineCallbackTransport(callback); - when(engineImpl.createListener(callback)).thenReturn(transport); - locationEngineProxy.getListener(callback); - - assertThat(locationEngineProxy.removeListener(callback)).isSameAs(transport); - assertThat(locationEngineProxy.removeListener(callback)).isNull(); - } -} \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/location/engine/LocationEngineProxyTest.kt b/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/location/engine/LocationEngineProxyTest.kt new file mode 100644 index 00000000000..1998177ce63 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/location/engine/LocationEngineProxyTest.kt @@ -0,0 +1,88 @@ +package org.maplibre.android.location.engine + +import android.location.LocationListener +import org.assertj.core.api.Assertions.* +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.maplibre.android.location.engine.AndroidLocationEngineImpl.AndroidLocationEngineCallbackTransport +import org.mockito.Mock +import org.mockito.Mockito.* +import org.mockito.junit.MockitoJUnitRunner + +@RunWith(MockitoJUnitRunner::class) +class LocationEngineProxyTest { + @Mock + private val callback: LocationEngineCallback? = null + + @Mock + private val engineImpl: LocationEngineImpl? = null + private var locationEngineProxy: LocationEngineProxy? = null + @Before + fun setUp() { + locationEngineProxy = LocationEngineProxy(engineImpl) + } + + @Test + fun testAddListener() { + val transport = AndroidLocationEngineCallbackTransport(callback) + `when`(engineImpl!!.createListener(callback)).thenReturn(transport) + val locationListener = locationEngineProxy!!.getListener(callback!!) + assertThat(locationListener).isSameAs(transport) + assertThat(locationEngineProxy!!.listenersCount).isEqualTo(1) + } + + @Test + fun testAddListenerTwice() { + val transport = AndroidLocationEngineCallbackTransport(callback) + `when`(engineImpl!!.createListener(callback)).thenReturn(transport) + locationEngineProxy!!.getListener(callback!!) + locationEngineProxy!!.getListener(callback) + assertThat(locationEngineProxy!!.listenersCount).isEqualTo(1) + } + + @Test + fun testAddTwoListeners() { + val transport = AndroidLocationEngineCallbackTransport(callback) + `when`(engineImpl!!.createListener(callback)).thenReturn(transport) + locationEngineProxy!!.getListener(callback!!) + // J2K: using IDE suggestion "as LocationEngineCallback" + val anotherCallback: LocationEngineCallback = mock(LocationEngineCallback::class.java) as LocationEngineCallback + val anotherTransport = AndroidLocationEngineCallbackTransport(anotherCallback) + `when`(engineImpl.createListener(anotherCallback)).thenReturn(anotherTransport) + locationEngineProxy!!.getListener(anotherCallback) + assertThat(locationEngineProxy!!.listenersCount).isEqualTo(2) + } + + @Test + fun testRemoveListener() { + val transport = AndroidLocationEngineCallbackTransport(callback) + `when`(engineImpl!!.createListener(callback)).thenReturn(transport) + locationEngineProxy!!.getListener(callback!!) + locationEngineProxy!!.removeListener(callback) + assertThat(locationEngineProxy!!.listenersCount).isEqualTo(0) + } + + @Test + fun testCheckRemovedListener() { + val transport = AndroidLocationEngineCallbackTransport(callback) + `when`(engineImpl!!.createListener(callback)).thenReturn(transport) + locationEngineProxy!!.getListener(callback!!) + // J2K: using IDE suggestion "as LocationEngineCallback" + val anotherCallback: LocationEngineCallback = mock(LocationEngineCallback::class.java) as LocationEngineCallback + val anotherTransport = AndroidLocationEngineCallbackTransport(anotherCallback) + `when`(engineImpl.createListener(anotherCallback)).thenReturn(anotherTransport) + locationEngineProxy!!.getListener(anotherCallback) + assertThat(locationEngineProxy!!.removeListener(callback)).isSameAs(transport) + assertThat(locationEngineProxy!!.removeListener(anotherCallback)).isSameAs(anotherTransport) + } + + @Test + fun testRemoveListenerTwice() { + val transport = AndroidLocationEngineCallbackTransport(callback) + `when`(engineImpl!!.createListener(callback)).thenReturn(transport) + locationEngineProxy!!.getListener(callback!!) + assertThat(locationEngineProxy!!.removeListener(callback)).isSameAs(transport) + assertThat(locationEngineProxy!!.removeListener(callback)).isNull() + } +} diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/location/engine/LocationEngineRequestTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/location/engine/LocationEngineRequestTest.java deleted file mode 100644 index f8471e30b1a..00000000000 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/location/engine/LocationEngineRequestTest.java +++ /dev/null @@ -1,72 +0,0 @@ -package org.maplibre.android.location.engine; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.junit.MockitoJUnitRunner; - -import static org.assertj.core.api.Assertions.assertThat; - -@RunWith(MockitoJUnitRunner.class) -public class LocationEngineRequestTest { - - @Test - public void checkDefaultValues() { - LocationEngineRequest request = new LocationEngineRequest.Builder(1000L).build(); - - assertThat(request.getInterval()).isEqualTo(1000L); - assertThat(request.getDisplacement()).isEqualTo(0.0f); - assertThat(request.getMaxWaitTime()).isEqualTo(0L); - assertThat(request.getFastestInterval()).isEqualTo(0L); - } - - @Test - public void checkBuilder() { - LocationEngineRequest request = new LocationEngineRequest.Builder(2000L) - .setDisplacement(100.0f) - .setMaxWaitTime(5000L) - .setFastestInterval(500L) - .build(); - - assertThat(request.getInterval()).isEqualTo(2000L); - assertThat(request.getDisplacement()).isEqualTo(100.0f); - assertThat(request.getMaxWaitTime()).isEqualTo(5000L); - assertThat(request.getFastestInterval()).isEqualTo(500L); - } - - @Test - public void checkRequestEqual() { - LocationEngineRequest request = new LocationEngineRequest.Builder(2000L).build(); - LocationEngineRequest otherRequest = new LocationEngineRequest.Builder(2000L).build(); - - assertThat(request).isEqualTo(otherRequest); - assertThat(request.hashCode()).isEqualTo(otherRequest.hashCode()); - } - - @Test - public void checkRequestsNotEqual() { - LocationEngineRequest request = new LocationEngineRequest.Builder(2000L).build(); - LocationEngineRequest otherRequest = new LocationEngineRequest.Builder(2000L) - .setFastestInterval(500L) - .build(); - - assertThat(request).isNotEqualTo(otherRequest); - } - - @Test - public void checkRequestsEqualHashCode() { - LocationEngineRequest request = new LocationEngineRequest.Builder(2000L).build(); - LocationEngineRequest otherRequest = new LocationEngineRequest.Builder(2000L).build(); - - assertThat(request.hashCode()).isEqualTo(otherRequest.hashCode()); - } - - @Test - public void checkRequestsNonEqualHashCode() { - LocationEngineRequest request = new LocationEngineRequest.Builder(2000L).build(); - LocationEngineRequest otherRequest = new LocationEngineRequest.Builder(2000L) - .setFastestInterval(500L) - .build(); - - assertThat(request.hashCode()).isNotEqualTo(otherRequest.hashCode()); - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/location/engine/LocationEngineRequestTest.kt b/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/location/engine/LocationEngineRequestTest.kt new file mode 100644 index 00000000000..123fd8e1ffc --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/location/engine/LocationEngineRequestTest.kt @@ -0,0 +1,64 @@ +package org.maplibre.android.location.engine + +import org.assertj.core.api.Assertions.* +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.junit.MockitoJUnitRunner + +@RunWith(MockitoJUnitRunner::class) +class LocationEngineRequestTest { + @Test + fun checkDefaultValues() { + val request = LocationEngineRequest.Builder(1000L).build() + assertThat(request.interval).isEqualTo(1000L) + assertThat(request.displacement).isEqualTo(0.0f) + assertThat(request.maxWaitTime).isEqualTo(0L) + assertThat(request.fastestInterval).isEqualTo(0L) + } + + @Test + fun checkBuilder() { + val request = LocationEngineRequest.Builder(2000L) + .setDisplacement(100.0f) + .setMaxWaitTime(5000L) + .setFastestInterval(500L) + .build() + assertThat(request.interval).isEqualTo(2000L) + assertThat(request.displacement).isEqualTo(100.0f) + assertThat(request.maxWaitTime).isEqualTo(5000L) + assertThat(request.fastestInterval).isEqualTo(500L) + } + + @Test + fun checkRequestEqual() { + val request = LocationEngineRequest.Builder(2000L).build() + val otherRequest = LocationEngineRequest.Builder(2000L).build() + assertThat(request).isEqualTo(otherRequest) + assertThat(request.hashCode()).isEqualTo(otherRequest.hashCode()) + } + + @Test + fun checkRequestsNotEqual() { + val request = LocationEngineRequest.Builder(2000L).build() + val otherRequest = LocationEngineRequest.Builder(2000L) + .setFastestInterval(500L) + .build() + assertThat(request).isNotEqualTo(otherRequest) + } + + @Test + fun checkRequestsEqualHashCode() { + val request = LocationEngineRequest.Builder(2000L).build() + val otherRequest = LocationEngineRequest.Builder(2000L).build() + assertThat(request.hashCode()).isEqualTo(otherRequest.hashCode()) + } + + @Test + fun checkRequestsNonEqualHashCode() { + val request = LocationEngineRequest.Builder(2000L).build() + val otherRequest = LocationEngineRequest.Builder(2000L) + .setFastestInterval(500L) + .build() + assertThat(request.hashCode()).isNotEqualTo(otherRequest.hashCode()) + } +} diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/location/engine/LocationEngineResultTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/location/engine/LocationEngineResultTest.java deleted file mode 100644 index 762c34ee7fe..00000000000 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/location/engine/LocationEngineResultTest.java +++ /dev/null @@ -1,133 +0,0 @@ -package org.maplibre.android.location.engine; - -import android.content.Intent; -import android.location.Location; -import android.location.LocationManager; -import android.os.Bundle; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.junit.MockitoJUnitRunner; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -@RunWith(MockitoJUnitRunner.class) -public class LocationEngineResultTest { - - private static final double TEST_LAT_LNG = 1.0; - - @Test - public void checkNullIntent() { - LocationEngineResult result = LocationEngineResult.extractResult(null); - assertThat(result).isNull(); - } - - @Test - public void passInvalidIntent() { - Intent intent = mock(Intent.class); - LocationEngineResult result = LocationEngineResult.extractResult(intent); - assertThat(result).isNull(); - } - - @Test - public void passValidIntent() { - Location location = mock(Location.class); - LocationEngineResult result = LocationEngineResult.extractResult(getValidIntent(location)); - assertThat(result).isNotNull(); - assertThat(result.getLastLocation()).isSameAs(location); - } - - @Test - public void passNullLocation() { - Location location = null; - LocationEngineResult result = LocationEngineResult.create(location); - assertForNullInput(result); - } - - @Test - public void passNullLocationList() { - List locations = null; - LocationEngineResult result = LocationEngineResult.create(locations); - assertForNullInput(result); - } - - @Test - public void passValidLocation() { - Location location = getValidLocation(); - LocationEngineResult result = LocationEngineResult.create(location); - assertForValidInput(result); - } - - @Test - public void passValidLocationList() { - List locations = Collections.singletonList(getValidLocation()); - LocationEngineResult result = LocationEngineResult.create(locations); - assertForValidInput(result); - } - - @Test - public void passMutableLocationListWithNulls() { - List locations = getLocationsWithNulls(); - LocationEngineResult result = LocationEngineResult.create(locations); - assertForValidInput(result); - } - - @Test - public void passImmutableLocationListWithNulls() { - List locations = Collections.unmodifiableList(getLocationsWithNulls()); - LocationEngineResult result = LocationEngineResult.create(locations); - assertForValidInput(result); - } - - @Test - public void passImmutableListWithNullLocation() { - List locations = Collections.singletonList(null); - LocationEngineResult result = LocationEngineResult.create(locations); - assertThat(result != null); - assertThat(result.getLocations().size() == 0); - } - - private static List getLocationsWithNulls() { - return new ArrayList() { - { - add(null); - add(getValidLocation()); - add(null); - } - }; - } - - private static Location getValidLocation() { - Location location = mock(Location.class); - when(location.getLatitude()).thenReturn(TEST_LAT_LNG); - when(location.getLongitude()).thenReturn(TEST_LAT_LNG); - return location; - } - - private static void assertForNullInput(LocationEngineResult result) { - assertThat(result).isNotNull(); - assertThat(result.getLocations()).isEmpty(); - } - - private static void assertForValidInput(LocationEngineResult result) { - assertThat(result.getLocations()).isNotNull(); - assertThat(result.getLocations().size()).isEqualTo(1); - assertThat(result.getLocations().get(0).getLatitude()).isEqualTo(TEST_LAT_LNG); - assertThat(result.getLocations().get(0).getLongitude()).isEqualTo(TEST_LAT_LNG); - } - - private static Intent getValidIntent(Location location) { - Intent intent = mock(Intent.class); - when(intent.hasExtra(LocationManager.KEY_LOCATION_CHANGED)).thenReturn(true); - Bundle bundle = mock(Bundle.class); - when(bundle.getParcelable(LocationManager.KEY_LOCATION_CHANGED)).thenReturn(location); - when(intent.getExtras()).thenReturn(bundle); - return intent; - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/location/engine/LocationEngineResultTest.kt b/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/location/engine/LocationEngineResultTest.kt new file mode 100644 index 00000000000..ec62e7460b9 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/location/engine/LocationEngineResultTest.kt @@ -0,0 +1,127 @@ +package org.maplibre.android.location.engine + +import android.content.Intent +import android.location.Location +import android.location.LocationManager +import android.os.Bundle +import org.assertj.core.api.Assertions.* +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.* +import org.mockito.junit.MockitoJUnitRunner +import java.util.Collections + +@RunWith(MockitoJUnitRunner::class) +class LocationEngineResultTest { + @Test + fun checkNullIntent() { + val result = LocationEngineResult.extractResult(null) + assertThat(result).isNull() + } + + @Test + fun passInvalidIntent() { + val intent = mock(Intent::class.java) + val result = LocationEngineResult.extractResult(intent) + assertThat(result).isNull() + } + + @Test + fun passValidIntent() { + val location = mock(Location::class.java) + val result = LocationEngineResult.extractResult(getValidIntent(location)) + assertThat(result).isNotNull() + assertThat(result!!.lastLocation).isSameAs(location) + } + + @Test + fun passNullLocation() { + val location: Location? = null + val result = LocationEngineResult.create(location) + assertForNullInput(result) + } + + @Test + fun passNullLocationList() { + val locations: List? = null + val result = LocationEngineResult.create(locations) + assertForNullInput(result) + } + + @Test + fun passValidLocation() { + val location = validLocation + val result = LocationEngineResult.create(location) + assertForValidInput(result) + } + + @Test + fun passValidLocationList() { + val locations = listOf(validLocation) + val result = LocationEngineResult.create(locations) + assertForValidInput(result) + } + + @Test + fun passMutableLocationListWithNulls() { + val locations = locationsWithNulls + val result = LocationEngineResult.create(locations) + assertForValidInput(result) + } + + @Test + fun passImmutableLocationListWithNulls() { + val locations = Collections.unmodifiableList(locationsWithNulls) + val result = LocationEngineResult.create(locations) + assertForValidInput(result) + } + + @Test + fun passImmutableListWithNullLocation() { + val locations = listOf(null) + val result = LocationEngineResult.create(locations) + assertThat(result != null) + assertThat(result.locations.size == 0) + } + + companion object { + private const val TEST_LAT_LNG = 1.0 + // J2K: IDE suggestion to use ArrayList + private val locationsWithNulls: ArrayList + private get() = object : ArrayList() { + init { + add(null) + add(validLocation) + add(null) + } + } + private val validLocation: Location + private get() { + val location = mock(Location::class.java) + `when`(location.latitude).thenReturn(TEST_LAT_LNG) + `when`(location.longitude).thenReturn(TEST_LAT_LNG) + return location + } + + private fun assertForNullInput(result: LocationEngineResult) { + assertThat(result).isNotNull() + assertThat(result.locations).isEmpty() + } + + private fun assertForValidInput(result: LocationEngineResult) { + assertThat(result.locations).isNotNull() + assertThat(result.locations.size).isEqualTo(1) + assertThat(result.locations[0].latitude).isEqualTo(TEST_LAT_LNG) + assertThat(result.locations[0].longitude).isEqualTo(TEST_LAT_LNG) + } + + private fun getValidIntent(location: Location): Intent { + val intent = mock(Intent::class.java) + `when`(intent.hasExtra(LocationManager.KEY_LOCATION_CHANGED)).thenReturn(true) + val bundle = mock(Bundle::class.java) + `when`(bundle.getParcelable(LocationManager.KEY_LOCATION_CHANGED)).thenReturn(location) + `when`(intent.extras).thenReturn(bundle) + return intent + } + } +} diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/location/engine/LocationEngineTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/location/engine/LocationEngineTest.java deleted file mode 100644 index d2f9ba7d34b..00000000000 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/location/engine/LocationEngineTest.java +++ /dev/null @@ -1,143 +0,0 @@ -package org.maplibre.android.location.engine; - -import android.location.Location; -import android.location.LocationListener; -import android.os.Looper; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.junit.MockitoJUnitRunner; -import org.mockito.stubbing.Answer; -import org.mockito.stubbing.Stubber; - -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.atomic.AtomicReference; - -import static java.util.concurrent.TimeUnit.SECONDS; -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.when; - -@RunWith(MockitoJUnitRunner.class) -public class LocationEngineTest { - private static final double LATITUDE = 37.7749; - private static final double LONGITUDE = 122.4194; - private static final long INTERVAL = 1000L; - - @Mock - private LocationEngineImpl locationEngineImpl; - - private LocationEngine engine; - - @Before - public void setUp() { - engine = new LocationEngineProxy<>(locationEngineImpl); - } - - @Test - public void getLastLocation() throws InterruptedException { - final CountDownLatch latch = new CountDownLatch(1); - final AtomicReference resultRef = new AtomicReference<>(); - - LocationEngineCallback callback = getCallback(resultRef, latch); - final Location location = getMockLocation(LATITUDE, LONGITUDE); - final LocationEngineResult expectedResult = getMockEngineResult(location); - - setupDoAnswer(expectedResult).when(locationEngineImpl).getLastLocation(callback); - - engine.getLastLocation(callback); - assertTrue(latch.await(5, SECONDS)); - - LocationEngineResult result = resultRef.get(); - assertThat(result).isSameAs(expectedResult); - assertThat(result.getLastLocation()).isEqualTo(expectedResult.getLastLocation()); - } - - @Test(expected = NullPointerException.class) - public void getLastLocationNullCallback() { - engine.getLastLocation(null); - } - - @Test - public void requestLocationUpdates() throws InterruptedException { - final CountDownLatch latch = new CountDownLatch(1); - final AtomicReference resultRef = new AtomicReference<>(); - - LocationEngineCallback callback = getCallback(resultRef, latch); - final Location location = getMockLocation(LATITUDE, LONGITUDE); - final LocationEngineResult expectedResult = getMockEngineResult(location); - Looper looper = mock(Looper.class); - - AndroidLocationEngineImpl.AndroidLocationEngineCallbackTransport transport = - new AndroidLocationEngineImpl.AndroidLocationEngineCallbackTransport(callback); - when(locationEngineImpl.createListener(callback)).thenReturn(transport); - - engine.requestLocationUpdates(getRequest(INTERVAL), callback, looper); - transport.onLocationChanged(location); - assertTrue(latch.await(5, SECONDS)); - - LocationEngineResult result = resultRef.get(); - assertThat(result.getLastLocation()).isEqualTo(expectedResult.getLastLocation()); - } - - @Test(expected = NullPointerException.class) - public void requestLocationUpdatesNullCallback() { - engine.requestLocationUpdates(null, null, null); - } - - @After - public void tearDown() { - reset(locationEngineImpl); - engine = null; - } - - private static Stubber setupDoAnswer(final LocationEngineResult expectedResult) { - return doAnswer(new Answer() { - @Override - public Object answer(InvocationOnMock invocation) { - LocationEngineCallback callback = invocation.getArgument(0); - callback.onSuccess(expectedResult); - return null; - } - }); - } - - private static LocationEngineRequest getRequest(long interval) { - return new LocationEngineRequest.Builder(interval).build(); - } - - private static LocationEngineCallback getCallback( - final AtomicReference resultRef, - final CountDownLatch latch) { - return new LocationEngineCallback() { - @Override - public void onSuccess(LocationEngineResult result) { - resultRef.set(result); - latch.countDown(); - } - - @Override - public void onFailure(Exception exception) { - exception.printStackTrace(); - } - }; - } - - private static LocationEngineResult getMockEngineResult(Location location) { - return LocationEngineResult.create(location); - } - - private static Location getMockLocation(double lat, double lon) { - Location location = mock(Location.class); - location.setLatitude(lat); - location.setLongitude(lon); - return location; - } -} \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/location/engine/LocationEngineTest.kt b/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/location/engine/LocationEngineTest.kt new file mode 100644 index 00000000000..f66e2f7168e --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/location/engine/LocationEngineTest.kt @@ -0,0 +1,127 @@ +package org.maplibre.android.location.engine + +import android.location.Location +import android.location.LocationListener +import android.os.Looper +import org.assertj.core.api.Assertions.* +import org.junit.After +import org.junit.Assert.* +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.maplibre.android.location.engine.AndroidLocationEngineImpl.AndroidLocationEngineCallbackTransport +import org.mockito.Mock +import org.mockito.Mockito.* +import org.mockito.junit.MockitoJUnitRunner +import org.mockito.stubbing.Stubber +import java.util.concurrent.CountDownLatch +import java.util.concurrent.TimeUnit +import java.util.concurrent.atomic.AtomicReference + +@RunWith(MockitoJUnitRunner::class) +class LocationEngineTest { + @Mock + private val locationEngineImpl: LocationEngineImpl? = null + private var engine: LocationEngine? = null + @Before + fun setUp() { + engine = LocationEngineProxy(locationEngineImpl) + } + + //@get:Throws(InterruptedException::class) + @Test + fun getLastLocation () + { + val latch = CountDownLatch(1) + val resultRef = AtomicReference() + val callback = getCallback(resultRef, latch) + val location = getMockLocation(LATITUDE, LONGITUDE) + val expectedResult = getMockEngineResult(location) + setupDoAnswer(expectedResult).`when`(locationEngineImpl)?.getLastLocation(callback) + engine!!.getLastLocation(callback) + assertTrue(latch.await(5, TimeUnit.SECONDS)) + val result = resultRef.get() + assertThat(result).isSameAs(expectedResult) + assertThat(result.lastLocation).isEqualTo(expectedResult.lastLocation) + } + + // J2K: // TODO: this becomes unreachable, validate necessity of test + @Test(expected = NullPointerException::class) + fun getLastLocationNullCallback () { + engine!!.getLastLocation(null!!) + } + + @Test + @Throws(InterruptedException::class) + fun requestLocationUpdates() { + val latch = CountDownLatch(1) + val resultRef = AtomicReference() + val callback = getCallback(resultRef, latch) + val location = getMockLocation(LATITUDE, LONGITUDE) + val expectedResult = getMockEngineResult(location) + val looper = mock(Looper::class.java) + val transport = AndroidLocationEngineCallbackTransport(callback) + `when`(locationEngineImpl!!.createListener(callback)).thenReturn(transport) + engine!!.requestLocationUpdates(getRequest(INTERVAL), callback, looper) + transport.onLocationChanged(location) + assertTrue(latch.await(5, TimeUnit.SECONDS)) + val result = resultRef.get() + assertThat(result.lastLocation).isEqualTo(expectedResult.lastLocation) + } + + // J2K: // TODO: this becomes unreachable, validate necessity of test + @Test(expected = NullPointerException::class) + fun requestLocationUpdatesNullCallback() { + engine!!.requestLocationUpdates(null!!, null!!, null!!) + } + + @After + fun tearDown() { + reset(locationEngineImpl) + engine = null + } + + companion object { + private const val LATITUDE = 37.7749 + private const val LONGITUDE = 122.4194 + private const val INTERVAL = 1000L + private fun setupDoAnswer(expectedResult: LocationEngineResult): Stubber { + return doAnswer { invocation -> + val callback = invocation.getArgument>(0) + callback.onSuccess(expectedResult) + null + } + } + + private fun getRequest(interval: Long): LocationEngineRequest { + return LocationEngineRequest.Builder(interval).build() + } + + private fun getCallback( + resultRef: AtomicReference, + latch: CountDownLatch): LocationEngineCallback { + // J2K: remove ? + return object : LocationEngineCallback { + override fun onSuccess(result: LocationEngineResult) { + resultRef.set(result) + latch.countDown() + } + + override fun onFailure(exception: Exception) { + exception.printStackTrace() + } + } + } + + private fun getMockEngineResult(location: Location): LocationEngineResult { + return LocationEngineResult.create(location) + } + + private fun getMockLocation(lat: Double, lon: Double): Location { + val location = mock(Location::class.java) + location.latitude = lat + location.longitude = lon + return location + } + } +} \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/location/engine/MapLibreFusedLocationEngineImplAdditionalTest2.java b/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/location/engine/MapLibreFusedLocationEngineImplAdditionalTest2.java deleted file mode 100644 index 64970a7e4ae..00000000000 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/location/engine/MapLibreFusedLocationEngineImplAdditionalTest2.java +++ /dev/null @@ -1,153 +0,0 @@ -package org.maplibre.android.location.engine; - -import android.content.Context; -import android.location.Criteria; -import android.location.Location; -import android.location.LocationListener; -import android.location.LocationManager; -import android.location.LocationProvider; -import android.os.Looper; - -import androidx.annotation.NonNull; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.junit.MockitoJUnitRunner; -import org.mockito.stubbing.Answer; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.anyFloat; -import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -@RunWith(MockitoJUnitRunner.class) -public class MapLibreFusedLocationEngineImplAdditionalTest2 { - private static final long INTERVAL = 1000L; - private static final String PROVIDER = "test_provider"; - private ArrayList engines = new ArrayList<>(); - private LocationManager mockLocationManager; - private Location location = new Location(PROVIDER); - - @Before - public void setUp() { - location = mock(Location.class); - when(location.getLatitude()).thenReturn(1.0); - when(location.getLongitude()).thenReturn(2.0); - Context mockContext = mock(Context.class); - mockLocationManager = mock(LocationManager.class); - when(mockContext.getSystemService(anyString())).thenReturn(mockLocationManager); - List providers = new ArrayList<>(); - providers.add(PROVIDER); - when(mockLocationManager.getAllProviders()).thenReturn(providers); - when(mockLocationManager.getBestProvider(any(Criteria.class), anyBoolean())) - .thenReturn(LocationManager.GPS_PROVIDER); - doAnswer(new Answer() { - @Override - public Object answer(InvocationOnMock invocation) { - LocationListener listener = (LocationListener) invocation.getArguments()[3]; - listener.onProviderEnabled(PROVIDER); - listener.onStatusChanged(PROVIDER, LocationProvider.AVAILABLE, null); - listener.onLocationChanged(location); - listener.onProviderDisabled(PROVIDER); - return null; - } - }).when(mockLocationManager) - .requestLocationUpdates(anyString(), anyLong(), anyFloat(), any(LocationListener.class), any(Looper.class)); - engines.add(new LocationEngineProxy<>(new MapLibreFusedLocationEngineImpl(mockContext))); - engines.add(new LocationEngineProxy<>(new AndroidLocationEngineImpl(mockContext))); - } - - @Test - public void checkGetLastLocation() throws InterruptedException { - final CountDownLatch latch = new CountDownLatch(engines.size()); - - for (LocationEngineProxy engineProxy : engines) { - engineProxy.getLastLocation(new LocationEngineCallback() { - @Override - public void onSuccess(LocationEngineResult result) { - } - - @Override - public void onFailure(@NonNull Exception exception) { - assertEquals("Last location unavailable", exception.getLocalizedMessage()); - latch.countDown(); - } - }); - } - assertTrue(latch.await(1, TimeUnit.SECONDS)); - - when(mockLocationManager.getLastKnownLocation(anyString())).thenReturn(location); - final CountDownLatch latch1 = new CountDownLatch(engines.size()); - - for (LocationEngineProxy engineProxy : engines) { - engineProxy.getLastLocation(new LocationEngineCallback() { - @Override - public void onSuccess(LocationEngineResult result) { - List list = result.getLocations(); - assertEquals(1, list.size()); - assertEquals(1.0, list.get(0).getLatitude(), 0); - assertEquals(2.0, list.get(0).getLongitude(), 0); - latch1.countDown(); - } - - @Override - public void onFailure(@NonNull Exception exception) { - - } - }); - - } - assertTrue(latch1.await(1, TimeUnit.SECONDS)); - - } - - @Test - public void checkRequestAndRemoveLocationUpdates() throws InterruptedException { - final CountDownLatch latch = new CountDownLatch(engines.size()); - - LocationEngineCallback engineCallback = new LocationEngineCallback() { - @Override - public void onSuccess(LocationEngineResult result) { - List list = result.getLocations(); - assertEquals(1, list.size()); - assertEquals(1.0, list.get(0).getLatitude(), 0); - assertEquals(2.0, list.get(0).getLongitude(), 0); - latch.countDown(); - } - - @Override - public void onFailure(@NonNull Exception exception) { - - } - }; - - - for (LocationEngineProxy engineProxy : engines) { - engineProxy.requestLocationUpdates(getRequest(INTERVAL, LocationEngineRequest.PRIORITY_HIGH_ACCURACY), - engineCallback, mock(Looper.class)); - - assertTrue(latch.await(1, TimeUnit.SECONDS)); - - assertNotNull(engineProxy.removeListener(engineCallback)); - } - - } - - private static LocationEngineRequest getRequest(long interval, int priority) { - return new LocationEngineRequest.Builder(interval).setPriority(priority).build(); - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/location/engine/MapLibreFusedLocationEngineImplAdditionalTest2.kt b/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/location/engine/MapLibreFusedLocationEngineImplAdditionalTest2.kt new file mode 100644 index 00000000000..84a77514f2d --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/location/engine/MapLibreFusedLocationEngineImplAdditionalTest2.kt @@ -0,0 +1,118 @@ +package org.maplibre.android.location.engine + +import android.content.Context +import android.location.Criteria +import android.location.Location +import android.location.LocationListener +import android.location.LocationManager +import android.location.LocationProvider +import android.os.Looper +import org.junit.Assert.* +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers +import org.mockito.Mockito.* +import org.mockito.junit.MockitoJUnitRunner +import java.util.concurrent.CountDownLatch +import java.util.concurrent.TimeUnit + +@RunWith(MockitoJUnitRunner::class) +class MapLibreFusedLocationEngineImplAdditionalTest2 { + private val engines = ArrayList>() + private var mockLocationManager: LocationManager? = null + private var location = Location(PROVIDER) + @Before + fun setUp() { + location = mock(Location::class.java) + `when`(location.latitude).thenReturn(1.0) + `when`(location.longitude).thenReturn(2.0) + val mockContext = mock(Context::class.java) + mockLocationManager = mock(LocationManager::class.java) + `when`(mockContext.getSystemService(anyString())).thenReturn(mockLocationManager) + val providers: MutableList = ArrayList() + providers.add(PROVIDER) + // J2K: add ? + `when`(mockLocationManager?.getAllProviders()).thenReturn(providers) + `when`(mockLocationManager?.getBestProvider(any(Criteria::class.java), anyBoolean())) + .thenReturn(LocationManager.GPS_PROVIDER) + doAnswer { invocation -> + val listener = invocation.arguments[3] as LocationListener + listener.onProviderEnabled(PROVIDER) + listener.onStatusChanged(PROVIDER, LocationProvider.AVAILABLE, null) + listener.onLocationChanged(location) + listener.onProviderDisabled(PROVIDER) + null + }.`when`(mockLocationManager) + // J2K: add ? + ?.requestLocationUpdates(ArgumentMatchers.anyString(), ArgumentMatchers.anyLong(), ArgumentMatchers.anyFloat(), ArgumentMatchers.any(LocationListener::class.java), ArgumentMatchers.any(Looper::class.java)) + engines.add(LocationEngineProxy(MapLibreFusedLocationEngineImpl(mockContext))) + engines.add(LocationEngineProxy(AndroidLocationEngineImpl(mockContext))) + } + + @Test + @Throws(InterruptedException::class) + fun checkGetLastLocation() { + val latch = CountDownLatch(engines.size) + for (engineProxy in engines) { + // J2K: remove ? from LocationEngineResult + engineProxy.getLastLocation(object : LocationEngineCallback { + override fun onSuccess(result: LocationEngineResult) {} + override fun onFailure(exception: Exception) { + assertEquals("Last location unavailable", exception.localizedMessage) + latch.countDown() + } + }) + } + assertTrue(latch.await(1, TimeUnit.SECONDS)) + `when`(mockLocationManager!!.getLastKnownLocation(anyString())).thenReturn(location) + val latch1 = CountDownLatch(engines.size) + for (engineProxy in engines) { + // J2K: remove ? from LocationEngineResult + engineProxy.getLastLocation(object : LocationEngineCallback { + override fun onSuccess(result: LocationEngineResult) { + val list = result.locations + assertEquals(1, list.size.toLong()) + assertEquals(1.0, list[0].latitude, 0.0) + assertEquals(2.0, list[0].longitude, 0.0) + latch1.countDown() + } + + override fun onFailure(exception: Exception) {} + }) + } + assertTrue(latch1.await(1, TimeUnit.SECONDS)) + } + + @Test + @Throws(InterruptedException::class) + fun checkRequestAndRemoveLocationUpdates() { + val latch = CountDownLatch(engines.size) + // J2K: remove ? from LocationEngineResult + val engineCallback: LocationEngineCallback = object : LocationEngineCallback { + override fun onSuccess(result: LocationEngineResult) { + val list = result.locations + assertEquals(1, list.size.toLong()) + assertEquals(1.0, list[0].latitude, 0.0) + assertEquals(2.0, list[0].longitude, 0.0) + latch.countDown() + } + + override fun onFailure(exception: Exception) {} + } + for (engineProxy in engines) { + engineProxy.requestLocationUpdates(getRequest(INTERVAL, LocationEngineRequest.PRIORITY_HIGH_ACCURACY), + engineCallback, mock(Looper::class.java)) + assertTrue(latch.await(1, TimeUnit.SECONDS)) + assertNotNull(engineProxy.removeListener(engineCallback)) + } + } + + companion object { + private const val INTERVAL = 1000L + private const val PROVIDER = "test_provider" + private fun getRequest(interval: Long, priority: Int): LocationEngineRequest { + return LocationEngineRequest.Builder(interval).setPriority(priority).build() + } + } +} diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/location/engine/MapLibreFusedLocationEngineImplTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/location/engine/MapLibreFusedLocationEngineImplTest.java deleted file mode 100644 index a526ccdbbdf..00000000000 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/location/engine/MapLibreFusedLocationEngineImplTest.java +++ /dev/null @@ -1,177 +0,0 @@ -package org.maplibre.android.location.engine; - -import android.app.PendingIntent; -import android.content.Context; -import android.location.Criteria; -import android.location.Location; -import android.location.LocationListener; -import android.location.LocationManager; -import android.os.Looper; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; - -import java.util.Arrays; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.atomic.AtomicReference; - -import static java.util.concurrent.TimeUnit.SECONDS; -import static junit.framework.TestCase.assertTrue; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyBoolean; -import static org.mockito.Mockito.anyFloat; -import static org.mockito.Mockito.anyLong; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -@RunWith(MockitoJUnitRunner.class) -public class MapLibreFusedLocationEngineImplTest { - private static final double LATITUDE = 37.7749; - private static final double LONGITUDE = 122.4194; - - @Mock - private LocationManager locationManagerMock; - - private LocationEngine engine; - private MapLibreFusedLocationEngineImpl maplibreFusedLocationEngineImpl; - - @Before - public void setUp() { - Context context = mock(Context.class); - when(context.getSystemService(Context.LOCATION_SERVICE)).thenReturn(locationManagerMock); - maplibreFusedLocationEngineImpl = new MapLibreFusedLocationEngineImpl(context); - engine = new LocationEngineProxy<>(maplibreFusedLocationEngineImpl); - } - - @Test - public void getLastLocation() throws InterruptedException { - final CountDownLatch latch = new CountDownLatch(1); - final AtomicReference resultRef = new AtomicReference<>(); - - LocationEngineCallback callback = getCallback(resultRef, latch); - final Location location = getMockLocation(LATITUDE, LONGITUDE); - final LocationEngineResult expectedResult = getMockEngineResult(location); - - when(locationManagerMock.getAllProviders()).thenReturn(Arrays.asList("gps", "network")); - when(locationManagerMock.getLastKnownLocation(anyString())).thenReturn(location); - - engine.getLastLocation(callback); - assertTrue(latch.await(5, SECONDS)); - - LocationEngineResult result = resultRef.get(); - assertThat(result.getLastLocation()).isEqualTo(expectedResult.getLastLocation()); - } - - @Test - public void createListener() { - LocationEngineCallback callback = mock(LocationEngineCallback.class); - LocationListener locationListener = maplibreFusedLocationEngineImpl.createListener(callback); - Location mockLocation = getMockLocation(LATITUDE, LONGITUDE); - locationListener.onLocationChanged(mockLocation); - ArgumentCaptor argument = ArgumentCaptor.forClass(LocationEngineResult.class); - verify(callback).onSuccess(argument.capture()); - - LocationEngineResult result = argument.getValue(); - assertThat(result.getLastLocation()).isSameAs(mockLocation); - } - - @Test - public void requestLocationUpdatesOutdoors() { - LocationEngineRequest request = new LocationEngineRequest.Builder(10) - .setPriority(LocationEngineRequest.PRIORITY_HIGH_ACCURACY).build(); - LocationEngineCallback callback = mock(LocationEngineCallback.class); - Looper looper = mock(Looper.class); - when(locationManagerMock.getBestProvider(any(Criteria.class), anyBoolean())).thenReturn("gps"); - engine.requestLocationUpdates(request, callback, looper); - verify(locationManagerMock, times(2)).requestLocationUpdates(anyString(), - anyLong(), anyFloat(), any(LocationListener.class), any(Looper.class)); - } - - @Test - public void requestLocationUpdatesIndoors() { - LocationEngineRequest request = new LocationEngineRequest.Builder(10) - .setPriority(LocationEngineRequest.PRIORITY_HIGH_ACCURACY).build(); - LocationEngineCallback callback = mock(LocationEngineCallback.class); - Looper looper = mock(Looper.class); - when(locationManagerMock.getBestProvider(any(Criteria.class), anyBoolean())).thenReturn("network"); - engine.requestLocationUpdates(request, callback, looper); - verify(locationManagerMock, times(1)).requestLocationUpdates(anyString(), - anyLong(), anyFloat(), any(LocationListener.class), any(Looper.class)); - } - - @Test - public void requestLocationUpdatesOutdoorsWithPendingIntent() { - LocationEngineRequest request = new LocationEngineRequest.Builder(10) - .setPriority(LocationEngineRequest.PRIORITY_HIGH_ACCURACY).build(); - PendingIntent pendingIntent = mock(PendingIntent.class); - when(locationManagerMock.getBestProvider(any(Criteria.class), anyBoolean())).thenReturn("gps"); - engine.requestLocationUpdates(request, pendingIntent); - verify(locationManagerMock, times(2)).requestLocationUpdates(anyString(), - anyLong(), anyFloat(), any(PendingIntent.class)); - } - - @Test - public void requestLocationUpdatesIndoorsWithPendingIntent() { - LocationEngineRequest request = new LocationEngineRequest.Builder(10) - .setPriority(LocationEngineRequest.PRIORITY_HIGH_ACCURACY).build(); - PendingIntent pendingIntent = mock(PendingIntent.class); - when(locationManagerMock.getBestProvider(any(Criteria.class), anyBoolean())).thenReturn("network"); - engine.requestLocationUpdates(request, pendingIntent); - verify(locationManagerMock, times(1)).requestLocationUpdates(anyString(), - anyLong(), anyFloat(), any(PendingIntent.class)); - } - - @Test(expected = NullPointerException.class) - public void getLastLocationNullCallback() { - engine.getLastLocation(null); - } - - @Test(expected = NullPointerException.class) - public void requestLocationUpdatesNullCallback() { - engine.requestLocationUpdates(null, null, null); - } - - @After - public void tearDown() { - reset(locationManagerMock); - engine = null; - } - - private static LocationEngineCallback getCallback( - final AtomicReference resultRef, - final CountDownLatch latch) { - return new LocationEngineCallback() { - @Override - public void onSuccess(LocationEngineResult result) { - resultRef.set(result); - latch.countDown(); - } - - @Override - public void onFailure(Exception exception) { - exception.printStackTrace(); - } - }; - } - - private static LocationEngineResult getMockEngineResult(Location location) { - return LocationEngineResult.create(location); - } - - private static Location getMockLocation(double lat, double lon) { - Location location = mock(Location.class); - location.setLatitude(lat); - location.setLongitude(lon); - return location; - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/location/engine/MapLibreFusedLocationEngineImplTest.kt b/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/location/engine/MapLibreFusedLocationEngineImplTest.kt new file mode 100644 index 00000000000..f7383e5f98f --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/location/engine/MapLibreFusedLocationEngineImplTest.kt @@ -0,0 +1,166 @@ +package org.maplibre.android.location.engine + +import android.app.PendingIntent +import android.content.Context +import android.location.Criteria +import android.location.Location +import android.location.LocationListener +import android.location.LocationManager +import android.os.Looper +import junit.framework.TestCase.* +import org.assertj.core.api.Assertions.* +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.ArgumentMatchers.* +import org.mockito.Mock +import org.mockito.Mockito.* +import org.mockito.junit.MockitoJUnitRunner +import java.util.concurrent.CountDownLatch +import java.util.concurrent.TimeUnit +import java.util.concurrent.atomic.AtomicReference + +@RunWith(MockitoJUnitRunner::class) +class MapLibreFusedLocationEngineImplTest { + @Mock + private val locationManagerMock: LocationManager? = null + private var engine: LocationEngine? = null + private var maplibreFusedLocationEngineImpl: MapLibreFusedLocationEngineImpl? = null + @Before + fun setUp() { + val context = mock(Context::class.java) + `when`(context.getSystemService(Context.LOCATION_SERVICE)).thenReturn(locationManagerMock) + maplibreFusedLocationEngineImpl = MapLibreFusedLocationEngineImpl(context) + engine = LocationEngineProxy(maplibreFusedLocationEngineImpl) + } + + // J2K: rewrite test to not use "@get" + @Test + fun getLastLocation() { + val latch = CountDownLatch(1) + val resultRef = AtomicReference() + val callback = getCallback(resultRef, latch) + val location = getMockLocation(LATITUDE, LONGITUDE) + val expectedResult = getMockEngineResult(location) + `when`(locationManagerMock!!.allProviders).thenReturn(mutableListOf("gps", "network")) + `when`(locationManagerMock.getLastKnownLocation(anyString())).thenReturn(location) + engine!!.getLastLocation(callback) + assertTrue(latch.await(5, TimeUnit.SECONDS)) + val result = resultRef.get() + assertThat(result.lastLocation).isEqualTo(expectedResult.lastLocation) + } + + @Test + fun createListener() { + // J2K: IDE suggestion "as LocationEngineCallback" + val callback: LocationEngineCallback = mock(LocationEngineCallback::class.java) as LocationEngineCallback + val locationListener = maplibreFusedLocationEngineImpl!!.createListener(callback) + val mockLocation = getMockLocation(LATITUDE, LONGITUDE) + locationListener.onLocationChanged(mockLocation) + val argument = ArgumentCaptor.forClass(LocationEngineResult::class.java) + verify(callback).onSuccess(argument.capture()) + val result = argument.value + assertThat(result.lastLocation).isSameAs(mockLocation) + } + + @Test + fun requestLocationUpdatesOutdoors() { + val request = LocationEngineRequest.Builder(10) + .setPriority(LocationEngineRequest.PRIORITY_HIGH_ACCURACY).build() + // J2K: IDE suggestion "as LocationEngineCallback" + val callback: LocationEngineCallback = mock(LocationEngineCallback::class.java) as LocationEngineCallback + val looper = mock(Looper::class.java) + `when`(locationManagerMock!!.getBestProvider(any(Criteria::class.java), anyBoolean())).thenReturn("gps") + engine!!.requestLocationUpdates(request, callback, looper) + verify(locationManagerMock, times(2)).requestLocationUpdates(anyString(), + anyLong(), anyFloat(), any(LocationListener::class.java), any(Looper::class.java)) + } + + @Test + fun requestLocationUpdatesIndoors() { + val request = LocationEngineRequest.Builder(10) + .setPriority(LocationEngineRequest.PRIORITY_HIGH_ACCURACY).build() + // J2K: IDE suggestion "as LocationEngineCallback" + val callback: LocationEngineCallback = mock(LocationEngineCallback::class.java) as LocationEngineCallback + val looper = mock(Looper::class.java) + `when`(locationManagerMock!!.getBestProvider(any(Criteria::class.java), anyBoolean())).thenReturn("network") + engine!!.requestLocationUpdates(request, callback, looper) + verify(locationManagerMock, times(1)).requestLocationUpdates(anyString(), + anyLong(), anyFloat(), any(LocationListener::class.java), any(Looper::class.java)) + } + + @Test + fun requestLocationUpdatesOutdoorsWithPendingIntent() { + val request = LocationEngineRequest.Builder(10) + .setPriority(LocationEngineRequest.PRIORITY_HIGH_ACCURACY).build() + val pendingIntent = mock(PendingIntent::class.java) + `when`(locationManagerMock!!.getBestProvider(any(Criteria::class.java), anyBoolean())).thenReturn("gps") + engine!!.requestLocationUpdates(request, pendingIntent) + verify(locationManagerMock, times(2)).requestLocationUpdates(anyString(), + anyLong(), anyFloat(), any(PendingIntent::class.java)) + } + + @Test + fun requestLocationUpdatesIndoorsWithPendingIntent() { + val request = LocationEngineRequest.Builder(10) + .setPriority(LocationEngineRequest.PRIORITY_HIGH_ACCURACY).build() + val pendingIntent = mock(PendingIntent::class.java) + `when`(locationManagerMock!!.getBestProvider(any(Criteria::class.java), anyBoolean())).thenReturn("network") + engine!!.requestLocationUpdates(request, pendingIntent) + verify(locationManagerMock, times(1)).requestLocationUpdates(anyString(), + anyLong(), anyFloat(), any(PendingIntent::class.java)) + } + +// J2k: // TODO: see https://github.com/maplibre/maplibre-native/issues/1949 +/* + // J2K: rewrite test to not use "@get", rename to "getLastLocationNull" + @Test(expected = NullPointerException::class) + fun getLastLocationNull() { + engine!!.getLastLocation(null!!) + } + + // J2K: changed "null" to "null!!" + @Test(expected = NullPointerException::class) + fun requestLocationUpdatesNullCallback() { + engine!!.requestLocationUpdates(null!!, null!!, null!!) + } +*/ + @After + fun tearDown() { + reset(locationManagerMock) + engine = null + } + + companion object { + private const val LATITUDE = 37.7749 + private const val LONGITUDE = 122.4194 + private fun getCallback( + resultRef: AtomicReference, + latch: CountDownLatch): LocationEngineCallback { + // J2K: remove '?' from LocationEngineResult? + return object : LocationEngineCallback { + override fun onSuccess(result: LocationEngineResult) { + resultRef.set(result) + latch.countDown() + } + + override fun onFailure(exception: Exception) { + exception.printStackTrace() + } + } + } + + private fun getMockEngineResult(location: Location): LocationEngineResult { + return LocationEngineResult.create(location) + } + + private fun getMockLocation(lat: Double, lon: Double): Location { + val location = mock(Location::class.java) + location.latitude = lat + location.longitude = lon + return location + } + } +} diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/utils/MockParcel.java b/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/utils/MockParcel.java deleted file mode 100644 index 8d1c72199e3..00000000000 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/utils/MockParcel.java +++ /dev/null @@ -1,268 +0,0 @@ -package org.maplibre.android.utils; - -import android.os.Parcel; -import android.os.Parcelable; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; - -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.List; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyByte; -import static org.mockito.ArgumentMatchers.anyDouble; -import static org.mockito.ArgumentMatchers.anyFloat; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class MockParcel { - - @Nullable - public static Parcelable obtain(@NonNull Parcelable object) { - return obtain(object, 0); - } - - @Nullable - public static Parcelable obtain(@NonNull Parcelable object, int describeContentsValue) { - testDescribeContents(object, describeContentsValue); - testParcelableArray(object); - return testParcelable(object); - } - - public static Parcelable testParcelable(@NonNull Parcelable object) { - Parcel parcel = ParcelMocker.obtain(object); - object.writeToParcel(parcel, 0); - parcel.setDataPosition(0); - - try { - Field field = object.getClass().getDeclaredField("CREATOR"); - field.setAccessible(true); - Class creatorClass = field.getType(); - Object fieldValue = field.get(object); - Method myMethod = creatorClass.getDeclaredMethod("createFromParcel", Parcel.class); - return (Parcelable) myMethod.invoke(fieldValue, parcel); - } catch (Exception exception) { - return null; - } - } - - public static void testParcelableArray(@NonNull Parcelable object) { - Parcelable[] objects = new Parcelable[] {object}; - Parcel parcel = ParcelMocker.obtain(objects); - parcel.writeParcelableArray(objects, 0); - parcel.setDataPosition(0); - Parcelable[] parcelableArray = parcel.readParcelableArray(object.getClass().getClassLoader()); - assertArrayEquals("parcel should match initial object", objects, parcelableArray); - } - - public static void testDescribeContents(@NonNull Parcelable object, int describeContentsValue) { - if (describeContentsValue == 0) { - assertEquals("\nExpecting a describeContents() value of 0 for a " + object.getClass().getSimpleName() - + " instance." + "\nYou can provide a different value for describeContentValue through the obtain method.", - 0, - object.describeContents()); - } else { - assertEquals("Expecting a describeContents() value of " + describeContentsValue, - describeContentsValue, - object.describeContents()); - } - } - - private static class ParcelMocker { - - public static Parcel obtain(@NonNull Parcelable target) { - Parcel parcel = new ParcelMocker(target).getMockedParcel(); - target.writeToParcel(parcel, 0); - parcel.setDataPosition(0); - return parcel; - } - - public static Parcel obtain(@NonNull Parcelable[] targets) { - if (targets.length == 0) { - throw new IllegalArgumentException("The passed argument may not be empty"); - } - Parcel parcel = new ParcelMocker(targets[0]).getMockedParcel(); - parcel.writeParcelableArray(targets, 0); - parcel.setDataPosition(0); - return parcel; - } - - private List objects; - private Object object; - private Parcel mockedParcel; - private int position; - - private ParcelMocker(Object o) { - this.object = o; - mockedParcel = mock(Parcel.class); - objects = new ArrayList<>(); - setupMock(); - } - - private Parcel getMockedParcel() { - return mockedParcel; - } - - private void setupMock() { - setupWrites(); - setupReads(); - setupOthers(); - } - - private void setupWrites() { - Answer writeValueAnswer = new Answer() { - @Override - public Void answer(InvocationOnMock invocation) throws Throwable { - Object parameter = invocation.getArguments()[0]; - objects.add(parameter); - return null; - } - }; - Answer writeArrayAnswer = new Answer() { - @Override - public Void answer(InvocationOnMock invocation) throws Throwable { - Object[] parameters = (Object[]) invocation.getArguments()[0]; - objects.add(parameters.length); - for (Object o : parameters) { - objects.add(o); - } - return null; - } - }; - Answer writeIntArrayAnswer = new Answer() { - @Override - public Void answer(InvocationOnMock invocation) throws Throwable { - int[] parameters = (int[]) invocation.getArguments()[0]; - if (parameters != null) { - objects.add(parameters.length); - for (Object o : parameters) { - objects.add(o); - } - } else { - objects.add(-1); - } - return null; - } - }; - doAnswer(writeValueAnswer).when(mockedParcel).writeByte(anyByte()); - doAnswer(writeValueAnswer).when(mockedParcel).writeLong(anyLong()); - doAnswer(writeValueAnswer).when(mockedParcel).writeString(anyString()); - doAnswer(writeValueAnswer).when(mockedParcel).writeInt(anyInt()); - doAnswer(writeIntArrayAnswer).when(mockedParcel).writeIntArray(any(int[].class)); - doAnswer(writeValueAnswer).when(mockedParcel).writeDouble(anyDouble()); - doAnswer(writeValueAnswer).when(mockedParcel).writeFloat(anyFloat()); - doAnswer(writeValueAnswer).when(mockedParcel).writeParcelable(any(Parcelable.class), eq(0)); - doAnswer(writeArrayAnswer).when(mockedParcel).writeParcelableArray(any(Parcelable[].class), eq(0)); - } - - private void setupReads() { - when(mockedParcel.readInt()).then(new Answer() { - @NonNull - @Override - public Integer answer(InvocationOnMock invocation) throws Throwable { - return (Integer) objects.get(position++); - } - }); - when(mockedParcel.readByte()).thenAnswer(new Answer() { - @NonNull - @Override - public Byte answer(InvocationOnMock invocation) throws Throwable { - return (Byte) objects.get(position++); - } - }); - when(mockedParcel.readLong()).thenAnswer(new Answer() { - @NonNull - @Override - public Long answer(InvocationOnMock invocation) throws Throwable { - return (Long) objects.get(position++); - } - }); - when(mockedParcel.readString()).thenAnswer(new Answer() { - @NonNull - @Override - public String answer(InvocationOnMock invocation) throws Throwable { - return (String) objects.get(position++); - } - }); - when(mockedParcel.readDouble()).thenAnswer(new Answer() { - @NonNull - @Override - public Double answer(InvocationOnMock invocation) throws Throwable { - return (Double) objects.get(position++); - } - }); - when(mockedParcel.readFloat()).thenAnswer(new Answer() { - @NonNull - @Override - public Float answer(InvocationOnMock invocation) throws Throwable { - return (Float) objects.get(position++); - } - }); - when(mockedParcel.readParcelable(Parcelable.class.getClassLoader())).thenAnswer(new Answer() { - @NonNull - @Override - public Parcelable answer(InvocationOnMock invocation) throws Throwable { - return (Parcelable) objects.get(position++); - } - }); - when(mockedParcel.readParcelableArray(Parcelable.class.getClassLoader())).thenAnswer(new Answer() { - @NonNull - @Override - public Object[] answer(InvocationOnMock invocation) throws Throwable { - int size = (Integer) objects.get(position++); - Field field = object.getClass().getDeclaredField("CREATOR"); - field.setAccessible(true); - Class creatorClass = field.getType(); - Object fieldValue = field.get(object); - Method myMethod = creatorClass.getDeclaredMethod("newArray", int.class); - Object[] array = (Object[]) myMethod.invoke(fieldValue, size); - for (int i = 0; i < size; i++) { - array[i] = objects.get(position++); - } - return array; - } - }); - when(mockedParcel.createIntArray()).then(new Answer() { - @Nullable - @Override - public int[] answer(InvocationOnMock invocation) throws Throwable { - int size = (Integer) objects.get(position++); - if (size == -1) { - return null; - } - - int[] array = new int[size]; - for (int i = 0; i < size; i++) { - array[i] = (Integer) objects.get(position++); - } - - return array; - } - }); - } - - private void setupOthers() { - doAnswer(new Answer() { - @Nullable - @Override - public Void answer(@NonNull InvocationOnMock invocation) throws Throwable { - position = ((Integer) invocation.getArguments()[0]); - return null; - } - }).when(mockedParcel).setDataPosition(anyInt()); - } - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/utils/MockParcel.kt b/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/utils/MockParcel.kt new file mode 100644 index 00000000000..5be7936333e --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/utils/MockParcel.kt @@ -0,0 +1,169 @@ +package org.maplibre.android.utils + +import android.os.Parcel +import android.os.Parcelable +import org.junit.Assert.* +import org.mockito.ArgumentMatchers +import org.mockito.Mockito.* +import org.mockito.stubbing.Answer + +object MockParcel { + @JvmOverloads + fun obtain(`object`: Parcelable, describeContentsValue: Int = 0): Parcelable? { + testDescribeContents(`object`, describeContentsValue) + testParcelableArray(`object`) + return testParcelable(`object`) + } + + fun testParcelable(`object`: Parcelable): Parcelable? { + val parcel = ParcelMocker.obtain(`object`) + `object`.writeToParcel(parcel, 0) + parcel.setDataPosition(0) + return try { + val field = `object`.javaClass.getDeclaredField("CREATOR") + field.isAccessible = true + val creatorClass = field.type + val fieldValue = field[`object`] + val myMethod = creatorClass.getDeclaredMethod("createFromParcel", Parcel::class.java) + myMethod.invoke(fieldValue, parcel) as Parcelable + } catch (exception: Exception) { + null + } + } + + fun testParcelableArray(`object`: Parcelable) { + val objects = arrayOf(`object`) + val parcel = ParcelMocker.obtain(objects) + parcel.writeParcelableArray(objects, 0) + parcel.setDataPosition(0) + val parcelableArray = parcel.readParcelableArray(`object`.javaClass.classLoader) + assertArrayEquals("parcel should match initial object", objects, parcelableArray) + } + + fun testDescribeContents(`object`: Parcelable, describeContentsValue: Int) { + if (describeContentsValue == 0) { + assertEquals(""" +Expecting a describeContents() value of 0 for a ${`object`.javaClass.simpleName} instance. +You can provide a different value for describeContentValue through the obtain method.""", + 0, + `object`.describeContents().toLong()) + } else { + assertEquals("Expecting a describeContents() value of $describeContentsValue", + describeContentsValue.toLong(), + `object`.describeContents().toLong()) + } + } + + private class ParcelMocker private constructor(private val `object`: Any) { + private val objects: MutableList + private val mockedParcel: Parcel + private var position = 0 + + init { + mockedParcel = mock(Parcel::class.java) + objects = ArrayList() + setupMock() + } + + private fun setupMock() { + setupWrites() + setupReads() + setupOthers() + } + + private fun setupWrites() { + val writeValueAnswer: Answer = Answer { invocation -> + val parameter = invocation.arguments[0] + objects.add(parameter) + null + } + val writeArrayAnswer: Answer = Answer { invocation -> + val parameters = invocation.arguments[0] as Array + objects.add(parameters.size) + for (o in parameters) { + objects.add(o) + } + null + } + val writeIntArrayAnswer: Answer = Answer { invocation -> + val parameters = invocation.arguments[0] as IntArray + if (parameters != null) { + objects.add(parameters.size) + for (o in parameters) { + objects.add(o) + } + } else { + objects.add(-1) + } + null + } + doAnswer(writeValueAnswer).`when`(mockedParcel).writeByte(ArgumentMatchers.anyByte()) + doAnswer(writeValueAnswer).`when`(mockedParcel).writeLong(ArgumentMatchers.anyLong()) + doAnswer(writeValueAnswer).`when`(mockedParcel).writeString(ArgumentMatchers.anyString()) + doAnswer(writeValueAnswer).`when`(mockedParcel).writeInt(ArgumentMatchers.anyInt()) + doAnswer(writeIntArrayAnswer).`when`(mockedParcel).writeIntArray(ArgumentMatchers.any(IntArray::class.java)) + doAnswer(writeValueAnswer).`when`(mockedParcel).writeDouble(ArgumentMatchers.anyDouble()) + doAnswer(writeValueAnswer).`when`(mockedParcel).writeFloat(ArgumentMatchers.anyFloat()) + doAnswer(writeValueAnswer).`when`(mockedParcel).writeParcelable(ArgumentMatchers.any(Parcelable::class.java), ArgumentMatchers.eq(0)) + doAnswer(writeArrayAnswer).`when`(mockedParcel).writeParcelableArray(ArgumentMatchers.any(Array::class.java), ArgumentMatchers.eq(0)) + } + + private fun setupReads() { + `when`(mockedParcel.readInt()).then { objects[position++] as Int } + `when`(mockedParcel.readByte()).thenAnswer { objects[position++] as Byte } + `when`(mockedParcel.readLong()).thenAnswer { objects[position++] as Long } + `when`(mockedParcel.readString()).thenAnswer { objects[position++] as String } + `when`(mockedParcel.readDouble()).thenAnswer { objects[position++] as Double } + `when`(mockedParcel.readFloat()).thenAnswer { objects[position++] as Float } + `when`(mockedParcel.readParcelable(Parcelable::class.java.classLoader)).thenAnswer { objects[position++] as Parcelable } + `when`(mockedParcel.readParcelableArray(Parcelable::class.java.classLoader)).thenAnswer { + val size = objects[position++] as Int + val field = `object`.javaClass.getDeclaredField("CREATOR") + field.isAccessible = true + val creatorClass = field.type + val fieldValue = field[`object`] + val myMethod = creatorClass.getDeclaredMethod("newArray", Int::class.javaPrimitiveType) + val array = myMethod.invoke(fieldValue, size) as Array + for (i in 0 until size) { + array[i] = objects[position++] + } + array + } + `when`(mockedParcel.createIntArray()).then(Answer { + val size = objects[position++] as Int + if (size == -1) { + return@Answer null + } + val array = IntArray(size) + for (i in 0 until size) { + array[i] = objects[position++] as Int + } + array + }) + } + + private fun setupOthers() { + doAnswer { invocation -> + position = invocation.arguments[0] as Int + null + }.`when`(mockedParcel).setDataPosition(ArgumentMatchers.anyInt()) + } + + companion object { + fun obtain(target: Parcelable): Parcel { + val parcel = ParcelMocker(target).mockedParcel + target.writeToParcel(parcel, 0) + parcel.setDataPosition(0) + return parcel + } + + fun obtain(targets: Array): Parcel { + require(targets.size != 0) { "The passed argument may not be empty" } + val parcel = ParcelMocker(targets[0]).mockedParcel + parcel.writeParcelableArray(targets, 0) + parcel.setDataPosition(0) + return parcel + } + } + } +}